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.
- package/LICENSE +2 -2
- package/README.md +71 -44
- package/dist/index.d.ts +116 -23
- package/dist/quickjs-emscripten-sync.mjs +842 -0
- package/dist/quickjs-emscripten-sync.umd.js +64 -2
- package/package.json +22 -53
- package/src/default.ts +2 -2
- package/src/index.test.ts +223 -59
- package/src/index.ts +72 -66
- package/src/marshal/custom.test.ts +50 -0
- package/src/marshal/custom.ts +36 -0
- package/src/marshal/function.test.ts +64 -75
- package/src/marshal/function.ts +15 -18
- package/src/marshal/index.test.ts +122 -77
- package/src/marshal/index.ts +19 -15
- package/src/marshal/json.test.ts +37 -41
- package/src/marshal/json.ts +5 -7
- package/src/marshal/object.test.ts +59 -84
- package/src/marshal/object.ts +8 -15
- package/src/marshal/primitive.test.ts +18 -21
- package/src/marshal/primitive.ts +11 -10
- package/src/marshal/promise.test.ts +85 -0
- package/src/marshal/promise.ts +19 -0
- package/src/marshal/properties.test.ts +24 -27
- package/src/marshal/properties.ts +15 -17
- package/src/unmarshal/custom.test.ts +50 -0
- package/src/unmarshal/custom.ts +31 -0
- package/src/unmarshal/function.test.ts +63 -86
- package/src/unmarshal/function.ts +13 -22
- package/src/unmarshal/index.test.ts +114 -109
- package/src/unmarshal/index.ts +15 -14
- package/src/unmarshal/object.test.ts +42 -46
- package/src/unmarshal/object.ts +16 -20
- package/src/unmarshal/primitive.test.ts +17 -15
- package/src/unmarshal/primitive.ts +11 -18
- package/src/unmarshal/promise.test.ts +44 -0
- package/src/unmarshal/promise.ts +31 -0
- package/src/unmarshal/properties.test.ts +11 -9
- package/src/unmarshal/properties.ts +44 -45
- package/src/util.test.ts +14 -2
- package/src/util.ts +18 -8
- package/src/vmmap.test.ts +88 -82
- package/src/vmmap.ts +30 -44
- package/src/vmutil.test.ts +100 -75
- package/src/vmutil.ts +51 -34
- package/src/wrapper.test.ts +129 -167
- package/src/wrapper.ts +59 -62
- package/dist/default.d.ts +0 -4
- package/dist/marshal/function.d.ts +0 -2
- package/dist/marshal/index.d.ts +0 -11
- package/dist/marshal/json.d.ts +0 -2
- package/dist/marshal/object.d.ts +0 -2
- package/dist/marshal/primitive.d.ts +0 -2
- package/dist/marshal/properties.d.ts +0 -2
- package/dist/marshal/symbol.d.ts +0 -2
- package/dist/quickjs-emscripten-sync.cjs +0 -2
- package/dist/quickjs-emscripten-sync.cjs.map +0 -1
- package/dist/quickjs-emscripten-sync.modern.js +0 -2
- package/dist/quickjs-emscripten-sync.modern.js.map +0 -1
- package/dist/quickjs-emscripten-sync.module.js +0 -2
- package/dist/quickjs-emscripten-sync.module.js.map +0 -1
- package/dist/quickjs-emscripten-sync.umd.js.map +0 -1
- package/dist/unmarshal/function.d.ts +0 -2
- package/dist/unmarshal/index.d.ts +0 -9
- package/dist/unmarshal/object.d.ts +0 -2
- package/dist/unmarshal/primitive.d.ts +0 -2
- package/dist/unmarshal/properties.d.ts +0 -2
- package/dist/unmarshal/symbol.d.ts +0 -2
- package/dist/util.d.ts +0 -7
- package/dist/vmmap.d.ts +0 -35
- package/dist/vmutil.d.ts +0 -11
- package/dist/wrapper.d.ts +0 -11
- package/src/marshal/symbol.test.ts +0 -22
- package/src/marshal/symbol.ts +0 -20
- package/src/unmarshal/symbol.test.ts +0 -23
- package/src/unmarshal/symbol.ts +0 -12
|
@@ -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];
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test, vi } from "vitest";
|
|
3
|
+
|
|
2
4
|
import { json } from "../vmutil";
|
|
5
|
+
|
|
3
6
|
import unmarshalFunction from "./function";
|
|
4
7
|
|
|
5
8
|
test("arrow function", async () => {
|
|
6
|
-
const
|
|
7
|
-
const marshal =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
]);
|
|
11
|
-
const unmarshal = jest.fn((v: QuickJSHandle): [unknown, boolean] => [
|
|
12
|
-
vm.dump(v),
|
|
13
|
-
false,
|
|
14
|
-
]);
|
|
15
|
-
const preUnmarshal = jest.fn((a) => a);
|
|
9
|
+
const ctx = (await getQuickJS()).newContext();
|
|
10
|
+
const marshal = vi.fn((v): [QuickJSHandle, boolean] => [json(ctx, v), false]);
|
|
11
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => [ctx.dump(v), false]);
|
|
12
|
+
const preUnmarshal = vi.fn(a => a);
|
|
16
13
|
|
|
17
|
-
const handle =
|
|
18
|
-
const func = unmarshalFunction(
|
|
14
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`(a, b) => a + b`));
|
|
15
|
+
const func = unmarshalFunction(ctx, handle, marshal, unmarshal, preUnmarshal);
|
|
19
16
|
if (!func) throw new Error("func is undefined");
|
|
20
17
|
|
|
21
18
|
expect(func(1, 2)).toBe(3);
|
|
@@ -35,30 +32,28 @@ test("arrow function", async () => {
|
|
|
35
32
|
handle.dispose();
|
|
36
33
|
expect(() => func(1, 2)).toThrow("Lifetime not alive");
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
ctx.dispose();
|
|
39
36
|
});
|
|
40
37
|
|
|
41
38
|
test("function", async () => {
|
|
42
|
-
const
|
|
39
|
+
const ctx = (await getQuickJS()).newContext();
|
|
43
40
|
const that = { a: 1 };
|
|
44
|
-
const thatHandle =
|
|
45
|
-
const marshal =
|
|
46
|
-
v === that ? thatHandle : json(
|
|
41
|
+
const thatHandle = ctx.unwrapResult(ctx.evalCode(`({ a: 1 })`));
|
|
42
|
+
const marshal = vi.fn((v): [QuickJSHandle, boolean] => [
|
|
43
|
+
v === that ? thatHandle : json(ctx, v),
|
|
47
44
|
false,
|
|
48
45
|
]);
|
|
49
46
|
const disposables: QuickJSHandle[] = [];
|
|
50
|
-
const unmarshal =
|
|
51
|
-
const ty =
|
|
47
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
|
|
48
|
+
const ty = ctx.typeof(v);
|
|
52
49
|
if (ty === "object" || ty === "function") disposables.push(v);
|
|
53
|
-
return [
|
|
50
|
+
return [ctx.dump(v), false];
|
|
54
51
|
});
|
|
55
|
-
const preUnmarshal =
|
|
52
|
+
const preUnmarshal = vi.fn(a => a);
|
|
56
53
|
|
|
57
|
-
const handle =
|
|
58
|
-
vm.evalCode(`(function (a) { return this.a + a; })`)
|
|
59
|
-
);
|
|
54
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`(function (a) { return this.a + a; })`));
|
|
60
55
|
|
|
61
|
-
const func = unmarshalFunction(
|
|
56
|
+
const func = unmarshalFunction(ctx, handle, marshal, unmarshal, preUnmarshal);
|
|
62
57
|
if (!func) throw new Error("func is undefined");
|
|
63
58
|
|
|
64
59
|
expect(func.call(that, 2)).toBe(3);
|
|
@@ -76,37 +71,29 @@ test("function", async () => {
|
|
|
76
71
|
expect(preUnmarshal).toBeCalledTimes(1);
|
|
77
72
|
expect(preUnmarshal).toBeCalledWith(func, handle);
|
|
78
73
|
|
|
79
|
-
disposables.forEach(
|
|
74
|
+
disposables.forEach(d => d.dispose());
|
|
80
75
|
thatHandle.dispose();
|
|
81
76
|
handle.dispose();
|
|
82
|
-
|
|
77
|
+
ctx.dispose();
|
|
83
78
|
});
|
|
84
79
|
|
|
85
80
|
test("constructor", async () => {
|
|
86
|
-
const
|
|
81
|
+
const ctx = (await getQuickJS()).newContext();
|
|
87
82
|
const disposables: QuickJSHandle[] = [];
|
|
88
|
-
const marshal =
|
|
89
|
-
typeof v === "object" ?
|
|
83
|
+
const marshal = vi.fn((v): [QuickJSHandle, boolean] => [
|
|
84
|
+
typeof v === "object" ? ctx.undefined : json(ctx, v),
|
|
90
85
|
false,
|
|
91
86
|
]);
|
|
92
|
-
const unmarshal =
|
|
93
|
-
const ty =
|
|
87
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
|
|
88
|
+
const ty = ctx.typeof(v);
|
|
94
89
|
if (ty === "object" || ty === "function") disposables.push(v);
|
|
95
|
-
return [
|
|
90
|
+
return [ctx.dump(v), false];
|
|
96
91
|
});
|
|
97
|
-
const preUnmarshal =
|
|
98
|
-
|
|
99
|
-
const handle =
|
|
100
|
-
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const Cls = unmarshalFunction(
|
|
104
|
-
vm,
|
|
105
|
-
handle,
|
|
106
|
-
marshal,
|
|
107
|
-
unmarshal,
|
|
108
|
-
preUnmarshal
|
|
109
|
-
) 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;
|
|
110
97
|
if (!Cls) throw new Error("Cls is undefined");
|
|
111
98
|
|
|
112
99
|
const instance = new Cls(100);
|
|
@@ -126,36 +113,28 @@ test("constructor", async () => {
|
|
|
126
113
|
expect(preUnmarshal).toBeCalledTimes(1);
|
|
127
114
|
expect(preUnmarshal).toBeCalledWith(Cls, handle);
|
|
128
115
|
|
|
129
|
-
disposables.forEach(
|
|
116
|
+
disposables.forEach(d => d.dispose());
|
|
130
117
|
handle.dispose();
|
|
131
|
-
|
|
118
|
+
ctx.dispose();
|
|
132
119
|
});
|
|
133
120
|
|
|
134
121
|
test("class", async () => {
|
|
135
|
-
const
|
|
136
|
-
const marshal =
|
|
137
|
-
typeof v === "object" ?
|
|
122
|
+
const ctx = (await getQuickJS()).newContext();
|
|
123
|
+
const marshal = vi.fn((v): [QuickJSHandle, boolean] => [
|
|
124
|
+
typeof v === "object" ? ctx.undefined : json(ctx, v),
|
|
138
125
|
false,
|
|
139
126
|
]);
|
|
140
127
|
const disposables: QuickJSHandle[] = [];
|
|
141
|
-
const unmarshal =
|
|
142
|
-
const ty =
|
|
128
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
|
|
129
|
+
const ty = ctx.typeof(v);
|
|
143
130
|
if (ty === "object" || ty === "function") disposables.push(v);
|
|
144
|
-
return [
|
|
131
|
+
return [ctx.dump(v), false];
|
|
145
132
|
});
|
|
146
|
-
const preUnmarshal =
|
|
147
|
-
|
|
148
|
-
const handle =
|
|
149
|
-
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
const Cls = unmarshalFunction(
|
|
153
|
-
vm,
|
|
154
|
-
handle,
|
|
155
|
-
marshal,
|
|
156
|
-
unmarshal,
|
|
157
|
-
preUnmarshal
|
|
158
|
-
) 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;
|
|
159
138
|
if (!Cls) throw new Error("Cls is undefined");
|
|
160
139
|
|
|
161
140
|
const instance = new Cls(2);
|
|
@@ -175,32 +154,30 @@ test("class", async () => {
|
|
|
175
154
|
expect(preUnmarshal).toBeCalledTimes(1);
|
|
176
155
|
expect(preUnmarshal).toBeCalledWith(Cls, handle);
|
|
177
156
|
|
|
178
|
-
disposables.forEach(
|
|
157
|
+
disposables.forEach(d => d.dispose());
|
|
179
158
|
handle.dispose();
|
|
180
|
-
|
|
159
|
+
ctx.dispose();
|
|
181
160
|
});
|
|
182
161
|
|
|
183
162
|
test("undefined", async () => {
|
|
184
|
-
const
|
|
185
|
-
const f =
|
|
186
|
-
|
|
187
|
-
expect(unmarshalFunction(
|
|
188
|
-
expect(unmarshalFunction(
|
|
189
|
-
expect(unmarshalFunction(
|
|
190
|
-
expect(unmarshalFunction(
|
|
191
|
-
expect(unmarshalFunction(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
expect(unmarshalFunction(
|
|
198
|
-
const array = vm.newArray();
|
|
199
|
-
expect(unmarshalFunction(vm, array, f, f, f)).toEqual(undefined);
|
|
163
|
+
const ctx = (await getQuickJS()).newContext();
|
|
164
|
+
const f = vi.fn();
|
|
165
|
+
|
|
166
|
+
expect(unmarshalFunction(ctx, ctx.undefined, f, f, f)).toEqual(undefined);
|
|
167
|
+
expect(unmarshalFunction(ctx, ctx.true, f, f, f)).toEqual(undefined);
|
|
168
|
+
expect(unmarshalFunction(ctx, ctx.false, f, f, f)).toEqual(undefined);
|
|
169
|
+
expect(unmarshalFunction(ctx, ctx.null, f, f, f)).toEqual(undefined);
|
|
170
|
+
expect(unmarshalFunction(ctx, ctx.newString("hoge"), f, f, f)).toEqual(undefined);
|
|
171
|
+
expect(unmarshalFunction(ctx, ctx.newNumber(-10), f, f, f)).toEqual(undefined);
|
|
172
|
+
|
|
173
|
+
const obj = ctx.newObject();
|
|
174
|
+
expect(unmarshalFunction(ctx, obj, f, f, f)).toEqual(undefined);
|
|
175
|
+
const array = ctx.newArray();
|
|
176
|
+
expect(unmarshalFunction(ctx, array, f, f, f)).toEqual(undefined);
|
|
200
177
|
|
|
201
178
|
expect(f).toBeCalledTimes(0);
|
|
202
179
|
|
|
203
180
|
obj.dispose();
|
|
204
181
|
array.dispose();
|
|
205
|
-
|
|
182
|
+
ctx.dispose();
|
|
206
183
|
});
|
|
@@ -1,52 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { call, mayConsumeAll } from "../vmutil";
|
|
4
|
+
|
|
3
5
|
import unmarshalProperties from "./properties";
|
|
4
6
|
|
|
5
7
|
export default function unmarshalFunction(
|
|
6
|
-
|
|
8
|
+
ctx: QuickJSContext,
|
|
7
9
|
handle: QuickJSHandle,
|
|
8
|
-
|
|
10
|
+
/** marshal returns handle and boolean indicates that the handle should be disposed after use */
|
|
9
11
|
marshal: (value: unknown) => [QuickJSHandle, boolean],
|
|
10
12
|
unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
|
|
11
|
-
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined
|
|
13
|
+
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
|
|
12
14
|
): Function | undefined {
|
|
13
|
-
if (
|
|
15
|
+
if (ctx.typeof(handle) !== "function") return;
|
|
14
16
|
|
|
15
17
|
const raw = function (this: any, ...args: any[]) {
|
|
16
18
|
return mayConsumeAll(
|
|
17
|
-
[marshal(this), ...args.map(
|
|
19
|
+
[marshal(this), ...args.map(a => marshal(a))],
|
|
18
20
|
(thisHandle, ...argHandles) => {
|
|
19
21
|
if (new.target) {
|
|
20
22
|
const [instance] = unmarshal(
|
|
21
|
-
call(
|
|
22
|
-
vm,
|
|
23
|
-
`(Cls, ...args) => new Cls(...args)`,
|
|
24
|
-
thisHandle,
|
|
25
|
-
handle,
|
|
26
|
-
...argHandles
|
|
27
|
-
)
|
|
28
|
-
);
|
|
29
|
-
Object.defineProperties(
|
|
30
|
-
this,
|
|
31
|
-
Object.getOwnPropertyDescriptors(instance)
|
|
23
|
+
call(ctx, `(Cls, ...args) => new Cls(...args)`, thisHandle, handle, ...argHandles),
|
|
32
24
|
);
|
|
25
|
+
Object.defineProperties(this, Object.getOwnPropertyDescriptors(instance));
|
|
33
26
|
return this;
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
const resultHandle =
|
|
37
|
-
vm.callFunction(handle, thisHandle, ...argHandles)
|
|
38
|
-
);
|
|
29
|
+
const resultHandle = ctx.unwrapResult(ctx.callFunction(handle, thisHandle, ...argHandles));
|
|
39
30
|
|
|
40
31
|
const [result, alreadyExists] = unmarshal(resultHandle);
|
|
41
32
|
if (alreadyExists) resultHandle.dispose();
|
|
42
33
|
|
|
43
34
|
return result;
|
|
44
|
-
}
|
|
35
|
+
},
|
|
45
36
|
);
|
|
46
37
|
};
|
|
47
38
|
|
|
48
39
|
const func = preUnmarshal(raw, handle) ?? raw;
|
|
49
|
-
unmarshalProperties(
|
|
40
|
+
unmarshalProperties(ctx, handle, raw, unmarshal);
|
|
50
41
|
|
|
51
42
|
return func;
|
|
52
43
|
}
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test, vi } from "vitest";
|
|
3
|
+
|
|
2
4
|
import VMMap from "../vmmap";
|
|
5
|
+
import { json } from "../vmutil";
|
|
6
|
+
|
|
3
7
|
import unmarshal from ".";
|
|
4
8
|
|
|
5
9
|
test("primitive, array, object", async () => {
|
|
6
|
-
const
|
|
7
|
-
const marshal = jest.fn((): [QuickJSHandle, boolean] => [
|
|
8
|
-
vm.undefined,
|
|
9
|
-
false,
|
|
10
|
-
]);
|
|
11
|
-
const map = new VMMap(vm);
|
|
12
|
-
const find = jest.fn((h) => map.getByHandle(h));
|
|
13
|
-
const pre = jest.fn((t: any, h: QuickJSHandle) => {
|
|
14
|
-
map.set(t, h);
|
|
15
|
-
return t;
|
|
16
|
-
});
|
|
10
|
+
const { ctx, unmarshal, marshal, map, dispose } = await setup();
|
|
17
11
|
|
|
18
|
-
const handle =
|
|
19
|
-
|
|
12
|
+
const handle = ctx.unwrapResult(
|
|
13
|
+
ctx.evalCode(`({
|
|
20
14
|
hoge: "foo",
|
|
21
15
|
foo: 1,
|
|
22
16
|
aaa: [1, true, {}],
|
|
23
17
|
nested: { aa: null, hoge: undefined },
|
|
24
18
|
bbb: () => "bar"
|
|
25
|
-
})`)
|
|
19
|
+
})`),
|
|
26
20
|
);
|
|
27
|
-
const target = unmarshal(handle
|
|
21
|
+
const target = unmarshal(handle);
|
|
28
22
|
|
|
29
23
|
expect(target).toEqual({
|
|
30
24
|
hoge: "foo",
|
|
@@ -35,119 +29,83 @@ test("primitive, array, object", async () => {
|
|
|
35
29
|
});
|
|
36
30
|
expect(map.size).toBe(5);
|
|
37
31
|
expect(map.getByHandle(handle)).toBe(target);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.consume(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(map.getByHandle(h)).toBe(target.nested)
|
|
46
|
-
);
|
|
47
|
-
vm.getProp(handle, "bbb").consume((h) =>
|
|
48
|
-
expect(map.getByHandle(h)).toBe(target.bbb)
|
|
49
|
-
);
|
|
32
|
+
ctx.getProp(handle, "aaa").consume(h => expect(map.getByHandle(h)).toBe(target.aaa));
|
|
33
|
+
ctx
|
|
34
|
+
.getProp(handle, "aaa")
|
|
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));
|
|
50
39
|
|
|
51
40
|
expect(marshal).toBeCalledTimes(0);
|
|
52
41
|
expect(target.bbb()).toBe("bar");
|
|
53
42
|
expect(marshal).toBeCalledTimes(1);
|
|
54
43
|
expect(marshal).toBeCalledWith(target); // thisArg of target.bbb()
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
map.dispose();
|
|
58
|
-
vm.dispose();
|
|
45
|
+
dispose();
|
|
59
46
|
});
|
|
60
47
|
|
|
61
48
|
test("object with symbol key", async () => {
|
|
62
|
-
const
|
|
63
|
-
const map = new VMMap(vm);
|
|
64
|
-
const pre = (t: any, h: QuickJSHandle) => {
|
|
65
|
-
map.set(t, h);
|
|
66
|
-
return t;
|
|
67
|
-
};
|
|
49
|
+
const { ctx, unmarshal, dispose } = await setup();
|
|
68
50
|
|
|
69
|
-
const handle =
|
|
70
|
-
|
|
51
|
+
const handle = ctx.unwrapResult(
|
|
52
|
+
ctx.evalCode(`({
|
|
71
53
|
hoge: "foo",
|
|
72
54
|
[Symbol("a")]: "bar"
|
|
73
|
-
})`)
|
|
55
|
+
})`),
|
|
74
56
|
);
|
|
75
|
-
const target = unmarshal(handle
|
|
76
|
-
vm,
|
|
77
|
-
pre,
|
|
78
|
-
find: () => undefined,
|
|
79
|
-
marshal: () => [vm.undefined, false],
|
|
80
|
-
});
|
|
57
|
+
const target = unmarshal(handle);
|
|
81
58
|
|
|
82
59
|
expect(target.hoge).toBe("foo");
|
|
83
60
|
expect(target[Object.getOwnPropertySymbols(target)[0]]).toBe("bar");
|
|
84
61
|
|
|
85
|
-
|
|
86
|
-
map.dispose();
|
|
87
|
-
vm.dispose();
|
|
62
|
+
dispose();
|
|
88
63
|
});
|
|
89
64
|
|
|
90
|
-
test("
|
|
91
|
-
const
|
|
92
|
-
const jsonParse = vm.unwrapResult(vm.evalCode(`JSON.parse`));
|
|
93
|
-
const disposables: QuickJSHandle[] = [];
|
|
94
|
-
const marshal = jest.fn((t: unknown): [QuickJSHandle, boolean] => {
|
|
95
|
-
const h =
|
|
96
|
-
t === undefined
|
|
97
|
-
? vm.undefined
|
|
98
|
-
: vm.unwrapResult(
|
|
99
|
-
vm.callFunction(
|
|
100
|
-
jsonParse,
|
|
101
|
-
vm.undefined,
|
|
102
|
-
vm.newString(JSON.stringify(t))
|
|
103
|
-
)
|
|
104
|
-
);
|
|
105
|
-
const ty = vm.typeof(h);
|
|
106
|
-
if (ty === "object" || ty === "function") disposables.push(h);
|
|
107
|
-
return [h, false];
|
|
108
|
-
});
|
|
65
|
+
test("function", async () => {
|
|
66
|
+
const { ctx, unmarshal, marshal, map, dispose } = await setup();
|
|
109
67
|
|
|
110
|
-
const handle =
|
|
111
|
-
|
|
112
|
-
);
|
|
113
|
-
const map = new VMMap(vm);
|
|
114
|
-
const find = jest.fn((h) => map.getByHandle(h));
|
|
115
|
-
const pre = jest.fn((t: any, h: QuickJSHandle) => {
|
|
116
|
-
map.set(t, h);
|
|
117
|
-
return t;
|
|
118
|
-
});
|
|
119
|
-
const func = unmarshal(handle, { vm, find, pre, marshal });
|
|
68
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`(function(a) { return a.a + "!"; })`));
|
|
69
|
+
const func = unmarshal(handle);
|
|
120
70
|
const arg = { a: "hoge" };
|
|
121
71
|
expect(func(arg)).toBe("hoge!");
|
|
122
72
|
expect(marshal).toBeCalledTimes(2);
|
|
123
73
|
expect(marshal).toBeCalledWith(undefined); // this
|
|
124
74
|
expect(marshal).toBeCalledWith(arg); // arg
|
|
125
|
-
expect(map.size).toBe(
|
|
75
|
+
expect(map.size).toBe(3);
|
|
126
76
|
expect(map.getByHandle(handle)).toBe(func);
|
|
77
|
+
expect(map.has(func)).toBe(true);
|
|
127
78
|
expect(map.has(func.prototype)).toBe(true);
|
|
79
|
+
expect(map.has(arg)).toBe(true);
|
|
128
80
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
81
|
+
dispose();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("promise", async () => {
|
|
85
|
+
const { ctx, unmarshal, dispose } = await setup();
|
|
86
|
+
|
|
87
|
+
const deferred = ctx.newPromise();
|
|
88
|
+
const promise = unmarshal(deferred.handle);
|
|
89
|
+
deferred.resolve(ctx.newString("resolved!"));
|
|
90
|
+
ctx.runtime.executePendingJobs();
|
|
91
|
+
await expect(promise).resolves.toBe("resolved!");
|
|
92
|
+
|
|
93
|
+
const deferred2 = ctx.newPromise();
|
|
94
|
+
const promise2 = unmarshal(deferred2.handle);
|
|
95
|
+
deferred2.reject(ctx.newString("rejected!"));
|
|
96
|
+
ctx.runtime.executePendingJobs();
|
|
97
|
+
await expect(promise2).rejects.toBe("rejected!");
|
|
98
|
+
|
|
99
|
+
deferred.dispose();
|
|
100
|
+
deferred2.dispose();
|
|
101
|
+
dispose();
|
|
133
102
|
});
|
|
134
103
|
|
|
135
104
|
test("class", async () => {
|
|
136
|
-
const
|
|
137
|
-
const jsonParse = vm.unwrapResult(vm.evalCode(`JSON.parse`));
|
|
138
|
-
const disposables: QuickJSHandle[] = [];
|
|
139
|
-
const map = new VMMap(vm);
|
|
140
|
-
const marshal = jest.fn((t: unknown): [QuickJSHandle, boolean] => {
|
|
141
|
-
const h = vm.unwrapResult(
|
|
142
|
-
vm.callFunction(jsonParse, vm.undefined, vm.newString(JSON.stringify(t)))
|
|
143
|
-
);
|
|
144
|
-
const ty = vm.typeof(h);
|
|
145
|
-
if (ty === "object" || ty === "function") disposables.push(h);
|
|
146
|
-
return [h, false];
|
|
147
|
-
});
|
|
105
|
+
const { ctx, unmarshal, dispose } = await setup();
|
|
148
106
|
|
|
149
|
-
const handle =
|
|
150
|
-
|
|
107
|
+
const handle = ctx.unwrapResult(
|
|
108
|
+
ctx.evalCode(`{
|
|
151
109
|
class Cls {
|
|
152
110
|
static hoge = "foo";
|
|
153
111
|
|
|
@@ -158,25 +116,72 @@ test("class", async () => {
|
|
|
158
116
|
Cls.foo = new Cls(1);
|
|
159
117
|
|
|
160
118
|
Cls
|
|
161
|
-
}`)
|
|
119
|
+
}`),
|
|
162
120
|
);
|
|
163
|
-
const
|
|
164
|
-
const pre = jest.fn((t: any, h: QuickJSHandle) => {
|
|
165
|
-
map.set(t, h);
|
|
166
|
-
return t;
|
|
167
|
-
});
|
|
168
|
-
const Cls = unmarshal(handle, { vm, find, pre, marshal });
|
|
121
|
+
const Cls = unmarshal(handle);
|
|
169
122
|
|
|
170
123
|
expect(Cls.hoge).toBe("foo");
|
|
171
|
-
expect(Cls.foo
|
|
124
|
+
expect(Cls.foo).toBeInstanceOf(Cls);
|
|
172
125
|
expect(Cls.foo.foo).toBe(3);
|
|
173
126
|
const cls = new Cls(2);
|
|
174
|
-
expect(cls
|
|
127
|
+
expect(cls).toBeInstanceOf(Cls);
|
|
175
128
|
expect(cls.foo).toBe(4);
|
|
176
129
|
|
|
177
130
|
handle.dispose();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
131
|
+
dispose();
|
|
132
|
+
});
|
|
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();
|
|
182
146
|
});
|
|
147
|
+
|
|
148
|
+
const setup = async () => {
|
|
149
|
+
const ctx = (await getQuickJS()).newContext();
|
|
150
|
+
const map = new VMMap(ctx);
|
|
151
|
+
const disposables: QuickJSHandle[] = [];
|
|
152
|
+
const marshal = vi.fn((target: unknown): [QuickJSHandle, boolean] => {
|
|
153
|
+
const handle = map.get(target);
|
|
154
|
+
if (handle) return [handle, false];
|
|
155
|
+
|
|
156
|
+
const handle2 =
|
|
157
|
+
typeof target === "function"
|
|
158
|
+
? ctx.newFunction(target.name, (...handles) => {
|
|
159
|
+
target(...handles.map(h => ctx.dump(h)));
|
|
160
|
+
})
|
|
161
|
+
: json(ctx, target);
|
|
162
|
+
const ty = ctx.typeof(handle2);
|
|
163
|
+
if (ty === "object" || ty === "function") map.set(target, handle2);
|
|
164
|
+
return [handle2, false];
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
ctx,
|
|
169
|
+
map,
|
|
170
|
+
unmarshal: (handle: QuickJSHandle) =>
|
|
171
|
+
unmarshal(handle, {
|
|
172
|
+
find: h => map.getByHandle(h),
|
|
173
|
+
marshal,
|
|
174
|
+
pre: (t, h) => {
|
|
175
|
+
map.set(t, h);
|
|
176
|
+
return t;
|
|
177
|
+
},
|
|
178
|
+
ctx: ctx,
|
|
179
|
+
}),
|
|
180
|
+
marshal,
|
|
181
|
+
dispose: () => {
|
|
182
|
+
disposables.forEach(d => d.dispose());
|
|
183
|
+
map.dispose();
|
|
184
|
+
ctx.dispose();
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
};
|
package/src/unmarshal/index.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
|
+
import unmarshalCustom, { defaultCustom } from "./custom";
|
|
2
4
|
import unmarshalFunction from "./function";
|
|
3
5
|
import unmarshalObject from "./object";
|
|
4
6
|
import unmarshalPrimitive from "./primitive";
|
|
5
|
-
import
|
|
7
|
+
import unmarshalPromise from "./promise";
|
|
6
8
|
|
|
7
9
|
export type Options = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
ctx: QuickJSContext;
|
|
11
|
+
/** marshal returns handle and boolean indicates that the handle should be disposed after use */
|
|
10
12
|
marshal: (target: unknown) => [QuickJSHandle, boolean];
|
|
11
13
|
find: (handle: QuickJSHandle) => unknown | undefined;
|
|
12
|
-
pre: <T>(target: T, handle: QuickJSHandle) => T | undefined;
|
|
14
|
+
pre: <T = unknown>(target: T, handle: QuickJSHandle) => T | undefined;
|
|
15
|
+
custom?: Iterable<(obj: QuickJSHandle, ctx: QuickJSContext) => any>;
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
export function unmarshal(handle: QuickJSHandle, options: Options): any {
|
|
@@ -17,14 +20,11 @@ export function unmarshal(handle: QuickJSHandle, options: Options): any {
|
|
|
17
20
|
return result;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
function unmarshalInner(
|
|
21
|
-
|
|
22
|
-
options: Options
|
|
23
|
-
): [any, boolean] {
|
|
24
|
-
const { vm, marshal, find, pre } = options;
|
|
23
|
+
function unmarshalInner(handle: QuickJSHandle, options: Options): [any, boolean] {
|
|
24
|
+
const { ctx, marshal, find, pre } = options;
|
|
25
25
|
|
|
26
26
|
{
|
|
27
|
-
const [target, ok] = unmarshalPrimitive(
|
|
27
|
+
const [target, ok] = unmarshalPrimitive(ctx, handle);
|
|
28
28
|
if (ok) return [target, false];
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -38,9 +38,10 @@ function unmarshalInner(
|
|
|
38
38
|
const unmarshal2 = (h: QuickJSHandle) => unmarshalInner(h, options);
|
|
39
39
|
|
|
40
40
|
const result =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
unmarshalCustom(ctx, handle, pre, [...defaultCustom, ...(options.custom ?? [])]) ??
|
|
42
|
+
unmarshalPromise(ctx, handle, marshal, pre) ??
|
|
43
|
+
unmarshalFunction(ctx, handle, marshal, unmarshal2, pre) ??
|
|
44
|
+
unmarshalObject(ctx, handle, unmarshal2, pre);
|
|
44
45
|
|
|
45
46
|
return [result, false];
|
|
46
47
|
}
|