From 8d17f5c1c78a20f5814c2eb1280569c2289c7f59 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 16 Aug 2022 22:02:32 +0900 Subject: [PATCH 01/20] Allocate function call argument buffer on stack --- .../JavaScriptKit/ConvertibleToJSValue.swift | 36 +++++----- .../FundamentalObjects/JSFunction.swift | 44 ++++++------- .../FundamentalObjects/JSString.swift | 5 +- .../JSThrowingFunction.swift | 66 +++++++++---------- Sources/JavaScriptKit/JSValue.swift | 19 +++--- 5 files changed, 79 insertions(+), 91 deletions(-) diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift index 572e867b0..95f96a2f7 100644 --- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift +++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift @@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue { } extension JSValue { - func withRawJSValue(_ body: (RawJSValue) -> T) -> T { + func toRawJSValue() -> RawJSValue { let kind: JavaScriptValueKind let payload1: JavaScriptPayload1 var payload2: JavaScriptPayload2 = 0 @@ -226,7 +226,7 @@ extension JSValue { payload1 = 0 payload2 = numberValue case let .string(string): - return string.withRawJSValue(body) + return string.toRawJSValue() case let .object(ref): kind = .object payload1 = JavaScriptPayload1(ref.id) @@ -246,30 +246,30 @@ extension JSValue { kind = .bigInt payload1 = JavaScriptPayload1(bigIntRef.id) } - let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2) - return body(rawValue) + return RawJSValue(kind: kind, payload1: payload1, payload2: payload2) } } -extension Array where Element == ConvertibleToJSValue { - func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { - func _withRawJSValues( - _ values: [ConvertibleToJSValue], _ index: Int, - _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T - ) -> T { - if index == values.count { return body(results) } - return values[index].jsValue.withRawJSValue { (rawValue) -> T in - results.append(rawValue) - return _withRawJSValues(values, index + 1, &results, body) +extension Array where Element == JSValue { + func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { + withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in + for (index, value) in self.enumerated() { + buffer[index] = value.toRawJSValue() } + return body(UnsafeBufferPointer(buffer)) } - var _results = [RawJSValue]() - return _withRawJSValues(self, 0, &_results, body) } } +extension Array where Element == ConvertibleToJSValue { + func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { + map { $0.jsValue }.withRawJSValues(body) + } +} + + extension Array where Element: ConvertibleToJSValue { - func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { - [ConvertibleToJSValue].withRawJSValues(self)(body) + func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { + map { $0.jsValue }.withRawJSValues(body) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 9cec5dad0..e54eede9c 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -38,10 +38,8 @@ public class JSFunction: JSObject { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) -> JSObject { - arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer in - JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) - } + arguments.withRawJSValues { bufferPointer in + JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) } } @@ -84,26 +82,24 @@ public class JSFunction: JSObject { } 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 - ) - } - assert(!kindAndFlags.isException) - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return result + arguments.withRawJSValues { 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 + ) } + assert(!kindAndFlags.isException) + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return result } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index 5621793d0..a10972cbe 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -95,10 +95,9 @@ extension JSString { guts.jsRef } - func withRawJSValue(_ body: (RawJSValue) -> T) -> T { - let rawValue = RawJSValue( + func toRawJSValue() -> RawJSValue { + return RawJSValue( kind: .string, payload1: guts.jsRef, payload2: 0 ) - return body(rawValue) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift index 3e21f0e1b..c605c6e7f 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift @@ -36,24 +36,22 @@ public class JSThrowingFunction { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject { - try arguments.withRawJSValues { rawValues -> Result in - rawValues.withUnsafeBufferPointer { bufferPointer in - let argv = bufferPointer.baseAddress - let argc = bufferPointer.count + try arguments.withRawJSValues { bufferPointer -> Result in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count - var exceptionKind = JavaScriptValueKindAndFlags() - var exceptionPayload1 = JavaScriptPayload1() - var exceptionPayload2 = JavaScriptPayload2() - let resultObj = _call_throwing_new( - self.base.id, argv, Int32(argc), - &exceptionKind, &exceptionPayload1, &exceptionPayload2 - ) - if exceptionKind.isException { - let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) - return .failure(exception.jsValue) - } - return .success(JSObject(id: resultObj)) + var exceptionKind = JavaScriptValueKindAndFlags() + var exceptionPayload1 = JavaScriptPayload1() + var exceptionPayload2 = JavaScriptPayload2() + let resultObj = _call_throwing_new( + self.base.id, argv, Int32(argc), + &exceptionKind, &exceptionPayload1, &exceptionPayload2 + ) + if exceptionKind.isException { + let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) + return .failure(exception.jsValue) } + return .success(JSObject(id: resultObj)) }.get() } @@ -64,26 +62,24 @@ public class JSThrowingFunction { } private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue { - let (result, isException) = arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) - } else { - _call_function( - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 - ) - } - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return (result.jsValue, kindAndFlags.isException) + let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId, + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) + } else { + _call_function( + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2 + ) } + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return (result.jsValue, kindAndFlags.isException) } if isException { throw result diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 973dfcb5d..8b6cfbd92 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -203,9 +203,8 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue { } public func setJSValue(this: JSObject, name: JSString, value: JSValue) { - value.withRawJSValue { rawValue in - _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) - } + let rawValue = value.toRawJSValue() + _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) } public func getJSValue(this: JSObject, index: Int32) -> JSValue { @@ -217,11 +216,10 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue { } public func setJSValue(this: JSObject, index: Int32, value: JSValue) { - value.withRawJSValue { rawValue in - _set_subscript(this.id, index, - rawValue.kind, - rawValue.payload1, rawValue.payload2) - } + let rawValue = value.toRawJSValue() + _set_subscript(this.id, index, + rawValue.kind, + rawValue.payload1, rawValue.payload2) } public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { @@ -233,9 +231,8 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { } public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) { - value.withRawJSValue { rawValue in - _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) - } + let rawValue = value.toRawJSValue() + _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) } public extension JSValue { From d68497860269c37b5e48aad03fead5f26f181cdb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 16 Aug 2022 23:57:30 +0900 Subject: [PATCH 02/20] Revert "Allocate function call argument buffer on stack" This reverts commit 8d17f5c1c78a20f5814c2eb1280569c2289c7f59. --- .../JavaScriptKit/ConvertibleToJSValue.swift | 36 +++++----- .../FundamentalObjects/JSFunction.swift | 44 +++++++------ .../FundamentalObjects/JSString.swift | 5 +- .../JSThrowingFunction.swift | 66 ++++++++++--------- Sources/JavaScriptKit/JSValue.swift | 19 +++--- 5 files changed, 91 insertions(+), 79 deletions(-) diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift index 95f96a2f7..572e867b0 100644 --- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift +++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift @@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue { } extension JSValue { - func toRawJSValue() -> RawJSValue { + func withRawJSValue(_ body: (RawJSValue) -> T) -> T { let kind: JavaScriptValueKind let payload1: JavaScriptPayload1 var payload2: JavaScriptPayload2 = 0 @@ -226,7 +226,7 @@ extension JSValue { payload1 = 0 payload2 = numberValue case let .string(string): - return string.toRawJSValue() + return string.withRawJSValue(body) case let .object(ref): kind = .object payload1 = JavaScriptPayload1(ref.id) @@ -246,30 +246,30 @@ extension JSValue { kind = .bigInt payload1 = JavaScriptPayload1(bigIntRef.id) } - return RawJSValue(kind: kind, payload1: payload1, payload2: payload2) + let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2) + return body(rawValue) } } -extension Array where Element == JSValue { - func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { - withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in - for (index, value) in self.enumerated() { - buffer[index] = value.toRawJSValue() +extension Array where Element == ConvertibleToJSValue { + func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { + func _withRawJSValues( + _ values: [ConvertibleToJSValue], _ index: Int, + _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T + ) -> T { + if index == values.count { return body(results) } + return values[index].jsValue.withRawJSValue { (rawValue) -> T in + results.append(rawValue) + return _withRawJSValues(values, index + 1, &results, body) } - return body(UnsafeBufferPointer(buffer)) } + var _results = [RawJSValue]() + return _withRawJSValues(self, 0, &_results, body) } } -extension Array where Element == ConvertibleToJSValue { - func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { - map { $0.jsValue }.withRawJSValues(body) - } -} - - extension Array where Element: ConvertibleToJSValue { - func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { - map { $0.jsValue }.withRawJSValues(body) + func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { + [ConvertibleToJSValue].withRawJSValues(self)(body) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index e54eede9c..9cec5dad0 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -38,8 +38,10 @@ public class JSFunction: JSObject { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) -> JSObject { - arguments.withRawJSValues { bufferPointer in - JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) + arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer in + JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) + } } } @@ -82,24 +84,26 @@ public class JSFunction: JSObject { } func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue { - arguments.withRawJSValues { 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 - ) + 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 + ) + } + 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/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index a10972cbe..5621793d0 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -95,9 +95,10 @@ extension JSString { guts.jsRef } - func toRawJSValue() -> RawJSValue { - return RawJSValue( + func withRawJSValue(_ body: (RawJSValue) -> T) -> T { + let rawValue = RawJSValue( kind: .string, payload1: guts.jsRef, payload2: 0 ) + return body(rawValue) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift index c605c6e7f..3e21f0e1b 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift @@ -36,22 +36,24 @@ public class JSThrowingFunction { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject { - try arguments.withRawJSValues { bufferPointer -> Result in - let argv = bufferPointer.baseAddress - let argc = bufferPointer.count + try arguments.withRawJSValues { rawValues -> Result in + rawValues.withUnsafeBufferPointer { bufferPointer in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count - var exceptionKind = JavaScriptValueKindAndFlags() - var exceptionPayload1 = JavaScriptPayload1() - var exceptionPayload2 = JavaScriptPayload2() - let resultObj = _call_throwing_new( - self.base.id, argv, Int32(argc), - &exceptionKind, &exceptionPayload1, &exceptionPayload2 - ) - if exceptionKind.isException { - let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) - return .failure(exception.jsValue) + var exceptionKind = JavaScriptValueKindAndFlags() + var exceptionPayload1 = JavaScriptPayload1() + var exceptionPayload2 = JavaScriptPayload2() + let resultObj = _call_throwing_new( + self.base.id, argv, Int32(argc), + &exceptionKind, &exceptionPayload1, &exceptionPayload2 + ) + if exceptionKind.isException { + let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) + return .failure(exception.jsValue) + } + return .success(JSObject(id: resultObj)) } - return .success(JSObject(id: resultObj)) }.get() } @@ -62,24 +64,26 @@ public class JSThrowingFunction { } private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue { - let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) - } else { - _call_function( - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 - ) + let (result, isException) = arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId, + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) + } else { + _call_function( + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2 + ) + } + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return (result.jsValue, kindAndFlags.isException) } - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return (result.jsValue, kindAndFlags.isException) } if isException { throw result diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 8b6cfbd92..973dfcb5d 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -203,8 +203,9 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue { } public func setJSValue(this: JSObject, name: JSString, value: JSValue) { - let rawValue = value.toRawJSValue() - _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) + value.withRawJSValue { rawValue in + _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) + } } public func getJSValue(this: JSObject, index: Int32) -> JSValue { @@ -216,10 +217,11 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue { } public func setJSValue(this: JSObject, index: Int32, value: JSValue) { - let rawValue = value.toRawJSValue() - _set_subscript(this.id, index, - rawValue.kind, - rawValue.payload1, rawValue.payload2) + value.withRawJSValue { rawValue in + _set_subscript(this.id, index, + rawValue.kind, + rawValue.payload1, rawValue.payload2) + } } public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { @@ -231,8 +233,9 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { } public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) { - let rawValue = value.toRawJSValue() - _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) + value.withRawJSValue { rawValue in + _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) + } } public extension JSValue { From 7f6e3d88f9df4c8272506b8d298fd45161b991c2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 16 Aug 2022 23:57:12 +0900 Subject: [PATCH 03/20] Reduce memory store for returned value kind --- Runtime/src/index.ts | 23 ++------ Runtime/src/js-value.ts | 58 ++++++++++++++++++- Runtime/src/types.ts | 4 +- .../FundamentalObjects/JSFunction.swift | 5 +- Sources/JavaScriptKit/XcodeSupport.swift | 3 +- .../_CJavaScriptKit/include/_CJavaScriptKit.h | 12 ++-- 6 files changed, 77 insertions(+), 28 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 8f90776f1..a60f0cf6e 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -237,35 +237,24 @@ 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 result = undefined; let isException = true; try { const args = JSValue.decodeArray(argv, argc, this.memory); - const result = func(...args); - JSValue.write( + result = func(...args); + isException = false; + } finally { + return JSValue.writeV2( result, - kind_ptr, payload1_ptr, payload2_ptr, - false, + isException, this.memory ); - isException = false; - } finally { - if (isException) { - JSValue.write( - undefined, - kind_ptr, - payload1_ptr, - payload2_ptr, - true, - this.memory - ); - } } }, diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index c3c24c3a9..0f86fa554 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, @@ -121,3 +121,59 @@ export const write = ( assertNever(type, `Type "${type}" is not supported yet`); } }; + + +export const writeV2 = ( + value: any, + payload1_ptr: pointer, + payload2_ptr: pointer, + is_exception: boolean, + memory: Memory +): JavaScriptValueKindAndFlags => { + if (value === undefined) { + return Kind.Undefined; + } + + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + return exceptionBit | Kind.Null; + } + + const writeRef = (kind: Kind) => { + memory.writeUint32(payload1_ptr, memory.retain(value)); + return exceptionBit | kind + }; + + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(payload1_ptr, value ? 1 : 0); + return exceptionBit | Kind.Boolean; + } + case "number": { + memory.writeFloat64(payload2_ptr, value); + return exceptionBit | Kind.Number; + } + case "string": { + return writeRef(Kind.String); + } + case "undefined": { + return exceptionBit | Kind.Undefined; + } + case "object": { + return writeRef(Kind.Object); + } + case "function": { + return writeRef(Kind.Function); + } + case "symbol": { + return writeRef(Kind.Symbol); + } + case "bigint": { + 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..f46ee9cdd 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -3,6 +3,7 @@ import * as JSValue from "./js-value.js"; export type ref = number; export type pointer = number; export type bool = number; +export type JavaScriptValueKindAndFlags = number; export interface ExportedFunctions { swjs_library_version(): number; @@ -63,10 +64,9 @@ export interface ImportedFunctions { 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, diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 9cec5dad0..6d0dbeda6 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -96,10 +96,11 @@ func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleTo jsFunc.id, argv, Int32(argc), &kindAndFlags, &payload1, &payload2) } else { - _call_function_no_catch( + let result = _call_function_no_catch( jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 + &payload1, &payload2 ) + kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self) } assert(!kindAndFlags.isException) let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index 5556cdba8..d231440d9 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -59,10 +59,9 @@ import _CJavaScriptKit func _call_function_no_catch( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _call_function_with_this( _: JavaScriptObjectRef, _: JavaScriptObjectRef, diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 59923e02d..b1fa593f4 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`. @@ -193,12 +194,15 @@ extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *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`. /// From 50fd47b8e90b4d846225acc40c589f4e316f77db Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 00:03:37 +0900 Subject: [PATCH 04/20] Add NODEJS_FLAGS to perform profiling by passing --prof --- IntegrationTests/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 4f850a032eb742af10900af47cb42e5a44c03d98 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 00:06:40 +0900 Subject: [PATCH 05/20] Revert "Revert "Allocate function call argument buffer on stack"" This reverts commit d68497860269c37b5e48aad03fead5f26f181cdb. --- .../JavaScriptKit/ConvertibleToJSValue.swift | 36 +++++----- .../FundamentalObjects/JSFunction.swift | 46 ++++++------- .../FundamentalObjects/JSString.swift | 5 +- .../JSThrowingFunction.swift | 66 +++++++++---------- Sources/JavaScriptKit/JSValue.swift | 19 +++--- 5 files changed, 80 insertions(+), 92 deletions(-) diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift index 572e867b0..95f96a2f7 100644 --- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift +++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift @@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue { } extension JSValue { - func withRawJSValue(_ body: (RawJSValue) -> T) -> T { + func toRawJSValue() -> RawJSValue { let kind: JavaScriptValueKind let payload1: JavaScriptPayload1 var payload2: JavaScriptPayload2 = 0 @@ -226,7 +226,7 @@ extension JSValue { payload1 = 0 payload2 = numberValue case let .string(string): - return string.withRawJSValue(body) + return string.toRawJSValue() case let .object(ref): kind = .object payload1 = JavaScriptPayload1(ref.id) @@ -246,30 +246,30 @@ extension JSValue { kind = .bigInt payload1 = JavaScriptPayload1(bigIntRef.id) } - let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2) - return body(rawValue) + return RawJSValue(kind: kind, payload1: payload1, payload2: payload2) } } -extension Array where Element == ConvertibleToJSValue { - func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { - func _withRawJSValues( - _ values: [ConvertibleToJSValue], _ index: Int, - _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T - ) -> T { - if index == values.count { return body(results) } - return values[index].jsValue.withRawJSValue { (rawValue) -> T in - results.append(rawValue) - return _withRawJSValues(values, index + 1, &results, body) +extension Array where Element == JSValue { + func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { + withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in + for (index, value) in self.enumerated() { + buffer[index] = value.toRawJSValue() } + return body(UnsafeBufferPointer(buffer)) } - var _results = [RawJSValue]() - return _withRawJSValues(self, 0, &_results, body) } } +extension Array where Element == ConvertibleToJSValue { + func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { + map { $0.jsValue }.withRawJSValues(body) + } +} + + extension Array where Element: ConvertibleToJSValue { - func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { - [ConvertibleToJSValue].withRawJSValues(self)(body) + func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { + map { $0.jsValue }.withRawJSValues(body) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 6d0dbeda6..07645ac94 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -38,10 +38,8 @@ public class JSFunction: JSObject { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) -> JSObject { - arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer in - JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) - } + arguments.withRawJSValues { bufferPointer in + JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) } } @@ -84,27 +82,25 @@ public class JSFunction: JSObject { } 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 { - let result = _call_function_no_catch( - jsFunc.id, argv, Int32(argc), - &payload1, &payload2 - ) - kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self) - } - assert(!kindAndFlags.isException) - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return result + arguments.withRawJSValues { 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 { + let result = _call_function_no_catch( + jsFunc.id, argv, Int32(argc), + &payload1, &payload2 + ) + kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self) } + assert(!kindAndFlags.isException) + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return result } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index 5621793d0..a10972cbe 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -95,10 +95,9 @@ extension JSString { guts.jsRef } - func withRawJSValue(_ body: (RawJSValue) -> T) -> T { - let rawValue = RawJSValue( + func toRawJSValue() -> RawJSValue { + return RawJSValue( kind: .string, payload1: guts.jsRef, payload2: 0 ) - return body(rawValue) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift index 3e21f0e1b..c605c6e7f 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift @@ -36,24 +36,22 @@ public class JSThrowingFunction { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject { - try arguments.withRawJSValues { rawValues -> Result in - rawValues.withUnsafeBufferPointer { bufferPointer in - let argv = bufferPointer.baseAddress - let argc = bufferPointer.count + try arguments.withRawJSValues { bufferPointer -> Result in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count - var exceptionKind = JavaScriptValueKindAndFlags() - var exceptionPayload1 = JavaScriptPayload1() - var exceptionPayload2 = JavaScriptPayload2() - let resultObj = _call_throwing_new( - self.base.id, argv, Int32(argc), - &exceptionKind, &exceptionPayload1, &exceptionPayload2 - ) - if exceptionKind.isException { - let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) - return .failure(exception.jsValue) - } - return .success(JSObject(id: resultObj)) + var exceptionKind = JavaScriptValueKindAndFlags() + var exceptionPayload1 = JavaScriptPayload1() + var exceptionPayload2 = JavaScriptPayload2() + let resultObj = _call_throwing_new( + self.base.id, argv, Int32(argc), + &exceptionKind, &exceptionPayload1, &exceptionPayload2 + ) + if exceptionKind.isException { + let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) + return .failure(exception.jsValue) } + return .success(JSObject(id: resultObj)) }.get() } @@ -64,26 +62,24 @@ public class JSThrowingFunction { } private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue { - let (result, isException) = arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) - } else { - _call_function( - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 - ) - } - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return (result.jsValue, kindAndFlags.isException) + let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId, + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) + } else { + _call_function( + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2 + ) } + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return (result.jsValue, kindAndFlags.isException) } if isException { throw result diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 973dfcb5d..8b6cfbd92 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -203,9 +203,8 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue { } public func setJSValue(this: JSObject, name: JSString, value: JSValue) { - value.withRawJSValue { rawValue in - _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) - } + let rawValue = value.toRawJSValue() + _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) } public func getJSValue(this: JSObject, index: Int32) -> JSValue { @@ -217,11 +216,10 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue { } public func setJSValue(this: JSObject, index: Int32, value: JSValue) { - value.withRawJSValue { rawValue in - _set_subscript(this.id, index, - rawValue.kind, - rawValue.payload1, rawValue.payload2) - } + let rawValue = value.toRawJSValue() + _set_subscript(this.id, index, + rawValue.kind, + rawValue.payload1, rawValue.payload2) } public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { @@ -233,9 +231,8 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { } public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) { - value.withRawJSValue { rawValue in - _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) - } + let rawValue = value.toRawJSValue() + _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) } public extension JSValue { From e66296fb8aeafb8a4ef0aee31f7dae60bd837bac Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 00:07:08 +0900 Subject: [PATCH 06/20] Revert "Revert "Revert "Allocate function call argument buffer on stack""" This reverts commit 4f850a032eb742af10900af47cb42e5a44c03d98. --- .../JavaScriptKit/ConvertibleToJSValue.swift | 36 +++++----- .../FundamentalObjects/JSFunction.swift | 46 +++++++------ .../FundamentalObjects/JSString.swift | 5 +- .../JSThrowingFunction.swift | 66 ++++++++++--------- Sources/JavaScriptKit/JSValue.swift | 19 +++--- 5 files changed, 92 insertions(+), 80 deletions(-) diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift index 95f96a2f7..572e867b0 100644 --- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift +++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift @@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue { } extension JSValue { - func toRawJSValue() -> RawJSValue { + func withRawJSValue(_ body: (RawJSValue) -> T) -> T { let kind: JavaScriptValueKind let payload1: JavaScriptPayload1 var payload2: JavaScriptPayload2 = 0 @@ -226,7 +226,7 @@ extension JSValue { payload1 = 0 payload2 = numberValue case let .string(string): - return string.toRawJSValue() + return string.withRawJSValue(body) case let .object(ref): kind = .object payload1 = JavaScriptPayload1(ref.id) @@ -246,30 +246,30 @@ extension JSValue { kind = .bigInt payload1 = JavaScriptPayload1(bigIntRef.id) } - return RawJSValue(kind: kind, payload1: payload1, payload2: payload2) + let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2) + return body(rawValue) } } -extension Array where Element == JSValue { - func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { - withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in - for (index, value) in self.enumerated() { - buffer[index] = value.toRawJSValue() +extension Array where Element == ConvertibleToJSValue { + func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { + func _withRawJSValues( + _ values: [ConvertibleToJSValue], _ index: Int, + _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T + ) -> T { + if index == values.count { return body(results) } + return values[index].jsValue.withRawJSValue { (rawValue) -> T in + results.append(rawValue) + return _withRawJSValues(values, index + 1, &results, body) } - return body(UnsafeBufferPointer(buffer)) } + var _results = [RawJSValue]() + return _withRawJSValues(self, 0, &_results, body) } } -extension Array where Element == ConvertibleToJSValue { - func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { - map { $0.jsValue }.withRawJSValues(body) - } -} - - extension Array where Element: ConvertibleToJSValue { - func withRawJSValues(_ body: (UnsafeBufferPointer) -> T) -> T { - map { $0.jsValue }.withRawJSValues(body) + func withRawJSValues(_ body: ([RawJSValue]) -> T) -> T { + [ConvertibleToJSValue].withRawJSValues(self)(body) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 07645ac94..6d0dbeda6 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -38,8 +38,10 @@ public class JSFunction: JSObject { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) -> JSObject { - arguments.withRawJSValues { bufferPointer in - JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) + arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer in + JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) + } } } @@ -82,25 +84,27 @@ public class JSFunction: JSObject { } func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue { - arguments.withRawJSValues { 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 { - let result = _call_function_no_catch( - jsFunc.id, argv, Int32(argc), - &payload1, &payload2 - ) - kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self) + 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 { + let result = _call_function_no_catch( + jsFunc.id, argv, Int32(argc), + &payload1, &payload2 + ) + kindAndFlags = unsafeBitCast(result, 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/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index a10972cbe..5621793d0 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -95,9 +95,10 @@ extension JSString { guts.jsRef } - func toRawJSValue() -> RawJSValue { - return RawJSValue( + func withRawJSValue(_ body: (RawJSValue) -> T) -> T { + let rawValue = RawJSValue( kind: .string, payload1: guts.jsRef, payload2: 0 ) + return body(rawValue) } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift index c605c6e7f..3e21f0e1b 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift @@ -36,22 +36,24 @@ public class JSThrowingFunction { /// - Parameter arguments: Arguments to be passed to this constructor function. /// - Returns: A new instance of this constructor. public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject { - try arguments.withRawJSValues { bufferPointer -> Result in - let argv = bufferPointer.baseAddress - let argc = bufferPointer.count + try arguments.withRawJSValues { rawValues -> Result in + rawValues.withUnsafeBufferPointer { bufferPointer in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count - var exceptionKind = JavaScriptValueKindAndFlags() - var exceptionPayload1 = JavaScriptPayload1() - var exceptionPayload2 = JavaScriptPayload2() - let resultObj = _call_throwing_new( - self.base.id, argv, Int32(argc), - &exceptionKind, &exceptionPayload1, &exceptionPayload2 - ) - if exceptionKind.isException { - let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) - return .failure(exception.jsValue) + var exceptionKind = JavaScriptValueKindAndFlags() + var exceptionPayload1 = JavaScriptPayload1() + var exceptionPayload2 = JavaScriptPayload2() + let resultObj = _call_throwing_new( + self.base.id, argv, Int32(argc), + &exceptionKind, &exceptionPayload1, &exceptionPayload2 + ) + if exceptionKind.isException { + let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) + return .failure(exception.jsValue) + } + return .success(JSObject(id: resultObj)) } - return .success(JSObject(id: resultObj)) }.get() } @@ -62,24 +64,26 @@ public class JSThrowingFunction { } private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue { - let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) - } else { - _call_function( - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2 - ) + let (result, isException) = arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId, + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) + } else { + _call_function( + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2 + ) + } + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return (result.jsValue, kindAndFlags.isException) } - let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return (result.jsValue, kindAndFlags.isException) } if isException { throw result diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 8b6cfbd92..973dfcb5d 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -203,8 +203,9 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue { } public func setJSValue(this: JSObject, name: JSString, value: JSValue) { - let rawValue = value.toRawJSValue() - _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) + value.withRawJSValue { rawValue in + _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) + } } public func getJSValue(this: JSObject, index: Int32) -> JSValue { @@ -216,10 +217,11 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue { } public func setJSValue(this: JSObject, index: Int32, value: JSValue) { - let rawValue = value.toRawJSValue() - _set_subscript(this.id, index, - rawValue.kind, - rawValue.payload1, rawValue.payload2) + value.withRawJSValue { rawValue in + _set_subscript(this.id, index, + rawValue.kind, + rawValue.payload1, rawValue.payload2) + } } public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { @@ -231,8 +233,9 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { } public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) { - let rawValue = value.toRawJSValue() - _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) + value.withRawJSValue { rawValue in + _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) + } } public extension JSValue { From 0f53ca55cf73cedbaa591c9f26231bedc12703f2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 00:28:35 +0900 Subject: [PATCH 07/20] Reduce retain/release dance caused by Optional --- .../FundamentalObjects/JSFunction.swift | 73 +++++++++++++------ .../FundamentalObjects/JSSymbol.swift | 2 +- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 6d0dbeda6..046a70440 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,30 +96,42 @@ 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 { - let result = _call_function_no_catch( - jsFunc.id, argv, Int32(argc), + final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue]) -> 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() + let resultBitPattern = _call_function_no_catch( + id, argv, Int32(argc), &payload1, &payload2 ) - kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self) + 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 { + 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() + _call_function_with_this_no_catch(this.id, + id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) + 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)) } From adfe1e8b5233ad1f876e2cd91477aa4781141451 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 01:10:11 +0900 Subject: [PATCH 08/20] Don't escape JSFunction self --- Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 046a70440..3c32327fd 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -98,7 +98,8 @@ public class JSFunction: JSObject { } final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue]) -> RawJSValue { - arguments.withRawJSValues { rawValues in + let id = self.id + return arguments.withRawJSValues { rawValues in rawValues.withUnsafeBufferPointer { bufferPointer in let argv = bufferPointer.baseAddress let argc = bufferPointer.count @@ -118,7 +119,8 @@ public class JSFunction: JSObject { } final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue], this: JSObject) -> RawJSValue { - arguments.withRawJSValues { rawValues in + let id = self.id + return arguments.withRawJSValues { rawValues in rawValues.withUnsafeBufferPointer { bufferPointer in let argv = bufferPointer.baseAddress let argc = bufferPointer.count From 78b442d6d0de461bb737c7153edf4ae00cf46319 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 01:28:51 +0900 Subject: [PATCH 09/20] Add fast path for empty JSValue array --- Sources/JavaScriptKit/ConvertibleToJSValue.swift | 3 +++ 1 file changed, 3 insertions(+) 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 From bffe009412c79060a6684f86cb5f23d59af3a836 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 01:39:14 +0900 Subject: [PATCH 10/20] Skip re-creating DataView in decodeArray --- Runtime/src/js-value.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 0f86fa554..14394259b 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -51,12 +51,18 @@ 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; From 2a8146bbd894eca9ef85531dd2a1c1c28be7f1bc Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 02:02:15 +0900 Subject: [PATCH 11/20] make regenerate_swiftpm_resources --- Sources/JavaScriptKit/Runtime/index.js | 68 +++++++++++++++++++++---- Sources/JavaScriptKit/Runtime/index.mjs | 68 +++++++++++++++++++++---- 2 files changed, 118 insertions(+), 18 deletions(-) diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index 43158fbab..b56db0d82 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -54,12 +54,19 @@ // 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; @@ -114,6 +121,51 @@ assertNever(type, `Type "${type}" is not supported yet`); } }; + const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { + if (value === undefined) { + return 5 /* Undefined */; + } + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + return exceptionBit | 4 /* Null */; + } + const writeRef = (kind) => { + memory.writeUint32(payload1_ptr, memory.retain(value)); + return exceptionBit | kind; + }; + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(payload1_ptr, value ? 1 : 0); + return exceptionBit | 0 /* Boolean */; + } + case "number": { + memory.writeFloat64(payload2_ptr, value); + return exceptionBit | 2 /* Number */; + } + case "string": { + return writeRef(1 /* String */); + } + case "undefined": { + return exceptionBit | 5 /* Undefined */; + } + case "object": { + return writeRef(3 /* Object */); + } + case "function": { + return writeRef(6 /* Function */); + } + case "symbol": { + return writeRef(7 /* Symbol */); + } + case "bigint": { + return writeRef(8 /* BigInt */); + } + default: + assertNever(type, `Type "${type}" is not supported yet`); + } + throw new Error("Unreachable"); + }; let globalVariable; if (typeof globalThis !== "undefined") { @@ -248,19 +300,17 @@ } 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) => { + swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref); + let result = undefined; 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); + result = func(...args); isException = false; } finally { - if (isException) { - write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - } + return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory); } }, swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 299bafdb5..0e91431fd 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -48,12 +48,19 @@ 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; @@ -108,6 +115,51 @@ const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory assertNever(type, `Type "${type}" is not supported yet`); } }; +const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { + if (value === undefined) { + return 5 /* Undefined */; + } + const exceptionBit = (is_exception ? 1 : 0) << 31; + if (value === null) { + return exceptionBit | 4 /* Null */; + } + const writeRef = (kind) => { + memory.writeUint32(payload1_ptr, memory.retain(value)); + return exceptionBit | kind; + }; + const type = typeof value; + switch (type) { + case "boolean": { + memory.writeUint32(payload1_ptr, value ? 1 : 0); + return exceptionBit | 0 /* Boolean */; + } + case "number": { + memory.writeFloat64(payload2_ptr, value); + return exceptionBit | 2 /* Number */; + } + case "string": { + return writeRef(1 /* String */); + } + case "undefined": { + return exceptionBit | 5 /* Undefined */; + } + case "object": { + return writeRef(3 /* Object */); + } + case "function": { + return writeRef(6 /* Function */); + } + case "symbol": { + return writeRef(7 /* Symbol */); + } + case "bigint": { + return writeRef(8 /* BigInt */); + } + default: + assertNever(type, `Type "${type}" is not supported yet`); + } + throw new Error("Unreachable"); +}; let globalVariable; if (typeof globalThis !== "undefined") { @@ -242,19 +294,17 @@ class SwiftRuntime { } 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) => { + swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref); + let result = undefined; 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); + result = func(...args); isException = false; } finally { - if (isException) { - write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - } + return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory); } }, swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { From 43d9a2ae113ffa559b2fecb4c38ff20e6bc82ce7 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 17 Aug 2022 20:48:05 +0900 Subject: [PATCH 12/20] Apply the same techniques to call families --- Runtime/src/index.ts | 76 ++++++------------- Runtime/src/js-value.ts | 4 - Runtime/src/types.ts | 9 +-- .../FundamentalObjects/JSFunction.swift | 12 +-- .../JSThrowingFunction.swift | 16 ++-- Sources/JavaScriptKit/Runtime/index.js | 50 ++++-------- Sources/JavaScriptKit/Runtime/index.mjs | 50 ++++-------- Sources/JavaScriptKit/XcodeSupport.swift | 9 +-- .../_CJavaScriptKit/include/_CJavaScriptKit.h | 43 ++++++----- 9 files changed, 98 insertions(+), 171 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index a60f0cf6e..45e1fc5ca 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -204,29 +204,25 @@ 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 result: any; + let result = undefined; try { const args = JSValue.decodeArray(argv, argc, this.memory); result = func(...args); } catch (error) { - JSValue.write( + return JSValue.writeV2( error, - kind_ptr, payload1_ptr, payload2_ptr, true, this.memory ); - return; } - JSValue.write( + return JSValue.writeV2( result, - kind_ptr, payload1_ptr, payload2_ptr, false, @@ -241,21 +237,15 @@ export class SwiftRuntime { payload2_ptr: pointer ) => { const func = this.memory.getObject(ref); - let result = undefined; - let isException = true; - try { - const args = JSValue.decodeArray(argv, argc, this.memory); - result = func(...args); - isException = false; - } finally { - return JSValue.writeV2( - result, - payload1_ptr, - payload2_ptr, - isException, - this.memory - ); - } + const args = JSValue.decodeArray(argv, argc, this.memory); + const result = func(...args); + return JSValue.writeV2( + result, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_call_function_with_this: ( @@ -263,7 +253,6 @@ export class SwiftRuntime { func_ref: ref, argv: pointer, argc: number, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { @@ -274,19 +263,16 @@ export class SwiftRuntime { const args = JSValue.decodeArray(argv, argc, this.memory); result = func.apply(obj, args); } catch (error) { - JSValue.write( + return JSValue.writeV2( error, - kind_ptr, payload1_ptr, payload2_ptr, true, this.memory ); - return; } - JSValue.write( + return JSValue.writeV2( result, - kind_ptr, payload1_ptr, payload2_ptr, false, @@ -298,37 +284,21 @@ 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 { + let result = undefined; 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 - ); - } - } + result = func.apply(obj, args); + return JSValue.writeV2( + result, + payload1_ptr, + payload2_ptr, + false, + this.memory + ); }, swjs_call_new: (ref: ref, argv: pointer, argc: number) => { diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 14394259b..7539ba9af 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -136,10 +136,6 @@ export const writeV2 = ( is_exception: boolean, memory: Memory ): JavaScriptValueKindAndFlags => { - if (value === undefined) { - return Kind.Undefined; - } - const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { return exceptionBit | Kind.Null; diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index f46ee9cdd..6dd7bb2d6 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -56,10 +56,9 @@ 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, @@ -72,19 +71,17 @@ export interface ImportedFunctions { 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/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 3c32327fd..66c613402 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -103,14 +103,13 @@ public class JSFunction: JSObject { rawValues.withUnsafeBufferPointer { bufferPointer in let argv = bufferPointer.baseAddress let argc = bufferPointer.count - var kindAndFlags = JavaScriptValueKindAndFlags() var payload1 = JavaScriptPayload1() var payload2 = JavaScriptPayload2() let resultBitPattern = _call_function_no_catch( id, argv, Int32(argc), &payload1, &payload2 ) - kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self) + let kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self) assert(!kindAndFlags.isException) let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) return result @@ -124,12 +123,13 @@ public class JSFunction: JSObject { rawValues.withUnsafeBufferPointer { bufferPointer in let argv = bufferPointer.baseAddress let argc = bufferPointer.count - var kindAndFlags = JavaScriptValueKindAndFlags() var payload1 = JavaScriptPayload1() var payload2 = JavaScriptPayload2() - _call_function_with_this_no_catch(this.id, - id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) + 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 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/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index b56db0d82..c00154d4b 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -122,9 +122,6 @@ } }; const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { - if (value === undefined) { - return 5 /* Undefined */; - } const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { return exceptionBit | 4 /* Null */; @@ -287,33 +284,25 @@ const bytes = this.memory.getObject(ref); this.memory.writeBytes(buffer, bytes); }, - swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref); - let result; + let result = undefined; 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; + return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref); - let result = undefined; - let isException = true; - try { - const args = decodeArray(argv, argc, this.memory); - result = func(...args); - isException = false; - } - finally { - return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory); - } + const args = decodeArray(argv, argc, this.memory); + const result = func(...args); + return writeV2(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) => { + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(obj_ref); const func = this.memory.getObject(func_ref); let result; @@ -322,26 +311,17 @@ result = func.apply(obj, args); } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeV2(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) => { + swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, 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); - } - } + let result = undefined; + const args = decodeArray(argv, argc, this.memory); + result = func.apply(obj, args); + return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_new: (ref, argv, argc) => { const constructor = this.memory.getObject(ref); diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 0e91431fd..32db67134 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -116,9 +116,6 @@ const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory } }; const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { - if (value === undefined) { - return 5 /* Undefined */; - } const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { return exceptionBit | 4 /* Null */; @@ -281,33 +278,25 @@ class SwiftRuntime { const bytes = this.memory.getObject(ref); this.memory.writeBytes(buffer, bytes); }, - swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => { + swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref); - let result; + let result = undefined; 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; + return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { const func = this.memory.getObject(ref); - let result = undefined; - let isException = true; - try { - const args = decodeArray(argv, argc, this.memory); - result = func(...args); - isException = false; - } - finally { - return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory); - } + const args = decodeArray(argv, argc, this.memory); + const result = func(...args); + return writeV2(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) => { + swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(obj_ref); const func = this.memory.getObject(func_ref); let result; @@ -316,26 +305,17 @@ class SwiftRuntime { result = func.apply(obj, args); } catch (error) { - write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory); - return; + return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); } - write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory); + return writeV2(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) => { + swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, 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); - } - } + let result = undefined; + const args = decodeArray(argv, argc, this.memory); + result = func.apply(obj, args); + return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_new: (ref, argv, argc) => { const constructor = this.memory.getObject(ref); diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index d231440d9..303b44a33 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -52,10 +52,9 @@ import _CJavaScriptKit func _call_function( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _call_function_no_catch( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, @@ -66,18 +65,16 @@ import _CJavaScriptKit _: 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 b1fa593f4..846c834f2 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -176,22 +176,23 @@ 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. @@ -210,17 +211,18 @@ extern uint32_t _call_function_no_catch( /// @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. /// @@ -228,17 +230,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. /// From 2b86b418207ccc48f699a8534c6241c5a4777d7d Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 18 Aug 2022 02:57:19 +0900 Subject: [PATCH 13/20] Reuse DataView as much as possible --- Runtime/src/index.ts | 90 +++++++++++++++---------- Sources/JavaScriptKit/Runtime/index.js | 90 +++++++++++++++---------- Sources/JavaScriptKit/Runtime/index.mjs | 90 +++++++++++++++---------- 3 files changed, 159 insertions(+), 111 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 45e1fc5ca..afc6c63b2 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -84,6 +84,7 @@ 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; @@ -93,12 +94,12 @@ export class SwiftRuntime { base + 4, base + 8, false, - this.memory + 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,9 +128,10 @@ 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: ( @@ -139,8 +141,9 @@ export class SwiftRuntime { 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( result, @@ -148,7 +151,7 @@ export class SwiftRuntime { payload1_ptr, payload2_ptr, false, - this.memory + memory ); }, @@ -159,8 +162,9 @@ 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: ( @@ -183,21 +187,24 @@ 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: ( @@ -207,10 +214,11 @@ export class SwiftRuntime { payload1_ptr: pointer, payload2_ptr: pointer ) => { - const func = this.memory.getObject(ref); + 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) { return JSValue.writeV2( @@ -236,8 +244,9 @@ export class SwiftRuntime { payload1_ptr: pointer, payload2_ptr: pointer ) => { - const func = this.memory.getObject(ref); - const args = JSValue.decodeArray(argv, argc, this.memory); + const memory = this.memory; + const func = memory.getObject(ref); + const args = JSValue.decodeArray(argv, argc, memory); const result = func(...args); return JSValue.writeV2( result, @@ -256,11 +265,12 @@ export class SwiftRuntime { 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) { return JSValue.writeV2( @@ -287,10 +297,11 @@ export class SwiftRuntime { 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 = undefined; - const args = JSValue.decodeArray(argv, argc, this.memory); + const args = JSValue.decodeArray(argv, argc, memory); result = func.apply(obj, args); return JSValue.writeV2( result, @@ -302,8 +313,9 @@ export class SwiftRuntime { }, 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); }, @@ -315,10 +327,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( @@ -331,20 +344,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; }, @@ -378,9 +393,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/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index c00154d4b..03affa6cf 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -246,20 +246,23 @@ 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); + 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); + write(result, kind_ptr, 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) => { @@ -268,27 +271,31 @@ 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); + 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, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref); + 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) { @@ -297,17 +304,19 @@ return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref); - const args = decodeArray(argv, argc, this.memory); + const memory = this.memory; + const func = memory.getObject(ref); + const args = decodeArray(argv, argc, memory); const result = func(...args); return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { - 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; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); } catch (error) { @@ -316,36 +325,41 @@ return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { - 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 = undefined; - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); return writeV2(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) => { @@ -363,9 +377,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); @@ -438,14 +453,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 32db67134..8b2396efd 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -240,20 +240,23 @@ 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); + 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); + write(result, kind_ptr, 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) => { @@ -262,27 +265,31 @@ class SwiftRuntime { 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); + 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, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref); + 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) { @@ -291,17 +298,19 @@ class SwiftRuntime { return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => { - const func = this.memory.getObject(ref); - const args = decodeArray(argv, argc, this.memory); + const memory = this.memory; + const func = memory.getObject(ref); + const args = decodeArray(argv, argc, memory); const result = func(...args); return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { - 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; try { - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); } catch (error) { @@ -310,36 +319,41 @@ class SwiftRuntime { return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { - 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 = undefined; - const args = decodeArray(argv, argc, this.memory); + const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); return writeV2(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) => { @@ -357,9 +371,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); @@ -432,14 +447,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); From 5a3ecf2799db828ca4607bd5f409059a6da8ca45 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 18 Aug 2022 02:58:09 +0900 Subject: [PATCH 14/20] npm run format --- Runtime/src/index.ts | 13 +++---------- Runtime/src/js-value.ts | 7 ++++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index afc6c63b2..73a3672ae 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -88,14 +88,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, - 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 @@ -301,8 +294,8 @@ export class SwiftRuntime { 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); + const args = JSValue.decodeArray(argv, argc, memory); + result = func.apply(obj, args); return JSValue.writeV2( result, payload1_ptr, diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 7539ba9af..b76d8b217 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -52,7 +52,9 @@ export const decode = ( // `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 []; } + if (length === 0) { + return []; + } let result = []; // It's safe to hold DataView here because WebAssembly.Memory.buffer won't @@ -128,7 +130,6 @@ export const write = ( } }; - export const writeV2 = ( value: any, payload1_ptr: pointer, @@ -143,7 +144,7 @@ export const writeV2 = ( const writeRef = (kind: Kind) => { memory.writeUint32(payload1_ptr, memory.retain(value)); - return exceptionBit | kind + return exceptionBit | kind; }; const type = typeof value; From 8184119185d0df88e996b987f9a84a52e15f4eaf Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 18 Aug 2022 14:54:35 +0900 Subject: [PATCH 15/20] Optimize swjs_get_prop to reduce memory store --- Runtime/src/index.ts | 4 +--- Runtime/src/types.ts | 4 ++-- Sources/JavaScriptKit/JSValue.swift | 16 ++++++++++------ Sources/JavaScriptKit/Runtime/index.js | 4 ++-- Sources/JavaScriptKit/Runtime/index.mjs | 4 ++-- Sources/JavaScriptKit/XcodeSupport.swift | 3 +-- .../_CJavaScriptKit/include/_CJavaScriptKit.h | 13 +++++++------ 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 73a3672ae..02b56c197 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -130,7 +130,6 @@ export class SwiftRuntime { swjs_get_prop: ( ref: ref, name: ref, - kind_ptr: pointer, payload1_ptr: pointer, payload2_ptr: pointer ) => { @@ -138,9 +137,8 @@ export class SwiftRuntime { const obj = memory.getObject(ref); const key = memory.getObject(name); const result = obj[key]; - JSValue.write( + return JSValue.writeV2( result, - kind_ptr, payload1_ptr, payload2_ptr, false, diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 6dd7bb2d6..3cd90ae2d 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -3,6 +3,7 @@ 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 { @@ -31,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, diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 973dfcb5d..99a051a54 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 } @@ -226,9 +228,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 03affa6cf..ce8b611eb 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -252,12 +252,12 @@ const value = decode(kind, payload1, payload2, memory); obj[key] = value; }, - swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => { + 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, memory); + return writeV2(result, payload1_ptr, payload2_ptr, false, memory); }, swjs_set_subscript: (ref, index, kind, payload1, payload2) => { const memory = this.memory; diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 8b2396efd..2c40e6bf0 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -246,12 +246,12 @@ class SwiftRuntime { const value = decode(kind, payload1, payload2, memory); obj[key] = value; }, - swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => { + 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, memory); + return writeV2(result, payload1_ptr, payload2_ptr, false, memory); }, swjs_set_subscript: (ref, index, kind, payload1, payload2) => { const memory = this.memory; diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index 303b44a33..34eada81f 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, diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 846c834f2..00bde65c9 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -93,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. /// From a1b690e5efdceef3db15a7ea12b3963ad1118352 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 18 Aug 2022 14:59:56 +0900 Subject: [PATCH 16/20] Optimize _get_subscript to reduce memory store --- Runtime/src/index.ts | 4 +--- Runtime/src/types.ts | 3 +-- Sources/JavaScriptKit/JSValue.swift | 8 +++++--- Sources/JavaScriptKit/Runtime/index.js | 4 ++-- Sources/JavaScriptKit/Runtime/index.mjs | 4 ++-- Sources/JavaScriptKit/XcodeSupport.swift | 3 +-- Sources/_CJavaScriptKit/include/_CJavaScriptKit.h | 13 +++++++------ 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 02b56c197..bb9e2fe12 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -161,15 +161,13 @@ export class SwiftRuntime { 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.writeV2( result, - kind_ptr, payload1_ptr, payload2_ptr, false, diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 3cd90ae2d..ff20999ea 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -45,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; diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 99a051a54..58b28e079 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -212,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 } diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index ce8b611eb..c383ab513 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -265,10 +265,10 @@ 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 writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_encode_string: (ref, bytes_ptr_result) => { const memory = this.memory; diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 2c40e6bf0..73522ce95 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -259,10 +259,10 @@ class SwiftRuntime { 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 writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_encode_string: (ref, bytes_ptr_result) => { const memory = this.memory; diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index 34eada81f..9689cf3b0 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -29,10 +29,9 @@ import _CJavaScriptKit func _get_subscript( _: JavaScriptObjectRef, _: Int32, - _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! - ) { fatalError() } + ) -> UInt32 { fatalError() } func _encode_string( _: JavaScriptObjectRef, _: UnsafeMutablePointer! diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 00bde65c9..adf732bd4 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -124,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. /// From 4110f7f1aa3b5372a67589a7c19873b553b55ee7 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 18 Aug 2022 15:18:41 +0900 Subject: [PATCH 17/20] Rename writeV2 -> writeAndReturnKindBits --- Runtime/src/index.ts | 16 ++++++++-------- Runtime/src/js-value.ts | 4 +++- Sources/JavaScriptKit/Runtime/index.js | 20 +++++++++++--------- Sources/JavaScriptKit/Runtime/index.mjs | 20 +++++++++++--------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index bb9e2fe12..a9da3eb9f 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -137,7 +137,7 @@ export class SwiftRuntime { const obj = memory.getObject(ref); const key = memory.getObject(name); const result = obj[key]; - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( result, payload1_ptr, payload2_ptr, @@ -166,7 +166,7 @@ export class SwiftRuntime { ) => { const obj = this.memory.getObject(ref); const result = obj[index]; - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( result, payload1_ptr, payload2_ptr, @@ -210,7 +210,7 @@ export class SwiftRuntime { const args = JSValue.decodeArray(argv, argc, memory); result = func(...args); } catch (error) { - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( error, payload1_ptr, payload2_ptr, @@ -218,7 +218,7 @@ export class SwiftRuntime { this.memory ); } - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( result, payload1_ptr, payload2_ptr, @@ -237,7 +237,7 @@ export class SwiftRuntime { const func = memory.getObject(ref); const args = JSValue.decodeArray(argv, argc, memory); const result = func(...args); - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( result, payload1_ptr, payload2_ptr, @@ -262,7 +262,7 @@ export class SwiftRuntime { const args = JSValue.decodeArray(argv, argc, memory); result = func.apply(obj, args); } catch (error) { - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( error, payload1_ptr, payload2_ptr, @@ -270,7 +270,7 @@ export class SwiftRuntime { this.memory ); } - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( result, payload1_ptr, payload2_ptr, @@ -292,7 +292,7 @@ export class SwiftRuntime { let result = undefined; const args = JSValue.decodeArray(argv, argc, memory); result = func.apply(obj, args); - return JSValue.writeV2( + return JSValue.writeAndReturnKindBits( result, payload1_ptr, payload2_ptr, diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index b76d8b217..76b554a28 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -130,7 +130,9 @@ export const write = ( } }; -export const writeV2 = ( +/// This is a fast version of the above `write` function. +/// Please synchronize with the above `write` function if you change either. +export const writeAndReturnKindBits = ( value: any, payload1_ptr: pointer, payload2_ptr: pointer, diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index c383ab513..f9e273d21 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -121,7 +121,9 @@ assertNever(type, `Type "${type}" is not supported yet`); } }; - const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { + /// This is a fast version of the above `write` function. + /// Please synchronize with the above `write` function if you change either. + const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { return exceptionBit | 4 /* Null */; @@ -257,7 +259,7 @@ const obj = memory.getObject(ref); const key = memory.getObject(name); const result = obj[key]; - return writeV2(result, payload1_ptr, payload2_ptr, false, memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory); }, swjs_set_subscript: (ref, index, kind, payload1, payload2) => { const memory = this.memory; @@ -268,7 +270,7 @@ swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(ref); const result = obj[index]; - return writeV2(result, 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 memory = this.memory; @@ -299,16 +301,16 @@ result = func(...args); } catch (error) { - return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - return writeV2(result, 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, payload1_ptr, payload2_ptr) => { const memory = this.memory; const func = memory.getObject(ref); const args = decodeArray(argv, argc, memory); const result = func(...args); - return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { const memory = this.memory; @@ -320,9 +322,9 @@ result = func.apply(obj, args); } catch (error) { - return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - return writeV2(result, 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, payload1_ptr, payload2_ptr) => { const memory = this.memory; @@ -331,7 +333,7 @@ let result = undefined; const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); - return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_new: (ref, argv, argc) => { const memory = this.memory; diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 73522ce95..32e90f739 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -115,7 +115,9 @@ const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory assertNever(type, `Type "${type}" is not supported yet`); } }; -const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { +/// This is a fast version of the above `write` function. +/// Please synchronize with the above `write` function if you change either. +const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { return exceptionBit | 4 /* Null */; @@ -251,7 +253,7 @@ class SwiftRuntime { const obj = memory.getObject(ref); const key = memory.getObject(name); const result = obj[key]; - return writeV2(result, payload1_ptr, payload2_ptr, false, memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory); }, swjs_set_subscript: (ref, index, kind, payload1, payload2) => { const memory = this.memory; @@ -262,7 +264,7 @@ class SwiftRuntime { swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => { const obj = this.memory.getObject(ref); const result = obj[index]; - return writeV2(result, 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 memory = this.memory; @@ -293,16 +295,16 @@ class SwiftRuntime { result = func(...args); } catch (error) { - return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - return writeV2(result, 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, payload1_ptr, payload2_ptr) => { const memory = this.memory; const func = memory.getObject(ref); const args = decodeArray(argv, argc, memory); const result = func(...args); - return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => { const memory = this.memory; @@ -314,9 +316,9 @@ class SwiftRuntime { result = func.apply(obj, args); } catch (error) { - return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory); + return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory); } - return writeV2(result, 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, payload1_ptr, payload2_ptr) => { const memory = this.memory; @@ -325,7 +327,7 @@ class SwiftRuntime { let result = undefined; const args = decodeArray(argv, argc, memory); result = func.apply(obj, args); - return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory); + return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory); }, swjs_call_new: (ref, argv, argc) => { const memory = this.memory; From d4c45b478eedbd847cce69adce40e665599b9a01 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 19 Aug 2022 00:04:43 +0900 Subject: [PATCH 18/20] Improve doc comment style --- Sources/_CJavaScriptKit/include/_CJavaScriptKit.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index adf732bd4..3bac436f4 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -95,7 +95,7 @@ extern void _set_prop(const JavaScriptObjectRef _this, /// @param prop A JavaScript string object to reference a member of `_this` object. /// @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. +/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_get_prop"))) extern uint32_t _get_prop( @@ -126,7 +126,7 @@ extern void _set_subscript(const JavaScriptObjectRef _this, /// @param index A subscript index to get value. /// @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. +/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_get_subscript"))) extern uint32_t _get_subscript( @@ -180,7 +180,7 @@ extern JavaScriptObjectRef _i64_to_bigint_slow(unsigned int lower, unsigned int /// @param argc The length of `argv``. /// @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. +/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function"))) extern uint32_t _call_function( @@ -197,7 +197,7 @@ extern uint32_t _call_function( /// @param argc The length of `argv``. /// @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. +/// @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 uint32_t _call_function_no_catch( @@ -215,7 +215,7 @@ extern uint32_t _call_function_no_catch( /// @param argc The length of `argv``. /// @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. +/// @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 uint32_t _call_function_with_this( @@ -234,7 +234,7 @@ extern uint32_t _call_function_with_this( /// @param argc The length of `argv``. /// @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. +/// @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 uint32_t _call_function_with_this_no_catch( From 7532e6a3b9a1b56f6803ab17bcdb9660d5e17668 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 19 Aug 2022 00:09:55 +0900 Subject: [PATCH 19/20] Use writeAndReturnKindBits in write --- Runtime/src/js-value.ts | 54 +------------------------ Sources/JavaScriptKit/Runtime/index.js | 52 +----------------------- Sources/JavaScriptKit/Runtime/index.mjs | 52 +----------------------- 3 files changed, 6 insertions(+), 152 deletions(-) diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 76b554a28..e3dda96db 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -78,60 +78,10 @@ export const write = ( is_exception: boolean, memory: Memory ) => { - const exceptionBit = (is_exception ? 1 : 0) << 31; - if (value === null) { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Null); - return; - } - - const writeRef = (kind: 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 | Kind.Boolean); - memory.writeUint32(payload1_ptr, value ? 1 : 0); - break; - } - case "number": { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Number); - memory.writeFloat64(payload2_ptr, value); - break; - } - case "string": { - writeRef(Kind.String); - break; - } - case "undefined": { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Undefined); - break; - } - case "object": { - writeRef(Kind.Object); - break; - } - case "function": { - writeRef(Kind.Function); - break; - } - case "symbol": { - writeRef(Kind.Symbol); - break; - } - case "bigint": { - writeRef(Kind.BigInt); - break; - } - default: - assertNever(type, `Type "${type}" is not supported yet`); - } + const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + memory.writeUint32(kind_ptr, kind); }; -/// This is a fast version of the above `write` function. -/// Please synchronize with the above `write` function if you change either. export const writeAndReturnKindBits = ( value: any, payload1_ptr: pointer, diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index f9e273d21..ec517f3e3 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -72,57 +72,9 @@ 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`); - } + const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + memory.writeUint32(kind_ptr, kind); }; - /// This is a fast version of the above `write` function. - /// Please synchronize with the above `write` function if you change either. const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 32e90f739..2592c82be 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -66,57 +66,9 @@ const decodeArray = (ptr, length, 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`); - } + const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + memory.writeUint32(kind_ptr, kind); }; -/// This is a fast version of the above `write` function. -/// Please synchronize with the above `write` function if you change either. const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => { const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { From a9843eeeed988ddf68f78d170e34858876a4cc6e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 19 Aug 2022 00:14:25 +0900 Subject: [PATCH 20/20] Add rationale comments for write --- Runtime/src/js-value.ts | 4 ++++ Sources/JavaScriptKit/Runtime/index.js | 4 ++++ Sources/JavaScriptKit/Runtime/index.mjs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index e3dda96db..9ff3d065e 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -70,6 +70,10 @@ export const decodeArray = (ptr: pointer, length: number, memory: 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, diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index ec517f3e3..02dc9382e 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -71,6 +71,10 @@ } 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); diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 2592c82be..823ffca60 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -65,6 +65,10 @@ const decodeArray = (ptr, length, 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);