quickjs-emscripten-sync 1.4.0 → 1.5.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.
Files changed (50) hide show
  1. package/README.md +28 -7
  2. package/dist/index.d.ts +12 -7
  3. package/dist/quickjs-emscripten-sync.mjs +842 -0
  4. package/dist/quickjs-emscripten-sync.umd.js +15 -15
  5. package/package.json +9 -18
  6. package/src/default.ts +2 -2
  7. package/src/index.test.ts +21 -13
  8. package/src/index.ts +30 -43
  9. package/src/marshal/custom.test.ts +50 -0
  10. package/src/marshal/custom.ts +36 -0
  11. package/src/marshal/function.test.ts +17 -37
  12. package/src/marshal/function.ts +8 -12
  13. package/src/marshal/index.test.ts +35 -39
  14. package/src/marshal/index.ts +6 -9
  15. package/src/marshal/json.test.ts +9 -15
  16. package/src/marshal/json.ts +1 -4
  17. package/src/marshal/object.test.ts +19 -51
  18. package/src/marshal/object.ts +3 -11
  19. package/src/marshal/primitive.test.ts +4 -13
  20. package/src/marshal/primitive.ts +1 -1
  21. package/src/marshal/promise.test.ts +9 -10
  22. package/src/marshal/promise.ts +4 -11
  23. package/src/marshal/properties.test.ts +7 -14
  24. package/src/marshal/properties.ts +6 -9
  25. package/src/unmarshal/custom.test.ts +50 -0
  26. package/src/unmarshal/custom.ts +31 -0
  27. package/src/unmarshal/function.test.ts +20 -44
  28. package/src/unmarshal/function.ts +7 -17
  29. package/src/unmarshal/index.test.ts +30 -23
  30. package/src/unmarshal/index.ts +4 -6
  31. package/src/unmarshal/object.test.ts +9 -17
  32. package/src/unmarshal/object.ts +7 -12
  33. package/src/unmarshal/primitive.test.ts +1 -4
  34. package/src/unmarshal/primitive.ts +3 -10
  35. package/src/unmarshal/promise.test.ts +3 -3
  36. package/src/unmarshal/promise.ts +3 -10
  37. package/src/unmarshal/properties.test.ts +2 -2
  38. package/src/unmarshal/properties.ts +4 -8
  39. package/src/util.test.ts +1 -7
  40. package/src/util.ts +3 -8
  41. package/src/vmmap.ts +13 -31
  42. package/src/vmutil.test.ts +15 -29
  43. package/src/vmutil.ts +12 -39
  44. package/src/wrapper.test.ts +27 -90
  45. package/src/wrapper.ts +43 -50
  46. package/dist/quickjs-emscripten-sync.es.js +0 -962
  47. package/src/marshal/symbol.test.ts +0 -24
  48. package/src/marshal/symbol.ts +0 -21
  49. package/src/unmarshal/symbol.test.ts +0 -25
  50. package/src/unmarshal/symbol.ts +0 -12
@@ -1,6 +1,7 @@
1
1
  import { QuickJSHandle, getQuickJS } from "quickjs-emscripten";
2
2
  import { expect, test, vi } from "vitest";
3
3
 
4
+ import { call, eq, json } from "./vmutil";
4
5
  import {
5
6
  wrap,
6
7
  unwrap,
@@ -10,7 +11,6 @@ import {
10
11
  isHandleWrapped,
11
12
  SyncMode,
12
13
  } from "./wrapper";
13
- import { call, eq, json } from "./vmutil";
14
14
 
15
15
  test("wrap, unwrap, isWrapped", async () => {
16
16
  const ctx = (await getQuickJS()).newContext();
@@ -24,34 +24,18 @@ test("wrap, unwrap, isWrapped", async () => {
24
24
  expect(isWrapped(target, proxyKeySymbol)).toBe(false);
25
25
  expect(unwrap(target, proxyKeySymbol)).toBe(target);
26
26
 
27
- const wrapped = wrap(
28
- ctx,
29
- target,
30
- proxyKeySymbol,
31
- proxyKeySymbolHandle,
32
- marshal,
33
- syncMode
34
- );
27
+ const wrapped = wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode);
35
28
  if (!wrapped) throw new Error("wrapped is undefined");
36
29
 
37
30
  expect(wrapped).toEqual(target);
38
31
  expect(isWrapped(wrapped, proxyKeySymbol)).toBe(true);
39
32
  expect(unwrap(wrapped, proxyKeySymbol)).toBe(target);
40
33
 
41
- expect(
42
- wrap(ctx, wrapped, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode)
43
- ).toBe(wrapped);
34
+ expect(wrap(ctx, wrapped, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode)).toBe(wrapped);
44
35
 
45
36
  // promise cannot be wrapped
46
37
  expect(
47
- wrap(
48
- ctx,
49
- Promise.resolve(1),
50
- proxyKeySymbol,
51
- proxyKeySymbolHandle,
52
- marshal,
53
- syncMode
54
- )
38
+ wrap(ctx, Promise.resolve(1), proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode),
55
39
  ).toBeUndefined();
56
40
 
57
41
  proxyKeySymbolHandle.dispose();
@@ -68,14 +52,7 @@ test("wrap without sync", async () => {
68
52
  const marshal = vi.fn();
69
53
  const syncMode = vi.fn();
70
54
 
71
- const wrapped = wrap(
72
- ctx,
73
- target,
74
- proxyKeySymbol,
75
- proxyKeySymbolHandle,
76
- marshal,
77
- syncMode
78
- );
55
+ const wrapped = wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode);
79
56
  if (!wrapped) throw new Error("wrapped is undefined");
80
57
 
81
58
  expect(marshal).toBeCalledTimes(0);
@@ -109,14 +86,7 @@ test("wrap with both sync", async () => {
109
86
  ]);
110
87
  const syncMode = vi.fn((): SyncMode => "both");
111
88
 
112
- const wrapped = wrap(
113
- ctx,
114
- target,
115
- proxyKeySymbol,
116
- proxyKeySymbolHandle,
117
- marshal,
118
- syncMode
119
- );
89
+ const wrapped = wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode);
120
90
  if (!wrapped) throw new Error("wrapped is undefined");
121
91
 
122
92
  expect(marshal).toBeCalledTimes(0);
@@ -156,14 +126,7 @@ test("wrap with vm sync", async () => {
156
126
  ]);
157
127
  const syncMode = vi.fn((): SyncMode => "vm");
158
128
 
159
- const wrapped = wrap(
160
- ctx,
161
- target,
162
- proxyKeySymbol,
163
- proxyKeySymbolHandle,
164
- marshal,
165
- syncMode
166
- );
129
+ const wrapped = wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode);
167
130
  if (!wrapped) throw new Error("wrapped is undefined");
168
131
 
169
132
  expect(marshal).toBeCalledTimes(0);
@@ -201,10 +164,7 @@ test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
201
164
  const syncMode = vi.fn();
202
165
 
203
166
  expect(isHandleWrapped(ctx, handle, proxyKeySymbolHandle)).toBe(false);
204
- expect(unwrapHandle(ctx, handle, proxyKeySymbolHandle)).toEqual([
205
- handle,
206
- false,
207
- ]);
167
+ expect(unwrapHandle(ctx, handle, proxyKeySymbolHandle)).toEqual([handle, false]);
208
168
 
209
169
  const [wrapped, w] = wrapHandle(
210
170
  ctx,
@@ -212,7 +172,7 @@ test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
212
172
  proxyKeySymbol,
213
173
  proxyKeySymbolHandle,
214
174
  unmarshal,
215
- syncMode
175
+ syncMode,
216
176
  );
217
177
  if (!wrapped || !w) throw new Error("wrapped is undefined");
218
178
 
@@ -220,13 +180,9 @@ test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
220
180
  expect(ctx.dump(ctx.getProp(wrapped, "a"))).toBe(1);
221
181
  expect(isHandleWrapped(ctx, wrapped, proxyKeySymbolHandle)).toBe(true);
222
182
 
223
- const [handle2, unwrapped2] = unwrapHandle(
224
- ctx,
225
- wrapped,
226
- proxyKeySymbolHandle
227
- );
183
+ const [handle2, unwrapped2] = unwrapHandle(ctx, wrapped, proxyKeySymbolHandle);
228
184
  expect(unwrapped2).toBe(true);
229
- handle2.consume((h) => {
185
+ handle2.consume(h => {
230
186
  expect(eq(ctx, handle, h)).toBe(true);
231
187
  });
232
188
 
@@ -236,15 +192,13 @@ test("wrapHandle, unwrapHandle, isHandleWrapped", async () => {
236
192
  proxyKeySymbol,
237
193
  proxyKeySymbolHandle,
238
194
  unmarshal,
239
- syncMode
195
+ syncMode,
240
196
  );
241
197
  expect(wrapped2 === wrapped).toBe(true);
242
198
 
243
199
  // promise cannot be wrapped
244
200
  const deferred = ctx.newPromise();
245
- expect(isHandleWrapped(ctx, deferred.handle, proxyKeySymbolHandle)).toBe(
246
- true
247
- );
201
+ expect(isHandleWrapped(ctx, deferred.handle, proxyKeySymbolHandle)).toBe(true);
248
202
 
249
203
  deferred.dispose();
250
204
  wrapped.dispose();
@@ -260,7 +214,7 @@ test("wrapHandle without sync", async () => {
260
214
  const proxyKeySymbol = Symbol();
261
215
  const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
262
216
  const unmarshal = vi.fn((h: QuickJSHandle) =>
263
- wrapped && eq(ctx, h, wrapped) ? target : ctx.dump(h)
217
+ wrapped && eq(ctx, h, wrapped) ? target : ctx.dump(h),
264
218
  );
265
219
  const syncMode = vi.fn();
266
220
 
@@ -270,7 +224,7 @@ test("wrapHandle without sync", async () => {
270
224
  proxyKeySymbol,
271
225
  proxyKeySymbolHandle,
272
226
  unmarshal,
273
- syncMode
227
+ syncMode,
274
228
  );
275
229
  if (!wrapped || !w) throw new Error("wrapped is undefined");
276
230
 
@@ -309,7 +263,7 @@ test("wrapHandle with both sync", async () => {
309
263
  proxyKeySymbol,
310
264
  proxyKeySymbolHandle,
311
265
  unmarshal,
312
- syncMode
266
+ syncMode,
313
267
  );
314
268
  if (!wrapped || !w) throw new Error("wrapped is undefined");
315
269
 
@@ -333,9 +287,7 @@ test("wrapHandle with both sync", async () => {
333
287
 
334
288
  // changing __proto__ will be blocked
335
289
  call(ctx, `a => { a.__proto__ = null; }`, undefined, wrapped);
336
- expect(ctx.dump(call(ctx, `Object.getPrototypeOf`, undefined, handle))).toBe(
337
- null
338
- );
290
+ expect(ctx.dump(call(ctx, `Object.getPrototypeOf`, undefined, handle))).toBe(null);
339
291
  expect(Object.getPrototypeOf(target)).toBe(Object.prototype); // not changed
340
292
 
341
293
  wrapped.dispose();
@@ -351,7 +303,7 @@ test("wrapHandle with host sync", async () => {
351
303
  const proxyKeySymbol = Symbol();
352
304
  const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
353
305
  const unmarshal = vi.fn((handle: QuickJSHandle) =>
354
- wrapped && eq(ctx, handle, wrapped) ? target : ctx.dump(handle)
306
+ wrapped && eq(ctx, handle, wrapped) ? target : ctx.dump(handle),
355
307
  );
356
308
  const syncMode = vi.fn((): SyncMode => "host");
357
309
 
@@ -361,7 +313,7 @@ test("wrapHandle with host sync", async () => {
361
313
  proxyKeySymbol,
362
314
  proxyKeySymbolHandle,
363
315
  unmarshal,
364
- syncMode
316
+ syncMode,
365
317
  );
366
318
  if (!wrapped || !w) throw new Error("wrapped is undefined");
367
319
 
@@ -400,18 +352,11 @@ test("wrap and wrapHandle", async () => {
400
352
  false,
401
353
  ]);
402
354
  const unmarshal = vi.fn((handle: QuickJSHandle) =>
403
- wrappedHandle && eq(ctx, handle, wrappedHandle) ? wrapped : ctx.dump(handle)
355
+ wrappedHandle && eq(ctx, handle, wrappedHandle) ? wrapped : ctx.dump(handle),
404
356
  );
405
357
  const syncMode = vi.fn((): SyncMode => "both");
406
358
 
407
- const wrapped = wrap(
408
- ctx,
409
- target,
410
- proxyKeySymbol,
411
- proxyKeySymbolHandle,
412
- marshal,
413
- syncMode
414
- );
359
+ const wrapped = wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, marshal, syncMode);
415
360
  if (!wrapped) throw new Error("wrapped is undefined");
416
361
  const [wrappedHandle, w] = wrapHandle(
417
362
  ctx,
@@ -419,7 +364,7 @@ test("wrap and wrapHandle", async () => {
419
364
  proxyKeySymbol,
420
365
  proxyKeySymbolHandle,
421
366
  unmarshal,
422
- syncMode
367
+ syncMode,
423
368
  );
424
369
  if (!wrappedHandle || !w) throw new Error("wrappedHandle is undefined");
425
370
 
@@ -459,20 +404,12 @@ test("non object", async () => {
459
404
  const proxyKeySymbol = Symbol();
460
405
  const proxyKeySymbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
461
406
 
462
- expect(
463
- wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, vi.fn(), vi.fn())
464
- ).toBe(undefined);
407
+ expect(wrap(ctx, target, proxyKeySymbol, proxyKeySymbolHandle, vi.fn(), vi.fn())).toBe(undefined);
465
408
 
466
- expect(
467
- wrapHandle(
468
- ctx,
469
- handle,
470
- proxyKeySymbol,
471
- proxyKeySymbolHandle,
472
- vi.fn(),
473
- vi.fn()
474
- )
475
- ).toEqual([undefined, false]);
409
+ expect(wrapHandle(ctx, handle, proxyKeySymbol, proxyKeySymbolHandle, vi.fn(), vi.fn())).toEqual([
410
+ undefined,
411
+ false,
412
+ ]);
476
413
 
477
414
  proxyKeySymbolHandle.dispose();
478
415
  ctx.dispose();
package/src/wrapper.ts CHANGED
@@ -13,10 +13,18 @@ export function wrap<T = any>(
13
13
  proxyKeySymbol: symbol,
14
14
  proxyKeySymbolHandle: QuickJSHandle,
15
15
  marshal: (target: any) => [QuickJSHandle, boolean],
16
- syncMode?: (target: T) => SyncMode | undefined
16
+ syncMode?: (target: T) => SyncMode | undefined,
17
+ wrappable?: (target: unknown) => boolean,
17
18
  ): Wrapped<T> | undefined {
18
- // promise cannot be wrapped
19
- if (!isObject(target) || target instanceof Promise) return undefined;
19
+ // promise and date cannot be wrapped
20
+ if (
21
+ !isObject(target) ||
22
+ target instanceof Promise ||
23
+ target instanceof Date ||
24
+ (wrappable && !wrappable(target))
25
+ )
26
+ return undefined;
27
+
20
28
  if (isWrapped(target, proxyKeySymbol)) return target;
21
29
 
22
30
  const rec = new Proxy(target as any, {
@@ -26,56 +34,39 @@ export function wrap<T = any>(
26
34
  set(obj, key, value, receiver) {
27
35
  const v = unwrap(value, proxyKeySymbol);
28
36
  const sync = syncMode?.(receiver) ?? "host";
29
- if (
30
- (sync !== "vm" && !Reflect.set(obj, key, v, receiver)) ||
31
- sync === "host" ||
32
- !ctx.alive
33
- )
37
+ if ((sync !== "vm" && !Reflect.set(obj, key, v, receiver)) || sync === "host" || !ctx.alive)
34
38
  return true;
35
39
 
36
40
  mayConsumeAll(
37
41
  [marshal(receiver), marshal(key), marshal(v)],
38
42
  (receiverHandle, keyHandle, valueHandle) => {
39
- const [handle2, unwrapped] = unwrapHandle(
40
- ctx,
41
- receiverHandle,
42
- proxyKeySymbolHandle
43
- );
43
+ const [handle2, unwrapped] = unwrapHandle(ctx, receiverHandle, proxyKeySymbolHandle);
44
44
  if (unwrapped) {
45
- handle2.consume((h) => ctx.setProp(h, keyHandle, valueHandle));
45
+ handle2.consume(h => ctx.setProp(h, keyHandle, valueHandle));
46
46
  } else {
47
47
  ctx.setProp(handle2, keyHandle, valueHandle);
48
48
  }
49
- }
49
+ },
50
50
  );
51
51
 
52
52
  return true;
53
53
  },
54
54
  deleteProperty(obj, key) {
55
55
  const sync = syncMode?.(rec) ?? "host";
56
- return mayConsumeAll(
57
- [marshal(rec), marshal(key)],
58
- (recHandle, keyHandle) => {
59
- const [handle2, unwrapped] = unwrapHandle(
60
- ctx,
61
- recHandle,
62
- proxyKeySymbolHandle
63
- );
64
-
65
- if (sync === "vm" || Reflect.deleteProperty(obj, key)) {
66
- if (sync === "host" || !ctx.alive) return true;
67
-
68
- if (unwrapped) {
69
- handle2.consume((h) =>
70
- call(ctx, `(a, b) => delete a[b]`, undefined, h, keyHandle)
71
- );
72
- } else {
73
- call(ctx, `(a, b) => delete a[b]`, undefined, handle2, keyHandle);
74
- }
56
+ return mayConsumeAll([marshal(rec), marshal(key)], (recHandle, keyHandle) => {
57
+ const [handle2, unwrapped] = unwrapHandle(ctx, recHandle, proxyKeySymbolHandle);
58
+
59
+ if (sync === "vm" || Reflect.deleteProperty(obj, key)) {
60
+ if (sync === "host" || !ctx.alive) return true;
61
+
62
+ if (unwrapped) {
63
+ handle2.consume(h => call(ctx, `(a, b) => delete a[b]`, undefined, h, keyHandle));
64
+ } else {
65
+ call(ctx, `(a, b) => delete a[b]`, undefined, handle2, keyHandle);
75
66
  }
76
- return true;
77
67
  }
78
- );
68
+ return true;
69
+ });
79
70
  },
80
71
  }) as Wrapped<T>;
81
72
  return rec;
@@ -87,15 +78,17 @@ export function wrapHandle(
87
78
  proxyKeySymbol: symbol,
88
79
  proxyKeySymbolHandle: QuickJSHandle,
89
80
  unmarshal: (handle: QuickJSHandle) => any,
90
- syncMode?: (target: QuickJSHandle) => SyncMode | undefined
81
+ syncMode?: (target: QuickJSHandle) => SyncMode | undefined,
82
+ wrappable?: (target: QuickJSHandle, ctx: QuickJSContext) => boolean,
91
83
  ): [Wrapped<QuickJSHandle> | undefined, boolean] {
92
- if (!isHandleObject(ctx, handle)) return [undefined, false];
93
- if (isHandleWrapped(ctx, handle, proxyKeySymbolHandle))
94
- return [handle, false];
84
+ if (!isHandleObject(ctx, handle) || (wrappable && !wrappable(handle, ctx)))
85
+ return [undefined, false];
86
+
87
+ if (isHandleWrapped(ctx, handle, proxyKeySymbolHandle)) return [handle, false];
95
88
 
96
89
  return consumeAll(
97
90
  [
98
- ctx.newFunction("getSyncMode", (h) => {
91
+ ctx.newFunction("getSyncMode", h => {
99
92
  const res = syncMode?.(unmarshal(h));
100
93
  if (typeof res === "string") return ctx.newString(res);
101
94
  return ctx.undefined;
@@ -115,7 +108,7 @@ export function wrapHandle(
115
108
  delete unwrap(target, proxyKeySymbol)[key];
116
109
  }),
117
110
  ],
118
- (args) => [
111
+ args => [
119
112
  call(
120
113
  ctx,
121
114
  `(target, sym, getSyncMode, setter, deleter) => {
@@ -150,10 +143,10 @@ export function wrapHandle(
150
143
  undefined,
151
144
  handle,
152
145
  proxyKeySymbolHandle,
153
- ...args
146
+ ...args,
154
147
  ) as Wrapped<QuickJSHandle>,
155
148
  true,
156
- ]
149
+ ],
157
150
  );
158
151
  }
159
152
 
@@ -164,7 +157,7 @@ export function unwrap<T>(obj: T, key: string | symbol): T {
164
157
  export function unwrapHandle(
165
158
  ctx: QuickJSContext,
166
159
  handle: QuickJSHandle,
167
- key: QuickJSHandle
160
+ key: QuickJSHandle,
168
161
  ): [QuickJSHandle, boolean] {
169
162
  if (!isHandleWrapped(ctx, handle, key)) return [handle, false];
170
163
  return [ctx.getProp(handle, key), true];
@@ -177,16 +170,16 @@ export function isWrapped<T>(obj: T, key: string | symbol): obj is Wrapped<T> {
177
170
  export function isHandleWrapped(
178
171
  ctx: QuickJSContext,
179
172
  handle: QuickJSHandle,
180
- key: QuickJSHandle
173
+ key: QuickJSHandle,
181
174
  ): handle is Wrapped<QuickJSHandle> {
182
175
  return !!ctx.dump(
183
176
  call(
184
177
  ctx,
185
- // promise cannot be wrapped
186
- `(a, s) => (a instanceof Promise) || (typeof a === "object" && a !== null || typeof a === "function") && !!a[s]`,
178
+ // promise and date cannot be wrapped
179
+ `(a, s) => (a instanceof Promise) || (a instanceof Date) || (typeof a === "object" && a !== null || typeof a === "function") && !!a[s]`,
187
180
  undefined,
188
181
  handle,
189
- key
190
- )
182
+ key,
183
+ ),
191
184
  );
192
185
  }