quickjs-emscripten-sync 1.3.0 → 1.5.1

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