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