blob: 8e9a892a6c759b1f84a3a81c8199eca275de121a [file] [log] [blame]
/*
* Copyright (C) 2014-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
WI.ScriptSyntaxTree = class ScriptSyntaxTree
{
constructor(sourceText, script)
{
console.assert(script && script instanceof WI.Script, script);
this._script = script;
try {
let sourceType = this._script.sourceType === WI.Script.SourceType.Module ? "module" : "script";
let esprimaSyntaxTree = esprima.parse(sourceText, {loc: true, range: true, sourceType});
this._syntaxTree = this._createInternalSyntaxTree(esprimaSyntaxTree);
this._parsedSuccessfully = true;
} catch (error) {
this._parsedSuccessfully = false;
this._syntaxTree = null;
console.error("Couldn't parse JavaScript File: " + script.url, error);
}
}
// Public
get parsedSuccessfully()
{
return this._parsedSuccessfully;
}
forEachNode(callback)
{
console.assert(this._parsedSuccessfully);
if (!this._parsedSuccessfully)
return;
this._recurse(this._syntaxTree, callback, this._defaultParserState());
}
filter(predicate, startNode)
{
console.assert(startNode && this._parsedSuccessfully);
if (!this._parsedSuccessfully)
return [];
var nodes = [];
function filter(node, state)
{
if (predicate(node))
nodes.push(node);
else
state.skipChildNodes = true;
}
this._recurse(startNode, filter, this._defaultParserState());
return nodes;
}
containersOfPosition(position)
{
console.assert(this._parsedSuccessfully);
if (!this._parsedSuccessfully)
return [];
let allNodes = [];
this.forEachNode((node, state) => {
if (node.endPosition.isBefore(position))
state.skipChildNodes = true;
else if (node.startPosition.isAfter(position))
state.shouldStopEarly = true;
else
allNodes.push(node);
});
return allNodes;
}
filterByRange(startPosition, endPosition)
{
console.assert(this._parsedSuccessfully);
if (!this._parsedSuccessfully)
return [];
var allNodes = [];
function filterForNodesInRange(node, state)
{
// program start range program end
// [ [ ] ]
// [ ] [ [ ] ] [ ]
// If a node's range ends before the range we're interested in starts, we don't need to search any of its
// enclosing ranges, because, by definition, those enclosing ranges are contained within this node's range.
if (node.endPosition.isBefore(startPosition)) {
state.skipChildNodes = true;
return;
}
// We are only interested in nodes whose start position is within our range.
if (node.startPosition.isWithin(startPosition, endPosition)) {
allNodes.push(node);
return;
}
// Once we see nodes that start beyond our range, we can quit traversing the AST. We can do this safely
// because we know the AST is traversed using depth first search, so it will traverse into enclosing ranges
// before it traverses into adjacent ranges.
if (node.startPosition.isAfter(endPosition))
state.shouldStopEarly = true;
}
this.forEachNode(filterForNodesInRange);
return allNodes;
}
containsNonEmptyReturnStatement(startNode)
{
console.assert(startNode && this._parsedSuccessfully);
if (!this._parsedSuccessfully)
return false;
if (startNode.attachments._hasNonEmptyReturnStatement !== undefined)
return startNode.attachments._hasNonEmptyReturnStatement;
function removeFunctionsFilter(node)
{
return node.type !== WI.ScriptSyntaxTree.NodeType.FunctionExpression
&& node.type !== WI.ScriptSyntaxTree.NodeType.FunctionDeclaration
&& node.type !== WI.ScriptSyntaxTree.NodeType.ArrowFunctionExpression;
}
var nodes = this.filter(removeFunctionsFilter, startNode);
var hasNonEmptyReturnStatement = false;
var returnStatementType = WI.ScriptSyntaxTree.NodeType.ReturnStatement;
for (var node of nodes) {
if (node.type === returnStatementType && node.argument) {
hasNonEmptyReturnStatement = true;
break;
}
}
startNode.attachments._hasNonEmptyReturnStatement = hasNonEmptyReturnStatement;
return hasNonEmptyReturnStatement;
}
static functionReturnDivot(node)
{
console.assert(node.type === WI.ScriptSyntaxTree.NodeType.FunctionDeclaration || node.type === WI.ScriptSyntaxTree.NodeType.FunctionExpression || node.type === WI.ScriptSyntaxTree.NodeType.MethodDefinition || node.type === WI.ScriptSyntaxTree.NodeType.ArrowFunctionExpression);
// COMPATIBILITY (iOS 9): Legacy Backends view the return type as being the opening "{" of the function body.
// After iOS 9, this is to move to the start of the function statement/expression. See below:
// Using DOM.pseudoElement because that was added after iOS 9.
if (!InspectorBackend.domains.DOM.hasEvent("pseudoElementAdded"))
return node.body.range[0];
// "f" in "function". "s" in "set". "g" in "get". First letter in any method name for classes and object literals.
// The "[" for computed methods in classes and object literals.
return node.typeProfilingReturnDivot;
}
updateTypes(nodesToUpdate, callback)
{
console.assert(this._script.target.RuntimeAgent.getRuntimeTypesForVariablesAtOffsets);
console.assert(Array.isArray(nodesToUpdate) && this._parsedSuccessfully);
if (!this._parsedSuccessfully)
return;
var allRequests = [];
var allRequestNodes = [];
var sourceID = this._script.id;
for (var node of nodesToUpdate) {
switch (node.type) {
case WI.ScriptSyntaxTree.NodeType.FunctionDeclaration:
case WI.ScriptSyntaxTree.NodeType.FunctionExpression:
case WI.ScriptSyntaxTree.NodeType.ArrowFunctionExpression:
for (var param of node.params) {
for (var identifier of this._gatherIdentifiersInDeclaration(param)) {
allRequests.push({
typeInformationDescriptor: WI.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
sourceID,
divot: identifier.range[0]
});
allRequestNodes.push(identifier);
}
}
allRequests.push({
typeInformationDescriptor: WI.ScriptSyntaxTree.TypeProfilerSearchDescriptor.FunctionReturn,
sourceID,
divot: WI.ScriptSyntaxTree.functionReturnDivot(node)
});
allRequestNodes.push(node);
break;
case WI.ScriptSyntaxTree.NodeType.VariableDeclarator:
for (var identifier of this._gatherIdentifiersInDeclaration(node.id)) {
allRequests.push({
typeInformationDescriptor: WI.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
sourceID,
divot: identifier.range[0]
});
allRequestNodes.push(identifier);
}
break;
}
}
console.assert(allRequests.length === allRequestNodes.length);
function handleTypes(error, typeInformationArray)
{
if (error)
return;
console.assert(typeInformationArray.length === allRequests.length);
for (var i = 0; i < typeInformationArray.length; i++) {
var node = allRequestNodes[i];
var typeInformation = WI.TypeDescription.fromPayload(typeInformationArray[i]);
if (allRequests[i].typeInformationDescriptor === WI.ScriptSyntaxTree.TypeProfilerSearchDescriptor.FunctionReturn)
node.attachments.returnTypes = typeInformation;
else
node.attachments.types = typeInformation;
}
callback(allRequestNodes);
}
this._script.target.RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handleTypes);
}
// Private
_gatherIdentifiersInDeclaration(node)
{
function gatherIdentifiers(node)
{
switch (node.type) {
case WI.ScriptSyntaxTree.NodeType.Identifier:
return [node];
case WI.ScriptSyntaxTree.NodeType.Property:
return gatherIdentifiers(node.value);
case WI.ScriptSyntaxTree.NodeType.ObjectPattern:
var identifiers = [];
for (var property of node.properties) {
for (var identifier of gatherIdentifiers(property))
identifiers.push(identifier);
}
return identifiers;
case WI.ScriptSyntaxTree.NodeType.ArrayPattern:
var identifiers = [];
for (var element of node.elements) {
for (var identifier of gatherIdentifiers(element))
identifiers.push(identifier);
}
return identifiers;
case WI.ScriptSyntaxTree.NodeType.AssignmentPattern:
return gatherIdentifiers(node.left);
case WI.ScriptSyntaxTree.NodeType.RestElement:
return gatherIdentifiers(node.argument);
default:
console.assert(false, "Unexpected node type in variable declarator: " + node.type);
return [];
}
}
console.assert(node.type === WI.ScriptSyntaxTree.NodeType.Identifier || node.type === WI.ScriptSyntaxTree.NodeType.ObjectPattern || node.type === WI.ScriptSyntaxTree.NodeType.ArrayPattern || node.type === WI.ScriptSyntaxTree.NodeType.RestElement);
return gatherIdentifiers(node);
}
_defaultParserState()
{
return {
shouldStopEarly: false,
skipChildNodes: false
};
}
_recurse(node, callback, state)
{
if (!node)
return;
if (state.shouldStopEarly || state.skipChildNodes)
return;
callback(node, state);
switch (node.type) {
case WI.ScriptSyntaxTree.NodeType.AssignmentExpression:
this._recurse(node.left, callback, state);
this._recurse(node.right, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ArrayExpression:
case WI.ScriptSyntaxTree.NodeType.ArrayPattern:
this._recurseArray(node.elements, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.AssignmentPattern:
this._recurse(node.left, callback, state);
this._recurse(node.right, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.AwaitExpression:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.BlockStatement:
this._recurseArray(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.BinaryExpression:
this._recurse(node.left, callback, state);
this._recurse(node.right, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.BreakStatement:
this._recurse(node.label, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.CatchClause:
this._recurse(node.param, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.CallExpression:
this._recurse(node.callee, callback, state);
this._recurseArray(node.arguments, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ClassBody:
this._recurseArray(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ClassDeclaration:
case WI.ScriptSyntaxTree.NodeType.ClassExpression:
this._recurse(node.id, callback, state);
this._recurse(node.superClass, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ContinueStatement:
this._recurse(node.label, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.DoWhileStatement:
this._recurse(node.body, callback, state);
this._recurse(node.test, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ExpressionStatement:
this._recurse(node.expression, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ForStatement:
this._recurse(node.init, callback, state);
this._recurse(node.test, callback, state);
this._recurse(node.update, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ForInStatement:
case WI.ScriptSyntaxTree.NodeType.ForOfStatement:
this._recurse(node.left, callback, state);
this._recurse(node.right, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.FunctionDeclaration:
case WI.ScriptSyntaxTree.NodeType.FunctionExpression:
case WI.ScriptSyntaxTree.NodeType.ArrowFunctionExpression:
this._recurse(node.id, callback, state);
this._recurseArray(node.params, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.IfStatement:
this._recurse(node.test, callback, state);
this._recurse(node.consequent, callback, state);
this._recurse(node.alternate, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.LabeledStatement:
this._recurse(node.label, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.LogicalExpression:
this._recurse(node.left, callback, state);
this._recurse(node.right, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.MemberExpression:
this._recurse(node.object, callback, state);
this._recurse(node.property, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.MethodDefinition:
this._recurse(node.key, callback, state);
this._recurse(node.value, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.NewExpression:
this._recurse(node.callee, callback, state);
this._recurseArray(node.arguments, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ObjectExpression:
case WI.ScriptSyntaxTree.NodeType.ObjectPattern:
this._recurseArray(node.properties, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.Program:
this._recurseArray(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.Property:
this._recurse(node.key, callback, state);
this._recurse(node.value, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.RestElement:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ReturnStatement:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.SequenceExpression:
this._recurseArray(node.expressions, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.SpreadElement:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.SwitchStatement:
this._recurse(node.discriminant, callback, state);
this._recurseArray(node.cases, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.SwitchCase:
this._recurse(node.test, callback, state);
this._recurseArray(node.consequent, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ConditionalExpression:
this._recurse(node.test, callback, state);
this._recurse(node.consequent, callback, state);
this._recurse(node.alternate, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.TaggedTemplateExpression:
this._recurse(node.tag, callback, state);
this._recurse(node.quasi, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.TemplateLiteral:
this._recurseArray(node.quasis, callback, state);
this._recurseArray(node.expressions, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ThrowStatement:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.TryStatement:
this._recurse(node.block, callback, state);
this._recurse(node.handler, callback, state);
this._recurse(node.finalizer, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.UnaryExpression:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.UpdateExpression:
this._recurse(node.argument, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.VariableDeclaration:
this._recurseArray(node.declarations, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.VariableDeclarator:
this._recurse(node.id, callback, state);
this._recurse(node.init, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.WhileStatement:
this._recurse(node.test, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.WithStatement:
this._recurse(node.object, callback, state);
this._recurse(node.body, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.YieldExpression:
this._recurse(node.argument, callback, state);
break;
// Modules.
case WI.ScriptSyntaxTree.NodeType.ExportAllDeclaration:
this._recurse(node.source, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ExportNamedDeclaration:
this._recurse(node.declaration, callback, state);
this._recurseArray(node.specifiers, callback, state);
this._recurse(node.source, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ExportDefaultDeclaration:
this._recurse(node.declaration, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ExportSpecifier:
this._recurse(node.local, callback, state);
this._recurse(node.exported, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ImportDeclaration:
this._recurseArray(node.specifiers, callback, state);
this._recurse(node.source, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ImportDefaultSpecifier:
this._recurse(node.local, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ImportNamespaceSpecifier:
this._recurse(node.local, callback, state);
break;
case WI.ScriptSyntaxTree.NodeType.ImportSpecifier:
this._recurse(node.imported, callback, state);
this._recurse(node.local, callback, state);
break;
// All the leaf nodes go here.
case WI.ScriptSyntaxTree.NodeType.DebuggerStatement:
case WI.ScriptSyntaxTree.NodeType.EmptyStatement:
case WI.ScriptSyntaxTree.NodeType.Identifier:
case WI.ScriptSyntaxTree.NodeType.Import:
case WI.ScriptSyntaxTree.NodeType.Literal:
case WI.ScriptSyntaxTree.NodeType.MetaProperty:
case WI.ScriptSyntaxTree.NodeType.Super:
case WI.ScriptSyntaxTree.NodeType.ThisExpression:
case WI.ScriptSyntaxTree.NodeType.TemplateElement:
break;
}
state.skipChildNodes = false;
}
_recurseArray(array, callback, state)
{
for (var node of array)
this._recurse(node, callback, state);
}
// This function translates from esprima's Abstract Syntax Tree to ours.
// Mostly, this is just the identity function. We've added an extra typeProfilingReturnDivot property for functions/methods.
// Our AST complies with the Mozilla parser API:
// https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API
_createInternalSyntaxTree(node)
{
if (!node)
return null;
var result = null;
switch (node.type) {
case "ArrayExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.ArrayExpression,
elements: node.elements.map(this._createInternalSyntaxTree, this)
};
break;
case "ArrayPattern":
result = {
type: WI.ScriptSyntaxTree.NodeType.ArrayPattern,
elements: node.elements.map(this._createInternalSyntaxTree, this)
};
break;
case "ArrowFunctionExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.ArrowFunctionExpression,
id: this._createInternalSyntaxTree(node.id),
params: node.params.map(this._createInternalSyntaxTree, this),
body: this._createInternalSyntaxTree(node.body),
generator: node.generator,
expression: node.expression, // Boolean indicating if the body a single expression or a block statement.
async: node.async,
typeProfilingReturnDivot: node.range[0]
};
break;
case "AssignmentExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.AssignmentExpression,
operator: node.operator,
left: this._createInternalSyntaxTree(node.left),
right: this._createInternalSyntaxTree(node.right)
};
break;
case "AssignmentPattern":
result = {
type: WI.ScriptSyntaxTree.NodeType.AssignmentPattern,
left: this._createInternalSyntaxTree(node.left),
right: this._createInternalSyntaxTree(node.right),
};
break;
case "AwaitExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.AwaitExpression,
argument: this._createInternalSyntaxTree(node.argument),
};
break;
case "BlockStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.BlockStatement,
body: node.body.map(this._createInternalSyntaxTree, this)
};
break;
case "BinaryExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.BinaryExpression,
operator: node.operator,
left: this._createInternalSyntaxTree(node.left),
right: this._createInternalSyntaxTree(node.right)
};
break;
case "BreakStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.BreakStatement,
label: this._createInternalSyntaxTree(node.label)
};
break;
case "CallExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.CallExpression,
callee: this._createInternalSyntaxTree(node.callee),
arguments: node.arguments.map(this._createInternalSyntaxTree, this)
};
break;
case "CatchClause":
result = {
type: WI.ScriptSyntaxTree.NodeType.CatchClause,
param: this._createInternalSyntaxTree(node.param),
body: this._createInternalSyntaxTree(node.body)
};
break;
case "ClassBody":
result = {
type: WI.ScriptSyntaxTree.NodeType.ClassBody,
body: node.body.map(this._createInternalSyntaxTree, this)
};
break;
case "ClassDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.ClassDeclaration,
id: this._createInternalSyntaxTree(node.id),
superClass: this._createInternalSyntaxTree(node.superClass),
body: this._createInternalSyntaxTree(node.body),
};
break;
case "ClassExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.ClassExpression,
id: this._createInternalSyntaxTree(node.id),
superClass: this._createInternalSyntaxTree(node.superClass),
body: this._createInternalSyntaxTree(node.body),
};
break;
case "ConditionalExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.ConditionalExpression,
test: this._createInternalSyntaxTree(node.test),
consequent: this._createInternalSyntaxTree(node.consequent),
alternate: this._createInternalSyntaxTree(node.alternate)
};
break;
case "ContinueStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ContinueStatement,
label: this._createInternalSyntaxTree(node.label)
};
break;
case "DoWhileStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.DoWhileStatement,
body: this._createInternalSyntaxTree(node.body),
test: this._createInternalSyntaxTree(node.test)
};
break;
case "DebuggerStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.DebuggerStatement
};
break;
case "EmptyStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.EmptyStatement
};
break;
case "ExpressionStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ExpressionStatement,
expression: this._createInternalSyntaxTree(node.expression)
};
break;
case "ForStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ForStatement,
init: this._createInternalSyntaxTree(node.init),
test: this._createInternalSyntaxTree(node.test),
update: this._createInternalSyntaxTree(node.update),
body: this._createInternalSyntaxTree(node.body)
};
break;
case "ForInStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ForInStatement,
left: this._createInternalSyntaxTree(node.left),
right: this._createInternalSyntaxTree(node.right),
body: this._createInternalSyntaxTree(node.body)
};
break;
case "ForOfStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ForOfStatement,
left: this._createInternalSyntaxTree(node.left),
right: this._createInternalSyntaxTree(node.right),
body: this._createInternalSyntaxTree(node.body),
await: node.await
};
break;
case "FunctionDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.FunctionDeclaration,
id: this._createInternalSyntaxTree(node.id),
params: node.params.map(this._createInternalSyntaxTree, this),
body: this._createInternalSyntaxTree(node.body),
generator: node.generator,
async: node.async,
typeProfilingReturnDivot: node.range[0]
};
break;
case "FunctionExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.FunctionExpression,
id: this._createInternalSyntaxTree(node.id),
params: node.params.map(this._createInternalSyntaxTree, this),
body: this._createInternalSyntaxTree(node.body),
generator: node.generator,
async: node.async,
typeProfilingReturnDivot: node.range[0] // This may be overridden in the Property AST node.
};
break;
case "Identifier":
result = {
type: WI.ScriptSyntaxTree.NodeType.Identifier,
name: node.name
};
break;
case "IfStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.IfStatement,
test: this._createInternalSyntaxTree(node.test),
consequent: this._createInternalSyntaxTree(node.consequent),
alternate: this._createInternalSyntaxTree(node.alternate)
};
break;
case "Literal":
result = {
type: WI.ScriptSyntaxTree.NodeType.Literal,
value: node.value,
raw: node.raw
};
break;
case "LabeledStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.LabeledStatement,
label: this._createInternalSyntaxTree(node.label),
body: this._createInternalSyntaxTree(node.body)
};
break;
case "LogicalExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.LogicalExpression,
left: this._createInternalSyntaxTree(node.left),
right: this._createInternalSyntaxTree(node.right),
operator: node.operator
};
break;
case "MemberExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.MemberExpression,
object: this._createInternalSyntaxTree(node.object),
property: this._createInternalSyntaxTree(node.property),
computed: node.computed
};
break;
case "MetaProperty":
// i.e: new.target produces {meta: "new", property: "target"}
result = {
type: WI.ScriptSyntaxTree.NodeType.MetaProperty,
meta: this._createInternalSyntaxTree(node.meta),
property: this._createInternalSyntaxTree(node.property),
};
break;
case "MethodDefinition":
result = {
type: WI.ScriptSyntaxTree.NodeType.MethodDefinition,
key: this._createInternalSyntaxTree(node.key),
value: this._createInternalSyntaxTree(node.value),
computed: node.computed,
kind: node.kind,
static: node.static
};
result.value.typeProfilingReturnDivot = node.range[0]; // "g" in "get" or "s" in "set" or "[" in "['computed']" or "m" in "methodName".
break;
case "NewExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.NewExpression,
callee: this._createInternalSyntaxTree(node.callee),
arguments: node.arguments.map(this._createInternalSyntaxTree, this)
};
break;
case "ObjectExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.ObjectExpression,
properties: node.properties.map(this._createInternalSyntaxTree, this)
};
break;
case "ObjectPattern":
result = {
type: WI.ScriptSyntaxTree.NodeType.ObjectPattern,
properties: node.properties.map(this._createInternalSyntaxTree, this)
};
break;
case "Program":
result = {
type: WI.ScriptSyntaxTree.NodeType.Program,
sourceType: node.sourceType,
body: node.body.map(this._createInternalSyntaxTree, this)
};
break;
case "Property":
result = {
type: WI.ScriptSyntaxTree.NodeType.Property,
key: this._createInternalSyntaxTree(node.key),
value: this._createInternalSyntaxTree(node.value),
kind: node.kind,
method: node.method,
computed: node.computed
};
if (result.kind === "get" || result.kind === "set" || result.method)
result.value.typeProfilingReturnDivot = node.range[0]; // "g" in "get" or "s" in "set" or "[" in "['computed']" method or "m" in "methodName".
break;
case "RestElement":
result = {
type: WI.ScriptSyntaxTree.NodeType.RestElement,
argument: this._createInternalSyntaxTree(node.argument)
};
break;
case "ReturnStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ReturnStatement,
argument: this._createInternalSyntaxTree(node.argument)
};
break;
case "SequenceExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.SequenceExpression,
expressions: node.expressions.map(this._createInternalSyntaxTree, this)
};
break;
case "SpreadElement":
result = {
type: WI.ScriptSyntaxTree.NodeType.SpreadElement,
argument: this._createInternalSyntaxTree(node.argument),
};
break;
case "Super":
result = {
type: WI.ScriptSyntaxTree.NodeType.Super
};
break;
case "SwitchStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.SwitchStatement,
discriminant: this._createInternalSyntaxTree(node.discriminant),
cases: node.cases.map(this._createInternalSyntaxTree, this)
};
break;
case "SwitchCase":
result = {
type: WI.ScriptSyntaxTree.NodeType.SwitchCase,
test: this._createInternalSyntaxTree(node.test),
consequent: node.consequent.map(this._createInternalSyntaxTree, this)
};
break;
case "TaggedTemplateExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.TaggedTemplateExpression,
tag: this._createInternalSyntaxTree(node.tag),
quasi: this._createInternalSyntaxTree(node.quasi)
};
break;
case "TemplateElement":
result = {
type: WI.ScriptSyntaxTree.NodeType.TemplateElement,
value: node.value,
tail: node.tail
};
break;
case "TemplateLiteral":
result = {
type: WI.ScriptSyntaxTree.NodeType.TemplateLiteral,
quasis: node.quasis.map(this._createInternalSyntaxTree, this),
expressions: node.expressions.map(this._createInternalSyntaxTree, this)
};
break;
case "ThisExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.ThisExpression
};
break;
case "ThrowStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.ThrowStatement,
argument: this._createInternalSyntaxTree(node.argument)
};
break;
case "TryStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.TryStatement,
block: this._createInternalSyntaxTree(node.block),
handler: this._createInternalSyntaxTree(node.handler),
finalizer: this._createInternalSyntaxTree(node.finalizer)
};
break;
case "UnaryExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.UnaryExpression,
operator: node.operator,
argument: this._createInternalSyntaxTree(node.argument)
};
break;
case "UpdateExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.UpdateExpression,
operator: node.operator,
prefix: node.prefix,
argument: this._createInternalSyntaxTree(node.argument)
};
break;
case "VariableDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.VariableDeclaration,
declarations: node.declarations.map(this._createInternalSyntaxTree, this),
kind: node.kind
};
break;
case "VariableDeclarator":
result = {
type: WI.ScriptSyntaxTree.NodeType.VariableDeclarator,
id: this._createInternalSyntaxTree(node.id),
init: this._createInternalSyntaxTree(node.init)
};
break;
case "WhileStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.WhileStatement,
test: this._createInternalSyntaxTree(node.test),
body: this._createInternalSyntaxTree(node.body)
};
break;
case "WithStatement":
result = {
type: WI.ScriptSyntaxTree.NodeType.WithStatement,
object: this._createInternalSyntaxTree(node.object),
body: this._createInternalSyntaxTree(node.body)
};
break;
case "YieldExpression":
result = {
type: WI.ScriptSyntaxTree.NodeType.YieldExpression,
argument: this._createInternalSyntaxTree(node.argument),
delegate: node.delegate
};
break;
// Modules.
case "ExportAllDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.ExportAllDeclaration,
source: this._createInternalSyntaxTree(node.source),
};
break;
case "ExportNamedDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.ExportNamedDeclaration,
declaration: this._createInternalSyntaxTree(node.declaration),
specifiers: node.specifiers.map(this._createInternalSyntaxTree, this),
source: this._createInternalSyntaxTree(node.source),
};
break;
case "ExportDefaultDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.ExportDefaultDeclaration,
declaration: this._createInternalSyntaxTree(node.declaration),
};
break;
case "ExportSpecifier":
result = {
type: WI.ScriptSyntaxTree.NodeType.ExportSpecifier,
local: this._createInternalSyntaxTree(node.local),
exported: this._createInternalSyntaxTree(node.exported),
};
break;
case "Import":
result = {
type: WI.ScriptSyntaxTree.NodeType.Import,
};
break;
case "ImportDeclaration":
result = {
type: WI.ScriptSyntaxTree.NodeType.ImportDeclaration,
specifiers: node.specifiers.map(this._createInternalSyntaxTree, this),
source: this._createInternalSyntaxTree(node.source),
};
break;
case "ImportDefaultSpecifier":
result = {
type: WI.ScriptSyntaxTree.NodeType.ImportDefaultSpecifier,
local: this._createInternalSyntaxTree(node.local),
};
break;
case "ImportNamespaceSpecifier":
result = {
type: WI.ScriptSyntaxTree.NodeType.ImportNamespaceSpecifier,
local: this._createInternalSyntaxTree(node.local),
};
break;
case "ImportSpecifier":
result = {
type: WI.ScriptSyntaxTree.NodeType.ImportSpecifier,
imported: this._createInternalSyntaxTree(node.imported),
local: this._createInternalSyntaxTree(node.local),
};
break;
default:
console.error("Unsupported Syntax Tree Node: " + node.type, node);
return null;
}
let {start, end} = node.loc;
result.startPosition = new WI.SourceCodePosition(start.line - 1, start.column);
result.endPosition = new WI.SourceCodePosition(end.line - 1, end.column);
result.range = node.range;
// This is an object for which you can add fields to an AST node without worrying about polluting the syntax-related fields of the node.
result.attachments = {};
return result;
}
};
// This should be kept in sync with an enum in JavaSciptCore/runtime/TypeProfiler.h
WI.ScriptSyntaxTree.TypeProfilerSearchDescriptor = {
NormalExpression: 1,
FunctionReturn: 2
};
WI.ScriptSyntaxTree.NodeType = {
ArrayExpression: Symbol("array-expression"),
ArrayPattern: Symbol("array-pattern"),
ArrowFunctionExpression: Symbol("arrow-function-expression"),
AssignmentExpression: Symbol("assignment-expression"),
AssignmentPattern: Symbol("assignment-pattern"),
AwaitExpression: Symbol("await-expression"),
BinaryExpression: Symbol("binary-expression"),
BlockStatement: Symbol("block-statement"),
BreakStatement: Symbol("break-statement"),
CallExpression: Symbol("call-expression"),
CatchClause: Symbol("catch-clause"),
ClassBody: Symbol("class-body"),
ClassDeclaration: Symbol("class-declaration"),
ClassExpression: Symbol("class-expression"),
ConditionalExpression: Symbol("conditional-expression"),
ContinueStatement: Symbol("continue-statement"),
DebuggerStatement: Symbol("debugger-statement"),
DoWhileStatement: Symbol("do-while-statement"),
EmptyStatement: Symbol("empty-statement"),
ExportAllDeclaration: Symbol("export-all-declaration"),
ExportDefaultDeclaration: Symbol("export-default-declaration"),
ExportNamedDeclaration: Symbol("export-named-declaration"),
ExportSpecifier: Symbol("export-specifier"),
ExpressionStatement: Symbol("expression-statement"),
ForInStatement: Symbol("for-in-statement"),
ForOfStatement: Symbol("for-of-statement"),
ForStatement: Symbol("for-statement"),
FunctionDeclaration: Symbol("function-declaration"),
FunctionExpression: Symbol("function-expression"),
Identifier: Symbol("identifier"),
IfStatement: Symbol("if-statement"),
Import: Symbol("import"),
ImportDeclaration: Symbol("import-declaration"),
ImportDefaultSpecifier: Symbol("import-default-specifier"),
ImportNamespaceSpecifier: Symbol("import-namespace-specifier"),
ImportSpecifier: Symbol("import-specifier"),
LabeledStatement: Symbol("labeled-statement"),
Literal: Symbol("literal"),
LogicalExpression: Symbol("logical-expression"),
MemberExpression: Symbol("member-expression"),
MetaProperty: Symbol("meta-property"),
MethodDefinition: Symbol("method-definition"),
NewExpression: Symbol("new-expression"),
ObjectExpression: Symbol("object-expression"),
ObjectPattern: Symbol("object-pattern"),
Program: Symbol("program"),
Property: Symbol("property"),
RestElement: Symbol("rest-element"),
ReturnStatement: Symbol("return-statement"),
SequenceExpression: Symbol("sequence-expression"),
SpreadElement: Symbol("spread-element"),
Super: Symbol("super"),
SwitchCase: Symbol("switch-case"),
SwitchStatement: Symbol("switch-statement"),
TaggedTemplateExpression: Symbol("tagged-template-expression"),
TemplateElement: Symbol("template-element"),
TemplateLiteral: Symbol("template-literal"),
ThisExpression: Symbol("this-expression"),
ThrowStatement: Symbol("throw-statement"),
TryStatement: Symbol("try-statement"),
UnaryExpression: Symbol("unary-expression"),
UpdateExpression: Symbol("update-expression"),
VariableDeclaration: Symbol("variable-declaration"),
VariableDeclarator: Symbol("variable-declarator"),
WhileStatement: Symbol("while-statement"),
WithStatement: Symbol("with-statement"),
YieldExpression: Symbol("yield-expression"),
};