From 04d2db5908f8acb7419ecd91d5a68ec8fd2e1727 Mon Sep 17 00:00:00 2001 From: Toyo Li Date: Fri, 1 Dec 2023 13:55:04 +0800 Subject: [PATCH] refactor: migrate to ESM (#93) --- package.json | 7 + packages/core/package.json | 6 - packages/core/script/build.js | 61 ++-- packages/core/tsconfig.json | 13 +- packages/emnapi/script/build.js | 154 ++++++-- .../emnapi/script/rollup-plugin-emscripten.js | 59 +++ packages/emnapi/src/async-work.ts | 14 +- packages/emnapi/src/core/async-work.ts | 39 +- packages/emnapi/src/core/async.ts | 300 +--------------- packages/emnapi/src/core/index.ts | 95 +++++ packages/emnapi/src/core/init.ts | 326 +++++++++++++++-- packages/emnapi/src/core/memory.ts | 10 +- packages/emnapi/src/core/tsconfig.json | 19 +- packages/emnapi/src/core/util.ts | 28 -- packages/emnapi/src/emnapi.ts | 65 ++-- packages/emnapi/src/emscripten/async-work.ts | 29 +- packages/emnapi/src/emscripten/async.ts | 146 ++++---- packages/emnapi/src/emscripten/emnapi.ts | 18 +- packages/emnapi/src/emscripten/index.ts | 40 +++ packages/emnapi/src/emscripten/init.ts | 52 ++- packages/emnapi/src/emscripten/memory.ts | 20 +- packages/emnapi/src/emscripten/runtime.d.ts | 40 +-- packages/emnapi/src/emscripten/util.ts | 42 --- packages/emnapi/src/env.ts | 12 +- packages/emnapi/src/error.ts | 63 ++-- packages/emnapi/src/function.ts | 25 +- packages/emnapi/src/internal.ts | 21 +- packages/emnapi/src/life.ts | 63 ++-- packages/emnapi/src/macro.ts | 13 +- packages/emnapi/src/memory.ts | 28 +- packages/emnapi/src/miscellaneous.ts | 8 +- packages/emnapi/src/node.ts | 49 ++- packages/emnapi/src/promise.ts | 19 +- packages/emnapi/src/property.ts | 76 ++-- packages/emnapi/src/script.ts | 11 +- packages/emnapi/src/string.ts | 16 +- packages/emnapi/src/threadsafe-function.ts | 75 ++-- packages/emnapi/src/util.ts | 84 +++-- packages/emnapi/src/value-operation.ts | 68 ++-- packages/emnapi/src/value/convert2c.ts | 138 ++++--- packages/emnapi/src/value/convert2napi.ts | 81 +++-- packages/emnapi/src/value/create.ts | 100 ++++-- packages/emnapi/src/value/global.ts | 21 +- packages/emnapi/src/version.ts | 8 +- packages/emnapi/src/wrap.ts | 55 ++- packages/emnapi/transformer/src/esm.ts | 337 ++++++++++++++++++ packages/emnapi/transformer/src/index.ts | 91 ++++- packages/emnapi/tsconfig.base.json | 3 +- packages/emnapi/tsconfig.json | 7 +- packages/runtime/package.json | 7 - packages/runtime/script/build.js | 101 +++--- packages/runtime/tsconfig.json | 10 +- packages/shared/tsconfig.base.json | 2 - packages/test/CMakeLists.txt | 3 + packages/test/async/async.test.js | 95 +---- packages/test/async/async_st.test.js | 9 + packages/test/async/main.js | 93 +++++ packages/test/tsfn2/main.js | 27 ++ packages/test/tsfn2/tsfn2.test.js | 29 +- packages/test/tsfn2/tsfn2_st.test.js | 11 + tsconfig.json | 1 - 61 files changed, 2170 insertions(+), 1273 deletions(-) create mode 100644 packages/emnapi/script/rollup-plugin-emscripten.js create mode 100644 packages/emnapi/src/core/index.ts delete mode 100644 packages/emnapi/src/core/util.ts create mode 100644 packages/emnapi/src/emscripten/index.ts delete mode 100644 packages/emnapi/src/emscripten/util.ts create mode 100644 packages/emnapi/transformer/src/esm.ts create mode 100644 packages/test/async/async_st.test.js create mode 100644 packages/test/async/main.js create mode 100644 packages/test/tsfn2/main.js create mode 100644 packages/test/tsfn2/tsfn2_st.test.js diff --git a/package.json b/package.json index 08fc49c6..94554835 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,12 @@ }, "license": "MIT", "devDependencies": { + "@microsoft/api-extractor": "^7.38.3", + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^5.0.5", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.5", "@tybys/cross-zip": "^3.1.0", "@tybys/ts-transform-pure-class": "^0.1.1", "@tybys/tsapi": "^0.6.0", @@ -41,6 +47,7 @@ "eslint-plugin-n": "^15.6.1", "eslint-plugin-promise": "^6.1.1", "fs-extra": "^10.1.0", + "rollup": "^4.5.2", "ts-clone-node": "^3.0.0", "typescript": "~5.0.4" }, diff --git a/packages/core/package.json b/packages/core/package.json index 91fa7b10..4a6c550b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,12 +30,6 @@ "dependencies": { "tslib": "^2.4.0" }, - "devDependencies": { - "@rollup/plugin-node-resolve": "^15.1.0", - "@rollup/plugin-replace": "^5.0.2", - "@rollup/plugin-terser": "^0.4.3", - "rollup": "^3.25.1" - }, "scripts": { "build": "node ./script/build.js" }, diff --git a/packages/core/script/build.js b/packages/core/script/build.js index 471a5e42..07ae0bf9 100644 --- a/packages/core/script/build.js +++ b/packages/core/script/build.js @@ -1,40 +1,45 @@ const path = require('path') const fs = require('fs-extra') const rollup = require('rollup') +const ts = require('typescript') +const rollupTypescript = require('@rollup/plugin-typescript').default const rollupNodeResolve = require('@rollup/plugin-node-resolve').default const rollupReplace = require('@rollup/plugin-replace').default const rollupTerser = require('@rollup/plugin-terser').default const dist = path.join(__dirname, '../dist') -const { compile } = require('@tybys/tsapi') function build () { - compile(path.join(__dirname, '../tsconfig.json'), { - optionsToExtend: { - target: require('typescript').ScriptTarget.ES5, - outDir: path.join(__dirname, '../lib/es5') - } - }) - compile(path.join(__dirname, '../tsconfig.json'), { - optionsToExtend: { - target: require('typescript').ScriptTarget.ES2019, - outDir: path.join(__dirname, '../lib/es2019'), - removeComments: true, - downlevelIteration: false - } - }) - /** - * @param {'es5' | 'es2019'} esversion + * @param {ts.ScriptTarget} esversion * @param {boolean=} minify * @returns {rollup.RollupOptions} */ - function createInput (esversion, minify, options) { + function createInput (esversion, minify, external) { return { - input: path.join(__dirname, '../lib', esversion, 'index.js'), + input: path.join(__dirname, '../src/index.js'), + external, plugins: [ + rollupTypescript({ + tsconfig: path.join(__dirname, '../tsconfig.json'), + tslib: path.join( + path.dirname(require.resolve('tslib')), + JSON.parse(fs.readFileSync(path.join(path.dirname(require.resolve('tslib')), 'package.json'))).module + ), + compilerOptions: { + target: esversion, + ...(esversion !== ts.ScriptTarget.ES5 ? { removeComments: true, downlevelIteration: false } : {}) + }, + include: [ + './src/**/*' + ], + transformers: { + after: [ + require('@tybys/ts-transform-pure-class').default + ] + } + }), rollupNodeResolve({ - mainFields: ['module', 'main'], - ...((options && options.resolveOnly) ? { resolveOnly: options.resolveOnly } : {}) + mainFields: ['module', 'main'] }), rollupReplace({ preventAssignment: true, @@ -59,7 +64,7 @@ function build () { return Promise.all(([ { - input: createInput('es5', false), + input: createInput(ts.ScriptTarget.ES5, false), output: { file: path.join(dist, 'emnapi-core.js'), format: 'umd', @@ -69,7 +74,7 @@ function build () { } }, { - input: createInput('es5', true), + input: createInput(ts.ScriptTarget.ES5, true), output: { file: path.join(dist, 'emnapi-core.min.js'), format: 'umd', @@ -79,7 +84,7 @@ function build () { } }, { - input: createInput('es2019', false, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, false, ['tslib']), output: { file: path.join(dist, 'emnapi-core.cjs.js'), format: 'cjs', @@ -89,7 +94,7 @@ function build () { } }, { - input: createInput('es2019', true, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, true, ['tslib']), output: { file: path.join(dist, 'emnapi-core.cjs.min.js'), format: 'cjs', @@ -99,7 +104,7 @@ function build () { } }, { - input: createInput('es2019', false, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, false, ['tslib']), output: { file: path.join(dist, 'emnapi-core.mjs'), format: 'esm', @@ -109,7 +114,7 @@ function build () { } }, { - input: createInput('es2019', true, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, true, ['tslib']), output: { file: path.join(dist, 'emnapi-core.min.mjs'), format: 'esm', @@ -119,7 +124,7 @@ function build () { } }, { - input: createInput('es5', false, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES5, false, ['tslib']), output: { file: path.join(dist, 'emnapi-core.esm-bundler.js'), format: 'esm', diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 0932cef1..8fef5d38 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -4,9 +4,10 @@ "allowJs": true, "target": "ES5", "module": "ESNext", + "moduleResolution": "Bundler", "noEmitHelpers": true, "importHelpers": true, - "importsNotUsedAsValues": "error", + "outDir": "lib", "paths": { "tslib" : ["../../node_modules/tslib/tslib.d.ts"] }, @@ -17,17 +18,9 @@ "ES2021.WeakRef", "ES2017.SharedMemory", "DOM" - ], - "plugins": [ - { - "transform": "@tybys/ts-transform-pure-class", - "type": "raw", - "after": true - } ] }, "include": [ - "./src/**/*.ts", - "./src/**/*.js" + "./src/**/*" ] } diff --git a/packages/emnapi/script/build.js b/packages/emnapi/script/build.js index 7e3b0481..ae55ec73 100644 --- a/packages/emnapi/script/build.js +++ b/packages/emnapi/script/build.js @@ -1,51 +1,149 @@ const fs = require('fs') const path = require('path') -const { EOL } = require('os') +const { createRequire } = require('module') +const ts = require('typescript') const { compile } = require('@tybys/tsapi') +const rollupTypescript = require('@rollup/plugin-typescript').default +const rollupAlias = require('@rollup/plugin-alias').default +const { rollup } = require('rollup') // const { // runtimeOut // } = require('../../runtime/script/build.js') -function replaceParseTool (code) { - return code - .replace(/(\r?\n)\s*\/\/\s+(#((if)|(else)|(elif)|(endif)))/g, '$1$2') - .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 }}}') -} - async function build () { const transformerTsconfigPath = path.join(__dirname, '../transformer/tsconfig.json') compile(transformerTsconfigPath) const libTsconfigPath = path.join(__dirname, '../tsconfig.json') - compile(libTsconfigPath) + // compile(libTsconfigPath) const libTsconfig = JSON.parse(fs.readFileSync(libTsconfigPath, 'utf8')) - const libOut = path.join(path.dirname(libTsconfigPath), libTsconfig.compilerOptions.outFile) + const libOut = path.join(path.dirname(libTsconfigPath), './dist/library_napi.js') + const runtimeRequire = createRequire(path.join(__dirname, '../../runtime/index.js')) - // const runtimeCode = fs.readFileSync(runtimeOut, 'utf8') - const libCode = fs.readFileSync(libOut, 'utf8') + const emnapiRollupBuild = await rollup({ + input: path.join(__dirname, '../src/emscripten/index.ts'), + treeshake: false, + plugins: [ + rollupTypescript({ + tsconfig: libTsconfigPath, + tslib: path.join( + path.dirname(runtimeRequire.resolve('tslib')), + JSON.parse(fs.readFileSync(path.join(path.dirname(runtimeRequire.resolve('tslib')), 'package.json'))).module + ), + compilerOptions: { + module: ts.ModuleKind.ESNext + }, + include: libTsconfig.include.map(s => path.join(__dirname, '..', s)), + transformers: { + before: [ + { + type: 'program', + factory: require('../transformer/out/macro.js').default + }, + { + 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') } + ] + }), + require('./rollup-plugin-emscripten.js').default({ + defaultLibraryFuncsToInclude: ['$emnapiInit'], + exportedRuntimeMethods: ['emnapiInit'], + 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 }}}') + } + }) + ] + }) + await emnapiRollupBuild.write({ + file: libOut, + format: 'esm', + exports: 'named', + strict: false + }) - fs.writeFileSync(libOut, - '{{{ ((DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.indexOf("$emnapiInit") === -1 ? DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push("$emnapiInit") : undefined), "") }}}' + EOL + - '{{{ ((EXPORTED_RUNTIME_METHODS.indexOf("emnapiInit") === -1 ? EXPORTED_RUNTIME_METHODS.push("emnapiInit") : undefined), "") }}}' + EOL + - replaceParseTool( - libCode - ), - 'utf8' - ) + // const runtimeCode = fs.readFileSync(runtimeOut, 'utf8') const coreTsconfigPath = path.join(__dirname, '../src/core/tsconfig.json') - compile(coreTsconfigPath) + // compile(coreTsconfigPath) const coreTsconfig = JSON.parse(fs.readFileSync(coreTsconfigPath, 'utf8')) - const coreOut = path.join(path.dirname(coreTsconfigPath), coreTsconfig.compilerOptions.outFile) + const coreOut = path.join(path.dirname(coreTsconfigPath), '../../dist/emnapi-core.js') + + const coreRollupBuild = await rollup({ + input: path.join(__dirname, '../src/core/index.ts'), + treeshake: false, + plugins: [ + rollupTypescript({ + tsconfig: coreTsconfigPath, + tslib: path.join( + path.dirname(runtimeRequire.resolve('tslib')), + JSON.parse(fs.readFileSync(path.join(path.dirname(runtimeRequire.resolve('tslib')), 'package.json'))).module + ), + compilerOptions: { + module: ts.ModuleKind.ESNext + }, + include: coreTsconfig.include.map(s => path.join(__dirname, '../src/core', s)), + transformers: { + before: [ + { + type: 'program', + factory: require('../transformer/out/macro.js').default + }, + { + type: 'program', + factory (program) { + return require('../transformer/out/index.js').default(program, { + defines: { + MEMORY64: 0 + } + }) + } + } + ] + } + }), + 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') } + ] + }) + ] + }) + await coreRollupBuild.write({ + file: coreOut, + format: 'iife', + name: 'napiModule', + strict: false + }) + const coreCode = fs.readFileSync(coreOut, 'utf8') const { Compiler } = require('./preprocess.js') const compiler = new Compiler({ diff --git a/packages/emnapi/script/rollup-plugin-emscripten.js b/packages/emnapi/script/rollup-plugin-emscripten.js new file mode 100644 index 00000000..f3e9c7c1 --- /dev/null +++ b/packages/emnapi/script/rollup-plugin-emscripten.js @@ -0,0 +1,59 @@ +const { EOL } = require('os') +const ts = require('typescript') +const createTransformer = require('../transformer/out/esm.js').default + +/** + * @param {string} fileName + * @param {string} sourceText + * @returns {string} + */ +function transform (fileName, sourceText) { + const compilerOptions = { + allowJs: true, + module: ts.ModuleKind.ESNext, + target: ts.ScriptTarget.ESNext, + noEmit: true + } + const source = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.ESNext, true, ts.ScriptKind.JS) + const host = ts.createCompilerHost(compilerOptions, true) + host.getSourceFile = filePath => filePath === fileName ? source : undefined + const program = ts.createProgram({ + rootNames: [fileName], + options: compilerOptions, + host + }) + + const transformerFactory = createTransformer(program) + + const transformResult = ts.transform(source, [transformerFactory]) + const printer = ts.createPrinter({ + newLine: process.platform === 'win32' ? ts.NewLineKind.CarriageReturnLineFeed : ts.NewLineKind.LineFeed + }) + return printer.printNode(ts.EmitHint.SourceFile, transformResult.transformed[0], transformResult.transformed[0]) +} + +/** + * @param {{ defaultLibraryFuncsToInclude?: string[]; exportedRuntimeMethods?: string[]; modifyOutput?: (output: string) => string }=} options + */ +exports.default = function (options) { + const defaultLibraryFuncsToInclude = (options ? options.defaultLibraryFuncsToInclude : []) || [] + const exportedRuntimeMethods = (options ? options.exportedRuntimeMethods : []) || [] + const defaultModify = _ => _ + const modifyOutput = (options ? options.modifyOutput : defaultModify) || defaultModify + + return { + name: 'ts-transform-emscriten', + renderChunk: function (code, chunk) { + const result = transform(chunk.fileName, code) + + const prefix = [ + ...defaultLibraryFuncsToInclude + .map(sym => `{{{ ((DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.indexOf("${sym}") === -1 ? DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push("${sym}") : undefined), "") }}}`), + ...exportedRuntimeMethods + .map(sym => `{{{ ((EXPORTED_RUNTIME_METHODS.indexOf("${sym}") === -1 ? EXPORTED_RUNTIME_METHODS.push("${sym}") : undefined), "") }}}`) + ].join(EOL) + + return modifyOutput(prefix + EOL + result.replace(/(\r?\n)\s*\/\/\s+(#((if)|(else)|(elif)|(endif)))/g, '$1$2')) + } + } +} diff --git a/packages/emnapi/src/async-work.ts b/packages/emnapi/src/async-work.ts index a429a025..0e8d7be5 100644 --- a/packages/emnapi/src/async-work.ts +++ b/packages/emnapi/src/async-work.ts @@ -1,4 +1,6 @@ -declare interface AsyncWork { +import { emnapiAsyncWorkPoolSize, emnapiNodeBinding, emnapiCtx } from 'emnapi:shared' + +export interface AsyncWork { id: number resource: object asyncId: number @@ -17,7 +19,13 @@ declare interface AsyncWork { status: 0 | 1 | 2 | 3 | 4 } -var emnapiAWST = { +/** + * @__postset + * ``` + * emnapiAWST.init(); + * ``` + */ +export var emnapiAWST = { idGen: {} as unknown as { nextId: number list: number[] @@ -170,5 +178,3 @@ var emnapiAWST = { emnapiAWST.idGen.reuse(id) } } - -emnapiDefineVar('$emnapiAWST', emnapiAWST, ['$emnapiAsyncWorkPoolSize'], 'emnapiAWST.init();') diff --git a/packages/emnapi/src/core/async-work.ts b/packages/emnapi/src/core/async-work.ts index ace11a89..457e8951 100644 --- a/packages/emnapi/src/core/async-work.ts +++ b/packages/emnapi/src/core/async-work.ts @@ -1,3 +1,11 @@ +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 { 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' +import { emnapiTSFN } from '../threadsafe-function' +import { _emnapi_runtime_keepalive_pop, _emnapi_runtime_keepalive_push } from '../util' + var emnapiAWMT = { unusedWorkers: [] as any[], runningWorkers: [] as any[], @@ -29,7 +37,7 @@ var emnapiAWMT = { const type = __emnapi__.type const payload = __emnapi__.payload if (type === 'async-work-complete') { - __emnapi_runtime_keepalive_pop() + _emnapi_runtime_keepalive_pop() emnapiCtx.decreaseWaitingRequestCounter() emnapiAWMT.runningWorkers.splice(emnapiAWMT.runningWorkers.indexOf(worker), 1) emnapiAWMT.unusedWorkers.push(worker) @@ -145,19 +153,19 @@ var emnapiAWMT = { }) return } - __emnapi_runtime_keepalive_push() + _emnapi_runtime_keepalive_push() emnapiCtx.increaseWaitingRequestCounter() emnapiAWMT.workQueue.push(work) if (emnapiAWMT.workerReady?.ready) { emnapiAWMT.checkIdleWorker() } else { const fail = (err: any): void => { - __emnapi_runtime_keepalive_pop() + _emnapi_runtime_keepalive_pop() emnapiCtx.decreaseWaitingRequestCounter() throw err } try { - emnapiAWMT.initWorkers(__emnapi_async_work_pool_size()).then(() => { + emnapiAWMT.initWorkers(_emnapi_async_work_pool_size()).then(() => { emnapiAWMT.workerReady!.ready = true emnapiAWMT.checkIdleWorker() }, fail) @@ -182,7 +190,7 @@ var emnapiAWMT = { emnapiAWMT.workQueue.splice(index, 1) emnapiCtx.feature.setImmediate(() => { - __emnapi_runtime_keepalive_pop() + _emnapi_runtime_keepalive_pop() emnapiCtx.decreaseWaitingRequestCounter() emnapiAWMT.checkIdleWorker() emnapiAWMT.callComplete(work, napi_status.napi_cancelled) @@ -226,7 +234,8 @@ var emnapiAWMT = { } } -var _napi_create_async_work = singleThreadAsyncWork +/** @__sig ippppppp */ +export var napi_create_async_work = singleThreadAsyncWork ? function (env: napi_env, resource: napi_value, resource_name: napi_value, execute: number, complete: number, data: number, result: number): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, execute) @@ -271,7 +280,7 @@ var _napi_create_async_work = singleThreadAsyncWork // eslint-disable-next-line @typescript-eslint/no-unused-vars const resource_ = resourceRef.id $makeSetValue('aw', 0, 'resource_', '*') - __emnapi_node_emit_async_init(s, resource_name, -1, aw + emnapiAWMT.offset.async_id) + _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', '*') @@ -281,7 +290,8 @@ var _napi_create_async_work = singleThreadAsyncWork return envObject.clearLastError() } -var _napi_delete_async_work = singleThreadAsyncWork +/** @__sig ipp */ +export var napi_delete_async_work = singleThreadAsyncWork ? function (env: napi_env, work: number): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, work) @@ -300,14 +310,15 @@ var _napi_delete_async_work = singleThreadAsyncWork const view = new DataView(wasmMemory.buffer) const asyncId = view.getFloat64(work + emnapiAWMT.offset.async_id, true) const triggerAsyncId = view.getFloat64(work + emnapiAWMT.offset.trigger_async_id, true) - __emnapi_node_emit_async_destroy(asyncId, triggerAsyncId) + _emnapi_node_emit_async_destroy(asyncId, triggerAsyncId) } _free($to64('work') as number) return envObject.clearLastError() } -var _napi_queue_async_work = singleThreadAsyncWork +/** @__sig ipp */ +export var napi_queue_async_work = singleThreadAsyncWork ? function (env: napi_env, work: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! @@ -325,7 +336,8 @@ var _napi_queue_async_work = singleThreadAsyncWork return envObject.clearLastError() } -var _napi_cancel_async_work = singleThreadAsyncWork +/** @__sig ipp */ +export var napi_cancel_async_work = singleThreadAsyncWork ? function (env: napi_env, work: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! @@ -372,8 +384,3 @@ function executeAsyncWork (work: number): void { } napiModule.initWorker = initWorker napiModule.executeAsyncWork = executeAsyncWork - -emnapiImplement('napi_create_async_work', 'ippppppp', _napi_create_async_work) -emnapiImplement('napi_delete_async_work', 'ipp', _napi_delete_async_work) -emnapiImplement('napi_queue_async_work', 'ipp', _napi_queue_async_work) -emnapiImplement('napi_cancel_async_work', 'ipp', _napi_cancel_async_work) diff --git a/packages/emnapi/src/core/async.ts b/packages/emnapi/src/core/async.ts index 923fdf75..13d06d99 100644 --- a/packages/emnapi/src/core/async.ts +++ b/packages/emnapi/src/core/async.ts @@ -1,129 +1,8 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -function terminateWorker (worker: any): void { - const tid = worker.__emnapi_tid - worker.terminate() - worker.onmessage = (e: any) => { - if (e.data.__emnapi__) { - err('received "' + e.data.__emnapi__.type + '" command from terminated worker: ' + tid) - } - } -} - -var PThread = { - unusedWorkers: [] as any[], - runningWorkers: [] as any[], - pthreads: Object.create(null), - nextWorkerID: 0, - init () {}, - returnWorkerToPool (worker: any) { - var tid = worker.__emnapi_tid - delete PThread.pthreads[tid] - PThread.unusedWorkers.push(worker) - PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1) - delete worker.__emnapi_tid - if (ENVIRONMENT_IS_NODE) { - worker.unref() - } - }, - loadWasmModuleToWorker: (worker: any) => { - if (worker.whenLoaded) return worker.whenLoaded - worker.whenLoaded = new Promise((resolve, reject) => { - worker.onmessage = function (e: any) { - if (e.data.__emnapi__) { - const type = e.data.__emnapi__.type - const payload = e.data.__emnapi__.payload - if (type === 'loaded') { - worker.loaded = true - if (ENVIRONMENT_IS_NODE && !worker.__emnapi_tid) { - worker.unref() - } - resolve(worker) - // if (payload.err) { - // err('failed to load in child thread: ' + (payload.err.message || payload.err)) - // } - } else if (type === 'spawn-thread') { - spawnThread(payload.startArg, payload.errorOrTid) - } else if (type === 'cleanup-thread') { - if (reuseWorker) { - PThread.returnWorkerToPool(worker) - } else { - delete PThread.pthreads[payload.tid] - PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1) - terminateWorker(worker) - delete worker.__emnapi_tid - } - } - } - } - worker.onerror = (e: any) => { - const message = 'worker sent an error!' - // if (worker.pthread_ptr) { - // message = 'Pthread ' + ptrToString(worker.pthread_ptr) + ' sent an error!' - // } - err(message + ' ' + e.message) - reject(e) - throw e - } - if (ENVIRONMENT_IS_NODE) { - worker.on('message', function (data: any) { - worker.onmessage({ - data - }) - }) - worker.on('error', function (e: any) { - worker.onerror(e) - }) - worker.on('detachedExit', function () {}) - } - // napiModule.emnapi.addSendListener(worker) - emnapiAddSendListener(worker) - if (typeof emnapiTSFN !== 'undefined') { - emnapiTSFN.addListener(worker) - } - try { - worker.postMessage({ - __emnapi__: { - type: 'load', - payload: { - wasmModule, - wasmMemory - } - } - }) - } catch (err) { - if (typeof SharedArrayBuffer === 'undefined' || !(wasmMemory.buffer instanceof SharedArrayBuffer)) { - throw new Error( - 'Multithread features require shared wasm memory. ' + - 'Try to compile with `-matomics -mbulk-memory` and use `--import-memory --shared-memory` during linking' - ) - } - throw err - } - }) - return worker.whenLoaded - }, - allocateUnusedWorker () { - if (typeof onCreateWorker !== 'function') { - throw new TypeError('`options.onCreateWorker` is not provided') - } - const worker = onCreateWorker({ type: 'thread' }) - PThread.unusedWorkers.push(worker) - return worker - }, - getNewWorker () { - if (reuseWorker) { - if (PThread.unusedWorkers.length === 0) { - const worker = PThread.allocateUnusedWorker() - PThread.loadWasmModuleToWorker(worker) - } - return PThread.unusedWorkers.pop() - } - const worker = PThread.allocateUnusedWorker() - PThread.loadWasmModuleToWorker(worker) - return worker - } -} +import { napiModule } from 'emnapi:shared' +import { ENVIRONMENT_IS_NODE, wasmMemory, ENVIRONMENT_IS_PTHREAD, PThread } from 'emnapi:emscripten-runtime' +import { _emnapi_set_immediate, _emnapi_next_tick } from '../util' function emnapiGetWorkerByPthreadPtr (pthreadPtr: number): any { const view = new DataView(wasmMemory.buffer) @@ -145,7 +24,8 @@ function emnapiGetWorkerByPthreadPtr (pthreadPtr: number): any { return worker } -function __emnapi_worker_unref (pthreadPtr: number): void { +/** @__sig vp */ +export function _emnapi_worker_unref (pthreadPtr: number): void { if (ENVIRONMENT_IS_PTHREAD) return const worker = emnapiGetWorkerByPthreadPtr(pthreadPtr) if (worker && typeof worker.unref === 'function') { @@ -153,41 +33,8 @@ function __emnapi_worker_unref (pthreadPtr: number): void { } } -function emnapiAddSendListener (worker: any): boolean { - if (!worker) return false - if (worker._emnapiSendListener) return true - const handler = function (e: any): void { - const data = ENVIRONMENT_IS_NODE ? e : e.data - const __emnapi__ = data.__emnapi__ - if (__emnapi__ && __emnapi__.type === 'async-send') { - if (ENVIRONMENT_IS_PTHREAD) { - const postMessage = napiModule.postMessage! - postMessage({ __emnapi__ }) - } else { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const callback = __emnapi__.payload.callback - $makeDynCall('vp', 'callback')(__emnapi__.payload.data) - } - } - } - const dispose = function (): void { - if (ENVIRONMENT_IS_NODE) { - worker.off('message', handler) - } else { - worker.removeEventListener('message', handler, false) - } - delete worker._emnapiSendListener - } - worker._emnapiSendListener = { handler, dispose } - if (ENVIRONMENT_IS_NODE) { - worker.on('message', handler) - } else { - worker.addEventListener('message', handler, false) - } - return true -} - -function __emnapi_async_send_js (type: number, callback: number, data: number): void { +/** @__sig vipp */ +export function _emnapi_async_send_js (type: number, callback: number, data: number): void { if (ENVIRONMENT_IS_PTHREAD) { const postMessage = napiModule.postMessage! postMessage({ @@ -201,8 +48,8 @@ function __emnapi_async_send_js (type: number, callback: number, data: number): }) } else { switch (type) { - case 0: __emnapi_set_immediate(callback, data); break - case 1: __emnapi_next_tick(callback, data); break + case 0: _emnapi_set_immediate(callback, data); break + case 1: _emnapi_next_tick(callback, data); break default: break } } @@ -212,115 +59,6 @@ function __emnapi_async_send_js (type: number, callback: number, data: number): // return '0x' + ('00000000' + ptr.toString(16)).slice(-8) // } -function spawnThread (startArg: number, errorOrTid: number): number { - const isNewABI = errorOrTid !== undefined - if (!isNewABI) { - errorOrTid = _malloc($to64('8')) - if (!errorOrTid) { - return -48 /* ENOMEM */ - } - } - const struct = new Int32Array(wasmMemory.buffer, errorOrTid, 2) - Atomics.store(struct, 0, 0) - Atomics.store(struct, 1, 0) - - if (ENVIRONMENT_IS_PTHREAD) { - const postMessage = napiModule.postMessage! - postMessage({ - __emnapi__: { - type: 'spawn-thread', - payload: { - startArg, - errorOrTid - } - } - }) - Atomics.wait(struct, 1, 0) - const isError = Atomics.load(struct, 0) - const result = Atomics.load(struct, 1) - if (isNewABI) { - return isError - } - _free($to64('errorOrTid')) - return isError ? -result : result - } - - let worker: any - try { - worker = PThread.getNewWorker() - if (!worker) { - throw new Error('failed to get new worker') - } - } catch (e) { - const EAGAIN = 6 - - Atomics.store(struct, 0, 1) - Atomics.store(struct, 1, EAGAIN) - Atomics.notify(struct, 1) - - err(e.message) - if (isNewABI) { - return 1 - } - _free($to64('errorOrTid')) - return -EAGAIN - } - - const tid = PThread.nextWorkerID + 43 - - Atomics.store(struct, 0, 0) - Atomics.store(struct, 1, tid) - Atomics.notify(struct, 1) - - const WASI_THREADS_MAX_TID = 0x1FFFFFFF - PThread.nextWorkerID = (PThread.nextWorkerID + 1) % (WASI_THREADS_MAX_TID - 42) - PThread.pthreads[tid] = worker - worker.__emnapi_tid = tid - PThread.runningWorkers.push(worker) - if (ENVIRONMENT_IS_NODE) { - worker.ref() - } - - worker.postMessage({ - __emnapi__: { - type: 'start', - payload: { - tid, - arg: startArg - } - } - }) - - if (isNewABI) { - return 0 - } - _free($to64('errorOrTid')) - return tid -} - -function startThread (tid: number, startArg: number): void { - if (napiModule.childThread) { - if (typeof wasmInstance.exports.wasi_thread_start !== 'function') { - throw new TypeError('wasi_thread_start is not exported') - } - const postMessage = napiModule.postMessage! - ;(wasmInstance.exports.wasi_thread_start as Function)(tid, startArg) - postMessage({ - __emnapi__: { - type: 'cleanup-thread', - payload: { - tid - } - } - }) - } else { - throw new Error('startThread is only available in child threads') - } -} - -napiModule.spawnThread = spawnThread -napiModule.startThread = startThread - var uvThreadpoolReadyResolve: () => void var uvThreadpoolReady: Promise & { ready: boolean } = new Promise((resolve) => { uvThreadpoolReadyResolve = function () { @@ -330,11 +68,13 @@ var uvThreadpoolReady: Promise & { ready: boolean } = new Promise((r }) as any uvThreadpoolReady.ready = false -function __emnapi_is_main_browser_thread (): number { +/** @__sig i */ +export function _emnapi_is_main_browser_thread (): number { return (typeof window !== 'undefined' && typeof document !== 'undefined' && !ENVIRONMENT_IS_NODE) ? 1 : 0 } -function __emnapi_after_uvthreadpool_ready (callback: number, q: number, type: number): void { +/** @__sig vppi */ +export function _emnapi_after_uvthreadpool_ready (callback: number, q: number, type: number): void { if (uvThreadpoolReady.ready) { $makeDynCall('vpi', 'callback')($to64('q'), type) } else { @@ -344,7 +84,8 @@ function __emnapi_after_uvthreadpool_ready (callback: number, q: number, type: n } } -function __emnapi_tell_js_uvthreadpool (threads: number, size: number): void { +/** @__sig vpi */ +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, '*') @@ -375,7 +116,8 @@ function __emnapi_tell_js_uvthreadpool (threads: number, size: number): void { Promise.all(p).then(uvThreadpoolReadyResolve) } -function __emnapi_emit_async_thread_ready (): void { +/** @__sig v */ +export function _emnapi_emit_async_thread_ready (): void { if (!ENVIRONMENT_IS_PTHREAD) return const postMessage = napiModule.postMessage! postMessage({ @@ -385,11 +127,3 @@ function __emnapi_emit_async_thread_ready (): void { } }) } - -emnapiImplementInternal('_emnapi_is_main_browser_thread', 'i', __emnapi_is_main_browser_thread) -emnapiImplementInternal('_emnapi_after_uvthreadpool_ready', 'vppi', __emnapi_after_uvthreadpool_ready) -emnapiImplementInternal('_emnapi_tell_js_uvthreadpool', 'vpi', __emnapi_tell_js_uvthreadpool) -emnapiImplementInternal('_emnapi_emit_async_thread_ready', 'v', __emnapi_emit_async_thread_ready) -emnapiImplementInternal('_emnapi_worker_unref', 'vp', __emnapi_worker_unref) -emnapiImplementInternal('_emnapi_async_send_js', 'vipp', __emnapi_async_send_js) -emnapiImplementHelper('$emnapiAddSendListener', undefined, emnapiAddSendListener, undefined, 'addSendListener') diff --git a/packages/emnapi/src/core/index.ts b/packages/emnapi/src/core/index.ts new file mode 100644 index 00000000..81c77f2a --- /dev/null +++ b/packages/emnapi/src/core/index.ts @@ -0,0 +1,95 @@ +import { napiModule } from 'emnapi:shared' + +import * as asyncMod from './async' +import * as memoryMod from './memory' +import * as asyncWorkMod from './async-work' + +import { emnapiAWST } from '../async-work' +import { emnapiExternalMemory } from '../memory' +import { emnapiString } from '../string' + +import * as utilMod from '../util' +import * as convert2cMod from '../value/convert2c' +import * as convert2napiMod from '../value/convert2napi' +import * as createMod from '../value/create' +import * as globalMod from '../value/global' +import * as wrapMod from '../wrap' +import * as envMod from '../env' +import * as emnapiMod from '../emnapi' +import * as errorMod from '../error' +import * as functionMod from '../function' +import * as lifeMod from '../life' +import * as miscellaneousMod from '../miscellaneous' +import * as nodeMod from '../node' +import * as promiseMod from '../promise' +import * as propertyMod from '../property' +import * as scriptMod from '../script' +import { + emnapiTSFN, + napi_create_threadsafe_function, + napi_get_threadsafe_function_context, + napi_call_threadsafe_function, + napi_acquire_threadsafe_function, + napi_release_threadsafe_function, + napi_unref_threadsafe_function, + napi_ref_threadsafe_function +} from '../threadsafe-function' +import * as valueOperationMod from '../value-operation' +import * as versionMod from '../version' + +emnapiAWST.init() +emnapiExternalMemory.init() +emnapiString.init() +emnapiTSFN.init() + +napiModule.emnapi.syncMemory = emnapiMod.$emnapiSyncMemory +napiModule.emnapi.getMemoryAddress = emnapiMod.$emnapiGetMemoryAddress + +function addImports (mod: any): void { + const keys = Object.keys(mod) + for (let i = 0; i < keys.length; ++i) { + const k = keys[i] + if (k.indexOf('$') === 0) continue + + if (k.indexOf('emnapi_') === 0) { + napiModule.imports.emnapi[k] = mod[k] + } else if (k.indexOf('_emnapi_') === 0 || k === 'napi_set_last_error' || k === 'napi_clear_last_error') { + napiModule.imports.env[k] = mod[k] + } else { + napiModule.imports.napi[k] = mod[k] + } + } +} + +addImports(asyncMod) +addImports(memoryMod) +addImports(asyncWorkMod) + +addImports(utilMod) +addImports(convert2cMod) +addImports(convert2napiMod) +addImports(createMod) +addImports(globalMod) +addImports(wrapMod) +addImports(envMod) +addImports(emnapiMod) +addImports(errorMod) +addImports(functionMod) +addImports(lifeMod) +addImports(miscellaneousMod) +addImports(nodeMod) +addImports(promiseMod) +addImports(propertyMod) +addImports(scriptMod) +addImports(valueOperationMod) +addImports(versionMod) + +napiModule.imports.napi.napi_create_threadsafe_function = napi_create_threadsafe_function +napiModule.imports.napi.napi_get_threadsafe_function_context = napi_get_threadsafe_function_context +napiModule.imports.napi.napi_call_threadsafe_function = napi_call_threadsafe_function +napiModule.imports.napi.napi_acquire_threadsafe_function = napi_acquire_threadsafe_function +napiModule.imports.napi.napi_release_threadsafe_function = napi_release_threadsafe_function +napiModule.imports.napi.napi_unref_threadsafe_function = napi_unref_threadsafe_function +napiModule.imports.napi.napi_ref_threadsafe_function = napi_ref_threadsafe_function + +export default napiModule diff --git a/packages/emnapi/src/core/init.ts b/packages/emnapi/src/core/init.ts index e7aa1299..f43a76b0 100644 --- a/packages/emnapi/src/core/init.ts +++ b/packages/emnapi/src/core/init.ts @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ -declare interface CreateOptions { +import { emnapiTSFN } from '../threadsafe-function' + +export interface CreateOptions { context: Context filename?: string nodeBinding?: NodeBinding @@ -14,7 +16,7 @@ declare interface CreateOptions { postMessage?: (msg: any) => any } -declare interface InitOptions { +export interface InitOptions { instance: WebAssembly.Instance module: WebAssembly.Module memory?: WebAssembly.Memory @@ -24,7 +26,7 @@ declare interface InitOptions { // factory parameter declare const options: CreateOptions -declare interface INapiModule { +export interface INapiModule { imports: { env: any napi: any @@ -45,31 +47,33 @@ declare interface INapiModule { postMessage?: (msg: any) => any } -var ENVIRONMENT_IS_NODE = typeof process === 'object' && process !== null && typeof process.versions === 'object' && process.versions !== null && typeof process.versions.node === 'string' -var ENVIRONMENT_IS_PTHREAD = Boolean(options.childThread) -var reuseWorker = Boolean(options.reuseWorker) +declare const process: any + +export var ENVIRONMENT_IS_NODE = typeof process === 'object' && process !== null && typeof process.versions === 'object' && process.versions !== null && typeof process.versions.node === 'string' +export var ENVIRONMENT_IS_PTHREAD = Boolean(options.childThread) +export var reuseWorker = Boolean(options.reuseWorker) -var wasmInstance: WebAssembly.Instance -var wasmModule: WebAssembly.Module -var wasmMemory: WebAssembly.Memory +export var wasmInstance: WebAssembly.Instance +export var wasmModule: WebAssembly.Module +export var wasmMemory: WebAssembly.Memory -var wasmTable: WebAssembly.Table +export var wasmTable: WebAssembly.Table -var _malloc: any -var _free: any +export var _malloc: any +export var _free: any -function abort (msg?: string): never { +export function abort (msg?: string): never { if (typeof WebAssembly.RuntimeError === 'function') { throw new WebAssembly.RuntimeError(msg) } throw Error(msg) } -function runtimeKeepalivePush (): void {} +export function runtimeKeepalivePush (): void {} -function runtimeKeepalivePop (): void {} +export function runtimeKeepalivePop (): void {} -var napiModule: INapiModule = { +export var napiModule: INapiModule = { imports: { env: {}, napi: {}, @@ -143,11 +147,11 @@ var napiModule: INapiModule = { } } -var emnapiCtx: Context -var emnapiNodeBinding: NodeBinding -var onCreateWorker: (info: { type: 'thread' | 'async-work' }) => any -var out: (str: string) => void -var err: (str: string) => void +export var emnapiCtx: Context +export var emnapiNodeBinding: NodeBinding +export var onCreateWorker: (info: { type: 'thread' | 'async-work' }) => any +export var out: (str: string) => void +export var err: (str: string) => void if (!ENVIRONMENT_IS_PTHREAD) { const context = options.context @@ -195,7 +199,7 @@ if ('nodeBinding' in options) { emnapiNodeBinding = nodeBinding } -var emnapiAsyncWorkPoolSize = 0 +export var emnapiAsyncWorkPoolSize = 0 if ('asyncWorkPoolSize' in options) { if (typeof options.asyncWorkPoolSize !== 'number') { throw new TypeError('options.asyncWorkPoolSize must be a integer') @@ -207,10 +211,282 @@ if ('asyncWorkPoolSize' in options) { emnapiAsyncWorkPoolSize = -1024 } } -var singleThreadAsyncWork = ENVIRONMENT_IS_PTHREAD ? false : (emnapiAsyncWorkPoolSize <= 0) +export var singleThreadAsyncWork = ENVIRONMENT_IS_PTHREAD ? false : (emnapiAsyncWorkPoolSize <= 0) -function __emnapi_async_work_pool_size (): number { +export function _emnapi_async_work_pool_size (): number { return Math.abs(emnapiAsyncWorkPoolSize) } -emnapiImplementInternal('_emnapi_async_work_pool_size', 'i', __emnapi_async_work_pool_size) +napiModule.imports.env._emnapi_async_work_pool_size = _emnapi_async_work_pool_size + +// ------------------------------ pthread ------------------------------- + +function emnapiAddSendListener (worker: any): boolean { + if (!worker) return false + if (worker._emnapiSendListener) return true + const handler = function (e: any): void { + const data = ENVIRONMENT_IS_NODE ? e : e.data + const __emnapi__ = data.__emnapi__ + if (__emnapi__ && __emnapi__.type === 'async-send') { + if (ENVIRONMENT_IS_PTHREAD) { + const postMessage = napiModule.postMessage! + postMessage({ __emnapi__ }) + } else { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const callback = __emnapi__.payload.callback + $makeDynCall('vp', 'callback')(__emnapi__.payload.data) + } + } + } + const dispose = function (): void { + if (ENVIRONMENT_IS_NODE) { + worker.off('message', handler) + } else { + worker.removeEventListener('message', handler, false) + } + delete worker._emnapiSendListener + } + worker._emnapiSendListener = { handler, dispose } + if (ENVIRONMENT_IS_NODE) { + worker.on('message', handler) + } else { + worker.addEventListener('message', handler, false) + } + return true +} + +napiModule.emnapi.addSendListener = emnapiAddSendListener + +function terminateWorker (worker: any): void { + const tid = worker.__emnapi_tid + worker.terminate() + worker.onmessage = (e: any) => { + if (e.data.__emnapi__) { + err('received "' + e.data.__emnapi__.type + '" command from terminated worker: ' + tid) + } + } +} + +function spawnThread (startArg: number, errorOrTid: number): number { + const isNewABI = errorOrTid !== undefined + if (!isNewABI) { + errorOrTid = _malloc($to64('8')) + if (!errorOrTid) { + return -48 /* ENOMEM */ + } + } + const struct = new Int32Array(wasmMemory.buffer, errorOrTid, 2) + Atomics.store(struct, 0, 0) + Atomics.store(struct, 1, 0) + + if (ENVIRONMENT_IS_PTHREAD) { + const postMessage = napiModule.postMessage! + postMessage({ + __emnapi__: { + type: 'spawn-thread', + payload: { + startArg, + errorOrTid + } + } + }) + Atomics.wait(struct, 1, 0) + const isError = Atomics.load(struct, 0) + const result = Atomics.load(struct, 1) + if (isNewABI) { + return isError + } + _free($to64('errorOrTid')) + return isError ? -result : result + } + + let worker: any + try { + worker = PThread.getNewWorker() + if (!worker) { + throw new Error('failed to get new worker') + } + } catch (e) { + const EAGAIN = 6 + + Atomics.store(struct, 0, 1) + Atomics.store(struct, 1, EAGAIN) + Atomics.notify(struct, 1) + + err(e.message) + if (isNewABI) { + return 1 + } + _free($to64('errorOrTid')) + return -EAGAIN + } + + const tid = PThread.nextWorkerID + 43 + + Atomics.store(struct, 0, 0) + Atomics.store(struct, 1, tid) + Atomics.notify(struct, 1) + + const WASI_THREADS_MAX_TID = 0x1FFFFFFF + PThread.nextWorkerID = (PThread.nextWorkerID + 1) % (WASI_THREADS_MAX_TID - 42) + PThread.pthreads[tid] = worker + worker.__emnapi_tid = tid + PThread.runningWorkers.push(worker) + if (ENVIRONMENT_IS_NODE) { + worker.ref() + } + + worker.postMessage({ + __emnapi__: { + type: 'start', + payload: { + tid, + arg: startArg + } + } + }) + + if (isNewABI) { + return 0 + } + _free($to64('errorOrTid')) + return tid +} + +function startThread (tid: number, startArg: number): void { + if (napiModule.childThread) { + if (typeof wasmInstance.exports.wasi_thread_start !== 'function') { + throw new TypeError('wasi_thread_start is not exported') + } + const postMessage = napiModule.postMessage! + ;(wasmInstance.exports.wasi_thread_start as Function)(tid, startArg) + postMessage({ + __emnapi__: { + type: 'cleanup-thread', + payload: { + tid + } + } + }) + } else { + throw new Error('startThread is only available in child threads') + } +} + +napiModule.spawnThread = spawnThread +napiModule.startThread = startThread + +export var PThread = { + unusedWorkers: [] as any[], + runningWorkers: [] as any[], + pthreads: Object.create(null), + nextWorkerID: 0, + init () {}, + returnWorkerToPool (worker: any) { + var tid = worker.__emnapi_tid + delete PThread.pthreads[tid] + PThread.unusedWorkers.push(worker) + PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1) + delete worker.__emnapi_tid + if (ENVIRONMENT_IS_NODE) { + worker.unref() + } + }, + loadWasmModuleToWorker: (worker: any) => { + if (worker.whenLoaded) return worker.whenLoaded + worker.whenLoaded = new Promise((resolve, reject) => { + worker.onmessage = function (e: any) { + if (e.data.__emnapi__) { + const type = e.data.__emnapi__.type + const payload = e.data.__emnapi__.payload + if (type === 'loaded') { + worker.loaded = true + if (ENVIRONMENT_IS_NODE && !worker.__emnapi_tid) { + worker.unref() + } + resolve(worker) + // if (payload.err) { + // err('failed to load in child thread: ' + (payload.err.message || payload.err)) + // } + } else if (type === 'spawn-thread') { + spawnThread(payload.startArg, payload.errorOrTid) + } else if (type === 'cleanup-thread') { + if (reuseWorker) { + PThread.returnWorkerToPool(worker) + } else { + delete PThread.pthreads[payload.tid] + PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1) + terminateWorker(worker) + delete worker.__emnapi_tid + } + } + } + } + worker.onerror = (e: any) => { + const message = 'worker sent an error!' + // if (worker.pthread_ptr) { + // message = 'Pthread ' + ptrToString(worker.pthread_ptr) + ' sent an error!' + // } + err(message + ' ' + e.message) + reject(e) + throw e + } + if (ENVIRONMENT_IS_NODE) { + worker.on('message', function (data: any) { + worker.onmessage({ + data + }) + }) + worker.on('error', function (e: any) { + worker.onerror(e) + }) + worker.on('detachedExit', function () {}) + } + // napiModule.emnapi.addSendListener(worker) + emnapiAddSendListener(worker) + if (typeof emnapiTSFN !== 'undefined') { + emnapiTSFN.addListener(worker) + } + try { + worker.postMessage({ + __emnapi__: { + type: 'load', + payload: { + wasmModule, + wasmMemory + } + } + }) + } catch (err) { + if (typeof SharedArrayBuffer === 'undefined' || !(wasmMemory.buffer instanceof SharedArrayBuffer)) { + throw new Error( + 'Multithread features require shared wasm memory. ' + + 'Try to compile with `-matomics -mbulk-memory` and use `--import-memory --shared-memory` during linking' + ) + } + throw err + } + }) + return worker.whenLoaded + }, + allocateUnusedWorker () { + if (typeof onCreateWorker !== 'function') { + throw new TypeError('`options.onCreateWorker` is not provided') + } + const worker = onCreateWorker({ type: 'thread' }) + PThread.unusedWorkers.push(worker) + return worker + }, + getNewWorker () { + if (reuseWorker) { + if (PThread.unusedWorkers.length === 0) { + const worker = PThread.allocateUnusedWorker() + PThread.loadWasmModuleToWorker(worker) + } + return PThread.unusedWorkers.pop() + } + const worker = PThread.allocateUnusedWorker() + PThread.loadWasmModuleToWorker(worker) + return worker + } +} diff --git a/packages/emnapi/src/core/memory.ts b/packages/emnapi/src/core/memory.ts index de1ba663..20a9a256 100644 --- a/packages/emnapi/src/core/memory.ts +++ b/packages/emnapi/src/core/memory.ts @@ -1,6 +1,12 @@ /* eslint-disable @typescript-eslint/indent */ -function _napi_adjust_external_memory ( +import { emnapiCtx } from 'emnapi:shared' +import { wasmMemory } from 'emnapi:emscripten-runtime' +import { $CHECK_ENV, $CHECK_ARG } from '../macro' +import { $emnapiSetValueI64 as emnapiSetValueI64 } from '../util' + +/** @__sig ipjp */ +export function napi_adjust_external_memory ( env: napi_env, change_in_bytes: bigint, adjusted_value: number @@ -31,5 +37,3 @@ function _napi_adjust_external_memory ( return envObject.clearLastError() } - -emnapiImplement('napi_adjust_external_memory', 'ipjp', _napi_adjust_external_memory, []) diff --git a/packages/emnapi/src/core/tsconfig.json b/packages/emnapi/src/core/tsconfig.json index 46d51a7a..b62d82d6 100644 --- a/packages/emnapi/src/core/tsconfig.json +++ b/packages/emnapi/src/core/tsconfig.json @@ -1,25 +1,18 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outFile": "../../dist/emnapi-core.js", - "plugins": [ - { - "transform": "../../transformer/out/macro.js" - }, - { - "transform": "../../transformer/out/index.js", - "defines": { - "MEMORY64": 0 - } - } - ] + "paths": { + "emnapi:shared": ["./init"], + "emnapi:emscripten-runtime": ["./init"] + } }, "include": [ + "./index.ts", + "./pthread.ts", "./init.ts", "./async.ts", "./miscellaneous.ts", "./string.ts", - "./util.ts", "./memory.ts", "../../../runtime/src/typings/**/*.d.ts", "../typings/**/*.d.ts", diff --git a/packages/emnapi/src/core/util.ts b/packages/emnapi/src/core/util.ts deleted file mode 100644 index df547aa8..00000000 --- a/packages/emnapi/src/core/util.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -function emnapiImplement (name: string, sig: string | undefined, compilerTimeFunction: Function, deps?: string[]): void { - napiModule.imports.napi[name] = compilerTimeFunction -} - -// emnapi_* -function emnapiImplement2 (name: string, sig: string | undefined, compilerTimeFunction: Function, deps?: string[]): void { - napiModule.imports.emnapi[name] = compilerTimeFunction -} - -// _emnapi_* -function emnapiImplementInternal (name: string, sig: string | undefined, compilerTimeFunction: Function, deps?: string[]): void { - napiModule.imports.env[name] = compilerTimeFunction -} - -// $emnapi* -function emnapiImplementHelper (_name: string, _sig: string | undefined, compilerTimeFunction: Function, _deps?: string[], exportName?: string): void { - if (exportName) { - napiModule.emnapi[exportName] = compilerTimeFunction - } -} - -function emnapiDefineVar (_name: string, _value: any, _deps?: string[], postset?: string): void { - if (typeof postset === 'function') { - (postset as () => void)() - } -} diff --git a/packages/emnapi/src/emnapi.ts b/packages/emnapi/src/emnapi.ts index 547951e1..00288b1f 100644 --- a/packages/emnapi/src/emnapi.ts +++ b/packages/emnapi/src/emnapi.ts @@ -1,4 +1,13 @@ -function _emnapi_create_memory_view ( +import { emnapiCtx, emnapiNodeBinding } from 'emnapi:shared' +import { wasmMemory } from 'emnapi:emscripten-runtime' +import { type MemoryViewDescriptor, type ArrayBufferPointer, emnapiExternalMemory } from './memory' +import { napi_add_finalizer } from './wrap' +import { $CHECK_ARG, $PREAMBLE, $CHECK_ENV } from './macro' + +/** + * @__sig ipippppp + */ +export function emnapi_create_memory_view ( env: napi_env, typedarray_type: emnapi_memory_view_type, external_data: void_p, @@ -86,7 +95,7 @@ function _emnapi_create_memory_view ( const handle = emnapiCtx.addToCurrentScope(typedArray) emnapiExternalMemory.wasmMemoryViewTable.set(typedArray, viewDescriptor) if (finalize_cb) { - const status = _napi_add_finalizer(env, handle.id, external_data, finalize_cb, finalize_hint, /* NULL */ 0) + const status = napi_add_finalizer(env, handle.id, external_data, finalize_cb, finalize_hint, /* NULL */ 0) if (status === napi_status.napi_pending_exception) { const err = envObject.tryCatch.extractException() envObject.clearLastError() @@ -101,19 +110,28 @@ function _emnapi_create_memory_view ( }) } -function emnapi_is_support_weakref (): int { +/** + * @__sig i + */ +export function emnapi_is_support_weakref (): int { return emnapiCtx.feature.supportFinalizer ? 1 : 0 } -function emnapi_is_support_bigint (): int { +/** + * @__sig i + */ +export function emnapi_is_support_bigint (): int { return emnapiCtx.feature.supportBigInt ? 1 : 0 } -function emnapi_is_node_binding_available (): int { +/** + * @__sig i + */ +export function emnapi_is_node_binding_available (): int { return emnapiNodeBinding ? 1 : 0 } -function emnapiSyncMemory ( +export function $emnapiSyncMemory ( js_to_wasm: boolean, arrayBufferOrView: T, offset?: number, @@ -166,7 +184,10 @@ function emnapiSyncMemory ( throw new TypeError('emnapiSyncMemory expect ArrayBuffer or ArrayBufferView as first parameter') } -function emnapi_sync_memory (env: napi_env, js_to_wasm: bool, arraybuffer_or_view: Pointer, offset: size_t, len: size_t): napi_status { +/** + * @__sig ipippp + */ +export function emnapi_sync_memory (env: napi_env, js_to_wasm: bool, arraybuffer_or_view: Pointer, offset: size_t, len: size_t): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -183,7 +204,7 @@ function emnapi_sync_memory (env: napi_env, js_to_wasm: bool, arraybuffer_or_vie if (!handle.isArrayBuffer() && !handle.isTypedArray() && !handle.isDataView()) { return envObject.setLastError(napi_status.napi_invalid_arg) } - const ret = emnapiSyncMemory(Boolean(js_to_wasm), handle.value, offset, len) + const ret = $emnapiSyncMemory(Boolean(js_to_wasm), handle.value, offset, len) if (handle.value !== ret) { $from64('arraybuffer_or_view') @@ -195,7 +216,7 @@ function emnapi_sync_memory (env: napi_env, js_to_wasm: bool, arraybuffer_or_vie }) } -function emnapiGetMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferView): ArrayBufferPointer { +export function $emnapiGetMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferView): ArrayBufferPointer { const isArrayBuffer = arrayBufferOrView instanceof ArrayBuffer const isDataView = arrayBufferOrView instanceof DataView const isTypedArray = ArrayBuffer.isView(arrayBufferOrView) && !isDataView @@ -216,7 +237,10 @@ function emnapiGetMemoryAddress (arrayBufferOrView: ArrayBuffer | ArrayBufferVie } } -function emnapi_get_memory_address (env: napi_env, arraybuffer_or_view: napi_value, address: Pointer, ownership: Pointer, runtime_allocated: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function emnapi_get_memory_address (env: napi_env, arraybuffer_or_view: napi_value, address: Pointer, ownership: Pointer, runtime_allocated: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let p: number, runtimeAllocated: number, ownershipOut: number let info: ArrayBufferPointer @@ -228,7 +252,7 @@ function emnapi_get_memory_address (env: napi_env, arraybuffer_or_view: napi_val } const handle: Handle = envObject.ctx.handleStore.get(arraybuffer_or_view)! - info = emnapiGetMemoryAddress(handle.value) + info = $emnapiGetMemoryAddress(handle.value) p = info.address if (address) { @@ -250,7 +274,10 @@ function emnapi_get_memory_address (env: napi_env, arraybuffer_or_view: napi_val }) } -function emnapi_get_runtime_version (env: napi_env, version: number): napi_status { +/** + * @__sig ipp + */ +export function emnapi_get_runtime_version (env: napi_env, version: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, version) @@ -275,15 +302,5 @@ function emnapi_get_runtime_version (env: napi_env, version: number): napi_statu return envObject.clearLastError() } -emnapiImplementHelper('$emnapiSyncMemory', undefined, emnapiSyncMemory, ['$emnapiExternalMemory'], 'syncMemory') -emnapiImplementHelper('$emnapiGetMemoryAddress', undefined, emnapiGetMemoryAddress, ['$emnapiExternalMemory'], 'getMemoryAddress') - -emnapiImplement2('emnapi_is_support_weakref', 'i', emnapi_is_support_weakref) -emnapiImplement2('emnapi_is_support_bigint', 'i', emnapi_is_support_bigint) -emnapiImplement2('emnapi_is_node_binding_available', 'i', emnapi_is_node_binding_available) - -emnapiImplement2('emnapi_create_memory_view', 'ipippppp', _emnapi_create_memory_view, ['napi_add_finalizer', '$emnapiExternalMemory']) -emnapiImplement2('emnapi_sync_memory', 'ipippp', emnapi_sync_memory, ['$emnapiSyncMemory']) -emnapiImplement2('emnapi_get_memory_address', 'ipppp', emnapi_get_memory_address, ['$emnapiGetMemoryAddress']) - -emnapiImplement2('emnapi_get_runtime_version', 'ipp', emnapi_get_runtime_version) +// emnapiImplementHelper('$emnapiSyncMemory', undefined, emnapiSyncMemory, ['$emnapiExternalMemory'], 'syncMemory') +// emnapiImplementHelper('$emnapiGetMemoryAddress', undefined, emnapiGetMemoryAddress, ['$emnapiExternalMemory'], 'getMemoryAddress') diff --git a/packages/emnapi/src/emscripten/async-work.ts b/packages/emnapi/src/emscripten/async-work.ts index d66ea6a6..0a1241d2 100644 --- a/packages/emnapi/src/emscripten/async-work.ts +++ b/packages/emnapi/src/emscripten/async-work.ts @@ -1,4 +1,11 @@ -function _napi_create_async_work (env: napi_env, resource: napi_value, resource_name: napi_value, execute: number, complete: number, data: number, result: number): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { emnapiAWST } from '../async-work' +import { $CHECK_ARG, $CHECK_ENV, $CHECK_ENV_NOT_IN_GC } from '../macro' + +/** + * @__sig ippppppp + */ +export function napi_create_async_work (env: napi_env, resource: napi_value, resource_name: napi_value, execute: number, complete: number, data: number, result: number): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, execute) $CHECK_ARG!(envObject, result) @@ -20,7 +27,10 @@ function _napi_create_async_work (env: napi_env, resource: napi_value, resource_ return envObject.clearLastError() } -function _napi_delete_async_work (env: napi_env, work: number): napi_status { +/** + * @__sig ipp + */ +export function napi_delete_async_work (env: napi_env, work: number): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, work) @@ -28,7 +38,10 @@ function _napi_delete_async_work (env: napi_env, work: number): napi_status { return envObject.clearLastError() } -function _napi_queue_async_work (env: napi_env, work: number): napi_status { +/** + * @__sig ipp + */ +export function napi_queue_async_work (env: napi_env, work: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, work) @@ -37,7 +50,10 @@ function _napi_queue_async_work (env: napi_env, work: number): napi_status { return envObject.clearLastError() } -function _napi_cancel_async_work (env: napi_env, work: number): napi_status { +/** + * @__sig ipp + */ +export function napi_cancel_async_work (env: napi_env, work: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, work) @@ -46,8 +62,3 @@ function _napi_cancel_async_work (env: napi_env, work: number): napi_status { if (status === napi_status.napi_ok) return envObject.clearLastError() return envObject.setLastError(status) } - -emnapiImplement('napi_create_async_work', 'ippppppp', _napi_create_async_work, ['$emnapiAWST']) -emnapiImplement('napi_delete_async_work', 'ipp', _napi_delete_async_work, ['$emnapiAWST']) -emnapiImplement('napi_queue_async_work', 'ipp', _napi_queue_async_work, ['$emnapiAWST']) -emnapiImplement('napi_cancel_async_work', 'ipp', _napi_cancel_async_work, ['$emnapiAWST']) diff --git a/packages/emnapi/src/emscripten/async.ts b/packages/emnapi/src/emscripten/async.ts index 7c2f01b9..60626cfe 100644 --- a/packages/emnapi/src/emscripten/async.ts +++ b/packages/emnapi/src/emscripten/async.ts @@ -1,83 +1,89 @@ -declare var PThread: any +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_PTHREAD, PThread } from 'emnapi:emscripten-runtime' +import { _emnapi_set_immediate, _emnapi_next_tick } from '../util' -mergeInto(LibraryManager.library, { - _emnapi_worker_unref__sig: 'vp', - _emnapi_worker_unref__deps: ['$PThread'], - _emnapi_worker_unref: function (pid: number): void { - let worker = PThread.pthreads[pid] - worker = worker.worker || worker - if (typeof worker.unref === 'function') { - worker.unref() - } - }, +/** + * @__deps $PThread + * @__sig vp + */ +export function _emnapi_worker_unref (pid: number): void { + let worker = PThread.pthreads[pid] + worker = worker.worker || worker + if (typeof worker.unref === 'function') { + worker.unref() + } +} - // if EMNAPI_USE_PROXYING=1 (default is 1 if emscripten version >= 3.1.9), - // the following helpers won't be linked into runtime code - $emnapiAddSendListener__deps: ['$PThread'], - $emnapiAddSendListener__postset: - 'PThread.unusedWorkers.forEach(emnapiAddSendListener);' + - 'PThread.runningWorkers.forEach(emnapiAddSendListener);' + - '(function () { var __original_getNewWorker = PThread.getNewWorker; PThread.getNewWorker = function () {' + - 'var r = __original_getNewWorker.apply(this, arguments);' + - 'emnapiAddSendListener(r);' + - 'return r;' + - '}; })();', - $emnapiAddSendListener: function (worker: any) { - if (!worker) return false - if (worker._emnapiSendListener) return true - const handler = function (e: any): void { - const data = ENVIRONMENT_IS_NODE ? e : e.data - const __emnapi__ = data.__emnapi__ - if (__emnapi__ && __emnapi__.type === 'async-send') { - if (ENVIRONMENT_IS_PTHREAD) { - postMessage({ __emnapi__ }) - } else { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const callback = __emnapi__.payload.callback - $makeDynCall('vp', 'callback')(__emnapi__.payload.data) - } - } - } - const dispose = function (): void { - if (ENVIRONMENT_IS_NODE) { - worker.off('message', handler) +/** + * if EMNAPI_USE_PROXYING=1 (default is 1 if emscripten version >= 3.1.9), + * the following helpers won't be linked into runtime code + * + * @__deps $PThread + * @__postset + * ``` + * PThread.unusedWorkers.forEach(emnapiAddSendListener); + * PThread.runningWorkers.forEach(emnapiAddSendListener); + * (function () { + * var __original_getNewWorker = PThread.getNewWorker; + * PThread.getNewWorker = function () { + * var r = __original_getNewWorker.apply(this, arguments); + * emnapiAddSendListener(r); + * return r; + * }; + * })(); + * ``` + */ +export function $emnapiAddSendListener (worker: any): boolean { + if (!worker) return false + if (worker._emnapiSendListener) return true + const handler = function (e: any): void { + const data = ENVIRONMENT_IS_NODE ? e : e.data + const __emnapi__ = data.__emnapi__ + if (__emnapi__ && __emnapi__.type === 'async-send') { + if (ENVIRONMENT_IS_PTHREAD) { + postMessage({ __emnapi__ }) } else { - worker.removeEventListener('message', handler, false) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const callback = __emnapi__.payload.callback + $makeDynCall('vp', 'callback')(__emnapi__.payload.data) } - delete worker._emnapiSendListener } - worker._emnapiSendListener = { handler, dispose } + } + const dispose = function (): void { if (ENVIRONMENT_IS_NODE) { - worker.on('message', handler) + worker.off('message', handler) } else { - worker.addEventListener('message', handler, false) + worker.removeEventListener('message', handler, false) } - return true - }, + delete worker._emnapiSendListener + } + worker._emnapiSendListener = { handler, dispose } + if (ENVIRONMENT_IS_NODE) { + worker.on('message', handler) + } else { + worker.addEventListener('message', handler, false) + } + return true +} - _emnapi_async_send_js__sig: 'vipp', - _emnapi_async_send_js__deps: [ - '$emnapiAddSendListener', - '_emnapi_set_immediate', - '_emnapi_next_tick' - ], - _emnapi_async_send_js: function (type: number, callback: number, data: number): void { - if (ENVIRONMENT_IS_PTHREAD) { - postMessage({ - __emnapi__: { - type: 'async-send', - payload: { - callback, - data - } +/** + * @__sig vipp + */ +export function _emnapi_async_send_js (type: number, callback: number, data: number): void { + if (ENVIRONMENT_IS_PTHREAD) { + postMessage({ + __emnapi__: { + type: 'async-send', + payload: { + callback, + data } - }) - } else { - switch (type) { - case 0: __emnapi_set_immediate(callback, data); break - case 1: __emnapi_next_tick(callback, data); break - default: break } + }) + } else { + switch (type) { + case 0: _emnapi_set_immediate(callback, data); break + case 1: _emnapi_next_tick(callback, data); break + default: break } } -}) +} diff --git a/packages/emnapi/src/emscripten/emnapi.ts b/packages/emnapi/src/emscripten/emnapi.ts index a36ff553..a937352d 100644 --- a/packages/emnapi/src/emscripten/emnapi.ts +++ b/packages/emnapi/src/emscripten/emnapi.ts @@ -1,4 +1,13 @@ -function emnapi_get_module_object (env: napi_env, result: Pointer): napi_status { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { emnapiCtx } from 'emnapi:shared' +import { emnapiString } from '../string' +import { $CHECK_ARG, $PREAMBLE } from '../macro' +import { Module } from 'emnapi:emscripten-runtime' + +/** + * @__sig ipp + */ +export function emnapi_get_module_object (env: napi_env, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -13,7 +22,10 @@ function emnapi_get_module_object (env: napi_env, result: Pointer): }) } -function emnapi_get_module_property (env: napi_env, utf8name: const_char_p, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function emnapi_get_module_property (env: napi_env, utf8name: const_char_p, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -29,5 +41,3 @@ function emnapi_get_module_property (env: napi_env, utf8name: const_char_p, resu return envObject.getReturnStatus() }) } -emnapiImplement2('emnapi_get_module_object', 'ipp', emnapi_get_module_object) -emnapiImplement2('emnapi_get_module_property', 'ippp', emnapi_get_module_property, ['$emnapiString']) diff --git a/packages/emnapi/src/emscripten/index.ts b/packages/emnapi/src/emscripten/index.ts new file mode 100644 index 00000000..a6f8fa2b --- /dev/null +++ b/packages/emnapi/src/emscripten/index.ts @@ -0,0 +1,40 @@ +export { + emnapiInit as $emnapiInit, + emnapiCtx as $emnapiCtx, + emnapiAsyncWorkPoolSize as $emnapiAsyncWorkPoolSize, + emnapiNodeBinding as $emnapiNodeBinding, + _emnapi_async_work_pool_size +} from 'emnapi:shared' + +export * from './memory' +export * from './async' +export * from './async-work' +export * from './emnapi' + +export * from '../util' +export * from '../value/convert2c' +export * from '../value/convert2napi' +export * from '../value/create' +export * from '../value/global' +export * from '../wrap' +export * from '../env' +export * from '../emnapi' +export * from '../error' +export * from '../function' +export * from '../life' +export * from '../miscellaneous' +export * from '../node' +export * from '../promise' +export * from '../property' +export * from '../script' +export { + napi_create_threadsafe_function, + napi_get_threadsafe_function_context, + napi_call_threadsafe_function, + napi_acquire_threadsafe_function, + napi_release_threadsafe_function, + napi_unref_threadsafe_function, + napi_ref_threadsafe_function +} from '../threadsafe-function' +export * from '../value-operation' +export * from '../version' diff --git a/packages/emnapi/src/emscripten/init.ts b/packages/emnapi/src/emscripten/init.ts index 2c884fa8..216ac716 100644 --- a/packages/emnapi/src/emscripten/init.ts +++ b/packages/emnapi/src/emscripten/init.ts @@ -2,43 +2,43 @@ /* eslint-disable no-new-func */ /* eslint-disable @typescript-eslint/no-implied-eval */ +import { abort } from 'emnapi:emscripten-runtime' + // declare const global: typeof globalThis // declare const require: any // declare const process: any // declare const __webpack_public_path__: any -declare var emnapiCtx: Context -// eslint-disable-next-line @typescript-eslint/no-unused-vars -declare var emnapiNodeBinding: NodeBinding -declare var emnapiAsyncWorkPoolSize: number - declare function _napi_register_wasm_v1 (env: Ptr, exports: Ptr): napi_value declare function _node_api_module_get_api_version_v1 (): number -declare const emnapiModule: { - exports: any - loaded: boolean - filename: string - envObject?: Env -} - -declare interface InitOptions { +export interface InitOptions { context: Context filename?: string asyncWorkPoolSize?: number nodeBinding?: NodeBinding } -emnapiDefineVar('$emnapiCtx', undefined) -emnapiDefineVar('$emnapiNodeBinding', undefined) -emnapiDefineVar('$emnapiModule', { +export var emnapiCtx: Context = undefined! +export var emnapiNodeBinding: NodeBinding = undefined! +export var emnapiAsyncWorkPoolSize: number = 0 + +const emnapiModule: { + exports: any + loaded: boolean + filename: string + envObject?: Env +} = { exports: {}, loaded: false, filename: '' -}) -emnapiDefineVar('$emnapiAsyncWorkPoolSize', 0) +} -function emnapiInit (options: InitOptions): any { +/** + * @__deps napi_register_wasm_v1 + * @__deps node_api_module_get_api_version_v1 + */ +export function emnapiInit (options: InitOptions): any { if (emnapiModule.loaded) return emnapiModule.exports if (typeof options !== 'object' || options === null) { @@ -106,15 +106,11 @@ function emnapiInit (options: InitOptions): any { return emnapiModule.exports } -emnapiImplementHelper( - '$emnapiInit', - undefined, - emnapiInit, - ['$emnapiModule', '$emnapiCtx', '$emnapiNodeBinding', '$emnapiAsyncWorkPoolSize', 'napi_register_wasm_v1', 'node_api_module_get_api_version_v1'] -) +export { emnapiInit as $emnapiInit } -function __emnapi_async_work_pool_size (): number { +/** + * @__sig i + */ +export function _emnapi_async_work_pool_size (): number { return Math.abs(emnapiAsyncWorkPoolSize) } - -emnapiImplementInternal('_emnapi_async_work_pool_size', 'i', __emnapi_async_work_pool_size, ['$emnapiAsyncWorkPoolSize']) diff --git a/packages/emnapi/src/emscripten/memory.ts b/packages/emnapi/src/emscripten/memory.ts index 61c1879c..5524e78b 100644 --- a/packages/emnapi/src/emscripten/memory.ts +++ b/packages/emnapi/src/emscripten/memory.ts @@ -1,7 +1,16 @@ +import { wasmMemory } from 'emnapi:emscripten-runtime' +import { emnapiCtx } from 'emnapi:shared' +import { $emnapiSetValueI64 as emnapiSetValueI64 } from '../util' +import { $CHECK_ENV } from '../macro' + /* eslint-disable @typescript-eslint/indent */ declare function _emscripten_resize_heap (requested: number): boolean -function _napi_adjust_external_memory ( +/** + * @__deps emscripten_resize_heap + * @__sig ipjp + */ +export function napi_adjust_external_memory ( env: napi_env, low: number, high: number, @@ -46,12 +55,3 @@ function _napi_adjust_external_memory ( return envObject.clearLastError() } - -emnapiImplement('napi_adjust_external_memory', 'ipjp', _napi_adjust_external_memory, - [ -// #if WASM_BIGINT - '$emnapiSetValueI64', -// #endif - 'emscripten_resize_heap' - ] -) diff --git a/packages/emnapi/src/emscripten/runtime.d.ts b/packages/emnapi/src/emscripten/runtime.d.ts index dfd2cf88..2f6418ed 100644 --- a/packages/emnapi/src/emscripten/runtime.d.ts +++ b/packages/emnapi/src/emscripten/runtime.d.ts @@ -5,35 +5,19 @@ declare const LibraryManager: { declare function mergeInto (target: any, source: Record): void -// runtime -declare var wasmMemory: WebAssembly.Memory -declare var ENVIRONMENT_IS_NODE: boolean -declare var ENVIRONMENT_IS_PTHREAD: boolean +declare module 'emnapi:emscripten-runtime' { + export const wasmMemory: WebAssembly.Memory + export const ENVIRONMENT_IS_NODE: boolean + export const ENVIRONMENT_IS_PTHREAD: boolean -// declare type I64Type = 'i64' -// declare type I32Type = 'i1' | 'i8' | 'i16' | 'i32' | 'float' | 'double' -// declare type ValueType = I32Type | I64Type -// declare type PointerType = '*' | `${ValueType}*` + export function _free (ptr: void_p): void + export function _malloc (size: number | bigint): void_p -// declare function getValue (ptr: number): number -// declare function getValue (ptr: number, type: I64Type): bigint -// declare function getValue (ptr: number, type: I32Type | PointerType): number -// declare function setValue (ptr: number, value: number | bigint, type: ValueType | PointerType): void + export function abort (msg?: string): never -declare const Module: any + export function runtimeKeepalivePush (): void + export function runtimeKeepalivePop (): void -// declare function allocateUTF8 (str: string): char_p -declare function _free (ptr: void_p): void -declare function _malloc (size: number | bigint): void_p - -// declare type LifecycleCallback = { -// func: (arg: Arg) => void -// arg: Arg -// } -// declare const __ATINIT__: Array<(Module: any) => void> -// declare function addOnInit (callback: number | ((Module: any) => void) | LifecycleCallback): void -// declare function addOnExit (callback: number | ((Module: any) => void) | LifecycleCallback): void -declare function abort (msg?: string): never - -declare function runtimeKeepalivePush (): void -declare function runtimeKeepalivePop (): void + export const Module: any + export const PThread: any +} diff --git a/packages/emnapi/src/emscripten/util.ts b/packages/emnapi/src/emscripten/util.ts deleted file mode 100644 index bdcda61a..00000000 --- a/packages/emnapi/src/emscripten/util.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -function emnapiImplement (name: string, sig: string | undefined, compilerTimeFunction: Function, deps?: string[]): void { - const sym: any = { - [name]: compilerTimeFunction, - [name + '__deps']: (['$emnapiInit']).concat(deps ?? []) - } - if (sig) { - sym[name + '__sig'] = sig - } - mergeInto(LibraryManager.library, sym) -} - -// emnapi_* -function emnapiImplement2 (...args: Parameters): void -function emnapiImplement2 (): void { - emnapiImplement.apply(null, arguments as any) -} - -// _emnapi_* -function emnapiImplementInternal (...args: Parameters): void -function emnapiImplementInternal (): void { - emnapiImplement.apply(null, arguments as any) -} - -// $emnapi* -function emnapiImplementHelper (name: string, sig: string | undefined, compilerTimeFunction: Function, deps?: string[], _exportName?: string): void { - emnapiImplement(name, sig, compilerTimeFunction, deps) -} - -function emnapiDefineVar (name: string, value: any, deps?: string[], postset?: string): void { - const obj = { - [name]: value - } - if (deps) { - obj[name + '__deps'] = deps - } - if (postset) { - obj[name + '__postset'] = postset - } - mergeInto(LibraryManager.library, obj) -} diff --git a/packages/emnapi/src/env.ts b/packages/emnapi/src/env.ts index 62de3758..b8ea2782 100644 --- a/packages/emnapi/src/env.ts +++ b/packages/emnapi/src/env.ts @@ -1,4 +1,8 @@ -function napi_set_instance_data (env: napi_env, data: void_p, finalize_cb: napi_finalize, finalize_hint: void_p): napi_status { +import { emnapiCtx } from 'emnapi:shared' +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') @@ -8,7 +12,8 @@ function napi_set_instance_data (env: napi_env, data: void_p, finalize_cb: napi_ return envObject.clearLastError() } -function napi_get_instance_data (env: napi_env, data: void_pp): napi_status { +/** @__sig ipp */ +export function napi_get_instance_data (env: napi_env, data: void_pp): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, data) @@ -19,6 +24,3 @@ function napi_get_instance_data (env: napi_env, data: void_pp): napi_status { $makeSetValue('data', 0, 'value', '*') return envObject.clearLastError() } - -emnapiImplement('napi_set_instance_data', 'ipppp', napi_set_instance_data) -emnapiImplement('napi_get_instance_data', 'ipp', napi_get_instance_data) diff --git a/packages/emnapi/src/error.ts b/packages/emnapi/src/error.ts index 865386f3..8c932055 100644 --- a/packages/emnapi/src/error.ts +++ b/packages/emnapi/src/error.ts @@ -1,4 +1,10 @@ -function __emnapi_get_last_error_info (env: napi_env, error_code: Pointer, engine_error_code: Pointer, engine_reserved: void_pp): void { +import { abort } from 'emnapi:emscripten-runtime' +import { emnapiCtx, emnapiNodeBinding } from 'emnapi:shared' +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') @@ -18,7 +24,8 @@ function __emnapi_get_last_error_info (env: napi_env, error_code: Pointer { $CHECK_ARG!(envObject, error) envObject.tryCatch.setError(emnapiCtx.handleStore.get(error)!.value) @@ -26,7 +33,8 @@ function napi_throw (env: napi_env, error: napi_value): napi_status { }) } -function napi_throw_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { +/** @__sig ippp */ +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') @@ -40,7 +48,8 @@ function napi_throw_error (env: napi_env, code: const_char_p, msg: const_char_p) }) } -function napi_throw_type_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { +/** @__sig ippp */ +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') @@ -54,7 +63,8 @@ function napi_throw_type_error (env: napi_env, code: const_char_p, msg: const_ch }) } -function napi_throw_range_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { +/** @__sig ippp */ +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') @@ -68,7 +78,8 @@ function napi_throw_range_error (env: napi_env, code: const_char_p, msg: const_c }) } -function node_api_throw_syntax_error (env: napi_env, code: const_char_p, msg: const_char_p): napi_status { +/** @__sig ippp */ +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') @@ -82,7 +93,8 @@ function node_api_throw_syntax_error (env: napi_env, code: const_char_p, msg: co }) } -function napi_is_exception_pending (env: napi_env, result: Pointer): napi_status { +/** @__sig ipp */ +export function napi_is_exception_pending (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -92,7 +104,8 @@ function napi_is_exception_pending (env: napi_env, result: Pointer): napi_ return envObject.clearLastError() } -function napi_create_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_create_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, msg) $CHECK_ARG!(envObject, result) @@ -118,7 +131,8 @@ function napi_create_error (env: napi_env, code: napi_value, msg: napi_value, re return envObject.clearLastError() } -function napi_create_type_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_create_type_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, msg) $CHECK_ARG!(envObject, result) @@ -143,7 +157,8 @@ function napi_create_type_error (env: napi_env, code: napi_value, msg: napi_valu return envObject.clearLastError() } -function napi_create_range_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_create_range_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, msg) $CHECK_ARG!(envObject, result) @@ -167,7 +182,8 @@ function napi_create_range_error (env: napi_env, code: napi_value, msg: napi_val return envObject.clearLastError() } -function node_api_create_syntax_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function node_api_create_syntax_error (env: napi_env, code: napi_value, msg: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, msg) $CHECK_ARG!(envObject, result) @@ -191,7 +207,8 @@ function node_api_create_syntax_error (env: napi_env, code: napi_value, msg: nap return envObject.clearLastError() } -function napi_get_and_clear_last_exception (env: napi_env, result: Pointer): napi_status { +/** @__sig ipp */ +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') @@ -209,7 +226,8 @@ function napi_get_and_clear_last_exception (env: napi_env, result: Pointer { $CHECK_ARG!(envObject, err) const error = envObject.ctx.handleStore.get(err)! @@ -236,19 +255,3 @@ function napi_fatal_exception (env: napi_env, err: napi_value): napi_status { return envObject.clearLastError() }) } - -emnapiImplementInternal('_emnapi_get_last_error_info', 'vpppp', __emnapi_get_last_error_info) - -emnapiImplement('napi_get_and_clear_last_exception', 'ipp', napi_get_and_clear_last_exception) -emnapiImplement('napi_throw', 'ipp', napi_throw) -emnapiImplement('napi_throw_error', 'ippp', napi_throw_error, ['$emnapiString']) -emnapiImplement('napi_throw_type_error', 'ippp', napi_throw_type_error, ['$emnapiString']) -emnapiImplement('napi_throw_range_error', 'ippp', napi_throw_range_error, ['$emnapiString']) -emnapiImplement('node_api_throw_syntax_error', 'ippp', node_api_throw_syntax_error, ['$emnapiString']) -emnapiImplement('napi_create_error', 'ipppp', napi_create_error, ['$emnapiString']) -emnapiImplement('napi_create_type_error', 'ipppp', napi_create_type_error, ['$emnapiString']) -emnapiImplement('napi_create_range_error', 'ipppp', napi_create_range_error, ['$emnapiString']) -emnapiImplement('node_api_create_syntax_error', 'ipppp', node_api_create_syntax_error, ['$emnapiString']) -emnapiImplement('napi_is_exception_pending', 'ipp', napi_is_exception_pending) -emnapiImplement('napi_fatal_error', 'vpppp', napi_fatal_error, ['$emnapiString']) -emnapiImplement('napi_fatal_exception', 'ipp', napi_fatal_exception) diff --git a/packages/emnapi/src/function.ts b/packages/emnapi/src/function.ts index c77c8a83..7657af3a 100644 --- a/packages/emnapi/src/function.ts +++ b/packages/emnapi/src/function.ts @@ -1,4 +1,9 @@ -function napi_create_function (env: napi_env, utf8name: Pointer, length: size_t, cb: napi_callback, data: void_p, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { emnapiCreateFunction } from './internal' +import { $PREAMBLE, $CHECK_ARG, $CHECK_ENV, $CHECK_ENV_NOT_IN_GC } from './macro' + +/** @__sig ipppppp */ +export function napi_create_function (env: napi_env, utf8name: Pointer, length: size_t, cb: napi_callback, data: void_p, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -20,7 +25,8 @@ function napi_create_function (env: napi_env, utf8name: Pointer, len }) } -function napi_get_cb_info (env: napi_env, cbinfo: napi_callback_info, argc: Pointer, argv: Pointer, this_arg: Pointer, data: void_pp): napi_status { +/** @__sig ipppppp */ +export function napi_get_cb_info (env: napi_env, cbinfo: napi_callback_info, argc: Pointer, argv: Pointer, this_arg: Pointer, data: void_pp): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! if (!cbinfo) return envObject.setLastError(napi_status.napi_invalid_arg) @@ -66,7 +72,8 @@ function napi_get_cb_info (env: napi_env, cbinfo: napi_callback_info, argc: Poin return envObject.clearLastError() } -function napi_call_function ( +/** @__sig ipppppp */ +export function napi_call_function ( env: napi_env, recv: napi_value, func: napi_value, @@ -107,7 +114,8 @@ function napi_call_function ( }) } -function napi_new_instance ( +/** @__sig ippppp */ +export function napi_new_instance ( env: napi_env, constructor: napi_value, argc: size_t, @@ -158,7 +166,8 @@ function napi_new_instance ( }) } -function napi_get_new_target ( +/** @__sig ippp */ +export function napi_get_new_target ( env: napi_env, cbinfo: napi_callback_info, result: Pointer @@ -175,9 +184,3 @@ function napi_get_new_target ( $makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } - -emnapiImplement('napi_create_function', 'ipppppp', napi_create_function, ['$emnapiCreateFunction']) -emnapiImplement('napi_get_cb_info', 'ipppppp', napi_get_cb_info) -emnapiImplement('napi_call_function', 'ipppppp', napi_call_function) -emnapiImplement('napi_new_instance', 'ippppp', napi_new_instance) -emnapiImplement('napi_get_new_target', 'ippp', napi_get_new_target) diff --git a/packages/emnapi/src/internal.ts b/packages/emnapi/src/internal.ts index 3b5f4e10..5fcae8af 100644 --- a/packages/emnapi/src/internal.ts +++ b/packages/emnapi/src/internal.ts @@ -2,7 +2,12 @@ /* eslint-disable no-new-func */ /* eslint-disable @typescript-eslint/no-implied-eval */ -function emnapiCreateFunction any> (envObject: Env, utf8name: Pointer, length: size_t, cb: napi_callback, data: void_p): { status: napi_status; f: F } { +import { emnapiCtx } from 'emnapi:shared' +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') const functionName = (!utf8name || !length) ? '' : (emnapiString.UTF8ToString(utf8name, length)) @@ -58,7 +63,7 @@ function emnapiCreateFunction any> (envObject: Env return { status: napi_status.napi_ok, f } } -function emnapiDefineProperty (envObject: Env, obj: object, propertyName: string | symbol, method: napi_callback, getter: napi_callback, setter: napi_callback, value: napi_value, attributes: number, data: void_p): void { +export function emnapiDefineProperty (envObject: Env, obj: object, propertyName: string | symbol, method: napi_callback, getter: napi_callback, setter: napi_callback, value: napi_value, attributes: number, data: void_p): void { if (getter || setter) { let localGetter: () => any let localSetter: (v: any) => void @@ -95,7 +100,7 @@ function emnapiDefineProperty (envObject: Env, obj: object, propertyName: string } } -function emnapiGetHandle (js_object: napi_value): { status: napi_status; handle?: Handle } { +export function emnapiGetHandle (js_object: napi_value): { status: napi_status; handle?: Handle } { let handle = emnapiCtx.handleStore.get(js_object)! if (!(handle.isObject() || handle.isFunction())) { return { status: napi_status.napi_invalid_arg } @@ -110,7 +115,7 @@ function emnapiGetHandle (js_object: napi_value): { status: napi_status; handle? return { status: napi_status.napi_ok, handle } } -function emnapiWrap (env: napi_env, js_object: napi_value, native_object: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { +export function emnapiWrap (env: napi_env, js_object: napi_value, native_object: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let referenceId: number return $PREAMBLE!(env, (envObject) => { @@ -150,7 +155,7 @@ function emnapiWrap (env: napi_env, js_object: napi_value, native_object: void_p }) } -function emnapiUnwrap (env: napi_env, js_object: napi_value, result: void_pp, action: UnwrapAction): napi_status { +export function emnapiUnwrap (env: napi_env, js_object: napi_value, result: void_pp, action: UnwrapAction): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let data: number return $PREAMBLE!(env, (envObject) => { @@ -185,9 +190,3 @@ function emnapiUnwrap (env: napi_env, js_object: napi_value, result: void_pp, ac return envObject.getReturnStatus() }) } - -emnapiImplementHelper('$emnapiCreateFunction', undefined, emnapiCreateFunction, ['$emnapiString']) -emnapiImplementHelper('$emnapiDefineProperty', undefined, emnapiDefineProperty, ['$emnapiCreateFunction']) -emnapiImplementHelper('$emnapiGetHandle', undefined, emnapiGetHandle) -emnapiImplementHelper('$emnapiWrap', undefined, emnapiWrap, ['$emnapiGetHandle']) -emnapiImplementHelper('$emnapiUnwrap', undefined, emnapiUnwrap) diff --git a/packages/emnapi/src/life.ts b/packages/emnapi/src/life.ts index 2860003a..4ee41e8f 100644 --- a/packages/emnapi/src/life.ts +++ b/packages/emnapi/src/life.ts @@ -1,4 +1,8 @@ -function napi_open_handle_scope (env: napi_env, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG, $CHECK_ENV } from './macro' + +/** @__sig ipp */ +export function napi_open_handle_scope (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -8,7 +12,8 @@ function napi_open_handle_scope (env: napi_env, result: Pointer): napi_status { +/** @__sig ipp */ +export function napi_open_escapable_handle_scope (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -29,7 +35,8 @@ function napi_open_escapable_handle_scope (env: napi_env, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_escape_handle (env: napi_env, scope: napi_escapable_handle_scope, escapee: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, scope) $CHECK_ARG!(envObject, escapee) @@ -59,7 +67,8 @@ function napi_escape_handle (env: napi_env, scope: napi_escapable_handle_scope, return envObject.setLastError(napi_status.napi_escape_called_twice) } -function napi_create_reference ( +/** @__sig ippip */ +export function napi_create_reference ( env: napi_env, value: napi_value, initial_refcount: uint32_t, @@ -82,7 +91,8 @@ function napi_create_reference ( return envObject.clearLastError() } -function napi_delete_reference ( +/** @__sig ipp */ +export function napi_delete_reference ( env: napi_env, ref: napi_ref ): napi_status { @@ -92,7 +102,8 @@ function napi_delete_reference ( return envObject.clearLastError() } -function napi_reference_ref ( +/** @__sig ippp */ +export function napi_reference_ref ( env: napi_env, ref: napi_ref, result: Pointer @@ -108,7 +119,8 @@ function napi_reference_ref ( return envObject.clearLastError() } -function napi_reference_unref ( +/** @__sig ippp */ +export function napi_reference_unref ( env: napi_env, ref: napi_ref, result: Pointer @@ -130,7 +142,8 @@ function napi_reference_unref ( return envObject.clearLastError() } -function napi_get_reference_value ( +/** @__sig ippp */ +export function napi_get_reference_value ( env: napi_env, ref: napi_ref, result: Pointer @@ -146,7 +159,8 @@ function napi_get_reference_value ( return envObject.clearLastError() } -function napi_add_env_cleanup_hook (env: napi_env, fun: number, arg: number): napi_status { +/** @__sig ippp */ +export function napi_add_env_cleanup_hook (env: napi_env, fun: number, arg: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, fun) @@ -159,7 +173,8 @@ function napi_add_env_cleanup_hook (env: napi_env, fun: number, arg: number): na return napi_status.napi_ok } -function napi_remove_env_cleanup_hook (env: napi_env, fun: number, arg: number): napi_status { +/** @__sig ippp */ +export function napi_remove_env_cleanup_hook (env: napi_env, fun: number, arg: number): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, fun) @@ -172,30 +187,14 @@ function napi_remove_env_cleanup_hook (env: napi_env, fun: number, arg: number): return napi_status.napi_ok } -function __emnapi_env_ref (env: napi_env): void { +/** @__sig vp */ +export function _emnapi_env_ref (env: napi_env): void { const envObject = emnapiCtx.envStore.get(env)! envObject.ref() } -function __emnapi_env_unref (env: napi_env): void { +/** @__sig vp */ +export function _emnapi_env_unref (env: napi_env): void { const envObject = emnapiCtx.envStore.get(env)! envObject.unref() } - -emnapiImplement('napi_open_handle_scope', 'ipp', napi_open_handle_scope) -emnapiImplement('napi_close_handle_scope', 'ipp', napi_close_handle_scope) -emnapiImplement('napi_open_escapable_handle_scope', 'ipp', napi_open_escapable_handle_scope) -emnapiImplement('napi_close_escapable_handle_scope', 'ipp', napi_close_escapable_handle_scope) -emnapiImplement('napi_escape_handle', 'ipppp', napi_escape_handle) - -emnapiImplement('napi_create_reference', 'ippip', napi_create_reference) -emnapiImplement('napi_delete_reference', 'ipp', napi_delete_reference) -emnapiImplement('napi_reference_ref', 'ippp', napi_reference_ref) -emnapiImplement('napi_reference_unref', 'ippp', napi_reference_unref) -emnapiImplement('napi_get_reference_value', 'ippp', napi_get_reference_value) - -emnapiImplement('napi_add_env_cleanup_hook', 'ippp', napi_add_env_cleanup_hook) -emnapiImplement('napi_remove_env_cleanup_hook', 'ippp', napi_remove_env_cleanup_hook) - -emnapiImplementInternal('_emnapi_env_ref', 'vp', __emnapi_env_ref) -emnapiImplementInternal('_emnapi_env_unref', 'vp', __emnapi_env_unref) diff --git a/packages/emnapi/src/macro.ts b/packages/emnapi/src/macro.ts index ca128320..10c1cfb5 100644 --- a/packages/emnapi/src/macro.ts +++ b/packages/emnapi/src/macro.ts @@ -5,22 +5,22 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /** @macro */ -function $CHECK_ENV (env: napi_env): any { +export function $CHECK_ENV (env: napi_env): any { if (!env) return napi_status.napi_invalid_arg } /** @macro */ -function $RETURN_STATUS_IF_FALSE (env: Env, condition: any, status: napi_status): any { +export function $RETURN_STATUS_IF_FALSE (env: Env, condition: any, status: napi_status): any { if (!condition) return env.setLastError(status) } /** @macro */ -function $CHECK_ARG (env: Env, arg: void_p) { +export function $CHECK_ARG (env: Env, arg: void_p) { $RETURN_STATUS_IF_FALSE!(env, arg, napi_status.napi_invalid_arg) } /** @macro */ -function $PREAMBLE (env: napi_env, fn: (envObject: Env) => napi_status): napi_status { +export function $PREAMBLE (env: napi_env, fn: (envObject: Env) => napi_status): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $RETURN_STATUS_IF_FALSE!( envObject, envObject.tryCatch.isEmpty(), napi_status.napi_pending_exception) @@ -41,14 +41,15 @@ function $PREAMBLE (env: napi_env, fn: (envObject: Env) => napi_status): napi_st } /** @macro */ -function $CHECK_ENV_NOT_IN_GC (env: napi_env): any { +export function $CHECK_ENV_NOT_IN_GC (env: napi_env): any { $CHECK_ENV!(env) + // @ts-expect-error const envObject = emnapiCtx.envStore.get(env)! envObject.checkGCAccess() } /** @macro */ -function $CHECK_NEW_STRING_ARGS (env: napi_env, str: const_char_p, length: number, result: Pointer): any { +export function $CHECK_NEW_STRING_ARGS (env: napi_env, str: const_char_p, length: number, result: Pointer): any { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) const autoLength = length === -1 const sizelength = length >>> 0 diff --git a/packages/emnapi/src/memory.ts b/packages/emnapi/src/memory.ts index 720a8eec..dbfc4d71 100644 --- a/packages/emnapi/src/memory.ts +++ b/packages/emnapi/src/memory.ts @@ -1,4 +1,7 @@ -declare type ViewConstuctor = +import { _free, wasmMemory, _malloc } from 'emnapi:emscripten-runtime' +import { emnapiCtx } from 'emnapi:shared' + +export type ViewConstuctor = Int8ArrayConstructor | Uint8ArrayConstructor | Uint8ClampedArrayConstructor | @@ -13,22 +16,30 @@ declare type ViewConstuctor = DataViewConstructor | BufferCtor -declare interface ArrayBufferPointer { +export interface ArrayBufferPointer { address: void_p ownership: Ownership runtimeAllocated: 0 | 1 } -declare interface MemoryViewDescriptor extends ArrayBufferPointer { +export interface MemoryViewDescriptor extends ArrayBufferPointer { Ctor: ViewConstuctor length: number } -declare interface ViewPointer extends ArrayBufferPointer { +export interface ViewPointer extends ArrayBufferPointer { view: T } -const emnapiExternalMemory: { +/** + * @__deps malloc + * @__deps free + * @__postset + * ``` + * emnapiExternalMemory.init(); + * ``` + */ +export const emnapiExternalMemory: { registry: FinalizationRegistry | undefined table: WeakMap wasmMemoryViewTable: WeakMap @@ -151,10 +162,3 @@ const emnapiExternalMemory: { return { address: address === 0 ? 0 : (address + view.byteOffset), ownership, runtimeAllocated, view } } } - -emnapiDefineVar( - '$emnapiExternalMemory', - emnapiExternalMemory, - ['malloc', 'free', '$emnapiInit'], - 'emnapiExternalMemory.init();' -) diff --git a/packages/emnapi/src/miscellaneous.ts b/packages/emnapi/src/miscellaneous.ts index c0dcc761..8573747c 100644 --- a/packages/emnapi/src/miscellaneous.ts +++ b/packages/emnapi/src/miscellaneous.ts @@ -1,4 +1,8 @@ -function __emnapi_get_filename (env: napi_env, buf: char_p, len: int): int { +import { emnapiCtx } from 'emnapi:shared' +import { emnapiString } from './string' + +/** @__sig ippi */ +export function _emnapi_get_filename (env: napi_env, buf: char_p, len: int): int { const envObject = emnapiCtx.envStore.get(env)! const filename = (envObject as NodeEnv).filename if (!buf) { @@ -6,5 +10,3 @@ function __emnapi_get_filename (env: napi_env, buf: char_p, len: int): int { } return emnapiString.stringToUTF8(filename, buf, len) } - -emnapiImplementInternal('_emnapi_get_filename', 'ippi', __emnapi_get_filename, ['$emnapiString']) diff --git a/packages/emnapi/src/node.ts b/packages/emnapi/src/node.ts index 66b9c582..56b9bdcb 100644 --- a/packages/emnapi/src/node.ts +++ b/packages/emnapi/src/node.ts @@ -1,4 +1,8 @@ -function __emnapi_node_emit_async_init ( +import { emnapiNodeBinding, emnapiCtx } from 'emnapi:shared' +import { $PREAMBLE, $CHECK_ARG } from './macro' + +/** @__sig vppdp */ +export function _emnapi_node_emit_async_init ( async_resource: napi_value, async_resource_name: napi_value, trigger_async_id: double, @@ -18,7 +22,8 @@ function __emnapi_node_emit_async_init ( } } -function __emnapi_node_emit_async_destroy (async_id: double, trigger_async_id: double): void { +/** @__sig vdd */ +export function _emnapi_node_emit_async_destroy (async_id: double, trigger_async_id: double): void { if (!emnapiNodeBinding) return emnapiNodeBinding.node.emitAsyncDestroy({ asyncId: async_id, @@ -26,7 +31,7 @@ function __emnapi_node_emit_async_destroy (async_id: double, trigger_async_id: d }) } -/* function __emnapi_node_open_callback_scope (async_resource: napi_value, async_id: double, trigger_async_id: double, result: Pointer): void { +/* vpddp export function _emnapi_node_open_callback_scope (async_resource: napi_value, async_id: double, trigger_async_id: double, result: Pointer): void { if (!emnapiNodeBinding || !result) return const resource = emnapiCtx.handleStore.get(async_resource)!.value // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -39,14 +44,16 @@ function __emnapi_node_emit_async_destroy (async_id: double, trigger_async_id: d $_TODO_makeSetValue('result', 0, 'nativeCallbackScopePointer', 'i64') } -function __emnapi_node_close_callback_scope (scope: Pointer): void { +vp +export function _emnapi_node_close_callback_scope (scope: Pointer): void { if (!emnapiNodeBinding || !scope) return $from64('scope') const nativeCallbackScopePointer = $_TODO_makeGetValue('scope', 0, 'i64') emnapiNodeBinding.node.closeCallbackScope(BigInt(nativeCallbackScopePointer)) } */ -function __emnapi_node_make_callback (env: napi_env, async_resource: napi_value, cb: napi_value, argv: Pointer, size: size_t, async_id: double, trigger_async_id: double, result: Pointer): void { +/** @__sig ipppppddp */ +export function _emnapi_node_make_callback (env: napi_env, async_resource: napi_value, cb: napi_value, argv: Pointer, size: size_t, async_id: double, trigger_async_id: double, result: Pointer): void { let i = 0 // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -75,7 +82,8 @@ function __emnapi_node_make_callback (env: napi_env, async_resource: napi_value, } } -function __emnapi_async_init_js (async_resource: napi_value, async_resource_name: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function _emnapi_async_init_js (async_resource: napi_value, async_resource_name: napi_value, result: Pointer): napi_status { if (!emnapiNodeBinding) { return napi_status.napi_generic_failure } @@ -108,7 +116,8 @@ function __emnapi_async_init_js (async_resource: napi_value, async_resource_name return napi_status.napi_ok } -function __emnapi_async_destroy_js (async_context: Pointer): napi_status { +/** @__sig ip */ +export function _emnapi_async_destroy_js (async_context: Pointer): napi_status { if (!emnapiNodeBinding) { return napi_status.napi_generic_failure } @@ -126,15 +135,18 @@ function __emnapi_async_destroy_js (async_context: Pointer): napi_statu // https://github.com/nodejs/node-addon-api/pull/1283 -function napi_open_callback_scope (env: napi_env, ignored: napi_value, async_context_handle: number, result: number): napi_status { +/** @__sig ipppp */ +export function napi_open_callback_scope (env: napi_env, ignored: napi_value, async_context_handle: number, result: number): napi_status { throw new Error('napi_open_callback_scope has not been implemented yet') } -function napi_close_callback_scope (env: napi_env, scope: number): napi_status { +/** @__sig ipp */ +export function napi_close_callback_scope (env: napi_env, scope: number): napi_status { throw new Error('napi_close_callback_scope has not been implemented yet') } -function napi_make_callback (env: napi_env, async_context: Pointer, recv: napi_value, func: napi_value, argc: size_t, argv: Pointer, result: Pointer): napi_status { +/** @__sig ippppppp */ +export function napi_make_callback (env: napi_env, async_context: Pointer, recv: napi_value, func: napi_value, argc: size_t, argv: Pointer, result: Pointer): napi_status { let i = 0 // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -184,21 +196,8 @@ function napi_make_callback (env: napi_env, async_context: Pointer, rec }) } -function __emnapi_env_check_gc_access (env: napi_env): void { +/** @__sig vp */ +export function _emnapi_env_check_gc_access (env: napi_env): void { const envObject = emnapiCtx.envStore.get(env)! envObject.checkGCAccess() } - -emnapiImplementInternal('_emnapi_node_emit_async_init', 'vppdp', __emnapi_node_emit_async_init) -emnapiImplementInternal('_emnapi_node_emit_async_destroy', 'vdd', __emnapi_node_emit_async_destroy) -// emnapiImplementInternal('_emnapi_node_open_callback_scope', 'vpddp', __emnapi_node_open_callback_scope) -// emnapiImplementInternal('_emnapi_node_close_callback_scope', 'vp', __emnapi_node_close_callback_scope) -emnapiImplementInternal('_emnapi_node_make_callback', 'ipppppddp', __emnapi_node_make_callback) - -emnapiImplementInternal('_emnapi_async_init_js', 'ippp', __emnapi_async_init_js) -emnapiImplementInternal('_emnapi_async_destroy_js', 'ip', __emnapi_async_destroy_js) -emnapiImplementInternal('_emnapi_env_check_gc_access', 'vp', __emnapi_env_check_gc_access) - -emnapiImplement('napi_open_callback_scope', 'ipppp', napi_open_callback_scope) -emnapiImplement('napi_close_callback_scope', 'ipp', napi_close_callback_scope) -emnapiImplement('napi_make_callback', 'ippppppp', napi_make_callback) diff --git a/packages/emnapi/src/promise.ts b/packages/emnapi/src/promise.ts index 4659fc5e..b758374e 100644 --- a/packages/emnapi/src/promise.ts +++ b/packages/emnapi/src/promise.ts @@ -1,5 +1,8 @@ +import { emnapiCtx } from 'emnapi:shared' +import { $PREAMBLE, $CHECK_ARG, $CHECK_ENV_NOT_IN_GC } from './macro' -function napi_create_promise (env: napi_env, deferred: Pointer, promise: Pointer): napi_status { +/** @__sig ippp */ +export function napi_create_promise (env: napi_env, deferred: Pointer, promise: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let deferredObjectId: number, value: number @@ -22,7 +25,8 @@ function napi_create_promise (env: napi_env, deferred: Pointer, p }) } -function napi_resolve_deferred (env: napi_env, deferred: napi_deferred, resolution: napi_value): napi_status { +/** @__sig ippp */ +export function napi_resolve_deferred (env: napi_env, deferred: napi_deferred, resolution: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, deferred) $CHECK_ARG!(envObject, resolution) @@ -32,7 +36,8 @@ function napi_resolve_deferred (env: napi_env, deferred: napi_deferred, resoluti }) } -function napi_reject_deferred (env: napi_env, deferred: napi_deferred, resolution: napi_value): napi_status { +/** @__sig ippp */ +export function napi_reject_deferred (env: napi_env, deferred: napi_deferred, resolution: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, deferred) $CHECK_ARG!(envObject, resolution) @@ -42,7 +47,8 @@ function napi_reject_deferred (env: napi_env, deferred: napi_deferred, resolutio }) } -function napi_is_promise (env: napi_env, value: napi_value, is_promise: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_promise (env: napi_env, value: napi_value, is_promise: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, is_promise) @@ -53,8 +59,3 @@ function napi_is_promise (env: napi_env, value: napi_value, is_promise: Pointer< $makeSetValue('is_promise', 0, 'r', 'i8') return envObject.clearLastError() } - -emnapiImplement('napi_create_promise', 'ippp', napi_create_promise) -emnapiImplement('napi_resolve_deferred', 'ippp', napi_resolve_deferred) -emnapiImplement('napi_reject_deferred', 'ippp', napi_reject_deferred) -emnapiImplement('napi_is_promise', 'ippp', napi_is_promise) diff --git a/packages/emnapi/src/property.ts b/packages/emnapi/src/property.ts index 72e224f4..9c21c953 100644 --- a/packages/emnapi/src/property.ts +++ b/packages/emnapi/src/property.ts @@ -1,4 +1,10 @@ -function _napi_get_all_property_names ( +import { emnapiCtx } from 'emnapi:shared' +import { emnapiDefineProperty } from './internal' +import { $PREAMBLE, $CHECK_ARG } from './macro' +import { emnapiString } from './string' + +/** @__sig ippiiip */ +export function napi_get_all_property_names ( env: napi_env, object: napi_value, key_mode: napi_key_collection_mode, @@ -133,8 +139,9 @@ function _napi_get_all_property_names ( }) } -function napi_get_property_names (env: napi_env, object: napi_value, result: Pointer): napi_status { - return _napi_get_all_property_names( +/** @__sig ippp */ +export function napi_get_property_names (env: napi_env, object: napi_value, result: Pointer): napi_status { + return napi_get_all_property_names( env, object, napi_key_collection_mode.napi_key_include_prototypes, @@ -144,7 +151,8 @@ function napi_get_property_names (env: napi_env, object: napi_value, result: Poi ) } -function napi_set_property (env: napi_env, object: napi_value, key: napi_value, value: napi_value): napi_status { +/** @__sig ipppp */ +export function napi_set_property (env: napi_env, object: napi_value, key: napi_value, value: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, key) $CHECK_ARG!(envObject, value) @@ -158,7 +166,8 @@ function napi_set_property (env: napi_env, object: napi_value, key: napi_value, }) } -function napi_has_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_has_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: number @@ -183,7 +192,8 @@ function napi_has_property (env: napi_env, object: napi_value, key: napi_value, }) } -function napi_get_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_get_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -209,7 +219,8 @@ function napi_get_property (env: napi_env, object: napi_value, key: napi_value, }) } -function napi_delete_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_delete_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: boolean @@ -238,7 +249,8 @@ function napi_delete_property (env: napi_env, object: napi_value, key: napi_valu }) } -function napi_has_own_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_has_own_property (env: napi_env, object: napi_value, key: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number, r: boolean @@ -267,7 +279,8 @@ function napi_has_own_property (env: napi_env, object: napi_value, key: napi_val }) } -function napi_set_named_property (env: napi_env, object: napi_value, cname: const_char_p, value: napi_value): napi_status { +/** @__sig ipppp */ +export function napi_set_named_property (env: napi_env, object: napi_value, cname: const_char_p, value: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, object) @@ -284,7 +297,8 @@ function napi_set_named_property (env: napi_env, object: napi_value, cname: cons }) } -function napi_has_named_property (env: napi_env, object: napi_value, utf8name: const_char_p, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_has_named_property (env: napi_env, object: napi_value, utf8name: const_char_p, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: boolean @@ -313,7 +327,8 @@ function napi_has_named_property (env: napi_env, object: napi_value, utf8name: c }) } -function napi_get_named_property (env: napi_env, object: napi_value, utf8name: const_char_p, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_get_named_property (env: napi_env, object: napi_value, utf8name: const_char_p, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -343,7 +358,8 @@ function napi_get_named_property (env: napi_env, object: napi_value, utf8name: c }) } -function napi_set_element (env: napi_env, object: napi_value, index: uint32_t, value: napi_value): napi_status { +/** @__sig ippip */ +export function napi_set_element (env: napi_env, object: napi_value, index: uint32_t, value: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, object) @@ -356,7 +372,8 @@ function napi_set_element (env: napi_env, object: napi_value, index: uint32_t, v }) } -function napi_has_element (env: napi_env, object: napi_value, index: uint32_t, result: Pointer): napi_status { +/** @__sig ippip */ +export function napi_has_element (env: napi_env, object: napi_value, index: uint32_t, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: number @@ -380,7 +397,8 @@ function napi_has_element (env: napi_env, object: napi_value, index: uint32_t, r }) } -function napi_get_element (env: napi_env, object: napi_value, index: uint32_t, result: Pointer): napi_status { +/** @__sig ippip */ +export function napi_get_element (env: napi_env, object: napi_value, index: uint32_t, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -405,7 +423,8 @@ function napi_get_element (env: napi_env, object: napi_value, index: uint32_t, r }) } -function napi_delete_element (env: napi_env, object: napi_value, index: uint32_t, result: Pointer): napi_status { +/** @__sig ippip */ +export function napi_delete_element (env: napi_env, object: napi_value, index: uint32_t, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: boolean @@ -432,7 +451,8 @@ function napi_delete_element (env: napi_env, object: napi_value, index: uint32_t }) } -function napi_define_properties ( +/** @__sig ipppp */ +export function napi_define_properties ( env: napi_env, object: napi_value, property_count: size_t, @@ -489,7 +509,8 @@ function napi_define_properties ( }) } -function napi_object_freeze (env: napi_env, object: napi_value): napi_status { +/** @__sig ipp */ +export function napi_object_freeze (env: napi_env, object: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { if (!object) return envObject.setLastError(napi_status.napi_invalid_arg) const h = emnapiCtx.handleStore.get(object)! @@ -502,7 +523,8 @@ function napi_object_freeze (env: napi_env, object: napi_value): napi_status { }) } -function napi_object_seal (env: napi_env, object: napi_value): napi_status { +/** @__sig ipp */ +export function napi_object_seal (env: napi_env, object: napi_value): napi_status { return $PREAMBLE!(env, (envObject) => { if (!object) return envObject.setLastError(napi_status.napi_invalid_arg) const h = emnapiCtx.handleStore.get(object)! @@ -514,21 +536,3 @@ function napi_object_seal (env: napi_env, object: napi_value): napi_status { return envObject.getReturnStatus() }) } - -emnapiImplement('napi_get_all_property_names', 'ippiiip', _napi_get_all_property_names) -emnapiImplement('napi_get_property_names', 'ippp', napi_get_property_names, ['napi_get_all_property_names']) -emnapiImplement('napi_set_property', 'ipppp', napi_set_property) -emnapiImplement('napi_has_property', 'ipppp', napi_has_property) -emnapiImplement('napi_get_property', 'ipppp', napi_get_property) -emnapiImplement('napi_delete_property', 'ipppp', napi_delete_property) -emnapiImplement('napi_has_own_property', 'ipppp', napi_has_own_property) -emnapiImplement('napi_set_named_property', 'ipppp', napi_set_named_property, ['$emnapiString']) -emnapiImplement('napi_has_named_property', 'ipppp', napi_has_named_property, ['$emnapiString']) -emnapiImplement('napi_get_named_property', 'ipppp', napi_get_named_property, ['$emnapiString']) -emnapiImplement('napi_set_element', 'ippip', napi_set_element) -emnapiImplement('napi_has_element', 'ippip', napi_has_element) -emnapiImplement('napi_get_element', 'ippip', napi_get_element) -emnapiImplement('napi_delete_element', 'ippip', napi_delete_element) -emnapiImplement('napi_define_properties', 'ipppp', napi_define_properties, ['$emnapiDefineProperty', '$emnapiString']) -emnapiImplement('napi_object_freeze', 'ipp', napi_object_freeze) -emnapiImplement('napi_object_seal', 'ipp', napi_object_seal) diff --git a/packages/emnapi/src/script.ts b/packages/emnapi/src/script.ts index 0b6ff882..dfb6957d 100644 --- a/packages/emnapi/src/script.ts +++ b/packages/emnapi/src/script.ts @@ -1,6 +1,11 @@ /* eslint-disable @typescript-eslint/indent */ -function napi_run_script (env: napi_env, script: napi_value, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { $CHECK_ARG, $PREAMBLE } from './macro' +import { napi_set_last_error } from './util' + +/** @__sig ippp */ +export function napi_run_script (env: napi_env, script: napi_value, result: Pointer): napi_status { let status: napi_status // #if DYNAMIC_EXECUTION // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -23,9 +28,7 @@ function napi_run_script (env: napi_env, script: napi_value, result: Pointer { $makeDynCall('vp', 'callback')(data) }) } -function __emnapi_next_tick (callback: number, data: number): void { +/** + * @__sig vpp + */ +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) }) } -function __emnapi_callback_into_module (forceUncaught: int, env: napi_env, callback: number, data: number, close_scope_if_throw: int): void { +/** + * @__sig vipppi + */ +export function _emnapi_callback_into_module (forceUncaught: int, env: napi_env, callback: number, data: number, close_scope_if_throw: int): void { const envObject = emnapiCtx.envStore.get(env)! const scope = emnapiCtx.openScope(envObject) try { @@ -76,23 +99,36 @@ function __emnapi_callback_into_module (forceUncaught: int, env: napi_env, callb emnapiCtx.closeScope(envObject, scope) } -function __emnapi_call_finalizer (forceUncaught: int, env: napi_env, callback: number, data: number, hint: number): void { +/** + * @__sig vipppp + */ +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') ;(envObject as NodeEnv).callFinalizerInternal(forceUncaught, callback, data, hint) } -function __emnapi_ctx_increase_waiting_request_counter (): void { +/** + * @__sig v + */ +export function _emnapi_ctx_increase_waiting_request_counter (): void { emnapiCtx.increaseWaitingRequestCounter() } -function __emnapi_ctx_decrease_waiting_request_counter (): void { +/** + * @__sig v + */ +export function _emnapi_ctx_decrease_waiting_request_counter (): void { emnapiCtx.decreaseWaitingRequestCounter() } -emnapiImplementInternal('_emnapi_set_immediate', 'vpp', __emnapi_set_immediate) -emnapiImplementInternal('_emnapi_next_tick', 'vpp', __emnapi_next_tick) -emnapiImplementInternal('_emnapi_callback_into_module', 'vipppi', __emnapi_callback_into_module) -emnapiImplementInternal('_emnapi_call_finalizer', 'vipppp', __emnapi_call_finalizer) -emnapiImplementInternal('_emnapi_ctx_increase_waiting_request_counter', 'v', __emnapi_ctx_increase_waiting_request_counter) -emnapiImplementInternal('_emnapi_ctx_decrease_waiting_request_counter', 'v', __emnapi_ctx_decrease_waiting_request_counter) +export function $emnapiSetValueI64 (result: Pointer, numberValue: number): void { + let tempDouble: number + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const tempI64 = [ + 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') +} diff --git a/packages/emnapi/src/value-operation.ts b/packages/emnapi/src/value-operation.ts index 5f0a5e7b..66216c9d 100644 --- a/packages/emnapi/src/value-operation.ts +++ b/packages/emnapi/src/value-operation.ts @@ -1,4 +1,8 @@ -function napi_typeof (env: napi_env, value: napi_value, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { $CHECK_ENV_NOT_IN_GC, $CHECK_ARG, $PREAMBLE } from './macro' + +/** @__sig ippp */ +export function napi_typeof (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -40,7 +44,8 @@ function napi_typeof (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_coerce_to_bool (env: napi_env, value: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -56,7 +61,8 @@ function napi_coerce_to_bool (env: napi_env, value: napi_value, result: Pointer< }) } -function napi_coerce_to_number (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_coerce_to_number (env: napi_env, value: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -76,7 +82,8 @@ function napi_coerce_to_number (env: napi_env, value: napi_value, result: Pointe }) } -function napi_coerce_to_object (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_coerce_to_object (env: napi_env, value: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -96,7 +103,8 @@ function napi_coerce_to_object (env: napi_env, value: napi_value, result: Pointe }) } -function napi_coerce_to_string (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_coerce_to_string (env: napi_env, value: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -116,7 +124,8 @@ function napi_coerce_to_string (env: napi_env, value: napi_value, result: Pointe }) } -function napi_instanceof (env: napi_env, object: napi_value, constructor: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_instanceof (env: napi_env, object: napi_value, constructor: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: number @@ -138,7 +147,8 @@ function napi_instanceof (env: napi_env, object: napi_value, constructor: napi_v }) } -function napi_is_array (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_array (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -150,7 +160,8 @@ function napi_is_array (env: napi_env, value: napi_value, result: Pointer) return envObject.clearLastError() } -function napi_is_arraybuffer (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_arraybuffer (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -162,7 +173,8 @@ function napi_is_arraybuffer (env: napi_env, value: napi_value, result: Pointer< return envObject.clearLastError() } -function napi_is_date (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_date (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -174,7 +186,8 @@ function napi_is_date (env: napi_env, value: napi_value, result: Pointer): return envObject.clearLastError() } -function napi_is_error (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_error (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -186,7 +199,8 @@ function napi_is_error (env: napi_env, value: napi_value, result: Pointer) return envObject.clearLastError() } -function napi_is_typedarray (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_typedarray (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -198,7 +212,8 @@ function napi_is_typedarray (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_buffer (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -210,7 +225,8 @@ function napi_is_buffer (env: napi_env, value: napi_value, result: Pointer return envObject.clearLastError() } -function napi_is_dataview (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_dataview (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -222,7 +238,8 @@ function napi_is_dataview (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** @__sig ipppp */ +export function napi_strict_equals (env: napi_env, lhs: napi_value, rhs: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let r: number @@ -239,7 +256,8 @@ function napi_strict_equals (env: napi_env, lhs: napi_value, rhs: napi_value, re }) } -function napi_detach_arraybuffer (env: napi_env, arraybuffer: napi_value): napi_status { +/** @__sig ipp */ +export function napi_detach_arraybuffer (env: napi_env, arraybuffer: napi_value): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, arraybuffer) const value = emnapiCtx.handleStore.get(arraybuffer)!.value @@ -260,7 +278,8 @@ function napi_detach_arraybuffer (env: napi_env, arraybuffer: napi_value): napi_ return envObject.clearLastError() } -function napi_is_detached_arraybuffer (env: napi_env, arraybuffer: napi_value, result: Pointer): napi_status { +/** @__sig ippp */ +export function napi_is_detached_arraybuffer (env: napi_env, arraybuffer: napi_value, result: Pointer): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, arraybuffer) $CHECK_ARG!(envObject, result) @@ -279,20 +298,3 @@ function napi_is_detached_arraybuffer (env: napi_env, arraybuffer: napi_value, r return envObject.getReturnStatus() }) } - -emnapiImplement('napi_typeof', 'ippp', napi_typeof) -emnapiImplement('napi_coerce_to_bool', 'ippp', napi_coerce_to_bool) -emnapiImplement('napi_coerce_to_number', 'ippp', napi_coerce_to_number) -emnapiImplement('napi_coerce_to_object', 'ippp', napi_coerce_to_object) -emnapiImplement('napi_coerce_to_string', 'ippp', napi_coerce_to_string) -emnapiImplement('napi_instanceof', 'ipppp', napi_instanceof) -emnapiImplement('napi_is_array', 'ippp', napi_is_array) -emnapiImplement('napi_is_arraybuffer', 'ippp', napi_is_arraybuffer) -emnapiImplement('napi_is_date', 'ippp', napi_is_date) -emnapiImplement('napi_is_error', 'ippp', napi_is_error) -emnapiImplement('napi_is_typedarray', 'ippp', napi_is_typedarray) -emnapiImplement('napi_is_buffer', 'ippp', napi_is_buffer) -emnapiImplement('napi_is_dataview', 'ippp', napi_is_dataview) -emnapiImplement('napi_strict_equals', 'ipppp', napi_strict_equals) -emnapiImplement('napi_detach_arraybuffer', 'ipp', napi_detach_arraybuffer, ['napi_set_last_error']) -emnapiImplement('napi_is_detached_arraybuffer', 'ippp', napi_is_detached_arraybuffer, ['napi_set_last_error']) diff --git a/packages/emnapi/src/value/convert2c.ts b/packages/emnapi/src/value/convert2c.ts index f0b56044..9ca8c10c 100644 --- a/packages/emnapi/src/value/convert2c.ts +++ b/packages/emnapi/src/value/convert2c.ts @@ -1,4 +1,13 @@ -function napi_get_array_length (env: napi_env, value: napi_value, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { $emnapiSetValueI64 as emnapiSetValueI64 } from '../util' +import { emnapiString } from '../string' +import { emnapiExternalMemory } from '../memory' +import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from '../macro' + +/** + * @__sig ippp + */ +export function napi_get_array_length (env: napi_env, value: napi_value, result: Pointer): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -14,7 +23,10 @@ function napi_get_array_length (env: napi_env, value: napi_value, result: Pointe }) } -function napi_get_arraybuffer_info (env: napi_env, arraybuffer: napi_value, data: void_pp, byte_length: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_get_arraybuffer_info (env: napi_env, arraybuffer: napi_value, data: void_pp, byte_length: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, arraybuffer) const handle = emnapiCtx.handleStore.get(arraybuffer)! @@ -35,7 +47,10 @@ function napi_get_arraybuffer_info (env: napi_env, arraybuffer: napi_value, data return envObject.clearLastError() } -function napi_get_prototype (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_prototype (env: napi_env, value: napi_value, result: Pointer): napi_status { return $PREAMBLE!(env, (envObject) => { $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -58,7 +73,10 @@ function napi_get_prototype (env: napi_env, value: napi_value, result: Pointer, @@ -134,7 +152,10 @@ function _napi_get_typedarray_info ( return envObject.clearLastError() } -function napi_get_buffer_info ( +/** + * @__sig ipppp + */ +export function napi_get_buffer_info ( env: napi_env, buffer: napi_value, data: void_pp, @@ -146,10 +167,13 @@ function napi_get_buffer_info ( if (!handle.isBuffer()) { return envObject.setLastError(napi_status.napi_invalid_arg) } - return _napi_get_typedarray_info(env, buffer, 0, length, data, 0, 0) + return napi_get_typedarray_info(env, buffer, 0, length, data, 0, 0) } -function napi_get_dataview_info ( +/** + * @__sig ipppppp + */ +export function napi_get_dataview_info ( env: napi_env, dataview: napi_value, byte_length: Pointer, @@ -193,7 +217,10 @@ function napi_get_dataview_info ( return envObject.clearLastError() } -function napi_get_date_value (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_date_value (env: napi_env, value: napi_value, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number @@ -211,7 +238,10 @@ function napi_get_date_value (env: napi_env, value: napi_value, result: Pointer< }) } -function napi_get_value_bool (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_value_bool (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -226,7 +256,10 @@ function napi_get_value_bool (env: napi_env, value: napi_value, result: Pointer< return envObject.clearLastError() } -function napi_get_value_double (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_value_double (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -241,7 +274,10 @@ function napi_get_value_double (env: napi_env, value: napi_value, result: Pointe return envObject.clearLastError() } -function napi_get_value_bigint_int64 (env: napi_env, value: napi_value, result: Pointer, lossless: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_get_value_bigint_int64 (env: napi_env, value: napi_value, result: Pointer, lossless: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) if (!emnapiCtx.feature.supportBigInt) { return envObject.setLastError(napi_status.napi_generic_failure) @@ -274,7 +310,10 @@ function napi_get_value_bigint_int64 (env: napi_env, value: napi_value, result: return envObject.clearLastError() } -function napi_get_value_bigint_uint64 (env: napi_env, value: napi_value, result: Pointer, lossless: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_get_value_bigint_uint64 (env: napi_env, value: napi_value, result: Pointer, lossless: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) if (!emnapiCtx.feature.supportBigInt) { return envObject.setLastError(napi_status.napi_generic_failure) @@ -304,7 +343,10 @@ function napi_get_value_bigint_uint64 (env: napi_env, value: napi_value, result: return envObject.clearLastError() } -function napi_get_value_bigint_words ( +/** + * @__sig ippppp + */ +export function napi_get_value_bigint_words ( env: napi_env, value: napi_value, sign_bit: Pointer, @@ -364,7 +406,10 @@ function napi_get_value_bigint_words ( return envObject.clearLastError() } -function napi_get_value_external (env: napi_env, value: napi_value, result: void_pp): napi_status { +/** + * @__sig ippp + */ +export function napi_get_value_external (env: napi_env, value: napi_value, result: void_pp): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -380,7 +425,10 @@ function napi_get_value_external (env: napi_env, value: napi_value, result: void return envObject.clearLastError() } -function napi_get_value_int32 (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_value_int32 (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -393,18 +441,10 @@ function napi_get_value_int32 (env: napi_env, value: napi_value, result: Pointer return envObject.clearLastError() } -function emnapiSetValueI64 (result: Pointer, numberValue: number): void { - let tempDouble: number - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const tempI64 = [ - 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') -} - -function napi_get_value_int64 (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_value_int64 (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -430,7 +470,10 @@ function napi_get_value_int64 (env: napi_env, value: napi_value, result: Pointer return envObject.clearLastError() } -function napi_get_value_string_latin1 (env: napi_env, value: napi_value, buf: char_p, buf_size: size_t, result: Pointer): napi_status { +/** + * @__sig ippppp + */ +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') @@ -465,7 +508,10 @@ function napi_get_value_string_latin1 (env: napi_env, value: napi_value, buf: ch return envObject.clearLastError() } -function napi_get_value_string_utf8 (env: napi_env, value: napi_value, buf: char_p, buf_size: size_t, result: Pointer): napi_status { +/** + * @__sig ippppp + */ +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') @@ -494,7 +540,10 @@ function napi_get_value_string_utf8 (env: napi_env, value: napi_value, buf: char return envObject.clearLastError() } -function napi_get_value_string_utf16 (env: napi_env, value: napi_value, buf: char16_t_p, buf_size: size_t, result: Pointer): napi_status { +/** + * @__sig ippppp + */ +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') @@ -521,7 +570,10 @@ function napi_get_value_string_utf16 (env: napi_env, value: napi_value, buf: cha return envObject.clearLastError() } -function napi_get_value_uint32 (env: napi_env, value: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +export function napi_get_value_uint32 (env: napi_env, value: napi_value, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, value) $CHECK_ARG!(envObject, result) @@ -533,27 +585,3 @@ function napi_get_value_uint32 (env: napi_env, value: napi_value, result: Pointe $makeSetValue('result', 0, 'handle.value', 'u32') return envObject.clearLastError() } - -emnapiImplementHelper('$emnapiSetValueI64', undefined, emnapiSetValueI64, undefined) - -emnapiImplement('napi_get_array_length', 'ippp', napi_get_array_length) -emnapiImplement('napi_get_arraybuffer_info', 'ipppp', napi_get_arraybuffer_info, ['$emnapiExternalMemory']) -emnapiImplement('napi_get_prototype', 'ippp', napi_get_prototype) -emnapiImplement('napi_get_typedarray_info', 'ippppppp', _napi_get_typedarray_info, ['$emnapiExternalMemory']) -emnapiImplement('napi_get_buffer_info', 'ipppp', napi_get_buffer_info, ['napi_get_typedarray_info']) -emnapiImplement('napi_get_dataview_info', 'ipppppp', napi_get_dataview_info, ['$emnapiExternalMemory']) -emnapiImplement('napi_get_date_value', 'ippp', napi_get_date_value) -emnapiImplement('napi_get_value_bool', 'ippp', napi_get_value_bool) -emnapiImplement('napi_get_value_double', 'ippp', napi_get_value_double) -emnapiImplement('napi_get_value_bigint_int64', 'ipppp', napi_get_value_bigint_int64) -emnapiImplement('napi_get_value_bigint_uint64', 'ipppp', napi_get_value_bigint_uint64) -emnapiImplement('napi_get_value_bigint_words', 'ippppp', napi_get_value_bigint_words) -emnapiImplement('napi_get_value_external', 'ippp', napi_get_value_external) -emnapiImplement('napi_get_value_int32', 'ippp', napi_get_value_int32) -emnapiImplement('napi_get_value_int64', 'ippp', napi_get_value_int64, ['$emnapiSetValueI64']) -emnapiImplement('napi_get_value_string_latin1', 'ippppp', napi_get_value_string_latin1) -emnapiImplement('napi_get_value_string_utf8', 'ippppp', napi_get_value_string_utf8, ['$emnapiString']) - -emnapiImplement('napi_get_value_string_utf16', 'ippppp', napi_get_value_string_utf16, ['$emnapiString']) - -emnapiImplement('napi_get_value_uint32', 'ippp', napi_get_value_uint32) diff --git a/packages/emnapi/src/value/convert2napi.ts b/packages/emnapi/src/value/convert2napi.ts index 1a4b0936..93c33398 100644 --- a/packages/emnapi/src/value/convert2napi.ts +++ b/packages/emnapi/src/value/convert2napi.ts @@ -1,6 +1,13 @@ /* eslint-disable @typescript-eslint/indent */ -function napi_create_int32 (env: napi_env, value: int32_t, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { emnapiString } from '../string' +import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from '../macro' + +/** + * @__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) $from64('result') @@ -10,7 +17,10 @@ function napi_create_int32 (env: napi_env, value: int32_t, result: Pointer): napi_status { +/** + * @__sig ipip + */ +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') @@ -20,7 +30,10 @@ function napi_create_uint32 (env: napi_env, value: uint32_t, result: Pointer): napi_status { +/** + * @__sig ipjp + */ +export function napi_create_int64 (env: napi_env, low: int32_t, high: int32_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) let value: number @@ -43,7 +56,10 @@ function napi_create_int64 (env: napi_env, low: int32_t, high: int32_t, result: return envObject.clearLastError() } -function napi_create_double (env: napi_env, value: double, result: Pointer): napi_status { +/** + * @__sig ipdp + */ +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') @@ -53,7 +69,10 @@ function napi_create_double (env: napi_env, value: double, result: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_create_string_latin1 (env: napi_env, str: const_char_p, length: size_t, result: Pointer): napi_status { return emnapiString.newString(env, str, length, result, (str, autoLength, sizeLength) => { let latin1String = '' let len = 0 @@ -77,19 +96,28 @@ function _napi_create_string_latin1 (env: napi_env, str: const_char_p, length: s }) } -function _napi_create_string_utf16 (env: napi_env, str: const_char16_t_p, length: size_t, result: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_create_string_utf16 (env: napi_env, str: const_char16_t_p, length: size_t, result: Pointer): napi_status { return emnapiString.newString(env, str, length, result, (str) => { return emnapiString.UTF16ToString(str, length) }) } -function napi_create_string_utf8 (env: napi_env, str: const_char_p, length: size_t, result: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_create_string_utf8 (env: napi_env, str: const_char_p, length: size_t, result: Pointer): napi_status { return emnapiString.newString(env, str, length, result, (str) => { return emnapiString.UTF8ToString(str, length) }) } -function node_api_create_external_string_latin1 ( +/** + * @__sig ippppppp + */ +export function node_api_create_external_string_latin1 ( env: napi_env, str: number, length: size_t, @@ -106,12 +134,15 @@ function node_api_create_external_string_latin1 ( finalize_hint, result, copied, - _napi_create_string_latin1, + napi_create_string_latin1, undefined! ) } -function node_api_create_external_string_utf16 ( +/** + * @__sig ippppppp + */ +export function node_api_create_external_string_utf16 ( env: napi_env, str: number, length: size_t, @@ -128,12 +159,15 @@ function node_api_create_external_string_utf16 ( finalize_hint, result, copied, - _napi_create_string_utf16, + napi_create_string_utf16, undefined! ) } -function napi_create_bigint_int64 (env: napi_env, low: int32_t, high: int32_t, result: Pointer): napi_status { +/** + * @__sig ipjp + */ +export function napi_create_bigint_int64 (env: napi_env, low: int32_t, high: int32_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) if (!emnapiCtx.feature.supportBigInt) { return envObject.setLastError(napi_status.napi_generic_failure) @@ -159,7 +193,10 @@ function napi_create_bigint_int64 (env: napi_env, low: int32_t, high: int32_t, r return envObject.clearLastError() } -function napi_create_bigint_uint64 (env: napi_env, low: int32_t, high: int32_t, result: Pointer): napi_status { +/** + * @__sig ipjp + */ +export function napi_create_bigint_uint64 (env: napi_env, low: int32_t, high: int32_t, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) if (!emnapiCtx.feature.supportBigInt) { return envObject.setLastError(napi_status.napi_generic_failure) @@ -185,7 +222,10 @@ function napi_create_bigint_uint64 (env: napi_env, low: int32_t, high: int32_t, return envObject.clearLastError() } -function napi_create_bigint_words (env: napi_env, sign_bit: int, word_count: size_t, words: Const>, result: Pointer): napi_status { +/** + * @__sig ipippp + */ +export function napi_create_bigint_words (env: napi_env, sign_bit: int, word_count: size_t, words: Const>, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let v: number, i: number @@ -217,16 +257,3 @@ function napi_create_bigint_words (env: napi_env, sign_bit: int, word_count: siz return envObject.getReturnStatus() }) } - -emnapiImplement('napi_create_int32', 'ipip', napi_create_int32) -emnapiImplement('napi_create_uint32', 'ipip', napi_create_uint32) -emnapiImplement('napi_create_int64', 'ipjp', napi_create_int64) -emnapiImplement('napi_create_double', 'ipdp', napi_create_double) -emnapiImplement('napi_create_bigint_int64', 'ipjp', napi_create_bigint_int64) -emnapiImplement('napi_create_bigint_uint64', 'ipjp', napi_create_bigint_uint64) -emnapiImplement('napi_create_bigint_words', 'ipippp', napi_create_bigint_words) -emnapiImplement('napi_create_string_latin1', 'ipppp', _napi_create_string_latin1, ['$emnapiString']) -emnapiImplement('napi_create_string_utf16', 'ipppp', _napi_create_string_utf16, ['$emnapiString']) -emnapiImplement('napi_create_string_utf8', 'ipppp', napi_create_string_utf8, ['$emnapiString']) -emnapiImplement('node_api_create_external_string_latin1', 'ippppppp', node_api_create_external_string_latin1, ['$emnapiString', 'napi_create_string_latin1']) -emnapiImplement('node_api_create_external_string_utf16', 'ippppppp', node_api_create_external_string_utf16, ['$emnapiString', 'napi_create_string_utf16']) diff --git a/packages/emnapi/src/value/create.ts b/packages/emnapi/src/value/create.ts index ea63fdf0..810cdd1d 100644 --- a/packages/emnapi/src/value/create.ts +++ b/packages/emnapi/src/value/create.ts @@ -1,4 +1,15 @@ -function napi_create_array (env: napi_env, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { wasmMemory, _malloc } from 'emnapi:emscripten-runtime' +import { emnapiString } from '../string' +import { type MemoryViewDescriptor, emnapiExternalMemory } from '../memory' +import { emnapi_create_memory_view } from '../emnapi' +import { napi_add_finalizer } from '../wrap' +import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $PREAMBLE } from '../macro' + +/** + * @__sig ipp + */ +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') @@ -8,7 +19,10 @@ function napi_create_array (env: napi_env, result: Pointer): napi_st return envObject.clearLastError() } -function napi_create_array_with_length (env: napi_env, length: size_t, result: Pointer): napi_status { +/** + * @__sig ippp + */ +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') @@ -35,7 +49,10 @@ function emnapiCreateArrayBuffer (byte_length: size_t, data: void_pp): ArrayBuff return arrayBuffer } -function napi_create_arraybuffer (env: napi_env, byte_length: size_t, data: void_pp, result: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_create_arraybuffer (env: napi_env, byte_length: size_t, data: void_pp, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -49,7 +66,10 @@ function napi_create_arraybuffer (env: napi_env, byte_length: size_t, data: void }) } -function napi_create_date (env: napi_env, time: double, result: Pointer): napi_status { +/** + * @__sig ipdp + */ +export function napi_create_date (env: napi_env, time: double, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -63,7 +83,10 @@ function napi_create_date (env: napi_env, time: double, result: Pointer): napi_status { +/** + * @__sig ippppp + */ +export function napi_create_external (env: napi_env, data: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars let value: number @@ -83,7 +106,10 @@ function napi_create_external (env: napi_env, data: void_p, finalize_cb: napi_fi }) } -function napi_create_external_arraybuffer ( +/** + * @__sig ipppppp + */ +export function napi_create_external_arraybuffer ( env: napi_env, external_data: void_p, byte_length: size_t, @@ -130,7 +156,7 @@ function napi_create_external_arraybuffer ( } const handle = emnapiCtx.addToCurrentScope(arrayBuffer) if (finalize_cb) { - const status = _napi_add_finalizer(env, handle.id, external_data, finalize_cb, finalize_hint, /* NULL */ 0) + const status = napi_add_finalizer(env, handle.id, external_data, finalize_cb, finalize_hint, /* NULL */ 0) if (status === napi_status.napi_pending_exception) { const err = envObject.tryCatch.extractException() envObject.clearLastError() @@ -145,7 +171,10 @@ function napi_create_external_arraybuffer ( }) } -function napi_create_object (env: napi_env, result: Pointer): napi_status { +/** + * @__sig ipp + */ +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') @@ -155,7 +184,10 @@ function napi_create_object (env: napi_env, result: Pointer): napi_s return envObject.clearLastError() } -function napi_create_symbol (env: napi_env, description: napi_value, result: Pointer): napi_status { +/** + * @__sig ippp + */ +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') @@ -177,7 +209,10 @@ function napi_create_symbol (env: napi_env, description: napi_value, result: Poi return envObject.clearLastError() } -function napi_create_typedarray ( +/** + * @__sig ipipppp + */ +export function napi_create_typedarray ( env: napi_env, type: napi_typedarray_type, length: size_t, @@ -269,7 +304,11 @@ function napi_create_typedarray ( }) } -function napi_create_buffer ( +/** + * @__deps malloc + * @__sig ippp + */ +export function napi_create_buffer ( env: napi_env, size: size_t, data: Pointer>, @@ -319,7 +358,10 @@ function napi_create_buffer ( }) } -function napi_create_buffer_copy ( +/** + * @__sig ippppp + */ +export function napi_create_buffer_copy ( env: napi_env, length: size_t, data: Pointer, @@ -347,7 +389,10 @@ function napi_create_buffer_copy ( }) } -function napi_create_external_buffer ( +/** + * @__sig ipppppp + */ +export function napi_create_external_buffer ( env: napi_env, length: size_t, data: Pointer, @@ -355,7 +400,7 @@ function napi_create_external_buffer ( finalize_hint: Pointer, result: Pointer ): napi_status { - return _emnapi_create_memory_view( + return emnapi_create_memory_view( env, emnapi_memory_view_type.emnapi_buffer, data, @@ -366,7 +411,10 @@ function napi_create_external_buffer ( ) } -function napi_create_dataview ( +/** + * @__sig ippppp + */ +export function napi_create_dataview ( env: napi_env, byte_length: size_t, arraybuffer: napi_value, @@ -416,7 +464,10 @@ function napi_create_dataview ( }) } -function node_api_symbol_for (env: napi_env, utf8description: const_char_p, length: size_t, result: Pointer): napi_status { +/** + * @__sig ipppp + */ +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') @@ -440,20 +491,3 @@ function node_api_symbol_for (env: napi_env, utf8description: const_char_p, leng $makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } - -emnapiImplementHelper('$emnapiCreateArrayBuffer', undefined, emnapiCreateArrayBuffer, ['$emnapiExternalMemory']) - -emnapiImplement('napi_create_array', 'ipp', napi_create_array) -emnapiImplement('napi_create_array_with_length', 'ippp', napi_create_array_with_length) -emnapiImplement('napi_create_arraybuffer', 'ipppp', napi_create_arraybuffer, ['$emnapiCreateArrayBuffer']) -emnapiImplement('napi_create_buffer', 'ippp', napi_create_buffer, ['$emnapiExternalMemory', 'malloc']) -emnapiImplement('napi_create_buffer_copy', 'ippppp', napi_create_buffer_copy, ['$emnapiCreateArrayBuffer']) -emnapiImplement('napi_create_date', 'ipdp', napi_create_date) -emnapiImplement('napi_create_external', 'ippppp', napi_create_external) -emnapiImplement('napi_create_external_arraybuffer', 'ipppppp', napi_create_external_arraybuffer, ['napi_add_finalizer']) -emnapiImplement('napi_create_external_buffer', 'ipppppp', napi_create_external_buffer, ['emnapi_create_memory_view']) -emnapiImplement('napi_create_object', 'ipp', napi_create_object) -emnapiImplement('napi_create_symbol', 'ippp', napi_create_symbol) -emnapiImplement('napi_create_typedarray', 'ipipppp', napi_create_typedarray, ['$emnapiExternalMemory']) -emnapiImplement('napi_create_dataview', 'ippppp', napi_create_dataview, ['$emnapiExternalMemory']) -emnapiImplement('node_api_symbol_for', 'ipppp', node_api_symbol_for, ['$emnapiString']) diff --git a/packages/emnapi/src/value/global.ts b/packages/emnapi/src/value/global.ts index f7afff5c..236d7153 100644 --- a/packages/emnapi/src/value/global.ts +++ b/packages/emnapi/src/value/global.ts @@ -1,4 +1,9 @@ -function napi_get_boolean (env: napi_env, value: bool, result: Pointer): napi_status { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { emnapiCtx } from 'emnapi:shared' +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') @@ -8,7 +13,8 @@ function napi_get_boolean (env: napi_env, value: bool, result: Pointer): napi_status { +/** @__sig ipp */ +export function napi_get_global (env: napi_env, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) $CHECK_ARG!(envObject, result) $from64('result') @@ -18,7 +24,8 @@ function napi_get_global (env: napi_env, result: Pointer): napi_stat return envObject.clearLastError() } -function napi_get_null (env: napi_env, result: Pointer): napi_status { +/** @__sig ipp */ +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') @@ -28,7 +35,8 @@ function napi_get_null (env: napi_env, result: Pointer): napi_status return envObject.clearLastError() } -function napi_get_undefined (env: napi_env, result: Pointer): napi_status { +/** @__sig ipp */ +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') @@ -37,8 +45,3 @@ function napi_get_undefined (env: napi_env, result: Pointer): napi_s $makeSetValue('result', 0, 'value', '*') return envObject.clearLastError() } - -emnapiImplement('napi_get_boolean', 'ipip', napi_get_boolean) -emnapiImplement('napi_get_global', 'ipp', napi_get_global) -emnapiImplement('napi_get_null', 'ipp', napi_get_null) -emnapiImplement('napi_get_undefined', 'ipp', napi_get_undefined) diff --git a/packages/emnapi/src/version.ts b/packages/emnapi/src/version.ts index 9439e3e2..4fbd9679 100644 --- a/packages/emnapi/src/version.ts +++ b/packages/emnapi/src/version.ts @@ -1,4 +1,8 @@ -function _napi_get_version (env: napi_env, result: Pointer): napi_status { +import { emnapiCtx } from 'emnapi:shared' +import { $CHECK_ENV, $CHECK_ARG } from './macro' + +/** @__sig ipp */ +export function napi_get_version (env: napi_env, result: Pointer): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! $CHECK_ARG!(envObject, result) @@ -7,5 +11,3 @@ function _napi_get_version (env: napi_env, result: Pointer): napi_stat $makeSetValue('result', 0, 'NODE_API_SUPPORTED_VERSION_MAX', 'u32') return envObject.clearLastError() } - -emnapiImplement('napi_get_version', 'ipp', _napi_get_version, []) diff --git a/packages/emnapi/src/wrap.ts b/packages/emnapi/src/wrap.ts index a60c1deb..e62ea254 100644 --- a/packages/emnapi/src/wrap.ts +++ b/packages/emnapi/src/wrap.ts @@ -1,4 +1,12 @@ -function napi_define_class ( +import { emnapiCtx } from 'emnapi:shared' +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' + +/** + * @__sig ipppppppp + */ +export function napi_define_class ( env: napi_env, utf8name: Pointer, length: size_t, @@ -73,19 +81,31 @@ function napi_define_class ( }) } -function napi_wrap (env: napi_env, js_object: napi_value, native_object: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { +/** + * @__sig ipppppp + */ +export function napi_wrap (env: napi_env, js_object: napi_value, native_object: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { return emnapiWrap(env, js_object, native_object, finalize_cb, finalize_hint, result) } -function napi_unwrap (env: napi_env, js_object: napi_value, result: void_pp): napi_status { +/** + * @__sig ippp + */ +export function napi_unwrap (env: napi_env, js_object: napi_value, result: void_pp): napi_status { return emnapiUnwrap(env, js_object, result, UnwrapAction.KeepWrap) } -function napi_remove_wrap (env: napi_env, js_object: napi_value, result: void_pp): napi_status { +/** + * @__sig ippp + */ +export function napi_remove_wrap (env: napi_env, js_object: napi_value, result: void_pp): napi_status { return emnapiUnwrap(env, js_object, result, UnwrapAction.RemoveWrap) } -function napi_type_tag_object (env: napi_env, object: napi_value, type_tag: Const>): napi_status { +/** + * @__sig ippp + */ +export function napi_type_tag_object (env: napi_env, object: napi_value, type_tag: Const>): napi_status { return $PREAMBLE!(env, (envObject) => { if (!object) { return envObject.setLastError(envObject.tryCatch.hasCaught() ? napi_status.napi_pending_exception : napi_status.napi_invalid_arg) @@ -113,7 +133,10 @@ function napi_type_tag_object (env: napi_env, object: napi_value, type_tag: Cons }) } -function napi_check_object_type_tag (env: napi_env, object: napi_value, type_tag: Const>, result: Pointer): napi_status { +/** + * @__sig ipppp + */ +export function napi_check_object_type_tag (env: napi_env, object: napi_value, type_tag: Const>, result: Pointer): napi_status { // eslint-disable-next-line @typescript-eslint/no-unused-vars, one-var let ret = true, i: number @@ -152,8 +175,10 @@ function napi_check_object_type_tag (env: napi_env, object: napi_value, type_tag }) } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function _napi_add_finalizer (env: napi_env, js_object: napi_value, finalize_data: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { +/** + * @__sig ipppppp + */ +export function napi_add_finalizer (env: napi_env, js_object: napi_value, finalize_data: void_p, finalize_cb: napi_finalize, finalize_hint: void_p, result: Pointer): napi_status { const envObject: Env = $CHECK_ENV_NOT_IN_GC!(env) if (!emnapiCtx.feature.supportFinalizer) { @@ -184,7 +209,10 @@ function _napi_add_finalizer (env: napi_env, js_object: napi_value, finalize_dat return envObject.clearLastError() } -function _node_api_post_finalizer (env: napi_env, finalize_cb: napi_finalize, finalize_data: void_p, finalize_hint: void_p): napi_status { +/** + * @__sig ipppp + */ +export function node_api_post_finalizer (env: napi_env, finalize_cb: napi_finalize, finalize_data: void_p, finalize_hint: void_p): napi_status { $CHECK_ENV!(env) const envObject = emnapiCtx.envStore.get(env)! envObject.enqueueFinalizer( @@ -197,12 +225,3 @@ function _node_api_post_finalizer (env: napi_env, finalize_cb: napi_finalize, fi ) return envObject.clearLastError() } - -emnapiImplement('napi_define_class', 'ipppppppp', napi_define_class, ['$emnapiCreateFunction', '$emnapiDefineProperty', '$emnapiString']) -emnapiImplement('napi_wrap', 'ipppppp', napi_wrap, ['$emnapiWrap']) -emnapiImplement('napi_unwrap', 'ippp', napi_unwrap, ['$emnapiUnwrap']) -emnapiImplement('napi_remove_wrap', 'ippp', napi_remove_wrap, ['$emnapiUnwrap']) -emnapiImplement('napi_type_tag_object', 'ippp', napi_type_tag_object) -emnapiImplement('napi_check_object_type_tag', 'ipppp', napi_check_object_type_tag) -emnapiImplement('napi_add_finalizer', 'ipppppp', _napi_add_finalizer, ['$emnapiGetHandle']) -emnapiImplement('node_api_post_finalizer', 'ipppp', _node_api_post_finalizer) diff --git a/packages/emnapi/transformer/src/esm.ts b/packages/emnapi/transformer/src/esm.ts new file mode 100644 index 00000000..cd2f8c5b --- /dev/null +++ b/packages/emnapi/transformer/src/esm.ts @@ -0,0 +1,337 @@ +import type { + TransformerFactory, + SourceFile, + Program, + TransformationContext, + VisitResult, + Node, + FunctionDeclaration, + VariableDeclaration, + Visitor, + Symbol, + JSDocTagInfo, + ExpressionStatement +} from 'typescript' + +import ts = require('typescript') + +interface SymbolWrap/* */ { + // decl: T + deps: Set + exported: Symbol | undefined + replaceNode: Node + jsdocTags: JSDocTagInfo[] | undefined +} + +class Transformer { + exported: Set = new Set() + wrap: WeakMap = new WeakMap() + + constructor (public ctx: TransformationContext, public program: Program) { + this.transform = this.transform.bind(this) + this.finalVisitor = this.finalVisitor.bind(this) + this.collectExportedFunctionVisitor = this.collectExportedFunctionVisitor.bind(this) + } + + transform (source: SourceFile): SourceFile { + ts.visitNode(source, this.collectExportedFunctionVisitor) as SourceFile + + const result = ts.visitEachChild(source, this.finalVisitor, this.ctx) + const statements = [ + ...result.statements.filter(s => !ts.isExportDeclaration(s)).map(s => { + if (ts.isFunctionDeclaration(s)) { + return this.ctx.factory.updateFunctionDeclaration(s, + s.modifiers?.filter((m) => m.kind !== ts.SyntaxKind.ExportKeyword), + s.asteriskToken, + s.name, + s.typeParameters, + s.parameters, + s.type, + s.body + ) + } + if (ts.isVariableStatement(s)) { + return this.ctx.factory.updateVariableStatement(s, + s.modifiers?.filter((m) => m.kind !== ts.SyntaxKind.ExportKeyword), + s.declarationList + ) + } + return s + }), + this.createMergeExported() + ] + return this.ctx.factory.updateSourceFile(result, statements) + } + + finalVisitor (node: Node): VisitResult { + if ((ts.isFunctionDeclaration(node) || ts.isVariableDeclaration(node)) && this.exported.has(node)) { + const wrap = this.wrap.get(node)! + if (wrap.replaceNode !== node) { + return wrap.replaceNode + } + } + return ts.visitEachChild(node, this.finalVisitor, this.ctx) + } + + tryGetExportSymbol (exportedSymbols: Symbol[], sym: Symbol): Symbol | undefined { + const typeChecker = this.program.getTypeChecker() + return exportedSymbols.includes(sym) + ? sym + : (() => { + const index = exportedSymbols + .map(s => { + if (s.flags & ts.SymbolFlags.Alias) { + return typeChecker.getAliasedSymbol(s) + } + return s + }) + .indexOf(sym) + if (index !== -1) { + return exportedSymbols[index] + } + return undefined + })() + } + + trackDeps (decl: FunctionDeclaration | VariableDeclaration, exported: Symbol | undefined, parent: SymbolWrap | null, exportedSymbols: Symbol[]): void { + if (parent) { + parent.deps.add(decl) + } + if (this.wrap.has(decl)) { + return + } + const typeChecker = this.program.getTypeChecker() + const wrap: SymbolWrap = { + deps: new Set(), + exported, + replaceNode: decl, + jsdocTags: typeChecker.getSymbolAtLocation(decl.name!)!.getJsDocTags() + } + this.wrap.set(decl, wrap) + + // const syms = typeChecker.getSymbolsInScope(decl, ts.SymbolFlags.Value) + // const functionScopedSyms = typeChecker.getSymbolsInScope(decl, ts.SymbolFlags.FunctionScopedVariable) + const targetSymbolFlags = ts.SymbolFlags.Variable | ts.SymbolFlags.Function | ts.SymbolFlags.Class | ts.SymbolFlags.Enum + const accessableSyms = ts.isFunctionDeclaration(decl) + ? typeChecker.getSymbolsInScope(decl, targetSymbolFlags) + : decl.initializer ? typeChecker.getSymbolsInScope(decl.initializer, targetSymbolFlags) : [] + + const outScopedSyms = accessableSyms.filter((s) => { + const valueDeclaration = s.declarations?.[0] + if (!valueDeclaration) return false + if (!(ts.isVariableDeclaration(valueDeclaration) || ts.isFunctionDeclaration(valueDeclaration))) { + return false + } + let node: Node = valueDeclaration + while (node && node !== decl) { + node = node.parent + } + // not in scope + return node !== decl + }).map(s => { + if (s.flags & ts.SymbolFlags.ExportValue) { + return typeChecker.getExportSymbolOfSymbol(s) + } + return s + }) + const outScopedAndUsedSyms = new Set() + + const valueVisitor: Visitor = (node) => { + if (ts.isIdentifier(node)) { + const symbolValue = typeChecker.getSymbolAtLocation(node) + if (symbolValue && outScopedSyms.includes(symbolValue)) { + outScopedAndUsedSyms.add(symbolValue) + } + } + return ts.visitEachChild(node, valueVisitor, this.ctx) + } + ts.visitNode(decl, valueVisitor) + + // console.log(decl.getText(), [...outScopedAndUsedSyms].map(s => s.escapedName)) + outScopedAndUsedSyms.forEach(s => { + this.handleInternalSymbol(s, wrap, exportedSymbols) + }) + + this.exported.add(decl) + const renameVisitor: Visitor = (node) => { + if (ts.isFunctionDeclaration(node)) { + if (node.body) { + return this.ctx.factory.updateFunctionDeclaration(node, + node.modifiers?.filter((m) => m.kind !== ts.SyntaxKind.ExportKeyword), + node.asteriskToken, + ts.visitNode(node.name, renameVisitor) as ts.Identifier | undefined, + node.typeParameters, + node.parameters, + node.type, + ts.visitEachChild(node.body, renameVisitor, this.ctx) + ) + } + return node + } + if (ts.isIdentifier(node)) { + const symbolValue = typeChecker.getSymbolAtLocation(node) + if (symbolValue) { + const exportedSym = this.tryGetExportSymbol(exportedSymbols, symbolValue) + if (exportedSym) { + if ((exportedSym.escapedName as string).startsWith('$')) { + return ts.setOriginalNode(this.ctx.factory.createIdentifier((exportedSym.escapedName as string).substring(1)), node) + } + return ts.setOriginalNode(this.ctx.factory.createIdentifier('_' + exportedSym.escapedName), node) + } + } + } + return ts.visitEachChild(node, renameVisitor, this.ctx) + } + + wrap.replaceNode = ts.visitNode(decl, renameVisitor)! + } + + handleInternalSymbol (sym: Symbol, parent: SymbolWrap, exportedSymbols: Symbol[]): void { + const decl = sym.declarations?.[0] as VariableDeclaration | FunctionDeclaration + + this.trackDeps(decl, this.tryGetExportSymbol(exportedSymbols, sym), parent, exportedSymbols) + } + + createMergeExported (): ExpressionStatement { + const factory = this.ctx.factory + return factory.createExpressionStatement(factory.createCallExpression( + factory.createIdentifier('mergeInto'), + undefined, + [ + factory.createPropertyAccessExpression( + factory.createIdentifier('LibraryManager'), + factory.createIdentifier('library') + ), + 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)) { + throw new Error('import declaration is not supported: ' + stmt.getText(source)) + } + } + + const typeChecker = this.program.getTypeChecker() + const exportedSymbols = typeChecker.getExportsOfModule(typeChecker.getSymbolAtLocation(source)!) + + for (let i = 0; i < exportedSymbols.length; ++i) { + const exportedSymMaybeAlias = exportedSymbols[i] + if ((exportedSymMaybeAlias.escapedName as string) === '$') { + throw new Error('cannot export $') + } + const exportedSym = (exportedSymMaybeAlias.flags & ts.SymbolFlags.Alias) + ? typeChecker.getAliasedSymbol(exportedSymMaybeAlias) + : exportedSymMaybeAlias + const valueDeclaration = exportedSym.declarations?.[0] + if (!(valueDeclaration && ((ts.isFunctionDeclaration(valueDeclaration) && valueDeclaration.body) || (ts.isVariableDeclaration(valueDeclaration) && valueDeclaration.initializer)))) { + throw new Error('Unsupported export') + } + } + + for (let i = 0; i < exportedSymbols.length; ++i) { + const exportedSymMaybeAlias = exportedSymbols[i] + const exportedSym = (exportedSymMaybeAlias.flags & ts.SymbolFlags.Alias) + ? typeChecker.getAliasedSymbol(exportedSymMaybeAlias) + : exportedSymMaybeAlias + const valueDeclaration = exportedSym.declarations?.[0] as FunctionDeclaration | VariableDeclaration + this.trackDeps(valueDeclaration, exportedSymMaybeAlias, null, exportedSymbols) + } + + return source + } +} + +export default function createTransformer (program: Program): TransformerFactory { + return (context) => { + const transformer = new Transformer(context, program) + + return transformer.transform + } +} diff --git a/packages/emnapi/transformer/src/index.ts b/packages/emnapi/transformer/src/index.ts index 0d4095d2..7f089c90 100644 --- a/packages/emnapi/transformer/src/index.ts +++ b/packages/emnapi/transformer/src/index.ts @@ -21,6 +21,7 @@ import type { } from 'typescript' import * as ts from 'typescript' +import { join } from 'path' export interface DefineOptions { defines?: Record @@ -213,12 +214,21 @@ function updateBody (factory: NodeFactory, no class Transform { ctx: TransformationContext defines: Record + insertWasmMemoryImport: boolean + insertWasmTableImport: boolean constructor (context: TransformationContext, defines: Record) { this.ctx = context this.defines = defines this.visitor = this.visitor.bind(this) this.functionLikeDeclarationVisitor = this.functionLikeDeclarationVisitor.bind(this) + this.insertWasmMemoryImport = false + this.insertWasmTableImport = false + } + + resetSource (): void { + this.insertWasmMemoryImport = false + this.insertWasmTableImport = false } createHeapDataViewDeclaration (): VariableStatement { @@ -270,6 +280,9 @@ class Transform { let includeGetOrSet = false const statementVisitor: Visitor = (n) => { if (ts.isIdentifier(n) && n.text === 'HEAP_DATA_VIEW') { + if (!this.insertWasmMemoryImport) { + this.insertWasmMemoryImport = true + } includeGetOrSet = true } return ts.visitEachChild(n, statementVisitor, this.ctx) @@ -590,6 +603,10 @@ class Transform { const pointerName = argv1.text if (!pointerName) return node + if (!this.insertWasmTableImport) { + this.insertWasmTableImport = true + } + return this.ctx.factory.createParenthesizedExpression(this.ctx.factory.createCallExpression( this.ctx.factory.createPropertyAccessExpression( this.ctx.factory.createIdentifier('wasmTable'), @@ -675,6 +692,32 @@ class Transform { } } +function getImportsOfModule (src: SourceFile): string[] { + const collection = new Set() + for (let i = 0; i < src.statements.length; ++i) { + const s = src.statements[i] + if (ts.isImportDeclaration(s)) { + if (s.importClause && !s.importClause.isTypeOnly) { + if (s.importClause.name) { + collection.add(s.importClause.name.text) + } + if (s.importClause.namedBindings) { + if (ts.isNamedImports(s.importClause.namedBindings)) { + s.importClause.namedBindings.elements.filter(e => !e.isTypeOnly).forEach(sp => { + collection.add(sp.name.text) + }) + } else if (ts.isNamespaceImport(s.importClause.namedBindings)) { + collection.add(s.importClause.namedBindings.name.text) + } + } + } + } else if (ts.isImportEqualsDeclaration(s)) { + collection.add(s.name.text) + } + } + return [...collection] +} + function createTransformerFactory (_program: Program, config: DefineOptions): TransformerFactory { const defines = config.defines ?? {} // const defineKeys = Object.keys(defines) @@ -684,10 +727,56 @@ function createTransformerFactory (_program: Program, config: DefineOptions): Tr return (src) => { if (src.isDeclarationFile) return src + transform.resetSource() // expand emscripten macros const transformedSrc = ts.visitEachChild(src, transform.visitor, context) // inject HEAP_DATA_VIEW - return ts.visitEachChild(transformedSrc, transform.functionLikeDeclarationVisitor, context) + const injectedSrc = ts.visitEachChild(transformedSrc, transform.functionLikeDeclarationVisitor, context) + + const doNotInsertImport = join(__dirname, '../../src/core/init.ts') + if (src.fileName === doNotInsertImport) { + return injectedSrc + } + + let resultSrc = injectedSrc + let importNames: string[] | null = null + const factory = context.factory + if (transform.insertWasmMemoryImport) { + importNames = getImportsOfModule(resultSrc) + if (!importNames.includes('wasmMemory')) { + resultSrc = factory.updateSourceFile(resultSrc, [ + factory.createImportDeclaration(undefined, + factory.createImportClause(false, undefined, + factory.createNamedImports([ + factory.createImportSpecifier(false, undefined, factory.createIdentifier('wasmMemory')) + ]) + ), + factory.createStringLiteral('emnapi:emscripten-runtime'), + undefined + ), + ...resultSrc.statements + ]) + } + } + if (transform.insertWasmTableImport) { + importNames = getImportsOfModule(resultSrc) + if (!importNames.includes('wasmTable')) { + resultSrc = factory.updateSourceFile(resultSrc, [ + factory.createImportDeclaration(undefined, + factory.createImportClause(false, undefined, + factory.createNamedImports([ + factory.createImportSpecifier(false, undefined, factory.createIdentifier('wasmTable')) + ]) + ), + factory.createStringLiteral('emnapi:emscripten-runtime'), + undefined + ), + ...resultSrc.statements + ]) + } + } + + return resultSrc } } } diff --git a/packages/emnapi/tsconfig.base.json b/packages/emnapi/tsconfig.base.json index 1d87661e..45f52902 100644 --- a/packages/emnapi/tsconfig.base.json +++ b/packages/emnapi/tsconfig.base.json @@ -2,7 +2,8 @@ "extends": "../shared/tsconfig.base.json", "compilerOptions": { "target": "ES5", - "module": "none", + "module": "ESNext", + "moduleResolution": "Bundler", "importHelpers": false, "noEmitHelpers": true, "noUnusedLocals": false, diff --git a/packages/emnapi/tsconfig.json b/packages/emnapi/tsconfig.json index 4d644f97..08921b03 100644 --- a/packages/emnapi/tsconfig.json +++ b/packages/emnapi/tsconfig.json @@ -1,10 +1,9 @@ { "extends": "./tsconfig.base.json", "compilerOptions": { - "outFile": "./dist/library_napi.js", - "plugins": [ - { "transform": "./transformer/out/macro.js" } - ] + "paths": { + "emnapi:shared": ["./src/emscripten/init"] + } }, "include": [ "../runtime/src/typings/**/*.d.ts", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index f69626a2..3cbebd75 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -30,13 +30,6 @@ "dependencies": { "tslib": "^2.4.0" }, - "devDependencies": { - "@microsoft/api-extractor": "^7.35.3", - "@rollup/plugin-node-resolve": "^15.1.0", - "@rollup/plugin-replace": "^5.0.2", - "@rollup/plugin-terser": "^0.4.3", - "rollup": "^3.25.1" - }, "scripts": { "build": "node ./script/build.js" }, diff --git a/packages/runtime/script/build.js b/packages/runtime/script/build.js index 74aaa868..c9a888d5 100644 --- a/packages/runtime/script/build.js +++ b/packages/runtime/script/build.js @@ -1,6 +1,8 @@ const path = require('path') const fs = require('fs-extra') const rollup = require('rollup') +const ts = require('typescript') +const rollupTypescript = require('@rollup/plugin-typescript').default const rollupNodeResolve = require('@rollup/plugin-node-resolve').default const rollupReplace = require('@rollup/plugin-replace').default const rollupTerser = require('@rollup/plugin-terser').default @@ -8,54 +10,52 @@ const runtimeOut = path.join(__dirname, '../dist/emnapi.iife.js') const { compile } = require('@tybys/tsapi') function build () { - compile(path.join(__dirname, '../tsconfig.json'), { - optionsToExtend: { - target: require('typescript').ScriptTarget.ES5, - outDir: path.join(__dirname, '../lib/es5') - } - }) compile(path.join(__dirname, '../tsconfig.json'), { optionsToExtend: { target: require('typescript').ScriptTarget.ES2019, - outDir: path.join(__dirname, '../lib/es2019'), - removeComments: true, + emitDeclarationOnly: true, declaration: true, declarationMap: true, - declarationDir: path.join(__dirname, '../lib/typings'), - downlevelIteration: false + declarationDir: path.join(__dirname, '../lib/typings') } }) - const { - Extractor, - ExtractorConfig - } = require('@microsoft/api-extractor') - const apiExtractorJsonPath = path.join(__dirname, '../api-extractor.json') - const extractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath) - const extractorResult = Extractor.invoke(extractorConfig, { - localBuild: true, - showVerboseMessages: true - }) - if (extractorResult.succeeded) { - console.log('API Extractor completed successfully') - } else { - const errmsg = `API Extractor completed with ${extractorResult.errorCount} errors and ${extractorResult.warningCount} warnings` - return Promise.reject(new Error(errmsg)) - } - - const runtimeDts = extractorConfig.publicTrimmedFilePath /** - * @param {'es5' | 'es2019'} esversion + * @param {ts.ScriptTarget} esversion * @param {boolean=} minify * @returns {rollup.RollupOptions} */ - function createInput (esversion, minify, options) { + function createInput (esversion, minify, external) { return { - input: path.join(__dirname, '../lib', esversion, 'index.js'), + input: path.join(__dirname, '../src/index.ts'), + external, plugins: [ + rollupTypescript({ + tsconfig: path.join(__dirname, '../tsconfig.json'), + tslib: path.join( + path.dirname(require.resolve('tslib')), + JSON.parse(fs.readFileSync(path.join(path.dirname(require.resolve('tslib')), 'package.json'))).module + ), + compilerOptions: { + target: esversion, + ...(esversion !== ts.ScriptTarget.ES5 + ? { + removeComments: true, + downlevelIteration: false + } + : {}) + }, + include: [ + './src/**/*.ts' + ], + transformers: { + after: [ + require('@tybys/ts-transform-pure-class').default + ] + } + }), rollupNodeResolve({ - mainFields: ['module', 'main'], - ...((options && options.resolveOnly) ? { resolveOnly: options.resolveOnly } : {}) + mainFields: ['module', 'main'] }), rollupReplace({ preventAssignment: true, @@ -80,7 +80,7 @@ function build () { return Promise.all(([ { - input: createInput('es5', false), + input: createInput(ts.ScriptTarget.ES5, false), output: { file: runtimeOut, format: 'iife', @@ -90,7 +90,7 @@ function build () { } }, { - input: createInput('es5', false), + input: createInput(ts.ScriptTarget.ES5, false), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.js'), format: 'umd', @@ -100,7 +100,7 @@ function build () { } }, { - input: createInput('es5', true), + input: createInput(ts.ScriptTarget.ES5, true), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.min.js'), format: 'umd', @@ -110,7 +110,7 @@ function build () { } }, { - input: createInput('es2019', false, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, false, ['tslib']), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.cjs.js'), format: 'cjs', @@ -120,7 +120,7 @@ function build () { } }, { - input: createInput('es2019', true, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, true, ['tslib']), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.cjs.min.js'), format: 'cjs', @@ -130,7 +130,7 @@ function build () { } }, { - input: createInput('es2019', false, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, false, ['tslib']), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.mjs'), format: 'esm', @@ -140,7 +140,7 @@ function build () { } }, { - input: createInput('es2019', true, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES2019, true, ['tslib']), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.min.mjs'), format: 'esm', @@ -150,7 +150,7 @@ function build () { } }, { - input: createInput('es5', false, { resolveOnly: [/^(?!(tslib)).*?$/] }), + input: createInput(ts.ScriptTarget.ES5, false, ['tslib']), output: { file: path.join(path.dirname(runtimeOut), 'emnapi.esm-bundler.js'), format: 'esm', @@ -162,6 +162,25 @@ function build () { ]).map(conf => { return rollup.rollup(conf.input).then(bundle => bundle.write(conf.output)) })).then(() => { + const { + Extractor, + ExtractorConfig + } = require('@microsoft/api-extractor') + const apiExtractorJsonPath = path.join(__dirname, '../api-extractor.json') + const extractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath) + const extractorResult = Extractor.invoke(extractorConfig, { + localBuild: true, + showVerboseMessages: true + }) + if (extractorResult.succeeded) { + console.log('API Extractor completed successfully') + } else { + const errmsg = `API Extractor completed with ${extractorResult.errorCount} errors and ${extractorResult.warningCount} warnings` + return Promise.reject(new Error(errmsg)) + } + + const runtimeDts = extractorConfig.publicTrimmedFilePath + const iifeDtsPath = path.join(__dirname, '../dist/emnapi.iife.d.ts') const mDtsPath = path.join(__dirname, '../dist/emnapi.d.mts') const cjsMinDtsPath = path.join(__dirname, '../dist/emnapi.cjs.min.d.ts') diff --git a/packages/runtime/tsconfig.json b/packages/runtime/tsconfig.json index c0b816c2..17b3f9ff 100644 --- a/packages/runtime/tsconfig.json +++ b/packages/runtime/tsconfig.json @@ -5,7 +5,8 @@ "module": "ESNext", "noEmitHelpers": true, "importHelpers": true, - "importsNotUsedAsValues": "error", + "moduleResolution": "Bundler", + "outDir": "lib", "paths": { "tslib" : ["../../node_modules/tslib/tslib.d.ts"] }, @@ -15,13 +16,6 @@ "ES2020.BigInt", "ES2021.WeakRef", "DOM" - ], - "plugins": [ - { - "transform": "@tybys/ts-transform-pure-class", - "type": "raw", - "after": true - } ] }, "include": [ diff --git a/packages/shared/tsconfig.base.json b/packages/shared/tsconfig.base.json index 821b4454..92994b1c 100644 --- a/packages/shared/tsconfig.base.json +++ b/packages/shared/tsconfig.base.json @@ -1,10 +1,8 @@ { "compilerOptions": { - "ignoreDeprecations": "5.0", "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, - "noImplicitUseStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "strictNullChecks": true, diff --git a/packages/test/CMakeLists.txt b/packages/test/CMakeLists.txt index 8236c1a0..4b4c429b 100644 --- a/packages/test/CMakeLists.txt +++ b/packages/test/CMakeLists.txt @@ -91,6 +91,7 @@ if(IS_EMSCRIPTEN) "-sDEFAULT_PTHREAD_STACK_SIZE=2MB" "-sSAFE_HEAP=1" "-sMODULARIZE=1" + "-sEXPORTED_RUNTIME_METHODS=['ExitStatus']" ) elseif(IS_WASI_THREADS) add_compile_options("-fno-exceptions") @@ -247,7 +248,9 @@ add_test("env" "./env/binding.c" OFF) add_test("hello" "./hello/binding.c" OFF) add_test("async" "./async/binding.c" ON) +add_test("async_st" "./async/binding.c" OFF) add_test("tsfn2" "./tsfn2/binding.c" ON) +add_test("tsfn2_st" "./tsfn2/binding.c" OFF) if((NOT IS_WASM) OR IS_EMSCRIPTEN OR IS_WASI_THREADS) add_test("string_mt" "./string/binding.c;./string/test_null.c" ON) diff --git a/packages/test/async/async.test.js b/packages/test/async/async.test.js index b15705f7..7c7e0436 100644 --- a/packages/test/async/async.test.js +++ b/packages/test/async/async.test.js @@ -1,98 +1,7 @@ /* eslint-disable camelcase */ 'use strict' const { load } = require('../util') -const common = require('../common') -const assert = require('assert') -const child_process = require('child_process') +const main = require('./main') -async function main () { - const loadPromise = load('async') - const test_async = await loadPromise - - const testException = 'test_async_cb_exception' - - // Exception thrown from async completion callback. - // (Tested in a spawned process because the exception is fatal.) - if (process.argv[2] === 'child') { - if (!process.env.EMNAPI_TEST_NATIVE) { - process.on('uncaughtException', function (ex) { - // suppress ExitStatus exceptions from showing an error - if (!loadPromise.Module || !loadPromise.Module.ExitStatus || !(ex instanceof loadPromise.Module.ExitStatus)) { - throw ex - } - }) - } - test_async.Test(1, {}, common.mustCall(function () { - throw new Error(testException) - })) - return - } - const p = child_process.spawnSync( - process.execPath, [ - ...(process.env.EMNAPI_TEST_WASI ? ['--experimental-wasi-unstable-preview1'] : []), - ...(process.env.MEMORY64 ? ['--experimental-wasm-memory64'] : []), - __filename, - 'child' - ]) - assert.ifError(p.error) - const stderr = p.stderr.toString() - assert.ok(stderr.includes(testException)) - - await new Promise((resolve) => { - // Successful async execution and completion callback. - test_async.Test(5, {}, common.mustCall(function (err, val) { - console.log('test_async.Test(5, {}, callback)') - assert.strictEqual(err, null) - assert.strictEqual(val, 10) - process.nextTick(common.mustCall(() => { - console.log('process.nextTick(callback)') - resolve() - })) - })) - }) - - await new Promise((resolve) => { - // Async work item cancellation with callback. - test_async.TestCancel(common.mustCall(() => { - console.log('test_async.TestCancel(callback)') - resolve() - })) - }) - - await new Promise((resolve) => { - const iterations = 500 - let x = 0 - const workDone = common.mustCall((status) => { - assert.strictEqual(status, 0) - if (++x < iterations) { - setImmediate(() => test_async.DoRepeatedWork(workDone)) - } else { - resolve() - } - }, iterations) - test_async.DoRepeatedWork(workDone) - }) - - await new Promise((resolve) => { - process.once('uncaughtException', common.mustCall(function (err) { - try { - throw new Error('should not fail') - } catch (err) { - assert.strictEqual(err.message, 'should not fail') - } - assert.strictEqual(err.message, 'uncaught') - console.log('process.once("uncaughtException", callback): ' + err.message) - resolve() - })) - - // Successful async execution and completion callback. - test_async.Test(5, {}, common.mustCall(function () { - throw new Error('uncaught') - })) - }) - - process.exitCode = 0 -} - -module.exports = main() +module.exports = main(load('async'), __filename) // module.exports.immdiateExit = true diff --git a/packages/test/async/async_st.test.js b/packages/test/async/async_st.test.js new file mode 100644 index 00000000..a76c2edf --- /dev/null +++ b/packages/test/async/async_st.test.js @@ -0,0 +1,9 @@ +/* eslint-disable camelcase */ +'use strict' +const { load } = require('../util') +const main = require('./main') + +const RUNTIME_UV_THREADPOOL_SIZE = ('UV_THREADPOOL_SIZE' in process.env) ? Number(process.env.UV_THREADPOOL_SIZE) : 4 +module.exports = main(load('async_st', { + asyncWorkPoolSize: -RUNTIME_UV_THREADPOOL_SIZE +}), __filename) diff --git a/packages/test/async/main.js b/packages/test/async/main.js new file mode 100644 index 00000000..c2d22610 --- /dev/null +++ b/packages/test/async/main.js @@ -0,0 +1,93 @@ +/* eslint-disable camelcase */ +'use strict' +const common = require('../common') +const assert = require('assert') +const child_process = require('child_process') + +module.exports = async function main (loadPromise, __filename) { + const test_async = await loadPromise + + const testException = 'test_async_cb_exception' + + // Exception thrown from async completion callback. + // (Tested in a spawned process because the exception is fatal.) + if (process.argv[2] === 'child') { + if (!process.env.EMNAPI_TEST_NATIVE) { + process.on('uncaughtException', function (ex) { + // suppress ExitStatus exceptions from showing an error + if (!loadPromise.Module || !loadPromise.Module.ExitStatus || !(ex instanceof loadPromise.Module.ExitStatus)) { + throw ex + } + }) + } + test_async.Test(1, {}, common.mustCall(function () { + throw new Error(testException) + })) + return + } + const p = child_process.spawnSync( + process.execPath, [ + ...(process.env.EMNAPI_TEST_WASI ? ['--experimental-wasi-unstable-preview1'] : []), + ...(process.env.MEMORY64 ? ['--experimental-wasm-memory64'] : []), + __filename, + 'child' + ]) + assert.ifError(p.error) + const stderr = p.stderr.toString() + assert.ok(stderr.includes(testException)) + + await new Promise((resolve) => { + // Successful async execution and completion callback. + test_async.Test(5, {}, common.mustCall(function (err, val) { + console.log('test_async.Test(5, {}, callback)') + assert.strictEqual(err, null) + assert.strictEqual(val, 10) + process.nextTick(common.mustCall(() => { + console.log('process.nextTick(callback)') + resolve() + })) + })) + }) + + await new Promise((resolve) => { + // Async work item cancellation with callback. + test_async.TestCancel(common.mustCall(() => { + console.log('test_async.TestCancel(callback)') + resolve() + })) + }) + + await new Promise((resolve) => { + const iterations = 500 + let x = 0 + const workDone = common.mustCall((status) => { + assert.strictEqual(status, 0) + if (++x < iterations) { + setImmediate(() => test_async.DoRepeatedWork(workDone)) + } else { + resolve() + } + }, iterations) + test_async.DoRepeatedWork(workDone) + }) + + await new Promise((resolve) => { + process.once('uncaughtException', common.mustCall(function (err) { + try { + throw new Error('should not fail') + } catch (err) { + assert.strictEqual(err.message, 'should not fail') + } + assert.strictEqual(err.message, 'uncaught') + console.log('process.once("uncaughtException", callback): ' + err.message) + resolve() + })) + + // Successful async execution and completion callback. + test_async.Test(5, {}, common.mustCall(function () { + throw new Error('uncaught') + })) + }) + + process.exitCode = 0 +} diff --git a/packages/test/tsfn2/main.js b/packages/test/tsfn2/main.js new file mode 100644 index 00000000..0d6b5256 --- /dev/null +++ b/packages/test/tsfn2/main.js @@ -0,0 +1,27 @@ +/* eslint-disable camelcase */ +'use strict' +const common = require('../common') +const assert = require('assert') + +module.exports = async function main (loadPromise) { + const binding = await loadPromise + + const input = 10 + const count = 3 + const result = input + count + const progressData = [] + for (let i = 1; i <= count; ++i) { + progressData.push(input + i) + } + const actual = [] + binding.testTSFN(input, count, common.mustCall(function (status, out) { + console.log(status, out) + assert.strictEqual(status, 0) + assert.strictEqual(out, result) + assert.deepStrictEqual(actual, progressData) + }, 1), common.mustCall(function (current) { + actual.push(current) + console.log(current) + }, count)) + console.log('testTSFN') +} diff --git a/packages/test/tsfn2/tsfn2.test.js b/packages/test/tsfn2/tsfn2.test.js index 375b25ff..3bbcce73 100644 --- a/packages/test/tsfn2/tsfn2.test.js +++ b/packages/test/tsfn2/tsfn2.test.js @@ -1,31 +1,6 @@ /* eslint-disable camelcase */ 'use strict' const { load } = require('../util') -const common = require('../common') -const assert = require('assert') +const main = require('./main') -async function main () { - const loadPromise = load('tsfn2', { nodeBinding: require('@emnapi/node-binding') }) - const binding = await loadPromise - - const input = 10 - const count = 3 - const result = input + count - const progressData = [] - for (let i = 1; i <= count; ++i) { - progressData.push(input + i) - } - const actual = [] - binding.testTSFN(input, count, common.mustCall(function (status, out) { - console.log(status, out) - assert.strictEqual(status, 0) - assert.strictEqual(out, result) - assert.deepStrictEqual(actual, progressData) - }, 1), common.mustCall(function (current) { - actual.push(current) - console.log(current) - }, count)) - console.log('testTSFN') -} - -module.exports = main() +module.exports = main(load('tsfn2', { nodeBinding: require('@emnapi/node-binding') })) diff --git a/packages/test/tsfn2/tsfn2_st.test.js b/packages/test/tsfn2/tsfn2_st.test.js new file mode 100644 index 00000000..385e951d --- /dev/null +++ b/packages/test/tsfn2/tsfn2_st.test.js @@ -0,0 +1,11 @@ +/* eslint-disable camelcase */ +'use strict' +const { load } = require('../util') +const main = require('./main') + +const RUNTIME_UV_THREADPOOL_SIZE = ('UV_THREADPOOL_SIZE' in process.env) ? Number(process.env.UV_THREADPOOL_SIZE) : 4 + +module.exports = main(load('tsfn2_st', { + nodeBinding: require('@emnapi/node-binding'), + asyncWorkPoolSize: -RUNTIME_UV_THREADPOOL_SIZE +})) diff --git a/tsconfig.json b/tsconfig.json index 4bf02f30..cd1eb7e0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,6 @@ "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, - "noImplicitUseStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "strictNullChecks": true,