From d97f07375becdd25735d5228bf6ed08ff8e3877c Mon Sep 17 00:00:00 2001 From: Toyo Li Date: Fri, 29 Dec 2023 16:09:34 +0800 Subject: [PATCH] refactor: improve TypeScript custom transformers (#97) --- .eslintignore | 1 + .github/workflows/main.yml | 6 + CONTRIBUTING.md | 64 +++- package.json | 2 +- packages/emnapi/README.md | 2 +- packages/emnapi/script/build.js | 43 +-- packages/emnapi/src/async-work.ts | 5 +- packages/emnapi/src/core/async-work.ts | 37 +- packages/emnapi/src/core/async.ts | 9 +- packages/emnapi/src/core/init.ts | 40 +-- packages/emnapi/src/core/memory.ts | 7 +- packages/emnapi/src/core/scope.d.ts | 15 + packages/emnapi/src/core/tsconfig.json | 3 +- packages/emnapi/src/emnapi.ts | 43 +-- packages/emnapi/src/emscripten/async-work.ts | 3 +- packages/emnapi/src/emscripten/async.ts | 5 +- packages/emnapi/src/emscripten/emnapi.ts | 13 +- packages/emnapi/src/emscripten/init.ts | 9 +- packages/emnapi/src/emscripten/memory.ts | 11 +- packages/emnapi/src/emscripten/runtime.d.ts | 2 +- packages/emnapi/src/env.ts | 11 +- packages/emnapi/src/error.ts | 67 ++-- packages/emnapi/src/function.ts | 55 +-- packages/emnapi/src/internal.ts | 13 +- packages/emnapi/src/life.ts | 39 +- packages/emnapi/src/memory.ts | 9 +- packages/emnapi/src/node.ts | 49 +-- packages/emnapi/src/promise.ts | 13 +- packages/emnapi/src/property.ts | 71 ++-- packages/emnapi/src/script.ts | 5 +- packages/emnapi/src/string.ts | 23 +- packages/emnapi/src/threadsafe-function.ts | 145 ++++---- packages/emnapi/src/typings/compiler.d.ts | 23 -- packages/emnapi/src/typings/parse-tools.d.ts | 26 ++ packages/emnapi/src/util.ts | 27 +- packages/emnapi/src/value-operation.ts | 65 ++-- packages/emnapi/src/value/convert2c.ts | 175 ++++----- packages/emnapi/src/value/convert2napi.ts | 47 +-- packages/emnapi/src/value/create.ts | 95 ++--- packages/emnapi/src/value/global.ts | 17 +- packages/emnapi/src/version.ts | 3 +- packages/emnapi/src/wrap.ts | 51 +-- .../README.md | 11 +- .../src/index.mts | 51 ++- .../src/index.ts | 51 ++- .../README.md | 133 ++++++- .../src/index.ts | 333 ++++++++++++------ .../test/expected/alias.js | 2 +- .../test/expected/bundle.js | 2 +- .../test/expected/directives.js | 12 + .../test/expected/exported-function-ref.js | 2 +- .../test/expected/exported-function.js | 2 +- .../test/expected/exported-var.js | 2 +- .../test/expected/external-variable.js | 2 +- .../test/expected/internal.js | 2 +- .../test/expected/object-literal.js | 2 +- .../test/expected/virtual-modules.js | 9 + .../test/index.js | 10 +- .../test/input/directives.js | 9 + .../test/input/virtual-modules.js | 9 + .../src/index.ts | 102 ++++-- 61 files changed, 1295 insertions(+), 800 deletions(-) create mode 100644 packages/emnapi/src/core/scope.d.ts delete mode 100644 packages/emnapi/src/typings/compiler.d.ts create mode 100644 packages/emnapi/src/typings/parse-tools.d.ts create mode 100644 packages/ts-transform-emscripten-esm-library/test/expected/directives.js create mode 100644 packages/ts-transform-emscripten-esm-library/test/expected/virtual-modules.js create mode 100644 packages/ts-transform-emscripten-esm-library/test/input/directives.js create mode 100644 packages/ts-transform-emscripten-esm-library/test/input/virtual-modules.js diff --git a/.eslintignore b/.eslintignore index cca3b43e..c6be225d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,5 +10,6 @@ node_modules /packages/emnapi/lib /packages/core/src/index.js /packages/**/test/**/input +/packages/**/test/**/actual /packages/**/test/**/expected /out diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 088ac6a1..d24abf60 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,12 +2,18 @@ name: Build on: push: + paths-ignore: + - '**/*.md' + - '**/docs/**' branches: - main - test-* tags: - v* pull_request: + paths-ignore: + - '**/*.md' + - '**/docs/**' workflow_dispatch: env: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23ec87fb..663569c7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,16 +8,52 @@ This doc will explain the structure of this project and some points need to note - `packages/emnapi` (`devDependencies`) - The main package of emnapi, including emnapi C headers, CMake configurations and - the [Emscripten JavaScript library](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#implement-a-c-api-in-javascript) build. + The main package of emnapi, including all Node-API implementation, emnapi C headers, CMake configurations, and + the [Emscripten JavaScript library](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#implement-a-c-api-in-javascript) build. We finally need to build a Emscripten JavaScript library file + which is designed for being used in link time. The library file is not a regular JavaScript file, + it may contain some macros wrapped in `{{{ }}}` only available in Emscripten, and can not use ES Module format. + Fortunately, we can write ESM code during development and then build the final library file through + TypeScript custom transformers those are also in this repo. - The TypeScript `module` compiler option for this package is set to `none`, because we finally need to build - a Emscripten JavaScript library file which is designed for being used in link time. The library file is not - a regular JavaScript file, it may contain some macros wrapped in `{{{ }}}` only available in Emscripten, - and all function body string will be inlined to runtime code, so the module system and closures are not - available. + For example, write the following source code: - For example, the `$makeSetValue(...)` in TypeScript source code will be transformed to `{{{ makeSetValue(...) }}}` + ```ts + import { makeSetValue } from 'emscripten:parse-tools' + + /** @__sig vp */ + export function foo (result: number) { + makeSetValue('result', 0, 0, 'i32') + } + ``` + + This will output: + + ```js + function _foo (result) { + {{{ makeSetValue('result', 0, 0, 'i32') }}} + } + addToLibrary({ + foo: _foo, + foo__sig: 'vp' + }) + ``` + + It is powered by `packages/rollup-plugin-emscripten-esm-library` and `packages/ts-transform-emscripten-esm-library`. + + In addition, macros are also heavily used in `packages/emnapi` to match the Node.js source as much as possible. + + ```ts + import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC } from '...' + + /** @__sig ipip */ + export function napi_create_int32 (env: napi_env, value: int32_t, result: Pointer): napi_status { + const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) + $CHECK_ARG!(envObject, result) + // ... + } + ``` + + Macros are powered by `packages/ts-transform-macro` - `packages/core` (`dependencies`) @@ -47,9 +83,11 @@ This doc will explain the structure of this project and some points need to note - [CMake](/~https://github.com/Kitware/CMake) `>= 3.13` - [ninja-build](/~https://github.com/ninja-build/ninja) -## Macro - -Macro is heavily used in `packages/emnapi`, there are two kinds of macro. +## Debugging -- `$macroName(...)`: transformed to `{{{ macroName(...) }}}` -- `$CUSTOM_MACRO!(...)`: powered by `packages/ts-transform-macro` +- Run `npm run build` to build packages +- Open `packages/test/**/*.js` +- Add break points +- Launch `Launch Test` VSCode launch configuration +- Input `UV_THREADPOOL_SIZE` environment variable +- Select target tripple diff --git a/package.json b/package.json index 595fd58d..c1e60657 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ }, "workspaces": [ "packages/ts-transform-macro", - "packages/ts-transform-emscripten-parse-tools", "packages/ts-transform-emscripten-esm-library", + "packages/ts-transform-emscripten-parse-tools", "packages/rollup-plugin-emscripten-esm-library", "packages/runtime", "packages/node", diff --git a/packages/emnapi/README.md b/packages/emnapi/README.md index 5bb51621..7e0a0ab0 100644 --- a/packages/emnapi/README.md +++ b/packages/emnapi/README.md @@ -109,7 +109,7 @@ Create `hello.c`. ```c #include -#define NODE_API_CALL(env, the_call) \ +#define NODE_API_CALL(env, the_call) \ do { \ if ((the_call) != napi_ok) { \ const napi_extended_error_info *error_info; \ diff --git a/packages/emnapi/script/build.js b/packages/emnapi/script/build.js index daa2bd07..593dd6ec 100644 --- a/packages/emnapi/script/build.js +++ b/packages/emnapi/script/build.js @@ -18,6 +18,10 @@ async function build () { const libOut = path.join(path.dirname(libTsconfigPath), './dist/library_napi.js') const runtimeRequire = createRequire(path.join(__dirname, '../../runtime/index.js')) + const runtimeModuleSpecifier = 'emscripten:runtime' + const parseToolsModuleSpecifier = 'emscripten:parse-tools' + const sharedModuleSpecifier = 'emnapi:shared' + const emnapiRollupBuild = await rollup({ input: path.join(__dirname, '../src/emscripten/index.ts'), treeshake: false, @@ -37,45 +41,20 @@ async function build () { { type: 'program', factory: require('@emnapi/ts-transform-macro').createTransformerFactory - }, - { - type: 'program', - factory: () => { - return (context) => { - return (src) => { - if (src.isDeclarationFile) return src - const statements = src.statements - const newStatements = statements.filter(s => { - return !(ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && (s.moduleSpecifier.text === 'emnapi:emscripten-runtime')) - }) - return context.factory.updateSourceFile(src, newStatements) - } - } - } } ] } }), rollupAlias({ entries: [ - { find: 'emnapi:shared', replacement: path.join(__dirname, '../src/emscripten/init.ts') } + { find: sharedModuleSpecifier, replacement: path.join(__dirname, '../src/emscripten/init.ts') } ] }), require('@emnapi/rollup-plugin-emscripten-esm-library').default({ defaultLibraryFuncsToInclude: ['$emnapiInit'], exportedRuntimeMethods: ['emnapiInit'], - processDirective: true, - modifyOutput (output) { - return output - .replace(/\$POINTER_SIZE/g, '{{{ POINTER_SIZE }}}') - .replace(/\$(from64\(.*?\))/g, '{{{ $1 }}}') - .replace(/\$(to64\(.*?\))/g, '{{{ $1 }}}') - .replace(/\$(makeGetValue\(.*?\))/g, '{{{ $1 }}}') - .replace(/\$(makeSetValue\(.*?\))/g, '{{{ $1 }}}') - .replace(/\$(makeDynCall\(.*?\))/g, '{{{ $1 }}}') - // .replace(/\$(makeMalloc\(.*?\))/g, '{{{ $1 }}}') - .replace(/\$(getUnsharedTextDecoderView\(.*?\))/g, '{{{ $1 }}}') - } + runtimeModuleSpecifier, + parseToolsModuleSpecifier }) ] }) @@ -119,7 +98,9 @@ async function build () { return require('@emnapi/ts-transform-emscripten-parse-tools').createTransformerFactory(program, { defines: { MEMORY64: 0 - } + }, + runtimeModuleSpecifier, + parseToolsModuleSpecifier }) } } @@ -128,8 +109,8 @@ async function build () { }), rollupAlias({ entries: [ - { find: 'emnapi:shared', replacement: path.join(__dirname, '../src/core/init.ts') }, - { find: 'emnapi:emscripten-runtime', replacement: path.join(__dirname, '../src/core/init.ts') } + { find: sharedModuleSpecifier, replacement: path.join(__dirname, '../src/core/init.ts') }, + { find: runtimeModuleSpecifier, replacement: path.join(__dirname, '../src/core/init.ts') } ] }) ] diff --git a/packages/emnapi/src/async-work.ts b/packages/emnapi/src/async-work.ts index 0e8d7be5..4e813166 100644 --- a/packages/emnapi/src/async-work.ts +++ b/packages/emnapi/src/async-work.ts @@ -1,4 +1,5 @@ import { emnapiAsyncWorkPoolSize, emnapiNodeBinding, emnapiCtx } from 'emnapi:shared' +import { makeDynCall } from 'emscripten:parse-tools' export interface AsyncWork { id: number @@ -95,7 +96,7 @@ export var emnapiAWST = { const scope = emnapiCtx.openScope(envObject) try { (envObject as NodeEnv).callbackIntoModule(true, () => { - $makeDynCall('vpip', 'complete')(env, status, data) + makeDynCall('vpip', 'complete')(env, status, data) }) } finally { emnapiCtx.closeScope(envObject, scope) @@ -128,7 +129,7 @@ export var emnapiAWST = { const execute = work.execute work.status = 2 emnapiCtx.feature.setImmediate(() => { - $makeDynCall('vpp', 'execute')(env, data) + makeDynCall('vpp', 'execute')(env, data) emnapiAWST.queued.delete(id) work.status = 3 diff --git a/packages/emnapi/src/core/async-work.ts b/packages/emnapi/src/core/async-work.ts index 457e8951..c3ee2929 100644 --- a/packages/emnapi/src/core/async-work.ts +++ b/packages/emnapi/src/core/async-work.ts @@ -1,5 +1,6 @@ import { emnapiCtx, onCreateWorker, napiModule, emnapiNodeBinding, singleThreadAsyncWork, _emnapi_async_work_pool_size } from 'emnapi:shared' -import { PThread, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, wasmInstance, _free, wasmMemory, _malloc } from 'emnapi:emscripten-runtime' +import { PThread, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, wasmInstance, _free, wasmMemory, _malloc } from 'emscripten:runtime' +import { POINTER_SIZE, to64, makeDynCall, makeSetValue, from64 } from 'emscripten:parse-tools' import { emnapiAWST } from '../async-work' import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG, $CHECK_ENV } from '../macro' import { _emnapi_node_emit_async_init, _emnapi_node_emit_async_destroy } from '../node' @@ -16,10 +17,10 @@ var emnapiAWMT = { /* double */ async_id: 8, /* double */ trigger_async_id: 16, /* napi_env */ env: 24, - /* void* */ data: 1 * $POINTER_SIZE + 24, - /* napi_async_execute_callback */ execute: 2 * $POINTER_SIZE + 24, - /* napi_async_complete_callback */ complete: 3 * $POINTER_SIZE + 24, - end: 4 * $POINTER_SIZE + 24 + /* void* */ data: 1 * POINTER_SIZE + 24, + /* napi_async_execute_callback */ execute: 2 * POINTER_SIZE + 24, + /* napi_async_complete_callback */ complete: 3 * POINTER_SIZE + 24, + end: 4 * POINTER_SIZE + 24 }, init () { emnapiAWMT.unusedWorkers = [] @@ -107,7 +108,7 @@ var emnapiAWMT = { for (let i = 0; i < n; ++i) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const arg = args[i] - _free($to64('arg')) + _free(to64('arg')) } throw err } @@ -209,7 +210,7 @@ var emnapiAWMT = { const callback = (): void => { if (!complete) return (envObject as NodeEnv).callbackIntoModule(true, () => { - $makeDynCall('vpip', 'complete')(env, status, data) + makeDynCall('vpip', 'complete')(env, status, data) }) } @@ -254,7 +255,7 @@ export var napi_create_async_work = singleThreadAsyncWork // eslint-disable-next-line @typescript-eslint/no-unused-vars const id = emnapiAWST.create(env, resourceObject, resourceName, execute, complete, data) - $makeSetValue('result', 0, 'id', '*') + makeSetValue('result', 0, 'id', '*') return envObject.clearLastError() } : function (env: napi_env, resource: napi_value, resource_name: napi_value, execute: number, complete: number, data: number, result: number): napi_status { @@ -272,21 +273,21 @@ export var napi_create_async_work = singleThreadAsyncWork $CHECK_ARG!(envObject, resource_name) const sizeofAW = emnapiAWMT.offset.end - const aw = _malloc($to64('sizeofAW')) + const aw = _malloc(to64('sizeofAW')) if (!aw) return envObject.setLastError(napi_status.napi_generic_failure) new Uint8Array(wasmMemory.buffer).subarray(aw, aw + sizeofAW).fill(0) const s = envObject.ensureHandleId(resourceObject) const resourceRef = emnapiCtx.createReference(envObject, s, 1, Ownership.kUserland as any) // eslint-disable-next-line @typescript-eslint/no-unused-vars const resource_ = resourceRef.id - $makeSetValue('aw', 0, 'resource_', '*') + makeSetValue('aw', 0, 'resource_', '*') _emnapi_node_emit_async_init(s, resource_name, -1, aw + emnapiAWMT.offset.async_id) - $makeSetValue('aw', 'emnapiAWMT.offset.env', 'env', '*') - $makeSetValue('aw', 'emnapiAWMT.offset.execute', 'execute', '*') - $makeSetValue('aw', 'emnapiAWMT.offset.complete', 'complete', '*') - $makeSetValue('aw', 'emnapiAWMT.offset.data', 'data', '*') - $from64('result') - $makeSetValue('result', 0, 'aw', '*') + makeSetValue('aw', 'emnapiAWMT.offset.env', 'env', '*') + makeSetValue('aw', 'emnapiAWMT.offset.execute', 'execute', '*') + makeSetValue('aw', 'emnapiAWMT.offset.complete', 'complete', '*') + makeSetValue('aw', 'emnapiAWMT.offset.data', 'data', '*') + from64('result') + makeSetValue('result', 0, 'aw', '*') return envObject.clearLastError() } @@ -313,7 +314,7 @@ export var napi_delete_async_work = singleThreadAsyncWork _emnapi_node_emit_async_destroy(asyncId, triggerAsyncId) } - _free($to64('work') as number) + _free(to64('work') as number) return envObject.clearLastError() } @@ -373,7 +374,7 @@ function executeAsyncWork (work: number): void { const execute = emnapiAWMT.getExecute(work) const env = emnapiAWMT.getEnv(work) const data = emnapiAWMT.getData(work) - $makeDynCall('vpp', 'execute')(env, data) + makeDynCall('vpp', 'execute')(env, data) const postMessage = napiModule.postMessage! postMessage({ __emnapi__: { diff --git a/packages/emnapi/src/core/async.ts b/packages/emnapi/src/core/async.ts index 13d06d99..7c5572b7 100644 --- a/packages/emnapi/src/core/async.ts +++ b/packages/emnapi/src/core/async.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { napiModule } from 'emnapi:shared' -import { ENVIRONMENT_IS_NODE, wasmMemory, ENVIRONMENT_IS_PTHREAD, PThread } from 'emnapi:emscripten-runtime' +import { ENVIRONMENT_IS_NODE, wasmMemory, ENVIRONMENT_IS_PTHREAD, PThread } from 'emscripten:runtime' +import { POINTER_SIZE, makeDynCall, makeGetValue, to64 } from 'emscripten:parse-tools' import { _emnapi_set_immediate, _emnapi_next_tick } from '../util' function emnapiGetWorkerByPthreadPtr (pthreadPtr: number): any { @@ -76,10 +77,10 @@ export function _emnapi_is_main_browser_thread (): number { /** @__sig vppi */ export function _emnapi_after_uvthreadpool_ready (callback: number, q: number, type: number): void { if (uvThreadpoolReady.ready) { - $makeDynCall('vpi', 'callback')($to64('q'), type) + makeDynCall('vpi', 'callback')(to64('q'), type) } else { uvThreadpoolReady.then(() => { - $makeDynCall('vpi', 'callback')($to64('q'), type) + makeDynCall('vpi', 'callback')(to64('q'), type) }) } } @@ -88,7 +89,7 @@ export function _emnapi_after_uvthreadpool_ready (callback: number, q: number, t export function _emnapi_tell_js_uvthreadpool (threads: number, size: number): void { const p = [] as Array> for (let i = 0; i < size; i++) { - const pthreadPtr = $makeGetValue('threads', 'i * ' + POINTER_SIZE, '*') + const pthreadPtr = makeGetValue('threads', 'i * ' + POINTER_SIZE, '*') const worker = emnapiGetWorkerByPthreadPtr(pthreadPtr) p.push(new Promise((resolve) => { const handler = function (e: any): void { diff --git a/packages/emnapi/src/core/init.ts b/packages/emnapi/src/core/init.ts index f43a76b0..3989c1f7 100644 --- a/packages/emnapi/src/core/init.ts +++ b/packages/emnapi/src/core/init.ts @@ -1,20 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ -import { emnapiTSFN } from '../threadsafe-function' - -export interface CreateOptions { - context: Context - filename?: string - nodeBinding?: NodeBinding - childThread?: boolean - reuseWorker?: boolean - asyncWorkPoolSize?: number - onCreateWorker?: () => any - print?: (str: string) => void - printErr?: (str: string) => void - postMessage?: (msg: any) => any -} +import { makeDynCall, to64 } from 'emscripten:parse-tools' export interface InitOptions { instance: WebAssembly.Instance @@ -23,9 +10,6 @@ export interface InitOptions { table?: WebAssembly.Table } -// factory parameter -declare const options: CreateOptions - export interface INapiModule { imports: { env: any @@ -122,8 +106,8 @@ export var napiModule: INapiModule = { const envObject = napiModule.envObject || (napiModule.envObject = emnapiCtx.createEnv( napiModule.filename, moduleApiVersion, - (cb: Ptr) => $makeDynCall('vppp', 'cb'), - (cb: Ptr) => $makeDynCall('vp', 'cb'), + (cb: Ptr) => makeDynCall('vppp', 'cb'), + (cb: Ptr) => makeDynCall('vp', 'cb'), abort, emnapiNodeBinding )) @@ -134,7 +118,7 @@ export var napiModule: INapiModule = { const exports = napiModule.exports const exportsHandle = scope.add(exports) const napi_register_wasm_v1 = instance.exports.napi_register_wasm_v1 as Function - const napiValue = napi_register_wasm_v1($to64('_envObject.id'), $to64('exportsHandle.id')) + const napiValue = napi_register_wasm_v1(to64('_envObject.id'), to64('exportsHandle.id')) napiModule.exports = (!napiValue) ? exports : emnapiCtx.handleStore.get(napiValue)!.value }) } finally { @@ -234,7 +218,7 @@ function emnapiAddSendListener (worker: any): boolean { } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars const callback = __emnapi__.payload.callback - $makeDynCall('vp', 'callback')(__emnapi__.payload.data) + makeDynCall('vp', 'callback')(__emnapi__.payload.data) } } } @@ -270,7 +254,7 @@ function terminateWorker (worker: any): void { function spawnThread (startArg: number, errorOrTid: number): number { const isNewABI = errorOrTid !== undefined if (!isNewABI) { - errorOrTid = _malloc($to64('8')) + errorOrTid = _malloc(to64('8')) if (!errorOrTid) { return -48 /* ENOMEM */ } @@ -296,7 +280,7 @@ function spawnThread (startArg: number, errorOrTid: number): number { if (isNewABI) { return isError } - _free($to64('errorOrTid')) + _free(to64('errorOrTid')) return isError ? -result : result } @@ -317,7 +301,7 @@ function spawnThread (startArg: number, errorOrTid: number): number { if (isNewABI) { return 1 } - _free($to64('errorOrTid')) + _free(to64('errorOrTid')) return -EAGAIN } @@ -349,7 +333,7 @@ function spawnThread (startArg: number, errorOrTid: number): number { if (isNewABI) { return 0 } - _free($to64('errorOrTid')) + _free(to64('errorOrTid')) return tid } @@ -444,9 +428,9 @@ export var PThread = { } // napiModule.emnapi.addSendListener(worker) emnapiAddSendListener(worker) - if (typeof emnapiTSFN !== 'undefined') { - emnapiTSFN.addListener(worker) - } + // if (typeof emnapiTSFN !== 'undefined') { + // emnapiTSFN.addListener(worker) + // } try { worker.postMessage({ __emnapi__: { diff --git a/packages/emnapi/src/core/memory.ts b/packages/emnapi/src/core/memory.ts index 20a9a256..a6323c45 100644 --- a/packages/emnapi/src/core/memory.ts +++ b/packages/emnapi/src/core/memory.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/indent */ import { emnapiCtx } from 'emnapi:shared' -import { wasmMemory } from 'emnapi:emscripten-runtime' +import { wasmMemory } from 'emscripten:runtime' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ENV, $CHECK_ARG } from '../macro' import { $emnapiSetValueI64 as emnapiSetValueI64 } from '../util' @@ -28,9 +29,9 @@ export function napi_adjust_external_memory ( return envObject.setLastError(napi_status.napi_generic_failure) } - $from64('adjusted_value') + from64('adjusted_value') if (emnapiCtx.feature.supportBigInt) { - $makeSetValue('adjusted_value', 0, 'wasmMemory.buffer.byteLength', 'i64') + makeSetValue('adjusted_value', 0, 'wasmMemory.buffer.byteLength', 'i64') } else { emnapiSetValueI64(adjusted_value, wasmMemory.buffer.byteLength) } diff --git a/packages/emnapi/src/core/scope.d.ts b/packages/emnapi/src/core/scope.d.ts new file mode 100644 index 00000000..d823e976 --- /dev/null +++ b/packages/emnapi/src/core/scope.d.ts @@ -0,0 +1,15 @@ +declare interface CreateOptions { + context: Context + filename?: string + nodeBinding?: NodeBinding + childThread?: boolean + reuseWorker?: boolean + asyncWorkPoolSize?: number + onCreateWorker?: () => any + print?: (str: string) => void + printErr?: (str: string) => void + postMessage?: (msg: any) => any +} + +// factory parameter +declare const options: CreateOptions diff --git a/packages/emnapi/src/core/tsconfig.json b/packages/emnapi/src/core/tsconfig.json index b62d82d6..47726e28 100644 --- a/packages/emnapi/src/core/tsconfig.json +++ b/packages/emnapi/src/core/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "paths": { "emnapi:shared": ["./init"], - "emnapi:emscripten-runtime": ["./init"] + "emscripten:runtime": ["./init"] } }, "include": [ @@ -14,6 +14,7 @@ "./miscellaneous.ts", "./string.ts", "./memory.ts", + "./scope.d.ts", "../../../runtime/src/typings/**/*.d.ts", "../typings/**/*.d.ts", "../*.ts", diff --git a/packages/emnapi/src/emnapi.ts b/packages/emnapi/src/emnapi.ts index 00288b1f..e59986a8 100644 --- a/packages/emnapi/src/emnapi.ts +++ b/packages/emnapi/src/emnapi.ts @@ -1,5 +1,6 @@ import { emnapiCtx, emnapiNodeBinding } from 'emnapi:shared' -import { wasmMemory } from 'emnapi:emscripten-runtime' +import { wasmMemory } from 'emscripten:runtime' +import { from64, makeSetValue, makeGetValue } from 'emscripten:parse-tools' import { type MemoryViewDescriptor, type ArrayBufferPointer, emnapiExternalMemory } from './memory' import { napi_add_finalizer } from './wrap' import { $CHECK_ARG, $PREAMBLE, $CHECK_ENV } from './macro' @@ -21,9 +22,9 @@ export function emnapi_create_memory_view ( return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, result) - $from64('byte_length') - $from64('external_data') - $from64('result') + from64('byte_length') + from64('external_data') + from64('result') byte_length = byte_length >>> 0 @@ -105,7 +106,7 @@ export function emnapi_create_memory_view ( } } value = handle.id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -194,11 +195,11 @@ export function emnapi_sync_memory (env: napi_env, js_to_wasm: bool, arraybuffer return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, arraybuffer_or_view) - $from64('arraybuffer_or_view') - $from64('offset') - $from64('len') + from64('arraybuffer_or_view') + from64('offset') + from64('len') - const handleId = $makeGetValue('arraybuffer_or_view', 0, '*') + const handleId = makeGetValue('arraybuffer_or_view', 0, '*') const handle: Handle = envObject.ctx.handleStore.get(handleId)! if (!handle.isArrayBuffer() && !handle.isTypedArray() && !handle.isDataView()) { @@ -207,9 +208,9 @@ export function emnapi_sync_memory (env: napi_env, js_to_wasm: bool, arraybuffer const ret = $emnapiSyncMemory(Boolean(js_to_wasm), handle.value, offset, len) if (handle.value !== ret) { - $from64('arraybuffer_or_view') + from64('arraybuffer_or_view') v = envObject.ensureHandleId(ret) - $makeSetValue('arraybuffer_or_view', 0, 'v', '*') + makeSetValue('arraybuffer_or_view', 0, 'v', '*') } return envObject.getReturnStatus() @@ -256,18 +257,18 @@ export function emnapi_get_memory_address (env: napi_env, arraybuffer_or_view: n p = info.address if (address) { - $from64('address') - $makeSetValue('address', 0, 'p', '*') + from64('address') + makeSetValue('address', 0, 'p', '*') } if (ownership) { - $from64('ownership') + from64('ownership') ownershipOut = info.ownership - $makeSetValue('ownership', 0, 'ownershipOut', 'i32') + makeSetValue('ownership', 0, 'ownershipOut', 'i32') } if (runtime_allocated) { - $from64('runtime_allocated') + from64('runtime_allocated') runtimeAllocated = info.runtimeAllocated - $makeSetValue('runtime_allocated', 0, 'runtimeAllocated', 'i8') + makeSetValue('runtime_allocated', 0, 'runtimeAllocated', 'i8') } return envObject.getReturnStatus() @@ -293,11 +294,11 @@ export function emnapi_get_runtime_version (env: napi_env, version: number): nap const versions = runtimeVersion.split('.') .map((n: string) => Number(n)) as [number, number, number] - $from64('version') + from64('version') - $makeSetValue('version', 0, 'versions[0]', 'u32') - $makeSetValue('version', 4, 'versions[1]', 'u32') - $makeSetValue('version', 8, 'versions[2]', 'u32') + makeSetValue('version', 0, 'versions[0]', 'u32') + makeSetValue('version', 4, 'versions[1]', 'u32') + makeSetValue('version', 8, 'versions[2]', 'u32') return envObject.clearLastError() } diff --git a/packages/emnapi/src/emscripten/async-work.ts b/packages/emnapi/src/emscripten/async-work.ts index 0a1241d2..600e4725 100644 --- a/packages/emnapi/src/emscripten/async-work.ts +++ b/packages/emnapi/src/emscripten/async-work.ts @@ -1,4 +1,5 @@ import { emnapiCtx } from 'emnapi:shared' +import { makeSetValue } from 'emscripten:parse-tools' import { emnapiAWST } from '../async-work' import { $CHECK_ARG, $CHECK_ENV, $CHECK_ENV_NOT_IN_GC } from '../macro' @@ -23,7 +24,7 @@ export function napi_create_async_work (env: napi_env, resource: napi_value, res // eslint-disable-next-line @typescript-eslint/no-unused-vars const id = emnapiAWST.create(env, resourceObject, resourceName, execute, complete, data) - $makeSetValue('result', 0, 'id', '*') + makeSetValue('result', 0, 'id', '*') return envObject.clearLastError() } diff --git a/packages/emnapi/src/emscripten/async.ts b/packages/emnapi/src/emscripten/async.ts index 60626cfe..54c6f0c0 100644 --- a/packages/emnapi/src/emscripten/async.ts +++ b/packages/emnapi/src/emscripten/async.ts @@ -1,4 +1,5 @@ -import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, PThread } from 'emnapi:emscripten-runtime' +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, PThread } from 'emscripten:runtime' +import { makeDynCall } from 'emscripten:parse-tools' import { _emnapi_set_immediate, _emnapi_next_tick } from '../util' /** @@ -44,7 +45,7 @@ export function $emnapiAddSendListener (worker: any): boolean { } else { // eslint-disable-next-line @typescript-eslint/no-unused-vars const callback = __emnapi__.payload.callback - $makeDynCall('vp', 'callback')(__emnapi__.payload.data) + makeDynCall('vp', 'callback')(__emnapi__.payload.data) } } } diff --git a/packages/emnapi/src/emscripten/emnapi.ts b/packages/emnapi/src/emscripten/emnapi.ts index a937352d..5000c36b 100644 --- a/packages/emnapi/src/emscripten/emnapi.ts +++ b/packages/emnapi/src/emscripten/emnapi.ts @@ -1,8 +1,9 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars import { emnapiCtx } from 'emnapi:shared' +import { Module } from 'emscripten:runtime' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { emnapiString } from '../string' import { $CHECK_ARG, $PREAMBLE } from '../macro' -import { Module } from 'emnapi:emscripten-runtime' /** * @__sig ipp @@ -13,11 +14,11 @@ export function emnapi_get_module_object (env: napi_env, result: Pointer { $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = envObject.ensureHandleId(Module) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -32,12 +33,12 @@ export function emnapi_get_module_property (env: napi_env, utf8name: const_char_ return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, utf8name) $CHECK_ARG!(envObject, result) - $from64('utf8name') - $from64('result') + from64('utf8name') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = envObject.ensureHandleId(Module[emnapiString.UTF8ToString(utf8name, -1)]) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } diff --git a/packages/emnapi/src/emscripten/init.ts b/packages/emnapi/src/emscripten/init.ts index 216ac716..fbef0659 100644 --- a/packages/emnapi/src/emscripten/init.ts +++ b/packages/emnapi/src/emscripten/init.ts @@ -2,7 +2,8 @@ /* eslint-disable no-new-func */ /* eslint-disable @typescript-eslint/no-implied-eval */ -import { abort } from 'emnapi:emscripten-runtime' +import { makeDynCall, to64 } from 'emscripten:parse-tools' +import { abort } from 'emscripten:runtime' // declare const global: typeof globalThis // declare const require: any @@ -81,8 +82,8 @@ export function emnapiInit (options: InitOptions): any { const envObject = emnapiModule.envObject || (emnapiModule.envObject = emnapiCtx.createEnv( filename, moduleApiVersion, - (cb: Ptr) => $makeDynCall('vppp', 'cb'), - (cb: Ptr) => $makeDynCall('vp', 'cb'), + (cb: Ptr) => makeDynCall('vppp', 'cb'), + (cb: Ptr) => makeDynCall('vp', 'cb'), abort, emnapiNodeBinding )) @@ -93,7 +94,7 @@ export function emnapiInit (options: InitOptions): any { const exports = emnapiModule.exports // eslint-disable-next-line @typescript-eslint/no-unused-vars const exportsHandle = scope.add(exports) - const napiValue = _napi_register_wasm_v1($to64('_envObject.id'), $to64('exportsHandle.id')) + const napiValue = _napi_register_wasm_v1(to64('_envObject.id'), to64('exportsHandle.id')) emnapiModule.exports = (!napiValue) ? exports : emnapiCtx.handleStore.get(napiValue)!.value }) } catch (err) { diff --git a/packages/emnapi/src/emscripten/memory.ts b/packages/emnapi/src/emscripten/memory.ts index 5524e78b..a1b0ab0f 100644 --- a/packages/emnapi/src/emscripten/memory.ts +++ b/packages/emnapi/src/emscripten/memory.ts @@ -1,5 +1,6 @@ -import { wasmMemory } from 'emnapi:emscripten-runtime' +import { wasmMemory } from 'emscripten:runtime' import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $emnapiSetValueI64 as emnapiSetValueI64 } from '../util' import { $CHECK_ENV } from '../macro' @@ -42,15 +43,15 @@ export function napi_adjust_external_memory ( } // #if WASM_BIGINT - $from64('high') + from64('high') if (emnapiCtx.feature.supportBigInt) { - $makeSetValue('high', 0, 'wasmMemory.buffer.byteLength', 'i64') + makeSetValue('high', 0, 'wasmMemory.buffer.byteLength', 'i64') } else { emnapiSetValueI64(high, wasmMemory.buffer.byteLength) } // #else - $from64('adjusted_value') - $makeSetValue('adjusted_value', 0, 'wasmMemory.buffer.byteLength', 'i64') + from64('adjusted_value') + makeSetValue('adjusted_value', 0, 'wasmMemory.buffer.byteLength', 'i64') // #endif return envObject.clearLastError() diff --git a/packages/emnapi/src/emscripten/runtime.d.ts b/packages/emnapi/src/emscripten/runtime.d.ts index 2f6418ed..1da22b09 100644 --- a/packages/emnapi/src/emscripten/runtime.d.ts +++ b/packages/emnapi/src/emscripten/runtime.d.ts @@ -5,7 +5,7 @@ declare const LibraryManager: { declare function mergeInto (target: any, source: Record): void -declare module 'emnapi:emscripten-runtime' { +declare module 'emscripten:runtime' { export const wasmMemory: WebAssembly.Memory export const ENVIRONMENT_IS_NODE: boolean export const ENVIRONMENT_IS_PTHREAD: boolean diff --git a/packages/emnapi/src/env.ts b/packages/emnapi/src/env.ts index b8ea2782..948681a4 100644 --- a/packages/emnapi/src/env.ts +++ b/packages/emnapi/src/env.ts @@ -1,13 +1,14 @@ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ENV, $CHECK_ARG } from './macro' /** @__sig ipppp */ export function napi_set_instance_data (env: napi_env, data: void_p, finalize_cb: napi_finalize, finalize_hint: void_p): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! - $from64('data') - $from64('finalize_cb') - $from64('finalize_hint') + from64('data') + from64('finalize_cb') + from64('finalize_hint') envObject.setInstanceData(data, finalize_cb, finalize_hint) return envObject.clearLastError() } @@ -17,10 +18,10 @@ export function napi_get_instance_data (env: napi_env, data: void_pp): napi_stat $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, data) - $from64('data') + from64('data') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = envObject.getInstanceData() - $makeSetValue('data', 0, 'value', '*') + makeSetValue('data', 0, 'value', '*') return envObject.clearLastError() } diff --git a/packages/emnapi/src/error.ts b/packages/emnapi/src/error.ts index 8c932055..2cacd761 100644 --- a/packages/emnapi/src/error.ts +++ b/packages/emnapi/src/error.ts @@ -1,13 +1,14 @@ -import { abort } from 'emnapi:emscripten-runtime' +import { abort } from 'emscripten:runtime' import { emnapiCtx, emnapiNodeBinding } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $PREAMBLE, $CHECK_ARG, $CHECK_ENV_NOT_IN_GC } from './macro' import { emnapiString } from './string' /** @__sig vpppp */ export function _emnapi_get_last_error_info (env: napi_env, error_code: Pointer, engine_error_code: Pointer, engine_reserved: void_pp): void { - $from64('error_code') - $from64('engine_error_code') - $from64('engine_reserved') + from64('error_code') + from64('engine_error_code') + from64('engine_reserved') const envObject = emnapiCtx.envStore.get(env)! const lastError = envObject.lastError @@ -17,11 +18,11 @@ export function _emnapi_get_last_error_info (env: napi_env, error_code: Pointer< const engineErrorCode = lastError.engineErrorCode >>> 0 // eslint-disable-next-line @typescript-eslint/no-unused-vars const engineReserved = lastError.engineReserved - $from64('engineReserved') + from64('engineReserved') - $makeSetValue('error_code', 0, 'errorCode', 'i32') - $makeSetValue('engine_error_code', 0, 'engineErrorCode', 'u32') - $makeSetValue('engine_reserved', 0, 'engineReserved', '*') + makeSetValue('error_code', 0, 'errorCode', 'i32') + makeSetValue('engine_error_code', 0, 'engineErrorCode', 'u32') + makeSetValue('engine_reserved', 0, 'engineReserved', '*') } /** @__sig ipp */ @@ -37,8 +38,8 @@ export function napi_throw (env: napi_env, error: napi_value): napi_status { export function napi_throw_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, msg) - $from64('code') - $from64('msg') + from64('code') + from64('msg') const error: Error & { code?: string } = new Error(emnapiString.UTF8ToString(msg, -1)) if (code) error.code = emnapiString.UTF8ToString(code, -1) @@ -52,8 +53,8 @@ export function napi_throw_error (env: napi_env, code: const_char_p, msg: const_ export function napi_throw_type_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, msg) - $from64('code') - $from64('msg') + from64('code') + from64('msg') const error: TypeError & { code?: string } = new TypeError(emnapiString.UTF8ToString(msg, -1)) if (code) error.code = emnapiString.UTF8ToString(code, -1) @@ -67,8 +68,8 @@ export function napi_throw_type_error (env: napi_env, code: const_char_p, msg: c export function napi_throw_range_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, msg) - $from64('code') - $from64('msg') + from64('code') + from64('msg') const error: RangeError & { code?: string } = new RangeError(emnapiString.UTF8ToString(msg, -1)) if (code) error.code = emnapiString.UTF8ToString(code, -1) @@ -82,8 +83,8 @@ export function napi_throw_range_error (env: napi_env, code: const_char_p, msg: export function node_api_throw_syntax_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, msg) - $from64('code') - $from64('msg') + from64('code') + from64('msg') const error: SyntaxError & { code?: string } = new SyntaxError(emnapiString.UTF8ToString(msg, -1)) if (code) error.code = emnapiString.UTF8ToString(code, -1) @@ -99,8 +100,8 @@ export function napi_is_exception_pending (env: napi_env, result: Pointer) $CHECK_ARG!(envObject, result) // eslint-disable-next-line @typescript-eslint/no-unused-vars const r = envObject.tryCatch.hasCaught() - $from64('result') - $makeSetValue('result', 0, 'r ? 1 : 0', 'i8') + from64('result') + makeSetValue('result', 0, 'r ? 1 : 0', 'i8') return envObject.clearLastError() } @@ -123,11 +124,11 @@ export function napi_create_error (env: napi_env, code: napi_value, msg: napi_va (error as any).code = codeValue } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(error).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -149,11 +150,11 @@ export function napi_create_type_error (env: napi_env, code: napi_value, msg: na (error as any).code = codeValue } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(error).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -174,11 +175,11 @@ export function napi_create_range_error (env: napi_env, code: napi_value, msg: n } (error as any).code = codeValue } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(error).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -199,11 +200,11 @@ export function node_api_create_syntax_error (env: napi_env, code: napi_value, m } (error as any).code = codeValue } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(error).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -211,16 +212,16 @@ export function node_api_create_syntax_error (env: napi_env, code: napi_value, m export function napi_get_and_clear_last_exception (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') if (!envObject.tryCatch.hasCaught()) { - $makeSetValue('result', 0, '1', '*') // ID_UNDEFINED + makeSetValue('result', 0, '1', '*') // ID_UNDEFINED return envObject.clearLastError() } else { const err = envObject.tryCatch.exception()! // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = envObject.ensureHandleId(err) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') envObject.tryCatch.reset() } return envObject.clearLastError() @@ -228,10 +229,10 @@ export function napi_get_and_clear_last_exception (env: napi_env, result: Pointe /** @__sig vpppp */ export function napi_fatal_error (location: const_char_p, location_len: size_t, message: const_char_p, message_len: size_t): void { - $from64('location') - $from64('location_len') - $from64('message') - $from64('message_len') + from64('location') + from64('location_len') + from64('message') + from64('message_len') const locationStr = emnapiString.UTF8ToString(location, location_len) const messageStr = emnapiString.UTF8ToString(message, message_len) diff --git a/packages/emnapi/src/function.ts b/packages/emnapi/src/function.ts index 7657af3a..bad65c4c 100644 --- a/packages/emnapi/src/function.ts +++ b/packages/emnapi/src/function.ts @@ -1,4 +1,5 @@ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue, makeGetValue, SIZE_TYPE, POINTER_SIZE } from 'emscripten:parse-tools' import { emnapiCreateFunction } from './internal' import { $PREAMBLE, $CHECK_ARG, $CHECK_ENV, $CHECK_ENV_NOT_IN_GC } from './macro' @@ -11,16 +12,16 @@ export function napi_create_function (env: napi_env, utf8name: Pointer { $CHECK_ARG!(envObject, recv) - $from64('argc') - $from64('argv') - $from64('result') + from64('argc') + from64('argv') + from64('result') argc = argc >>> 0 if (argc > 0) { @@ -102,13 +103,13 @@ export function napi_call_function ( if (typeof v8func !== 'function') return envObject.setLastError(napi_status.napi_invalid_arg) const args = [] for (; i < argc; i++) { - const argVal = $makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') + const argVal = makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') args.push(emnapiCtx.handleStore.get(argVal)!.value) } const ret = v8func.apply(v8recv, args) if (result) { v = envObject.ensureHandleId(ret) - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') } return envObject.clearLastError() }) @@ -128,9 +129,9 @@ export function napi_new_instance ( return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, constructor) - $from64('argc') - $from64('argv') - $from64('result') + from64('argc') + from64('argv') + from64('result') argc = argc >>> 0 if (argc > 0) { @@ -144,7 +145,7 @@ export function napi_new_instance ( if (emnapiCtx.feature.supportReflect) { const argList = Array(argc) for (i = 0; i < argc; i++) { - const argVal = $makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') + const argVal = makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') argList[i] = emnapiCtx.handleStore.get(argVal)!.value } ret = Reflect.construct(Ctor, argList, Ctor) @@ -152,7 +153,7 @@ export function napi_new_instance ( const args = Array(argc + 1) as [undefined, ...any[]] args[0] = undefined for (i = 0; i < argc; i++) { - const argVal = $makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') + const argVal = makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') args[i + 1] = emnapiCtx.handleStore.get(argVal)!.value } const BoundCtor = Ctor.bind.apply(Ctor, args) as new () => any @@ -160,7 +161,7 @@ export function napi_new_instance ( } if (result) { v = envObject.ensureHandleId(ret) - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') } return envObject.getReturnStatus() }) @@ -176,11 +177,11 @@ export function napi_get_new_target ( $CHECK_ARG!(envObject, cbinfo) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') const cbinfoValue = emnapiCtx.cbinfoStack.get(cbinfo)! // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = cbinfoValue.getNewTarget(envObject) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } diff --git a/packages/emnapi/src/internal.ts b/packages/emnapi/src/internal.ts index 5fcae8af..3e74dc8d 100644 --- a/packages/emnapi/src/internal.ts +++ b/packages/emnapi/src/internal.ts @@ -3,12 +3,13 @@ /* eslint-disable @typescript-eslint/no-implied-eval */ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeDynCall, makeSetValue } from 'emscripten:parse-tools' import { emnapiString } from './string' import { emnapiExternalMemory } from './memory' import { $CHECK_ARG, $PREAMBLE } from './macro' export function emnapiCreateFunction any> (envObject: Env, utf8name: Pointer, length: size_t, cb: napi_callback, data: void_p): { status: napi_status; f: F } { - $from64('utf8name') + from64('utf8name') const functionName = (!utf8name || !length) ? '' : (emnapiString.UTF8ToString(utf8name, length)) @@ -20,7 +21,7 @@ export function emnapiCreateFunction any> (envObje const scope = emnapiCtx.openScope(envObject) try { return envObject.callIntoModule((envObject) => { - const napiValue = $makeDynCall('ppp', 'cb')(envObject.id, cbinfo) + const napiValue = makeDynCall('ppp', 'cb')(envObject.id, cbinfo) return (!napiValue) ? undefined : emnapiCtx.handleStore.get(napiValue)!.value }) } finally { @@ -143,9 +144,9 @@ export function emnapiWrap (env: napi_env, js_object: napi_value, native_object: if (result) { if (!finalize_cb) return envObject.setLastError(napi_status.napi_invalid_arg) reference = emnapiCtx.createReference(envObject, handle.id, 0, Ownership.kUserland as any, finalize_cb, native_object, finalize_hint) - $from64('result') + from64('result') referenceId = reference.id - $makeSetValue('result', 0, 'referenceId', '*') + makeSetValue('result', 0, 'referenceId', '*') } else { reference = emnapiCtx.createReference(envObject, handle.id, 0, Ownership.kRuntime as any, finalize_cb, native_object, !finalize_cb ? finalize_cb : finalize_hint) } @@ -172,11 +173,11 @@ export function emnapiUnwrap (env: napi_env, js_object: napi_value, result: void const ref = emnapiCtx.refStore.get(referenceId) if (!ref) return envObject.setLastError(napi_status.napi_invalid_arg) if (result) { - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars data = ref.data() - $makeSetValue('result', 0, 'data', '*') + makeSetValue('result', 0, 'data', '*') } if (action === UnwrapAction.RemoveWrap) { binding.wrapped = 0 diff --git a/packages/emnapi/src/life.ts b/packages/emnapi/src/life.ts index 4ee41e8f..4916475b 100644 --- a/packages/emnapi/src/life.ts +++ b/packages/emnapi/src/life.ts @@ -1,4 +1,5 @@ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG, $CHECK_ENV } from './macro' /** @__sig ipp */ @@ -7,8 +8,8 @@ export function napi_open_handle_scope (env: napi_env, result: Pointer>> 0, Ownership.kUserland as any) - $from64('result') - $makeSetValue('result', 0, 'ref.id', '*') + from64('result') + makeSetValue('result', 0, 'ref.id', '*') return envObject.clearLastError() } @@ -113,8 +114,8 @@ export function napi_reference_ref ( // eslint-disable-next-line @typescript-eslint/no-unused-vars const count = emnapiCtx.refStore.get(ref)!.ref() if (result) { - $from64('result') - $makeSetValue('result', 0, 'count', 'u32') + from64('result') + makeSetValue('result', 0, 'count', 'u32') } return envObject.clearLastError() } @@ -136,8 +137,8 @@ export function napi_reference_unref ( // eslint-disable-next-line @typescript-eslint/no-unused-vars const count = reference.unref() if (result) { - $from64('result') - $makeSetValue('result', 0, 'count', 'u32') + from64('result') + makeSetValue('result', 0, 'count', 'u32') } return envObject.clearLastError() } @@ -154,8 +155,8 @@ export function napi_get_reference_value ( const reference = emnapiCtx.refStore.get(ref)! // eslint-disable-next-line @typescript-eslint/no-unused-vars const handleId = reference.get() - $from64('result') - $makeSetValue('result', 0, 'handleId', '*') + from64('result') + makeSetValue('result', 0, 'handleId', '*') return envObject.clearLastError() } @@ -165,8 +166,8 @@ export function napi_add_env_cleanup_hook (env: napi_env, fun: number, arg: numb const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, fun) - $from64('fun') - $from64('arg') + from64('fun') + from64('arg') emnapiCtx.addCleanupHook(envObject, fun, arg) @@ -179,8 +180,8 @@ export function napi_remove_env_cleanup_hook (env: napi_env, fun: number, arg: n const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, fun) - $from64('fun') - $from64('arg') + from64('fun') + from64('arg') emnapiCtx.removeCleanupHook(envObject, fun, arg) diff --git a/packages/emnapi/src/memory.ts b/packages/emnapi/src/memory.ts index dbfc4d71..75df0a2f 100644 --- a/packages/emnapi/src/memory.ts +++ b/packages/emnapi/src/memory.ts @@ -1,5 +1,6 @@ -import { _free, wasmMemory, _malloc } from 'emnapi:emscripten-runtime' +import { _free, wasmMemory, _malloc } from 'emscripten:runtime' import { emnapiCtx } from 'emnapi:shared' +import { to64 } from 'emscripten:parse-tools' export type ViewConstuctor = Int8ArrayConstructor | @@ -49,12 +50,12 @@ export const emnapiExternalMemory: { getArrayBufferPointer: (arrayBuffer: ArrayBuffer, shouldCopy: boolean) => ArrayBufferPointer getViewPointer: (view: T, shouldCopy: boolean) => ViewPointer } = { - registry: typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(function (_pointer) { _free($to64('_pointer') as number) }) : undefined, + registry: typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(function (_pointer) { _free(to64('_pointer') as number) }) : undefined, table: new WeakMap(), wasmMemoryViewTable: new WeakMap(), init: function () { - emnapiExternalMemory.registry = typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(function (_pointer) { _free($to64('_pointer') as number) }) : undefined + emnapiExternalMemory.registry = typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(function (_pointer) { _free(to64('_pointer') as number) }) : undefined emnapiExternalMemory.table = new WeakMap() emnapiExternalMemory.wasmMemoryViewTable = new WeakMap() }, @@ -102,7 +103,7 @@ export const emnapiExternalMemory: { return info } - const pointer = _malloc($to64('arrayBuffer.byteLength')) + const pointer = _malloc(to64('arrayBuffer.byteLength')) if (!pointer) throw new Error('Out of memory') new Uint8Array(wasmMemory.buffer).set(new Uint8Array(arrayBuffer), pointer) diff --git a/packages/emnapi/src/node.ts b/packages/emnapi/src/node.ts index 56b9bdcb..31f266a9 100644 --- a/packages/emnapi/src/node.ts +++ b/packages/emnapi/src/node.ts @@ -1,4 +1,5 @@ import { emnapiNodeBinding, emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue, makeGetValue, POINTER_SIZE } from 'emscripten:parse-tools' import { $PREAMBLE, $CHECK_ARG } from './macro' /** @__sig vppdp */ @@ -16,9 +17,9 @@ export function _emnapi_node_emit_async_init ( // eslint-disable-next-line @typescript-eslint/no-unused-vars const asyncId = asyncContext.asyncId; const triggerAsyncId = asyncContext.triggerAsyncId if (result) { - $from64('result') - $makeSetValue('result', 0, 'asyncId', 'double') - $makeSetValue('result', 8, 'triggerAsyncId', 'double') + from64('result') + makeSetValue('result', 0, 'asyncId', 'double') + makeSetValue('result', 8, 'triggerAsyncId', 'double') } } @@ -40,14 +41,14 @@ export function _emnapi_node_emit_async_destroy (async_id: double, trigger_async triggerAsyncId: trigger_async_id }) - $from64('result') + from64('result') $_TODO_makeSetValue('result', 0, 'nativeCallbackScopePointer', 'i64') } vp export function _emnapi_node_close_callback_scope (scope: Pointer): void { if (!emnapiNodeBinding || !scope) return - $from64('scope') + from64('scope') const nativeCallbackScopePointer = $_TODO_makeGetValue('scope', 0, 'i64') emnapiNodeBinding.node.closeCallbackScope(BigInt(nativeCallbackScopePointer)) } */ @@ -61,12 +62,12 @@ export function _emnapi_node_make_callback (env: napi_env, async_resource: napi_ if (!emnapiNodeBinding) return const resource = emnapiCtx.handleStore.get(async_resource)!.value const callback = emnapiCtx.handleStore.get(cb)!.value - $from64('argv') - $from64('size') + from64('argv') + from64('size') size = size >>> 0 const arr = Array(size) for (; i < size; i++) { - const argVal = $makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') + const argVal = makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') arr[i] = emnapiCtx.handleStore.get(argVal)!.value } const ret = emnapiNodeBinding.node.makeCallback(resource, callback, arr, { @@ -74,11 +75,11 @@ export function _emnapi_node_make_callback (env: napi_env, async_resource: napi_ triggerAsyncId: trigger_async_id }) if (result) { - $from64('result') + from64('result') const envObject = emnapiCtx.envStore.get(env)! // eslint-disable-next-line @typescript-eslint/no-unused-vars v = envObject.ensureHandleId(ret) - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') } } @@ -109,9 +110,9 @@ export function _emnapi_async_init_js (async_resource: napi_value, async_resourc const low = Number(numberValue & BigInt(0xffffffff)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const high = Number(numberValue >> BigInt(32)) - $from64('result') - $makeSetValue('result', 0, 'low', 'i32') - $makeSetValue('result', 4, 'high', 'i32') + from64('result') + makeSetValue('result', 0, 'low', 'i32') + makeSetValue('result', 4, 'high', 'i32') return napi_status.napi_ok } @@ -121,9 +122,9 @@ export function _emnapi_async_destroy_js (async_context: Pointer): napi if (!emnapiNodeBinding) { return napi_status.napi_generic_failure } - $from64('async_context') - const low = $makeGetValue('async_context', 0, 'i32') - const high = $makeGetValue('async_context', 4, 'i32') + from64('async_context') + const low = makeGetValue('async_context', 0, 'i32') + const high = makeGetValue('async_context', 4, 'i32') const pointer = BigInt((low as number) >>> 0) | (BigInt(high) << BigInt(32)) @@ -166,17 +167,17 @@ export function napi_make_callback (env: napi_env, async_context: Pointer>> 0) | (BigInt(high) << BigInt(32)) - $from64('argv') - $from64('argc') + from64('argv') + from64('argc') argc = argc >>> 0 const arr = Array(argc) for (; i < argc; i++) { - const argVal = $makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') + const argVal = makeGetValue('argv', 'i * ' + POINTER_SIZE, '*') arr[i] = emnapiCtx.handleStore.get(argVal)!.value } const ret = emnapiNodeBinding.napi.makeCallback(ctx, v8recv, v8func, arr) @@ -187,10 +188,10 @@ export function napi_make_callback (env: napi_env, async_context: Pointer((resolve, reject) => { const deferredObject = emnapiCtx.createDeferred({ resolve, reject }) deferredObjectId = deferredObject.id - $from64('deferred') - $makeSetValue('deferred', 0, 'deferredObjectId', '*') + from64('deferred') + makeSetValue('deferred', 0, 'deferredObjectId', '*') }) - $from64('promise') + from64('promise') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = emnapiCtx.addToCurrentScope(p).id - $makeSetValue('promise', 0, 'value', '*') + makeSetValue('promise', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -53,9 +54,9 @@ export function napi_is_promise (env: napi_env, value: napi_value, is_promise: P $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, is_promise) const h = emnapiCtx.handleStore.get(value)! - $from64('is_promise') + from64('is_promise') // eslint-disable-next-line @typescript-eslint/no-unused-vars const r = h.isPromise() ? 1 : 0 - $makeSetValue('is_promise', 0, 'r', 'i8') + makeSetValue('is_promise', 0, 'r', 'i8') return envObject.clearLastError() } diff --git a/packages/emnapi/src/property.ts b/packages/emnapi/src/property.ts index 9c21c953..b450d2b2 100644 --- a/packages/emnapi/src/property.ts +++ b/packages/emnapi/src/property.ts @@ -2,6 +2,7 @@ import { emnapiCtx } from 'emnapi:shared' import { emnapiDefineProperty } from './internal' import { $PREAMBLE, $CHECK_ARG } from './macro' import { emnapiString } from './string' +import { POINTER_SIZE, POINTER_WASM_TYPE, from64, makeGetValue, makeSetValue } from 'emscripten:parse-tools' /** @__sig ippiiip */ export function napi_get_all_property_names ( @@ -130,11 +131,11 @@ export function napi_get_all_property_names ( } } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = emnapiCtx.addToCurrentScope(ret).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -185,9 +186,9 @@ export function napi_has_property (env: napi_env, object: napi_value, key: napi_ } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('result') + from64('result') r = (emnapiCtx.handleStore.get(key)!.value in v) ? 1 : 0 - $makeSetValue('result', 0, 'r', 'i8') + makeSetValue('result', 0, 'r', 'i8') return envObject.getReturnStatus() }) } @@ -211,10 +212,10 @@ export function napi_get_property (env: napi_env, object: napi_value, key: napi_ } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = envObject.ensureHandleId(v[emnapiCtx.handleStore.get(key)!.value]) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -242,8 +243,8 @@ export function napi_delete_property (env: napi_env, object: napi_value, key: na } } if (result) { - $from64('result') - $makeSetValue('result', 0, 'r ? 1 : 0', 'i8') + from64('result') + makeSetValue('result', 0, 'r ? 1 : 0', 'i8') } return envObject.getReturnStatus() }) @@ -273,8 +274,8 @@ export function napi_has_own_property (env: napi_env, object: napi_value, key: n return envObject.setLastError(napi_status.napi_name_expected) } r = Object.prototype.hasOwnProperty.call(v, emnapiCtx.handleStore.get(key)!.value) - $from64('result') - $makeSetValue('result', 0, 'r ? 1 : 0', 'i8') + from64('result') + makeSetValue('result', 0, 'r ? 1 : 0', 'i8') return envObject.getReturnStatus() }) } @@ -291,7 +292,7 @@ export function napi_set_named_property (env: napi_env, object: napi_value, cnam if (!cname) { return envObject.setLastError(napi_status.napi_invalid_arg) } - $from64('cname') + from64('cname') emnapiCtx.handleStore.get(object)!.value[emnapiString.UTF8ToString(cname, -1)] = emnapiCtx.handleStore.get(value)!.value return envObject.getReturnStatus() }) @@ -318,11 +319,11 @@ export function napi_has_named_property (env: napi_env, object: napi_value, utf8 } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('utf8name') - $from64('result') + from64('utf8name') + from64('result') r = emnapiString.UTF8ToString(utf8name, -1) in v - $makeSetValue('result', 0, 'r ? 1 : 0', 'i8') + makeSetValue('result', 0, 'r ? 1 : 0', 'i8') return envObject.getReturnStatus() }) } @@ -348,12 +349,12 @@ export function napi_get_named_property (env: napi_env, object: napi_value, utf8 } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('utf8name') - $from64('result') + from64('utf8name') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = envObject.ensureHandleId(v[emnapiString.UTF8ToString(utf8name, -1)]) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -390,9 +391,9 @@ export function napi_has_element (env: napi_env, object: napi_value, index: uint } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('result') + from64('result') r = ((index >>> 0) in v) ? 1 : 0 - $makeSetValue('result', 0, 'r', 'i8') + makeSetValue('result', 0, 'r', 'i8') return envObject.getReturnStatus() }) } @@ -415,10 +416,10 @@ export function napi_get_element (env: napi_env, object: napi_value, index: uint } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = envObject.ensureHandleId(v[index >>> 0]) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -444,8 +445,8 @@ export function napi_delete_element (env: napi_env, object: napi_value, index: u } } if (result) { - $from64('result') - $makeSetValue('result', 0, 'r ? 1 : 0', 'i8') + from64('result') + makeSetValue('result', 0, 'r ? 1 : 0', 'i8') } return envObject.getReturnStatus() }) @@ -462,8 +463,8 @@ export function napi_define_properties ( let propPtr: number, attributes: number return $PREAMBLE!(env, (envObject) => { - $from64('properties') - $from64('property_count') + from64('properties') + from64('property_count') property_count = property_count >>> 0 @@ -481,16 +482,16 @@ export function napi_define_properties ( for (let i = 0; i < property_count; i++) { // eslint-disable-next-line @typescript-eslint/no-unused-vars - propPtr = properties + (i * ($POINTER_SIZE * 8)) - const utf8Name = $makeGetValue('propPtr', 0, '*') - const name = $makeGetValue('propPtr', POINTER_SIZE, '*') - const method = $makeGetValue('propPtr', POINTER_SIZE * 2, '*') - const getter = $makeGetValue('propPtr', POINTER_SIZE * 3, '*') - const setter = $makeGetValue('propPtr', POINTER_SIZE * 4, '*') - const value = $makeGetValue('propPtr', POINTER_SIZE * 5, '*') - attributes = $makeGetValue('propPtr', POINTER_SIZE * 6, POINTER_WASM_TYPE) as number - $from64('attributes') - const data = $makeGetValue('propPtr', POINTER_SIZE * 7, '*') + propPtr = properties + (i * (POINTER_SIZE * 8)) + const utf8Name = makeGetValue('propPtr', 0, '*') + const name = makeGetValue('propPtr', POINTER_SIZE, '*') + const method = makeGetValue('propPtr', POINTER_SIZE * 2, '*') + const getter = makeGetValue('propPtr', POINTER_SIZE * 3, '*') + const setter = makeGetValue('propPtr', POINTER_SIZE * 4, '*') + const value = makeGetValue('propPtr', POINTER_SIZE * 5, '*') + attributes = makeGetValue('propPtr', POINTER_SIZE * 6, POINTER_WASM_TYPE) as number + from64('attributes') + const data = makeGetValue('propPtr', POINTER_SIZE * 7, '*') if (utf8Name) { propertyName = emnapiString.UTF8ToString(utf8Name, -1) diff --git a/packages/emnapi/src/script.ts b/packages/emnapi/src/script.ts index dfb6957d..3740d97a 100644 --- a/packages/emnapi/src/script.ts +++ b/packages/emnapi/src/script.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/indent */ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ARG, $PREAMBLE } from './macro' import { napi_set_last_error } from './util' @@ -20,11 +21,11 @@ export function napi_run_script (env: napi_env, script: napi_value, result: Poin } const g: typeof globalThis = emnapiCtx.handleStore.get(GlobalHandle.GLOBAL)!.value const ret = g.eval(v8Script.value) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = envObject.ensureHandleId(ret) - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') status = envObject.getReturnStatus() }) // #else diff --git a/packages/emnapi/src/string.ts b/packages/emnapi/src/string.ts index 433ef67a..14947d5a 100644 --- a/packages/emnapi/src/string.ts +++ b/packages/emnapi/src/string.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/indent */ -import { wasmMemory } from 'emnapi:emscripten-runtime' +import { wasmMemory } from 'emscripten:runtime' +import { getUnsharedTextDecoderView, makeSetValue, from64 } from 'emscripten:parse-tools' import { emnapiCtx } from 'emnapi:shared' import { $CHECK_NEW_STRING_ARGS } from './macro' @@ -165,7 +166,7 @@ export var emnapiString = { return str } // #endif - return emnapiString.utf8Decoder.decode($getUnsharedTextDecoderView('HEAPU8', 'ptr', 'end') as Uint8Array) + return emnapiString.utf8Decoder.decode(getUnsharedTextDecoderView('HEAPU8', 'ptr', 'end') as Uint8Array) }, stringToUTF8 (str: string, outPtr: number, maxBytesToWrite: number): number { const HEAPU8 = new Uint8Array(wasmMemory.buffer) @@ -224,7 +225,7 @@ export var emnapiString = { // #endif // eslint-disable-next-line @typescript-eslint/no-unused-vars const HEAPU8 = new Uint8Array(wasmMemory.buffer) - return emnapiString.utf16Decoder.decode($getUnsharedTextDecoderView('HEAPU8', 'ptr', 'end') as Uint8Array) + return emnapiString.utf16Decoder.decode(getUnsharedTextDecoderView('HEAPU8', 'ptr', 'end') as Uint8Array) }, stringToUTF16 (str: string, outPtr: number, maxBytesToWrite: number): number { if (maxBytesToWrite === undefined) { @@ -237,10 +238,10 @@ export var emnapiString = { for (var i = 0; i < numCharsToWrite; ++i) { // eslint-disable-next-line @typescript-eslint/no-unused-vars var codeUnit = str.charCodeAt(i) - $makeSetValue('outPtr', 0, 'codeUnit', 'i16') + makeSetValue('outPtr', 0, 'codeUnit', 'i16') outPtr += 2 } - $makeSetValue('outPtr', 0, '0', 'i16') + makeSetValue('outPtr', 0, '0', 'i16') return outPtr - startPtr }, newString (env: napi_env, @@ -249,7 +250,7 @@ export var emnapiString = { result: Pointer, stringMaker: (str: number, autoLength: boolean, sizeLength: number) => string ) { - $from64('length') + from64('length') const { autoLength, sizelength, @@ -260,12 +261,12 @@ export var emnapiString = { envObject: Env } = $CHECK_NEW_STRING_ARGS!(env, str, length, result) - $from64('str') + from64('str') const strValue = stringMaker(str, autoLength, sizelength) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(strValue).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() }, newExternalString ( @@ -279,7 +280,7 @@ export var emnapiString = { createApi: (env: napi_env, str: number, length: size_t, result: Pointer) => napi_status, stringMaker: (str: number, autoLength: boolean, sizeLength: number) => string ) { - $from64('length') + from64('length') const { envObject }: { @@ -291,7 +292,7 @@ export var emnapiString = { const status = createApi(env, str, length, result) if (status === napi_status.napi_ok) { if (copied) { - $makeSetValue('copied', 0, '1', 'i8') + makeSetValue('copied', 0, '1', 'i8') } if (finalize_callback) { envObject.callFinalizer(finalize_callback, str, finalize_hint) diff --git a/packages/emnapi/src/threadsafe-function.ts b/packages/emnapi/src/threadsafe-function.ts index 62188b01..6e53b554 100644 --- a/packages/emnapi/src/threadsafe-function.ts +++ b/packages/emnapi/src/threadsafe-function.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/indent */ -import { ENVIRONMENT_IS_NODE, _malloc, wasmMemory, _free, ENVIRONMENT_IS_PTHREAD, abort, PThread } from 'emnapi:emscripten-runtime' +import { ENVIRONMENT_IS_NODE, _malloc, wasmMemory, _free, ENVIRONMENT_IS_PTHREAD, abort, PThread } from 'emscripten:runtime' import { emnapiCtx, emnapiNodeBinding } from 'emnapi:shared' +import { POINTER_SIZE, to64, makeDynCall, from64, makeSetValue, SIZE_TYPE } from 'emscripten:parse-tools' import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG } from './macro' import { _emnapi_node_emit_async_destroy, _emnapi_node_emit_async_init } from './node' import { _emnapi_runtime_keepalive_pop, _emnapi_runtime_keepalive_push } from './util' @@ -20,22 +21,22 @@ export const emnapiTSFN = { /* double */ async_id: 8, /* double */ trigger_async_id: 16, /* size_t */ queue_size: 24, - /* void* */ queue: 1 * $POINTER_SIZE + 24, - /* size_t */ thread_count: 2 * $POINTER_SIZE + 24, - /* bool */ is_closing: 3 * $POINTER_SIZE + 24, - /* atomic_uchar */ dispatch_state: 3 * $POINTER_SIZE + 28, - /* void* */ context: 3 * $POINTER_SIZE + 32, - /* size_t */ max_queue_size: 4 * $POINTER_SIZE + 32, - /* napi_ref */ ref: 5 * $POINTER_SIZE + 32, - /* napi_env */ env: 6 * $POINTER_SIZE + 32, - /* void* */ finalize_data: 7 * $POINTER_SIZE + 32, - /* napi_finalize */ finalize_cb: 8 * $POINTER_SIZE + 32, - /* napi_threadsafe_function_call_js */ call_js_cb: 9 * $POINTER_SIZE + 32, - /* bool */ handles_closing: 10 * $POINTER_SIZE + 32, - /* bool */ async_ref: 10 * $POINTER_SIZE + 36, - /* int32_t */ mutex: 10 * $POINTER_SIZE + 40, - /* int32_t */ cond: 10 * $POINTER_SIZE + 44, - end: 10 * $POINTER_SIZE + 48 + /* void* */ queue: 1 * POINTER_SIZE + 24, + /* size_t */ thread_count: 2 * POINTER_SIZE + 24, + /* bool */ is_closing: 3 * POINTER_SIZE + 24, + /* atomic_uchar */ dispatch_state: 3 * POINTER_SIZE + 28, + /* void* */ context: 3 * POINTER_SIZE + 32, + /* size_t */ max_queue_size: 4 * POINTER_SIZE + 32, + /* napi_ref */ ref: 5 * POINTER_SIZE + 32, + /* napi_env */ env: 6 * POINTER_SIZE + 32, + /* void* */ finalize_data: 7 * POINTER_SIZE + 32, + /* napi_finalize */ finalize_cb: 8 * POINTER_SIZE + 32, + /* napi_threadsafe_function_call_js */ call_js_cb: 9 * POINTER_SIZE + 32, + /* bool */ handles_closing: 10 * POINTER_SIZE + 32, + /* bool */ async_ref: 10 * POINTER_SIZE + 36, + /* int32_t */ mutex: 10 * POINTER_SIZE + 40, + /* int32_t */ cond: 10 * POINTER_SIZE + 44, + end: 10 * POINTER_SIZE + 48 }, init () { if (typeof PThread !== 'undefined') { @@ -80,8 +81,8 @@ export const emnapiTSFN = { return true }, initQueue (func: number): boolean { - const size = 2 * $POINTER_SIZE - const queue = _malloc($to64('size')) + const size = 2 * POINTER_SIZE + const queue = _malloc(to64('size')) if (!queue) return false new Uint8Array(wasmMemory.buffer, queue, size).fill(0) emnapiTSFN.storeSizeTypeValue(func + emnapiTSFN.offset.queue, queue, false) @@ -90,25 +91,25 @@ export const emnapiTSFN = { destroyQueue (func: number) { const queue = emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.queue, false) if (queue) { - _free($to64('queue') as number) + _free(to64('queue') as number) } }, pushQueue (func: number, data: number): void { const queue = emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.queue, false) const head = emnapiTSFN.loadSizeTypeValue(queue, false) - const tail = emnapiTSFN.loadSizeTypeValue(queue + $POINTER_SIZE, false) + const tail = emnapiTSFN.loadSizeTypeValue(queue + POINTER_SIZE, false) // eslint-disable-next-line @typescript-eslint/no-unused-vars - const size = 2 * $POINTER_SIZE - const node = _malloc($to64('size')) + const size = 2 * POINTER_SIZE + const node = _malloc(to64('size')) if (!node) throw new Error('OOM') emnapiTSFN.storeSizeTypeValue(node, data, false) - emnapiTSFN.storeSizeTypeValue(node + $POINTER_SIZE, 0, false) + emnapiTSFN.storeSizeTypeValue(node + POINTER_SIZE, 0, false) if (head === 0 && tail === 0) { emnapiTSFN.storeSizeTypeValue(queue, node, false) - emnapiTSFN.storeSizeTypeValue(queue + $POINTER_SIZE, node, false) + emnapiTSFN.storeSizeTypeValue(queue + POINTER_SIZE, node, false) } else { - emnapiTSFN.storeSizeTypeValue(tail + $POINTER_SIZE, node, false) - emnapiTSFN.storeSizeTypeValue(queue + $POINTER_SIZE, node, false) + emnapiTSFN.storeSizeTypeValue(tail + POINTER_SIZE, node, false) + emnapiTSFN.storeSizeTypeValue(queue + POINTER_SIZE, node, false) } emnapiTSFN.addQueueSize(func) }, @@ -117,14 +118,14 @@ export const emnapiTSFN = { const head = emnapiTSFN.loadSizeTypeValue(queue, false) if (head === 0) return 0 const node = head - const next = emnapiTSFN.loadSizeTypeValue(head + $POINTER_SIZE, false) + const next = emnapiTSFN.loadSizeTypeValue(head + POINTER_SIZE, false) emnapiTSFN.storeSizeTypeValue(queue, next, false) if (next === 0) { - emnapiTSFN.storeSizeTypeValue(queue + $POINTER_SIZE, 0, false) + emnapiTSFN.storeSizeTypeValue(queue + POINTER_SIZE, 0, false) } - emnapiTSFN.storeSizeTypeValue(node + $POINTER_SIZE, 0, false) + emnapiTSFN.storeSizeTypeValue(node + POINTER_SIZE, 0, false) const value = emnapiTSFN.loadSizeTypeValue(node, false) - _free($to64('node') as number) + _free(to64('node') as number) emnapiTSFN.subQueueSize(func) return value }, @@ -282,7 +283,7 @@ export const emnapiTSFN = { arr = new Uint32Array(wasmMemory.buffer) index = (func + offset) >> 2 // #endif - Atomics.add(arr, index, $to64('1') as any) + Atomics.add(arr, index, to64('1') as any) }, subQueueSize (func: number): void { const offset = emnapiTSFN.offset.queue_size @@ -294,7 +295,7 @@ export const emnapiTSFN = { arr = new Uint32Array(wasmMemory.buffer) index = (func + offset) >> 2 // #endif - Atomics.sub(arr, index, $to64('1') as any) + Atomics.sub(arr, index, to64('1') as any) }, getThreadCount (func: number): number { return emnapiTSFN.loadSizeTypeValue(func + emnapiTSFN.offset.thread_count, true) @@ -309,7 +310,7 @@ export const emnapiTSFN = { arr = new Uint32Array(wasmMemory.buffer) index = (func + offset) >> 2 // #endif - Atomics.add(arr, index, $to64('1') as any) + Atomics.add(arr, index, to64('1') as any) }, subThreadCount (func: number): void { const offset = emnapiTSFN.offset.thread_count @@ -321,7 +322,7 @@ export const emnapiTSFN = { arr = new Uint32Array(wasmMemory.buffer) index = (func + offset) >> 2 // #endif - Atomics.sub(arr, index, $to64('1') as any) + Atomics.sub(arr, index, to64('1') as any) }, getIsClosing (func: number): number { return Atomics.load(new Int32Array(wasmMemory.buffer), (func + emnapiTSFN.offset.is_closing) >> 2) @@ -436,7 +437,7 @@ export const emnapiTSFN = { _emnapi_node_emit_async_destroy(asyncId, triggerAsyncId) } - _free($to64('func') as number) + _free(to64('func') as number) }, emptyQueueAndDelete (func: number) { const callJsCb = emnapiTSFN.getCallJSCb(func) @@ -447,7 +448,7 @@ export const emnapiTSFN = { // eslint-disable-next-line @typescript-eslint/no-unused-vars data = emnapiTSFN.shiftQueue(func) if (callJsCb) { - $makeDynCall('vpppp', 'callJsCb')($to64('0'), $to64('0'), $to64('context'), $to64('data')) + makeDynCall('vpppp', 'callJsCb')(to64('0'), to64('0'), to64('context'), to64('data')) } } emnapiTSFN.destroy(func) @@ -466,9 +467,9 @@ export const emnapiTSFN = { const f = (): void => { (envObject as NodeEnv).callFinalizerInternal( 0, - $to64('finalize') as number, - $to64('data') as number, - $to64('context') as number + to64('finalize') as number, + to64('data') as number, + to64('context') as number ) } @@ -570,7 +571,7 @@ export const emnapiTSFN = { if (callJsCb) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const context = emnapiTSFN.getContext(func) - $makeDynCall('vpppp', 'callJsCb')($to64('env'), $to64('js_callback'), $to64('context'), $to64('data')) + makeDynCall('vpppp', 'callJsCb')(to64('env'), to64('js_callback'), to64('context'), to64('data')) } else { const jsCallback = js_callback ? emnapiCtx.handleStore.get(js_callback)!.value : null if (typeof jsCallback === 'function') { @@ -657,13 +658,13 @@ export function napi_create_threadsafe_function ( ): napi_status { const envObject = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, async_resource_name) - $from64('max_queue_size') - $from64('initial_thread_count') - $from64('env') - $from64('thread_finalize_data') - $from64('thread_finalize_cb') - $from64('context') - $from64('call_js_cb') + from64('max_queue_size') + from64('initial_thread_count') + from64('env') + from64('thread_finalize_data') + from64('thread_finalize_cb') + from64('context') + from64('call_js_cb') max_queue_size = max_queue_size >>> 0 initial_thread_count = initial_thread_count >>> 0 if (initial_thread_count === 0) { @@ -672,7 +673,7 @@ export function napi_create_threadsafe_function ( $CHECK_ARG!(envObject, result) let ref: napi_ref = 0 - $from64('func') + from64('func') if (!func) { $CHECK_ARG!(envObject, call_js_cb) } else { @@ -705,36 +706,36 @@ export function napi_create_threadsafe_function ( // tsfn create const sizeofTSFN = emnapiTSFN.offset.end - const tsfn = _malloc($to64('sizeofTSFN')) + const tsfn = _malloc(to64('sizeofTSFN')) if (!tsfn) return envObject.setLastError(napi_status.napi_generic_failure) new Uint8Array(wasmMemory.buffer).subarray(tsfn, tsfn + sizeofTSFN).fill(0) const resourceRef = emnapiCtx.createReference(envObject, resource, 1, Ownership.kUserland as any) // eslint-disable-next-line @typescript-eslint/no-unused-vars const resource_ = resourceRef.id - $makeSetValue('tsfn', 0, 'resource_', '*') + makeSetValue('tsfn', 0, 'resource_', '*') if (!emnapiTSFN.initQueue(tsfn)) { - _free($to64('tsfn') as number) + _free(to64('tsfn') as number) resourceRef.dispose() return envObject.setLastError(napi_status.napi_generic_failure) } _emnapi_node_emit_async_init(resource, resource_name, -1, tsfn + emnapiTSFN.offset.async_id) - $makeSetValue('tsfn', 'emnapiTSFN.offset.thread_count', 'initial_thread_count', SIZE_TYPE) - $makeSetValue('tsfn', 'emnapiTSFN.offset.context', 'context', '*') - $makeSetValue('tsfn', 'emnapiTSFN.offset.max_queue_size', 'max_queue_size', SIZE_TYPE) - $makeSetValue('tsfn', 'emnapiTSFN.offset.ref', 'ref', '*') - $makeSetValue('tsfn', 'emnapiTSFN.offset.env', 'env', '*') - $makeSetValue('tsfn', 'emnapiTSFN.offset.finalize_data', 'thread_finalize_data', '*') - $makeSetValue('tsfn', 'emnapiTSFN.offset.finalize_cb', 'thread_finalize_cb', '*') - $makeSetValue('tsfn', 'emnapiTSFN.offset.call_js_cb', 'call_js_cb', '*') + makeSetValue('tsfn', 'emnapiTSFN.offset.thread_count', 'initial_thread_count', SIZE_TYPE) + makeSetValue('tsfn', 'emnapiTSFN.offset.context', 'context', '*') + makeSetValue('tsfn', 'emnapiTSFN.offset.max_queue_size', 'max_queue_size', SIZE_TYPE) + makeSetValue('tsfn', 'emnapiTSFN.offset.ref', 'ref', '*') + makeSetValue('tsfn', 'emnapiTSFN.offset.env', 'env', '*') + makeSetValue('tsfn', 'emnapiTSFN.offset.finalize_data', 'thread_finalize_data', '*') + makeSetValue('tsfn', 'emnapiTSFN.offset.finalize_cb', 'thread_finalize_cb', '*') + makeSetValue('tsfn', 'emnapiTSFN.offset.call_js_cb', 'call_js_cb', '*') emnapiCtx.addCleanupHook(envObject, emnapiTSFN.cleanup, tsfn) envObject.ref() _emnapi_runtime_keepalive_push() emnapiCtx.increaseWaitingRequestCounter() - $makeSetValue('tsfn', 'emnapiTSFN.offset.async_ref', '1', 'i32') + makeSetValue('tsfn', 'emnapiTSFN.offset.async_ref', '1', 'i32') - $from64('result') - $makeSetValue('result', 0, 'tsfn', '*') + from64('result') + makeSetValue('result', 0, 'tsfn', '*') return envObject.clearLastError() } @@ -745,11 +746,11 @@ export function napi_get_threadsafe_function_context (func: number, result: void abort() return napi_status.napi_invalid_arg } - $from64('func') - $from64('result') + from64('func') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const context = emnapiTSFN.getContext(func) - $makeSetValue('result', 0, 'context', '*') + makeSetValue('result', 0, 'context', '*') return napi_status.napi_ok } @@ -759,8 +760,8 @@ export function napi_call_threadsafe_function (func: number, data: void_p, mode: abort() return napi_status.napi_invalid_arg } - $from64('func') - $from64('data') + from64('func') + from64('data') return emnapiTSFN.push(func, data, mode) } @@ -771,7 +772,7 @@ export function napi_acquire_threadsafe_function (func: number): napi_status { abort() return napi_status.napi_invalid_arg } - $from64('func') + from64('func') const mutex = emnapiTSFN.getMutex(func) return mutex.execute(() => { @@ -789,7 +790,7 @@ export function napi_release_threadsafe_function (func: number, mode: napi_threa abort() return napi_status.napi_invalid_arg } - $from64('func') + from64('func') const mutex = emnapiTSFN.getMutex(func) const cond = emnapiTSFN.getCond(func) @@ -823,7 +824,7 @@ export function napi_unref_threadsafe_function (env: napi_env, func: number): na abort() return napi_status.napi_invalid_arg } - $from64('func') + from64('func') const asyncRefOffset = (func + emnapiTSFN.offset.async_ref) >> 2 const arr = new Int32Array(wasmMemory.buffer) if (Atomics.load(arr, asyncRefOffset)) { @@ -840,7 +841,7 @@ export function napi_ref_threadsafe_function (env: napi_env, func: number): napi abort() return napi_status.napi_invalid_arg } - $from64('func') + from64('func') const asyncRefOffset = (func + emnapiTSFN.offset.async_ref) >> 2 const arr = new Int32Array(wasmMemory.buffer) if (!Atomics.load(arr, asyncRefOffset)) { diff --git a/packages/emnapi/src/typings/compiler.d.ts b/packages/emnapi/src/typings/compiler.d.ts deleted file mode 100644 index 5a713d7a..00000000 --- a/packages/emnapi/src/typings/compiler.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -// fake -declare function $makeDynCall (sig: string, ptr: string): (...args: any[]) => any -// declare function $makeMalloc (source: string, size: string | number): void_p - -declare type Bit = '8' | '16' | '32' | '64' -declare type Sign = 'i' | 'u' -declare type Integer = `${Sign}${Bit}` -declare type Float = 'float' | 'double' -declare type PtrType = '*' | `${Integer | Float}*` -declare type ValType = Integer | Float | PtrType - -declare function $makeGetValue (ptrVar: string, pos: number | string, type: PtrType): number -declare function $makeGetValue (ptrVar: string, pos: number | string, type: ValType): number | bigint -declare function $makeSetValue (ptrVar: string, pos: number | string, valueVar: string, type: ValType): void -declare function $getUnsharedTextDecoderView (heap: string, start: string, end: string): ArrayBufferView - -declare function $from64 (x: string | string[]): void -declare function $to64 (x: string): number | bigint - -declare const $POINTER_SIZE: number -declare const POINTER_SIZE: number -declare const SIZE_TYPE: 'u32' | 'u64' -declare const POINTER_WASM_TYPE: 'i32' | 'i64' diff --git a/packages/emnapi/src/typings/parse-tools.d.ts b/packages/emnapi/src/typings/parse-tools.d.ts new file mode 100644 index 00000000..19a125ea --- /dev/null +++ b/packages/emnapi/src/typings/parse-tools.d.ts @@ -0,0 +1,26 @@ +declare module 'emscripten:parse-tools' { + export function makeDynCall (sig: string, ptr: string): (...args: any[]) => any + + export type Bit = '8' | '16' | '32' | '64' + export type Sign = 'i' | 'u' + export type Integer = `${Sign}${Bit}` + export type Float = 'float' | 'double' + export type PtrType = '*' | `${Integer | Float}*` + export type ValType = Integer | Float | PtrType + + export function makeGetValue (ptrVar: string, pos: number | string, type: PtrType): number + export function makeGetValue (ptrVar: string, pos: number | string, type: ValType): number | bigint + export function makeSetValue (ptrVar: string, pos: number | string, valueVar: string, type: ValType): void + export function getUnsharedTextDecoderView (heap: string, start: string, end: string): ArrayBufferView + + export function from64 (x: string | string[]): void + export function to64 (x: string): number | bigint + + export const POINTER_SIZE: number + export const SIZE_TYPE: 'u32' | 'u64' + export const POINTER_WASM_TYPE: 'i32' | 'i64' +} + +// declare const POINTER_SIZE: number +// declare const SIZE_TYPE: 'u32' | 'u64' +// declare const POINTER_WASM_TYPE: 'i32' | 'i64' diff --git a/packages/emnapi/src/util.ts b/packages/emnapi/src/util.ts index 6f907533..6a527ece 100644 --- a/packages/emnapi/src/util.ts +++ b/packages/emnapi/src/util.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/indent */ -import { runtimeKeepalivePop, runtimeKeepalivePush } from 'emnapi:emscripten-runtime' +import { runtimeKeepalivePop, runtimeKeepalivePush } from 'emscripten:runtime' +import { from64, makeSetValue, makeDynCall } from 'emscripten:parse-tools' import { emnapiCtx } from 'emnapi:shared' /** @@ -25,9 +26,9 @@ declare const process: any * @__sig vppp */ export function _emnapi_get_node_version (major: number, minor: number, patch: number): void { - $from64('major') - $from64('minor') - $from64('patch') + from64('major') + from64('minor') + from64('patch') // eslint-disable-next-line @typescript-eslint/no-unused-vars const versions: [number, number, number] = @@ -39,9 +40,9 @@ export function _emnapi_get_node_version (major: number, minor: number, patch: n ? process.versions.node.split('.').map((n: string) => Number(n)) : [0, 0, 0] - $makeSetValue('major', 0, 'versions[0]', 'u32') - $makeSetValue('minor', 0, 'versions[1]', 'u32') - $makeSetValue('patch', 0, 'versions[2]', 'u32') + makeSetValue('major', 0, 'versions[0]', 'u32') + makeSetValue('minor', 0, 'versions[1]', 'u32') + makeSetValue('patch', 0, 'versions[2]', 'u32') } /** @@ -65,7 +66,7 @@ export function _emnapi_runtime_keepalive_pop (): void { */ export function _emnapi_set_immediate (callback: number, data: number): void { emnapiCtx.feature.setImmediate(() => { - $makeDynCall('vp', 'callback')(data) + makeDynCall('vp', 'callback')(data) }) } @@ -75,7 +76,7 @@ export function _emnapi_set_immediate (callback: number, data: number): void { export function _emnapi_next_tick (callback: number, data: number): void { // eslint-disable-next-line @typescript-eslint/no-floating-promises Promise.resolve().then(() => { - $makeDynCall('vp', 'callback')(data) + makeDynCall('vp', 'callback')(data) }) } @@ -87,7 +88,7 @@ export function _emnapi_callback_into_module (forceUncaught: int, env: napi_env, const scope = emnapiCtx.openScope(envObject) try { (envObject as NodeEnv).callbackIntoModule(Boolean(forceUncaught), () => { - $makeDynCall('vpp', 'callback')(env, data) + makeDynCall('vpp', 'callback')(env, data) }) } catch (err) { emnapiCtx.closeScope(envObject, scope) @@ -104,7 +105,7 @@ export function _emnapi_callback_into_module (forceUncaught: int, env: napi_env, */ export function _emnapi_call_finalizer (forceUncaught: int, env: napi_env, callback: number, data: number, hint: number): void { const envObject = emnapiCtx.envStore.get(env)! - $from64('callback') + from64('callback') ;(envObject as NodeEnv).callFinalizerInternal(forceUncaught, callback, data, hint) } @@ -129,6 +130,6 @@ export function $emnapiSetValueI64 (result: Pointer, numberValue: numbe numberValue >>> 0, (tempDouble = numberValue, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0) ] - $makeSetValue('result', 0, 'tempI64[0]', 'i32') - $makeSetValue('result', 4, 'tempI64[1]', 'i32') + makeSetValue('result', 0, 'tempI64[0]', 'i32') + makeSetValue('result', 4, 'tempI64[1]', 'i32') } diff --git a/packages/emnapi/src/value-operation.ts b/packages/emnapi/src/value-operation.ts index 66216c9d..dc8176c9 100644 --- a/packages/emnapi/src/value-operation.ts +++ b/packages/emnapi/src/value-operation.ts @@ -1,4 +1,5 @@ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG, $PREAMBLE } from './macro' /** @__sig ippp */ @@ -7,7 +8,7 @@ export function napi_typeof (env: napi_env, value: napi_value, result: Pointer>> 0 - $makeSetValue('result', 0, 'v', 'u32') + makeSetValue('result', 0, 'v', 'u32') return envObject.getReturnStatus() }) } @@ -34,15 +35,15 @@ export function napi_get_arraybuffer_info (env: napi_env, arraybuffer: napi_valu return envObject.setLastError(napi_status.napi_invalid_arg) } if (data) { - $from64('data') + from64('data') // eslint-disable-next-line @typescript-eslint/no-unused-vars const p: number = emnapiExternalMemory.getArrayBufferPointer(handle.value, true).address - $makeSetValue('data', 0, 'p', '*') + makeSetValue('data', 0, 'p', '*') } if (byte_length) { - $from64('byte_length') - $makeSetValue('byte_length', 0, 'handle.value.byteLength', SIZE_TYPE) + from64('byte_length') + makeSetValue('byte_length', 0, 'handle.value.byteLength', SIZE_TYPE) } return envObject.clearLastError() } @@ -64,11 +65,11 @@ export function napi_get_prototype (env: napi_env, value: napi_value, result: Po } catch (_) { return envObject.setLastError(napi_status.napi_object_expected) } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const p = envObject.ensureHandleId(Object.getPrototypeOf(v)) - $makeSetValue('result', 0, 'p', '*') + makeSetValue('result', 0, 'p', '*') return envObject.getReturnStatus() }) } @@ -93,7 +94,7 @@ export function napi_get_typedarray_info ( } const v: ArrayBufferView = handle.value if (type) { - $from64('type') + from64('type') let t: napi_typedarray_type if (v instanceof Int8Array) { t = napi_typedarray_type.napi_int8_array @@ -121,33 +122,33 @@ export function napi_get_typedarray_info ( } else { return envObject.setLastError(napi_status.napi_generic_failure) } - $makeSetValue('type', 0, 't', 'i32') + makeSetValue('type', 0, 't', 'i32') } if (length) { - $from64('length') - $makeSetValue('length', 0, 'v.length', SIZE_TYPE) + from64('length') + makeSetValue('length', 0, 'v.length', SIZE_TYPE) } let buffer: ArrayBuffer if (data || arraybuffer) { buffer = v.buffer if (data) { - $from64('data') + from64('data') // eslint-disable-next-line @typescript-eslint/no-unused-vars const p: number = emnapiExternalMemory.getViewPointer(v, true).address - $makeSetValue('data', 0, 'p', '*') + makeSetValue('data', 0, 'p', '*') } if (arraybuffer) { - $from64('arraybuffer') + from64('arraybuffer') // eslint-disable-next-line @typescript-eslint/no-unused-vars const ab = envObject.ensureHandleId(buffer) - $makeSetValue('arraybuffer', 0, 'ab', '*') + makeSetValue('arraybuffer', 0, 'ab', '*') } } if (byte_offset) { - $from64('byte_offset') - $makeSetValue('byte_offset', 0, 'v.byteOffset', SIZE_TYPE) + from64('byte_offset') + makeSetValue('byte_offset', 0, 'v.byteOffset', SIZE_TYPE) } return envObject.clearLastError() } @@ -189,30 +190,30 @@ export function napi_get_dataview_info ( } const v = handle.value as DataView if (byte_length) { - $from64('byte_length') - $makeSetValue('byte_length', 0, 'v.byteLength', SIZE_TYPE) + from64('byte_length') + makeSetValue('byte_length', 0, 'v.byteLength', SIZE_TYPE) } let buffer: ArrayBuffer if (data || arraybuffer) { buffer = v.buffer if (data) { - $from64('data') + from64('data') // eslint-disable-next-line @typescript-eslint/no-unused-vars const p: number = emnapiExternalMemory.getViewPointer(v, true).address - $makeSetValue('data', 0, 'p', '*') + makeSetValue('data', 0, 'p', '*') } if (arraybuffer) { - $from64('arraybuffer') + from64('arraybuffer') // eslint-disable-next-line @typescript-eslint/no-unused-vars const ab = envObject.ensureHandleId(buffer) - $makeSetValue('arraybuffer', 0, 'ab', '*') + makeSetValue('arraybuffer', 0, 'ab', '*') } } if (byte_offset) { - $from64('byte_offset') - $makeSetValue('byte_offset', 0, 'v.byteOffset', SIZE_TYPE) + from64('byte_offset') + makeSetValue('byte_offset', 0, 'v.byteOffset', SIZE_TYPE) } return envObject.clearLastError() } @@ -231,9 +232,9 @@ export function napi_get_date_value (env: napi_env, value: napi_value, result: P if (!handle.isDate()) { return envObject.setLastError(napi_status.napi_invalid_arg) } - $from64('result') + from64('result') v = (handle.value as Date).valueOf() - $makeSetValue('result', 0, 'v', 'double') + makeSetValue('result', 0, 'v', 'double') return envObject.getReturnStatus() }) } @@ -249,10 +250,10 @@ export function napi_get_value_bool (env: napi_env, value: napi_value, result: P if (typeof handle.value !== 'boolean') { return envObject.setLastError(napi_status.napi_boolean_expected) } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const r = handle.value ? 1 : 0 - $makeSetValue('result', 0, 'r', 'i8') + makeSetValue('result', 0, 'r', 'i8') return envObject.clearLastError() } @@ -267,10 +268,10 @@ export function napi_get_value_double (env: napi_env, value: napi_value, result: if (typeof handle.value !== 'number') { return envObject.setLastError(napi_status.napi_number_expected) } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const r = handle.value - $makeSetValue('result', 0, 'r', 'double') + makeSetValue('result', 0, 'r', 'double') return envObject.clearLastError() } @@ -290,12 +291,12 @@ export function napi_get_value_bigint_int64 (env: napi_env, value: napi_value, r if (typeof numberValue !== 'bigint') { return envObject.setLastError(napi_status.napi_number_expected) } - $from64('lossless') - $from64('result') + from64('lossless') + from64('result') if ((numberValue >= (BigInt(-1) * (BigInt(1) << BigInt(63)))) && (numberValue < (BigInt(1) << BigInt(63)))) { - $makeSetValue('lossless', 0, '1', 'i8') + makeSetValue('lossless', 0, '1', 'i8') } else { - $makeSetValue('lossless', 0, '0', 'i8') + makeSetValue('lossless', 0, '0', 'i8') numberValue = numberValue & ((BigInt(1) << BigInt(64)) - BigInt(1)) if (numberValue >= (BigInt(1) << BigInt(63))) { numberValue = numberValue - (BigInt(1) << BigInt(64)) @@ -305,8 +306,8 @@ export function napi_get_value_bigint_int64 (env: napi_env, value: napi_value, r const low = Number(numberValue & BigInt(0xffffffff)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const high = Number(numberValue >> BigInt(32)) - $makeSetValue('result', 0, 'low', 'i32') - $makeSetValue('result', 4, 'high', 'i32') + makeSetValue('result', 0, 'low', 'i32') + makeSetValue('result', 4, 'high', 'i32') return envObject.clearLastError() } @@ -326,20 +327,20 @@ export function napi_get_value_bigint_uint64 (env: napi_env, value: napi_value, if (typeof numberValue !== 'bigint') { return envObject.setLastError(napi_status.napi_number_expected) } - $from64('lossless') - $from64('result') + from64('lossless') + from64('result') if ((numberValue >= BigInt(0)) && (numberValue < (BigInt(1) << BigInt(64)))) { - $makeSetValue('lossless', 0, '1', 'i8') + makeSetValue('lossless', 0, '1', 'i8') } else { - $makeSetValue('lossless', 0, '0', 'i8') + makeSetValue('lossless', 0, '0', 'i8') numberValue = numberValue & ((BigInt(1) << BigInt(64)) - BigInt(1)) } // eslint-disable-next-line @typescript-eslint/no-unused-vars const low = Number(numberValue & BigInt(0xffffffff)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const high = Number(numberValue >> BigInt(32)) - $makeSetValue('result', 0, 'low', 'u32') - $makeSetValue('result', 4, 'high', 'u32') + makeSetValue('result', 0, 'low', 'u32') + makeSetValue('result', 4, 'high', 'u32') return envObject.clearLastError() } @@ -365,12 +366,12 @@ export function napi_get_value_bigint_words ( } const isMinus = handle.value < BigInt(0) - $from64('sign_bit') - $from64('words') - $from64('word_count') + from64('sign_bit') + from64('words') + from64('word_count') - let word_count_int = $makeGetValue('word_count', 0, SIZE_TYPE) as number - $from64('word_count_int') + let word_count_int = makeGetValue('word_count', 0, SIZE_TYPE) as number + from64('word_count_int') let wordCount = 0 let bigintValue: bigint = isMinus ? (handle.value * BigInt(-1)) : handle.value @@ -381,7 +382,7 @@ export function napi_get_value_bigint_words ( bigintValue = isMinus ? (handle.value * BigInt(-1)) : handle.value if (!sign_bit && !words) { word_count_int = wordCount - $makeSetValue('word_count', 0, 'word_count_int', SIZE_TYPE) + makeSetValue('word_count', 0, 'word_count_int', SIZE_TYPE) } else { if (!sign_bit) return envObject.setLastError(napi_status.napi_invalid_arg) if (!words) return envObject.setLastError(napi_status.napi_invalid_arg) @@ -397,11 +398,11 @@ export function napi_get_value_bigint_words ( const low = Number(wordsArr[i] & BigInt(0xffffffff)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const high = Number(wordsArr[i] >> BigInt(32)) - $makeSetValue('words', 'i * 8', 'low', 'u32') - $makeSetValue('words', 'i * 8 + 4', 'high', 'u32') + makeSetValue('words', 'i * 8', 'low', 'u32') + makeSetValue('words', 'i * 8 + 4', 'high', 'u32') } - $makeSetValue('sign_bit', 0, 'isMinus ? 1 : 0', 'i32') - $makeSetValue('word_count', 0, 'len', SIZE_TYPE) + makeSetValue('sign_bit', 0, 'isMinus ? 1 : 0', 'i32') + makeSetValue('word_count', 0, 'len', SIZE_TYPE) } return envObject.clearLastError() } @@ -417,11 +418,11 @@ export function napi_get_value_external (env: napi_env, value: napi_value, resul if (!handle.isExternal()) { return envObject.setLastError(napi_status.napi_invalid_arg) } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const p = handle.data(envObject) - $makeSetValue('result', 0, 'p', '*') + makeSetValue('result', 0, 'p', '*') return envObject.clearLastError() } @@ -436,8 +437,8 @@ export function napi_get_value_int32 (env: napi_env, value: napi_value, result: if (typeof handle.value !== 'number') { return envObject.setLastError(napi_status.napi_number_expected) } - $from64('result') - $makeSetValue('result', 0, 'handle.value', 'i32') + from64('result') + makeSetValue('result', 0, 'handle.value', 'i32') return envObject.clearLastError() } @@ -453,17 +454,17 @@ export function napi_get_value_int64 (env: napi_env, value: napi_value, result: return envObject.setLastError(napi_status.napi_number_expected) } const numberValue = handle.value - $from64('result') + from64('result') if (numberValue === Number.POSITIVE_INFINITY || numberValue === Number.NEGATIVE_INFINITY || isNaN(numberValue)) { - $makeSetValue('result', 0, '0', 'i32') - $makeSetValue('result', 4, '0', 'i32') + makeSetValue('result', 0, '0', 'i32') + makeSetValue('result', 4, '0', 'i32') } else if (numberValue < /* INT64_RANGE_NEGATIVE */ -9223372036854776000) { - $makeSetValue('result', 0, '0', 'i32') - $makeSetValue('result', 4, '0x80000000', 'i32') + makeSetValue('result', 0, '0', 'i32') + makeSetValue('result', 4, '0x80000000', 'i32') } else if (numberValue >= /* INT64_RANGE_POSITIVE */ 9223372036854776000) { - $makeSetValue('result', 0, '0xffffffff', 'u32') - $makeSetValue('result', 4, '0x7fffffff', 'u32') + makeSetValue('result', 0, '0xffffffff', 'u32') + makeSetValue('result', 4, '0x7fffffff', 'u32') } else { emnapiSetValueI64(result, numberValue) } @@ -476,9 +477,9 @@ export function napi_get_value_int64 (env: napi_env, value: napi_value, result: export function napi_get_value_string_latin1 (env: napi_env, value: napi_value, buf: char_p, buf_size: size_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) - $from64('result') - $from64('buf') - $from64('buf_size') + from64('result') + from64('buf') + from64('buf_size') buf_size = buf_size >>> 0 const handle = emnapiCtx.handleStore.get(value)! @@ -487,23 +488,23 @@ export function napi_get_value_string_latin1 (env: napi_env, value: napi_value, } if (!buf) { if (!result) return envObject.setLastError(napi_status.napi_invalid_arg) - $makeSetValue('result', 0, 'handle.value.length', SIZE_TYPE) + makeSetValue('result', 0, 'handle.value.length', SIZE_TYPE) } else if (buf_size !== 0) { let copied: number = 0 let v: number for (let i = 0; i < buf_size - 1; ++i) { // eslint-disable-next-line @typescript-eslint/no-unused-vars v = handle.value.charCodeAt(i) & 0xff - $makeSetValue('buf', 'i', 'v', 'u8') + makeSetValue('buf', 'i', 'v', 'u8') // eslint-disable-next-line @typescript-eslint/no-unused-vars copied++ } - $makeSetValue('buf', 'copied', '0', 'u8') + makeSetValue('buf', 'copied', '0', 'u8') if (result) { - $makeSetValue('result', 0, 'copied', SIZE_TYPE) + makeSetValue('result', 0, 'copied', SIZE_TYPE) } } else if (result) { - $makeSetValue('result', 0, '0', SIZE_TYPE) + makeSetValue('result', 0, '0', SIZE_TYPE) } return envObject.clearLastError() } @@ -514,9 +515,9 @@ export function napi_get_value_string_latin1 (env: napi_env, value: napi_value, export function napi_get_value_string_utf8 (env: napi_env, value: napi_value, buf: char_p, buf_size: size_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) - $from64('result') - $from64('buf') - $from64('buf_size') + from64('result') + from64('buf') + from64('buf_size') buf_size = buf_size >>> 0 const handle = emnapiCtx.handleStore.get(value)! @@ -527,15 +528,15 @@ export function napi_get_value_string_utf8 (env: napi_env, value: napi_value, bu if (!result) return envObject.setLastError(napi_status.napi_invalid_arg) // eslint-disable-next-line @typescript-eslint/no-unused-vars const strLength = emnapiString.lengthBytesUTF8(handle.value) - $makeSetValue('result', 0, 'strLength', SIZE_TYPE) + makeSetValue('result', 0, 'strLength', SIZE_TYPE) } else if (buf_size !== 0) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const copied = emnapiString.stringToUTF8(handle.value, buf, buf_size) if (result) { - $makeSetValue('result', 0, 'copied', SIZE_TYPE) + makeSetValue('result', 0, 'copied', SIZE_TYPE) } } else if (result) { - $makeSetValue('result', 0, '0', SIZE_TYPE) + makeSetValue('result', 0, '0', SIZE_TYPE) } return envObject.clearLastError() } @@ -546,9 +547,9 @@ export function napi_get_value_string_utf8 (env: napi_env, value: napi_value, bu export function napi_get_value_string_utf16 (env: napi_env, value: napi_value, buf: char16_t_p, buf_size: size_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) - $from64('result') - $from64('buf') - $from64('buf_size') + from64('result') + from64('buf') + from64('buf_size') buf_size = buf_size >>> 0 const handle = emnapiCtx.handleStore.get(value)! @@ -557,15 +558,15 @@ export function napi_get_value_string_utf16 (env: napi_env, value: napi_value, b } if (!buf) { if (!result) return envObject.setLastError(napi_status.napi_invalid_arg) - $makeSetValue('result', 0, 'handle.value.length', SIZE_TYPE) + makeSetValue('result', 0, 'handle.value.length', SIZE_TYPE) } else if (buf_size !== 0) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const copied = emnapiString.stringToUTF16(handle.value, buf, buf_size * 2) if (result) { - $makeSetValue('result', 0, 'copied / 2', SIZE_TYPE) + makeSetValue('result', 0, 'copied / 2', SIZE_TYPE) } } else if (result) { - $makeSetValue('result', 0, '0', SIZE_TYPE) + makeSetValue('result', 0, '0', SIZE_TYPE) } return envObject.clearLastError() } @@ -581,7 +582,7 @@ export function napi_get_value_uint32 (env: napi_env, value: napi_value, result: if (typeof handle.value !== 'number') { return envObject.setLastError(napi_status.napi_number_expected) } - $from64('result') - $makeSetValue('result', 0, 'handle.value', 'u32') + from64('result') + makeSetValue('result', 0, 'handle.value', 'u32') return envObject.clearLastError() } diff --git a/packages/emnapi/src/value/convert2napi.ts b/packages/emnapi/src/value/convert2napi.ts index 93c33398..1872bcbd 100644 --- a/packages/emnapi/src/value/convert2napi.ts +++ b/packages/emnapi/src/value/convert2napi.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/indent */ import { emnapiCtx } from 'emnapi:shared' +import { from64, makeGetValue, makeSetValue } from 'emscripten:parse-tools' import { emnapiString } from '../string' import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from '../macro' @@ -10,10 +11,10 @@ import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from '../macro' export function napi_create_int32 (env: napi_env, value: int32_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const v = emnapiCtx.addToCurrentScope(value).id - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') return envObject.clearLastError() } @@ -23,10 +24,10 @@ export function napi_create_int32 (env: napi_env, value: int32_t, result: Pointe export function napi_create_uint32 (env: napi_env, value: uint32_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const v = emnapiCtx.addToCurrentScope(value >>> 0).id - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') return envObject.clearLastError() } @@ -43,14 +44,14 @@ export function napi_create_int64 (env: napi_env, low: int32_t, high: int32_t, r value = Number(low) // eslint-disable-next-line @typescript-eslint/no-unused-vars const v1 = emnapiCtx.addToCurrentScope(value).id - $from64('high') - $makeSetValue('high', 0, 'v1', '*') + from64('high') + makeSetValue('high', 0, 'v1', '*') // #else if (!result) return envObject.setLastError(napi_status.napi_invalid_arg) value = (low >>> 0) + (high * Math.pow(2, 32)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const v2 = emnapiCtx.addToCurrentScope(value).id - $makeSetValue('result', 0, 'v2', '*') + makeSetValue('result', 0, 'v2', '*') // #endif return envObject.clearLastError() @@ -62,10 +63,10 @@ export function napi_create_int64 (env: napi_env, low: int32_t, high: int32_t, r export function napi_create_double (env: napi_env, value: double, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const v = emnapiCtx.addToCurrentScope(value).id - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') return envObject.clearLastError() } @@ -78,14 +79,14 @@ export function napi_create_string_latin1 (env: napi_env, str: const_char_p, len let len = 0 if (autoLength) { while (true) { - const ch = $makeGetValue('str', 0, 'u8') as number + const ch = makeGetValue('str', 0, 'u8') as number if (!ch) break latin1String += String.fromCharCode(ch) str++ } } else { while (len < sizeLength) { - const ch = $makeGetValue('str', 0, 'u8') as number + const ch = makeGetValue('str', 0, 'u8') as number if (!ch) break latin1String += String.fromCharCode(ch) len++ @@ -180,14 +181,14 @@ export function napi_create_bigint_int64 (env: napi_env, low: int32_t, high: int value = low as unknown as BigInt // eslint-disable-next-line @typescript-eslint/no-unused-vars const v1 = emnapiCtx.addToCurrentScope(value).id - $from64('high') - $makeSetValue('high', 0, 'v1', '*') + from64('high') + makeSetValue('high', 0, 'v1', '*') // #else if (!result) return envObject.setLastError(napi_status.napi_invalid_arg) value = BigInt(low >>> 0) | (BigInt(high) << BigInt(32)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const v2 = emnapiCtx.addToCurrentScope(value).id - $makeSetValue('result', 0, 'v2', '*') + makeSetValue('result', 0, 'v2', '*') // #endif return envObject.clearLastError() @@ -209,14 +210,14 @@ export function napi_create_bigint_uint64 (env: napi_env, low: int32_t, high: in value = low as unknown as BigInt // eslint-disable-next-line @typescript-eslint/no-unused-vars const v1 = emnapiCtx.addToCurrentScope(value).id - $from64('high') - $makeSetValue('high', 0, 'v1', '*') + from64('high') + makeSetValue('high', 0, 'v1', '*') // #else if (!result) return envObject.setLastError(napi_status.napi_invalid_arg) value = BigInt(low >>> 0) | (BigInt(high >>> 0) << BigInt(32)) // eslint-disable-next-line @typescript-eslint/no-unused-vars const v2 = emnapiCtx.addToCurrentScope(value).id - $makeSetValue('result', 0, 'v2', '*') + makeSetValue('result', 0, 'v2', '*') // #endif return envObject.clearLastError() @@ -234,8 +235,8 @@ export function napi_create_bigint_words (env: napi_env, sign_bit: int, word_cou return envObject.setLastError(napi_status.napi_generic_failure) } $CHECK_ARG!(envObject, result) - $from64('words') - $from64('word_count') + from64('words') + from64('word_count') word_count = word_count >>> 0 if (word_count > 2147483647) { return envObject.setLastError(napi_status.napi_invalid_arg) @@ -245,15 +246,15 @@ export function napi_create_bigint_words (env: napi_env, sign_bit: int, word_cou } let value: bigint = BigInt(0) for (i = 0; i < word_count; i++) { - const low = $makeGetValue('words', 'i * 8', 'u32') - const high = $makeGetValue('words', 'i * 8 + 4', 'u32') + const low = makeGetValue('words', 'i * 8', 'u32') + const high = makeGetValue('words', 'i * 8 + 4', 'u32') const wordi = BigInt(low) | (BigInt(high) << BigInt(32)) value += wordi << BigInt(64 * i) } value *= ((BigInt(sign_bit) % BigInt(2) === BigInt(0)) ? BigInt(1) : BigInt(-1)) - $from64('result') + from64('result') v = emnapiCtx.addToCurrentScope(value).id - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') return envObject.getReturnStatus() }) } diff --git a/packages/emnapi/src/value/create.ts b/packages/emnapi/src/value/create.ts index 810cdd1d..d3e6bd04 100644 --- a/packages/emnapi/src/value/create.ts +++ b/packages/emnapi/src/value/create.ts @@ -1,5 +1,6 @@ import { emnapiCtx } from 'emnapi:shared' -import { wasmMemory, _malloc } from 'emnapi:emscripten-runtime' +import { wasmMemory, _malloc } from 'emscripten:runtime' +import { from64, makeSetValue, to64 } from 'emscripten:parse-tools' import { emnapiString } from '../string' import { type MemoryViewDescriptor, emnapiExternalMemory } from '../memory' import { emnapi_create_memory_view } from '../emnapi' @@ -12,10 +13,10 @@ import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from '../macro' export function napi_create_array (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope([]).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -25,25 +26,25 @@ export function napi_create_array (env: napi_env, result: Pointer): export function napi_create_array_with_length (env: napi_env, length: size_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('length') - $from64('result') + from64('length') + from64('result') length = length >>> 0 // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(new Array(length)).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } function emnapiCreateArrayBuffer (byte_length: size_t, data: void_pp): ArrayBuffer { - $from64('byte_length') + from64('byte_length') byte_length = byte_length >>> 0 const arrayBuffer = new ArrayBuffer(byte_length) if (data) { - $from64('data') + from64('data') // eslint-disable-next-line @typescript-eslint/no-unused-vars const p = emnapiExternalMemory.getArrayBufferPointer(arrayBuffer, true).address - $makeSetValue('data', 0, 'p', '*') + makeSetValue('data', 0, 'p', '*') } return arrayBuffer @@ -58,10 +59,10 @@ export function napi_create_arraybuffer (env: napi_env, byte_length: size_t, dat return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') const arrayBuffer = emnapiCreateArrayBuffer(byte_length, data) value = emnapiCtx.addToCurrentScope(arrayBuffer).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -75,10 +76,10 @@ export function napi_create_date (env: napi_env, time: double, result: Pointer { $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = emnapiCtx.addToCurrentScope(new Date(time)).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -99,9 +100,9 @@ export function napi_create_external (env: napi_env, data: void_p, finalize_cb: if (finalize_cb) { emnapiCtx.createReference(envObject, externalHandle.id, 0, Ownership.kRuntime as any, finalize_cb, data, finalize_hint) } - $from64('result') + from64('result') value = externalHandle.id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() }) } @@ -122,9 +123,9 @@ export function napi_create_external_arraybuffer ( return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, result) - $from64('byte_length') - $from64('external_data') - $from64('result') + from64('byte_length') + from64('external_data') + from64('result') byte_length = byte_length >>> 0 @@ -166,7 +167,7 @@ export function napi_create_external_arraybuffer ( } } value = handle.id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -177,10 +178,10 @@ export function napi_create_external_arraybuffer ( export function napi_create_object (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope({}).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -190,12 +191,12 @@ export function napi_create_object (env: napi_env, result: Pointer): export function napi_create_symbol (env: napi_env, description: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') if (!description) { // eslint-disable-next-line symbol-description, @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(Symbol()).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') } else { const handle = emnapiCtx.handleStore.get(description)! const desc = handle.value @@ -204,7 +205,7 @@ export function napi_create_symbol (env: napi_env, description: napi_value, resu } // eslint-disable-next-line @typescript-eslint/no-unused-vars const v = emnapiCtx.addToCurrentScope(Symbol(desc)).id - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') } return envObject.clearLastError() } @@ -233,8 +234,8 @@ export function napi_create_typedarray ( return envObject.setLastError(napi_status.napi_invalid_arg) } - $from64('byte_offset') - $from64('length') + from64('byte_offset') + from64('length') const createTypedArray = function (envObject: Env, Type: { new (...args: any[]): ArrayBufferView; name?: string }, size_of_element: number, buffer: ArrayBuffer, byte_offset: size_t, length: size_t): napi_status { byte_offset = byte_offset >>> 0 @@ -267,11 +268,11 @@ export function napi_create_typedarray ( } } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = emnapiCtx.addToCurrentScope(out).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() } @@ -324,17 +325,17 @@ export function napi_create_buffer ( if (!Buffer) { throw emnapiCtx.createNotSupportBufferError('napi_create_buffer', '') } - $from64('result') + from64('result') let buffer: Uint8Array - $from64('size') + from64('size') size = size >>> 0 if (!data || (size === 0)) { buffer = Buffer.alloc(size) value = emnapiCtx.addToCurrentScope(buffer).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') } else { - pointer = _malloc($to64('size')) + pointer = _malloc(to64('size')) if (!pointer) throw new Error('Out of memory') new Uint8Array(wasmMemory.buffer).subarray(pointer, pointer + size).fill(0) const buffer = Buffer.from(wasmMemory.buffer, pointer, size) @@ -349,9 +350,9 @@ export function napi_create_buffer ( emnapiExternalMemory.registry?.register(viewDescriptor, pointer) value = emnapiCtx.addToCurrentScope(buffer).id - $makeSetValue('result', 0, 'value', '*') - $from64('data') - $makeSetValue('data', 0, 'pointer', '*') + makeSetValue('result', 0, 'value', '*') + from64('data') + makeSetValue('data', 0, 'pointer', '*') } return envObject.getReturnStatus() @@ -379,12 +380,12 @@ export function napi_create_buffer_copy ( } const arrayBuffer = emnapiCreateArrayBuffer(length, result_data) const buffer = Buffer.from(arrayBuffer) - $from64('data') - $from64('length') + from64('data') + from64('length') buffer.set(new Uint8Array(wasmMemory.buffer).subarray(data, data + length)) value = emnapiCtx.addToCurrentScope(buffer).id - $from64('result') - $makeSetValue('result', 0, 'value', '*') + from64('result') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -427,8 +428,8 @@ export function napi_create_dataview ( return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, arraybuffer) $CHECK_ARG!(envObject, result) - $from64('byte_length') - $from64('byte_offset') + from64('byte_length') + from64('byte_offset') byte_length = byte_length >>> 0 byte_offset = byte_offset >>> 0 const handle = emnapiCtx.handleStore.get(arraybuffer)! @@ -455,11 +456,11 @@ export function napi_create_dataview ( }) } } - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars value = emnapiCtx.addToCurrentScope(dataview).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.getReturnStatus() }) } @@ -470,9 +471,9 @@ export function napi_create_dataview ( export function node_api_symbol_for (env: napi_env, utf8description: const_char_p, length: size_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('length') - $from64('utf8description') - $from64('result') + from64('length') + from64('utf8description') + from64('result') const autoLength = length === -1 const sizelength = length >>> 0 @@ -488,6 +489,6 @@ export function node_api_symbol_for (env: napi_env, utf8description: const_char_ // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = emnapiCtx.addToCurrentScope(Symbol.for(descriptionString)).id - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } diff --git a/packages/emnapi/src/value/global.ts b/packages/emnapi/src/value/global.ts index 236d7153..ad32df77 100644 --- a/packages/emnapi/src/value/global.ts +++ b/packages/emnapi/src/value/global.ts @@ -1,15 +1,16 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars import { emnapiCtx } from 'emnapi:shared' +import { from64, makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG } from '../macro' /** @__sig ipip */ export function napi_get_boolean (env: napi_env, value: bool, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const v = value === 0 ? GlobalHandle.FALSE : GlobalHandle.TRUE - $makeSetValue('result', 0, 'v', '*') + makeSetValue('result', 0, 'v', '*') return envObject.clearLastError() } @@ -17,10 +18,10 @@ export function napi_get_boolean (env: napi_env, value: bool, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = GlobalHandle.GLOBAL - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -28,10 +29,10 @@ export function napi_get_global (env: napi_env, result: Pointer): na export function napi_get_null (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = GlobalHandle.NULL - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } @@ -39,9 +40,9 @@ export function napi_get_null (env: napi_env, result: Pointer): napi export function napi_get_undefined (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const value = GlobalHandle.UNDEFINED - $makeSetValue('result', 0, 'value', '*') + makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } diff --git a/packages/emnapi/src/version.ts b/packages/emnapi/src/version.ts index 4fbd9679..a0ebe455 100644 --- a/packages/emnapi/src/version.ts +++ b/packages/emnapi/src/version.ts @@ -1,4 +1,5 @@ import { emnapiCtx } from 'emnapi:shared' +import { makeSetValue } from 'emscripten:parse-tools' import { $CHECK_ENV, $CHECK_ARG } from './macro' /** @__sig ipp */ @@ -8,6 +9,6 @@ export function napi_get_version (env: napi_env, result: Pointer): nap $CHECK_ARG!(envObject, result) // eslint-disable-next-line @typescript-eslint/no-unused-vars const NODE_API_SUPPORTED_VERSION_MAX = Version.NODE_API_SUPPORTED_VERSION_MAX - $makeSetValue('result', 0, 'NODE_API_SUPPORTED_VERSION_MAX', 'u32') + makeSetValue('result', 0, 'NODE_API_SUPPORTED_VERSION_MAX', 'u32') return envObject.clearLastError() } diff --git a/packages/emnapi/src/wrap.ts b/packages/emnapi/src/wrap.ts index 31c2fb0d..a28a38b7 100644 --- a/packages/emnapi/src/wrap.ts +++ b/packages/emnapi/src/wrap.ts @@ -1,5 +1,6 @@ import { emnapiCtx } from 'emnapi:shared' -import { wasmMemory } from 'emnapi:emscripten-runtime' +import { wasmMemory } from 'emscripten:runtime' +import { from64, POINTER_SIZE, makeGetValue, POINTER_WASM_TYPE, makeSetValue } from 'emscripten:parse-tools' import { emnapiString } from './string' import { emnapiCreateFunction, emnapiDefineProperty, emnapiWrap, emnapiUnwrap, emnapiGetHandle } from './internal' import { $CHECK_ARG, $CHECK_ENV, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from './macro' @@ -23,9 +24,9 @@ export function napi_define_class ( return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, result) $CHECK_ARG!(envObject, constructor) - $from64('length') - $from64('properties') - $from64('property_count') + from64('length') + from64('properties') + from64('property_count') property_count = property_count >>> 0 @@ -43,16 +44,16 @@ export function napi_define_class ( for (let i = 0; i < property_count; i++) { // eslint-disable-next-line @typescript-eslint/no-unused-vars - propPtr = properties + (i * ($POINTER_SIZE * 8)) - const utf8Name = $makeGetValue('propPtr', 0, '*') - const name = $makeGetValue('propPtr', POINTER_SIZE, '*') - const method = $makeGetValue('propPtr', POINTER_SIZE * 2, '*') - const getter = $makeGetValue('propPtr', POINTER_SIZE * 3, '*') - const setter = $makeGetValue('propPtr', POINTER_SIZE * 4, '*') - const value = $makeGetValue('propPtr', POINTER_SIZE * 5, '*') - attributes = $makeGetValue('propPtr', POINTER_SIZE * 6, POINTER_WASM_TYPE) as number - $from64('attributes') - const data = $makeGetValue('propPtr', POINTER_SIZE * 7, '*') + propPtr = properties + (i * (POINTER_SIZE * 8)) + const utf8Name = makeGetValue('propPtr', 0, '*') + const name = makeGetValue('propPtr', POINTER_SIZE, '*') + const method = makeGetValue('propPtr', POINTER_SIZE * 2, '*') + const getter = makeGetValue('propPtr', POINTER_SIZE * 3, '*') + const setter = makeGetValue('propPtr', POINTER_SIZE * 4, '*') + const value = makeGetValue('propPtr', POINTER_SIZE * 5, '*') + attributes = makeGetValue('propPtr', POINTER_SIZE * 6, POINTER_WASM_TYPE) as number + from64('attributes') + const data = makeGetValue('propPtr', POINTER_SIZE * 7, '*') if (utf8Name) { propertyName = emnapiString.UTF8ToString(utf8Name, -1) @@ -76,8 +77,8 @@ export function napi_define_class ( // eslint-disable-next-line @typescript-eslint/no-unused-vars const valueHandle = emnapiCtx.addToCurrentScope(F) valueHandleId = valueHandle.id - $from64('result') - $makeSetValue('result', 0, 'valueHandleId', '*') + from64('result') + makeSetValue('result', 0, 'valueHandleId', '*') return envObject.getReturnStatus() }) } @@ -115,7 +116,7 @@ export function napi_type_tag_object (env: napi_env, object: napi_value, type_ta if (!(value.isObject() || value.isFunction())) { return envObject.setLastError(envObject.tryCatch.hasCaught() ? napi_status.napi_pending_exception : napi_status.napi_object_expected) } - $from64('type_tag') + from64('type_tag') if (!type_tag) { return envObject.setLastError(envObject.tryCatch.hasCaught() ? napi_status.napi_pending_exception : napi_status.napi_invalid_arg) } @@ -154,7 +155,7 @@ export function napi_check_object_type_tag (env: napi_env, object: napi_value, t } const binding = envObject.getObjectBinding(value.value) if (binding.tag !== null) { - $from64('type_tag') + from64('type_tag') const tag = binding.tag const typeTag = new Uint32Array(wasmMemory.buffer, type_tag, 4) ret = ( @@ -167,8 +168,8 @@ export function napi_check_object_type_tag (env: napi_env, object: napi_value, t ret = false } - $from64('result') - $makeSetValue('result', 0, 'ret ? 1 : 0', 'i8') + from64('result') + makeSetValue('result', 0, 'ret ? 1 : 0', 'i8') return envObject.getReturnStatus() }) @@ -194,15 +195,15 @@ export function napi_add_finalizer (env: napi_env, js_object: napi_value, finali const handle = handleResult.handle! const ownership: Ownership = !result ? Ownership.kRuntime : Ownership.kUserland - $from64('finalize_data') - $from64('finalize_cb') - $from64('finalize_hint') + from64('finalize_data') + from64('finalize_cb') + from64('finalize_hint') const reference = emnapiCtx.createReference(envObject, handle.id, 0, ownership as any, finalize_cb, finalize_data, finalize_hint) if (result) { - $from64('result') + from64('result') // eslint-disable-next-line @typescript-eslint/no-unused-vars const referenceId = reference.id - $makeSetValue('result', 0, 'referenceId', '*') + makeSetValue('result', 0, 'referenceId', '*') } return envObject.clearLastError() diff --git a/packages/rollup-plugin-emscripten-esm-library/README.md b/packages/rollup-plugin-emscripten-esm-library/README.md index ca439473..568b0606 100644 --- a/packages/rollup-plugin-emscripten-esm-library/README.md +++ b/packages/rollup-plugin-emscripten-esm-library/README.md @@ -27,7 +27,16 @@ export default defineConfig({ exportedRuntimeMethods: [ /* ... */ ], /** @type {boolean} */ - processDirective: true + processDirective: true, + + /** @type {boolean} */ + processParseTools: true + + /** @type {string} */ + runtimeModuleSpecifier: 'emscripten:runtime', + + /** @type {string} */ + parseToolsModuleSpecifier: 'emscripten:parse-tools' }) ] }) diff --git a/packages/rollup-plugin-emscripten-esm-library/src/index.mts b/packages/rollup-plugin-emscripten-esm-library/src/index.mts index c725a354..d0f96ec1 100644 --- a/packages/rollup-plugin-emscripten-esm-library/src/index.mts +++ b/packages/rollup-plugin-emscripten-esm-library/src/index.mts @@ -1,16 +1,59 @@ -import type { Plugin } from 'rollup' -import { transformWithOptions, type TransformOptions } from '@emnapi/ts-transform-emscripten-esm-library' +import type { Plugin, ExternalOption } from 'rollup' +import { transformWithOptions, getDefaultOptions as getDefaultOptionsBase, type TransformOptions } from '@emnapi/ts-transform-emscripten-esm-library' export interface PluginOptions extends TransformOptions { + /** @default _ => _ */ modifyOutput?: (output: string) => string } +export function getDefaultOptions (options?: PluginOptions): Required { + return { + ...getDefaultOptionsBase(options), + modifyOutput: options?.modifyOutput ?? ((_) => _) + } +} + export default function (options?: PluginOptions): Plugin { - function defaultModify (_: T): T { return _ } - const modifyOutput = options?.modifyOutput ?? defaultModify + const { + modifyOutput, + runtimeModuleSpecifier, + parseToolsModuleSpecifier + } = getDefaultOptions(options) + + const addExternal = (originalExternal: ExternalOption | undefined, sp: string): ExternalOption => { + if (originalExternal == null) { + return [sp] + } else if (typeof originalExternal === 'string' || originalExternal instanceof RegExp) { + return [originalExternal, sp] + } else if (Array.isArray(originalExternal)) { + return [...originalExternal, sp] + } else if (typeof originalExternal === 'function') { + return ((id, ...args) => { + if (id === sp) { + return true + } + return originalExternal(id, ...args) + }) satisfies ExternalOption + } + throw new TypeError('Invalid external option') + } return { name: '@emnapi/rollup-plugin-emscripten-esm-library', + options (options) { + let external = options.external + if (runtimeModuleSpecifier) { + external = addExternal(external, runtimeModuleSpecifier) + } + if (parseToolsModuleSpecifier) { + external = addExternal(external, parseToolsModuleSpecifier) + } + + return { + ...options, + external + } + }, renderChunk: function (code, chunk) { return modifyOutput(transformWithOptions(chunk.fileName, code, options)) } diff --git a/packages/rollup-plugin-emscripten-esm-library/src/index.ts b/packages/rollup-plugin-emscripten-esm-library/src/index.ts index c725a354..d0f96ec1 100644 --- a/packages/rollup-plugin-emscripten-esm-library/src/index.ts +++ b/packages/rollup-plugin-emscripten-esm-library/src/index.ts @@ -1,16 +1,59 @@ -import type { Plugin } from 'rollup' -import { transformWithOptions, type TransformOptions } from '@emnapi/ts-transform-emscripten-esm-library' +import type { Plugin, ExternalOption } from 'rollup' +import { transformWithOptions, getDefaultOptions as getDefaultOptionsBase, type TransformOptions } from '@emnapi/ts-transform-emscripten-esm-library' export interface PluginOptions extends TransformOptions { + /** @default _ => _ */ modifyOutput?: (output: string) => string } +export function getDefaultOptions (options?: PluginOptions): Required { + return { + ...getDefaultOptionsBase(options), + modifyOutput: options?.modifyOutput ?? ((_) => _) + } +} + export default function (options?: PluginOptions): Plugin { - function defaultModify (_: T): T { return _ } - const modifyOutput = options?.modifyOutput ?? defaultModify + const { + modifyOutput, + runtimeModuleSpecifier, + parseToolsModuleSpecifier + } = getDefaultOptions(options) + + const addExternal = (originalExternal: ExternalOption | undefined, sp: string): ExternalOption => { + if (originalExternal == null) { + return [sp] + } else if (typeof originalExternal === 'string' || originalExternal instanceof RegExp) { + return [originalExternal, sp] + } else if (Array.isArray(originalExternal)) { + return [...originalExternal, sp] + } else if (typeof originalExternal === 'function') { + return ((id, ...args) => { + if (id === sp) { + return true + } + return originalExternal(id, ...args) + }) satisfies ExternalOption + } + throw new TypeError('Invalid external option') + } return { name: '@emnapi/rollup-plugin-emscripten-esm-library', + options (options) { + let external = options.external + if (runtimeModuleSpecifier) { + external = addExternal(external, runtimeModuleSpecifier) + } + if (parseToolsModuleSpecifier) { + external = addExternal(external, parseToolsModuleSpecifier) + } + + return { + ...options, + external + } + }, renderChunk: function (code, chunk) { return modifyOutput(transformWithOptions(chunk.fileName, code, options)) } diff --git a/packages/ts-transform-emscripten-esm-library/README.md b/packages/ts-transform-emscripten-esm-library/README.md index d9f6c1a4..f4f367ce 100644 --- a/packages/ts-transform-emscripten-esm-library/README.md +++ b/packages/ts-transform-emscripten-esm-library/README.md @@ -19,7 +19,16 @@ const output = transformWithOptions( exportedRuntimeMethods: [ /* ... */ ], /** @type {boolean} */ - processDirective: true + processDirective: true, + + /** @type {boolean} */ + processParseTools: true, + + /** @type {string} */ + runtimeModuleSpecifier: 'emscripten:runtime', + + /** @type {string} */ + parseToolsModuleSpecifier: 'emscripten:parse-tools' } ) ``` @@ -45,7 +54,11 @@ export function x () { function _x() { return 42 } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ x: _x }) ``` @@ -69,7 +82,11 @@ export function $x () { function x() { return 42 } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ $x: x }) ``` @@ -96,7 +113,11 @@ const _x = getX() function getX() { return 42; } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ $getX: getX, x: "getX()", x__deps: ["$getX"] @@ -139,7 +160,11 @@ function _exportedFunc() { f() console.log(_exportedVar) } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ exportedVar: "10", $f: f, $f__deps: ["exportedVar"], @@ -204,16 +229,110 @@ function _y() { _emscripten_resize_heap() return _x() } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ x: _x, x__deps: ["$external"], y: _y, - y__deps: ["x", "emscripten_resize_heap", "$runtimeKeepalivePush", "$runtimeKeepalivePop"], + y__deps: [ + "x", + "emscripten_resize_heap", + "$runtimeKeepalivePush", + "$runtimeKeepalivePop" + ], y__sig: "v", y__postset: "console.log(42);\nconsole.log(_y);" }) ``` + + + + + + +```js +export function getPointerSize () { + let result + // #if MEMORY64 + result = 8 + // #else + result = 4 + // #endif + return result +} +``` + + + + +```js +function _getPointerSize() { + let result +#if MEMORY64 + result = 8 +#else + result = 4 +#endif + return result +} +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ + getPointerSize: _getPointerSize +}) +``` + + + + + + + +```js +import { + from64, + makeSetValue, + SIZE_TYPE, + POINTER_SIZE +} from 'emscripten:parse-tools' +import { + wasmMemory, + HEAPU8 +} from 'emscripten:runtime' + +export function getPointerSize (ret) { + from64('ret') + makeSetValue('ret', 0, POINTER_SIZE, SIZE_TYPE) + console.log(HEAPU8.buffer === wasmMemory.buffer) + return POINTER_SIZE +} +``` + + + + +```js +function _getPointerSize(ret) { + {{{ from64('ret') }}} + {{{ makeSetValue('ret', 0, POINTER_SIZE, SIZE_TYPE) }}} + console.log(HEAPU8.buffer === wasmMemory.buffer) + return {{{ POINTER_SIZE }}} +} +(typeof addToLibrary === "function" + ? addToLibrary : + (...args) => mergeInto( + LibraryManager.library, ...args) +)({ + getPointerSize: _getPointerSize +}); +``` + diff --git a/packages/ts-transform-emscripten-esm-library/src/index.ts b/packages/ts-transform-emscripten-esm-library/src/index.ts index 5c71212f..dc411eb6 100644 --- a/packages/ts-transform-emscripten-esm-library/src/index.ts +++ b/packages/ts-transform-emscripten-esm-library/src/index.ts @@ -10,13 +10,22 @@ import type { Visitor, Symbol, JSDocTagInfo, - ExpressionStatement + ExpressionStatement, + ObjectLiteralExpression } from 'typescript' import { EOL } from 'os' import ts = require('typescript') +export interface BaseTransformOptions { + /** @default 'emscripten:runtime' */ + runtimeModuleSpecifier?: string + + /** @default 'emscripten:parse-tools' */ + parseToolsModuleSpecifier?: string +} + interface SymbolWrap/* */ { // decl: T deps: Set @@ -29,17 +38,26 @@ class Transformer { exported: Set = new Set() wrap: WeakMap = new WeakMap() - constructor (public ctx: TransformationContext, public program: Program) { + private readonly _runtimeModuleSpecifier: string + private readonly _parseToolsModuleSpecifier: string + + constructor (public ctx: TransformationContext, public program: Program, options?: BaseTransformOptions) { + const { runtimeModuleSpecifier, parseToolsModuleSpecifier } = getDefaultBaseOptions(options) + this._runtimeModuleSpecifier = runtimeModuleSpecifier + this._parseToolsModuleSpecifier = parseToolsModuleSpecifier + this.transform = this.transform.bind(this) + this.parseToolsVisitor = this.parseToolsVisitor.bind(this) this.finalVisitor = this.finalVisitor.bind(this) this.collectExportedFunctionVisitor = this.collectExportedFunctionVisitor.bind(this) } transform (source: SourceFile): SourceFile { + source = ts.visitNode(source, this.parseToolsVisitor) as SourceFile ts.visitNode(source, this.collectExportedFunctionVisitor) as SourceFile const result = ts.visitEachChild(source, this.finalVisitor, this.ctx) - const statements = [ + let statements = [ ...result.statements.filter(s => !ts.isExportDeclaration(s)).map(s => { if (ts.isFunctionDeclaration(s)) { return this.ctx.factory.updateFunctionDeclaration(s, @@ -62,9 +80,51 @@ class Transformer { }), this.createMergeExported() ] + + if (this._runtimeModuleSpecifier) { + statements = statements.filter(s => { + return !(ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && (s.moduleSpecifier.text === this._runtimeModuleSpecifier)) + }) + } + if (this._parseToolsModuleSpecifier) { + statements = statements.filter(s => { + return !(ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && (s.moduleSpecifier.text === this._parseToolsModuleSpecifier)) + }) + } return this.ctx.factory.updateSourceFile(result, statements) } + parseToolsVisitor (node: Node): VisitResult { + if (this._parseToolsModuleSpecifier) { + const typeChecker = this.program.getTypeChecker() + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { + const d = typeChecker.getSymbolAtLocation(node.expression)?.declarations?.[0] + if (d && ts.isImportSpecifier(d)) { + const moduleSpecifier = d.parent.parent.parent.moduleSpecifier + if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === this._parseToolsModuleSpecifier) { + return ts.addSyntheticTrailingComment(ts.addSyntheticLeadingComment(node, + ts.SyntaxKind.MultiLineCommentTrivia, ' {{{ ', false + ), ts.SyntaxKind.MultiLineCommentTrivia, ' }}} ', false) + } + } + } + if (ts.isIdentifier(node) && !ts.isImportSpecifier(node.parent) && !ts.isImportClause(node.parent) && !ts.isImportEqualsDeclaration(node.parent) && !ts.isImportTypeNode(node.parent)) { + const d = typeChecker.getSymbolAtLocation(node)?.declarations?.[0] + if (d && ts.isImportSpecifier(d)) { + const moduleSpecifier = d.parent.parent.parent.moduleSpecifier + if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === this._parseToolsModuleSpecifier) { + return ts.addSyntheticTrailingComment(ts.addSyntheticLeadingComment(node, + ts.SyntaxKind.MultiLineCommentTrivia, ' {{{ ', false + ), ts.SyntaxKind.MultiLineCommentTrivia, ' }}} ', false) + } + } + } + return ts.visitEachChild(node, this.parseToolsVisitor, this.ctx) + } + + return node + } + finalVisitor (node: Node): VisitResult { if ((ts.isFunctionDeclaration(node) || ts.isVariableDeclaration(node)) && this.exported.has(node)) { const wrap = this.wrap.get(node)! @@ -194,108 +254,153 @@ class Transformer { this.trackDeps(decl, this.tryGetExportSymbol(exportedSymbols, sym), parent, exportedSymbols) } - createMergeExported (): ExpressionStatement { + createMergeExpressionStatement (objectLiteral: ObjectLiteralExpression): ExpressionStatement { const factory = this.ctx.factory return factory.createExpressionStatement(factory.createCallExpression( - factory.createIdentifier('mergeInto'), - undefined, - [ - factory.createPropertyAccessExpression( - factory.createIdentifier('LibraryManager'), - factory.createIdentifier('library') + factory.createParenthesizedExpression(factory.createConditionalExpression( + factory.createBinaryExpression( + factory.createTypeOfExpression(factory.createIdentifier('addToLibrary')), + factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), + factory.createStringLiteral('function') ), - factory.createObjectLiteralExpression( - [...this.exported].map(decl => { - const wrap = this.wrap.get(decl)! - const name = wrap.exported ? wrap.exported.escapedName as string : decl.name!.getText() - - // console.log(JSON.stringify(wrap.jsdocTags, null, 2)) - const map = new Map() - if (wrap.jsdocTags) { - const emscriptenModifiers = wrap.jsdocTags.filter(info => Boolean(info.name.startsWith('__') && info.name !== '__deps' && info.text?.[0]?.text)) - .map(info => { - let text = info.text![0].text - if (text.startsWith('```')) { - text = text.match(/^```(\r?\n)*([\s\S]*?)(\r?\n)*```$/)?.[2] ?? text - } else if (text.startsWith('`')) { - text = text.match(/^`([\s\S]*?)`$/)?.[1] ?? text - } else if (text.startsWith('{')) { - text = text.match(/^\{([\s\S]*?)\}$/)?.[1] ?? text - } - return { - key: info.name, - value: text - } - }) - for (let i = 0; i < emscriptenModifiers.length; ++i) { - const pair = emscriptenModifiers[i] - if (map.has(pair.key)) { - map.get(pair.key)!.push(pair.value) - } else { - map.set(pair.key, [pair.value]) - } - } - } - - const depsInTags = wrap.jsdocTags?.filter(info => Boolean((info.name === '__deps' || info.name === 'deps') && info.text?.[0]?.text)) ?? [] - - return [ - factory.createPropertyAssignment( - wrap.exported ? factory.createIdentifier(name) : factory.createIdentifier('$' + name), - wrap.exported - ? ts.isFunctionDeclaration(decl) - ? name.startsWith('$') ? factory.createIdentifier(name.substring(1)) : factory.createIdentifier('_' + name) - : decl.initializer - ? ts.isFunctionExpression(decl.initializer) || ts.isArrowFunction(decl.initializer) || ts.isObjectLiteralExpression(decl.initializer) || ts.isArrayLiteralExpression(decl.initializer) - ? name.startsWith('$') ? factory.createIdentifier(name.substring(1)) : factory.createIdentifier('_' + name) - : factory.createStringLiteral(decl.initializer.getText()) - : factory.createIdentifier('undefined') - : ts.isFunctionDeclaration(decl) - ? factory.createIdentifier(name) - : decl.initializer - ? ts.isFunctionExpression(decl.initializer) || ts.isArrowFunction(decl.initializer) || ts.isObjectLiteralExpression(decl.initializer) || ts.isArrayLiteralExpression(decl.initializer) - ? factory.createIdentifier(name) - : factory.createStringLiteral(decl.initializer.getText()) - : factory.createIdentifier('undefined') + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createIdentifier('addToLibrary'), + factory.createToken(ts.SyntaxKind.ColonToken), + factory.createArrowFunction( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + factory.createToken(ts.SyntaxKind.DotDotDotToken), + factory.createIdentifier('args'), + undefined, + undefined, + undefined + )], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + factory.createCallExpression( + factory.createIdentifier('mergeInto'), + undefined, + [ + factory.createPropertyAccessExpression( + factory.createIdentifier('LibraryManager'), + factory.createIdentifier('library') ), - ...((wrap.deps.size || depsInTags.length) - ? [ - factory.createPropertyAssignment( - factory.createIdentifier((wrap.exported ? name : ('$' + name)) + '__deps'), - factory.createArrayLiteralExpression([ - ...[...wrap.deps].map(d => { - const w = this.wrap.get(d)! - const name = w.exported ? w.exported.escapedName as string : d.name!.getText() - return factory.createStringLiteral(w.exported ? name : `$${name as string}`) - }), - ...(depsInTags.map(info => { - return factory.createStringLiteral(info.text![0].text) - })) - ], false) - ) - ] - : []), - ...([...map.entries()].map(([m, value]) => { - return factory.createPropertyAssignment( - factory.createIdentifier((wrap.exported ? name : ('$' + name)) + m), - value.length > 1 - ? factory.createArrayLiteralExpression(value.map(v => factory.createStringLiteral(v)), false) - : factory.createStringLiteral(value[0]) - ) - })) + factory.createSpreadElement(factory.createIdentifier('args')) ] - }).flat(), - true + ) ) - ] + )), + undefined, + [objectLiteral] )) } + createMergeExported (): ExpressionStatement { + const factory = this.ctx.factory + return this.createMergeExpressionStatement( + factory.createObjectLiteralExpression( + [...this.exported].map(decl => { + const wrap = this.wrap.get(decl)! + const name = wrap.exported ? wrap.exported.escapedName as string : decl.name!.getText() + + // console.log(JSON.stringify(wrap.jsdocTags, null, 2)) + const map = new Map() + if (wrap.jsdocTags) { + const emscriptenModifiers = wrap.jsdocTags.filter(info => Boolean(info.name.startsWith('__') && info.name !== '__deps' && info.text?.[0]?.text)) + .map(info => { + let text = info.text![0].text + if (text.startsWith('```')) { + text = text.match(/^```(\r?\n)*([\s\S]*?)(\r?\n)*```$/)?.[2] ?? text + } else if (text.startsWith('`')) { + text = text.match(/^`([\s\S]*?)`$/)?.[1] ?? text + } else if (text.startsWith('{')) { + text = text.match(/^\{([\s\S]*?)\}$/)?.[1] ?? text + } + return { + key: info.name, + value: text + } + }) + for (let i = 0; i < emscriptenModifiers.length; ++i) { + const pair = emscriptenModifiers[i] + if (map.has(pair.key)) { + map.get(pair.key)!.push(pair.value) + } else { + map.set(pair.key, [pair.value]) + } + } + } + + const depsInTags = wrap.jsdocTags?.filter(info => Boolean((info.name === '__deps' || info.name === 'deps') && info.text?.[0]?.text)) ?? [] + + return [ + factory.createPropertyAssignment( + wrap.exported ? factory.createIdentifier(name) : factory.createIdentifier('$' + name), + wrap.exported + ? ts.isFunctionDeclaration(decl) + ? name.startsWith('$') ? factory.createIdentifier(name.substring(1)) : factory.createIdentifier('_' + name) + : decl.initializer + ? ts.isFunctionExpression(decl.initializer) || ts.isArrowFunction(decl.initializer) || ts.isObjectLiteralExpression(decl.initializer) || ts.isArrayLiteralExpression(decl.initializer) + ? name.startsWith('$') ? factory.createIdentifier(name.substring(1)) : factory.createIdentifier('_' + name) + : factory.createStringLiteral(decl.initializer.getText()) + : factory.createIdentifier('undefined') + : ts.isFunctionDeclaration(decl) + ? factory.createIdentifier(name) + : decl.initializer + ? ts.isFunctionExpression(decl.initializer) || ts.isArrowFunction(decl.initializer) || ts.isObjectLiteralExpression(decl.initializer) || ts.isArrayLiteralExpression(decl.initializer) + ? factory.createIdentifier(name) + : factory.createStringLiteral(decl.initializer.getText()) + : factory.createIdentifier('undefined') + ), + ...((wrap.deps.size || depsInTags.length) + ? [ + factory.createPropertyAssignment( + factory.createIdentifier((wrap.exported ? name : ('$' + name)) + '__deps'), + factory.createArrayLiteralExpression([ + ...[...wrap.deps].map(d => { + const w = this.wrap.get(d)! + const name = w.exported ? w.exported.escapedName as string : d.name!.getText() + return factory.createStringLiteral(w.exported ? name : `$${name as string}`) + }), + ...(depsInTags.map(info => { + return factory.createStringLiteral(info.text![0].text) + })) + ], false) + ) + ] + : []), + ...([...map.entries()].map(([m, value]) => { + return factory.createPropertyAssignment( + factory.createIdentifier((wrap.exported ? name : ('$' + name)) + m), + value.length > 1 + ? factory.createArrayLiteralExpression(value.map(v => factory.createStringLiteral(v)), false) + : factory.createStringLiteral(value[0]) + ) + })) + ] + }).flat(), + true + ) + ) + } + collectExportedFunctionVisitor (source: SourceFile): VisitResult { // disallow import declaration for (let i = 0; i < source.statements.length; ++i) { const stmt = source.statements[i] if (ts.isImportDeclaration(stmt)) { + if (this._runtimeModuleSpecifier) { + if (ts.isStringLiteral(stmt.moduleSpecifier) && stmt.moduleSpecifier.text === this._runtimeModuleSpecifier) { + continue + } + } + if (this._parseToolsModuleSpecifier) { + if (ts.isStringLiteral(stmt.moduleSpecifier) && stmt.moduleSpecifier.text === this._parseToolsModuleSpecifier) { + continue + } + } throw new Error('import declaration is not supported: ' + stmt.getText(source)) } } @@ -330,15 +435,15 @@ class Transformer { } } -function createTransformerFactory (program: Program): TransformerFactory { +function createTransformerFactory (program: Program, options?: BaseTransformOptions): TransformerFactory { return (context) => { - const transformer = new Transformer(context, program) + const transformer = new Transformer(context, program, options) return transformer.transform } } -function transform (fileName: string, sourceText: string): string { +function transform (fileName: string, sourceText: string, options?: BaseTransformOptions): string { const compilerOptions = { allowJs: true, module: ts.ModuleKind.ESNext, @@ -354,7 +459,7 @@ function transform (fileName: string, sourceText: string): string { host }) - const transformerFactory = createTransformerFactory(program) + const transformerFactory = createTransformerFactory(program, options) const transformResult = ts.transform(source, [transformerFactory]) const printer = ts.createPrinter({ @@ -363,17 +468,24 @@ function transform (fileName: string, sourceText: string): string { return printer.printNode(ts.EmitHint.SourceFile, transformResult.transformed[0], transformResult.transformed[0]) } -export interface TransformOptions { +export interface TransformOptions extends BaseTransformOptions { + /** @default [] */ defaultLibraryFuncsToInclude?: string[] + /** @default [] */ exportedRuntimeMethods?: string[] + /** @default true */ processDirective?: boolean + /** @default true */ + processParseTools?: boolean } function transformWithOptions (fileName: string, sourceText: string, options?: TransformOptions): string { const defaultLibraryFuncsToInclude = options?.defaultLibraryFuncsToInclude ?? [] const exportedRuntimeMethods = options?.exportedRuntimeMethods ?? [] + const processDirective = options?.processDirective ?? true + const processParseTools = options?.processParseTools ?? true - let result = transform(fileName, sourceText) + let result = transform(fileName, sourceText, options) const prefix = [ ...defaultLibraryFuncsToInclude @@ -382,11 +494,34 @@ function transformWithOptions (fileName: string, sourceText: string, options?: T .map(sym => `{{{ ((EXPORTED_RUNTIME_METHODS.indexOf("${sym}") === -1 ? EXPORTED_RUNTIME_METHODS.push("${sym}") : undefined), "") }}}`) ].join(EOL) - if (options?.processDirective) { + if (processDirective) { result = result.replace(/(\r?\n)\s*\/\/\s+(#((if)|(else)|(elif)|(endif)))/g, '$1$2') } + if (processParseTools) { + result = result + .replace(/\/\* \{\{\{ \*\//g, '{{{') + .replace(/\/\* \}\}\} \*\//g, '}}}') + } + return prefix + (prefix ? EOL : '') + result } -export { createTransformerFactory, transform, transformWithOptions } +function getDefaultBaseOptions (options?: BaseTransformOptions): Required { + return { + runtimeModuleSpecifier: options?.runtimeModuleSpecifier ?? 'emscripten:runtime', + parseToolsModuleSpecifier: options?.parseToolsModuleSpecifier ?? 'emscripten:parse-tools' + } +} + +function getDefaultOptions (options?: TransformOptions): Required { + return { + ...getDefaultBaseOptions(options), + defaultLibraryFuncsToInclude: options?.defaultLibraryFuncsToInclude ?? [], + exportedRuntimeMethods: options?.exportedRuntimeMethods ?? [], + processDirective: options?.processDirective ?? true, + processParseTools: options?.processParseTools ?? true + } +} + +export { createTransformerFactory, transform, transformWithOptions, getDefaultBaseOptions, getDefaultOptions } diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/alias.js b/packages/ts-transform-emscripten-esm-library/test/expected/alias.js index d4a14cca..ceae6c26 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/alias.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/alias.js @@ -6,7 +6,7 @@ function _exportedFunc() { f(); console.log(_exportedVar); } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ exportedVar: "10", $f: f, $f__deps: ["exportedVar"], diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/bundle.js b/packages/ts-transform-emscripten-esm-library/test/expected/bundle.js index a4ea5eac..03f89381 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/bundle.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/bundle.js @@ -3,7 +3,7 @@ function _x() { console.log(fortyTwo); } const _y = () => fortyTwo; -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ $fortyTwo: "42", x: _x, x__deps: ["$fortyTwo"], diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/directives.js b/packages/ts-transform-emscripten-esm-library/test/expected/directives.js new file mode 100644 index 00000000..c8ba06c5 --- /dev/null +++ b/packages/ts-transform-emscripten-esm-library/test/expected/directives.js @@ -0,0 +1,12 @@ +function _getPointerSize() { + let result; +#if MEMORY64 + result = 8; +#else + result = 4; +#endif + return result; +} +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ + getPointerSize: _getPointerSize +}); diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/exported-function-ref.js b/packages/ts-transform-emscripten-esm-library/test/expected/exported-function-ref.js index 2e6a5ed0..4781e77d 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/exported-function-ref.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/exported-function-ref.js @@ -19,7 +19,7 @@ function _y() { _emscripten_resize_heap(); return _x(); } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ x: _x, x__deps: ["$external"], y: _y, diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/exported-function.js b/packages/ts-transform-emscripten-esm-library/test/expected/exported-function.js index 0f858ff7..21db17b8 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/exported-function.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/exported-function.js @@ -1,6 +1,6 @@ function _x() { return 42; } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ x: _x }); diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/exported-var.js b/packages/ts-transform-emscripten-esm-library/test/expected/exported-var.js index 17ee9b0b..22ca6ec7 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/exported-var.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/exported-var.js @@ -2,7 +2,7 @@ const _x = getX(); function getX() { return 42; } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ $getX: getX, x: "getX()", x__deps: ["$getX"] diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/external-variable.js b/packages/ts-transform-emscripten-esm-library/test/expected/external-variable.js index 05bc33e6..e5d7b830 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/external-variable.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/external-variable.js @@ -4,7 +4,7 @@ function _x(param) { const ret = arr; return param + ret; } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ $fortyTwo: "42", $arr: "new Uint8Array(fortyTwo)", $arr__deps: ["$fortyTwo"], diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/internal.js b/packages/ts-transform-emscripten-esm-library/test/expected/internal.js index ba7fca6b..6960f390 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/internal.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/internal.js @@ -9,7 +9,7 @@ function foo() { function _z() { return foo(); } -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ $fortyTwo: "42", $x: x, $x__deps: ["$fortyTwo"], diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/object-literal.js b/packages/ts-transform-emscripten-esm-library/test/expected/object-literal.js index 07ba46d5..9092eaa1 100644 --- a/packages/ts-transform-emscripten-esm-library/test/expected/object-literal.js +++ b/packages/ts-transform-emscripten-esm-library/test/expected/object-literal.js @@ -30,7 +30,7 @@ const _fe = function () { const _af = (param) => { obj.bar(param); }; -mergeInto(LibraryManager.library, { +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ bar: _bar, bar__sig: "ii", $fortyTwo: "42", diff --git a/packages/ts-transform-emscripten-esm-library/test/expected/virtual-modules.js b/packages/ts-transform-emscripten-esm-library/test/expected/virtual-modules.js new file mode 100644 index 00000000..6eb7b19b --- /dev/null +++ b/packages/ts-transform-emscripten-esm-library/test/expected/virtual-modules.js @@ -0,0 +1,9 @@ +function _getPointerSize(ret) { + {{{ from64('ret') }}}; + {{{ makeSetValue('ret', 0, POINTER_SIZE, SIZE_TYPE) }}}; + console.log(HEAPU8.buffer === wasmMemory.buffer); + return {{{ POINTER_SIZE }}}; +} +(typeof addToLibrary === "function" ? addToLibrary : (...args) => mergeInto(LibraryManager.library, ...args))({ + getPointerSize: _getPointerSize +}); diff --git a/packages/ts-transform-emscripten-esm-library/test/index.js b/packages/ts-transform-emscripten-esm-library/test/index.js index 89a3119d..2480561c 100644 --- a/packages/ts-transform-emscripten-esm-library/test/index.js +++ b/packages/ts-transform-emscripten-esm-library/test/index.js @@ -3,7 +3,7 @@ const { strictEqual } = require('assert') const { readFileSync, writeFileSync, mkdirSync } = require('fs') const { join, dirname } = require('path') const { rollup } = require('rollup') -const { transform } = require('..') +const { transformWithOptions: transform } = require('..') function testTransform (file) { const exportedFunction = join(__dirname, `input/${file}.js`) @@ -43,6 +43,14 @@ test('internal', () => { testTransform('internal') }) +test('directives', () => { + testTransform('directives') +}) + +test('virtual modules', () => { + testTransform('virtual-modules') +}) + test('rollup', async () => { const config = (await import('./rollup/rollup.config.mjs')).default const build = await rollup(config) diff --git a/packages/ts-transform-emscripten-esm-library/test/input/directives.js b/packages/ts-transform-emscripten-esm-library/test/input/directives.js new file mode 100644 index 00000000..0cf6e3ea --- /dev/null +++ b/packages/ts-transform-emscripten-esm-library/test/input/directives.js @@ -0,0 +1,9 @@ +export function getPointerSize () { + let result + // #if MEMORY64 + result = 8 + // #else + result = 4 + // #endif + return result +} diff --git a/packages/ts-transform-emscripten-esm-library/test/input/virtual-modules.js b/packages/ts-transform-emscripten-esm-library/test/input/virtual-modules.js new file mode 100644 index 00000000..a9b7ea8d --- /dev/null +++ b/packages/ts-transform-emscripten-esm-library/test/input/virtual-modules.js @@ -0,0 +1,9 @@ +import { from64, makeSetValue, SIZE_TYPE, POINTER_SIZE } from 'emscripten:parse-tools' +import { wasmMemory, HEAPU8 } from 'emscripten:runtime' + +export function getPointerSize (ret) { + from64('ret') + makeSetValue('ret', 0, POINTER_SIZE, SIZE_TYPE) + console.log(HEAPU8.buffer === wasmMemory.buffer) + return POINTER_SIZE +} diff --git a/packages/ts-transform-emscripten-parse-tools/src/index.ts b/packages/ts-transform-emscripten-parse-tools/src/index.ts index bf33e8b0..aebfed54 100644 --- a/packages/ts-transform-emscripten-parse-tools/src/index.ts +++ b/packages/ts-transform-emscripten-parse-tools/src/index.ts @@ -22,17 +22,22 @@ import type { import ts = require('typescript') import { join, resolve } from 'path' +import { getDefaultBaseOptions, type BaseTransformOptions } from '@emnapi/ts-transform-emscripten-esm-library' -export interface DefineOptions { - defines?: { - MEMORY64?: 0 | 1 - } +export type CStyleBoolean = 0 | 1 + +export interface Defines { + MEMORY64?: CStyleBoolean } -function isEmscriptenMacro (text: string): boolean { - return text.length > 1 && text.charAt(0) === '$' && text.charAt(1) !== '$' +export interface TransformOptions extends BaseTransformOptions { + defines?: Defines } +// function isEmscriptenMacro (text: string): boolean { +// return text.length > 1 && text.charAt(0) === '$' && text.charAt(1) !== '$' +// } + function expandFrom64 (factory: NodeFactory, defines: Record, node: ExpressionStatement): Statement | undefined { if (defines.MEMORY64) { const varName = ((node.expression as CallExpression).arguments[0] as StringLiteral).text @@ -127,7 +132,7 @@ function byteOffsetParameter (factory: NodeFactory, defines: Record } throw new Error('byteOffsetParameter unsupport binary expression') } - throw new Error('$makeGetValue unsupported pos') + throw new Error('makeGetValue unsupported pos') } function isFunctionLikeDeclaration (node: Node): node is FunctionLikeDeclaration { @@ -219,9 +224,16 @@ class Transform { insertWasmMemoryImport: boolean insertWasmTableImport: boolean - constructor (context: TransformationContext, defines: Record) { + readonly runtimeModuleSpecifier: string + readonly parseToolsModuleSpecifier: string + + constructor (public program: Program, context: TransformationContext, config?: TransformOptions) { + const { runtimeModuleSpecifier, parseToolsModuleSpecifier } = getDefaultBaseOptions(config) + this.runtimeModuleSpecifier = runtimeModuleSpecifier + this.parseToolsModuleSpecifier = parseToolsModuleSpecifier + this.ctx = context - this.defines = defines + this.defines = config?.defines ?? {} this.visitor = this.visitor.bind(this) this.functionLikeDeclarationVisitor = this.functionLikeDeclarationVisitor.bind(this) this.insertWasmMemoryImport = false @@ -388,37 +400,60 @@ class Transform { return statements } + isEmscriptenMacro (node: Node): boolean { + const typeChecker = this.program.getTypeChecker() + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { + const d = typeChecker.getSymbolAtLocation(node.expression)?.declarations?.[0] + if (d && ts.isImportSpecifier(d)) { + const moduleSpecifier = d.parent.parent.parent.moduleSpecifier + if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === this.parseToolsModuleSpecifier) { + return true + } + } + } + if (ts.isIdentifier(node) && !ts.isImportSpecifier(node.parent) && !ts.isImportClause(node.parent) && !ts.isImportEqualsDeclaration(node.parent) && !ts.isImportTypeNode(node.parent)) { + const d = typeChecker.getSymbolAtLocation(node)?.declarations?.[0] + if (d && ts.isImportSpecifier(d)) { + const moduleSpecifier = d.parent.parent.parent.moduleSpecifier + if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === this.parseToolsModuleSpecifier) { + return true + } + } + } + return false + } + visitor (node: Node): VisitResult { if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression) && - isEmscriptenMacro(node.expression.expression.text as string) + this.isEmscriptenMacro(node.expression.expression) ) { const functionName = node.expression.expression.text - if (functionName === '$from64' && ts.isStringLiteral(node.expression.arguments[0])) { + if (functionName === 'from64' && ts.isStringLiteral(node.expression.arguments[0])) { return expandFrom64(this.ctx.factory, this.defines, node) } return ts.visitEachChild(node, this.visitor, this.ctx) } if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { const functionName = node.expression.text - if (isEmscriptenMacro(functionName)) { - if (functionName === '$to64' && ts.isStringLiteral(node.arguments[0])) { + if (this.isEmscriptenMacro(node.expression)) { + if (functionName === 'to64' && ts.isStringLiteral(node.arguments[0])) { return expandTo64(this.ctx.factory, this.defines, node) } - if (functionName === '$makeGetValue') { + if (functionName === 'makeGetValue') { return this.expandMakeGetValue(node) } - if (functionName === '$makeSetValue') { + if (functionName === 'makeSetValue') { return this.expandMakeSetValue(node) } // if (functionName === '$makeMalloc') { // return this.expandMakeMalloc(node) // } - if (functionName === '$makeDynCall') { + if (functionName === 'makeDynCall') { return this.expandMakeDynCall(node) } - if (functionName === '$getUnsharedTextDecoderView') { + if (functionName === 'getUnsharedTextDecoderView') { return this.expandGetUnsharedTextDecoderView(node) } } @@ -470,7 +505,7 @@ class Transform { } return ts.visitEachChild(node, this.visitor, this.ctx) } - if (ts.isIdentifier(node) && node.text === '$POINTER_SIZE') { + if (ts.isIdentifier(node) && !ts.isImportSpecifier(node.parent) && !ts.isImportClause(node.parent) && !ts.isImportEqualsDeclaration(node.parent) && !ts.isImportTypeNode(node.parent) && node.text === 'POINTER_SIZE') { return this.ctx.factory.createNumericLiteral(this.defines.MEMORY64 ? 8 : 4) } return ts.visitEachChild(node, this.visitor, this.ctx) @@ -494,10 +529,10 @@ class Transform { } else if (argv2.text === 'POINTER_WASM_TYPE') { type = this.defines.MEMORY64 ? 'i64' : 'i32' } else { - throw new Error('$makeGetValue Invalid type') + throw new Error('makeGetValue Invalid type') } } else { - throw new Error('$makeGetValue Invalid type') + throw new Error('makeGetValue Invalid type') } } @@ -540,10 +575,10 @@ class Transform { } else if (argv3.text === 'POINTER_WASM_TYPE') { type = this.defines.MEMORY64 ? 'i64' : 'i32' } else { - throw new Error('$makeGetValue Invalid type') + throw new Error('makeGetValue Invalid type') } } else { - throw new Error('$makeGetValue Invalid type') + throw new Error('makeGetValue Invalid type') } } @@ -630,7 +665,7 @@ class Transform { expandGetUnsharedTextDecoderView (node: CallExpression): Expression { const argv = node.arguments if (argv.length !== 3) { - throw new Error('$getUnsharedTextDecoderView argument length != 3') + throw new Error('getUnsharedTextDecoderView argument length != 3') } const argv0 = argv[0] @@ -641,7 +676,7 @@ class Transform { !ts.isStringLiteral(argv1) || !ts.isStringLiteral(argv2) ) { - throw new Error('$getUnsharedTextDecoderView arguments include non string literal') + throw new Error('getUnsharedTextDecoderView arguments include non string literal') } const heap = argv0.text @@ -720,20 +755,26 @@ function getImportsOfModule (src: SourceFile): string[] { return [...collection] } -function createTransformerFactory (_program: Program, config: DefineOptions): TransformerFactory { - const defines = config.defines ?? {} +function createTransformerFactory (program: Program, config: TransformOptions): TransformerFactory { // const defineKeys = Object.keys(defines) // const typeChecker = program.getTypeChecker() return (context) => { - const transform = new Transform(context, defines) + const transform = new Transform(program, context, config) return (src) => { if (src.isDeclarationFile) return src + const factory = context.factory transform.resetSource() // expand emscripten macros const transformedSrc = ts.visitEachChild(src, transform.visitor, context) // inject HEAP_DATA_VIEW - const injectedSrc = ts.visitEachChild(transformedSrc, transform.functionLikeDeclarationVisitor, context) + let injectedSrc = ts.visitEachChild(transformedSrc, transform.functionLikeDeclarationVisitor, context) + + const newStatements = injectedSrc.statements.filter(s => { + return !(ts.isImportDeclaration(s) && ts.isStringLiteral(s.moduleSpecifier) && (s.moduleSpecifier.text === transform.parseToolsModuleSpecifier)) + }) + + injectedSrc = factory.updateSourceFile(injectedSrc, newStatements) const doNotInsertImport = join(__dirname, '../../emnapi/src/core/init.ts') @@ -750,7 +791,6 @@ function createTransformerFactory (_program: Program, config: DefineOptions): Tr let resultSrc = injectedSrc let importNames: string[] | null = null - const factory = context.factory if (transform.insertWasmMemoryImport) { importNames = getImportsOfModule(resultSrc) if (!importNames.includes('wasmMemory')) { @@ -761,7 +801,7 @@ function createTransformerFactory (_program: Program, config: DefineOptions): Tr factory.createImportSpecifier(false, undefined, factory.createIdentifier('wasmMemory')) ]) ), - factory.createStringLiteral('emnapi:emscripten-runtime'), + factory.createStringLiteral(transform.runtimeModuleSpecifier), undefined ), ...resultSrc.statements @@ -778,7 +818,7 @@ function createTransformerFactory (_program: Program, config: DefineOptions): Tr factory.createImportSpecifier(false, undefined, factory.createIdentifier('wasmTable')) ]) ), - factory.createStringLiteral('emnapi:emscripten-runtime'), + factory.createStringLiteral(transform.runtimeModuleSpecifier), undefined ), ...resultSrc.statements