Clone engine262 in /engine262

This commit is contained in:
2020-09-07 10:19:00 +05:30
parent 7688de2e5c
commit 66c1aeb9e0
313 changed files with 46014 additions and 0 deletions
+330
View File
@@ -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 };
+6
View File
@@ -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
+105
View File
@@ -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() {},
},
};
+87
View File
@@ -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();