emnapi 1.11.0 → 2.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +127 -57
- package/README.md +164 -776
- package/cmake/wasm32.cmake +0 -3
- package/common.gypi +16 -14
- package/dist/library_async_work.js +390 -0
- package/dist/library_napi.js +1476 -2802
- package/dist/library_threadsafe_function.js +1178 -0
- package/dist/library_v8.js +1594 -0
- package/emnapi.gyp +37 -0
- package/include/node/emnapi.h +13 -2
- package/include/node/js_native_api_types.h +22 -0
- package/include/node/node.h +198 -0
- package/include/node/node_api.h +0 -10
- package/include/node/node_buffer.h +92 -0
- package/include/node/node_object_wrap.h +132 -0
- package/include/node/node_version.h +110 -0
- package/include/node/uv/unix.h +1 -0
- package/include/node/uv/version.h +43 -0
- package/include/node/uv.h +6 -0
- package/index.js +15 -7
- package/lib/wasm32-emscripten/libemnapi-mt.a +0 -0
- package/lib/wasm32-emscripten/libemnapi.a +0 -0
- package/lib/wasm32-wasip1-threads/libemnapi-mt.a +0 -0
- package/lib/wasm32-wasip1-threads/libemnapi-napi-rs-mt.a +0 -0
- package/lib/wasm32-wasip1-threads/libemnapi.a +0 -0
- package/lib/wasm64-emscripten/libemnapi-mt.a +0 -0
- package/lib/wasm64-emscripten/libemnapi.a +0 -0
- package/package.json +1 -1
- package/src/async_cleanup_hook.c +6 -6
- package/src/emnapi_internal.h +5 -10
- package/src/js_native_api.c +37 -21
- package/src/js_native_api_internal.h +66 -0
- package/src/node_api.c +1 -1
- package/src/threadsafe_function.c +2 -2
- package/src/uv/unix/thread.c +21 -0
- package/src/v8/array.cc +19 -0
- package/src/v8/boolean.cc +26 -0
- package/src/v8/date.cc +15 -0
- package/src/v8/exception.cc +48 -0
- package/src/v8/external.cc +23 -0
- package/src/v8/function.cc +35 -0
- package/src/v8/handle_scope.cc +46 -0
- package/src/v8/internal.cc +126 -0
- package/src/v8/internal.h +41 -0
- package/src/v8/isolate.cc +35 -0
- package/src/v8/json.cc +25 -0
- package/src/v8/node.cc +24 -0
- package/src/v8/number.cc +62 -0
- package/src/v8/object.cc +106 -0
- package/src/v8/script.cc +75 -0
- package/src/v8/string.cc +104 -0
- package/src/v8/template.cc +234 -0
- package/src/v8/try_catch.cc +50 -0
- package/src/v8/v8_impl.h +42 -0
- package/src/v8/value.cc +138 -0
- package/lib/wasm32/libdlmalloc-mt.a +0 -0
- package/lib/wasm32/libdlmalloc.a +0 -0
- package/lib/wasm32/libemmalloc-mt.a +0 -0
- package/lib/wasm32/libemmalloc.a +0 -0
- package/lib/wasm32/libemnapi-basic-mt.a +0 -0
- package/lib/wasm32/libemnapi-basic.a +0 -0
- package/lib/wasm32/libemnapi.a +0 -0
- package/lib/wasm32-emscripten/libemnapi-basic.a +0 -0
- package/lib/wasm32-wasi/libemnapi-basic-mt.a +0 -0
- package/lib/wasm32-wasi/libemnapi-basic.a +0 -0
- package/lib/wasm32-wasi/libemnapi.a +0 -0
- package/lib/wasm32-wasi-threads/libemnapi-basic-mt.a +0 -0
- package/lib/wasm32-wasi-threads/libemnapi-basic-napi-rs-mt.a +0 -0
- package/lib/wasm32-wasi-threads/libemnapi-basic.a +0 -0
- package/lib/wasm32-wasi-threads/libemnapi-mt.a +0 -0
- package/lib/wasm32-wasi-threads/libemnapi-napi-rs-mt.a +0 -0
- package/lib/wasm32-wasi-threads/libemnapi.a +0 -0
- package/lib/wasm32-wasip1/libemnapi-basic-mt.a +0 -0
- package/lib/wasm32-wasip1/libemnapi-basic.a +0 -0
- package/lib/wasm32-wasip1/libemnapi.a +0 -0
- package/lib/wasm32-wasip1-threads/libemnapi-basic-mt.a +0 -0
- package/lib/wasm32-wasip1-threads/libemnapi-basic-napi-rs-mt.a +0 -0
- package/lib/wasm32-wasip1-threads/libemnapi-basic.a +0 -0
- package/lib/wasm64-emscripten/libemnapi-basic.a +0 -0
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
[](https://github.com/toyobayashi/emnapi/actions/workflows/main.yml)
|
|
16
16
|
|
|
17
|
+
# This is 2.x branch, [go stable 1.x branch here](https://github.com/toyobayashi/emnapi/tree/v1.x)
|
|
18
|
+
|
|
17
19
|
[Node-API](https://nodejs.org/docs/latest/api/n-api.html) implementation for [Emscripten](https://emscripten.org/index.html), [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) and clang with wasm support.
|
|
18
20
|
|
|
19
21
|
This project aims to
|
|
@@ -43,51 +45,33 @@ If you want to deep dive into WebAssembly, highly recommend you to visit [learn-
|
|
|
43
45
|
|
|
44
46
|
You will need to install:
|
|
45
47
|
|
|
46
|
-
- Node.js `>=
|
|
48
|
+
- Node.js `>= v22.12.0`
|
|
47
49
|
- npm `>= v8`
|
|
48
|
-
- Emscripten `>= v3.1.9` / wasi-sdk / LLVM clang with wasm support
|
|
49
|
-
- (Optional) CMake `>= v3.13`
|
|
50
|
-
- (Optional) node-gyp `>= v10.2.0`
|
|
51
|
-
- (Optional) ninja
|
|
52
|
-
- (Optional) make
|
|
53
|
-
- (Optional) [node-addon-api](https://github.com/nodejs/node-addon-api) `>= 6.1.0`
|
|
54
50
|
|
|
55
|
-
|
|
51
|
+
Tool chain choices:
|
|
56
52
|
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
- Install [Visual Studio 2022](https://visualstudio.microsoft.com/) C++ desktop workload, use `nmake` in `Visual Studio Developer Command Prompt`
|
|
60
|
-
- Install [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/), use `nmake` in `Visual Studio Developer Command Prompt`
|
|
53
|
+
- Emscripten `>= v4.1.7` with `EMSDK` environment variable set
|
|
54
|
+
- wasi-sdk `>= v26` with `WASI_SDK_PATH` environment variable set
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
Build system choices:
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
node
|
|
66
|
-
|
|
67
|
-
emcc -v
|
|
58
|
+
- CMake `>= v3.13`
|
|
59
|
+
- node-gyp `>= v10.2.0`
|
|
60
|
+
- make / ninja
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
# clang -print-targets # ensure wasm32 target exists
|
|
62
|
+
Optional:
|
|
71
63
|
|
|
72
|
-
|
|
64
|
+
- [node-addon-api](https://github.com/nodejs/node-addon-api) `>= 6.1.0`
|
|
73
65
|
|
|
74
|
-
|
|
75
|
-
node-gyp --version
|
|
76
|
-
|
|
77
|
-
# if you use ninja
|
|
78
|
-
ninja --version
|
|
79
|
-
|
|
80
|
-
# if you use make
|
|
81
|
-
make -v
|
|
66
|
+
There are several choices to get `make` for Windows user
|
|
82
67
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
- Install [mingw-w64](https://www.mingw-w64.org/downloads/), then use `mingw32-make`
|
|
69
|
+
- Download [MSVC prebuilt binary of GNU make](https://github.com/toyobayashi/make-win-build/releases), add to `%Path%` then rename it to `mingw32-make`
|
|
70
|
+
- Install [Visual Studio 2022](https://visualstudio.microsoft.com/) C++ desktop workload, use `nmake` in `Visual Studio Developer Command Prompt`
|
|
71
|
+
- Install [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/), use `nmake` in `Visual Studio Developer Command Prompt`
|
|
86
72
|
|
|
87
73
|
## Build from source
|
|
88
74
|
|
|
89
|
-
You need to set `EMSDK` and `WASI_SDK_PATH` environment variables.
|
|
90
|
-
|
|
91
75
|
```bash
|
|
92
76
|
git clone https://github.com/toyobayashi/emnapi.git
|
|
93
77
|
cd ./emnapi
|
|
@@ -96,25 +80,31 @@ npm install
|
|
|
96
80
|
npm run build # output ./packages/*/dist
|
|
97
81
|
node ./script/release.js # output ./out
|
|
98
82
|
|
|
99
|
-
# test
|
|
100
|
-
npm run rebuild
|
|
101
|
-
npm test
|
|
83
|
+
# test for emscripten
|
|
84
|
+
npm run rebuild -w packages/test
|
|
85
|
+
npm run test -w packages/test
|
|
86
|
+
|
|
87
|
+
# test for wasi-sdk
|
|
88
|
+
npm run rebuild:wt -w packages/test
|
|
89
|
+
npm run test:wt -w packages/test
|
|
102
90
|
```
|
|
103
91
|
|
|
104
92
|
See [CONTRIBUTING](https://github.com/toyobayashi/emnapi/blob/main/CONTRIBUTING.md) for more details.
|
|
105
93
|
|
|
106
94
|
## Quick Start
|
|
107
95
|
|
|
96
|
+
[See full example](https://github.com/toyobayashi/emnapi/tree/main/packages/example)
|
|
97
|
+
|
|
108
98
|
### NPM Install
|
|
109
99
|
|
|
110
100
|
```bash
|
|
111
101
|
npm install -D emnapi
|
|
112
102
|
npm install @emnapi/runtime
|
|
113
103
|
|
|
114
|
-
# for
|
|
104
|
+
# additionally, for wasi-sdk, install @emnapi/core
|
|
115
105
|
npm install @emnapi/core
|
|
116
106
|
|
|
117
|
-
# if you
|
|
107
|
+
# if you need node-addon-api
|
|
118
108
|
npm install node-addon-api
|
|
119
109
|
```
|
|
120
110
|
|
|
@@ -122,525 +112,197 @@ Each package should match the same version.
|
|
|
122
112
|
|
|
123
113
|
### Using C
|
|
124
114
|
|
|
125
|
-
Create `
|
|
115
|
+
Create `binding.c`:
|
|
126
116
|
|
|
127
117
|
```c
|
|
128
118
|
#include <node_api.h>
|
|
129
119
|
|
|
130
|
-
|
|
131
|
-
do { \
|
|
132
|
-
if ((the_call) != napi_ok) { \
|
|
133
|
-
const napi_extended_error_info *error_info; \
|
|
134
|
-
napi_get_last_error_info((env), &error_info); \
|
|
135
|
-
bool is_pending; \
|
|
136
|
-
const char* err_message = error_info->error_message; \
|
|
137
|
-
napi_is_exception_pending((env), &is_pending); \
|
|
138
|
-
if (!is_pending) { \
|
|
139
|
-
const char* error_message = err_message != NULL ? \
|
|
140
|
-
err_message : \
|
|
141
|
-
"empty error message"; \
|
|
142
|
-
napi_throw_error((env), NULL, error_message); \
|
|
143
|
-
} \
|
|
144
|
-
return NULL; \
|
|
145
|
-
} \
|
|
146
|
-
} while (0)
|
|
147
|
-
|
|
148
|
-
static napi_value js_hello(napi_env env, napi_callback_info info) {
|
|
149
|
-
napi_value world;
|
|
150
|
-
const char* str = "world";
|
|
151
|
-
NODE_API_CALL(env, napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &world));
|
|
152
|
-
return world;
|
|
153
|
-
}
|
|
120
|
+
// ...
|
|
154
121
|
|
|
155
122
|
NAPI_MODULE_INIT() {
|
|
156
|
-
napi_value
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
NODE_API_CALL(env, napi_set_named_property(env, exports, "hello", hello));
|
|
123
|
+
napi_value fn;
|
|
124
|
+
napi_create_function(env, "run", NAPI_AUTO_LENGTH, run, NULL, &fn);
|
|
125
|
+
napi_set_named_property(env, exports, "run", fn);
|
|
160
126
|
return exports;
|
|
161
127
|
}
|
|
162
128
|
```
|
|
163
129
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```js
|
|
167
|
-
module.exports = (function (exports) {
|
|
168
|
-
const hello = function hello () {
|
|
169
|
-
// native code in js_hello
|
|
170
|
-
const world = 'world'
|
|
171
|
-
return world
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
exports.hello = hello
|
|
175
|
-
return exports
|
|
176
|
-
})(module.exports)
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
#### Building
|
|
180
|
-
|
|
181
|
-
<details>
|
|
182
|
-
<summary>emscripten</summary><br />
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
emcc -O3 \
|
|
186
|
-
-DBUILDING_NODE_EXTENSION \
|
|
187
|
-
"-DNAPI_EXTERN=__attribute__((__import_module__(\"env\")))" \
|
|
188
|
-
-I./node_modules/emnapi/include/node \
|
|
189
|
-
-L./node_modules/emnapi/lib/wasm32-emscripten \
|
|
190
|
-
--js-library=./node_modules/emnapi/dist/library_napi.js \
|
|
191
|
-
-sEXPORTED_FUNCTIONS="['_malloc','_free','_napi_register_wasm_v1','_node_api_module_get_api_version_v1']" \
|
|
192
|
-
-sEXPORTED_RUNTIME_METHODS=['emnapiInit'] \
|
|
193
|
-
-o hello.js \
|
|
194
|
-
hello.c \
|
|
195
|
-
-lemnapi
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
</details>
|
|
199
|
-
|
|
200
|
-
<details>
|
|
201
|
-
<summary>wasi-sdk</summary><br />
|
|
130
|
+
Build with Emscripten:
|
|
202
131
|
|
|
203
132
|
```bash
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
</details>
|
|
225
|
-
|
|
226
|
-
<details>
|
|
227
|
-
<summary>clang wasm32</summary><br />
|
|
228
|
-
|
|
229
|
-
Choose `libdlmalloc.a` or `libemmalloc.a` for `malloc` and `free`.
|
|
133
|
+
emcc -O3 -pthread \
|
|
134
|
+
-DBUILDING_NODE_EXTENSION \
|
|
135
|
+
"-DNAPI_EXTERN=__attribute__((__import_module__(\"env\")))" \
|
|
136
|
+
-I./node_modules/emnapi/include/node \
|
|
137
|
+
-L./node_modules/emnapi/lib/wasm32-emscripten \
|
|
138
|
+
--js-library=./node_modules/emnapi/dist/library_napi.js \
|
|
139
|
+
-sWASM_BIGINT=1 \
|
|
140
|
+
-sALLOW_MEMORY_GROWTH=1 \
|
|
141
|
+
-sALLOW_TABLE_GROWTH=1 \
|
|
142
|
+
-sEXPORTED_FUNCTIONS=$(node -p "JSON.stringify(require('emnapi').requiredConfig.emscripten.settings.EXPORTED_FUNCTIONS)") \
|
|
143
|
+
-sEXPORTED_RUNTIME_METHODS=$(node -p "JSON.stringify(require('emnapi').requiredConfig.emscripten.settings.EXPORTED_RUNTIME_METHODS)") \
|
|
144
|
+
-sEXPORT_ES6=1 \
|
|
145
|
+
-sPTHREAD_POOL_SIZE=4 \
|
|
146
|
+
-o out/binding.js \
|
|
147
|
+
binding.c \
|
|
148
|
+
-lemnapi-mt
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Build with wasi-sdk:
|
|
230
152
|
|
|
231
153
|
```bash
|
|
232
|
-
clang -O3 \
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
</details>
|
|
254
|
-
|
|
255
|
-
#### Initialization
|
|
256
|
-
|
|
257
|
-
To initialize emnapi, you need to import the emnapi runtime to create a `Context` by `createContext` or `getDefaultContext` first.
|
|
258
|
-
Each context owns isolated Node-API object such as `napi_env`, `napi_value`, `napi_ref`. If you have multiple emnapi modules, you should reuse the same `Context` across them.
|
|
259
|
-
|
|
260
|
-
```ts
|
|
261
|
-
declare namespace emnapi {
|
|
262
|
-
// module '@emnapi/runtime'
|
|
263
|
-
export class Context { /* ... */ }
|
|
264
|
-
/** Create a new context */
|
|
265
|
-
export function createContext (): Context
|
|
266
|
-
/** Create or get */
|
|
267
|
-
export function getDefaultContext (): Context
|
|
268
|
-
// ...
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
<details>
|
|
273
|
-
<summary>emscripten</summary><br />
|
|
274
|
-
|
|
275
|
-
then call `Module.emnapiInit` after emscripten runtime initialized.
|
|
276
|
-
`Module.emnapiInit` only do initialization once, it will always return the same binding exports after successfully initialized.
|
|
277
|
-
|
|
278
|
-
```ts
|
|
279
|
-
declare namespace Module {
|
|
280
|
-
interface EmnapiInitOptions {
|
|
281
|
-
context: emnapi.Context
|
|
282
|
-
|
|
283
|
-
/** node_api_get_module_file_name */
|
|
284
|
-
filename?: string
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Support following async_hooks related things
|
|
288
|
-
* on Node.js runtime only
|
|
289
|
-
*
|
|
290
|
-
* napi_async_init,
|
|
291
|
-
* napi_async_destroy,
|
|
292
|
-
* napi_make_callback,
|
|
293
|
-
* async resource parameter of
|
|
294
|
-
* napi_create_async_work and napi_create_threadsafe_function
|
|
295
|
-
*/
|
|
296
|
-
nodeBinding?: typeof import('@emnapi/node-binding')
|
|
297
|
-
|
|
298
|
-
/** See Multithread part */
|
|
299
|
-
asyncWorkPoolSize?: number
|
|
300
|
-
}
|
|
301
|
-
export function emnapiInit (options: EmnapiInitOptions): any
|
|
302
|
-
}
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
```html
|
|
306
|
-
<script src="node_modules/@emnapi/runtime/dist/emnapi.min.js"></script>
|
|
307
|
-
<script src="hello.js"></script>
|
|
308
|
-
<script>
|
|
309
|
-
Module.onRuntimeInitialized = function () {
|
|
310
|
-
var binding;
|
|
311
|
-
try {
|
|
312
|
-
binding = Module.emnapiInit({ context: emnapi.getDefaultContext() });
|
|
313
|
-
} catch (err) {
|
|
314
|
-
console.error(err);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
var msg = 'hello ' + binding.hello();
|
|
318
|
-
window.alert(msg);
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// if -sMODULARIZE=1
|
|
322
|
-
Module({ /* Emscripten module init options */ }).then(function (Module) {
|
|
323
|
-
var binding = Module.emnapiInit({ context: emnapi.getDefaultContext() });
|
|
324
|
-
});
|
|
325
|
-
</script>
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
If you are using `Visual Studio Code` and have `Live Server` extension installed, you can right click the HTML file in Visual Studio Code source tree and click `Open With Live Server`, then you can see the hello world alert!
|
|
329
|
-
|
|
330
|
-
Running on Node.js:
|
|
154
|
+
"$WASI_SDK_PATH/bin/clang" --target=wasm32-wasip1-threads -O3 \
|
|
155
|
+
-pthread -matomics -mbulk-memory \
|
|
156
|
+
-DBUILDING_NODE_EXTENSION \
|
|
157
|
+
-I./node_modules/emnapi/include/node \
|
|
158
|
+
-L./node_modules/emnapi/lib/wasm32-wasip1-threads \
|
|
159
|
+
--sysroot="$WASI_SDK_PATH/share/wasi-sysroot" \
|
|
160
|
+
-mexec-model=reactor \
|
|
161
|
+
-Wl,--import-memory -Wl,--shared-memory -Wl,--export-memory \
|
|
162
|
+
-Wl,--export-table -Wl,--growable-table \
|
|
163
|
+
-Wl,--export=malloc,--export=free \
|
|
164
|
+
-Wl,--export=napi_register_wasm_v1 \
|
|
165
|
+
-Wl,--export=emnapi_create_env,--export=emnapi_delete_env \
|
|
166
|
+
-Wl,--export-if-defined=node_api_module_get_api_version_v1 \
|
|
167
|
+
-Wl,--export-if-defined=uv_library_shutdown \
|
|
168
|
+
-Wl,--import-undefined \
|
|
169
|
+
-o out/binding.wasm \
|
|
170
|
+
binding.c \
|
|
171
|
+
-lemnapi-mt
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Use in JavaScript (Emscripten):
|
|
331
175
|
|
|
332
176
|
```js
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
console.error(err)
|
|
342
|
-
return
|
|
343
|
-
}
|
|
344
|
-
const msg = `hello ${binding.hello()}`
|
|
345
|
-
console.log(msg)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// if -sMODULARIZE=1
|
|
349
|
-
Module({ /* Emscripten module init options */ }).then((Module) => {
|
|
350
|
-
const binding = Module.emnapiInit({ context: emnapi.getDefaultContext() })
|
|
177
|
+
import init from './out/binding.js'
|
|
178
|
+
import { createContext } from '@emnapi/runtime'
|
|
179
|
+
|
|
180
|
+
const emnapiCtx = createContext()
|
|
181
|
+
const Module = await init()
|
|
182
|
+
const binding = Module.emnapiInit({
|
|
183
|
+
context: emnapiCtx,
|
|
184
|
+
asyncWorkPoolSize: 4 // the same effect to UV_THREADPOOL_SIZE, must less than PTHREAD_POOL_SIZE
|
|
351
185
|
})
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
</details>
|
|
186
|
+
binding.run(/* ... */)
|
|
355
187
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
For non-emscripten, you need to use `@emnapi/core`. The initialization is similar to emscripten.
|
|
360
|
-
|
|
361
|
-
```html
|
|
362
|
-
<script src="node_modules/@emnapi/runtime/dist/emnapi.min.js"></script>
|
|
363
|
-
<script src="node_modules/@emnapi/core/dist/emnapi-core.min.js"></script>
|
|
364
|
-
<script>
|
|
365
|
-
emnapiCore.instantiateNapiModule(fetch('./hello.wasm'), {
|
|
366
|
-
context: emnapi.getDefaultContext(),
|
|
367
|
-
overwriteImports (importObject) {
|
|
368
|
-
// importObject.env = {
|
|
369
|
-
// ...importObject.env,
|
|
370
|
-
// ...importObject.napi,
|
|
371
|
-
// ...importObject.emnapi,
|
|
372
|
-
// // ...
|
|
373
|
-
// }
|
|
374
|
-
}
|
|
375
|
-
}).then(({ instance, module, napiModule }) => {
|
|
376
|
-
const binding = napiModule.exports
|
|
377
|
-
// ...
|
|
378
|
-
})
|
|
379
|
-
</script>
|
|
188
|
+
// cleanup
|
|
189
|
+
Module._uv_library_shutdown()
|
|
190
|
+
emnapiCtx.destroy()
|
|
380
191
|
```
|
|
381
192
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
```js
|
|
385
|
-
const { instantiateNapiModule } = require('@emnapi/core')
|
|
386
|
-
const { getDefaultContext } = require('@emnapi/runtime')
|
|
387
|
-
const { WASI } = require('wasi')
|
|
388
|
-
const fs = require('fs')
|
|
389
|
-
|
|
390
|
-
instantiateNapiModule(fs.promises.readFile('./hello.wasm'), {
|
|
391
|
-
wasi: new WASI({ /* ... */ }),
|
|
392
|
-
context: getDefaultContext(),
|
|
393
|
-
overwriteImports (importObject) {
|
|
394
|
-
// importObject.env = {
|
|
395
|
-
// ...importObject.env,
|
|
396
|
-
// ...importObject.napi,
|
|
397
|
-
// ...importObject.emnapi,
|
|
398
|
-
// // ...
|
|
399
|
-
// }
|
|
400
|
-
}
|
|
401
|
-
}).then(({ instance, module, napiModule }) => {
|
|
402
|
-
const binding = napiModule.exports
|
|
403
|
-
// ...
|
|
404
|
-
})
|
|
405
|
-
```
|
|
193
|
+
For non-emscripten please [reference full JavaScript example](https://github.com/toyobayashi/emnapi/tree/main/packages/example/index-wasi.js)
|
|
406
194
|
|
|
407
|
-
|
|
408
|
-
and [memfs-browser](https://github.com/toyobayashi/memfs-browser)
|
|
409
|
-
|
|
410
|
-
```js
|
|
411
|
-
import { instantiateNapiModule } from '@emnapi/core'
|
|
412
|
-
import { getDefaultContext } from '@emnapi/runtime'
|
|
413
|
-
import { WASI } from '@tybys/wasm-util'
|
|
414
|
-
import { Volume, createFsFromVolume } from 'memfs-browser'
|
|
415
|
-
|
|
416
|
-
const fs = createFsFromVolume(Volume.fromJSON({ /* ... */ }))
|
|
417
|
-
instantiateNapiModule(fetch('./hello.wasm'), {
|
|
418
|
-
wasi: new WASI({ fs, /* ... */ })
|
|
419
|
-
context: getDefaultContext(),
|
|
420
|
-
overwriteImports (importObject) {
|
|
421
|
-
// importObject.env = {
|
|
422
|
-
// ...importObject.env,
|
|
423
|
-
// ...importObject.napi,
|
|
424
|
-
// ...importObject.emnapi,
|
|
425
|
-
// // ...
|
|
426
|
-
// }
|
|
427
|
-
}
|
|
428
|
-
}).then(({ instance, module, napiModule }) => {
|
|
429
|
-
const binding = napiModule.exports
|
|
430
|
-
// ...
|
|
431
|
-
})
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
</details>
|
|
195
|
+
<!---->
|
|
435
196
|
|
|
436
197
|
### Using C++ and node-addon-api
|
|
437
198
|
|
|
438
|
-
Require [`node-addon-api`](https://github.com/nodejs/node-addon-api) `>= 6.1.0`
|
|
439
|
-
|
|
440
|
-
```bash
|
|
441
|
-
npm install node-addon-api
|
|
442
|
-
```
|
|
443
|
-
|
|
444
199
|
**Note: C++ wrapper can only be used to target Node.js v14.6.0+ and modern browsers those support `FinalizationRegistry` and `WeakRef` ([v8 engine v8.4+](https://v8.dev/blog/v8-release-84))!**
|
|
445
200
|
|
|
446
|
-
Create `
|
|
201
|
+
Create `binding.cpp`:
|
|
447
202
|
|
|
448
203
|
```cpp
|
|
449
204
|
#include <napi.h>
|
|
450
205
|
|
|
451
|
-
|
|
452
|
-
Napi::Env env = info.Env();
|
|
453
|
-
return Napi::String::New(env, "world");
|
|
454
|
-
}
|
|
206
|
+
// ...
|
|
455
207
|
|
|
456
208
|
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
457
|
-
exports.Set(Napi::
|
|
458
|
-
Napi::Function::New(env, Method)).Check();
|
|
209
|
+
exports.Set("run", Napi::Function::New(env, Run));
|
|
459
210
|
return exports;
|
|
460
211
|
}
|
|
461
212
|
|
|
462
|
-
NODE_API_MODULE(
|
|
213
|
+
NODE_API_MODULE(binding, Init)
|
|
463
214
|
```
|
|
464
215
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
#### Building
|
|
468
|
-
|
|
469
|
-
<details>
|
|
470
|
-
<summary>emscripten</summary><br />
|
|
216
|
+
Build with Emscripten:
|
|
471
217
|
|
|
472
218
|
```bash
|
|
473
|
-
em++ -O3 \
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
219
|
+
em++ -O3 -pthread \
|
|
220
|
+
-DBUILDING_NODE_EXTENSION \
|
|
221
|
+
-DNAPI_DISABLE_CPP_EXCEPTIONS \
|
|
222
|
+
"-DNAPI_EXTERN=__attribute__((__import_module__(\"env\")))" \
|
|
223
|
+
-I./node_modules/emnapi/include/node \
|
|
224
|
+
-I$(node -p "require('node-addon-api').include_dir") \
|
|
225
|
+
-L./node_modules/emnapi/lib/wasm32-emscripten \
|
|
226
|
+
--js-library=./node_modules/emnapi/dist/library_napi.js \
|
|
227
|
+
-sWASM_BIGINT=1 \
|
|
228
|
+
-sALLOW_MEMORY_GROWTH=1 \
|
|
229
|
+
-sALLOW_TABLE_GROWTH=1 \
|
|
230
|
+
-sEXPORTED_FUNCTIONS=$(node -p "JSON.stringify(require('emnapi').requiredConfig.emscripten.settings.EXPORTED_FUNCTIONS)") \
|
|
231
|
+
-sEXPORTED_RUNTIME_METHODS=$(node -p "JSON.stringify(require('emnapi').requiredConfig.emscripten.settings.EXPORTED_RUNTIME_METHODS)") \
|
|
232
|
+
-sEXPORT_ES6=1 \
|
|
233
|
+
-sPTHREAD_POOL_SIZE=4 \
|
|
234
|
+
-o out/binding-naa.js \
|
|
235
|
+
binding.cpp \
|
|
236
|
+
-lemnapi-mt
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Build with wasi-sdk:
|
|
493
240
|
|
|
494
241
|
```bash
|
|
495
|
-
clang++ -O3 \
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
hello.cpp \
|
|
516
|
-
-lemnapi
|
|
242
|
+
"$WASI_SDK_PATH/bin/clang++" --target=wasm32-wasip1-threads -O3 \
|
|
243
|
+
-pthread -matomics -mbulk-memory -fno-exceptions \
|
|
244
|
+
-DBUILDING_NODE_EXTENSION \
|
|
245
|
+
-DNAPI_DISABLE_CPP_EXCEPTIONS \
|
|
246
|
+
-I./node_modules/emnapi/include/node \
|
|
247
|
+
-I$(node -p "require('node-addon-api').include_dir") \
|
|
248
|
+
-L./node_modules/emnapi/lib/wasm32-wasip1-threads \
|
|
249
|
+
--sysroot="$WASI_SDK_PATH/share/wasi-sysroot" \
|
|
250
|
+
-mexec-model=reactor \
|
|
251
|
+
-Wl,--import-memory -Wl,--shared-memory -Wl,--export-memory \
|
|
252
|
+
-Wl,--export-table -Wl,--growable-table \
|
|
253
|
+
-Wl,--export=malloc,--export=free \
|
|
254
|
+
-Wl,--export=napi_register_wasm_v1 \
|
|
255
|
+
-Wl,--export=emnapi_create_env,--export=emnapi_delete_env \
|
|
256
|
+
-Wl,--export-if-defined=node_api_module_get_api_version_v1 \
|
|
257
|
+
-Wl,--export-if-defined=uv_library_shutdown \
|
|
258
|
+
-Wl,--import-undefined \
|
|
259
|
+
-o out/binding-naa.wasm \
|
|
260
|
+
binding.cpp \
|
|
261
|
+
-lemnapi-mt
|
|
517
262
|
```
|
|
518
263
|
|
|
519
|
-
</details>
|
|
520
|
-
|
|
521
|
-
<details>
|
|
522
|
-
<summary>clang wasm32</summary><br />
|
|
523
|
-
|
|
524
|
-
`node-addon-api` is using the C++ standard libraries, so you must use WASI if you are using `node-addon-api`.
|
|
525
|
-
|
|
526
|
-
You can still use `wasm32-unknown-unknown` target if you use Node-API C API only in C++.
|
|
527
|
-
|
|
528
|
-
```bash
|
|
529
|
-
clang++ -O3 \
|
|
530
|
-
-DBUILDING_NODE_EXTENSION \
|
|
531
|
-
-I./node_modules/emnapi/include/node \
|
|
532
|
-
-L./node_modules/emnapi/lib/wasm32 \
|
|
533
|
-
--target=wasm32 \
|
|
534
|
-
-fno-exceptions \
|
|
535
|
-
-nostdlib \
|
|
536
|
-
-Wl,--no-entry \
|
|
537
|
-
-Wl,--initial-memory=16777216 \
|
|
538
|
-
-Wl,--export-dynamic \
|
|
539
|
-
-Wl,--export=malloc \
|
|
540
|
-
-Wl,--export=free \
|
|
541
|
-
-Wl,--export=napi_register_wasm_v1 \
|
|
542
|
-
-Wl,--export-if-defined=node_api_module_get_api_version_v1 \
|
|
543
|
-
-Wl,--import-undefined \
|
|
544
|
-
-Wl,--export-table \
|
|
545
|
-
-o node_api_c_api_only.wasm \
|
|
546
|
-
node_api_c_api_only.cpp \
|
|
547
|
-
-lemnapi \
|
|
548
|
-
-ldlmalloc # -lemmalloc
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
`operator new` and `operator delete`.
|
|
552
|
-
|
|
553
|
-
```cpp
|
|
554
|
-
#include <stddef.h>
|
|
555
|
-
|
|
556
|
-
extern "C" void* malloc(size_t size);
|
|
557
|
-
extern "C" void free(void* p);
|
|
558
|
-
|
|
559
|
-
void* operator new(size_t size) {
|
|
560
|
-
return malloc(size);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
void operator delete(void* p) noexcept {
|
|
564
|
-
free(p);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
void operator delete(void* p, size_t) noexcept {
|
|
568
|
-
free(p);
|
|
569
|
-
}
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
</details>
|
|
573
|
-
|
|
574
264
|
### Using CMake
|
|
575
265
|
|
|
576
|
-
Create `CMakeLists.txt
|
|
266
|
+
Create `CMakeLists.txt`:
|
|
577
267
|
|
|
578
268
|
```cmake
|
|
579
269
|
cmake_minimum_required(VERSION 3.13)
|
|
270
|
+
project(myproject)
|
|
580
271
|
|
|
581
|
-
|
|
272
|
+
# optional: enable node-addon-api
|
|
273
|
+
# set(EMNAPI_FIND_NODE_ADDON_API ON)
|
|
582
274
|
|
|
583
|
-
add_subdirectory("
|
|
275
|
+
add_subdirectory("node_modules/emnapi")
|
|
584
276
|
|
|
585
|
-
add_executable(
|
|
277
|
+
add_executable(binding binding.c)
|
|
278
|
+
|
|
279
|
+
target_link_libraries(binding PRIVATE emnapi-mt)
|
|
586
280
|
|
|
587
|
-
target_link_libraries(hello emnapi)
|
|
588
281
|
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
)
|
|
593
|
-
elseif(CMAKE_SYSTEM_NAME STREQUAL "WASI")
|
|
594
|
-
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
595
|
-
target_link_options(hello PRIVATE
|
|
596
|
-
"-mexec-model=reactor"
|
|
597
|
-
"-Wl,--export=napi_register_wasm_v1"
|
|
598
|
-
"-Wl,--export-if-defined=node_api_module_get_api_version_v1"
|
|
599
|
-
"-Wl,--initial-memory=16777216,--export-dynamic,--export=malloc,--export=free,--import-undefined,--export-table"
|
|
600
|
-
)
|
|
601
|
-
elseif((CMAKE_C_COMPILER_TARGET STREQUAL "wasm32") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-unknown-unknown"))
|
|
602
|
-
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
603
|
-
target_link_options(hello PRIVATE
|
|
604
|
-
"-nostdlib"
|
|
605
|
-
"-Wl,--export=napi_register_wasm_v1"
|
|
606
|
-
"-Wl,--export-if-defined=node_api_module_get_api_version_v1"
|
|
607
|
-
"-Wl,--no-entry"
|
|
608
|
-
"-Wl,--initial-memory=16777216,--export-dynamic,--export=malloc,--export=free,--import-undefined,--export-table"
|
|
609
|
-
)
|
|
610
|
-
target_link_libraries(hello dlmalloc)
|
|
611
|
-
# target_link_libraries(hello emmalloc)
|
|
282
|
+
# ...
|
|
283
|
+
elseif((CMAKE_SYSTEM_NAME STREQUAL "WASI") AND ((CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasip1-threads")))
|
|
284
|
+
# ...
|
|
612
285
|
endif()
|
|
613
286
|
```
|
|
614
287
|
|
|
615
|
-
|
|
288
|
+
Build with Emscripten:
|
|
616
289
|
|
|
617
290
|
```bash
|
|
618
|
-
mkdir build
|
|
291
|
+
mkdir build && cd build
|
|
292
|
+
emcmake cmake .. -DCMAKE_BUILD_TYPE=Release
|
|
293
|
+
cmake --build .
|
|
294
|
+
```
|
|
619
295
|
|
|
620
|
-
|
|
621
|
-
emcmake cmake -DCMAKE_BUILD_TYPE=Release \
|
|
622
|
-
-DEMNAPI_FIND_NODE_ADDON_API=ON \
|
|
623
|
-
-G Ninja -H. -Bbuild
|
|
296
|
+
Build with wasi-sdk:
|
|
624
297
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
-G Ninja -H. -Bbuild
|
|
631
|
-
|
|
632
|
-
# wasm32
|
|
633
|
-
cmake -DCMAKE_TOOLCHAIN_FILE=node_modules/emnapi/cmake/wasm32.cmake \
|
|
634
|
-
-DLLVM_PREFIX=$WASI_SDK_PATH \
|
|
635
|
-
-DCMAKE_BUILD_TYPE=Release \
|
|
636
|
-
-G Ninja -H. -Bbuild
|
|
637
|
-
|
|
638
|
-
cmake --build build
|
|
298
|
+
```bash
|
|
299
|
+
mkdir build && cd build
|
|
300
|
+
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
|
301
|
+
-DCMAKE_TOOLCHAIN_FILE="$WASI_SDK_PATH/share/cmake/wasi-sdk-pthread.cmake"
|
|
302
|
+
cmake --build .
|
|
639
303
|
```
|
|
640
304
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
### Using node-gyp (Experimental)
|
|
305
|
+
### Using node-gyp
|
|
644
306
|
|
|
645
307
|
Require node-gyp `>= 10.2.0`
|
|
646
308
|
|
|
@@ -787,157 +449,13 @@ make -C %~dp0build
|
|
|
787
449
|
|
|
788
450
|
See [napi-rs](https://github.com/napi-rs/napi-rs)
|
|
789
451
|
|
|
790
|
-
###
|
|
791
|
-
|
|
792
|
-
Related API:
|
|
793
|
-
|
|
794
|
-
- [napi_*_async_work](https://nodejs.org/dist/latest/docs/api/n-api.html#napi_create_async_work)
|
|
795
|
-
- [napi_*_threadsafe_function](https://nodejs.org/dist/latest/docs/api/n-api.html#asynchronous-thread-safe-function-calls)
|
|
796
|
-
|
|
797
|
-
They are available in emnapi, but you need to know more details before you start to use them.
|
|
798
|
-
Now emnapi has 3 implementations of async work and 2 implementations of TSFN:
|
|
799
|
-
|
|
800
|
-
- Async work
|
|
801
|
-
- A. Libuv threadpool and pthread based implementation in C
|
|
802
|
-
- B. Single thread mock in JavaScript
|
|
803
|
-
- C. Web worker based implementation in C (stack allocation) and JavaScript
|
|
804
|
-
- TSFN
|
|
805
|
-
- D. Libuv and pthread based implementation in C
|
|
806
|
-
- E. Web worker based implementation in JavaScript
|
|
807
|
-
|
|
808
|
-
| | Library to Link | `wasm32-emscripten` | `wasm32` | `wasm32-wasi` | `wasm32-wasi-threads` |
|
|
809
|
-
|---|------------------------|---------------------|----------|---------------|-----------------------|
|
|
810
|
-
| A | libemnapi-mt.a | ✅ | ❌ | ❌ | ✅ |
|
|
811
|
-
| B | libemnapi-basic(-mt).a | ✅ | ✅ | ✅ | ✅ |
|
|
812
|
-
| C | libemnapi-basic-mt.a | ❌ | ✅ | ❌ | ✅ |
|
|
813
|
-
| D | libemnapi-mt.a | ✅ | ❌ | ❌ | ✅ |
|
|
814
|
-
| E | libemnapi-basic(-mt).a | ✅ | ✅ | ✅ | ✅ |
|
|
815
|
-
|
|
816
|
-
There are some limitations on browser about wasi-libc's pthread implementation, for example
|
|
817
|
-
`pthread_mutex_lock` may call `__builtin_wasm_memory_atomic_wait32`(`memory.atomic.wait32`)
|
|
818
|
-
which is disallowed in browser JS main thread. While Emscripten's pthread implementation
|
|
819
|
-
has considered usage in browser. This issue can be solved by upgrading `wasi-sdk` to v26+
|
|
820
|
-
and emnapi v1.5.0+ then pass `--export=emnapi_thread_crashed` to the linker. If you need to
|
|
821
|
-
run your addon with multithreaded features, we recommend you use A & D or C & E.
|
|
822
|
-
|
|
823
|
-
Note: For browsers, all the multithreaded features relying on Web Workers (Emscripten pthread also relying on Web Workers)
|
|
824
|
-
require cross-origin isolation to enable `SharedArrayBuffer`. You can make a page cross-origin isolated
|
|
825
|
-
by serving the page with these headers:
|
|
826
|
-
|
|
827
|
-
```
|
|
828
|
-
Cross-Origin-Embedder-Policy: require-corp
|
|
829
|
-
Cross-Origin-Opener-Policy: same-origin
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
If you would like to avoid `SharedArrayBuffer` and cross-origin isolation, please use B & E (link against `libemnapi-basic.a`), see the following table for more details.
|
|
833
|
-
|
|
834
|
-
#### About Prebuilt Libraries
|
|
835
|
-
|
|
836
|
-
Prebuilt libraries can be found in the `lib` directory in `emnapi` npm package.
|
|
837
|
-
|
|
838
|
-
| Library | Description | `wasm32-emscripten` | `wasm32` | `wasm32-wasi` | `wasm32-wasi-threads` |
|
|
839
|
-
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------|----------|---------------|-----------------------------------------|
|
|
840
|
-
| libemnapi.a | no atomics feature.<br/><br/> no libuv port.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` always return `napi_generic_failure`. | ✅ | ✅ | ✅ | ✅ |
|
|
841
|
-
| libemnapi-mt.a | atomics feature enabled.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` are based on pthread and libuv port. | ✅ | ❌ | ❌ | ✅ |
|
|
842
|
-
| libemnapi-basic.a | no atomics feature.<br/><br/> no libuv port.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` are imported from JavaScript land. | ✅ | ✅ | ✅ | ✅ |
|
|
843
|
-
| libemnapi-basic-mt.a | atomics feature enabled.<br/><br/> no libuv port.<br/><br/> `napi_*_async_work` and `napi_*_threadsafe_function` are imported from JavaScript land.<br/><br/> include `emnapi_async_worker_create` and `emnapi_async_worker_init` for WebWorker based async work implementation. | ❌ | ✅ | ✅ | ✅ |
|
|
844
|
-
| libdlmalloc.a | no atomics feature, no thread safe garanteed. | ❌ | ✅ | ❌ | ❌ |
|
|
845
|
-
| libdlmalloc-mt.a | atomics feature enabled, thread safe. | ❌ | ✅ | ❌ | ❌ |
|
|
846
|
-
| libemmalloc.a | no atomics feature, no thread safe garanteed. | ❌ | ✅ | ❌ | ❌ |
|
|
847
|
-
| libemmalloc-mt.a | atomics feature enabled, thread safe. | ❌ | ✅ | ❌ | ❌ |
|
|
848
|
-
|
|
849
|
-
#### Usage
|
|
850
|
-
|
|
851
|
-
```cmake
|
|
852
|
-
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/node_modules/emnapi")
|
|
853
|
-
|
|
854
|
-
add_executable(hello hello.c)
|
|
855
|
-
|
|
856
|
-
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|
857
|
-
target_link_libraries(hello emnapi-mt)
|
|
858
|
-
target_compile_options(hello PRIVATE "-pthread")
|
|
859
|
-
target_link_options(hello PRIVATE
|
|
860
|
-
"-sALLOW_MEMORY_GROWTH=1"
|
|
861
|
-
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_napi_register_wasm_v1','_node_api_module_get_api_version_v1']"
|
|
862
|
-
"-sEXPORTED_RUNTIME_METHODS=['emnapiInit']"
|
|
863
|
-
"-pthread"
|
|
864
|
-
"-sPTHREAD_POOL_SIZE=4"
|
|
865
|
-
# try to specify stack size if you experience pthread errors
|
|
866
|
-
"-sSTACK_SIZE=2MB"
|
|
867
|
-
"-sDEFAULT_PTHREAD_STACK_SIZE=2MB"
|
|
868
|
-
)
|
|
869
|
-
elseif(CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-wasi-threads")
|
|
870
|
-
target_link_libraries(hello emnapi-mt)
|
|
871
|
-
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
872
|
-
target_compile_options(hello PRIVATE "-fno-exceptions" "-pthread")
|
|
873
|
-
target_link_options(hello PRIVATE
|
|
874
|
-
"-pthread"
|
|
875
|
-
"-mexec-model=reactor"
|
|
876
|
-
"-Wl,--import-memory"
|
|
877
|
-
"-Wl,--max-memory=2147483648"
|
|
878
|
-
"-Wl,--export-dynamic"
|
|
879
|
-
"-Wl,--export=napi_register_wasm_v1"
|
|
880
|
-
"-Wl,--export-if-defined=node_api_module_get_api_version_v1"
|
|
881
|
-
"-Wl,--export=malloc"
|
|
882
|
-
"-Wl,--export=free"
|
|
883
|
-
"-Wl,--export=emnapi_thread_crashed"
|
|
884
|
-
"-Wl,--import-undefined"
|
|
885
|
-
"-Wl,--export-table"
|
|
886
|
-
)
|
|
887
|
-
elseif((CMAKE_C_COMPILER_TARGET STREQUAL "wasm32") OR (CMAKE_C_COMPILER_TARGET STREQUAL "wasm32-unknown-unknown"))
|
|
888
|
-
target_link_libraries(hello emnapi-basic-mt)
|
|
889
|
-
set_target_properties(hello PROPERTIES SUFFIX ".wasm")
|
|
890
|
-
target_compile_options(hello PRIVATE "-fno-exceptions" "-matomics" "-mbulk-memory")
|
|
891
|
-
target_link_options(hello PRIVATE
|
|
892
|
-
"-nostdlib"
|
|
893
|
-
"-Wl,--no-entry"
|
|
894
|
-
"-Wl,--export=napi_register_wasm_v1"
|
|
895
|
-
"-Wl,--export-if-defined=node_api_module_get_api_version_v1"
|
|
896
|
-
"-Wl,--export=emnapi_async_worker_create"
|
|
897
|
-
"-Wl,--export=emnapi_async_worker_init"
|
|
898
|
-
"-Wl,--import-memory,--shared-memory,--max-memory=2147483648,--import-undefined"
|
|
899
|
-
"-Wl,--export-dynamic,--export=malloc,--export=free,--export-table"
|
|
900
|
-
)
|
|
901
|
-
endif()
|
|
902
|
-
```
|
|
903
|
-
|
|
904
|
-
```bash
|
|
905
|
-
# emscripten
|
|
906
|
-
emcmake cmake -DCMAKE_BUILD_TYPE=Release \
|
|
907
|
-
-DEMNAPI_FIND_NODE_ADDON_API=ON \
|
|
908
|
-
-DEMNAPI_WORKER_POOL_SIZE=4 \
|
|
909
|
-
-G Ninja -H. -Bbuild
|
|
910
|
-
|
|
911
|
-
# wasi-sdk with thread support
|
|
912
|
-
cmake -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk-pthread.cmake \
|
|
913
|
-
-DWASI_SDK_PREFIX=$WASI_SDK_PATH \
|
|
914
|
-
-DEMNAPI_FIND_NODE_ADDON_API=ON \
|
|
915
|
-
-DCMAKE_BUILD_TYPE=Release \
|
|
916
|
-
-G Ninja -H. -Bbuild
|
|
917
|
-
|
|
918
|
-
cmake -DCMAKE_TOOLCHAIN_FILE=node_modules/emnapi/cmake/wasm32.cmake \
|
|
919
|
-
-DLLVM_PREFIX=$WASI_SDK_PATH \
|
|
920
|
-
-DCMAKE_BUILD_TYPE=Release \
|
|
921
|
-
-G Ninja -H. -Bbuild
|
|
922
|
-
|
|
923
|
-
cmake --build build
|
|
924
|
-
```
|
|
925
|
-
|
|
926
|
-
And additional work is required during instantiating wasm compiled with non-emscripten.
|
|
452
|
+
### Reference for instantiateNapiModule input parameters
|
|
927
453
|
|
|
928
454
|
```js
|
|
929
455
|
// emnapi main thread (could be in a Worker)
|
|
930
456
|
instantiateNapiModule(input, {
|
|
931
457
|
context: getDefaultContext(),
|
|
932
|
-
|
|
933
|
-
* emscripten
|
|
934
|
-
* 0: no effect
|
|
935
|
-
* > 0: the same effect to UV_THREADPOOL_SIZE
|
|
936
|
-
* non-emscripten
|
|
937
|
-
* 0: single thread mock
|
|
938
|
-
* > 0 schedule async work in web worker
|
|
939
|
-
*/
|
|
940
|
-
asyncWorkPoolSize: 4, // 0: single thread mock, > 0: schedule async work in web worker
|
|
458
|
+
asyncWorkPoolSize: 4, // the same effect to UV_THREADPOOL_SIZE, must less than `reuseWorker.size`
|
|
941
459
|
wasi: new WASI(/* ... */),
|
|
942
460
|
|
|
943
461
|
/**
|
|
@@ -984,141 +502,11 @@ instantiateNapiModule(input, {
|
|
|
984
502
|
})
|
|
985
503
|
```
|
|
986
504
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
let fs, WASI, emnapiCore
|
|
991
|
-
|
|
992
|
-
const ENVIRONMENT_IS_NODE =
|
|
993
|
-
typeof process === 'object' && process !== null &&
|
|
994
|
-
typeof process.versions === 'object' && process.versions !== null &&
|
|
995
|
-
typeof process.versions.node === 'string'
|
|
996
|
-
|
|
997
|
-
if (ENVIRONMENT_IS_NODE) {
|
|
998
|
-
const nodeWorkerThreads = require('worker_threads')
|
|
999
|
-
|
|
1000
|
-
const parentPort = nodeWorkerThreads.parentPort
|
|
1001
|
-
|
|
1002
|
-
parentPort.on('message', (data) => {
|
|
1003
|
-
globalThis.onmessage({ data })
|
|
1004
|
-
})
|
|
1005
|
-
|
|
1006
|
-
fs = require('fs')
|
|
1007
|
-
|
|
1008
|
-
Object.assign(globalThis, {
|
|
1009
|
-
self: globalThis,
|
|
1010
|
-
require,
|
|
1011
|
-
Worker: nodeWorkerThreads.Worker,
|
|
1012
|
-
importScripts: function (f) {
|
|
1013
|
-
(0, eval)(fs.readFileSync(f, 'utf8') + '//# sourceURL=' + f)
|
|
1014
|
-
},
|
|
1015
|
-
postMessage: function (msg) {
|
|
1016
|
-
parentPort.postMessage(msg)
|
|
1017
|
-
}
|
|
1018
|
-
})
|
|
1019
|
-
|
|
1020
|
-
WASI = require('wasi').WASI
|
|
1021
|
-
emnapiCore = require('@emnapi/core')
|
|
1022
|
-
} else {
|
|
1023
|
-
importScripts('./node_modules/memfs-browser/dist/memfs.js')
|
|
1024
|
-
importScripts('./node_modules/@tybys/wasm-util/dist/wasm-util.min.js')
|
|
1025
|
-
importScripts('./node_modules/@emnapi/core/dist/emnapi-core.js')
|
|
1026
|
-
emnapiCore = globalThis.emnapiCore
|
|
1027
|
-
|
|
1028
|
-
const { Volume, createFsFromVolume } = memfs
|
|
1029
|
-
fs = createFsFromVolume(Volume.fromJSON({
|
|
1030
|
-
'/': null
|
|
1031
|
-
}))
|
|
1032
|
-
|
|
1033
|
-
WASI = globalThis.wasmUtil.WASI
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
const { instantiateNapiModuleSync, MessageHandler } = emnapiCore
|
|
1037
|
-
|
|
1038
|
-
const handler = new MessageHandler({
|
|
1039
|
-
onLoad ({ wasmModule, wasmMemory }) {
|
|
1040
|
-
const wasi = new WASI({ fs })
|
|
1041
|
-
|
|
1042
|
-
return instantiateNapiModuleSync(wasmModule, {
|
|
1043
|
-
childThread: true,
|
|
1044
|
-
wasi,
|
|
1045
|
-
overwriteImports (importObject) {
|
|
1046
|
-
importObject.env.memory = wasmMemory
|
|
1047
|
-
}
|
|
1048
|
-
})
|
|
1049
|
-
}
|
|
1050
|
-
})
|
|
1051
|
-
|
|
1052
|
-
globalThis.onmessage = function (e) {
|
|
1053
|
-
handler.handle(e)
|
|
1054
|
-
// handle other messages
|
|
1055
|
-
}
|
|
1056
|
-
})()
|
|
1057
|
-
```
|
|
1058
|
-
|
|
1059
|
-
## Preprocess Macro Options
|
|
1060
|
-
|
|
1061
|
-
### `-DEMNAPI_WORKER_POOL_SIZE=4`
|
|
1062
|
-
|
|
1063
|
-
This is [`UV_THREADPOOL_SIZE`](http://docs.libuv.org/en/v1.x/threadpool.html?highlight=UV_THREADPOOL_SIZE) equivalent at compile time, if not predefined, emnapi will read `asyncWorkPoolSize` option or `UV_THREADPOOL_SIZE` from Emscripten [environment variable](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#interacting-with-code-environment-variables) at runtime:
|
|
1064
|
-
|
|
1065
|
-
```js
|
|
1066
|
-
Module.init({
|
|
1067
|
-
// ...
|
|
1068
|
-
asyncWorkPoolSize: 2
|
|
1069
|
-
})
|
|
505
|
+
Note: For browsers, all the multithreaded features relying on Web Workers (Emscripten pthread also relying on Web Workers)
|
|
506
|
+
require cross-origin isolation to enable `SharedArrayBuffer`. You can make a page cross-origin isolated
|
|
507
|
+
by serving the page with these headers:
|
|
1070
508
|
|
|
1071
|
-
// if asyncWorkPoolSize is not specified
|
|
1072
|
-
Module.preRun = Module.preRun || [];
|
|
1073
|
-
Module.preRun.push(function () {
|
|
1074
|
-
if (typeof ENV !== 'undefined') {
|
|
1075
|
-
ENV.UV_THREADPOOL_SIZE = '2';
|
|
1076
|
-
}
|
|
1077
|
-
});
|
|
1078
509
|
```
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
// wasi
|
|
1082
|
-
instantiateNapiModule({
|
|
1083
|
-
// ...
|
|
1084
|
-
asyncWorkPoolSize: 2
|
|
1085
|
-
})
|
|
1086
|
-
// if asyncWorkPoolSize is not specified
|
|
1087
|
-
new WASI({
|
|
1088
|
-
env: {
|
|
1089
|
-
UV_THREADPOOL_SIZE: '2'
|
|
1090
|
-
}
|
|
1091
|
-
})
|
|
510
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
511
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
1092
512
|
```
|
|
1093
|
-
|
|
1094
|
-
It represent max of `EMNAPI_WORKER_POOL_SIZE` async work (`napi_queue_async_work`) can be executed in parallel. Default is not defined.
|
|
1095
|
-
|
|
1096
|
-
You can set both `PTHREAD_POOL_SIZE` and `EMNAPI_WORKER_POOL_SIZE` to `number of CPU cores` in general.
|
|
1097
|
-
If you use another library function which may create `N` child threads in async work,
|
|
1098
|
-
then you need to set `PTHREAD_POOL_SIZE` to `EMNAPI_WORKER_POOL_SIZE * (N + 1)`.
|
|
1099
|
-
|
|
1100
|
-
This option only has effect if you use `-pthread`.
|
|
1101
|
-
Emnapi will create `EMNAPI_WORKER_POOL_SIZE` threads when initializing,
|
|
1102
|
-
it will throw error if `PTHREAD_POOL_SIZE < EMNAPI_WORKER_POOL_SIZE && PTHREAD_POOL_SIZE_STRICT == 2`.
|
|
1103
|
-
|
|
1104
|
-
See [Issue #8](https://github.com/toyobayashi/emnapi/issues/8) for more detail.
|
|
1105
|
-
|
|
1106
|
-
### `-DEMNAPI_NEXTTICK_TYPE=0`
|
|
1107
|
-
|
|
1108
|
-
This option only has effect if you use `-pthread`, Default is `0`.
|
|
1109
|
-
Tell emnapi how to delay async work in `uv_async_send` / `uv__async_close`.
|
|
1110
|
-
|
|
1111
|
-
- `0`: Use `setImmediate()` (Node.js native `setImmediate` or browser `MessageChannel` and `port.postMessage`)
|
|
1112
|
-
- `1`: Use `Promise.resolve().then()`
|
|
1113
|
-
|
|
1114
|
-
### `-DEMNAPI_USE_PROXYING=1`
|
|
1115
|
-
|
|
1116
|
-
This option only has effect if you use emscripten `-pthread`. Default is `1` if emscripten version `>= 3.1.9`, else `0`.
|
|
1117
|
-
|
|
1118
|
-
- `0`
|
|
1119
|
-
|
|
1120
|
-
Use JavaScript implementation to send async work from worker threads, runtime code will access the Emscripten internal `PThread` object to add custom worker message listener.
|
|
1121
|
-
|
|
1122
|
-
- `1`:
|
|
1123
|
-
|
|
1124
|
-
Use Emscripten [proxying API](https://emscripten.org/docs/api_reference/proxying.h.html) to send async work from worker threads in C. If you experience something wrong, you can switch set this to `0` and feel free to create an issue.
|