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.
- package/README.md +60 -37
- package/dist/index.d.ts +29 -22
- package/dist/quickjs-emscripten-sync.mjs +842 -0
- package/dist/quickjs-emscripten-sync.umd.js +15 -15
- package/package.json +12 -21
- package/src/default.ts +2 -2
- package/src/index.test.ts +159 -65
- package/src/index.ts +67 -66
- package/src/marshal/custom.test.ts +50 -0
- package/src/marshal/custom.ts +36 -0
- package/src/marshal/function.test.ts +60 -72
- package/src/marshal/function.ts +15 -18
- package/src/marshal/index.test.ts +88 -84
- package/src/marshal/index.ts +16 -18
- package/src/marshal/json.test.ts +32 -38
- package/src/marshal/json.ts +5 -7
- package/src/marshal/object.test.ts +52 -79
- package/src/marshal/object.ts +8 -15
- package/src/marshal/primitive.test.ts +16 -21
- package/src/marshal/primitive.ts +11 -10
- package/src/marshal/promise.test.ts +21 -22
- package/src/marshal/promise.ts +6 -13
- package/src/marshal/properties.test.ts +21 -26
- 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 +52 -74
- package/src/unmarshal/function.ts +13 -22
- package/src/unmarshal/index.test.ts +58 -50
- package/src/unmarshal/index.ts +12 -13
- package/src/unmarshal/object.test.ts +37 -43
- package/src/unmarshal/object.ts +16 -20
- package/src/unmarshal/primitive.test.ts +15 -15
- package/src/unmarshal/primitive.ts +11 -18
- package/src/unmarshal/promise.test.ts +13 -13
- package/src/unmarshal/promise.ts +10 -19
- package/src/unmarshal/properties.test.ts +8 -8
- package/src/unmarshal/properties.ts +44 -45
- package/src/util.test.ts +2 -7
- package/src/util.ts +3 -8
- package/src/vmmap.test.ts +72 -88
- package/src/vmmap.ts +26 -44
- package/src/vmutil.test.ts +73 -89
- package/src/vmutil.ts +24 -47
- package/src/wrapper.test.ts +108 -148
- package/src/wrapper.ts +59 -62
- package/dist/quickjs-emscripten-sync.es.js +0 -951
- package/src/marshal/symbol.test.ts +0 -24
- package/src/marshal/symbol.ts +0 -20
- package/src/unmarshal/symbol.test.ts +0 -25
- package/src/unmarshal/symbol.ts +0 -12
package/src/marshal/function.ts
CHANGED
|
@@ -1,43 +1,40 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { isES2015Class, isObject } from "../util";
|
|
3
4
|
import { call } from "../vmutil";
|
|
5
|
+
|
|
4
6
|
import marshalProperties from "./properties";
|
|
5
7
|
|
|
6
8
|
export default function marshalFunction(
|
|
7
|
-
|
|
9
|
+
ctx: QuickJSContext,
|
|
8
10
|
target: unknown,
|
|
9
11
|
marshal: (target: unknown) => QuickJSHandle,
|
|
10
12
|
unmarshal: (handle: QuickJSHandle) => unknown,
|
|
11
|
-
preMarshal: (
|
|
12
|
-
|
|
13
|
-
handle: QuickJSHandle
|
|
14
|
-
) => QuickJSHandle | undefined,
|
|
15
|
-
preApply?: (target: Function, thisArg: unknown, args: unknown[]) => any
|
|
13
|
+
preMarshal: (target: unknown, handle: QuickJSHandle) => QuickJSHandle | undefined,
|
|
14
|
+
preApply?: (target: Function, thisArg: unknown, args: unknown[]) => any,
|
|
16
15
|
): QuickJSHandle | undefined {
|
|
17
16
|
if (typeof target !== "function") return;
|
|
18
17
|
|
|
19
|
-
const raw =
|
|
18
|
+
const raw = ctx
|
|
20
19
|
.newFunction(target.name, function (...argHandles) {
|
|
21
20
|
const that = unmarshal(this);
|
|
22
|
-
const args = argHandles.map(
|
|
21
|
+
const args = argHandles.map(a => unmarshal(a));
|
|
23
22
|
|
|
24
23
|
if (isES2015Class(target) && isObject(that)) {
|
|
25
24
|
// Class constructors cannot be invoked without new expression, and new.target is not changed
|
|
26
25
|
const result = new target(...args);
|
|
27
26
|
Object.entries(result).forEach(([key, value]) => {
|
|
28
|
-
|
|
27
|
+
ctx.setProp(this, key, marshal(value));
|
|
29
28
|
});
|
|
30
29
|
return this;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
return marshal(
|
|
34
|
-
preApply ? preApply(target, that, args) : target.apply(that, args)
|
|
35
|
-
);
|
|
32
|
+
return marshal(preApply ? preApply(target, that, args) : target.apply(that, args));
|
|
36
33
|
})
|
|
37
|
-
.consume(
|
|
34
|
+
.consume(handle2 =>
|
|
38
35
|
// fucntions created by vm.newFunction are not callable as a class constrcutor
|
|
39
36
|
call(
|
|
40
|
-
|
|
37
|
+
ctx,
|
|
41
38
|
`Cls => {
|
|
42
39
|
const fn = function(...args) { return Cls.apply(this, args); };
|
|
43
40
|
fn.name = Cls.name;
|
|
@@ -45,12 +42,12 @@ export default function marshalFunction(
|
|
|
45
42
|
return fn;
|
|
46
43
|
}`,
|
|
47
44
|
undefined,
|
|
48
|
-
handle2
|
|
49
|
-
)
|
|
45
|
+
handle2,
|
|
46
|
+
),
|
|
50
47
|
);
|
|
51
48
|
|
|
52
49
|
const handle = preMarshal(target, raw) ?? raw;
|
|
53
|
-
marshalProperties(
|
|
50
|
+
marshalProperties(ctx, target, raw, marshal);
|
|
54
51
|
|
|
55
52
|
return handle;
|
|
56
53
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { getQuickJS } from "quickjs-emscripten";
|
|
2
2
|
import { expect, test, vi } from "vitest";
|
|
3
3
|
|
|
4
|
+
import { newDeferred } from "../util";
|
|
4
5
|
import VMMap from "../vmmap";
|
|
5
6
|
import { instanceOf, call, handleFrom, fn } from "../vmutil";
|
|
7
|
+
|
|
6
8
|
import marshal from ".";
|
|
7
|
-
import { newDeferred } from "../util";
|
|
8
9
|
|
|
9
10
|
test("primitive, array, object", async () => {
|
|
10
|
-
const {
|
|
11
|
+
const { ctx, map, marshal, dispose } = await setup();
|
|
11
12
|
|
|
12
13
|
const target = {
|
|
13
14
|
hoge: "foo",
|
|
@@ -17,7 +18,7 @@ test("primitive, array, object", async () => {
|
|
|
17
18
|
};
|
|
18
19
|
const handle = marshal(target);
|
|
19
20
|
|
|
20
|
-
expect(
|
|
21
|
+
expect(ctx.dump(handle)).toEqual(target);
|
|
21
22
|
expect(map.size).toBe(4);
|
|
22
23
|
expect(map.get(target)).toBe(handle);
|
|
23
24
|
expect(map.has(target.aaa)).toBe(true);
|
|
@@ -28,37 +29,33 @@ test("primitive, array, object", async () => {
|
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
test("object with symbol key", async () => {
|
|
31
|
-
const {
|
|
32
|
+
const { ctx, marshal, dispose } = await setup();
|
|
32
33
|
|
|
33
34
|
const target = {
|
|
34
35
|
foo: "hoge",
|
|
35
36
|
[Symbol("a")]: 1,
|
|
36
37
|
};
|
|
37
38
|
const handle = marshal(target);
|
|
38
|
-
expect(
|
|
39
|
-
expect(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
).toBe(1);
|
|
39
|
+
expect(ctx.dump(call(ctx, `a => a.foo`, undefined, handle))).toBe("hoge");
|
|
40
|
+
expect(ctx.dump(call(ctx, `a => a[Object.getOwnPropertySymbols(a)[0]]`, undefined, handle))).toBe(
|
|
41
|
+
1,
|
|
42
|
+
);
|
|
44
43
|
|
|
45
44
|
dispose();
|
|
46
45
|
});
|
|
47
46
|
|
|
48
47
|
test("arrow function", async () => {
|
|
49
|
-
const {
|
|
48
|
+
const { ctx, map, marshal, dispose } = await setup();
|
|
50
49
|
const hoge = () => "foo";
|
|
51
50
|
hoge.foo = { bar: 1 };
|
|
52
51
|
const handle = marshal(hoge);
|
|
53
52
|
|
|
54
|
-
expect(
|
|
55
|
-
expect(
|
|
56
|
-
expect(
|
|
57
|
-
const foo =
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
60
|
-
"foo"
|
|
61
|
-
);
|
|
53
|
+
expect(ctx.typeof(handle)).toBe("function");
|
|
54
|
+
expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(0);
|
|
55
|
+
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("hoge");
|
|
56
|
+
const foo = ctx.getProp(handle, "foo");
|
|
57
|
+
expect(ctx.dump(foo)).toEqual({ bar: 1 });
|
|
58
|
+
expect(ctx.dump(ctx.unwrapResult(ctx.callFunction(handle, ctx.undefined)))).toBe("foo");
|
|
62
59
|
expect(map.size).toBe(2);
|
|
63
60
|
expect(map.get(hoge)).toBe(handle);
|
|
64
61
|
expect(map.has(hoge.foo)).toBe(true);
|
|
@@ -68,25 +65,23 @@ test("arrow function", async () => {
|
|
|
68
65
|
});
|
|
69
66
|
|
|
70
67
|
test("function", async () => {
|
|
71
|
-
const {
|
|
68
|
+
const { ctx, map, marshal, dispose } = await setup();
|
|
72
69
|
|
|
73
70
|
const bar = function (a: number, b: { hoge: number }) {
|
|
74
71
|
return a + b.hoge;
|
|
75
72
|
};
|
|
76
73
|
const handle = marshal(bar);
|
|
77
74
|
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
80
|
-
expect(
|
|
75
|
+
expect(ctx.typeof(handle)).toBe("function");
|
|
76
|
+
expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(2);
|
|
77
|
+
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("bar");
|
|
81
78
|
expect(map.size).toBe(2);
|
|
82
79
|
expect(map.get(bar)).toBe(handle);
|
|
83
80
|
expect(map.has(bar.prototype)).toBe(true);
|
|
84
81
|
|
|
85
|
-
const b =
|
|
82
|
+
const b = ctx.unwrapResult(ctx.evalCode(`({ hoge: 2 })`));
|
|
86
83
|
expect(
|
|
87
|
-
|
|
88
|
-
vm.unwrapResult(vm.callFunction(handle, vm.undefined, vm.newNumber(1), b))
|
|
89
|
-
)
|
|
84
|
+
ctx.dump(ctx.unwrapResult(ctx.callFunction(handle, ctx.undefined, ctx.newNumber(1), b))),
|
|
90
85
|
).toBe(3);
|
|
91
86
|
|
|
92
87
|
b.dispose();
|
|
@@ -94,18 +89,20 @@ test("function", async () => {
|
|
|
94
89
|
});
|
|
95
90
|
|
|
96
91
|
test("promise", async () => {
|
|
97
|
-
const {
|
|
92
|
+
const { ctx, marshal, dispose } = await setup();
|
|
98
93
|
const register = fn(
|
|
99
|
-
|
|
100
|
-
`promise => { promise.then(d => notify("resolve", d), d => notify("reject", d)); }
|
|
94
|
+
ctx,
|
|
95
|
+
`promise => { promise.then(d => notify("resolve", d), d => notify("reject", d)); }`,
|
|
101
96
|
);
|
|
102
97
|
|
|
103
98
|
let notified: any;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
ctx
|
|
100
|
+
.newFunction("notify", (...handles) => {
|
|
101
|
+
notified = handles.map(h => ctx.dump(h));
|
|
102
|
+
})
|
|
103
|
+
.consume(h => {
|
|
104
|
+
ctx.setProp(ctx.global, "notify", h);
|
|
105
|
+
});
|
|
109
106
|
|
|
110
107
|
const deferred = newDeferred();
|
|
111
108
|
const handle = marshal(deferred.promise);
|
|
@@ -113,7 +110,7 @@ test("promise", async () => {
|
|
|
113
110
|
|
|
114
111
|
deferred.resolve("foo");
|
|
115
112
|
await deferred.promise;
|
|
116
|
-
expect(
|
|
113
|
+
expect(ctx.unwrapResult(ctx.runtime.executePendingJobs())).toBe(1);
|
|
117
114
|
expect(notified).toEqual(["resolve", "foo"]);
|
|
118
115
|
|
|
119
116
|
const deferred2 = newDeferred();
|
|
@@ -122,7 +119,7 @@ test("promise", async () => {
|
|
|
122
119
|
|
|
123
120
|
deferred2.reject("bar");
|
|
124
121
|
await expect(deferred2.promise).rejects.toBe("bar");
|
|
125
|
-
expect(
|
|
122
|
+
expect(ctx.unwrapResult(ctx.runtime.executePendingJobs())).toBe(1);
|
|
126
123
|
expect(notified).toEqual(["reject", "bar"]);
|
|
127
124
|
|
|
128
125
|
register.dispose();
|
|
@@ -130,7 +127,7 @@ test("promise", async () => {
|
|
|
130
127
|
});
|
|
131
128
|
|
|
132
129
|
test("class", async () => {
|
|
133
|
-
const {
|
|
130
|
+
const { ctx, map, marshal, dispose } = await setup();
|
|
134
131
|
|
|
135
132
|
class A {
|
|
136
133
|
a: number;
|
|
@@ -165,41 +162,33 @@ test("class", async () => {
|
|
|
165
162
|
expect(map.has(A.prototype)).toBe(true);
|
|
166
163
|
expect(map.has(A.a)).toBe(true);
|
|
167
164
|
expect(map.has(A.prototype.hoge)).toBe(true);
|
|
168
|
-
expect(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
expect(
|
|
172
|
-
|
|
173
|
-
).toBe(
|
|
174
|
-
|
|
175
|
-
expect(
|
|
176
|
-
expect(
|
|
177
|
-
expect(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
expect(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
expect(
|
|
186
|
-
expect(
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
expect(vm.dump(vm.getProp(instance, "a"))).toBe(100); // not synced
|
|
193
|
-
|
|
194
|
-
const getter = vm.unwrapResult(vm.evalCode(`a => a.foo`));
|
|
195
|
-
const setter = vm.unwrapResult(vm.evalCode(`(a, b) => a.foo = b`));
|
|
196
|
-
expect(
|
|
197
|
-
vm.dump(vm.unwrapResult(vm.callFunction(getter, vm.undefined, instance)))
|
|
198
|
-
).toBe("foo!");
|
|
199
|
-
vm.unwrapResult(
|
|
200
|
-
vm.callFunction(setter, vm.undefined, instance, vm.newString("b"))
|
|
165
|
+
expect(map.has(Object.getOwnPropertyDescriptor(A.prototype, "foo")?.get)).toBe(true);
|
|
166
|
+
expect(map.has(Object.getOwnPropertyDescriptor(A.prototype, "foo")?.set)).toBe(true);
|
|
167
|
+
|
|
168
|
+
expect(ctx.typeof(handle)).toBe("function");
|
|
169
|
+
expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(1);
|
|
170
|
+
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("_A");
|
|
171
|
+
const staticA = ctx.getProp(handle, "a");
|
|
172
|
+
expect(instanceOf(ctx, staticA, handle)).toBe(true);
|
|
173
|
+
expect(ctx.dump(ctx.getProp(staticA, "a"))).toBe(100);
|
|
174
|
+
expect(ctx.dump(ctx.getProp(staticA, "b"))).toBe("a!");
|
|
175
|
+
|
|
176
|
+
const newA = ctx.unwrapResult(ctx.evalCode(`A => new A("foo")`));
|
|
177
|
+
const instance = ctx.unwrapResult(ctx.callFunction(newA, ctx.undefined, handle));
|
|
178
|
+
expect(instanceOf(ctx, instance, handle)).toBe(true);
|
|
179
|
+
expect(ctx.dump(ctx.getProp(instance, "a"))).toBe(100);
|
|
180
|
+
expect(ctx.dump(ctx.getProp(instance, "b"))).toBe("foo!");
|
|
181
|
+
const methodHoge = ctx.getProp(instance, "hoge");
|
|
182
|
+
expect(ctx.dump(ctx.unwrapResult(ctx.callFunction(methodHoge, instance)))).toBe(101);
|
|
183
|
+
expect(ctx.dump(ctx.getProp(instance, "a"))).toBe(100); // not synced
|
|
184
|
+
|
|
185
|
+
const getter = ctx.unwrapResult(ctx.evalCode(`a => a.foo`));
|
|
186
|
+
const setter = ctx.unwrapResult(ctx.evalCode(`(a, b) => a.foo = b`));
|
|
187
|
+
expect(ctx.dump(ctx.unwrapResult(ctx.callFunction(getter, ctx.undefined, instance)))).toBe(
|
|
188
|
+
"foo!",
|
|
201
189
|
);
|
|
202
|
-
|
|
190
|
+
ctx.unwrapResult(ctx.callFunction(setter, ctx.undefined, instance, ctx.newString("b")));
|
|
191
|
+
expect(ctx.dump(ctx.getProp(instance, "b"))).toBe("foo!"); // not synced
|
|
203
192
|
|
|
204
193
|
staticA.dispose();
|
|
205
194
|
newA.dispose();
|
|
@@ -207,19 +196,34 @@ test("class", async () => {
|
|
|
207
196
|
methodHoge.dispose();
|
|
208
197
|
getter.dispose();
|
|
209
198
|
setter.dispose();
|
|
210
|
-
|
|
199
|
+
dispose();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("date", async () => {
|
|
203
|
+
const { ctx, map, marshal, dispose } = await setup();
|
|
204
|
+
|
|
205
|
+
const date = new Date(2022, 7, 26);
|
|
206
|
+
const handle = marshal(date);
|
|
207
|
+
if (!map) throw new Error("map is undefined");
|
|
208
|
+
|
|
209
|
+
expect(map.size).toBe(1);
|
|
210
|
+
expect(map.get(date)).toBe(handle);
|
|
211
|
+
|
|
212
|
+
expect(ctx.dump(call(ctx, "d => d instanceof Date", undefined, handle))).toBe(true);
|
|
213
|
+
expect(ctx.dump(call(ctx, "d => d.getTime()", undefined, handle))).toBe(date.getTime());
|
|
214
|
+
|
|
211
215
|
dispose();
|
|
212
216
|
});
|
|
213
217
|
|
|
214
218
|
test("marshalable", async () => {
|
|
215
219
|
const isMarshalable = vi.fn((a: any) => a !== globalThis);
|
|
216
|
-
const {
|
|
220
|
+
const { ctx, marshal, dispose } = await setup({
|
|
217
221
|
isMarshalable,
|
|
218
222
|
});
|
|
219
223
|
|
|
220
224
|
const handle = marshal({ a: globalThis, b: 1 });
|
|
221
225
|
|
|
222
|
-
expect(
|
|
226
|
+
expect(ctx.dump(handle)).toEqual({ a: undefined, b: 1 });
|
|
223
227
|
expect(isMarshalable).toBeCalledWith(globalThis);
|
|
224
228
|
expect(isMarshalable).toReturnWith(false);
|
|
225
229
|
|
|
@@ -228,7 +232,7 @@ test("marshalable", async () => {
|
|
|
228
232
|
|
|
229
233
|
test("marshalable json", async () => {
|
|
230
234
|
const isMarshalable = vi.fn(() => "json" as const);
|
|
231
|
-
const {
|
|
235
|
+
const { ctx, marshal, dispose } = await setup({
|
|
232
236
|
isMarshalable,
|
|
233
237
|
});
|
|
234
238
|
|
|
@@ -239,7 +243,7 @@ test("marshalable json", async () => {
|
|
|
239
243
|
};
|
|
240
244
|
const handle = marshal(target);
|
|
241
245
|
|
|
242
|
-
expect(
|
|
246
|
+
expect(ctx.dump(handle)).toEqual({
|
|
243
247
|
a: { d: target.a.d.toISOString(), e: [null, 1, {}] },
|
|
244
248
|
b: 1,
|
|
245
249
|
});
|
|
@@ -255,26 +259,26 @@ const setup = async ({
|
|
|
255
259
|
}: {
|
|
256
260
|
isMarshalable?: (target: any) => boolean | "json";
|
|
257
261
|
} = {}) => {
|
|
258
|
-
const
|
|
259
|
-
const map = new VMMap(
|
|
262
|
+
const ctx = (await getQuickJS()).newContext();
|
|
263
|
+
const map = new VMMap(ctx);
|
|
260
264
|
return {
|
|
261
|
-
|
|
265
|
+
ctx,
|
|
262
266
|
map,
|
|
263
267
|
marshal: (v: any) =>
|
|
264
268
|
marshal(v, {
|
|
265
|
-
|
|
266
|
-
unmarshal:
|
|
269
|
+
ctx: ctx,
|
|
270
|
+
unmarshal: h => map.getByHandle(h) ?? ctx.dump(h),
|
|
267
271
|
isMarshalable,
|
|
268
272
|
pre: (t, d) => {
|
|
269
273
|
const h = handleFrom(d);
|
|
270
274
|
map.set(t, h);
|
|
271
275
|
return h;
|
|
272
276
|
},
|
|
273
|
-
find:
|
|
277
|
+
find: t => map.get(t),
|
|
274
278
|
}),
|
|
275
279
|
dispose: () => {
|
|
276
280
|
map.dispose();
|
|
277
|
-
|
|
281
|
+
ctx.dispose();
|
|
278
282
|
},
|
|
279
283
|
};
|
|
280
284
|
};
|
package/src/marshal/index.ts
CHANGED
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
QuickJSVm,
|
|
5
|
-
} from "quickjs-emscripten";
|
|
1
|
+
import type { QuickJSDeferredPromise, QuickJSHandle, QuickJSContext } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
|
+
import marshalCustom, { defaultCustom } from "./custom";
|
|
6
4
|
import marshalFunction from "./function";
|
|
5
|
+
import marshalJSON from "./json";
|
|
7
6
|
import marshalObject from "./object";
|
|
8
7
|
import marshalPrimitive from "./primitive";
|
|
9
|
-
import marshalSymbol from "./symbol";
|
|
10
|
-
import marshalJSON from "./json";
|
|
11
8
|
import marshalPromise from "./promise";
|
|
12
9
|
|
|
13
10
|
export type Options = {
|
|
14
|
-
|
|
11
|
+
ctx: QuickJSContext;
|
|
15
12
|
unmarshal: (handle: QuickJSHandle) => unknown;
|
|
16
13
|
isMarshalable?: (target: unknown) => boolean | "json";
|
|
17
14
|
find: (target: unknown) => QuickJSHandle | undefined;
|
|
18
15
|
pre: (
|
|
19
16
|
target: unknown,
|
|
20
17
|
handle: QuickJSHandle | QuickJSDeferredPromise,
|
|
21
|
-
mode: true | "json" | undefined
|
|
18
|
+
mode: true | "json" | undefined,
|
|
22
19
|
) => QuickJSHandle | undefined;
|
|
23
20
|
preApply?: (target: Function, thisArg: unknown, args: unknown[]) => any;
|
|
21
|
+
custom?: Iterable<(obj: unknown, ctx: QuickJSContext) => QuickJSHandle | undefined>;
|
|
24
22
|
};
|
|
25
23
|
|
|
26
24
|
export function marshal(target: unknown, options: Options): QuickJSHandle {
|
|
27
|
-
const {
|
|
25
|
+
const { ctx, unmarshal, isMarshalable, find, pre } = options;
|
|
28
26
|
|
|
29
27
|
{
|
|
30
|
-
const primitive = marshalPrimitive(
|
|
28
|
+
const primitive = marshalPrimitive(ctx, target);
|
|
31
29
|
if (primitive) {
|
|
32
30
|
return primitive;
|
|
33
31
|
}
|
|
@@ -40,22 +38,22 @@ export function marshal(target: unknown, options: Options): QuickJSHandle {
|
|
|
40
38
|
|
|
41
39
|
const marshalable = isMarshalable?.(target);
|
|
42
40
|
if (marshalable === false) {
|
|
43
|
-
return
|
|
41
|
+
return ctx.undefined;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
const pre2 = (target: any, handle: QuickJSHandle | QuickJSDeferredPromise) =>
|
|
47
45
|
pre(target, handle, marshalable);
|
|
48
46
|
if (marshalable === "json") {
|
|
49
|
-
return marshalJSON(
|
|
47
|
+
return marshalJSON(ctx, target, pre2);
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
const marshal2 = (t: unknown) => marshal(t, options);
|
|
53
51
|
return (
|
|
54
|
-
|
|
55
|
-
marshalPromise(
|
|
56
|
-
marshalFunction(
|
|
57
|
-
marshalObject(
|
|
58
|
-
|
|
52
|
+
marshalCustom(ctx, target, pre2, [...defaultCustom, ...(options.custom ?? [])]) ??
|
|
53
|
+
marshalPromise(ctx, target, marshal2, pre2) ??
|
|
54
|
+
marshalFunction(ctx, target, marshal2, unmarshal, pre2, options.preApply) ??
|
|
55
|
+
marshalObject(ctx, target, marshal2, pre2) ??
|
|
56
|
+
ctx.undefined
|
|
59
57
|
);
|
|
60
58
|
}
|
|
61
59
|
|
package/src/marshal/json.test.ts
CHANGED
|
@@ -4,58 +4,54 @@ import { expect, test, vi } from "vitest";
|
|
|
4
4
|
import marshalJSON from "./json";
|
|
5
5
|
|
|
6
6
|
test("empty object", async () => {
|
|
7
|
-
const
|
|
8
|
-
const prototypeCheck =
|
|
9
|
-
|
|
7
|
+
const ctx = (await getQuickJS()).newContext();
|
|
8
|
+
const prototypeCheck = ctx.unwrapResult(
|
|
9
|
+
ctx.evalCode(`o => Object.getPrototypeOf(o) === Object.prototype`),
|
|
10
10
|
);
|
|
11
11
|
|
|
12
12
|
const obj = {};
|
|
13
13
|
const preMarshal = vi.fn((_, a) => a);
|
|
14
14
|
|
|
15
|
-
const handle = marshalJSON(
|
|
15
|
+
const handle = marshalJSON(ctx, obj, preMarshal);
|
|
16
16
|
if (!handle) throw new Error("handle is undefined");
|
|
17
17
|
|
|
18
|
-
expect(
|
|
18
|
+
expect(ctx.typeof(handle)).toBe("object");
|
|
19
19
|
expect(preMarshal).toBeCalledTimes(1);
|
|
20
20
|
expect(preMarshal.mock.calls[0][0]).toBe(obj);
|
|
21
21
|
expect(preMarshal.mock.calls[0][1] === handle).toBe(true); // avoid freeze
|
|
22
|
-
expect(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
)
|
|
26
|
-
).toBe(true);
|
|
22
|
+
expect(ctx.dump(ctx.unwrapResult(ctx.callFunction(prototypeCheck, ctx.undefined, handle)))).toBe(
|
|
23
|
+
true,
|
|
24
|
+
);
|
|
27
25
|
|
|
28
26
|
handle.dispose();
|
|
29
27
|
prototypeCheck.dispose();
|
|
30
|
-
|
|
28
|
+
ctx.dispose();
|
|
31
29
|
});
|
|
32
30
|
|
|
33
31
|
test("normal object", async () => {
|
|
34
|
-
const
|
|
35
|
-
const prototypeCheck =
|
|
36
|
-
|
|
32
|
+
const ctx = (await getQuickJS()).newContext();
|
|
33
|
+
const prototypeCheck = ctx.unwrapResult(
|
|
34
|
+
ctx.evalCode(`o => Object.getPrototypeOf(o) === Object.prototype`),
|
|
37
35
|
);
|
|
38
|
-
const entries =
|
|
36
|
+
const entries = ctx.unwrapResult(ctx.evalCode(`Object.entries`));
|
|
39
37
|
|
|
40
38
|
const obj = { a: 100, b: "hoge" };
|
|
41
39
|
const preMarshal = vi.fn((_, a) => a);
|
|
42
40
|
|
|
43
|
-
const handle = marshalJSON(
|
|
41
|
+
const handle = marshalJSON(ctx, obj, preMarshal);
|
|
44
42
|
if (!handle) throw new Error("handle is undefined");
|
|
45
43
|
|
|
46
|
-
expect(
|
|
47
|
-
expect(
|
|
48
|
-
expect(
|
|
44
|
+
expect(ctx.typeof(handle)).toBe("object");
|
|
45
|
+
expect(ctx.getNumber(ctx.getProp(handle, "a"))).toBe(100);
|
|
46
|
+
expect(ctx.getString(ctx.getProp(handle, "b"))).toBe("hoge");
|
|
49
47
|
expect(preMarshal).toBeCalledTimes(1);
|
|
50
48
|
expect(preMarshal.mock.calls[0][0]).toBe(obj);
|
|
51
49
|
expect(preMarshal.mock.calls[0][1] === handle).toBe(true); // avoid freeze
|
|
52
|
-
expect(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
).
|
|
57
|
-
const e = vm.unwrapResult(vm.callFunction(entries, vm.undefined, handle));
|
|
58
|
-
expect(vm.dump(e)).toEqual([
|
|
50
|
+
expect(ctx.dump(ctx.unwrapResult(ctx.callFunction(prototypeCheck, ctx.undefined, handle)))).toBe(
|
|
51
|
+
true,
|
|
52
|
+
);
|
|
53
|
+
const e = ctx.unwrapResult(ctx.callFunction(entries, ctx.undefined, handle));
|
|
54
|
+
expect(ctx.dump(e)).toEqual([
|
|
59
55
|
["a", 100],
|
|
60
56
|
["b", "hoge"],
|
|
61
57
|
]);
|
|
@@ -64,31 +60,29 @@ test("normal object", async () => {
|
|
|
64
60
|
handle.dispose();
|
|
65
61
|
prototypeCheck.dispose();
|
|
66
62
|
entries.dispose();
|
|
67
|
-
|
|
63
|
+
ctx.dispose();
|
|
68
64
|
});
|
|
69
65
|
|
|
70
66
|
test("array", async () => {
|
|
71
|
-
const
|
|
72
|
-
const isArray =
|
|
67
|
+
const ctx = (await getQuickJS()).newContext();
|
|
68
|
+
const isArray = ctx.unwrapResult(ctx.evalCode(`Array.isArray`));
|
|
73
69
|
|
|
74
70
|
const array = [1, "aa"];
|
|
75
71
|
const preMarshal = vi.fn((_, a) => a);
|
|
76
72
|
|
|
77
|
-
const handle = marshalJSON(
|
|
73
|
+
const handle = marshalJSON(ctx, array, preMarshal);
|
|
78
74
|
if (!handle) throw new Error("handle is undefined");
|
|
79
75
|
|
|
80
|
-
expect(
|
|
81
|
-
expect(
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
76
|
+
expect(ctx.typeof(handle)).toBe("object");
|
|
77
|
+
expect(ctx.getNumber(ctx.getProp(handle, 0))).toBe(1);
|
|
78
|
+
expect(ctx.getString(ctx.getProp(handle, 1))).toBe("aa");
|
|
79
|
+
expect(ctx.getNumber(ctx.getProp(handle, "length"))).toBe(2);
|
|
84
80
|
expect(preMarshal).toBeCalledTimes(1);
|
|
85
81
|
expect(preMarshal.mock.calls[0][0]).toBe(array);
|
|
86
82
|
expect(preMarshal.mock.calls[0][1] === handle).toBe(true); // avoid freeze
|
|
87
|
-
expect(
|
|
88
|
-
vm.dump(vm.unwrapResult(vm.callFunction(isArray, vm.undefined, handle)))
|
|
89
|
-
).toBe(true);
|
|
83
|
+
expect(ctx.dump(ctx.unwrapResult(ctx.callFunction(isArray, ctx.undefined, handle)))).toBe(true);
|
|
90
84
|
|
|
91
85
|
handle.dispose();
|
|
92
86
|
isArray.dispose();
|
|
93
|
-
|
|
87
|
+
ctx.dispose();
|
|
94
88
|
});
|
package/src/marshal/json.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { json } from "../vmutil";
|
|
3
4
|
|
|
4
5
|
export default function marshalJSON(
|
|
5
|
-
|
|
6
|
+
ctx: QuickJSContext,
|
|
6
7
|
target: unknown,
|
|
7
|
-
preMarshal: (
|
|
8
|
-
target: unknown,
|
|
9
|
-
handle: QuickJSHandle
|
|
10
|
-
) => QuickJSHandle | undefined
|
|
8
|
+
preMarshal: (target: unknown, handle: QuickJSHandle) => QuickJSHandle | undefined,
|
|
11
9
|
): QuickJSHandle {
|
|
12
|
-
const raw = json(
|
|
10
|
+
const raw = json(ctx, target);
|
|
13
11
|
const handle = preMarshal(target, raw) ?? raw;
|
|
14
12
|
return handle;
|
|
15
13
|
}
|