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