quickjs-emscripten-sync 1.2.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 (76) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +71 -44
  3. package/dist/index.d.ts +116 -23
  4. package/dist/quickjs-emscripten-sync.mjs +842 -0
  5. package/dist/quickjs-emscripten-sync.umd.js +64 -2
  6. package/package.json +22 -53
  7. package/src/default.ts +2 -2
  8. package/src/index.test.ts +223 -59
  9. package/src/index.ts +72 -66
  10. package/src/marshal/custom.test.ts +50 -0
  11. package/src/marshal/custom.ts +36 -0
  12. package/src/marshal/function.test.ts +64 -75
  13. package/src/marshal/function.ts +15 -18
  14. package/src/marshal/index.test.ts +122 -77
  15. package/src/marshal/index.ts +19 -15
  16. package/src/marshal/json.test.ts +37 -41
  17. package/src/marshal/json.ts +5 -7
  18. package/src/marshal/object.test.ts +59 -84
  19. package/src/marshal/object.ts +8 -15
  20. package/src/marshal/primitive.test.ts +18 -21
  21. package/src/marshal/primitive.ts +11 -10
  22. package/src/marshal/promise.test.ts +85 -0
  23. package/src/marshal/promise.ts +19 -0
  24. package/src/marshal/properties.test.ts +24 -27
  25. package/src/marshal/properties.ts +15 -17
  26. package/src/unmarshal/custom.test.ts +50 -0
  27. package/src/unmarshal/custom.ts +31 -0
  28. package/src/unmarshal/function.test.ts +63 -86
  29. package/src/unmarshal/function.ts +13 -22
  30. package/src/unmarshal/index.test.ts +114 -109
  31. package/src/unmarshal/index.ts +15 -14
  32. package/src/unmarshal/object.test.ts +42 -46
  33. package/src/unmarshal/object.ts +16 -20
  34. package/src/unmarshal/primitive.test.ts +17 -15
  35. package/src/unmarshal/primitive.ts +11 -18
  36. package/src/unmarshal/promise.test.ts +44 -0
  37. package/src/unmarshal/promise.ts +31 -0
  38. package/src/unmarshal/properties.test.ts +11 -9
  39. package/src/unmarshal/properties.ts +44 -45
  40. package/src/util.test.ts +14 -2
  41. package/src/util.ts +18 -8
  42. package/src/vmmap.test.ts +88 -82
  43. package/src/vmmap.ts +30 -44
  44. package/src/vmutil.test.ts +100 -75
  45. package/src/vmutil.ts +51 -34
  46. package/src/wrapper.test.ts +129 -167
  47. package/src/wrapper.ts +59 -62
  48. package/dist/default.d.ts +0 -4
  49. package/dist/marshal/function.d.ts +0 -2
  50. package/dist/marshal/index.d.ts +0 -11
  51. package/dist/marshal/json.d.ts +0 -2
  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 +0 -2
  57. package/dist/quickjs-emscripten-sync.cjs.map +0 -1
  58. package/dist/quickjs-emscripten-sync.modern.js +0 -2
  59. package/dist/quickjs-emscripten-sync.modern.js.map +0 -1
  60. package/dist/quickjs-emscripten-sync.module.js +0 -2
  61. package/dist/quickjs-emscripten-sync.module.js.map +0 -1
  62. package/dist/quickjs-emscripten-sync.umd.js.map +0 -1
  63. package/dist/unmarshal/function.d.ts +0 -2
  64. package/dist/unmarshal/index.d.ts +0 -9
  65. package/dist/unmarshal/object.d.ts +0 -2
  66. package/dist/unmarshal/primitive.d.ts +0 -2
  67. package/dist/unmarshal/properties.d.ts +0 -2
  68. package/dist/unmarshal/symbol.d.ts +0 -2
  69. package/dist/util.d.ts +0 -7
  70. package/dist/vmmap.d.ts +0 -35
  71. package/dist/vmutil.d.ts +0 -11
  72. package/dist/wrapper.d.ts +0 -11
  73. package/src/marshal/symbol.test.ts +0 -22
  74. package/src/marshal/symbol.ts +0 -20
  75. package/src/unmarshal/symbol.test.ts +0 -23
  76. package/src/unmarshal/symbol.ts +0 -12
@@ -0,0 +1,31 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+
3
+ import { call } from "../vmutil";
4
+
5
+ export default function unmarshalCustom(
6
+ ctx: QuickJSContext,
7
+ handle: QuickJSHandle,
8
+ preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
9
+ custom: Iterable<(handle: QuickJSHandle, ctx: QuickJSContext) => any>,
10
+ ): symbol | undefined {
11
+ let obj: any;
12
+ for (const c of custom) {
13
+ obj = c(handle, ctx);
14
+ if (obj) break;
15
+ }
16
+ return obj ? preUnmarshal(obj, handle) ?? obj : undefined;
17
+ }
18
+
19
+ export function symbol(handle: QuickJSHandle, ctx: QuickJSContext): symbol | undefined {
20
+ if (ctx.typeof(handle) !== "symbol") return;
21
+ const desc = ctx.getString(ctx.getProp(handle, "description"));
22
+ return Symbol(desc);
23
+ }
24
+
25
+ export function date(handle: QuickJSHandle, ctx: QuickJSContext): Date | undefined {
26
+ if (!ctx.dump(call(ctx, "a => a instanceof Date", undefined, handle))) return;
27
+ const t = ctx.getNumber(call(ctx, "a => a.getTime()", undefined, handle));
28
+ return new Date(t);
29
+ }
30
+
31
+ export const defaultCustom = [symbol, date];
@@ -1,21 +1,18 @@
1
1
  import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
2
4
  import { json } from "../vmutil";
5
+
3
6
  import unmarshalFunction from "./function";
4
7
 
5
8
  test("arrow function", async () => {
6
- const vm = (await getQuickJS()).createVm();
7
- const marshal = jest.fn((v): [QuickJSHandle, boolean] => [
8
- json(vm, v),
9
- false,
10
- ]);
11
- const unmarshal = jest.fn((v: QuickJSHandle): [unknown, boolean] => [
12
- vm.dump(v),
13
- false,
14
- ]);
15
- const preUnmarshal = jest.fn((a) => a);
9
+ const ctx = (await getQuickJS()).newContext();
10
+ const marshal = vi.fn((v): [QuickJSHandle, boolean] => [json(ctx, v), false]);
11
+ const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => [ctx.dump(v), false]);
12
+ const preUnmarshal = vi.fn(a => a);
16
13
 
17
- const handle = vm.unwrapResult(vm.evalCode(`(a, b) => a + b`));
18
- const func = unmarshalFunction(vm, handle, marshal, unmarshal, preUnmarshal);
14
+ const handle = ctx.unwrapResult(ctx.evalCode(`(a, b) => a + b`));
15
+ const func = unmarshalFunction(ctx, handle, marshal, unmarshal, preUnmarshal);
19
16
  if (!func) throw new Error("func is undefined");
20
17
 
21
18
  expect(func(1, 2)).toBe(3);
@@ -35,30 +32,28 @@ test("arrow function", async () => {
35
32
  handle.dispose();
36
33
  expect(() => func(1, 2)).toThrow("Lifetime not alive");
37
34
 
38
- vm.dispose();
35
+ ctx.dispose();
39
36
  });
40
37
 
41
38
  test("function", async () => {
42
- const vm = (await getQuickJS()).createVm();
39
+ const ctx = (await getQuickJS()).newContext();
43
40
  const that = { a: 1 };
44
- const thatHandle = vm.unwrapResult(vm.evalCode(`({ a: 1 })`));
45
- const marshal = jest.fn((v): [QuickJSHandle, boolean] => [
46
- v === that ? thatHandle : json(vm, v),
41
+ const thatHandle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
42
+ const marshal = vi.fn((v): [QuickJSHandle, boolean] => [
43
+ v === that ? thatHandle : json(ctx, v),
47
44
  false,
48
45
  ]);
49
46
  const disposables: QuickJSHandle[] = [];
50
- const unmarshal = jest.fn((v: QuickJSHandle): [unknown, boolean] => {
51
- const ty = vm.typeof(v);
47
+ const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
48
+ const ty = ctx.typeof(v);
52
49
  if (ty === "object" || ty === "function") disposables.push(v);
53
- return [vm.dump(v), false];
50
+ return [ctx.dump(v), false];
54
51
  });
55
- const preUnmarshal = jest.fn((a) => a);
52
+ const preUnmarshal = vi.fn(a => a);
56
53
 
57
- const handle = vm.unwrapResult(
58
- vm.evalCode(`(function (a) { return this.a + a; })`)
59
- );
54
+ const handle = ctx.unwrapResult(ctx.evalCode(`(function (a) { return this.a + a; })`));
60
55
 
61
- const func = unmarshalFunction(vm, handle, marshal, unmarshal, preUnmarshal);
56
+ const func = unmarshalFunction(ctx, handle, marshal, unmarshal, preUnmarshal);
62
57
  if (!func) throw new Error("func is undefined");
63
58
 
64
59
  expect(func.call(that, 2)).toBe(3);
@@ -76,37 +71,29 @@ test("function", async () => {
76
71
  expect(preUnmarshal).toBeCalledTimes(1);
77
72
  expect(preUnmarshal).toBeCalledWith(func, handle);
78
73
 
79
- disposables.forEach((d) => d.dispose());
74
+ disposables.forEach(d => d.dispose());
80
75
  thatHandle.dispose();
81
76
  handle.dispose();
82
- vm.dispose();
77
+ ctx.dispose();
83
78
  });
84
79
 
85
80
  test("constructor", async () => {
86
- const vm = (await getQuickJS()).createVm();
81
+ const ctx = (await getQuickJS()).newContext();
87
82
  const disposables: QuickJSHandle[] = [];
88
- const marshal = jest.fn((v): [QuickJSHandle, boolean] => [
89
- typeof v === "object" ? vm.undefined : json(vm, v),
83
+ const marshal = vi.fn((v): [QuickJSHandle, boolean] => [
84
+ typeof v === "object" ? ctx.undefined : json(ctx, v),
90
85
  false,
91
86
  ]);
92
- const unmarshal = jest.fn((v: QuickJSHandle): [unknown, boolean] => {
93
- const ty = vm.typeof(v);
87
+ const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
88
+ const ty = ctx.typeof(v);
94
89
  if (ty === "object" || ty === "function") disposables.push(v);
95
- return [vm.dump(v), false];
90
+ return [ctx.dump(v), false];
96
91
  });
97
- const preUnmarshal = jest.fn((a) => a);
98
-
99
- const handle = vm.unwrapResult(
100
- vm.evalCode(`(function (b) { this.a = b + 2; })`)
101
- );
102
-
103
- const Cls = unmarshalFunction(
104
- vm,
105
- handle,
106
- marshal,
107
- unmarshal,
108
- preUnmarshal
109
- ) as any;
92
+ const preUnmarshal = vi.fn(a => a);
93
+
94
+ const handle = ctx.unwrapResult(ctx.evalCode(`(function (b) { this.a = b + 2; })`));
95
+
96
+ const Cls = unmarshalFunction(ctx, handle, marshal, unmarshal, preUnmarshal) as any;
110
97
  if (!Cls) throw new Error("Cls is undefined");
111
98
 
112
99
  const instance = new Cls(100);
@@ -126,36 +113,28 @@ test("constructor", async () => {
126
113
  expect(preUnmarshal).toBeCalledTimes(1);
127
114
  expect(preUnmarshal).toBeCalledWith(Cls, handle);
128
115
 
129
- disposables.forEach((d) => d.dispose());
116
+ disposables.forEach(d => d.dispose());
130
117
  handle.dispose();
131
- vm.dispose();
118
+ ctx.dispose();
132
119
  });
133
120
 
134
121
  test("class", async () => {
135
- const vm = (await getQuickJS()).createVm();
136
- const marshal = jest.fn((v): [QuickJSHandle, boolean] => [
137
- typeof v === "object" ? vm.undefined : json(vm, v),
122
+ const ctx = (await getQuickJS()).newContext();
123
+ const marshal = vi.fn((v): [QuickJSHandle, boolean] => [
124
+ typeof v === "object" ? ctx.undefined : json(ctx, v),
138
125
  false,
139
126
  ]);
140
127
  const disposables: QuickJSHandle[] = [];
141
- const unmarshal = jest.fn((v: QuickJSHandle): [unknown, boolean] => {
142
- const ty = vm.typeof(v);
128
+ const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
129
+ const ty = ctx.typeof(v);
143
130
  if (ty === "object" || ty === "function") disposables.push(v);
144
- return [vm.dump(v), false];
131
+ return [ctx.dump(v), false];
145
132
  });
146
- const preUnmarshal = jest.fn((a) => a);
147
-
148
- const handle = vm.unwrapResult(
149
- vm.evalCode(`(class A { constructor(a) { this.a = a + 1; } })`)
150
- );
151
-
152
- const Cls = unmarshalFunction(
153
- vm,
154
- handle,
155
- marshal,
156
- unmarshal,
157
- preUnmarshal
158
- ) as any;
133
+ const preUnmarshal = vi.fn(a => a);
134
+
135
+ const handle = ctx.unwrapResult(ctx.evalCode(`(class A { constructor(a) { this.a = a + 1; } })`));
136
+
137
+ const Cls = unmarshalFunction(ctx, handle, marshal, unmarshal, preUnmarshal) as any;
159
138
  if (!Cls) throw new Error("Cls is undefined");
160
139
 
161
140
  const instance = new Cls(2);
@@ -175,32 +154,30 @@ test("class", async () => {
175
154
  expect(preUnmarshal).toBeCalledTimes(1);
176
155
  expect(preUnmarshal).toBeCalledWith(Cls, handle);
177
156
 
178
- disposables.forEach((d) => d.dispose());
157
+ disposables.forEach(d => d.dispose());
179
158
  handle.dispose();
180
- vm.dispose();
159
+ ctx.dispose();
181
160
  });
182
161
 
183
162
  test("undefined", async () => {
184
- const vm = (await getQuickJS()).createVm();
185
- const f = jest.fn();
186
-
187
- expect(unmarshalFunction(vm, vm.undefined, f, f, f)).toEqual(undefined);
188
- expect(unmarshalFunction(vm, vm.true, f, f, f)).toEqual(undefined);
189
- expect(unmarshalFunction(vm, vm.false, f, f, f)).toEqual(undefined);
190
- expect(unmarshalFunction(vm, vm.null, f, f, f)).toEqual(undefined);
191
- expect(unmarshalFunction(vm, vm.newString("hoge"), f, f, f)).toEqual(
192
- undefined
193
- );
194
- expect(unmarshalFunction(vm, vm.newNumber(-10), f, f, f)).toEqual(undefined);
195
-
196
- const obj = vm.newObject();
197
- expect(unmarshalFunction(vm, obj, f, f, f)).toEqual(undefined);
198
- const array = vm.newArray();
199
- expect(unmarshalFunction(vm, array, f, f, f)).toEqual(undefined);
163
+ const ctx = (await getQuickJS()).newContext();
164
+ const f = vi.fn();
165
+
166
+ expect(unmarshalFunction(ctx, ctx.undefined, f, f, f)).toEqual(undefined);
167
+ expect(unmarshalFunction(ctx, ctx.true, f, f, f)).toEqual(undefined);
168
+ expect(unmarshalFunction(ctx, ctx.false, f, f, f)).toEqual(undefined);
169
+ expect(unmarshalFunction(ctx, ctx.null, f, f, f)).toEqual(undefined);
170
+ expect(unmarshalFunction(ctx, ctx.newString("hoge"), f, f, f)).toEqual(undefined);
171
+ expect(unmarshalFunction(ctx, ctx.newNumber(-10), f, f, f)).toEqual(undefined);
172
+
173
+ const obj = ctx.newObject();
174
+ expect(unmarshalFunction(ctx, obj, f, f, f)).toEqual(undefined);
175
+ const array = ctx.newArray();
176
+ expect(unmarshalFunction(ctx, array, f, f, f)).toEqual(undefined);
200
177
 
201
178
  expect(f).toBeCalledTimes(0);
202
179
 
203
180
  obj.dispose();
204
181
  array.dispose();
205
- vm.dispose();
182
+ ctx.dispose();
206
183
  });
@@ -1,52 +1,43 @@
1
- import { QuickJSVm, QuickJSHandle } from "quickjs-emscripten";
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+
2
3
  import { call, mayConsumeAll } from "../vmutil";
4
+
3
5
  import unmarshalProperties from "./properties";
4
6
 
5
7
  export default function unmarshalFunction(
6
- vm: QuickJSVm,
8
+ ctx: QuickJSContext,
7
9
  handle: QuickJSHandle,
8
- // marshal returns handle and boolean indicates that the handle should be disposed after use
10
+ /** marshal returns handle and boolean indicates that the handle should be disposed after use */
9
11
  marshal: (value: unknown) => [QuickJSHandle, boolean],
10
12
  unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
11
- preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined
13
+ preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
12
14
  ): Function | undefined {
13
- if (vm.typeof(handle) !== "function") return;
15
+ if (ctx.typeof(handle) !== "function") return;
14
16
 
15
17
  const raw = function (this: any, ...args: any[]) {
16
18
  return mayConsumeAll(
17
- [marshal(this), ...args.map((a) => marshal(a))],
19
+ [marshal(this), ...args.map(a => marshal(a))],
18
20
  (thisHandle, ...argHandles) => {
19
21
  if (new.target) {
20
22
  const [instance] = unmarshal(
21
- call(
22
- vm,
23
- `(Cls, ...args) => new Cls(...args)`,
24
- thisHandle,
25
- handle,
26
- ...argHandles
27
- )
28
- );
29
- Object.defineProperties(
30
- this,
31
- Object.getOwnPropertyDescriptors(instance)
23
+ call(ctx, `(Cls, ...args) => new Cls(...args)`, thisHandle, handle, ...argHandles),
32
24
  );
25
+ Object.defineProperties(this, Object.getOwnPropertyDescriptors(instance));
33
26
  return this;
34
27
  }
35
28
 
36
- const resultHandle = vm.unwrapResult(
37
- vm.callFunction(handle, thisHandle, ...argHandles)
38
- );
29
+ const resultHandle = ctx.unwrapResult(ctx.callFunction(handle, thisHandle, ...argHandles));
39
30
 
40
31
  const [result, alreadyExists] = unmarshal(resultHandle);
41
32
  if (alreadyExists) resultHandle.dispose();
42
33
 
43
34
  return result;
44
- }
35
+ },
45
36
  );
46
37
  };
47
38
 
48
39
  const func = preUnmarshal(raw, handle) ?? raw;
49
- unmarshalProperties(vm, handle, raw, unmarshal);
40
+ unmarshalProperties(ctx, handle, raw, unmarshal);
50
41
 
51
42
  return func;
52
43
  }
@@ -1,30 +1,24 @@
1
1
  import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
2
4
  import VMMap from "../vmmap";
5
+ import { json } from "../vmutil";
6
+
3
7
  import unmarshal from ".";
4
8
 
5
9
  test("primitive, array, object", async () => {
6
- const vm = (await getQuickJS()).createVm();
7
- const marshal = jest.fn((): [QuickJSHandle, boolean] => [
8
- vm.undefined,
9
- false,
10
- ]);
11
- const map = new VMMap(vm);
12
- const find = jest.fn((h) => map.getByHandle(h));
13
- const pre = jest.fn((t: any, h: QuickJSHandle) => {
14
- map.set(t, h);
15
- return t;
16
- });
10
+ const { ctx, unmarshal, marshal, map, dispose } = await setup();
17
11
 
18
- const handle = vm.unwrapResult(
19
- vm.evalCode(`({
12
+ const handle = ctx.unwrapResult(
13
+ ctx.evalCode(`({
20
14
  hoge: "foo",
21
15
  foo: 1,
22
16
  aaa: [1, true, {}],
23
17
  nested: { aa: null, hoge: undefined },
24
18
  bbb: () => "bar"
25
- })`)
19
+ })`),
26
20
  );
27
- const target = unmarshal(handle, { vm, pre, find, marshal });
21
+ const target = unmarshal(handle);
28
22
 
29
23
  expect(target).toEqual({
30
24
  hoge: "foo",
@@ -35,119 +29,83 @@ test("primitive, array, object", async () => {
35
29
  });
36
30
  expect(map.size).toBe(5);
37
31
  expect(map.getByHandle(handle)).toBe(target);
38
- vm.getProp(handle, "aaa").consume((h) =>
39
- expect(map.getByHandle(h)).toBe(target.aaa)
40
- );
41
- vm.getProp(handle, "aaa")
42
- .consume((h) => vm.getProp(h, 2))
43
- .consume((h) => expect(map.getByHandle(h)).toBe(target.aaa[2]));
44
- vm.getProp(handle, "nested").consume((h) =>
45
- expect(map.getByHandle(h)).toBe(target.nested)
46
- );
47
- vm.getProp(handle, "bbb").consume((h) =>
48
- expect(map.getByHandle(h)).toBe(target.bbb)
49
- );
32
+ ctx.getProp(handle, "aaa").consume(h => expect(map.getByHandle(h)).toBe(target.aaa));
33
+ ctx
34
+ .getProp(handle, "aaa")
35
+ .consume(h => ctx.getProp(h, 2))
36
+ .consume(h => expect(map.getByHandle(h)).toBe(target.aaa[2]));
37
+ ctx.getProp(handle, "nested").consume(h => expect(map.getByHandle(h)).toBe(target.nested));
38
+ ctx.getProp(handle, "bbb").consume(h => expect(map.getByHandle(h)).toBe(target.bbb));
50
39
 
51
40
  expect(marshal).toBeCalledTimes(0);
52
41
  expect(target.bbb()).toBe("bar");
53
42
  expect(marshal).toBeCalledTimes(1);
54
43
  expect(marshal).toBeCalledWith(target); // thisArg of target.bbb()
55
44
 
56
- handle.dispose();
57
- map.dispose();
58
- vm.dispose();
45
+ dispose();
59
46
  });
60
47
 
61
48
  test("object with symbol key", async () => {
62
- const vm = (await getQuickJS()).createVm();
63
- const map = new VMMap(vm);
64
- const pre = (t: any, h: QuickJSHandle) => {
65
- map.set(t, h);
66
- return t;
67
- };
49
+ const { ctx, unmarshal, dispose } = await setup();
68
50
 
69
- const handle = vm.unwrapResult(
70
- vm.evalCode(`({
51
+ const handle = ctx.unwrapResult(
52
+ ctx.evalCode(`({
71
53
  hoge: "foo",
72
54
  [Symbol("a")]: "bar"
73
- })`)
55
+ })`),
74
56
  );
75
- const target = unmarshal(handle, {
76
- vm,
77
- pre,
78
- find: () => undefined,
79
- marshal: () => [vm.undefined, false],
80
- });
57
+ const target = unmarshal(handle);
81
58
 
82
59
  expect(target.hoge).toBe("foo");
83
60
  expect(target[Object.getOwnPropertySymbols(target)[0]]).toBe("bar");
84
61
 
85
- handle.dispose();
86
- map.dispose();
87
- vm.dispose();
62
+ dispose();
88
63
  });
89
64
 
90
- test("func", async () => {
91
- const vm = (await getQuickJS()).createVm();
92
- const jsonParse = vm.unwrapResult(vm.evalCode(`JSON.parse`));
93
- const disposables: QuickJSHandle[] = [];
94
- const marshal = jest.fn((t: unknown): [QuickJSHandle, boolean] => {
95
- const h =
96
- t === undefined
97
- ? vm.undefined
98
- : vm.unwrapResult(
99
- vm.callFunction(
100
- jsonParse,
101
- vm.undefined,
102
- vm.newString(JSON.stringify(t))
103
- )
104
- );
105
- const ty = vm.typeof(h);
106
- if (ty === "object" || ty === "function") disposables.push(h);
107
- return [h, false];
108
- });
65
+ test("function", async () => {
66
+ const { ctx, unmarshal, marshal, map, dispose } = await setup();
109
67
 
110
- const handle = vm.unwrapResult(
111
- vm.evalCode(`(function(a) { return a.a + "!"; })`)
112
- );
113
- const map = new VMMap(vm);
114
- const find = jest.fn((h) => map.getByHandle(h));
115
- const pre = jest.fn((t: any, h: QuickJSHandle) => {
116
- map.set(t, h);
117
- return t;
118
- });
119
- const func = unmarshal(handle, { vm, find, pre, marshal });
68
+ const handle = ctx.unwrapResult(ctx.evalCode(`(function(a) { return a.a + "!"; })`));
69
+ const func = unmarshal(handle);
120
70
  const arg = { a: "hoge" };
121
71
  expect(func(arg)).toBe("hoge!");
122
72
  expect(marshal).toBeCalledTimes(2);
123
73
  expect(marshal).toBeCalledWith(undefined); // this
124
74
  expect(marshal).toBeCalledWith(arg); // arg
125
- expect(map.size).toBe(2);
75
+ expect(map.size).toBe(3);
126
76
  expect(map.getByHandle(handle)).toBe(func);
77
+ expect(map.has(func)).toBe(true);
127
78
  expect(map.has(func.prototype)).toBe(true);
79
+ expect(map.has(arg)).toBe(true);
128
80
 
129
- map.dispose();
130
- disposables.forEach((d) => d.dispose());
131
- jsonParse.dispose();
132
- vm.dispose();
81
+ dispose();
82
+ });
83
+
84
+ test("promise", async () => {
85
+ const { ctx, unmarshal, dispose } = await setup();
86
+
87
+ const deferred = ctx.newPromise();
88
+ const promise = unmarshal(deferred.handle);
89
+ deferred.resolve(ctx.newString("resolved!"));
90
+ ctx.runtime.executePendingJobs();
91
+ await expect(promise).resolves.toBe("resolved!");
92
+
93
+ const deferred2 = ctx.newPromise();
94
+ const promise2 = unmarshal(deferred2.handle);
95
+ deferred2.reject(ctx.newString("rejected!"));
96
+ ctx.runtime.executePendingJobs();
97
+ await expect(promise2).rejects.toBe("rejected!");
98
+
99
+ deferred.dispose();
100
+ deferred2.dispose();
101
+ dispose();
133
102
  });
134
103
 
135
104
  test("class", async () => {
136
- const vm = (await getQuickJS()).createVm();
137
- const jsonParse = vm.unwrapResult(vm.evalCode(`JSON.parse`));
138
- const disposables: QuickJSHandle[] = [];
139
- const map = new VMMap(vm);
140
- const marshal = jest.fn((t: unknown): [QuickJSHandle, boolean] => {
141
- const h = vm.unwrapResult(
142
- vm.callFunction(jsonParse, vm.undefined, vm.newString(JSON.stringify(t)))
143
- );
144
- const ty = vm.typeof(h);
145
- if (ty === "object" || ty === "function") disposables.push(h);
146
- return [h, false];
147
- });
105
+ const { ctx, unmarshal, dispose } = await setup();
148
106
 
149
- const handle = vm.unwrapResult(
150
- vm.evalCode(`{
107
+ const handle = ctx.unwrapResult(
108
+ ctx.evalCode(`{
151
109
  class Cls {
152
110
  static hoge = "foo";
153
111
 
@@ -158,25 +116,72 @@ test("class", async () => {
158
116
  Cls.foo = new Cls(1);
159
117
 
160
118
  Cls
161
- }`)
119
+ }`),
162
120
  );
163
- const find = jest.fn((h) => map.getByHandle(h));
164
- const pre = jest.fn((t: any, h: QuickJSHandle) => {
165
- map.set(t, h);
166
- return t;
167
- });
168
- const Cls = unmarshal(handle, { vm, find, pre, marshal });
121
+ const Cls = unmarshal(handle);
169
122
 
170
123
  expect(Cls.hoge).toBe("foo");
171
- expect(Cls.foo instanceof Cls).toBe(true);
124
+ expect(Cls.foo).toBeInstanceOf(Cls);
172
125
  expect(Cls.foo.foo).toBe(3);
173
126
  const cls = new Cls(2);
174
- expect(cls instanceof Cls).toBe(true);
127
+ expect(cls).toBeInstanceOf(Cls);
175
128
  expect(cls.foo).toBe(4);
176
129
 
177
130
  handle.dispose();
178
- map.dispose();
179
- disposables.forEach((d) => d.dispose());
180
- jsonParse.dispose();
181
- vm.dispose();
131
+ dispose();
132
+ });
133
+
134
+ test("date", async () => {
135
+ const { ctx, unmarshal, dispose } = await setup();
136
+
137
+ const handle = ctx.unwrapResult(ctx.evalCode("new Date(2022, 7, 26)"));
138
+ const date = unmarshal(handle);
139
+ const expected = new Date(2022, 7, 26);
140
+
141
+ expect(date).toBeInstanceOf(Date);
142
+ expect(date.getTime()).toBe(expected.getTime());
143
+
144
+ handle.dispose();
145
+ dispose();
182
146
  });
147
+
148
+ const setup = async () => {
149
+ const ctx = (await getQuickJS()).newContext();
150
+ const map = new VMMap(ctx);
151
+ const disposables: QuickJSHandle[] = [];
152
+ const marshal = vi.fn((target: unknown): [QuickJSHandle, boolean] => {
153
+ const handle = map.get(target);
154
+ if (handle) return [handle, false];
155
+
156
+ const handle2 =
157
+ typeof target === "function"
158
+ ? ctx.newFunction(target.name, (...handles) => {
159
+ target(...handles.map(h => ctx.dump(h)));
160
+ })
161
+ : json(ctx, target);
162
+ const ty = ctx.typeof(handle2);
163
+ if (ty === "object" || ty === "function") map.set(target, handle2);
164
+ return [handle2, false];
165
+ });
166
+
167
+ return {
168
+ ctx,
169
+ map,
170
+ unmarshal: (handle: QuickJSHandle) =>
171
+ unmarshal(handle, {
172
+ find: h => map.getByHandle(h),
173
+ marshal,
174
+ pre: (t, h) => {
175
+ map.set(t, h);
176
+ return t;
177
+ },
178
+ ctx: ctx,
179
+ }),
180
+ marshal,
181
+ dispose: () => {
182
+ disposables.forEach(d => d.dispose());
183
+ map.dispose();
184
+ ctx.dispose();
185
+ },
186
+ };
187
+ };
@@ -1,15 +1,18 @@
1
- import { QuickJSVm, QuickJSHandle } from "quickjs-emscripten";
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+
3
+ import unmarshalCustom, { defaultCustom } from "./custom";
2
4
  import unmarshalFunction from "./function";
3
5
  import unmarshalObject from "./object";
4
6
  import unmarshalPrimitive from "./primitive";
5
- import unmarshalSymbol from "./symbol";
7
+ import unmarshalPromise from "./promise";
6
8
 
7
9
  export type Options = {
8
- vm: QuickJSVm;
9
- // marshal returns handle and boolean indicates that the handle should be disposed after use
10
+ ctx: QuickJSContext;
11
+ /** marshal returns handle and boolean indicates that the handle should be disposed after use */
10
12
  marshal: (target: unknown) => [QuickJSHandle, boolean];
11
13
  find: (handle: QuickJSHandle) => unknown | undefined;
12
- pre: <T>(target: T, handle: QuickJSHandle) => T | undefined;
14
+ pre: <T = unknown>(target: T, handle: QuickJSHandle) => T | undefined;
15
+ custom?: Iterable<(obj: QuickJSHandle, ctx: QuickJSContext) => any>;
13
16
  };
14
17
 
15
18
  export function unmarshal(handle: QuickJSHandle, options: Options): any {
@@ -17,14 +20,11 @@ export function unmarshal(handle: QuickJSHandle, options: Options): any {
17
20
  return result;
18
21
  }
19
22
 
20
- function unmarshalInner(
21
- handle: QuickJSHandle,
22
- options: Options
23
- ): [any, boolean] {
24
- const { vm, marshal, find, pre } = options;
23
+ function unmarshalInner(handle: QuickJSHandle, options: Options): [any, boolean] {
24
+ const { ctx, marshal, find, pre } = options;
25
25
 
26
26
  {
27
- const [target, ok] = unmarshalPrimitive(vm, handle);
27
+ const [target, ok] = unmarshalPrimitive(ctx, handle);
28
28
  if (ok) return [target, false];
29
29
  }
30
30
 
@@ -38,9 +38,10 @@ function unmarshalInner(
38
38
  const unmarshal2 = (h: QuickJSHandle) => unmarshalInner(h, options);
39
39
 
40
40
  const result =
41
- unmarshalSymbol(vm, handle, pre) ??
42
- unmarshalFunction(vm, handle, marshal, unmarshal2, pre) ??
43
- unmarshalObject(vm, handle, unmarshal2, pre);
41
+ unmarshalCustom(ctx, handle, pre, [...defaultCustom, ...(options.custom ?? [])]) ??
42
+ unmarshalPromise(ctx, handle, marshal, pre) ??
43
+ unmarshalFunction(ctx, handle, marshal, unmarshal2, pre) ??
44
+ unmarshalObject(ctx, handle, unmarshal2, pre);
44
45
 
45
46
  return [result, false];
46
47
  }