Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 702dd89

Browse files
authoredSep 18, 2020
Merge pull request #26 from j-f1/webidl
Add JSBridgedType and JSBridgedClass
2 parents 8cdc2d1 + 767d05d commit 702dd89

File tree

10 files changed

+266
-86
lines changed

10 files changed

+266
-86
lines changed
 

‎Sources/JavaScriptKit/BasicObjects/JSArray.swift

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,63 @@
11
/// A wrapper around [the JavaScript Array class](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array)
22
/// that exposes its properties in a type-safe and Swifty way.
3-
public class JSArray {
4-
static let classObject = JSObject.global.Array.function!
3+
public class JSArray: JSBridgedClass {
4+
public static let constructor = JSObject.global.Array.function!
55

66
static func isArray(_ object: JSObject) -> Bool {
7-
classObject.isArray!(object).boolean!
7+
constructor.isArray!(object).boolean!
8+
}
9+
10+
public let jsObject: JSObject
11+
12+
public required convenience init?(from value: JSValue) {
13+
guard let object = value.object else { return nil }
14+
self.init(object)
815
}
916

10-
let ref: JSObject
11-
1217
/// Construct a `JSArray` from Array `JSObject`.
1318
/// Return `nil` if the object is not an Array.
1419
///
1520
/// - Parameter object: A `JSObject` expected to be a JavaScript Array
16-
public init?(_ ref: JSObject) {
17-
guard Self.isArray(ref) else { return nil }
18-
self.ref = ref
21+
public convenience init?(_ jsObject: JSObject) {
22+
guard Self.isArray(jsObject) else { return nil }
23+
self.init(unsafelyWrapping: jsObject)
24+
}
25+
26+
public required init(unsafelyWrapping jsObject: JSObject) {
27+
self.jsObject = jsObject
1928
}
2029
}
2130

2231
extension JSArray: RandomAccessCollection {
2332
public typealias Element = JSValue
2433

2534
public func makeIterator() -> Iterator {
26-
Iterator(ref: ref)
35+
Iterator(jsObject: jsObject)
2736
}
2837

2938
public class Iterator: IteratorProtocol {
30-
let ref: JSObject
31-
var index = 0
32-
init(ref: JSObject) {
33-
self.ref = ref
39+
private let jsObject: JSObject
40+
private var index = 0
41+
init(jsObject: JSObject) {
42+
self.jsObject = jsObject
3443
}
3544

3645
public func next() -> Element? {
3746
let currentIndex = index
38-
guard currentIndex < Int(ref.length.number!) else {
47+
guard currentIndex < Int(jsObject.length.number!) else {
3948
return nil
4049
}
4150
index += 1
42-
guard ref.hasOwnProperty!(currentIndex).boolean! else {
51+
guard jsObject.hasOwnProperty!(currentIndex).boolean! else {
4352
return next()
4453
}
45-
let value = ref[currentIndex]
54+
let value = jsObject[currentIndex]
4655
return value
4756
}
4857
}
4958

5059
public subscript(position: Int) -> JSValue {
51-
ref[position]
60+
jsObject[position]
5261
}
5362

5463
public var startIndex: Int { 0 }
@@ -68,14 +77,14 @@ extension JSArray: RandomAccessCollection {
6877
/// array.count // 2
6978
/// ```
7079
public var length: Int {
71-
return Int(ref.length.number!)
80+
Int(jsObject.length.number!)
7281
}
7382

7483
/// The number of elements in that array **not** including empty hole.
7584
/// Note that `count` syncs with the number that `Iterator` can iterate.
7685
/// See also: `JSArray.length`
7786
public var count: Int {
78-
return getObjectValuesLength(ref)
87+
getObjectValuesLength(jsObject)
7988
}
8089
}
8190

‎Sources/JavaScriptKit/BasicObjects/JSDate.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ in the naming. Parts of the JavaScript `Date` API that are not consistent across
66
implementations are not exposed in a type-safe manner, you should access the underlying `jsObject`
77
property if you need those.
88
*/
9-
public final class JSDate {
9+
public final class JSDate: JSBridgedClass {
1010
/// The constructor function used to create new `Date` objects.
11-
private static let constructor = JSObject.global.Date.function!
11+
public static let constructor = JSObject.global.Date.function!
1212

1313
/// The underlying JavaScript `Date` object.
1414
public let jsObject: JSObject
@@ -39,6 +39,10 @@ public final class JSDate {
3939
jsObject = Self.constructor.new(year, monthIndex, day, hours, minutes, seconds, milliseconds)
4040
}
4141

42+
public init(unsafelyWrapping jsObject: JSObject) {
43+
self.jsObject = jsObject
44+
}
45+
4246
/// Year of this date in local time zone.
4347
public var fullYear: Int {
4448
get {

‎Sources/JavaScriptKit/BasicObjects/JSError.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) that
33
exposes its properties in a type-safe way.
44
*/
5-
public final class JSError: Error {
5+
public final class JSError: Error, JSBridgedClass {
66
/// The constructor function used to create new `Error` objects.
7-
private static let constructor = JSObject.global.Error.function!
7+
public static let constructor = JSObject.global.Error.function!
88

99
/// The underlying JavaScript `Error` object.
1010
public let jsObject: JSObject
@@ -14,6 +14,10 @@ public final class JSError: Error {
1414
jsObject = Self.constructor.new([message])
1515
}
1616

17+
public init(unsafelyWrapping jsObject: JSObject) {
18+
self.jsObject = jsObject
19+
}
20+
1721
/// The error message of the underlying `Error` object.
1822
public var message: String {
1923
jsObject.message.string!

‎Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,42 @@
44

55
import _CJavaScriptKit
66

7+
/// A protocol that allows a Swift numeric type to be mapped to the JavaScript TypedArray that holds integers of its type
78
public protocol TypedArrayElement: JSValueConvertible, JSValueConstructible {
9+
/// The constructor function for the TypedArray class for this particular kind of number
810
static var typedArrayClass: JSFunction { get }
911
}
1012

11-
/// A wrapper around [the JavaScript TypedArray class](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)
12-
/// that exposes its properties in a type-safe and Swifty way.
13-
public class JSTypedArray<Element>: JSValueConvertible, ExpressibleByArrayLiteral where Element: TypedArrayElement {
14-
let ref: JSObject
15-
public func jsValue() -> JSValue {
16-
.object(ref)
17-
}
13+
/// A wrapper around all JavaScript [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) classes that exposes their properties in a type-safe way.
14+
/// FIXME: the BigInt-based TypedArrays are not supported (https://github.com/swiftwasm/JavaScriptKit/issues/56)
15+
public class JSTypedArray<Element>: JSBridgedClass, ExpressibleByArrayLiteral where Element: TypedArrayElement {
16+
public static var constructor: JSFunction { Element.typedArrayClass }
17+
public var jsObject: JSObject
1818

1919
public subscript(_ index: Int) -> Element {
2020
get {
21-
return Element.construct(from: getJSValue(this: ref, index: Int32(index)))!
21+
return Element.construct(from: jsObject[index])!
2222
}
2323
set {
24-
setJSValue(this: ref, index: Int32(index), value: newValue.jsValue())
24+
self.jsObject[index] = newValue.jsValue()
2525
}
2626
}
2727

28-
// This private initializer assumes that the passed object is TypedArray
29-
private init(unsafe object: JSObject) {
30-
self.ref = object
31-
}
32-
33-
/// Construct a `JSTypedArray` from TypedArray `JSObject`.
34-
/// Return `nil` if the object is not TypedArray.
28+
/// Initialize a new instance of TypedArray in JavaScript environment with given length.
29+
/// All the elements will be initialized to zero.
3530
///
36-
/// - Parameter object: A `JSObject` expected to be TypedArray
37-
public init?(_ object: JSObject) {
38-
guard object.isInstanceOf(Element.typedArrayClass) else { return nil }
39-
self.ref = object
31+
/// - Parameter length: The number of elements that will be allocated.
32+
public init(length: Int) {
33+
jsObject = Element.typedArrayClass.new(length)
4034
}
4135

42-
/// Initialize a new instance of TypedArray in JavaScript environment with given length zero value.
43-
///
44-
/// - Parameter length: The length of elements that will be allocated.
45-
public convenience init(length: Int) {
46-
let jsObject = Element.typedArrayClass.new(length)
47-
self.init(unsafe: jsObject)
36+
required public init(unsafelyWrapping jsObject: JSObject) {
37+
self.jsObject = jsObject
4838
}
4939

5040
required public convenience init(arrayLiteral elements: Element...) {
5141
self.init(elements)
5242
}
53-
5443
/// Initialize a new instance of TypedArray in JavaScript environment with given elements.
5544
///
5645
/// - Parameter array: The array that will be copied to create a new instance of TypedArray
@@ -59,7 +48,7 @@ public class JSTypedArray<Element>: JSValueConvertible, ExpressibleByArrayLitera
5948
array.withUnsafeBufferPointer { ptr in
6049
_create_typed_array(Element.typedArrayClass.id, ptr.baseAddress!, Int32(array.count), &resultObj)
6150
}
62-
self.init(unsafe: JSObject(id: resultObj))
51+
self.init(unsafelyWrapping: JSObject(id: resultObj))
6352
}
6453

6554
/// Convenience initializer for `Sequence`.
@@ -90,8 +79,6 @@ extension UInt: TypedArrayElement {
9079
valueForBitWidth(typeName: "UInt", bitWidth: Int.bitWidth, when32: JSObject.global.Uint32Array).function!
9180
}
9281

93-
// MARK: - Concrete TypedArray classes
94-
9582
extension Int8: TypedArrayElement {
9683
public static var typedArrayClass = JSObject.global.Int8Array.function!
9784
}

‎Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ public class JSFunction: JSObject {
6262
let argv = bufferPointer.baseAddress
6363
let argc = bufferPointer.count
6464
var resultObj = JavaScriptObjectRef()
65-
_call_new(
66-
self.id, argv, Int32(argc),
67-
&resultObj
68-
)
65+
_call_new(self.id, argv, Int32(argc), &resultObj)
6966
return JSObject(id: resultObj)
7067
}
7168
}
@@ -81,6 +78,10 @@ public class JSFunction: JSObject {
8178
fatalError("unavailable")
8279
}
8380

81+
public override class func construct(from value: JSValue) -> Self? {
82+
return value.function as? Self
83+
}
84+
8485
override public func jsValue() -> JSValue {
8586
.function(self)
8687
}

‎Sources/JavaScriptKit/FundamentalObjects/JSObject.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,20 @@ public class JSObject: Equatable {
3232
/// - Parameter name: The name of this object's member to access.
3333
/// - Returns: The `name` member method binding this object as `this` context.
3434
@_disfavoredOverload
35-
public subscript(dynamicMember name: String) -> ((JSValueConvertible...) -> JSValue)? {
35+
public subscript(_ name: String) -> ((JSValueConvertible...) -> JSValue)? {
3636
guard let function = self[name].function else { return nil }
3737
return { (arguments: JSValueConvertible...) in
3838
function(this: self, arguments: arguments)
3939
}
4040
}
4141

42+
/// A convenience method of `subscript(_ name: String) -> ((JSValueConvertible...) -> JSValue)?`
43+
/// to access the member through Dynamic Member Lookup.
44+
@_disfavoredOverload
45+
public subscript(dynamicMember name: String) -> ((JSValueConvertible...) -> JSValue)? {
46+
self[name]
47+
}
48+
4249
/// A convenience method of `subscript(_ name: String) -> JSValue`
4350
/// to access the member through Dynamic Member Lookup.
4451
public subscript(dynamicMember name: String) -> JSValue {
@@ -62,9 +69,9 @@ public class JSObject: Equatable {
6269
set { setJSValue(this: self, index: Int32(index), value: newValue) }
6370
}
6471

65-
/// Return `true` if this object is an instance of the `constructor`. Return `false`, if not.
72+
/// Return `true` if this value is an instance of the passed `constructor` function.
6673
/// - Parameter constructor: The constructor function to check.
67-
/// - Returns: The result of `instanceof` in JavaScript environment.
74+
/// - Returns: The result of `instanceof` in the JavaScript environment.
6875
public func isInstanceOf(_ constructor: JSFunction) -> Bool {
6976
_instanceof(id, constructor.id)
7077
}
@@ -86,6 +93,10 @@ public class JSObject: Equatable {
8693
return lhs.id == rhs.id
8794
}
8895

96+
public class func construct(from value: JSValue) -> Self? {
97+
return value.object as? Self
98+
}
99+
89100
public func jsValue() -> JSValue {
90101
.object(self)
91102
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/// Use this protocol when your type has no single JavaScript class.
2+
/// For example, a union type of multiple classes or primitive values.
3+
public protocol JSBridgedType: JSValueCodable, CustomStringConvertible {
4+
/// This is the value your class wraps.
5+
var value: JSValue { get }
6+
7+
/// If your class is incompatible with the provided value, return `nil`.
8+
init?(from value: JSValue)
9+
}
10+
11+
extension JSBridgedType {
12+
public static func construct(from value: JSValue) -> Self? {
13+
return Self.init(from: value)
14+
}
15+
16+
public func jsValue() -> JSValue { value }
17+
18+
public var description: String { value.description }
19+
}
20+
21+
/// Conform to this protocol when your Swift class wraps a JavaScript class.
22+
public protocol JSBridgedClass: JSBridgedType {
23+
/// The constructor function for the JavaScript class
24+
static var constructor: JSFunction { get }
25+
26+
/// The JavaScript object wrapped by this instance.
27+
/// You may assume that `jsObject instanceof Self.constructor == true`
28+
var jsObject: JSObject { get }
29+
30+
/// Create an instannce wrapping the given JavaScript object.
31+
/// You may assume that `jsObject instanceof Self.constructor`
32+
init(unsafelyWrapping jsObject: JSObject)
33+
}
34+
35+
extension JSBridgedClass {
36+
public var value: JSValue { jsObject.jsValue() }
37+
public init?(from value: JSValue) {
38+
guard let object = value.object, object.isInstanceOf(Self.constructor) else { return nil }
39+
self.init(unsafelyWrapping: object)
40+
}
41+
}

‎Sources/JavaScriptKit/JSValue.swift

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,21 @@ public enum JSValue: Equatable {
5757

5858
/// Returns the `true` if this JS value is null.
5959
/// If not, returns `false`.
60-
public var isNull: Bool { return self == .null }
60+
public var isNull: Bool {
61+
return self == .null
62+
}
6163

6264
/// Returns the `true` if this JS value is undefined.
6365
/// If not, returns `false`.
64-
public var isUndefined: Bool { return self == .undefined }
66+
public var isUndefined: Bool {
67+
return self == .undefined
68+
}
69+
}
6570

71+
extension JSValue {
72+
public func fromJSValue<Type>() -> Type? where Type: JSValueConstructible {
73+
return Type.construct(from: self)
74+
}
6675
}
6776

6877
extension JSValue {
@@ -104,7 +113,13 @@ extension JSValue: ExpressibleByStringLiteral {
104113
}
105114

106115
extension JSValue: ExpressibleByIntegerLiteral {
107-
public init(integerLiteral value: Double) {
116+
public init(integerLiteral value: Int32) {
117+
self = .number(Double(value))
118+
}
119+
}
120+
121+
extension JSValue: ExpressibleByFloatLiteral {
122+
public init(floatLiteral value: Double) {
108123
self = .number(value)
109124
}
110125
}
@@ -144,3 +159,39 @@ public func setJSValue(this: JSObject, index: Int32, value: JSValue) {
144159
rawValue.payload1, rawValue.payload2, rawValue.payload3)
145160
}
146161
}
162+
163+
extension JSValue {
164+
/// Return `true` if this value is an instance of the passed `constructor` function.
165+
/// Returns `false` for everything except objects and functions.
166+
/// - Parameter constructor: The constructor function to check.
167+
/// - Returns: The result of `instanceof` in the JavaScript environment.
168+
public func isInstanceOf(_ constructor: JSFunction) -> Bool {
169+
switch self {
170+
case .boolean, .string, .number, .null, .undefined:
171+
return false
172+
case let .object(ref):
173+
return ref.isInstanceOf(constructor)
174+
case let .function(ref):
175+
return ref.isInstanceOf(constructor)
176+
}
177+
}
178+
}
179+
180+
extension JSValue: CustomStringConvertible {
181+
public var description: String {
182+
switch self {
183+
case let .boolean(boolean):
184+
return boolean.description
185+
case .string(let string):
186+
return string
187+
case .number(let number):
188+
return number.description
189+
case .object(let object), .function(let object as JSObject):
190+
return object.toString!().fromJSValue()!
191+
case .null:
192+
return "null"
193+
case .undefined:
194+
return "undefined"
195+
}
196+
}
197+
}

‎Sources/JavaScriptKit/JSValueConstructible.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// Types conforming to this protocol can be constructed from `JSValue`.
22
public protocol JSValueConstructible {
33
/// Construct an instance of `Self`, if possible, from the given `JSValue`.
4-
/// Return `nil` if fail to construct.
4+
/// Return `nil` if the value is not compatible with the conforming Swift type.
55
///
66
/// - Parameter value: The `JSValue` to decode
77
/// - Returns: An instance of `Self`, if one was successfully constructed from the value.

‎Sources/JavaScriptKit/JSValueConvertible.swift

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import _CJavaScriptKit
22

3-
/// Confirming types are convertible to `JSValue`.
3+
/// Objects that can be converted to a JavaScript value, preferably in a lossless manner.
44
public protocol JSValueConvertible {
5-
/// Convert this object into a `JSValue`.
5+
/// Create a JSValue that represents this object
66
func jsValue() -> JSValue
77
}
88

9-
extension JSValue: JSValueConvertible {
9+
public typealias JSValueCodable = JSValueConvertible & JSValueConstructible
10+
11+
extension JSValue: JSValueCodable {
12+
public static func construct(from value: JSValue) -> Self? {
13+
return value
14+
}
15+
1016
public func jsValue() -> JSValue { self }
1117
}
1218

@@ -18,20 +24,20 @@ extension Int: JSValueConvertible {
1824
public func jsValue() -> JSValue { .number(Double(self)) }
1925
}
2026

21-
extension Int8: JSValueConvertible {
27+
extension UInt: JSValueConvertible {
2228
public func jsValue() -> JSValue { .number(Double(self)) }
2329
}
2430

25-
extension Int16: JSValueConvertible {
31+
extension Float: JSValueConvertible {
2632
public func jsValue() -> JSValue { .number(Double(self)) }
2733
}
2834

29-
extension Int32: JSValueConvertible {
30-
public func jsValue() -> JSValue { .number(Double(self)) }
35+
extension Double: JSValueConvertible {
36+
public func jsValue() -> JSValue { .number(self) }
3137
}
3238

33-
extension UInt: JSValueConvertible {
34-
public func jsValue() -> JSValue { .number(Double(self)) }
39+
extension String: JSValueConvertible {
40+
public func jsValue() -> JSValue { .string(self) }
3541
}
3642

3743
extension UInt8: JSValueConvertible {
@@ -43,27 +49,36 @@ extension UInt16: JSValueConvertible {
4349
}
4450

4551
extension UInt32: JSValueConvertible {
46-
public func jsValue() -> JSValue { .number(Double(self)) }
52+
public func jsValue() -> JSValue { .number(Double(self)) }
4753
}
4854

49-
extension Float: JSValueConvertible {
55+
extension UInt64: JSValueConvertible {
5056
public func jsValue() -> JSValue { .number(Double(self)) }
5157
}
5258

53-
extension Double: JSValueConvertible {
54-
public func jsValue() -> JSValue { .number(self) }
59+
extension Int8: JSValueConvertible {
60+
public func jsValue() -> JSValue { .number(Double(self)) }
5561
}
5662

57-
extension String: JSValueConvertible {
58-
public func jsValue() -> JSValue { .string(self) }
63+
extension Int16: JSValueConvertible {
64+
public func jsValue() -> JSValue { .number(Double(self)) }
5965
}
6066

61-
extension JSObject: JSValueConvertible {
67+
extension Int32: JSValueConvertible {
68+
public func jsValue() -> JSValue { .number(Double(self)) }
69+
}
70+
71+
extension Int64: JSValueConvertible {
72+
public func jsValue() -> JSValue { .number(Double(self)) }
73+
}
74+
75+
extension JSObject: JSValueCodable {
6276
// `JSObject.jsValue` is defined in JSObject.swift to be able to overridden
6377
// from `JSFunction`
6478
}
6579

66-
private let Object = JSObject.global.Object.function!
80+
private let objectConstructor = JSObject.global.Object.function!
81+
private let arrayConstructor = JSObject.global.Array.function!
6782

6883
extension Dictionary where Value: JSValueConvertible, Key == String {
6984
public func jsValue() -> JSValue {
@@ -73,32 +88,89 @@ extension Dictionary where Value: JSValueConvertible, Key == String {
7388

7489
extension Dictionary: JSValueConvertible where Value == JSValueConvertible, Key == String {
7590
public func jsValue() -> JSValue {
76-
let object = Object.new()
91+
let object = objectConstructor.new()
7792
for (key, value) in self {
7893
object[key] = value.jsValue()
7994
}
8095
return .object(object)
8196
}
8297
}
8398

84-
private let Array = JSObject.global.Array.function!
99+
extension Dictionary: JSValueConstructible where Value: JSValueConstructible, Key == String {
100+
public static func construct(from value: JSValue) -> Self? {
101+
guard
102+
let objectRef = value.object,
103+
let keys: [String] = objectConstructor.keys!(objectRef.jsValue()).fromJSValue()
104+
else { return nil }
105+
106+
var entries = [(String, Value)]()
107+
entries.reserveCapacity(keys.count)
108+
for key in keys {
109+
guard let value: Value = objectRef[key].fromJSValue() else {
110+
return nil
111+
}
112+
entries.append((key, value))
113+
}
114+
return Dictionary(uniqueKeysWithValues: entries)
115+
}
116+
}
117+
118+
extension Optional: JSValueConstructible where Wrapped: JSValueConstructible {
119+
public static func construct(from value: JSValue) -> Self? {
120+
switch value {
121+
case .null, .undefined:
122+
return nil
123+
default:
124+
return Wrapped.construct(from: value)
125+
}
126+
}
127+
}
128+
129+
extension Optional: JSValueConvertible where Wrapped: JSValueConvertible {
130+
public func jsValue() -> JSValue {
131+
switch self {
132+
case .none: return .null
133+
case let .some(wrapped): return wrapped.jsValue()
134+
}
135+
}
136+
}
85137

86138
extension Array where Element: JSValueConvertible {
87139
public func jsValue() -> JSValue {
88-
Swift.Array<JSValueConvertible>.jsValue(self)()
140+
Array<JSValueConvertible>.jsValue(self)()
89141
}
90142
}
91143

92144
extension Array: JSValueConvertible where Element == JSValueConvertible {
93145
public func jsValue() -> JSValue {
94-
let array = Array.new(count)
146+
let array = arrayConstructor.new(count)
95147
for (index, element) in enumerated() {
96148
array[index] = element.jsValue()
97149
}
98150
return .object(array)
99151
}
100152
}
101153

154+
extension Array: JSValueConstructible where Element: JSValueConstructible {
155+
public static func construct(from value: JSValue) -> [Element]? {
156+
guard
157+
let objectRef = value.object,
158+
objectRef.isInstanceOf(JSObject.global.Array.function!)
159+
else { return nil }
160+
161+
let count: Int = objectRef.length.fromJSValue()!
162+
var array = [Element]()
163+
array.reserveCapacity(count)
164+
165+
for i in 0 ..< count {
166+
guard let value: Element = objectRef[i].fromJSValue() else { return nil }
167+
array.append(value)
168+
}
169+
170+
return array
171+
}
172+
}
173+
102174
extension RawJSValue: JSValueConvertible {
103175
public func jsValue() -> JSValue {
104176
switch kind {
@@ -194,6 +266,6 @@ extension Array where Element == JSValueConvertible {
194266

195267
extension Array where Element: JSValueConvertible {
196268
func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
197-
Swift.Array<JSValueConvertible>.withRawJSValues(self)(body)
269+
[JSValueConvertible].withRawJSValues(self)(body)
198270
}
199271
}

0 commit comments

Comments
 (0)
Please sign in to comment.