From 990479ef616eb14bec7fce2a29b9f5695df65a1d Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 7 Apr 2022 03:39:06 +0900 Subject: [PATCH 1/2] Revert "Add support for Symbol objects via JSSymbol (#179)" This reverts commit 081784b34ad94c91eddccca58f1592ce181328ad. --- .../Sources/PrimaryTests/main.swift | 23 ------- Runtime/src/js-value.ts | 6 -- Runtime/src/object-heap.ts | 24 ++++--- .../JavaScriptKit/ConvertibleToJSValue.swift | 5 -- .../FundamentalObjects/JSFunction.swift | 17 ++--- .../FundamentalObjects/JSObject.swift | 8 --- .../FundamentalObjects/JSSymbol.swift | 56 ---------------- Sources/JavaScriptKit/JSValue.swift | 66 +++++++------------ .../_CJavaScriptKit/include/_CJavaScriptKit.h | 5 -- 9 files changed, 47 insertions(+), 163 deletions(-) delete mode 100644 Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift index bf4c9bc4f..ff42d3358 100644 --- a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift +++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift @@ -804,28 +804,5 @@ try test("Hashable Conformance") { try expectEqual(firstHash, secondHash) } -try test("Symbols") { - let symbol1 = JSSymbol("abc") - let symbol2 = JSSymbol("abc") - try expectNotEqual(symbol1, symbol2) - try expectEqual(symbol1.name, symbol2.name) - try expectEqual(symbol1.name, "abc") - - try expectEqual(JSSymbol.iterator, JSSymbol.iterator) - - // let hasInstanceClass = { - // prop: Object.assign(function () {}, { - // [Symbol.hasInstance]() { return true } - // }) - // }.prop - let hasInstanceObject = JSObject.global.Object.function!.new() - hasInstanceObject.prop = JSClosure { _ in .undefined }.jsValue - let hasInstanceClass = hasInstanceObject.prop.function! - hasInstanceClass[JSSymbol.hasInstance] = JSClosure { _ in - return .boolean(true) - }.jsValue - try expectEqual(hasInstanceClass[JSSymbol.hasInstance].function!().boolean, true) - try expectEqual(JSObject.global.Object.isInstanceOf(hasInstanceClass), true) -} Expectation.wait(expectations) diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 67ac5d46a..61f7b486a 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -10,7 +10,6 @@ export enum Kind { Null = 4, Undefined = 5, Function = 6, - Symbol = 7, } export const decode = ( @@ -103,11 +102,6 @@ export const write = ( memory.writeUint32(payload1_ptr, memory.retain(value)); break; } - case "symbol": { - memory.writeUint32(kind_ptr, exceptionBit | Kind.Symbol); - memory.writeUint32(payload1_ptr, memory.retain(value)); - break; - } default: throw new Error(`Type "${typeof value}" is not supported yet`); } diff --git a/Runtime/src/object-heap.ts b/Runtime/src/object-heap.ts index 2f9b1fdf3..61f1925fe 100644 --- a/Runtime/src/object-heap.ts +++ b/Runtime/src/object-heap.ts @@ -22,25 +22,33 @@ export class SwiftRuntimeHeap { } retain(value: any) { + const isObject = typeof value == "object"; const entry = this._heapEntryByValue.get(value); - if (entry) { + if (isObject && entry) { entry.rc++; return entry.id; } const id = this._heapNextKey++; this._heapValueById.set(id, value); - this._heapEntryByValue.set(value, { id: id, rc: 1 }); + if (isObject) { + this._heapEntryByValue.set(value, { id: id, rc: 1 }); + } return id; } release(ref: ref) { const value = this._heapValueById.get(ref); - const entry = this._heapEntryByValue.get(value)!; - entry.rc--; - if (entry.rc != 0) return; - - this._heapEntryByValue.delete(value); - this._heapValueById.delete(ref); + const isObject = typeof value == "object"; + if (isObject) { + const entry = this._heapEntryByValue.get(value)!; + entry.rc--; + if (entry.rc != 0) return; + + this._heapEntryByValue.delete(value); + this._heapValueById.delete(ref); + } else { + this._heapValueById.delete(ref); + } } referenceHeap(ref: ref) { diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift index 83680ad02..10d29a1a6 100644 --- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift +++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift @@ -207,8 +207,6 @@ extension RawJSValue: ConvertibleToJSValue { return .undefined case .function: return .function(JSFunction(id: UInt32(payload1))) - case .symbol: - return .symbol(JSSymbol(id: UInt32(payload1))) } } } @@ -240,9 +238,6 @@ extension JSValue { case let .function(functionRef): kind = .function payload1 = JavaScriptPayload1(functionRef.id) - case let .symbol(symbolRef): - kind = .symbol - payload1 = JavaScriptPayload1(symbolRef.id) } let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2) return body(rawValue) diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index 9cec5dad0..cbc17a6f5 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -11,6 +11,7 @@ import _CJavaScriptKit /// ``` /// public class JSFunction: JSObject { + /// Call this function with given `arguments` and binding given `this` as context. /// - Parameters: /// - this: The value to be passed as the `this` parameter to this function. @@ -18,7 +19,7 @@ public class JSFunction: JSObject { /// - Returns: The result of this call. @discardableResult public func callAsFunction(this: JSObject? = nil, arguments: [ConvertibleToJSValue]) -> JSValue { - invokeNonThrowingJSFunction(self, arguments: arguments, this: this).jsValue + invokeNonThrowingJSFunction(self, arguments: arguments, this: this) } /// A variadic arguments version of `callAsFunction`. @@ -40,7 +41,7 @@ public class JSFunction: JSObject { 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))) + return JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count))) } } } @@ -74,7 +75,7 @@ public class JSFunction: JSObject { fatalError("unavailable") } - override public class func construct(from value: JSValue) -> Self? { + public override class func construct(from value: JSValue) -> Self? { return value.function as? Self } @@ -83,9 +84,9 @@ public class JSFunction: JSObject { } } -func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue { +private func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> JSValue { arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer in + rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue) in let argv = bufferPointer.baseAddress let argc = bufferPointer.count var kindAndFlags = JavaScriptValueKindAndFlags() @@ -93,8 +94,8 @@ func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleTo var payload2 = JavaScriptPayload2() if let thisId = this?.id { _call_function_with_this_no_catch(thisId, - jsFunc.id, argv, Int32(argc), - &kindAndFlags, &payload1, &payload2) + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) } else { _call_function_no_catch( jsFunc.id, argv, Int32(argc), @@ -103,7 +104,7 @@ func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleTo } assert(!kindAndFlags.isException) let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) - return result + return result.jsValue() } } } diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift index 427648bcc..659d9212a 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift @@ -95,14 +95,6 @@ public class JSObject: Equatable { set { setJSValue(this: self, index: Int32(index), value: newValue) } } - /// Access the `symbol` member dynamically through JavaScript and Swift runtime bridge library. - /// - Parameter symbol: The name of this object's member to access. - /// - Returns: The value of the `name` member of this object. - public subscript(_ name: JSSymbol) -> JSValue { - get { getJSValue(this: self, symbol: name) } - set { setJSValue(this: self, symbol: name, value: newValue) } - } - /// A modifier to call methods as throwing methods capturing `this` /// /// diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift deleted file mode 100644 index 3ec1b2902..000000000 --- a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift +++ /dev/null @@ -1,56 +0,0 @@ -import _CJavaScriptKit - -private let Symbol = JSObject.global.Symbol.function! - -public class JSSymbol: JSObject { - public var name: String? { self["description"].string } - - public init(_ description: JSString) { - // can’t do `self =` so we have to get the ID manually - let result = invokeNonThrowingJSFunction(Symbol, arguments: [description], this: nil) - precondition(result.kind == .symbol) - super.init(id: UInt32(result.payload1)) - } - @_disfavoredOverload - public convenience init(_ description: String) { - self.init(JSString(description)) - } - - override init(id: JavaScriptObjectRef) { - super.init(id: id) - } - - public static func `for`(key: JSString) -> JSSymbol { - Symbol.for!(key).symbol! - } - - @_disfavoredOverload - public static func `for`(key: String) -> JSSymbol { - Symbol.for!(key).symbol! - } - - public static func key(for symbol: JSSymbol) -> JSString? { - Symbol.keyFor!(symbol).jsString - } - - @_disfavoredOverload - public static func key(for symbol: JSSymbol) -> String? { - Symbol.keyFor!(symbol).string - } -} - -extension JSSymbol { - public static let asyncIterator: JSSymbol! = Symbol.asyncIterator.symbol - public static let hasInstance: JSSymbol! = Symbol.hasInstance.symbol - public static let isConcatSpreadable: JSSymbol! = Symbol.isConcatSpreadable.symbol - public static let iterator: JSSymbol! = Symbol.iterator.symbol - public static let match: JSSymbol! = Symbol.match.symbol - public static let matchAll: JSSymbol! = Symbol.matchAll.symbol - public static let replace: JSSymbol! = Symbol.replace.symbol - public static let search: JSSymbol! = Symbol.search.symbol - public static let species: JSSymbol! = Symbol.species.symbol - public static let split: JSSymbol! = Symbol.split.symbol - public static let toPrimitive: JSSymbol! = Symbol.toPrimitive.symbol - public static let toStringTag: JSSymbol! = Symbol.toStringTag.symbol - public static let unscopables: JSSymbol! = Symbol.unscopables.symbol -} diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index b001dc7ab..03c2a81ab 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -10,7 +10,6 @@ public enum JSValue: Equatable { case null case undefined case function(JSFunction) - case symbol(JSSymbol) /// Returns the `Bool` value of this JS value if its type is boolean. /// If not, returns `nil`. @@ -68,13 +67,6 @@ public enum JSValue: Equatable { } } - public var symbol: JSSymbol? { - switch self { - case let .symbol(symbol): return symbol - default: return nil - } - } - /// Returns the `true` if this JS value is null. /// If not, returns `false`. public var isNull: Bool { @@ -88,23 +80,23 @@ public enum JSValue: Equatable { } } -public extension JSValue { +extension JSValue { /// An unsafe convenience method of `JSObject.subscript(_ name: String) -> ((ConvertibleToJSValue...) -> JSValue)?` /// - Precondition: `self` must be a JavaScript Object and specified member should be a callable object. - subscript(dynamicMember name: String) -> ((ConvertibleToJSValue...) -> JSValue) { + public subscript(dynamicMember name: String) -> ((ConvertibleToJSValue...) -> JSValue) { object![dynamicMember: name]! } /// An unsafe convenience method of `JSObject.subscript(_ index: Int) -> JSValue` /// - Precondition: `self` must be a JavaScript Object. - subscript(dynamicMember name: String) -> JSValue { + public subscript(dynamicMember name: String) -> JSValue { get { self.object![name] } set { self.object![name] = newValue } } /// An unsafe convenience method of `JSObject.subscript(_ index: Int) -> JSValue` /// - Precondition: `self` must be a JavaScript Object. - subscript(_ index: Int) -> JSValue { + public subscript(_ index: Int) -> JSValue { get { object![index] } set { object![index] = newValue } } @@ -112,14 +104,15 @@ public extension JSValue { extension JSValue: Swift.Error {} -public extension JSValue { - func fromJSValue() -> Type? where Type: ConstructibleFromJSValue { +extension JSValue { + public func fromJSValue() -> Type? where Type: ConstructibleFromJSValue { return Type.construct(from: self) } } -public extension JSValue { - static func string(_ value: String) -> JSValue { +extension JSValue { + + public static func string(_ value: String) -> JSValue { .string(JSString(value)) } @@ -148,12 +141,12 @@ public extension JSValue { /// eventListenter.release() /// ``` @available(*, deprecated, message: "Please create JSClosure directly and manage its lifetime manually.") - static func function(_ body: @escaping ([JSValue]) -> JSValue) -> JSValue { + public static func function(_ body: @escaping ([JSValue]) -> JSValue) -> JSValue { .object(JSClosure(body)) } @available(*, deprecated, renamed: "object", message: "JSClosure is no longer a subclass of JSFunction. Use .object(closure) instead.") - static func function(_ closure: JSClosure) -> JSValue { + public static func function(_ closure: JSClosure) -> JSValue { .object(closure) } } @@ -177,7 +170,7 @@ extension JSValue: ExpressibleByFloatLiteral { } extension JSValue: ExpressibleByNilLiteral { - public init(nilLiteral _: ()) { + public init(nilLiteral: ()) { self = .null } } @@ -212,28 +205,14 @@ 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) - return rawValue.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) - } -} - -public extension JSValue { - /// Return `true` if this value is an instance of the passed `constructor` function. - /// Returns `false` for everything except objects and functions. - /// - Parameter constructor: The constructor function to check. - /// - Returns: The result of `instanceof` in the JavaScript environment. - func isInstanceOf(_ constructor: JSFunction) -> Bool { +extension JSValue { + /// Return `true` if this value is an instance of the passed `constructor` function. + /// Returns `false` for everything except objects and functions. + /// - Parameter constructor: The constructor function to check. + /// - Returns: The result of `instanceof` in the JavaScript environment. + public func isInstanceOf(_ constructor: JSFunction) -> Bool { switch self { - case .boolean, .string, .number, .null, .undefined, .symbol: + case .boolean, .string, .number, .null, .undefined: return false case let .object(ref): return ref.isInstanceOf(constructor) @@ -248,12 +227,11 @@ extension JSValue: CustomStringConvertible { switch self { case let .boolean(boolean): return boolean.description - case let .string(string): + case .string(let string): return string.description - case let .number(number): + case .number(let number): return number.description - case let .object(object), let .function(object as JSObject), - .symbol(let object as JSObject): + case .object(let object), .function(let object as JSObject): return object.toString!().fromJSValue()! case .null: return "null" diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index daf405141..ce0bf5862 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -21,7 +21,6 @@ typedef enum __attribute__((enum_extensibility(closed))) { JavaScriptValueKindNull = 4, JavaScriptValueKindUndefined = 5, JavaScriptValueKindFunction = 6, - JavaScriptValueKindSymbol = 7, } JavaScriptValueKind; typedef struct { @@ -61,10 +60,6 @@ typedef double JavaScriptPayload2; /// payload1: the target `JavaScriptHostFuncRef` /// payload2: 0 /// -/// For symbol value: -/// payload1: `JavaScriptObjectRef` -/// payload2: 0 -/// typedef struct { JavaScriptValueKind kind; JavaScriptPayload1 payload1; From d7ed468afed62940d1fdc3310f123e3f99679b26 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 7 Apr 2022 03:44:49 +0900 Subject: [PATCH 2/2] Export `main` and execute `_initialize` and `main` in test entry IntegrationTests are unintentionally disabled since 0a38d705e0c310f236599e2a0d62afa8012578db because reactor mode doesn't export `_start` and `WASI.start` doesn't do anything except setting the instance to WASI module. So export `main` manually, and call `_initialize` according to the "new-style commands" described in https://github.com/WebAssembly/WASI/blob/59cbe140561db52fc505555e859de884e0ee7f00/legacy/application-abi.md#current-unstable-abi Symbol support (081784b34ad94c91eddccca58f1592ce181328ad) broke tests silently, so revert it for now. --- IntegrationTests/Makefile | 1 + IntegrationTests/lib.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/IntegrationTests/Makefile b/IntegrationTests/Makefile index 575a8f20d..21e8be315 100644 --- a/IntegrationTests/Makefile +++ b/IntegrationTests/Makefile @@ -8,6 +8,7 @@ TestSuites/.build/$(CONFIGURATION)/%.wasm: FORCE --triple wasm32-unknown-wasi \ --configuration $(CONFIGURATION) \ -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor \ + -Xlinker --export=main \ $(SWIFT_BUILD_FLAGS) dist/%.wasm: TestSuites/.build/$(CONFIGURATION)/%.wasm diff --git a/IntegrationTests/lib.js b/IntegrationTests/lib.js index ee0a6a33f..c55ed3b4d 100644 --- a/IntegrationTests/lib.js +++ b/IntegrationTests/lib.js @@ -45,6 +45,8 @@ const startWasiTask = async (wasmPath) => { swift.setInstance(instance); // Start the WebAssembly WASI instance! wasi.start(instance); + instance.exports._initialize(); + instance.exports.main(); }; module.exports = { startWasiTask };