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,123 +1,229 @@
1
- import { getQuickJS } from "quickjs-emscripten";
1
+ import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
2
4
  import {
5
+ fn,
3
6
  call,
4
7
  consumeAll,
5
8
  eq,
6
9
  instanceOf,
7
10
  isHandleObject,
8
- send,
11
+ json,
12
+ mayConsume,
13
+ mayConsumeAll,
14
+ handleFrom,
9
15
  } from "./vmutil";
10
16
 
17
+ test("fn", async () => {
18
+ const ctx = (await getQuickJS()).newContext();
19
+
20
+ const f = fn(ctx, "(a, b) => a + b");
21
+ expect(ctx.getNumber(f(undefined, ctx.newNumber(1), ctx.newNumber(2)))).toBe(
22
+ 3
23
+ );
24
+
25
+ const obj = ctx.newObject();
26
+ ctx.setProp(obj, "a", ctx.newNumber(2));
27
+ const f2 = fn(ctx, "(function() { return this.a + 1; })");
28
+ expect(ctx.getNumber(f2(obj))).toBe(3);
29
+
30
+ obj.dispose();
31
+ expect(f.alive).toBe(true);
32
+ expect(f2.alive).toBe(true);
33
+ f2.dispose();
34
+ f.dispose();
35
+ expect(f.alive).toBe(false);
36
+ expect(f2.alive).toBe(false);
37
+ ctx.dispose();
38
+ });
39
+
11
40
  test("call", async () => {
12
- const quickjs = await getQuickJS();
13
- const vm = quickjs.createVm();
41
+ const ctx = (await getQuickJS()).newContext();
14
42
 
15
43
  expect(
16
- vm.getNumber(
17
- call(vm, "(a, b) => a + b", undefined, vm.newNumber(1), vm.newNumber(2))
44
+ ctx.getNumber(
45
+ call(
46
+ ctx,
47
+ "(a, b) => a + b",
48
+ undefined,
49
+ ctx.newNumber(1),
50
+ ctx.newNumber(2)
51
+ )
18
52
  )
19
53
  ).toBe(3);
20
54
 
21
- const obj = vm.newObject();
22
- vm.setProp(obj, "a", vm.newNumber(2));
55
+ const obj = ctx.newObject();
56
+ ctx.setProp(obj, "a", ctx.newNumber(2));
23
57
  expect(
24
- vm.getNumber(call(vm, "(function() { return this.a + 1; })", obj))
58
+ ctx.getNumber(call(ctx, "(function() { return this.a + 1; })", obj))
25
59
  ).toBe(3);
26
60
 
27
61
  obj.dispose();
28
- vm.dispose();
62
+ ctx.dispose();
29
63
  });
30
64
 
31
65
  test("eq", async () => {
32
- const quickjs = await getQuickJS();
33
- const vm = quickjs.createVm();
66
+ const ctx = (await getQuickJS()).newContext();
34
67
 
35
- const math1 = vm.unwrapResult(vm.evalCode("Math"));
36
- const math2 = vm.unwrapResult(vm.evalCode("Math"));
37
- const obj = vm.newObject();
68
+ const math1 = ctx.unwrapResult(ctx.evalCode("Math"));
69
+ const math2 = ctx.unwrapResult(ctx.evalCode("Math"));
70
+ const obj = ctx.newObject();
38
71
  expect(math1 === math2).toBe(false);
39
- expect(eq(vm, math1, math2)).toBe(true);
40
- expect(eq(vm, math1, obj)).toBe(false);
72
+ expect(eq(ctx, math1, math2)).toBe(true);
73
+ expect(eq(ctx, math1, obj)).toBe(false);
41
74
 
42
75
  math1.dispose();
43
76
  math2.dispose();
44
77
  obj.dispose();
45
- vm.dispose();
78
+ ctx.dispose();
46
79
  });
47
80
 
48
81
  test("instanceOf", async () => {
49
- const quickjs = await getQuickJS();
50
- const vm = quickjs.createVm();
82
+ const ctx = (await getQuickJS()).newContext();
51
83
 
52
- const pr = vm.unwrapResult(vm.evalCode("Promise"));
53
- const func = vm.unwrapResult(vm.evalCode("(function() {})"));
54
- const p = vm.unwrapResult(vm.evalCode("Promise.resolve()"));
55
- expect(instanceOf(vm, p, pr)).toBe(true);
56
- expect(instanceOf(vm, p, func)).toBe(false);
84
+ const pr = ctx.unwrapResult(ctx.evalCode("Promise"));
85
+ const func = ctx.unwrapResult(ctx.evalCode("(function() {})"));
86
+ const p = ctx.unwrapResult(ctx.evalCode("Promise.resolve()"));
87
+ expect(instanceOf(ctx, p, pr)).toBe(true);
88
+ expect(instanceOf(ctx, p, func)).toBe(false);
57
89
 
58
90
  p.dispose();
59
91
  pr.dispose();
60
92
  func.dispose();
61
- vm.dispose();
93
+ ctx.dispose();
62
94
  });
63
95
 
64
96
  test("isHandleObject", async () => {
65
- const quickjs = await getQuickJS();
66
- const vm = quickjs.createVm();
67
-
68
- const obj = vm.newObject();
69
- expect(isHandleObject(vm, obj)).toBe(true);
70
- const func = vm.newFunction("", () => {});
71
- expect(isHandleObject(vm, func)).toBe(true);
72
- const array = vm.newArray();
73
- expect(isHandleObject(vm, array)).toBe(true);
74
- const num = vm.newNumber(NaN);
75
- expect(isHandleObject(vm, num)).toBe(false);
97
+ const ctx = (await getQuickJS()).newContext();
98
+
99
+ const obj = ctx.newObject();
100
+ expect(isHandleObject(ctx, obj)).toBe(true);
101
+ const func = ctx.newFunction("", () => {});
102
+ expect(isHandleObject(ctx, func)).toBe(true);
103
+ const array = ctx.newArray();
104
+ expect(isHandleObject(ctx, array)).toBe(true);
105
+ const num = ctx.newNumber(NaN);
106
+ expect(isHandleObject(ctx, num)).toBe(false);
76
107
 
77
108
  obj.dispose();
78
109
  func.dispose();
79
110
  array.dispose();
80
- vm.dispose();
111
+ ctx.dispose();
81
112
  });
82
113
 
83
- test("send", async () => {
84
- const quickjs = await getQuickJS();
85
- const vm = quickjs.createVm();
114
+ test("json", async () => {
115
+ const ctx = (await getQuickJS()).newContext();
86
116
 
87
- const handle = send(vm, {
117
+ const handle = json(ctx, {
88
118
  hoge: { foo: ["bar"] },
89
119
  });
90
120
  expect(
91
- vm.dump(call(vm, `a => a.hoge.foo[0] === "bar"`, undefined, handle))
121
+ ctx.dump(call(ctx, `a => a.hoge.foo[0] === "bar"`, undefined, handle))
92
122
  ).toBe(true);
93
- expect(vm.typeof(send(vm, undefined))).toBe("undefined");
123
+ expect(ctx.typeof(json(ctx, undefined))).toBe("undefined");
94
124
 
95
125
  handle.dispose();
96
- vm.dispose();
126
+ ctx.dispose();
97
127
  });
98
128
 
99
129
  test("consumeAll", async () => {
100
- const quickjs = await getQuickJS();
101
- const vm = quickjs.createVm();
130
+ const ctx = (await getQuickJS()).newContext();
102
131
 
103
132
  const o = {};
104
133
 
105
- const handles = [vm.newObject(), vm.newObject()];
134
+ const handles = [ctx.newObject(), ctx.newObject()];
106
135
  expect(
107
136
  consumeAll(
108
137
  handles,
109
- jest.fn(_ => o)
138
+ vi.fn(() => o)
110
139
  )
111
140
  ).toBe(o);
112
- expect(handles.every(h => !h.alive)).toBe(true);
141
+ expect(handles.every((h) => !h.alive)).toBe(true);
113
142
 
114
- const handles2 = [vm.newObject(), vm.newObject()];
143
+ const handles2 = [ctx.newObject(), ctx.newObject()];
115
144
  expect(() =>
116
145
  consumeAll(handles2, () => {
117
146
  throw new Error("qes error");
118
147
  })
119
148
  ).toThrow("qes error");
120
- expect(handles2.every(h => !h.alive)).toBe(true);
149
+ expect(handles2.every((h) => !h.alive)).toBe(true);
150
+
151
+ ctx.dispose();
152
+ });
153
+
154
+ test("mayConsume", async () => {
155
+ const ctx = (await getQuickJS()).newContext();
156
+
157
+ const o = {};
158
+
159
+ const handle = ctx.newArray();
160
+ expect(
161
+ mayConsume(
162
+ [handle, false],
163
+ vi.fn(() => o)
164
+ )
165
+ ).toBe(o);
166
+ expect(handle.alive).toBe(true);
167
+
168
+ mayConsume([handle, true], () => {});
169
+ expect(handle.alive).toBe(false);
170
+
171
+ const handle2 = ctx.newArray();
172
+ expect(() =>
173
+ mayConsume([handle2, true], () => {
174
+ throw new Error("qes error");
175
+ })
176
+ ).toThrow("qes error");
177
+ expect(handle.alive).toBe(false);
178
+
179
+ ctx.dispose();
180
+ });
181
+
182
+ test("mayConsumeAll", async () => {
183
+ const ctx = (await getQuickJS()).newContext();
184
+
185
+ const o = {};
186
+
187
+ const handles: [QuickJSHandle, boolean][] = [
188
+ [ctx.newObject(), false],
189
+ [ctx.newObject(), true],
190
+ ];
191
+ expect(
192
+ mayConsumeAll(
193
+ handles,
194
+ vi.fn((..._: any[]) => o)
195
+ )
196
+ ).toBe(o);
197
+ expect(handles[0][0].alive).toBe(true);
198
+ expect(handles[1][0].alive).toBe(false);
199
+
200
+ const handles2: [QuickJSHandle, boolean][] = [
201
+ [ctx.newObject(), false],
202
+ [ctx.newObject(), true],
203
+ ];
204
+ expect(() =>
205
+ mayConsumeAll(handles2, (..._args) => {
206
+ throw new Error("qes error");
207
+ })
208
+ ).toThrow("qes error");
209
+ expect(handles2[0][0].alive).toBe(true);
210
+ expect(handles2[1][0].alive).toBe(false);
121
211
 
122
- vm.dispose();
212
+ handles[0][0].dispose();
213
+ handles2[0][0].dispose();
214
+ ctx.dispose();
215
+ });
216
+
217
+ test("handleFrom", async () => {
218
+ const ctx = (await getQuickJS()).newContext();
219
+
220
+ const handle = ctx.newObject();
221
+ const promise = ctx.newPromise();
222
+
223
+ expect(handleFrom(handle) === handle).toBe(true);
224
+ expect(handleFrom(promise) === promise.handle).toBe(true);
225
+
226
+ handle.dispose();
227
+ promise.dispose();
228
+ ctx.dispose();
123
229
  });
package/src/vmutil.ts CHANGED
@@ -1,46 +1,80 @@
1
- import { QuickJSVm, QuickJSHandle } from "quickjs-emscripten";
1
+ import type {
2
+ Disposable,
3
+ QuickJSContext,
4
+ QuickJSHandle,
5
+ QuickJSDeferredPromise,
6
+ } from "quickjs-emscripten";
7
+
8
+ export function fn(
9
+ ctx: QuickJSContext,
10
+ code: string
11
+ ): ((
12
+ thisArg: QuickJSHandle | undefined,
13
+ ...args: QuickJSHandle[]
14
+ ) => QuickJSHandle) &
15
+ Disposable {
16
+ const handle = ctx.unwrapResult(ctx.evalCode(code));
17
+ const f = (
18
+ thisArg: QuickJSHandle | undefined,
19
+ ...args: QuickJSHandle[]
20
+ ): any => {
21
+ return ctx.unwrapResult(
22
+ ctx.callFunction(handle, thisArg ?? ctx.undefined, ...args)
23
+ );
24
+ };
25
+ f.dispose = () => handle.dispose();
26
+ f.alive = true;
27
+ Object.defineProperty(f, "alive", {
28
+ get: () => handle.alive,
29
+ });
30
+ return f;
31
+ }
2
32
 
3
33
  export function call(
4
- vm: QuickJSVm,
34
+ ctx: QuickJSContext,
5
35
  code: string,
6
36
  thisArg?: QuickJSHandle,
7
37
  ...args: QuickJSHandle[]
8
38
  ): QuickJSHandle {
9
- return vm.unwrapResult(vm.evalCode(code)).consume(f => {
10
- if (typeof thisArg === "undefined" && args.length === 0) return f;
11
- return vm.unwrapResult(
12
- vm.callFunction(f, thisArg ?? vm.undefined, ...args)
13
- );
14
- });
39
+ const f = fn(ctx, code);
40
+ try {
41
+ return f(thisArg, ...args);
42
+ } finally {
43
+ f.dispose();
44
+ }
15
45
  }
16
46
 
17
- export function eq(vm: QuickJSVm, a: QuickJSHandle, b: QuickJSHandle): boolean {
18
- return vm.dump(call(vm, "Object.is", undefined, a, b));
47
+ export function eq(
48
+ ctx: QuickJSContext,
49
+ a: QuickJSHandle,
50
+ b: QuickJSHandle
51
+ ): boolean {
52
+ return ctx.dump(call(ctx, "Object.is", undefined, a, b));
19
53
  }
20
54
 
21
55
  export function instanceOf(
22
- vm: QuickJSVm,
56
+ ctx: QuickJSContext,
23
57
  a: QuickJSHandle,
24
58
  b: QuickJSHandle
25
59
  ): boolean {
26
- return vm.dump(call(vm, "(a, b) => a instanceof b", undefined, a, b));
60
+ return ctx.dump(call(ctx, "(a, b) => a instanceof b", undefined, a, b));
27
61
  }
28
62
 
29
- export function isHandleObject(vm: QuickJSVm, a: QuickJSHandle): boolean {
30
- return vm.dump(
63
+ export function isHandleObject(ctx: QuickJSContext, h: QuickJSHandle): boolean {
64
+ return ctx.dump(
31
65
  call(
32
- vm,
66
+ ctx,
33
67
  `a => typeof a === "object" && a !== null || typeof a === "function"`,
34
68
  undefined,
35
- a
69
+ h
36
70
  )
37
71
  );
38
72
  }
39
73
 
40
- export function send(vm: QuickJSVm, target: any): QuickJSHandle {
74
+ export function json(ctx: QuickJSContext, target: any): QuickJSHandle {
41
75
  const json = JSON.stringify(target);
42
- if (!json) return vm.undefined;
43
- return call(vm, `JSON.parse`, undefined, vm.newString(json));
76
+ if (!json) return ctx.undefined;
77
+ return call(ctx, `JSON.parse`, undefined, ctx.newString(json));
44
78
  }
45
79
 
46
80
  export function consumeAll<T extends QuickJSHandle[], K>(
@@ -55,3 +89,41 @@ export function consumeAll<T extends QuickJSHandle[], K>(
55
89
  }
56
90
  }
57
91
  }
92
+
93
+ export function mayConsume<T>(
94
+ [handle, shouldBeDisposed]: [QuickJSHandle, boolean],
95
+ fn: (h: QuickJSHandle) => T
96
+ ) {
97
+ try {
98
+ return fn(handle);
99
+ } finally {
100
+ if (shouldBeDisposed) {
101
+ handle.dispose();
102
+ }
103
+ }
104
+ }
105
+
106
+ export function mayConsumeAll<T, H extends QuickJSHandle[]>(
107
+ handles: { [P in keyof H]: [QuickJSHandle, boolean] },
108
+ fn: (...args: H) => T
109
+ ) {
110
+ try {
111
+ return fn(...(handles.map((h) => h[0]) as H));
112
+ } finally {
113
+ for (const [handle, shouldBeDisposed] of handles) {
114
+ if (shouldBeDisposed) {
115
+ handle.dispose();
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ function isQuickJSDeferredPromise(d: Disposable): d is QuickJSDeferredPromise {
122
+ return "handle" in d;
123
+ }
124
+
125
+ export function handleFrom(
126
+ d: QuickJSDeferredPromise | QuickJSHandle
127
+ ): QuickJSHandle {
128
+ return isQuickJSDeferredPromise(d) ? d.handle : d;
129
+ }