diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift index bd0831e7d..8d8dda331 100644 --- a/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift +++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift @@ -6,11 +6,15 @@ func testI64() throws { func expectPassesThrough(signed value: Int64) throws { let bigInt = JSBigInt(value) try expectEqual(bigInt.description, value.description) + let bigInt2 = JSBigInt(_slowBridge: value) + try expectEqual(bigInt2.description, value.description) } func expectPassesThrough(unsigned value: UInt64) throws { let bigInt = JSBigInt(unsigned: value) try expectEqual(bigInt.description, value.description) + let bigInt2 = JSBigInt(_slowBridge: value) + try expectEqual(bigInt2.description, value.description) } try expectPassesThrough(signed: 0) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 77264223c..8f90776f1 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -76,7 +76,12 @@ export class SwiftRuntime { return this._closureDeallocator; } - private callHostFunction(host_func_id: number, line: number, file: string, args: any[]) { + private callHostFunction( + host_func_id: number, + line: number, + file: string, + args: any[] + ) { const argc = args.length; const argv = this.exports.swjs_prepare_host_function_call(argc); for (let index = 0; index < args.length; index++) { @@ -103,7 +108,9 @@ export class SwiftRuntime { callback_func_ref ); if (alreadyReleased) { - throw new Error(`The JSClosure has been already released by Swift side. The closure is created at ${file}:${line}`); + throw new Error( + `The JSClosure has been already released by Swift side. The closure is created at ${file}:${line}` + ); } this.exports.swjs_cleanup_host_function_call(argv); return output; @@ -382,7 +389,11 @@ export class SwiftRuntime { return obj instanceof constructor; }, - swjs_create_function: (host_func_id: number, line: number, file: ref) => { + swjs_create_function: ( + host_func_id: number, + line: number, + file: ref + ) => { const fileString = this.memory.getObject(file) as string; const func = (...args: any[]) => this.callHostFunction(host_func_id, line, fileString, args); @@ -436,5 +447,13 @@ export class SwiftRuntime { return BigInt.asIntN(64, object); } }, + swjs_i64_to_bigint_slow: (lower, upper, signed) => { + const value = + BigInt.asUintN(32, BigInt(lower)) + + (BigInt.asUintN(32, BigInt(upper)) << BigInt(32)); + return this.memory.retain( + signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value) + ); + }, }; } diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 913837e32..a6e3dd1d2 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -105,6 +105,7 @@ export interface ImportedFunctions { swjs_release(ref: number): void; swjs_i64_to_bigint(value: bigint, signed: bool): ref; swjs_bigint_to_i64(ref: ref, signed: bool): bigint; + swjs_i64_to_bigint_slow(lower: number, upper: number, signed: bool): ref; } export const enum LibraryFeatures { diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSBigInt.swift b/Sources/JavaScriptKit/FundamentalObjects/JSBigInt.swift index 4513c14a7..724e969a1 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSBigInt.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSBigInt.swift @@ -7,6 +7,19 @@ public final class JSBigInt: JSObject { override public init(id: JavaScriptObjectRef) { super.init(id: id) } + + /// Instantiate a new `JSBigInt` with given Int64 value in a slow path + /// This doesn't require [JS-BigInt-integration](https://github.com/WebAssembly/JS-BigInt-integration) feature. + public init(_slowBridge value: Int64) { + let value = UInt64(bitPattern: value) + super.init(id: _i64_to_bigint_slow(UInt32(value & 0xffffffff), UInt32(value >> 32), true)) + } + + /// Instantiate a new `JSBigInt` with given UInt64 value in a slow path + /// This doesn't require [JS-BigInt-integration](https://github.com/WebAssembly/JS-BigInt-integration) feature. + public init(_slowBridge value: UInt64) { + super.init(id: _i64_to_bigint_slow(UInt32(value & 0xffffffff), UInt32(value >> 32), false)) + } override public class func construct(from value: JSValue) -> Self? { value.bigInt as? Self diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index eb55ebc58..43158fbab 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -358,6 +358,11 @@ return BigInt.asIntN(64, object); } }, + swjs_i64_to_bigint_slow: (lower, upper, signed) => { + const value = BigInt.asUintN(32, BigInt(lower)) + + (BigInt.asUintN(32, BigInt(upper)) << BigInt(32)); + return this.memory.retain(signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value)); + }, }; this._instance = null; this._memory = null; diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 8bd8043d9..299bafdb5 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -352,6 +352,11 @@ class SwiftRuntime { return BigInt.asIntN(64, object); } }, + swjs_i64_to_bigint_slow: (lower, upper, signed) => { + const value = BigInt.asUintN(32, BigInt(lower)) + + (BigInt.asUintN(32, BigInt(upper)) << BigInt(32)); + return this.memory.retain(signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value)); + }, }; this._instance = null; this._memory = null; diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index a3c8aeb1a..5556cdba8 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -46,6 +46,9 @@ import _CJavaScriptKit _: JavaScriptObjectRef, _: UnsafeMutablePointer! ) { fatalError() } + func _i64_to_bigint_slow( + _: UInt32, _: UInt32, _: Bool + ) -> JavaScriptObjectRef { fatalError() } func _call_function( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index fb07d1e09..59923e02d 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -160,6 +160,16 @@ __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_load_string"))) extern void _load_string(const JavaScriptObjectRef bytes, unsigned char *buffer); +/// Converts the provided Int64 or UInt64 to a BigInt in slow path by splitting 64bit integer to two 32bit integers +/// to avoid depending on [JS-BigInt-integration](https://github.com/WebAssembly/JS-BigInt-integration) feature +/// +/// @param lower The lower 32bit of the value to convert. +/// @param upper The upper 32bit of the value to convert. +/// @param is_signed Whether to treat the value as a signed integer or not. +__attribute__((__import_module__("javascript_kit"), + __import_name__("swjs_i64_to_bigint_slow"))) +extern JavaScriptObjectRef _i64_to_bigint_slow(unsigned int lower, unsigned int upper, bool is_signed); + /// `_call_function` calls JavaScript function with given arguments list. /// /// @param ref The target JavaScript function to call.