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
+355
View File
@@ -0,0 +1,355 @@
import type { Protocol } from 'devtools-protocol';
import type {
DebuggerContext,
DebuggerNamespace, HeapProfilerNamespace, ProfilerNamespace, RuntimeNamespace,
TargetNamespace,
} from './types.mts';
import { getParsedEvent } from './internal-utils.mts';
import { InspectorContext } from './context.mts';
import {
Call, NormalCompletion, ObjectValue, ParseScript, runJobQueue, ScriptRecord, surroundingAgent, ThrowCompletion, skipDebugger, Value, type FunctionObject,
ParseModule,
SourceTextModuleRecord, performDevtoolsEval,
ValueOfNormalCompletion,
JSStringValue,
evalQ,
Assert,
kInternal,
captureStack,
} from '#self';
export const Debugger: DebuggerNamespace = {
enable(_req, { onDebuggerAttached }) {
onDebuggerAttached();
return { debuggerId: 'debugger.0' };
},
getScriptSource({ scriptId }) {
const source = surroundingAgent.parsedSources.get(scriptId);
if (!source) {
throw new Error('Not found');
}
return { scriptSource: source.ECMAScriptCode.sourceText };
},
setAsyncCallStackDepth() { },
setBlackboxPatterns() { },
setBlackboxExecutionContexts() { },
// #region breakpoints
getPossibleBreakpoints() {
// getPossibleBreakpoints({ start, end, restrictToFunction }) {
return { locations: [] };
// return { locations: getBreakpointCandidates(start, end, restrictToFunction) };
},
removeBreakpoint({ breakpointId }) {
surroundingAgent?.removeBreakpoint(breakpointId);
},
// setBreakpoint({ location, condition }) { },
setBreakpointByUrl(req) {
return surroundingAgent?.addBreakpointByUrl(req);
},
// setBreakpointOnFunctionCall({ objectId, condition }) { },
setBreakpointsActive({ active }) {
surroundingAgent.breakpointsEnabled = active;
},
// setInstrumentationBreakpoint({ instrumentation }) { },
setPauseOnExceptions({ state }) {
if (surroundingAgent) {
surroundingAgent.pauseOnExceptions = state === 'none' ? undefined : state;
}
},
// #endregion
stepInto(_, { sendEvent }) {
sendEvent['Debugger.resumed']();
surroundingAgent.resumeEvaluate({ pauseAt: 'step-in' });
},
resume(_, { sendEvent }) {
sendEvent['Debugger.resumed']();
surroundingAgent.resumeEvaluate();
},
stepOver(_req, { sendEvent }) {
sendEvent['Debugger.resumed']();
surroundingAgent.resumeEvaluate({ pauseAt: 'step-over' });
},
stepOut(_req, { sendEvent }) {
sendEvent['Debugger.resumed']();
surroundingAgent.resumeEvaluate({ pauseAt: 'step-out' });
},
evaluateOnCallFrame(req, context) {
return evaluate({
...req,
uniqueContextId: context.context.getRealm(undefined)!.descriptor.uniqueId,
evalMode: context.context.evaluateMode,
}, context);
},
engine262_setEvaluateMode({ mode }, { context }) {
if (mode === 'module' || mode === 'script' || mode === 'console') {
context.evaluateMode = mode;
}
},
engine262_setFeatures() {
throw new Error('Method should not be implemented here.');
},
};
export const Profiler: ProfilerNamespace = {
enable() { },
};
export const Runtime: RuntimeNamespace = {
discardConsoleEntries() { },
enable() {},
compileScript(options, { context, sendEvent }) {
let parsed!: ScriptRecord | SourceTextModuleRecord | ObjectValue[];
let realm = context.getRealm(options.executionContextId);
if (!realm && !options.persistScript) {
realm = context.getAnyRealm();
}
if (!realm) {
return unsupportedError;
}
realm.realm.scope(() => {
if (context.evaluateMode === 'module') {
parsed = ParseModule(options.expression, realm.realm, { specifier: options.sourceURL, doNotTrackScriptId: !options.persistScript });
} else {
parsed = ParseScript(options.expression, realm.realm, { specifier: options.sourceURL, doNotTrackScriptId: !options.persistScript, [kInternal]: { allowAllPrivateNames: true } });
}
});
if (!parsed) {
throw new Error('No parsed result');
}
if (Array.isArray(parsed)) {
const e = context.createExceptionDetails(ThrowCompletion(parsed[0]), false);
// Note: it has to be this message to trigger devtools' line wrap.
e.exception!.description = 'SyntaxError: Unexpected end of input';
return { exceptionDetails: e };
}
if (options.persistScript) {
if (realm?.descriptor.id === undefined) {
throw new Error('No realm id found');
}
const event = getParsedEvent(parsed, parsed.HostDefined.scriptId!, realm.descriptor.id);
sendEvent['Debugger.scriptParsed'](event);
return { scriptId: event.scriptId };
}
return {};
},
callFunctionOn(options, { context }): Protocol.Runtime.CallFunctionOnResponse {
const realmDesc = context.getRealm(options.uniqueContextId || options.executionContextId) || context.getAnyRealm();
if (!realmDesc) {
throw new Error('No realm found');
}
const { Value: F } = realmDesc.realm.evaluateScript(`(${options.functionDeclaration})`, { doNotTrackScriptId: true }) as NormalCompletion<FunctionObject>;
const thisValue = options.objectId
? context.getObject(options.objectId)!
: Value.undefined;
const args = options.arguments?.map((a) => {
// TODO: revisit
if ('value' in a) {
return Value(a.value);
}
if (a.objectId) {
return context.getObject(a.objectId)!;
}
if ('unserializableValue' in a) {
throw new RangeError();
}
return Value.undefined;
});
return realmDesc.realm.scope((): Protocol.Runtime.CallFunctionOnResponse => {
const completion = evalQ((Q, X): Protocol.Runtime.CallFunctionOnResponse => {
const r = Q(skipDebugger(Call(F, thisValue, args || [])));
if (options.returnByValue) {
const value = X(Call(realmDesc.realm.Intrinsics['%JSON.stringify%'], Value.undefined, [r]));
if (value instanceof JSStringValue) {
const valueRealized = JSON.parse(value.stringValue());
return { result: { type: typeof value, value: valueRealized } };
}
}
return context.createEvaluationResult(r);
});
if (completion instanceof ThrowCompletion) {
return { result: { type: 'undefined' }, exceptionDetails: context.createExceptionDetails(completion, false) };
}
return completion.Value;
});
},
evaluate(options, context) {
return evaluate({
...options,
evalMode: context.context.evaluateMode,
uniqueContextId: options.uniqueContextId!,
}, context);
},
getExceptionDetails(req, { context }) {
const object = context.getObject(req.errorObjectId)!;
if (object instanceof ObjectValue) {
return {
exceptionDetails: context.createExceptionDetails(ThrowCompletion(object), false),
};
}
return {
exceptionDetails: {
text: 'unsupported', lineNumber: 0, columnNumber: 0, exceptionId: 0,
},
};
},
getHeapUsage() {
return {
usedSize: 0, totalSize: 0, backingStorageSize: 0, embedderHeapUsedSize: 0,
};
},
getIsolateId() {
return { id: 'isolate.0' };
},
getProperties(options, { context }) {
return context.getProperties(options);
},
globalLexicalScopeNames({ executionContextId }, { context }) {
const global = context.getRealm(executionContextId)?.realm.GlobalObject;
if (!global) {
return { names: [] };
}
const keys = skipDebugger(global.OwnPropertyKeys());
if (keys instanceof ThrowCompletion) {
return { names: [] };
}
return { names: ValueOfNormalCompletion(keys).map((k) => (k instanceof JSStringValue ? k.stringValue() : null!)).filter(Boolean) };
},
releaseObject(req, { context }) {
context.releaseObject(req.objectId);
},
releaseObjectGroup({ objectGroup }, { context }) {
context.releaseObjectGroup(objectGroup);
},
runIfWaitingForDebugger() { },
};
export const HeapProfiler: HeapProfilerNamespace = {
enable() { },
collectGarbage() { },
};
export const Target: TargetNamespace = {
setDiscoverTargets() { },
// @ts-expect-error no doc
setRemoteLocations() { },
};
const unsupportedError: Protocol.Runtime.EvaluateResponse = {
result: { type: 'undefined' },
exceptionDetails: {
text: 'unsupported', lineNumber: 0, columnNumber: 0, exceptionId: 0,
},
};
function evaluate(options: {
uniqueContextId: string,
expression: string,
evalMode: InspectorContext['evaluateMode'],
throwOnSideEffect?: boolean,
awaitPromise?: boolean,
callFrameId?: string,
}, _context: DebuggerContext): Protocol.Runtime.EvaluateResponse | Promise<Protocol.Runtime.EvaluateResponse> {
const { context } = _context;
const isPreview = options.throwOnSideEffect;
if (options.awaitPromise) {
return unsupportedError;
}
const realm = context.getRealm(options.uniqueContextId);
if (!realm) {
return unsupportedError;
}
const isCallOnFrame = typeof options.callFrameId === 'string';
let callOnFramePoppedLevel = 0;
const oldExecutionStack = [...surroundingAgent.executionContextStack];
if (isCallOnFrame) {
const frame = surroundingAgent.executionContextStack[options.callFrameId as `${number}`];
if (!frame) {
// eslint-disable-next-line no-console
console.error('Execution context not found: ', options.callFrameId);
return unsupportedError;
}
for (const currentFrame of [...surroundingAgent.executionContextStack].reverse()) {
if (currentFrame === frame) {
break;
}
callOnFramePoppedLevel += 1;
surroundingAgent.executionContextStack.pop(currentFrame);
}
}
const promise = new Promise<Protocol.Runtime.EvaluateResponse>((resolve) => {
let toBeEvaluated;
if (isPreview || options.evalMode === 'console' || isCallOnFrame) {
toBeEvaluated = performDevtoolsEval(options.expression, realm.realm, false, !!(isPreview || isCallOnFrame));
} else {
let parsed!: ScriptRecord | SourceTextModuleRecord | ObjectValue[];
const realm = context.getRealm(options.uniqueContextId);
realm?.realm.scope(() => {
if (options.evalMode === 'module') {
parsed = ParseModule(options.expression, realm.realm);
} else {
parsed = ParseScript(options.expression, realm.realm);
}
});
if (Array.isArray(parsed)) {
const e = context.createExceptionDetails(ThrowCompletion(parsed[0]), false);
resolve({ exceptionDetails: e, result: { type: 'undefined' } });
return;
}
toBeEvaluated = parsed;
}
const noDebuggerEvaluate = () => {
if (!('next' in toBeEvaluated)) {
throw new Assert.Error('Unexpected');
}
resolve(context.createEvaluationResult(skipDebugger(toBeEvaluated)));
};
if (isPreview) {
surroundingAgent.debugger_scopePreview(noDebuggerEvaluate);
return;
}
if (isCallOnFrame) {
noDebuggerEvaluate();
return;
}
const completion = realm.realm.evaluate(toBeEvaluated, (completion) => {
resolve(context.createEvaluationResult(completion));
runJobQueue();
});
if (completion) {
return;
}
surroundingAgent.resumeEvaluate();
});
promise.then(() => {
if (callOnFramePoppedLevel) {
Assert(oldExecutionStack.length - callOnFramePoppedLevel === surroundingAgent.executionContextStack.length);
for (const [newIndex, newStack] of surroundingAgent.executionContextStack.entries()) {
Assert(newStack === oldExecutionStack[newIndex]);
}
surroundingAgent.executionContextStack.length = 0;
for (const stack of oldExecutionStack) {
surroundingAgent.executionContextStack.push(stack);
}
}
}, (err): Protocol.Runtime.EvaluateResponse => {
const expr = surroundingAgent.runningExecutionContext.callSite.lastNode?.sourceText;
const frame = InspectorContext.callSiteToCallFrame(captureStack().stack);
_context.sendEvent['Runtime.exceptionThrown']({
timestamp: Date.now(),
exceptionDetails: {
stackTrace: frame.length ? { callFrames: frame } : undefined,
text: `engine262 error when evaluating the following node:\n\n ${expr}\n\n${err.constructor.name}: ${err.message}\n${err.stack.slice(err.stack.indexOf(err.message) + err.message.length + 1)}\n\nFrom now on, the engine262 VM state is broken, please press the reload button.`,
columnNumber: frame[0]?.columnNumber,
lineNumber: frame[0]?.lineNumber,
scriptId: frame[0]?.scriptId,
url: frame[0]?.url,
exceptionId: 0,
},
});
return {
result: { type: 'undefined' },
};
});
return promise;
}