quickjs-emscripten-sync 1.1.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.
Files changed (71) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +59 -49
  3. package/dist/index.d.ts +121 -26
  4. package/dist/quickjs-emscripten-sync.es.js +962 -0
  5. package/dist/quickjs-emscripten-sync.umd.js +64 -0
  6. package/package.json +28 -40
  7. package/src/default.ts +2 -2
  8. package/src/index.test.ts +371 -69
  9. package/src/index.ts +135 -72
  10. package/src/marshal/function.test.ts +70 -61
  11. package/src/marshal/function.ts +10 -9
  12. package/src/marshal/index.test.ts +135 -62
  13. package/src/marshal/index.ts +33 -16
  14. package/src/marshal/json.test.ts +94 -0
  15. package/src/marshal/json.ts +16 -0
  16. package/src/marshal/object.test.ts +65 -58
  17. package/src/marshal/object.ts +6 -5
  18. package/src/marshal/primitive.test.ts +23 -17
  19. package/src/marshal/primitive.ts +10 -9
  20. package/src/marshal/promise.test.ts +86 -0
  21. package/src/marshal/promise.ts +26 -0
  22. package/src/marshal/properties.test.ts +23 -19
  23. package/src/marshal/properties.ts +11 -10
  24. package/src/marshal/symbol.test.ts +9 -7
  25. package/src/marshal/symbol.ts +5 -4
  26. package/src/unmarshal/function.test.ts +70 -61
  27. package/src/unmarshal/function.ts +39 -30
  28. package/src/unmarshal/index.test.ts +101 -100
  29. package/src/unmarshal/index.ts +13 -9
  30. package/src/unmarshal/object.test.ts +45 -41
  31. package/src/unmarshal/object.ts +14 -13
  32. package/src/unmarshal/primitive.test.ts +20 -15
  33. package/src/unmarshal/primitive.ts +10 -10
  34. package/src/unmarshal/promise.test.ts +44 -0
  35. package/src/unmarshal/promise.ts +38 -0
  36. package/src/unmarshal/properties.test.ts +10 -8
  37. package/src/unmarshal/properties.ts +47 -42
  38. package/src/unmarshal/symbol.test.ts +9 -7
  39. package/src/unmarshal/symbol.ts +4 -4
  40. package/src/util.test.ts +23 -5
  41. package/src/util.ts +15 -0
  42. package/src/vmmap.test.ts +88 -82
  43. package/src/vmmap.ts +21 -17
  44. package/src/vmutil.test.ts +159 -53
  45. package/src/vmutil.ts +91 -19
  46. package/src/wrapper.test.ts +151 -115
  47. package/src/wrapper.ts +69 -48
  48. package/dist/default.d.ts +0 -4
  49. package/dist/index.js +0 -8
  50. package/dist/marshal/function.d.ts +0 -2
  51. package/dist/marshal/index.d.ts +0 -11
  52. package/dist/marshal/object.d.ts +0 -2
  53. package/dist/marshal/primitive.d.ts +0 -2
  54. package/dist/marshal/properties.d.ts +0 -2
  55. package/dist/marshal/symbol.d.ts +0 -2
  56. package/dist/quickjs-emscripten-sync.cjs.development.js +0 -1289
  57. package/dist/quickjs-emscripten-sync.cjs.development.js.map +0 -1
  58. package/dist/quickjs-emscripten-sync.cjs.production.min.js +0 -2
  59. package/dist/quickjs-emscripten-sync.cjs.production.min.js.map +0 -1
  60. package/dist/quickjs-emscripten-sync.esm.js +0 -1272
  61. package/dist/quickjs-emscripten-sync.esm.js.map +0 -1
  62. package/dist/unmarshal/function.d.ts +0 -2
  63. package/dist/unmarshal/index.d.ts +0 -9
  64. package/dist/unmarshal/object.d.ts +0 -2
  65. package/dist/unmarshal/primitive.d.ts +0 -2
  66. package/dist/unmarshal/properties.d.ts +0 -2
  67. package/dist/unmarshal/symbol.d.ts +0 -2
  68. package/dist/util.d.ts +0 -7
  69. package/dist/vmmap.d.ts +0 -35
  70. package/dist/vmutil.d.ts +0 -7
  71. package/dist/wrapper.d.ts +0 -11
@@ -1,10 +1,13 @@
1
1
  import { getQuickJS } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
2
4
  import VMMap from "../vmmap";
3
- import { instanceOf, call } from "../vmutil";
5
+ import { instanceOf, call, handleFrom, fn } from "../vmutil";
4
6
  import marshal from ".";
7
+ import { newDeferred } from "../util";
5
8
 
6
9
  test("primitive, array, object", async () => {
7
- const { vm, map, marshal, dispose } = await setup();
10
+ const { ctx, map, marshal, dispose } = await setup();
8
11
 
9
12
  const target = {
10
13
  hoge: "foo",
@@ -14,7 +17,7 @@ test("primitive, array, object", async () => {
14
17
  };
15
18
  const handle = marshal(target);
16
19
 
17
- expect(vm.dump(handle)).toEqual(target);
20
+ expect(ctx.dump(handle)).toEqual(target);
18
21
  expect(map.size).toBe(4);
19
22
  expect(map.get(target)).toBe(handle);
20
23
  expect(map.has(target.aaa)).toBe(true);
@@ -25,17 +28,17 @@ test("primitive, array, object", async () => {
25
28
  });
26
29
 
27
30
  test("object with symbol key", async () => {
28
- const { vm, marshal, dispose } = await setup();
31
+ const { ctx, marshal, dispose } = await setup();
29
32
 
30
33
  const target = {
31
34
  foo: "hoge",
32
35
  [Symbol("a")]: 1,
33
36
  };
34
37
  const handle = marshal(target);
35
- expect(vm.dump(call(vm, `a => a.foo`, undefined, handle))).toBe("hoge");
38
+ expect(ctx.dump(call(ctx, `a => a.foo`, undefined, handle))).toBe("hoge");
36
39
  expect(
37
- vm.dump(
38
- call(vm, `a => a[Object.getOwnPropertySymbols(a)[0]]`, undefined, handle)
40
+ ctx.dump(
41
+ call(ctx, `a => a[Object.getOwnPropertySymbols(a)[0]]`, undefined, handle)
39
42
  )
40
43
  ).toBe(1);
41
44
 
@@ -43,19 +46,19 @@ test("object with symbol key", async () => {
43
46
  });
44
47
 
45
48
  test("arrow function", async () => {
46
- const { vm, map, marshal, dispose } = await setup();
49
+ const { ctx, map, marshal, dispose } = await setup();
47
50
  const hoge = () => "foo";
48
51
  hoge.foo = { bar: 1 };
49
52
  const handle = marshal(hoge);
50
53
 
51
- expect(vm.typeof(handle)).toBe("function");
52
- expect(vm.dump(vm.getProp(handle, "length"))).toBe(0);
53
- expect(vm.dump(vm.getProp(handle, "name"))).toBe("hoge");
54
- const foo = vm.getProp(handle, "foo");
55
- expect(vm.dump(foo)).toEqual({ bar: 1 });
56
- expect(vm.dump(vm.unwrapResult(vm.callFunction(handle, vm.undefined)))).toBe(
57
- "foo"
58
- );
54
+ expect(ctx.typeof(handle)).toBe("function");
55
+ expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(0);
56
+ expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("hoge");
57
+ const foo = ctx.getProp(handle, "foo");
58
+ expect(ctx.dump(foo)).toEqual({ bar: 1 });
59
+ expect(
60
+ ctx.dump(ctx.unwrapResult(ctx.callFunction(handle, ctx.undefined)))
61
+ ).toBe("foo");
59
62
  expect(map.size).toBe(2);
60
63
  expect(map.get(hoge)).toBe(handle);
61
64
  expect(map.has(hoge.foo)).toBe(true);
@@ -65,24 +68,26 @@ test("arrow function", async () => {
65
68
  });
66
69
 
67
70
  test("function", async () => {
68
- const { vm, map, marshal, dispose } = await setup();
71
+ const { ctx, map, marshal, dispose } = await setup();
69
72
 
70
- const bar = function(a: number, b: { hoge: number }) {
73
+ const bar = function (a: number, b: { hoge: number }) {
71
74
  return a + b.hoge;
72
75
  };
73
76
  const handle = marshal(bar);
74
77
 
75
- expect(vm.typeof(handle)).toBe("function");
76
- expect(vm.dump(vm.getProp(handle, "length"))).toBe(2);
77
- expect(vm.dump(vm.getProp(handle, "name"))).toBe("bar");
78
+ expect(ctx.typeof(handle)).toBe("function");
79
+ expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(2);
80
+ expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("bar");
78
81
  expect(map.size).toBe(2);
79
82
  expect(map.get(bar)).toBe(handle);
80
83
  expect(map.has(bar.prototype)).toBe(true);
81
84
 
82
- const b = vm.unwrapResult(vm.evalCode(`({ hoge: 2 })`));
85
+ const b = ctx.unwrapResult(ctx.evalCode(`({ hoge: 2 })`));
83
86
  expect(
84
- vm.dump(
85
- vm.unwrapResult(vm.callFunction(handle, vm.undefined, vm.newNumber(1), b))
87
+ ctx.dump(
88
+ ctx.unwrapResult(
89
+ ctx.callFunction(handle, ctx.undefined, ctx.newNumber(1), b)
90
+ )
86
91
  )
87
92
  ).toBe(3);
88
93
 
@@ -90,8 +95,46 @@ test("function", async () => {
90
95
  dispose();
91
96
  });
92
97
 
98
+ test("promise", async () => {
99
+ const { ctx, marshal, dispose } = await setup();
100
+ const register = fn(
101
+ ctx,
102
+ `promise => { promise.then(d => notify("resolve", d), d => notify("reject", d)); }`
103
+ );
104
+
105
+ let notified: any;
106
+ ctx
107
+ .newFunction("notify", (...handles) => {
108
+ notified = handles.map((h) => ctx.dump(h));
109
+ })
110
+ .consume((h) => {
111
+ ctx.setProp(ctx.global, "notify", h);
112
+ });
113
+
114
+ const deferred = newDeferred();
115
+ const handle = marshal(deferred.promise);
116
+ register(undefined, handle);
117
+
118
+ deferred.resolve("foo");
119
+ await deferred.promise;
120
+ expect(ctx.unwrapResult(ctx.runtime.executePendingJobs())).toBe(1);
121
+ expect(notified).toEqual(["resolve", "foo"]);
122
+
123
+ const deferred2 = newDeferred();
124
+ const handle2 = marshal(deferred2.promise);
125
+ register(undefined, handle2);
126
+
127
+ deferred2.reject("bar");
128
+ await expect(deferred2.promise).rejects.toBe("bar");
129
+ expect(ctx.unwrapResult(ctx.runtime.executePendingJobs())).toBe(1);
130
+ expect(notified).toEqual(["reject", "bar"]);
131
+
132
+ register.dispose();
133
+ dispose();
134
+ });
135
+
93
136
  test("class", async () => {
94
- const { vm, map, marshal, dispose } = await setup();
137
+ const { ctx, map, marshal, dispose } = await setup();
95
138
 
96
139
  class A {
97
140
  a: number;
@@ -117,6 +160,7 @@ test("class", async () => {
117
160
  }
118
161
  }
119
162
 
163
+ expect(A.name).toBe("_A"); // class A's name is _A in vitest
120
164
  const handle = marshal(A);
121
165
  if (!map) throw new Error("map is undefined");
122
166
 
@@ -126,40 +170,44 @@ test("class", async () => {
126
170
  expect(map.has(A.a)).toBe(true);
127
171
  expect(map.has(A.prototype.hoge)).toBe(true);
128
172
  expect(
129
- map.has(Object.getOwnPropertyDescriptor(A.prototype, "foo")!.get)
173
+ map.has(Object.getOwnPropertyDescriptor(A.prototype, "foo")?.get)
130
174
  ).toBe(true);
131
175
  expect(
132
- map.has(Object.getOwnPropertyDescriptor(A.prototype, "foo")!.set)
176
+ map.has(Object.getOwnPropertyDescriptor(A.prototype, "foo")?.set)
133
177
  ).toBe(true);
134
178
 
135
- expect(vm.typeof(handle)).toBe("function");
136
- expect(vm.dump(vm.getProp(handle, "length"))).toBe(1);
137
- expect(vm.dump(vm.getProp(handle, "name"))).toBe("A");
138
- const staticA = vm.getProp(handle, "a");
139
- expect(instanceOf(vm, staticA, handle)).toBe(true);
140
- expect(vm.dump(vm.getProp(staticA, "a"))).toBe(100);
141
- expect(vm.dump(vm.getProp(staticA, "b"))).toBe("a!");
142
-
143
- const newA = vm.unwrapResult(vm.evalCode(`A => new A("foo")`));
144
- const instance = vm.unwrapResult(vm.callFunction(newA, vm.undefined, handle));
145
- expect(instanceOf(vm, instance, handle)).toBe(true);
146
- expect(vm.dump(vm.getProp(instance, "a"))).toBe(100);
147
- expect(vm.dump(vm.getProp(instance, "b"))).toBe("foo!");
148
- const methodHoge = vm.getProp(instance, "hoge");
149
- expect(vm.dump(vm.unwrapResult(vm.callFunction(methodHoge, instance)))).toBe(
150
- 101
179
+ expect(ctx.typeof(handle)).toBe("function");
180
+ expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(1);
181
+ expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("_A");
182
+ const staticA = ctx.getProp(handle, "a");
183
+ expect(instanceOf(ctx, staticA, handle)).toBe(true);
184
+ expect(ctx.dump(ctx.getProp(staticA, "a"))).toBe(100);
185
+ expect(ctx.dump(ctx.getProp(staticA, "b"))).toBe("a!");
186
+
187
+ const newA = ctx.unwrapResult(ctx.evalCode(`A => new A("foo")`));
188
+ const instance = ctx.unwrapResult(
189
+ ctx.callFunction(newA, ctx.undefined, handle)
151
190
  );
152
- expect(vm.dump(vm.getProp(instance, "a"))).toBe(100); // not synced
191
+ expect(instanceOf(ctx, instance, handle)).toBe(true);
192
+ expect(ctx.dump(ctx.getProp(instance, "a"))).toBe(100);
193
+ expect(ctx.dump(ctx.getProp(instance, "b"))).toBe("foo!");
194
+ const methodHoge = ctx.getProp(instance, "hoge");
195
+ expect(
196
+ ctx.dump(ctx.unwrapResult(ctx.callFunction(methodHoge, instance)))
197
+ ).toBe(101);
198
+ expect(ctx.dump(ctx.getProp(instance, "a"))).toBe(100); // not synced
153
199
 
154
- const getter = vm.unwrapResult(vm.evalCode(`a => a.foo`));
155
- const setter = vm.unwrapResult(vm.evalCode(`(a, b) => a.foo = b`));
200
+ const getter = ctx.unwrapResult(ctx.evalCode(`a => a.foo`));
201
+ const setter = ctx.unwrapResult(ctx.evalCode(`(a, b) => a.foo = b`));
156
202
  expect(
157
- vm.dump(vm.unwrapResult(vm.callFunction(getter, vm.undefined, instance)))
203
+ ctx.dump(
204
+ ctx.unwrapResult(ctx.callFunction(getter, ctx.undefined, instance))
205
+ )
158
206
  ).toBe("foo!");
159
- vm.unwrapResult(
160
- vm.callFunction(setter, vm.undefined, instance, vm.newString("b"))
207
+ ctx.unwrapResult(
208
+ ctx.callFunction(setter, ctx.undefined, instance, ctx.newString("b"))
161
209
  );
162
- expect(vm.dump(vm.getProp(instance, "b"))).toBe("foo!"); // not synced
210
+ expect(ctx.dump(ctx.getProp(instance, "b"))).toBe("foo!"); // not synced
163
211
 
164
212
  staticA.dispose();
165
213
  newA.dispose();
@@ -172,44 +220,69 @@ test("class", async () => {
172
220
  });
173
221
 
174
222
  test("marshalable", async () => {
175
- const isMarshalable = jest.fn((a: any) => a !== globalThis);
176
- const { vm, marshal, dispose } = await setup({
223
+ const isMarshalable = vi.fn((a: any) => a !== globalThis);
224
+ const { ctx, marshal, dispose } = await setup({
177
225
  isMarshalable,
178
226
  });
179
227
 
180
228
  const handle = marshal({ a: globalThis, b: 1 });
181
229
 
182
- expect(vm.dump(handle)).toEqual({ a: undefined, b: 1 });
230
+ expect(ctx.dump(handle)).toEqual({ a: undefined, b: 1 });
183
231
  expect(isMarshalable).toBeCalledWith(globalThis);
184
232
  expect(isMarshalable).toReturnWith(false);
185
233
 
186
234
  dispose();
187
235
  });
188
236
 
237
+ test("marshalable json", async () => {
238
+ const isMarshalable = vi.fn(() => "json" as const);
239
+ const { ctx, marshal, dispose } = await setup({
240
+ isMarshalable,
241
+ });
242
+
243
+ class Hoge {}
244
+ const target = {
245
+ a: { c: () => 1, d: new Date(), e: [() => 1, 1, new Hoge()] },
246
+ b: 1,
247
+ };
248
+ const handle = marshal(target);
249
+
250
+ expect(ctx.dump(handle)).toEqual({
251
+ a: { d: target.a.d.toISOString(), e: [null, 1, {}] },
252
+ b: 1,
253
+ });
254
+ expect(isMarshalable).toBeCalledTimes(1);
255
+ expect(isMarshalable).toBeCalledWith(target);
256
+ expect(isMarshalable).toReturnWith("json");
257
+
258
+ dispose();
259
+ });
260
+
189
261
  const setup = async ({
190
262
  isMarshalable,
191
263
  }: {
192
- isMarshalable?: (target: any) => boolean;
264
+ isMarshalable?: (target: any) => boolean | "json";
193
265
  } = {}) => {
194
- const vm = (await getQuickJS()).createVm();
195
- const map = new VMMap(vm);
266
+ const ctx = (await getQuickJS()).newContext();
267
+ const map = new VMMap(ctx);
196
268
  return {
197
- vm,
269
+ ctx,
198
270
  map,
199
271
  marshal: (v: any) =>
200
272
  marshal(v, {
201
- vm,
202
- unmarshal: h => map.getByHandle(h) ?? vm.dump(h),
273
+ ctx: ctx,
274
+ unmarshal: (h) => map.getByHandle(h) ?? ctx.dump(h),
203
275
  isMarshalable,
204
- pre: (t, h) => {
276
+ pre: (t, d) => {
277
+ const h = handleFrom(d);
205
278
  map.set(t, h);
206
279
  return h;
207
280
  },
208
- find: t => map.get(t),
281
+ find: (t) => map.get(t),
209
282
  }),
210
283
  dispose: () => {
211
284
  map.dispose();
212
- vm.dispose();
285
+ ctx.dispose();
213
286
  },
214
287
  };
215
288
  };
@@ -1,23 +1,34 @@
1
- import { QuickJSHandle, QuickJSVm } from "quickjs-emscripten";
1
+ import type {
2
+ QuickJSDeferredPromise,
3
+ QuickJSHandle,
4
+ QuickJSContext,
5
+ } from "quickjs-emscripten";
6
+
2
7
  import marshalFunction from "./function";
3
8
  import marshalObject from "./object";
4
9
  import marshalPrimitive from "./primitive";
5
10
  import marshalSymbol from "./symbol";
11
+ import marshalJSON from "./json";
12
+ import marshalPromise from "./promise";
6
13
 
7
14
  export type Options = {
8
- vm: QuickJSVm;
15
+ ctx: QuickJSContext;
9
16
  unmarshal: (handle: QuickJSHandle) => unknown;
10
- isMarshalable?: (target: unknown) => boolean;
17
+ isMarshalable?: (target: unknown) => boolean | "json";
11
18
  find: (target: unknown) => QuickJSHandle | undefined;
12
- pre: (target: unknown, handle: QuickJSHandle) => QuickJSHandle | undefined;
19
+ pre: (
20
+ target: unknown,
21
+ handle: QuickJSHandle | QuickJSDeferredPromise,
22
+ mode: true | "json" | undefined
23
+ ) => QuickJSHandle | undefined;
13
24
  preApply?: (target: Function, thisArg: unknown, args: unknown[]) => any;
14
25
  };
15
26
 
16
27
  export function marshal(target: unknown, options: Options): QuickJSHandle {
17
- const { vm, unmarshal, isMarshalable, find, pre } = options;
28
+ const { ctx, unmarshal, isMarshalable, find, pre } = options;
18
29
 
19
30
  {
20
- const primitive = marshalPrimitive(vm, target);
31
+ const primitive = marshalPrimitive(ctx, target);
21
32
  if (primitive) {
22
33
  return primitive;
23
34
  }
@@ -28,19 +39,25 @@ export function marshal(target: unknown, options: Options): QuickJSHandle {
28
39
  if (handle) return handle;
29
40
  }
30
41
 
31
- if (isMarshalable?.(target) === false) {
32
- return vm.undefined;
42
+ const marshalable = isMarshalable?.(target);
43
+ if (marshalable === false) {
44
+ return ctx.undefined;
33
45
  }
34
46
 
35
- const marshal2 = (t: unknown) => marshal(t, options);
36
-
37
- const result =
38
- marshalSymbol(vm, target, pre) ??
39
- marshalFunction(vm, target, marshal2, unmarshal, pre, options.preApply) ??
40
- marshalObject(vm, target, marshal2, pre) ??
41
- vm.undefined;
47
+ const pre2 = (target: any, handle: QuickJSHandle | QuickJSDeferredPromise) =>
48
+ pre(target, handle, marshalable);
49
+ if (marshalable === "json") {
50
+ return marshalJSON(ctx, target, pre2);
51
+ }
42
52
 
43
- return result;
53
+ const marshal2 = (t: unknown) => marshal(t, options);
54
+ return (
55
+ marshalSymbol(ctx, target, pre2) ??
56
+ marshalPromise(ctx, target, marshal2, pre2) ??
57
+ marshalFunction(ctx, target, marshal2, unmarshal, pre2, options.preApply) ??
58
+ marshalObject(ctx, target, marshal2, pre2) ??
59
+ ctx.undefined
60
+ );
44
61
  }
45
62
 
46
63
  export default marshal;
@@ -0,0 +1,94 @@
1
+ import { getQuickJS } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
4
+ import marshalJSON from "./json";
5
+
6
+ test("empty object", async () => {
7
+ const ctx = (await getQuickJS()).newContext();
8
+ const prototypeCheck = ctx.unwrapResult(
9
+ ctx.evalCode(`o => Object.getPrototypeOf(o) === Object.prototype`)
10
+ );
11
+
12
+ const obj = {};
13
+ const preMarshal = vi.fn((_, a) => a);
14
+
15
+ const handle = marshalJSON(ctx, obj, preMarshal);
16
+ if (!handle) throw new Error("handle is undefined");
17
+
18
+ expect(ctx.typeof(handle)).toBe("object");
19
+ expect(preMarshal).toBeCalledTimes(1);
20
+ expect(preMarshal.mock.calls[0][0]).toBe(obj);
21
+ expect(preMarshal.mock.calls[0][1] === handle).toBe(true); // avoid freeze
22
+ expect(
23
+ ctx.dump(
24
+ ctx.unwrapResult(ctx.callFunction(prototypeCheck, ctx.undefined, handle))
25
+ )
26
+ ).toBe(true);
27
+
28
+ handle.dispose();
29
+ prototypeCheck.dispose();
30
+ ctx.dispose();
31
+ });
32
+
33
+ test("normal object", async () => {
34
+ const ctx = (await getQuickJS()).newContext();
35
+ const prototypeCheck = ctx.unwrapResult(
36
+ ctx.evalCode(`o => Object.getPrototypeOf(o) === Object.prototype`)
37
+ );
38
+ const entries = ctx.unwrapResult(ctx.evalCode(`Object.entries`));
39
+
40
+ const obj = { a: 100, b: "hoge" };
41
+ const preMarshal = vi.fn((_, a) => a);
42
+
43
+ const handle = marshalJSON(ctx, obj, preMarshal);
44
+ if (!handle) throw new Error("handle is undefined");
45
+
46
+ expect(ctx.typeof(handle)).toBe("object");
47
+ expect(ctx.getNumber(ctx.getProp(handle, "a"))).toBe(100);
48
+ expect(ctx.getString(ctx.getProp(handle, "b"))).toBe("hoge");
49
+ expect(preMarshal).toBeCalledTimes(1);
50
+ expect(preMarshal.mock.calls[0][0]).toBe(obj);
51
+ expect(preMarshal.mock.calls[0][1] === handle).toBe(true); // avoid freeze
52
+ expect(
53
+ ctx.dump(
54
+ ctx.unwrapResult(ctx.callFunction(prototypeCheck, ctx.undefined, handle))
55
+ )
56
+ ).toBe(true);
57
+ const e = ctx.unwrapResult(ctx.callFunction(entries, ctx.undefined, handle));
58
+ expect(ctx.dump(e)).toEqual([
59
+ ["a", 100],
60
+ ["b", "hoge"],
61
+ ]);
62
+
63
+ e.dispose();
64
+ handle.dispose();
65
+ prototypeCheck.dispose();
66
+ entries.dispose();
67
+ ctx.dispose();
68
+ });
69
+
70
+ test("array", async () => {
71
+ const ctx = (await getQuickJS()).newContext();
72
+ const isArray = ctx.unwrapResult(ctx.evalCode(`Array.isArray`));
73
+
74
+ const array = [1, "aa"];
75
+ const preMarshal = vi.fn((_, a) => a);
76
+
77
+ const handle = marshalJSON(ctx, array, preMarshal);
78
+ if (!handle) throw new Error("handle is undefined");
79
+
80
+ expect(ctx.typeof(handle)).toBe("object");
81
+ expect(ctx.getNumber(ctx.getProp(handle, 0))).toBe(1);
82
+ expect(ctx.getString(ctx.getProp(handle, 1))).toBe("aa");
83
+ expect(ctx.getNumber(ctx.getProp(handle, "length"))).toBe(2);
84
+ expect(preMarshal).toBeCalledTimes(1);
85
+ expect(preMarshal.mock.calls[0][0]).toBe(array);
86
+ expect(preMarshal.mock.calls[0][1] === handle).toBe(true); // avoid freeze
87
+ expect(
88
+ ctx.dump(ctx.unwrapResult(ctx.callFunction(isArray, ctx.undefined, handle)))
89
+ ).toBe(true);
90
+
91
+ handle.dispose();
92
+ isArray.dispose();
93
+ ctx.dispose();
94
+ });
@@ -0,0 +1,16 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+
3
+ import { json } from "../vmutil";
4
+
5
+ export default function marshalJSON(
6
+ ctx: QuickJSContext,
7
+ target: unknown,
8
+ preMarshal: (
9
+ target: unknown,
10
+ handle: QuickJSHandle
11
+ ) => QuickJSHandle | undefined
12
+ ): QuickJSHandle {
13
+ const raw = json(ctx, target);
14
+ const handle = preMarshal(target, raw) ?? raw;
15
+ return handle;
16
+ }