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
package/src/index.ts CHANGED
@@ -1,23 +1,18 @@
1
- import { QuickJSHandle, QuickJSVm } from "quickjs-emscripten";
2
- import {
1
+ import type {
2
+ QuickJSDeferredPromise,
3
+ QuickJSHandle,
4
+ QuickJSContext,
3
5
  SuccessOrFail,
4
6
  VmCallResult,
5
- } from "quickjs-emscripten/dist/vm-interface";
7
+ } from "quickjs-emscripten";
6
8
 
7
- import VMMap from "./vmmap";
9
+ import { defaultRegisteredObjects } from "./default";
8
10
  import marshal from "./marshal";
9
11
  import unmarshal from "./unmarshal";
10
- import { wrap, wrapHandle, unwrap, unwrapHandle, Wrapped } from "./wrapper";
11
12
  import { complexity, isES2015Class, isObject, walkObject } from "./util";
12
- import {
13
- call,
14
- eq,
15
- isHandleObject,
16
- json,
17
- consumeAll,
18
- mayConsume,
19
- } from "./vmutil";
20
- import { defaultRegisteredObjects } from "./default";
13
+ import VMMap from "./vmmap";
14
+ import { call, eq, isHandleObject, json, consumeAll, mayConsume, handleFrom } from "./vmutil";
15
+ import { wrap, wrapHandle, unwrap, unwrapHandle, Wrapped } from "./wrapper";
21
16
 
22
17
  export {
23
18
  VMMap,
@@ -38,18 +33,28 @@ export {
38
33
  export type Options = {
39
34
  /** A callback that returns a boolean value that determines whether an object is marshalled or not. If false, no marshaling will be done and undefined will be passed to the QuickJS VM, otherwise marshaling will be done. By default, all objects will be marshalled. */
40
35
  isMarshalable?: boolean | "json" | ((target: any) => boolean | "json");
41
- /** You can pre-register a pair of objects that will be considered the same between the host and the QuickJS VM. This will be used automatically during the conversion. By default, it will be registered automatically with `defaultRegisteredObjects`.
36
+ /** Pre-registered pairs of objects that will be considered the same between the host and the QuickJS VM. This will be used automatically during the conversion. By default, it will be registered automatically with `defaultRegisteredObjects`.
42
37
  *
43
38
  * Instead of a string, you can also pass a QuickJSHandle directly. In that case, however, you have to dispose of them manually when destroying the VM.
44
39
  */
45
40
  registeredObjects?: Iterable<[any, QuickJSHandle | string]>;
41
+ /** Register functions to convert an object to a QuickJS handle. */
42
+ customMarshaller?: Iterable<(target: unknown, ctx: QuickJSContext) => QuickJSHandle | undefined>;
43
+ /** Register functions to convert a QuickJS handle to an object. */
44
+ customUnmarshaller?: Iterable<(target: QuickJSHandle, ctx: QuickJSContext) => any>;
45
+ /** A callback that returns a boolean value that determines whether an object is wrappable by proxies. If returns false, note that the object cannot be synchronized between the host and the QuickJS even if arena.sync is used. */
46
+ isWrappable?: (target: any) => boolean;
47
+ /** A callback that returns a boolean value that determines whether an QuickJS handle is wrappable by proxies. If returns false, note that the handle cannot be synchronized between the host and the QuickJS even if arena.sync is used. */
48
+ isHandleWrappable?: (handle: QuickJSHandle, ctx: QuickJSContext) => boolean;
49
+ /** Compatibility with quickjs-emscripten prior to v0.15. Inject code for compatibility into context at Arena class initialization time. */
50
+ compat?: boolean;
46
51
  };
47
52
 
48
53
  /**
49
54
  * The Arena class manages all generated handles at once by quickjs-emscripten and automatically converts objects between the host and the QuickJS VM.
50
55
  */
51
56
  export class Arena {
52
- vm: QuickJSVm;
57
+ context: QuickJSContext;
53
58
  _map: VMMap;
54
59
  _registeredMap: VMMap;
55
60
  _registeredMapDispose: Set<any> = new Set();
@@ -59,13 +64,21 @@ export class Arena {
59
64
  _symbolHandle: QuickJSHandle;
60
65
  _options?: Options;
61
66
 
62
- /** Constructs a new Arena instance. It requires a quickjs-emscripten VM initialized with `quickjs.createVM()`. */
63
- constructor(vm: QuickJSVm, options?: Options) {
64
- this.vm = vm;
67
+ /** Constructs a new Arena instance. It requires a quickjs-emscripten context initialized with `quickjs.newContext()`. */
68
+ constructor(ctx: QuickJSContext, options?: Options) {
69
+ if (options?.compat && !("runtime" in ctx)) {
70
+ (ctx as any).runtime = {
71
+ hasPendingJob: () => (ctx as any).hasPendingJob(),
72
+ executePendingJobs: (maxJobsToExecute?: number | void) =>
73
+ (ctx as any).executePendingJobs(maxJobsToExecute),
74
+ };
75
+ }
76
+
77
+ this.context = ctx;
65
78
  this._options = options;
66
- this._symbolHandle = vm.unwrapResult(vm.evalCode(`Symbol()`));
67
- this._map = new VMMap(vm);
68
- this._registeredMap = new VMMap(vm);
79
+ this._symbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
80
+ this._map = new VMMap(ctx);
81
+ this._registeredMap = new VMMap(ctx);
69
82
  this.registerAll(options?.registeredObjects ?? defaultRegisteredObjects);
70
83
  }
71
84
 
@@ -82,7 +95,7 @@ export class Arena {
82
95
  * Evaluate JS code in the VM and get the result as an object on the host side. It also converts and re-throws error objects when an error is thrown during evaluation.
83
96
  */
84
97
  evalCode<T = any>(code: string): T {
85
- const handle = this.vm.evalCode(code);
98
+ const handle = this.context.evalCode(code);
86
99
  return this._unwrapResultAndUnmarshal(handle);
87
100
  }
88
101
 
@@ -90,7 +103,7 @@ export class Arena {
90
103
  * Almost same as `vm.executePendingJobs()`, but it converts and re-throws error objects when an error is thrown during evaluation.
91
104
  */
92
105
  executePendingJobs(maxJobsToExecute?: number): number {
93
- const result = this.vm.executePendingJobs(maxJobsToExecute);
106
+ const result = this.context.runtime.executePendingJobs(maxJobsToExecute);
94
107
  if ("value" in result) {
95
108
  return result.value;
96
109
  }
@@ -105,8 +118,8 @@ export class Arena {
105
118
  */
106
119
  expose(obj: { [k: string]: any }) {
107
120
  for (const [key, value] of Object.entries(obj)) {
108
- mayConsume(this._marshal(value), (handle) => {
109
- this.vm.setProp(this.vm.global, key, handle);
121
+ mayConsume(this._marshal(value), handle => {
122
+ this.context.setProp(this.context.global, key, handle);
110
123
  });
111
124
  }
112
125
  }
@@ -119,8 +132,9 @@ export class Arena {
119
132
  sync<T>(target: T): T {
120
133
  const wrapped = this._wrap(target);
121
134
  if (typeof wrapped === "undefined") return target;
122
- walkObject(wrapped, (v) => {
123
- this._sync.add(this._unwrap(v));
135
+ walkObject(wrapped, v => {
136
+ const u = this._unwrap(v);
137
+ this._sync.add(u);
124
138
  });
125
139
  return wrapped;
126
140
  }
@@ -134,9 +148,9 @@ export class Arena {
134
148
  if (this._registeredMap.has(target)) return;
135
149
  const handle =
136
150
  typeof handleOrCode === "string"
137
- ? this._unwrapResult(this.vm.evalCode(handleOrCode))
151
+ ? this._unwrapResult(this.context.evalCode(handleOrCode))
138
152
  : handleOrCode;
139
- if (eq(this.vm, handle, this.vm.undefined)) return;
153
+ if (eq(this.context, handle, this.context.undefined)) return;
140
154
  if (typeof handleOrCode === "string") {
141
155
  this._registeredMapDispose.add(target);
142
156
  }
@@ -156,10 +170,7 @@ export class Arena {
156
170
  * Unregister a pair of objects that were registered with `registeredObjects` option and `register` method.
157
171
  */
158
172
  unregister(target: any, dispose?: boolean) {
159
- this._registeredMap.delete(
160
- target,
161
- this._registeredMapDispose.has(target) || dispose
162
- );
173
+ this._registeredMap.delete(target, this._registeredMapDispose.has(target) || dispose);
163
174
  this._registeredMapDispose.delete(target);
164
175
  }
165
176
 
@@ -174,7 +185,8 @@ export class Arena {
174
185
 
175
186
  startSync(target: any) {
176
187
  if (!isObject(target)) return;
177
- this._sync.add(this._unwrap(target));
188
+ const u = this._unwrap(target);
189
+ this._sync.add(u);
178
190
  }
179
191
 
180
192
  endSync(target: any) {
@@ -188,13 +200,9 @@ export class Arena {
188
200
  throw this._unwrapIfNotSynced(result.error.consume(this._unmarshal));
189
201
  }
190
202
 
191
- _unwrapResultAndUnmarshal(
192
- result: VmCallResult<QuickJSHandle> | undefined
193
- ): any {
203
+ _unwrapResultAndUnmarshal(result: VmCallResult<QuickJSHandle> | undefined): any {
194
204
  if (!result) return;
195
- return this._unwrapIfNotSynced(
196
- this._unwrapResult(result).consume(this._unmarshal)
197
- );
205
+ return this._unwrapIfNotSynced(this._unwrapResult(result).consume(this._unmarshal));
198
206
  }
199
207
 
200
208
  _isMarshalable = (t: unknown): boolean | "json" => {
@@ -214,18 +222,14 @@ export class Arena {
214
222
 
215
223
  _marshalPre = (
216
224
  t: unknown,
217
- h: QuickJSHandle,
218
- mode: true | "json" | undefined
225
+ h: QuickJSHandle | QuickJSDeferredPromise,
226
+ mode: true | "json" | undefined,
219
227
  ): Wrapped<QuickJSHandle> | undefined => {
220
228
  if (mode === "json") return;
221
- return this._register(t, h, this._map)?.[1];
229
+ return this._register(t, handleFrom(h), this._map)?.[1];
222
230
  };
223
231
 
224
- _marshalPreApply = (
225
- target: Function,
226
- that: unknown,
227
- args: unknown[]
228
- ): void => {
232
+ _marshalPreApply = (target: Function, that: unknown, args: unknown[]): void => {
229
233
  const unwrapped = isObject(that) ? this._unwrap(that) : undefined;
230
234
  // override sync mode of this object while calling the function
231
235
  if (unwrapped) this._temporalSync.add(unwrapped);
@@ -244,12 +248,13 @@ export class Arena {
244
248
  }
245
249
 
246
250
  const handle = marshal(this._wrap(target) ?? target, {
247
- vm: this.vm,
251
+ ctx: this.context,
248
252
  unmarshal: this._unmarshal,
249
253
  isMarshalable: this._isMarshalable,
250
254
  find: this._marshalFind,
251
255
  pre: this._marshalPre,
252
256
  preApply: this._marshalPreApply,
257
+ custom: this._options?.customMarshaller,
253
258
  });
254
259
 
255
260
  return [handle, !this._map.hasHandle(handle)];
@@ -271,10 +276,11 @@ export class Arena {
271
276
 
272
277
  const [wrappedHandle] = this._wrapHandle(handle);
273
278
  return unmarshal(wrappedHandle ?? handle, {
274
- vm: this.vm,
279
+ ctx: this.context,
275
280
  marshal: this._marshal,
276
281
  find: this._unmarshalFind,
277
282
  pre: this._preUnmarshal,
283
+ custom: this._options?.customUnmarshaller,
278
284
  });
279
285
  };
280
286
 
@@ -282,15 +288,17 @@ export class Arena {
282
288
  t: any,
283
289
  h: QuickJSHandle,
284
290
  map: VMMap = this._map,
285
- sync?: boolean
291
+ sync?: boolean,
286
292
  ): [Wrapped<any>, Wrapped<QuickJSHandle>] | undefined {
287
293
  if (this._registeredMap.has(t) || this._registeredMap.hasHandle(h)) {
288
294
  return;
289
295
  }
290
296
 
291
- const wrappedT = this._wrap(t);
297
+ let wrappedT = this._wrap(t);
292
298
  const [wrappedH] = this._wrapHandle(h);
293
- if (!wrappedT || !wrappedH) return; // t or h is not an object
299
+ const isPromise = t instanceof Promise;
300
+ if (!wrappedH || (!wrappedT && !isPromise)) return; // t or h is not an object
301
+ if (isPromise) wrappedT = t;
294
302
 
295
303
  const unwrappedT = this._unwrap(t);
296
304
  const [unwrappedH, unwrapped] = this._unwrapHandle(h);
@@ -309,19 +317,18 @@ export class Arena {
309
317
 
310
318
  _syncMode = (obj: any): "both" | undefined => {
311
319
  const obj2 = this._unwrap(obj);
312
- return this._sync.has(obj2) || this._temporalSync.has(obj2)
313
- ? "both"
314
- : undefined;
320
+ return this._sync.has(obj2) || this._temporalSync.has(obj2) ? "both" : undefined;
315
321
  };
316
322
 
317
323
  _wrap<T>(target: T): Wrapped<T> | undefined {
318
324
  return wrap(
319
- this.vm,
325
+ this.context,
320
326
  target,
321
327
  this._symbol,
322
328
  this._symbolHandle,
323
329
  this._marshal,
324
- this._syncMode
330
+ this._syncMode,
331
+ this._options?.isWrappable,
325
332
  );
326
333
  }
327
334
 
@@ -331,23 +338,22 @@ export class Arena {
331
338
 
332
339
  _unwrapIfNotSynced = <T>(target: T): T => {
333
340
  const unwrapped = this._unwrap(target);
334
- return this._sync.has(unwrapped) ? target : unwrapped;
341
+ return unwrapped instanceof Promise || !this._sync.has(unwrapped) ? unwrapped : target;
335
342
  };
336
343
 
337
- _wrapHandle(
338
- handle: QuickJSHandle
339
- ): [Wrapped<QuickJSHandle> | undefined, boolean] {
344
+ _wrapHandle(handle: QuickJSHandle): [Wrapped<QuickJSHandle> | undefined, boolean] {
340
345
  return wrapHandle(
341
- this.vm,
346
+ this.context,
342
347
  handle,
343
348
  this._symbol,
344
349
  this._symbolHandle,
345
350
  this._unmarshal,
346
- this._syncMode
351
+ this._syncMode,
352
+ this._options?.isHandleWrappable,
347
353
  );
348
354
  }
349
355
 
350
356
  _unwrapHandle(target: QuickJSHandle): [QuickJSHandle, boolean] {
351
- return unwrapHandle(this.vm, target, this._symbolHandle);
357
+ return unwrapHandle(this.context, target, this._symbolHandle);
352
358
  }
353
359
  }
@@ -0,0 +1,50 @@
1
+ import { getQuickJS } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
4
+ import { call } from "../vmutil";
5
+
6
+ import marshalCustom, { defaultCustom } from "./custom";
7
+
8
+ test("symbol", async () => {
9
+ const ctx = (await getQuickJS()).newContext();
10
+ const pre = vi.fn();
11
+ const sym = Symbol("foobar");
12
+
13
+ const marshal = (t: unknown) => marshalCustom(ctx, t, pre, defaultCustom);
14
+
15
+ expect(marshal({})).toBe(undefined);
16
+ expect(pre).toBeCalledTimes(0);
17
+
18
+ const handle = marshal(sym);
19
+ if (!handle) throw new Error("handle is undefined");
20
+ expect(ctx.typeof(handle)).toBe("symbol");
21
+ expect(ctx.getString(ctx.getProp(handle, "description"))).toBe("foobar");
22
+ expect(pre).toReturnTimes(1);
23
+ expect(pre.mock.calls[0][0]).toBe(sym);
24
+ expect(pre.mock.calls[0][1] === handle).toBe(true);
25
+
26
+ handle.dispose();
27
+ ctx.dispose();
28
+ });
29
+
30
+ test("date", async () => {
31
+ const ctx = (await getQuickJS()).newContext();
32
+ const pre = vi.fn();
33
+ const date = new Date(2022, 7, 26);
34
+
35
+ const marshal = (t: unknown) => marshalCustom(ctx, t, pre, defaultCustom);
36
+
37
+ expect(marshal({})).toBe(undefined);
38
+ expect(pre).toBeCalledTimes(0);
39
+
40
+ const handle = marshal(date);
41
+ if (!handle) throw new Error("handle is undefined");
42
+ expect(ctx.dump(call(ctx, "d => d instanceof Date", undefined, handle))).toBe(true);
43
+ expect(ctx.dump(call(ctx, "d => d.getTime()", undefined, handle))).toBe(date.getTime());
44
+ expect(pre).toReturnTimes(1);
45
+ expect(pre.mock.calls[0][0]).toBe(date);
46
+ expect(pre.mock.calls[0][1] === handle).toBe(true);
47
+
48
+ handle.dispose();
49
+ ctx.dispose();
50
+ });
@@ -0,0 +1,36 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+
3
+ import { call } from "../vmutil";
4
+
5
+ export default function marshalCustom(
6
+ ctx: QuickJSContext,
7
+ target: unknown,
8
+ preMarshal: (target: unknown, handle: QuickJSHandle) => QuickJSHandle | undefined,
9
+ custom: Iterable<(target: unknown, ctx: QuickJSContext) => QuickJSHandle | undefined>,
10
+ ): QuickJSHandle | undefined {
11
+ let handle: QuickJSHandle | undefined;
12
+ for (const c of custom) {
13
+ handle = c(target, ctx);
14
+ if (handle) break;
15
+ }
16
+ return handle ? preMarshal(target, handle) ?? handle : undefined;
17
+ }
18
+
19
+ export function symbol(target: unknown, ctx: QuickJSContext): QuickJSHandle | undefined {
20
+ if (typeof target !== "symbol") return;
21
+ const handle = call(
22
+ ctx,
23
+ "d => Symbol(d)",
24
+ undefined,
25
+ target.description ? ctx.newString(target.description) : ctx.undefined,
26
+ );
27
+ return handle;
28
+ }
29
+
30
+ export function date(target: unknown, ctx: QuickJSContext): QuickJSHandle | undefined {
31
+ if (!(target instanceof Date)) return;
32
+ const handle = call(ctx, "d => new Date(d)", undefined, ctx.newNumber(target.getTime()));
33
+ return handle;
34
+ }
35
+
36
+ export const defaultCustom = [symbol, date];
@@ -1,32 +1,33 @@
1
1
  import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
2
+ import { expect, test, vi } from "vitest";
3
+
2
4
  import { json, eq, call } from "../vmutil";
5
+
3
6
  import marshalFunction from "./function";
4
7
 
5
8
  test("normal func", async () => {
6
- const vm = (await getQuickJS()).createVm();
9
+ const ctx = (await getQuickJS()).newContext();
7
10
 
8
- const marshal = jest.fn((v) => json(vm, v));
9
- const unmarshal = jest.fn((v) =>
10
- eq(vm, v, vm.global) ? undefined : vm.dump(v)
11
- );
12
- const preMarshal = jest.fn((_, a) => a);
13
- const innerfn = jest.fn((..._args: any[]) => "hoge");
11
+ const marshal = vi.fn(v => json(ctx, v));
12
+ const unmarshal = vi.fn(v => (eq(ctx, v, ctx.global) ? undefined : ctx.dump(v)));
13
+ const preMarshal = vi.fn((_, a) => a);
14
+ const innerfn = vi.fn((..._args: any[]) => "hoge");
14
15
  const fn = (...args: any[]) => innerfn(...args);
15
16
 
16
- const handle = marshalFunction(vm, fn, marshal, unmarshal, preMarshal);
17
+ const handle = marshalFunction(ctx, fn, marshal, unmarshal, preMarshal);
17
18
  if (!handle) throw new Error("handle is undefined");
18
19
 
19
20
  expect(marshal.mock.calls).toEqual([["length"], [0], ["name"], ["fn"]]); // fn.length, fn.name
20
21
  expect(preMarshal.mock.calls).toEqual([[fn, handle]]); // fn.length, fn.name
21
- expect(vm.typeof(handle)).toBe("function");
22
- expect(vm.dump(vm.getProp(handle, "length"))).toBe(0);
23
- expect(vm.dump(vm.getProp(handle, "name"))).toBe("fn");
22
+ expect(ctx.typeof(handle)).toBe("function");
23
+ expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(0);
24
+ expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("fn");
24
25
 
25
- const result = vm.unwrapResult(
26
- vm.callFunction(handle, vm.undefined, vm.newNumber(1), vm.true)
26
+ const result = ctx.unwrapResult(
27
+ ctx.callFunction(handle, ctx.undefined, ctx.newNumber(1), ctx.true),
27
28
  );
28
29
 
29
- expect(vm.dump(result)).toBe("hoge");
30
+ expect(ctx.dump(result)).toBe("hoge");
30
31
  expect(innerfn).toBeCalledWith(1, true);
31
32
  expect(marshal).toHaveBeenLastCalledWith("hoge");
32
33
  expect(unmarshal).toBeCalledTimes(3);
@@ -35,48 +36,48 @@ test("normal func", async () => {
35
36
  expect(unmarshal.mock.results[2].value).toBe(true);
36
37
 
37
38
  handle.dispose();
38
- vm.dispose();
39
+ ctx.dispose();
39
40
  });
40
41
 
41
42
  test("func which has properties", async () => {
42
- const vm = (await getQuickJS()).createVm();
43
- const marshal = jest.fn((v) => json(vm, v));
43
+ const ctx = (await getQuickJS()).newContext();
44
+ const marshal = vi.fn(v => json(ctx, v));
44
45
 
45
46
  const fn = () => {};
46
47
  fn.hoge = "foo";
47
48
 
48
49
  const handle = marshalFunction(
49
- vm,
50
+ ctx,
50
51
  fn,
51
52
  marshal,
52
- (v) => vm.dump(v),
53
- (_, a) => a
53
+ v => ctx.dump(v),
54
+ (_, a) => a,
54
55
  );
55
56
  if (!handle) throw new Error("handle is undefined");
56
57
 
57
- expect(vm.typeof(handle)).toBe("function");
58
- expect(vm.dump(vm.getProp(handle, "hoge"))).toBe("foo");
58
+ expect(ctx.typeof(handle)).toBe("function");
59
+ expect(ctx.dump(ctx.getProp(handle, "hoge"))).toBe("foo");
59
60
  expect(marshal).toBeCalledWith("foo");
60
61
 
61
62
  handle.dispose();
62
- vm.dispose();
63
+ ctx.dispose();
63
64
  });
64
65
 
65
66
  test("class", async () => {
66
- const vm = (await getQuickJS()).createVm();
67
+ const ctx = (await getQuickJS()).newContext();
67
68
 
68
69
  const disposables: QuickJSHandle[] = [];
69
70
  const marshal = (v: any) => {
70
- if (typeof v === "string") return vm.newString(v);
71
- if (typeof v === "number") return vm.newNumber(v);
71
+ if (typeof v === "string") return ctx.newString(v);
72
+ if (typeof v === "number") return ctx.newNumber(v);
72
73
  if (typeof v === "object") {
73
- const obj = vm.newObject();
74
+ const obj = ctx.newObject();
74
75
  disposables.push(obj);
75
76
  return obj;
76
77
  }
77
- return vm.null;
78
+ return ctx.null;
78
79
  };
79
- const unmarshal = (v: QuickJSHandle) => vm.dump(v);
80
+ const unmarshal = (v: QuickJSHandle) => ctx.dump(v);
80
81
 
81
82
  class A {
82
83
  a: number;
@@ -86,82 +87,70 @@ test("class", async () => {
86
87
  }
87
88
  }
88
89
 
89
- const handle = marshalFunction(vm, A, marshal, unmarshal, (_, a) => a);
90
+ const handle = marshalFunction(ctx, A, marshal, unmarshal, (_, a) => a);
90
91
  if (!handle) throw new Error("handle is undefined");
91
92
 
92
- const newA = vm.unwrapResult(vm.evalCode(`A => new A(100)`));
93
- const instance = vm.unwrapResult(vm.callFunction(newA, vm.undefined, handle));
93
+ const newA = ctx.unwrapResult(ctx.evalCode(`A => new A(100)`));
94
+ const instance = ctx.unwrapResult(ctx.callFunction(newA, ctx.undefined, handle));
94
95
 
95
- expect(vm.dump(vm.getProp(handle, "name"))).toBe("A");
96
- expect(vm.dump(vm.getProp(handle, "length"))).toBe(1);
97
- expect(
98
- vm.dump(
99
- call(vm, "(cls, i) => i instanceof cls", undefined, handle, instance)
100
- )
101
- ).toBe(true);
102
- expect(vm.dump(vm.getProp(instance, "a"))).toBe(100);
96
+ expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("A");
97
+ expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(1);
98
+ expect(ctx.dump(call(ctx, "(cls, i) => i instanceof cls", undefined, handle, instance))).toBe(
99
+ true,
100
+ );
101
+ expect(ctx.dump(ctx.getProp(instance, "a"))).toBe(100);
103
102
 
104
- disposables.forEach((d) => d.dispose());
103
+ disposables.forEach(d => d.dispose());
105
104
  instance.dispose();
106
105
  newA.dispose();
107
106
  handle.dispose();
108
- vm.dispose();
107
+ ctx.dispose();
109
108
  });
110
109
 
111
110
  test("preApply", async () => {
112
- const vm = (await getQuickJS()).createVm();
111
+ const ctx = (await getQuickJS()).newContext();
113
112
 
114
113
  const marshal = (v: any) => {
115
- if (typeof v === "string") return vm.newString(v);
116
- if (typeof v === "number") return vm.newNumber(v);
117
- return vm.null;
114
+ if (typeof v === "string") return ctx.newString(v);
115
+ if (typeof v === "number") return ctx.newNumber(v);
116
+ return ctx.null;
118
117
  };
119
- const unmarshal = (v: QuickJSHandle) =>
120
- vm.typeof(v) === "object" ? that : vm.dump(v);
121
- const preApply = jest.fn(
122
- (a: Function, b: any, c: any[]) => a.apply(b, c) + "!"
123
- );
118
+ const unmarshal = (v: QuickJSHandle) => (ctx.typeof(v) === "object" ? that : ctx.dump(v));
119
+ const preApply = vi.fn((a: Function, b: any, c: any[]) => a.apply(b, c) + "!");
124
120
  const that = {};
125
- const thatHandle = vm.newObject();
121
+ const thatHandle = ctx.newObject();
126
122
 
127
123
  const fn = () => "foo";
128
- const handle = marshalFunction(
129
- vm,
130
- fn,
131
- marshal,
132
- unmarshal,
133
- (_, a) => a,
134
- preApply
135
- );
124
+ const handle = marshalFunction(ctx, fn, marshal, unmarshal, (_, a) => a, preApply);
136
125
  if (!handle) throw new Error("handle is undefined");
137
126
 
138
127
  expect(preApply).toBeCalledTimes(0);
139
128
 
140
- const res = vm.unwrapResult(
141
- vm.callFunction(handle, thatHandle, vm.newNumber(100), vm.newString("hoge"))
129
+ const res = ctx.unwrapResult(
130
+ ctx.callFunction(handle, thatHandle, ctx.newNumber(100), ctx.newString("hoge")),
142
131
  );
143
132
 
144
133
  expect(preApply).toBeCalledTimes(1);
145
134
  expect(preApply).toBeCalledWith(fn, that, [100, "hoge"]);
146
- expect(vm.dump(res)).toBe("foo!");
135
+ expect(ctx.dump(res)).toBe("foo!");
147
136
 
148
137
  thatHandle.dispose();
149
138
  handle.dispose();
150
- vm.dispose();
139
+ ctx.dispose();
151
140
  });
152
141
 
153
142
  test("undefined", async () => {
154
- const vm = (await getQuickJS()).createVm();
155
- const f = jest.fn();
156
-
157
- expect(marshalFunction(vm, undefined, f, f, f)).toBe(undefined);
158
- expect(marshalFunction(vm, null, f, f, f)).toBe(undefined);
159
- expect(marshalFunction(vm, false, f, f, f)).toBe(undefined);
160
- expect(marshalFunction(vm, true, f, f, f)).toBe(undefined);
161
- expect(marshalFunction(vm, 1, f, f, f)).toBe(undefined);
162
- expect(marshalFunction(vm, [1], f, f, f)).toBe(undefined);
163
- expect(marshalFunction(vm, { a: 1 }, f, f, f)).toBe(undefined);
143
+ const ctx = (await getQuickJS()).newContext();
144
+ const f = vi.fn();
145
+
146
+ expect(marshalFunction(ctx, undefined, f, f, f)).toBe(undefined);
147
+ expect(marshalFunction(ctx, null, f, f, f)).toBe(undefined);
148
+ expect(marshalFunction(ctx, false, f, f, f)).toBe(undefined);
149
+ expect(marshalFunction(ctx, true, f, f, f)).toBe(undefined);
150
+ expect(marshalFunction(ctx, 1, f, f, f)).toBe(undefined);
151
+ expect(marshalFunction(ctx, [1], f, f, f)).toBe(undefined);
152
+ expect(marshalFunction(ctx, { a: 1 }, f, f, f)).toBe(undefined);
164
153
  expect(f).toBeCalledTimes(0);
165
154
 
166
- vm.dispose();
155
+ ctx.dispose();
167
156
  });