quickjs-emscripten-sync 1.8.4 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -150
- package/dist/index.d.ts +50 -7
- package/dist/quickjs-emscripten-sync.js +652 -387
- package/dist/quickjs-emscripten-sync.umd.cjs +17 -8
- package/package.json +5 -2
- package/src/index.test.ts +250 -5
- package/src/index.ts +126 -9
- package/src/jsonleak.test.ts +178 -0
- package/src/marshal/custom.ts +16 -2
- package/src/marshal/function.ts +6 -2
- package/src/marshal/hostref.ts +18 -0
- package/src/marshal/index.ts +25 -2
- package/src/marshal/mapset.ts +37 -0
- package/src/marshal/object.ts +4 -1
- package/src/marshal/primitive.ts +2 -11
- package/src/marshal/properties.ts +19 -6
- package/src/unmarshal/custom.ts +35 -4
- package/src/unmarshal/function.ts +2 -2
- package/src/unmarshal/hostref.ts +21 -0
- package/src/unmarshal/index.ts +21 -1
- package/src/unmarshal/mapset.ts +43 -0
- package/src/unmarshal/object.ts +16 -20
- package/src/unmarshal/primitive.ts +6 -17
- package/src/unmarshal/properties.ts +7 -6
- package/src/vmmap.ts +59 -69
- package/src/vmutil.ts +97 -12
- package/src/wrapper.ts +97 -30
|
@@ -7,13 +7,21 @@ export default function marshalProperties(
|
|
|
7
7
|
target: object | ((...args: any[]) => any),
|
|
8
8
|
handle: QuickJSHandle,
|
|
9
9
|
marshal: (target: unknown) => QuickJSHandle,
|
|
10
|
+
disposeTransient: (handle: QuickJSHandle) => void = () => {},
|
|
10
11
|
): void {
|
|
11
12
|
const descs = ctx.newObject();
|
|
13
|
+
// Property values may be transient (json copies / BigInt) with no owner; once
|
|
14
|
+
// `Object.defineProperties` has copied them into `handle` the standalone
|
|
15
|
+
// handles are redundant and disposed below. Owned handles are left untouched.
|
|
16
|
+
const transient: QuickJSHandle[] = [];
|
|
12
17
|
const cb = (key: string | number | symbol, desc: PropertyDescriptor) => {
|
|
13
18
|
const keyHandle = marshal(key);
|
|
14
19
|
const valueHandle = typeof desc.value === "undefined" ? undefined : marshal(desc.value);
|
|
15
20
|
const getHandle = typeof desc.get === "undefined" ? undefined : marshal(desc.get);
|
|
16
21
|
const setHandle = typeof desc.set === "undefined" ? undefined : marshal(desc.set);
|
|
22
|
+
if (valueHandle) transient.push(valueHandle);
|
|
23
|
+
if (getHandle) transient.push(getHandle);
|
|
24
|
+
if (setHandle) transient.push(setHandle);
|
|
17
25
|
|
|
18
26
|
ctx.newObject().consume(descObj => {
|
|
19
27
|
Object.entries(desc).forEach(([k, v]) => {
|
|
@@ -35,11 +43,16 @@ export default function marshalProperties(
|
|
|
35
43
|
});
|
|
36
44
|
};
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
try {
|
|
47
|
+
const desc = Object.getOwnPropertyDescriptors(target);
|
|
48
|
+
Object.entries(desc).forEach(([k, v]) => cb(k, v));
|
|
49
|
+
Object.getOwnPropertySymbols(desc).forEach(k => cb(k, (desc as any)[k]));
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
call(ctx, `Object.defineProperties`, undefined, handle, descs).dispose();
|
|
52
|
+
// Safe only after defineProperties has dup'd the values into `handle`; `descs`
|
|
53
|
+
// still holds its own references until its own dispose() in the finally.
|
|
54
|
+
for (const h of transient) disposeTransient(h);
|
|
55
|
+
} finally {
|
|
56
|
+
descs.dispose();
|
|
57
|
+
}
|
|
45
58
|
}
|
package/src/unmarshal/custom.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
|
|
3
|
-
import { call } from "../vmutil";
|
|
3
|
+
import { call, consume } from "../vmutil";
|
|
4
4
|
|
|
5
5
|
export default function unmarshalCustom(
|
|
6
6
|
ctx: QuickJSContext,
|
|
@@ -23,9 +23,40 @@ export function symbol(handle: QuickJSHandle, ctx: QuickJSContext): symbol | und
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function date(handle: QuickJSHandle, ctx: QuickJSContext): Date | undefined {
|
|
26
|
-
if (!
|
|
27
|
-
const t =
|
|
26
|
+
if (!consume(call(ctx, "a => a instanceof Date", undefined, handle), h => ctx.dump(h))) return;
|
|
27
|
+
const t = consume(call(ctx, "a => a.getTime()", undefined, handle), h => ctx.getNumber(h));
|
|
28
28
|
return new Date(t);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export
|
|
31
|
+
export function arrayBuffer(
|
|
32
|
+
handle: QuickJSHandle,
|
|
33
|
+
ctx: QuickJSContext,
|
|
34
|
+
): ArrayBuffer | ArrayBufferView | undefined {
|
|
35
|
+
if (consume(call(ctx, "a => a instanceof ArrayBuffer", undefined, handle), h => ctx.dump(h))) {
|
|
36
|
+
const lifetime = ctx.getArrayBuffer(handle);
|
|
37
|
+
const copy = lifetime.value.slice();
|
|
38
|
+
lifetime.dispose();
|
|
39
|
+
return copy.buffer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (consume(call(ctx, "a => ArrayBuffer.isView(a)", undefined, handle), h => ctx.dump(h))) {
|
|
43
|
+
const name = consume(call(ctx, "a => a.constructor.name", undefined, handle), h =>
|
|
44
|
+
ctx.getString(h),
|
|
45
|
+
);
|
|
46
|
+
const Ctor = (globalThis as any)[name];
|
|
47
|
+
if (typeof Ctor !== "function") return;
|
|
48
|
+
const bufHandle = call(
|
|
49
|
+
ctx,
|
|
50
|
+
"a => a.buffer.slice(a.byteOffset, a.byteOffset + a.byteLength)",
|
|
51
|
+
undefined,
|
|
52
|
+
handle,
|
|
53
|
+
);
|
|
54
|
+
const lifetime = ctx.getArrayBuffer(bufHandle);
|
|
55
|
+
const bytes = lifetime.value.slice();
|
|
56
|
+
lifetime.dispose();
|
|
57
|
+
bufHandle.dispose();
|
|
58
|
+
return new Ctor(bytes.buffer);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const defaultCustom = [symbol, date, arrayBuffer];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
|
|
3
|
-
import { call, mayConsumeAll } from "../vmutil";
|
|
3
|
+
import { call, mayConsumeAll, unwrapResult } from "../vmutil";
|
|
4
4
|
|
|
5
5
|
import unmarshalProperties from "./properties";
|
|
6
6
|
|
|
@@ -26,7 +26,7 @@ export default function unmarshalFunction(
|
|
|
26
26
|
return this;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const resultHandle =
|
|
29
|
+
const resultHandle = unwrapResult(ctx, ctx.callFunction(handle, thisHandle, ...argHandles));
|
|
30
30
|
|
|
31
31
|
const [result, alreadyExists] = unmarshal(resultHandle);
|
|
32
32
|
if (alreadyExists) resultHandle.dispose();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* If `handle` is an opaque HostRef, resolve it back to the original host value.
|
|
5
|
+
* Returns a wrapper so the caller can tell "resolved to a falsy value" apart
|
|
6
|
+
* from "not a HostRef". `toHostRef` is cheap for non-HostRef handles.
|
|
7
|
+
*/
|
|
8
|
+
export default function unmarshalHostRef(
|
|
9
|
+
ctx: QuickJSContext,
|
|
10
|
+
handle: QuickJSHandle,
|
|
11
|
+
): { value: unknown } | undefined {
|
|
12
|
+
const ref = ctx.toHostRef(handle);
|
|
13
|
+
if (!ref) return;
|
|
14
|
+
try {
|
|
15
|
+
return { value: ref.value };
|
|
16
|
+
} finally {
|
|
17
|
+
// toHostRef returns a HostRef wrapping a *dup* of the handle, so disposing
|
|
18
|
+
// it does not affect the caller's handle.
|
|
19
|
+
ref.dispose();
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/unmarshal/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
|
2
2
|
|
|
3
3
|
import unmarshalCustom, { defaultCustom } from "./custom";
|
|
4
4
|
import unmarshalFunction from "./function";
|
|
5
|
+
import unmarshalHostRef from "./hostref";
|
|
6
|
+
import unmarshalMapSet from "./mapset";
|
|
5
7
|
import unmarshalObject from "./object";
|
|
6
8
|
import unmarshalPrimitive from "./primitive";
|
|
7
9
|
import unmarshalPromise from "./promise";
|
|
@@ -13,6 +15,8 @@ export type Options = {
|
|
|
13
15
|
find: (handle: QuickJSHandle) => unknown | undefined;
|
|
14
16
|
pre: <T = unknown>(target: T, handle: QuickJSHandle) => T | undefined;
|
|
15
17
|
custom?: Iterable<(obj: QuickJSHandle, ctx: QuickJSContext) => any>;
|
|
18
|
+
/** When true, resolve opaque HostRef handles back to their host value. */
|
|
19
|
+
hostRef?: boolean;
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
export function unmarshal(handle: QuickJSHandle, options: Options): any {
|
|
@@ -37,8 +41,24 @@ function unmarshalInner(handle: QuickJSHandle, options: Options): [any, boolean]
|
|
|
37
41
|
|
|
38
42
|
const unmarshal2 = (h: QuickJSHandle) => unmarshalInner(h, options);
|
|
39
43
|
|
|
44
|
+
// Opaque HostRef handles resolve back to the original host value by reference.
|
|
45
|
+
if (options.hostRef) {
|
|
46
|
+
const ref = unmarshalHostRef(ctx, handle);
|
|
47
|
+
if (ref) return [ref.value, true];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Custom types (Symbol, Date, ArrayBuffer, TypedArray, ...) are unmarshalled
|
|
51
|
+
// by value and not tracked in the map, so their source handle is not owned by
|
|
52
|
+
// anyone else and the caller must dispose it.
|
|
53
|
+
const custom = unmarshalCustom(ctx, handle, pre, [...defaultCustom, ...(options.custom ?? [])]);
|
|
54
|
+
if (custom) return [custom, true];
|
|
55
|
+
|
|
56
|
+
// Map/Set are unmarshalled by value (snapshot copy), so the source handle is
|
|
57
|
+
// not tracked and the caller must dispose it.
|
|
58
|
+
const mapSet = unmarshalMapSet(ctx, handle, unmarshal2, pre);
|
|
59
|
+
if (mapSet) return [mapSet, true];
|
|
60
|
+
|
|
40
61
|
const result =
|
|
41
|
-
unmarshalCustom(ctx, handle, pre, [...defaultCustom, ...(options.custom ?? [])]) ??
|
|
42
62
|
unmarshalPromise(ctx, handle, marshal, pre) ??
|
|
43
63
|
unmarshalFunction(ctx, handle, marshal, unmarshal2, pre) ??
|
|
44
64
|
unmarshalObject(ctx, handle, unmarshal2, pre);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
|
+
import { call, consume } from "../vmutil";
|
|
4
|
+
|
|
5
|
+
export default function unmarshalMapSet(
|
|
6
|
+
ctx: QuickJSContext,
|
|
7
|
+
handle: QuickJSHandle,
|
|
8
|
+
unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
|
|
9
|
+
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
|
|
10
|
+
): Map<any, any> | Set<any> | undefined {
|
|
11
|
+
const isMap = consume(call(ctx, "a => a instanceof Map", undefined, handle), h => ctx.dump(h));
|
|
12
|
+
const isSet =
|
|
13
|
+
!isMap && consume(call(ctx, "a => a instanceof Set", undefined, handle), h => ctx.dump(h));
|
|
14
|
+
if (!isMap && !isSet) return;
|
|
15
|
+
|
|
16
|
+
const result: Map<any, any> | Set<any> = isMap ? new Map() : new Set();
|
|
17
|
+
preUnmarshal(result, handle);
|
|
18
|
+
|
|
19
|
+
const iterator = ctx.unwrapResult(ctx.getIterator(handle));
|
|
20
|
+
try {
|
|
21
|
+
for (const elResult of iterator) {
|
|
22
|
+
const el = ctx.unwrapResult(elResult);
|
|
23
|
+
if (isMap) {
|
|
24
|
+
const keyHandle = ctx.getProp(el, 0);
|
|
25
|
+
const valueHandle = ctx.getProp(el, 1);
|
|
26
|
+
const [key, disposeKey] = unmarshal(keyHandle);
|
|
27
|
+
const [value, disposeValue] = unmarshal(valueHandle);
|
|
28
|
+
if (disposeKey) keyHandle.dispose();
|
|
29
|
+
if (disposeValue) valueHandle.dispose();
|
|
30
|
+
(result as Map<any, any>).set(key, value);
|
|
31
|
+
el.dispose();
|
|
32
|
+
} else {
|
|
33
|
+
const [value, disposeValue] = unmarshal(el);
|
|
34
|
+
if (disposeValue) el.dispose();
|
|
35
|
+
(result as Set<any>).add(value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} finally {
|
|
39
|
+
if (iterator.alive) iterator.dispose();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
package/src/unmarshal/object.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
|
|
3
|
-
import { call } from "../vmutil";
|
|
3
|
+
import { call, consume } from "../vmutil";
|
|
4
4
|
|
|
5
5
|
import unmarshalProperties from "./properties";
|
|
6
6
|
|
|
@@ -10,31 +10,27 @@ export default function unmarshalObject(
|
|
|
10
10
|
unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
|
|
11
11
|
preUnmarshal: <T>(target: T, handle: QuickJSHandle) => T | undefined,
|
|
12
12
|
): object | undefined {
|
|
13
|
-
if (
|
|
14
|
-
ctx.typeof(handle) !== "object" ||
|
|
15
|
-
// null check
|
|
16
|
-
ctx
|
|
17
|
-
.unwrapResult(ctx.evalCode("o => o === null"))
|
|
18
|
-
.consume(n => ctx.dump(ctx.unwrapResult(ctx.callFunction(n, ctx.undefined, handle))))
|
|
19
|
-
)
|
|
20
|
-
return;
|
|
13
|
+
if (ctx.typeof(handle) !== "object" || ctx.sameValue(handle, ctx.null)) return;
|
|
21
14
|
|
|
22
|
-
const raw = call(ctx, "Array.isArray", undefined, handle)
|
|
15
|
+
const raw = consume(call(ctx, "Array.isArray", undefined, handle), r => ctx.dump(r)) ? [] : {};
|
|
23
16
|
const obj = preUnmarshal(raw, handle) ?? raw;
|
|
24
17
|
|
|
25
|
-
const prototype =
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
const prototype = consume(
|
|
19
|
+
call(
|
|
20
|
+
ctx,
|
|
21
|
+
`o => {
|
|
28
22
|
const p = Object.getPrototypeOf(o);
|
|
29
23
|
return !p || p === Object.prototype || p === Array.prototype ? undefined : p;
|
|
30
24
|
}`,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
undefined,
|
|
26
|
+
handle,
|
|
27
|
+
),
|
|
28
|
+
prototype => {
|
|
29
|
+
if (ctx.typeof(prototype) === "undefined") return;
|
|
30
|
+
const [proto] = unmarshal(prototype);
|
|
31
|
+
return proto;
|
|
32
|
+
},
|
|
33
|
+
);
|
|
38
34
|
if (typeof prototype === "object") {
|
|
39
35
|
Object.setPrototypeOf(obj, prototype);
|
|
40
36
|
}
|
|
@@ -7,24 +7,13 @@ export default function unmarshalPrimitive(
|
|
|
7
7
|
const ty = ctx.typeof(handle);
|
|
8
8
|
if (ty === "undefined" || ty === "number" || ty === "string" || ty === "boolean") {
|
|
9
9
|
return [ctx.dump(handle), true];
|
|
10
|
-
} else if (ty === "object") {
|
|
11
|
-
const isNull = ctx
|
|
12
|
-
.unwrapResult(ctx.evalCode("a => a === null"))
|
|
13
|
-
.consume(n => ctx.dump(ctx.unwrapResult(ctx.callFunction(n, ctx.undefined, handle))));
|
|
14
|
-
if (isNull) {
|
|
15
|
-
return [null, true];
|
|
16
|
-
}
|
|
17
10
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// .consume(str => ctx.getString(str));
|
|
25
|
-
// const bi = BigInt(str);
|
|
26
|
-
// return [bi, true];
|
|
27
|
-
// }
|
|
11
|
+
if (ty === "bigint") {
|
|
12
|
+
return [ctx.getBigInt(handle), true];
|
|
13
|
+
}
|
|
14
|
+
if (ty === "object" && ctx.sameValue(handle, ctx.null)) {
|
|
15
|
+
return [null, true];
|
|
16
|
+
}
|
|
28
17
|
|
|
29
18
|
return [undefined, false];
|
|
30
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
|
|
3
|
-
import { call } from "../vmutil";
|
|
3
|
+
import { call, consume } from "../vmutil";
|
|
4
4
|
|
|
5
5
|
export default function unmarshalProperties(
|
|
6
6
|
ctx: QuickJSContext,
|
|
@@ -8,8 +8,8 @@ export default function unmarshalProperties(
|
|
|
8
8
|
target: object | ((...args: any[]) => any),
|
|
9
9
|
unmarshal: (handle: QuickJSHandle) => [unknown, boolean],
|
|
10
10
|
) {
|
|
11
|
-
|
|
12
|
-
.newFunction("", (key, value) => {
|
|
11
|
+
consume(
|
|
12
|
+
ctx.newFunction("", (key, value) => {
|
|
13
13
|
const [keyName] = unmarshal(key);
|
|
14
14
|
if (typeof keyName !== "string" && typeof keyName !== "number" && typeof keyName !== "symbol")
|
|
15
15
|
return;
|
|
@@ -43,8 +43,8 @@ export default function unmarshalProperties(
|
|
|
43
43
|
}, {});
|
|
44
44
|
|
|
45
45
|
Object.defineProperty(target, keyName, desc);
|
|
46
|
-
})
|
|
47
|
-
|
|
46
|
+
}),
|
|
47
|
+
fn => {
|
|
48
48
|
call(
|
|
49
49
|
ctx,
|
|
50
50
|
`(o, fn) => {
|
|
@@ -56,5 +56,6 @@ export default function unmarshalProperties(
|
|
|
56
56
|
handle,
|
|
57
57
|
fn,
|
|
58
58
|
).dispose();
|
|
59
|
-
}
|
|
59
|
+
},
|
|
60
|
+
);
|
|
60
61
|
}
|
package/src/vmmap.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
|
|
3
|
+
import { unwrapResult } from "./vmutil";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Bidirectional map between host values and QuickJS handles.
|
|
7
|
+
*
|
|
8
|
+
* Each registered pair gets a numeric id. A value may be registered under two
|
|
9
|
+
* keys (e.g. a proxy-wrapped object and the underlying object), each with its
|
|
10
|
+
* own handle, so every lookup direction is backed by an explicit map and id
|
|
11
|
+
* reverse-lookups, keeping `delete` O(1).
|
|
12
|
+
*/
|
|
3
13
|
export default class VMMap {
|
|
4
14
|
ctx: QuickJSContext;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
_keyToId = new Map<any, number>();
|
|
16
|
+
_key2ToId = new Map<any, number>();
|
|
17
|
+
_idToHandle = new Map<number, QuickJSHandle>();
|
|
18
|
+
_idToHandle2 = new Map<number, QuickJSHandle>();
|
|
19
|
+
_idToKey = new Map<number, any>();
|
|
20
|
+
_idToKey2 = new Map<number, any>();
|
|
10
21
|
_disposables = new Set<QuickJSHandle>();
|
|
11
22
|
_mapGet: QuickJSHandle;
|
|
12
23
|
_mapSet: QuickJSHandle;
|
|
13
24
|
_mapDelete: QuickJSHandle;
|
|
14
25
|
_mapClear: QuickJSHandle;
|
|
15
|
-
|
|
26
|
+
_nextId = Number.MIN_SAFE_INTEGER;
|
|
16
27
|
|
|
17
28
|
constructor(ctx: QuickJSContext) {
|
|
18
29
|
this.ctx = ctx;
|
|
@@ -68,18 +79,19 @@ export default class VMMap {
|
|
|
68
79
|
return v === handle || v === handle2;
|
|
69
80
|
}
|
|
70
81
|
|
|
71
|
-
const
|
|
72
|
-
this.
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
82
|
+
const id = this._nextId++;
|
|
83
|
+
this._keyToId.set(key, id);
|
|
84
|
+
this._idToHandle.set(id, handle);
|
|
85
|
+
this._idToKey.set(id, key);
|
|
75
86
|
if (key2) {
|
|
76
|
-
this.
|
|
87
|
+
this._key2ToId.set(key2, id);
|
|
88
|
+
this._idToKey2.set(id, key2);
|
|
77
89
|
if (handle2) {
|
|
78
|
-
this.
|
|
90
|
+
this._idToHandle2.set(id, handle2);
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
this.ctx.newNumber(
|
|
94
|
+
this.ctx.newNumber(id).consume(c => {
|
|
83
95
|
this._call(this._mapSet, undefined, handle, c, handle2 ?? this.ctx.undefined);
|
|
84
96
|
});
|
|
85
97
|
|
|
@@ -104,8 +116,8 @@ export default class VMMap {
|
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
get(key: any) {
|
|
107
|
-
const
|
|
108
|
-
const handle = typeof
|
|
119
|
+
const id = this._keyToId.get(key) ?? this._key2ToId.get(key);
|
|
120
|
+
const handle = typeof id === "number" ? this._idToHandle.get(id) : undefined;
|
|
109
121
|
|
|
110
122
|
if (!handle) return;
|
|
111
123
|
if (!handle.alive) {
|
|
@@ -120,7 +132,7 @@ export default class VMMap {
|
|
|
120
132
|
if (!handle.alive) {
|
|
121
133
|
return;
|
|
122
134
|
}
|
|
123
|
-
return this.
|
|
135
|
+
return this._idToKey.get(this.ctx.getNumber(this._call(this._mapGet, undefined, handle)));
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
has(key: any) {
|
|
@@ -132,46 +144,29 @@ export default class VMMap {
|
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
keys() {
|
|
135
|
-
return this.
|
|
147
|
+
return this._keyToId.keys();
|
|
136
148
|
}
|
|
137
149
|
|
|
138
150
|
delete(key: any, dispose?: boolean) {
|
|
139
|
-
const
|
|
140
|
-
if (typeof
|
|
151
|
+
const id = this._keyToId.get(key) ?? this._key2ToId.get(key);
|
|
152
|
+
if (typeof id === "undefined") return;
|
|
141
153
|
|
|
142
|
-
const handle = this.
|
|
143
|
-
const handle2 = this.
|
|
154
|
+
const handle = this._idToHandle.get(id);
|
|
155
|
+
const handle2 = this._idToHandle2.get(id);
|
|
144
156
|
this._call(
|
|
145
157
|
this._mapDelete,
|
|
146
158
|
undefined,
|
|
147
159
|
...[handle, handle2].filter((h): h is QuickJSHandle => !!h?.alive),
|
|
148
160
|
);
|
|
149
161
|
|
|
150
|
-
this.
|
|
151
|
-
this.
|
|
152
|
-
this.
|
|
153
|
-
this.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
for (const [k, v] of this._map2) {
|
|
163
|
-
if (v === num) {
|
|
164
|
-
this._map2.delete(k);
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
for (const [k, v] of this._counterMap) {
|
|
170
|
-
if (v === key) {
|
|
171
|
-
this._counterMap.delete(k);
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
162
|
+
const key1 = this._idToKey.get(id);
|
|
163
|
+
const key2 = this._idToKey2.get(id);
|
|
164
|
+
if (typeof key1 !== "undefined") this._keyToId.delete(key1);
|
|
165
|
+
if (typeof key2 !== "undefined") this._key2ToId.delete(key2);
|
|
166
|
+
this._idToHandle.delete(id);
|
|
167
|
+
this._idToHandle2.delete(id);
|
|
168
|
+
this._idToKey.delete(id);
|
|
169
|
+
this._idToKey2.delete(id);
|
|
175
170
|
|
|
176
171
|
if (dispose) {
|
|
177
172
|
if (handle?.alive) handle.dispose();
|
|
@@ -187,12 +182,13 @@ export default class VMMap {
|
|
|
187
182
|
}
|
|
188
183
|
|
|
189
184
|
clear() {
|
|
190
|
-
this.
|
|
191
|
-
this.
|
|
192
|
-
this.
|
|
193
|
-
this.
|
|
194
|
-
this.
|
|
195
|
-
this.
|
|
185
|
+
this._nextId = 0;
|
|
186
|
+
this._keyToId.clear();
|
|
187
|
+
this._key2ToId.clear();
|
|
188
|
+
this._idToHandle.clear();
|
|
189
|
+
this._idToHandle2.clear();
|
|
190
|
+
this._idToKey.clear();
|
|
191
|
+
this._idToKey2.clear();
|
|
196
192
|
if (this._mapClear.alive) {
|
|
197
193
|
this._call(this._mapClear, undefined);
|
|
198
194
|
}
|
|
@@ -204,12 +200,12 @@ export default class VMMap {
|
|
|
204
200
|
v.dispose();
|
|
205
201
|
}
|
|
206
202
|
}
|
|
207
|
-
for (const v of this.
|
|
203
|
+
for (const v of this._idToHandle.values()) {
|
|
208
204
|
if (v.alive) {
|
|
209
205
|
v.dispose();
|
|
210
206
|
}
|
|
211
207
|
}
|
|
212
|
-
for (const v of this.
|
|
208
|
+
for (const v of this._idToHandle2.values()) {
|
|
213
209
|
if (v.alive) {
|
|
214
210
|
v.dispose();
|
|
215
211
|
}
|
|
@@ -219,37 +215,31 @@ export default class VMMap {
|
|
|
219
215
|
}
|
|
220
216
|
|
|
221
217
|
get size() {
|
|
222
|
-
return this.
|
|
218
|
+
return this._keyToId.size;
|
|
223
219
|
}
|
|
224
220
|
|
|
225
221
|
[Symbol.iterator](): Iterator<[any, QuickJSHandle, any, QuickJSHandle | undefined]> {
|
|
226
|
-
const keys = this.
|
|
222
|
+
const keys = this._keyToId.keys();
|
|
227
223
|
return {
|
|
228
224
|
next: () => {
|
|
229
|
-
|
|
230
225
|
while (true) {
|
|
231
226
|
const k1 = keys.next();
|
|
232
227
|
if (k1.done) return { value: undefined, done: true };
|
|
233
|
-
const
|
|
234
|
-
if (typeof
|
|
235
|
-
const v1 = this.
|
|
236
|
-
const v2 = this.
|
|
228
|
+
const id = this._keyToId.get(k1.value);
|
|
229
|
+
if (typeof id === "undefined") continue;
|
|
230
|
+
const v1 = this._idToHandle.get(id);
|
|
231
|
+
const v2 = this._idToHandle2.get(id);
|
|
237
232
|
if (!v1) continue;
|
|
238
|
-
const k2 = this.
|
|
233
|
+
const k2 = this._idToKey2.get(id);
|
|
239
234
|
return { value: [k1.value, v1, k2, v2], done: false };
|
|
240
235
|
}
|
|
241
236
|
},
|
|
242
237
|
};
|
|
243
238
|
}
|
|
244
239
|
|
|
245
|
-
_get2(num: number) {
|
|
246
|
-
for (const [k, v] of this._map2) {
|
|
247
|
-
if (v === num) return k;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
240
|
_call(fn: QuickJSHandle, thisArg: QuickJSHandle | undefined, ...args: QuickJSHandle[]) {
|
|
252
|
-
return
|
|
241
|
+
return unwrapResult(
|
|
242
|
+
this.ctx,
|
|
253
243
|
this.ctx.callFunction(
|
|
254
244
|
fn,
|
|
255
245
|
typeof thisArg === "undefined" ? this.ctx.undefined : thisArg,
|