From bb47d01e8dbcebb3ae204122cc359ba2598d7396 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 21 Mar 2025 18:27:32 +0000 Subject: [PATCH 1/3] Use `elementsEqual` in `JSString: Equatable` conformance --- Sources/JavaScriptKit/FundamentalObjects/JSString.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index cd88a530..9c237085 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -77,7 +77,11 @@ public struct JSString: LosslessStringConvertible, Equatable { /// - lhs: A string to compare. /// - rhs: Another string to compare. public static func == (lhs: JSString, rhs: JSString) -> Bool { - return lhs.guts.buffer == rhs.guts.buffer + guard !(lhs.guts.shouldDeallocateRef && rhs.guts.shouldDeallocateRef) else { + return lhs.guts.jsRef == rhs.guts.jsRef + } + + return lhs.guts.buffer.utf8.elementsEqual(rhs.guts.buffer.utf8) } } From 0581f76930152dc2b8c687343b1275e2c38c9a1e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 15:01:52 +0000 Subject: [PATCH 2/3] Use JS's `==` operator for `JSString` equality comparison --- Runtime/src/index.ts | 7 +++++++ Runtime/src/types.ts | 1 + .../JavaScriptKit/FundamentalObjects/JSString.swift | 6 +----- Sources/_CJavaScriptKit/include/_CJavaScriptKit.h | 10 ++++++++++ Tests/JavaScriptKitTests/JSStringTests.swift | 13 +++++++++++++ 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 Tests/JavaScriptKitTests/JSStringTests.swift diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 3f23ed75..fef4c2f2 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -479,6 +479,13 @@ export class SwiftRuntime { return obj instanceof constructor; }, + swjs_value_equals: (lhs_ref: ref, rhs_ref: ref) => { + const memory = this.memory; + const lhs = memory.getObject(lhs_ref); + const rhs = memory.getObject(rhs_ref); + return lhs == rhs; + }, + swjs_create_function: ( host_func_id: number, line: number, diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 587b6077..b81818ad 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -96,6 +96,7 @@ export interface ImportedFunctions { exception_payload2_ptr: pointer ): number; swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean; + swjs_value_equals(lhs_ref: ref, rhs_ref: ref): boolean; swjs_create_function(host_func_id: number, line: number, file: ref): number; swjs_create_typed_array( constructor_ref: ref, diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift index 9c237085..b4ad1023 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -77,11 +77,7 @@ public struct JSString: LosslessStringConvertible, Equatable { /// - lhs: A string to compare. /// - rhs: Another string to compare. public static func == (lhs: JSString, rhs: JSString) -> Bool { - guard !(lhs.guts.shouldDeallocateRef && rhs.guts.shouldDeallocateRef) else { - return lhs.guts.jsRef == rhs.guts.jsRef - } - - return lhs.guts.buffer.utf8.elementsEqual(rhs.guts.buffer.utf8) + return swjs_value_equals(lhs.guts.jsRef, rhs.guts.jsRef) } } diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 2b96a81e..05f4ed0f 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -257,6 +257,16 @@ IMPORT_JS_FUNCTION(swjs_call_throwing_new, JavaScriptObjectRef, (const JavaScrip IMPORT_JS_FUNCTION(swjs_instanceof, bool, (const JavaScriptObjectRef obj, const JavaScriptObjectRef constructor)) +/// Acts like JavaScript `==` operator. +/// Performs "==" comparison, a.k.a the "Abstract Equality Comparison" +/// algorithm defined in the ECMAScript. +/// https://262.ecma-international.org/11.0/#sec-abstract-equality-comparison +/// +/// @param lhs The left-hand side value to compare. +/// @param rhs The right-hand side value to compare. +/// @result Return `true` if `lhs` is `==` to `rhs`. Return `false` if not. +IMPORT_JS_FUNCTION(swjs_value_equals, bool, (const JavaScriptObjectRef lhs, const JavaScriptObjectRef rhs)) + /// Creates a JavaScript thunk function that calls Swift side closure. /// See also comments on JSFunction.swift /// diff --git a/Tests/JavaScriptKitTests/JSStringTests.swift b/Tests/JavaScriptKitTests/JSStringTests.swift new file mode 100644 index 00000000..456c2414 --- /dev/null +++ b/Tests/JavaScriptKitTests/JSStringTests.swift @@ -0,0 +1,13 @@ +import JavaScriptKit +import XCTest + +final class JSStringTests: XCTestCase { + func testEquatable() { + let string1 = JSString("Hello, world!") + let string2 = JSString("Hello, world!") + let string3 = JSString("Hello, world") + XCTAssertEqual(string1, string1) + XCTAssertEqual(string1, string2) + XCTAssertNotEqual(string1, string3) + } +} From 90d7238ebaf2a430a3176cf02f80f5cf5419b3e1 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 15:41:19 +0000 Subject: [PATCH 3/3] make regenerate_swiftpm_resources --- Sources/JavaScriptKit/Runtime/index.d.ts | 1 + Sources/JavaScriptKit/Runtime/index.js | 6 ++++++ Sources/JavaScriptKit/Runtime/index.mjs | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/Sources/JavaScriptKit/Runtime/index.d.ts b/Sources/JavaScriptKit/Runtime/index.d.ts index 5bfa4c24..adfa8161 100644 --- a/Sources/JavaScriptKit/Runtime/index.d.ts +++ b/Sources/JavaScriptKit/Runtime/index.d.ts @@ -50,6 +50,7 @@ interface ImportedFunctions { swjs_call_new(ref: number, argv: pointer, argc: number): number; swjs_call_throwing_new(ref: number, argv: pointer, argc: number, exception_kind_ptr: pointer, exception_payload1_ptr: pointer, exception_payload2_ptr: pointer): number; swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean; + swjs_value_equals(lhs_ref: ref, rhs_ref: ref): boolean; swjs_create_function(host_func_id: number, line: number, file: ref): number; swjs_create_typed_array(constructor_ref: ref, elementsPtr: pointer, length: number): number; swjs_load_typed_array(ref: ref, buffer: pointer): void; diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index a3bc3139..840d843b 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -604,6 +604,12 @@ const constructor = memory.getObject(constructor_ref); return obj instanceof constructor; }, + swjs_value_equals: (lhs_ref, rhs_ref) => { + const memory = this.memory; + const lhs = memory.getObject(lhs_ref); + const rhs = memory.getObject(rhs_ref); + return lhs == rhs; + }, swjs_create_function: (host_func_id, line, file) => { var _a; const fileString = this.memory.getObject(file); diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index ba1b6bea..50799f2c 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -598,6 +598,12 @@ class SwiftRuntime { const constructor = memory.getObject(constructor_ref); return obj instanceof constructor; }, + swjs_value_equals: (lhs_ref, rhs_ref) => { + const memory = this.memory; + const lhs = memory.getObject(lhs_ref); + const rhs = memory.getObject(rhs_ref); + return lhs == rhs; + }, swjs_create_function: (host_func_id, line, file) => { var _a; const fileString = this.memory.getObject(file);