From 6eb534b932fc5843492aac731eb3901cb2d9f8e8 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 21 Mar 2025 18:10:49 +0000 Subject: [PATCH 01/18] Add `JSDictionary` type for Embedded Swift compat --- Runtime/src/index.ts | 2 ++ Runtime/src/types.ts | 1 + .../FundamentalObjects/JSDictionary.swift | 34 +++++++++++++++++++ .../_CJavaScriptKit/include/_CJavaScriptKit.h | 2 ++ 4 files changed, 39 insertions(+) create mode 100644 Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 3f23ed753..2bd4ffcef 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -517,6 +517,8 @@ export class SwiftRuntime { return this.memory.retain(array.slice()); }, + swjs_create_object: () => { return this.memory.retain({}); }, + swjs_load_typed_array: (ref: ref, buffer: pointer) => { const memory = this.memory; const typedArray = memory.getObject(ref); diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index 587b60770..5348f4c16 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -102,6 +102,7 @@ export interface ImportedFunctions { elementsPtr: pointer, length: number ): number; + swjs_create_object(): number; swjs_load_typed_array(ref: ref, buffer: pointer): void; swjs_release(ref: number): void; swjs_release_remote(tid: number, ref: number): void; diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift b/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift new file mode 100644 index 000000000..51957b279 --- /dev/null +++ b/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift @@ -0,0 +1,34 @@ +import _CJavaScriptKit + +public final class JSDictionary { + private let ref: JSObject + + init(ref: JSObject) { self.ref = ref } + + public init() { ref = JSObject(id: swjs_create_object()) } + + public subscript(key: String) -> JSValue { + get { ref[dynamicMember: key] } + set { ref[dynamicMember: key] = newValue } + } +} + +extension JSDictionary: ExpressibleByDictionaryLiteral { + public convenience init(dictionaryLiteral elements: (String, JSValue)...) { + self.init() + + for (key, value) in elements { self[key] = value } + } +} + +extension JSDictionary: ConvertibleToJSValue { + public var jsValue: JSValue { .object(ref) } +} + +extension JSDictionary: ConstructibleFromJSValue { + public static func construct(from value: JSValue) -> JSDictionary? { + guard let object = value.object else { return nil } + + return JSDictionary(ref: object) + } +} diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index 2b96a81ea..6416379f8 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -314,6 +314,8 @@ IMPORT_JS_FUNCTION(swjs_terminate_worker_thread, void, (int tid)) IMPORT_JS_FUNCTION(swjs_get_worker_thread_id, int, (void)) +IMPORT_JS_FUNCTION(swjs_create_object, JavaScriptObjectRef, (void)) + int swjs_get_worker_thread_id_cached(void); /// Requests sending a JavaScript object to another worker thread. From 5c7e75e526fe6cf971d3653bb38e3de8494651e2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 15:13:37 +0000 Subject: [PATCH 02/18] ./Utilities/format.swift --- .../FundamentalObjects/JSDictionary.swift | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift b/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift index 51957b279..d56cedf17 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift @@ -1,34 +1,34 @@ import _CJavaScriptKit public final class JSDictionary { - private let ref: JSObject + private let ref: JSObject - init(ref: JSObject) { self.ref = ref } + init(ref: JSObject) { self.ref = ref } - public init() { ref = JSObject(id: swjs_create_object()) } + public init() { ref = JSObject(id: swjs_create_object()) } - public subscript(key: String) -> JSValue { - get { ref[dynamicMember: key] } - set { ref[dynamicMember: key] = newValue } - } + public subscript(key: String) -> JSValue { + get { ref[dynamicMember: key] } + set { ref[dynamicMember: key] = newValue } + } } extension JSDictionary: ExpressibleByDictionaryLiteral { - public convenience init(dictionaryLiteral elements: (String, JSValue)...) { - self.init() + public convenience init(dictionaryLiteral elements: (String, JSValue)...) { + self.init() - for (key, value) in elements { self[key] = value } - } + for (key, value) in elements { self[key] = value } + } } extension JSDictionary: ConvertibleToJSValue { - public var jsValue: JSValue { .object(ref) } + public var jsValue: JSValue { .object(ref) } } extension JSDictionary: ConstructibleFromJSValue { - public static func construct(from value: JSValue) -> JSDictionary? { - guard let object = value.object else { return nil } + public static func construct(from value: JSValue) -> JSDictionary? { + guard let object = value.object else { return nil } - return JSDictionary(ref: object) - } + return JSDictionary(ref: object) + } } From 0d7d3de483bc687942f90e0d0c18ec34ff9f6319 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 15:37:57 +0000 Subject: [PATCH 03/18] make regenerate_swiftpm_resources --- Sources/JavaScriptKit/Runtime/index.d.ts | 1 + Sources/JavaScriptKit/Runtime/index.js | 1 + Sources/JavaScriptKit/Runtime/index.mjs | 1 + 3 files changed, 3 insertions(+) diff --git a/Sources/JavaScriptKit/Runtime/index.d.ts b/Sources/JavaScriptKit/Runtime/index.d.ts index 5bfa4c242..6bffb1ba4 100644 --- a/Sources/JavaScriptKit/Runtime/index.d.ts +++ b/Sources/JavaScriptKit/Runtime/index.d.ts @@ -52,6 +52,7 @@ interface ImportedFunctions { swjs_instanceof(obj_ref: ref, constructor_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_create_object(): number; swjs_load_typed_array(ref: ref, buffer: pointer): void; swjs_release(ref: number): void; swjs_release_remote(tid: number, ref: number): void; diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index a3bc31397..0ef6c9b32 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -627,6 +627,7 @@ // Call `.slice()` to copy the memory return this.memory.retain(array.slice()); }, + swjs_create_object: () => { return this.memory.retain({}); }, swjs_load_typed_array: (ref, buffer) => { const memory = this.memory; const typedArray = memory.getObject(ref); diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index ba1b6beaf..8f85b2c47 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -621,6 +621,7 @@ class SwiftRuntime { // Call `.slice()` to copy the memory return this.memory.retain(array.slice()); }, + swjs_create_object: () => { return this.memory.retain({}); }, swjs_load_typed_array: (ref, buffer) => { const memory = this.memory; const typedArray = memory.getObject(ref); From ec0bbe6b5ebd1da915ef7c0a35518f3f750be583 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 15:39:57 +0000 Subject: [PATCH 04/18] Make JSObject conform to ExpressibleByDictionaryLiteral directly --- .../FundamentalObjects/JSClosure.swift | 10 ++++++ .../FundamentalObjects/JSDictionary.swift | 34 ------------------- .../FundamentalObjects/JSObject.swift | 15 +++++++- .../FundamentalObjects/JSSymbol.swift | 5 +++ Tests/JavaScriptKitTests/JSObjectTests.swift | 28 +++++++++++++++ 5 files changed, 57 insertions(+), 35 deletions(-) delete mode 100644 Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift create mode 100644 Tests/JavaScriptKitTests/JSObjectTests.swift diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift index 66ce009bf..fa713c3b9 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift @@ -35,6 +35,11 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol { ) } + @available(*, unavailable, message: "JSOneshotClosure does not support dictionary literal initialization") + public required init(dictionaryLiteral elements: (String, JSValue)...) { + fatalError("JSOneshotClosure does not support dictionary literal initialization") + } + #if compiler(>=5.5) && !hasFeature(Embedded) @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public static func async(_ body: sending @escaping (sending [JSValue]) async throws -> JSValue) -> JSOneshotClosure @@ -122,6 +127,11 @@ public class JSClosure: JSFunction, JSClosureProtocol { Self.sharedClosures.wrappedValue[hostFuncRef] = (self, body) } + @available(*, unavailable, message: "JSClosure does not support dictionary literal initialization") + public required init(dictionaryLiteral elements: (String, JSValue)...) { + fatalError("JSClosure does not support dictionary literal initialization") + } + #if compiler(>=5.5) && !hasFeature(Embedded) @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public static func async(_ body: @Sendable @escaping (sending [JSValue]) async throws -> JSValue) -> JSClosure { diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift b/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift deleted file mode 100644 index d56cedf17..000000000 --- a/Sources/JavaScriptKit/FundamentalObjects/JSDictionary.swift +++ /dev/null @@ -1,34 +0,0 @@ -import _CJavaScriptKit - -public final class JSDictionary { - private let ref: JSObject - - init(ref: JSObject) { self.ref = ref } - - public init() { ref = JSObject(id: swjs_create_object()) } - - public subscript(key: String) -> JSValue { - get { ref[dynamicMember: key] } - set { ref[dynamicMember: key] = newValue } - } -} - -extension JSDictionary: ExpressibleByDictionaryLiteral { - public convenience init(dictionaryLiteral elements: (String, JSValue)...) { - self.init() - - for (key, value) in elements { self[key] = value } - } -} - -extension JSDictionary: ConvertibleToJSValue { - public var jsValue: JSValue { .object(ref) } -} - -extension JSDictionary: ConstructibleFromJSValue { - public static func construct(from value: JSValue) -> JSDictionary? { - guard let object = value.object else { return nil } - - return JSDictionary(ref: object) - } -} diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift index 33a20f3b5..12dbf9e02 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift @@ -15,7 +15,7 @@ import _CJavaScriptKit /// The lifetime of this object is managed by the JavaScript and Swift runtime bridge library with /// reference counting system. @dynamicMemberLookup -public class JSObject: Equatable { +public class JSObject: Equatable, ExpressibleByDictionaryLiteral { internal static var constructor: JSFunction { _constructor.wrappedValue } private static let _constructor = LazyThreadLocal(initialize: { JSObject.global.Object.function! }) @@ -38,6 +38,19 @@ public class JSObject: Equatable { #endif } + /// Creates an empty JavaScript object. + public convenience init() { + self.init(id: swjs_create_object()) + } + + /// Creates a new object with the key-value pairs in the dictionary literal. + /// + /// - Parameter elements: A variadic list of key-value pairs where all keys are strings + public convenience required init(dictionaryLiteral elements: (String, JSValue)...) { + self.init() + for (key, value) in elements { self[key] = value } + } + /// Asserts that the object is being accessed from the owner thread. /// /// - Parameter hint: A string to provide additional context for debugging. diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift index 42f63e010..a9461317b 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift @@ -24,6 +24,11 @@ public class JSSymbol: JSObject { super.init(id: id) } + @available(*, unavailable, message: "JSSymbol does not support dictionary literal initialization") + public required init(dictionaryLiteral elements: (String, JSValue)...) { + fatalError("JSSymbol does not support dictionary literal initialization") + } + public static func `for`(key: JSString) -> JSSymbol { Symbol.for!(key).symbol! } diff --git a/Tests/JavaScriptKitTests/JSObjectTests.swift b/Tests/JavaScriptKitTests/JSObjectTests.swift new file mode 100644 index 000000000..e283da608 --- /dev/null +++ b/Tests/JavaScriptKitTests/JSObjectTests.swift @@ -0,0 +1,28 @@ +import JavaScriptKit +import XCTest + +final class JSObjectTests: XCTestCase { + func testEmptyObject() { + let object = JSObject() + let keys = JSObject.global.Object.function!.keys.function!(object) + XCTAssertEqual(keys.array?.count, 0) + } + + func testInitWithDictionaryLiteral() { + let object: JSObject = [ + "key1": 1, + "key2": "value2", + "key3": .boolean(true), + "key4": .object(JSObject()), + "key5": [1, 2, 3].jsValue, + "key6": ["key": "value"].jsValue, + ] + XCTAssertEqual(object.key1, .number(1)) + XCTAssertEqual(object.key2, "value2") + XCTAssertEqual(object.key3, .boolean(true)) + let getKeys = JSObject.global.Object.function!.keys.function! + XCTAssertEqual(getKeys(object.key4).array?.count, 0) + XCTAssertEqual(object.key5.array.map(Array.init), [1, 2, 3]) + XCTAssertEqual(object.key6.object?.key, "value") + } +} From bb47d01e8dbcebb3ae204122cc359ba2598d7396 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 21 Mar 2025 18:27:32 +0000 Subject: [PATCH 05/18] 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 cd88a5302..9c2370859 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 06/18] 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 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 9c2370859..b4ad10237 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 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) + } +} From 90d7238ebaf2a430a3176cf02f80f5cf5419b3e1 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 15:41:19 +0000 Subject: [PATCH 07/18] 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 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); From 4c7ea176f58180421ac00a3272d05c2fce6df386 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 09:23:51 +0000 Subject: [PATCH 08/18] Unlock `JSTypedArray` for Embedded Swift --- .../BasicObjects/JSTypedArray.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift index 2d6fc33b8..3104fa1c5 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift @@ -1,11 +1,11 @@ // // Created by Manuel Burghard. Licensed unter MIT. // -#if !hasFeature(Embedded) import _CJavaScriptKit /// A protocol that allows a Swift numeric type to be mapped to the JavaScript TypedArray that holds integers of its type -public protocol TypedArrayElement: ConvertibleToJSValue, ConstructibleFromJSValue { +public protocol TypedArrayElement { + associatedtype Element: ConvertibleToJSValue, ConstructibleFromJSValue = Self /// The constructor function for the TypedArray class for this particular kind of number static var typedArrayClass: JSFunction { get } } @@ -13,8 +13,9 @@ public protocol TypedArrayElement: ConvertibleToJSValue, ConstructibleFromJSValu /// A wrapper around all [JavaScript `TypedArray` /// classes](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) /// that exposes their properties in a type-safe way. -public class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiteral where Element: TypedArrayElement { - public class var constructor: JSFunction? { Element.typedArrayClass } +public final class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiteral where Traits: TypedArrayElement { + public typealias Element = Traits.Element + public class var constructor: JSFunction? { Traits.typedArrayClass } public var jsObject: JSObject public subscript(_ index: Int) -> Element { @@ -176,13 +177,6 @@ extension UInt8: TypedArrayElement { public static var typedArrayClass: JSFunction { JSObject.global.Uint8Array.function! } } -/// A wrapper around [the JavaScript `Uint8ClampedArray` -/// class](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) -/// that exposes its properties in a type-safe and Swifty way. -public class JSUInt8ClampedArray: JSTypedArray { - override public class var constructor: JSFunction? { JSObject.global.Uint8ClampedArray.function! } -} - extension Int16: TypedArrayElement { public static var typedArrayClass: JSFunction { JSObject.global.Int16Array.function! } } @@ -206,4 +200,10 @@ extension Float32: TypedArrayElement { extension Float64: TypedArrayElement { public static var typedArrayClass: JSFunction { JSObject.global.Float64Array.function! } } -#endif + +public enum JSUInt8Clamped: TypedArrayElement { + public typealias Element = UInt8 + public static var typedArrayClass: JSFunction { JSObject.global.Uint8ClampedArray.function! } +} + +public typealias JSUInt8ClampedArray = JSTypedArray From f41f2340279641428ef3a3072867489015926660 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 09:24:30 +0000 Subject: [PATCH 09/18] Add an example of using TypedArray in an embedded app --- .../Embedded/Sources/EmbeddedApp/main.swift | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/Examples/Embedded/Sources/EmbeddedApp/main.swift b/Examples/Embedded/Sources/EmbeddedApp/main.swift index 3f8c18ca6..37b2334ba 100644 --- a/Examples/Embedded/Sources/EmbeddedApp/main.swift +++ b/Examples/Embedded/Sources/EmbeddedApp/main.swift @@ -11,18 +11,49 @@ var divElement = document.createElement("div") divElement.innerText = .string("Count \(count)") _ = document.body.appendChild(divElement) -var buttonElement = document.createElement("button") -buttonElement.innerText = "Click me" -buttonElement.onclick = JSValue.object( +var clickMeElement = document.createElement("button") +clickMeElement.innerText = "Click me" +clickMeElement.onclick = JSValue.object( JSClosure { _ in count += 1 divElement.innerText = .string("Count \(count)") return .undefined } ) +_ = document.body.appendChild(clickMeElement) -_ = document.body.appendChild(buttonElement) +var encodeResultElement = document.createElement("pre") +var textInputElement = document.createElement("input") +textInputElement.type = "text" +textInputElement.placeholder = "Enter text to encode to UTF-8" +textInputElement.oninput = JSValue.object( + JSClosure { _ in + let textEncoder = JSObject.global.TextEncoder.function!.new() + let encode = textEncoder.encode.function! + let encodedData = JSTypedArray( + unsafelyWrapping: encode(this: textEncoder, textInputElement.value).object! + ) + encodeResultElement.innerText = .string( + encodedData.withUnsafeBytes { bytes in + bytes.map { hex($0) }.joined(separator: " ") + } + ) + return .undefined + } +) +let encoderContainer = document.createElement("div") +_ = encoderContainer.appendChild(textInputElement) +_ = encoderContainer.appendChild(encodeResultElement) +_ = document.body.appendChild(encoderContainer) func print(_ message: String) { _ = JSObject.global.console.log(message) } + +func hex(_ value: UInt8) -> String { + var result = "0x" + let hexChars: [Character] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"] + result.append(hexChars[Int(value / 16)]) + result.append(hexChars[Int(value % 16)]) + return result +} From af86aee0601a84b99fc8d7e88eba4fd27d8d3a8c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 09:41:47 +0000 Subject: [PATCH 10/18] Fix JSTypedArrayTests to follow API change --- Tests/JavaScriptKitTests/JSTypedArrayTests.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/JavaScriptKitTests/JSTypedArrayTests.swift b/Tests/JavaScriptKitTests/JSTypedArrayTests.swift index 0465b1e43..a4649879e 100644 --- a/Tests/JavaScriptKitTests/JSTypedArrayTests.swift +++ b/Tests/JavaScriptKitTests/JSTypedArrayTests.swift @@ -17,8 +17,8 @@ final class JSTypedArrayTests: XCTestCase { } func testTypedArray() { - func checkArray(_ array: [T]) where T: TypedArrayElement & Equatable { - XCTAssertEqual(toString(JSTypedArray(array).jsValue.object!), jsStringify(array)) + func checkArray(_ array: [T]) where T: TypedArrayElement & Equatable, T.Element == T { + XCTAssertEqual(toString(JSTypedArray(array).jsValue.object!), jsStringify(array)) checkArrayUnsafeBytes(array) } @@ -30,20 +30,20 @@ final class JSTypedArrayTests: XCTestCase { array.map({ String(describing: $0) }).joined(separator: ",") } - func checkArrayUnsafeBytes(_ array: [T]) where T: TypedArrayElement & Equatable { - let copyOfArray: [T] = JSTypedArray(array).withUnsafeBytes { buffer in + func checkArrayUnsafeBytes(_ array: [T]) where T: TypedArrayElement & Equatable, T.Element == T { + let copyOfArray: [T] = JSTypedArray(array).withUnsafeBytes { buffer in Array(buffer) } XCTAssertEqual(copyOfArray, array) } let numbers = [UInt8](0...255) - let typedArray = JSTypedArray(numbers) + let typedArray = JSTypedArray(numbers) XCTAssertEqual(typedArray[12], 12) XCTAssertEqual(numbers.count, typedArray.lengthInBytes) let numbersSet = Set(0...255) - let typedArrayFromSet = JSTypedArray(numbersSet) + let typedArrayFromSet = JSTypedArray(numbersSet) XCTAssertEqual(typedArrayFromSet.jsObject.length, 256) XCTAssertEqual(typedArrayFromSet.lengthInBytes, 256 * MemoryLayout.size) @@ -63,7 +63,7 @@ final class JSTypedArrayTests: XCTestCase { 0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude, .leastNormalMagnitude, 42, ] - let jsFloat32Array = JSTypedArray(float32Array) + let jsFloat32Array = JSTypedArray(float32Array) for (i, num) in float32Array.enumerated() { XCTAssertEqual(num, jsFloat32Array[i]) } @@ -72,7 +72,7 @@ final class JSTypedArrayTests: XCTestCase { 0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude, .leastNormalMagnitude, 42, ] - let jsFloat64Array = JSTypedArray(float64Array) + let jsFloat64Array = JSTypedArray(float64Array) for (i, num) in float64Array.enumerated() { XCTAssertEqual(num, jsFloat64Array[i]) } From e99b99096ed322b19fefef49ccdbdab795aa1be3 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 09:52:56 +0000 Subject: [PATCH 11/18] Remove possible use of `fatalError` in `JSTypedArray` --- .../BasicObjects/JSTypedArray.swift | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift index 3104fa1c5..47919b17d 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift @@ -140,33 +140,28 @@ public final class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiter } } -// MARK: - Int and UInt support - -// FIXME: Should be updated to support wasm64 when that becomes available. -func valueForBitWidth(typeName: String, bitWidth: Int, when32: T) -> T { - if bitWidth == 32 { - return when32 - } else if bitWidth == 64 { - fatalError("64-bit \(typeName)s are not yet supported in JSTypedArray") - } else { - fatalError( - "Unsupported bit width for type \(typeName): \(bitWidth) (hint: stick to fixed-size \(typeName)s to avoid this issue)" - ) - } -} - extension Int: TypedArrayElement { - public static var typedArrayClass: JSFunction { _typedArrayClass.wrappedValue } - private static let _typedArrayClass = LazyThreadLocal(initialize: { - valueForBitWidth(typeName: "Int", bitWidth: Int.bitWidth, when32: JSObject.global.Int32Array).function! - }) + public static var typedArrayClass: JSFunction { + #if _pointerBitWidth(_32) + return JSObject.global.Int32Array.function! + #elseif _pointerBitWidth(_64) + return JSObject.global.Int64Array.function! + #else + #error("Unsupported pointer width") + #endif + } } extension UInt: TypedArrayElement { - public static var typedArrayClass: JSFunction { _typedArrayClass.wrappedValue } - private static let _typedArrayClass = LazyThreadLocal(initialize: { - valueForBitWidth(typeName: "UInt", bitWidth: Int.bitWidth, when32: JSObject.global.Uint32Array).function! - }) + public static var typedArrayClass: JSFunction { + #if _pointerBitWidth(_32) + return JSObject.global.Uint32Array.function! + #elseif _pointerBitWidth(_64) + return JSObject.global.Uint64Array.function! + #else + #error("Unsupported pointer width") + #endif + } } extension Int8: TypedArrayElement { From a4376e3e36532673c8907c4cd0b8ea88e5fa273a Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 12:14:14 +0000 Subject: [PATCH 12/18] Fix potential use-after-free in JSString The guts' lifetime was not guaranteed to be longer than `swjs_value_equals` call, which could lead to a use-after-free. --- 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 b4ad10237..f084ffc81 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 swjs_value_equals(lhs.guts.jsRef, rhs.guts.jsRef) + withExtendedLifetime(lhs.guts) { lhsGuts in + withExtendedLifetime(rhs.guts) { rhsGuts in + return swjs_value_equals(lhsGuts.jsRef, rhsGuts.jsRef) + } + } } } From bb6fad8a3d1ed694d568409b6e2fb07aadfe0a77 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 10:44:28 +0000 Subject: [PATCH 13/18] Rename Package.swift -> Package@swift-6.0.swift --- Package.swift => Package@swift-6.0.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Package.swift => Package@swift-6.0.swift (100%) diff --git a/Package.swift b/Package@swift-6.0.swift similarity index 100% rename from Package.swift rename to Package@swift-6.0.swift From f9b3973044cb2cde6a335f09ddcb7c958c97a798 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 10:45:06 +0000 Subject: [PATCH 14/18] Add package-trait based Package.swift --- Package.swift | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Package.swift diff --git a/Package.swift b/Package.swift new file mode 100644 index 000000000..5a3a1ca5d --- /dev/null +++ b/Package.swift @@ -0,0 +1,94 @@ +// swift-tools-version:6.1 + +import PackageDescription + +// NOTE: needed for embedded customizations, ideally this will not be necessary at all in the future, or can be replaced with traits +let useLegacyResourceBundling = + Context.environment["JAVASCRIPTKIT_USE_LEGACY_RESOURCE_BUNDLING"].flatMap(Bool.init) ?? false + +let package = Package( + name: "JavaScriptKit", + products: [ + .library(name: "JavaScriptKit", targets: ["JavaScriptKit"]), + .library(name: "JavaScriptEventLoop", targets: ["JavaScriptEventLoop"]), + .library(name: "JavaScriptBigIntSupport", targets: ["JavaScriptBigIntSupport"]), + .library(name: "JavaScriptEventLoopTestSupport", targets: ["JavaScriptEventLoopTestSupport"]), + .plugin(name: "PackageToJS", targets: ["PackageToJS"]), + ], + traits: [ + "Embedded" + ], + targets: [ + .target( + name: "JavaScriptKit", + dependencies: ["_CJavaScriptKit"], + exclude: useLegacyResourceBundling ? [] : ["Runtime"], + resources: useLegacyResourceBundling ? [.copy("Runtime")] : [], + cSettings: [ + .unsafeFlags(["-fdeclspec"], .when(traits: ["Embedded"])) + ], + swiftSettings: [ + .enableExperimentalFeature("Embedded", .when(traits: ["Embedded"])), + .enableExperimentalFeature("Extern", .when(traits: ["Embedded"])), + .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"], .when(traits: ["Embedded"])), + ] + ), + .target(name: "_CJavaScriptKit"), + .testTarget( + name: "JavaScriptKitTests", + dependencies: ["JavaScriptKit"], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ] + ), + + .target( + name: "JavaScriptBigIntSupport", + dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"] + ), + .target(name: "_CJavaScriptBigIntSupport", dependencies: ["_CJavaScriptKit"]), + .testTarget( + name: "JavaScriptBigIntSupportTests", + dependencies: ["JavaScriptBigIntSupport", "JavaScriptKit"] + ), + + .target( + name: "JavaScriptEventLoop", + dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"] + ), + .target(name: "_CJavaScriptEventLoop"), + .testTarget( + name: "JavaScriptEventLoopTests", + dependencies: [ + "JavaScriptEventLoop", + "JavaScriptKit", + "JavaScriptEventLoopTestSupport", + ], + swiftSettings: [ + .enableExperimentalFeature("Extern") + ] + ), + .target( + name: "JavaScriptEventLoopTestSupport", + dependencies: [ + "_CJavaScriptEventLoopTestSupport", + "JavaScriptEventLoop", + ] + ), + .target(name: "_CJavaScriptEventLoopTestSupport"), + .testTarget( + name: "JavaScriptEventLoopTestSupportTests", + dependencies: [ + "JavaScriptKit", + "JavaScriptEventLoopTestSupport", + ] + ), + .plugin( + name: "PackageToJS", + capability: .command( + intent: .custom(verb: "js", description: "Convert a Swift package to a JavaScript package") + ), + sources: ["Sources"] + ), + ] +) From 64fb506b50c6ac1e35118eb01b8e471ed63a6208 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 12:11:53 +0000 Subject: [PATCH 15/18] Update Examples/Embedded to use the new `Embedded` trait --- Examples/Embedded/Package.swift | 4 ++-- Examples/Embedded/build.sh | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Examples/Embedded/Package.swift b/Examples/Embedded/Package.swift index 5ae19adc6..aae080022 100644 --- a/Examples/Embedded/Package.swift +++ b/Examples/Embedded/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:6.0 +// swift-tools-version:6.1 import PackageDescription let package = Package( name: "Embedded", dependencies: [ - .package(name: "JavaScriptKit", path: "../../"), + .package(name: "JavaScriptKit", path: "../../", traits: ["Embedded"]), .package(url: "https://github.com/swiftwasm/swift-dlmalloc", branch: "0.1.0"), ], targets: [ diff --git a/Examples/Embedded/build.sh b/Examples/Embedded/build.sh index f807cdbf5..81840e76f 100755 --- a/Examples/Embedded/build.sh +++ b/Examples/Embedded/build.sh @@ -1,5 +1,4 @@ #!/bin/bash package_dir="$(cd "$(dirname "$0")" && pwd)" -JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM=true \ - swift package --package-path "$package_dir" \ +swift package --package-path "$package_dir" \ -c release --triple wasm32-unknown-none-wasm js From 047e5a631393f94d877a12ebcfc39aa9cd637223 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 13:15:05 +0000 Subject: [PATCH 16/18] Detect Embedded build mode by compilation condition --- .../Sources/PackageToJSPlugin.swift | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift index 9d0b2c19d..559022c2c 100644 --- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift +++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift @@ -161,9 +161,11 @@ struct PackageToJSPlugin: CommandPlugin { } // Build products + let selfPackage = try findSelfPackage(in: context.package) let productName = try buildOptions.product ?? deriveDefaultProduct(package: context.package) let build = try buildWasm( productName: productName, + selfPackage: selfPackage, context: context, options: buildOptions.packageOptions ) @@ -178,14 +180,6 @@ struct PackageToJSPlugin: CommandPlugin { } else { context.pluginWorkDirectoryURL.appending(path: "Package") } - guard - let selfPackage = findPackageInDependencies( - package: context.package, - id: Self.JAVASCRIPTKIT_PACKAGE_ID - ) - else { - throw PackageToJSError("Failed to find JavaScriptKit in dependencies!?") - } var make = MiniMake( explain: buildOptions.packageOptions.explain, printProgress: self.printProgress @@ -226,9 +220,11 @@ struct PackageToJSPlugin: CommandPlugin { exit(1) } + let selfPackage = try findSelfPackage(in: context.package) let productName = "\(context.package.displayName)PackageTests" let build = try buildWasm( productName: productName, + selfPackage: selfPackage, context: context, options: testOptions.packageOptions ) @@ -264,14 +260,6 @@ struct PackageToJSPlugin: CommandPlugin { } else { context.pluginWorkDirectoryURL.appending(path: "PackageTests") } - guard - let selfPackage = findPackageInDependencies( - package: context.package, - id: Self.JAVASCRIPTKIT_PACKAGE_ID - ) - else { - throw PackageToJSError("Failed to find JavaScriptKit in dependencies!?") - } var make = MiniMake( explain: testOptions.packageOptions.explain, printProgress: self.printProgress @@ -311,6 +299,7 @@ struct PackageToJSPlugin: CommandPlugin { private func buildWasm( productName: String, + selfPackage: Package, context: PluginContext, options: PackageToJS.PackageOptions ) throws @@ -331,11 +320,7 @@ struct PackageToJSPlugin: CommandPlugin { ) parameters.echoLogs = true parameters.otherSwiftcFlags = ["-color-diagnostics"] - let buildingForEmbedded = - ProcessInfo.processInfo.environment["JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM"].flatMap( - Bool.init - ) ?? false - if !buildingForEmbedded { + if !isBuildingForEmbedded(selfPackage: selfPackage) { // NOTE: We only support static linking for now, and the new SwiftDriver // does not infer `-static-stdlib` for WebAssembly targets intentionally // for future dynamic linking support. @@ -355,6 +340,31 @@ struct PackageToJSPlugin: CommandPlugin { return try self.packageManager.build(.product(productName), parameters: parameters) } + /// Check if the build is for embedded WebAssembly + private func isBuildingForEmbedded(selfPackage: Package) -> Bool { + let coreTarget = selfPackage.targets.first { $0.name == "JavaScriptKit" } + guard let swiftTarget = coreTarget as? SwiftSourceModuleTarget else { + return false + } + // SwiftPM defines "Embedded" compilation condition when `Embedded` experimental + // feature is enabled. + // TODO: This should be replaced with a proper trait-based solution in the future. + return swiftTarget.compilationConditions.contains("Embedded") + } + + /// Find JavaScriptKit package in the dependencies of the given package recursively + private func findSelfPackage(in package: Package) throws -> Package { + guard + let selfPackage = findPackageInDependencies( + package: package, + id: Self.JAVASCRIPTKIT_PACKAGE_ID + ) + else { + throw PackageToJSError("Failed to find JavaScriptKit in dependencies!?") + } + return selfPackage + } + /// Clean if the build graph of the packaging process has changed /// /// This is especially important to detect user changes debug/release From b77d01564de4b50a9eafc266647b2ad919fd5825 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 13:45:35 +0000 Subject: [PATCH 17/18] Enable `Embedded` feature for more modules --- Package.swift | 12 ++++++++++-- Package@swift-6.0.swift | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 5a3a1ca5d..cf88b3db9 100644 --- a/Package.swift +++ b/Package.swift @@ -44,7 +44,11 @@ let package = Package( .target( name: "JavaScriptBigIntSupport", - dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"] + dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"], + swiftSettings: [ + .enableExperimentalFeature("Embedded", .when(traits: ["Embedded"])), + .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"], .when(traits: ["Embedded"])), + ] ), .target(name: "_CJavaScriptBigIntSupport", dependencies: ["_CJavaScriptKit"]), .testTarget( @@ -54,7 +58,11 @@ let package = Package( .target( name: "JavaScriptEventLoop", - dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"] + dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"], + swiftSettings: [ + .enableExperimentalFeature("Embedded", .when(traits: ["Embedded"])), + .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"], .when(traits: ["Embedded"])), + ] ), .target(name: "_CJavaScriptEventLoop"), .testTarget( diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 85a9a616d..fcf40524a 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -44,7 +44,12 @@ let package = Package( .target( name: "JavaScriptBigIntSupport", - dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"] + dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"], + swiftSettings: shouldBuildForEmbedded + ? [ + .enableExperimentalFeature("Embedded"), + .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"]), + ] : [] ), .target(name: "_CJavaScriptBigIntSupport", dependencies: ["_CJavaScriptKit"]), .testTarget( @@ -54,7 +59,12 @@ let package = Package( .target( name: "JavaScriptEventLoop", - dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"] + dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"], + swiftSettings: shouldBuildForEmbedded + ? [ + .enableExperimentalFeature("Embedded"), + .unsafeFlags(["-Xfrontend", "-emit-empty-object-file"]), + ] : [] ), .target(name: "_CJavaScriptEventLoop"), .testTarget( From 6105c33b9e922139cc4e2979be5c0b3311660843 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 27 Mar 2025 14:16:20 +0000 Subject: [PATCH 18/18] [skip ci] Fix comment in Utilities/format.swift --- Utilities/format.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/format.swift b/Utilities/format.swift index 9700e9ea3..be6e70858 100755 --- a/Utilities/format.swift +++ b/Utilities/format.swift @@ -67,7 +67,7 @@ let excluded: Set = [ URL(fileURLWithPath: #filePath).lastPathComponent, ] -/// Returns a list of directories to format. +/// Returns a list of file paths to format. func filesToFormat() -> [String] { var files: [String] = [] let fileManager = FileManager.default