mirror of
https://github.com/bendtherules/completion-viz.git
synced 2026-08-19 00:31:34 +00:00
Clone engine262 in /engine262
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
'use strict';
|
||||
|
||||
const engine262 = require('..');
|
||||
|
||||
const contexts = [];
|
||||
|
||||
class InspectorContext {
|
||||
constructor(realm) {
|
||||
this.realm = realm;
|
||||
this.idToObject = new Map();
|
||||
this.objectToId = new Map();
|
||||
this.objectCounter = 0;
|
||||
this.previewStack = [];
|
||||
}
|
||||
|
||||
internObject(object, 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;
|
||||
}
|
||||
|
||||
releaseObjectGroup(group) {
|
||||
for (const [id, object] of this.idToObject.entries()) {
|
||||
if (id.startsWith(group)) {
|
||||
this.idToObject.delete(id);
|
||||
this.objectToId.delete(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getObject(objectId) {
|
||||
return this.idToObject.get(objectId);
|
||||
}
|
||||
|
||||
toRemoteObject(object, options) {
|
||||
const result = {};
|
||||
switch (engine262.Type(object)) {
|
||||
case 'Object':
|
||||
result.objectId = this.internObject(object, options.objectGroup);
|
||||
if ('Call' in object) {
|
||||
result.type = 'function';
|
||||
} else {
|
||||
result.type = 'object';
|
||||
if ('PromiseState' in object) {
|
||||
result.subtype = 'promise';
|
||||
} else if ('MapData' in object) {
|
||||
result.subtype = 'map';
|
||||
} else if ('SetData' in object) {
|
||||
result.subtype = 'set';
|
||||
} else if ('ErrorData' in object) {
|
||||
result.subtype = 'error';
|
||||
} else if ('TypedArrayName' in object) {
|
||||
result.subtype = 'typedarray';
|
||||
} else if ('DataView' in object) {
|
||||
result.subtype = 'dataview';
|
||||
} else if ('ProxyTarget' in object) {
|
||||
result.subtype = 'proxy';
|
||||
} else if ('DateValue' in object) {
|
||||
result.subtype = 'date';
|
||||
} else if ('GeneratorState' in object) {
|
||||
result.subtype = 'generator';
|
||||
} else if (engine262.IsArray(object) === engine262.Value.true) {
|
||||
result.subtype = 'array';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Null':
|
||||
result.type = 'object';
|
||||
result.subtype = 'null';
|
||||
result.value = null;
|
||||
break;
|
||||
case 'Undefined':
|
||||
result.type = 'undefined';
|
||||
break;
|
||||
case 'String':
|
||||
result.type = 'string';
|
||||
result.value = object.stringValue();
|
||||
break;
|
||||
case 'Number': {
|
||||
result.type = 'number';
|
||||
const v = object.numberValue();
|
||||
if (!Number.isFinite(v)) {
|
||||
result.unserializableValue = v.toString();
|
||||
} else {
|
||||
result.value = v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Boolean':
|
||||
result.type = 'boolean';
|
||||
result.value = object.booleanValue();
|
||||
break;
|
||||
case 'BigInt':
|
||||
result.type = 'bigint';
|
||||
result.unserializableValue = `${object.bigintValue().toString()}n`;
|
||||
break;
|
||||
case 'Symbol':
|
||||
result.type = 'symbol';
|
||||
result.description = object.Description === engine262.Value.undefined
|
||||
? undefined
|
||||
: object.Description.stringValue();
|
||||
break;
|
||||
default:
|
||||
throw new RangeError();
|
||||
}
|
||||
if (options.generatePreview
|
||||
&& result.type === 'object'
|
||||
&& result.subtype !== 'null'
|
||||
&& !this.previewStack.includes(object)) {
|
||||
this.previewStack.push(object);
|
||||
const properties = this.getPropertyPreview(object, options);
|
||||
let entries;
|
||||
if ('MapData' in object) {
|
||||
entries = object.MapData.map((d) => ({
|
||||
key: this.toRemoteObject(d.Key, options).preview,
|
||||
value: this.toRemoteObject(d.Value, options).preview,
|
||||
}));
|
||||
}
|
||||
this.previewStack.pop();
|
||||
result.preview = {
|
||||
type: result.type,
|
||||
subtype: result.subtype,
|
||||
overflow: properties.length > 5,
|
||||
properties: properties.slice(0, 5),
|
||||
entries,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getProperties(object, options) {
|
||||
const wrap = (v) => this.toRemoteObject(v, options);
|
||||
|
||||
const properties = [];
|
||||
const internalProperties = [];
|
||||
|
||||
let p = object;
|
||||
while (p !== engine262.Value.null) {
|
||||
const keys = p.OwnPropertyKeys();
|
||||
if (keys instanceof engine262.AbruptCompletion) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
const desc = p.GetOwnProperty(key);
|
||||
if (desc instanceof engine262.AbruptCompletion) {
|
||||
return desc;
|
||||
}
|
||||
if (options.accessorPropertiesOnly && desc.Value) {
|
||||
continue;
|
||||
}
|
||||
const descriptor = {
|
||||
name: key.stringValue
|
||||
? key.stringValue()
|
||||
: undefined,
|
||||
value: desc.Value ? wrap(desc.Value) : undefined,
|
||||
writable: desc.Writable === engine262.Value.true,
|
||||
get: desc.Get ? wrap(desc.Get) : undefined,
|
||||
set: desc.Set ? wrap(desc.Set) : undefined,
|
||||
configurable: desc.Configurable === engine262.Value.true,
|
||||
enumerable: desc.Enumerable === engine262.Value.true,
|
||||
wasThrown: false,
|
||||
isOwn: p === object,
|
||||
symbol: key.stringValue ? undefined : wrap(key),
|
||||
};
|
||||
properties.push(descriptor);
|
||||
}
|
||||
|
||||
if (options.ownProperties) {
|
||||
break;
|
||||
}
|
||||
p = p.GetPrototypeOf();
|
||||
if (p instanceof engine262.AbruptCompletion) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
if ('PromiseState' in object) {
|
||||
internalProperties.push({
|
||||
name: '[[PromiseState]]',
|
||||
value: {
|
||||
type: 'string',
|
||||
value: object.PromiseState,
|
||||
},
|
||||
});
|
||||
internalProperties.push({
|
||||
name: '[[PromiseResult]]',
|
||||
value: wrap(object.PromiseResult),
|
||||
});
|
||||
}
|
||||
|
||||
return { properties, internalProperties };
|
||||
}
|
||||
|
||||
getPropertyPreview(object, options) {
|
||||
const wrap = (v) => this.toRemoteObject(v, options);
|
||||
|
||||
const keys = object.OwnPropertyKeys();
|
||||
if (keys instanceof engine262.AbruptCompletion) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
const properties = [];
|
||||
for (const key of keys) {
|
||||
const desc = object.GetOwnProperty(key);
|
||||
if (desc instanceof engine262.AbruptCompletion) {
|
||||
return desc;
|
||||
}
|
||||
const descriptor = {
|
||||
name: key.stringValue
|
||||
? key.stringValue()
|
||||
: `Symbol(${key.Description.stringValue ? key.Description.stringValue() : ''})`,
|
||||
};
|
||||
if (desc.Value) {
|
||||
desc.valuePreview = wrap(desc.Value).preview;
|
||||
switch (engine262.Type(desc.Value)) {
|
||||
case 'Object':
|
||||
if ('Call' in desc.Value) {
|
||||
descriptor.type = 'function';
|
||||
} else {
|
||||
descriptor.type = 'object';
|
||||
if ('PromiseState' in desc.Value) {
|
||||
descriptor.subtype = 'promise';
|
||||
} else if ('MapData' in desc.Value) {
|
||||
descriptor.subtype = 'map';
|
||||
} else if ('SetData' in desc.Value) {
|
||||
descriptor.subtype = 'set';
|
||||
} else if ('ErrorData' in desc.Value) {
|
||||
descriptor.subtype = 'error';
|
||||
} else if ('TypedArrayName' in desc.Value) {
|
||||
descriptor.subtype = 'typedarray';
|
||||
} else if ('DataView' in desc.Value) {
|
||||
descriptor.subtype = 'dataview';
|
||||
} else if ('ProxyTarget' in desc.Value) {
|
||||
descriptor.subtype = 'proxy';
|
||||
} else if ('DateValue' in desc.Value) {
|
||||
descriptor.subtype = 'date';
|
||||
} else if ('GeneratorState' in desc.Value) {
|
||||
descriptor.subtype = 'generator';
|
||||
} else if (engine262.IsArray(desc.Value) === engine262.Value.true) {
|
||||
descriptor.subtype = 'array';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Null':
|
||||
descriptor.type = 'object';
|
||||
descriptor.subtype = 'null';
|
||||
descriptor.value = 'null';
|
||||
break;
|
||||
case 'Undefined':
|
||||
descriptor.type = 'undefined';
|
||||
descriptor.value = 'undefined';
|
||||
break;
|
||||
case 'String':
|
||||
descriptor.type = 'string';
|
||||
descriptor.value = desc.Value.stringValue();
|
||||
break;
|
||||
case 'Number': {
|
||||
descriptor.type = 'number';
|
||||
descriptor.value = desc.Value.numberValue().toString();
|
||||
break;
|
||||
}
|
||||
case 'Boolean':
|
||||
descriptor.type = 'boolean';
|
||||
descriptor.value = desc.Value.booleanValue().toString();
|
||||
break;
|
||||
case 'BigInt':
|
||||
descriptor.type = 'bigint';
|
||||
descriptor.value = `${desc.Value.bigintValue().toString()}n`;
|
||||
break;
|
||||
case 'Symbol': {
|
||||
descriptor.type = 'symbol';
|
||||
const description = desc.Value.Description === engine262.Value.undefined
|
||||
? ''
|
||||
: desc.Value.Description.stringValue();
|
||||
descriptor.value = `Symbol(${description})`;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new RangeError();
|
||||
}
|
||||
} else {
|
||||
desc.type = 'accessor';
|
||||
}
|
||||
properties.push(descriptor);
|
||||
}
|
||||
|
||||
if ('PromiseState' in object) {
|
||||
properties.push({
|
||||
name: '[[PromiseState]]',
|
||||
type: 'string',
|
||||
value: object.PromiseState,
|
||||
});
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
createEvaluationResult(completion, options) {
|
||||
if (completion instanceof engine262.AbruptCompletion) {
|
||||
return {
|
||||
exceptionDetails: {
|
||||
text: 'uh oh',
|
||||
lineNumber: 0,
|
||||
columnNumber: 0,
|
||||
exception: this.toRemoteObject(completion.Value, options),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
result: this.toRemoteObject(completion.Value, options),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function attachRealm(realm) {
|
||||
contexts.push(new InspectorContext(realm));
|
||||
}
|
||||
|
||||
function getContext(id) {
|
||||
return contexts[id] || contexts[0];
|
||||
}
|
||||
|
||||
module.exports = { attachRealm, getContext };
|
||||
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
require('./server');
|
||||
const { attachRealm } = require('./context');
|
||||
|
||||
module.exports = { attachRealm };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
'use strict';
|
||||
|
||||
const engine262 = require('..');
|
||||
const { getContext } = require('./context');
|
||||
|
||||
module.exports = {
|
||||
Debugger: {
|
||||
enable() {
|
||||
return { debuggerId: 'debugger.0' };
|
||||
},
|
||||
setAsyncCallStackDepth() {},
|
||||
setBlackboxPatterns() {},
|
||||
setPauseOnExceptions() {},
|
||||
},
|
||||
Profiler: {
|
||||
enable() {},
|
||||
},
|
||||
Runtime: {
|
||||
enable() {},
|
||||
compileScript() {
|
||||
return { scriptId: 'script.0' };
|
||||
},
|
||||
callFunctionOn(options) {
|
||||
const context = getContext(options.executionContextId);
|
||||
const { Value: F } = context.realm.evaluateScript(`(${options.functionDeclaration})`);
|
||||
const thisValue = options.objectId
|
||||
? context.getObject(options.objectId)
|
||||
: engine262.Value.undefined;
|
||||
const args = options.arguments.map((a) => {
|
||||
if ('value' in a) {
|
||||
return new engine262.Value(context.realm, a.value);
|
||||
}
|
||||
if ('objectId' in a) {
|
||||
return context.getObject(a.objectId);
|
||||
}
|
||||
if ('unserializableValue' in a) {
|
||||
throw new RangeError();
|
||||
}
|
||||
return engine262.Value.undefined;
|
||||
});
|
||||
const r = engine262.Call(F, thisValue, args);
|
||||
return context.createEvaluationResult(r, options);
|
||||
},
|
||||
evaluate(options) {
|
||||
if (options.throwOnSideEffect || options.awaitPromise) {
|
||||
return {
|
||||
exceptionDetails: {
|
||||
text: 'unsupported',
|
||||
lineNumber: 0,
|
||||
columnNumber: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const context = getContext(options.contextId);
|
||||
const r = context.realm.evaluateScript(options.expression);
|
||||
return context.createEvaluationResult(r, options);
|
||||
},
|
||||
getHeapUsage() {
|
||||
return { usedSize: 0, totalSize: 0 };
|
||||
},
|
||||
getIsolateId() {
|
||||
return { id: 'isolate.0' };
|
||||
},
|
||||
getProperties(options) {
|
||||
const context = getContext();
|
||||
const object = context.getObject(options.objectId);
|
||||
|
||||
const properties = context.getProperties(object, options);
|
||||
if (properties instanceof engine262.AbruptCompletion) {
|
||||
return context.createEvaluationResult(properties, options);
|
||||
}
|
||||
|
||||
return {
|
||||
result: properties.properties,
|
||||
internalProperties: properties.internalProperties,
|
||||
};
|
||||
},
|
||||
globalLexicalScopeNames({ executionContextId }) {
|
||||
const context = getContext(executionContextId);
|
||||
const envRec = context.realm.realm.GlobalEnv.EnvironmentRecord;
|
||||
const names = Map.prototype.keys.call(envRec.DeclarativeRecord.bindings);
|
||||
return {
|
||||
names: [...names],
|
||||
};
|
||||
},
|
||||
releaseObjectGroup({ objectGroup }) {
|
||||
getContext().releaseObjectGroup(objectGroup);
|
||||
},
|
||||
runIfWaitingForDebugger(params, ctx) {
|
||||
ctx.sendEvent('Runtime.executionContextCreated', {
|
||||
context: {
|
||||
id: '0',
|
||||
origin: 'file://',
|
||||
name: 'context.0',
|
||||
auxData: {},
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
HeapProfiler: {
|
||||
enable() {},
|
||||
collectGarbage() {},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const http = require('http');
|
||||
const WebSocket = require('ws'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const packageJson = require('../package.json');
|
||||
const protocol = require('./js_protocol.json');
|
||||
const methods = require('./methods');
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const json = (obj) => {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
const wss = new WebSocket.Server({ server });
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
const send = (obj) => {
|
||||
const s = JSON.stringify(obj);
|
||||
// console.log('<-', s);
|
||||
ws.send(s);
|
||||
};
|
||||
|
||||
ws._socket.unref();
|
||||
|
||||
const context = {
|
||||
sendEvent(event, params) {
|
||||
send({ method: event, params });
|
||||
},
|
||||
};
|
||||
|
||||
ws.on('message', (data) => {
|
||||
// console.log('->', data);
|
||||
const { id, method, params } = JSON.parse(data);
|
||||
const [k, v] = method.split('.');
|
||||
Promise.resolve(methods[k][v](params, context))
|
||||
.then((result = {}) => {
|
||||
send({ id, result });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(9229, '127.0.0.1', () => {
|
||||
console.log('Debugger listening at localhost:9229'); // eslint-disable-line no-console
|
||||
});
|
||||
server.unref();
|
||||
Reference in New Issue
Block a user