/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.objects.reflect; import static com.github.anba.es6draft.runtime.AbstractOperations.*; import static com.github.anba.es6draft.runtime.objects.text.RegExpConstructor.RegExpCreate; import static com.github.anba.es6draft.runtime.types.Null.NULL; import static com.github.anba.es6draft.runtime.types.Type.isUndefinedOrNull; import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject.ObjectCreate; import static com.github.anba.es6draft.semantics.StaticSemantics.LexicallyScopedDeclarations; import static com.github.anba.es6draft.semantics.StaticSemantics.Substitutions; import static com.github.anba.es6draft.semantics.StaticSemantics.TemplateStrings; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; import java.util.Iterator; import java.util.List; import com.github.anba.es6draft.ast.*; import com.github.anba.es6draft.ast.synthetic.ExpressionMethod; import com.github.anba.es6draft.ast.synthetic.MethodDefinitionsMethod; import com.github.anba.es6draft.ast.synthetic.PropertyDefinitionsMethod; import com.github.anba.es6draft.ast.synthetic.SpreadArrayLiteral; import com.github.anba.es6draft.ast.synthetic.SpreadElementMethod; import com.github.anba.es6draft.ast.synthetic.StatementListMethod; import com.github.anba.es6draft.parser.ParserException; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.Source; import com.github.anba.es6draft.runtime.objects.text.RegExpObject; import com.github.anba.es6draft.runtime.types.Callable; import com.github.anba.es6draft.runtime.types.Intrinsics; import com.github.anba.es6draft.runtime.types.ScriptObject; import com.github.anba.es6draft.runtime.types.builtins.ArrayObject; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject; /** * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API */ public final class ReflectParser implements NodeVisitor<Object, Void> { private final ExecutionContext cx; private final boolean location; private final String sourceInfo; private final EnumMap<Type, Callable> builder; private enum Type {/* @formatter:off */ // Programs Program("program"), // Miscellaneous Identifier("identifier"), Literal("literal"), Property("property"), PrototypeMutation("prototypeMutation"), // Declarations ModuleDeclaration("moduleDeclaration"), FunctionDeclaration("functionDeclaration"), VariableDeclaration("variableDeclaration"), VariableDeclarator("variableDeclarator"), // Expressions SequenceExpression("sequenceExpression"), ConditionalExpression("conditionalExpression"), UnaryExpression("unaryExpression"), BinaryExpression("binaryExpression"), AssignmentExpression("assignmentExpression"), LogicalExpression("logicalExpression"), UpdateExpression("updateExpression"), NewExpression("newExpression"), CallExpression("callExpression"), MemberExpression("memberExpression"), FunctionExpression("functionExpression"), ArrowFunctionExpression("arrowFunctionExpression"), ArrayExpression("arrayExpression"), SpreadExpression("spreadExpression"), ObjectExpression("objectExpression"), ThisExpression("thisExpression"), ComprehensionExpression("comprehensionExpression"), GeneratorExpression("generatorExpression"), YieldExpression("yieldExpression"), ClassExpression("classExpression"), MetaProperty("metaProperty"), Super("super"), // Statements EmptyStatement("emptyStatement"), BlockStatement("blockStatement"), ExpressionStatement("expressionStatement"), LabeledStatement("labeledStatement"), IfStatement("ifStatement"), SwitchStatement("switchStatement"), WhileStatement("whileStatement"), DoWhileStatement("doWhileStatement"), ForStatement("forStatement"), ForInStatement("forInStatement"), ForOfStatement("forOfStatement"), BreakStatement("breakStatement"), ContinueStatement("continueStatement"), WithStatement("withStatement"), ReturnStatement("returnStatement"), TryStatement("tryStatement"), ThrowStatement("throwStatement"), DebuggerStatement("debuggerStatement"), LetStatement("letStatement"), // Modules ImportDeclaration("importDeclaration"), ImportSpecifier("importSpecifier"), ExportDeclaration("exportDeclaration"), ExportSpecifier("exportSpecifier"), ExportBatchSpecifier("exportBatchSpecifier"), // Clauses SwitchCase("switchCase"), CatchClause("catchClause"), ComprehensionBlock("comprehensionBlock"), ComprehensionIf("comprehensionIf"), // Patterns ArrayPattern("arrayPattern"), ObjectPattern("objectPattern"), PropertyPattern("propertyPattern"), // Template strings TemplateLiteral("templateLiteral"), TaggedTemplate("taggedTemplate"), CallSiteObject("callSiteObject"), // Computed property name ComputedName("computedName"), // Classes ClassStatement("classStatement"), ClassMethod("classMethod"), // New node types AwaitExpression(), DoExpression(), ForAwaitStatement(), // Removed node types LetExpression("letExpression"), ; /* @formatter:on */ private final String name; private Type() { // <no custom builder> this(null); } private Type(String name) { this.name = name; } } private enum GeneratorStyle { ES6("es6"), Legacy("legacy"); final String name; private GeneratorStyle(String name) { this.name = name; } } private static EnumMap<Type, Callable> toBuilderMap(ExecutionContext cx, ScriptObject builderObject) { EnumMap<Type, Callable> map = new EnumMap<>(Type.class); for (Type builder : Type.values()) { String methodName = builder.name; if (methodName == null) { continue; } if (!HasProperty(cx, builderObject, methodName)) { continue; } Callable method = GetMethod(cx, builderObject, methodName); if (method == null) { continue; } map.put(builder, method); } return map; } private boolean hasBuilder(Type type) { return builder.containsKey(type); } private Object call(Type type, Node node, Object... arguments) { if (location && node != null) { Object[] args = Arrays.copyOf(arguments, arguments.length + 1); args[arguments.length] = createSourceLocation(node); arguments = args; } return builder.get(type).call(cx, NULL, arguments); } private ReflectParser(ExecutionContext cx, boolean location, String sourceInfo, EnumMap<Type, Callable> builder) { this.cx = cx; this.location = location; this.sourceInfo = sourceInfo; this.builder = builder; } /** * Parses the given script code and returns the matching Reflect AST nodes. * * @param cx * the execution context * @param source * the source string * @param options * the options object * @return the parsed node */ public static Object parse(ExecutionContext cx, String source, ScriptObject options) { boolean location = true; String sourceInfo = null; int line = 1; EnumMap<Type, Callable> builder = new EnumMap<>(Type.class); if (options != null) { if (HasProperty(cx, options, "loc")) { location = ToBoolean(Get(cx, options, "loc")); } if (HasProperty(cx, options, "source")) { sourceInfo = ToFlatString(cx, Get(cx, options, "source")); } if (HasProperty(cx, options, "line")) { line = ToInt32(cx, Get(cx, options, "line")); } if (HasProperty(cx, options, "builder")) { Object value = Get(cx, options, "builder"); if (!isUndefinedOrNull(value)) { builder = toBuilderMap(cx, ToObject(cx, value)); } } } return parse(cx, source, location, sourceInfo, line, builder); } /** * Parses the given script code and returns the matching Reflect AST nodes. * * @param cx * the execution context * @param sourceCode * the source string * @param location * if set to {@code true} node locations will be recorded * @param sourceInfo * the source info string * @param line * the start line offset * @param builder * map to customize AST node processing * @return the parsed node */ public static Object parse(ExecutionContext cx, String sourceCode, boolean location, String sourceInfo, int line, EnumMap<Type, Callable> builder) { Realm realm = cx.getRealm(); ReflectParser reflect = new ReflectParser(cx, location, sourceInfo, builder); Source source = new Source("<parse>", line); TopLevelNode<?> parsedNode = null; try { parsedNode = realm.getScriptLoader().parseScript(source, sourceCode); } catch (ParserException ignore) { // TODO: Reflect.parse() currently accepts scripts and modules... try { parsedNode = realm.getScriptLoader().parseModule(source, sourceCode); } catch (ParserException e) { throw e.toScriptException(cx); } } return parsedNode.accept(reflect, null); } private OrdinaryObject createEmptyNode() { return ObjectCreate(cx, Intrinsics.ObjectPrototype); } private void addProperty(OrdinaryObject holder, String key, Object value) { CreateDataProperty(cx, holder, key, value); } private void addNodeInfo(OrdinaryObject holder, Node node, Type type) { Object loc = location ? createSourceLocation(node) : NULL; addSourceLocation(holder, loc); addType(holder, type); } private void addNodeInfo(OrdinaryObject holder, Type type) { addSourceLocation(holder, NULL); addType(holder, type); } private void addSourceLocation(OrdinaryObject holder, Object loc) { addProperty(holder, "loc", loc); } private void addType(OrdinaryObject holder, Type type) { addProperty(holder, "type", type.name()); } private OrdinaryObject createSourceLocation(Node node) { OrdinaryObject loc = createEmptyNode(); addProperty(loc, "start", createPosition(node.getBeginLine(), node.getBeginColumn())); addProperty(loc, "end", createPosition(node.getEndLine(), node.getEndColumn())); addProperty(loc, "source", sourceInfo != null ? sourceInfo : NULL); return loc; } private OrdinaryObject createPosition(int line, int column) { // subtract one to make columns 0-indexed OrdinaryObject pos = createEmptyNode(); addProperty(pos, "line", line); addProperty(pos, "column", column - 1); return pos; } private OrdinaryObject createNode(Node node, Type type) { OrdinaryObject object = createEmptyNode(); addNodeInfo(object, node, type); return object; } private OrdinaryObject createNode(Type type) { OrdinaryObject object = createEmptyNode(); addNodeInfo(object, type); return object; } private OrdinaryObject createModuleItem(ModuleItem node, Type type) { return createNode(node, type); } private OrdinaryObject createStatement(Statement node, Type type) { return createNode(node, type); } private OrdinaryObject createDeclaration(LexicalDeclaration node, Type type) { return createNode(node, type); } private OrdinaryObject createDeclaration(VariableStatement node, Type type) { return createNode(node, type); } private OrdinaryObject createExpression(Expression node, Type type) { return createNode(node, type); } private OrdinaryObject createPattern(AssignmentPattern node, Type type) { return createNode(node, type); } private OrdinaryObject createPattern(BindingPattern node, Type type) { return createNode(node, type); } private OrdinaryObject createBinding(Binding node, Type type) { return createNode(node, type); } private OrdinaryObject createFunction(FunctionNode node, Type type) { return createNode(node, type); } private OrdinaryObject createClass(ClassDefinition node, Type type) { return createNode(node, type); } private Object createLiteral(ValueLiteral<?> node) { return createLiteral(node, node.getValue()); } private Object createLiteral(TemplateCharacters node) { return createLiteral(node, node.getValue()); } private Object createLiteral(Expression node, Object value) { if (hasBuilder(Type.Literal)) { return call(Type.Literal, node, value); } OrdinaryObject literal = createExpression(node, Type.Literal); addProperty(literal, "value", value); return literal; } private Object createLiteral(String value) { if (hasBuilder(Type.Literal)) { return call(Type.Literal, null, value); } OrdinaryObject literal = createNode(Type.Literal); addProperty(literal, "value", value); return literal; } private Object createIdentifierOrNull(String name) { return name != null ? createIdentifier(name) : NULL; } private Object createIdentifier(String name) { if (hasBuilder(Type.Identifier)) { return call(Type.Identifier, null, name); } OrdinaryObject identifier = createNode(Type.Identifier); addProperty(identifier, "name", name); return identifier; } private ArrayObject createList(List<? extends Node> nodes, Void value) { return CreateArrayFromList(cx, nodes.stream().map(node -> node.accept(this, value))); } private ArrayObject createListWithNull(List<? extends Node> nodes, Void value) { return CreateArrayFromList(cx, nodes.stream().map(node -> acceptOrNull(node, value))); } private ArrayObject createListFromValues(List<? extends Object> values) { return CreateArrayFromList(cx, values); } private Object acceptOrNull(Node node, Void value) { return node != null ? node.accept(this, value) : NULL; } private static <T> T lastElement(List<T> list) { return !list.isEmpty() ? list.get(list.size() - 1) : null; } private List<Binding> getParameterBindings(FormalParameterList formals) { ArrayList<Binding> bindings = new ArrayList<>(); for (FormalParameter formalParameter : formals) { BindingElementItem element = formalParameter.getElement(); if (element instanceof BindingElement) { bindings.add(((BindingElement) element).getBinding()); } } return bindings; } private List<Expression> getParameterDefaults(FormalParameterList formals) { boolean hasDefault = false; ArrayList<Expression> defaults = new ArrayList<>(); for (FormalParameter formalParameter : formals) { BindingElementItem element = formalParameter.getElement(); if (element instanceof BindingElement) { Expression initializer = ((BindingElement) element).getInitializer(); hasDefault |= initializer != null; defaults.add(initializer != null ? initializer : null); } else { defaults.add(null); } } return hasDefault ? defaults : Collections.<Expression> emptyList(); } private Binding getRestParameter(FormalParameterList formals) { FormalParameter last = lastElement(formals.getFormals()); if (last != null && last.getElement() instanceof BindingRestElement) { return ((BindingRestElement) last.getElement()).getBinding(); } return null; } private <STATEMENT extends Statement & AbruptNode> Object createLabelledStatement(STATEMENT node, Object body) { if (node.getLabelSet().isEmpty()) { return body; } Iterator<String> labels = new ArrayDeque<>(node.getLabelSet()).descendingIterator(); while (labels.hasNext()) { Object label = createIdentifier(labels.next()); if (hasBuilder(Type.LabeledStatement)) { body = call(Type.LabeledStatement, node, label, body); } else { OrdinaryObject statement = createStatement(node, Type.LabeledStatement); addProperty(statement, "label", label); addProperty(statement, "body", body); body = statement; } } return body; } private Object createFunctionBody(FunctionNode node, Void value) { // FunctionBody is materalized as BlockStatement ArrayObject body = createList(node.getStatements(), value); if (hasBuilder(Type.BlockStatement)) { return call(Type.BlockStatement, node, body); } OrdinaryObject statement = createNode(node, Type.BlockStatement); addProperty(statement, "body", body); return statement; } private OrdinaryObject createClassBody(ClassDefinition node, Void value) { ArrayList<OrdinaryObject> methods = new ArrayList<>(); for (PropertyDefinition property : node.getProperties()) { if (property instanceof MethodDefinition) { methods.add(createClassMethod((MethodDefinition) property, value)); } else { // TODO: Implement } } return createListFromValues(methods); } private OrdinaryObject createClassMethod(MethodDefinition node, Void value) { Object name = node.getPropertyName().accept(this, null); Object body = toFunctionExpression(node, value); String kind = methodKind(node, "method"); OrdinaryObject property = createNode(node, Type.ClassMethod); addProperty(property, "name", name); addProperty(property, "body", body); addProperty(property, "kind", kind); addProperty(property, "static", node.isStatic()); return property; } private Object toFunctionExpression(MethodDefinition node, Void value) { Object id; if (isGetterOrSetter(node) || node.getPropertyName() instanceof ComputedPropertyName) { id = NULL; } else { id = node.getPropertyName().accept(this, value); } ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); boolean generator = false; switch (node.getType()) { case AsyncFunction: case AsyncGenerator: // TODO: async functions break; case ConstructorGenerator: case Generator: generator = true; break; default: } boolean expression = false; if (hasBuilder(Type.FunctionExpression)) { return call(Type.FunctionExpression, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); if (generator) { addProperty(function, "style", GeneratorStyle.ES6.name); } addProperty(function, "expression", expression); return function; } private static String methodKind(MethodDefinition method, String defaultKind) { switch (method.getType()) { case Getter: return "get"; case Setter: return "set"; case AsyncFunction: case AsyncGenerator: case BaseConstructor: case DerivedConstructor: case CallConstructor: case Function: case Generator: case ConstructorGenerator: default: return defaultKind; } } private static boolean isGetterOrSetter(MethodDefinition method) { switch (method.getType()) { case Getter: case Setter: return true; case AsyncFunction: case AsyncGenerator: case BaseConstructor: case DerivedConstructor: case CallConstructor: case Function: case Generator: case ConstructorGenerator: default: return false; } } private Object toAssignmentExpression(Node left, Expression right, Void value) { EmptyExpression empty = new EmptyExpression(left.getBeginPosition(), right.getEndPosition()); Object left_ = left.accept(this, value); Object right_ = right.accept(this, value); String operator = AssignmentExpression.Operator.ASSIGN.getName(); if (hasBuilder(Type.AssignmentExpression)) { return call(Type.AssignmentExpression, empty, operator, left_, right_); } OrdinaryObject expression = createExpression(empty, Type.AssignmentExpression); addProperty(expression, "left", left_); addProperty(expression, "right", right_); addProperty(expression, "operator", operator); return expression; } @Override public Object visit(ArrayAssignmentPattern node, Void value) { ArrayObject elements = createList(node.getElements(), value); if (hasBuilder(Type.ArrayPattern)) { return call(Type.ArrayPattern, node, elements); } OrdinaryObject pattern = createPattern(node, Type.ArrayPattern); addProperty(pattern, "elements", elements); return pattern; } @Override public Object visit(ArrayBindingPattern node, Void value) { ArrayObject elements = createList(node.getElements(), value); if (hasBuilder(Type.ArrayPattern)) { return call(Type.ArrayPattern, node, elements); } OrdinaryObject pattern = createPattern(node, Type.ArrayPattern); addProperty(pattern, "elements", elements); return pattern; } @Override public Object visit(ArrayComprehension node, Void value) { // Comprehension/LegacyComprehension already created a partial result OrdinaryObject expression = (OrdinaryObject) node.getComprehension().accept(this, value); if (hasBuilder(Type.ComprehensionExpression)) { Object body = Get(cx, expression, "body"); Object blocks = Get(cx, expression, "blocks"); Object filter = Get(cx, expression, "filter"); return call(Type.ComprehensionExpression, node, body, blocks, filter); } addNodeInfo(expression, node, Type.ComprehensionExpression); return expression; } @Override public Object visit(ArrayLiteral node, Void value) { ArrayObject elements = createList(node.getElements(), value); if (hasBuilder(Type.ArrayExpression)) { return call(Type.ArrayExpression, node, elements); } OrdinaryObject expression = createExpression(node, Type.ArrayExpression); addProperty(expression, "elements", elements); return expression; } @Override public Object visit(ArrowFunction node, Void value) { Object id = NULL; ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body; if (node.getExpression() == null) { body = createFunctionBody(node, value); } else { body = node.getExpression().accept(this, value); } boolean generator = false; boolean expression = node.getExpression() != null; if (hasBuilder(Type.ArrowFunctionExpression)) { return call(Type.ArrowFunctionExpression, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.ArrowFunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(AssignmentElement node, Void value) { if (node.getInitializer() == null) { return node.getTarget().accept(this, value); } return toAssignmentExpression(node.getTarget(), node.getInitializer(), value); } @Override public Object visit(AssignmentExpression node, Void value) { Object left = node.getLeft().accept(this, value); Object right = node.getRight().accept(this, value); String operator = node.getOperator().getName(); if (hasBuilder(Type.AssignmentExpression)) { return call(Type.AssignmentExpression, node, operator, left, right); } OrdinaryObject expression = createExpression(node, Type.AssignmentExpression); addProperty(expression, "left", left); addProperty(expression, "right", right); addProperty(expression, "operator", operator); return expression; } @Override public Object visit(AssignmentProperty node, Void value) { Object key; if (node.getPropertyName() == null) { key = node.getTarget().accept(this, value); } else { key = node.getPropertyName().accept(this, value); } Object _value; if (node.getInitializer() == null) { _value = node.getTarget().accept(this, value); } else { _value = toAssignmentExpression(node.getTarget(), node.getInitializer(), value); } String kind = "init"; boolean method = false; boolean shorthand = node.getPropertyName() == null; boolean computed = node.getPropertyName() instanceof ComputedPropertyName; if (hasBuilder(Type.PropertyPattern)) { return call(Type.PropertyPattern, node, kind, key, _value); } OrdinaryObject property = createNode(node, Type.Property); // not PropertyPattern! addProperty(property, "key", key); addProperty(property, "value", _value); addProperty(property, "kind", kind); addProperty(property, "method", method); addProperty(property, "shorthand", shorthand); addProperty(property, "computed", computed); return property; } @Override public Object visit(AssignmentRestElement node, Void value) { Object expr = node.getTarget().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createNode(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(AssignmentRestProperty node, Void value) { Object expr = node.getTarget().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createNode(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(AsyncArrowFunction node, Void value) { Object id = NULL; ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body; if (node.getExpression() == null) { body = createFunctionBody(node, value); } else { body = node.getExpression().accept(this, value); } // TODO: flag for async boolean generator = false; boolean expression = node.getExpression() != null; if (hasBuilder(Type.ArrowFunctionExpression)) { return call(Type.ArrowFunctionExpression, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.ArrowFunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(AsyncFunctionDeclaration node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); // TODO: flag for async boolean generator = false; boolean expression = false; if (hasBuilder(Type.FunctionDeclaration)) { return call(Type.FunctionDeclaration, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionDeclaration); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(AsyncFunctionExpression node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); // TODO: flag for async boolean generator = false; boolean expression = false; if (hasBuilder(Type.FunctionExpression)) { return call(Type.FunctionExpression, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(AsyncGeneratorDeclaration node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); // TODO: flag for async generator boolean generator = false; boolean expression = false; if (hasBuilder(Type.FunctionDeclaration)) { return call(Type.FunctionDeclaration, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionDeclaration); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(AsyncGeneratorExpression node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); // TODO: flag for async generator boolean generator = false; boolean expression = false; if (hasBuilder(Type.FunctionExpression)) { return call(Type.FunctionExpression, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(AwaitExpression node, Void value) { Object argument = acceptOrNull(node.getExpression(), value); OrdinaryObject expression = createExpression(node, Type.AwaitExpression); addProperty(expression, "argument", argument); return expression; } @Override public Object visit(BinaryExpression node, Void value) { Object left = node.getLeft().accept(this, value); Object right = node.getRight().accept(this, value); String operator = node.getOperator().getName(); if (hasBuilder(type(node))) { return call(type(node), node, operator, left, right); } OrdinaryObject expression = createExpression(node, type(node)); addProperty(expression, "left", left); addProperty(expression, "right", right); addProperty(expression, "operator", operator); return expression; } private static Type type(BinaryExpression node) { switch (node.getOperator()) { case AND: case OR: return Type.LogicalExpression; default: return Type.BinaryExpression; } } @Override public Object visit(BindingElement node, Void value) { if (node.getInitializer() == null) { return node.getBinding().accept(this, value); } return toAssignmentExpression(node.getBinding(), node.getInitializer(), value); } @Override public Object visit(BindingElision node, Void value) { return NULL; } @Override public Object visit(BindingIdentifier node, Void value) { String name = node.getName().getIdentifier(); if (hasBuilder(Type.Identifier)) { return call(Type.Identifier, node, name); } OrdinaryObject binding = createBinding(node, Type.Identifier); addProperty(binding, "name", name); return binding; } @Override public Object visit(BindingProperty node, Void value) { Object key; if (node.getPropertyName() == null) { // BindingProperty : SingleNameBinding key = node.getBinding().accept(this, value); } else { key = node.getPropertyName().accept(this, value); } Object _value; if (node.getInitializer() == null) { _value = node.getBinding().accept(this, value); } else { _value = toAssignmentExpression(node.getBinding(), node.getInitializer(), value); } String kind = "init"; boolean method = false; boolean shorthand = node.getPropertyName() == null; boolean computed = node.getPropertyName() instanceof ComputedPropertyName; if (hasBuilder(Type.PropertyPattern)) { return call(Type.PropertyPattern, node, kind, key, _value); } OrdinaryObject property = createNode(node, Type.Property); // not PropertyPattern! addProperty(property, "key", key); addProperty(property, "value", _value); addProperty(property, "kind", kind); addProperty(property, "method", method); addProperty(property, "shorthand", shorthand); addProperty(property, "computed", computed); return property; } @Override public Object visit(BindingRestElement node, Void value) { Object expr = node.getBinding().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createNode(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(BindingRestProperty node, Void value) { Object expr = node.getBindingIdentifier().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createNode(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(BlockStatement node, Void value) { ArrayObject body = createList(node.getStatements(), value); if (hasBuilder(Type.BlockStatement)) { return call(Type.BlockStatement, node, body); } OrdinaryObject statement = createStatement(node, Type.BlockStatement); addProperty(statement, "body", body); return statement; } @Override public Object visit(BooleanLiteral node, Void value) { return createLiteral(node); } @Override public Object visit(BreakStatement node, Void value) { Object label = createIdentifierOrNull(node.getLabel()); if (hasBuilder(Type.BreakStatement)) { return call(Type.BreakStatement, node, label); } OrdinaryObject statement = createStatement(node, Type.BreakStatement); addProperty(statement, "label", label); return statement; } @Override public Object visit(CallExpression node, Void value) { Object callee = node.getBase().accept(this, value); ArrayObject arguments = createList(node.getArguments(), value); if (hasBuilder(Type.CallExpression)) { return call(Type.CallExpression, node, callee, arguments); } OrdinaryObject expression = createExpression(node, Type.CallExpression); addProperty(expression, "callee", callee); addProperty(expression, "arguments", arguments); return expression; } @Override public Object visit(CallSpreadElement node, Void value) { Object expr = node.getExpression().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createExpression(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(CatchNode node, Void value) { Object param = node.getCatchParameter().accept(this, value); Object guard = NULL; Object body = node.getCatchBlock().accept(this, value); if (hasBuilder(Type.CatchClause)) { return call(Type.CatchClause, node, param, guard, body); } OrdinaryObject catchClause = createNode(node, Type.CatchClause); addProperty(catchClause, "param", param); addProperty(catchClause, "guard", guard); addProperty(catchClause, "body", body); return catchClause; } @Override public Object visit(ClassDeclaration node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); Object superClass = acceptOrNull(node.getHeritage(), value); Object body = createClassBody(node, value); OrdinaryObject classDef = createClass(node, Type.ClassStatement); addProperty(classDef, "id", id); addProperty(classDef, "superClass", superClass); addProperty(classDef, "body", body); return classDef; } @Override public Object visit(ClassExpression node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); Object superClass = acceptOrNull(node.getHeritage(), value); Object body = createClassBody(node, value); OrdinaryObject classDef = createClass(node, Type.ClassExpression); addProperty(classDef, "id", id); addProperty(classDef, "superClass", superClass); addProperty(classDef, "body", body); return classDef; } @Override public Object visit(CommaExpression node, Void value) { ArrayObject expressions = createList(node.getOperands(), value); if (hasBuilder(Type.SequenceExpression)) { return call(Type.SequenceExpression, node, expressions); } OrdinaryObject expression = createExpression(node, Type.SequenceExpression); addProperty(expression, "expressions", expressions); return expression; } @Override public Object visit(Comprehension node, Void value) { // multiple filters possible in Comprehension, single element 'filter' useless here... Object body = node.getExpression().accept(this, value); ArrayObject blocks = createList(node.getList(), value); Object filter = NULL; OrdinaryObject expression = createEmptyNode(); addProperty(expression, "body", body); addProperty(expression, "blocks", blocks); addProperty(expression, "filter", filter); addProperty(expression, "style", "modern"); return expression; } @Override public Object visit(ComprehensionFor node, Void value) { Object left = node.getBinding().accept(this, value); Object right = node.getExpression().accept(this, value); boolean each = false; boolean of = true; if (hasBuilder(Type.ComprehensionBlock)) { return call(Type.ComprehensionBlock, node, left, right, each); } OrdinaryObject comprehensionBlock = createNode(node, Type.ComprehensionBlock); addProperty(comprehensionBlock, "left", left); addProperty(comprehensionBlock, "right", right); addProperty(comprehensionBlock, "each", each); addProperty(comprehensionBlock, "of", of); return comprehensionBlock; } @Override public Object visit(ComprehensionIf node, Void value) { Object test = node.getTest().accept(this, value); if (hasBuilder(Type.ComprehensionBlock)) { return call(Type.ComprehensionBlock, node, test); } OrdinaryObject comprehensionIf = createNode(node, Type.ComprehensionIf); addProperty(comprehensionIf, "test", test); return comprehensionIf; } @Override public Object visit(ComputedPropertyName node, Void value) { Object expr = node.getExpression().accept(this, value); if (hasBuilder(Type.ComputedName)) { return call(Type.ComputedName, node, expr); } OrdinaryObject propertyName = createNode(node, Type.ComputedName); addProperty(propertyName, "name", expr); return propertyName; } @Override public Object visit(ConditionalExpression node, Void value) { Object test = node.getTest().accept(this, value); Object consequent = node.getThen().accept(this, value); Object alternate = node.getOtherwise().accept(this, value); if (hasBuilder(Type.ConditionalExpression)) { return call(Type.ConditionalExpression, node, test, consequent, alternate); } OrdinaryObject expression = createExpression(node, Type.ConditionalExpression); addProperty(expression, "test", test); addProperty(expression, "consequent", consequent); addProperty(expression, "alternate", alternate); return expression; } @Override public Object visit(ContinueStatement node, Void value) { Object label = createIdentifierOrNull(node.getLabel()); if (hasBuilder(Type.ContinueStatement)) { return call(Type.ContinueStatement, node, label); } OrdinaryObject statement = createStatement(node, Type.ContinueStatement); addProperty(statement, "label", label); return statement; } @Override public Object visit(DebuggerStatement node, Void value) { if (hasBuilder(Type.DebuggerStatement)) { return call(Type.DebuggerStatement, node); } return createStatement(node, Type.DebuggerStatement); } @Override public Object visit(DoExpression node, Void value) { OrdinaryObject expression = createExpression(node, Type.DoExpression); addProperty(expression, "statement", node.getStatement().accept(this, value)); return expression; } @Override public Object visit(DoWhileStatement node, Void value) { Object doWhileStatement; Object test = node.getTest().accept(this, value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.DoWhileStatement)) { doWhileStatement = call(Type.DoWhileStatement, node, test, body); } else { OrdinaryObject statement = createStatement(node, Type.DoWhileStatement); addProperty(statement, "test", test); addProperty(statement, "body", body); doWhileStatement = statement; } return createLabelledStatement(node, doWhileStatement); } @Override public Object visit(ElementAccessor node, Void value) { Object object = node.getBase().accept(this, value); Object property = node.getElement().accept(this, value); boolean computed = true; if (hasBuilder(Type.MemberExpression)) { return call(Type.MemberExpression, node, object, property, computed); } OrdinaryObject expression = createExpression(node, Type.MemberExpression); addProperty(expression, "object", object); addProperty(expression, "property", property); addProperty(expression, "computed", computed); return expression; } @Override public Object visit(Elision node, Void value) { return NULL; } @Override public Object visit(EmptyExpression node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(EmptyStatement node, Void value) { if (hasBuilder(Type.EmptyStatement)) { return call(Type.EmptyStatement, node); } return createStatement(node, Type.EmptyStatement); } @Override public Object visit(ExportDeclaration node, Void value) { Object declaration = NULL; Object expression = NULL; Object specifiers = NULL; Object source = NULL; switch (node.getType()) { case All: specifiers = createListFromValues(Collections.singletonList(createNode(Type.ExportBatchSpecifier))); source = createLiteral(node.getModuleSpecifier()); break; case External: // TODO: default entry and namespace export specifiers = node.getExportClause().accept(this, value); source = createLiteral(node.getModuleSpecifier()); break; case Local: specifiers = node.getExportClause().accept(this, value); break; case Variable: declaration = node.getVariableStatement().accept(this, value); break; case Declaration: declaration = node.getDeclaration().accept(this, value); break; case DefaultHoistableDeclaration: declaration = node.getHoistableDeclaration().accept(this, value); break; case DefaultClassDeclaration: declaration = node.getClassDeclaration().accept(this, value); break; case DefaultExpression: expression = node.getExpression().accept(this, value); break; default: throw new AssertionError(); } OrdinaryObject exportDecl = createModuleItem(node, Type.ExportDeclaration); addProperty(exportDecl, "declaration", declaration); addProperty(exportDecl, "expression", expression); addProperty(exportDecl, "specifiers", specifiers); addProperty(exportDecl, "source", source); return exportDecl; } @Override public Object visit(ExportDefaultExpression node, Void value) { return node.getExpression().accept(this, value); } @Override public Object visit(ExportSpecifier node, Void value) { Object id = createIdentifier(node.getSourceName()); Object name = createIdentifier(node.getExportName()); OrdinaryObject exportSpec = createNode(node, Type.ExportSpecifier); addProperty(exportSpec, "id", id); addProperty(exportSpec, "name", name); return exportSpec; } @Override public Object visit(ExportClause node, Void value) { return createList(node.getExports(), value); } @Override public Object visit(ExpressionMethod node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(ExpressionStatement node, Void value) { Object expression = node.getExpression().accept(this, value); if (hasBuilder(Type.ExpressionStatement)) { return call(Type.ExpressionStatement, node, expression); } OrdinaryObject statement = createStatement(node, Type.ExpressionStatement); addProperty(statement, "expression", expression); return statement; } @Override public Object visit(ForAwaitStatement node, Void value) { Object forAwaitStatement; Object left = node.getHead().accept(this, value); Object right = node.getExpression().accept(this, value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.ForAwaitStatement)) { forAwaitStatement = call(Type.ForAwaitStatement, node, left, right, body); } else { OrdinaryObject statement = createStatement(node, Type.ForAwaitStatement); addProperty(statement, "left", left); addProperty(statement, "right", right); addProperty(statement, "body", body); forAwaitStatement = statement; } return createLabelledStatement(node, forAwaitStatement); } @Override public Object visit(ForEachStatement node, Void value) { Object forEachStatement; Object left = node.getHead().accept(this, value); Object right = node.getExpression().accept(this, value); Object body = node.getStatement().accept(this, value); boolean each = true; if (hasBuilder(Type.ForInStatement)) { forEachStatement = call(Type.ForInStatement, node, left, right, body, each); } else { OrdinaryObject statement = createStatement(node, Type.ForInStatement); addProperty(statement, "left", left); addProperty(statement, "right", right); addProperty(statement, "body", body); addProperty(statement, "each", each); forEachStatement = statement; } return createLabelledStatement(node, forEachStatement); } @Override public Object visit(ForInStatement node, Void value) { Object forInStatement; Object left = node.getHead().accept(this, value); Object right = node.getExpression().accept(this, value); Object body = node.getStatement().accept(this, value); boolean each = false; if (hasBuilder(Type.ForInStatement)) { forInStatement = call(Type.ForInStatement, node, left, right, body, each); } else { OrdinaryObject statement = createStatement(node, Type.ForInStatement); addProperty(statement, "left", left); addProperty(statement, "right", right); addProperty(statement, "body", body); addProperty(statement, "each", each); forInStatement = statement; } return createLabelledStatement(node, forInStatement); } @Override public Object visit(FormalParameter node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(FormalParameterList node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(ForOfStatement node, Void value) { Object forOfStatement; Object left = node.getHead().accept(this, value); Object right = node.getExpression().accept(this, value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.ForOfStatement)) { forOfStatement = call(Type.ForOfStatement, node, left, right, body); } else { OrdinaryObject statement = createStatement(node, Type.ForOfStatement); addProperty(statement, "left", left); addProperty(statement, "right", right); addProperty(statement, "body", body); forOfStatement = statement; } return createLabelledStatement(node, forOfStatement); } @Override public Object visit(ForStatement node, Void value) { Object forStatement; Object init = acceptOrNull(node.getHead(), value); Object test = acceptOrNull(node.getTest(), value); Object update = acceptOrNull(node.getStep(), value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.ForStatement)) { forStatement = call(Type.ForStatement, node, init, test, update, body); } else { OrdinaryObject statement = createStatement(node, Type.ForStatement); addProperty(statement, "init", init); addProperty(statement, "test", test); addProperty(statement, "update", update); addProperty(statement, "body", body); forStatement = statement; } return createLabelledStatement(node, forStatement); } @Override public Object visit(FunctionDeclaration node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); boolean generator = false; boolean expression = false; if (hasBuilder(Type.FunctionDeclaration)) { return call(Type.FunctionDeclaration, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionDeclaration); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(FunctionExpression node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); boolean generator = false; boolean expression = false; if (hasBuilder(Type.FunctionExpression)) { return call(Type.FunctionExpression, node, id, params, body, generator, expression); } OrdinaryObject function = createFunction(node, Type.FunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "expression", expression); return function; } @Override public Object visit(FunctionSent node, Void value) { // TODO: Attach location information. Object meta = createIdentifier("function"); Object property = createIdentifier("sent"); if (hasBuilder(Type.MetaProperty)) { return call(Type.MetaProperty, node, meta, property); } OrdinaryObject expression = createExpression(node, Type.MetaProperty); addProperty(expression, "meta", meta); addProperty(expression, "property", property); return expression; } @Override public Object visit(GeneratorComprehension node, Void value) { // Comprehension/LegacyComprehension already created a partial result OrdinaryObject expression = (OrdinaryObject) node.getComprehension().accept(this, value); if (hasBuilder(Type.GeneratorExpression)) { Object body = Get(cx, expression, "body"); Object blocks = Get(cx, expression, "blocks"); Object filter = Get(cx, expression, "filter"); return call(Type.GeneratorExpression, node, body, blocks, filter); } addNodeInfo(expression, node, Type.GeneratorExpression); return expression; } @Override public Object visit(GeneratorDeclaration node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); boolean generator = true; boolean expression = false; if (hasBuilder(Type.FunctionDeclaration)) { return call(Type.FunctionDeclaration, node, id, params, body, generator, expression); } GeneratorStyle style = node instanceof LegacyGeneratorDeclaration ? GeneratorStyle.Legacy : GeneratorStyle.ES6; OrdinaryObject function = createFunction(node, Type.FunctionDeclaration); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "style", style.name); addProperty(function, "expression", expression); return function; } @Override public Object visit(GeneratorExpression node, Void value) { Object id = acceptOrNull(node.getIdentifier(), value); ArrayObject params = createList(getParameterBindings(node.getParameters()), value); ArrayObject defaults = createListWithNull(getParameterDefaults(node.getParameters()), value); Object rest = acceptOrNull(getRestParameter(node.getParameters()), value); Object body = createFunctionBody(node, value); boolean generator = true; boolean expression = false; if (hasBuilder(Type.FunctionExpression)) { return call(Type.FunctionExpression, node, id, params, body, generator, expression); } GeneratorStyle style = node instanceof LegacyGeneratorExpression ? GeneratorStyle.Legacy : GeneratorStyle.ES6; OrdinaryObject function = createFunction(node, Type.FunctionExpression); addProperty(function, "id", id); addProperty(function, "params", params); addProperty(function, "defaults", defaults); addProperty(function, "body", body); addProperty(function, "rest", rest); addProperty(function, "generator", generator); addProperty(function, "style", style.name); addProperty(function, "expression", expression); return function; } @Override public Object visit(GuardedCatchNode node, Void value) { Object param = node.getCatchParameter().accept(this, value); Object guard = node.getGuard().accept(this, value); Object body = node.getCatchBlock().accept(this, value); if (hasBuilder(Type.CatchClause)) { return call(Type.CatchClause, node, param, guard, body); } OrdinaryObject catchClause = createNode(node, Type.CatchClause); addProperty(catchClause, "param", param); addProperty(catchClause, "guard", guard); addProperty(catchClause, "body", body); return catchClause; } @Override public Object visit(IdentifierName node, Void value) { String name = node.getName(); if (hasBuilder(Type.Identifier)) { return call(Type.Identifier, node, name); } OrdinaryObject expression = createNode(node, Type.Identifier); addProperty(expression, "name", name); return expression; } @Override public Object visit(IdentifierReference node, Void value) { String name = node.getName(); if (hasBuilder(Type.Identifier)) { return call(Type.Identifier, node, name); } OrdinaryObject expression = createExpression(node, Type.Identifier); addProperty(expression, "name", name); return expression; } @Override public Object visit(IfStatement node, Void value) { Object test = node.getTest().accept(this, value); Object consequent = node.getThen().accept(this, value); Object alternate = acceptOrNull(node.getOtherwise(), value); if (hasBuilder(Type.IfStatement)) { return call(Type.IfStatement, node, test, consequent, alternate); } OrdinaryObject statement = createStatement(node, Type.IfStatement); addProperty(statement, "test", test); addProperty(statement, "consequent", consequent); addProperty(statement, "alternate", alternate); return statement; } @Override public Object visit(ImportDeclaration node, Void value) { Object specifiers = NULL; Object source = NULL; switch (node.getType()) { case ImportFrom: specifiers = node.getImportClause().accept(this, value); source = createLiteral(node.getModuleSpecifier()); break; case ImportModule: specifiers = createList(Collections.<Node> emptyList(), value); source = createLiteral(node.getModuleSpecifier()); break; default: throw new AssertionError(); } OrdinaryObject importDecl = createModuleItem(node, Type.ImportDeclaration); addProperty(importDecl, "specifiers", specifiers); addProperty(importDecl, "source", source); return importDecl; } @Override public Object visit(ImportClause node, Void value) { ArrayList<Object> specifiers = new ArrayList<>(); if (node.getDefaultEntry() != null) { specifiers.add(createImportSpecifier(node, "default", node.getDefaultEntry(), value)); } for (ImportSpecifier specifier : node.getNamedImports()) { specifiers.add(specifier.accept(this, value)); } return createListFromValues(specifiers); } @Override public Object visit(ImportSpecifier node, Void value) { return createImportSpecifier(node, node.getImportName(), node.getLocalName(), value); } private OrdinaryObject createImportSpecifier(Node node, String importName, BindingIdentifier localName, Void value) { Object id = createIdentifier(importName); Object name = localName.accept(this, value); OrdinaryObject importSpec = createNode(node, Type.ImportSpecifier); addProperty(importSpec, "id", id); addProperty(importSpec, "name", name); return importSpec; } @Override public Object visit(SpreadArrayLiteral node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(SpreadElementMethod node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(PropertyDefinitionsMethod node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(LabelledFunctionStatement node, Void value) { Object body = node.getFunction().accept(this, value); return createLabelledStatement(node, body); } @Override public Object visit(LabelledStatement node, Void value) { Object body = node.getStatement().accept(this, value); return createLabelledStatement(node, body); } @Override public Object visit(LegacyComprehension node, Void value) { // Extract the single if-qualifier, if present Expression test = null; List<ComprehensionQualifier> qualifiers = node.getList(); ComprehensionQualifier last = lastElement(qualifiers); if (last instanceof ComprehensionIf) { test = ((ComprehensionIf) last).getTest(); qualifiers = qualifiers.subList(0, qualifiers.size() - 1); } Object body = node.getExpression().accept(this, value); ArrayObject blocks = createList(qualifiers, value); Object filter = acceptOrNull(test, value); OrdinaryObject expression = createEmptyNode(); addProperty(expression, "body", body); addProperty(expression, "blocks", blocks); addProperty(expression, "filter", filter); addProperty(expression, "style", "legacy"); return expression; } @Override public Object visit(LegacyComprehensionFor node, Void value) { Object left = node.getBinding().accept(this, value); Object right = node.getExpression().accept(this, value); boolean each = node.getIterationKind() == LegacyComprehensionFor.IterationKind.EnumerateValues; boolean of = node.getIterationKind() == LegacyComprehensionFor.IterationKind.Iterate; if (hasBuilder(Type.ComprehensionBlock)) { return call(Type.ComprehensionBlock, node, left, right, each); } OrdinaryObject comprehensionBlock = createNode(node, Type.ComprehensionBlock); addProperty(comprehensionBlock, "left", left); addProperty(comprehensionBlock, "right", right); addProperty(comprehensionBlock, "each", each); addProperty(comprehensionBlock, "of", of); return comprehensionBlock; } @Override public Object visit(LegacyGeneratorDeclaration node, Void value) { return visit((GeneratorDeclaration) node, value); } @Override public Object visit(LegacyGeneratorExpression node, Void value) { return visit((GeneratorExpression) node, value); } @Override public Object visit(LetExpression node, Void value) { ArrayObject head = createList(node.getBindings(), value); Object body = node.getExpression().accept(this, value); if (hasBuilder(Type.LetExpression)) { return call(Type.LetExpression, node, head, body); } OrdinaryObject expression = createExpression(node, Type.LetExpression); addProperty(expression, "head", head); addProperty(expression, "body", body); return expression; } @Override public Object visit(LetStatement node, Void value) { ArrayObject head = createList(node.getBindings(), value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.LetStatement)) { return call(Type.LetStatement, node, head, body); } OrdinaryObject statement = createStatement(node, Type.LetStatement); addProperty(statement, "head", head); addProperty(statement, "body", body); return statement; } @Override public Object visit(LexicalBinding node, Void value) { Object id = node.getBinding().accept(this, value); Object init = acceptOrNull(node.getInitializer(), value); if (hasBuilder(Type.VariableDeclarator)) { return call(Type.VariableDeclarator, node, id, init); } OrdinaryObject declarator = createNode(node, Type.VariableDeclarator); addProperty(declarator, "id", id); addProperty(declarator, "init", init); return declarator; } @Override public Object visit(LexicalDeclaration node, Void value) { ArrayObject declarations = createList(node.getElements(), value); String kind = node.isConstDeclaration() ? "const" : "let"; if (hasBuilder(Type.VariableDeclaration)) { return call(Type.VariableDeclaration, node, kind, declarations); } OrdinaryObject declaration = createDeclaration(node, Type.VariableDeclaration); addProperty(declaration, "declarations", declarations); addProperty(declaration, "kind", kind); return declaration; } @Override public Object visit(MethodDefinition node, Void value) { Object key = node.getPropertyName().accept(this, value); Object _value = toFunctionExpression(node, value); String kind = methodKind(node, "init"); boolean method = !isGetterOrSetter(node); boolean shorthand = false; boolean computed = node.getPropertyName() instanceof ComputedPropertyName; if (hasBuilder(Type.Property)) { return call(Type.Property, node, kind, key, _value); } OrdinaryObject property = createNode(node, Type.Property); addProperty(property, "key", key); addProperty(property, "value", _value); addProperty(property, "kind", kind); addProperty(property, "method", method); addProperty(property, "shorthand", shorthand); addProperty(property, "computed", computed); return property; } @Override public Object visit(MethodDefinitionsMethod node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(Module node, Void value) { ArrayObject body = createList(node.getStatements(), value); if (hasBuilder(Type.Program)) { return call(Type.Program, node, body); } OrdinaryObject program = createNode(node, Type.Program); addProperty(program, "body", body); addProperty(program, "sourceType", "module"); return program; } @Override public Object visit(NativeCallExpression node, Void value) { Object callee = node.getBase().accept(this, value); ArrayObject arguments = createList(node.getArguments(), value); if (hasBuilder(Type.CallExpression)) { return call(Type.CallExpression, node, callee, arguments); } OrdinaryObject expression = createExpression(node, Type.CallExpression); addProperty(expression, "callee", callee); addProperty(expression, "arguments", arguments); addProperty(expression, "native", true); return expression; } @Override public Object visit(NewExpression node, Void value) { Object callee = node.getExpression().accept(this, value); ArrayObject arguments = createList(node.getArguments(), value); if (hasBuilder(Type.NewExpression)) { return call(Type.NewExpression, node, callee, arguments); } OrdinaryObject expression = createExpression(node, Type.NewExpression); addProperty(expression, "callee", callee); addProperty(expression, "arguments", arguments); return expression; } @Override public Object visit(NewTarget node, Void value) { // TODO: Attach location information. Object meta = createIdentifier("new"); Object property = createIdentifier("target"); if (hasBuilder(Type.MetaProperty)) { return call(Type.MetaProperty, node, meta, property); } OrdinaryObject expression = createExpression(node, Type.MetaProperty); addProperty(expression, "meta", meta); addProperty(expression, "property", property); return expression; } @Override public Object visit(NullLiteral node, Void value) { return createLiteral(node, NULL); } @Override public Object visit(NumericLiteral node, Void value) { return createLiteral(node); } @Override public Object visit(ObjectAssignmentPattern node, Void value) { ArrayObject properties = createList(node.getProperties(), value); // TODO: rest property if (hasBuilder(Type.ObjectPattern)) { return call(Type.ObjectPattern, node, properties); } OrdinaryObject pattern = createPattern(node, Type.ObjectPattern); addProperty(pattern, "properties", properties); return pattern; } @Override public Object visit(ObjectBindingPattern node, Void value) { ArrayObject properties = createList(node.getProperties(), value); // TODO: rest property if (hasBuilder(Type.ObjectPattern)) { return call(Type.ObjectPattern, node, properties); } OrdinaryObject pattern = createPattern(node, Type.ObjectPattern); addProperty(pattern, "properties", properties); return pattern; } @Override public Object visit(ObjectLiteral node, Void value) { ArrayObject properties = createList(node.getProperties(), value); if (hasBuilder(Type.ObjectExpression)) { return call(Type.ObjectExpression, node, properties); } OrdinaryObject expression = createExpression(node, Type.ObjectExpression); addProperty(expression, "properties", properties); return expression; } @Override public Object visit(PropertyAccessor node, Void value) { Object object = node.getBase().accept(this, value); Object property = createIdentifier(node.getName()); boolean computed = false; if (hasBuilder(Type.MemberExpression)) { return call(Type.MemberExpression, node, object, property, computed); } OrdinaryObject expression = createExpression(node, Type.MemberExpression); addProperty(expression, "object", object); addProperty(expression, "property", property); addProperty(expression, "computed", computed); return expression; } @Override public Object visit(PropertyNameDefinition node, Void value) { Object key = node.getPropertyName().accept(this, value); Object _value = key; String kind = "init"; boolean method = false; boolean shorthand = true; boolean computed = false; if (hasBuilder(Type.Property)) { return call(Type.Property, node, kind, key, _value); } OrdinaryObject property = createNode(node, Type.Property); addProperty(property, "key", key); addProperty(property, "value", _value); addProperty(property, "kind", kind); addProperty(property, "method", method); addProperty(property, "shorthand", shorthand); addProperty(property, "computed", computed); return property; } @Override public Object visit(PropertyValueDefinition node, Void value) { String propertyName = node.getPropertyName().getName(); if ("__proto__".equals(propertyName)) { Object _value = node.getPropertyValue().accept(this, value); if (hasBuilder(Type.PrototypeMutation)) { return call(Type.PrototypeMutation, node, _value); } OrdinaryObject property = createNode(node, Type.PrototypeMutation); addProperty(property, "value", _value); return property; } Object key = node.getPropertyName().accept(this, value); Object _value = node.getPropertyValue().accept(this, value); String kind = "init"; boolean method = false; boolean shorthand = false; boolean computed = node.getPropertyName() instanceof ComputedPropertyName; if (hasBuilder(Type.Property)) { return call(Type.Property, node, kind, key, _value); } OrdinaryObject property = createNode(node, Type.Property); addProperty(property, "key", key); addProperty(property, "value", _value); addProperty(property, "kind", kind); addProperty(property, "method", method); addProperty(property, "shorthand", shorthand); addProperty(property, "computed", computed); return property; } @Override public Object visit(RegularExpressionLiteral node, Void value) { RegExpObject _value = RegExpCreate(cx, node.getRegexp(), node.getFlags()); if (hasBuilder(Type.Literal)) { return call(Type.Literal, node, _value); } OrdinaryObject expression = createExpression(node, Type.Literal); addProperty(expression, "value", _value); return expression; } @Override public Object visit(ReturnStatement node, Void value) { Object argument = acceptOrNull(node.getExpression(), value); if (hasBuilder(Type.ReturnStatement)) { return call(Type.ReturnStatement, node, argument); } OrdinaryObject statement = createStatement(node, Type.ReturnStatement); addProperty(statement, "argument", argument); return statement; } @Override public Object visit(Script node, Void value) { ArrayObject body = createList(node.getStatements(), value); if (hasBuilder(Type.Program)) { return call(Type.Program, node, body); } OrdinaryObject program = createNode(node, Type.Program); addProperty(program, "body", body); addProperty(program, "sourceType", "script"); return program; } @Override public Object visit(SpreadElement node, Void value) { Object expr = node.getExpression().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createExpression(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(SpreadProperty node, Void value) { Object expr = node.getExpression().accept(this, value); if (hasBuilder(Type.SpreadExpression)) { return call(Type.SpreadExpression, node, expr); } OrdinaryObject expression = createNode(node, Type.SpreadExpression); addProperty(expression, "expression", expr); return expression; } @Override public Object visit(StatementListMethod node, Void value) { throw new IllegalStateException(node.getClass().toString()); } @Override public Object visit(StringLiteral node, Void value) { return createLiteral(node); } @Override public Object visit(SuperCallExpression node, Void value) { Object callee = superNode(node); ArrayObject arguments = createList(node.getArguments(), value); if (hasBuilder(Type.CallExpression)) { return call(Type.CallExpression, node, callee, arguments); } OrdinaryObject expression = createExpression(node, Type.CallExpression); addProperty(expression, "callee", callee); addProperty(expression, "arguments", arguments); return expression; } @Override public Object visit(SuperElementAccessor node, Void value) { Object object = superNode(node); Object property = node.getElement().accept(this, value); boolean computed = true; if (hasBuilder(Type.MemberExpression)) { return call(Type.MemberExpression, node, object, property, computed); } OrdinaryObject expression = createExpression(node, Type.MemberExpression); addProperty(expression, "object", object); addProperty(expression, "property", property); addProperty(expression, "computed", computed); return expression; } @Override public Object visit(SuperNewExpression node, Void value) { Object callee = superNode(node); ArrayObject arguments = createList(node.getArguments(), value); if (hasBuilder(Type.NewExpression)) { return call(Type.NewExpression, node, callee, arguments); } OrdinaryObject expression = createExpression(node, Type.NewExpression); addProperty(expression, "callee", callee); addProperty(expression, "arguments", arguments); return expression; } @Override public Object visit(SuperPropertyAccessor node, Void value) { Object object = superNode(node); Object property = createIdentifier(node.getName()); boolean computed = false; if (hasBuilder(Type.MemberExpression)) { return call(Type.MemberExpression, node, object, property, computed); } OrdinaryObject expression = createExpression(node, Type.MemberExpression); addProperty(expression, "object", object); addProperty(expression, "property", property); addProperty(expression, "computed", computed); return expression; } private Object superNode(Node node) { // TODO: Attach correct source location. if (hasBuilder(Type.Super)) { return call(Type.Super, node); } return createNode(node, Type.Super); } @Override public Object visit(SwitchClause node, Void value) { Object test = acceptOrNull(node.getExpression(), value); ArrayObject consequent = createList(node.getStatements(), value); if (hasBuilder(Type.SwitchCase)) { return call(Type.SwitchCase, node, test, consequent); } OrdinaryObject switchCase = createNode(node, Type.SwitchCase); addProperty(switchCase, "test", test); addProperty(switchCase, "consequent", consequent); return switchCase; } @Override public Object visit(SwitchStatement node, Void value) { Object switchStatement; Object discriminant = node.getExpression().accept(this, value); ArrayObject cases = createList(node.getClauses(), value); boolean lexical = !LexicallyScopedDeclarations(node).isEmpty(); if (hasBuilder(Type.SwitchStatement)) { switchStatement = call(Type.SwitchStatement, node, discriminant, cases, lexical); } else { OrdinaryObject statement = createStatement(node, Type.SwitchStatement); addProperty(statement, "discriminant", discriminant); addProperty(statement, "cases", cases); addProperty(statement, "lexical", lexical); switchStatement = statement; } return createLabelledStatement(node, switchStatement); } @Override public Object visit(TemplateCallExpression node, Void value) { Object callee = node.getBase().accept(this, value); Object arguments = node.getTemplate().accept(this, value); if (hasBuilder(Type.TaggedTemplate)) { return call(Type.TaggedTemplate, node, callee, arguments); } OrdinaryObject expression = createExpression(node, Type.TaggedTemplate); addProperty(expression, "callee", callee); addProperty(expression, "arguments", arguments); return expression; } @Override public Object visit(TemplateCharacters node, Void value) { String raw = node.getRawValue(); String cooked = node.getValue(); if (hasBuilder(Type.TemplateLiteral)) { return call(Type.TemplateLiteral, node, raw, cooked); } OrdinaryObject expression = createExpression(node, Type.TemplateLiteral); addProperty(expression, "raw", raw); addProperty(expression, "cooked", cooked); return expression; } @Override public Object visit(TemplateLiteral node, Void value) { if (!node.isTagged()) { ArrayList<Object> list = new ArrayList<>(); for (Expression element : node.getElements()) { if (element instanceof TemplateCharacters) { list.add(createLiteral((TemplateCharacters) element)); } else { list.add(element.accept(this, value)); } } if (list.size() == 1) { return list.get(0); } ArrayObject elements = createListFromValues(list); if (hasBuilder(Type.TemplateLiteral)) { return call(Type.TemplateLiteral, node, elements); } OrdinaryObject expression = createExpression(node, Type.TemplateLiteral); addProperty(expression, "elements", elements); return expression; } else { ArrayList<String> rawList = new ArrayList<>(), cookedList = new ArrayList<>(); for (TemplateCharacters chars : TemplateStrings(node)) { rawList.add(chars.getRawValue()); cookedList.add(chars.getValue()); } Object callSiteObject; ArrayObject raw = createListFromValues(rawList); ArrayObject cooked = createListFromValues(cookedList); if (hasBuilder(Type.CallSiteObject)) { callSiteObject = call(Type.CallSiteObject, node, raw, cooked); } else { OrdinaryObject callSiteObj = createExpression(node, Type.CallSiteObject); addProperty(callSiteObj, "raw", raw); addProperty(callSiteObj, "cooked", cooked); callSiteObject = callSiteObj; } ArrayList<Object> arguments = new ArrayList<>(); arguments.add(callSiteObject); for (Expression subst : Substitutions(node)) { arguments.add(subst.accept(this, value)); } return createListFromValues(arguments); } } @Override public Object visit(ThisExpression node, Void value) { if (hasBuilder(Type.ThisExpression)) { return call(Type.ThisExpression, node); } return createExpression(node, Type.ThisExpression); } @Override public Object visit(ThrowStatement node, Void value) { Object argument = node.getExpression().accept(this, value); if (hasBuilder(Type.ThrowStatement)) { return call(Type.ThrowStatement, node, argument); } OrdinaryObject statement = createStatement(node, Type.ThrowStatement); addProperty(statement, "argument", argument); return statement; } @Override public Object visit(TryStatement node, Void value) { Object block = node.getTryBlock().accept(this, value); Object handler = acceptOrNull(node.getCatchNode(), value); ArrayObject guardedHandlers = createList(node.getGuardedCatchNodes(), value); Object finalizer = acceptOrNull(node.getFinallyBlock(), value); if (hasBuilder(Type.TryStatement)) { return call(Type.TryStatement, node, block, guardedHandlers, handler, finalizer); } OrdinaryObject statement = createStatement(node, Type.TryStatement); addProperty(statement, "block", block); addProperty(statement, "handler", handler); addProperty(statement, "guardedHandlers", guardedHandlers); addProperty(statement, "finalizer", finalizer); return statement; } @Override public Object visit(UnaryExpression node, Void value) { Object argument = node.getOperand().accept(this, value); String operator = node.getOperator().getName(); boolean prefix = !node.getOperator().isPostfix(); if (hasBuilder(type(node))) { return call(type(node), node, operator, argument, prefix); } OrdinaryObject expression = createExpression(node, type(node)); addProperty(expression, "argument", argument); addProperty(expression, "operator", operator); addProperty(expression, "prefix", prefix); return expression; } private static Type type(UnaryExpression node) { switch (node.getOperator()) { case POST_DEC: case POST_INC: case PRE_DEC: case PRE_INC: return Type.UpdateExpression; default: return Type.UnaryExpression; } } @Override public Object visit(VariableDeclaration node, Void value) { Object id = node.getBinding().accept(this, value); Object init = acceptOrNull(node.getInitializer(), value); if (hasBuilder(Type.VariableDeclarator)) { return call(Type.VariableDeclarator, node, id, init); } OrdinaryObject declarator = createNode(node, Type.VariableDeclarator); addProperty(declarator, "id", id); addProperty(declarator, "init", init); return declarator; } @Override public Object visit(VariableStatement node, Void value) { ArrayObject declarations = createList(node.getElements(), value); String kind = "var"; if (hasBuilder(Type.VariableDeclaration)) { return call(Type.VariableDeclaration, node, kind, declarations); } OrdinaryObject declaration = createDeclaration(node, Type.VariableDeclaration); addProperty(declaration, "declarations", declarations); addProperty(declaration, "kind", kind); return declaration; } @Override public Object visit(WhileStatement node, Void value) { Object whileStatement; Object test = node.getTest().accept(this, value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.WhileStatement)) { whileStatement = call(Type.WhileStatement, node, test, body); } else { OrdinaryObject statement = createStatement(node, Type.WhileStatement); addProperty(statement, "test", test); addProperty(statement, "body", body); whileStatement = statement; } return createLabelledStatement(node, whileStatement); } @Override public Object visit(WithStatement node, Void value) { Object object = node.getExpression().accept(this, value); Object body = node.getStatement().accept(this, value); if (hasBuilder(Type.WithStatement)) { return call(Type.WithStatement, node, object, body); } OrdinaryObject statement = createStatement(node, Type.WithStatement); addProperty(statement, "object", object); addProperty(statement, "body", body); return statement; } @Override public Object visit(YieldExpression node, Void value) { Object argument = acceptOrNull(node.getExpression(), value); boolean delegate = node.isDelegatedYield(); if (hasBuilder(Type.YieldExpression)) { return call(Type.YieldExpression, node, argument, delegate); } OrdinaryObject expression = createExpression(node, Type.YieldExpression); addProperty(expression, "argument", argument); addProperty(expression, "delegate", delegate); return expression; } }