mirror of
https://github.com/bendtherules/ask262.git
synced 2026-08-18 21:31:46 +00:00
625 lines
24 KiB
TypeScript
625 lines
24 KiB
TypeScript
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;
|
|
}
|
|
}
|