Skip to content

Commit c67bd51

Browse files
Support Holes in Array (#41)
* Support Holes in Array * Use filter(() => true) * Revert accidently changed line
1 parent 3314250 commit c67bd51

File tree

3 files changed

+56
-8
lines changed

3 files changed

+56
-8
lines changed

IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,36 @@ try test("Array Iterator") {
8686
let globalObject1 = getJSValue(this: .global, name: "globalObject1")
8787
let globalObject1Ref = try expectObject(globalObject1)
8888
let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
89-
let array = try expectArray(prop_4)
89+
let array1 = try expectArray(prop_4)
9090
let expectedProp_4: [JSValue] = [
9191
.number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
9292
]
93-
try expectEqual(Array(array), expectedProp_4)
93+
try expectEqual(Array(array1), expectedProp_4)
94+
95+
// Ensure that iterator skips empty hole as JavaScript does.
96+
let prop_8 = getJSValue(this: globalObject1Ref, name: "prop_8")
97+
let array2 = try expectArray(prop_8)
98+
let expectedProp_8: [JSValue] = [0, 2, 3, 6]
99+
try expectEqual(Array(array2), expectedProp_8)
94100
}
95101

96102
try test("Array RandomAccessCollection") {
97103
let globalObject1 = getJSValue(this: .global, name: "globalObject1")
98104
let globalObject1Ref = try expectObject(globalObject1)
99105
let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
100-
let array = try expectArray(prop_4)
106+
let array1 = try expectArray(prop_4)
101107
let expectedProp_4: [JSValue] = [
102108
.number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
103109
]
104-
try expectEqual([array[0], array[1], array[2], array[3], array[4], array[5]], expectedProp_4)
110+
try expectEqual([array1[0], array1[1], array1[2], array1[3], array1[4], array1[5]], expectedProp_4)
111+
112+
// Ensure that subscript can access empty hole
113+
let prop_8 = getJSValue(this: globalObject1Ref, name: "prop_8")
114+
let array2 = try expectArray(prop_8)
115+
let expectedProp_8: [JSValue] = [
116+
0, .undefined, 2, 3, .undefined, .undefined, 6
117+
]
118+
try expectEqual([array2[0], array2[1], array2[2], array2[3], array2[4], array2[5], array2[6]], expectedProp_8)
105119
}
106120

107121
try test("Value Decoder") {

IntegrationTests/bin/primary-tests.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ global.globalObject1 = {
2323
}
2424
},
2525
"prop_7": 3.14,
26+
"prop_8": [0, , 2, 3, , , 6],
2627
}
2728

2829
global.Animal = function(name, age, isCat) {

Sources/JavaScriptKit/BasicObjects/JSArray.swift

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ extension JSArray: RandomAccessCollection {
2828
}
2929

3030
public func next() -> Element? {
31-
defer { index += 1 }
32-
guard index < Int(ref.length.number!) else {
31+
let currentIndex = index
32+
guard currentIndex < Int(ref.length.number!) else {
3333
return nil
3434
}
35-
let value = ref[index]
35+
index += 1
36+
guard ref.hasOwnProperty!(currentIndex).boolean! else {
37+
return next()
38+
}
39+
let value = ref[currentIndex]
3640
return value
3741
}
3842
}
@@ -43,7 +47,36 @@ extension JSArray: RandomAccessCollection {
4347

4448
public var startIndex: Int { 0 }
4549

46-
public var endIndex: Int { ref.length.number.map(Int.init) ?? 0 }
50+
public var endIndex: Int { length }
51+
52+
/// The number of elements in that array including empty hole.
53+
/// Note that `length` respects JavaScript's `Array.prototype.length`
54+
///
55+
/// e.g.
56+
/// ```javascript
57+
/// const array = [1, , 3];
58+
/// ```
59+
/// ```swift
60+
/// let array: JSArray = ...
61+
/// array.length // 3
62+
/// array.count // 2
63+
/// ```
64+
public var length: Int {
65+
return Int(ref.length.number!)
66+
}
67+
68+
/// The number of elements in that array **not** including empty hole.
69+
/// Note that `count` syncs with the number that `Iterator` can iterate.
70+
/// See also: `JSArray.length`
71+
public var count: Int {
72+
return getObjectValuesLength(ref)
73+
}
74+
}
75+
76+
private let alwaysTrue = JSClosure { _ in .boolean(true) }
77+
private func getObjectValuesLength(_ object: JSObject) -> Int {
78+
let values = object.filter!(alwaysTrue).object!
79+
return Int(values.length.number!)
4780
}
4881

4982
extension JSValue {

0 commit comments

Comments
 (0)