See More

import _CJavaScriptKit /// `JSValue` represents a value in JavaScript. @dynamicMemberLookup public enum JSValue: Equatable { case boolean(Bool) case string(JSString) case number(Double) case object(JSObject) case null case undefined case symbol(JSSymbol) case bigInt(JSBigInt) /// Returns the `Bool` value of this JS value if its type is boolean. /// If not, returns `nil`. public var boolean: Bool? { switch self { case .boolean(let boolean): return boolean default: return nil } } /// Returns the `String` value of this JS value if the type is string. /// If not, returns `nil`. /// /// Note that this accessor may copy the JS string value into Swift side memory. /// /// To avoid the copying, please consider the `jsString` instead. public var string: String? { jsString.map(String.init) } /// Returns the `JSString` value of this JS value if the type is string. /// If not, returns `nil`. /// public var jsString: JSString? { switch self { case .string(let string): return string default: return nil } } /// Returns the `Double` value of this JS value if the type is number. /// If not, returns `nil`. public var number: Double? { switch self { case .number(let number): return number default: return nil } } /// Returns the `JSObject` of this JS value if its type is object. /// If not, returns `nil`. public var object: JSObject? { switch self { case .object(let object): return object default: return nil } } // @available(*, deprecated, renamed: "object", message: "Use the .object property instead") public var function: JSObject? { object } /// Returns the `JSSymbol` of this JS value if its type is function. /// If not, returns `nil`. public var symbol: JSSymbol? { switch self { case .symbol(let symbol): return symbol default: return nil } } /// Returns the `JSBigInt` of this JS value if its type is function. /// If not, returns `nil`. public var bigInt: JSBigInt? { switch self { case .bigInt(let bigInt): return bigInt default: return nil } } /// Returns the `true` if this JS value is null. /// If not, returns `false`. public var isNull: Bool { return self == .null } /// Returns the `true` if this JS value is undefined. /// If not, returns `false`. public var isUndefined: Bool { return self == .undefined } } /// JSValue is intentionally not `Sendable` because accessing a JSValue living in a different /// thread is invalid. Although there are some cases where Swift allows sending a non-Sendable /// values to other isolation domains, not conforming `Sendable` is still useful to prevent /// accidental misuse. @available(*, unavailable) extension JSValue: Sendable {} extension JSValue { #if !hasFeature(Embedded) /// 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. public subscript(dynamicMember name: String) -> ((ConvertibleToJSValue...) -> JSValue) { object![dynamicMember: name]! } #endif /// An unsafe convenience method of `JSObject.subscript(_ index: Int) -> JSValue` /// - Precondition: `self` must be a JavaScript Object. public subscript(dynamicMember name: String) -> JSValue { get { self.object![name] } nonmutating set { self.object![name] = newValue } } /// An unsafe convenience method of `JSObject.subscript(_ index: Int) -> JSValue` /// - Precondition: `self` must be a JavaScript Object. public subscript(_ index: Int) -> JSValue { get { object![index] } nonmutating set { object![index] = newValue } } } extension JSValue { public func fromJSValue() -> Type? where Type: ConstructibleFromJSValue { return Type.construct(from: self) } } extension JSValue { public static func string(_ value: String) -> JSValue { .string(JSString(value)) } /// Deprecated: Please create `JSClosure` directly and manage its lifetime manually. /// /// Migrate this usage /// /// ```swift /// button.addEventListener!("click", JSValue.function { _ in /// ... /// return JSValue.undefined /// }) /// ``` /// /// into below code. /// /// ```swift /// let eventListener = JSClosure { _ in /// ... /// return JSValue.undefined /// } /// /// button.addEventListener!("click", JSValue.function(eventListener)) /// ... /// button.removeEventListener!("click", JSValue.function(eventListener)) /// eventListener.release() /// ``` @available(*, deprecated, message: "Please create JSClosure directly and manage its lifetime manually.") 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." ) public static func function(_ closure: JSClosure) -> JSValue { .object(closure) } @available(*, deprecated, renamed: "object", message: "Use .object(function) instead") public static func function(_ function: JSObject) -> JSValue { .object(function) } } extension JSValue: ExpressibleByStringLiteral { public init(stringLiteral value: String) { self = .string(JSString(value)) } } extension JSValue: ExpressibleByIntegerLiteral { public init(integerLiteral value: Int32) { self = .number(Double(value)) } } extension JSValue: ExpressibleByFloatLiteral { public init(floatLiteral value: Double) { self = .number(value) } } extension JSValue: ExpressibleByNilLiteral { public init(nilLiteral _: ()) { self = .null } } public func getJSValue(this: JSObject, name: JSString) -> JSValue { var rawValue = RawJSValue() let rawBitPattern = swjs_get_prop( this.id, name.asInternalJSRef(), &rawValue.payload1, &rawValue.payload2 ) rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self) return rawValue.jsValue } public func setJSValue(this: JSObject, name: JSString, value: JSValue) { value.withRawJSValue { rawValue in swjs_set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2) } } public func getJSValue(this: JSObject, index: Int32) -> JSValue { var rawValue = RawJSValue() let rawBitPattern = swjs_get_subscript( this.id, index, &rawValue.payload1, &rawValue.payload2 ) rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self) return rawValue.jsValue } public func setJSValue(this: JSObject, index: Int32, value: JSValue) { value.withRawJSValue { rawValue in swjs_set_subscript( this.id, index, rawValue.kind, rawValue.payload1, rawValue.payload2 ) } } public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue { var rawValue = RawJSValue() let rawBitPattern = swjs_get_prop( this.id, symbol.id, &rawValue.payload1, &rawValue.payload2 ) rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self) return rawValue.jsValue } public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) { value.withRawJSValue { rawValue in swjs_set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2) } } 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: JSObject) -> Bool { switch self { case .boolean, .string, .number, .null, .undefined, .symbol, .bigInt: return false case .object(let ref): return ref.isInstanceOf(constructor) } } } extension JSValue: CustomStringConvertible { public var description: String { // per https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value // this always returns a string JSObject.global.String.object!(self).string! } } #if hasFeature(Embedded) // Overloads of `JSValue.subscript(dynamicMember name: String) -> ((ConvertibleToJSValue...) -> JSValue)` // for 0 through 7 arguments for Embedded Swift. // // These are required because the `ConvertibleToJSValue...` subscript is not // available in Embedded Swift due to lack of support for existentials. // // Note: Once Embedded Swift supports parameter packs/variadic generics, we can // replace all of these with a single method that takes a generic pack. extension JSValue { @_disfavoredOverload public subscript(dynamicMember name: String) -> (() -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript(dynamicMember name: String) -> ((A0) -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript< A0: ConvertibleToJSValue, A1: ConvertibleToJSValue >(dynamicMember name: String) -> ((A0, A1) -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript< A0: ConvertibleToJSValue, A1: ConvertibleToJSValue, A2: ConvertibleToJSValue >(dynamicMember name: String) -> ((A0, A1, A2) -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript< A0: ConvertibleToJSValue, A1: ConvertibleToJSValue, A2: ConvertibleToJSValue, A3: ConvertibleToJSValue >(dynamicMember name: String) -> ((A0, A1, A2, A3) -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript< A0: ConvertibleToJSValue, A1: ConvertibleToJSValue, A2: ConvertibleToJSValue, A3: ConvertibleToJSValue, A4: ConvertibleToJSValue >(dynamicMember name: String) -> ((A0, A1, A2, A3, A4) -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript< A0: ConvertibleToJSValue, A1: ConvertibleToJSValue, A2: ConvertibleToJSValue, A3: ConvertibleToJSValue, A4: ConvertibleToJSValue, A5: ConvertibleToJSValue >(dynamicMember name: String) -> ((A0, A1, A2, A3, A4, A5) -> JSValue) { object![dynamicMember: name]! } @_disfavoredOverload public subscript< A0: ConvertibleToJSValue, A1: ConvertibleToJSValue, A2: ConvertibleToJSValue, A3: ConvertibleToJSValue, A4: ConvertibleToJSValue, A5: ConvertibleToJSValue, A6: ConvertibleToJSValue >(dynamicMember name: String) -> ((A0, A1, A2, A3, A4, A5, A6) -> JSValue) { object![dynamicMember: name]! } } #endif