Skip to content

Commit f43b895

Browse files
committed
Pass around a DataView instead of individual kind/payload fields; change memory layout for numbers
There are now only two payload slots. Numbers use them as a single Double, while every other type just uses them as two UInt32s
1 parent b5382e6 commit f43b895

File tree

6 files changed

+95
-147
lines changed

6 files changed

+95
-147
lines changed

Runtime/src/index.ts

Lines changed: 69 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class SwiftRuntimeHeap {
5959

6060
allocHeap(value: any) {
6161
const isObject = typeof value == "object";
62-
const entry = this._heapEntryByValue.get(value);
62+
const entry = this._heapEntryByValue.get(value);
6363
if (isObject && entry) {
6464
entry.rc++
6565
return entry.id
@@ -124,10 +124,12 @@ export class SwiftRuntime {
124124
const exports = this.instance.exports as any as SwiftRuntimeExportedFunctions;
125125
const argc = args.length
126126
const argv = exports.swjs_prepare_host_function_call(argc)
127+
const uint32Memory = new Uint32Array(memory().buffer, argv, args.length * 3)
127128
for (let index = 0; index < args.length; index++) {
128129
const argument = args[index]
129-
const base = argv + 24 * index
130-
writeValue(argument, base, base + 4, base + 8, base + 16)
130+
const offset = 12 * index
131+
const dataView = new DataView(memory().buffer, argv + offset, 12);
132+
writeValue(argument, dataView)
131133
}
132134
let output: any;
133135
const callback_func_ref = this.heap.allocHeap(function (result: any) {
@@ -142,24 +144,20 @@ export class SwiftRuntime {
142144
const textEncoder = new TextEncoder(); // Only support utf-8
143145

144146
const readString = (ptr: pointer, len: number) => {
145-
const uint8Memory = new Uint8Array(memory().buffer);
146-
return textDecoder.decode(uint8Memory.subarray(ptr, ptr + len));
147+
const uint8Memory = new Uint8Array(memory().buffer, ptr, len);
148+
return textDecoder.decode(uint8Memory);
147149
}
148150

149151
const writeString = (ptr: pointer, bytes: Uint8Array) => {
150-
const uint8Memory = new Uint8Array(memory().buffer);
152+
const uint8Memory = new Uint8Array(memory().buffer, ptr);
151153
for (const [index, byte] of bytes.entries()) {
152-
uint8Memory[ptr + index] = byte
154+
uint8Memory[index] = byte
153155
}
154-
uint8Memory[ptr]
155156
}
156157

157158
const readUInt32 = (ptr: pointer) => {
158-
const uint8Memory = new Uint8Array(memory().buffer);
159-
return uint8Memory[ptr + 0]
160-
+ (uint8Memory[ptr + 1] << 8)
161-
+ (uint8Memory[ptr + 2] << 16)
162-
+ (uint8Memory[ptr + 3] << 24)
159+
const uint32Memory = new Uint32Array(memory().buffer, ptr, 1);
160+
return uint32Memory[0];
163161
}
164162

165163
const readFloat64 = (ptr: pointer) => {
@@ -168,37 +166,37 @@ export class SwiftRuntime {
168166
}
169167

170168
const writeUint32 = (ptr: pointer, value: number) => {
171-
const uint8Memory = new Uint8Array(memory().buffer);
172-
uint8Memory[ptr + 0] = (value & 0x000000ff) >> 0
173-
uint8Memory[ptr + 1] = (value & 0x0000ff00) >> 8
174-
uint8Memory[ptr + 2] = (value & 0x00ff0000) >> 16
175-
uint8Memory[ptr + 3] = (value & 0xff000000) >> 24
169+
const uint32Memory = new Uint32Array(memory().buffer, ptr);
170+
uint32Memory[0] = (value & 0xffffffff);
176171
}
177172

178173
const writeFloat64 = (ptr: pointer, value: number) => {
179174
const dataView = new DataView(memory().buffer);
180175
dataView.setFloat64(ptr, value, true);
181176
}
182177

183-
const decodeValue = (
184-
kind: JavaScriptValueKind,
185-
payload1: number, payload2: number, payload3: number
186-
) => {
178+
const decodeValue = (dataView: DataView) => {
179+
const kind = dataView.getUint32(0, true);
180+
const payload1_ptr = Uint32Array.BYTES_PER_ELEMENT
181+
const payload2_ptr = 2 * Uint32Array.BYTES_PER_ELEMENT
187182
switch (kind) {
188183
case JavaScriptValueKind.Boolean: {
189-
switch (payload1) {
184+
switch (dataView.getUint32(payload1_ptr, true)) {
190185
case 0: return false
191186
case 1: return true
192187
}
193188
}
194189
case JavaScriptValueKind.Number: {
195-
return payload3;
190+
return dataView.getFloat64(payload1_ptr, true);
196191
}
197192
case JavaScriptValueKind.String: {
198-
return readString(payload1, payload2)
193+
return readString(
194+
dataView.getUint32(payload1_ptr, true),
195+
dataView.getUint32(payload2_ptr, true)
196+
);
199197
}
200198
case JavaScriptValueKind.Object: {
201-
return this.heap.referenceHeap(payload1)
199+
return this.heap.referenceHeap(dataView.getUint32(payload1_ptr, true))
202200
}
203201
case JavaScriptValueKind.Null: {
204202
return null
@@ -207,60 +205,57 @@ export class SwiftRuntime {
207205
return undefined
208206
}
209207
case JavaScriptValueKind.Function: {
210-
return this.heap.referenceHeap(payload1)
208+
return this.heap.referenceHeap(dataView.getUint32(payload1_ptr, true))
211209
}
212210
default:
213211
throw new Error(`Type kind "${kind}" is not supported`)
214212
}
215213
}
216214

217-
const writeValue = (
218-
value: any, kind_ptr: pointer,
219-
payload1_ptr: pointer, payload2_ptr: pointer, payload3_ptr: pointer
220-
) => {
215+
const writeValue = (value: any, dataView: DataView) => {
216+
const payload1_ptr = Uint32Array.BYTES_PER_ELEMENT
217+
const payload2_ptr = 2 * Uint32Array.BYTES_PER_ELEMENT
221218
if (value === null) {
222-
writeUint32(kind_ptr, JavaScriptValueKind.Null);
223-
writeUint32(payload1_ptr, 0);
224-
writeUint32(payload2_ptr, 0);
219+
dataView.setUint32(0, JavaScriptValueKind.Null, true);
220+
dataView.setUint32(payload1_ptr, 0, true);
221+
dataView.setUint32(payload2_ptr, 0, true);
225222
return;
226223
}
227224
switch (typeof value) {
228225
case "boolean": {
229-
writeUint32(kind_ptr, JavaScriptValueKind.Boolean);
230-
writeUint32(payload1_ptr, value ? 1 : 0);
231-
writeUint32(payload2_ptr, 0);
226+
dataView.setUint32(0, JavaScriptValueKind.Boolean, true);
227+
dataView.setUint32(payload1_ptr, value ? 1 : 0, true);
228+
dataView.setUint32(payload2_ptr, 0, true);
232229
break;
233230
}
234231
case "number": {
235-
writeUint32(kind_ptr, JavaScriptValueKind.Number);
236-
writeUint32(payload1_ptr, 0);
237-
writeUint32(payload2_ptr, 0);
238-
writeFloat64(payload3_ptr, value);
232+
dataView.setUint32(0, JavaScriptValueKind.Number, true);
233+
dataView.setFloat64(payload1_ptr, value, true);
239234
break;
240235
}
241236
case "string": {
242237
const bytes = textEncoder.encode(value);
243-
writeUint32(kind_ptr, JavaScriptValueKind.String);
244-
writeUint32(payload1_ptr, this.heap.allocHeap(bytes));
245-
writeUint32(payload2_ptr, bytes.length);
238+
dataView.setUint32(0, JavaScriptValueKind.String, true);
239+
dataView.setUint32(payload1_ptr, this.heap.allocHeap(bytes), true);
240+
dataView.setUint32(payload2_ptr, bytes.length, true);
246241
break;
247242
}
248243
case "undefined": {
249-
writeUint32(kind_ptr, JavaScriptValueKind.Undefined);
250-
writeUint32(payload1_ptr, 0);
251-
writeUint32(payload2_ptr, 0);
244+
dataView.setUint32(0, JavaScriptValueKind.Undefined, true);
245+
dataView.setUint32(payload1_ptr, 0, true);
246+
dataView.setUint32(payload2_ptr, 0, true);
252247
break;
253248
}
254249
case "object": {
255-
writeUint32(kind_ptr, JavaScriptValueKind.Object);
256-
writeUint32(payload1_ptr, this.heap.allocHeap(value));
257-
writeUint32(payload2_ptr, 0);
250+
dataView.setUint32(0, JavaScriptValueKind.Object, true);
251+
dataView.setUint32(payload1_ptr, this.heap.allocHeap(value), true);
252+
dataView.setUint32(payload2_ptr, 0, true);
258253
break;
259254
}
260255
case "function": {
261-
writeUint32(kind_ptr, JavaScriptValueKind.Function);
262-
writeUint32(payload1_ptr, this.heap.allocHeap(value));
263-
writeUint32(payload2_ptr, 0);
256+
dataView.setUint32(0, JavaScriptValueKind.Function, true);
257+
dataView.setUint32(payload1_ptr, this.heap.allocHeap(value), true);
258+
dataView.setUint32(payload2_ptr, 0, true);
264259
break;
265260
}
266261
default:
@@ -274,74 +269,71 @@ export class SwiftRuntime {
274269
const decodeValues = (ptr: pointer, length: number) => {
275270
let result = []
276271
for (let index = 0; index < length; index++) {
277-
const base = ptr + 24 * index
278-
const kind = readUInt32(base)
279-
const payload1 = readUInt32(base + 4)
280-
const payload2 = readUInt32(base + 8)
281-
const payload3 = readFloat64(base + 16)
282-
result.push(decodeValue(kind, payload1, payload2, payload3))
272+
const offset = 12 * index
273+
const dataView = new DataView(memory().buffer, ptr + offset, 12);
274+
result.push(decodeValue(dataView))
283275
}
284276
return result
285277
}
286278

287279
return {
288280
swjs_set_prop: (
289281
ref: ref, name: pointer, length: number,
290-
kind: JavaScriptValueKind,
291-
payload1: number, payload2: number, payload3: number
282+
rawJSValuePtr: pointer
292283
) => {
293284
const obj = this.heap.referenceHeap(ref);
294-
Reflect.set(obj, readString(name, length), decodeValue(kind, payload1, payload2, payload3))
285+
const dataView = new DataView(memory().buffer, rawJSValuePtr, 12);
286+
Reflect.set(obj, readString(name, length), decodeValue(dataView))
295287
},
296288
swjs_get_prop: (
297289
ref: ref, name: pointer, length: number,
298-
kind_ptr: pointer,
299-
payload1_ptr: pointer, payload2_ptr: pointer, payload3_ptr: number
290+
rawJSValuePtr: pointer
300291
) => {
301292
const obj = this.heap.referenceHeap(ref);
302293
const result = Reflect.get(obj, readString(name, length));
303-
writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, payload3_ptr);
294+
const dataView = new DataView(memory().buffer, rawJSValuePtr, 12);
295+
writeValue(result, dataView);
304296
},
305297
swjs_set_subscript: (
306298
ref: ref, index: number,
307-
kind: JavaScriptValueKind,
308-
payload1: number, payload2: number, payload3: number
299+
rawJSValuePtr: pointer
309300
) => {
310301
const obj = this.heap.referenceHeap(ref);
311-
Reflect.set(obj, index, decodeValue(kind, payload1, payload2, payload3))
302+
const dataView = new DataView(memory().buffer, rawJSValuePtr, 12);
303+
Reflect.set(obj, index, decodeValue(dataView))
312304
},
313305
swjs_get_subscript: (
314306
ref: ref, index: number,
315-
kind_ptr: pointer,
316-
payload1_ptr: pointer, payload2_ptr: pointer, payload3_ptr: pointer
307+
rawJSValuePtr: pointer
317308
) => {
318309
const obj = this.heap.referenceHeap(ref);
319310
const result = Reflect.get(obj, index);
320-
writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, payload3_ptr);
311+
const dataView = new DataView(memory().buffer, rawJSValuePtr, 12);
312+
writeValue(result, dataView);
321313
},
322314
swjs_load_string: (ref: ref, buffer: pointer) => {
323315
const bytes = this.heap.referenceHeap(ref);
324316
writeString(buffer, bytes);
325317
},
326318
swjs_call_function: (
327319
ref: ref, argv: pointer, argc: number,
328-
kind_ptr: pointer,
329-
payload1_ptr: pointer, payload2_ptr: pointer, payload3_ptr: pointer
320+
rawJSValuePtr: pointer
330321
) => {
331322
const func = this.heap.referenceHeap(ref)
332323
const result = Reflect.apply(func, undefined, decodeValues(argv, argc))
333-
writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, payload3_ptr);
324+
const dataView = new DataView(memory().buffer, rawJSValuePtr, 12);
325+
writeValue(result, dataView);
334326
},
335327
swjs_call_function_with_this: (
336328
obj_ref: ref, func_ref: ref,
337329
argv: pointer, argc: number,
338-
kind_ptr: pointer,
339-
payload1_ptr: pointer, payload2_ptr: pointer, payload3_ptr: pointer
330+
rawJSValuePtr: pointer
340331
) => {
341332
const obj = this.heap.referenceHeap(obj_ref)
342333
const func = this.heap.referenceHeap(func_ref)
343334
const result = Reflect.apply(func, obj, decodeValues(argv, argc))
344-
writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, payload3_ptr);
335+
const dataView = new DataView(memory().buffer, rawJSValuePtr, 12);
336+
writeValue(result, dataView);
345337
},
346338
swjs_create_function: (
347339
host_func_id: number,

Sources/JavaScriptKit/JSFunction.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ public class JSFunctionRef: JSObjectRef {
99
let argv = bufferPointer.baseAddress
1010
let argc = bufferPointer.count
1111
var result = RawJSValue()
12-
_call_function(
13-
self.id, argv, Int32(argc),
14-
&result.kind, &result.payload1, &result.payload2, &result.payload3
15-
)
12+
_call_function(self.id, argv, Int32(argc), &result)
1613
return result
1714
}
1815
}
@@ -29,9 +26,7 @@ public class JSFunctionRef: JSObjectRef {
2926
let argv = bufferPointer.baseAddress
3027
let argc = bufferPointer.count
3128
var result = RawJSValue()
32-
_call_function_with_this(this.id,
33-
self.id, argv, Int32(argc),
34-
&result.kind, &result.payload1, &result.payload2, &result.payload3)
29+
_call_function_with_this(this.id, self.id, argv, Int32(argc), &result)
3530
return result
3631
}
3732
}

Sources/JavaScriptKit/JSValue.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,24 @@ extension JSValue: ExpressibleByIntegerLiteral {
7171

7272
public func getJSValue(this: JSObjectRef, name: String) -> JSValue {
7373
var rawValue = RawJSValue()
74-
_get_prop(this.id, name, Int32(name.count),
75-
&rawValue.kind,
76-
&rawValue.payload1, &rawValue.payload2, &rawValue.payload3)
74+
_get_prop(this.id, name, Int32(name.count), &rawValue)
7775
return rawValue.jsValue()
7876
}
7977

8078
public func setJSValue(this: JSObjectRef, name: String, value: JSValue) {
8179
value.withRawJSValue { rawValue in
82-
_set_prop(this.id, name, Int32(name.count), rawValue.kind, rawValue.payload1, rawValue.payload2, rawValue.payload3)
80+
_set_prop(this.id, name, Int32(name.count), &rawValue)
8381
}
8482
}
8583

8684
public func getJSValue(this: JSObjectRef, index: Int32) -> JSValue {
8785
var rawValue = RawJSValue()
88-
_get_subscript(this.id, index,
89-
&rawValue.kind,
90-
&rawValue.payload1, &rawValue.payload2, &rawValue.payload3)
86+
_get_subscript(this.id, index, &rawValue)
9187
return rawValue.jsValue()
9288
}
9389

9490
public func setJSValue(this: JSObjectRef, index: Int32, value: JSValue) {
9591
value.withRawJSValue { rawValue in
96-
_set_subscript(this.id, index,
97-
rawValue.kind,
98-
rawValue.payload1, rawValue.payload2, rawValue.payload3)
92+
_set_subscript(this.id, index, &rawValue)
9993
}
10094
}

Sources/JavaScriptKit/JSValueConvertible.swift

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ extension RawJSValue: JSValueConvertible {
101101
case .boolean:
102102
return .boolean(payload1 != 0)
103103
case .number:
104-
return .number(payload3)
104+
return .number(Double(bitPattern: UInt64(payload1) | (UInt64(payload2) << 32)))
105105
case .string:
106106
// +1 for null terminator
107107
let buffer = malloc(Int(payload2 + 1))!.assumingMemoryBound(to: UInt8.self)
@@ -125,27 +125,25 @@ extension RawJSValue: JSValueConvertible {
125125
}
126126

127127
extension JSValue {
128-
func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
128+
func withRawJSValue<T>(_ body: (inout RawJSValue) -> T) -> T {
129129
let kind: JavaScriptValueKind
130130
let payload1: JavaScriptPayload1
131131
let payload2: JavaScriptPayload2
132-
var payload3: JavaScriptPayload3 = 0
133132
switch self {
134133
case let .boolean(boolValue):
135134
kind = .boolean
136135
payload1 = boolValue ? 1 : 0
137136
payload2 = 0
138137
case let .number(numberValue):
139138
kind = .number
140-
payload1 = 0
141-
payload2 = 0
142-
payload3 = numberValue
139+
payload1 = UInt32(numberValue.bitPattern & 0x00000000ffffffff)
140+
payload2 = UInt32((numberValue.bitPattern & 0xffffffff00000000) >> 32)
143141
case var .string(stringValue):
144142
kind = .string
145143
return stringValue.withUTF8 { bufferPtr in
146144
let ptrValue = UInt32(UInt(bitPattern: bufferPtr.baseAddress!))
147-
let rawValue = RawJSValue(kind: kind, payload1: JavaScriptPayload1(ptrValue), payload2: JavaScriptPayload2(bufferPtr.count), payload3: 0)
148-
return body(rawValue)
145+
var rawValue = RawJSValue(kind: kind, payload1: JavaScriptPayload1(ptrValue), payload2: JavaScriptPayload2(bufferPtr.count))
146+
return body(&rawValue)
149147
}
150148
case let .object(ref):
151149
kind = .object
@@ -164,8 +162,8 @@ extension JSValue {
164162
payload1 = JavaScriptPayload1(functionRef.id)
165163
payload2 = 0
166164
}
167-
let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2, payload3: payload3)
168-
return body(rawValue)
165+
var rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
166+
return body(&rawValue)
169167
}
170168
}
171169

0 commit comments

Comments
 (0)