Skip to content

Commit 348129d

Browse files
Support String interpolation
1 parent 051ef68 commit 348129d

File tree

5 files changed

+109
-25
lines changed

5 files changed

+109
-25
lines changed

src/swift/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ build: .wasi-sdk/dummy
66
swift build --triple wasm32-unknown-wasi \
77
-Xswiftc -Xclang-linker \
88
-Xswiftc --sysroot=$(WASI_SDK_DIR)/share/wasi-sysroot \
9+
-Xcc --sysroot=$(WASI_SDK_DIR)/share/wasi-sysroot \
910
-Xlinker --allow-undefined
1011
.wasi-sdk/dummy:
1112
./script/install-wasi-sdk.sh $(WASI_SDK_DIR)

src/swift/Sources/JavaScriptKit/JSValue.swift

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
public class JSRef {
1+
public class JSRef: Equatable {
22
let id: UInt32
3-
init(id: UInt32) {
3+
fileprivate init(id: UInt32) {
44
self.id = id
55
}
66
public static func global() -> JSRef {
@@ -10,6 +10,10 @@ public class JSRef {
1010
deinit {
1111

1212
}
13+
14+
public static func == (lhs: JSRef, rhs: JSRef) -> Bool {
15+
return lhs.id == rhs.id
16+
}
1317
}
1418

1519
public enum JSValue: Equatable {
@@ -31,16 +35,19 @@ import _CJavaScriptKit
3135

3236
public func getJSValue(this: JSRef, name: String) -> JSValue {
3337
var kind: JavaScriptValueKind = JavaScriptValueKind_Invalid
34-
var payload: JavaScriptPayload = 0
35-
_get_js_value(this.id, name, Int32(name.count), &kind, &payload)
38+
var payload1: JavaScriptPayload = 0
39+
var payload2: JavaScriptPayload = 0
40+
_get_js_value(this.id, name, Int32(name.count), &kind, &payload1, &payload2)
3641
switch kind {
3742
case JavaScriptValueKind_Invalid:
3843
fatalError()
3944
case JavaScriptValueKind_Boolean:
40-
return .boolean(payload != 0)
45+
return .boolean(payload1 != 0)
4146
case JavaScriptValueKind_String:
42-
let ptr = UnsafePointer<UInt8>(bitPattern: UInt(payload))!
43-
return .string(String(decodingCString: ptr, as: UTF8.self))
47+
let buffer = malloc(Int(payload2))!.assumingMemoryBound(to: UInt8.self)
48+
_load_string(payload1 as JavaScriptValueId, buffer)
49+
let string = String(decodingCString: UnsafePointer(buffer), as: UTF8.self)
50+
return .string(string)
4451
default:
4552
fatalError("unreachable")
4653
}
@@ -56,17 +63,28 @@ public func setJSValue(this: JSRef, name: String, value: JSValue) {
5663
payload = boolValue ? 1 : 0
5764
case var .string(stringValue):
5865
kind = JavaScriptValueKind_String
59-
stringValue.withUTF8 { ptr in
60-
let ptrValue = unsafeBitCast(ptr, to: UInt32.self)
61-
_set_js_value(this.id, name, Int32(name.count), kind, ptrValue)
66+
stringValue.withUTF8 { bufferPtr in
67+
let ptrValue = UInt32(UInt(bitPattern: bufferPtr.baseAddress!))
68+
_set_js_value(this.id, name, Int32(name.count), kind, ptrValue, 0)
6269
}
6370
return
6471
}
65-
_set_js_value(this.id, name, Int32(name.count), kind, payload)
72+
_set_js_value(this.id, name, Int32(name.count), kind, payload, 0)
6673
}
6774

6875

6976
#if Xcode
70-
func _set_js_value(_ _this: JavaScriptValueId, _ prop: UnsafePointer<Int8>!, _ length: Int32, _ kind: JavaScriptValueKind, _ value: JavaScriptPayload) { fatalError() }
71-
func _get_js_value(_ _this: JavaScriptValueId, _ prop: UnsafePointer<Int8>!, _ length: Int32, _ kind: UnsafeMutablePointer<JavaScriptValueKind>!, _ value: UnsafeMutablePointer<JavaScriptPayload>!) {}
77+
func _set_js_value(
78+
_ _this: JavaScriptValueId,
79+
_ prop: UnsafePointer<Int8>!, _ length: Int32,
80+
_ kind: JavaScriptValueKind,
81+
_ payload1: JavaScriptPayload,
82+
_ payload2: JavaScriptPayload) { fatalError() }
83+
func _get_js_value(
84+
_ _this: JavaScriptValueId,
85+
_ prop: UnsafePointer<Int8>!, _ length: Int32,
86+
_ kind: UnsafeMutablePointer<JavaScriptValueKind>!,
87+
_ payload1: UnsafeMutablePointer<JavaScriptPayload>!,
88+
_ payload2: UnsafeMutablePointer<JavaScriptPayload>!) { fatalError() }
89+
func _load_string(_ ref: JavaScriptValueId, _ buffer: UnsafeMutablePointer<UInt8>!) { fatalError() }
7290
#endif

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
#ifndef _CJavaScriptKit_h
22
#define _CJavaScriptKit_h
33

4+
#include <stdlib.h>
5+
46
typedef unsigned int JavaScriptValueId;
57

68
typedef enum {
7-
JavaScriptValueKind_Invalid = -1,
8-
JavaScriptValueKind_Boolean = 0,
9-
JavaScriptValueKind_String = 1,
9+
JavaScriptValueKind_Invalid = -1,
10+
JavaScriptValueKind_Boolean = 0,
11+
JavaScriptValueKind_String = 1,
12+
JavaScriptValueKind_Number = 2,
13+
JavaScriptValueKind_Object = 3,
14+
JavaScriptValueKind_Array = 4,
15+
JavaScriptValueKind_Null = 5,
16+
JavaScriptValueKind_Function = 6,
1017
} JavaScriptValueKind;
1118

1219
typedef unsigned JavaScriptPayload;
@@ -23,7 +30,8 @@ extern void _set_js_value(
2330
const char *prop,
2431
const int length,
2532
const JavaScriptValueKind kind,
26-
const JavaScriptPayload value
33+
const JavaScriptPayload payload1,
34+
const JavaScriptPayload payload2
2735
);
2836

2937
__attribute__((
@@ -35,7 +43,17 @@ extern void _get_js_value(
3543
const char *prop,
3644
const int length,
3745
JavaScriptValueKind *kind,
38-
JavaScriptPayload *value
46+
JavaScriptPayload *payload1,
47+
JavaScriptPayload *payload2
48+
);
49+
50+
__attribute__((
51+
__import_module__("javascript_kit"),
52+
__import_name__("swjs_load_string")
53+
))
54+
extern void _load_string(
55+
const JavaScriptValueId ref,
56+
unsigned char *buffer
3957
);
4058

4159
#endif /* _CJavaScriptKit_h */

src/web/src/index.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@ interface ExportedMemory {
55
type ref = number;
66
type pointer = number;
77

8-
interface GlobalVariable {}
8+
interface GlobalVariable { }
99
declare const window: GlobalVariable;
1010
declare const global: GlobalVariable;
1111

12+
13+
enum JavaScriptValueKind {
14+
Invalid = -1,
15+
Boolean = 0,
16+
String = 1,
17+
Number = 2,
18+
Object = 3,
19+
Array = 4,
20+
Null = 5,
21+
Function = 6,
22+
}
23+
1224
export class SwiftRuntime {
1325
private instance: WebAssembly.Instance | null;
1426
private _heapValues: any[]
@@ -36,7 +48,16 @@ export class SwiftRuntime {
3648
return this.instance.exports.memory as ExportedMemory;
3749
throw new Error("WebAssembly instance is not set yet");
3850
}
51+
52+
const allocValue = (value: any) => {
53+
// TODO
54+
const id = this._heapValues.length
55+
this._heapValues.push(value)
56+
return id
57+
}
58+
3959
const textDecoder = new TextDecoder('utf-8');
60+
const textEncoder = new TextEncoder(); // Only support utf-8
4061

4162
const readString = (ptr: pointer, len: number) => {
4263
const uint8Memory = new Uint8Array(memory().buffer);
@@ -48,6 +69,15 @@ export class SwiftRuntime {
4869
return textDecoder.decode(uint8Memory.slice(ptr));
4970
}
5071

72+
const writeString = (ptr: pointer, value: string) => {
73+
const bytes = textEncoder.encode(value);
74+
const uint8Memory = new Uint8Array(memory().buffer);
75+
for (const [index, byte] of bytes.entries()) {
76+
uint8Memory[ptr + index] = byte
77+
}
78+
uint8Memory[ptr]
79+
}
80+
5181
const writeUint32 = (ptr: pointer, value: number) => {
5282
const uint8Memory = new Uint8Array(memory().buffer);
5383
uint8Memory[ptr + 0] = value & 0x000000ff
@@ -56,17 +86,21 @@ export class SwiftRuntime {
5686
uint8Memory[ptr + 3] = value & 0xff000000
5787
}
5888

59-
const decodeValue = (kind: number, payload: number) => {
89+
const decodeValue = (kind: JavaScriptValueKind, payload: number) => {
6090
switch (kind) {
61-
case 0: {
91+
case JavaScriptValueKind.Boolean: {
6292
switch (payload) {
6393
case 0: return false
6494
case 1: return true
6595
}
6696
}
67-
case 1: {
97+
case JavaScriptValueKind.String: {
6898
return readStringUntilNull(payload)
6999
}
100+
case JavaScriptValueKind.Object: {
101+
}
102+
default:
103+
throw new Error(`Type kind "${kind}" is not supported`)
70104
}
71105
}
72106

@@ -77,22 +111,32 @@ export class SwiftRuntime {
77111
kind: 0,
78112
payload: value ? 1 : 0,
79113
}
114+
case "string": {
115+
return {
116+
kind: JavaScriptValueKind.String,
117+
payload: allocValue(value),
118+
}
119+
}
80120
default:
81121
throw new Error(`Type "${typeof value}" is not supported yet`)
82122
}
83123
}
84124

85125
return {
86-
swjs_set_js_value: (ref: ref, name: number, length: number, kind: number, payload: number) => {
126+
swjs_set_js_value: (ref: ref, name: pointer, length: number, kind: JavaScriptValueKind, payload: number) => {
87127
const obj = this._heapValues[ref];
88128
Reflect.set(obj, readString(name, length), decodeValue(kind, payload))
89129
},
90-
swjs_get_js_value: (ref: ref, name: number, length: number, kind_ptr: pointer, payload_ptr: pointer) => {
130+
swjs_get_js_value: (ref: ref, name: pointer, length: number, kind_ptr: pointer, payload_ptr: pointer) => {
91131
const obj = this._heapValues[ref];
92132
const result = Reflect.get(obj, readString(name, length));
93133
const { kind, payload } = encodeValue(result);
94134
writeUint32(kind_ptr, kind);
95135
writeUint32(payload_ptr, payload);
136+
},
137+
swjs_load_string: (ref: ref, buffer: pointer) => {
138+
const string = this._heapValues[ref];
139+
writeString(buffer, string);
96140
}
97141
}
98142
}

test/JavaScriptKitExec/Sources/JavaScriptKitExec/main.swift

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

33
func expectEqual<T: Equatable>(_ lhs: T, _ rhs: T) {
4-
assert(lhs == rhs, "Expect to be equal \"\(lhs)\" and \"\(rhs)\"")
4+
if lhs != rhs {
5+
print("[ERROR] Expect to be equal \"\(lhs)\" and \"\(rhs)\"")
6+
}
57
}
68

79
let global = JSRef.global()
810
do {
911
let inputs: [JSValue] = [
1012
.boolean(true),
1113
.boolean(false),
14+
.string("foobar"),
1215
]
1316
for (index, input) in inputs.enumerated() {
1417
let prop = "prop_\(index)"

0 commit comments

Comments
 (0)