diff --git a/IntegrationTests/Makefile b/IntegrationTests/Makefile index 312977482..57b99c8da 100644 --- a/IntegrationTests/Makefile +++ b/IntegrationTests/Makefile @@ -1,7 +1,8 @@ CONFIGURATION ?= debug SWIFT_BUILD_FLAGS ?= +NODEJS_FLAGS ?= -NODEJS = node --experimental-wasi-unstable-preview1 +NODEJS = node --experimental-wasi-unstable-preview1 $(NODEJS_FLAGS) FORCE: TestSuites/.build/$(CONFIGURATION)/%.wasm: FORCE diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 8f90776f1..a9da3eb9f 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -84,21 +84,15 @@ export class SwiftRuntime { ) { const argc = args.length; const argv = this.exports.swjs_prepare_host_function_call(argc); + const memory = this.memory; 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 - ); + JSValue.write(argument, base, base + 4, base + 8, false, memory); } let output: any; // This ref is released by the swjs_call_host_function implementation - const callback_func_ref = this.memory.retain((result: any) => { + const callback_func_ref = memory.retain((result: any) => { output = result; }); const alreadyReleased = this.exports.swjs_call_host_function( @@ -127,28 +121,28 @@ export class SwiftRuntime { payload1: number, payload2: number ) => { - const obj = this.memory.getObject(ref); - const key = this.memory.getObject(name); - const value = JSValue.decode(kind, payload1, payload2, this.memory); + const memory = this.memory; + const obj = memory.getObject(ref); + const key = memory.getObject(name); + const value = JSValue.decode(kind, payload1, payload2, memory); obj[key] = value; }, swjs_get_prop: ( ref: ref, name: ref, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { - const obj = this.memory.getObject(ref); - const key = this.memory.getObject(name); + const memory = this.memory; + const obj = memory.getObject(ref); + const key = memory.getObject(name); const result = obj[key]; - JSValue.write( + return JSValue.writeAndReturnKindBits( result, - kind_ptr, payload1_ptr, payload2_ptr, false, - this.memory + memory ); }, @@ -159,22 +153,21 @@ export class SwiftRuntime { payload1: number, payload2: number ) => { - const obj = this.memory.getObject(ref); - const value = JSValue.decode(kind, payload1, payload2, this.memory); + const memory = this.memory; + const obj = memory.getObject(ref); + const value = JSValue.decode(kind, payload1, payload2, memory); obj[index] = value; }, swjs_get_subscript: ( ref: ref, index: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { const obj = this.memory.getObject(ref); const result = obj[index]; - JSValue.write( + return JSValue.writeAndReturnKindBits( result, - kind_ptr, payload1_ptr, payload2_ptr, false, @@ -183,50 +176,50 @@ export class SwiftRuntime { }, swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => { - const bytes = this.textEncoder.encode(this.memory.getObject(ref)); - const bytes_ptr = this.memory.retain(bytes); - this.memory.writeUint32(bytes_ptr_result, bytes_ptr); + const memory = this.memory; + const bytes = this.textEncoder.encode(memory.getObject(ref)); + const bytes_ptr = memory.retain(bytes); + memory.writeUint32(bytes_ptr_result, bytes_ptr); return bytes.length; }, swjs_decode_string: (bytes_ptr: pointer, length: number) => { - const bytes = this.memory + const memory = this.memory; + const bytes = memory .bytes() .subarray(bytes_ptr, bytes_ptr + length); const string = this.textDecoder.decode(bytes); - return this.memory.retain(string); + return memory.retain(string); }, swjs_load_string: (ref: ref, buffer: pointer) => { - const bytes = this.memory.getObject(ref); - this.memory.writeBytes(buffer, bytes); + const memory = this.memory; + const bytes = memory.getObject(ref); + memory.writeBytes(buffer, bytes); }, swjs_call_function: ( ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { - const func = this.memory.getObject(ref); - let result: any; + const memory = this.memory; + const func = memory.getObject(ref); + let result = undefined; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = JSValue.decodeArray(argv, argc, memory); result = func(...args); } catch (error) { - JSValue.write( + return JSValue.writeAndReturnKindBits( error, - kind_ptr, payload1_ptr, payload2_ptr, true, this.memory ); - return; } - JSValue.write( + return JSValue.writeAndReturnKindBits( result, - kind_ptr, payload1_ptr, payload2_ptr, false, @@ -237,36 +230,20 @@ export class SwiftRuntime { ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { - const func = this.memory.getObject(ref); - 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( - undefined, - kind_ptr, - payload1_ptr, - payload2_ptr, - true, - this.memory - ); - } - } + const memory = this.memory; + const func = memory.getObject(ref); + const args = JSValue.decodeArray(argv, argc, memory); + const result = func(...args); + return JSValue.writeAndReturnKindBits( + result, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_call_function_with_this: ( @@ -274,30 +251,27 @@ export class SwiftRuntime { func_ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { - const obj = this.memory.getObject(obj_ref); - const func = this.memory.getObject(func_ref); + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const func = memory.getObject(func_ref); let result: any; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = JSValue.decodeArray(argv, argc, memory); result = func.apply(obj, args); } catch (error) { - JSValue.write( + return JSValue.writeAndReturnKindBits( error, - kind_ptr, payload1_ptr, payload2_ptr, true, this.memory ); - return; } - JSValue.write( + return JSValue.writeAndReturnKindBits( result, - kind_ptr, payload1_ptr, payload2_ptr, false, @@ -309,42 +283,28 @@ export class SwiftRuntime { func_ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { - const obj = this.memory.getObject(obj_ref); - const func = this.memory.getObject(func_ref); - let 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( - undefined, - kind_ptr, - payload1_ptr, - payload2_ptr, - true, - this.memory - ); - } - } + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const func = memory.getObject(func_ref); + let result = undefined; + const args = JSValue.decodeArray(argv, argc, memory); + result = func.apply(obj, args); + return JSValue.writeAndReturnKindBits( + result, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_call_new: (ref: ref, argv: pointer, argc: number) => { - const constructor = this.memory.getObject(ref); - const args = JSValue.decodeArray(argv, argc, this.memory); + const memory = this.memory; + const constructor = memory.getObject(ref); + const args = JSValue.decodeArray(argv, argc, memory); const instance = new constructor(...args); return this.memory.retain(instance); }, @@ -356,10 +316,11 @@ export class SwiftRuntime { exception_payload1_ptr: pointer, exception_payload2_ptr: pointer ) => { - const constructor = this.memory.getObject(ref); + let memory = this.memory; + const constructor = memory.getObject(ref); let result: any; try { - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = JSValue.decodeArray(argv, argc, memory); result = new constructor(...args); } catch (error) { JSValue.write( @@ -372,20 +333,22 @@ export class SwiftRuntime { ); return -1; } + memory = this.memory; JSValue.write( null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, - this.memory + memory ); - return this.memory.retain(result); + return memory.retain(result); }, swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => { - const obj = this.memory.getObject(obj_ref); - const constructor = this.memory.getObject(constructor_ref); + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const constructor = memory.getObject(constructor_ref); return obj instanceof constructor; }, @@ -419,9 +382,10 @@ export class SwiftRuntime { }, swjs_load_typed_array: (ref: ref, buffer: pointer) => { - const typedArray = this.memory.getObject(ref); + const memory = this.memory; + const typedArray = memory.getObject(ref); const bytes = new Uint8Array(typedArray.buffer); - this.memory.writeBytes(buffer, bytes); + memory.writeBytes(buffer, bytes); }, swjs_release: (ref: ref) => { diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index c3c24c3a9..9ff3d065e 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -1,5 +1,5 @@ import { Memory } from "./memory.js"; -import { assertNever, pointer } from "./types.js"; +import { assertNever, JavaScriptValueKindAndFlags, pointer } from "./types.js"; export const enum Kind { Boolean = 0, @@ -51,17 +51,29 @@ export const decode = ( // Note: // `decodeValues` assumes that the size of RawJSValue is 16. export const decodeArray = (ptr: pointer, length: number, memory: Memory) => { + // fast path for empty array + if (length === 0) { + return []; + } + let result = []; + // It's safe to hold DataView here because WebAssembly.Memory.buffer won't + // change within this function. + const view = memory.dataView(); 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); + const kind = view.getUint32(base, true); + const payload1 = view.getUint32(base + 4, true); + const payload2 = view.getFloat64(base + 8, true); result.push(decode(kind, payload1, payload2, memory)); } return result; }; +// A helper function to encode a RawJSValue into a pointers. +// Please prefer to use `writeAndReturnKindBits` to avoid unnecessary +// memory stores. +// This function should be used only when kind flag is stored in memory. export const write = ( value: any, kind_ptr: pointer, @@ -70,54 +82,57 @@ export const write = ( is_exception: boolean, memory: Memory ) => { + const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + memory.writeUint32(kind_ptr, kind); +}; + +export const writeAndReturnKindBits = ( + value: any, + payload1_ptr: pointer, + payload2_ptr: pointer, + is_exception: boolean, + memory: Memory +): JavaScriptValueKindAndFlags => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Null); - return; + return exceptionBit | Kind.Null; } const writeRef = (kind: Kind) => { - memory.writeUint32(kind_ptr, exceptionBit | kind); memory.writeUint32(payload1_ptr, memory.retain(value)); + return exceptionBit | kind; }; const type = typeof value; switch (type) { case "boolean": { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Boolean); memory.writeUint32(payload1_ptr, value ? 1 : 0); - break; + return exceptionBit | Kind.Boolean; } case "number": { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Number); memory.writeFloat64(payload2_ptr, value); - break; + return exceptionBit | Kind.Number; } case "string": { - writeRef(Kind.String); - break; + return writeRef(Kind.String); } case "undefined": { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Undefined); - break; + return exceptionBit | Kind.Undefined; } case "object": { - writeRef(Kind.Object); - break; + return writeRef(Kind.Object); } case "function": { - writeRef(Kind.Function); - break; + return writeRef(Kind.Function); } case "symbol": { - writeRef(Kind.Symbol); - break; + return writeRef(Kind.Symbol); } case "bigint": { - writeRef(Kind.BigInt); - break; + return writeRef(Kind.BigInt); } default: assertNever(type, `Type "${type}" is not supported yet`); } + throw new Error("Unreachable"); }; diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index a6e3dd1d2..ff20999ea 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -3,6 +3,8 @@ import * as JSValue from "./js-value.js"; export type ref = number; export type pointer = number; export type bool = number; +export type JavaScriptValueKind = number; +export type JavaScriptValueKindAndFlags = number; export interface ExportedFunctions { swjs_library_version(): number; @@ -30,10 +32,9 @@ export interface ImportedFunctions { swjs_get_prop( ref: number, name: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer - ): void; + ): JavaScriptValueKind; swjs_set_subscript( ref: number, index: number, @@ -44,10 +45,9 @@ export interface ImportedFunctions { swjs_get_subscript( ref: number, index: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer - ): void; + ): JavaScriptValueKind; swjs_encode_string(ref: number, bytes_ptr_result: pointer): number; swjs_decode_string(bytes_ptr: pointer, length: number): number; swjs_load_string(ref: number, buffer: pointer): void; @@ -55,36 +55,32 @@ export interface ImportedFunctions { ref: number, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer - ): void; + ): JavaScriptValueKindAndFlags; swjs_call_function_no_catch( ref: number, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer - ): void; + ): JavaScriptValueKindAndFlags; swjs_call_function_with_this( obj_ref: ref, func_ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer - ): void; + ): JavaScriptValueKindAndFlags; swjs_call_function_with_this_no_catch( obj_ref: ref, func_ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer - ): void; + ): JavaScriptValueKindAndFlags; swjs_call_new(ref: number, argv: pointer, argc: number): number; swjs_call_throwing_new( ref: number, diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift index 572e867b0..638672ca5 100644 --- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift +++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift @@ -253,6 +253,9 @@ extension JSValue { extension Array where Element == ConvertibleToJSValue { func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { + // fast path for empty array + guard self.count != 0 else { return body([]) } + func _withRawJSValues( _ values: [ConvertibleToJSValue], _ index: Int, _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 9cec5dad0..66c613402 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -17,16 +17,31 @@ public class JSFunction: JSObject { /// - arguments: Arguments to be passed to this function. /// - Returns: The result of this call. @discardableResult - public func callAsFunction(this: JSObject? = nil, arguments: [ConvertibleToJSValue]) -> JSValue { - invokeNonThrowingJSFunction(self, arguments: arguments, this: this).jsValue + public func callAsFunction(this: JSObject, arguments: [ConvertibleToJSValue]) -> JSValue { + invokeNonThrowingJSFunction(arguments: arguments, this: this).jsValue + } + + /// Call this function with given `arguments`. + /// - Parameters: + /// - arguments: Arguments to be passed to this function. + /// - Returns: The result of this call. + @discardableResult + public func callAsFunction(arguments: [ConvertibleToJSValue]) -> JSValue { + invokeNonThrowingJSFunction(arguments: arguments).jsValue } /// A variadic arguments version of `callAsFunction`. @discardableResult - public func callAsFunction(this: JSObject? = nil, _ arguments: ConvertibleToJSValue...) -> JSValue { + public func callAsFunction(this: JSObject, _ arguments: ConvertibleToJSValue...) -> JSValue { self(this: this, arguments: arguments) } + /// A variadic arguments version of `callAsFunction`. + @discardableResult + public func callAsFunction(_ arguments: ConvertibleToJSValue...) -> JSValue { + self(arguments: arguments) + } + /// Instantiate an object from this function as a constructor. /// /// Guaranteed to return an object because either: @@ -81,29 +96,44 @@ public class JSFunction: JSObject { override public var jsValue: JSValue { .function(self) } -} -func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue { - arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer in - let argv = bufferPointer.baseAddress - let argc = bufferPointer.count - var kindAndFlags = JavaScriptValueKindAndFlags() - var payload1 = JavaScriptPayload1() - var payload2 = JavaScriptPayload2() - if let thisId = this?.id { - _call_function_with_this_no_catch(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) - } else { - _call_function_no_catch( - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 + final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue]) -> RawJSValue { + let id = self.id + return arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count + var payload1 = JavaScriptPayload1() + var payload2 = JavaScriptPayload2() + let resultBitPattern = _call_function_no_catch( + id, argv, Int32(argc), + &payload1, &payload2 + ) + let kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self) + assert(!kindAndFlags.isException) + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return result + } + } + } + + final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue], this: JSObject) -> RawJSValue { + let id = self.id + return arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count + var payload1 = JavaScriptPayload1() + var payload2 = JavaScriptPayload2() + let resultBitPattern = _call_function_with_this_no_catch(this.id, + id, argv, Int32(argc), + &payload1, &payload2 ) + let kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self) + assert(!kindAndFlags.isException) + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return result } - assert(!kindAndFlags.isException) - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return result } } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift index a0dea3937..fbb932dd1 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift @@ -7,7 +7,7 @@ public class JSSymbol: JSObject { public init(_ description: JSString) { // can’t do `self =` so we have to get the ID manually - let result = invokeNonThrowingJSFunction(Symbol, arguments: [description], this: nil) + let result = Symbol.invokeNonThrowingJSFunction(arguments: [description]) precondition(result.kind == .symbol) super.init(id: UInt32(result.payload1)) } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift index 3e21f0e1b..705899000 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift @@ -64,6 +64,7 @@ public class JSThrowingFunction { } private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue { + let id = jsFunc.id let (result, isException) = arguments.withRawJSValues { rawValues in rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) in let argv = bufferPointer.baseAddress @@ -72,14 +73,17 @@ private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSV var payload1 = JavaScriptPayload1() var payload2 = JavaScriptPayload2() if let thisId = this?.id { - _call_function_with_this(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) + let resultBitPattern = _call_function_with_this( + thisId, id, argv, Int32(argc), + &payload1, &payload2 + ) + kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self) } else { - _call_function( - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 + let resultBitPattern = _call_function( + id, argv, Int32(argc), + &payload1, &payload2 ) + kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self) } let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) return (result.jsValue, kindAndFlags.isException) diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 973dfcb5d..58b28e079 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -196,9 +196,11 @@ extension JSValue: ExpressibleByNilLiteral { public func getJSValue(this: JSObject, name: JSString) -> JSValue { var rawValue = RawJSValue() - _get_prop(this.id, name.asInternalJSRef(), - &rawValue.kind, - &rawValue.payload1, &rawValue.payload2) + let rawBitPattern = _get_prop( + this.id, name.asInternalJSRef(), + &rawValue.payload1, &rawValue.payload2 + ) + rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self) return rawValue.jsValue } @@ -210,9 +212,11 @@ public func setJSValue(this: JSObject, name: JSString, value: JSValue) { public func getJSValue(this: JSObject, index: Int32) -> JSValue { var rawValue = RawJSValue() - _get_subscript(this.id, index, - &rawValue.kind, - &rawValue.payload1, &rawValue.payload2) + let rawBitPattern = _get_subscript( + this.id, index, + &rawValue.payload1, &rawValue.payload2 + ) + rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self) return rawValue.jsValue } @@ -226,9 +230,11 @@ public func setJSValue(this: JSObject, index: Int32, value: JSValue) { public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { var rawValue = RawJSValue() - _get_prop(this.id, symbol.id, - &rawValue.kind, - &rawValue.payload1, &rawValue.payload2) + let rawBitPattern = _get_prop( + this.id, symbol.id, + &rawValue.payload1, &rawValue.payload2 + ) + rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self) return rawValue.jsValue } diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index 43158fbab..02dc9382e 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -54,65 +54,72 @@ // Note: // `decodeValues` assumes that the size of RawJSValue is 16. const decodeArray = (ptr, length, memory) => { + // fast path for empty array + if (length === 0) { + return []; + } let result = []; + // It's safe to hold DataView here because WebAssembly.Memory.buffer won't + // change within this function. + const view = memory.dataView(); 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); + const kind = view.getUint32(base, true); + const payload1 = view.getUint32(base + 4, true); + const payload2 = view.getFloat64(base + 8, true); result.push(decode(kind, payload1, payload2, memory)); } return result; }; + // A helper function to encode a RawJSValue into a pointers. + // Please prefer to use `writeAndReturnKindBits` to avoid unnecessary + // memory stores. + // This function should be used only when kind flag is stored in memory. const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { + const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + memory.writeUint32(kind_ptr, kind); + }; + const writeAndReturnKindBits = (value, 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; + return exceptionBit | 4 /* Null */; } const writeRef = (kind) => { - memory.writeUint32(kind_ptr, exceptionBit | kind); memory.writeUint32(payload1_ptr, memory.retain(value)); + return exceptionBit | kind; }; const type = typeof value; switch (type) { case "boolean": { - memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); memory.writeUint32(payload1_ptr, value ? 1 : 0); - break; + return exceptionBit | 0 /* Boolean */; } case "number": { - memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); memory.writeFloat64(payload2_ptr, value); - break; + return exceptionBit | 2 /* Number */; } case "string": { - writeRef(1 /* String */); - break; + return writeRef(1 /* String */); } case "undefined": { - memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); - break; + return exceptionBit | 5 /* Undefined */; } case "object": { - writeRef(3 /* Object */); - break; + return writeRef(3 /* Object */); } case "function": { - writeRef(6 /* Function */); - break; + return writeRef(6 /* Function */); } case "symbol": { - writeRef(7 /* Symbol */); - break; + return writeRef(7 /* Symbol */); } case "bigint": { - writeRef(8 /* BigInt */); - break; + return writeRef(8 /* BigInt */); } default: assertNever(type, `Type "${type}" is not supported yet`); } + throw new Error("Unreachable"); }; let globalVariable; @@ -197,125 +204,120 @@ 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); + const memory = this.memory; + const obj = memory.getObject(ref); + const key = memory.getObject(name); + const value = decode(kind, payload1, payload2, 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); + swjs_get_prop: (ref, name, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const obj = memory.getObject(ref); + const key = memory.getObject(name); const result = obj[key]; - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory); }, swjs_set_subscript: (ref, index, kind, payload1, payload2) => { - const obj = this.memory.getObject(ref); - const value = decode(kind, payload1, payload2, this.memory); + const memory = this.memory; + const obj = memory.getObject(ref); + const value = decode(kind, payload1, payload2, memory); obj[index] = value; }, - swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => { + swjs_get_subscript: (ref, index, 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); + return writeAndReturnKindBits(result, 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); + const memory = this.memory; + const bytes = this.textEncoder.encode(memory.getObject(ref)); + const bytes_ptr = memory.retain(bytes); + memory.writeUint32(bytes_ptr_result, bytes_ptr); return bytes.length; }, swjs_decode_string: (bytes_ptr, length) => { - const bytes = this.memory + const memory = this.memory; + const bytes = memory .bytes() .subarray(bytes_ptr, bytes_ptr + length); const string = this.textDecoder.decode(bytes); - return this.memory.retain(string); + return memory.retain(string); }, swjs_load_string: (ref, buffer) => { - const bytes = this.memory.getObject(ref); - this.memory.writeBytes(buffer, bytes); + const memory = this.memory; + const bytes = memory.getObject(ref); + memory.writeBytes(buffer, bytes); }, - swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref); - let result; + swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const func = memory.getObject(ref); + let result = undefined; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func(...args); } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, 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_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const func = memory.getObject(ref); + const args = decodeArray(argv, argc, memory); + const result = func(...args); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, 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); + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const func = memory.getObject(func_ref); let result; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, 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_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const func = memory.getObject(func_ref); + let result = undefined; + const args = decodeArray(argv, argc, memory); + result = func.apply(obj, args); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_new: (ref, argv, argc) => { - const constructor = this.memory.getObject(ref); - const args = decodeArray(argv, argc, this.memory); + const memory = this.memory; + const constructor = memory.getObject(ref); + const args = decodeArray(argv, argc, 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 memory = this.memory; + const constructor = memory.getObject(ref); let result; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, 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); + memory = this.memory; + write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, memory); + return memory.retain(result); }, swjs_instanceof: (obj_ref, constructor_ref) => { - const obj = this.memory.getObject(obj_ref); - const constructor = this.memory.getObject(constructor_ref); + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const constructor = memory.getObject(constructor_ref); return obj instanceof constructor; }, swjs_create_function: (host_func_id, line, file) => { @@ -333,9 +335,10 @@ return this.memory.retain(array.slice()); }, swjs_load_typed_array: (ref, buffer) => { - const typedArray = this.memory.getObject(ref); + const memory = this.memory; + const typedArray = memory.getObject(ref); const bytes = new Uint8Array(typedArray.buffer); - this.memory.writeBytes(buffer, bytes); + memory.writeBytes(buffer, bytes); }, swjs_release: (ref) => { this.memory.release(ref); @@ -408,14 +411,15 @@ callHostFunction(host_func_id, line, file, args) { const argc = args.length; const argv = this.exports.swjs_prepare_host_function_call(argc); + const memory = this.memory; 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); + write(argument, base, base + 4, base + 8, false, memory); } let output; // This ref is released by the swjs_call_host_function implementation - const callback_func_ref = this.memory.retain((result) => { + const callback_func_ref = memory.retain((result) => { output = result; }); const alreadyReleased = this.exports.swjs_call_host_function(host_func_id, argv, argc, callback_func_ref); diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 299bafdb5..823ffca60 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -48,65 +48,72 @@ const decode = (kind, payload1, payload2, memory) => { // Note: // `decodeValues` assumes that the size of RawJSValue is 16. const decodeArray = (ptr, length, memory) => { + // fast path for empty array + if (length === 0) { + return []; + } let result = []; + // It's safe to hold DataView here because WebAssembly.Memory.buffer won't + // change within this function. + const view = memory.dataView(); 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); + const kind = view.getUint32(base, true); + const payload1 = view.getUint32(base + 4, true); + const payload2 = view.getFloat64(base + 8, true); result.push(decode(kind, payload1, payload2, memory)); } return result; }; +// A helper function to encode a RawJSValue into a pointers. +// Please prefer to use `writeAndReturnKindBits` to avoid unnecessary +// memory stores. +// This function should be used only when kind flag is stored in memory. const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => { + const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + memory.writeUint32(kind_ptr, kind); +}; +const writeAndReturnKindBits = (value, 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; + return exceptionBit | 4 /* Null */; } const writeRef = (kind) => { - memory.writeUint32(kind_ptr, exceptionBit | kind); memory.writeUint32(payload1_ptr, memory.retain(value)); + return exceptionBit | kind; }; const type = typeof value; switch (type) { case "boolean": { - memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */); memory.writeUint32(payload1_ptr, value ? 1 : 0); - break; + return exceptionBit | 0 /* Boolean */; } case "number": { - memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */); memory.writeFloat64(payload2_ptr, value); - break; + return exceptionBit | 2 /* Number */; } case "string": { - writeRef(1 /* String */); - break; + return writeRef(1 /* String */); } case "undefined": { - memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */); - break; + return exceptionBit | 5 /* Undefined */; } case "object": { - writeRef(3 /* Object */); - break; + return writeRef(3 /* Object */); } case "function": { - writeRef(6 /* Function */); - break; + return writeRef(6 /* Function */); } case "symbol": { - writeRef(7 /* Symbol */); - break; + return writeRef(7 /* Symbol */); } case "bigint": { - writeRef(8 /* BigInt */); - break; + return writeRef(8 /* BigInt */); } default: assertNever(type, `Type "${type}" is not supported yet`); } + throw new Error("Unreachable"); }; let globalVariable; @@ -191,125 +198,120 @@ class SwiftRuntime { 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); + const memory = this.memory; + const obj = memory.getObject(ref); + const key = memory.getObject(name); + const value = decode(kind, payload1, payload2, 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); + swjs_get_prop: (ref, name, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const obj = memory.getObject(ref); + const key = memory.getObject(name); const result = obj[key]; - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory); }, swjs_set_subscript: (ref, index, kind, payload1, payload2) => { - const obj = this.memory.getObject(ref); - const value = decode(kind, payload1, payload2, this.memory); + const memory = this.memory; + const obj = memory.getObject(ref); + const value = decode(kind, payload1, payload2, memory); obj[index] = value; }, - swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => { + swjs_get_subscript: (ref, index, 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); + return writeAndReturnKindBits(result, 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); + const memory = this.memory; + const bytes = this.textEncoder.encode(memory.getObject(ref)); + const bytes_ptr = memory.retain(bytes); + memory.writeUint32(bytes_ptr_result, bytes_ptr); return bytes.length; }, swjs_decode_string: (bytes_ptr, length) => { - const bytes = this.memory + const memory = this.memory; + const bytes = memory .bytes() .subarray(bytes_ptr, bytes_ptr + length); const string = this.textDecoder.decode(bytes); - return this.memory.retain(string); + return memory.retain(string); }, swjs_load_string: (ref, buffer) => { - const bytes = this.memory.getObject(ref); - this.memory.writeBytes(buffer, bytes); + const memory = this.memory; + const bytes = memory.getObject(ref); + memory.writeBytes(buffer, bytes); }, - swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref); - let result; + swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const func = memory.getObject(ref); + let result = undefined; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func(...args); } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, 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_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const func = memory.getObject(ref); + const args = decodeArray(argv, argc, memory); + const result = func(...args); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, 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); + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const func = memory.getObject(func_ref); let result; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, 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_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const func = memory.getObject(func_ref); + let result = undefined; + const args = decodeArray(argv, argc, memory); + result = func.apply(obj, args); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_new: (ref, argv, argc) => { - const constructor = this.memory.getObject(ref); - const args = decodeArray(argv, argc, this.memory); + const memory = this.memory; + const constructor = memory.getObject(ref); + const args = decodeArray(argv, argc, 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 memory = this.memory; + const constructor = memory.getObject(ref); let result; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, 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); + memory = this.memory; + write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, memory); + return memory.retain(result); }, swjs_instanceof: (obj_ref, constructor_ref) => { - const obj = this.memory.getObject(obj_ref); - const constructor = this.memory.getObject(constructor_ref); + const memory = this.memory; + const obj = memory.getObject(obj_ref); + const constructor = memory.getObject(constructor_ref); return obj instanceof constructor; }, swjs_create_function: (host_func_id, line, file) => { @@ -327,9 +329,10 @@ class SwiftRuntime { return this.memory.retain(array.slice()); }, swjs_load_typed_array: (ref, buffer) => { - const typedArray = this.memory.getObject(ref); + const memory = this.memory; + const typedArray = memory.getObject(ref); const bytes = new Uint8Array(typedArray.buffer); - this.memory.writeBytes(buffer, bytes); + memory.writeBytes(buffer, bytes); }, swjs_release: (ref) => { this.memory.release(ref); @@ -402,14 +405,15 @@ class SwiftRuntime { callHostFunction(host_func_id, line, file, args) { const argc = args.length; const argv = this.exports.swjs_prepare_host_function_call(argc); + const memory = this.memory; 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); + write(argument, base, base + 4, base + 8, false, memory); } let output; // This ref is released by the swjs_call_host_function implementation - const callback_func_ref = this.memory.retain((result) => { + const callback_func_ref = memory.retain((result) => { output = result; }); const alreadyReleased = this.exports.swjs_call_host_function(host_func_id, argv, argc, callback_func_ref); diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index 5556cdba8..9689cf3b0 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -16,10 +16,9 @@ import _CJavaScriptKit func _get_prop( _: JavaScriptObjectRef, _: JavaScriptObjectRef, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _set_subscript( _: JavaScriptObjectRef, _: Int32, @@ -30,10 +29,9 @@ import _CJavaScriptKit func _get_subscript( _: JavaScriptObjectRef, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _encode_string( _: JavaScriptObjectRef, _: UnsafeMutablePointer! @@ -52,33 +50,29 @@ import _CJavaScriptKit func _call_function( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _call_function_no_catch( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _call_function_with_this( _: JavaScriptObjectRef, _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _call_function_with_this_no_catch( _: JavaScriptObjectRef, _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _call_new( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32 diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 59923e02d..3bac436f4 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -3,6 +3,7 @@ #include #include +#include /// `JavaScriptObjectRef` represents JavaScript object reference that is referenced by Swift side. /// This value is an address of `SwiftRuntimeHeap`. @@ -92,16 +93,17 @@ extern void _set_prop(const JavaScriptObjectRef _this, /// /// @param _this The target JavaScript object to get its member value. /// @param prop A JavaScript string object to reference a member of `_this` object. -/// @param kind A result pointer of JavaScript value kind to get. /// @param payload1 A result pointer of first payload of JavaScript value to set the target object. /// @param payload2 A result pointer of second payload of JavaScript value to set the target object. +/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_get_prop"))) -extern void _get_prop(const JavaScriptObjectRef _this, - const JavaScriptObjectRef prop, - JavaScriptValueKind *kind, - JavaScriptPayload1 *payload1, - JavaScriptPayload2 *payload2); +extern uint32_t _get_prop( + const JavaScriptObjectRef _this, + const JavaScriptObjectRef prop, + JavaScriptPayload1 *payload1, + JavaScriptPayload2 *payload2 +); /// `_set_subscript` sets a value of `_this` JavaScript object. /// @@ -122,16 +124,17 @@ extern void _set_subscript(const JavaScriptObjectRef _this, /// /// @param _this The target JavaScript object to get its member value. /// @param index A subscript index to get value. -/// @param kind A result pointer of JavaScript value kind to get. /// @param payload1 A result pointer of first payload of JavaScript value to get the target object. /// @param payload2 A result pointer of second payload of JavaScript value to get the target object. +/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_get_subscript"))) -extern void _get_subscript(const JavaScriptObjectRef _this, - const int index, - JavaScriptValueKind *kind, - JavaScriptPayload1 *payload1, - JavaScriptPayload2 *payload2); +extern uint32_t _get_subscript( + const JavaScriptObjectRef _this, + const int index, + JavaScriptPayload1 *payload1, + JavaScriptPayload2 *payload2 +); /// `_encode_string` encodes the `str_obj` to bytes sequence and returns the length of bytes. /// @@ -175,30 +178,34 @@ extern JavaScriptObjectRef _i64_to_bigint_slow(unsigned int lower, unsigned int /// @param ref The target JavaScript function to call. /// @param argv A list of `RawJSValue` arguments to apply. /// @param argc The length of `argv``. -/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception. /// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception. /// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception. +/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function"))) -extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *argv, - const int argc, JavaScriptValueKindAndFlags *result_kind, - JavaScriptPayload1 *result_payload1, - JavaScriptPayload2 *result_payload2); +extern uint32_t _call_function( + const JavaScriptObjectRef ref, const RawJSValue *argv, + const int argc, + JavaScriptPayload1 *result_payload1, + JavaScriptPayload2 *result_payload2 +); /// `_call_function` calls JavaScript function with given arguments list without capturing any exception /// /// @param ref The target JavaScript function to call. /// @param argv A list of `RawJSValue` arguments to apply. /// @param argc The length of `argv``. -/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception. /// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception. /// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception. +/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function_no_catch"))) -extern void _call_function_no_catch(const JavaScriptObjectRef ref, const RawJSValue *argv, - const int argc, JavaScriptValueKindAndFlags *result_kind, - JavaScriptPayload1 *result_payload1, - JavaScriptPayload2 *result_payload2); +extern uint32_t _call_function_no_catch( + const JavaScriptObjectRef ref, const RawJSValue *argv, + const int argc, + JavaScriptPayload1 *result_payload1, + JavaScriptPayload2 *result_payload2 +); /// `_call_function_with_this` calls JavaScript function with given arguments list and given `_this`. /// @@ -206,17 +213,18 @@ extern void _call_function_no_catch(const JavaScriptObjectRef ref, const RawJSVa /// @param func_ref The target JavaScript function to call. /// @param argv A list of `RawJSValue` arguments to apply. /// @param argc The length of `argv``. -/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception. /// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception. /// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception. +/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function_with_this"))) -extern void _call_function_with_this(const JavaScriptObjectRef _this, - const JavaScriptObjectRef func_ref, - const RawJSValue *argv, const int argc, - JavaScriptValueKindAndFlags *result_kind, - JavaScriptPayload1 *result_payload1, - JavaScriptPayload2 *result_payload2); +extern uint32_t _call_function_with_this( + const JavaScriptObjectRef _this, + const JavaScriptObjectRef func_ref, + const RawJSValue *argv, const int argc, + JavaScriptPayload1 *result_payload1, + JavaScriptPayload2 *result_payload2 +); /// `_call_function_with_this` calls JavaScript function with given arguments list and given `_this` without capturing any exception. /// @@ -224,17 +232,18 @@ extern void _call_function_with_this(const JavaScriptObjectRef _this, /// @param func_ref The target JavaScript function to call. /// @param argv A list of `RawJSValue` arguments to apply. /// @param argc The length of `argv``. -/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception. /// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception. /// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception. +/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function_with_this_no_catch"))) -extern void _call_function_with_this_no_catch(const JavaScriptObjectRef _this, - const JavaScriptObjectRef func_ref, - const RawJSValue *argv, const int argc, - JavaScriptValueKindAndFlags *result_kind, - JavaScriptPayload1 *result_payload1, - JavaScriptPayload2 *result_payload2); +extern uint32_t _call_function_with_this_no_catch( + const JavaScriptObjectRef _this, + const JavaScriptObjectRef func_ref, + const RawJSValue *argv, const int argc, + JavaScriptPayload1 *result_payload1, + JavaScriptPayload2 *result_payload2 +); /// `_call_new` calls JavaScript object constructor with given arguments list. ///