From d590c9e4d79d653e132881dcec016685e1b4b292 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 09:30:19 -0500 Subject: [PATCH 01/21] Split runtime into multiple files --- Runtime/src/closure-heap.ts | 16 ++++ Runtime/src/find-global.ts | 13 +++ Runtime/src/index.ts | 166 +++++------------------------------- Runtime/src/object-heap.ts | 63 ++++++++++++++ Runtime/src/types.ts | 48 +++++++++++ 5 files changed, 159 insertions(+), 147 deletions(-) create mode 100644 Runtime/src/closure-heap.ts create mode 100644 Runtime/src/find-global.ts create mode 100644 Runtime/src/object-heap.ts create mode 100644 Runtime/src/types.ts diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts new file mode 100644 index 000000000..a27625dcf --- /dev/null +++ b/Runtime/src/closure-heap.ts @@ -0,0 +1,16 @@ +import { SwiftRuntimeExportedFunctions } from "./types"; + +/// Memory lifetime of closures in Swift are managed by Swift side +export class SwiftClosureHeap { + private functionRegistry: FinalizationRegistry; + + constructor(exports: SwiftRuntimeExportedFunctions) { + this.functionRegistry = new FinalizationRegistry((id) => { + exports.swjs_free_host_function(id); + }); + } + + alloc(func: any, func_ref: number) { + this.functionRegistry.register(func, func_ref); + } +} diff --git a/Runtime/src/find-global.ts b/Runtime/src/find-global.ts new file mode 100644 index 000000000..f20afbf34 --- /dev/null +++ b/Runtime/src/find-global.ts @@ -0,0 +1,13 @@ +interface GlobalVariable {} +declare const global: GlobalVariable; + +export let globalVariable: any; +if (typeof globalThis !== "undefined") { + globalVariable = globalThis; +} else if (typeof window !== "undefined") { + globalVariable = window; +} else if (typeof global !== "undefined") { + globalVariable = global; +} else if (typeof self !== "undefined") { + globalVariable = self; +} diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 2fc71c6c8..4a6e30378 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -1,141 +1,14 @@ -interface ExportedMemory { - buffer: any; -} - -type ref = number; -type pointer = number; - -interface GlobalVariable {} -declare const window: GlobalVariable; -declare const global: GlobalVariable; -let globalVariable: any; -if (typeof globalThis !== "undefined") { - globalVariable = globalThis; -} else if (typeof window !== "undefined") { - globalVariable = window; -} else if (typeof global !== "undefined") { - globalVariable = global; -} else if (typeof self !== "undefined") { - globalVariable = self; -} - -interface SwiftRuntimeExportedFunctions { - swjs_library_version(): number; - swjs_library_features(): number; - swjs_prepare_host_function_call(size: number): pointer; - swjs_cleanup_host_function_call(argv: pointer): void; - swjs_call_host_function( - host_func_id: number, - argv: pointer, - argc: number, - callback_func_ref: ref - ): void; - - swjs_free_host_function(host_func_id: number): void; -} - -enum JavaScriptValueKind { - Invalid = -1, - Boolean = 0, - String = 1, - Number = 2, - Object = 3, - Null = 4, - Undefined = 5, - Function = 6, -} - -enum LibraryFeatures { - WeakRefs = 1 << 0, -} - -type TypedArray = - | Int8ArrayConstructor - | Uint8ArrayConstructor - | Int16ArrayConstructor - | Uint16ArrayConstructor - | Int32ArrayConstructor - | Uint32ArrayConstructor - // | BigInt64ArrayConstructor - // | BigUint64ArrayConstructor - | Float32ArrayConstructor - | Float64ArrayConstructor; - -type SwiftRuntimeHeapEntry = { - id: number; - rc: number; -}; -class SwiftRuntimeHeap { - private _heapValueById: Map; - private _heapEntryByValue: Map; - private _heapNextKey: number; - - constructor() { - this._heapValueById = new Map(); - this._heapValueById.set(0, globalVariable); - - this._heapEntryByValue = new Map(); - this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); - - // Note: 0 is preserved for global - this._heapNextKey = 1; - } - - retain(value: any) { - const isObject = typeof value == "object"; - const entry = this._heapEntryByValue.get(value); - if (isObject && entry) { - entry.rc++; - return entry.id; - } - const id = this._heapNextKey++; - this._heapValueById.set(id, value); - if (isObject) { - this._heapEntryByValue.set(value, { id: id, rc: 1 }); - } - return id; - } - - release(ref: ref) { - const value = this._heapValueById.get(ref); - const isObject = typeof value == "object"; - if (isObject) { - const entry = this._heapEntryByValue.get(value)!; - entry.rc--; - if (entry.rc != 0) return; - - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref); - } else { - this._heapValueById.delete(ref); - } - } - - referenceHeap(ref: ref) { - const value = this._heapValueById.get(ref); - if (value === undefined) { - throw new ReferenceError( - "Attempted to read invalid reference " + ref - ); - } - return value; - } -} - -/// Memory lifetime of closures in Swift are managed by Swift side -class SwiftClosureHeap { - private functionRegistry: FinalizationRegistry; - - constructor(exports: SwiftRuntimeExportedFunctions) { - this.functionRegistry = new FinalizationRegistry((id) => { - exports.swjs_free_host_function(id); - }); - } - - alloc(func: any, func_ref: number) { - this.functionRegistry.register(func, func_ref); - } -} +import { SwiftClosureHeap } from "./closure-heap"; +import { SwiftRuntimeHeap } from "./object-heap"; +import { + ExportedMemory, + LibraryFeatures, + SwiftRuntimeExportedFunctions, + ref, + pointer, + JavaScriptValueKind, + TypedArray, +} from "./types"; export class SwiftRuntime { private instance: WebAssembly.Instance | null; @@ -151,8 +24,8 @@ export class SwiftRuntime { setInstance(instance: WebAssembly.Instance) { this.instance = instance; - const exports = (this.instance - .exports as any) as SwiftRuntimeExportedFunctions; + const exports = this.instance + .exports as any as SwiftRuntimeExportedFunctions; if (exports.swjs_library_version() != this.version) { throw new Error( `The versions of JavaScriptKit are incompatible. ${exports.swjs_library_version()} != ${ @@ -166,8 +39,8 @@ export class SwiftRuntime { if (!this.instance) throw new Error("WebAssembly instance is not set yet"); - const exports = (this.instance - .exports as any) as SwiftRuntimeExportedFunctions; + const exports = this.instance + .exports as any as SwiftRuntimeExportedFunctions; const features = exports.swjs_library_features(); const librarySupportsWeakRef = (features & LibraryFeatures.WeakRefs) != 0; @@ -194,8 +67,8 @@ export class SwiftRuntime { const callHostFunction = (host_func_id: number, args: any[]) => { if (!this.instance) throw new Error("WebAssembly instance is not set yet"); - const exports = (this.instance - .exports as any) as SwiftRuntimeExportedFunctions; + const exports = this.instance + .exports as any as SwiftRuntimeExportedFunctions; const argc = args.length; const argv = exports.swjs_prepare_host_function_call(argc); for (let index = 0; index < args.length; index++) { @@ -570,9 +443,8 @@ export class SwiftRuntime { elementsPtr: pointer, length: number ) => { - const ArrayType: TypedArray = this.heap.referenceHeap( - constructor_ref - ); + const ArrayType: TypedArray = + this.heap.referenceHeap(constructor_ref); const array = new ArrayType( memory().buffer, elementsPtr, diff --git a/Runtime/src/object-heap.ts b/Runtime/src/object-heap.ts new file mode 100644 index 000000000..61f1925fe --- /dev/null +++ b/Runtime/src/object-heap.ts @@ -0,0 +1,63 @@ +import { globalVariable } from "./find-global"; +import { ref } from "./types"; + +type SwiftRuntimeHeapEntry = { + id: number; + rc: number; +}; +export class SwiftRuntimeHeap { + private _heapValueById: Map; + private _heapEntryByValue: Map; + private _heapNextKey: number; + + constructor() { + this._heapValueById = new Map(); + this._heapValueById.set(0, globalVariable); + + this._heapEntryByValue = new Map(); + this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); + + // Note: 0 is preserved for global + this._heapNextKey = 1; + } + + retain(value: any) { + const isObject = typeof value == "object"; + const entry = this._heapEntryByValue.get(value); + if (isObject && entry) { + entry.rc++; + return entry.id; + } + const id = this._heapNextKey++; + this._heapValueById.set(id, value); + if (isObject) { + this._heapEntryByValue.set(value, { id: id, rc: 1 }); + } + return id; + } + + release(ref: ref) { + const value = this._heapValueById.get(ref); + const isObject = typeof value == "object"; + if (isObject) { + const entry = this._heapEntryByValue.get(value)!; + entry.rc--; + if (entry.rc != 0) return; + + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref); + } else { + this._heapValueById.delete(ref); + } + } + + referenceHeap(ref: ref) { + const value = this._heapValueById.get(ref); + if (value === undefined) { + throw new ReferenceError( + "Attempted to read invalid reference " + ref + ); + } + return value; + } +} diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts new file mode 100644 index 000000000..15f4cb73a --- /dev/null +++ b/Runtime/src/types.ts @@ -0,0 +1,48 @@ +export interface ExportedMemory { + buffer: any; +} + +export type ref = number; +export type pointer = number; + +export interface SwiftRuntimeExportedFunctions { + swjs_library_version(): number; + swjs_library_features(): number; + swjs_prepare_host_function_call(size: number): pointer; + swjs_cleanup_host_function_call(argv: pointer): void; + swjs_call_host_function( + host_func_id: number, + argv: pointer, + argc: number, + callback_func_ref: ref + ): void; + + swjs_free_host_function(host_func_id: number): void; +} + +export enum JavaScriptValueKind { + Invalid = -1, + Boolean = 0, + String = 1, + Number = 2, + Object = 3, + Null = 4, + Undefined = 5, + Function = 6, +} + +export enum LibraryFeatures { + WeakRefs = 1 << 0, +} + +export type TypedArray = + | Int8ArrayConstructor + | Uint8ArrayConstructor + | Int16ArrayConstructor + | Uint16ArrayConstructor + | Int32ArrayConstructor + | Uint32ArrayConstructor + // | BigInt64ArrayConstructor + // | BigUint64ArrayConstructor + | Float32ArrayConstructor + | Float64ArrayConstructor; From a274ca26d009536ef5006e8e8b0fdd3f52b8e8dc Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 09:36:18 -0500 Subject: [PATCH 02/21] use Rollup to bundle everything into one file --- Runtime/rollup.config.js | 13 ++ Runtime/tsconfig.json | 2 +- package-lock.json | 271 +++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 4 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 Runtime/rollup.config.js diff --git a/Runtime/rollup.config.js b/Runtime/rollup.config.js new file mode 100644 index 000000000..57ee7a3e2 --- /dev/null +++ b/Runtime/rollup.config.js @@ -0,0 +1,13 @@ +import typescript from "@rollup/plugin-typescript"; + +/** @type {import('rollup').RollupOptions} */ +const config = { + input: "src/index.ts", + output: { + dir: "lib", + format: "cjs", + }, + plugins: [typescript()], +}; + +export default config; diff --git a/Runtime/tsconfig.json b/Runtime/tsconfig.json index 3182659ca..567b743f5 100644 --- a/Runtime/tsconfig.json +++ b/Runtime/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "declaration": true, "importHelpers": true, - "module": "commonjs", + "module": "esnext", "outDir": "lib", "rootDir": "src", "strict": true, diff --git a/package-lock.json b/package-lock.json index d6c775156..c45476520 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,121 @@ "version": "0.11.1", "license": "MIT", "devDependencies": { + "@rollup/plugin-typescript": "^8.3.0", "prettier": "2.5.1", + "rollup": "^2.63.0", "typescript": "^4.4.2" } }, + "node_modules/@rollup/plugin-typescript": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz", + "integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0", + "tslib": "*", + "typescript": ">=3.7.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/prettier": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", @@ -25,6 +136,57 @@ "node": ">=10.13.0" } }, + "node_modules/resolve": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "2.63.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz", + "integrity": "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true, + "peer": true + }, "node_modules/typescript": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", @@ -40,12 +202,121 @@ } }, "dependencies": { + "@rollup/plugin-typescript": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz", + "integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "resolve": "^1.17.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "prettier": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", "dev": true }, + "resolve": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", + "dev": true, + "requires": { + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rollup": { + "version": "2.63.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz", + "integrity": "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true, + "peer": true + }, "typescript": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", diff --git a/package.json b/package.json index 3beae47fa..749d4b379 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "npm run build:clean && npm run build:ts", "build:clean": "rm -rf Runtime/lib", - "build:ts": "cd Runtime; tsc -b", + "build:ts": "cd Runtime; rollup -c", "prepublishOnly": "npm run build", "format": "prettier --write Runtime/src" }, @@ -32,7 +32,9 @@ "author": "swiftwasm", "license": "MIT", "devDependencies": { + "@rollup/plugin-typescript": "^8.3.0", "prettier": "2.5.1", + "rollup": "^2.63.0", "typescript": "^4.4.2" } } From d76b29522cf2bb4c2d9bd0351dfb5a9f4d6e656f Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 09:37:12 -0500 Subject: [PATCH 03/21] use UMD to allow directly loading via a script tag --- Runtime/rollup.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Runtime/rollup.config.js b/Runtime/rollup.config.js index 57ee7a3e2..ce5870d87 100644 --- a/Runtime/rollup.config.js +++ b/Runtime/rollup.config.js @@ -5,7 +5,8 @@ const config = { input: "src/index.ts", output: { dir: "lib", - format: "cjs", + format: "umd", + name: "JavaScriptKit", }, plugins: [typescript()], }; From 66e33c2b32c86bdb9641d01d4b3a24b25be1773e Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 09:47:19 -0500 Subject: [PATCH 04/21] Build ESM and UMD builds separately --- Runtime/rollup.config.js | 16 +++++++++++----- Runtime/tsconfig.json | 3 ++- package.json | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Runtime/rollup.config.js b/Runtime/rollup.config.js index ce5870d87..cf9b49b57 100644 --- a/Runtime/rollup.config.js +++ b/Runtime/rollup.config.js @@ -3,11 +3,17 @@ import typescript from "@rollup/plugin-typescript"; /** @type {import('rollup').RollupOptions} */ const config = { input: "src/index.ts", - output: { - dir: "lib", - format: "umd", - name: "JavaScriptKit", - }, + output: [ + { + file: "lib/index.mjs", + format: "esm", + }, + { + dir: "lib", + format: "umd", + name: "JavaScriptKit", + }, + ], plugins: [typescript()], }; diff --git a/Runtime/tsconfig.json b/Runtime/tsconfig.json index 567b743f5..8fa8e650d 100644 --- a/Runtime/tsconfig.json +++ b/Runtime/tsconfig.json @@ -1,9 +1,10 @@ { "compilerOptions": { "declaration": true, + "declarationDir": "lib", "importHelpers": true, "module": "esnext", - "outDir": "lib", + "noEmit": true, "rootDir": "src", "strict": true, "target": "es2017", diff --git a/package.json b/package.json index 749d4b379..ffe8fc4cf 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,8 @@ "version": "0.11.1", "description": "A runtime library of JavaScriptKit which is Swift framework to interact with JavaScript through WebAssembly.", "main": "Runtime/lib/index.js", + "module": "Runtime/lib/index.mjs", + "types": "Runtime/lib/index.d.ts", "files": [ "Runtime/lib" ], From 2f1d6b178ca9a7d906db256e37a8e6c32904e7df Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 09:50:56 -0500 Subject: [PATCH 05/21] Add memory and exports getters on SwiftRuntime --- Runtime/src/index.ts | 52 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 4a6e30378..0bcaea39e 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -34,19 +34,31 @@ export class SwiftRuntime { ); } } - get closureHeap(): SwiftClosureHeap | null { - if (this._closureHeap) return this._closureHeap; + + private ensureInstance() { if (!this.instance) throw new Error("WebAssembly instance is not set yet"); + return this.instance; + } - const exports = this.instance + get memory() { + return this.ensureInstance().exports.memory as ExportedMemory; + } + + get exports() { + return this.ensureInstance() .exports as any as SwiftRuntimeExportedFunctions; - const features = exports.swjs_library_features(); + } + + get closureHeap(): SwiftClosureHeap | null { + if (this._closureHeap) return this._closureHeap; + + const features = this.exports.swjs_library_features(); const librarySupportsWeakRef = (features & LibraryFeatures.WeakRefs) != 0; if (librarySupportsWeakRef) { if (typeof FinalizationRegistry !== "undefined") { - this._closureHeap = new SwiftClosureHeap(exports); + this._closureHeap = new SwiftClosureHeap(this.exports); return this._closureHeap; } else { throw new Error( @@ -58,19 +70,9 @@ export class SwiftRuntime { } importObjects() { - const memory = () => { - if (this.instance) - return this.instance.exports.memory as ExportedMemory; - throw new Error("WebAssembly instance is not set yet"); - }; - const callHostFunction = (host_func_id: number, args: any[]) => { - if (!this.instance) - throw new Error("WebAssembly instance is not set yet"); - const exports = this.instance - .exports as any as SwiftRuntimeExportedFunctions; const argc = args.length; - const argv = exports.swjs_prepare_host_function_call(argc); + const argv = this.exports.swjs_prepare_host_function_call(argc); for (let index = 0; index < args.length; index++) { const argument = args[index]; const base = argv + 16 * index; @@ -80,13 +82,13 @@ export class SwiftRuntime { const callback_func_ref = this.heap.retain(function (result: any) { output = result; }); - exports.swjs_call_host_function( + this.exports.swjs_call_host_function( host_func_id, argv, argc, callback_func_ref ); - exports.swjs_cleanup_host_function_call(argv); + this.exports.swjs_cleanup_host_function_call(argv); return output; }; @@ -98,12 +100,12 @@ export class SwiftRuntime { }; const writeString = (ptr: pointer, bytes: Uint8Array) => { - const uint8Memory = new Uint8Array(memory().buffer); + const uint8Memory = new Uint8Array(this.memory.buffer); uint8Memory.set(bytes, ptr); }; const readUInt32 = (ptr: pointer) => { - const uint8Memory = new Uint8Array(memory().buffer); + const uint8Memory = new Uint8Array(this.memory.buffer); return ( uint8Memory[ptr + 0] + (uint8Memory[ptr + 1] << 8) + @@ -113,12 +115,12 @@ export class SwiftRuntime { }; const readFloat64 = (ptr: pointer) => { - const dataView = new DataView(memory().buffer); + const dataView = new DataView(this.memory.buffer); return dataView.getFloat64(ptr, true); }; const writeUint32 = (ptr: pointer, value: number) => { - const uint8Memory = new Uint8Array(memory().buffer); + const uint8Memory = new Uint8Array(this.memory.buffer); uint8Memory[ptr + 0] = (value & 0x000000ff) >> 0; uint8Memory[ptr + 1] = (value & 0x0000ff00) >> 8; uint8Memory[ptr + 2] = (value & 0x00ff0000) >> 16; @@ -126,7 +128,7 @@ export class SwiftRuntime { }; const writeFloat64 = (ptr: pointer, value: number) => { - const dataView = new DataView(memory().buffer); + const dataView = new DataView(this.memory.buffer); dataView.setFloat64(ptr, value, true); }; @@ -307,7 +309,7 @@ export class SwiftRuntime { }, swjs_decode_string: (bytes_ptr: pointer, length: number) => { - const uint8Memory = new Uint8Array(memory().buffer); + const uint8Memory = new Uint8Array(this.memory.buffer); const bytes = uint8Memory.subarray( bytes_ptr, bytes_ptr + length @@ -446,7 +448,7 @@ export class SwiftRuntime { const ArrayType: TypedArray = this.heap.referenceHeap(constructor_ref); const array = new ArrayType( - memory().buffer, + this.memory.buffer, elementsPtr, length ); From 630495c92dfe3129eda272f7d14fee05bad6bb2c Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 09:53:04 -0500 Subject: [PATCH 06/21] move WeakRef availability checking to SwiftClosureHeap --- Runtime/src/closure-heap.ts | 6 ++++++ Runtime/src/index.ts | 11 ++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts index a27625dcf..d8b7de566 100644 --- a/Runtime/src/closure-heap.ts +++ b/Runtime/src/closure-heap.ts @@ -5,6 +5,12 @@ export class SwiftClosureHeap { private functionRegistry: FinalizationRegistry; constructor(exports: SwiftRuntimeExportedFunctions) { + if (typeof FinalizationRegistry === "undefined") { + throw new Error( + "The Swift part of JavaScriptKit was configured to require the availability of JavaScript WeakRefs. Please build with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to disable features that use WeakRefs." + ); + } + this.functionRegistry = new FinalizationRegistry((id) => { exports.swjs_free_host_function(id); }); diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 0bcaea39e..ff4640113 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -57,16 +57,9 @@ export class SwiftRuntime { const librarySupportsWeakRef = (features & LibraryFeatures.WeakRefs) != 0; if (librarySupportsWeakRef) { - if (typeof FinalizationRegistry !== "undefined") { - this._closureHeap = new SwiftClosureHeap(this.exports); - return this._closureHeap; - } else { - throw new Error( - "The Swift part of JavaScriptKit was configured to require the availability of JavaScript WeakRefs. Please build with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to disable features that use WeakRefs." - ); - } + this._closureHeap = new SwiftClosureHeap(this.exports); } - return null; + return this._closureHeap; } importObjects() { From 2b26cc4eb917bcdfcc54743f9446d1b9376a6162 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 10:32:52 -0500 Subject: [PATCH 07/21] add a Memory class that wraps the heap and raw memory --- Runtime/src/index.ts | 350 +++++++++++++++------------------------- Runtime/src/js-value.ts | 130 +++++++++++++++ Runtime/src/memory.ts | 32 ++++ Runtime/src/types.ts | 15 -- 4 files changed, 288 insertions(+), 239 deletions(-) create mode 100644 Runtime/src/js-value.ts create mode 100644 Runtime/src/memory.ts diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index ff4640113..ba173313b 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -1,53 +1,57 @@ import { SwiftClosureHeap } from "./closure-heap"; -import { SwiftRuntimeHeap } from "./object-heap"; import { - ExportedMemory, LibraryFeatures, SwiftRuntimeExportedFunctions, ref, pointer, - JavaScriptValueKind, TypedArray, } from "./types"; +import { + decodeJSValue, + decodeJSValueArray, + JavaScriptValueKind, + writeJSValue, +} from "./js-value"; +import { Memory } from "./memory"; export class SwiftRuntime { - private instance: WebAssembly.Instance | null; - private heap: SwiftRuntimeHeap; + private _instance: WebAssembly.Instance | null; + private _memory: Memory | null; private _closureHeap: SwiftClosureHeap | null; private version: number = 703; constructor() { - this.instance = null; - this.heap = new SwiftRuntimeHeap(); + this._instance = null; + this._memory = null; this._closureHeap = null; } setInstance(instance: WebAssembly.Instance) { - this.instance = instance; - const exports = this.instance - .exports as any as SwiftRuntimeExportedFunctions; - if (exports.swjs_library_version() != this.version) { + this._instance = instance; + if (this.exports.swjs_library_version() != this.version) { throw new Error( - `The versions of JavaScriptKit are incompatible. ${exports.swjs_library_version()} != ${ + `The versions of JavaScriptKit are incompatible. ${this.exports.swjs_library_version()} != ${ this.version }` ); } } - private ensureInstance() { - if (!this.instance) + get instance() { + if (!this._instance) throw new Error("WebAssembly instance is not set yet"); - return this.instance; + return this._instance; } - get memory() { - return this.ensureInstance().exports.memory as ExportedMemory; + get exports() { + return this.instance.exports as any as SwiftRuntimeExportedFunctions; } - get exports() { - return this.ensureInstance() - .exports as any as SwiftRuntimeExportedFunctions; + private get memory() { + if (!this._memory) { + this._memory = new Memory(this.instance.exports); + } + return this._memory; } get closureHeap(): SwiftClosureHeap | null { @@ -69,10 +73,18 @@ export class SwiftRuntime { for (let index = 0; index < args.length; index++) { const argument = args[index]; const base = argv + 16 * index; - writeValue(argument, base, base + 4, base + 8, false); + writeJSValue( + argument, + base, + base + 4, + base + 8, + false, + this.memory + ); } let output: any; - const callback_func_ref = this.heap.retain(function (result: any) { + // This ref is released by the swjs_call_host_function implementation + const callback_func_ref = this.memory.retain((result: any) => { output = result; }); this.exports.swjs_call_host_function( @@ -88,161 +100,6 @@ export class SwiftRuntime { const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder(); // Only support utf-8 - const readString = (ref: ref) => { - return this.heap.referenceHeap(ref); - }; - - const writeString = (ptr: pointer, bytes: Uint8Array) => { - const uint8Memory = new Uint8Array(this.memory.buffer); - uint8Memory.set(bytes, ptr); - }; - - const readUInt32 = (ptr: pointer) => { - const uint8Memory = new Uint8Array(this.memory.buffer); - return ( - uint8Memory[ptr + 0] + - (uint8Memory[ptr + 1] << 8) + - (uint8Memory[ptr + 2] << 16) + - (uint8Memory[ptr + 3] << 24) - ); - }; - - const readFloat64 = (ptr: pointer) => { - const dataView = new DataView(this.memory.buffer); - return dataView.getFloat64(ptr, true); - }; - - const writeUint32 = (ptr: pointer, value: number) => { - const uint8Memory = new Uint8Array(this.memory.buffer); - uint8Memory[ptr + 0] = (value & 0x000000ff) >> 0; - uint8Memory[ptr + 1] = (value & 0x0000ff00) >> 8; - uint8Memory[ptr + 2] = (value & 0x00ff0000) >> 16; - uint8Memory[ptr + 3] = (value & 0xff000000) >> 24; - }; - - const writeFloat64 = (ptr: pointer, value: number) => { - const dataView = new DataView(this.memory.buffer); - dataView.setFloat64(ptr, value, true); - }; - - const decodeValue = ( - kind: JavaScriptValueKind, - payload1: number, - payload2: number - ) => { - switch (kind) { - case JavaScriptValueKind.Boolean: { - switch (payload1) { - case 0: - return false; - case 1: - return true; - } - } - case JavaScriptValueKind.Number: { - return payload2; - } - case JavaScriptValueKind.String: { - return readString(payload1); - } - case JavaScriptValueKind.Object: { - return this.heap.referenceHeap(payload1); - } - case JavaScriptValueKind.Null: { - return null; - } - case JavaScriptValueKind.Undefined: { - return undefined; - } - case JavaScriptValueKind.Function: { - return this.heap.referenceHeap(payload1); - } - default: - throw new Error(`Type kind "${kind}" is not supported`); - } - }; - - const writeValue = ( - value: any, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer, - is_exception: boolean - ) => { - const exceptionBit = (is_exception ? 1 : 0) << 31; - if (value === null) { - writeUint32(kind_ptr, exceptionBit | JavaScriptValueKind.Null); - return; - } - switch (typeof value) { - case "boolean": { - writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Boolean - ); - writeUint32(payload1_ptr, value ? 1 : 0); - break; - } - case "number": { - writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Number - ); - writeFloat64(payload2_ptr, value); - break; - } - case "string": { - writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.String - ); - writeUint32(payload1_ptr, this.heap.retain(value)); - break; - } - case "undefined": { - writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Undefined - ); - break; - } - case "object": { - writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Object - ); - writeUint32(payload1_ptr, this.heap.retain(value)); - break; - } - case "function": { - writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Function - ); - writeUint32(payload1_ptr, this.heap.retain(value)); - break; - } - default: - throw new Error( - `Type "${typeof value}" is not supported yet` - ); - } - }; - - // Note: - // `decodeValues` assumes that the size of RawJSValue is 16. - const decodeValues = (ptr: pointer, length: number) => { - let result = []; - for (let index = 0; index < length; index++) { - const base = ptr + 16 * index; - const kind = readUInt32(base); - const payload1 = readUInt32(base + 4); - const payload2 = readFloat64(base + 8); - result.push(decodeValue(kind, payload1, payload2)); - } - return result; - }; - return { swjs_set_prop: ( ref: ref, @@ -251,11 +108,11 @@ export class SwiftRuntime { payload1: number, payload2: number ) => { - const obj = this.heap.referenceHeap(ref); + const obj = this.memory.getObject(ref); Reflect.set( obj, - readString(name), - decodeValue(kind, payload1, payload2) + this.memory.getObject(name), + decodeJSValue(kind, payload1, payload2, this.memory) ); }, @@ -266,9 +123,16 @@ export class SwiftRuntime { payload1_ptr: pointer, payload2_ptr: pointer ) => { - const obj = this.heap.referenceHeap(ref); - const result = Reflect.get(obj, readString(name)); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); + const obj = this.memory.getObject(ref); + const result = Reflect.get(obj, this.memory.getObject(name)); + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_set_subscript: ( @@ -278,8 +142,12 @@ export class SwiftRuntime { payload1: number, payload2: number ) => { - const obj = this.heap.referenceHeap(ref); - Reflect.set(obj, index, decodeValue(kind, payload1, payload2)); + const obj = this.memory.getObject(ref); + Reflect.set( + obj, + index, + decodeJSValue(kind, payload1, payload2, this.memory) + ); }, swjs_get_subscript: ( @@ -289,34 +157,40 @@ export class SwiftRuntime { payload1_ptr: pointer, payload2_ptr: pointer ) => { - const obj = this.heap.referenceHeap(ref); + const obj = this.memory.getObject(ref); const result = Reflect.get(obj, index); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => { - const bytes = textEncoder.encode(this.heap.referenceHeap(ref)); - const bytes_ptr = this.heap.retain(bytes); - writeUint32(bytes_ptr_result, bytes_ptr); + const bytes = textEncoder.encode(this.memory.getObject(ref)); + const bytes_ptr = this.memory.retain(bytes); + this.memory.writeUint32(bytes_ptr_result, bytes_ptr); return bytes.length; }, swjs_decode_string: (bytes_ptr: pointer, length: number) => { - const uint8Memory = new Uint8Array(this.memory.buffer); - const bytes = uint8Memory.subarray( + const bytes = this.memory.bytes.subarray( bytes_ptr, bytes_ptr + length ); const string = textDecoder.decode(bytes); - return this.heap.retain(string); + return this.memory.retain(string); }, swjs_load_string: (ref: ref, buffer: pointer) => { - const bytes = this.heap.referenceHeap(ref); - writeString(buffer, bytes); + const bytes = this.memory.getObject(ref); + this.memory.writeBytes(buffer, bytes); }, - swjs_call_function: ( + _swjs_call_function: ( ref: ref, argv: pointer, argc: number, @@ -324,25 +198,39 @@ export class SwiftRuntime { payload1_ptr: pointer, payload2_ptr: pointer ) => { - const func = this.heap.referenceHeap(ref); + const func = this.memory.getObject(ref); let result: any; try { result = Reflect.apply( func, undefined, - decodeValues(argv, argc) + decodeJSValueArray(argv, argc, this.memory) ); } catch (error) { - writeValue( + writeJSValue( error, kind_ptr, payload1_ptr, payload2_ptr, - true + true, + this.memory ); return; } - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, + get swjs_call_function() { + return this._swjs_call_function; + }, + set swjs_call_function(value) { + this._swjs_call_function = value; }, swjs_call_function_with_this: ( @@ -354,30 +242,42 @@ export class SwiftRuntime { payload1_ptr: pointer, payload2_ptr: pointer ) => { - const obj = this.heap.referenceHeap(obj_ref); - const func = this.heap.referenceHeap(func_ref); + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); let result: any; try { - result = Reflect.apply(func, obj, decodeValues(argv, argc)); + result = Reflect.apply( + func, + obj, + decodeJSValueArray(argv, argc, this.memory) + ); } catch (error) { - writeValue( + writeJSValue( error, kind_ptr, payload1_ptr, payload2_ptr, - true + true, + this.memory ); return; } - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_call_new: (ref: ref, argv: pointer, argc: number) => { - const constructor = this.heap.referenceHeap(ref); + const constructor = this.memory.getObject(ref); const instance = Reflect.construct( constructor, - decodeValues(argv, argc) + decodeJSValueArray(argv, argc, this.memory) ); - return this.heap.retain(instance); + return this.memory.retain(instance); }, swjs_call_throwing_new: ( @@ -388,36 +288,38 @@ export class SwiftRuntime { exception_payload1_ptr: pointer, exception_payload2_ptr: pointer ) => { - const constructor = this.heap.referenceHeap(ref); + const constructor = this.memory.getObject(ref); let result: any; try { result = Reflect.construct( constructor, - decodeValues(argv, argc) + decodeJSValueArray(argv, argc, this.memory) ); } catch (error) { - writeValue( + writeJSValue( error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, - true + true, + this.memory ); return -1; } - writeValue( + writeJSValue( null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, - false + false, + this.memory ); - return this.heap.retain(result); + return this.memory.retain(result); }, swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => { - const obj = this.heap.referenceHeap(obj_ref); - const constructor = this.heap.referenceHeap(constructor_ref); + const obj = this.memory.getObject(obj_ref); + const constructor = this.memory.getObject(constructor_ref); return obj instanceof constructor; }, @@ -428,7 +330,7 @@ export class SwiftRuntime { Array.prototype.slice.call(arguments) ); }; - const func_ref = this.heap.retain(func); + const func_ref = this.memory.retain(func); this.closureHeap?.alloc(func, func_ref); return func_ref; }, @@ -439,18 +341,18 @@ export class SwiftRuntime { length: number ) => { const ArrayType: TypedArray = - this.heap.referenceHeap(constructor_ref); + this.memory.getObject(constructor_ref); const array = new ArrayType( - this.memory.buffer, + this.memory.rawMemory.buffer, elementsPtr, length ); // Call `.slice()` to copy the memory - return this.heap.retain(array.slice()); + return this.memory.retain(array.slice()); }, swjs_release: (ref: ref) => { - this.heap.release(ref); + this.memory.release(ref); }, }; } diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts new file mode 100644 index 000000000..2045dd8aa --- /dev/null +++ b/Runtime/src/js-value.ts @@ -0,0 +1,130 @@ +import { Memory } from "./memory"; +import { pointer } from "./types"; + +export enum JavaScriptValueKind { + Invalid = -1, + Boolean = 0, + String = 1, + Number = 2, + Object = 3, + Null = 4, + Undefined = 5, + Function = 6, +} + +export const decodeJSValue = ( + kind: JavaScriptValueKind, + payload1: number, + payload2: number, + memory: Memory +) => { + switch (kind) { + case JavaScriptValueKind.Boolean: + switch (payload1) { + case 0: + return false; + case 1: + return true; + } + case JavaScriptValueKind.Number: + return payload2; + + case JavaScriptValueKind.String: + case JavaScriptValueKind.Object: + case JavaScriptValueKind.Function: + return memory.getObject(payload1); + + case JavaScriptValueKind.Null: + return null; + + case JavaScriptValueKind.Undefined: + return undefined; + + default: + throw new Error(`JSValue Type kind "${kind}" is not supported`); + } +}; + +// Note: +// `decodeValues` assumes that the size of RawJSValue is 16. +export const decodeJSValueArray = ( + ptr: pointer, + length: number, + memory: Memory +) => { + let result = []; + for (let index = 0; index < length; index++) { + const base = ptr + 16 * index; + const kind = memory.readUint32(base); + const payload1 = memory.readUint32(base + 4); + const payload2 = memory.readFloat64(base + 8); + result.push(decodeJSValue(kind, payload1, payload2, memory)); + } + return result; +}; + +export const writeJSValue = ( + value: any, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer, + is_exception: boolean, + memory: Memory +) => { + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + memory.writeUint32(kind_ptr, exceptionBit | JavaScriptValueKind.Null); + return; + } + switch (typeof value) { + case "boolean": { + memory.writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Boolean + ); + memory.writeUint32(payload1_ptr, value ? 1 : 0); + break; + } + case "number": { + memory.writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Number + ); + memory.writeFloat64(payload2_ptr, value); + break; + } + case "string": { + memory.writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.String + ); + memory.writeUint32(payload1_ptr, memory.retain(value)); + break; + } + case "undefined": { + memory.writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Undefined + ); + break; + } + case "object": { + memory.writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Object + ); + memory.writeUint32(payload1_ptr, memory.retain(value)); + break; + } + case "function": { + memory.writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Function + ); + memory.writeUint32(payload1_ptr, memory.retain(value)); + break; + } + default: + throw new Error(`Type "${typeof value}" is not supported yet`); + } +}; diff --git a/Runtime/src/memory.ts b/Runtime/src/memory.ts new file mode 100644 index 000000000..dc127aed2 --- /dev/null +++ b/Runtime/src/memory.ts @@ -0,0 +1,32 @@ +import { SwiftRuntimeHeap } from "./object-heap"; +import { pointer } from "./types"; + +export class Memory { + readonly rawMemory: WebAssembly.Memory; + readonly bytes: Uint8Array; + readonly dataView: DataView; + + private readonly heap = new SwiftRuntimeHeap(); + + constructor(exports: WebAssembly.Exports) { + this.rawMemory = exports.memory as WebAssembly.Memory; + this.bytes = new Uint8Array(this.rawMemory.buffer); + this.dataView = new DataView(this.rawMemory.buffer); + } + + retain = (value: any) => this.heap.retain(value); + getObject = (ref: number) => this.heap.referenceHeap(ref); + release = (ref: number) => this.heap.release(ref); + + writeBytes(ptr: pointer, bytes: Uint8Array) { + this.bytes.set(bytes, ptr); + } + + readUint32 = (ptr: pointer) => this.dataView.getUint32(ptr, true); + readFloat64 = (ptr: pointer) => this.dataView.getFloat64(ptr, true); + + writeUint32 = (ptr: pointer, value: number) => + this.dataView.setUint32(ptr, value, true); + writeFloat64 = (ptr: pointer, value: number) => + this.dataView.setFloat64(ptr, value); +} diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 15f4cb73a..952bbc7af 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -1,7 +1,3 @@ -export interface ExportedMemory { - buffer: any; -} - export type ref = number; export type pointer = number; @@ -20,17 +16,6 @@ export interface SwiftRuntimeExportedFunctions { swjs_free_host_function(host_func_id: number): void; } -export enum JavaScriptValueKind { - Invalid = -1, - Boolean = 0, - String = 1, - Number = 2, - Object = 3, - Null = 4, - Undefined = 5, - Function = 6, -} - export enum LibraryFeatures { WeakRefs = 1 << 0, } From 3104c4a63b11b47de0bd201b7c3661adabb0ab0e Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 10:38:54 -0500 Subject: [PATCH 08/21] rename to SwiftClosureDeallocator --- Runtime/src/closure-heap.ts | 4 ++-- Runtime/src/index.ts | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts index d8b7de566..e39ef7f2f 100644 --- a/Runtime/src/closure-heap.ts +++ b/Runtime/src/closure-heap.ts @@ -1,7 +1,7 @@ import { SwiftRuntimeExportedFunctions } from "./types"; /// Memory lifetime of closures in Swift are managed by Swift side -export class SwiftClosureHeap { +export class SwiftClosureDeallocator { private functionRegistry: FinalizationRegistry; constructor(exports: SwiftRuntimeExportedFunctions) { @@ -16,7 +16,7 @@ export class SwiftClosureHeap { }); } - alloc(func: any, func_ref: number) { + track(func: Function, func_ref: number) { this.functionRegistry.register(func, func_ref); } } diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index ba173313b..3c8a27e75 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -1,4 +1,4 @@ -import { SwiftClosureHeap } from "./closure-heap"; +import { SwiftClosureDeallocator } from "./closure-heap"; import { LibraryFeatures, SwiftRuntimeExportedFunctions, @@ -17,13 +17,13 @@ import { Memory } from "./memory"; export class SwiftRuntime { private _instance: WebAssembly.Instance | null; private _memory: Memory | null; - private _closureHeap: SwiftClosureHeap | null; + private _closureDeallocator: SwiftClosureDeallocator | null; private version: number = 703; constructor() { this._instance = null; this._memory = null; - this._closureHeap = null; + this._closureDeallocator = null; } setInstance(instance: WebAssembly.Instance) { @@ -54,16 +54,18 @@ export class SwiftRuntime { return this._memory; } - get closureHeap(): SwiftClosureHeap | null { - if (this._closureHeap) return this._closureHeap; + get closureDeallocator(): SwiftClosureDeallocator | null { + if (this._closureDeallocator) return this._closureDeallocator; const features = this.exports.swjs_library_features(); const librarySupportsWeakRef = (features & LibraryFeatures.WeakRefs) != 0; if (librarySupportsWeakRef) { - this._closureHeap = new SwiftClosureHeap(this.exports); + this._closureDeallocator = new SwiftClosureDeallocator( + this.exports + ); } - return this._closureHeap; + return this._closureDeallocator; } importObjects() { @@ -331,7 +333,7 @@ export class SwiftRuntime { ); }; const func_ref = this.memory.retain(func); - this.closureHeap?.alloc(func, func_ref); + this.closureDeallocator?.track(func, func_ref); return func_ref; }, From aba43c1b18b9d238d9f21278a2b5e58a9deab1f2 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 10:47:25 -0500 Subject: [PATCH 09/21] make callHostFunction a method --- Runtime/src/index.ts | 66 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 3c8a27e75..66b10e42f 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -68,37 +68,37 @@ export class SwiftRuntime { return this._closureDeallocator; } - importObjects() { - const callHostFunction = (host_func_id: number, args: any[]) => { - const argc = args.length; - const argv = this.exports.swjs_prepare_host_function_call(argc); - for (let index = 0; index < args.length; index++) { - const argument = args[index]; - const base = argv + 16 * index; - writeJSValue( - argument, - base, - base + 4, - base + 8, - false, - this.memory - ); - } - let output: any; - // This ref is released by the swjs_call_host_function implementation - const callback_func_ref = this.memory.retain((result: any) => { - output = result; - }); - this.exports.swjs_call_host_function( - host_func_id, - argv, - argc, - callback_func_ref + callHostFunction(host_func_id: number, args: any[]) { + const argc = args.length; + const argv = this.exports.swjs_prepare_host_function_call(argc); + for (let index = 0; index < args.length; index++) { + const argument = args[index]; + const base = argv + 16 * index; + writeJSValue( + argument, + base, + base + 4, + base + 8, + false, + this.memory ); - this.exports.swjs_cleanup_host_function_call(argv); - return output; - }; + } + let output: any; + // This ref is released by the swjs_call_host_function implementation + const callback_func_ref = this.memory.retain((result: any) => { + output = result; + }); + this.exports.swjs_call_host_function( + host_func_id, + argv, + argc, + callback_func_ref + ); + this.exports.swjs_cleanup_host_function_call(argv); + return output; + } + importObjects() { const textDecoder = new TextDecoder("utf-8"); const textEncoder = new TextEncoder(); // Only support utf-8 @@ -326,12 +326,8 @@ export class SwiftRuntime { }, swjs_create_function: (host_func_id: number) => { - const func = function () { - return callHostFunction( - host_func_id, - Array.prototype.slice.call(arguments) - ); - }; + const func = (...args: any[]) => + this.callHostFunction(host_func_id, args); const func_ref = this.memory.retain(func); this.closureDeallocator?.track(func, func_ref); return func_ref; From 486d9417651a69061df82d03bba26e8f3ad3bb96 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 10:55:05 -0500 Subject: [PATCH 10/21] store textEncoder, textDecoder on the instance --- Runtime/src/index.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 66b10e42f..85d535281 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -20,6 +20,9 @@ export class SwiftRuntime { private _closureDeallocator: SwiftClosureDeallocator | null; private version: number = 703; + private textDecoder = new TextDecoder("utf-8"); + private textEncoder = new TextEncoder(); // Only support utf-8 + constructor() { this._instance = null; this._memory = null; @@ -99,9 +102,6 @@ export class SwiftRuntime { } importObjects() { - const textDecoder = new TextDecoder("utf-8"); - const textEncoder = new TextEncoder(); // Only support utf-8 - return { swjs_set_prop: ( ref: ref, @@ -172,7 +172,9 @@ export class SwiftRuntime { }, swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => { - const bytes = textEncoder.encode(this.memory.getObject(ref)); + const bytes = this.textEncoder.encode( + this.memory.getObject(ref) + ); const bytes_ptr = this.memory.retain(bytes); this.memory.writeUint32(bytes_ptr_result, bytes_ptr); return bytes.length; @@ -183,7 +185,7 @@ export class SwiftRuntime { bytes_ptr, bytes_ptr + length ); - const string = textDecoder.decode(bytes); + const string = this.textDecoder.decode(bytes); return this.memory.retain(string); }, From 341f847ac81171156622681b6ea4bd1b1b526098 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 10:55:38 -0500 Subject: [PATCH 11/21] mark APIs as private --- Runtime/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 85d535281..fe60d39da 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -40,13 +40,13 @@ export class SwiftRuntime { } } - get instance() { + private get instance() { if (!this._instance) throw new Error("WebAssembly instance is not set yet"); return this._instance; } - get exports() { + private get exports() { return this.instance.exports as any as SwiftRuntimeExportedFunctions; } @@ -57,7 +57,7 @@ export class SwiftRuntime { return this._memory; } - get closureDeallocator(): SwiftClosureDeallocator | null { + private get closureDeallocator(): SwiftClosureDeallocator | null { if (this._closureDeallocator) return this._closureDeallocator; const features = this.exports.swjs_library_features(); @@ -71,7 +71,7 @@ export class SwiftRuntime { return this._closureDeallocator; } - callHostFunction(host_func_id: number, args: any[]) { + private callHostFunction(host_func_id: number, args: any[]) { const argc = args.length; const argv = this.exports.swjs_prepare_host_function_call(argc); for (let index = 0; index < args.length; index++) { From 5c38015a663d67c2d18204790dde3368999ba5bb Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 10:55:49 -0500 Subject: [PATCH 12/21] convert from method to property --- Runtime/src/index.ts | 443 +++++++++++++++++++++---------------------- 1 file changed, 221 insertions(+), 222 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index fe60d39da..e53566fb9 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -101,259 +101,258 @@ export class SwiftRuntime { return output; } - importObjects() { - return { - swjs_set_prop: ( - ref: ref, - name: ref, - kind: JavaScriptValueKind, - payload1: number, - payload2: number - ) => { - const obj = this.memory.getObject(ref); - Reflect.set( - obj, - this.memory.getObject(name), - decodeJSValue(kind, payload1, payload2, this.memory) - ); - }, + /** @deprecated Use `wasmImports` instead */ + importObjects = () => this.wasmImports; - swjs_get_prop: ( - ref: ref, - name: ref, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer - ) => { - const obj = this.memory.getObject(ref); - const result = Reflect.get(obj, this.memory.getObject(name)); - writeJSValue( - result, - kind_ptr, - payload1_ptr, - payload2_ptr, - false, - this.memory - ); - }, + readonly wasmImports = { + swjs_set_prop: ( + ref: ref, + name: ref, + kind: JavaScriptValueKind, + payload1: number, + payload2: number + ) => { + const obj = this.memory.getObject(ref); + Reflect.set( + obj, + this.memory.getObject(name), + decodeJSValue(kind, payload1, payload2, this.memory) + ); + }, - swjs_set_subscript: ( - ref: ref, - index: number, - kind: JavaScriptValueKind, - payload1: number, - payload2: number - ) => { - const obj = this.memory.getObject(ref); - Reflect.set( - obj, - index, - decodeJSValue(kind, payload1, payload2, this.memory) - ); - }, + swjs_get_prop: ( + ref: ref, + name: ref, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ) => { + const obj = this.memory.getObject(ref); + const result = Reflect.get(obj, this.memory.getObject(name)); + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, - swjs_get_subscript: ( - ref: ref, - index: number, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer - ) => { - const obj = this.memory.getObject(ref); - const result = Reflect.get(obj, index); - writeJSValue( - result, - kind_ptr, - payload1_ptr, - payload2_ptr, - false, - this.memory - ); - }, + swjs_set_subscript: ( + ref: ref, + index: number, + kind: JavaScriptValueKind, + payload1: number, + payload2: number + ) => { + const obj = this.memory.getObject(ref); + Reflect.set( + obj, + index, + decodeJSValue(kind, payload1, payload2, this.memory) + ); + }, - swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => { - const bytes = this.textEncoder.encode( - this.memory.getObject(ref) - ); - const bytes_ptr = this.memory.retain(bytes); - this.memory.writeUint32(bytes_ptr_result, bytes_ptr); - return bytes.length; - }, + swjs_get_subscript: ( + ref: ref, + index: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ) => { + const obj = this.memory.getObject(ref); + const result = Reflect.get(obj, index); + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, - swjs_decode_string: (bytes_ptr: pointer, length: number) => { - const bytes = this.memory.bytes.subarray( - bytes_ptr, - bytes_ptr + length - ); - const string = this.textDecoder.decode(bytes); - return this.memory.retain(string); - }, + swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => { + const bytes = this.textEncoder.encode(this.memory.getObject(ref)); + const bytes_ptr = this.memory.retain(bytes); + this.memory.writeUint32(bytes_ptr_result, bytes_ptr); + return bytes.length; + }, + + swjs_decode_string: (bytes_ptr: pointer, length: number) => { + const bytes = this.memory.bytes.subarray( + bytes_ptr, + bytes_ptr + length + ); + const string = this.textDecoder.decode(bytes); + return this.memory.retain(string); + }, - swjs_load_string: (ref: ref, buffer: pointer) => { - const bytes = this.memory.getObject(ref); - this.memory.writeBytes(buffer, bytes); - }, + swjs_load_string: (ref: ref, buffer: pointer) => { + const bytes = this.memory.getObject(ref); + this.memory.writeBytes(buffer, bytes); + }, - _swjs_call_function: ( - ref: ref, - argv: pointer, - argc: number, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer - ) => { - const func = this.memory.getObject(ref); - let result: any; - try { - result = Reflect.apply( - func, - undefined, - decodeJSValueArray(argv, argc, this.memory) - ); - } catch (error) { - writeJSValue( - error, - kind_ptr, - payload1_ptr, - payload2_ptr, - true, - this.memory - ); - return; - } + _swjs_call_function: ( + ref: ref, + argv: pointer, + argc: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ) => { + const func = this.memory.getObject(ref); + let result: any; + try { + result = Reflect.apply( + func, + undefined, + decodeJSValueArray(argv, argc, this.memory) + ); + } catch (error) { writeJSValue( - result, + error, kind_ptr, payload1_ptr, payload2_ptr, - false, + true, this.memory ); - }, - get swjs_call_function() { - return this._swjs_call_function; - }, - set swjs_call_function(value) { - this._swjs_call_function = value; - }, + return; + } + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, + get swjs_call_function() { + return this._swjs_call_function; + }, + set swjs_call_function(value) { + this._swjs_call_function = value; + }, - swjs_call_function_with_this: ( - obj_ref: ref, - func_ref: ref, - argv: pointer, - argc: number, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer - ) => { - const obj = this.memory.getObject(obj_ref); - const func = this.memory.getObject(func_ref); - let result: any; - try { - result = Reflect.apply( - func, - obj, - decodeJSValueArray(argv, argc, this.memory) - ); - } catch (error) { - writeJSValue( - error, - kind_ptr, - payload1_ptr, - payload2_ptr, - true, - this.memory - ); - return; - } + swjs_call_function_with_this: ( + obj_ref: ref, + func_ref: ref, + argv: pointer, + argc: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let result: any; + try { + result = Reflect.apply( + func, + obj, + decodeJSValueArray(argv, argc, this.memory) + ); + } catch (error) { writeJSValue( - result, + error, kind_ptr, payload1_ptr, payload2_ptr, - false, + true, this.memory ); - }, - swjs_call_new: (ref: ref, argv: pointer, argc: number) => { - const constructor = this.memory.getObject(ref); - const instance = Reflect.construct( + return; + } + writeJSValue( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, + swjs_call_new: (ref: ref, argv: pointer, argc: number) => { + const constructor = this.memory.getObject(ref); + const instance = Reflect.construct( + constructor, + decodeJSValueArray(argv, argc, this.memory) + ); + return this.memory.retain(instance); + }, + + swjs_call_throwing_new: ( + ref: ref, + argv: pointer, + argc: number, + exception_kind_ptr: pointer, + exception_payload1_ptr: pointer, + exception_payload2_ptr: pointer + ) => { + const constructor = this.memory.getObject(ref); + let result: any; + try { + result = Reflect.construct( constructor, decodeJSValueArray(argv, argc, this.memory) ); - return this.memory.retain(instance); - }, - - swjs_call_throwing_new: ( - ref: ref, - argv: pointer, - argc: number, - exception_kind_ptr: pointer, - exception_payload1_ptr: pointer, - exception_payload2_ptr: pointer - ) => { - const constructor = this.memory.getObject(ref); - let result: any; - try { - result = Reflect.construct( - constructor, - decodeJSValueArray(argv, argc, this.memory) - ); - } catch (error) { - writeJSValue( - error, - exception_kind_ptr, - exception_payload1_ptr, - exception_payload2_ptr, - true, - this.memory - ); - return -1; - } + } catch (error) { writeJSValue( - null, + error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, - false, + true, this.memory ); - return this.memory.retain(result); - }, + return -1; + } + writeJSValue( + null, + exception_kind_ptr, + exception_payload1_ptr, + exception_payload2_ptr, + false, + this.memory + ); + return this.memory.retain(result); + }, - swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => { - const obj = this.memory.getObject(obj_ref); - const constructor = this.memory.getObject(constructor_ref); - return obj instanceof constructor; - }, + swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => { + const obj = this.memory.getObject(obj_ref); + const constructor = this.memory.getObject(constructor_ref); + return obj instanceof constructor; + }, - swjs_create_function: (host_func_id: number) => { - const func = (...args: any[]) => - this.callHostFunction(host_func_id, args); - const func_ref = this.memory.retain(func); - this.closureDeallocator?.track(func, func_ref); - return func_ref; - }, + swjs_create_function: (host_func_id: number) => { + const func = (...args: any[]) => + this.callHostFunction(host_func_id, args); + const func_ref = this.memory.retain(func); + this.closureDeallocator?.track(func, func_ref); + return func_ref; + }, - swjs_create_typed_array: ( - constructor_ref: ref, - elementsPtr: pointer, - length: number - ) => { - const ArrayType: TypedArray = - this.memory.getObject(constructor_ref); - const array = new ArrayType( - this.memory.rawMemory.buffer, - elementsPtr, - length - ); - // Call `.slice()` to copy the memory - return this.memory.retain(array.slice()); - }, + swjs_create_typed_array: ( + constructor_ref: ref, + elementsPtr: pointer, + length: number + ) => { + const ArrayType: TypedArray = + this.memory.getObject(constructor_ref); + const array = new ArrayType( + this.memory.rawMemory.buffer, + elementsPtr, + length + ); + // Call `.slice()` to copy the memory + return this.memory.retain(array.slice()); + }, - swjs_release: (ref: ref) => { - this.memory.release(ref); - }, - }; - } + swjs_release: (ref: ref) => { + this.memory.release(ref); + }, + }; } From ab9d4c323274bddcf5213766f6ad88be0b46170c Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 11:01:35 -0500 Subject: [PATCH 13/21] Update TS to fix rollup issue --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c45476520..b793e24a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -188,9 +188,9 @@ "peer": true }, "node_modules/typescript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", - "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -318,9 +318,9 @@ "peer": true }, "typescript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", - "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true } } From dc0014156e87db9d9b292ca46ff94b9ea3940954 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 11:02:04 -0500 Subject: [PATCH 14/21] Access JSValue APIs as a namespace --- Runtime/src/index.ts | 41 ++++++++++++--------------- Runtime/src/js-value.ts | 62 +++++++++++++---------------------------- 2 files changed, 38 insertions(+), 65 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index e53566fb9..d6fd257a2 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -6,12 +6,7 @@ import { pointer, TypedArray, } from "./types"; -import { - decodeJSValue, - decodeJSValueArray, - JavaScriptValueKind, - writeJSValue, -} from "./js-value"; +import * as JSValue from "./js-value"; import { Memory } from "./memory"; export class SwiftRuntime { @@ -77,7 +72,7 @@ export class SwiftRuntime { for (let index = 0; index < args.length; index++) { const argument = args[index]; const base = argv + 16 * index; - writeJSValue( + JSValue.write( argument, base, base + 4, @@ -108,7 +103,7 @@ export class SwiftRuntime { swjs_set_prop: ( ref: ref, name: ref, - kind: JavaScriptValueKind, + kind: JSValue.Kind, payload1: number, payload2: number ) => { @@ -116,7 +111,7 @@ export class SwiftRuntime { Reflect.set( obj, this.memory.getObject(name), - decodeJSValue(kind, payload1, payload2, this.memory) + JSValue.decode(kind, payload1, payload2, this.memory) ); }, @@ -129,7 +124,7 @@ export class SwiftRuntime { ) => { const obj = this.memory.getObject(ref); const result = Reflect.get(obj, this.memory.getObject(name)); - writeJSValue( + JSValue.write( result, kind_ptr, payload1_ptr, @@ -142,7 +137,7 @@ export class SwiftRuntime { swjs_set_subscript: ( ref: ref, index: number, - kind: JavaScriptValueKind, + kind: JSValue.Kind, payload1: number, payload2: number ) => { @@ -150,7 +145,7 @@ export class SwiftRuntime { Reflect.set( obj, index, - decodeJSValue(kind, payload1, payload2, this.memory) + JSValue.decode(kind, payload1, payload2, this.memory) ); }, @@ -163,7 +158,7 @@ export class SwiftRuntime { ) => { const obj = this.memory.getObject(ref); const result = Reflect.get(obj, index); - writeJSValue( + JSValue.write( result, kind_ptr, payload1_ptr, @@ -208,10 +203,10 @@ export class SwiftRuntime { result = Reflect.apply( func, undefined, - decodeJSValueArray(argv, argc, this.memory) + JSValue.decodeArray(argv, argc, this.memory) ); } catch (error) { - writeJSValue( + JSValue.write( error, kind_ptr, payload1_ptr, @@ -221,7 +216,7 @@ export class SwiftRuntime { ); return; } - writeJSValue( + JSValue.write( result, kind_ptr, payload1_ptr, @@ -253,10 +248,10 @@ export class SwiftRuntime { result = Reflect.apply( func, obj, - decodeJSValueArray(argv, argc, this.memory) + JSValue.decodeArray(argv, argc, this.memory) ); } catch (error) { - writeJSValue( + JSValue.write( error, kind_ptr, payload1_ptr, @@ -266,7 +261,7 @@ export class SwiftRuntime { ); return; } - writeJSValue( + JSValue.write( result, kind_ptr, payload1_ptr, @@ -279,7 +274,7 @@ export class SwiftRuntime { const constructor = this.memory.getObject(ref); const instance = Reflect.construct( constructor, - decodeJSValueArray(argv, argc, this.memory) + JSValue.decodeArray(argv, argc, this.memory) ); return this.memory.retain(instance); }, @@ -297,10 +292,10 @@ export class SwiftRuntime { try { result = Reflect.construct( constructor, - decodeJSValueArray(argv, argc, this.memory) + JSValue.decodeArray(argv, argc, this.memory) ); } catch (error) { - writeJSValue( + JSValue.write( error, exception_kind_ptr, exception_payload1_ptr, @@ -310,7 +305,7 @@ export class SwiftRuntime { ); return -1; } - writeJSValue( + JSValue.write( null, exception_kind_ptr, exception_payload1_ptr, diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 2045dd8aa..61f7b486a 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -1,7 +1,7 @@ import { Memory } from "./memory"; import { pointer } from "./types"; -export enum JavaScriptValueKind { +export enum Kind { Invalid = -1, Boolean = 0, String = 1, @@ -12,32 +12,32 @@ export enum JavaScriptValueKind { Function = 6, } -export const decodeJSValue = ( - kind: JavaScriptValueKind, +export const decode = ( + kind: Kind, payload1: number, payload2: number, memory: Memory ) => { switch (kind) { - case JavaScriptValueKind.Boolean: + case Kind.Boolean: switch (payload1) { case 0: return false; case 1: return true; } - case JavaScriptValueKind.Number: + case Kind.Number: return payload2; - case JavaScriptValueKind.String: - case JavaScriptValueKind.Object: - case JavaScriptValueKind.Function: + case Kind.String: + case Kind.Object: + case Kind.Function: return memory.getObject(payload1); - case JavaScriptValueKind.Null: + case Kind.Null: return null; - case JavaScriptValueKind.Undefined: + case Kind.Undefined: return undefined; default: @@ -47,23 +47,19 @@ export const decodeJSValue = ( // Note: // `decodeValues` assumes that the size of RawJSValue is 16. -export const decodeJSValueArray = ( - ptr: pointer, - length: number, - memory: Memory -) => { +export const decodeArray = (ptr: pointer, length: number, memory: Memory) => { let result = []; for (let index = 0; index < length; index++) { const base = ptr + 16 * index; const kind = memory.readUint32(base); const payload1 = memory.readUint32(base + 4); const payload2 = memory.readFloat64(base + 8); - result.push(decodeJSValue(kind, payload1, payload2, memory)); + result.push(decode(kind, payload1, payload2, memory)); } return result; }; -export const writeJSValue = ( +export const write = ( value: any, kind_ptr: pointer, payload1_ptr: pointer, @@ -73,54 +69,36 @@ export const writeJSValue = ( ) => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { - memory.writeUint32(kind_ptr, exceptionBit | JavaScriptValueKind.Null); + memory.writeUint32(kind_ptr, exceptionBit | Kind.Null); return; } switch (typeof value) { case "boolean": { - memory.writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Boolean - ); + memory.writeUint32(kind_ptr, exceptionBit | Kind.Boolean); memory.writeUint32(payload1_ptr, value ? 1 : 0); break; } case "number": { - memory.writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Number - ); + memory.writeUint32(kind_ptr, exceptionBit | Kind.Number); memory.writeFloat64(payload2_ptr, value); break; } case "string": { - memory.writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.String - ); + memory.writeUint32(kind_ptr, exceptionBit | Kind.String); memory.writeUint32(payload1_ptr, memory.retain(value)); break; } case "undefined": { - memory.writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Undefined - ); + memory.writeUint32(kind_ptr, exceptionBit | Kind.Undefined); break; } case "object": { - memory.writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Object - ); + memory.writeUint32(kind_ptr, exceptionBit | Kind.Object); memory.writeUint32(payload1_ptr, memory.retain(value)); break; } case "function": { - memory.writeUint32( - kind_ptr, - exceptionBit | JavaScriptValueKind.Function - ); + memory.writeUint32(kind_ptr, exceptionBit | Kind.Function); memory.writeUint32(payload1_ptr, memory.retain(value)); break; } From a42b0fb3ec549867a843125dcc490bda2a9b1378 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 11:03:12 -0500 Subject: [PATCH 15/21] Update memory.ts --- Runtime/src/memory.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Runtime/src/memory.ts b/Runtime/src/memory.ts index dc127aed2..6ca6792df 100644 --- a/Runtime/src/memory.ts +++ b/Runtime/src/memory.ts @@ -18,9 +18,8 @@ export class Memory { getObject = (ref: number) => this.heap.referenceHeap(ref); release = (ref: number) => this.heap.release(ref); - writeBytes(ptr: pointer, bytes: Uint8Array) { + writeBytes = (ptr: pointer, bytes: Uint8Array) => this.bytes.set(bytes, ptr); - } readUint32 = (ptr: pointer) => this.dataView.getUint32(ptr, true); readFloat64 = (ptr: pointer) => this.dataView.getFloat64(ptr, true); From fcfdc63f038815143eb29ccc1d407bcb625d781d Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 11:04:13 -0500 Subject: [PATCH 16/21] explicitly type wasmImports --- Runtime/src/closure-heap.ts | 4 +-- Runtime/src/index.ts | 7 ++-- Runtime/src/types.ts | 72 ++++++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts index e39ef7f2f..b2564245d 100644 --- a/Runtime/src/closure-heap.ts +++ b/Runtime/src/closure-heap.ts @@ -1,10 +1,10 @@ -import { SwiftRuntimeExportedFunctions } from "./types"; +import { ExportedFunctions } from "./types"; /// Memory lifetime of closures in Swift are managed by Swift side export class SwiftClosureDeallocator { private functionRegistry: FinalizationRegistry; - constructor(exports: SwiftRuntimeExportedFunctions) { + constructor(exports: ExportedFunctions) { if (typeof FinalizationRegistry === "undefined") { throw new Error( "The Swift part of JavaScriptKit was configured to require the availability of JavaScript WeakRefs. Please build with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to disable features that use WeakRefs." diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index d6fd257a2..7d0fec224 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -1,10 +1,11 @@ import { SwiftClosureDeallocator } from "./closure-heap"; import { LibraryFeatures, - SwiftRuntimeExportedFunctions, + ExportedFunctions, ref, pointer, TypedArray, + ImportedFunctions, } from "./types"; import * as JSValue from "./js-value"; import { Memory } from "./memory"; @@ -42,7 +43,7 @@ export class SwiftRuntime { } private get exports() { - return this.instance.exports as any as SwiftRuntimeExportedFunctions; + return this.instance.exports as any as ExportedFunctions; } private get memory() { @@ -99,7 +100,7 @@ export class SwiftRuntime { /** @deprecated Use `wasmImports` instead */ importObjects = () => this.wasmImports; - readonly wasmImports = { + readonly wasmImports: ImportedFunctions = { swjs_set_prop: ( ref: ref, name: ref, diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 952bbc7af..56982a66b 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -1,7 +1,9 @@ +import * as JSValue from "./js-value"; + export type ref = number; export type pointer = number; -export interface SwiftRuntimeExportedFunctions { +export interface ExportedFunctions { swjs_library_version(): number; swjs_library_features(): number; swjs_prepare_host_function_call(size: number): pointer; @@ -16,6 +18,74 @@ export interface SwiftRuntimeExportedFunctions { swjs_free_host_function(host_func_id: number): void; } +export interface ImportedFunctions { + swjs_set_prop( + ref: number, + name: number, + kind: JSValue.Kind, + payload1: number, + payload2: number + ): void; + swjs_get_prop( + ref: number, + name: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ): void; + swjs_set_subscript( + ref: number, + index: number, + kind: JSValue.Kind, + payload1: number, + payload2: number + ): void; + swjs_get_subscript( + ref: number, + index: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ): void; + swjs_encode_string(ref: number, bytes_ptr_result: pointer): number; + swjs_decode_string(bytes_ptr: pointer, length: number): number; + swjs_load_string(ref: number, buffer: pointer): void; + swjs_call_function( + ref: number, + argv: pointer, + argc: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ): void; + swjs_call_function_with_this( + obj_ref: ref, + func_ref: ref, + argv: pointer, + argc: number, + kind_ptr: pointer, + payload1_ptr: pointer, + payload2_ptr: pointer + ): void; + swjs_call_new(ref: number, argv: pointer, argc: number): number; + swjs_call_throwing_new( + ref: number, + argv: pointer, + argc: number, + exception_kind_ptr: pointer, + exception_payload1_ptr: pointer, + exception_payload2_ptr: pointer + ): number; + swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean; + swjs_create_function(host_func_id: number): number; + swjs_create_typed_array( + constructor_ref: ref, + elementsPtr: pointer, + length: number + ): number; + swjs_release(ref: number): void; +} + export enum LibraryFeatures { WeakRefs = 1 << 0, } From 418640eae2d98b2dd03a528b3cc1038b34da6de8 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Jan 2022 11:05:37 -0500 Subject: [PATCH 17/21] remove auto-generated getter/setter pair --- Runtime/src/index.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 7d0fec224..10ec7b447 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -190,7 +190,7 @@ export class SwiftRuntime { this.memory.writeBytes(buffer, bytes); }, - _swjs_call_function: ( + swjs_call_function: ( ref: ref, argv: pointer, argc: number, @@ -226,12 +226,6 @@ export class SwiftRuntime { this.memory ); }, - get swjs_call_function() { - return this._swjs_call_function; - }, - set swjs_call_function(value) { - this._swjs_call_function = value; - }, swjs_call_function_with_this: ( obj_ref: ref, From fe516bcd281195ca2cecbe21dc56c8244efe2b98 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Mon, 24 Jan 2022 07:37:27 -0500 Subject: [PATCH 18/21] fix endianness --- Runtime/src/memory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/src/memory.ts b/Runtime/src/memory.ts index 6ca6792df..a8391cfd1 100644 --- a/Runtime/src/memory.ts +++ b/Runtime/src/memory.ts @@ -27,5 +27,5 @@ export class Memory { writeUint32 = (ptr: pointer, value: number) => this.dataView.setUint32(ptr, value, true); writeFloat64 = (ptr: pointer, value: number) => - this.dataView.setFloat64(ptr, value); + this.dataView.setFloat64(ptr, value, true); } From 23490c689ea18af0f33c61185ac7c8e278077aee Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Mon, 24 Jan 2022 07:52:55 -0500 Subject: [PATCH 19/21] empty commit to trigger ci From 9ffef919cdff04e75bacbf788006ee3ec2f42746 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Mon, 24 Jan 2022 08:22:56 -0500 Subject: [PATCH 20/21] Break WeakRefs error onto multiple lines --- Runtime/src/closure-heap.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts index b2564245d..8d2c5600c 100644 --- a/Runtime/src/closure-heap.ts +++ b/Runtime/src/closure-heap.ts @@ -7,7 +7,10 @@ export class SwiftClosureDeallocator { constructor(exports: ExportedFunctions) { if (typeof FinalizationRegistry === "undefined") { throw new Error( - "The Swift part of JavaScriptKit was configured to require the availability of JavaScript WeakRefs. Please build with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to disable features that use WeakRefs." + "The Swift part of JavaScriptKit was configured to require " + + "the availability of JavaScript WeakRefs. Please build " + + "with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to " + + "disable features that use WeakRefs." ); } From 3c51cba41f11d0d6099209915faadabc5992c7c9 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Mon, 24 Jan 2022 08:34:14 -0500 Subject: [PATCH 21/21] Add a comment about BigInt typed arrays not being supported --- Runtime/src/types.ts | 1 + Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 3aebfa857..4f613cc6a 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -98,6 +98,7 @@ export type TypedArray = | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor + // BigInt is not yet supported, see https://github.com/swiftwasm/JavaScriptKit/issues/56 // | BigInt64ArrayConstructor // | BigUint64ArrayConstructor | Float32ArrayConstructor diff --git a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift index 3518c4b99..e073d7c29 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift @@ -133,6 +133,7 @@ extension UInt32: TypedArrayElement { } // FIXME: Support passing BigInts across the bridge +// See https://github.com/swiftwasm/JavaScriptKit/issues/56 //extension Int64: TypedArrayElement { // public static var typedArrayClass = JSObject.global.BigInt64Array.function! //}