@gjsify/v8 0.1.15 → 0.2.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/lib/esm/heap.js +37 -0
- package/lib/esm/index.js +79 -29
- package/lib/esm/serdes.js +536 -0
- package/lib/types/heap.d.ts +16 -0
- package/lib/types/index.d.ts +46 -7
- package/lib/types/serdes.d.ts +42 -0
- package/package.json +5 -5
- package/src/heap.ts +53 -0
- package/src/index.spec.ts +256 -14
- package/src/index.ts +88 -30
- package/src/serdes.ts +609 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function getHeapStatistics(): {
|
|
2
|
+
total_heap_size: number;
|
|
3
|
+
total_heap_size_executable: number;
|
|
4
|
+
total_physical_size: number;
|
|
5
|
+
total_available_size: number;
|
|
6
|
+
used_heap_size: number;
|
|
7
|
+
heap_size_limit: number;
|
|
8
|
+
malloced_memory: number;
|
|
9
|
+
peak_malloced_memory: number;
|
|
10
|
+
does_zap_garbage: number;
|
|
11
|
+
number_of_native_contexts: number;
|
|
12
|
+
number_of_detached_contexts: number;
|
|
13
|
+
total_global_handles_size: number;
|
|
14
|
+
used_global_handles_size: number;
|
|
15
|
+
external_memory: number;
|
|
16
|
+
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,19 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { getHeapStatistics } from './heap.js';
|
|
2
|
+
import { Serializer, Deserializer, DefaultSerializer, DefaultDeserializer } from './serdes.js';
|
|
3
|
+
export { getHeapStatistics };
|
|
4
|
+
export { Serializer, Deserializer, DefaultSerializer, DefaultDeserializer };
|
|
5
|
+
export declare function serialize(value: unknown): Buffer;
|
|
6
|
+
export declare function deserialize(buffer: NodeJS.ArrayBufferView | ArrayBuffer): unknown;
|
|
7
|
+
export interface HeapSpaceInfo {
|
|
8
|
+
space_name: string;
|
|
9
|
+
space_size: number;
|
|
10
|
+
space_used_size: number;
|
|
11
|
+
space_available_size: number;
|
|
12
|
+
physical_space_size: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function getHeapSpaceStatistics(): HeapSpaceInfo[];
|
|
15
|
+
export declare function getHeapCodeStatistics(): {
|
|
16
|
+
code_and_metadata_size: number;
|
|
17
|
+
bytecode_and_metadata_size: number;
|
|
18
|
+
external_script_source_size: number;
|
|
19
|
+
cpu_profiler_metadata_size: number;
|
|
20
|
+
};
|
|
3
21
|
export declare function setFlagsFromString(_flags: string): void;
|
|
4
|
-
export declare function getHeapSnapshot():
|
|
22
|
+
export declare function getHeapSnapshot(_options?: object): null;
|
|
5
23
|
export declare function writeHeapSnapshot(_filename?: string): string;
|
|
6
|
-
export declare function
|
|
7
|
-
export declare
|
|
8
|
-
|
|
24
|
+
export declare function isStringOneByteRepresentation(content: string): boolean;
|
|
25
|
+
export declare class GCProfiler {
|
|
26
|
+
#private;
|
|
27
|
+
start(): void;
|
|
28
|
+
stop(): {
|
|
29
|
+
version: number;
|
|
30
|
+
startTime: number;
|
|
31
|
+
endTime: number;
|
|
32
|
+
stats: never[];
|
|
33
|
+
} | undefined;
|
|
34
|
+
[Symbol.dispose](): void;
|
|
35
|
+
}
|
|
36
|
+
export declare class SyncCPUProfileHandle {
|
|
37
|
+
stop(): undefined;
|
|
38
|
+
[Symbol.dispose](): void;
|
|
39
|
+
}
|
|
40
|
+
export declare function startCpuProfile(): SyncCPUProfileHandle;
|
|
9
41
|
declare const _default: {
|
|
10
42
|
getHeapStatistics: typeof getHeapStatistics;
|
|
11
43
|
getHeapSpaceStatistics: typeof getHeapSpaceStatistics;
|
|
44
|
+
getHeapCodeStatistics: typeof getHeapCodeStatistics;
|
|
12
45
|
setFlagsFromString: typeof setFlagsFromString;
|
|
13
46
|
getHeapSnapshot: typeof getHeapSnapshot;
|
|
14
47
|
writeHeapSnapshot: typeof writeHeapSnapshot;
|
|
15
|
-
getHeapCodeStatistics: typeof getHeapCodeStatistics;
|
|
16
48
|
serialize: typeof serialize;
|
|
17
49
|
deserialize: typeof deserialize;
|
|
50
|
+
isStringOneByteRepresentation: typeof isStringOneByteRepresentation;
|
|
51
|
+
Serializer: typeof Serializer;
|
|
52
|
+
Deserializer: typeof Deserializer;
|
|
53
|
+
DefaultSerializer: typeof DefaultSerializer;
|
|
54
|
+
DefaultDeserializer: typeof DefaultDeserializer;
|
|
55
|
+
GCProfiler: typeof GCProfiler;
|
|
56
|
+
startCpuProfile: typeof startCpuProfile;
|
|
18
57
|
};
|
|
19
58
|
export default _default;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
export declare class Serializer {
|
|
3
|
+
protected _bytes: number[];
|
|
4
|
+
protected _seen: object[];
|
|
5
|
+
protected _treatAbvAsHostObjects: boolean;
|
|
6
|
+
_getDataCloneError: ErrorConstructor;
|
|
7
|
+
writeHeader(): void;
|
|
8
|
+
writeValue(value: unknown): void;
|
|
9
|
+
releaseBuffer(): Buffer;
|
|
10
|
+
writeUint32(n: number): void;
|
|
11
|
+
writeUint64(hi: number, lo: number): void;
|
|
12
|
+
writeDouble(d: number): void;
|
|
13
|
+
writeRawBytes(source: NodeJS.ArrayBufferView): void;
|
|
14
|
+
_writeHostObject(_obj: object): void;
|
|
15
|
+
_setTreatArrayBufferViewsAsHostObjects(flag: boolean): void;
|
|
16
|
+
private _writeString;
|
|
17
|
+
private _writeBigInt;
|
|
18
|
+
}
|
|
19
|
+
export declare class Deserializer {
|
|
20
|
+
protected _pos: number;
|
|
21
|
+
protected _wireVersion: number;
|
|
22
|
+
protected _seen: unknown[];
|
|
23
|
+
readonly buffer: Buffer;
|
|
24
|
+
constructor(buffer: NodeJS.ArrayBufferView | ArrayBuffer);
|
|
25
|
+
readHeader(): boolean;
|
|
26
|
+
getWireFormatVersion(): number;
|
|
27
|
+
readValue(): unknown;
|
|
28
|
+
readUint32(): number;
|
|
29
|
+
readUint64(): [number, number];
|
|
30
|
+
readDouble(): number;
|
|
31
|
+
_readRawBytes(length: number): number;
|
|
32
|
+
readRawBytes(length: number): Buffer;
|
|
33
|
+
_readHostObject(): unknown;
|
|
34
|
+
private _readBigInt;
|
|
35
|
+
}
|
|
36
|
+
export declare class DefaultSerializer extends Serializer {
|
|
37
|
+
constructor();
|
|
38
|
+
_writeHostObject(abView: NodeJS.ArrayBufferView): void;
|
|
39
|
+
}
|
|
40
|
+
export declare class DefaultDeserializer extends Deserializer {
|
|
41
|
+
_readHostObject(): NodeJS.ArrayBufferView;
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/v8",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Node.js v8 module for Gjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
22
22
|
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
23
23
|
"test": "yarn build:gjsify && yarn build:test && yarn test:node && yarn test:gjs",
|
|
24
|
-
"test:gjs": "
|
|
24
|
+
"test:gjs": "gjsify run test.gjs.mjs",
|
|
25
25
|
"test:node": "node test.node.mjs"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"v8"
|
|
31
31
|
],
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@gjsify/cli": "^0.
|
|
34
|
-
"@gjsify/unit": "^0.
|
|
33
|
+
"@gjsify/cli": "^0.2.0",
|
|
34
|
+
"@gjsify/unit": "^0.2.0",
|
|
35
35
|
"@types/node": "^25.6.0",
|
|
36
|
-
"typescript": "^6.0.
|
|
36
|
+
"typescript": "^6.0.3"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/heap.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Reference: Node.js lib/v8.js (getHeapStatistics)
|
|
2
|
+
// Reimplemented for GJS using /proc/self/status (Linux)
|
|
3
|
+
|
|
4
|
+
import GLib from '@girs/glib-2.0';
|
|
5
|
+
|
|
6
|
+
function readProcStatus(): Map<string, number> {
|
|
7
|
+
const map = new Map<string, number>();
|
|
8
|
+
try {
|
|
9
|
+
const [ok, contents] = GLib.file_get_contents('/proc/self/status');
|
|
10
|
+
if (!ok || !contents) return map;
|
|
11
|
+
const text = new TextDecoder().decode(contents as unknown as Uint8Array);
|
|
12
|
+
for (const line of text.split('\n')) {
|
|
13
|
+
const m = /^(\w+):\s+(\d+)(\s+kB)?/.exec(line);
|
|
14
|
+
if (m) map.set(m[1], parseInt(m[2]) * (m[3] ? 1024 : 1));
|
|
15
|
+
}
|
|
16
|
+
} catch { /* /proc not available on non-Linux */ }
|
|
17
|
+
return map;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getHeapStatistics(): {
|
|
21
|
+
total_heap_size: number;
|
|
22
|
+
total_heap_size_executable: number;
|
|
23
|
+
total_physical_size: number;
|
|
24
|
+
total_available_size: number;
|
|
25
|
+
used_heap_size: number;
|
|
26
|
+
heap_size_limit: number;
|
|
27
|
+
malloced_memory: number;
|
|
28
|
+
peak_malloced_memory: number;
|
|
29
|
+
does_zap_garbage: number;
|
|
30
|
+
number_of_native_contexts: number;
|
|
31
|
+
number_of_detached_contexts: number;
|
|
32
|
+
total_global_handles_size: number;
|
|
33
|
+
used_global_handles_size: number;
|
|
34
|
+
external_memory: number;
|
|
35
|
+
} {
|
|
36
|
+
const proc = readProcStatus();
|
|
37
|
+
return {
|
|
38
|
+
total_heap_size: proc.get('VmSize') ?? 0,
|
|
39
|
+
total_heap_size_executable: 0,
|
|
40
|
+
total_physical_size: proc.get('VmRSS') ?? 0,
|
|
41
|
+
total_available_size: 0,
|
|
42
|
+
used_heap_size: proc.get('VmRSS') ?? 0,
|
|
43
|
+
heap_size_limit: 0,
|
|
44
|
+
malloced_memory: proc.get('VmData') ?? 0,
|
|
45
|
+
peak_malloced_memory: proc.get('VmPeak') ?? 0,
|
|
46
|
+
does_zap_garbage: 0,
|
|
47
|
+
number_of_native_contexts: 0,
|
|
48
|
+
number_of_detached_contexts: 0,
|
|
49
|
+
total_global_handles_size: 0,
|
|
50
|
+
used_global_handles_size: 0,
|
|
51
|
+
external_memory: 0,
|
|
52
|
+
};
|
|
53
|
+
}
|
package/src/index.spec.ts
CHANGED
|
@@ -1,28 +1,270 @@
|
|
|
1
|
+
// Ported from refs/node-test/parallel/test-v8-stats.js,
|
|
2
|
+
// refs/node-test/parallel/test-v8-serdes.js,
|
|
3
|
+
// refs/node-test/parallel/test-v8-deserialize-buffer.js
|
|
4
|
+
// Original: MIT, Node.js contributors.
|
|
5
|
+
// Rewritten for @gjsify/unit — behavior preserved, assertion dialect adapted.
|
|
6
|
+
|
|
1
7
|
import { describe, it, expect } from '@gjsify/unit';
|
|
2
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getHeapStatistics,
|
|
10
|
+
getHeapCodeStatistics,
|
|
11
|
+
getHeapSpaceStatistics,
|
|
12
|
+
serialize,
|
|
13
|
+
deserialize,
|
|
14
|
+
Serializer,
|
|
15
|
+
Deserializer,
|
|
16
|
+
DefaultSerializer,
|
|
17
|
+
DefaultDeserializer,
|
|
18
|
+
isStringOneByteRepresentation,
|
|
19
|
+
GCProfiler,
|
|
20
|
+
startCpuProfile,
|
|
21
|
+
} from 'node:v8';
|
|
3
22
|
|
|
4
23
|
export default async () => {
|
|
5
|
-
await describe('v8', async () => {
|
|
6
|
-
await it('
|
|
7
|
-
|
|
24
|
+
await describe('v8.getHeapStatistics', async () => {
|
|
25
|
+
await it('returns an object with all 14 required fields', async () => {
|
|
26
|
+
const stats = getHeapStatistics();
|
|
27
|
+
const fields = [
|
|
28
|
+
'total_heap_size',
|
|
29
|
+
'total_heap_size_executable',
|
|
30
|
+
'total_physical_size',
|
|
31
|
+
'total_available_size',
|
|
32
|
+
'used_heap_size',
|
|
33
|
+
'heap_size_limit',
|
|
34
|
+
'malloced_memory',
|
|
35
|
+
'peak_malloced_memory',
|
|
36
|
+
'does_zap_garbage',
|
|
37
|
+
'number_of_native_contexts',
|
|
38
|
+
'number_of_detached_contexts',
|
|
39
|
+
'total_global_handles_size',
|
|
40
|
+
'used_global_handles_size',
|
|
41
|
+
'external_memory',
|
|
42
|
+
];
|
|
43
|
+
for (const field of fields) {
|
|
44
|
+
expect(typeof (stats as any)[field]).toBe('number');
|
|
45
|
+
}
|
|
8
46
|
});
|
|
9
47
|
|
|
10
|
-
await it('
|
|
48
|
+
await it('used_heap_size is a positive number on Linux (via /proc)', async () => {
|
|
11
49
|
const stats = getHeapStatistics();
|
|
12
|
-
expect(stats).
|
|
13
|
-
expect(typeof stats.total_heap_size).toBe('number');
|
|
14
|
-
expect(typeof stats.used_heap_size).toBe('number');
|
|
50
|
+
expect(stats.used_heap_size).toBeGreaterThan(0);
|
|
15
51
|
});
|
|
52
|
+
});
|
|
16
53
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
54
|
+
await describe('v8.getHeapCodeStatistics', async () => {
|
|
55
|
+
await it('returns object with 4 numeric fields', async () => {
|
|
56
|
+
const stats = getHeapCodeStatistics() as any;
|
|
20
57
|
expect(typeof stats.code_and_metadata_size).toBe('number');
|
|
58
|
+
expect(typeof stats.bytecode_and_metadata_size).toBe('number');
|
|
59
|
+
expect(typeof stats.external_script_source_size).toBe('number');
|
|
60
|
+
expect(typeof stats.cpu_profiler_metadata_size).toBe('number');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await describe('v8.getHeapSpaceStatistics', async () => {
|
|
65
|
+
await it('returns an array', async () => {
|
|
66
|
+
expect(Array.isArray(getHeapSpaceStatistics())).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await describe('v8.serialize / v8.deserialize round-trips', async () => {
|
|
71
|
+
await it('null', async () => {
|
|
72
|
+
expect(deserialize(serialize(null))).toBe(null);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await it('undefined', async () => {
|
|
76
|
+
expect(deserialize(serialize(undefined))).toBe(undefined);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await it('boolean true', async () => {
|
|
80
|
+
expect(deserialize(serialize(true))).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await it('boolean false', async () => {
|
|
84
|
+
expect(deserialize(serialize(false))).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await it('integer', async () => {
|
|
88
|
+
expect(deserialize(serialize(42))).toBe(42);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await it('negative integer', async () => {
|
|
92
|
+
expect(deserialize(serialize(-7))).toBe(-7);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await it('float', async () => {
|
|
96
|
+
expect(deserialize(serialize(3.14))).toBe(3.14);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await it('ASCII string', async () => {
|
|
100
|
+
expect(deserialize(serialize('hello'))).toBe('hello');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await it('Unicode string', async () => {
|
|
104
|
+
expect(deserialize(serialize('héllo wörld'))).toBe('héllo wörld');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await it('Date', async () => {
|
|
108
|
+
const d = new Date('2020-01-01T00:00:00Z');
|
|
109
|
+
const result = deserialize(serialize(d)) as Date;
|
|
110
|
+
expect(result instanceof Date).toBe(true);
|
|
111
|
+
expect(result.getTime()).toBe(d.getTime());
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await it('plain object', async () => {
|
|
115
|
+
const obj = { a: 1, b: 'two', c: true };
|
|
116
|
+
const result = deserialize(serialize(obj)) as typeof obj;
|
|
117
|
+
expect(result.a).toBe(1);
|
|
118
|
+
expect(result.b).toBe('two');
|
|
119
|
+
expect(result.c).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await it('array', async () => {
|
|
123
|
+
const arr = [1, 'two', null, true];
|
|
124
|
+
const result = deserialize(serialize(arr)) as typeof arr;
|
|
125
|
+
expect(result[0]).toBe(1);
|
|
126
|
+
expect(result[1]).toBe('two');
|
|
127
|
+
expect(result[2]).toBe(null);
|
|
128
|
+
expect(result[3]).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await it('Uint8Array', async () => {
|
|
132
|
+
const ta = new Uint8Array([1, 2, 3]);
|
|
133
|
+
const result = deserialize(serialize(ta)) as Uint8Array;
|
|
134
|
+
expect(result instanceof Uint8Array).toBe(true);
|
|
135
|
+
expect(result[0]).toBe(1);
|
|
136
|
+
expect(result[1]).toBe(2);
|
|
137
|
+
expect(result[2]).toBe(3);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await it('Int32Array', async () => {
|
|
141
|
+
const ta = new Int32Array([-1, 0, 1]);
|
|
142
|
+
const result = deserialize(serialize(ta)) as Int32Array;
|
|
143
|
+
expect(result instanceof Int32Array).toBe(true);
|
|
144
|
+
expect(result[0]).toBe(-1);
|
|
145
|
+
expect(result[2]).toBe(1);
|
|
21
146
|
});
|
|
22
147
|
|
|
23
|
-
await it('
|
|
24
|
-
|
|
25
|
-
|
|
148
|
+
await it('Float64Array', async () => {
|
|
149
|
+
const ta = new Float64Array([1.1, 2.2, 3.3]);
|
|
150
|
+
const result = deserialize(serialize(ta)) as Float64Array;
|
|
151
|
+
expect(result instanceof Float64Array).toBe(true);
|
|
152
|
+
expect(Math.abs(result[0] - 1.1) < 1e-9).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await it('Buffer', async () => {
|
|
156
|
+
const buf = Buffer.from([10, 20, 30]);
|
|
157
|
+
const result = deserialize(serialize(buf)) as Buffer;
|
|
158
|
+
expect(Buffer.isBuffer(result)).toBe(true);
|
|
159
|
+
expect(result[0]).toBe(10);
|
|
160
|
+
expect(result[2]).toBe(30);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await it('BigInt', async () => {
|
|
164
|
+
const big = 9007199254740993n;
|
|
165
|
+
expect(deserialize(serialize(big))).toBe(big);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
await it('negative BigInt', async () => {
|
|
169
|
+
expect(deserialize(serialize(-42n))).toBe(-42n);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await it('circular object produces backref on deserialize', async () => {
|
|
173
|
+
const obj: any = { a: 1 };
|
|
174
|
+
obj.self = obj;
|
|
175
|
+
const result = deserialize(serialize(obj)) as any;
|
|
176
|
+
expect(result.a).toBe(1);
|
|
177
|
+
expect(result.self).toBe(result);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await describe('v8.Serializer / Deserializer classes', async () => {
|
|
182
|
+
await it('Serializer can write header + value → Buffer', async () => {
|
|
183
|
+
const s = new Serializer();
|
|
184
|
+
s.writeHeader();
|
|
185
|
+
s.writeValue(42);
|
|
186
|
+
const buf = s.releaseBuffer();
|
|
187
|
+
expect(Buffer.isBuffer(buf)).toBe(true);
|
|
188
|
+
expect(buf.length).toBeGreaterThan(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await it('Deserializer can read header + value', async () => {
|
|
192
|
+
const s = new Serializer();
|
|
193
|
+
s.writeHeader();
|
|
194
|
+
s.writeValue('test');
|
|
195
|
+
const buf = s.releaseBuffer();
|
|
196
|
+
|
|
197
|
+
const d = new Deserializer(buf);
|
|
198
|
+
d.readHeader();
|
|
199
|
+
expect(d.readValue()).toBe('test');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await it('getWireFormatVersion returns 15', async () => {
|
|
203
|
+
const s = new Serializer();
|
|
204
|
+
s.writeHeader();
|
|
205
|
+
s.writeValue(null);
|
|
206
|
+
const buf = s.releaseBuffer();
|
|
207
|
+
const d = new Deserializer(buf);
|
|
208
|
+
d.readHeader();
|
|
209
|
+
expect(d.getWireFormatVersion()).toBe(15);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await describe('v8.DefaultSerializer / DefaultDeserializer', async () => {
|
|
214
|
+
await it('TypedArray host-object round-trip', async () => {
|
|
215
|
+
const original = new Uint32Array([100, 200, 300]);
|
|
216
|
+
const s = new DefaultSerializer();
|
|
217
|
+
s.writeHeader();
|
|
218
|
+
s.writeValue(original);
|
|
219
|
+
const buf = s.releaseBuffer();
|
|
220
|
+
|
|
221
|
+
const d = new DefaultDeserializer(buf);
|
|
222
|
+
d.readHeader();
|
|
223
|
+
const result = d.readValue() as Uint32Array;
|
|
224
|
+
expect(result instanceof Uint32Array).toBe(true);
|
|
225
|
+
expect(result[0]).toBe(100);
|
|
226
|
+
expect(result[1]).toBe(200);
|
|
227
|
+
expect(result[2]).toBe(300);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
await describe('v8.isStringOneByteRepresentation', async () => {
|
|
232
|
+
await it('ASCII string returns true', async () => {
|
|
233
|
+
expect(isStringOneByteRepresentation('hello')).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await it('string with ü (>255 codepoint) returns false', async () => {
|
|
237
|
+
// ü = U+00FC (fits in Latin-1), 日 = U+65E5 (does not)
|
|
238
|
+
expect(isStringOneByteRepresentation('日本語')).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
await it('empty string returns true', async () => {
|
|
242
|
+
expect(isStringOneByteRepresentation('')).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
await describe('v8.GCProfiler', async () => {
|
|
247
|
+
await it('start/stop returns defined object with version and timing', async () => {
|
|
248
|
+
const profiler = new GCProfiler();
|
|
249
|
+
profiler.start();
|
|
250
|
+
const stats = profiler.stop() as any;
|
|
251
|
+
expect(stats).toBeDefined();
|
|
252
|
+
expect(stats.version).toBe(1);
|
|
253
|
+
expect(typeof stats.startTime).toBe('number');
|
|
254
|
+
expect(typeof stats.endTime).toBe('number');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await it('stop without start returns undefined', async () => {
|
|
258
|
+
const profiler = new GCProfiler();
|
|
259
|
+
expect(profiler.stop()).toBe(undefined);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
await describe('v8.startCpuProfile', async () => {
|
|
264
|
+
await it('returns an object with a stop() method that does not throw', async () => {
|
|
265
|
+
const handle = startCpuProfile();
|
|
266
|
+
expect(typeof handle.stop).toBe('function');
|
|
267
|
+
expect(() => handle.stop()).not.toThrow();
|
|
26
268
|
});
|
|
27
269
|
});
|
|
28
270
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,37 +1,45 @@
|
|
|
1
|
-
// Reference: Node.js lib/v8.js
|
|
1
|
+
// Reference: Node.js lib/v8.js
|
|
2
|
+
// Reimplemented for GJS — heap stats via /proc/self/status, serialization via V8 wire format
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
external_memory: 0,
|
|
17
|
-
};
|
|
4
|
+
import { getHeapStatistics } from './heap.js';
|
|
5
|
+
import {
|
|
6
|
+
Serializer, Deserializer, DefaultSerializer, DefaultDeserializer,
|
|
7
|
+
} from './serdes.js';
|
|
8
|
+
|
|
9
|
+
export { getHeapStatistics };
|
|
10
|
+
export { Serializer, Deserializer, DefaultSerializer, DefaultDeserializer };
|
|
11
|
+
|
|
12
|
+
export function serialize(value: unknown): Buffer {
|
|
13
|
+
const ser = new DefaultSerializer();
|
|
14
|
+
ser.writeHeader();
|
|
15
|
+
ser.writeValue(value);
|
|
16
|
+
return ser.releaseBuffer();
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
export function
|
|
21
|
-
|
|
19
|
+
export function deserialize(buffer: NodeJS.ArrayBufferView | ArrayBuffer): unknown {
|
|
20
|
+
const des = new DefaultDeserializer(buffer);
|
|
21
|
+
des.readHeader();
|
|
22
|
+
return des.readValue();
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
// ─── Stubs — no GJS equivalent ────────────────────────────────────────────────
|
|
25
26
|
|
|
26
|
-
export
|
|
27
|
-
|
|
27
|
+
export interface HeapSpaceInfo {
|
|
28
|
+
space_name: string;
|
|
29
|
+
space_size: number;
|
|
30
|
+
space_used_size: number;
|
|
31
|
+
space_available_size: number;
|
|
32
|
+
physical_space_size: number;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
export function
|
|
31
|
-
return '';
|
|
32
|
-
}
|
|
35
|
+
export function getHeapSpaceStatistics(): HeapSpaceInfo[] { return []; }
|
|
33
36
|
|
|
34
|
-
export function getHeapCodeStatistics():
|
|
37
|
+
export function getHeapCodeStatistics(): {
|
|
38
|
+
code_and_metadata_size: number;
|
|
39
|
+
bytecode_and_metadata_size: number;
|
|
40
|
+
external_script_source_size: number;
|
|
41
|
+
cpu_profiler_metadata_size: number;
|
|
42
|
+
} {
|
|
35
43
|
return {
|
|
36
44
|
code_and_metadata_size: 0,
|
|
37
45
|
bytecode_and_metadata_size: 0,
|
|
@@ -40,21 +48,71 @@ export function getHeapCodeStatistics(): Record<string, number> {
|
|
|
40
48
|
};
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
export function
|
|
44
|
-
|
|
51
|
+
export function setFlagsFromString(_flags: string): void {}
|
|
52
|
+
|
|
53
|
+
export function getHeapSnapshot(_options?: object): null { return null; }
|
|
54
|
+
|
|
55
|
+
export function writeHeapSnapshot(_filename?: string): string { return ''; }
|
|
56
|
+
|
|
57
|
+
export function isStringOneByteRepresentation(content: string): boolean {
|
|
58
|
+
for (let i = 0; i < content.length; i++) {
|
|
59
|
+
if (content.charCodeAt(i) > 255) return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── GCProfiler ───────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
export class GCProfiler {
|
|
67
|
+
#running = false;
|
|
68
|
+
#startTime = 0;
|
|
69
|
+
|
|
70
|
+
start(): void {
|
|
71
|
+
if (this.#running) return;
|
|
72
|
+
this.#running = true;
|
|
73
|
+
this.#startTime = Date.now();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
stop(): { version: number; startTime: number; endTime: number; stats: never[] } | undefined {
|
|
77
|
+
if (!this.#running) return undefined;
|
|
78
|
+
this.#running = false;
|
|
79
|
+
try {
|
|
80
|
+
const system = (globalThis as any).imports?.system;
|
|
81
|
+
if (typeof system?.gc === 'function') system.gc();
|
|
82
|
+
} catch { /* ignore */ }
|
|
83
|
+
return { version: 1, startTime: this.#startTime, endTime: Date.now(), stats: [] };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
[Symbol.dispose](): void { this.stop(); }
|
|
45
87
|
}
|
|
46
88
|
|
|
47
|
-
|
|
48
|
-
|
|
89
|
+
// ─── SyncCPUProfileHandle / startCpuProfile ───────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export class SyncCPUProfileHandle {
|
|
92
|
+
stop(): undefined { return undefined; }
|
|
93
|
+
[Symbol.dispose](): void { this.stop(); }
|
|
49
94
|
}
|
|
50
95
|
|
|
96
|
+
export function startCpuProfile(): SyncCPUProfileHandle {
|
|
97
|
+
return new SyncCPUProfileHandle();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── default export ───────────────────────────────────────────────────────────
|
|
101
|
+
|
|
51
102
|
export default {
|
|
52
103
|
getHeapStatistics,
|
|
53
104
|
getHeapSpaceStatistics,
|
|
105
|
+
getHeapCodeStatistics,
|
|
54
106
|
setFlagsFromString,
|
|
55
107
|
getHeapSnapshot,
|
|
56
108
|
writeHeapSnapshot,
|
|
57
|
-
getHeapCodeStatistics,
|
|
58
109
|
serialize,
|
|
59
110
|
deserialize,
|
|
111
|
+
isStringOneByteRepresentation,
|
|
112
|
+
Serializer,
|
|
113
|
+
Deserializer,
|
|
114
|
+
DefaultSerializer,
|
|
115
|
+
DefaultDeserializer,
|
|
116
|
+
GCProfiler,
|
|
117
|
+
startCpuProfile,
|
|
60
118
|
};
|