Merge commit '6e92d2345e8231a44556ce7d416451f5f3e6c398' as 'engine262'

This commit is contained in:
2026-03-27 10:07:29 +05:30
433 changed files with 90478 additions and 0 deletions
@@ -0,0 +1,71 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`preview evaluation > [] 1`] = `
{
"className": "Array",
"description": "Array(0)",
"objectId": "default:1",
"preview": {
"description": "Array(0)",
"overflow": false,
"properties": [],
"subtype": "array",
"type": "object",
},
"subtype": "array",
"type": "object",
}
`;
exports[`preview evaluation > [1, 2, 3].map(x => x + 1) 1`] = `
{
"className": "Array",
"description": "Array(3)",
"objectId": "default:4",
"preview": {
"description": "Array(3)",
"overflow": false,
"properties": [
{
"name": "0",
"type": "number",
"value": "2",
},
{
"name": "1",
"type": "number",
"value": "3",
},
{
"name": "2",
"type": "number",
"value": "4",
},
],
"subtype": "array",
"type": "object",
},
"subtype": "array",
"type": "object",
}
`;
exports[`preview evaluation > { let a = 1; a } 1`] = `
{
"description": "1",
"type": "number",
"value": 1,
}
`;
exports[`preview evaluation > { var a = 1; a } 1`] = `"side effect"`;
exports[`preview evaluation > 1 1`] = `
{
"description": "1",
"type": "number",
"value": 1,
}
`;
exports[`preview evaluation > globalThis.x = 1 1`] = `"side effect"`;
@@ -0,0 +1,95 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`code in ShadowRealm 1`] = `
[
[
{
"columnNumber": 6,
"functionName": "<anonymous>",
"lineNumber": 0,
"scriptId": "1",
"url": "<anonymous>",
},
undefined,
{
"columnNumber": 15,
"functionName": "<anonymous>",
"lineNumber": 1,
"scriptId": "0",
"url": "<anonymous>",
},
],
]
`;
exports[`code in eval 1`] = `
[
[
{
"columnNumber": 6,
"functionName": "<anonymous>",
"lineNumber": 0,
"scriptId": "1",
"url": "<anonymous>",
},
{
"columnNumber": 5,
"functionName": "<anonymous>",
"lineNumber": 0,
"scriptId": "0",
"url": "<anonymous>",
},
],
[
{
"columnNumber": 12,
"functionName": "f",
"lineNumber": 1,
"scriptId": "2",
"url": "<anonymous>",
},
{
"columnNumber": 4,
"functionName": "<anonymous>",
"lineNumber": 3,
"scriptId": "2",
"url": "<anonymous>",
},
{
"columnNumber": 5,
"functionName": "<anonymous>",
"lineNumber": 1,
"scriptId": "0",
"url": "<anonymous>",
},
],
]
`;
exports[`code in new Function 1`] = `
[
[
{
"columnNumber": 14,
"functionName": "x",
"lineNumber": 4,
"scriptId": "1",
"url": "<anonymous>",
},
{
"columnNumber": 8,
"functionName": "y",
"lineNumber": 7,
"scriptId": "1",
"url": "<anonymous>",
},
{
"columnNumber": 17,
"functionName": "<anonymous>",
"lineNumber": 1,
"scriptId": "0",
"url": "<anonymous>",
},
],
]
`;
File diff suppressed because it is too large Load Diff
+278
View File
@@ -0,0 +1,278 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable quotes */
import { expect, test } from 'vitest';
import { TestInspector } from './utils.mts';
import { Agent, ManagedRealm, setSurroundingAgent } from '#self';
test('compile script (for invalid code break line)', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
const result = await inspector.runtime.compileScript({
expression: 'function f() {',
persistScript: false,
sourceURL: '',
executionContextId: 0,
});
expect(result).toMatchInlineSnapshot(`
{
"exceptionDetails": {
"columnNumber": 0,
"exception": {
"className": "SyntaxError",
"description": "SyntaxError: Unexpected end of input",
"objectId": "default:2",
"preview": {
"description": "
function f() {
^
SyntaxError: Unexpected end of source",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "message",
"type": "string",
"value": "Unexpected end of source",
},
{
"name": "stack",
"type": "string",
"value": "
function f() {
^
SyntaxError: Unexpected end of source",
},
],
"subtype": "error",
"type": "object",
},
"subtype": "error",
"type": "object",
},
"exceptionId": 1,
"lineNumber": 0,
"scriptId": undefined,
"stackTrace": {
"callFrames": [],
},
"text": "Uncaught",
"url": undefined,
},
}
`);
});
test('preview evaluation', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const code of [
`1`,
`[]`,
`{ var a = 1; a }`,
`{ let a = 1; a }`,
`[1, 2, 3].map(x => x + 1)`,
`globalThis.x = 1`,
]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any = await inspector.perview(code);
if (result.exceptionDetails?.exception.preview.properties[0].value === 'Preview evaluator cannot evaluate side-effecting code') {
expect('side effect').toMatchSnapshot(code);
} else {
expect(result).toMatchSnapshot(code);
}
}
});
test('get local lexical names', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
expect(await inspector.runtime.globalLexicalScopeNames({
executionContextId: 0,
})).toMatchInlineSnapshot(`
{
"names": [
"Infinity",
"NaN",
"undefined",
"globalThis",
"eval",
"isFinite",
"isNaN",
"parseFloat",
"parseInt",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"AggregateError",
"Array",
"ArrayBuffer",
"Boolean",
"BigInt",
"BigInt64Array",
"BigUint64Array",
"DataView",
"Date",
"Error",
"EvalError",
"FinalizationRegistry",
"Float32Array",
"Float64Array",
"Function",
"Int8Array",
"Int16Array",
"Int32Array",
"Iterator",
"Map",
"Number",
"Object",
"Promise",
"Proxy",
"RangeError",
"ReferenceError",
"RegExp",
"Set",
"ShadowRealm",
"String",
"Symbol",
"SyntaxError",
"TypeError",
"Uint8Array",
"Uint8ClampedArray",
"Uint16Array",
"Uint32Array",
"URIError",
"WeakMap",
"WeakRef",
"WeakSet",
"JSON",
"Math",
"Reflect",
],
}
`);
});
test('call function on', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
inspector.flush();
await inspector.eval('const a = { x: 1 }; a');
expect(inspector.flush()).toMatchInlineSnapshot(`
[
{
"method": "Debugger.scriptParsed",
"params": {
"buildId": "",
"endColumn": 21,
"endLine": 1,
"executionContextId": 0,
"hash": "",
"isModule": false,
"scriptId": "0",
"startColumn": 0,
"startLine": 0,
"url": "vm:///0",
},
},
{
"id": 0,
"method": "Runtime.evaluate",
"params": {
"expression": "const a = { x: 1 }; a",
"uniqueContextId": "0",
},
},
{
"id": 0,
"result": {
"exceptionDetails": undefined,
"result": {
"className": "Object",
"description": "Object",
"objectId": "default:1",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "x",
"type": "number",
"value": "1",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
},
},
]
`);
expect(await inspector.runtime.callFunctionOn({
functionDeclaration: 'function (x) { return this.x + x }',
arguments: [{ value: 1 }],
executionContextId: 0,
objectId: 'default:1',
})).toMatchInlineSnapshot(`
{
"description": "2",
"type": "number",
"value": 2,
}
`);
expect(await inspector.runtime.callFunctionOn({
functionDeclaration: 'function (x) { return [this.x, x, 2, 3] }',
arguments: [{ value: 1 }],
executionContextId: 0,
objectId: 'default:1',
returnByValue: true,
})).toMatchInlineSnapshot(`
{
"type": "object",
"value": [
1,
1,
2,
3,
],
}
`);
});
test('private field jailbreak', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
await inspector.eval('class A { #x = 1; }; globalThis.a = new A();');
await inspector.debugger.engine262_setEvaluateMode({ mode: 'console' });
expect(await inspector.runtime.evaluate({ expression: 'a.#x', uniqueContextId: '0' })).toMatchInlineSnapshot(`
{
"description": "1",
"type": "number",
"value": 1,
}
`);
});
+489
View File
@@ -0,0 +1,489 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable quotes */
import { expect, test } from 'vitest';
import { TestInspector } from './utils.mts';
import { Agent, ManagedRealm, setSurroundingAgent } from '#self';
test('evaluate on frame', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
const paused = inspector.eval(`
'use strict';
function f() {
const a = 1;
debugger;
}
function y() {
const a = 0;
f();
}
y();
`);
expect(inspector.flush()).toMatchInlineSnapshot(`
[
{
"method": "Runtime.executionContextCreated",
"params": {
"context": {
"id": 0,
"name": "engine262",
"origin": "vm://repl",
"uniqueId": "0",
},
},
},
{
"method": "Debugger.scriptParsed",
"params": {
"buildId": "",
"endColumn": 2,
"endLine": 12,
"executionContextId": 0,
"hash": "",
"isModule": false,
"scriptId": "0",
"startColumn": 0,
"startLine": 0,
"url": "vm:///0",
},
},
{
"method": "Debugger.paused",
"params": {
"callFrames": [
{
"callFrameId": "3",
"canBeRestarted": false,
"functionLocation": {
"columnNumber": 17,
"lineNumber": 2,
"scriptId": "0",
},
"functionName": "f",
"location": {
"columnNumber": 6,
"lineNumber": 4,
"scriptId": "0",
},
"scopeChain": [
{
"object": {
"className": "Object",
"description": "Object",
"objectId": "default:1",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "a",
"type": "number",
"value": "1",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
"type": "local",
},
{
"object": {
"className": "Object",
"description": "Object",
"objectId": "default:2",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": true,
"properties": [
{
"name": "Infinity",
"type": "number",
"value": "Infinity",
},
{
"name": "NaN",
"type": "number",
"value": "NaN",
},
{
"name": "undefined",
"type": "undefined",
"value": "undefined",
},
{
"name": "globalThis",
"subtype": undefined,
"type": "object",
"value": "Object",
},
{
"name": "eval",
"type": "function",
"value": "",
},
{
"name": "isFinite",
"type": "function",
"value": "",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
"type": "global",
},
],
"this": {
"type": "undefined",
},
"url": "",
},
{
"callFrameId": "2",
"canBeRestarted": false,
"functionLocation": {
"columnNumber": 17,
"lineNumber": 6,
"scriptId": "0",
},
"functionName": "y",
"location": {
"columnNumber": 6,
"lineNumber": 8,
"scriptId": "0",
},
"scopeChain": [
{
"object": {
"className": "Object",
"description": "Object",
"objectId": "default:3",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "a",
"type": "number",
"value": "0",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
"type": "local",
},
{
"object": {
"className": "Object",
"description": "Object",
"objectId": "default:2",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": true,
"properties": [
{
"name": "Infinity",
"type": "number",
"value": "Infinity",
},
{
"name": "NaN",
"type": "number",
"value": "NaN",
},
{
"name": "undefined",
"type": "undefined",
"value": "undefined",
},
{
"name": "globalThis",
"subtype": undefined,
"type": "object",
"value": "Object",
},
{
"name": "eval",
"type": "function",
"value": "",
},
{
"name": "isFinite",
"type": "function",
"value": "",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
"type": "global",
},
],
"this": {
"type": "undefined",
},
"url": "",
},
{
"callFrameId": "1",
"canBeRestarted": false,
"functionLocation": undefined,
"functionName": "<anonymous>",
"location": {
"columnNumber": 4,
"lineNumber": 10,
"scriptId": "0",
},
"scopeChain": [
{
"object": {
"className": "Object",
"description": "Object",
"objectId": "default:2",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": true,
"properties": [
{
"name": "Infinity",
"type": "number",
"value": "Infinity",
},
{
"name": "NaN",
"type": "number",
"value": "NaN",
},
{
"name": "undefined",
"type": "undefined",
"value": "undefined",
},
{
"name": "globalThis",
"subtype": undefined,
"type": "object",
"value": "Object",
},
{
"name": "eval",
"type": "function",
"value": "",
},
{
"name": "isFinite",
"type": "function",
"value": "",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
"type": "global",
},
],
"this": {
"className": "Object",
"description": "Object",
"objectId": "default:2",
"preview": {
"description": "Object",
"entries": undefined,
"overflow": true,
"properties": [
{
"name": "Infinity",
"type": "number",
"value": "Infinity",
},
{
"name": "NaN",
"type": "number",
"value": "NaN",
},
{
"name": "undefined",
"type": "undefined",
"value": "undefined",
},
{
"name": "globalThis",
"subtype": undefined,
"type": "object",
"value": "Object",
},
{
"name": "eval",
"type": "function",
"value": "",
},
{
"name": "isFinite",
"type": "function",
"value": "",
},
],
"subtype": undefined,
"type": "object",
},
"subtype": undefined,
"type": "object",
},
"url": "",
},
],
"reason": "debugCommand",
},
},
{
"id": 0,
"method": "Runtime.evaluate",
"params": {
"expression": "
'use strict';
function f() {
const a = 1;
debugger;
}
function y() {
const a = 0;
f();
}
y();
",
"uniqueContextId": "0",
},
},
]
`);
expect(await inspector.debugger.evaluateOnCallFrame({
callFrameId: "3",
expression: 'a',
})).toMatchInlineSnapshot(`
{
"description": "1",
"type": "number",
"value": 1,
}
`);
expect(await inspector.debugger.evaluateOnCallFrame({
callFrameId: "2",
expression: 'a',
})).toMatchInlineSnapshot(`
{
"description": "0",
"type": "number",
"value": 0,
}
`);
expect(await inspector.debugger.evaluateOnCallFrame({
callFrameId: "1",
expression: 'a',
})).toMatchInlineSnapshot(`
{
"exceptionDetails": {
"columnNumber": 0,
"exception": {
"className": "SyntaxError",
"description": "ReferenceError: 'a' is not defined
at <anonymous>:1:1
at <anonymous>:11:5",
"objectId": "default:5",
"preview": {
"description": "ReferenceError: 'a' is not defined
at <anonymous>:1:1
at <anonymous>:11:5",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "message",
"type": "string",
"value": "'a' is not defined",
},
],
"subtype": "error",
"type": "object",
},
"subtype": "error",
"type": "object",
},
"exceptionId": 4,
"lineNumber": 0,
"scriptId": "0",
"stackTrace": {
"callFrames": [
{
"columnNumber": 0,
"functionName": "<anonymous>",
"lineNumber": 0,
"scriptId": "0",
"url": "<anonymous>",
},
{
"columnNumber": 4,
"functionName": "<anonymous>",
"lineNumber": 10,
"scriptId": "0",
"url": "<anonymous>",
},
],
},
"text": "Uncaught",
"url": "<anonymous>",
},
"result": {
"className": "SyntaxError",
"description": "ReferenceError: 'a' is not defined
at <anonymous>:1:1
at <anonymous>:11:5",
"objectId": "default:5",
"preview": {
"description": "ReferenceError: 'a' is not defined
at <anonymous>:1:1
at <anonymous>:11:5",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "message",
"type": "string",
"value": "'a' is not defined",
},
],
"subtype": "error",
"type": "object",
},
"subtype": "error",
"type": "object",
},
}
`);
await inspector.debugger.resume();
await paused;
});
+224
View File
@@ -0,0 +1,224 @@
import { expect, test } from 'vitest';
import { TestInspector } from './utils.mts';
import {
Agent, ManagedRealm, runJobQueue, setSurroundingAgent,
} from '#self';
import { createConsole } from '#self/inspector';
test('console', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
let count = 0;
createConsole(realm, {
log(args) {
count += args.length;
},
});
inspector.flush();
await inspector.eval('console.log("hello", "world")');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const a: any = inspector.flush();
a[1].params.timestamp = 0;
expect(a).toMatchInlineSnapshot(`
[
{
"method": "Debugger.scriptParsed",
"params": {
"buildId": "",
"endColumn": 29,
"endLine": 1,
"executionContextId": 0,
"hash": "",
"isModule": false,
"scriptId": "0",
"startColumn": 0,
"startLine": 0,
"url": "vm:///0",
},
},
{
"method": "Runtime.consoleAPICalled",
"params": {
"args": [
{
"type": "string",
"value": "hello",
},
{
"type": "string",
"value": "world",
},
],
"executionContextId": 0,
"timestamp": 0,
"type": "log",
},
},
{
"id": 0,
"method": "Runtime.evaluate",
"params": {
"expression": "console.log("hello", "world")",
"uniqueContextId": "0",
},
},
{
"id": 0,
"result": {
"exceptionDetails": undefined,
"result": {
"type": "undefined",
},
},
},
]
`);
expect(count).eq(2);
await inspector.perview('console.log("hello", "world")');
expect(count).eq(2);
});
test('unhandled promise rejection', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
inspector.flush();
await inspector.eval('var a = Promise.reject(new Error())');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const i: any = inspector.flush();
i[1].params.timestamp = 0;
expect(i).toMatchInlineSnapshot(`
[
{
"method": "Debugger.scriptParsed",
"params": {
"buildId": "",
"endColumn": 35,
"endLine": 1,
"executionContextId": 0,
"hash": "",
"isModule": false,
"scriptId": "0",
"startColumn": 0,
"startLine": 0,
"url": "vm:///0",
},
},
{
"method": "Runtime.exceptionThrown",
"params": {
"exceptionDetails": {
"columnNumber": 0,
"exception": {
"className": "Promise",
"description": "Promise",
"objectId": "default:2",
"preview": {
"description": "Promise",
"entries": undefined,
"overflow": false,
"properties": [
{
"name": "[[PromiseState]]",
"type": "string",
"value": "rejected",
},
{
"name": "[[PromiseResult]]",
"subtype": "error",
"type": "object",
"value": "Error
at <anonymous>:1:28",
},
],
"subtype": "promise",
"type": "object",
},
"subtype": "promise",
"type": "object",
},
"exceptionId": 1,
"lineNumber": 0,
"scriptId": undefined,
"stackTrace": undefined,
"text": "Uncaught (in promise)",
"url": undefined,
},
"timestamp": 0,
},
},
{
"id": 0,
"method": "Runtime.evaluate",
"params": {
"expression": "var a = Promise.reject(new Error())",
"uniqueContextId": "0",
},
},
{
"id": 0,
"result": {
"exceptionDetails": undefined,
"result": {
"type": "undefined",
},
},
},
]
`);
await inspector.eval('void a.catch(() => {});');
runJobQueue();
expect(inspector.flush()).toMatchInlineSnapshot(`
[
{
"method": "Debugger.scriptParsed",
"params": {
"buildId": "",
"endColumn": 23,
"endLine": 1,
"executionContextId": 0,
"hash": "",
"isModule": false,
"scriptId": "1",
"startColumn": 0,
"startLine": 0,
"url": "vm:///1",
},
},
{
"method": "Runtime.exceptionRevoked",
"params": {
"exceptionId": 1,
"reason": "Handler added to rejected promise",
},
},
{
"id": 1,
"method": "Runtime.evaluate",
"params": {
"expression": "void a.catch(() => {});",
"uniqueContextId": "0",
},
},
{
"id": 1,
"result": {
"exceptionDetails": undefined,
"result": {
"type": "undefined",
},
},
},
]
`);
});
+90
View File
@@ -0,0 +1,90 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable quotes */
import { expect, test } from 'vitest';
import { TestInspector } from './utils.mts';
import {
Agent, Construct, CreateBuiltinFunction, Descriptor, evalQ, getHostDefinedErrorStack, ManagedRealm, setSurroundingAgent,
surroundingAgent,
Value,
type ShadowRealmObject,
} from '#self';
test('code in eval', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
const messages: unknown[] = [];
realm.scope(() => {
realm.GlobalObject.properties.set('e', new Descriptor({
Value: CreateBuiltinFunction.from(function* e(e = Value.undefined) {
messages.push(getHostDefinedErrorStack(e)?.map((f) => f.toCallFrame()));
}),
}));
});
await inspector.eval([
`e(new Error());`,
`function f() {
e(new Error());
};
f();`,
].map((code) => `eval(\`${code}\`)`).join('\n'));
expect(messages).matchSnapshot();
});
test('code in new Function', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
const messages: unknown[] = [];
realm.scope(() => {
realm.GlobalObject.properties.set('e', new Descriptor({
Value: CreateBuiltinFunction.from(function* e(e = Value.undefined) {
messages.push(getHostDefinedErrorStack(e)?.map((f) => f.toCallFrame()));
}),
}));
});
await inspector.eval(`
new Function(\`
function x() {
e(new Error());
}
function y() {
x()
}
return y\`)()()
`);
expect(messages).matchSnapshot();
});
test('code in ShadowRealm', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
const messages: unknown[] = [];
realm.scope(() => {
evalQ((_Q, X) => {
const shadowRealm = X(Construct(surroundingAgent.intrinsic('%ShadowRealm%'))) as ShadowRealmObject;
realm.GlobalObject.properties.set('r', new Descriptor({
Value: shadowRealm,
}));
shadowRealm.ShadowRealm.GlobalObject.properties.set('e', new Descriptor({
Value: CreateBuiltinFunction.from(function* e(e = Value.undefined) {
messages.push(getHostDefinedErrorStack(e)?.map((f) => f.toCallFrame()));
}),
}));
});
});
await inspector.eval(`
r.evaluate('e(new Error())');
`);
expect(messages).matchSnapshot();
});
@@ -0,0 +1,341 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-await-in-loop */
import { expect, test } from 'vitest';
import type Protocol from 'devtools-protocol';
import { TestInspector } from './utils.mts';
import { Agent, ManagedRealm, setSurroundingAgent } from '#self';
test('primitive values', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'undefined',
'null',
'false',
'true',
'42',
'-42',
'42n',
'-42n',
'0',
'-0',
'Infinity',
'-Infinity',
'NaN',
'"engine262"',
'Symbol()',
'Symbol("desc")',
'Symbol.for("symbol")',
'Symbol.iterator',
]) {
// eslint-disable-next-line no-await-in-loop
expect(await inspector.eval(value)).toMatchSnapshot(value);
}
});
test('functions', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
// function declaration
'function f() { /* comment */ }; f',
'function* f() { /* comment */ }; f',
'function *f() { /* comment */ }; f',
'async function f() { /* comment */ }; f',
'async function* f() { /* comment */ }; f',
'async function *f() { /* comment */ }; f',
// function expression
'(function f() { /* comment */ })',
'(function* f() { /* comment */ })',
'(function *f() { /* comment */ })',
'(async function f() { /* comment */ })',
'(async function* f() { /* comment */ })',
'(async function *f() { /* comment */ })',
// arrow expression
'(() => { /* comment */ })',
'(() => 42)',
'(async () => { /* comment */ })',
'(async () => 42)',
// computed function name
'var a = 1; ({ [a]() {} })[a]',
'({ *[Symbol.iterator]() {} })[Symbol.iterator]',
// getter & setter
'var o = { get f() { /* comment */ } }; Reflect.getOwnPropertyDescriptor(o, "f").get',
'var o = { set f(v) { /* comment */ } }; Reflect.getOwnPropertyDescriptor(o, "f").set',
// getter & setter with computed name
'var o = { get [Symbol.iterator]() { /* comment */ } }; Reflect.getOwnPropertyDescriptor(o, Symbol.iterator).get',
'var o = { set [Symbol.iterator](v) { /* comment */ } }; Reflect.getOwnPropertyDescriptor(o, Symbol.iterator).set',
// built-in function
'Array.prototype.map',
// built-in getter
'Reflect.getOwnPropertyDescriptor(Function.prototype, "caller").get',
// method
'class C { static method() {} }; C.method',
'class C { constructor() {}; #f }; C.prototype.constructor',
]) {
// eslint-disable-next-line no-await-in-loop
expect(await inspector.eval(value), value).toMatchSnapshot(value);
}
});
async function snapshotObject(inspector: TestInspector, value: string) {
const result = await inspector.eval(value);
expect(result).toMatchSnapshot(value);
const properties = await inspector.runtime.getProperties({ objectId: (result as any).objectId!, ownProperties: true, generatePreview: true }) as Protocol.Protocol.Runtime.GetPropertiesResponse;
properties.internalProperties = properties.internalProperties?.filter((prop) => prop.name !== '[[Prototype]]');
expect(properties).toMatchSnapshot(`${value} properties`);
}
test('array', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'[]',
'[1]',
'Array(10)',
'[,,,]',
'var a = [1,,2]; a.x = 1; a',
]) {
await snapshotObject(inspector, value);
}
});
test('regex', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'/cat/',
'/cat/g',
'/cat/i',
'var a = /cat/; a.lastIndex = 1; a',
]) {
expect(await inspector.eval(value)).toMatchSnapshot(value);
}
});
test('date', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new Date(0)',
'new Date(NaN)',
'new Date(-1)',
'new Date(9999999999999)',
]) {
expect(await inspector.eval(value)).toMatchSnapshot(value);
}
});
test('map and set', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new Map',
'new Map([["a", 1], ["b", 2]])',
'var x = new Map([["a", 1], ["b", 2]]); x.x = 1; x',
'new Set',
'new Set(["a", 1, "b", 2])',
'var x = new Set(["a", 1, "b", 2]); x.x = 1; x',
'new WeakMap',
'new WeakMap([[{}, 1], [{}, 2]])',
'var x = new WeakMap([[{}, 1], [{}, 2]]); x.x = 1; x',
'new WeakSet',
'new WeakSet([{}, {}])',
'var x = new WeakSet([{}, {}]); x.x = 1; x',
]) {
await snapshotObject(inspector, value);
}
});
// TODO: generator
test('error', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new Error()',
'new Error("message")',
'new Error("message", { cause: new Error("cause") })',
'new RangeError()',
'new (class MyError extends Error {})()',
// TODO: className should not be syntaxError
'new (class MyError extends Error { constructor() { super(); this.message = "hello" } })()',
]) {
expect(await inspector.eval(value)).toMatchSnapshot(value);
}
});
test('proxy', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new Proxy({}, {})',
'new Proxy({}, { get: () => {} })',
'new Proxy({ a: 1 }, {})',
'new Proxy(Function, {})',
'new Proxy(() => {}, {})',
'var a = Proxy.revocable({}, {}); a.revoke(); a.proxy',
]) {
await snapshotObject(inspector, value);
}
});
test('promise', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new Promise(() => {})',
'var a = new Promise(() => {}); a.x = 1; a',
'Promise.resolve()',
'Promise.resolve(42)',
'Promise.reject()',
'Promise.reject(42)',
]) {
await snapshotObject(inspector, value);
}
});
test('typed array', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new Uint8Array()',
'new Uint8Array(10)',
'new Uint8Array([1, 2, 3])',
'var x = new Uint8Array(10); x.a = 1; x',
'new Int32Array()',
'new Int32Array(10)',
'new Int32Array([1, 2, 3])',
// TODO: test with detached arraybuffer
]) {
await snapshotObject(inspector, value);
}
});
test('array buffer', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new ArrayBuffer(0)',
'new ArrayBuffer(10)',
'var x = new ArrayBuffer(10); x.a = 1; x',
// TODO: test with detached arraybuffer
]) {
await snapshotObject(inspector, value);
}
});
test('data view', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'new DataView(new ArrayBuffer(0))',
'new DataView(new ArrayBuffer(10))',
'var x = new DataView(new ArrayBuffer(10), 0); x.a = 1; x',
]) {
await snapshotObject(inspector, value);
}
});
test('module namespace', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
await inspector.debugger.engine262_setEvaluateMode({ mode: 'module' });
for (const value of [
// Note: our inspector will return the module namespace object after evaluation
'',
'export const a = 1',
'export const b = 2; export { b as c }',
'export default 42',
'export default function() {}',
]) {
await snapshotObject(inspector, value);
}
});
test('shadow realm', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
await snapshotObject(inspector, 'new ShadowRealm');
expect(await inspector.eval('new ShadowRealm().evaluate("(() => {})")')).toMatchSnapshot('ShadowRealm function');
});
test('normal object', async () => {
const agent = new Agent();
setSurroundingAgent(agent);
const inspector = new TestInspector();
const realm = new ManagedRealm();
inspector.attachAgent(agent, [realm]);
for (const value of [
'({})',
'({ a: 1 })',
'({ a: 1, b: 2 })',
'({ __proto__: null })',
'({ __proto__: { a: 1 } })',
'({ [Symbol.iterator]: () => {} })',
'({ f() {} })',
'({ get f() {}, set f(v) {} })',
'{ class T { #priv = 1 }; new T }',
'{ class T { #priv = 1; normal = 2 }; new T }',
'({ a: 1n, b: undefined, c: null, d: true, e: Symbol.iterator, f: [] })',
]) {
await snapshotObject(inspector, value);
}
});
+86
View File
@@ -0,0 +1,86 @@
import type { DebuggerContext, DebuggerNamespace, RuntimeNamespace } from '../../lib/inspector/types.d.mts';
import { Inspector } from '#self/inspector';
export class TestInspector extends Inspector {
messages: object[] = [];
flush() {
const old = this.messages;
this.messages = [];
return old;
}
onInspectorMessage?: (message: object) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected override send(data: any): void {
this.messages.push(data);
if ('id' in data) {
let meaningfulData = data;
if (data.result) {
meaningfulData = data.result;
if (!data.result.exceptionDetails) {
if (data.result.result && Object.keys(data.result).length <= 2) {
meaningfulData = data.result.result;
}
}
}
this.#callbacks.at(data.id)?.resolve(meaningfulData);
}
this.onInspectorMessage?.(data);
}
protected override onMessage(id: number, method: string, params: object | void): void {
super.onMessage(id, method, params);
this.messages.push({ id, method, params });
}
debugger: {
[T in keyof DebuggerNamespace]-?: (params: DebuggerNamespace[T] extends undefined | ((params: infer O, context: DebuggerContext) => unknown) ? O : void) => Promise<object>;
};
runtime: {
[T in keyof RuntimeNamespace]-?: (params: RuntimeNamespace[T] extends undefined | ((params: infer O, context: DebuggerContext) => unknown) ? O : void) => Promise<object>;
};
#callbacks: PromiseWithResolvers<unknown>[] = [];
constructor() {
super();
const object = (namespace: string) => Object.create(
new Proxy({}, {
get: (_, p, receiver) => {
if (typeof p === 'symbol') {
return undefined;
}
const f = (params: object) => {
this.onMessage(this.#callbacks.length, `${namespace}.${p}`, params);
const promise = Promise.withResolvers();
this.#callbacks.push(promise);
return promise.promise;
};
Reflect.defineProperty(receiver, p, { configurable: true, value: f });
return f;
},
}),
);
this.runtime = object('Runtime');
this.debugger = object('Debugger');
}
// helpers
eval(expression: string) {
return this.runtime.evaluate({
expression,
uniqueContextId: '0',
});
}
perview(expression: string) {
return this.runtime.evaluate({
expression,
uniqueContextId: '0',
throwOnSideEffect: true,
});
}
}