quickjs-emscripten-sync 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -34
- package/dist/index.d.ts +17 -15
- package/dist/quickjs-emscripten-sync.es.js +146 -135
- package/dist/quickjs-emscripten-sync.umd.js +6 -6
- package/package.json +3 -3
- package/src/index.test.ts +146 -60
- package/src/index.ts +40 -26
- package/src/marshal/function.test.ts +61 -53
- package/src/marshal/function.ts +7 -6
- package/src/marshal/index.test.ts +73 -65
- package/src/marshal/index.ts +12 -11
- package/src/marshal/json.test.ts +30 -30
- package/src/marshal/json.ts +4 -3
- package/src/marshal/object.test.ts +55 -50
- package/src/marshal/object.ts +6 -5
- package/src/marshal/primitive.test.ts +21 -17
- package/src/marshal/primitive.ts +10 -9
- package/src/marshal/promise.test.ts +14 -14
- package/src/marshal/promise.ts +3 -3
- package/src/marshal/properties.test.ts +17 -15
- package/src/marshal/properties.ts +10 -9
- package/src/marshal/symbol.test.ts +6 -6
- package/src/marshal/symbol.ts +5 -4
- package/src/unmarshal/function.test.ts +45 -43
- package/src/unmarshal/function.ts +9 -8
- package/src/unmarshal/index.test.ts +41 -40
- package/src/unmarshal/index.ts +9 -8
- package/src/unmarshal/object.test.ts +33 -31
- package/src/unmarshal/object.ts +12 -11
- package/src/unmarshal/primitive.test.ts +18 -15
- package/src/unmarshal/primitive.ts +9 -9
- package/src/unmarshal/promise.test.ts +11 -11
- package/src/unmarshal/promise.ts +9 -11
- package/src/unmarshal/properties.test.ts +6 -6
- package/src/unmarshal/properties.ts +47 -44
- package/src/unmarshal/symbol.test.ts +6 -6
- package/src/unmarshal/symbol.ts +4 -4
- package/src/util.test.ts +1 -0
- package/src/vmmap.test.ts +72 -88
- package/src/vmmap.ts +16 -16
- package/src/vmutil.test.ts +71 -73
- package/src/vmutil.ts +22 -18
- package/src/wrapper.test.ts +111 -88
- package/src/wrapper.ts +31 -27
package/src/wrapper.test.ts
CHANGED
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
import { call, eq, json } from "./vmutil";
|
|
14
14
|
|
|
15
15
|
test("wrap, unwrap, isWrapped", async () => {
|
|
16
|
-
const
|
|
16
|
+
const ctx = (await getQuickJS()).newContext();
|
|
17
17
|
const target = { a: 1 };
|
|
18
|
-
const handle =
|
|
18
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
19
19
|
const proxyKeySymbol = Symbol();
|
|
20
|
-
const proxyKeySymbolHandle =
|
|
20
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
21
21
|
const marshal = vi.fn();
|
|
22
22
|
const syncMode = vi.fn();
|
|
23
23
|
|
|
@@ -25,7 +25,7 @@ test("wrap, unwrap, isWrapped", async () => {
|
|
|
25
25
|
expect(unwrap(target, proxyKeySymbol)).toBe(target);
|
|
26
26
|
|
|
27
27
|
const wrapped = wrap(
|
|
28
|
-
|
|
28
|
+
ctx,
|
|
29
29
|
target,
|
|
30
30
|
proxyKeySymbol,
|
|
31
31
|
proxyKeySymbolHandle,
|
|
@@ -39,25 +39,37 @@ test("wrap, unwrap, isWrapped", async () => {
|
|
|
39
39
|
expect(unwrap(wrapped, proxyKeySymbol)).toBe(target);
|
|
40
40
|
|
|
41
41
|
expect(
|
|
42
|
-
wrap(
|
|
42
|
+
wrap(ctx, wrapped, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode)
|
|
43
43
|
).toBe(wrapped);
|
|
44
44
|
|
|
45
|
+
// promise cannot be wrapped
|
|
46
|
+
expect(
|
|
47
|
+
wrap(
|
|
48
|
+
ctx,
|
|
49
|
+
Promise.resolve(1),
|
|
50
|
+
proxyKeySymbol,
|
|
51
|
+
proxyKeySymbolHandle,
|
|
52
|
+
marshal,
|
|
53
|
+
syncMode
|
|
54
|
+
)
|
|
55
|
+
).toBeUndefined();
|
|
56
|
+
|
|
45
57
|
proxyKeySymbolHandle.dispose();
|
|
46
58
|
handle.dispose();
|
|
47
|
-
|
|
59
|
+
ctx.dispose();
|
|
48
60
|
});
|
|
49
61
|
|
|
50
62
|
test("wrap without sync", async () => {
|
|
51
|
-
const
|
|
63
|
+
const ctx = (await getQuickJS()).newContext();
|
|
52
64
|
const target = { a: 1 };
|
|
53
|
-
const handle =
|
|
65
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
54
66
|
const proxyKeySymbol = Symbol();
|
|
55
|
-
const proxyKeySymbolHandle =
|
|
67
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
56
68
|
const marshal = vi.fn();
|
|
57
69
|
const syncMode = vi.fn();
|
|
58
70
|
|
|
59
71
|
const wrapped = wrap(
|
|
60
|
-
|
|
72
|
+
ctx,
|
|
61
73
|
target,
|
|
62
74
|
proxyKeySymbol,
|
|
63
75
|
proxyKeySymbolHandle,
|
|
@@ -72,33 +84,33 @@ test("wrap without sync", async () => {
|
|
|
72
84
|
wrapped.a = 2;
|
|
73
85
|
|
|
74
86
|
expect(target.a).toBe(2);
|
|
75
|
-
expect(
|
|
87
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(1); // not synced
|
|
76
88
|
expect(marshal).toBeCalledTimes(0);
|
|
77
89
|
expect(syncMode).toBeCalledTimes(1);
|
|
78
90
|
expect(syncMode).toBeCalledWith(wrapped);
|
|
79
91
|
|
|
80
92
|
proxyKeySymbolHandle.dispose();
|
|
81
93
|
handle.dispose();
|
|
82
|
-
|
|
94
|
+
ctx.dispose();
|
|
83
95
|
|
|
84
96
|
wrapped.a = 3; // no error even if vm is disposed
|
|
85
97
|
expect(wrapped.a).toBe(3);
|
|
86
98
|
});
|
|
87
99
|
|
|
88
100
|
test("wrap with both sync", async () => {
|
|
89
|
-
const
|
|
101
|
+
const ctx = (await getQuickJS()).newContext();
|
|
90
102
|
const target = { a: 1 } as { a?: number };
|
|
91
|
-
const handle =
|
|
103
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
92
104
|
const proxyKeySymbol = Symbol();
|
|
93
|
-
const proxyKeySymbolHandle =
|
|
105
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
94
106
|
const marshal = vi.fn((t: any): [QuickJSHandle, boolean] => [
|
|
95
|
-
t === wrapped ? handle : json(
|
|
107
|
+
t === wrapped ? handle : json(ctx, t),
|
|
96
108
|
false,
|
|
97
109
|
]);
|
|
98
110
|
const syncMode = vi.fn((): SyncMode => "both");
|
|
99
111
|
|
|
100
112
|
const wrapped = wrap(
|
|
101
|
-
|
|
113
|
+
ctx,
|
|
102
114
|
target,
|
|
103
115
|
proxyKeySymbol,
|
|
104
116
|
proxyKeySymbolHandle,
|
|
@@ -113,7 +125,7 @@ test("wrap with both sync", async () => {
|
|
|
113
125
|
wrapped.a = 2;
|
|
114
126
|
|
|
115
127
|
expect(target.a).toBe(2);
|
|
116
|
-
expect(
|
|
128
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(2); // synced
|
|
117
129
|
expect(marshal).toBeCalledTimes(3);
|
|
118
130
|
expect(marshal).toBeCalledWith(2);
|
|
119
131
|
expect(marshal).toBeCalledWith("a");
|
|
@@ -122,30 +134,30 @@ test("wrap with both sync", async () => {
|
|
|
122
134
|
|
|
123
135
|
delete wrapped.a;
|
|
124
136
|
expect(target.a).toBe(undefined);
|
|
125
|
-
expect(
|
|
137
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(undefined); // synced
|
|
126
138
|
|
|
127
139
|
proxyKeySymbolHandle.dispose();
|
|
128
140
|
handle.dispose();
|
|
129
|
-
|
|
141
|
+
ctx.dispose();
|
|
130
142
|
|
|
131
143
|
wrapped.a = 3; // no error even if vm is disposed
|
|
132
144
|
expect(wrapped.a).toBe(3);
|
|
133
145
|
});
|
|
134
146
|
|
|
135
147
|
test("wrap with vm sync", async () => {
|
|
136
|
-
const
|
|
148
|
+
const ctx = (await getQuickJS()).newContext();
|
|
137
149
|
const target = { a: 1 } as { a?: number };
|
|
138
|
-
const handle =
|
|
150
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
139
151
|
const proxyKeySymbol = Symbol();
|
|
140
|
-
const proxyKeySymbolHandle =
|
|
152
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
141
153
|
const marshal = vi.fn((t: any): [QuickJSHandle, boolean] => [
|
|
142
|
-
t === wrapped ? handle : json(
|
|
154
|
+
t === wrapped ? handle : json(ctx, t),
|
|
143
155
|
false,
|
|
144
156
|
]);
|
|
145
157
|
const syncMode = vi.fn((): SyncMode => "vm");
|
|
146
158
|
|
|
147
159
|
const wrapped = wrap(
|
|
148
|
-
|
|
160
|
+
ctx,
|
|
149
161
|
target,
|
|
150
162
|
proxyKeySymbol,
|
|
151
163
|
proxyKeySymbolHandle,
|
|
@@ -160,7 +172,7 @@ test("wrap with vm sync", async () => {
|
|
|
160
172
|
wrapped.a = 2;
|
|
161
173
|
|
|
162
174
|
expect(target.a).toBe(1); // not set
|
|
163
|
-
expect(
|
|
175
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(2); // synced
|
|
164
176
|
expect(marshal).toBeCalledTimes(3);
|
|
165
177
|
expect(marshal).toBeCalledWith(2);
|
|
166
178
|
expect(marshal).toBeCalledWith("a");
|
|
@@ -169,33 +181,33 @@ test("wrap with vm sync", async () => {
|
|
|
169
181
|
|
|
170
182
|
delete wrapped.a;
|
|
171
183
|
expect(target.a).toBe(1); // not deleted
|
|
172
|
-
expect(
|
|
184
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(undefined); // synced
|
|
173
185
|
|
|
174
186
|
proxyKeySymbolHandle.dispose();
|
|
175
187
|
handle.dispose();
|
|
176
|
-
|
|
188
|
+
ctx.dispose();
|
|
177
189
|
|
|
178
190
|
wrapped.a = 3; // no error even after vm is disposed
|
|
179
191
|
expect(wrapped.a).toBe(1); // vm mode cannot modify obj in host
|
|
180
192
|
});
|
|
181
193
|
|
|
182
194
|
test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
|
|
183
|
-
const
|
|
195
|
+
const ctx = (await getQuickJS()).newContext();
|
|
184
196
|
const target = { a: 1 };
|
|
185
|
-
const handle =
|
|
197
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
186
198
|
const proxyKeySymbol = Symbol();
|
|
187
|
-
const proxyKeySymbolHandle =
|
|
199
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
188
200
|
const unmarshal = vi.fn();
|
|
189
201
|
const syncMode = vi.fn();
|
|
190
202
|
|
|
191
|
-
expect(isHandleWrapped(
|
|
192
|
-
expect(unwrapHandle(
|
|
203
|
+
expect(isHandleWrapped(ctx, handle, proxyKeySymbolHandle)).toBe(false);
|
|
204
|
+
expect(unwrapHandle(ctx, handle, proxyKeySymbolHandle)).toEqual([
|
|
193
205
|
handle,
|
|
194
206
|
false,
|
|
195
207
|
]);
|
|
196
208
|
|
|
197
209
|
const [wrapped, w] = wrapHandle(
|
|
198
|
-
|
|
210
|
+
ctx,
|
|
199
211
|
handle,
|
|
200
212
|
proxyKeySymbol,
|
|
201
213
|
proxyKeySymbolHandle,
|
|
@@ -204,18 +216,22 @@ test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
|
|
|
204
216
|
);
|
|
205
217
|
if (!wrapped || !w) throw new Error("wrapped is undefined");
|
|
206
218
|
|
|
207
|
-
expect(
|
|
208
|
-
expect(
|
|
209
|
-
expect(isHandleWrapped(
|
|
219
|
+
expect(ctx.dump(wrapped)).toEqual(target); // vm.dump does not support proxies
|
|
220
|
+
expect(ctx.dump(ctx.getProp(wrapped, "a"))).toBe(1);
|
|
221
|
+
expect(isHandleWrapped(ctx, wrapped, proxyKeySymbolHandle)).toBe(true);
|
|
210
222
|
|
|
211
|
-
const [handle2, unwrapped2] = unwrapHandle(
|
|
223
|
+
const [handle2, unwrapped2] = unwrapHandle(
|
|
224
|
+
ctx,
|
|
225
|
+
wrapped,
|
|
226
|
+
proxyKeySymbolHandle
|
|
227
|
+
);
|
|
212
228
|
expect(unwrapped2).toBe(true);
|
|
213
229
|
handle2.consume((h) => {
|
|
214
|
-
expect(eq(
|
|
230
|
+
expect(eq(ctx, handle, h)).toBe(true);
|
|
215
231
|
});
|
|
216
232
|
|
|
217
233
|
const [wrapped2] = wrapHandle(
|
|
218
|
-
|
|
234
|
+
ctx,
|
|
219
235
|
wrapped,
|
|
220
236
|
proxyKeySymbol,
|
|
221
237
|
proxyKeySymbolHandle,
|
|
@@ -224,25 +240,32 @@ test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
|
|
|
224
240
|
);
|
|
225
241
|
expect(wrapped2 === wrapped).toBe(true);
|
|
226
242
|
|
|
243
|
+
// promise cannot be wrapped
|
|
244
|
+
const deferred = ctx.newPromise();
|
|
245
|
+
expect(isHandleWrapped(ctx, deferred.handle, proxyKeySymbolHandle)).toBe(
|
|
246
|
+
true
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
deferred.dispose();
|
|
227
250
|
wrapped.dispose();
|
|
228
251
|
handle.dispose();
|
|
229
252
|
proxyKeySymbolHandle.dispose();
|
|
230
|
-
|
|
253
|
+
ctx.dispose();
|
|
231
254
|
});
|
|
232
255
|
|
|
233
256
|
test("wrapHandle without sync", async () => {
|
|
234
|
-
const
|
|
257
|
+
const ctx = (await getQuickJS()).newContext();
|
|
235
258
|
const target = { a: 1 };
|
|
236
|
-
const handle =
|
|
259
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
237
260
|
const proxyKeySymbol = Symbol();
|
|
238
|
-
const proxyKeySymbolHandle =
|
|
261
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
239
262
|
const unmarshal = vi.fn((h: QuickJSHandle) =>
|
|
240
|
-
wrapped && eq(
|
|
263
|
+
wrapped && eq(ctx, h, wrapped) ? target : ctx.dump(h)
|
|
241
264
|
);
|
|
242
265
|
const syncMode = vi.fn();
|
|
243
266
|
|
|
244
267
|
const [wrapped, w] = wrapHandle(
|
|
245
|
-
|
|
268
|
+
ctx,
|
|
246
269
|
handle,
|
|
247
270
|
proxyKeySymbol,
|
|
248
271
|
proxyKeySymbolHandle,
|
|
@@ -254,9 +277,9 @@ test("wrapHandle without sync", async () => {
|
|
|
254
277
|
expect(unmarshal).toBeCalledTimes(0);
|
|
255
278
|
expect(syncMode).toBeCalledTimes(0);
|
|
256
279
|
|
|
257
|
-
call(
|
|
280
|
+
call(ctx, `a => a.a = 2`, undefined, wrapped);
|
|
258
281
|
|
|
259
|
-
expect(
|
|
282
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(2);
|
|
260
283
|
expect(target.a).toBe(1); // not synced
|
|
261
284
|
expect(unmarshal).toBeCalledTimes(1);
|
|
262
285
|
expect(unmarshal).toReturnWith(target);
|
|
@@ -266,22 +289,22 @@ test("wrapHandle without sync", async () => {
|
|
|
266
289
|
wrapped.dispose();
|
|
267
290
|
handle.dispose();
|
|
268
291
|
proxyKeySymbolHandle.dispose();
|
|
269
|
-
|
|
292
|
+
ctx.dispose();
|
|
270
293
|
});
|
|
271
294
|
|
|
272
295
|
test("wrapHandle with both sync", async () => {
|
|
273
|
-
const
|
|
296
|
+
const ctx = (await getQuickJS()).newContext();
|
|
274
297
|
const target = { a: 1 };
|
|
275
|
-
const handle =
|
|
298
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
276
299
|
const proxyKeySymbol = Symbol();
|
|
277
|
-
const proxyKeySymbolHandle =
|
|
300
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
278
301
|
const unmarshal = vi.fn((h: QuickJSHandle) => {
|
|
279
|
-
return wrapped && eq(
|
|
302
|
+
return wrapped && eq(ctx, h, wrapped) ? target : ctx.dump(h);
|
|
280
303
|
});
|
|
281
304
|
const syncMode = vi.fn((): SyncMode => "both");
|
|
282
305
|
|
|
283
306
|
const [wrapped, w] = wrapHandle(
|
|
284
|
-
|
|
307
|
+
ctx,
|
|
285
308
|
handle,
|
|
286
309
|
proxyKeySymbol,
|
|
287
310
|
proxyKeySymbolHandle,
|
|
@@ -293,9 +316,9 @@ test("wrapHandle with both sync", async () => {
|
|
|
293
316
|
expect(unmarshal).toBeCalledTimes(0);
|
|
294
317
|
expect(syncMode).toBeCalledTimes(0);
|
|
295
318
|
|
|
296
|
-
call(
|
|
319
|
+
call(ctx, `a => a.a = 2`, undefined, wrapped);
|
|
297
320
|
|
|
298
|
-
expect(
|
|
321
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(2);
|
|
299
322
|
expect(target.a).toBe(2); // synced
|
|
300
323
|
expect(unmarshal).toBeCalledTimes(4);
|
|
301
324
|
expect(unmarshal).toReturnWith(target); // twice
|
|
@@ -304,13 +327,13 @@ test("wrapHandle with both sync", async () => {
|
|
|
304
327
|
expect(syncMode).toBeCalledTimes(1);
|
|
305
328
|
expect(syncMode).toBeCalledWith(target);
|
|
306
329
|
|
|
307
|
-
call(
|
|
308
|
-
expect(
|
|
330
|
+
call(ctx, `a => { delete a.a; }`, undefined, wrapped);
|
|
331
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(undefined);
|
|
309
332
|
expect(target.a).toBe(undefined); // synced
|
|
310
333
|
|
|
311
334
|
// changing __proto__ will be blocked
|
|
312
|
-
call(
|
|
313
|
-
expect(
|
|
335
|
+
call(ctx, `a => { a.__proto__ = null; }`, undefined, wrapped);
|
|
336
|
+
expect(ctx.dump(call(ctx, `Object.getPrototypeOf`, undefined, handle))).toBe(
|
|
314
337
|
null
|
|
315
338
|
);
|
|
316
339
|
expect(Object.getPrototypeOf(target)).toBe(Object.prototype); // not changed
|
|
@@ -318,22 +341,22 @@ test("wrapHandle with both sync", async () => {
|
|
|
318
341
|
wrapped.dispose();
|
|
319
342
|
handle.dispose();
|
|
320
343
|
proxyKeySymbolHandle.dispose();
|
|
321
|
-
|
|
344
|
+
ctx.dispose();
|
|
322
345
|
});
|
|
323
346
|
|
|
324
347
|
test("wrapHandle with host sync", async () => {
|
|
325
|
-
const
|
|
348
|
+
const ctx = (await getQuickJS()).newContext();
|
|
326
349
|
const target = { a: 1 };
|
|
327
|
-
const handle =
|
|
350
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
328
351
|
const proxyKeySymbol = Symbol();
|
|
329
|
-
const proxyKeySymbolHandle =
|
|
352
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
330
353
|
const unmarshal = vi.fn((handle: QuickJSHandle) =>
|
|
331
|
-
wrapped && eq(
|
|
354
|
+
wrapped && eq(ctx, handle, wrapped) ? target : ctx.dump(handle)
|
|
332
355
|
);
|
|
333
356
|
const syncMode = vi.fn((): SyncMode => "host");
|
|
334
357
|
|
|
335
358
|
const [wrapped, w] = wrapHandle(
|
|
336
|
-
|
|
359
|
+
ctx,
|
|
337
360
|
handle,
|
|
338
361
|
proxyKeySymbol,
|
|
339
362
|
proxyKeySymbolHandle,
|
|
@@ -345,9 +368,9 @@ test("wrapHandle with host sync", async () => {
|
|
|
345
368
|
expect(unmarshal).toBeCalledTimes(0);
|
|
346
369
|
expect(syncMode).toBeCalledTimes(0);
|
|
347
370
|
|
|
348
|
-
call(
|
|
371
|
+
call(ctx, `a => a.a = 2`, undefined, wrapped);
|
|
349
372
|
|
|
350
|
-
expect(
|
|
373
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(1); // not set
|
|
351
374
|
expect(target.a).toBe(2); // synced
|
|
352
375
|
expect(unmarshal).toBeCalledTimes(4);
|
|
353
376
|
expect(unmarshal).toReturnWith(target); // twice
|
|
@@ -356,33 +379,33 @@ test("wrapHandle with host sync", async () => {
|
|
|
356
379
|
expect(syncMode).toBeCalledTimes(1);
|
|
357
380
|
expect(syncMode).toBeCalledWith(target);
|
|
358
381
|
|
|
359
|
-
call(
|
|
360
|
-
expect(
|
|
382
|
+
call(ctx, `a => delete a.a`, undefined, handle);
|
|
383
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(undefined);
|
|
361
384
|
expect(target.a).toBe(2); // not synced
|
|
362
385
|
|
|
363
386
|
wrapped.dispose();
|
|
364
387
|
handle.dispose();
|
|
365
388
|
proxyKeySymbolHandle.dispose();
|
|
366
|
-
|
|
389
|
+
ctx.dispose();
|
|
367
390
|
});
|
|
368
391
|
|
|
369
392
|
test("wrap and wrapHandle", async () => {
|
|
370
|
-
const
|
|
393
|
+
const ctx = (await getQuickJS()).newContext();
|
|
371
394
|
const target = { a: 1 };
|
|
372
|
-
const handle =
|
|
395
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
373
396
|
const proxyKeySymbol = Symbol();
|
|
374
|
-
const proxyKeySymbolHandle =
|
|
397
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
375
398
|
const marshal = vi.fn((t: any): [QuickJSHandle, boolean] => [
|
|
376
|
-
wrappedHandle && t === wrapped ? wrappedHandle : json(
|
|
399
|
+
wrappedHandle && t === wrapped ? wrappedHandle : json(ctx, t),
|
|
377
400
|
false,
|
|
378
401
|
]);
|
|
379
402
|
const unmarshal = vi.fn((handle: QuickJSHandle) =>
|
|
380
|
-
wrappedHandle && eq(
|
|
403
|
+
wrappedHandle && eq(ctx, handle, wrappedHandle) ? wrapped : ctx.dump(handle)
|
|
381
404
|
);
|
|
382
405
|
const syncMode = vi.fn((): SyncMode => "both");
|
|
383
406
|
|
|
384
407
|
const wrapped = wrap(
|
|
385
|
-
|
|
408
|
+
ctx,
|
|
386
409
|
target,
|
|
387
410
|
proxyKeySymbol,
|
|
388
411
|
proxyKeySymbolHandle,
|
|
@@ -391,7 +414,7 @@ test("wrap and wrapHandle", async () => {
|
|
|
391
414
|
);
|
|
392
415
|
if (!wrapped) throw new Error("wrapped is undefined");
|
|
393
416
|
const [wrappedHandle, w] = wrapHandle(
|
|
394
|
-
|
|
417
|
+
ctx,
|
|
395
418
|
handle,
|
|
396
419
|
proxyKeySymbol,
|
|
397
420
|
proxyKeySymbolHandle,
|
|
@@ -400,9 +423,9 @@ test("wrap and wrapHandle", async () => {
|
|
|
400
423
|
);
|
|
401
424
|
if (!wrappedHandle || !w) throw new Error("wrappedHandle is undefined");
|
|
402
425
|
|
|
403
|
-
call(
|
|
426
|
+
call(ctx, `a => a.a = 2`, undefined, wrappedHandle);
|
|
404
427
|
|
|
405
|
-
expect(
|
|
428
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(2);
|
|
406
429
|
expect(target.a).toBe(2);
|
|
407
430
|
expect(marshal).toBeCalledTimes(0);
|
|
408
431
|
expect(unmarshal).toBeCalledTimes(4);
|
|
@@ -415,7 +438,7 @@ test("wrap and wrapHandle", async () => {
|
|
|
415
438
|
|
|
416
439
|
wrapped.a = 3;
|
|
417
440
|
|
|
418
|
-
expect(
|
|
441
|
+
expect(ctx.dump(ctx.getProp(handle, "a"))).toBe(3);
|
|
419
442
|
expect(target.a).toBe(3);
|
|
420
443
|
expect(marshal).toBeCalledTimes(3);
|
|
421
444
|
expect(marshal).toBeCalledWith(wrapped);
|
|
@@ -426,23 +449,23 @@ test("wrap and wrapHandle", async () => {
|
|
|
426
449
|
wrappedHandle.dispose();
|
|
427
450
|
handle.dispose();
|
|
428
451
|
proxyKeySymbolHandle.dispose();
|
|
429
|
-
|
|
452
|
+
ctx.dispose();
|
|
430
453
|
});
|
|
431
454
|
|
|
432
455
|
test("non object", async () => {
|
|
433
|
-
const
|
|
456
|
+
const ctx = (await getQuickJS()).newContext();
|
|
434
457
|
const target = 1;
|
|
435
|
-
const handle =
|
|
458
|
+
const handle = ctx.newNumber(1);
|
|
436
459
|
const proxyKeySymbol = Symbol();
|
|
437
|
-
const proxyKeySymbolHandle =
|
|
460
|
+
const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
438
461
|
|
|
439
462
|
expect(
|
|
440
|
-
wrap(
|
|
463
|
+
wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, vi.fn(), vi.fn())
|
|
441
464
|
).toBe(undefined);
|
|
442
465
|
|
|
443
466
|
expect(
|
|
444
467
|
wrapHandle(
|
|
445
|
-
|
|
468
|
+
ctx,
|
|
446
469
|
handle,
|
|
447
470
|
proxyKeySymbol,
|
|
448
471
|
proxyKeySymbolHandle,
|
|
@@ -452,5 +475,5 @@ test("non object", async () => {
|
|
|
452
475
|
).toEqual([undefined, false]);
|
|
453
476
|
|
|
454
477
|
proxyKeySymbolHandle.dispose();
|
|
455
|
-
|
|
478
|
+
ctx.dispose();
|
|
456
479
|
});
|
package/src/wrapper.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSHandle, QuickJSContext } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { isObject } from "./util";
|
|
3
4
|
import { call, isHandleObject, consumeAll, mayConsumeAll } from "./vmutil";
|
|
4
5
|
|
|
@@ -7,14 +8,15 @@ export type SyncMode = "both" | "vm" | "host";
|
|
|
7
8
|
export type Wrapped<T> = T & { __qes_wrapped: never };
|
|
8
9
|
|
|
9
10
|
export function wrap<T = any>(
|
|
10
|
-
|
|
11
|
+
ctx: QuickJSContext,
|
|
11
12
|
target: T,
|
|
12
13
|
proxyKeySymbol: symbol,
|
|
13
14
|
proxyKeySymbolHandle: QuickJSHandle,
|
|
14
15
|
marshal: (target: any) => [QuickJSHandle, boolean],
|
|
15
16
|
syncMode?: (target: T) => SyncMode | undefined
|
|
16
17
|
): Wrapped<T> | undefined {
|
|
17
|
-
|
|
18
|
+
// promise cannot be wrapped
|
|
19
|
+
if (!isObject(target) || target instanceof Promise) return undefined;
|
|
18
20
|
if (isWrapped(target, proxyKeySymbol)) return target;
|
|
19
21
|
|
|
20
22
|
const rec = new Proxy(target as any, {
|
|
@@ -27,7 +29,7 @@ export function wrap<T = any>(
|
|
|
27
29
|
if (
|
|
28
30
|
(sync !== "vm" && !Reflect.set(obj, key, v, receiver)) ||
|
|
29
31
|
sync === "host" ||
|
|
30
|
-
!
|
|
32
|
+
!ctx.alive
|
|
31
33
|
)
|
|
32
34
|
return true;
|
|
33
35
|
|
|
@@ -35,14 +37,14 @@ export function wrap<T = any>(
|
|
|
35
37
|
[marshal(receiver), marshal(key), marshal(v)],
|
|
36
38
|
(receiverHandle, keyHandle, valueHandle) => {
|
|
37
39
|
const [handle2, unwrapped] = unwrapHandle(
|
|
38
|
-
|
|
40
|
+
ctx,
|
|
39
41
|
receiverHandle,
|
|
40
42
|
proxyKeySymbolHandle
|
|
41
43
|
);
|
|
42
44
|
if (unwrapped) {
|
|
43
|
-
handle2.consume((h) =>
|
|
45
|
+
handle2.consume((h) => ctx.setProp(h, keyHandle, valueHandle));
|
|
44
46
|
} else {
|
|
45
|
-
|
|
47
|
+
ctx.setProp(handle2, keyHandle, valueHandle);
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
);
|
|
@@ -55,20 +57,20 @@ export function wrap<T = any>(
|
|
|
55
57
|
[marshal(rec), marshal(key)],
|
|
56
58
|
(recHandle, keyHandle) => {
|
|
57
59
|
const [handle2, unwrapped] = unwrapHandle(
|
|
58
|
-
|
|
60
|
+
ctx,
|
|
59
61
|
recHandle,
|
|
60
62
|
proxyKeySymbolHandle
|
|
61
63
|
);
|
|
62
64
|
|
|
63
65
|
if (sync === "vm" || Reflect.deleteProperty(obj, key)) {
|
|
64
|
-
if (sync === "host" || !
|
|
66
|
+
if (sync === "host" || !ctx.alive) return true;
|
|
65
67
|
|
|
66
68
|
if (unwrapped) {
|
|
67
69
|
handle2.consume((h) =>
|
|
68
|
-
call(
|
|
70
|
+
call(ctx, `(a, b) => delete a[b]`, undefined, h, keyHandle)
|
|
69
71
|
);
|
|
70
72
|
} else {
|
|
71
|
-
call(
|
|
73
|
+
call(ctx, `(a, b) => delete a[b]`, undefined, handle2, keyHandle);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
return true;
|
|
@@ -80,24 +82,25 @@ export function wrap<T = any>(
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
export function wrapHandle(
|
|
83
|
-
|
|
85
|
+
ctx: QuickJSContext,
|
|
84
86
|
handle: QuickJSHandle,
|
|
85
87
|
proxyKeySymbol: symbol,
|
|
86
88
|
proxyKeySymbolHandle: QuickJSHandle,
|
|
87
89
|
unmarshal: (handle: QuickJSHandle) => any,
|
|
88
90
|
syncMode?: (target: QuickJSHandle) => SyncMode | undefined
|
|
89
91
|
): [Wrapped<QuickJSHandle> | undefined, boolean] {
|
|
90
|
-
if (!isHandleObject(
|
|
91
|
-
if (isHandleWrapped(
|
|
92
|
+
if (!isHandleObject(ctx, handle)) return [undefined, false];
|
|
93
|
+
if (isHandleWrapped(ctx, handle, proxyKeySymbolHandle))
|
|
94
|
+
return [handle, false];
|
|
92
95
|
|
|
93
96
|
return consumeAll(
|
|
94
97
|
[
|
|
95
|
-
|
|
98
|
+
ctx.newFunction("getSyncMode", (h) => {
|
|
96
99
|
const res = syncMode?.(unmarshal(h));
|
|
97
|
-
if (typeof res === "string") return
|
|
98
|
-
return
|
|
100
|
+
if (typeof res === "string") return ctx.newString(res);
|
|
101
|
+
return ctx.undefined;
|
|
99
102
|
}),
|
|
100
|
-
|
|
103
|
+
ctx.newFunction("setter", (h, keyHandle, valueHandle) => {
|
|
101
104
|
const target = unmarshal(h);
|
|
102
105
|
if (!target) return;
|
|
103
106
|
const key = unmarshal(keyHandle);
|
|
@@ -105,7 +108,7 @@ export function wrapHandle(
|
|
|
105
108
|
const value = unmarshal(valueHandle);
|
|
106
109
|
unwrap(target, proxyKeySymbol)[key] = value;
|
|
107
110
|
}),
|
|
108
|
-
|
|
111
|
+
ctx.newFunction("deleter", (h, keyHandle) => {
|
|
109
112
|
const target = unmarshal(h);
|
|
110
113
|
if (!target) return;
|
|
111
114
|
const key = unmarshal(keyHandle);
|
|
@@ -114,7 +117,7 @@ export function wrapHandle(
|
|
|
114
117
|
],
|
|
115
118
|
(args) => [
|
|
116
119
|
call(
|
|
117
|
-
|
|
120
|
+
ctx,
|
|
118
121
|
`(target, sym, getSyncMode, setter, deleter) => {
|
|
119
122
|
const rec = new Proxy(target, {
|
|
120
123
|
get(obj, key, receiver) {
|
|
@@ -159,12 +162,12 @@ export function unwrap<T>(obj: T, key: string | symbol): T {
|
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
export function unwrapHandle(
|
|
162
|
-
|
|
165
|
+
ctx: QuickJSContext,
|
|
163
166
|
handle: QuickJSHandle,
|
|
164
167
|
key: QuickJSHandle
|
|
165
168
|
): [QuickJSHandle, boolean] {
|
|
166
|
-
if (!isHandleWrapped(
|
|
167
|
-
return [
|
|
169
|
+
if (!isHandleWrapped(ctx, handle, key)) return [handle, false];
|
|
170
|
+
return [ctx.getProp(handle, key), true];
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
export function isWrapped<T>(obj: T, key: string | symbol): obj is Wrapped<T> {
|
|
@@ -172,14 +175,15 @@ export function isWrapped<T>(obj: T, key: string | symbol): obj is Wrapped<T> {
|
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
export function isHandleWrapped(
|
|
175
|
-
|
|
178
|
+
ctx: QuickJSContext,
|
|
176
179
|
handle: QuickJSHandle,
|
|
177
180
|
key: QuickJSHandle
|
|
178
181
|
): handle is Wrapped<QuickJSHandle> {
|
|
179
|
-
return !!
|
|
182
|
+
return !!ctx.dump(
|
|
180
183
|
call(
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
ctx,
|
|
185
|
+
// promise cannot be wrapped
|
|
186
|
+
`(a, s) => (a instanceof Promise) || (typeof a === "object" && a !== null || typeof a === "function") && !!a[s]`,
|
|
183
187
|
undefined,
|
|
184
188
|
handle,
|
|
185
189
|
key
|