diff --git a/Runtime/rollup.config.js b/Runtime/rollup.config.js new file mode 100644 index 000000000..cf9b49b57 --- /dev/null +++ b/Runtime/rollup.config.js @@ -0,0 +1,20 @@ +import typescript from "@rollup/plugin-typescript"; + +/** @type {import('rollup').RollupOptions} */ +const config = { + input: "src/index.ts", + output: [ + { + file: "lib/index.mjs", + format: "esm", + }, + { + dir: "lib", + format: "umd", + name: "JavaScriptKit", + }, + ], + plugins: [typescript()], +}; + +export default config; diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts new file mode 100644 index 000000000..8d2c5600c --- /dev/null +++ b/Runtime/src/closure-heap.ts @@ -0,0 +1,25 @@ +import { ExportedFunctions } from "./types"; + +/// Memory lifetime of closures in Swift are managed by Swift side +export class SwiftClosureDeallocator { + private functionRegistry: FinalizationRegistry; + + 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." + ); + } + + this.functionRegistry = new FinalizationRegistry((id) => { + exports.swjs_free_host_function(id); + }); + } + + track(func: Function, 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 fae61b7ff..74abed94d 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -1,596 +1,354 @@ -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; -} +import { SwiftClosureDeallocator } from "./closure-heap"; +import { + LibraryFeatures, + ExportedFunctions, + ref, + pointer, + TypedArray, + ImportedFunctions, +} from "./types"; +import * as JSValue from "./js-value"; +import { Memory } from "./memory"; -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; +export class SwiftRuntime { + private _instance: WebAssembly.Instance | null; + private _memory: Memory | null; + private _closureDeallocator: SwiftClosureDeallocator | null; + private version: number = 705; -type SwiftRuntimeHeapEntry = { - id: number; - rc: number; -}; -class SwiftRuntimeHeap { - private _heapValueById: Map; - private _heapEntryByValue: Map; - private _heapNextKey: number; + private textDecoder = new TextDecoder("utf-8"); + private textEncoder = new TextEncoder(); // Only support utf-8 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; + this._instance = null; + this._memory = null; + this._closureDeallocator = null; } - 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 + setInstance(instance: WebAssembly.Instance) { + this._instance = instance; + if (this.exports.swjs_library_version() != this.version) { + throw new Error( + `The versions of JavaScriptKit are incompatible. ${this.exports.swjs_library_version()} != ${ + this.version + }` ); } - 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); - }); + private get instance() { + if (!this._instance) + throw new Error("WebAssembly instance is not set yet"); + return this._instance; } - alloc(func: any, func_ref: number) { - this.functionRegistry.register(func, func_ref); + private get exports() { + return this.instance.exports as any as ExportedFunctions; } -} -export class SwiftRuntime { - private instance: WebAssembly.Instance | null; - private heap: SwiftRuntimeHeap; - private _closureHeap: SwiftClosureHeap | null; - private version: number = 704; - - constructor() { - this.instance = null; - this.heap = new SwiftRuntimeHeap(); - 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) { - throw new Error( - `The versions of JavaScriptKit are incompatible. ${exports.swjs_library_version()} != ${ - this.version - }` - ); + private get memory() { + if (!this._memory) { + this._memory = new Memory(this.instance.exports); } + return this._memory; } - get closureHeap(): SwiftClosureHeap | null { - if (this._closureHeap) return this._closureHeap; - if (!this.instance) - throw new Error("WebAssembly instance is not set yet"); - const exports = (this.instance - .exports as any) as SwiftRuntimeExportedFunctions; - const features = exports.swjs_library_features(); + private 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) { - if (typeof FinalizationRegistry !== "undefined") { - this._closureHeap = new SwiftClosureHeap(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._closureDeallocator = new SwiftClosureDeallocator( + this.exports + ); } - return null; + return this._closureDeallocator; } - 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); - 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); - } - let output: any; - const callback_func_ref = this.heap.retain(function (result: any) { - output = result; - }); - exports.swjs_call_host_function( - host_func_id, - argv, - argc, - callback_func_ref + 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++) { + const argument = args[index]; + const base = argv + 16 * index; + JSValue.write( + argument, + base, + base + 4, + base + 8, + false, + this.memory ); - exports.swjs_cleanup_host_function_call(argv); - return output; - }; - - const textDecoder = new TextDecoder("utf-8"); - const textEncoder = new TextEncoder(); // Only support utf-8 - - const readString = (ref: ref) => { - return this.heap.referenceHeap(ref); - }; + } + 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; + } - const writeBytes = (ptr: pointer, bytes: Uint8Array) => { - const uint8Memory = new Uint8Array(memory().buffer); - uint8Memory.set(bytes, ptr); - }; + /** @deprecated Use `wasmImports` instead */ + importObjects = () => this.wasmImports; - const readUInt32 = (ptr: pointer) => { - const uint8Memory = new Uint8Array(memory().buffer); - return ( - uint8Memory[ptr + 0] + - (uint8Memory[ptr + 1] << 8) + - (uint8Memory[ptr + 2] << 16) + - (uint8Memory[ptr + 3] << 24) + readonly wasmImports: ImportedFunctions = { + swjs_set_prop: ( + ref: ref, + name: ref, + kind: JSValue.Kind, + payload1: number, + payload2: number + ) => { + const obj = this.memory.getObject(ref); + Reflect.set( + obj, + this.memory.getObject(name), + JSValue.decode(kind, payload1, payload2, this.memory) ); - }; + }, - const readFloat64 = (ptr: pointer) => { - const dataView = new DataView(memory().buffer); - return dataView.getFloat64(ptr, true); - }; - - const writeUint32 = (ptr: pointer, value: number) => { - const uint8Memory = new Uint8Array(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(memory().buffer); - dataView.setFloat64(ptr, value, true); - }; + 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)); + JSValue.write( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, - const decodeValue = ( - kind: JavaScriptValueKind, + swjs_set_subscript: ( + ref: ref, + index: number, + kind: JSValue.Kind, 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 obj = this.memory.getObject(ref); + Reflect.set( + obj, + index, + JSValue.decode(kind, payload1, payload2, this.memory) + ); + }, - const writeValue = ( - value: any, + 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); + JSValue.write( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + 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_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_call_function: ( + ref: ref, + argv: pointer, + argc: number, kind_ptr: pointer, payload1_ptr: pointer, - payload2_ptr: pointer, - is_exception: boolean + payload2_ptr: pointer ) => { - const exceptionBit = (is_exception ? 1 : 0) << 31; - if (value === null) { - writeUint32(kind_ptr, exceptionBit | JavaScriptValueKind.Null); + const func = this.memory.getObject(ref); + let result: any; + try { + result = Reflect.apply( + func, + undefined, + JSValue.decodeArray(argv, argc, this.memory) + ); + } catch (error) { + JSValue.write( + error, + kind_ptr, + payload1_ptr, + payload2_ptr, + true, + this.memory + ); 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; - }; + JSValue.write( + result, + kind_ptr, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); + }, - return { - swjs_set_prop: ( - ref: ref, - name: ref, - kind: JavaScriptValueKind, - payload1: number, - payload2: number - ) => { - const obj = this.heap.referenceHeap(ref); - Reflect.set( + 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, - readString(name), - decodeValue(kind, payload1, payload2) + JSValue.decodeArray(argv, argc, this.memory) ); - }, - - swjs_get_prop: ( - ref: ref, - name: ref, - kind_ptr: pointer, - 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); - }, - - swjs_set_subscript: ( - ref: ref, - index: number, - kind: JavaScriptValueKind, - payload1: number, - payload2: number - ) => { - const obj = this.heap.referenceHeap(ref); - Reflect.set(obj, index, decodeValue(kind, payload1, payload2)); - }, - - swjs_get_subscript: ( - ref: ref, - index: number, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer - ) => { - const obj = this.heap.referenceHeap(ref); - const result = Reflect.get(obj, index); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); - }, - - 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); - return bytes.length; - }, - - swjs_decode_string: (bytes_ptr: pointer, length: number) => { - const uint8Memory = new Uint8Array(memory().buffer); - const bytes = uint8Memory.subarray( - bytes_ptr, - bytes_ptr + length + } catch (error) { + JSValue.write( + error, + kind_ptr, + payload1_ptr, + payload2_ptr, + true, + this.memory ); - const string = textDecoder.decode(bytes); - return this.heap.retain(string); - }, - - swjs_load_string: (ref: ref, buffer: pointer) => { - const bytes = this.heap.referenceHeap(ref); - writeBytes(buffer, bytes); - }, - - swjs_call_function: ( - ref: ref, - argv: pointer, - argc: number, - kind_ptr: pointer, - payload1_ptr: pointer, - payload2_ptr: pointer - ) => { - const func = this.heap.referenceHeap(ref); - let result: any; - try { - result = Reflect.apply( - func, - undefined, - decodeValues(argv, argc) - ); - } catch (error) { - writeValue( - error, - kind_ptr, - payload1_ptr, - payload2_ptr, - true - ); - return; - } - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); - }, - - 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.heap.referenceHeap(obj_ref); - const func = this.heap.referenceHeap(func_ref); - let result: any; - try { - result = Reflect.apply(func, obj, decodeValues(argv, argc)); - } catch (error) { - writeValue( - error, - kind_ptr, - payload1_ptr, - payload2_ptr, - true - ); - return; - } - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); - }, - swjs_call_new: (ref: ref, argv: pointer, argc: number) => { - const constructor = this.heap.referenceHeap(ref); - const instance = Reflect.construct( + return; + } + JSValue.write( + 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, + JSValue.decodeArray(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, - decodeValues(argv, argc) + JSValue.decodeArray(argv, argc, this.memory) ); - return this.heap.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.heap.referenceHeap(ref); - let result: any; - try { - result = Reflect.construct( - constructor, - decodeValues(argv, argc) - ); - } catch (error) { - writeValue( - error, - exception_kind_ptr, - exception_payload1_ptr, - exception_payload2_ptr, - true - ); - return -1; - } - writeValue( - null, + } catch (error) { + JSValue.write( + error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, - false + true, + this.memory ); - return this.heap.retain(result); - }, - - swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => { - const obj = this.heap.referenceHeap(obj_ref); - const constructor = this.heap.referenceHeap(constructor_ref); - return obj instanceof constructor; - }, - - swjs_create_function: (host_func_id: number) => { - const func = function () { - return callHostFunction( - host_func_id, - Array.prototype.slice.call(arguments) - ); - }; - const func_ref = this.heap.retain(func); - this.closureHeap?.alloc(func, func_ref); - return func_ref; - }, - - swjs_create_typed_array: ( - constructor_ref: ref, - elementsPtr: pointer, - length: number - ) => { - const ArrayType: TypedArray = this.heap.referenceHeap( - constructor_ref - ); - const array = new ArrayType( - memory().buffer, - elementsPtr, - length - ); - // Call `.slice()` to copy the memory - return this.heap.retain(array.slice()); - }, - - swjs_load_typed_array: (ref: ref, buffer: pointer) => { - const typedArray = this.heap.referenceHeap(ref); - const bytes = new Uint8Array(typedArray.buffer); - writeBytes(buffer, bytes); - }, - - swjs_release: (ref: ref) => { - this.heap.release(ref); - }, - }; - } + return -1; + } + JSValue.write( + 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_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_load_typed_array: (ref: ref, buffer: pointer) => { + const typedArray = this.memory.getObject(ref); + const bytes = new Uint8Array(typedArray.buffer); + this.memory.writeBytes(buffer, bytes); + }, + + swjs_release: (ref: 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..61f7b486a --- /dev/null +++ b/Runtime/src/js-value.ts @@ -0,0 +1,108 @@ +import { Memory } from "./memory"; +import { pointer } from "./types"; + +export enum Kind { + Invalid = -1, + Boolean = 0, + String = 1, + Number = 2, + Object = 3, + Null = 4, + Undefined = 5, + Function = 6, +} + +export const decode = ( + kind: Kind, + payload1: number, + payload2: number, + memory: Memory +) => { + switch (kind) { + case Kind.Boolean: + switch (payload1) { + case 0: + return false; + case 1: + return true; + } + case Kind.Number: + return payload2; + + case Kind.String: + case Kind.Object: + case Kind.Function: + return memory.getObject(payload1); + + case Kind.Null: + return null; + + case Kind.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 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(decode(kind, payload1, payload2, memory)); + } + return result; +}; + +export const write = ( + 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 | Kind.Null); + return; + } + switch (typeof value) { + case "boolean": { + memory.writeUint32(kind_ptr, exceptionBit | Kind.Boolean); + memory.writeUint32(payload1_ptr, value ? 1 : 0); + break; + } + case "number": { + memory.writeUint32(kind_ptr, exceptionBit | Kind.Number); + memory.writeFloat64(payload2_ptr, value); + break; + } + case "string": { + memory.writeUint32(kind_ptr, exceptionBit | Kind.String); + memory.writeUint32(payload1_ptr, memory.retain(value)); + break; + } + case "undefined": { + memory.writeUint32(kind_ptr, exceptionBit | Kind.Undefined); + break; + } + case "object": { + memory.writeUint32(kind_ptr, exceptionBit | Kind.Object); + memory.writeUint32(payload1_ptr, memory.retain(value)); + break; + } + case "function": { + memory.writeUint32(kind_ptr, exceptionBit | Kind.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..a8391cfd1 --- /dev/null +++ b/Runtime/src/memory.ts @@ -0,0 +1,31 @@ +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, true); +} 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..4f613cc6a --- /dev/null +++ b/Runtime/src/types.ts @@ -0,0 +1,105 @@ +import * as JSValue from "./js-value"; + +export type ref = number; +export type pointer = number; + +export interface ExportedFunctions { + 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 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_load_typed_array(ref: ref, buffer: pointer): void; + swjs_release(ref: number): void; +} + +export enum LibraryFeatures { + WeakRefs = 1 << 0, +} + +export type TypedArray = + | Int8ArrayConstructor + | Uint8ArrayConstructor + | Int16ArrayConstructor + | Uint16ArrayConstructor + | Int32ArrayConstructor + | Uint32ArrayConstructor + // BigInt is not yet supported, see https://github.com/swiftwasm/JavaScriptKit/issues/56 + // | BigInt64ArrayConstructor + // | BigUint64ArrayConstructor + | Float32ArrayConstructor + | Float64ArrayConstructor; diff --git a/Runtime/tsconfig.json b/Runtime/tsconfig.json index 3182659ca..8fa8e650d 100644 --- a/Runtime/tsconfig.json +++ b/Runtime/tsconfig.json @@ -1,9 +1,10 @@ { "compilerOptions": { "declaration": true, + "declarationDir": "lib", "importHelpers": true, - "module": "commonjs", - "outDir": "lib", + "module": "esnext", + "noEmit": true, "rootDir": "src", "strict": true, "target": "es2017", 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! //} diff --git a/Sources/_CJavaScriptKit/_CJavaScriptKit.c b/Sources/_CJavaScriptKit/_CJavaScriptKit.c index 6222bfe90..38329ff14 100644 --- a/Sources/_CJavaScriptKit/_CJavaScriptKit.c +++ b/Sources/_CJavaScriptKit/_CJavaScriptKit.c @@ -36,7 +36,7 @@ void swjs_cleanup_host_function_call(void *argv_buffer) { /// this and `SwiftRuntime.version` in `./Runtime/src/index.ts`. __attribute__((export_name("swjs_library_version"))) int swjs_library_version(void) { - return 704; + return 705; } int _library_features(void); diff --git a/package-lock.json b/package-lock.json index d6c775156..b793e24a9 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,10 +136,61 @@ "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", - "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", @@ -40,16 +202,125 @@ } }, "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", - "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 } } diff --git a/package.json b/package.json index 3beae47fa..ffe8fc4cf 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,15 @@ "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" ], "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 +34,9 @@ "author": "swiftwasm", "license": "MIT", "devDependencies": { + "@rollup/plugin-typescript": "^8.3.0", "prettier": "2.5.1", + "rollup": "^2.63.0", "typescript": "^4.4.2" } }