Skip to content

Commit bc11c12

Browse files
Support Array interpolation
1 parent c29cc45 commit bc11c12

File tree

6 files changed

+172
-64
lines changed

6 files changed

+172
-64
lines changed

src/swift/Sources/JavaScriptKit/JSValue.swift

Lines changed: 102 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
public class JSRef: Equatable {
1+
public class JSObjectRef: Equatable {
22
let id: UInt32
33
fileprivate init(id: UInt32) {
44
self.id = id
55
}
6-
public static func global() -> JSRef {
6+
public static func global() -> JSObjectRef {
77
.init(id: _JS_Predef_Value_Global)
88
}
99

1010
deinit {
1111

1212
}
1313

14-
public static func == (lhs: JSRef, rhs: JSRef) -> Bool {
14+
public static func == (lhs: JSObjectRef, rhs: JSObjectRef) -> Bool {
1515
return lhs.id == rhs.id
1616
}
1717
}
@@ -20,7 +20,7 @@ public enum JSValue: Equatable {
2020
case boolean(Bool)
2121
case string(String)
2222
case number(Int32)
23-
case object(JSRef)
23+
case object(JSObjectRef)
2424
}
2525

2626
protocol JSValueConvertible {
@@ -35,76 +35,126 @@ extension Bool: JSValueConvertible {
3535

3636
import _CJavaScriptKit
3737

38-
public func getJSValue(this: JSRef, name: String) -> JSValue {
38+
struct RawJSValue {
3939
var kind: JavaScriptValueKind = JavaScriptValueKind_Invalid
4040
var payload1: JavaScriptPayload = 0
4141
var payload2: JavaScriptPayload = 0
42-
_get_js_value(this.id, name, Int32(name.count), &kind, &payload1, &payload2)
43-
switch kind {
44-
case JavaScriptValueKind_Invalid:
45-
fatalError()
46-
case JavaScriptValueKind_Boolean:
47-
return .boolean(payload1 != 0)
48-
case JavaScriptValueKind_Number:
49-
return .number(Int32(bitPattern: payload1))
50-
case JavaScriptValueKind_String:
51-
let buffer = malloc(Int(payload2))!.assumingMemoryBound(to: UInt8.self)
52-
_load_string(payload1 as JavaScriptValueId, buffer)
53-
let string = String(decodingCString: UnsafePointer(buffer), as: UTF8.self)
54-
return .string(string)
55-
case JavaScriptValueKind_Object:
56-
return .object(JSRef(id: payload1))
57-
default:
58-
fatalError("unreachable")
42+
}
43+
44+
extension RawJSValue: JSValueConvertible {
45+
func jsValue() -> JSValue {
46+
switch kind {
47+
case JavaScriptValueKind_Invalid:
48+
fatalError()
49+
case JavaScriptValueKind_Boolean:
50+
return .boolean(payload1 != 0)
51+
case JavaScriptValueKind_Number:
52+
return .number(Int32(bitPattern: payload1))
53+
case JavaScriptValueKind_String:
54+
// +1 for null terminator
55+
let buffer = malloc(Int(payload2 + 1))!.assumingMemoryBound(to: UInt8.self)
56+
defer { free(buffer) }
57+
_load_string(payload1 as JavaScriptValueId, buffer)
58+
buffer[Int(payload2)] = 0
59+
let string = String(decodingCString: UnsafePointer(buffer), as: UTF8.self)
60+
return .string(string)
61+
case JavaScriptValueKind_Object:
62+
return .object(JSObjectRef(id: payload1))
63+
default:
64+
fatalError("unreachable")
65+
}
5966
}
6067
}
6168

62-
public func setJSValue(this: JSRef, name: String, value: JSValue) {
63-
64-
let kind: JavaScriptValueKind
65-
let payload1: JavaScriptPayload
66-
let payload2: JavaScriptPayload
67-
switch value {
68-
case let .boolean(boolValue):
69-
kind = JavaScriptValueKind_Boolean
70-
payload1 = boolValue ? 1 : 0
71-
payload2 = 0
72-
case let .number(numberValue):
73-
kind = JavaScriptValueKind_Number
74-
payload1 = JavaScriptPayload(bitPattern: numberValue)
75-
payload2 = 0
76-
case var .string(stringValue):
77-
kind = JavaScriptValueKind_String
78-
stringValue.withUTF8 { bufferPtr in
79-
let ptrValue = UInt32(UInt(bitPattern: bufferPtr.baseAddress!))
80-
_set_js_value(
81-
this.id, name, Int32(name.count),
82-
kind, ptrValue, JavaScriptPayload(bufferPtr.count)
83-
)
69+
extension JSValue {
70+
func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
71+
let kind: JavaScriptValueKind
72+
let payload1: JavaScriptPayload
73+
let payload2: JavaScriptPayload
74+
switch self {
75+
case let .boolean(boolValue):
76+
kind = JavaScriptValueKind_Boolean
77+
payload1 = boolValue ? 1 : 0
78+
payload2 = 0
79+
case let .number(numberValue):
80+
kind = JavaScriptValueKind_Number
81+
payload1 = JavaScriptPayload(bitPattern: numberValue)
82+
payload2 = 0
83+
case var .string(stringValue):
84+
kind = JavaScriptValueKind_String
85+
return stringValue.withUTF8 { bufferPtr in
86+
let ptrValue = UInt32(UInt(bitPattern: bufferPtr.baseAddress!))
87+
let rawValue = RawJSValue(kind: kind, payload1: ptrValue, payload2: JavaScriptPayload(bufferPtr.count))
88+
return body(rawValue)
89+
}
90+
case let .object(ref):
91+
kind = JavaScriptValueKind_Object
92+
payload1 = ref.id
93+
payload2 = 0
8494
}
85-
return
86-
case let .object(ref):
87-
kind = JavaScriptValueKind_Object
88-
payload1 = ref.id
89-
payload2 = 0
95+
let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
96+
return body(rawValue)
97+
}
98+
}
99+
100+
public func getJSValue(this: JSObjectRef, name: String) -> JSValue {
101+
var rawValue = RawJSValue()
102+
_get_prop(this.id, name, Int32(name.count),
103+
&rawValue.kind,
104+
&rawValue.payload1, &rawValue.payload2)
105+
return rawValue.jsValue()
106+
}
107+
108+
public func setJSValue(this: JSObjectRef, name: String, value: JSValue) {
109+
value.withRawJSValue { rawValue in
110+
_set_prop(this.id, name, Int32(name.count), rawValue.kind, rawValue.payload1, rawValue.payload2)
111+
}
112+
}
113+
114+
115+
public func getJSValue(this: JSObjectRef, index: Int32) -> JSValue {
116+
var rawValue = RawJSValue()
117+
_get_subscript(this.id, index,
118+
&rawValue.kind,
119+
&rawValue.payload1, &rawValue.payload2)
120+
return rawValue.jsValue()
121+
}
122+
123+
124+
public func setJSValue(this: JSObjectRef, index: Int32, value: JSValue) {
125+
value.withRawJSValue { rawValue in
126+
_set_subscript(this.id, index,
127+
rawValue.kind,
128+
rawValue.payload1, rawValue.payload2)
90129
}
91-
_set_js_value(this.id, name, Int32(name.count), kind, payload1, payload2)
92130
}
93131

94132

95133
#if Xcode
96-
func _set_js_value(
134+
func _set_prop(
97135
_ _this: JavaScriptValueId,
98136
_ prop: UnsafePointer<Int8>!, _ length: Int32,
99137
_ kind: JavaScriptValueKind,
100138
_ payload1: JavaScriptPayload,
101139
_ payload2: JavaScriptPayload) { fatalError() }
102-
func _get_js_value(
140+
func _get_prop(
103141
_ _this: JavaScriptValueId,
104142
_ prop: UnsafePointer<Int8>!, _ length: Int32,
105143
_ kind: UnsafeMutablePointer<JavaScriptValueKind>!,
106144
_ payload1: UnsafeMutablePointer<JavaScriptPayload>!,
107145
_ payload2: UnsafeMutablePointer<JavaScriptPayload>!) { fatalError() }
146+
func _set_subscript(
147+
_ _this: JavaScriptValueId,
148+
_ index: Int32,
149+
_ kind: JavaScriptValueKind,
150+
_ payload1: JavaScriptPayload,
151+
_ payload2: JavaScriptPayload) { fatalError() }
152+
func _get_subscript(
153+
_ _this: JavaScriptValueId,
154+
_ index: Int32,
155+
_ kind: UnsafeMutablePointer<JavaScriptValueKind>!,
156+
_ payload1: UnsafeMutablePointer<JavaScriptPayload>!,
157+
_ payload2: UnsafeMutablePointer<JavaScriptPayload>!) { fatalError() }
108158
func _load_string(
109159
_ ref: JavaScriptValueId,
110160
_ buffer: UnsafeMutablePointer<UInt8>!) { fatalError() }

src/swift/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ typedef enum {
1111
JavaScriptValueKind_String = 1,
1212
JavaScriptValueKind_Number = 2,
1313
JavaScriptValueKind_Object = 3,
14-
JavaScriptValueKind_Array = 4,
15-
JavaScriptValueKind_Null = 5,
16-
JavaScriptValueKind_Function = 6,
14+
JavaScriptValueKind_Null = 4,
15+
JavaScriptValueKind_Function = 5,
1716
} JavaScriptValueKind;
1817

1918
typedef unsigned JavaScriptPayload;
@@ -23,9 +22,9 @@ const unsigned int _JS_Predef_Value_Global = 0;
2322

2423
__attribute__((
2524
__import_module__("javascript_kit"),
26-
__import_name__("swjs_set_js_value")
25+
__import_name__("swjs_set_prop")
2726
))
28-
extern void _set_js_value(
27+
extern void _set_prop(
2928
const JavaScriptValueId _this,
3029
const char *prop,
3130
const int length,
@@ -36,9 +35,9 @@ extern void _set_js_value(
3635

3736
__attribute__((
3837
__import_module__("javascript_kit"),
39-
__import_name__("swjs_get_js_value")
38+
__import_name__("swjs_get_prop")
4039
))
41-
extern void _get_js_value(
40+
extern void _get_prop(
4241
const JavaScriptValueId _this,
4342
const char *prop,
4443
const int length,
@@ -47,6 +46,30 @@ extern void _get_js_value(
4746
JavaScriptPayload *payload2
4847
);
4948

49+
__attribute__((
50+
__import_module__("javascript_kit"),
51+
__import_name__("swjs_set_subscript")
52+
))
53+
extern void _set_subscript(
54+
const JavaScriptValueId _this,
55+
const int length,
56+
const JavaScriptValueKind kind,
57+
const JavaScriptPayload payload1,
58+
const JavaScriptPayload payload2
59+
);
60+
61+
__attribute__((
62+
__import_module__("javascript_kit"),
63+
__import_name__("swjs_get_subscript")
64+
))
65+
extern void _get_subscript(
66+
const JavaScriptValueId _this,
67+
const int length,
68+
JavaScriptValueKind *kind,
69+
JavaScriptPayload *payload1,
70+
JavaScriptPayload *payload2
71+
);
72+
5073
__attribute__((
5174
__import_module__("javascript_kit"),
5275
__import_name__("swjs_load_string")

src/web/src/index.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,15 @@ export class SwiftRuntime {
142142
}
143143

144144
return {
145-
swjs_set_js_value: (
145+
swjs_set_prop: (
146146
ref: ref, name: pointer, length: number,
147147
kind: JavaScriptValueKind,
148148
payload1: number, payload2: number
149149
) => {
150150
const obj = this._heapValues[ref];
151151
Reflect.set(obj, readString(name, length), decodeValue(kind, payload1, payload2))
152152
},
153-
swjs_get_js_value: (
153+
swjs_get_prop: (
154154
ref: ref, name: pointer, length: number,
155155
kind_ptr: pointer,
156156
payload1_ptr: pointer, payload2_ptr: pointer
@@ -162,6 +162,26 @@ export class SwiftRuntime {
162162
writeUint32(payload1_ptr, payload1);
163163
writeUint32(payload2_ptr, payload2);
164164
},
165+
swjs_set_subscript: (
166+
ref: ref, index: number,
167+
kind: JavaScriptValueKind,
168+
payload1: number, payload2: number
169+
) => {
170+
const obj = this._heapValues[ref];
171+
Reflect.set(obj, index, decodeValue(kind, payload1, payload2))
172+
},
173+
swjs_get_subscript: (
174+
ref: ref, index: number,
175+
kind_ptr: pointer,
176+
payload1_ptr: pointer, payload2_ptr: pointer
177+
) => {
178+
const obj = this._heapValues[ref];
179+
const result = Reflect.get(obj, index);
180+
const { kind, payload1, payload2 } = encodeValue(result);
181+
writeUint32(kind_ptr, kind);
182+
writeUint32(payload1_ptr, payload1);
183+
writeUint32(payload2_ptr, payload2);
184+
},
165185
swjs_load_string: (ref: ref, buffer: pointer) => {
166186
const string = this._heapValues[ref];
167187
writeString(buffer, string);

test/JavaScriptKitExec/Sources/JavaScriptKitExec/UnitTestUtils.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func expectEqual<T: Equatable>(_ lhs: T, _ rhs: T) throws {
1313
}
1414
}
1515

16-
func expectObject(_ value: JSValue) throws -> JSRef {
16+
func expectObject(_ value: JSValue) throws -> JSObjectRef {
1717
switch value {
1818
case .object(let ref): return ref
1919
default:
@@ -33,6 +33,6 @@ func expectNumber(_ value: JSValue) throws -> Int32 {
3333
switch value {
3434
case .number(let number): return number
3535
default:
36-
throw MessageError("Type of \(value) should be \"boolean\"")
36+
throw MessageError("Type of \(value) should be \"number\"")
3737
}
3838
}

test/JavaScriptKitExec/Sources/JavaScriptKitExec/main.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import JavaScriptKit
22

33
Literal_Conversion: do {
4-
let global = JSRef.global()
4+
let global = JSObjectRef.global()
55
let inputs: [JSValue] = [
66
.boolean(true),
77
.boolean(false),
@@ -30,6 +30,9 @@ Object_Conversion: do {
3030
// },
3131
// "prop_2": 2,
3232
// "prop_3": true,
33+
// "prop_4": [
34+
// 3, 4, "str_elm_1", 5,
35+
// ],
3336
// }
3437
// ```
3538
//
@@ -44,6 +47,15 @@ Object_Conversion: do {
4447
try expectEqual(prop_2, .number(2))
4548
let prop_3 = getJSValue(this: globalObject1Ref, name: "prop_3")
4649
try expectEqual(prop_3, .boolean(true))
50+
let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
51+
let prop_4Array = try expectObject(prop_4)
52+
let expectedProp_4: [JSValue] = [
53+
.number(3), .number(4), .string("str_elm_1"), .number(5)
54+
]
55+
for (index, expectedElement) in expectedProp_4.enumerated() {
56+
let actualElement = getJSValue(this: prop_4Array, index: Int32(index))
57+
try expectEqual(actualElement, expectedElement)
58+
}
4759
} catch {
4860
print(error)
4961
}

test/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ global.globalObject1 = {
2424
},
2525
"prop_2": 2,
2626
"prop_3": true,
27+
"prop_4": [
28+
3, 4, "str_elm_1", 5,
29+
],
2730
}
2831

2932
const startWasiTask = async () => {

0 commit comments

Comments
 (0)