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
package/src/index.test.ts
CHANGED
|
@@ -1,40 +1,129 @@
|
|
|
1
1
|
import { getQuickJS } from "quickjs-emscripten";
|
|
2
|
-
import
|
|
2
|
+
import { describe, expect, test, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { Arena } from ".";
|
|
5
|
+
import { isWrapped } from "./wrapper";
|
|
6
|
+
|
|
7
|
+
describe("readme", () => {
|
|
8
|
+
test("first", async () => {
|
|
9
|
+
class Cls {
|
|
10
|
+
field = 0;
|
|
11
|
+
|
|
12
|
+
method() {
|
|
13
|
+
return ++this.field;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ctx = (await getQuickJS()).newContext();
|
|
18
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
19
|
+
|
|
20
|
+
// We can pass objects to the VM and run code safely
|
|
21
|
+
const exposed = {
|
|
22
|
+
Cls,
|
|
23
|
+
cls: new Cls(),
|
|
24
|
+
syncedCls: arena.sync(new Cls()),
|
|
25
|
+
};
|
|
26
|
+
arena.expose(exposed);
|
|
27
|
+
|
|
28
|
+
expect(arena.evalCode(`cls instanceof Cls`)).toBe(true);
|
|
29
|
+
expect(arena.evalCode(`cls.field`)).toBe(0);
|
|
30
|
+
expect(arena.evalCode(`cls.method()`)).toBe(1);
|
|
31
|
+
expect(arena.evalCode(`cls.field`)).toBe(1);
|
|
32
|
+
|
|
33
|
+
expect(arena.evalCode(`syncedCls.field`)).toBe(0);
|
|
34
|
+
expect(exposed.syncedCls.method()).toBe(1);
|
|
35
|
+
expect(arena.evalCode(`syncedCls.field`)).toBe(1);
|
|
36
|
+
|
|
37
|
+
arena.dispose();
|
|
38
|
+
ctx.dispose();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("usage", async () => {
|
|
42
|
+
const quickjs = await getQuickJS();
|
|
43
|
+
const ctx = quickjs.newContext();
|
|
44
|
+
|
|
45
|
+
// init Arena
|
|
46
|
+
// ⚠️ Marshaling is opt-in for security reasons.
|
|
47
|
+
// ⚠️ Be careful when activating marshalling.
|
|
48
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
49
|
+
|
|
50
|
+
// expose objects as global objects in QuickJS VM
|
|
51
|
+
const log = vi.fn();
|
|
52
|
+
arena.expose({
|
|
53
|
+
console: { log },
|
|
54
|
+
});
|
|
55
|
+
arena.evalCode(`console.log("hello, world");`); // run console.log
|
|
56
|
+
expect(log).toBeCalledWith("hello, world");
|
|
57
|
+
arena.evalCode(`1 + 1`); // 2
|
|
58
|
+
|
|
59
|
+
// expose objects but also enable sync
|
|
60
|
+
const data = arena.sync({ hoge: "foo" });
|
|
61
|
+
arena.expose({ data });
|
|
62
|
+
|
|
63
|
+
arena.evalCode(`data.hoge = "bar"`);
|
|
64
|
+
// eval code and operations to exposed objects are automatically synced
|
|
65
|
+
expect(data.hoge).toBe("bar");
|
|
66
|
+
data.hoge = "changed!";
|
|
67
|
+
expect(arena.evalCode(`data.hoge`)).toBe("changed!");
|
|
68
|
+
|
|
69
|
+
// Don't forget calling arena.dispose() before disposing QuickJS VM!
|
|
70
|
+
arena.dispose();
|
|
71
|
+
ctx.dispose();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
3
74
|
|
|
4
75
|
describe("evalCode", () => {
|
|
5
76
|
test("simple object and function", async () => {
|
|
6
|
-
const
|
|
7
|
-
const arena = new Arena(
|
|
77
|
+
const ctx = (await getQuickJS()).newContext();
|
|
78
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
8
79
|
|
|
9
80
|
const result = arena.evalCode(
|
|
10
|
-
`({
|
|
81
|
+
`({
|
|
82
|
+
a: 1,
|
|
83
|
+
b: a => Math.floor(a),
|
|
84
|
+
c: () => { throw new Error("hoge") },
|
|
85
|
+
d: (yourFavoriteNumber) => ({
|
|
86
|
+
myFavoriteNumber: 42,
|
|
87
|
+
yourFavoriteNumber,
|
|
88
|
+
}),
|
|
89
|
+
get e() {
|
|
90
|
+
return { a: 1 };
|
|
91
|
+
}
|
|
92
|
+
})`
|
|
11
93
|
);
|
|
12
94
|
expect(result).toEqual({
|
|
13
95
|
a: 1,
|
|
14
96
|
b: expect.any(Function),
|
|
15
97
|
c: expect.any(Function),
|
|
98
|
+
d: expect.any(Function),
|
|
99
|
+
e: { a: 1 },
|
|
16
100
|
});
|
|
17
101
|
expect(result.b(1.1)).toBe(1);
|
|
18
102
|
expect(() => result.c()).toThrow("hoge");
|
|
103
|
+
expect(result.d(1)).toStrictEqual({
|
|
104
|
+
myFavoriteNumber: 42,
|
|
105
|
+
yourFavoriteNumber: 1,
|
|
106
|
+
});
|
|
107
|
+
expect(result.e).toStrictEqual({ a: 1 });
|
|
19
108
|
|
|
20
109
|
arena.dispose();
|
|
21
|
-
|
|
110
|
+
ctx.dispose();
|
|
22
111
|
});
|
|
23
112
|
|
|
24
113
|
test("Math", async () => {
|
|
25
|
-
const
|
|
26
|
-
const arena = new Arena(
|
|
114
|
+
const ctx = (await getQuickJS()).newContext();
|
|
115
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
27
116
|
|
|
28
117
|
const VMMath = arena.evalCode(`Math`) as Math;
|
|
29
118
|
expect(VMMath.floor(1.1)).toBe(1);
|
|
30
119
|
|
|
31
120
|
arena.dispose();
|
|
32
|
-
|
|
121
|
+
ctx.dispose();
|
|
33
122
|
});
|
|
34
123
|
|
|
35
124
|
test("class", async () => {
|
|
36
|
-
const
|
|
37
|
-
const arena = new Arena(
|
|
125
|
+
const ctx = (await getQuickJS()).newContext();
|
|
126
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
38
127
|
|
|
39
128
|
const instance = arena.evalCode(`{
|
|
40
129
|
globalThis.Cls = class D {
|
|
@@ -45,7 +134,7 @@ describe("evalCode", () => {
|
|
|
45
134
|
return ++this.a;
|
|
46
135
|
}
|
|
47
136
|
};
|
|
48
|
-
|
|
137
|
+
|
|
49
138
|
new Cls(100);
|
|
50
139
|
}`);
|
|
51
140
|
const Cls = arena.evalCode(`globalThis.Cls`);
|
|
@@ -55,12 +144,12 @@ describe("evalCode", () => {
|
|
|
55
144
|
expect(instance.a).toBe(102);
|
|
56
145
|
|
|
57
146
|
arena.dispose();
|
|
58
|
-
|
|
147
|
+
ctx.dispose();
|
|
59
148
|
});
|
|
60
149
|
|
|
61
150
|
test("obj", async () => {
|
|
62
|
-
const
|
|
63
|
-
const arena = new Arena(
|
|
151
|
+
const ctx = (await getQuickJS()).newContext();
|
|
152
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
64
153
|
|
|
65
154
|
const obj = arena.evalCode(`globalThis.AAA = { a: 1 }`);
|
|
66
155
|
|
|
@@ -71,47 +160,109 @@ describe("evalCode", () => {
|
|
|
71
160
|
expect(arena.evalCode(`AAA.a`)).toBe(2);
|
|
72
161
|
|
|
73
162
|
arena.dispose();
|
|
74
|
-
|
|
163
|
+
ctx.dispose();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("promise", async () => {
|
|
167
|
+
const ctx = (await getQuickJS()).newContext();
|
|
168
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
169
|
+
|
|
170
|
+
const [promise, resolve] = arena.evalCode<
|
|
171
|
+
[Promise<string>, (d: string) => void]
|
|
172
|
+
>(`
|
|
173
|
+
let resolve;
|
|
174
|
+
const promise = new Promise(r => {
|
|
175
|
+
resolve = r;
|
|
176
|
+
}).then(d => d + "!");
|
|
177
|
+
[promise, resolve]
|
|
178
|
+
`);
|
|
179
|
+
expect(promise).instanceOf(Promise);
|
|
180
|
+
expect(isWrapped(arena._unwrapIfNotSynced(promise), arena._symbol)).toBe(
|
|
181
|
+
false
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
resolve("hoge");
|
|
185
|
+
expect(arena.executePendingJobs()).toBe(2);
|
|
186
|
+
expect(await promise).toBe("hoge!");
|
|
187
|
+
|
|
188
|
+
arena.dispose();
|
|
189
|
+
ctx.dispose();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("promise2", async () => {
|
|
193
|
+
const ctx = (await getQuickJS()).newContext();
|
|
194
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
195
|
+
|
|
196
|
+
const deferred: { resolve?: (s: string) => void } = {};
|
|
197
|
+
const promise = new Promise((resolve) => {
|
|
198
|
+
deferred.resolve = resolve;
|
|
199
|
+
});
|
|
200
|
+
const res = vi.fn();
|
|
201
|
+
arena.evalCode(`(p, r) => { p.then(d => { r(d + "!"); }); }`)(promise, res);
|
|
202
|
+
|
|
203
|
+
deferred.resolve?.("hoge");
|
|
204
|
+
await promise;
|
|
205
|
+
expect(arena.executePendingJobs()).toBe(1);
|
|
206
|
+
expect(res).toBeCalledWith("hoge!");
|
|
207
|
+
|
|
208
|
+
arena.dispose();
|
|
209
|
+
ctx.dispose();
|
|
75
210
|
});
|
|
76
211
|
});
|
|
77
212
|
|
|
78
213
|
describe("expose without sync", () => {
|
|
79
214
|
test("simple object and function", async () => {
|
|
80
|
-
const
|
|
81
|
-
const arena = new Arena(
|
|
215
|
+
const ctx = (await getQuickJS()).newContext();
|
|
216
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
82
217
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
218
|
+
const obj = {
|
|
219
|
+
a: 1,
|
|
220
|
+
b: (a: number) => Math.floor(a),
|
|
221
|
+
c: () => {
|
|
222
|
+
throw new Error("hoge");
|
|
223
|
+
},
|
|
224
|
+
d: (yourFavoriteNumber: number) => ({
|
|
225
|
+
myFavoriteNumber: 42,
|
|
226
|
+
yourFavoriteNumber,
|
|
227
|
+
}),
|
|
228
|
+
get e() {
|
|
229
|
+
return { a: 1 };
|
|
90
230
|
},
|
|
231
|
+
};
|
|
232
|
+
arena.expose({
|
|
233
|
+
obj,
|
|
91
234
|
});
|
|
92
235
|
|
|
236
|
+
expect(arena.evalCode(`obj`)).toBe(obj);
|
|
93
237
|
expect(arena.evalCode(`obj.a`)).toBe(1);
|
|
94
238
|
expect(arena.evalCode(`obj.b(1.1)`)).toBe(1);
|
|
95
239
|
expect(() => arena.evalCode(`obj.c()`)).toThrow("hoge");
|
|
240
|
+
expect(arena.evalCode(`obj.d(1)`)).toStrictEqual({
|
|
241
|
+
myFavoriteNumber: 42,
|
|
242
|
+
yourFavoriteNumber: 1,
|
|
243
|
+
});
|
|
244
|
+
expect(arena.evalCode(`obj.e`)).toStrictEqual({ a: 1 });
|
|
96
245
|
|
|
97
246
|
arena.dispose();
|
|
98
|
-
|
|
247
|
+
ctx.dispose();
|
|
99
248
|
});
|
|
100
249
|
|
|
101
250
|
test("Math", async () => {
|
|
102
|
-
const
|
|
103
|
-
const arena = new Arena(
|
|
251
|
+
const ctx = (await getQuickJS()).newContext();
|
|
252
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
104
253
|
|
|
105
|
-
arena.expose({ Math });
|
|
106
|
-
expect(arena.evalCode(`Math
|
|
254
|
+
arena.expose({ Math2: Math });
|
|
255
|
+
expect(arena.evalCode(`Math`)).not.toBe(Math);
|
|
256
|
+
expect(arena.evalCode(`Math2`)).toBe(Math);
|
|
257
|
+
expect(arena.evalCode(`Math2.floor(1.1)`)).toBe(1);
|
|
107
258
|
|
|
108
259
|
arena.dispose();
|
|
109
|
-
|
|
260
|
+
ctx.dispose();
|
|
110
261
|
});
|
|
111
262
|
|
|
112
263
|
test("class", async () => {
|
|
113
|
-
const
|
|
114
|
-
const arena = new Arena(
|
|
264
|
+
const ctx = (await getQuickJS()).newContext();
|
|
265
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
115
266
|
|
|
116
267
|
class D {
|
|
117
268
|
a: number;
|
|
@@ -125,19 +276,22 @@ describe("expose without sync", () => {
|
|
|
125
276
|
}
|
|
126
277
|
}
|
|
127
278
|
|
|
128
|
-
|
|
279
|
+
const d = new D(100);
|
|
280
|
+
arena.expose({ D, d });
|
|
281
|
+
expect(arena.evalCode(`D`)).toBe(D);
|
|
282
|
+
expect(arena.evalCode(`d`)).toBe(d);
|
|
129
283
|
expect(arena.evalCode(`d instanceof D`)).toBe(true);
|
|
130
284
|
expect(arena.evalCode(`d.a`)).toBe(101);
|
|
131
285
|
expect(arena.evalCode(`d.foo()`)).toBe(102);
|
|
132
286
|
expect(arena.evalCode(`d.a`)).toBe(102);
|
|
133
287
|
|
|
134
288
|
arena.dispose();
|
|
135
|
-
|
|
289
|
+
ctx.dispose();
|
|
136
290
|
});
|
|
137
291
|
|
|
138
292
|
test("object and function", async () => {
|
|
139
|
-
const
|
|
140
|
-
const arena = new Arena(
|
|
293
|
+
const ctx = (await getQuickJS()).newContext();
|
|
294
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
141
295
|
|
|
142
296
|
const obj = {
|
|
143
297
|
a: 1,
|
|
@@ -148,7 +302,11 @@ describe("expose without sync", () => {
|
|
|
148
302
|
};
|
|
149
303
|
arena.expose({ obj });
|
|
150
304
|
|
|
305
|
+
expect(arena.evalCode(`obj`)).toBe(obj);
|
|
306
|
+
expect(arena.evalCode(`obj.a`)).toBe(1);
|
|
307
|
+
expect(arena.evalCode(`obj.b`)).toBe(obj.b);
|
|
151
308
|
expect(arena.evalCode(`obj.b(1.1)`)).toBe(1);
|
|
309
|
+
expect(arena.evalCode(`obj.c`)).toBe(obj.c);
|
|
152
310
|
expect(arena.evalCode(`obj.c()`)).toBe(1);
|
|
153
311
|
expect(arena.evalCode(`obj.a`)).toBe(2);
|
|
154
312
|
expect(obj.a).toBe(2);
|
|
@@ -165,14 +323,14 @@ describe("expose without sync", () => {
|
|
|
165
323
|
expect(arena.evalCode(`obj.a`)).toBe(100);
|
|
166
324
|
|
|
167
325
|
arena.dispose();
|
|
168
|
-
|
|
326
|
+
ctx.dispose();
|
|
169
327
|
});
|
|
170
328
|
});
|
|
171
329
|
|
|
172
330
|
describe("expose with sync", () => {
|
|
173
|
-
test("
|
|
174
|
-
const
|
|
175
|
-
const arena = new Arena(
|
|
331
|
+
test("sync before expose", async () => {
|
|
332
|
+
const ctx = (await getQuickJS()).newContext();
|
|
333
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
176
334
|
|
|
177
335
|
const obj = {
|
|
178
336
|
a: 1,
|
|
@@ -184,7 +342,61 @@ describe("expose with sync", () => {
|
|
|
184
342
|
const obj2 = arena.sync(obj);
|
|
185
343
|
arena.expose({ obj: obj2 });
|
|
186
344
|
|
|
345
|
+
const obj3 = arena.evalCode(`obj`);
|
|
346
|
+
expect(obj3).toBe(obj2);
|
|
347
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj.c); // wrapped object
|
|
348
|
+
expect(arena.evalCode(`obj.b`)).not.toBe(obj2.b); // wrapped object
|
|
349
|
+
expect(arena.evalCode(`obj.b`)).not.toBe(obj3.b); // wrapped object
|
|
350
|
+
expect(arena.evalCode(`obj.b(1.1)`)).toBe(1);
|
|
351
|
+
expect(arena.evalCode(`obj.a`)).toBe(1);
|
|
352
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj.c); // wrapped object
|
|
353
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj2.c); // wrapped object
|
|
354
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj3.c); // wrapped object
|
|
355
|
+
expect(arena.evalCode(`obj.c()`)).toBe(1);
|
|
356
|
+
expect(arena.evalCode(`obj.a`)).toBe(2);
|
|
357
|
+
expect(obj.a).toBe(2);
|
|
358
|
+
expect(arena.evalCode(`obj.c()`)).toBe(2);
|
|
359
|
+
expect(arena.evalCode(`obj.a`)).toBe(3);
|
|
360
|
+
expect(obj.a).toBe(3);
|
|
361
|
+
|
|
362
|
+
expect(obj).not.toBe(obj2);
|
|
363
|
+
obj2.a = 10;
|
|
364
|
+
expect(obj.a).toBe(10);
|
|
365
|
+
expect(arena.evalCode(`obj.a`)).toBe(10); // affected
|
|
366
|
+
|
|
367
|
+
arena.evalCode(`obj.a = 100`);
|
|
368
|
+
expect(obj.a).toBe(100); // affected
|
|
369
|
+
expect(arena.evalCode(`obj.a`)).toBe(100);
|
|
370
|
+
|
|
371
|
+
arena.dispose();
|
|
372
|
+
ctx.dispose();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test("sync after expose", async () => {
|
|
376
|
+
const ctx = (await getQuickJS()).newContext();
|
|
377
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
378
|
+
|
|
379
|
+
const obj = {
|
|
380
|
+
a: 1,
|
|
381
|
+
b: (a: number) => Math.floor(a),
|
|
382
|
+
c() {
|
|
383
|
+
return this.a++;
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
arena.expose({ obj });
|
|
387
|
+
const obj2 = arena.sync(obj);
|
|
388
|
+
|
|
389
|
+
const obj3 = arena.evalCode(`obj`);
|
|
390
|
+
expect(obj3).not.toBe(obj); // wrapped object
|
|
391
|
+
expect(obj3).not.toBe(obj2); // wrapped object
|
|
392
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj.c); // wrapped object
|
|
393
|
+
expect(arena.evalCode(`obj.b`)).not.toBe(obj2.b); // wrapped object
|
|
394
|
+
expect(arena.evalCode(`obj.b`)).not.toBe(obj3.b); // wrapped object
|
|
187
395
|
expect(arena.evalCode(`obj.b(1.1)`)).toBe(1);
|
|
396
|
+
expect(arena.evalCode(`obj.a`)).toBe(1);
|
|
397
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj.c); // wrapped object
|
|
398
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj2.c); // wrapped object
|
|
399
|
+
expect(arena.evalCode(`obj.c`)).not.toBe(obj3.c); // wrapped object
|
|
188
400
|
expect(arena.evalCode(`obj.c()`)).toBe(1);
|
|
189
401
|
expect(arena.evalCode(`obj.a`)).toBe(2);
|
|
190
402
|
expect(obj.a).toBe(2);
|
|
@@ -202,17 +414,18 @@ describe("expose with sync", () => {
|
|
|
202
414
|
expect(arena.evalCode(`obj.a`)).toBe(100);
|
|
203
415
|
|
|
204
416
|
arena.dispose();
|
|
205
|
-
|
|
417
|
+
ctx.dispose();
|
|
206
418
|
});
|
|
207
419
|
});
|
|
208
420
|
|
|
209
421
|
test("evalCode -> expose", async () => {
|
|
210
|
-
const
|
|
211
|
-
const arena = new Arena(
|
|
422
|
+
const ctx = (await getQuickJS()).newContext();
|
|
423
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
212
424
|
|
|
213
425
|
const obj = arena.evalCode(`({ a: 1, b: 1 })`);
|
|
214
426
|
arena.expose({ obj });
|
|
215
427
|
|
|
428
|
+
expect(obj).toBe(obj);
|
|
216
429
|
expect(obj.a).toBe(1);
|
|
217
430
|
expect(arena.evalCode(`obj.a`)).toBe(1);
|
|
218
431
|
expect(obj.b).toBe(1);
|
|
@@ -233,35 +446,47 @@ test("evalCode -> expose", async () => {
|
|
|
233
446
|
expect(arena.evalCode(`obj.b`)).toBe(2);
|
|
234
447
|
|
|
235
448
|
arena.dispose();
|
|
236
|
-
|
|
449
|
+
ctx.dispose();
|
|
237
450
|
});
|
|
238
451
|
|
|
239
452
|
test("expose -> evalCode", async () => {
|
|
240
|
-
const
|
|
241
|
-
const arena = new Arena(
|
|
453
|
+
const ctx = (await getQuickJS()).newContext();
|
|
454
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
242
455
|
|
|
243
456
|
const obj = { a: 1 };
|
|
244
|
-
|
|
245
|
-
arena.
|
|
246
|
-
const obj3 = arena.evalCode(`obj`);
|
|
457
|
+
arena.expose({ obj });
|
|
458
|
+
const obj2 = arena.evalCode(`obj`);
|
|
247
459
|
|
|
248
|
-
expect(
|
|
460
|
+
expect(obj2).toBe(obj);
|
|
249
461
|
|
|
250
|
-
|
|
462
|
+
obj2.a = 2;
|
|
251
463
|
expect(obj.a).toBe(2);
|
|
252
|
-
expect(arena.evalCode(`obj.a`)).toBe(
|
|
464
|
+
expect(arena.evalCode(`obj.a`)).toBe(1);
|
|
465
|
+
|
|
466
|
+
arena.evalCode("obj.a = 3");
|
|
467
|
+
expect(obj.a).toBe(2);
|
|
468
|
+
expect(arena.evalCode(`obj.a`)).toBe(3);
|
|
253
469
|
|
|
254
|
-
arena.
|
|
255
|
-
|
|
256
|
-
|
|
470
|
+
arena.dispose();
|
|
471
|
+
ctx.dispose();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test("evalCode -> expose -> evalCode", async () => {
|
|
475
|
+
const ctx = (await getQuickJS()).newContext();
|
|
476
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
477
|
+
|
|
478
|
+
const obj = [1];
|
|
479
|
+
expect(arena.evalCode("a => a[0] + 10")(obj)).toBe(11);
|
|
480
|
+
arena.expose({ obj });
|
|
481
|
+
expect(arena.evalCode("obj")).toBe(obj);
|
|
257
482
|
|
|
258
483
|
arena.dispose();
|
|
259
|
-
|
|
484
|
+
ctx.dispose();
|
|
260
485
|
});
|
|
261
486
|
|
|
262
487
|
test("register and unregister", async () => {
|
|
263
|
-
const
|
|
264
|
-
const arena = new Arena(
|
|
488
|
+
const ctx = (await getQuickJS()).newContext();
|
|
489
|
+
const arena = new Arena(ctx, { isMarshalable: true, registeredObjects: [] });
|
|
265
490
|
|
|
266
491
|
arena.register(Math, `Math`);
|
|
267
492
|
expect(arena.evalCode(`Math`)).toBe(Math);
|
|
@@ -276,12 +501,13 @@ test("register and unregister", async () => {
|
|
|
276
501
|
expect(arena.evalCode(`new Error()`)).toBeInstanceOf(Error);
|
|
277
502
|
|
|
278
503
|
arena.dispose();
|
|
279
|
-
|
|
504
|
+
ctx.dispose();
|
|
280
505
|
});
|
|
281
506
|
|
|
282
507
|
test("registeredObjects option", async () => {
|
|
283
|
-
const
|
|
284
|
-
const arena = new Arena(
|
|
508
|
+
const ctx = (await getQuickJS()).newContext();
|
|
509
|
+
const arena = new Arena(ctx, {
|
|
510
|
+
isMarshalable: true,
|
|
285
511
|
registeredObjects: [[Symbol.iterator, "Symbol.iterator"]],
|
|
286
512
|
});
|
|
287
513
|
|
|
@@ -291,17 +517,93 @@ test("registeredObjects option", async () => {
|
|
|
291
517
|
);
|
|
292
518
|
|
|
293
519
|
arena.dispose();
|
|
294
|
-
|
|
520
|
+
ctx.dispose();
|
|
295
521
|
});
|
|
296
522
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
523
|
+
describe("isMarshalable option", () => {
|
|
524
|
+
test("false", async () => {
|
|
525
|
+
const ctx = (await getQuickJS()).newContext();
|
|
526
|
+
const arena = new Arena(ctx, { isMarshalable: false });
|
|
527
|
+
|
|
528
|
+
expect(arena.evalCode(`s => s === undefined`)(globalThis)).toBe(true);
|
|
529
|
+
expect(arena.evalCode(`s => s === undefined`)({})).toBe(true);
|
|
530
|
+
arena.expose({ aaa: globalThis });
|
|
531
|
+
expect(arena.evalCode(`aaa`)).toBeUndefined();
|
|
532
|
+
|
|
533
|
+
arena.dispose();
|
|
534
|
+
ctx.dispose();
|
|
301
535
|
});
|
|
302
536
|
|
|
303
|
-
|
|
537
|
+
test("json", async () => {
|
|
538
|
+
const ctx = (await getQuickJS()).newContext();
|
|
539
|
+
const arena = new Arena(ctx, { isMarshalable: "json" });
|
|
304
540
|
|
|
305
|
-
|
|
306
|
-
|
|
541
|
+
const obj = { a: () => {}, b: new Date(), c: [() => {}, 1] };
|
|
542
|
+
const objJSON = { b: obj.b.toISOString(), c: [null, 1] };
|
|
543
|
+
const objJSON2 = arena.evalCode(`a => a`)(obj);
|
|
544
|
+
expect(objJSON2).toStrictEqual(objJSON);
|
|
545
|
+
arena.expose({ obj });
|
|
546
|
+
const exposedObj = arena.evalCode(`obj`);
|
|
547
|
+
expect(exposedObj).toStrictEqual(objJSON);
|
|
548
|
+
expect(exposedObj).not.toBe(objJSON2);
|
|
549
|
+
|
|
550
|
+
arena.dispose();
|
|
551
|
+
ctx.dispose();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test("conditional", async () => {
|
|
555
|
+
const ctx = (await getQuickJS()).newContext();
|
|
556
|
+
const arena = new Arena(ctx, {
|
|
557
|
+
isMarshalable: (o) => o !== globalThis,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
const obj = { a: 1 };
|
|
561
|
+
expect(arena.evalCode(`s => s === undefined`)(globalThis)).toBe(true);
|
|
562
|
+
expect(arena.evalCode(`s => s === undefined`)(obj)).toBe(false);
|
|
563
|
+
arena.expose({ aaa: globalThis, bbb: obj });
|
|
564
|
+
expect(arena.evalCode(`aaa`)).toBeUndefined();
|
|
565
|
+
expect(arena.evalCode(`bbb`)).toBe(obj);
|
|
566
|
+
|
|
567
|
+
arena.dispose();
|
|
568
|
+
ctx.dispose();
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
describe("edge cases", () => {
|
|
573
|
+
test("getter", async () => {
|
|
574
|
+
const ctx = (await getQuickJS()).newContext();
|
|
575
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
576
|
+
|
|
577
|
+
const called: string[] = [];
|
|
578
|
+
const obj = { c: 0 };
|
|
579
|
+
const exposed = {
|
|
580
|
+
get a() {
|
|
581
|
+
called.push("a");
|
|
582
|
+
return {
|
|
583
|
+
get b() {
|
|
584
|
+
called.push("b");
|
|
585
|
+
return obj;
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
const cb: { current?: () => any } = {};
|
|
591
|
+
const register = (fn: () => any) => {
|
|
592
|
+
cb.current = fn;
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
arena.expose({ exposed, register });
|
|
596
|
+
expect(called).toEqual([]);
|
|
597
|
+
|
|
598
|
+
arena.evalCode(`register(() => exposed.a.b.c);`);
|
|
599
|
+
expect(cb.current?.()).toBe(0);
|
|
600
|
+
expect(called).toEqual(["a", "b"]);
|
|
601
|
+
|
|
602
|
+
obj.c = 1;
|
|
603
|
+
expect(cb.current?.()).toBe(1); // this line causes an error when context is disposed
|
|
604
|
+
expect(called).toEqual(["a", "b", "a", "b"]);
|
|
605
|
+
|
|
606
|
+
arena.dispose();
|
|
607
|
+
// ctx.dispose(); // reports an error
|
|
608
|
+
});
|
|
307
609
|
});
|