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
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test, vi } from "vitest";
|
|
3
|
+
|
|
2
4
|
import unmarshalObject from "./object";
|
|
3
5
|
|
|
4
6
|
test("normal object", async () => {
|
|
5
|
-
const
|
|
6
|
-
const unmarshal =
|
|
7
|
-
|
|
8
|
-
false,
|
|
9
|
-
]);
|
|
10
|
-
const preUnmarshal = jest.fn((a) => a);
|
|
7
|
+
const ctx = (await getQuickJS()).newContext();
|
|
8
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => [ctx.dump(v), false]);
|
|
9
|
+
const preUnmarshal = vi.fn(a => a);
|
|
11
10
|
|
|
12
|
-
const handle =
|
|
13
|
-
const obj = unmarshalObject(
|
|
11
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`({ a: 1, b: true })`));
|
|
12
|
+
const obj = unmarshalObject(ctx, handle, unmarshal, preUnmarshal);
|
|
14
13
|
if (!obj) throw new Error("obj is undefined");
|
|
15
14
|
expect(obj).toEqual({ a: 1, b: true });
|
|
16
15
|
expect(unmarshal).toReturnTimes(4);
|
|
@@ -22,20 +21,20 @@ test("normal object", async () => {
|
|
|
22
21
|
expect(preUnmarshal).toBeCalledWith(obj, handle);
|
|
23
22
|
|
|
24
23
|
handle.dispose();
|
|
25
|
-
|
|
24
|
+
ctx.dispose();
|
|
26
25
|
});
|
|
27
26
|
|
|
28
27
|
test("properties", async () => {
|
|
29
|
-
const
|
|
28
|
+
const ctx = (await getQuickJS()).newContext();
|
|
30
29
|
const disposables: QuickJSHandle[] = [];
|
|
31
|
-
const unmarshal =
|
|
30
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
|
|
32
31
|
disposables.push(v);
|
|
33
|
-
return [
|
|
32
|
+
return [ctx.typeof(v) === "function" ? () => {} : ctx.dump(v), false];
|
|
34
33
|
});
|
|
35
|
-
const preUnmarshal =
|
|
34
|
+
const preUnmarshal = vi.fn(a => a);
|
|
36
35
|
|
|
37
|
-
const handle =
|
|
38
|
-
|
|
36
|
+
const handle = ctx.unwrapResult(
|
|
37
|
+
ctx.evalCode(`{
|
|
39
38
|
const obj = {};
|
|
40
39
|
Object.defineProperties(obj, {
|
|
41
40
|
a: { value: 1, writable: true, configurable: true, enumerable: true },
|
|
@@ -43,9 +42,9 @@ test("properties", async () => {
|
|
|
43
42
|
c: { get: () => {}, set: () => {} },
|
|
44
43
|
});
|
|
45
44
|
obj
|
|
46
|
-
}`)
|
|
45
|
+
}`),
|
|
47
46
|
);
|
|
48
|
-
const obj = unmarshalObject(
|
|
47
|
+
const obj = unmarshalObject(ctx, handle, unmarshal, preUnmarshal);
|
|
49
48
|
if (!obj) throw new Error("obj is undefined");
|
|
50
49
|
expect(obj).toEqual({
|
|
51
50
|
a: 1,
|
|
@@ -70,21 +69,18 @@ test("properties", async () => {
|
|
|
70
69
|
expect(preUnmarshal).toBeCalledTimes(1);
|
|
71
70
|
expect(preUnmarshal).toBeCalledWith(obj, handle);
|
|
72
71
|
|
|
73
|
-
disposables.forEach(
|
|
72
|
+
disposables.forEach(d => d.dispose());
|
|
74
73
|
handle.dispose();
|
|
75
|
-
|
|
74
|
+
ctx.dispose();
|
|
76
75
|
});
|
|
77
76
|
|
|
78
77
|
test("array", async () => {
|
|
79
|
-
const
|
|
80
|
-
const unmarshal =
|
|
81
|
-
|
|
82
|
-
false,
|
|
83
|
-
]);
|
|
84
|
-
const preUnmarshal = jest.fn((a) => a);
|
|
78
|
+
const ctx = (await getQuickJS()).newContext();
|
|
79
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => [ctx.dump(v), false]);
|
|
80
|
+
const preUnmarshal = vi.fn(a => a);
|
|
85
81
|
|
|
86
|
-
const handle =
|
|
87
|
-
const array = unmarshalObject(
|
|
82
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`[1, true, "a"]`));
|
|
83
|
+
const array = unmarshalObject(ctx, handle, unmarshal, preUnmarshal);
|
|
88
84
|
expect((array as any)[0]).toEqual(1);
|
|
89
85
|
expect(Array.isArray(array)).toBe(true);
|
|
90
86
|
expect(unmarshal.mock.results[0].value).toEqual(["0", false]);
|
|
@@ -98,19 +94,19 @@ test("array", async () => {
|
|
|
98
94
|
expect(preUnmarshal).toBeCalledWith(array, handle);
|
|
99
95
|
|
|
100
96
|
handle.dispose();
|
|
101
|
-
|
|
97
|
+
ctx.dispose();
|
|
102
98
|
});
|
|
103
99
|
|
|
104
100
|
test("prototype", async () => {
|
|
105
|
-
const
|
|
106
|
-
const unmarshal =
|
|
107
|
-
|
|
101
|
+
const ctx = (await getQuickJS()).newContext();
|
|
102
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => [
|
|
103
|
+
ctx.typeof(v) === "object" ? { a: () => 1 } : ctx.dump(v),
|
|
108
104
|
false,
|
|
109
105
|
]);
|
|
110
|
-
const preUnmarshal =
|
|
106
|
+
const preUnmarshal = vi.fn(a => a);
|
|
111
107
|
|
|
112
|
-
const handle =
|
|
113
|
-
const obj = unmarshalObject(
|
|
108
|
+
const handle = ctx.unwrapResult(ctx.evalCode(`Object.create({ a: () => 1 })`));
|
|
109
|
+
const obj = unmarshalObject(ctx, handle, unmarshal, preUnmarshal) as any;
|
|
114
110
|
if (!obj) throw new Error("obj is undefined");
|
|
115
111
|
expect(Object.getPrototypeOf(obj)).toEqual({ a: expect.any(Function) });
|
|
116
112
|
expect(obj.a()).toBe(1);
|
|
@@ -120,25 +116,25 @@ test("prototype", async () => {
|
|
|
120
116
|
expect(preUnmarshal).toBeCalledWith(obj, handle);
|
|
121
117
|
|
|
122
118
|
handle.dispose();
|
|
123
|
-
|
|
119
|
+
ctx.dispose();
|
|
124
120
|
});
|
|
125
121
|
|
|
126
122
|
test("undefined", async () => {
|
|
127
|
-
const
|
|
128
|
-
const f =
|
|
123
|
+
const ctx = (await getQuickJS()).newContext();
|
|
124
|
+
const f = vi.fn();
|
|
129
125
|
|
|
130
|
-
expect(unmarshalObject(
|
|
131
|
-
expect(unmarshalObject(
|
|
132
|
-
expect(unmarshalObject(
|
|
133
|
-
expect(unmarshalObject(
|
|
134
|
-
expect(unmarshalObject(
|
|
135
|
-
expect(unmarshalObject(
|
|
126
|
+
expect(unmarshalObject(ctx, ctx.undefined, f, f)).toEqual(undefined);
|
|
127
|
+
expect(unmarshalObject(ctx, ctx.true, f, f)).toEqual(undefined);
|
|
128
|
+
expect(unmarshalObject(ctx, ctx.false, f, f)).toEqual(undefined);
|
|
129
|
+
expect(unmarshalObject(ctx, ctx.null, f, f)).toEqual(undefined);
|
|
130
|
+
expect(unmarshalObject(ctx, ctx.newString("hoge"), f, f)).toEqual(undefined);
|
|
131
|
+
expect(unmarshalObject(ctx, ctx.newNumber(-10), f, f)).toEqual(undefined);
|
|
136
132
|
|
|
137
|
-
const func =
|
|
138
|
-
expect(unmarshalObject(
|
|
133
|
+
const func = ctx.newFunction("", () => {});
|
|
134
|
+
expect(unmarshalObject(ctx, func, f, f)).toEqual(undefined);
|
|
139
135
|
|
|
140
136
|
expect(f).toBeCalledTimes(0);
|
|
141
137
|
|
|
142
138
|
func.dispose();
|
|
143
|
-
|
|
139
|
+
ctx.dispose();
|
|
144
140
|
});
|
package/src/unmarshal/object.ts
CHANGED
|
@@ -1,41 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
3
|
import { call } from "../vmutil";
|
|
4
4
|
|
|
5
|
+
import unmarshalProperties from "./properties";
|
|
6
|
+
|
|
5
7
|
export default function unmarshalObject(
|
|
6
|
-
|
|
8
|
+
ctx: QuickJSContext,
|
|
7
9
|
handle: QuickJSHandle,
|
|
8
10
|
unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
|
|
9
|
-
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined
|
|
11
|
+
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
|
|
10
12
|
): object | undefined {
|
|
11
13
|
if (
|
|
12
|
-
|
|
14
|
+
ctx.typeof(handle) !== "object" ||
|
|
13
15
|
// null check
|
|
14
|
-
|
|
15
|
-
.unwrapResult(
|
|
16
|
-
.consume((n)
|
|
17
|
-
vm.dump(vm.unwrapResult(vm.callFunction(n, vm.undefined, handle)))
|
|
18
|
-
)
|
|
16
|
+
ctx
|
|
17
|
+
.unwrapResult(ctx.evalCode("o => o === null"))
|
|
18
|
+
.consume(n => ctx.dump(ctx.unwrapResult(ctx.callFunction(n, ctx.undefined, handle))))
|
|
19
19
|
)
|
|
20
20
|
return;
|
|
21
21
|
|
|
22
|
-
const raw = call(
|
|
23
|
-
vm.dump(r)
|
|
24
|
-
)
|
|
25
|
-
? []
|
|
26
|
-
: {};
|
|
22
|
+
const raw = call(ctx, "Array.isArray", undefined, handle).consume(r => ctx.dump(r)) ? [] : {};
|
|
27
23
|
const obj = preUnmarshal(raw, handle) ?? raw;
|
|
28
24
|
|
|
29
25
|
const prototype = call(
|
|
30
|
-
|
|
26
|
+
ctx,
|
|
31
27
|
`o => {
|
|
32
28
|
const p = Object.getPrototypeOf(o);
|
|
33
29
|
return !p || p === Object.prototype || p === Array.prototype ? undefined : p;
|
|
34
30
|
}`,
|
|
35
31
|
undefined,
|
|
36
|
-
handle
|
|
37
|
-
).consume(
|
|
38
|
-
if (
|
|
32
|
+
handle,
|
|
33
|
+
).consume(prototype => {
|
|
34
|
+
if (ctx.typeof(prototype) === "undefined") return;
|
|
39
35
|
const [proto] = unmarshal(prototype);
|
|
40
36
|
return proto;
|
|
41
37
|
});
|
|
@@ -43,7 +39,7 @@ export default function unmarshalObject(
|
|
|
43
39
|
Object.setPrototypeOf(obj, prototype);
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
unmarshalProperties(
|
|
42
|
+
unmarshalProperties(ctx, handle, raw, unmarshal);
|
|
47
43
|
|
|
48
44
|
return obj;
|
|
49
45
|
}
|
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
import { getQuickJS } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test } from "vitest";
|
|
3
|
+
|
|
2
4
|
import unmarshalPrimitive from "./primitive";
|
|
3
5
|
|
|
4
6
|
test("works", async () => {
|
|
5
|
-
const
|
|
7
|
+
const ctx = (await getQuickJS()).newContext();
|
|
6
8
|
|
|
7
|
-
expect(unmarshalPrimitive(
|
|
8
|
-
expect(unmarshalPrimitive(
|
|
9
|
-
expect(unmarshalPrimitive(
|
|
10
|
-
expect(unmarshalPrimitive(
|
|
11
|
-
expect(unmarshalPrimitive(
|
|
12
|
-
expect(unmarshalPrimitive(
|
|
9
|
+
expect(unmarshalPrimitive(ctx, ctx.undefined)).toEqual([undefined, true]);
|
|
10
|
+
expect(unmarshalPrimitive(ctx, ctx.true)).toEqual([true, true]);
|
|
11
|
+
expect(unmarshalPrimitive(ctx, ctx.false)).toEqual([false, true]);
|
|
12
|
+
expect(unmarshalPrimitive(ctx, ctx.null)).toEqual([null, true]);
|
|
13
|
+
expect(unmarshalPrimitive(ctx, ctx.newString("hoge"))).toEqual(["hoge", true]);
|
|
14
|
+
expect(unmarshalPrimitive(ctx, ctx.newNumber(-10))).toEqual([-10, true]);
|
|
13
15
|
// expect(
|
|
14
|
-
// unmarshalPrimitive(
|
|
16
|
+
// unmarshalPrimitive(ctx, ctx.unwrapResult(vm.evalCode(`BigInt(1)`)))
|
|
15
17
|
// ).toEqual([BigInt(1), true]);
|
|
16
18
|
|
|
17
|
-
const obj =
|
|
18
|
-
expect(unmarshalPrimitive(
|
|
19
|
-
const array =
|
|
20
|
-
expect(unmarshalPrimitive(
|
|
21
|
-
const func =
|
|
22
|
-
expect(unmarshalPrimitive(
|
|
19
|
+
const obj = ctx.newObject();
|
|
20
|
+
expect(unmarshalPrimitive(ctx, obj)).toEqual([undefined, false]);
|
|
21
|
+
const array = ctx.newArray();
|
|
22
|
+
expect(unmarshalPrimitive(ctx, array)).toEqual([undefined, false]);
|
|
23
|
+
const func = ctx.newFunction("", () => {});
|
|
24
|
+
expect(unmarshalPrimitive(ctx, func)).toEqual([undefined, false]);
|
|
23
25
|
|
|
24
26
|
obj.dispose();
|
|
25
27
|
array.dispose();
|
|
26
28
|
func.dispose();
|
|
27
|
-
|
|
29
|
+
ctx.dispose();
|
|
28
30
|
});
|
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
|
|
3
3
|
export default function unmarshalPrimitive(
|
|
4
|
-
|
|
5
|
-
handle: QuickJSHandle
|
|
4
|
+
ctx: QuickJSContext,
|
|
5
|
+
handle: QuickJSHandle,
|
|
6
6
|
): [any, boolean] {
|
|
7
|
-
const ty =
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
ty === "number" ||
|
|
11
|
-
ty === "string" ||
|
|
12
|
-
ty === "boolean"
|
|
13
|
-
) {
|
|
14
|
-
return [vm.dump(handle), true];
|
|
7
|
+
const ty = ctx.typeof(handle);
|
|
8
|
+
if (ty === "undefined" || ty === "number" || ty === "string" || ty === "boolean") {
|
|
9
|
+
return [ctx.dump(handle), true];
|
|
15
10
|
} else if (ty === "object") {
|
|
16
|
-
const isNull =
|
|
17
|
-
.unwrapResult(
|
|
18
|
-
.consume((n)
|
|
19
|
-
vm.dump(vm.unwrapResult(vm.callFunction(n, vm.undefined, handle)))
|
|
20
|
-
);
|
|
11
|
+
const isNull = ctx
|
|
12
|
+
.unwrapResult(ctx.evalCode("a => a === null"))
|
|
13
|
+
.consume(n => ctx.dump(ctx.unwrapResult(ctx.callFunction(n, ctx.undefined, handle))));
|
|
21
14
|
if (isNull) {
|
|
22
15
|
return [null, true];
|
|
23
16
|
}
|
|
@@ -25,10 +18,10 @@ export default function unmarshalPrimitive(
|
|
|
25
18
|
|
|
26
19
|
// BigInt is not supported by quickjs-emscripten
|
|
27
20
|
// if (ty === "bigint") {
|
|
28
|
-
// const str =
|
|
21
|
+
// const str = ctx
|
|
29
22
|
// .getProp(handle, "toString")
|
|
30
23
|
// .consume(toString => vm.unwrapResult(vm.callFunction(toString, handle)))
|
|
31
|
-
// .consume(str =>
|
|
24
|
+
// .consume(str => ctx.getString(str));
|
|
32
25
|
// const bi = BigInt(str);
|
|
33
26
|
// return [bi, true];
|
|
34
27
|
// }
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Disposable, getQuickJS, type QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import unmarshalPromise from "./promise";
|
|
5
|
+
|
|
6
|
+
const testPromise = (reject: boolean) => async () => {
|
|
7
|
+
const ctx = (await getQuickJS()).newContext();
|
|
8
|
+
const disposables: Disposable[] = [];
|
|
9
|
+
const marshal = vi.fn((v): [QuickJSHandle, boolean] => {
|
|
10
|
+
const f = ctx.newFunction(v.name, h => {
|
|
11
|
+
v(ctx.dump(h));
|
|
12
|
+
});
|
|
13
|
+
disposables.push(f);
|
|
14
|
+
return [f, false];
|
|
15
|
+
});
|
|
16
|
+
const preUnmarshal = vi.fn(a => a);
|
|
17
|
+
|
|
18
|
+
const deferred = ctx.newPromise();
|
|
19
|
+
disposables.push(deferred);
|
|
20
|
+
const promise = unmarshalPromise(ctx, deferred.handle, marshal, preUnmarshal);
|
|
21
|
+
|
|
22
|
+
expect(marshal).toBeCalledTimes(2);
|
|
23
|
+
expect(preUnmarshal).toBeCalledTimes(1);
|
|
24
|
+
expect(ctx.runtime.hasPendingJob()).toBe(false);
|
|
25
|
+
|
|
26
|
+
if (reject) {
|
|
27
|
+
deferred.reject(ctx.newString("hoge"));
|
|
28
|
+
} else {
|
|
29
|
+
deferred.resolve(ctx.newString("hoge"));
|
|
30
|
+
}
|
|
31
|
+
expect(ctx.runtime.hasPendingJob()).toBe(true);
|
|
32
|
+
expect(ctx.unwrapResult(ctx.runtime.executePendingJobs())).toBe(1);
|
|
33
|
+
if (reject) {
|
|
34
|
+
expect(promise).rejects.toThrow("hoge");
|
|
35
|
+
} else {
|
|
36
|
+
expect(promise).resolves.toBe("hoge");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
disposables.forEach(d => d.dispose());
|
|
40
|
+
ctx.dispose();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
test("resolve", testPromise(false));
|
|
44
|
+
test("reject", testPromise(true));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
|
+
import { newDeferred } from "../util";
|
|
4
|
+
import { call, instanceOf } from "../vmutil";
|
|
5
|
+
|
|
6
|
+
export default function unmarshalPromise<T = unknown>(
|
|
7
|
+
ctx: QuickJSContext,
|
|
8
|
+
handle: QuickJSHandle,
|
|
9
|
+
/** marshal returns handle and boolean indicates that the handle should be disposed after use */
|
|
10
|
+
marshal: (value: unknown) => [QuickJSHandle, boolean],
|
|
11
|
+
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
|
|
12
|
+
): Promise<T> | undefined {
|
|
13
|
+
if (!isPromiseHandle(ctx, handle)) return;
|
|
14
|
+
|
|
15
|
+
const deferred = newDeferred<T>();
|
|
16
|
+
const [resHandle, resShouldBeDisposed] = marshal(deferred.resolve);
|
|
17
|
+
const [rejHandle, rejShouldBeDisposed] = marshal(deferred.reject);
|
|
18
|
+
call(ctx, "(p, res, rej) => { p.then(res, rej); }", undefined, handle, resHandle, rejHandle);
|
|
19
|
+
if (resShouldBeDisposed) resHandle.dispose();
|
|
20
|
+
if (rejShouldBeDisposed) rejHandle.dispose();
|
|
21
|
+
|
|
22
|
+
return preUnmarshal(deferred.promise, handle) ?? deferred.promise;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isPromiseHandle(ctx: QuickJSContext, handle: QuickJSHandle): boolean {
|
|
26
|
+
if (!handle.owner) return false;
|
|
27
|
+
return ctx.unwrapResult(ctx.evalCode("Promise")).consume(promise => {
|
|
28
|
+
if (!handle.owner) return false;
|
|
29
|
+
return instanceOf(ctx, handle, promise);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test, vi } from "vitest";
|
|
3
|
+
|
|
2
4
|
import unmarshalProperties from "./properties";
|
|
3
5
|
|
|
4
6
|
test("works", async () => {
|
|
5
|
-
const
|
|
7
|
+
const ctx = (await getQuickJS()).newContext();
|
|
6
8
|
const disposables: QuickJSHandle[] = [];
|
|
7
|
-
const unmarshal =
|
|
9
|
+
const unmarshal = vi.fn((v: QuickJSHandle): [unknown, boolean] => {
|
|
8
10
|
disposables.push(v);
|
|
9
|
-
return [
|
|
11
|
+
return [ctx.typeof(v) === "function" ? () => {} : ctx.dump(v), false];
|
|
10
12
|
});
|
|
11
13
|
const obj = {};
|
|
12
14
|
|
|
13
|
-
const handle =
|
|
14
|
-
|
|
15
|
+
const handle = ctx.unwrapResult(
|
|
16
|
+
ctx.evalCode(`{
|
|
15
17
|
const obj = {};
|
|
16
18
|
Object.defineProperties(obj, {
|
|
17
19
|
a: { value: 1, writable: true, configurable: true, enumerable: true },
|
|
@@ -19,10 +21,10 @@ test("works", async () => {
|
|
|
19
21
|
c: { get: () => {}, set: () => {} },
|
|
20
22
|
});
|
|
21
23
|
obj
|
|
22
|
-
}`)
|
|
24
|
+
}`),
|
|
23
25
|
);
|
|
24
26
|
|
|
25
|
-
unmarshalProperties(
|
|
27
|
+
unmarshalProperties(ctx, handle, obj, unmarshal);
|
|
26
28
|
|
|
27
29
|
expect(obj).toEqual({
|
|
28
30
|
a: 1,
|
|
@@ -45,7 +47,7 @@ test("works", async () => {
|
|
|
45
47
|
expect(unmarshal).toReturnWith(["c", false]);
|
|
46
48
|
expect(unmarshal).toReturnWith([expect.any(Function), false]); // get, set
|
|
47
49
|
|
|
48
|
-
disposables.forEach(
|
|
50
|
+
disposables.forEach(d => d.dispose());
|
|
49
51
|
handle.dispose();
|
|
50
|
-
|
|
52
|
+
ctx.dispose();
|
|
51
53
|
});
|
|
@@ -1,61 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
2
3
|
import { call } from "../vmutil";
|
|
3
4
|
|
|
4
5
|
export default function unmarshalProperties(
|
|
5
|
-
|
|
6
|
+
ctx: QuickJSContext,
|
|
6
7
|
handle: QuickJSHandle,
|
|
7
8
|
target: object | Function,
|
|
8
|
-
unmarshal: (handle: QuickJSHandle) => [unknown, boolean]
|
|
9
|
+
unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
|
|
9
10
|
) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
typeof keyName !== "string" &&
|
|
14
|
-
|
|
15
|
-
typeof keyName !== "symbol"
|
|
16
|
-
)
|
|
17
|
-
return;
|
|
11
|
+
ctx
|
|
12
|
+
.newFunction("", (key, value) => {
|
|
13
|
+
const [keyName] = unmarshal(key);
|
|
14
|
+
if (typeof keyName !== "string" && typeof keyName !== "number" && typeof keyName !== "symbol")
|
|
15
|
+
return;
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
const desc = (
|
|
18
|
+
[
|
|
19
|
+
["value", true],
|
|
20
|
+
["get", true],
|
|
21
|
+
["set", true],
|
|
22
|
+
["configurable", false],
|
|
23
|
+
["enumerable", false],
|
|
24
|
+
["writable", false],
|
|
25
|
+
] as const
|
|
26
|
+
).reduce<PropertyDescriptor>((desc, [key, unmarshable]) => {
|
|
27
|
+
const h = ctx.getProp(value, key);
|
|
28
|
+
const ty = ctx.typeof(h);
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
if (ty === "undefined") return desc;
|
|
31
|
+
if (!unmarshable && ty === "boolean") {
|
|
32
|
+
desc[key] = ctx.dump(ctx.getProp(value, key));
|
|
33
|
+
return desc;
|
|
34
|
+
}
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
const [v, alreadyExists] = unmarshal(h);
|
|
37
|
+
if (alreadyExists) {
|
|
38
|
+
h.dispose();
|
|
39
|
+
}
|
|
40
|
+
desc[key] = v;
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
return desc;
|
|
43
|
+
}, {});
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
Object.defineProperty(target, keyName, desc);
|
|
46
|
+
})
|
|
47
|
+
.consume(fn => {
|
|
48
|
+
call(
|
|
49
|
+
ctx,
|
|
50
|
+
`(o, fn) => {
|
|
52
51
|
const descs = Object.getOwnPropertyDescriptors(o);
|
|
53
52
|
Object.entries(descs).forEach(([k, v]) => fn(k, v));
|
|
54
53
|
Object.getOwnPropertySymbols(descs).forEach(k => fn(k, descs[k]));
|
|
55
54
|
}`,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
undefined,
|
|
56
|
+
handle,
|
|
57
|
+
fn,
|
|
58
|
+
).dispose();
|
|
59
|
+
});
|
|
61
60
|
}
|
package/src/util.test.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { expect, test, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { isES2015Class, isObject, walkObject, complexity, newDeferred } from "./util";
|
|
2
4
|
|
|
3
5
|
test("isES2015Class", () => {
|
|
4
6
|
expect(isES2015Class(class {})).toBe(true);
|
|
@@ -25,7 +27,7 @@ test("isObject", () => {
|
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
test("walkObject", () => {
|
|
28
|
-
const cb =
|
|
30
|
+
const cb = vi.fn();
|
|
29
31
|
const obj = { a: { b: 1, c: () => {} } };
|
|
30
32
|
const set = new Set<any>([obj, obj.a, obj.a.c]);
|
|
31
33
|
expect(walkObject(obj, cb)).toEqual(set);
|
|
@@ -52,3 +54,13 @@ test("complexity", () => {
|
|
|
52
54
|
expect(complexity({ a: {} })).toBe(2);
|
|
53
55
|
expect(complexity({ a: {} }, 1)).toBe(1);
|
|
54
56
|
});
|
|
57
|
+
|
|
58
|
+
test("newDeferred", () => {
|
|
59
|
+
const deferred = newDeferred();
|
|
60
|
+
deferred.resolve("foo");
|
|
61
|
+
expect(deferred.promise).resolves.toBe("foo");
|
|
62
|
+
|
|
63
|
+
const deferred2 = newDeferred();
|
|
64
|
+
deferred2.reject("bar");
|
|
65
|
+
expect(deferred2.promise).rejects.toBe("bar");
|
|
66
|
+
});
|
package/src/util.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
export function isES2015Class(cls: any): cls is new (...args: any[]) => any {
|
|
2
|
-
return (
|
|
3
|
-
typeof cls === "function" &&
|
|
4
|
-
/^class\s/.test(Function.prototype.toString.call(cls))
|
|
5
|
-
);
|
|
2
|
+
return typeof cls === "function" && /^class\s/.test(Function.prototype.toString.call(cls));
|
|
6
3
|
}
|
|
7
4
|
|
|
8
5
|
export function isObject(value: any): value is object | Function {
|
|
9
|
-
return (
|
|
10
|
-
typeof value === "function" || (typeof value === "object" && value !== null)
|
|
11
|
-
);
|
|
6
|
+
return typeof value === "function" || (typeof value === "object" && value !== null);
|
|
12
7
|
}
|
|
13
8
|
|
|
14
9
|
export function walkObject(
|
|
15
10
|
value: any,
|
|
16
|
-
callback?: (target: any, set: Set<any>) => boolean | void
|
|
11
|
+
callback?: (target: any, set: Set<any>) => boolean | void,
|
|
17
12
|
): Set<any> {
|
|
18
13
|
const set = new Set<any>();
|
|
19
14
|
const walk = (v: any) => {
|
|
@@ -51,3 +46,18 @@ export function walkObject(
|
|
|
51
46
|
export function complexity(value: any, max?: number): number {
|
|
52
47
|
return walkObject(value, max ? (_, set) => set.size < max : undefined).size;
|
|
53
48
|
}
|
|
49
|
+
|
|
50
|
+
export function newDeferred<T = unknown>() {
|
|
51
|
+
let res: (v: T | PromiseLike<T>) => void = () => {};
|
|
52
|
+
let rej: (v: T | PromiseLike<T>) => void = () => {};
|
|
53
|
+
const promise = new Promise<T>((resolve, reject) => {
|
|
54
|
+
res = resolve;
|
|
55
|
+
rej = reject;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
promise,
|
|
60
|
+
resolve: res,
|
|
61
|
+
reject: rej,
|
|
62
|
+
};
|
|
63
|
+
}
|