mirror of
https://github.com/bendtherules/ask262.git
synced 2026-08-18 13:21:55 +00:00
Merge commit '6e92d2345e8231a44556ce7d416451f5f3e6c398' as 'engine262'
This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { getInspector } from './inspect.mts';
|
||||
import type { Inspector } from './index.mts';
|
||||
import {
|
||||
EnsureCompletion, JSStringValue, ManagedRealm, NullValue, ObjectValue, SymbolValue, ThrowCompletion, Value,
|
||||
getHostDefinedErrorStack,
|
||||
type ValueCompletion,
|
||||
getCurrentStack,
|
||||
isECMAScriptFunctionObject,
|
||||
SymbolDescriptiveString,
|
||||
type EnvironmentRecordWithThisBinding,
|
||||
EnvironmentRecord,
|
||||
DeclarativeEnvironmentRecord,
|
||||
ObjectEnvironmentRecord,
|
||||
FunctionEnvironmentRecord,
|
||||
GlobalEnvironmentRecord,
|
||||
ModuleEnvironmentRecord,
|
||||
OrdinaryObjectCreate,
|
||||
Descriptor,
|
||||
isArgumentExoticObject,
|
||||
Agent,
|
||||
surroundingAgent,
|
||||
IsAccessorDescriptor,
|
||||
isIntegerIndex,
|
||||
isBuiltinFunctionObject,
|
||||
isArrayBufferObject,
|
||||
DataBlock,
|
||||
CallSite,
|
||||
CallFrame,
|
||||
type OrdinaryObject,
|
||||
} from '#self';
|
||||
|
||||
interface InspectedRealmDescriptor {
|
||||
readonly realm: ManagedRealm;
|
||||
readonly descriptor: Protocol.Runtime.ExecutionContextDescription;
|
||||
readonly agent: Agent;
|
||||
detach(): void;
|
||||
}
|
||||
export class InspectorContext {
|
||||
#io: Inspector;
|
||||
|
||||
constructor(io: Inspector) {
|
||||
this.#io = io;
|
||||
}
|
||||
|
||||
realms: (InspectedRealmDescriptor | undefined)[] = [];
|
||||
|
||||
attachRealm(realm: ManagedRealm, agent: Agent) {
|
||||
const id = this.realms.length;
|
||||
const descriptor: Protocol.Runtime.ExecutionContextDescription = {
|
||||
id,
|
||||
origin: realm.HostDefined.specifier || 'vm://repl',
|
||||
name: realm.HostDefined.name || 'engine262',
|
||||
uniqueId: id.toString(),
|
||||
};
|
||||
this.realms.push({
|
||||
realm,
|
||||
descriptor,
|
||||
agent,
|
||||
detach: () => {
|
||||
realm.HostDefined.attachingInspector = oldInspector;
|
||||
realm.HostDefined.attachingInspectorReportError = function attachingInspectorReportError(realm, error) {
|
||||
if (this.attachingInspector && realm instanceof ManagedRealm) {
|
||||
(this.attachingInspector as Inspector).console(realm, 'error' as Protocol.Runtime.ConsoleAPICalledEventType, [error]);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
const oldInspector = realm.HostDefined.attachingInspector;
|
||||
realm.HostDefined.attachingInspector = this.#io;
|
||||
const oldPromiseRejectionTracker = realm.HostDefined.promiseRejectionTracker;
|
||||
realm.HostDefined.promiseRejectionTracker = (promise, operation) => {
|
||||
oldPromiseRejectionTracker?.(promise, operation);
|
||||
if (operation === 'reject') {
|
||||
this.#io.sendEvent['Runtime.exceptionThrown']({
|
||||
timestamp: Date.now(),
|
||||
exceptionDetails: this.createExceptionDetails(promise, true),
|
||||
});
|
||||
} else {
|
||||
const id = this.#exceptionMap.get(promise);
|
||||
if (id) {
|
||||
this.#io.sendEvent['Runtime.exceptionRevoked']({
|
||||
reason: 'Handler added to rejected promise',
|
||||
exceptionId: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
this.#io.sendEvent['Runtime.executionContextCreated']({ context: descriptor });
|
||||
}
|
||||
|
||||
detachAgent(agent: Agent) {
|
||||
for (const realm of this.realms) {
|
||||
if (realm?.agent === agent) {
|
||||
this.detachRealm(realm.realm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
detachRealm(realm: ManagedRealm) {
|
||||
const index = this.realms.findIndex((c) => c?.realm === realm);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
const { descriptor } = this.realms[index]!;
|
||||
realm.HostDefined.attachingInspector = undefined;
|
||||
realm.HostDefined.attachingInspectorReportError = undefined;
|
||||
this.realms[index] = undefined;
|
||||
this.#io.sendEvent['Runtime.executionContextDestroyed']({ executionContextId: descriptor.id, executionContextUniqueId: descriptor.uniqueId });
|
||||
}
|
||||
|
||||
getRealm(realm: ManagedRealm | string | number | undefined) {
|
||||
if (realm === undefined) {
|
||||
if (surroundingAgent.runningExecutionContext && surroundingAgent.currentRealmRecord instanceof ManagedRealm) {
|
||||
realm = surroundingAgent.currentRealmRecord;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (typeof realm === 'string') {
|
||||
return this.realms.find((c) => c?.descriptor.uniqueId === realm);
|
||||
} else if (typeof realm === 'number') {
|
||||
return this.realms[realm];
|
||||
}
|
||||
return this.realms.find((c) => c?.realm === realm);
|
||||
}
|
||||
|
||||
/** @deprecated in this case we are guessing the realm should be using, which may create bad result */
|
||||
getAnyRealm() {
|
||||
return this.realms.find(Boolean);
|
||||
}
|
||||
|
||||
#idToObject = new Map<string, ObjectValue | SymbolValue>();
|
||||
|
||||
// id 0 is falsy, skip it
|
||||
#idToArrayBufferBlock: (undefined | ArrayBuffer)[] = [undefined];
|
||||
|
||||
#objectToId = new Map<ObjectValue | SymbolValue, string>();
|
||||
|
||||
#objectCounter = 1;
|
||||
|
||||
#internObject(object: ObjectValue | SymbolValue, group = 'default') {
|
||||
if (this.#objectToId.has(object)) {
|
||||
return this.#objectToId.get(object)!;
|
||||
}
|
||||
const id = `${group}:${this.#objectCounter}`;
|
||||
this.#objectCounter += 1;
|
||||
this.#idToObject.set(id, object);
|
||||
this.#objectToId.set(object, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
releaseObject(id: string) {
|
||||
const object = this.#idToObject.get(id);
|
||||
if (object) {
|
||||
this.#idToObject.delete(id);
|
||||
this.#objectToId.delete(object);
|
||||
}
|
||||
}
|
||||
|
||||
releaseObjectGroup(group: string) {
|
||||
for (const [id, object] of this.#idToObject.entries()) {
|
||||
if (id.startsWith(group)) {
|
||||
this.#idToObject.delete(id);
|
||||
this.#objectToId.delete(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getObject(objectId: string) {
|
||||
return this.#idToObject.get(objectId);
|
||||
}
|
||||
|
||||
toRemoteObject(value: Value, options: { objectGroup?: string, generatePreview?: boolean }): Protocol.Runtime.RemoteObject {
|
||||
return getInspector(value).toRemoteObject(value, (val) => this.#internObject(val, options.objectGroup), options.generatePreview);
|
||||
}
|
||||
|
||||
getProperties({
|
||||
objectId, accessorPropertiesOnly, generatePreview, nonIndexedPropertiesOnly, ownProperties,
|
||||
}: Protocol.Runtime.GetPropertiesRequest): Protocol.Runtime.GetPropertiesResponse {
|
||||
const object = this.getObject(objectId);
|
||||
if (!(object instanceof ObjectValue)) {
|
||||
return { result: [] };
|
||||
}
|
||||
const wrap = (v: Value) => this.toRemoteObject(v, { generatePreview });
|
||||
|
||||
const properties: Protocol.Runtime.PropertyDescriptor[] = [];
|
||||
const internalProperties: Protocol.Runtime.InternalPropertyDescriptor[] = [];
|
||||
const privateProperties: Protocol.Runtime.PrivatePropertyDescriptor[] = [];
|
||||
|
||||
object.PrivateElements.forEach((value) => {
|
||||
privateProperties.push({
|
||||
name: value.Key.Description.stringValue(),
|
||||
value: value.Value ? wrap(value.Value) : undefined,
|
||||
get: value.Get ? wrap(value.Get) : undefined,
|
||||
set: value.Set ? wrap(value.Set) : undefined,
|
||||
});
|
||||
});
|
||||
|
||||
(() => {
|
||||
let p: NullValue | ObjectValue = object;
|
||||
while (p instanceof ObjectValue) {
|
||||
for (const key of p.properties.keys()) {
|
||||
if (nonIndexedPropertiesOnly && isIntegerIndex(key)) {
|
||||
continue;
|
||||
}
|
||||
const desc = (p.properties.get(key));
|
||||
if (!desc) {
|
||||
return;
|
||||
}
|
||||
if (accessorPropertiesOnly && !IsAccessorDescriptor(desc)) {
|
||||
continue;
|
||||
}
|
||||
const descriptor: Protocol.Runtime.PropertyDescriptor = {
|
||||
name: key instanceof JSStringValue
|
||||
? key.stringValue()
|
||||
: SymbolDescriptiveString(key).stringValue(),
|
||||
value: desc.Value && !('HostUninitializedBindingMarkerObject' in desc.Value) ? wrap(desc.Value) : undefined,
|
||||
writable: desc.Writable === Value.true,
|
||||
get: desc.Get ? wrap(desc.Get) : undefined,
|
||||
set: desc.Set ? wrap(desc.Set) : undefined,
|
||||
configurable: desc.Configurable === Value.true,
|
||||
enumerable: desc.Enumerable === Value.true,
|
||||
wasThrown: false,
|
||||
isOwn: p === object,
|
||||
symbol: key instanceof SymbolValue ? wrap(key) : undefined,
|
||||
};
|
||||
properties.push(descriptor);
|
||||
}
|
||||
|
||||
if (ownProperties) {
|
||||
break;
|
||||
}
|
||||
if ('Prototype' in p) {
|
||||
p = (p as OrdinaryObject).Prototype;
|
||||
} else {
|
||||
p = Value.null;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
const additionalInternalFields = getInspector(object).toInternalProperties?.(object, (val) => this.#internObject(val, 'default'), generatePreview);
|
||||
if (additionalInternalFields) {
|
||||
internalProperties.push(...additionalInternalFields);
|
||||
}
|
||||
|
||||
if ('Prototype' in object) {
|
||||
internalProperties.push({
|
||||
name: '[[Prototype]]',
|
||||
value: wrap(object.Prototype as Value),
|
||||
});
|
||||
}
|
||||
if (isBuiltinFunctionObject(object) && object.nativeFunction.section) {
|
||||
internalProperties.push({
|
||||
name: '[[Section]]',
|
||||
value: {
|
||||
type: 'string',
|
||||
value: object.nativeFunction.section,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (isArrayBufferObject(object) && object.ArrayBufferData instanceof DataBlock) {
|
||||
internalProperties.push({
|
||||
name: '[[ArrayBufferByteLength]]',
|
||||
value: {
|
||||
type: 'number',
|
||||
value: object.ArrayBufferByteLength,
|
||||
},
|
||||
});
|
||||
this.#idToArrayBufferBlock.push(object.ArrayBufferData.buffer);
|
||||
internalProperties.push({
|
||||
name: '[[ArrayBufferData]]',
|
||||
value: {
|
||||
type: 'number',
|
||||
value: this.#idToArrayBufferBlock.length - 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return { result: properties, internalProperties, privateProperties };
|
||||
}
|
||||
|
||||
#exceptionMap = new WeakMap<Value, number>();
|
||||
|
||||
createExceptionDetails(completion: ThrowCompletion | Value, isPromise: boolean): Protocol.Runtime.ExceptionDetails {
|
||||
const value = completion instanceof ThrowCompletion ? completion.Value : completion;
|
||||
const stack = getHostDefinedErrorStack(value);
|
||||
const frames = InspectorContext.callSiteToCallFrame(stack);
|
||||
const exceptionId = this.#objectCounter;
|
||||
this.#objectCounter += 1;
|
||||
this.#exceptionMap.set(value, exceptionId);
|
||||
return {
|
||||
text: isPromise ? 'Uncaught (in promise)' : 'Uncaught',
|
||||
stackTrace: stack ? { callFrames: frames } : undefined,
|
||||
exception: getInspector(value).toRemoteObject(value, (val) => this.#internObject(val), false),
|
||||
lineNumber: frames[0]?.lineNumber || 0,
|
||||
columnNumber: frames[0]?.columnNumber || 0,
|
||||
exceptionId,
|
||||
scriptId: frames[0]?.scriptId,
|
||||
url: frames[0]?.url,
|
||||
};
|
||||
}
|
||||
|
||||
static callSiteToCallFrame(callSite: readonly (CallSite | CallFrame)[] | undefined): Protocol.Runtime.CallFrame[] {
|
||||
return callSite?.map((call) => call.toCallFrame()!).filter(Boolean) || [];
|
||||
}
|
||||
|
||||
createEvaluationResult(completion: ValueCompletion): Protocol.Runtime.EvaluateResponse {
|
||||
completion = EnsureCompletion(completion);
|
||||
if (!(completion.Value instanceof Value)) {
|
||||
throw new RangeError('Invalid completion value');
|
||||
}
|
||||
return {
|
||||
exceptionDetails: completion instanceof ThrowCompletion ? this.createExceptionDetails(completion, false) : undefined,
|
||||
result: this.toRemoteObject(completion.Value, {}),
|
||||
};
|
||||
}
|
||||
|
||||
getDebuggerCallFrame(): Protocol.Debugger.CallFrame[] {
|
||||
const stacks = getCurrentStack(false);
|
||||
const length = surroundingAgent.executionContextStack.length;
|
||||
return stacks.map((stack, index): Protocol.Debugger.CallFrame => {
|
||||
if (!stack.getScriptId()) {
|
||||
return undefined!;
|
||||
}
|
||||
const scopeChain: Protocol.Debugger.Scope[] = [];
|
||||
let env: EnvironmentRecord | NullValue = stack.context.LexicalEnvironment;
|
||||
while (env instanceof EnvironmentRecord) {
|
||||
const result = getDisplayObjectFromEnvironmentRecord(env);
|
||||
if (result) {
|
||||
scopeChain.push({ type: result.type, object: this.toRemoteObject(result.object, {}) });
|
||||
}
|
||||
env = env.OuterEnv;
|
||||
}
|
||||
return {
|
||||
callFrameId: String(length - index - 1),
|
||||
functionName: stack.getFunctionName() || '<anonymous>',
|
||||
location: {
|
||||
scriptId: stack.getScriptId()!,
|
||||
lineNumber: (stack.lineNumber || 1) - 1,
|
||||
columnNumber: (stack.columnNumber || 1) - 1,
|
||||
},
|
||||
this: this.toRemoteObject(HostGetThisEnvironment(stack.context.LexicalEnvironment), {}),
|
||||
url: stack.getSpecifier() || '',
|
||||
canBeRestarted: false,
|
||||
functionLocation: isECMAScriptFunctionObject(stack.context.Function) ? {
|
||||
lineNumber: (stack.context.Function.ECMAScriptCode?.location.start.line || 1) - 1,
|
||||
columnNumber: (stack.context.Function.ECMAScriptCode?.location.start.column || 1) - 1,
|
||||
scriptId: stack.getScriptId() || '',
|
||||
} : undefined,
|
||||
scopeChain,
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
evaluateMode: 'script' | 'module' | 'console' = 'script';
|
||||
}
|
||||
|
||||
function HostGetThisEnvironment(env: EnvironmentRecord | NullValue): Value {
|
||||
while (!(env instanceof NullValue)) {
|
||||
const exists = env.HasThisBinding();
|
||||
if (exists === Value.true) {
|
||||
const value = (env as EnvironmentRecordWithThisBinding).GetThisBinding();
|
||||
if (value instanceof ThrowCompletion) {
|
||||
return Value.undefined;
|
||||
}
|
||||
return value as Value;
|
||||
}
|
||||
const outer = env.OuterEnv;
|
||||
env = outer;
|
||||
}
|
||||
throw new ReferenceError('No this environment found');
|
||||
}
|
||||
|
||||
function getDisplayObjectFromEnvironmentRecord(record: EnvironmentRecord): undefined | { type: Protocol.Debugger.Scope['type'], object: ObjectValue } {
|
||||
if (record instanceof DeclarativeEnvironmentRecord) {
|
||||
const object = OrdinaryObjectCreate(Value.null, ['HostInspectorScopePreview']);
|
||||
for (const [key, binding] of record.bindings) {
|
||||
const value = binding.initialized ? binding.value! : OrdinaryObjectCreate(Value.null, ['HostUninitializedBindingMarkerObject']);
|
||||
if (isArgumentExoticObject(value)) {
|
||||
continue;
|
||||
}
|
||||
object.properties.set(key, Descriptor({
|
||||
Enumerable: isArgumentExoticObject(value) ? Value.false : Value.true,
|
||||
Value: value,
|
||||
Writable: binding.mutable ? Value.true : Value.false,
|
||||
}));
|
||||
}
|
||||
let type: Protocol.Debugger.Scope['type'] = 'block';
|
||||
if (record instanceof FunctionEnvironmentRecord) {
|
||||
type = 'local';
|
||||
} else if (record instanceof ModuleEnvironmentRecord) {
|
||||
type = 'module';
|
||||
}
|
||||
if (type !== 'local' && !object.properties.size) {
|
||||
return undefined;
|
||||
}
|
||||
return { type, object };
|
||||
} else if (record instanceof ObjectEnvironmentRecord) {
|
||||
return { type: record.IsWithEnvironment === Value.true ? 'with' : 'global', object: record.BindingObject };
|
||||
} else if (record instanceof GlobalEnvironmentRecord) {
|
||||
return { type: 'global', object: record.GlobalThisValue };
|
||||
}
|
||||
throw new TypeError('Unknown environment record');
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { InspectorContext } from './context.mts';
|
||||
import * as impl from './methods.mts';
|
||||
import type { DebuggerContext, DebuggerPreference, DevtoolEvents } from './types.mts';
|
||||
import { getParsedEvent } from './internal-utils.mts';
|
||||
import {
|
||||
Agent, ManagedRealm, Realm, type Arguments,
|
||||
} from '#self';
|
||||
|
||||
const ignoreNamespaces = ['Network'];
|
||||
const ignoreMethods: string[] = [];
|
||||
|
||||
export type { DebuggerPreference } from './types.mts';
|
||||
export { createConsole } from './utils.mts';
|
||||
|
||||
interface AgentRecord {
|
||||
readonly agent: Agent;
|
||||
onDetach(): void;
|
||||
}
|
||||
export abstract class Inspector {
|
||||
#context = new InspectorContext(this);
|
||||
|
||||
#agents: AgentRecord[] = [];
|
||||
|
||||
attachAgent(agent: Agent, priorRealms: ManagedRealm[]) {
|
||||
const oldOnDebugger = agent.hostDefinedOptions.onDebugger;
|
||||
agent.hostDefinedOptions.onDebugger = () => {
|
||||
oldOnDebugger?.();
|
||||
this.sendEvent['Debugger.paused']({
|
||||
reason: 'debugCommand',
|
||||
callFrames: this.#context.getDebuggerCallFrame(),
|
||||
});
|
||||
};
|
||||
|
||||
const oldOnRealmCreated = agent.hostDefinedOptions.onRealmCreated;
|
||||
agent.hostDefinedOptions.onRealmCreated = (realm) => {
|
||||
oldOnRealmCreated?.(realm);
|
||||
this.#context.attachRealm(realm, agent);
|
||||
};
|
||||
|
||||
const oldOnScriptParsed = agent.hostDefinedOptions.onScriptParsed;
|
||||
agent.hostDefinedOptions.onScriptParsed = (script, id) => {
|
||||
oldOnScriptParsed?.(script, id);
|
||||
const realmId = this.#context.getRealm(script.Realm as ManagedRealm)?.descriptor.id;
|
||||
if (realmId === undefined) {
|
||||
return;
|
||||
}
|
||||
this.sendEvent['Debugger.scriptParsed'](getParsedEvent(script, id, realmId));
|
||||
};
|
||||
this.#agents.push({
|
||||
agent,
|
||||
onDetach: () => {
|
||||
agent.hostDefinedOptions.onDebugger = oldOnDebugger;
|
||||
agent.hostDefinedOptions.onRealmCreated = oldOnRealmCreated;
|
||||
agent.hostDefinedOptions.onScriptParsed = oldOnScriptParsed;
|
||||
this.#agents = this.#agents.filter((x) => x.agent !== agent);
|
||||
},
|
||||
});
|
||||
priorRealms.forEach((realm) => {
|
||||
this.#context.attachRealm(realm, agent);
|
||||
});
|
||||
}
|
||||
|
||||
detachAgent(agent: Agent) {
|
||||
const record = this.#agents.find((x) => x.agent === agent);
|
||||
record?.onDetach();
|
||||
this.#context.detachAgent(agent);
|
||||
}
|
||||
|
||||
protected abstract send(data: object): void;
|
||||
|
||||
readonly preference: DebuggerPreference = { previewDebug: false };
|
||||
|
||||
protected onMessage(id: unknown, methodArg: string, params: unknown): void {
|
||||
if (ignoreMethods.includes(methodArg)) {
|
||||
return;
|
||||
}
|
||||
const [namespace, method] = methodArg.split('.');
|
||||
if (ignoreNamespaces.includes(namespace)) {
|
||||
return;
|
||||
}
|
||||
if (!(namespace in impl)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Unknown namespace requested: ${namespace}`);
|
||||
return;
|
||||
}
|
||||
const ns = (impl as Record<string, object>)[namespace];
|
||||
if (!(method in ns)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Unknown method requested: ${namespace}.${method}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const f = (ns as Record<string, (args: unknown, context: DebuggerContext) => unknown>)[method];
|
||||
new Promise((resolve) => {
|
||||
resolve(f(params, this.#debugContext));
|
||||
}).then((result = {}) => {
|
||||
this.send({ id, result });
|
||||
});
|
||||
}
|
||||
|
||||
sendEvent: DevtoolEvents = Object.create(new Proxy({}, {
|
||||
get: (_, key: string) => {
|
||||
const f = (params: Record<string, unknown>) => {
|
||||
this.send({ method: key, params });
|
||||
};
|
||||
Object.defineProperty(this.sendEvent, key, { value: f });
|
||||
return f;
|
||||
},
|
||||
}));
|
||||
|
||||
console(realm: Realm, type: Protocol.Runtime.ConsoleAPICalledEventType, args: Arguments) {
|
||||
const context = this.#context.getRealm(realm as ManagedRealm);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
this.sendEvent['Runtime.consoleAPICalled']({
|
||||
type,
|
||||
args: args.map((x) => this.#context.toRemoteObject(x, { })),
|
||||
executionContextId: context.descriptor.id,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
#debugContext: DebuggerContext = {
|
||||
sendEvent: this.sendEvent,
|
||||
preference: this.preference,
|
||||
context: this.#context,
|
||||
onDebuggerAttached: () => {
|
||||
this.#context.realms.forEach((realm) => {
|
||||
if (realm) {
|
||||
this.sendEvent['Runtime.executionContextCreated']({
|
||||
context: realm.descriptor,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,624 @@
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import {
|
||||
BigIntValue,
|
||||
Descriptor,
|
||||
evalQ,
|
||||
Get,
|
||||
IntrinsicsFunctionToString, isArrayBufferObject, isArrayExoticObject, IsCallable, isDataViewObject, isDateObject, isECMAScriptFunctionObject, isErrorObject, isIntegerIndex, isMapObject, isPromiseObject, isProxyExoticObject, isRegExpObject, isSetObject, isTypedArrayObject, isWeakMapObject, isWeakSetObject, JSStringValue, NumberValue, ObjectValue, PrivateElementRecord, PrivateName, R, surroundingAgent, SymbolDescriptiveString, SymbolValue, ToString, skipDebugger, UndefinedValue, Value, type ArrayBufferObject, type BooleanValue, type DataViewObject, type DateObject, type FunctionObject, type MapObject, type NullValue, type PromiseObject, type PropertyKeyValue, type ProxyObject, type RegExpObject, type SetObject, type TypedArrayObject,
|
||||
type WeakMapObject,
|
||||
type WeakSetObject,
|
||||
type ModuleNamespaceObject,
|
||||
isModuleNamespaceObject,
|
||||
DataBlock,
|
||||
TypedArrayGetElement,
|
||||
TypedArrayLength,
|
||||
MakeTypedArrayWithBufferWitnessRecord,
|
||||
DateProto_toISOString,
|
||||
ValueOfNormalCompletion,
|
||||
NormalCompletion,
|
||||
type ShadowRealmObject,
|
||||
isShadowRealmObject,
|
||||
isWrappedFunctionExoticObject,
|
||||
ArrayExoticObjectInternalMethods,
|
||||
F,
|
||||
type TemporalInstantObject,
|
||||
TemporalInstantToString,
|
||||
isTemporalInstantObject,
|
||||
TemporalDurationToString,
|
||||
type TemporalDurationObject,
|
||||
isTemporalDurationObject,
|
||||
isTemporalPlainDateObject,
|
||||
type TemporalPlainDateObject,
|
||||
ISODateTimeToString,
|
||||
isTemporalPlainDateTimeObject,
|
||||
type TemporalPlainDateTimeObject,
|
||||
TemporalMonthDayToString,
|
||||
isTemporalPlainMonthDayObject,
|
||||
type TemporalPlainMonthDayObject,
|
||||
TimeRecordToString,
|
||||
isTemporalPlainTimeObject,
|
||||
type TemporalPlainTimeObject,
|
||||
TemporalYearMonthToString,
|
||||
isTemporalPlainYearMonthObject,
|
||||
type TemporalPlainYearMonthObject,
|
||||
TemporalDateToString,
|
||||
TemporalZonedDateTimeToString,
|
||||
isTemporalZonedDateTimeObject,
|
||||
type TemporalZonedDateTimeObject,
|
||||
} from '#self';
|
||||
|
||||
/*
|
||||
Test code: copy this into the inspector console.
|
||||
primitive: console.log('primitive:', null, undefined, true, false, 0, -0, NaN, Infinity, -Infinity, 1n, 'string', Symbol(), Symbol('text'), Symbol.for('global'), Symbol.iterator);
|
||||
fn: console.log('builtin:', eval, '\nfunction:', function() { code }, '\ngenerator:', function*() { code }, '\nasync:', async function() { code }, '\nasync generator:', async function*() { code }, '\narrow:', () => { code }, '\narrow async:', async () => { code });
|
||||
|
||||
normal: console.log('normal:', {}, new (class T { #a }), globalThis);
|
||||
arraybuffer: console.log('arraybuffer:', new ArrayBuffer(8));
|
||||
dataview: console.log('dataview:', new DataView(new ArrayBuffer(8)));
|
||||
map: console.log('map:', new Map(), new Map([[eval, globalThis], [1, 2]]));
|
||||
set: console.log('set:', new Set(), new Set([1, globalThis]));
|
||||
weakmap: console.log('weakmap:', new WeakMap(), new WeakMap([[{}, 1]]));
|
||||
weakset: console.log('weakset:', new WeakSet(), new WeakSet([{}]));
|
||||
date: console.log('date:', new Date());
|
||||
promise: console.log('promise:', new Promise(() => {}), Promise.resolve(globalThis), Promise.reject(globalThis));
|
||||
proxy: { const x = Proxy.revocable({}, {}); x.revoke(); console.log('proxy:', new Proxy({}, {}), new Proxy(function() {}, {}), x.proxy); }
|
||||
regexp: console.log('regexp:', /pattern/, new RegExp('pattern', 'g'));
|
||||
array: console.log('array:', [], [1, 2], Object.assign([1, 2], { a: 1 }), [0, ,,, 3]);
|
||||
typedarray: console.log('typedarray:', new Int8Array(8), new Int16Array(8), new Int32Array(8), new Uint8Array(8), new Uint16Array(8), new Uint32Array(8), new Uint8ClampedArray(8), new Float32Array(8), new Float64Array(8), new BigInt64Array(8), new BigUint64Array(8));
|
||||
*/
|
||||
interface Inspector<T extends Value> {
|
||||
toRemoteObject(value: T, getObjectId: (val: SymbolValue | ObjectValue) => string, generatePreview: boolean | undefined): Protocol.Runtime.RemoteObject;
|
||||
toObjectPreview(value: T): Protocol.Runtime.ObjectPreview;
|
||||
toPropertyPreview(name: string, value: T): Protocol.Runtime.PropertyPreview;
|
||||
toDescription(value: T): string;
|
||||
toInternalProperties?(value: T, getObjectId: (val: SymbolValue | ObjectValue) => string, generatePreview: boolean | undefined): Protocol.Runtime.InternalPropertyDescriptor[];
|
||||
}
|
||||
|
||||
const Null: Inspector<NullValue> = {
|
||||
toRemoteObject: () => ({ type: 'object', subtype: 'null', value: null }),
|
||||
toObjectPreview: () => ({
|
||||
type: 'object', subtype: 'null', properties: [], overflow: false,
|
||||
}),
|
||||
toPropertyPreview: (name) => ({
|
||||
name, type: 'object', subtype: 'null', value: 'null',
|
||||
}),
|
||||
toDescription: () => '',
|
||||
};
|
||||
|
||||
const Undefined: Inspector<UndefinedValue> = {
|
||||
toRemoteObject: () => ({ type: 'undefined' }),
|
||||
toObjectPreview: () => ({
|
||||
type: 'undefined', properties: [], overflow: false,
|
||||
}),
|
||||
toPropertyPreview: (name) => ({
|
||||
name, type: 'undefined', value: 'undefined',
|
||||
}),
|
||||
toDescription: () => 'undefined',
|
||||
};
|
||||
|
||||
const Boolean: Inspector<BooleanValue> = {
|
||||
toRemoteObject: (value) => ({ type: 'boolean', value: value.booleanValue() }),
|
||||
toPropertyPreview: (name, value) => ({
|
||||
name, type: 'boolean', value: value.booleanValue().toString(),
|
||||
}),
|
||||
toObjectPreview(value) {
|
||||
return {
|
||||
type: 'boolean',
|
||||
value: value.booleanValue(),
|
||||
description: value.booleanValue().toString(),
|
||||
overflow: false,
|
||||
properties: [],
|
||||
};
|
||||
},
|
||||
toDescription: (value) => value.booleanValue().toString(),
|
||||
};
|
||||
|
||||
const Symbol: Inspector<SymbolValue> = {
|
||||
toRemoteObject: (value, getObjectId) => ({
|
||||
type: 'symbol',
|
||||
description: SymbolDescriptiveString(value).stringValue(),
|
||||
objectId: getObjectId(value),
|
||||
}),
|
||||
toPropertyPreview: (name, value) => ({
|
||||
name, type: 'symbol', value: SymbolDescriptiveString(value).stringValue(),
|
||||
}),
|
||||
toObjectPreview: (value) => ({
|
||||
type: 'symbol',
|
||||
description: SymbolDescriptiveString(value).stringValue(),
|
||||
overflow: false,
|
||||
properties: [],
|
||||
}),
|
||||
toDescription: (value) => SymbolDescriptiveString(value).stringValue(),
|
||||
};
|
||||
|
||||
const String: Inspector<JSStringValue> = {
|
||||
toRemoteObject: (value) => ({ type: 'string', value: value.stringValue() }),
|
||||
toPropertyPreview(name, value) {
|
||||
return {
|
||||
name, type: 'string', value: value.stringValue(),
|
||||
};
|
||||
},
|
||||
toObjectPreview(value) {
|
||||
return {
|
||||
type: 'string',
|
||||
description: value.stringValue(),
|
||||
overflow: false,
|
||||
properties: [],
|
||||
};
|
||||
},
|
||||
toDescription: (value) => value.stringValue(),
|
||||
};
|
||||
|
||||
const Number: Inspector<NumberValue> = {
|
||||
toRemoteObject(value) {
|
||||
const v = R(value);
|
||||
let description = v.toString();
|
||||
const isNeg0 = Object.is(v, -0);
|
||||
// Includes values `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
|
||||
if (isNeg0 || !globalThis.Number.isFinite(v)) {
|
||||
if (typeof v === 'bigint') {
|
||||
description += 'n';
|
||||
return { type: 'bigint', unserializableValue: description, description };
|
||||
}
|
||||
return { type: 'number', unserializableValue: description, description: isNeg0 ? '-0' : description };
|
||||
}
|
||||
return { type: 'number', value: v, description };
|
||||
},
|
||||
toPropertyPreview(name, value) {
|
||||
return {
|
||||
name, type: 'number', value: this.toDescription(value),
|
||||
};
|
||||
},
|
||||
toObjectPreview(value) {
|
||||
return {
|
||||
type: 'number',
|
||||
description: this.toDescription(value),
|
||||
overflow: false,
|
||||
properties: [],
|
||||
};
|
||||
},
|
||||
toDescription: (value) => {
|
||||
const r = R(value);
|
||||
return value instanceof BigIntValue ? `${r}n` : r.toString();
|
||||
},
|
||||
};
|
||||
|
||||
function unwrapFunction(value: FunctionObject): FunctionObject {
|
||||
if (isWrappedFunctionExoticObject(value)) {
|
||||
return unwrapFunction(value.WrappedTargetFunction);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const Function: Inspector<FunctionObject> = {
|
||||
toRemoteObject(value, getObjectId) {
|
||||
value = unwrapFunction(value);
|
||||
const result: Protocol.Runtime.RemoteObject = {
|
||||
type: 'function',
|
||||
objectId: getObjectId(value),
|
||||
};
|
||||
result.description = IntrinsicsFunctionToString(value);
|
||||
if (isECMAScriptFunctionObject(value) && value.ECMAScriptCode) {
|
||||
if (value.ECMAScriptCode.type === 'FunctionBody') {
|
||||
result.className = 'Function';
|
||||
} else if (value.ECMAScriptCode.type === 'GeneratorBody') {
|
||||
result.className = 'GeneratorFunction';
|
||||
} else if (value.ECMAScriptCode.type === 'AsyncBody') {
|
||||
result.className = 'AsyncFunction';
|
||||
} else if (value.ECMAScriptCode.type === 'AsyncGeneratorBody') {
|
||||
result.className = 'AsyncGeneratorFunction';
|
||||
}
|
||||
} else {
|
||||
result.className = 'Function';
|
||||
}
|
||||
return result;
|
||||
},
|
||||
toPropertyPreview: (name) => ({ name, type: 'function', value: '' }),
|
||||
toObjectPreview(value) {
|
||||
return {
|
||||
type: 'function',
|
||||
description: IntrinsicsFunctionToString(value),
|
||||
overflow: false,
|
||||
properties: [],
|
||||
};
|
||||
},
|
||||
toDescription: () => 'Function',
|
||||
};
|
||||
|
||||
class ObjectInspector<T extends ObjectValue> implements Inspector<T> {
|
||||
subtype;
|
||||
|
||||
className;
|
||||
|
||||
toDescription;
|
||||
|
||||
private toEntries;
|
||||
|
||||
private additionalProperties;
|
||||
|
||||
private internalProperties;
|
||||
|
||||
constructor(
|
||||
className: string | ((value: Value) => string),
|
||||
subtype: Protocol.Runtime.RemoteObject['subtype'],
|
||||
toDescription: (value: T) => string,
|
||||
additionalOptions?: {
|
||||
entries?: (value: T) => Protocol.Runtime.ObjectPreview['entries'];
|
||||
additionalProperties?: (value: T) => Iterable<[string, Value]>;
|
||||
internalProperties?: (value: T) => Iterable<[string, Value | MapObject['MapData'] | SetObject['SetData']]>;
|
||||
},
|
||||
) {
|
||||
this.className = className;
|
||||
this.subtype = subtype;
|
||||
this.toDescription = toDescription;
|
||||
this.toEntries = additionalOptions?.entries;
|
||||
this.additionalProperties = additionalOptions?.additionalProperties;
|
||||
this.internalProperties = additionalOptions?.internalProperties;
|
||||
}
|
||||
|
||||
toRemoteObject(value: T, getObjectId: (val: ObjectValue) => string): Protocol.Runtime.RemoteObject {
|
||||
return {
|
||||
type: 'object',
|
||||
subtype: this.subtype,
|
||||
objectId: getObjectId(value),
|
||||
className: typeof this.className === 'string' ? this.className : this.className(value),
|
||||
description: this.toDescription(value),
|
||||
preview: this.toObjectPreview(value),
|
||||
};
|
||||
}
|
||||
|
||||
toPropertyPreview(name: string, value: T): Protocol.Runtime.PropertyPreview {
|
||||
return {
|
||||
name,
|
||||
type: 'object',
|
||||
subtype: this.subtype,
|
||||
value: this.toDescription(value),
|
||||
};
|
||||
}
|
||||
|
||||
toInternalProperties(value: T, getObjectId: (val: ObjectValue | SymbolValue) => string, generatePreview: boolean | undefined): Protocol.Runtime.InternalPropertyDescriptor[] {
|
||||
const internalProperties = [...this.internalProperties?.(value) || []];
|
||||
if (!internalProperties.length) {
|
||||
return [];
|
||||
}
|
||||
return internalProperties.map(([name, val]): Protocol.Runtime.InternalPropertyDescriptor => {
|
||||
let value: Protocol.Runtime.RemoteObject;
|
||||
if (val instanceof Value) {
|
||||
value = getInspector(val).toRemoteObject(val, getObjectId, generatePreview);
|
||||
} else {
|
||||
const array = new ObjectValue([]);
|
||||
array.DefineOwnProperty = ArrayExoticObjectInternalMethods.DefineOwnProperty;
|
||||
array.properties.set('length', Descriptor({ Value: F(val.length) }));
|
||||
for (const [index, item] of val.entries()) {
|
||||
let value;
|
||||
if (item instanceof Value) {
|
||||
value = item;
|
||||
} else {
|
||||
if (!item?.Key || !item.Value) {
|
||||
continue;
|
||||
}
|
||||
value = new ObjectValue(['InspectorEntry']);
|
||||
value.properties.set('key', Descriptor({ Value: item.Key }));
|
||||
value.properties.set('value', Descriptor({ Value: item.Value }));
|
||||
}
|
||||
array.properties.set(Value(index.toString()), Descriptor({ Value: value }));
|
||||
}
|
||||
value = Array.toRemoteObject(array, getObjectId, generatePreview);
|
||||
}
|
||||
return ({ name, value });
|
||||
});
|
||||
}
|
||||
|
||||
toObjectPreview(value: T): Protocol.Runtime.ObjectPreview {
|
||||
const e = this.toEntries?.(value);
|
||||
return {
|
||||
type: 'object',
|
||||
subtype: this.subtype,
|
||||
description: this.toDescription(value),
|
||||
entries: e?.length ? e : undefined,
|
||||
...propertiesToPropertyPreview(value, [...this.internalProperties?.(value) || [], ...this.additionalProperties?.(value) || []]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const InspectorEntry = new ObjectInspector<ObjectValue>('Object', 'internal#entry' as never, (value) => {
|
||||
const key = value.properties.get(Value('key'))!.Value!;
|
||||
const val = value.properties.get(Value('value'))!.Value!;
|
||||
return `{${getInspector(key).toDescription(key)} => ${getInspector(val).toDescription(val)}}`;
|
||||
});
|
||||
|
||||
const Default = new ObjectInspector<ObjectValue>('Object', undefined, (object) => {
|
||||
const [ctor] = object.ConstructedBy;
|
||||
if (!ctor) {
|
||||
return 'Object';
|
||||
}
|
||||
return propertyNameToString(ctor.HostInitialName);
|
||||
});
|
||||
|
||||
const ArrayBuffer = new ObjectInspector<ArrayBufferObject>('ArrayBuffer', 'arraybuffer', (value) => `ArrayBuffer(${value.ArrayBufferByteLength})`, {});
|
||||
const DataView = new ObjectInspector<DataViewObject>('DataView', 'dataview', (value) => `DataView(${value.ByteLength})`);
|
||||
const Error = new ObjectInspector<ObjectValue>('SyntaxError', 'error', (value) => {
|
||||
let text = '';
|
||||
surroundingAgent.debugger_scopePreview(() => {
|
||||
evalQ((Q) => {
|
||||
if (value instanceof ObjectValue) {
|
||||
const stack = Q(skipDebugger(Get(value, Value('stack'))));
|
||||
if (stack !== Value.undefined) {
|
||||
text += Q(skipDebugger(ToString(stack))).stringValue();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return text;
|
||||
});
|
||||
|
||||
const Map = new ObjectInspector<MapObject>('Map', 'map', (value) => `Map(${value.MapData.filter((x) => !!x.Key).length})`, {
|
||||
additionalProperties: (value) => [['size', Value(value.MapData.filter((x) => !!x.Key).length)]],
|
||||
internalProperties: (value) => [['[[Entries]]', value.MapData]],
|
||||
entries: (value) => value.MapData.filter((x) => x.Key).map(({ Key, Value }) => ({
|
||||
key: getInspector(Key!).toObjectPreview(Key!),
|
||||
value: getInspector(Value!).toObjectPreview(Value!),
|
||||
})),
|
||||
});
|
||||
const Set = new ObjectInspector<SetObject>('Set', 'set', (value) => `Set(${value.SetData.filter(globalThis.Boolean).length})`, {
|
||||
additionalProperties: (value) => [['size', Value(value.SetData.filter(globalThis.Boolean).length)]],
|
||||
internalProperties: (value) => [['[[Entries]]', value.SetData]],
|
||||
entries: (value) => value.SetData.filter(globalThis.Boolean).map((Value) => ({
|
||||
value: getInspector(Value!).toObjectPreview(Value!),
|
||||
})),
|
||||
});
|
||||
const WeakMap = new ObjectInspector<WeakMapObject>('WeakMap', 'weakmap', () => 'WeakMap', {
|
||||
internalProperties: (value) => [['[[Entries]]', value.WeakMapData]],
|
||||
entries: (value) => value.WeakMapData.filter((x) => x.Key).map(({ Key, Value }) => ({
|
||||
key: getInspector(Key!).toObjectPreview(Key!),
|
||||
value: getInspector(Value!).toObjectPreview(Value!),
|
||||
})),
|
||||
});
|
||||
const WeakSet = new ObjectInspector<WeakSetObject>('WeakSet', 'weakset', () => 'WeakSet', {
|
||||
internalProperties: (value) => [['[[Entries]]', value.WeakSetData]],
|
||||
entries: (value) => value.WeakSetData.filter(globalThis.Boolean).map((Value) => ({
|
||||
value: getInspector(Value!).toObjectPreview(Value!),
|
||||
})),
|
||||
});
|
||||
|
||||
const Date = new ObjectInspector<DateObject>('Date', 'date', ((value: DateObject) => {
|
||||
if (!globalThis.Number.isFinite(R(value.DateValue))) {
|
||||
return 'Invalid Date';
|
||||
}
|
||||
const val = DateProto_toISOString([], { thisValue: value, NewTarget: Value.undefined });
|
||||
return ValueOfNormalCompletion(val as NormalCompletion<JSStringValue>).stringValue();
|
||||
}));
|
||||
const TemporalInstant = new ObjectInspector<TemporalInstantObject>(
|
||||
'Temporal.Instant',
|
||||
'date',
|
||||
(value) => `Temporal.Instant <${TemporalInstantToString(value, undefined, 'auto')}>`,
|
||||
);
|
||||
const TemporalDuration = new ObjectInspector<TemporalDurationObject>('Temporal.Duration', 'date', (value) => `Temporal.Duration <${TemporalDurationToString(value, 'auto')}>`);
|
||||
const TemporalPlainDate = new ObjectInspector<TemporalPlainDateObject>('Temporal.PlainDate', 'date', (value) => `Temporal.PlainDate <${TemporalDateToString(value, 'auto')}>`);
|
||||
const TemporalPlainDateTime = new ObjectInspector<TemporalPlainDateTimeObject>(
|
||||
'Temporal.PlainDateTime',
|
||||
'date',
|
||||
(value) => `Temporal.PlainDateTime <${ISODateTimeToString(value.ISODateTime, value.Calendar, 'auto', 'auto')}>`,
|
||||
);
|
||||
const TemporalPlainMonthDay = new ObjectInspector<TemporalPlainMonthDayObject>(
|
||||
'Temporal.PlainMonthDay',
|
||||
'date',
|
||||
(value) => `Temporal.PlainMonthDay <${TemporalMonthDayToString(value, 'auto')}>`,
|
||||
);
|
||||
const TemporalPlainTime = new ObjectInspector<TemporalPlainTimeObject>('Temporal.PlainTime', 'date', (value) => `Temporal.PlainTime <${TimeRecordToString(value.Time, 'auto')}>`);
|
||||
const TemporalPlainYearMonth = new ObjectInspector<TemporalPlainYearMonthObject>(
|
||||
'Temporal.PlainYearMonth',
|
||||
'date',
|
||||
(value) => `Temporal.PlainYearMonth <${TemporalYearMonthToString(value, 'auto')}>`,
|
||||
);
|
||||
const TemporalZonedDateTime = new ObjectInspector<TemporalZonedDateTimeObject>(
|
||||
'Temporal.ZonedDateTime',
|
||||
'date',
|
||||
(value) => `Temporal.ZonedDateTime <${TemporalZonedDateTimeToString(value, 'auto', 'auto', 'auto', 'auto')}>`,
|
||||
);
|
||||
const Promise = new ObjectInspector<PromiseObject>('Promise', 'promise', () => 'Promise', {
|
||||
internalProperties: (value) => [['[[PromiseState]]', Value(value.PromiseState)], ['[[PromiseResult]]', value.PromiseResult || Value.undefined]],
|
||||
});
|
||||
const Proxy = new ObjectInspector<ProxyObject>('Proxy', 'proxy', (value) => {
|
||||
if (IsCallable(value.ProxyTarget)) {
|
||||
return 'Proxy(Function)';
|
||||
}
|
||||
if (value.ProxyTarget instanceof ObjectValue) {
|
||||
return 'Proxy(Object)';
|
||||
}
|
||||
return 'Proxy';
|
||||
});
|
||||
const RegExp = new ObjectInspector<RegExpObject>('RegExp', 'regexp', (value) => `/${value.OriginalSource.stringValue()}/${value.OriginalFlags.stringValue()}`);
|
||||
const Module = new ObjectInspector<ModuleNamespaceObject>('Module', undefined, () => 'Module', {});
|
||||
const ShadowRealm = new ObjectInspector<ShadowRealmObject>('ShadowRealm', undefined, () => 'ShadowRealm', {
|
||||
internalProperties: (realm) => [['[[GlobalObject]]', realm.ShadowRealm.GlobalObject]],
|
||||
});
|
||||
|
||||
const Array: Inspector<ObjectValue> = {
|
||||
toRemoteObject(value, getObjectId) {
|
||||
return {
|
||||
type: 'object',
|
||||
className: 'Array',
|
||||
subtype: 'array',
|
||||
objectId: getObjectId(value),
|
||||
description: getInspector(value).toDescription(value),
|
||||
preview: this.toObjectPreview?.(value),
|
||||
};
|
||||
},
|
||||
toPropertyPreview(name, value) {
|
||||
return {
|
||||
name, type: 'object', subtype: 'array', value: this.toDescription(value),
|
||||
};
|
||||
},
|
||||
toObjectPreview(value) {
|
||||
const result: Protocol.Runtime.ObjectPreview = {
|
||||
type: 'object',
|
||||
subtype: 'array',
|
||||
overflow: false,
|
||||
properties: [],
|
||||
description: this.toDescription(value),
|
||||
};
|
||||
const indexProp: Protocol.Runtime.PropertyPreview[] = [];
|
||||
const otherProp: Protocol.Runtime.PropertyPreview[] = [];
|
||||
for (const [key, desc] of value.properties) {
|
||||
if (indexProp.length > 100) {
|
||||
result.overflow = true;
|
||||
break;
|
||||
}
|
||||
if (isIntegerIndex(key)) {
|
||||
indexProp.push(propertyToPropertyPreview(key, desc));
|
||||
} else if (!(key instanceof JSStringValue && key.stringValue() === 'length')) {
|
||||
otherProp.push(propertyToPropertyPreview(key, desc));
|
||||
}
|
||||
}
|
||||
result.properties = indexProp.concat(otherProp).slice(0, 100);
|
||||
return result;
|
||||
},
|
||||
toDescription(value) {
|
||||
const length = [...value.properties.entries()].find(([key]) => key instanceof JSStringValue && key.stringValue() === 'length');
|
||||
if (!length || !(length[1].Value instanceof NumberValue)) {
|
||||
throw new TypeError('Bad ArrayExoticObject');
|
||||
}
|
||||
return `Array(${R(length[1].Value)})`;
|
||||
},
|
||||
};
|
||||
const TypedArray = new ObjectInspector<TypedArrayObject>('TypedArray', 'typedarray', (value) => `${value.TypedArrayName.stringValue()}(${value.ArrayLength})`);
|
||||
|
||||
function propertyNameToString(value: PropertyKeyValue | PrivateName): string {
|
||||
if (value instanceof JSStringValue) {
|
||||
return value.stringValue();
|
||||
} else if (value instanceof PrivateName) {
|
||||
return value.Description.stringValue();
|
||||
} else {
|
||||
return SymbolDescriptiveString(value).stringValue();
|
||||
}
|
||||
}
|
||||
function propertyToPropertyPreview(key: PropertyKeyValue | PrivateName, desc: Descriptor | PrivateElementRecord): Protocol.Runtime.PropertyPreview {
|
||||
const name = propertyNameToString(key);
|
||||
if (desc.Get || desc.Set) {
|
||||
return { name, type: 'accessor' };
|
||||
} else {
|
||||
return getInspector(desc.Value!).toPropertyPreview(name, desc.Value!);
|
||||
}
|
||||
}
|
||||
|
||||
function propertiesToPropertyPreview(value: ObjectValue, extra: undefined | Iterable<[string, Value | MapObject['MapData'] | SetObject['SetData']]>, max = 5) {
|
||||
let overflow = false;
|
||||
const properties: Protocol.Runtime.PropertyPreview[] = [];
|
||||
if (extra) {
|
||||
for (const [key, value] of extra) {
|
||||
if (value instanceof Value) {
|
||||
properties.push(getInspector(value).toPropertyPreview(key, value));
|
||||
}
|
||||
// TODO:... handle Value[]
|
||||
}
|
||||
}
|
||||
if (isTypedArrayObject(value) && value.ViewedArrayBuffer instanceof ObjectValue && value.ViewedArrayBuffer.ArrayBufferData instanceof DataBlock) {
|
||||
const record = MakeTypedArrayWithBufferWitnessRecord(value, 'seq-cst');
|
||||
const length = TypedArrayLength(record);
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
const index_value = TypedArrayGetElement(value, Value(index));
|
||||
if (index_value instanceof UndefinedValue) {
|
||||
break;
|
||||
}
|
||||
if (properties.length > 100) {
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
properties.push(getInspector(index_value).toPropertyPreview(index.toString(), index_value));
|
||||
}
|
||||
properties.push(
|
||||
{
|
||||
name: 'buffer', type: 'object', subtype: 'arraybuffer', value: `ArrayBuffer(${value.ViewedArrayBuffer.ArrayBufferData.byteLength})`,
|
||||
},
|
||||
{ name: 'byteLength', type: 'number', value: globalThis.String(value.ArrayLength) },
|
||||
{ name: 'byteOffset', type: 'number', value: globalThis.String(value.ByteOffset) },
|
||||
{ name: 'length', type: 'number', value: globalThis.String(length) },
|
||||
);
|
||||
}
|
||||
for (const [key, desc] of value.properties) {
|
||||
if (properties.length > max) {
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
properties.push(propertyToPropertyPreview(key, desc));
|
||||
}
|
||||
for (const desc of value.PrivateElements) {
|
||||
if (properties.length > max) {
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
properties.push(propertyToPropertyPreview(desc.Key, desc));
|
||||
}
|
||||
return { overflow, properties };
|
||||
}
|
||||
|
||||
export function getInspector(value: Value): Inspector<Value> {
|
||||
switch (true) {
|
||||
case value === Value.null:
|
||||
return Null;
|
||||
case value === Value.undefined:
|
||||
return Undefined;
|
||||
case value === Value.true || value === Value.false:
|
||||
return Boolean;
|
||||
case value instanceof SymbolValue:
|
||||
return Symbol;
|
||||
case value instanceof JSStringValue:
|
||||
return String;
|
||||
case value instanceof NumberValue:
|
||||
case value instanceof BigIntValue:
|
||||
return Number;
|
||||
case isProxyExoticObject(value):
|
||||
return Proxy;
|
||||
case IsCallable(value):
|
||||
return Function;
|
||||
case isArrayExoticObject(value):
|
||||
return Array;
|
||||
case isRegExpObject(value):
|
||||
return RegExp;
|
||||
case isDateObject(value):
|
||||
return Date;
|
||||
case isMapObject(value):
|
||||
return Map;
|
||||
case isSetObject(value):
|
||||
return Set;
|
||||
case isWeakMapObject(value):
|
||||
return WeakMap;
|
||||
case isWeakSetObject(value):
|
||||
return WeakSet;
|
||||
// generator
|
||||
case isErrorObject(value):
|
||||
return Error;
|
||||
case isPromiseObject(value):
|
||||
return Promise;
|
||||
case isTypedArrayObject(value):
|
||||
return TypedArray;
|
||||
case isArrayBufferObject(value):
|
||||
return ArrayBuffer;
|
||||
case isDataViewObject(value):
|
||||
return DataView;
|
||||
case isModuleNamespaceObject(value):
|
||||
return Module;
|
||||
case isShadowRealmObject(value):
|
||||
return ShadowRealm;
|
||||
case isTemporalInstantObject(value):
|
||||
return TemporalInstant;
|
||||
case isTemporalDurationObject(value):
|
||||
return TemporalDuration;
|
||||
case isTemporalPlainDateObject(value):
|
||||
return TemporalPlainDate;
|
||||
case isTemporalPlainDateTimeObject(value):
|
||||
return TemporalPlainDateTime;
|
||||
case isTemporalPlainMonthDayObject(value):
|
||||
return TemporalPlainMonthDay;
|
||||
case isTemporalPlainTimeObject(value):
|
||||
return TemporalPlainTime;
|
||||
case isTemporalPlainYearMonthObject(value):
|
||||
return TemporalPlainYearMonth;
|
||||
case isTemporalZonedDateTimeObject(value):
|
||||
return TemporalZonedDateTime;
|
||||
case (value as ObjectValue).internalSlotsList.includes('InspectorEntry'):
|
||||
return InspectorEntry;
|
||||
default:
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { DynamicParsedCodeRecord, SourceTextModuleRecord, type ScriptRecord } from '#self';
|
||||
|
||||
export function getParsedEvent(source: ScriptRecord | SourceTextModuleRecord | DynamicParsedCodeRecord, id: string, executionContextId: number): Protocol.Debugger.ScriptParsedEvent {
|
||||
const lines = source.ECMAScriptCode.sourceText.split('\n');
|
||||
return {
|
||||
isModule: source instanceof SourceTextModuleRecord,
|
||||
scriptId: id,
|
||||
url: source.HostDefined.specifier || `vm:///${id}`,
|
||||
startLine: 0,
|
||||
startColumn: 0,
|
||||
endLine: lines.length,
|
||||
endColumn: lines.pop()!.length,
|
||||
executionContextId,
|
||||
hash: '',
|
||||
buildId: '',
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"references": [{ "path": "../../src/" }],
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"incremental": true,
|
||||
"declarationDir": "../../lib/inspector",
|
||||
"tsBuildInfoFile": "../../lib/inspector/.tsbuildinfo",
|
||||
"erasableSyntaxOnly": true,
|
||||
"rootDir": "./",
|
||||
"outDir": "../../lib/inspector/",
|
||||
"allowImportingTsExtensions": true,
|
||||
"rewriteRelativeImportExtensions": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["./*.mts"]
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import type { InspectorContext } from './context.mts';
|
||||
|
||||
export interface DebuggerPreference {
|
||||
previewDebug: boolean;
|
||||
}
|
||||
|
||||
export interface DebuggerContext {
|
||||
sendEvent: DevtoolEvents;
|
||||
onDebuggerAttached(): void;
|
||||
preference: DebuggerPreference;
|
||||
context: InspectorContext;
|
||||
}
|
||||
|
||||
export interface DebuggerNamespace {
|
||||
engine262_setEvaluateMode(req: { mode: 'module' | 'script' | 'console' }, context: DebuggerContext): void;
|
||||
engine262_setFeatures(req: { features: string[] }, context: DebuggerContext): void;
|
||||
}
|
||||
export interface DebuggerNamespace {
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-continueToLocation */
|
||||
continueToLocation?(req: Protocol.Debugger.ContinueToLocationRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-disable */
|
||||
disable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-enable */
|
||||
enable?(req: Protocol.Debugger.EnableRequest, context: DebuggerContext): Protocol.Debugger.EnableResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-evaluateOnCallFrame */
|
||||
evaluateOnCallFrame?(req: Protocol.Debugger.EvaluateOnCallFrameRequest, context: DebuggerContext): Protocol.Debugger.EvaluateOnCallFrameResponse | Promise<Protocol.Debugger.EvaluateOnCallFrameResponse>;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-getPossibleBreakpoints */
|
||||
getPossibleBreakpoints?(req: Protocol.Debugger.GetPossibleBreakpointsRequest, context: DebuggerContext): Protocol.Debugger.GetPossibleBreakpointsResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-getScriptSource */
|
||||
getScriptSource?(req: Protocol.Debugger.GetScriptSourceRequest, context: DebuggerContext): Protocol.Debugger.GetScriptSourceResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-pause */
|
||||
pause?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-removeBreakpoint */
|
||||
removeBreakpoint?(req: Protocol.Debugger.RemoveBreakpointRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-restartFrame */
|
||||
restartFrame?(req: Protocol.Debugger.RestartFrameRequest, context: DebuggerContext): Protocol.Debugger.RestartFrameResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-resume */
|
||||
resume?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-searchInContent */
|
||||
searchInContent?(req: Protocol.Debugger.SearchInContentRequest, context: DebuggerContext): Protocol.Debugger.SearchInContentResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setAsyncCallStackDepth */
|
||||
setAsyncCallStackDepth?(req: Protocol.Debugger.SetAsyncCallStackDepthRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBreakpoint */
|
||||
setBreakpoint?(req: Protocol.Debugger.SetBreakpointRequest, context: DebuggerContext): Protocol.Debugger.SetBreakpointResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBreakpointByUrl */
|
||||
setBreakpointByUrl?(req: Protocol.Debugger.SetBreakpointByUrlRequest, context: DebuggerContext): Protocol.Debugger.SetBreakpointByUrlResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBreakpointsActive */
|
||||
setBreakpointsActive?(req: Protocol.Debugger.SetBreakpointsActiveRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setInstrumentationBreakpoint */
|
||||
setInstrumentationBreakpoint?(req: Protocol.Debugger.SetInstrumentationBreakpointRequest, context: DebuggerContext): Protocol.Debugger.SetInstrumentationBreakpointResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setPauseOnExceptions */
|
||||
setPauseOnExceptions?(req: Protocol.Debugger.SetPauseOnExceptionsRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setScriptSource */
|
||||
setScriptSource?(req: Protocol.Debugger.SetScriptSourceRequest, context: DebuggerContext): Protocol.Debugger.SetScriptSourceResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setSkipAllPauses */
|
||||
setSkipAllPauses?(req: Protocol.Debugger.SetSkipAllPausesRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setVariableValue */
|
||||
setVariableValue?(req: Protocol.Debugger.SetVariableValueRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-stepInto */
|
||||
stepInto?(req: Protocol.Debugger.StepIntoRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-stepOut */
|
||||
stepOut?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-stepOver */
|
||||
stepOver?(req: Protocol.Debugger.StepOverRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-getStackTrace */
|
||||
getStackTrace?(req: Protocol.Debugger.GetStackTraceRequest, context: DebuggerContext): Protocol.Debugger.GetStackTraceResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBlackboxedRanges */
|
||||
setBlackboxedRanges?(req: Protocol.Debugger.SetBlackboxedRangesRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBlackboxExecutionContexts */
|
||||
setBlackboxExecutionContexts?(req: Protocol.Debugger.SetBlackboxExecutionContextsRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBlackboxPatterns */
|
||||
setBlackboxPatterns?(req: Protocol.Debugger.SetBlackboxPatternsRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setBreakpointOnFunctionCall */
|
||||
setBreakpointOnFunctionCall?(req: Protocol.Debugger.SetBreakpointOnFunctionCallRequest, context: DebuggerContext): Protocol.Debugger.SetBreakpointOnFunctionCallResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#method-setReturnValue */
|
||||
setReturnValue?(req: Protocol.Debugger.SetReturnValueRequest, context: DebuggerContext): void;
|
||||
}
|
||||
|
||||
export interface ProfilerNamespace {
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-disable */
|
||||
disable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-enable */
|
||||
enable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-getBestEffortCoverage */
|
||||
getBestEffortCoverage?(req: void, context: DebuggerContext): Protocol.Profiler.GetBestEffortCoverageResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-setSamplingInterval */
|
||||
setSamplingInterval?(req: Protocol.Profiler.SetSamplingIntervalRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-start */
|
||||
start?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-startPreciseCoverage */
|
||||
startPreciseCoverage?(req: Protocol.Profiler.StartPreciseCoverageRequest, context: DebuggerContext): Protocol.Profiler.StartPreciseCoverageResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-stop */
|
||||
stop?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-stopPreciseCoverage */
|
||||
stopPreciseCoverage?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#method-takePreciseCoverage */
|
||||
takePreciseCoverage?(req: void, context: DebuggerContext): Protocol.Profiler.TakePreciseCoverageResponse;
|
||||
}
|
||||
|
||||
export interface RuntimeNamespace {
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-addBinding */
|
||||
addBinding?(req: Protocol.Runtime.AddBindingRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-awaitPromise */
|
||||
awaitPromise?(req: Protocol.Runtime.AwaitPromiseRequest, context: DebuggerContext): Protocol.Runtime.AwaitPromiseResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-callFunctionOn */
|
||||
callFunctionOn?(req: Protocol.Runtime.CallFunctionOnRequest, context: DebuggerContext): Protocol.Runtime.CallFunctionOnResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-compileScript */
|
||||
compileScript?(req: Protocol.Runtime.CompileScriptRequest, context: DebuggerContext): Protocol.Runtime.CompileScriptResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-disable */
|
||||
disable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-discardConsoleEntries */
|
||||
discardConsoleEntries?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-enable */
|
||||
enable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-evaluate */
|
||||
evaluate?(req: Protocol.Runtime.EvaluateRequest, context: DebuggerContext): Protocol.Runtime.EvaluateResponse | Promise<Protocol.Runtime.EvaluateResponse>;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-getProperties */
|
||||
getProperties?(req: Protocol.Runtime.GetPropertiesRequest, context: DebuggerContext): Protocol.Runtime.GetPropertiesResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-globalLexicalScopeNames */
|
||||
globalLexicalScopeNames?(req: Protocol.Runtime.GlobalLexicalScopeNamesRequest, context: DebuggerContext): Protocol.Runtime.GlobalLexicalScopeNamesResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-queryObjects */
|
||||
queryObjects?(req: Protocol.Runtime.QueryObjectsRequest, context: DebuggerContext): Protocol.Runtime.QueryObjectsResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-releaseObject */
|
||||
releaseObject?(req: Protocol.Runtime.ReleaseObjectRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-releaseObjectGroup */
|
||||
releaseObjectGroup?(req: Protocol.Runtime.ReleaseObjectGroupRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-removeBinding */
|
||||
removeBinding?(req: Protocol.Runtime.RemoveBindingRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-runIfWaitingForDebugger */
|
||||
runIfWaitingForDebugger?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-runScript */
|
||||
runScript?(req: Protocol.Runtime.RunScriptRequest, context: DebuggerContext): Protocol.Runtime.RunScriptResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-setAsyncCallStackDepth */
|
||||
setAsyncCallStackDepth?(req: Protocol.Runtime.SetAsyncCallStackDepthRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-getExceptionDetails */
|
||||
getExceptionDetails?(req: Protocol.Runtime.GetExceptionDetailsRequest, context: DebuggerContext): Protocol.Runtime.GetExceptionDetailsResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-getHeapUsage */
|
||||
getHeapUsage?(req: void, context: DebuggerContext): Protocol.Runtime.GetHeapUsageResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-getIsolateId */
|
||||
getIsolateId?(req: void, context: DebuggerContext): Protocol.Runtime.GetIsolateIdResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-setCustomObjectFormatterEnabled */
|
||||
setCustomObjectFormatterEnabled?(req: Protocol.Runtime.SetCustomObjectFormatterEnabledRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-setMaxCallStackSizeToCapture */
|
||||
setMaxCallStackSizeToCapture?(req: Protocol.Runtime.SetMaxCallStackSizeToCaptureRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#method-terminateExecution */
|
||||
terminateExecution?(req: void, context: DebuggerContext): void;
|
||||
}
|
||||
|
||||
export interface HeapProfilerNamespace {
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-addInspectedHeapObject */
|
||||
addInspectedHeapObject?(req: Protocol.HeapProfiler.AddInspectedHeapObjectRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-collectGarbage */
|
||||
collectGarbage?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-disable */
|
||||
disable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-enable */
|
||||
enable?(req: void, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-getHeapObjectId */
|
||||
getHeapObjectId?(req: Protocol.HeapProfiler.GetHeapObjectIdRequest, context: DebuggerContext): Protocol.HeapProfiler.GetHeapObjectIdResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-getObjectByHeapObjectId */
|
||||
getObjectByHeapObjectId?(req: Protocol.HeapProfiler.GetObjectByHeapObjectIdRequest, context: DebuggerContext): Protocol.HeapProfiler.GetObjectByHeapObjectIdResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-getSamplingProfile */
|
||||
getSamplingProfile?(req: void, context: DebuggerContext): Protocol.HeapProfiler.GetSamplingProfileResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-startSampling */
|
||||
startSampling?(req: Protocol.HeapProfiler.StartSamplingRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-startTrackingHeapObjects */
|
||||
startTrackingHeapObjects?(req: Protocol.HeapProfiler.StartTrackingHeapObjectsRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-stopSampling */
|
||||
stopSampling?(req: void, context: DebuggerContext): Protocol.HeapProfiler.StopSamplingResponse;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-stopTrackingHeapObjects */
|
||||
stopTrackingHeapObjects?(req: Protocol.HeapProfiler.StopTrackingHeapObjectsRequest, context: DebuggerContext): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#method-takeHeapSnapshot */
|
||||
takeHeapSnapshot?(req: Protocol.HeapProfiler.TakeHeapSnapshotRequest, context: DebuggerContext): void;
|
||||
}
|
||||
|
||||
// https://chromedevtools.github.io/devtools-protocol/1-3/Target/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface TargetNamespace {
|
||||
/** https://chromedevtools.github.io/devtools-protocol/1-3/Target/#method-setDiscoverTargets */
|
||||
setDiscoverTargets?(req: Protocol.Target.SetDiscoverTargetsRequest, context: DebuggerContext): void;
|
||||
}
|
||||
|
||||
export interface DevtoolEvents {
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-paused */
|
||||
'Debugger.paused'(event: Protocol.Debugger.PausedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-resumed */
|
||||
'Debugger.resumed'(event: void): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-scriptFailedToParse */
|
||||
'Debugger.scriptFailedToParse'(event: Protocol.Debugger.ScriptFailedToParseEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-scriptParsed */
|
||||
'Debugger.scriptParsed'(event: Protocol.Debugger.ScriptParsedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#event-addHeapSnapshotChunk */
|
||||
'HeapProfiler.addHeapSnapshotChunk'(event: Protocol.HeapProfiler.AddHeapSnapshotChunkEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#event-heapStatsUpdate */
|
||||
'HeapProfiler.heapStatsUpdate'(event: Protocol.HeapProfiler.HeapStatsUpdateEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#event-lastSeenObjectId */
|
||||
'HeapProfiler.lastSeenObjectId'(event: Protocol.HeapProfiler.LastSeenObjectIdEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#event-reportHeapSnapshotProgress */
|
||||
'HeapProfiler.reportHeapSnapshotProgress'(event: Protocol.HeapProfiler.ReportHeapSnapshotProgressEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/#event-resetProfiles */
|
||||
'HeapProfiler.resetProfiles'(event: void): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#event-consoleProfileFinished */
|
||||
'Profiler.consoleProfileFinished'(event: Protocol.Profiler.ConsoleProfileFinishedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#event-consoleProfileStarted */
|
||||
'Profiler.consoleProfileStarted'(event: Protocol.Profiler.ConsoleProfileStartedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Profiler/#event-preciseCoverageDeltaUpdate */
|
||||
'Profiler.preciseCoverageDeltaUpdate'(event: Protocol.Profiler.PreciseCoverageDeltaUpdateEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-consoleAPICalled */
|
||||
'Runtime.consoleAPICalled'(event: Protocol.Runtime.ConsoleAPICalledEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-exceptionRevoked */
|
||||
'Runtime.exceptionRevoked'(event: Protocol.Runtime.ExceptionRevokedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-exceptionThrown */
|
||||
'Runtime.exceptionThrown'(event: Protocol.Runtime.ExceptionThrownEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-executionContextCreated */
|
||||
'Runtime.executionContextCreated'(event: Protocol.Runtime.ExecutionContextCreatedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-executionContextDestroyed */
|
||||
'Runtime.executionContextDestroyed'(event: Protocol.Runtime.ExecutionContextDestroyedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-executionContextsCleared */
|
||||
'Runtime.executionContextsCleared'(event: void): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-inspectRequested */
|
||||
'Runtime.inspectRequested'(event: Protocol.Runtime.InspectRequestedEvent): void;
|
||||
/** https://chromedevtools.github.io/devtools-protocol/v8/Runtime/#event-bindingCalled */
|
||||
'Runtime.bindingCalled'(event: Protocol.Runtime.BindingCalledEvent): void;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import type Protocol from 'devtools-protocol';
|
||||
import type { Inspector } from './index.mts';
|
||||
import {
|
||||
CreateBuiltinFunction, CreateDataProperty, DefinePropertyOrThrow, Descriptor, OrdinaryObjectCreate, surroundingAgent, ThrowCompletion, skipDebugger, Value, type Arguments, type ManagedRealm,
|
||||
type PlainEvaluator,
|
||||
type PlainCompletion,
|
||||
} from '#self';
|
||||
|
||||
const consoleMethods = [
|
||||
'log',
|
||||
'debug',
|
||||
'info',
|
||||
'error',
|
||||
'warning',
|
||||
'dir',
|
||||
'dirxml',
|
||||
'table',
|
||||
'trace',
|
||||
'clear',
|
||||
'startGroup',
|
||||
'startGroupCollapsed',
|
||||
'endGroup',
|
||||
'assert',
|
||||
'profile',
|
||||
'profileEnd',
|
||||
'count',
|
||||
'timeEnd',
|
||||
] as const;
|
||||
type ConsoleMethod = typeof consoleMethods[number];
|
||||
export function createConsole(
|
||||
realm: ManagedRealm,
|
||||
defaultBehaviour: Partial<Record<ConsoleMethod, (args: Arguments) => void | PlainCompletion<void> | PlainEvaluator<void>>> & { default?: (method: ConsoleMethod, args: Arguments) => void | PlainCompletion<void> | PlainEvaluator<void> },
|
||||
) {
|
||||
realm.scope(() => {
|
||||
const console = OrdinaryObjectCreate(realm.Intrinsics['%Object.prototype%']);
|
||||
skipDebugger(DefinePropertyOrThrow(
|
||||
realm.GlobalObject,
|
||||
Value('console'),
|
||||
Descriptor({
|
||||
Configurable: Value.true,
|
||||
Enumerable: Value.false,
|
||||
Writable: Value.true,
|
||||
Value: console,
|
||||
}),
|
||||
));
|
||||
consoleMethods.forEach((method) => {
|
||||
const f = CreateBuiltinFunction(
|
||||
function* Console(args): PlainEvaluator<Value> {
|
||||
if (surroundingAgent.debugger_isPreviewing) {
|
||||
return Value.undefined;
|
||||
}
|
||||
|
||||
let completion;
|
||||
if (defaultBehaviour[method]) {
|
||||
completion = defaultBehaviour[method](args);
|
||||
} else if (defaultBehaviour.default) {
|
||||
completion = defaultBehaviour.default(method, args);
|
||||
}
|
||||
|
||||
if (completion) {
|
||||
if (typeof completion === 'object' && 'next' in completion) {
|
||||
completion = yield* completion;
|
||||
}
|
||||
// Do not use Q(host) here. A host may return something invalid like ReturnCompletion.
|
||||
if (completion instanceof ThrowCompletion) {
|
||||
return completion;
|
||||
}
|
||||
}
|
||||
if (realm.HostDefined.attachingInspector) {
|
||||
(realm.HostDefined.attachingInspector as Inspector).console(realm, method as Protocol.Protocol.Runtime.ConsoleAPICalledEventType, args);
|
||||
}
|
||||
return Value.undefined;
|
||||
},
|
||||
1,
|
||||
Value(method),
|
||||
[],
|
||||
);
|
||||
skipDebugger(CreateDataProperty(console, Value(method), f));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { start } from 'node:repl';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { format as _format, inspect as _inspect, parseArgs } from 'node:util';
|
||||
// let's try if the following message on old node causes test failure on test262.fyi
|
||||
// "ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time"
|
||||
// import packageJson from '../../package.json' with { type: 'json' };
|
||||
import { createRequire } from 'node:module';
|
||||
import { createConsole } from '../inspector/utils.mts';
|
||||
import type { NodeWebsocketInspector } from './inspector.mts';
|
||||
import { loadImportedModule } from './module.mts';
|
||||
import {
|
||||
setSurroundingAgent, FEATURES, inspect, Value, Completion, AbruptCompletion,
|
||||
type Arguments,
|
||||
evalQ,
|
||||
Agent,
|
||||
ManagedRealm,
|
||||
skipDebugger,
|
||||
type ValueCompletion,
|
||||
createTest262Intrinsics,
|
||||
surroundingAgent,
|
||||
ThrowCompletion,
|
||||
ValueOfNormalCompletion,
|
||||
ScriptEvaluation,
|
||||
type PlainEvaluator,
|
||||
} from '#self';
|
||||
|
||||
const packageJson = createRequire(import.meta.url)('../../package.json');
|
||||
const help = `
|
||||
engine262 v${packageJson.version}
|
||||
|
||||
Usage:
|
||||
|
||||
engine262 [options]
|
||||
engine262 [options] [input file]
|
||||
engine262 [input file]
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Show help (this screen)
|
||||
-m, --module Evaluate contents of input-file as a module.
|
||||
-e, --eval Evaluate the given string.
|
||||
--features=... A comma separated list of features.
|
||||
--features=all Enable all features.
|
||||
--list-features List available features.
|
||||
--no-test262 Do not expose $ and $262 for test262.
|
||||
--no-inspector Do not attach an inspector.
|
||||
--no-preview Do not enable preview in the inspector.
|
||||
`;
|
||||
|
||||
const argv = parseArgs({
|
||||
args: process.argv.slice(2),
|
||||
allowPositionals: true,
|
||||
allowNegative: true,
|
||||
strict: true,
|
||||
options: {
|
||||
'help': { type: 'boolean', short: 'h' },
|
||||
'eval': { type: 'string', short: 'e' },
|
||||
'module': { type: 'boolean', short: 'm' },
|
||||
'features': { type: 'string' },
|
||||
'list-features': { type: 'boolean' },
|
||||
'inspector': { type: 'boolean' },
|
||||
'test262': { type: 'boolean', default: true },
|
||||
// hidden options
|
||||
'preview-debug': { type: 'boolean' },
|
||||
},
|
||||
});
|
||||
|
||||
if (argv.values.help) {
|
||||
process.stdout.write(help);
|
||||
process.exit(0);
|
||||
} else if (argv.values['list-features']) {
|
||||
let nameLength = 0;
|
||||
let flagLength = 0;
|
||||
FEATURES.forEach((f) => {
|
||||
if (f.name.length > nameLength) {
|
||||
nameLength = f.name.length;
|
||||
}
|
||||
if (f.flag.length > flagLength) {
|
||||
flagLength = f.flag.length;
|
||||
}
|
||||
});
|
||||
const log = (f: string, n: string, u: string) => {
|
||||
process.stdout.write(`${f.padEnd(flagLength, ' ')} ${n.padEnd(nameLength, ' ')} ${u}\n`);
|
||||
};
|
||||
log('flag', 'name', 'url');
|
||||
log('----', '----', '---');
|
||||
FEATURES.forEach((f) => {
|
||||
log(f.flag, f.name, f.url);
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let features: string[];
|
||||
if (argv.values.features === 'all') {
|
||||
features = FEATURES.map((f) => f.flag);
|
||||
} else if (argv.values.features) {
|
||||
features = argv.values.features.split(',');
|
||||
} else {
|
||||
features = [];
|
||||
}
|
||||
|
||||
const agent = new Agent({
|
||||
features,
|
||||
supportedImportAttributes: ['type'],
|
||||
loadImportedModule,
|
||||
});
|
||||
setSurroundingAgent(agent);
|
||||
|
||||
const realm = new ManagedRealm({ resolverCache: new Map(), name: 'repl', specifier: process.cwd() });
|
||||
// Define console.log
|
||||
{
|
||||
const format = (function* format(args: Arguments): PlainEvaluator<string> {
|
||||
const str = [];
|
||||
for (const arg of args.values()) {
|
||||
// TODO: inspect should return a PlainEvaluator so debugger can hook in.
|
||||
str.push(inspect(arg));
|
||||
}
|
||||
return str.join(' ');
|
||||
});
|
||||
createConsole(realm, {
|
||||
* log(args) {
|
||||
process.stdout.write(`${yield* format(args)}\n`);
|
||||
},
|
||||
* error(args) {
|
||||
process.stderr.write(`${yield* format(args)}\n`);
|
||||
},
|
||||
* debug(args) {
|
||||
process.stderr.write(`${yield* format(args)}\n`);
|
||||
},
|
||||
});
|
||||
}
|
||||
if (argv.values.test262) {
|
||||
createTest262Intrinsics(realm, argv.values.test262);
|
||||
}
|
||||
|
||||
let inspector: NodeWebsocketInspector | undefined;
|
||||
if (argv.values.inspector !== false) {
|
||||
let has_ws = false;
|
||||
try {
|
||||
await import('ws');
|
||||
has_ws = true;
|
||||
} catch {
|
||||
if (argv.values.inspector === true) {
|
||||
process.stderr.write('--inspector requires the "ws" package to be installed.\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (has_ws) {
|
||||
const { NodeWebsocketInspector } = await import('./inspector.mts');
|
||||
inspector = await NodeWebsocketInspector.new();
|
||||
inspector.attachAgent(surroundingAgent, [realm]);
|
||||
inspector.preference.previewDebug = argv.values['preview-debug'] || false;
|
||||
}
|
||||
}
|
||||
|
||||
function oneShotEval(source: string, filename: string) {
|
||||
realm.scope(() => {
|
||||
const completion = evalQ((Q) => {
|
||||
if (argv.values.module || filename.endsWith('.mjs')) {
|
||||
const module = Q(realm.compileModule(source, { specifier: filename }));
|
||||
realm.HostDefined.resolverCache?.set(filename, module);
|
||||
const load = Q(module.LoadRequestedModules());
|
||||
if (load.PromiseState === 'rejected') {
|
||||
Q(ThrowCompletion(load.PromiseResult!));
|
||||
} else if (load.PromiseState === 'pending') {
|
||||
throw new Error('Internal error: .LoadRequestedModules() returned a pending promise');
|
||||
}
|
||||
Q(module.Link());
|
||||
const evaluate = Q(skipDebugger(module.Evaluate()));
|
||||
if (evaluate.PromiseState === 'rejected') {
|
||||
Q(ThrowCompletion(evaluate.PromiseResult!));
|
||||
}
|
||||
} else {
|
||||
Q(realm.evaluateScript(source, { specifier: filename }));
|
||||
}
|
||||
});
|
||||
if (completion instanceof AbruptCompletion) {
|
||||
const inspected = inspect(completion);
|
||||
process.stderr.write(`${inspected}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
inspector?.stop();
|
||||
}
|
||||
|
||||
if (argv.positionals[0]) {
|
||||
const source = readFileSync(argv.positionals[0], 'utf8');
|
||||
oneShotEval(source, resolve(argv.positionals[0]));
|
||||
} else if (!process.stdin.isTTY) {
|
||||
process.stdin.setEncoding('utf8');
|
||||
let source = '';
|
||||
process.stdin.on('data', (data) => {
|
||||
source += data;
|
||||
});
|
||||
process.stdin.once('end', () => {
|
||||
oneShotEval(source, process.cwd());
|
||||
});
|
||||
} else if (argv.values.eval) {
|
||||
oneShotEval(argv.values.eval, process.cwd());
|
||||
} else {
|
||||
process.stdout.write(`${packageJson.name} v${String(packageJson.version).replace('0.0.1-', '')}
|
||||
Type ".help" for more information. Please report bugs to ${packageJson.bugs.url}
|
||||
`);
|
||||
const server = start({
|
||||
prompt: '> ',
|
||||
eval: (cmd, _context, _filename, callback) => {
|
||||
try {
|
||||
const script = realm.compileScript(cmd, {});
|
||||
if (script instanceof ThrowCompletion) {
|
||||
callback(null, script);
|
||||
return;
|
||||
}
|
||||
let c;
|
||||
surroundingAgent.evaluate(ScriptEvaluation(ValueOfNormalCompletion(script)), (completion) => {
|
||||
c = completion;
|
||||
callback(null, completion);
|
||||
});
|
||||
if (!c) {
|
||||
surroundingAgent.resumeEvaluate();
|
||||
}
|
||||
} catch (e) {
|
||||
callback(e as Error, null);
|
||||
}
|
||||
},
|
||||
preview: false,
|
||||
writer: (o) => realm.scope(() => {
|
||||
if (o instanceof Value || o instanceof Completion) {
|
||||
return inspect(o as Value | ValueCompletion);
|
||||
}
|
||||
return _inspect(o);
|
||||
}),
|
||||
});
|
||||
|
||||
server.on('exit', () => inspector?.stop());
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* eslint-disable no-console */
|
||||
import {
|
||||
Agent, inspect, ManagedRealm, NormalCompletion, setSurroundingAgent, ThrowCompletion, type Arguments, type PlainEvaluator,
|
||||
} from '#self';
|
||||
import { createConsole } from '#self/inspector';
|
||||
|
||||
// Agent is the running environment.
|
||||
const agent = new Agent({
|
||||
});
|
||||
// Only one agent can be active at a time.
|
||||
setSurroundingAgent(agent);
|
||||
|
||||
// A Realm is a separate global environment.
|
||||
// In Web browsers, each iframe has its own Realm and they may interact with each other.
|
||||
const realm = new ManagedRealm({ resolverCache: new Map(), name: 'My Realm', specifier: process.cwd() });
|
||||
|
||||
// Define console.log
|
||||
{
|
||||
const format = (function* format(args: Arguments): PlainEvaluator<string> {
|
||||
const str = [];
|
||||
for (const arg of args.values()) {
|
||||
str.push(inspect(arg));
|
||||
}
|
||||
return str.join(' ');
|
||||
});
|
||||
createConsole(realm, {
|
||||
* log(args) {
|
||||
process.stdout.write(`${yield* format(args)}\n`);
|
||||
},
|
||||
* error(args) {
|
||||
process.stderr.write(`${yield* format(args)}\n`);
|
||||
},
|
||||
* debug(args) {
|
||||
process.stderr.write(`${yield* format(args)}\n`);
|
||||
},
|
||||
* default(method, args) {
|
||||
process.stdout.write(`[console.${method}] ${yield* format(args)}\n`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Do not forget to use realm.scope when running code.
|
||||
realm.scope(() => {
|
||||
// Run ECMAScript code in the Realm.
|
||||
realm.evaluateScript(`
|
||||
console.log('Hello from engine262!');
|
||||
console.log('2 + 2 =', 2 + 2);
|
||||
`, { specifier: 'example.mts' });
|
||||
|
||||
const result = realm.evaluateScript(`
|
||||
throw new Error('This is an example error');
|
||||
`, { specifier: 'example.mts' });
|
||||
if (result instanceof NormalCompletion) {
|
||||
console.log('No Error');
|
||||
} else if (result instanceof ThrowCompletion) {
|
||||
console.error('Caught error from evaluated script:', inspect(result.Value));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,124 @@
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
import { WebSocketServer } from 'ws';
|
||||
import packageJson from '../../package.json' with { type: 'json' };
|
||||
// Note: typescript will not copy json files, so it will not appear in the lib directory
|
||||
// eslint-disable-next-line import/no-useless-path-segments
|
||||
import protocol from '../../lib-src/inspector/js_protocol.json' with { type: 'json' };
|
||||
import { Inspector } from '../inspector/index.mts';
|
||||
|
||||
const ANSI = {
|
||||
reset: '\u001b[0m',
|
||||
red: '\u001b[31m',
|
||||
green: '\u001b[32m',
|
||||
yellow: '\u001b[33m',
|
||||
blue: '\u001b[34m',
|
||||
};
|
||||
|
||||
export class NodeWebsocketInspector extends Inspector {
|
||||
_server: http.Server | https.Server;
|
||||
|
||||
_ws: WebSocketServer;
|
||||
|
||||
isDebug = false;
|
||||
|
||||
protected override send(data: object): void {
|
||||
const s = JSON.stringify(data);
|
||||
this._ws.clients.forEach((ws) => {
|
||||
ws.send(s);
|
||||
});
|
||||
}
|
||||
|
||||
protected constructor(server: http.Server | https.Server, isDebug: boolean) {
|
||||
super();
|
||||
this._server = server;
|
||||
const ws = new WebSocketServer({ server });
|
||||
this._ws = ws;
|
||||
ws.on('connection', (ws) => {
|
||||
const send = (obj: unknown) => {
|
||||
const s = JSON.stringify(obj);
|
||||
ws.send(s);
|
||||
};
|
||||
|
||||
const sendEvent = Object.create(new Proxy({}, {
|
||||
get: (_, key: string) => {
|
||||
const f = (params: Record<string, unknown>) => {
|
||||
send({ method: key, params });
|
||||
};
|
||||
Object.defineProperty(sendEvent, key, { value: key });
|
||||
return f;
|
||||
},
|
||||
}));
|
||||
|
||||
ws.on('message', (data: string) => {
|
||||
const { id, method, params } = JSON.parse(data);
|
||||
if (isDebug) {
|
||||
process.stdout.write(`${ANSI.green}${method}${ANSI.reset}: ${JSON.stringify(params)}\n`);
|
||||
}
|
||||
this.onMessage(id, method, params);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static inspectorHTTPServer(req: http.IncomingMessage, res: http.ServerResponse<http.IncomingMessage>) {
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const json = (obj: unknown) => {
|
||||
const s = JSON.stringify(obj);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(s),
|
||||
});
|
||||
res.end(s);
|
||||
};
|
||||
|
||||
switch (req.url) {
|
||||
case '/json':
|
||||
case '/json/list':
|
||||
json([{
|
||||
description: `${packageJson.name} instance`,
|
||||
devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=localhost:9229/',
|
||||
devtoolsFrontendUrlCompat: 'chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=localhost:9229/',
|
||||
faviconUrl: 'https://avatars0.githubusercontent.com/u/51185628',
|
||||
id: 'inspector.0',
|
||||
title: 'engine262',
|
||||
type: 'node',
|
||||
url: `file://${process.cwd()}`,
|
||||
webSocketDebuggerUrl: 'ws://localhost:9229/',
|
||||
}]);
|
||||
break;
|
||||
case '/json/version':
|
||||
json({
|
||||
'Browser': `${packageJson.name}/v${packageJson.version}`,
|
||||
'Protocol-Version': `${protocol.version.major}.${protocol.version.minor}`,
|
||||
});
|
||||
break;
|
||||
case '/json/protocol':
|
||||
json(protocol);
|
||||
break;
|
||||
default:
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static new(port = 9229, host = '127.0.0.1', isDebug = !!process.env.DEBUG) {
|
||||
const server = http.createServer(NodeWebsocketInspector.inspectorHTTPServer);
|
||||
const inspector = new NodeWebsocketInspector(server, isDebug);
|
||||
return new Promise<NodeWebsocketInspector>((resolve) => {
|
||||
server.listen(port, host, () => {
|
||||
resolve(inspector);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this._server.close();
|
||||
this._ws.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { readFile, readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
evalQ, ManagedRealm, Realm, Throw, ThrowCompletion, type AgentHostDefined,
|
||||
} from '#self';
|
||||
|
||||
export function createLoadImportedModule(getCache = (realm: ManagedRealm) => realm.HostDefined.resolverCache) {
|
||||
const validateType = (attributes: Map<string, string>, finish: (completion: ThrowCompletion) => void) => {
|
||||
const type = attributes.get('type');
|
||||
if (type && type !== 'json') {
|
||||
finish(Throw('TypeError', 'UnsupportedModuleType', type));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const parseModule = (realm: ManagedRealm, resolved: string, attributes: Map<string, string>, source: string) => (attributes.get('type') === 'json' || resolved.endsWith('.json')
|
||||
? realm.createJSONModule(resolved, source)
|
||||
: realm.compileModule(source, { specifier: resolved }));
|
||||
|
||||
const loadImportedModuleSyncOrAsync = (
|
||||
readFile: (path: string, callback: (err: NodeJS.ErrnoException | null, data: string) => void) => void,
|
||||
...[referrer, specifier, attributes, _hostDefined, finish]: Parameters<NonNullable<AgentHostDefined['loadImportedModule']>>
|
||||
) => {
|
||||
const realm = (referrer instanceof Realm ? referrer : referrer.Realm) as ManagedRealm;
|
||||
const cache = getCache(realm);
|
||||
|
||||
if (!referrer.HostDefined.specifier) {
|
||||
finish(Throw('SyntaxError', 'CouldNotResolveModule', specifier));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateType(attributes, finish)) {
|
||||
return;
|
||||
}
|
||||
|
||||
evalQ(async (Q) => {
|
||||
const base = path.dirname(referrer.HostDefined.specifier!);
|
||||
const resolved = path.resolve(base, specifier);
|
||||
if (cache?.has(resolved)) {
|
||||
finish(cache.get(resolved)!);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
readFile(resolved, (err, data) => {
|
||||
if (err) {
|
||||
finish(Throw('SyntaxError', 'CouldNotResolveModule', specifier));
|
||||
return;
|
||||
}
|
||||
const m = Q(parseModule(realm, resolved, attributes, data));
|
||||
cache?.set(resolved, m);
|
||||
finish(m);
|
||||
});
|
||||
} catch (error) {
|
||||
finish(Throw('SyntaxError', 'CouldNotResolveModule', specifier));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadImportedModule: NonNullable<AgentHostDefined['loadImportedModule']> = loadImportedModuleSyncOrAsync.bind(null, (path, callback) => {
|
||||
readFile(path, 'utf8', callback);
|
||||
});
|
||||
const loadImportedModuleSync: NonNullable<AgentHostDefined['loadImportedModule']> = loadImportedModuleSyncOrAsync.bind(null, (path, callback) => {
|
||||
try {
|
||||
const data = readFileSync(path, 'utf8');
|
||||
callback(null, data);
|
||||
} catch (error) {
|
||||
callback(error as NodeJS.ErrnoException, '');
|
||||
}
|
||||
});
|
||||
return { loadImportedModule, loadImportedModuleSync };
|
||||
}
|
||||
|
||||
export const { loadImportedModule, loadImportedModuleSync } = createLoadImportedModule();
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"references": [{ "path": "../inspector" }, { "path": "../../src" }],
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"declarationDir": "../../lib/node",
|
||||
"tsBuildInfoFile": "../../lib/node/.tsbuildinfo",
|
||||
"erasableSyntaxOnly": true,
|
||||
"rewriteRelativeImportExtensions": true,
|
||||
"rootDir": "./",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "../../lib/node/"
|
||||
},
|
||||
"include": [
|
||||
"./example.mts",
|
||||
"./bin.mts",
|
||||
"./inspector.mts",
|
||||
"./module.mts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user