quickjs-emscripten-sync 1.3.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.
- package/README.md +36 -34
- package/dist/index.d.ts +17 -15
- package/dist/quickjs-emscripten-sync.es.js +146 -135
- package/dist/quickjs-emscripten-sync.umd.js +6 -6
- package/package.json +3 -3
- package/src/index.test.ts +146 -60
- package/src/index.ts +40 -26
- package/src/marshal/function.test.ts +61 -53
- package/src/marshal/function.ts +7 -6
- package/src/marshal/index.test.ts +73 -65
- package/src/marshal/index.ts +12 -11
- package/src/marshal/json.test.ts +30 -30
- package/src/marshal/json.ts +4 -3
- package/src/marshal/object.test.ts +55 -50
- package/src/marshal/object.ts +6 -5
- package/src/marshal/primitive.test.ts +21 -17
- package/src/marshal/primitive.ts +10 -9
- package/src/marshal/promise.test.ts +14 -14
- package/src/marshal/promise.ts +3 -3
- package/src/marshal/properties.test.ts +17 -15
- package/src/marshal/properties.ts +10 -9
- package/src/marshal/symbol.test.ts +6 -6
- package/src/marshal/symbol.ts +5 -4
- package/src/unmarshal/function.test.ts +45 -43
- package/src/unmarshal/function.ts +9 -8
- package/src/unmarshal/index.test.ts +41 -40
- package/src/unmarshal/index.ts +9 -8
- package/src/unmarshal/object.test.ts +33 -31
- package/src/unmarshal/object.ts +12 -11
- package/src/unmarshal/primitive.test.ts +18 -15
- package/src/unmarshal/primitive.ts +9 -9
- package/src/unmarshal/promise.test.ts +11 -11
- package/src/unmarshal/promise.ts +9 -11
- package/src/unmarshal/properties.test.ts +6 -6
- package/src/unmarshal/properties.ts +47 -44
- package/src/unmarshal/symbol.test.ts +6 -6
- package/src/unmarshal/symbol.ts +4 -4
- package/src/util.test.ts +1 -0
- package/src/vmmap.test.ts +72 -88
- package/src/vmmap.ts +16 -16
- package/src/vmutil.test.ts +71 -73
- package/src/vmutil.ts +22 -18
- package/src/wrapper.test.ts +111 -88
- package/src/wrapper.ts +31 -27
package/src/index.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
QuickJSDeferredPromise,
|
|
3
3
|
QuickJSHandle,
|
|
4
|
-
|
|
5
|
-
} from "quickjs-emscripten";
|
|
6
|
-
import type {
|
|
4
|
+
QuickJSContext,
|
|
7
5
|
SuccessOrFail,
|
|
8
6
|
VmCallResult,
|
|
9
|
-
} from "quickjs-emscripten
|
|
7
|
+
} from "quickjs-emscripten";
|
|
10
8
|
|
|
11
9
|
import VMMap from "./vmmap";
|
|
12
10
|
import marshal from "./marshal";
|
|
@@ -48,13 +46,15 @@ export type Options = {
|
|
|
48
46
|
* 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
47
|
*/
|
|
50
48
|
registeredObjects?: Iterable<[any, QuickJSHandle | string]>;
|
|
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
|
-
|
|
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
|
|
68
|
-
constructor(
|
|
69
|
-
|
|
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 =
|
|
72
|
-
this._map = new VMMap(
|
|
73
|
-
this._registeredMap = new VMMap(
|
|
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.
|
|
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.
|
|
106
|
+
const result = this.context.runtime.executePendingJobs(maxJobsToExecute);
|
|
99
107
|
if ("value" in result) {
|
|
100
108
|
return result.value;
|
|
101
109
|
}
|
|
@@ -111,7 +119,7 @@ export class Arena {
|
|
|
111
119
|
expose(obj: { [k: string]: any }) {
|
|
112
120
|
for (const [key, value] of Object.entries(obj)) {
|
|
113
121
|
mayConsume(this._marshal(value), (handle) => {
|
|
114
|
-
this.
|
|
122
|
+
this.context.setProp(this.context.global, key, handle);
|
|
115
123
|
});
|
|
116
124
|
}
|
|
117
125
|
}
|
|
@@ -125,7 +133,8 @@ export class Arena {
|
|
|
125
133
|
const wrapped = this._wrap(target);
|
|
126
134
|
if (typeof wrapped === "undefined") return target;
|
|
127
135
|
walkObject(wrapped, (v) => {
|
|
128
|
-
this.
|
|
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.
|
|
151
|
+
? this._unwrapResult(this.context.evalCode(handleOrCode))
|
|
143
152
|
: handleOrCode;
|
|
144
|
-
if (eq(this.
|
|
153
|
+
if (eq(this.context, handle, this.context.undefined)) return;
|
|
145
154
|
if (typeof handleOrCode === "string") {
|
|
146
155
|
this._registeredMapDispose.add(target);
|
|
147
156
|
}
|
|
@@ -179,7 +188,8 @@ export class Arena {
|
|
|
179
188
|
|
|
180
189
|
startSync(target: any) {
|
|
181
190
|
if (!isObject(target)) return;
|
|
182
|
-
this.
|
|
191
|
+
const u = this._unwrap(target);
|
|
192
|
+
this._sync.add(u);
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
endSync(target: any) {
|
|
@@ -249,7 +259,7 @@ export class Arena {
|
|
|
249
259
|
}
|
|
250
260
|
|
|
251
261
|
const handle = marshal(this._wrap(target) ?? target, {
|
|
252
|
-
|
|
262
|
+
ctx: this.context,
|
|
253
263
|
unmarshal: this._unmarshal,
|
|
254
264
|
isMarshalable: this._isMarshalable,
|
|
255
265
|
find: this._marshalFind,
|
|
@@ -276,7 +286,7 @@ export class Arena {
|
|
|
276
286
|
|
|
277
287
|
const [wrappedHandle] = this._wrapHandle(handle);
|
|
278
288
|
return unmarshal(wrappedHandle ?? handle, {
|
|
279
|
-
|
|
289
|
+
ctx: this.context,
|
|
280
290
|
marshal: this._marshal,
|
|
281
291
|
find: this._unmarshalFind,
|
|
282
292
|
pre: this._preUnmarshal,
|
|
@@ -293,9 +303,11 @@ export class Arena {
|
|
|
293
303
|
return;
|
|
294
304
|
}
|
|
295
305
|
|
|
296
|
-
|
|
306
|
+
let wrappedT = this._wrap(t);
|
|
297
307
|
const [wrappedH] = this._wrapHandle(h);
|
|
298
|
-
|
|
308
|
+
const isPromise = t instanceof Promise;
|
|
309
|
+
if (!wrappedH || (!wrappedT && !isPromise)) return; // t or h is not an object
|
|
310
|
+
if (isPromise) wrappedT = t;
|
|
299
311
|
|
|
300
312
|
const unwrappedT = this._unwrap(t);
|
|
301
313
|
const [unwrappedH, unwrapped] = this._unwrapHandle(h);
|
|
@@ -321,7 +333,7 @@ export class Arena {
|
|
|
321
333
|
|
|
322
334
|
_wrap<T>(target: T): Wrapped<T> | undefined {
|
|
323
335
|
return wrap(
|
|
324
|
-
this.
|
|
336
|
+
this.context,
|
|
325
337
|
target,
|
|
326
338
|
this._symbol,
|
|
327
339
|
this._symbolHandle,
|
|
@@ -336,14 +348,16 @@ export class Arena {
|
|
|
336
348
|
|
|
337
349
|
_unwrapIfNotSynced = <T>(target: T): T => {
|
|
338
350
|
const unwrapped = this._unwrap(target);
|
|
339
|
-
return this._sync.has(unwrapped)
|
|
351
|
+
return unwrapped instanceof Promise || !this._sync.has(unwrapped)
|
|
352
|
+
? unwrapped
|
|
353
|
+
: target;
|
|
340
354
|
};
|
|
341
355
|
|
|
342
356
|
_wrapHandle(
|
|
343
357
|
handle: QuickJSHandle
|
|
344
358
|
): [Wrapped<QuickJSHandle> | undefined, boolean] {
|
|
345
359
|
return wrapHandle(
|
|
346
|
-
this.
|
|
360
|
+
this.context,
|
|
347
361
|
handle,
|
|
348
362
|
this._symbol,
|
|
349
363
|
this._symbolHandle,
|
|
@@ -353,6 +367,6 @@ export class Arena {
|
|
|
353
367
|
}
|
|
354
368
|
|
|
355
369
|
_unwrapHandle(target: QuickJSHandle): [QuickJSHandle, boolean] {
|
|
356
|
-
return unwrapHandle(this.
|
|
370
|
+
return unwrapHandle(this.context, target, this._symbolHandle);
|
|
357
371
|
}
|
|
358
372
|
}
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { json, eq, call } from "../vmutil";
|
|
3
4
|
import marshalFunction from "./function";
|
|
4
5
|
import { expect, test, vi } from "vitest";
|
|
5
6
|
|
|
6
7
|
test("normal func", async () => {
|
|
7
|
-
const
|
|
8
|
+
const ctx = (await getQuickJS()).newContext();
|
|
8
9
|
|
|
9
|
-
const marshal = vi.fn((v) => json(
|
|
10
|
+
const marshal = vi.fn((v) => json(ctx, v));
|
|
10
11
|
const unmarshal = vi.fn((v) =>
|
|
11
|
-
eq(
|
|
12
|
+
eq(ctx, v, ctx.global) ? undefined : ctx.dump(v)
|
|
12
13
|
);
|
|
13
14
|
const preMarshal = vi.fn((_, a) => a);
|
|
14
15
|
const innerfn = vi.fn((..._args: any[]) => "hoge");
|
|
15
16
|
const fn = (...args: any[]) => innerfn(...args);
|
|
16
17
|
|
|
17
|
-
const handle = marshalFunction(
|
|
18
|
+
const handle = marshalFunction(ctx, fn, marshal, unmarshal, preMarshal);
|
|
18
19
|
if (!handle) throw new Error("handle is undefined");
|
|
19
20
|
|
|
20
21
|
expect(marshal.mock.calls).toEqual([["length"], [0], ["name"], ["fn"]]); // fn.length, fn.name
|
|
21
22
|
expect(preMarshal.mock.calls).toEqual([[fn, handle]]); // fn.length, fn.name
|
|
22
|
-
expect(
|
|
23
|
-
expect(
|
|
24
|
-
expect(
|
|
23
|
+
expect(ctx.typeof(handle)).toBe("function");
|
|
24
|
+
expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(0);
|
|
25
|
+
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("fn");
|
|
25
26
|
|
|
26
|
-
const result =
|
|
27
|
-
|
|
27
|
+
const result = ctx.unwrapResult(
|
|
28
|
+
ctx.callFunction(handle, ctx.undefined, ctx.newNumber(1), ctx.true)
|
|
28
29
|
);
|
|
29
30
|
|
|
30
|
-
expect(
|
|
31
|
+
expect(ctx.dump(result)).toBe("hoge");
|
|
31
32
|
expect(innerfn).toBeCalledWith(1, true);
|
|
32
33
|
expect(marshal).toHaveBeenLastCalledWith("hoge");
|
|
33
34
|
expect(unmarshal).toBeCalledTimes(3);
|
|
@@ -36,48 +37,48 @@ test("normal func", async () => {
|
|
|
36
37
|
expect(unmarshal.mock.results[2].value).toBe(true);
|
|
37
38
|
|
|
38
39
|
handle.dispose();
|
|
39
|
-
|
|
40
|
+
ctx.dispose();
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
test("func which has properties", async () => {
|
|
43
|
-
const
|
|
44
|
-
const marshal = vi.fn((v) => json(
|
|
44
|
+
const ctx = (await getQuickJS()).newContext();
|
|
45
|
+
const marshal = vi.fn((v) => json(ctx, v));
|
|
45
46
|
|
|
46
47
|
const fn = () => {};
|
|
47
48
|
fn.hoge = "foo";
|
|
48
49
|
|
|
49
50
|
const handle = marshalFunction(
|
|
50
|
-
|
|
51
|
+
ctx,
|
|
51
52
|
fn,
|
|
52
53
|
marshal,
|
|
53
|
-
(v) =>
|
|
54
|
+
(v) => ctx.dump(v),
|
|
54
55
|
(_, a) => a
|
|
55
56
|
);
|
|
56
57
|
if (!handle) throw new Error("handle is undefined");
|
|
57
58
|
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
59
|
+
expect(ctx.typeof(handle)).toBe("function");
|
|
60
|
+
expect(ctx.dump(ctx.getProp(handle, "hoge"))).toBe("foo");
|
|
60
61
|
expect(marshal).toBeCalledWith("foo");
|
|
61
62
|
|
|
62
63
|
handle.dispose();
|
|
63
|
-
|
|
64
|
+
ctx.dispose();
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
test("class", async () => {
|
|
67
|
-
const
|
|
68
|
+
const ctx = (await getQuickJS()).newContext();
|
|
68
69
|
|
|
69
70
|
const disposables: QuickJSHandle[] = [];
|
|
70
71
|
const marshal = (v: any) => {
|
|
71
|
-
if (typeof v === "string") return
|
|
72
|
-
if (typeof v === "number") return
|
|
72
|
+
if (typeof v === "string") return ctx.newString(v);
|
|
73
|
+
if (typeof v === "number") return ctx.newNumber(v);
|
|
73
74
|
if (typeof v === "object") {
|
|
74
|
-
const obj =
|
|
75
|
+
const obj = ctx.newObject();
|
|
75
76
|
disposables.push(obj);
|
|
76
77
|
return obj;
|
|
77
78
|
}
|
|
78
|
-
return
|
|
79
|
+
return ctx.null;
|
|
79
80
|
};
|
|
80
|
-
const unmarshal = (v: QuickJSHandle) =>
|
|
81
|
+
const unmarshal = (v: QuickJSHandle) => ctx.dump(v);
|
|
81
82
|
|
|
82
83
|
class A {
|
|
83
84
|
a: number;
|
|
@@ -87,47 +88,49 @@ test("class", async () => {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
const handle = marshalFunction(
|
|
91
|
+
const handle = marshalFunction(ctx, A, marshal, unmarshal, (_, a) => a);
|
|
91
92
|
if (!handle) throw new Error("handle is undefined");
|
|
92
93
|
|
|
93
|
-
const newA =
|
|
94
|
-
const instance =
|
|
94
|
+
const newA = ctx.unwrapResult(ctx.evalCode(`A => new A(100)`));
|
|
95
|
+
const instance = ctx.unwrapResult(
|
|
96
|
+
ctx.callFunction(newA, ctx.undefined, handle)
|
|
97
|
+
);
|
|
95
98
|
|
|
96
|
-
expect(
|
|
97
|
-
expect(
|
|
99
|
+
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("A");
|
|
100
|
+
expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(1);
|
|
98
101
|
expect(
|
|
99
|
-
|
|
100
|
-
call(
|
|
102
|
+
ctx.dump(
|
|
103
|
+
call(ctx, "(cls, i) => i instanceof cls", undefined, handle, instance)
|
|
101
104
|
)
|
|
102
105
|
).toBe(true);
|
|
103
|
-
expect(
|
|
106
|
+
expect(ctx.dump(ctx.getProp(instance, "a"))).toBe(100);
|
|
104
107
|
|
|
105
108
|
disposables.forEach((d) => d.dispose());
|
|
106
109
|
instance.dispose();
|
|
107
110
|
newA.dispose();
|
|
108
111
|
handle.dispose();
|
|
109
|
-
|
|
112
|
+
ctx.dispose();
|
|
110
113
|
});
|
|
111
114
|
|
|
112
115
|
test("preApply", async () => {
|
|
113
|
-
const
|
|
116
|
+
const ctx = (await getQuickJS()).newContext();
|
|
114
117
|
|
|
115
118
|
const marshal = (v: any) => {
|
|
116
|
-
if (typeof v === "string") return
|
|
117
|
-
if (typeof v === "number") return
|
|
118
|
-
return
|
|
119
|
+
if (typeof v === "string") return ctx.newString(v);
|
|
120
|
+
if (typeof v === "number") return ctx.newNumber(v);
|
|
121
|
+
return ctx.null;
|
|
119
122
|
};
|
|
120
123
|
const unmarshal = (v: QuickJSHandle) =>
|
|
121
|
-
|
|
124
|
+
ctx.typeof(v) === "object" ? that : ctx.dump(v);
|
|
122
125
|
const preApply = vi.fn(
|
|
123
126
|
(a: Function, b: any, c: any[]) => a.apply(b, c) + "!"
|
|
124
127
|
);
|
|
125
128
|
const that = {};
|
|
126
|
-
const thatHandle =
|
|
129
|
+
const thatHandle = ctx.newObject();
|
|
127
130
|
|
|
128
131
|
const fn = () => "foo";
|
|
129
132
|
const handle = marshalFunction(
|
|
130
|
-
|
|
133
|
+
ctx,
|
|
131
134
|
fn,
|
|
132
135
|
marshal,
|
|
133
136
|
unmarshal,
|
|
@@ -138,31 +141,36 @@ test("preApply", async () => {
|
|
|
138
141
|
|
|
139
142
|
expect(preApply).toBeCalledTimes(0);
|
|
140
143
|
|
|
141
|
-
const res =
|
|
142
|
-
|
|
144
|
+
const res = ctx.unwrapResult(
|
|
145
|
+
ctx.callFunction(
|
|
146
|
+
handle,
|
|
147
|
+
thatHandle,
|
|
148
|
+
ctx.newNumber(100),
|
|
149
|
+
ctx.newString("hoge")
|
|
150
|
+
)
|
|
143
151
|
);
|
|
144
152
|
|
|
145
153
|
expect(preApply).toBeCalledTimes(1);
|
|
146
154
|
expect(preApply).toBeCalledWith(fn, that, [100, "hoge"]);
|
|
147
|
-
expect(
|
|
155
|
+
expect(ctx.dump(res)).toBe("foo!");
|
|
148
156
|
|
|
149
157
|
thatHandle.dispose();
|
|
150
158
|
handle.dispose();
|
|
151
|
-
|
|
159
|
+
ctx.dispose();
|
|
152
160
|
});
|
|
153
161
|
|
|
154
162
|
test("undefined", async () => {
|
|
155
|
-
const
|
|
163
|
+
const ctx = (await getQuickJS()).newContext();
|
|
156
164
|
const f = vi.fn();
|
|
157
165
|
|
|
158
|
-
expect(marshalFunction(
|
|
159
|
-
expect(marshalFunction(
|
|
160
|
-
expect(marshalFunction(
|
|
161
|
-
expect(marshalFunction(
|
|
162
|
-
expect(marshalFunction(
|
|
163
|
-
expect(marshalFunction(
|
|
164
|
-
expect(marshalFunction(
|
|
166
|
+
expect(marshalFunction(ctx, undefined, f, f, f)).toBe(undefined);
|
|
167
|
+
expect(marshalFunction(ctx, null, f, f, f)).toBe(undefined);
|
|
168
|
+
expect(marshalFunction(ctx, false, f, f, f)).toBe(undefined);
|
|
169
|
+
expect(marshalFunction(ctx, true, f, f, f)).toBe(undefined);
|
|
170
|
+
expect(marshalFunction(ctx, 1, f, f, f)).toBe(undefined);
|
|
171
|
+
expect(marshalFunction(ctx, [1], f, f, f)).toBe(undefined);
|
|
172
|
+
expect(marshalFunction(ctx, { a: 1 }, f, f, f)).toBe(undefined);
|
|
165
173
|
expect(f).toBeCalledTimes(0);
|
|
166
174
|
|
|
167
|
-
|
|
175
|
+
ctx.dispose();
|
|
168
176
|
});
|
package/src/marshal/function.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { isES2015Class, isObject } from "../util";
|
|
3
4
|
import { call } from "../vmutil";
|
|
4
5
|
import marshalProperties from "./properties";
|
|
5
6
|
|
|
6
7
|
export default function marshalFunction(
|
|
7
|
-
|
|
8
|
+
ctx: QuickJSContext,
|
|
8
9
|
target: unknown,
|
|
9
10
|
marshal: (target: unknown) => QuickJSHandle,
|
|
10
11
|
unmarshal: (handle: QuickJSHandle) => unknown,
|
|
@@ -16,7 +17,7 @@ export default function marshalFunction(
|
|
|
16
17
|
): QuickJSHandle | undefined {
|
|
17
18
|
if (typeof target !== "function") return;
|
|
18
19
|
|
|
19
|
-
const raw =
|
|
20
|
+
const raw = ctx
|
|
20
21
|
.newFunction(target.name, function (...argHandles) {
|
|
21
22
|
const that = unmarshal(this);
|
|
22
23
|
const args = argHandles.map((a) => unmarshal(a));
|
|
@@ -25,7 +26,7 @@ export default function marshalFunction(
|
|
|
25
26
|
// Class constructors cannot be invoked without new expression, and new.target is not changed
|
|
26
27
|
const result = new target(...args);
|
|
27
28
|
Object.entries(result).forEach(([key, value]) => {
|
|
28
|
-
|
|
29
|
+
ctx.setProp(this, key, marshal(value));
|
|
29
30
|
});
|
|
30
31
|
return this;
|
|
31
32
|
}
|
|
@@ -37,7 +38,7 @@ export default function marshalFunction(
|
|
|
37
38
|
.consume((handle2) =>
|
|
38
39
|
// fucntions created by vm.newFunction are not callable as a class constrcutor
|
|
39
40
|
call(
|
|
40
|
-
|
|
41
|
+
ctx,
|
|
41
42
|
`Cls => {
|
|
42
43
|
const fn = function(...args) { return Cls.apply(this, args); };
|
|
43
44
|
fn.name = Cls.name;
|
|
@@ -50,7 +51,7 @@ export default function marshalFunction(
|
|
|
50
51
|
);
|
|
51
52
|
|
|
52
53
|
const handle = preMarshal(target, raw) ?? raw;
|
|
53
|
-
marshalProperties(
|
|
54
|
+
marshalProperties(ctx, target, raw, marshal);
|
|
54
55
|
|
|
55
56
|
return handle;
|
|
56
57
|
}
|