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