From 7cc8ff34fa1deaea15484104ea60931e7a800d33 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 9 May 2022 13:21:17 +0100 Subject: [PATCH 01/12] Supply JSKit runtime in SwiftPM resources --- Package.swift | 5 +- Sources/JavaScriptKit/Runtime/closure-heap.js | 13 ++ Sources/JavaScriptKit/Runtime/find-global.js | 10 + Sources/JavaScriptKit/Runtime/index.js | 218 ++++++++++++++++++ Sources/JavaScriptKit/Runtime/js-value.js | 99 ++++++++ Sources/JavaScriptKit/Runtime/memory.js | 21 ++ Sources/JavaScriptKit/Runtime/object-heap.js | 37 +++ Sources/JavaScriptKit/Runtime/types.js | 7 + 8 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 Sources/JavaScriptKit/Runtime/closure-heap.js create mode 100644 Sources/JavaScriptKit/Runtime/find-global.js create mode 100644 Sources/JavaScriptKit/Runtime/index.js create mode 100644 Sources/JavaScriptKit/Runtime/js-value.js create mode 100644 Sources/JavaScriptKit/Runtime/memory.js create mode 100644 Sources/JavaScriptKit/Runtime/object-heap.js create mode 100644 Sources/JavaScriptKit/Runtime/types.js diff --git a/Package.swift b/Package.swift index 3d07321ac..d278e5ab9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.3 import PackageDescription @@ -12,7 +12,8 @@ let package = Package( targets: [ .target( name: "JavaScriptKit", - dependencies: ["_CJavaScriptKit"] + dependencies: ["_CJavaScriptKit"], + resources: [.copy("Runtime")] ), .target(name: "_CJavaScriptKit"), .target( diff --git a/Sources/JavaScriptKit/Runtime/closure-heap.js b/Sources/JavaScriptKit/Runtime/closure-heap.js new file mode 100644 index 000000000..b566c8565 --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/closure-heap.js @@ -0,0 +1,13 @@ +export class SwiftClosureDeallocator { + constructor(exports) { + 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, func_ref) { + this.functionRegistry.register(func, func_ref); + } +} diff --git a/Sources/JavaScriptKit/Runtime/find-global.js b/Sources/JavaScriptKit/Runtime/find-global.js new file mode 100644 index 000000000..ce5e0f3c3 --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/find-global.js @@ -0,0 +1,10 @@ +export let globalVariable; +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/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js new file mode 100644 index 000000000..3ed9e2115 --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -0,0 +1,218 @@ +import { SwiftClosureDeallocator } from "./closure-heap"; +import { + LibraryFeatures +} from "./types"; +import * as JSValue from "./js-value"; +import { Memory } from "./memory"; +export class SwiftRuntime { + constructor() { + this.version = 707; + this.textDecoder = new TextDecoder("utf-8"); + this.textEncoder = new TextEncoder(); + this.importObjects = () => this.wasmImports; + this.wasmImports = { + swjs_set_prop: (ref2, name, kind, payload1, payload2) => { + const obj = this.memory.getObject(ref2); + const key = this.memory.getObject(name); + const value = JSValue.decode(kind, payload1, payload2, this.memory); + obj[key] = value; + }, + swjs_get_prop: (ref2, name, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(ref2); + const key = this.memory.getObject(name); + const result = obj[key]; + JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_set_subscript: (ref2, index, kind, payload1, payload2) => { + const obj = this.memory.getObject(ref2); + const value = JSValue.decode(kind, payload1, payload2, this.memory); + obj[index] = value; + }, + swjs_get_subscript: (ref2, index, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(ref2); + const result = obj[index]; + JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_encode_string: (ref2, bytes_ptr_result) => { + const bytes = this.textEncoder.encode(this.memory.getObject(ref2)); + const bytes_ptr = this.memory.retain(bytes); + this.memory.writeUint32(bytes_ptr_result, bytes_ptr); + return bytes.length; + }, + swjs_decode_string: (bytes_ptr, length) => { + 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: (ref2, buffer) => { + const bytes = this.memory.getObject(ref2); + this.memory.writeBytes(buffer, bytes); + }, + swjs_call_function: (ref2, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const func = this.memory.getObject(ref2); + let result; + try { + const args = JSValue.decodeArray(argv, argc, this.memory); + result = func(...args); + } catch (error) { + JSValue.write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + return; + } + JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_call_function_no_catch: (ref2, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const func = this.memory.getObject(ref2); + let isException = true; + try { + const args = JSValue.decodeArray(argv, argc, this.memory); + const result = func(...args); + JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + isException = false; + } finally { + if (isException) { + JSValue.write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + } + } + }, + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let result; + try { + const args = JSValue.decodeArray(argv, argc, this.memory); + result = func.apply(obj, args); + } catch (error) { + JSValue.write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + return; + } + JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let isException = true; + try { + const args = JSValue.decodeArray(argv, argc, this.memory); + const result = func.apply(obj, args); + JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + isException = false; + } finally { + if (isException) { + JSValue.write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + } + } + }, + swjs_call_new: (ref2, argv, argc) => { + const constructor = this.memory.getObject(ref2); + const args = JSValue.decodeArray(argv, argc, this.memory); + const instance = new constructor(...args); + return this.memory.retain(instance); + }, + swjs_call_throwing_new: (ref2, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => { + const constructor = this.memory.getObject(ref2); + let result; + try { + const args = JSValue.decodeArray(argv, argc, this.memory); + result = new constructor(...args); + } catch (error) { + JSValue.write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory); + 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, constructor_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) => { + var _a; + const func = (...args) => this.callHostFunction(host_func_id, args); + const func_ref = this.memory.retain(func); + (_a = this.closureDeallocator) == null ? void 0 : _a.track(func, func_ref); + return func_ref; + }, + swjs_create_typed_array: (constructor_ref, elementsPtr, length) => { + const ArrayType = this.memory.getObject(constructor_ref); + const array = new ArrayType(this.memory.rawMemory.buffer, elementsPtr, length); + return this.memory.retain(array.slice()); + }, + swjs_load_typed_array: (ref2, buffer) => { + const typedArray = this.memory.getObject(ref2); + const bytes = new Uint8Array(typedArray.buffer); + this.memory.writeBytes(buffer, bytes); + }, + swjs_release: (ref2) => { + this.memory.release(ref2); + }, + swjs_i64_to_bigint: (value, signed) => { + return this.memory.retain(signed ? value : BigInt.asUintN(64, value)); + }, + swjs_bigint_to_i64: (ref2, signed) => { + const object = this.memory.getObject(ref2); + if (typeof object !== "bigint") { + throw new Error(`Expected a BigInt, but got ${typeof object}`); + } + if (signed) { + return object; + } else { + if (object < BigInt(0)) { + return BigInt(0); + } + return BigInt.asIntN(64, object); + } + } + }; + this._instance = null; + this._memory = null; + this._closureDeallocator = null; + } + setInstance(instance) { + this._instance = instance; + if (this.exports.swjs_library_version() != this.version) { + throw new Error(`The versions of JavaScriptKit are incompatible. + WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`); + } + } + get instance() { + if (!this._instance) + throw new Error("WebAssembly instance is not set yet"); + return this._instance; + } + get exports() { + return this.instance.exports; + } + get memory() { + if (!this._memory) { + this._memory = new Memory(this.instance.exports); + } + return this._memory; + } + get closureDeallocator() { + if (this._closureDeallocator) + return this._closureDeallocator; + const features = this.exports.swjs_library_features(); + const librarySupportsWeakRef = (features & LibraryFeatures.WeakRefs) != 0; + if (librarySupportsWeakRef) { + this._closureDeallocator = new SwiftClosureDeallocator(this.exports); + } + return this._closureDeallocator; + } + callHostFunction(host_func_id, args) { + 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); + } + let output; + const callback_func_ref = this.memory.retain((result) => { + 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; + } +} diff --git a/Sources/JavaScriptKit/Runtime/js-value.js b/Sources/JavaScriptKit/Runtime/js-value.js new file mode 100644 index 000000000..14270b578 --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/js-value.js @@ -0,0 +1,99 @@ +import { assertNever } from "./types"; +export var Kind = /* @__PURE__ */ ((Kind2) => { + Kind2[Kind2["Boolean"] = 0] = "Boolean"; + Kind2[Kind2["String"] = 1] = "String"; + Kind2[Kind2["Number"] = 2] = "Number"; + Kind2[Kind2["Object"] = 3] = "Object"; + Kind2[Kind2["Null"] = 4] = "Null"; + Kind2[Kind2["Undefined"] = 5] = "Undefined"; + Kind2[Kind2["Function"] = 6] = "Function"; + Kind2[Kind2["Symbol"] = 7] = "Symbol"; + Kind2[Kind2["BigInt"] = 8] = "BigInt"; + return Kind2; +})(Kind || {}); +export const decode = (kind, payload1, payload2, memory) => { + switch (kind) { + case 0 /* Boolean */: + switch (payload1) { + case 0: + return false; + case 1: + return true; + } + case 2 /* Number */: + return payload2; + case 1 /* String */: + case 3 /* Object */: + case 6 /* Function */: + case 7 /* Symbol */: + case 8 /* BigInt */: + return memory.getObject(payload1); + case 4 /* Null */: + return null; + case 5 /* Undefined */: + return void 0; + default: + assertNever(kind, `JSValue Type kind "${kind}" is not supported`); + } +}; +export const decodeArray = (ptr, length, 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, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); + return; + } + const writeRef = (kind) => { + memory.writeUint32(kind_ptr, exceptionBit | kind); + memory.writeUint32(payload1_ptr, memory.retain(value)); + }; + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); + memory.writeUint32(payload1_ptr, value ? 1 : 0); + break; + } + case "number": { + memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); + memory.writeFloat64(payload2_ptr, value); + break; + } + case "string": { + writeRef(1 /* String */); + break; + } + case "undefined": { + memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); + break; + } + case "object": { + writeRef(3 /* Object */); + break; + } + case "function": { + writeRef(6 /* Function */); + break; + } + case "symbol": { + writeRef(7 /* Symbol */); + break; + } + case "bigint": { + writeRef(8 /* BigInt */); + break; + } + default: + assertNever(type, `Type "${type}" is not supported yet`); + } +}; diff --git a/Sources/JavaScriptKit/Runtime/memory.js b/Sources/JavaScriptKit/Runtime/memory.js new file mode 100644 index 000000000..3df0e17cf --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/memory.js @@ -0,0 +1,21 @@ +import { SwiftRuntimeHeap } from "./object-heap"; +export class Memory { + constructor(exports) { + this.heap = new SwiftRuntimeHeap(); + this.retain = (value) => this.heap.retain(value); + this.getObject = (ref) => this.heap.referenceHeap(ref); + this.release = (ref) => this.heap.release(ref); + this.bytes = () => new Uint8Array(this.rawMemory.buffer); + this.dataView = () => new DataView(this.rawMemory.buffer); + this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr); + this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true); + this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true); + this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true); + this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true); + this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true); + this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true); + this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true); + this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); + this.rawMemory = exports.memory; + } +} diff --git a/Sources/JavaScriptKit/Runtime/object-heap.js b/Sources/JavaScriptKit/Runtime/object-heap.js new file mode 100644 index 000000000..57d241ab5 --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/object-heap.js @@ -0,0 +1,37 @@ +import { globalVariable } from "./find-global"; +export class SwiftRuntimeHeap { + constructor() { + this._heapValueById = /* @__PURE__ */ new Map(); + this._heapValueById.set(0, globalVariable); + this._heapEntryByValue = /* @__PURE__ */ new Map(); + this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); + this._heapNextKey = 1; + } + retain(value) { + const entry = this._heapEntryByValue.get(value); + if (entry) { + entry.rc++; + return entry.id; + } + const id = this._heapNextKey++; + this._heapValueById.set(id, value); + this._heapEntryByValue.set(value, { id, rc: 1 }); + return id; + } + release(ref2) { + const value = this._heapValueById.get(ref2); + const entry = this._heapEntryByValue.get(value); + entry.rc--; + if (entry.rc != 0) + return; + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref2); + } + referenceHeap(ref2) { + const value = this._heapValueById.get(ref2); + if (value === void 0) { + throw new ReferenceError("Attempted to read invalid reference " + ref2); + } + return value; + } +} diff --git a/Sources/JavaScriptKit/Runtime/types.js b/Sources/JavaScriptKit/Runtime/types.js new file mode 100644 index 000000000..32ed3f23e --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/types.js @@ -0,0 +1,7 @@ +export var LibraryFeatures = /* @__PURE__ */ ((LibraryFeatures2) => { + LibraryFeatures2[LibraryFeatures2["WeakRefs"] = 1] = "WeakRefs"; + return LibraryFeatures2; +})(LibraryFeatures || {}); +export function assertNever(x, message) { + throw new Error(message); +} From 561110a0f93888d387e35ae3d3406fb2a0fcc6c8 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 11 May 2022 17:28:14 +0100 Subject: [PATCH 02/12] Use `.js` suffix when importing --- Runtime/src/closure-heap.ts | 2 +- Runtime/src/index.ts | 8 ++++---- Runtime/src/memory.ts | 4 ++-- Runtime/src/object-heap.ts | 4 ++-- Runtime/src/types.ts | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Runtime/src/closure-heap.ts b/Runtime/src/closure-heap.ts index 8d2c5600c..269934390 100644 --- a/Runtime/src/closure-heap.ts +++ b/Runtime/src/closure-heap.ts @@ -1,4 +1,4 @@ -import { ExportedFunctions } from "./types"; +import { ExportedFunctions } from "./types.js"; /// Memory lifetime of closures in Swift are managed by Swift side export class SwiftClosureDeallocator { diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index dcd817a72..17c53696e 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -1,4 +1,4 @@ -import { SwiftClosureDeallocator } from "./closure-heap"; +import { SwiftClosureDeallocator } from "./closure-heap.js"; import { LibraryFeatures, ExportedFunctions, @@ -6,9 +6,9 @@ import { pointer, TypedArray, ImportedFunctions, -} from "./types"; -import * as JSValue from "./js-value"; -import { Memory } from "./memory"; +} from "./types.js"; +import * as JSValue from "./js-value.js"; +import { Memory } from "./memory.js"; export class SwiftRuntime { private _instance: WebAssembly.Instance | null; diff --git a/Runtime/src/memory.ts b/Runtime/src/memory.ts index 29f827623..d8334516d 100644 --- a/Runtime/src/memory.ts +++ b/Runtime/src/memory.ts @@ -1,5 +1,5 @@ -import { SwiftRuntimeHeap } from "./object-heap"; -import { pointer } from "./types"; +import { SwiftRuntimeHeap } from "./object-heap.js"; +import { pointer } from "./types.js"; export class Memory { readonly rawMemory: WebAssembly.Memory; diff --git a/Runtime/src/object-heap.ts b/Runtime/src/object-heap.ts index 2f9b1fdf3..d59f5101e 100644 --- a/Runtime/src/object-heap.ts +++ b/Runtime/src/object-heap.ts @@ -1,5 +1,5 @@ -import { globalVariable } from "./find-global"; -import { ref } from "./types"; +import { globalVariable } from "./find-global.js"; +import { ref } from "./types.js"; type SwiftRuntimeHeapEntry = { id: number; diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index bc6700877..d587b2c8a 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -1,4 +1,4 @@ -import * as JSValue from "./js-value"; +import * as JSValue from "./js-value.js"; export type ref = number; export type pointer = number; From 4f1f9d2677f04e523db1634e506859545d77c321 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 11 May 2022 17:35:23 +0100 Subject: [PATCH 03/12] Update assets --- Runtime/src/js-value.ts | 4 ++-- Sources/JavaScriptKit/Runtime/index.js | 8 ++++---- Sources/JavaScriptKit/Runtime/js-value.js | 2 +- Sources/JavaScriptKit/Runtime/memory.js | 2 +- Sources/JavaScriptKit/Runtime/object-heap.js | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index c8896900f..c3c24c3a9 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -1,5 +1,5 @@ -import { Memory } from "./memory"; -import { assertNever, pointer } from "./types"; +import { Memory } from "./memory.js"; +import { assertNever, pointer } from "./types.js"; export const enum Kind { Boolean = 0, diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index 3ed9e2115..0bc263e54 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -1,9 +1,9 @@ -import { SwiftClosureDeallocator } from "./closure-heap"; +import { SwiftClosureDeallocator } from "./closure-heap.js"; import { LibraryFeatures -} from "./types"; -import * as JSValue from "./js-value"; -import { Memory } from "./memory"; +} from "./types.js"; +import * as JSValue from "./js-value.js"; +import { Memory } from "./memory.js"; export class SwiftRuntime { constructor() { this.version = 707; diff --git a/Sources/JavaScriptKit/Runtime/js-value.js b/Sources/JavaScriptKit/Runtime/js-value.js index 14270b578..85cc8146e 100644 --- a/Sources/JavaScriptKit/Runtime/js-value.js +++ b/Sources/JavaScriptKit/Runtime/js-value.js @@ -1,4 +1,4 @@ -import { assertNever } from "./types"; +import { assertNever } from "./types.js"; export var Kind = /* @__PURE__ */ ((Kind2) => { Kind2[Kind2["Boolean"] = 0] = "Boolean"; Kind2[Kind2["String"] = 1] = "String"; diff --git a/Sources/JavaScriptKit/Runtime/memory.js b/Sources/JavaScriptKit/Runtime/memory.js index 3df0e17cf..2e02f58a9 100644 --- a/Sources/JavaScriptKit/Runtime/memory.js +++ b/Sources/JavaScriptKit/Runtime/memory.js @@ -1,4 +1,4 @@ -import { SwiftRuntimeHeap } from "./object-heap"; +import { SwiftRuntimeHeap } from "./object-heap.js"; export class Memory { constructor(exports) { this.heap = new SwiftRuntimeHeap(); diff --git a/Sources/JavaScriptKit/Runtime/object-heap.js b/Sources/JavaScriptKit/Runtime/object-heap.js index 57d241ab5..0c0ac00a3 100644 --- a/Sources/JavaScriptKit/Runtime/object-heap.js +++ b/Sources/JavaScriptKit/Runtime/object-heap.js @@ -1,4 +1,4 @@ -import { globalVariable } from "./find-global"; +import { globalVariable } from "./find-global.js"; export class SwiftRuntimeHeap { constructor() { this._heapValueById = /* @__PURE__ */ new Map(); From 26aaeeae9691481d884748af035f78a18cb06889 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 11 May 2022 22:06:02 +0100 Subject: [PATCH 04/12] Attempt to use CommonJS format --- Sources/JavaScriptKit/Runtime/closure-heap.js | 28 +- Sources/JavaScriptKit/Runtime/find-global.js | 26 +- Sources/JavaScriptKit/Runtime/index.js | 260 ++++++++++++++++-- Sources/JavaScriptKit/Runtime/js-value.js | 43 ++- Sources/JavaScriptKit/Runtime/memory.js | 81 +++++- Sources/JavaScriptKit/Runtime/object-heap.js | 55 +++- Sources/JavaScriptKit/Runtime/types.js | 29 +- 7 files changed, 470 insertions(+), 52 deletions(-) diff --git a/Sources/JavaScriptKit/Runtime/closure-heap.js b/Sources/JavaScriptKit/Runtime/closure-heap.js index b566c8565..daec31324 100644 --- a/Sources/JavaScriptKit/Runtime/closure-heap.js +++ b/Sources/JavaScriptKit/Runtime/closure-heap.js @@ -1,4 +1,28 @@ -export class SwiftClosureDeallocator { +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/closure-heap.ts +var closure_heap_exports = {}; +__export(closure_heap_exports, { + SwiftClosureDeallocator: () => SwiftClosureDeallocator +}); +module.exports = __toCommonJS(closure_heap_exports); +var SwiftClosureDeallocator = class { constructor(exports) { 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."); @@ -10,4 +34,4 @@ export class SwiftClosureDeallocator { track(func, func_ref) { this.functionRegistry.register(func, func_ref); } -} +}; diff --git a/Sources/JavaScriptKit/Runtime/find-global.js b/Sources/JavaScriptKit/Runtime/find-global.js index ce5e0f3c3..f50f7c98b 100644 --- a/Sources/JavaScriptKit/Runtime/find-global.js +++ b/Sources/JavaScriptKit/Runtime/find-global.js @@ -1,4 +1,28 @@ -export let globalVariable; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/find-global.ts +var find_global_exports = {}; +__export(find_global_exports, { + globalVariable: () => globalVariable +}); +module.exports = __toCommonJS(find_global_exports); +var globalVariable; if (typeof globalThis !== "undefined") { globalVariable = globalThis; } else if (typeof window !== "undefined") { diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index 0bc263e54..db6b19b76 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -1,10 +1,210 @@ -import { SwiftClosureDeallocator } from "./closure-heap.js"; -import { - LibraryFeatures -} from "./types.js"; -import * as JSValue from "./js-value.js"; -import { Memory } from "./memory.js"; -export class SwiftRuntime { +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/index.ts +var src_exports = {}; +__export(src_exports, { + SwiftRuntime: () => SwiftRuntime +}); +module.exports = __toCommonJS(src_exports); + +// src/closure-heap.ts +var SwiftClosureDeallocator = class { + constructor(exports) { + 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, func_ref) { + this.functionRegistry.register(func, func_ref); + } +}; + +// src/types.ts +function assertNever(x, message) { + throw new Error(message); +} + +// src/js-value.ts +var decode = (kind, payload1, payload2, memory) => { + switch (kind) { + case 0 /* Boolean */: + switch (payload1) { + case 0: + return false; + case 1: + return true; + } + case 2 /* Number */: + return payload2; + case 1 /* String */: + case 3 /* Object */: + case 6 /* Function */: + case 7 /* Symbol */: + case 8 /* BigInt */: + return memory.getObject(payload1); + case 4 /* Null */: + return null; + case 5 /* Undefined */: + return void 0; + default: + assertNever(kind, `JSValue Type kind "${kind}" is not supported`); + } +}; +var decodeArray = (ptr, length, 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; +}; +var write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); + return; + } + const writeRef = (kind) => { + memory.writeUint32(kind_ptr, exceptionBit | kind); + memory.writeUint32(payload1_ptr, memory.retain(value)); + }; + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); + memory.writeUint32(payload1_ptr, value ? 1 : 0); + break; + } + case "number": { + memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); + memory.writeFloat64(payload2_ptr, value); + break; + } + case "string": { + writeRef(1 /* String */); + break; + } + case "undefined": { + memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); + break; + } + case "object": { + writeRef(3 /* Object */); + break; + } + case "function": { + writeRef(6 /* Function */); + break; + } + case "symbol": { + writeRef(7 /* Symbol */); + break; + } + case "bigint": { + writeRef(8 /* BigInt */); + break; + } + default: + assertNever(type, `Type "${type}" is not supported yet`); + } +}; + +// src/find-global.ts +var globalVariable; +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; +} + +// src/object-heap.ts +var SwiftRuntimeHeap = class { + constructor() { + this._heapValueById = /* @__PURE__ */ new Map(); + this._heapValueById.set(0, globalVariable); + this._heapEntryByValue = /* @__PURE__ */ new Map(); + this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); + this._heapNextKey = 1; + } + retain(value) { + const entry = this._heapEntryByValue.get(value); + if (entry) { + entry.rc++; + return entry.id; + } + const id = this._heapNextKey++; + this._heapValueById.set(id, value); + this._heapEntryByValue.set(value, { id, rc: 1 }); + return id; + } + release(ref2) { + const value = this._heapValueById.get(ref2); + const entry = this._heapEntryByValue.get(value); + entry.rc--; + if (entry.rc != 0) + return; + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref2); + } + referenceHeap(ref2) { + const value = this._heapValueById.get(ref2); + if (value === void 0) { + throw new ReferenceError("Attempted to read invalid reference " + ref2); + } + return value; + } +}; + +// src/memory.ts +var Memory = class { + constructor(exports) { + this.heap = new SwiftRuntimeHeap(); + this.retain = (value) => this.heap.retain(value); + this.getObject = (ref2) => this.heap.referenceHeap(ref2); + this.release = (ref2) => this.heap.release(ref2); + this.bytes = () => new Uint8Array(this.rawMemory.buffer); + this.dataView = () => new DataView(this.rawMemory.buffer); + this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr); + this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true); + this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true); + this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true); + this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true); + this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true); + this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true); + this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true); + this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); + this.rawMemory = exports.memory; + } +}; + +// src/index.ts +var SwiftRuntime = class { constructor() { this.version = 707; this.textDecoder = new TextDecoder("utf-8"); @@ -14,24 +214,24 @@ export class SwiftRuntime { swjs_set_prop: (ref2, name, kind, payload1, payload2) => { const obj = this.memory.getObject(ref2); const key = this.memory.getObject(name); - const value = JSValue.decode(kind, payload1, payload2, this.memory); + const value = decode(kind, payload1, payload2, this.memory); obj[key] = value; }, swjs_get_prop: (ref2, name, kind_ptr, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(ref2); const key = this.memory.getObject(name); const result = obj[key]; - JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_set_subscript: (ref2, index, kind, payload1, payload2) => { const obj = this.memory.getObject(ref2); - const value = JSValue.decode(kind, payload1, payload2, this.memory); + const value = decode(kind, payload1, payload2, this.memory); obj[index] = value; }, swjs_get_subscript: (ref2, index, kind_ptr, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(ref2); const result = obj[index]; - JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_encode_string: (ref2, bytes_ptr_result) => { const bytes = this.textEncoder.encode(this.memory.getObject(ref2)); @@ -52,25 +252,25 @@ export class SwiftRuntime { const func = this.memory.getObject(ref2); let result; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, this.memory); result = func(...args); } catch (error) { - JSValue.write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); return; } - JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_no_catch: (ref2, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref2); let isException = true; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, this.memory); const result = func(...args); - JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); isException = false; } finally { if (isException) { - JSValue.write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); } } }, @@ -79,32 +279,32 @@ export class SwiftRuntime { const func = this.memory.getObject(func_ref); let result; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, this.memory); result = func.apply(obj, args); } catch (error) { - JSValue.write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); return; } - JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(obj_ref); const func = this.memory.getObject(func_ref); let isException = true; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, this.memory); const result = func.apply(obj, args); - JSValue.write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); isException = false; } finally { if (isException) { - JSValue.write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); } } }, swjs_call_new: (ref2, argv, argc) => { const constructor = this.memory.getObject(ref2); - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, this.memory); const instance = new constructor(...args); return this.memory.retain(instance); }, @@ -112,13 +312,13 @@ export class SwiftRuntime { const constructor = this.memory.getObject(ref2); let result; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, this.memory); result = new constructor(...args); } catch (error) { - JSValue.write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory); + write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory); return -1; } - JSValue.write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory); + write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory); return this.memory.retain(result); }, swjs_instanceof: (obj_ref, constructor_ref) => { @@ -193,7 +393,7 @@ export class SwiftRuntime { if (this._closureDeallocator) return this._closureDeallocator; const features = this.exports.swjs_library_features(); - const librarySupportsWeakRef = (features & LibraryFeatures.WeakRefs) != 0; + const librarySupportsWeakRef = (features & 1 /* WeakRefs */) != 0; if (librarySupportsWeakRef) { this._closureDeallocator = new SwiftClosureDeallocator(this.exports); } @@ -205,7 +405,7 @@ export class SwiftRuntime { 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); + write(argument, base, base + 4, base + 8, false, this.memory); } let output; const callback_func_ref = this.memory.retain((result) => { @@ -215,4 +415,4 @@ export class SwiftRuntime { this.exports.swjs_cleanup_host_function_call(argv); return output; } -} +}; diff --git a/Sources/JavaScriptKit/Runtime/js-value.js b/Sources/JavaScriptKit/Runtime/js-value.js index 85cc8146e..0dc31a939 100644 --- a/Sources/JavaScriptKit/Runtime/js-value.js +++ b/Sources/JavaScriptKit/Runtime/js-value.js @@ -1,5 +1,38 @@ -import { assertNever } from "./types.js"; -export var Kind = /* @__PURE__ */ ((Kind2) => { +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/js-value.ts +var js_value_exports = {}; +__export(js_value_exports, { + Kind: () => Kind, + decode: () => decode, + decodeArray: () => decodeArray, + write: () => write +}); +module.exports = __toCommonJS(js_value_exports); + +// src/types.ts +function assertNever(x, message) { + throw new Error(message); +} + +// src/js-value.ts +var Kind = /* @__PURE__ */ ((Kind2) => { Kind2[Kind2["Boolean"] = 0] = "Boolean"; Kind2[Kind2["String"] = 1] = "String"; Kind2[Kind2["Number"] = 2] = "Number"; @@ -11,7 +44,7 @@ export var Kind = /* @__PURE__ */ ((Kind2) => { Kind2[Kind2["BigInt"] = 8] = "BigInt"; return Kind2; })(Kind || {}); -export const decode = (kind, payload1, payload2, memory) => { +var decode = (kind, payload1, payload2, memory) => { switch (kind) { case 0 /* Boolean */: switch (payload1) { @@ -36,7 +69,7 @@ export const decode = (kind, payload1, payload2, memory) => { assertNever(kind, `JSValue Type kind "${kind}" is not supported`); } }; -export const decodeArray = (ptr, length, memory) => { +var decodeArray = (ptr, length, memory) => { let result = []; for (let index = 0; index < length; index++) { const base = ptr + 16 * index; @@ -47,7 +80,7 @@ export const decodeArray = (ptr, length, memory) => { } return result; }; -export const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { +var write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); diff --git a/Sources/JavaScriptKit/Runtime/memory.js b/Sources/JavaScriptKit/Runtime/memory.js index 2e02f58a9..fb73821a8 100644 --- a/Sources/JavaScriptKit/Runtime/memory.js +++ b/Sources/JavaScriptKit/Runtime/memory.js @@ -1,5 +1,80 @@ -import { SwiftRuntimeHeap } from "./object-heap.js"; -export class Memory { +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/memory.ts +var memory_exports = {}; +__export(memory_exports, { + Memory: () => Memory +}); +module.exports = __toCommonJS(memory_exports); + +// src/find-global.ts +var globalVariable; +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; +} + +// src/object-heap.ts +var SwiftRuntimeHeap = class { + constructor() { + this._heapValueById = /* @__PURE__ */ new Map(); + this._heapValueById.set(0, globalVariable); + this._heapEntryByValue = /* @__PURE__ */ new Map(); + this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); + this._heapNextKey = 1; + } + retain(value) { + const entry = this._heapEntryByValue.get(value); + if (entry) { + entry.rc++; + return entry.id; + } + const id = this._heapNextKey++; + this._heapValueById.set(id, value); + this._heapEntryByValue.set(value, { id, rc: 1 }); + return id; + } + release(ref) { + const value = this._heapValueById.get(ref); + const entry = this._heapEntryByValue.get(value); + entry.rc--; + if (entry.rc != 0) + return; + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref); + } + referenceHeap(ref) { + const value = this._heapValueById.get(ref); + if (value === void 0) { + throw new ReferenceError("Attempted to read invalid reference " + ref); + } + return value; + } +}; + +// src/memory.ts +var Memory = class { constructor(exports) { this.heap = new SwiftRuntimeHeap(); this.retain = (value) => this.heap.retain(value); @@ -18,4 +93,4 @@ export class Memory { this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); this.rawMemory = exports.memory; } -} +}; diff --git a/Sources/JavaScriptKit/Runtime/object-heap.js b/Sources/JavaScriptKit/Runtime/object-heap.js index 0c0ac00a3..b54274031 100644 --- a/Sources/JavaScriptKit/Runtime/object-heap.js +++ b/Sources/JavaScriptKit/Runtime/object-heap.js @@ -1,5 +1,42 @@ -import { globalVariable } from "./find-global.js"; -export class SwiftRuntimeHeap { +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/object-heap.ts +var object_heap_exports = {}; +__export(object_heap_exports, { + SwiftRuntimeHeap: () => SwiftRuntimeHeap +}); +module.exports = __toCommonJS(object_heap_exports); + +// src/find-global.ts +var globalVariable; +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; +} + +// src/object-heap.ts +var SwiftRuntimeHeap = class { constructor() { this._heapValueById = /* @__PURE__ */ new Map(); this._heapValueById.set(0, globalVariable); @@ -18,20 +55,20 @@ export class SwiftRuntimeHeap { this._heapEntryByValue.set(value, { id, rc: 1 }); return id; } - release(ref2) { - const value = this._heapValueById.get(ref2); + release(ref) { + const value = this._heapValueById.get(ref); const entry = this._heapEntryByValue.get(value); entry.rc--; if (entry.rc != 0) return; this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref2); + this._heapValueById.delete(ref); } - referenceHeap(ref2) { - const value = this._heapValueById.get(ref2); + referenceHeap(ref) { + const value = this._heapValueById.get(ref); if (value === void 0) { - throw new ReferenceError("Attempted to read invalid reference " + ref2); + throw new ReferenceError("Attempted to read invalid reference " + ref); } return value; } -} +}; diff --git a/Sources/JavaScriptKit/Runtime/types.js b/Sources/JavaScriptKit/Runtime/types.js index 32ed3f23e..da447fc4d 100644 --- a/Sources/JavaScriptKit/Runtime/types.js +++ b/Sources/JavaScriptKit/Runtime/types.js @@ -1,7 +1,32 @@ -export var LibraryFeatures = /* @__PURE__ */ ((LibraryFeatures2) => { +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/types.ts +var types_exports = {}; +__export(types_exports, { + LibraryFeatures: () => LibraryFeatures, + assertNever: () => assertNever +}); +module.exports = __toCommonJS(types_exports); +var LibraryFeatures = /* @__PURE__ */ ((LibraryFeatures2) => { LibraryFeatures2[LibraryFeatures2["WeakRefs"] = 1] = "WeakRefs"; return LibraryFeatures2; })(LibraryFeatures || {}); -export function assertNever(x, message) { +function assertNever(x, message) { throw new Error(message); } From 9eeece48977313153334b7fd7f9f279cdc8ef39b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 10:38:48 +0100 Subject: [PATCH 05/12] Use Rollup to generate resources --- Sources/JavaScriptKit/Runtime/closure-heap.js | 37 - Sources/JavaScriptKit/Runtime/find-global.js | 34 - Sources/JavaScriptKit/Runtime/index.js | 795 +++++++++--------- Sources/JavaScriptKit/Runtime/index.mjs | 409 +++++++++ Sources/JavaScriptKit/Runtime/js-value.js | 132 --- Sources/JavaScriptKit/Runtime/memory.js | 96 --- Sources/JavaScriptKit/Runtime/object-heap.js | 74 -- Sources/JavaScriptKit/Runtime/types.js | 32 - 8 files changed, 807 insertions(+), 802 deletions(-) delete mode 100644 Sources/JavaScriptKit/Runtime/closure-heap.js delete mode 100644 Sources/JavaScriptKit/Runtime/find-global.js create mode 100644 Sources/JavaScriptKit/Runtime/index.mjs delete mode 100644 Sources/JavaScriptKit/Runtime/js-value.js delete mode 100644 Sources/JavaScriptKit/Runtime/memory.js delete mode 100644 Sources/JavaScriptKit/Runtime/object-heap.js delete mode 100644 Sources/JavaScriptKit/Runtime/types.js diff --git a/Sources/JavaScriptKit/Runtime/closure-heap.js b/Sources/JavaScriptKit/Runtime/closure-heap.js deleted file mode 100644 index daec31324..000000000 --- a/Sources/JavaScriptKit/Runtime/closure-heap.js +++ /dev/null @@ -1,37 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/closure-heap.ts -var closure_heap_exports = {}; -__export(closure_heap_exports, { - SwiftClosureDeallocator: () => SwiftClosureDeallocator -}); -module.exports = __toCommonJS(closure_heap_exports); -var SwiftClosureDeallocator = class { - constructor(exports) { - 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, func_ref) { - this.functionRegistry.register(func, func_ref); - } -}; diff --git a/Sources/JavaScriptKit/Runtime/find-global.js b/Sources/JavaScriptKit/Runtime/find-global.js deleted file mode 100644 index f50f7c98b..000000000 --- a/Sources/JavaScriptKit/Runtime/find-global.js +++ /dev/null @@ -1,34 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/find-global.ts -var find_global_exports = {}; -__export(find_global_exports, { - globalVariable: () => globalVariable -}); -module.exports = __toCommonJS(find_global_exports); -var globalVariable; -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/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index db6b19b76..68f1c433c 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -1,418 +1,419 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JavaScriptKit = {})); +})(this, (function (exports) { 'use strict'; -// src/index.ts -var src_exports = {}; -__export(src_exports, { - SwiftRuntime: () => SwiftRuntime -}); -module.exports = __toCommonJS(src_exports); + /// Memory lifetime of closures in Swift are managed by Swift side + class SwiftClosureDeallocator { + constructor(exports) { + 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, func_ref) { + this.functionRegistry.register(func, func_ref); + } + } -// src/closure-heap.ts -var SwiftClosureDeallocator = class { - constructor(exports) { - 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."); + function assertNever(x, message) { + throw new Error(message); } - this.functionRegistry = new FinalizationRegistry((id) => { - exports.swjs_free_host_function(id); - }); - } - track(func, func_ref) { - this.functionRegistry.register(func, func_ref); - } -}; -// src/types.ts -function assertNever(x, message) { - throw new Error(message); -} + const decode = (kind, payload1, payload2, memory) => { + switch (kind) { + case 0 /* Boolean */: + switch (payload1) { + case 0: + return false; + case 1: + return true; + } + case 2 /* Number */: + return payload2; + case 1 /* String */: + case 3 /* Object */: + case 6 /* Function */: + case 7 /* Symbol */: + case 8 /* BigInt */: + return memory.getObject(payload1); + case 4 /* Null */: + return null; + case 5 /* Undefined */: + return undefined; + default: + assertNever(kind, `JSValue Type kind "${kind}" is not supported`); + } + }; + // Note: + // `decodeValues` assumes that the size of RawJSValue is 16. + const decodeArray = (ptr, length, 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; + }; + const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); + return; + } + const writeRef = (kind) => { + memory.writeUint32(kind_ptr, exceptionBit | kind); + memory.writeUint32(payload1_ptr, memory.retain(value)); + }; + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); + memory.writeUint32(payload1_ptr, value ? 1 : 0); + break; + } + case "number": { + memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); + memory.writeFloat64(payload2_ptr, value); + break; + } + case "string": { + writeRef(1 /* String */); + break; + } + case "undefined": { + memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); + break; + } + case "object": { + writeRef(3 /* Object */); + break; + } + case "function": { + writeRef(6 /* Function */); + break; + } + case "symbol": { + writeRef(7 /* Symbol */); + break; + } + case "bigint": { + writeRef(8 /* BigInt */); + break; + } + default: + assertNever(type, `Type "${type}" is not supported yet`); + } + }; -// src/js-value.ts -var decode = (kind, payload1, payload2, memory) => { - switch (kind) { - case 0 /* Boolean */: - switch (payload1) { - case 0: - return false; - case 1: - return true; - } - case 2 /* Number */: - return payload2; - case 1 /* String */: - case 3 /* Object */: - case 6 /* Function */: - case 7 /* Symbol */: - case 8 /* BigInt */: - return memory.getObject(payload1); - case 4 /* Null */: - return null; - case 5 /* Undefined */: - return void 0; - default: - assertNever(kind, `JSValue Type kind "${kind}" is not supported`); - } -}; -var decodeArray = (ptr, length, 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; -}; -var write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { - const exceptionBit = (is_exception ? 1 : 0) << 31; - if (value === null) { - memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); - return; - } - const writeRef = (kind) => { - memory.writeUint32(kind_ptr, exceptionBit | kind); - memory.writeUint32(payload1_ptr, memory.retain(value)); - }; - const type = typeof value; - switch (type) { - case "boolean": { - memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); - memory.writeUint32(payload1_ptr, value ? 1 : 0); - break; - } - case "number": { - memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); - memory.writeFloat64(payload2_ptr, value); - break; - } - case "string": { - writeRef(1 /* String */); - break; + let globalVariable; + if (typeof globalThis !== "undefined") { + globalVariable = globalThis; } - case "undefined": { - memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); - break; + else if (typeof window !== "undefined") { + globalVariable = window; } - case "object": { - writeRef(3 /* Object */); - break; + else if (typeof global !== "undefined") { + globalVariable = global; } - case "function": { - writeRef(6 /* Function */); - break; + else if (typeof self !== "undefined") { + globalVariable = self; } - case "symbol": { - writeRef(7 /* Symbol */); - break; - } - case "bigint": { - writeRef(8 /* BigInt */); - break; - } - default: - assertNever(type, `Type "${type}" is not supported yet`); - } -}; -// src/find-global.ts -var globalVariable; -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; -} - -// src/object-heap.ts -var SwiftRuntimeHeap = class { - constructor() { - this._heapValueById = /* @__PURE__ */ new Map(); - this._heapValueById.set(0, globalVariable); - this._heapEntryByValue = /* @__PURE__ */ new Map(); - this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); - this._heapNextKey = 1; - } - retain(value) { - const entry = this._heapEntryByValue.get(value); - if (entry) { - entry.rc++; - return entry.id; - } - const id = this._heapNextKey++; - this._heapValueById.set(id, value); - this._heapEntryByValue.set(value, { id, rc: 1 }); - return id; - } - release(ref2) { - const value = this._heapValueById.get(ref2); - const entry = this._heapEntryByValue.get(value); - entry.rc--; - if (entry.rc != 0) - return; - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref2); - } - referenceHeap(ref2) { - const value = this._heapValueById.get(ref2); - if (value === void 0) { - throw new ReferenceError("Attempted to read invalid reference " + ref2); + class SwiftRuntimeHeap { + 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) { + const entry = this._heapEntryByValue.get(value); + if (entry) { + entry.rc++; + return entry.id; + } + const id = this._heapNextKey++; + this._heapValueById.set(id, value); + this._heapEntryByValue.set(value, { id: id, rc: 1 }); + return id; + } + release(ref) { + const value = this._heapValueById.get(ref); + const entry = this._heapEntryByValue.get(value); + entry.rc--; + if (entry.rc != 0) + return; + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref); + } + referenceHeap(ref) { + const value = this._heapValueById.get(ref); + if (value === undefined) { + throw new ReferenceError("Attempted to read invalid reference " + ref); + } + return value; + } } - return value; - } -}; -// src/memory.ts -var Memory = class { - constructor(exports) { - this.heap = new SwiftRuntimeHeap(); - this.retain = (value) => this.heap.retain(value); - this.getObject = (ref2) => this.heap.referenceHeap(ref2); - this.release = (ref2) => this.heap.release(ref2); - this.bytes = () => new Uint8Array(this.rawMemory.buffer); - this.dataView = () => new DataView(this.rawMemory.buffer); - this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr); - this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true); - this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true); - this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true); - this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true); - this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true); - this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true); - this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true); - this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); - this.rawMemory = exports.memory; - } -}; + class Memory { + constructor(exports) { + this.heap = new SwiftRuntimeHeap(); + this.retain = (value) => this.heap.retain(value); + this.getObject = (ref) => this.heap.referenceHeap(ref); + this.release = (ref) => this.heap.release(ref); + this.bytes = () => new Uint8Array(this.rawMemory.buffer); + this.dataView = () => new DataView(this.rawMemory.buffer); + this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr); + this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true); + this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true); + this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true); + this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true); + this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true); + this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true); + this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true); + this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); + this.rawMemory = exports.memory; + } + } -// src/index.ts -var SwiftRuntime = class { - constructor() { - this.version = 707; - this.textDecoder = new TextDecoder("utf-8"); - this.textEncoder = new TextEncoder(); - this.importObjects = () => this.wasmImports; - this.wasmImports = { - swjs_set_prop: (ref2, name, kind, payload1, payload2) => { - const obj = this.memory.getObject(ref2); - const key = this.memory.getObject(name); - const value = decode(kind, payload1, payload2, this.memory); - obj[key] = value; - }, - swjs_get_prop: (ref2, name, kind_ptr, payload1_ptr, payload2_ptr) => { - const obj = this.memory.getObject(ref2); - const key = this.memory.getObject(name); - const result = obj[key]; - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); - }, - swjs_set_subscript: (ref2, index, kind, payload1, payload2) => { - const obj = this.memory.getObject(ref2); - const value = decode(kind, payload1, payload2, this.memory); - obj[index] = value; - }, - swjs_get_subscript: (ref2, index, kind_ptr, payload1_ptr, payload2_ptr) => { - const obj = this.memory.getObject(ref2); - const result = obj[index]; - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); - }, - swjs_encode_string: (ref2, bytes_ptr_result) => { - const bytes = this.textEncoder.encode(this.memory.getObject(ref2)); - const bytes_ptr = this.memory.retain(bytes); - this.memory.writeUint32(bytes_ptr_result, bytes_ptr); - return bytes.length; - }, - swjs_decode_string: (bytes_ptr, length) => { - 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: (ref2, buffer) => { - const bytes = this.memory.getObject(ref2); - this.memory.writeBytes(buffer, bytes); - }, - swjs_call_function: (ref2, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref2); - let result; - try { - const args = decodeArray(argv, argc, this.memory); - result = func(...args); - } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + class SwiftRuntime { + constructor() { + this.version = 707; + this.textDecoder = new TextDecoder("utf-8"); + this.textEncoder = new TextEncoder(); // Only support utf-8 + /** @deprecated Use `wasmImports` instead */ + this.importObjects = () => this.wasmImports; + this.wasmImports = { + swjs_set_prop: (ref, name, kind, payload1, payload2) => { + const obj = this.memory.getObject(ref); + const key = this.memory.getObject(name); + const value = decode(kind, payload1, payload2, this.memory); + obj[key] = value; + }, + swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(ref); + const key = this.memory.getObject(name); + const result = obj[key]; + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_set_subscript: (ref, index, kind, payload1, payload2) => { + const obj = this.memory.getObject(ref); + const value = decode(kind, payload1, payload2, this.memory); + obj[index] = value; + }, + swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(ref); + const result = obj[index]; + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_encode_string: (ref, bytes_ptr_result) => { + 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, length) => { + 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, buffer) => { + const bytes = this.memory.getObject(ref); + this.memory.writeBytes(buffer, bytes); + }, + swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const func = this.memory.getObject(ref); + let result; + try { + const args = decodeArray(argv, argc, this.memory); + result = func(...args); + } + catch (error) { + write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + return; + } + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_call_function_no_catch: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const func = this.memory.getObject(ref); + let isException = true; + try { + const args = decodeArray(argv, argc, this.memory); + const result = func(...args); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + isException = false; + } + finally { + if (isException) { + write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + } + } + }, + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let result; + try { + const args = decodeArray(argv, argc, this.memory); + result = func.apply(obj, args); + } + catch (error) { + write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + return; + } + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let isException = true; + try { + const args = decodeArray(argv, argc, this.memory); + const result = func.apply(obj, args); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + isException = false; + } + finally { + if (isException) { + write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + } + } + }, + swjs_call_new: (ref, argv, argc) => { + const constructor = this.memory.getObject(ref); + const args = decodeArray(argv, argc, this.memory); + const instance = new constructor(...args); + return this.memory.retain(instance); + }, + swjs_call_throwing_new: (ref, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => { + const constructor = this.memory.getObject(ref); + let result; + try { + const args = decodeArray(argv, argc, this.memory); + result = new constructor(...args); + } + catch (error) { + write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory); + return -1; + } + write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory); + return this.memory.retain(result); + }, + swjs_instanceof: (obj_ref, constructor_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) => { + var _a; + const func = (...args) => this.callHostFunction(host_func_id, args); + const func_ref = this.memory.retain(func); + (_a = this.closureDeallocator) === null || _a === void 0 ? void 0 : _a.track(func, func_ref); + return func_ref; + }, + swjs_create_typed_array: (constructor_ref, elementsPtr, length) => { + const ArrayType = 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, buffer) => { + const typedArray = this.memory.getObject(ref); + const bytes = new Uint8Array(typedArray.buffer); + this.memory.writeBytes(buffer, bytes); + }, + swjs_release: (ref) => { + this.memory.release(ref); + }, + swjs_i64_to_bigint: (value, signed) => { + return this.memory.retain(signed ? value : BigInt.asUintN(64, value)); + }, + swjs_bigint_to_i64: (ref, signed) => { + const object = this.memory.getObject(ref); + if (typeof object !== "bigint") { + throw new Error(`Expected a BigInt, but got ${typeof object}`); + } + if (signed) { + return object; + } + else { + if (object < BigInt(0)) { + return BigInt(0); + } + return BigInt.asIntN(64, object); + } + }, + }; + this._instance = null; + this._memory = null; + this._closureDeallocator = null; } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); - }, - swjs_call_function_no_catch: (ref2, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref2); - let isException = true; - try { - const args = decodeArray(argv, argc, this.memory); - const result = func(...args); - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); - isException = false; - } finally { - if (isException) { - write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - } + setInstance(instance) { + this._instance = instance; + if (this.exports.swjs_library_version() != this.version) { + throw new Error(`The versions of JavaScriptKit are incompatible. + WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`); + } } - }, - swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { - const obj = this.memory.getObject(obj_ref); - const func = this.memory.getObject(func_ref); - let result; - try { - const args = decodeArray(argv, argc, this.memory); - result = func.apply(obj, args); - } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + get instance() { + if (!this._instance) + throw new Error("WebAssembly instance is not set yet"); + return this._instance; } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); - }, - swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { - const obj = this.memory.getObject(obj_ref); - const func = this.memory.getObject(func_ref); - let isException = true; - try { - const args = decodeArray(argv, argc, this.memory); - const result = func.apply(obj, args); - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); - isException = false; - } finally { - if (isException) { - write(void 0, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - } + get exports() { + return this.instance.exports; } - }, - swjs_call_new: (ref2, argv, argc) => { - const constructor = this.memory.getObject(ref2); - const args = decodeArray(argv, argc, this.memory); - const instance = new constructor(...args); - return this.memory.retain(instance); - }, - swjs_call_throwing_new: (ref2, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => { - const constructor = this.memory.getObject(ref2); - let result; - try { - const args = decodeArray(argv, argc, this.memory); - result = new constructor(...args); - } catch (error) { - write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory); - return -1; + get memory() { + if (!this._memory) { + this._memory = new Memory(this.instance.exports); + } + return this._memory; } - write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory); - return this.memory.retain(result); - }, - swjs_instanceof: (obj_ref, constructor_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) => { - var _a; - const func = (...args) => this.callHostFunction(host_func_id, args); - const func_ref = this.memory.retain(func); - (_a = this.closureDeallocator) == null ? void 0 : _a.track(func, func_ref); - return func_ref; - }, - swjs_create_typed_array: (constructor_ref, elementsPtr, length) => { - const ArrayType = this.memory.getObject(constructor_ref); - const array = new ArrayType(this.memory.rawMemory.buffer, elementsPtr, length); - return this.memory.retain(array.slice()); - }, - swjs_load_typed_array: (ref2, buffer) => { - const typedArray = this.memory.getObject(ref2); - const bytes = new Uint8Array(typedArray.buffer); - this.memory.writeBytes(buffer, bytes); - }, - swjs_release: (ref2) => { - this.memory.release(ref2); - }, - swjs_i64_to_bigint: (value, signed) => { - return this.memory.retain(signed ? value : BigInt.asUintN(64, value)); - }, - swjs_bigint_to_i64: (ref2, signed) => { - const object = this.memory.getObject(ref2); - if (typeof object !== "bigint") { - throw new Error(`Expected a BigInt, but got ${typeof object}`); + get closureDeallocator() { + if (this._closureDeallocator) + return this._closureDeallocator; + const features = this.exports.swjs_library_features(); + const librarySupportsWeakRef = (features & 1 /* WeakRefs */) != 0; + if (librarySupportsWeakRef) { + this._closureDeallocator = new SwiftClosureDeallocator(this.exports); + } + return this._closureDeallocator; } - if (signed) { - return object; - } else { - if (object < BigInt(0)) { - return BigInt(0); - } - return BigInt.asIntN(64, object); + callHostFunction(host_func_id, args) { + 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; + write(argument, base, base + 4, base + 8, false, this.memory); + } + let output; + // This ref is released by the swjs_call_host_function implementation + const callback_func_ref = this.memory.retain((result) => { + 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; } - } - }; - this._instance = null; - this._memory = null; - this._closureDeallocator = null; - } - setInstance(instance) { - this._instance = instance; - if (this.exports.swjs_library_version() != this.version) { - throw new Error(`The versions of JavaScriptKit are incompatible. - WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`); - } - } - get instance() { - if (!this._instance) - throw new Error("WebAssembly instance is not set yet"); - return this._instance; - } - get exports() { - return this.instance.exports; - } - get memory() { - if (!this._memory) { - this._memory = new Memory(this.instance.exports); - } - return this._memory; - } - get closureDeallocator() { - if (this._closureDeallocator) - return this._closureDeallocator; - const features = this.exports.swjs_library_features(); - const librarySupportsWeakRef = (features & 1 /* WeakRefs */) != 0; - if (librarySupportsWeakRef) { - this._closureDeallocator = new SwiftClosureDeallocator(this.exports); - } - return this._closureDeallocator; - } - callHostFunction(host_func_id, args) { - 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; - write(argument, base, base + 4, base + 8, false, this.memory); } - let output; - const callback_func_ref = this.memory.retain((result) => { - 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; - } -}; + + exports.SwiftRuntime = SwiftRuntime; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs new file mode 100644 index 000000000..1874a8fee --- /dev/null +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -0,0 +1,409 @@ +/// Memory lifetime of closures in Swift are managed by Swift side +class SwiftClosureDeallocator { + constructor(exports) { + 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, func_ref) { + this.functionRegistry.register(func, func_ref); + } +} + +function assertNever(x, message) { + throw new Error(message); +} + +const decode = (kind, payload1, payload2, memory) => { + switch (kind) { + case 0 /* Boolean */: + switch (payload1) { + case 0: + return false; + case 1: + return true; + } + case 2 /* Number */: + return payload2; + case 1 /* String */: + case 3 /* Object */: + case 6 /* Function */: + case 7 /* Symbol */: + case 8 /* BigInt */: + return memory.getObject(payload1); + case 4 /* Null */: + return null; + case 5 /* Undefined */: + return undefined; + default: + assertNever(kind, `JSValue Type kind "${kind}" is not supported`); + } +}; +// Note: +// `decodeValues` assumes that the size of RawJSValue is 16. +const decodeArray = (ptr, length, 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; +}; +const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); + return; + } + const writeRef = (kind) => { + memory.writeUint32(kind_ptr, exceptionBit | kind); + memory.writeUint32(payload1_ptr, memory.retain(value)); + }; + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); + memory.writeUint32(payload1_ptr, value ? 1 : 0); + break; + } + case "number": { + memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); + memory.writeFloat64(payload2_ptr, value); + break; + } + case "string": { + writeRef(1 /* String */); + break; + } + case "undefined": { + memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); + break; + } + case "object": { + writeRef(3 /* Object */); + break; + } + case "function": { + writeRef(6 /* Function */); + break; + } + case "symbol": { + writeRef(7 /* Symbol */); + break; + } + case "bigint": { + writeRef(8 /* BigInt */); + break; + } + default: + assertNever(type, `Type "${type}" is not supported yet`); + } +}; + +let globalVariable; +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; +} + +class SwiftRuntimeHeap { + 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) { + const entry = this._heapEntryByValue.get(value); + if (entry) { + entry.rc++; + return entry.id; + } + const id = this._heapNextKey++; + this._heapValueById.set(id, value); + this._heapEntryByValue.set(value, { id: id, rc: 1 }); + return id; + } + release(ref) { + const value = this._heapValueById.get(ref); + const entry = this._heapEntryByValue.get(value); + entry.rc--; + if (entry.rc != 0) + return; + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref); + } + referenceHeap(ref) { + const value = this._heapValueById.get(ref); + if (value === undefined) { + throw new ReferenceError("Attempted to read invalid reference " + ref); + } + return value; + } +} + +class Memory { + constructor(exports) { + this.heap = new SwiftRuntimeHeap(); + this.retain = (value) => this.heap.retain(value); + this.getObject = (ref) => this.heap.referenceHeap(ref); + this.release = (ref) => this.heap.release(ref); + this.bytes = () => new Uint8Array(this.rawMemory.buffer); + this.dataView = () => new DataView(this.rawMemory.buffer); + this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr); + this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true); + this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true); + this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true); + this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true); + this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true); + this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true); + this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true); + this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); + this.rawMemory = exports.memory; + } +} + +class SwiftRuntime { + constructor() { + this.version = 707; + this.textDecoder = new TextDecoder("utf-8"); + this.textEncoder = new TextEncoder(); // Only support utf-8 + /** @deprecated Use `wasmImports` instead */ + this.importObjects = () => this.wasmImports; + this.wasmImports = { + swjs_set_prop: (ref, name, kind, payload1, payload2) => { + const obj = this.memory.getObject(ref); + const key = this.memory.getObject(name); + const value = decode(kind, payload1, payload2, this.memory); + obj[key] = value; + }, + swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(ref); + const key = this.memory.getObject(name); + const result = obj[key]; + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_set_subscript: (ref, index, kind, payload1, payload2) => { + const obj = this.memory.getObject(ref); + const value = decode(kind, payload1, payload2, this.memory); + obj[index] = value; + }, + swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(ref); + const result = obj[index]; + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_encode_string: (ref, bytes_ptr_result) => { + 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, length) => { + 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, buffer) => { + const bytes = this.memory.getObject(ref); + this.memory.writeBytes(buffer, bytes); + }, + swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const func = this.memory.getObject(ref); + let result; + try { + const args = decodeArray(argv, argc, this.memory); + result = func(...args); + } + catch (error) { + write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + return; + } + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_call_function_no_catch: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const func = this.memory.getObject(ref); + let isException = true; + try { + const args = decodeArray(argv, argc, this.memory); + const result = func(...args); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + isException = false; + } + finally { + if (isException) { + write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + } + } + }, + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let result; + try { + const args = decodeArray(argv, argc, this.memory); + result = func.apply(obj, args); + } + catch (error) { + write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + return; + } + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + }, + swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + const obj = this.memory.getObject(obj_ref); + const func = this.memory.getObject(func_ref); + let isException = true; + try { + const args = decodeArray(argv, argc, this.memory); + const result = func.apply(obj, args); + write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + isException = false; + } + finally { + if (isException) { + write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); + } + } + }, + swjs_call_new: (ref, argv, argc) => { + const constructor = this.memory.getObject(ref); + const args = decodeArray(argv, argc, this.memory); + const instance = new constructor(...args); + return this.memory.retain(instance); + }, + swjs_call_throwing_new: (ref, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => { + const constructor = this.memory.getObject(ref); + let result; + try { + const args = decodeArray(argv, argc, this.memory); + result = new constructor(...args); + } + catch (error) { + write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory); + return -1; + } + write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory); + return this.memory.retain(result); + }, + swjs_instanceof: (obj_ref, constructor_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) => { + var _a; + const func = (...args) => this.callHostFunction(host_func_id, args); + const func_ref = this.memory.retain(func); + (_a = this.closureDeallocator) === null || _a === void 0 ? void 0 : _a.track(func, func_ref); + return func_ref; + }, + swjs_create_typed_array: (constructor_ref, elementsPtr, length) => { + const ArrayType = 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, buffer) => { + const typedArray = this.memory.getObject(ref); + const bytes = new Uint8Array(typedArray.buffer); + this.memory.writeBytes(buffer, bytes); + }, + swjs_release: (ref) => { + this.memory.release(ref); + }, + swjs_i64_to_bigint: (value, signed) => { + return this.memory.retain(signed ? value : BigInt.asUintN(64, value)); + }, + swjs_bigint_to_i64: (ref, signed) => { + const object = this.memory.getObject(ref); + if (typeof object !== "bigint") { + throw new Error(`Expected a BigInt, but got ${typeof object}`); + } + if (signed) { + return object; + } + else { + if (object < BigInt(0)) { + return BigInt(0); + } + return BigInt.asIntN(64, object); + } + }, + }; + this._instance = null; + this._memory = null; + this._closureDeallocator = null; + } + setInstance(instance) { + this._instance = instance; + if (this.exports.swjs_library_version() != this.version) { + throw new Error(`The versions of JavaScriptKit are incompatible. + WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`); + } + } + get instance() { + if (!this._instance) + throw new Error("WebAssembly instance is not set yet"); + return this._instance; + } + get exports() { + return this.instance.exports; + } + get memory() { + if (!this._memory) { + this._memory = new Memory(this.instance.exports); + } + return this._memory; + } + get closureDeallocator() { + if (this._closureDeallocator) + return this._closureDeallocator; + const features = this.exports.swjs_library_features(); + const librarySupportsWeakRef = (features & 1 /* WeakRefs */) != 0; + if (librarySupportsWeakRef) { + this._closureDeallocator = new SwiftClosureDeallocator(this.exports); + } + return this._closureDeallocator; + } + callHostFunction(host_func_id, args) { + 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; + write(argument, base, base + 4, base + 8, false, this.memory); + } + let output; + // This ref is released by the swjs_call_host_function implementation + const callback_func_ref = this.memory.retain((result) => { + 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; + } +} + +export { SwiftRuntime }; diff --git a/Sources/JavaScriptKit/Runtime/js-value.js b/Sources/JavaScriptKit/Runtime/js-value.js deleted file mode 100644 index 0dc31a939..000000000 --- a/Sources/JavaScriptKit/Runtime/js-value.js +++ /dev/null @@ -1,132 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/js-value.ts -var js_value_exports = {}; -__export(js_value_exports, { - Kind: () => Kind, - decode: () => decode, - decodeArray: () => decodeArray, - write: () => write -}); -module.exports = __toCommonJS(js_value_exports); - -// src/types.ts -function assertNever(x, message) { - throw new Error(message); -} - -// src/js-value.ts -var Kind = /* @__PURE__ */ ((Kind2) => { - Kind2[Kind2["Boolean"] = 0] = "Boolean"; - Kind2[Kind2["String"] = 1] = "String"; - Kind2[Kind2["Number"] = 2] = "Number"; - Kind2[Kind2["Object"] = 3] = "Object"; - Kind2[Kind2["Null"] = 4] = "Null"; - Kind2[Kind2["Undefined"] = 5] = "Undefined"; - Kind2[Kind2["Function"] = 6] = "Function"; - Kind2[Kind2["Symbol"] = 7] = "Symbol"; - Kind2[Kind2["BigInt"] = 8] = "BigInt"; - return Kind2; -})(Kind || {}); -var decode = (kind, payload1, payload2, memory) => { - switch (kind) { - case 0 /* Boolean */: - switch (payload1) { - case 0: - return false; - case 1: - return true; - } - case 2 /* Number */: - return payload2; - case 1 /* String */: - case 3 /* Object */: - case 6 /* Function */: - case 7 /* Symbol */: - case 8 /* BigInt */: - return memory.getObject(payload1); - case 4 /* Null */: - return null; - case 5 /* Undefined */: - return void 0; - default: - assertNever(kind, `JSValue Type kind "${kind}" is not supported`); - } -}; -var decodeArray = (ptr, length, 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; -}; -var write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { - const exceptionBit = (is_exception ? 1 : 0) << 31; - if (value === null) { - memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */); - return; - } - const writeRef = (kind) => { - memory.writeUint32(kind_ptr, exceptionBit | kind); - memory.writeUint32(payload1_ptr, memory.retain(value)); - }; - const type = typeof value; - switch (type) { - case "boolean": { - memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); - memory.writeUint32(payload1_ptr, value ? 1 : 0); - break; - } - case "number": { - memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); - memory.writeFloat64(payload2_ptr, value); - break; - } - case "string": { - writeRef(1 /* String */); - break; - } - case "undefined": { - memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); - break; - } - case "object": { - writeRef(3 /* Object */); - break; - } - case "function": { - writeRef(6 /* Function */); - break; - } - case "symbol": { - writeRef(7 /* Symbol */); - break; - } - case "bigint": { - writeRef(8 /* BigInt */); - break; - } - default: - assertNever(type, `Type "${type}" is not supported yet`); - } -}; diff --git a/Sources/JavaScriptKit/Runtime/memory.js b/Sources/JavaScriptKit/Runtime/memory.js deleted file mode 100644 index fb73821a8..000000000 --- a/Sources/JavaScriptKit/Runtime/memory.js +++ /dev/null @@ -1,96 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/memory.ts -var memory_exports = {}; -__export(memory_exports, { - Memory: () => Memory -}); -module.exports = __toCommonJS(memory_exports); - -// src/find-global.ts -var globalVariable; -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; -} - -// src/object-heap.ts -var SwiftRuntimeHeap = class { - constructor() { - this._heapValueById = /* @__PURE__ */ new Map(); - this._heapValueById.set(0, globalVariable); - this._heapEntryByValue = /* @__PURE__ */ new Map(); - this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); - this._heapNextKey = 1; - } - retain(value) { - const entry = this._heapEntryByValue.get(value); - if (entry) { - entry.rc++; - return entry.id; - } - const id = this._heapNextKey++; - this._heapValueById.set(id, value); - this._heapEntryByValue.set(value, { id, rc: 1 }); - return id; - } - release(ref) { - const value = this._heapValueById.get(ref); - const entry = this._heapEntryByValue.get(value); - entry.rc--; - if (entry.rc != 0) - return; - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref); - } - referenceHeap(ref) { - const value = this._heapValueById.get(ref); - if (value === void 0) { - throw new ReferenceError("Attempted to read invalid reference " + ref); - } - return value; - } -}; - -// src/memory.ts -var Memory = class { - constructor(exports) { - this.heap = new SwiftRuntimeHeap(); - this.retain = (value) => this.heap.retain(value); - this.getObject = (ref) => this.heap.referenceHeap(ref); - this.release = (ref) => this.heap.release(ref); - this.bytes = () => new Uint8Array(this.rawMemory.buffer); - this.dataView = () => new DataView(this.rawMemory.buffer); - this.writeBytes = (ptr, bytes) => this.bytes().set(bytes, ptr); - this.readUint32 = (ptr) => this.dataView().getUint32(ptr, true); - this.readUint64 = (ptr) => this.dataView().getBigUint64(ptr, true); - this.readInt64 = (ptr) => this.dataView().getBigInt64(ptr, true); - this.readFloat64 = (ptr) => this.dataView().getFloat64(ptr, true); - this.writeUint32 = (ptr, value) => this.dataView().setUint32(ptr, value, true); - this.writeUint64 = (ptr, value) => this.dataView().setBigUint64(ptr, value, true); - this.writeInt64 = (ptr, value) => this.dataView().setBigInt64(ptr, value, true); - this.writeFloat64 = (ptr, value) => this.dataView().setFloat64(ptr, value, true); - this.rawMemory = exports.memory; - } -}; diff --git a/Sources/JavaScriptKit/Runtime/object-heap.js b/Sources/JavaScriptKit/Runtime/object-heap.js deleted file mode 100644 index b54274031..000000000 --- a/Sources/JavaScriptKit/Runtime/object-heap.js +++ /dev/null @@ -1,74 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/object-heap.ts -var object_heap_exports = {}; -__export(object_heap_exports, { - SwiftRuntimeHeap: () => SwiftRuntimeHeap -}); -module.exports = __toCommonJS(object_heap_exports); - -// src/find-global.ts -var globalVariable; -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; -} - -// src/object-heap.ts -var SwiftRuntimeHeap = class { - constructor() { - this._heapValueById = /* @__PURE__ */ new Map(); - this._heapValueById.set(0, globalVariable); - this._heapEntryByValue = /* @__PURE__ */ new Map(); - this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); - this._heapNextKey = 1; - } - retain(value) { - const entry = this._heapEntryByValue.get(value); - if (entry) { - entry.rc++; - return entry.id; - } - const id = this._heapNextKey++; - this._heapValueById.set(id, value); - this._heapEntryByValue.set(value, { id, rc: 1 }); - return id; - } - release(ref) { - const value = this._heapValueById.get(ref); - const entry = this._heapEntryByValue.get(value); - entry.rc--; - if (entry.rc != 0) - return; - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref); - } - referenceHeap(ref) { - const value = this._heapValueById.get(ref); - if (value === void 0) { - throw new ReferenceError("Attempted to read invalid reference " + ref); - } - return value; - } -}; diff --git a/Sources/JavaScriptKit/Runtime/types.js b/Sources/JavaScriptKit/Runtime/types.js deleted file mode 100644 index da447fc4d..000000000 --- a/Sources/JavaScriptKit/Runtime/types.js +++ /dev/null @@ -1,32 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/types.ts -var types_exports = {}; -__export(types_exports, { - LibraryFeatures: () => LibraryFeatures, - assertNever: () => assertNever -}); -module.exports = __toCommonJS(types_exports); -var LibraryFeatures = /* @__PURE__ */ ((LibraryFeatures2) => { - LibraryFeatures2[LibraryFeatures2["WeakRefs"] = 1] = "WeakRefs"; - return LibraryFeatures2; -})(LibraryFeatures || {}); -function assertNever(x, message) { - throw new Error(message); -} From 53f49a128df25ae784ea82dbef1e0660d42b0fb7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 12:45:20 +0100 Subject: [PATCH 06/12] Address PR feedback --- .gitattributes | 1 + .github/workflows/test.yml | 4 ++++ Makefile | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..1c692a819 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Sources/JavaScriptKit/Runtime linguist-generated diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 819089a19..859b160de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,6 +38,10 @@ jobs: SWIFT_VERSION=${{ matrix.entry.toolchain }} make bootstrap echo ${{ matrix.entry.toolchain }} > .swift-version make test + - name: Check if SwiftPM resources are stale + run: + git diff --exit-code Sources/JavaScriptKit/Runtime + native-build: # Check native build to make it easy to develop applications by Xcode name: Build for native target diff --git a/Makefile b/Makefile index b48af9e2b..ea62a164a 100644 --- a/Makefile +++ b/Makefile @@ -29,3 +29,8 @@ run_benchmark: .PHONY: perf-tester perf-tester: cd ci/perf-tester && npm ci + +.PHONY: regenerate_runtime_resources +regenerate_runtime_resources: + npm run build + cp Runtime/lib/index.{js,mjs} Sources/JavaScriptKit/Runtime \ No newline at end of file From e5d8d82f4fa1f56faae09f6950a030d6dbd2ddef Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 12:45:49 +0100 Subject: [PATCH 07/12] Add missing final newline --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ea62a164a..b3af74b3c 100644 --- a/Makefile +++ b/Makefile @@ -33,4 +33,4 @@ perf-tester: .PHONY: regenerate_runtime_resources regenerate_runtime_resources: npm run build - cp Runtime/lib/index.{js,mjs} Sources/JavaScriptKit/Runtime \ No newline at end of file + cp Runtime/lib/index.{js,mjs} Sources/JavaScriptKit/Runtime From 7e8a3ac8dbfba0c0e2f36ff3e1789b83c8df61fa Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 12:49:56 +0100 Subject: [PATCH 08/12] Rename `Makefile` job to avoid ambiguity --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b3af74b3c..55ab79a7b 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ run_benchmark: perf-tester: cd ci/perf-tester && npm ci -.PHONY: regenerate_runtime_resources -regenerate_runtime_resources: +.PHONY: regenerate_swiftpm_resources +regenerate_swiftpm_resources: npm run build cp Runtime/lib/index.{js,mjs} Sources/JavaScriptKit/Runtime From 23d9529e0b84e87de498bf90050e2c9a466ee72b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 13:17:14 +0100 Subject: [PATCH 09/12] Update .github/workflows/test.yml Co-authored-by: Yuta Saito --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 859b160de..1a677b037 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,6 +40,7 @@ jobs: make test - name: Check if SwiftPM resources are stale run: + make regenerate_swiftpm_resources git diff --exit-code Sources/JavaScriptKit/Runtime native-build: From f9e95fbbb0c090ab8a748d693c252e7320e949d8 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 13:35:17 +0100 Subject: [PATCH 10/12] Fix conjoined commands in `test.yml` --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1a677b037..e2d2e7d18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,7 +39,7 @@ jobs: echo ${{ matrix.entry.toolchain }} > .swift-version make test - name: Check if SwiftPM resources are stale - run: + run: | make regenerate_swiftpm_resources git diff --exit-code Sources/JavaScriptKit/Runtime From d651be43c184ee399afdb67ad29e857c13edb980 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 13:42:44 +0100 Subject: [PATCH 11/12] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 55ab79a7b..44d3de624 100644 --- a/Makefile +++ b/Makefile @@ -33,4 +33,4 @@ perf-tester: .PHONY: regenerate_swiftpm_resources regenerate_swiftpm_resources: npm run build - cp Runtime/lib/index.{js,mjs} Sources/JavaScriptKit/Runtime + cp Runtime/lib/index.js Runtime/lib/index.mjs Sources/JavaScriptKit/Runtime From c95b3c687bffcfd642632eca83154b668ce30315 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 12 May 2022 14:14:34 +0100 Subject: [PATCH 12/12] Update .gitattributes Co-authored-by: Yuta Saito --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 1c692a819..95204dad7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -Sources/JavaScriptKit/Runtime linguist-generated +Sources/JavaScriptKit/Runtime/** linguist-generated