diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 3f23ed753..fef4c2f21 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 587b60770..b81818ade 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 cd88a5302..b4ad10237 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift @@ -77,7 +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 { - return lhs.guts.buffer == rhs.guts.buffer + return swjs_value_equals(lhs.guts.jsRef, rhs.guts.jsRef) } } diff --git a/Sources/JavaScriptKit/Runtime/index.d.ts b/Sources/JavaScriptKit/Runtime/index.d.ts index 5bfa4c242..adfa8161d 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 a3bc31397..840d843b5 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 ba1b6beaf..50799f2c1 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); diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 2b96a81ea..05f4ed0f8 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 000000000..456c24147 --- /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) + } +}