| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // @dart = 2.9 |
| |
| // ignore_for_file: slash_for_doc_comments, prefer_single_quotes |
| // ignore_for_file: always_declare_return_types, prefer_final_fields |
| // ignore_for_file: always_require_non_null_named_parameters |
| // ignore_for_file: omit_local_variable_types, unnecessary_this |
| |
| part of js_ast; |
| |
| abstract class NodeVisitor<T> { |
| T visitProgram(Program node); |
| |
| T visitBlock(Block node); |
| T visitDebuggerStatement(DebuggerStatement node); |
| T visitExpressionStatement(ExpressionStatement node); |
| T visitEmptyStatement(EmptyStatement node); |
| T visitIf(If node); |
| T visitFor(For node); |
| T visitForIn(ForIn node); |
| T visitForOf(ForOf node); |
| T visitWhile(While node); |
| T visitDo(Do node); |
| T visitContinue(Continue node); |
| T visitBreak(Break node); |
| T visitReturn(Return node); |
| T visitThrow(Throw node); |
| T visitTry(Try node); |
| T visitCatch(Catch node); |
| T visitSwitch(Switch node); |
| T visitSwitchCase(SwitchCase node); |
| T visitFunctionDeclaration(FunctionDeclaration node); |
| T visitLabeledStatement(LabeledStatement node); |
| T visitLiteralStatement(LiteralStatement node); |
| T visitDartYield(DartYield node); |
| |
| T visitLiteralExpression(LiteralExpression node); |
| T visitVariableDeclarationList(VariableDeclarationList node); |
| T visitAssignment(Assignment node); |
| T visitVariableInitialization(VariableInitialization node); |
| T visitConditional(Conditional cond); |
| T visitNew(New node); |
| T visitCall(Call node); |
| T visitBinary(Binary node); |
| T visitPrefix(Prefix node); |
| T visitPostfix(Postfix node); |
| T visitSpread(Spread node); |
| T visitYield(Yield node); |
| |
| T visitIdentifier(Identifier node); |
| T visitThis(This node); |
| T visitSuper(Super node); |
| T visitAccess(PropertyAccess node); |
| T visitRestParameter(RestParameter node); |
| |
| T visitNamedFunction(NamedFunction node); |
| T visitFun(Fun node); |
| T visitArrowFun(ArrowFun node); |
| |
| T visitLiteralBool(LiteralBool node); |
| T visitLiteralString(LiteralString node); |
| T visitLiteralNumber(LiteralNumber node); |
| T visitLiteralNull(LiteralNull node); |
| |
| T visitArrayInitializer(ArrayInitializer node); |
| T visitArrayHole(ArrayHole node); |
| T visitObjectInitializer(ObjectInitializer node); |
| T visitProperty(Property node); |
| T visitRegExpLiteral(RegExpLiteral node); |
| T visitTemplateString(TemplateString node); |
| T visitTaggedTemplate(TaggedTemplate node); |
| |
| T visitAwait(Await node); |
| |
| T visitClassDeclaration(ClassDeclaration node); |
| T visitClassExpression(ClassExpression node); |
| T visitMethod(Method node); |
| |
| T visitImportDeclaration(ImportDeclaration node); |
| T visitExportDeclaration(ExportDeclaration node); |
| T visitExportClause(ExportClause node); |
| T visitNameSpecifier(NameSpecifier node); |
| T visitModule(Module node); |
| |
| T visitComment(Comment node); |
| T visitCommentExpression(CommentExpression node); |
| |
| T visitInterpolatedExpression(InterpolatedExpression node); |
| T visitInterpolatedLiteral(InterpolatedLiteral node); |
| T visitInterpolatedParameter(InterpolatedParameter node); |
| T visitInterpolatedSelector(InterpolatedSelector node); |
| T visitInterpolatedStatement(InterpolatedStatement node); |
| T visitInterpolatedMethod(InterpolatedMethod node); |
| T visitInterpolatedIdentifier(InterpolatedIdentifier node); |
| |
| T visitArrayBindingPattern(ArrayBindingPattern node); |
| T visitObjectBindingPattern(ObjectBindingPattern node); |
| T visitDestructuredVariable(DestructuredVariable node); |
| T visitSimpleBindingPattern(SimpleBindingPattern node); |
| } |
| |
| class BaseVisitor<T> implements NodeVisitor<T> { |
| T visitNode(Node node) { |
| node.visitChildren(this); |
| return null; |
| } |
| |
| @override |
| T visitProgram(Program node) => visitNode(node); |
| |
| T visitStatement(Statement node) => visitModuleItem(node); |
| T visitLoop(Loop node) => visitStatement(node); |
| T visitJump(Statement node) => visitStatement(node); |
| |
| @override |
| T visitBlock(Block node) => visitStatement(node); |
| @override |
| T visitDebuggerStatement(node) => visitStatement(node); |
| @override |
| T visitExpressionStatement(ExpressionStatement node) => visitStatement(node); |
| @override |
| T visitEmptyStatement(EmptyStatement node) => visitStatement(node); |
| @override |
| T visitIf(If node) => visitStatement(node); |
| @override |
| T visitFor(For node) => visitLoop(node); |
| @override |
| T visitForIn(ForIn node) => visitLoop(node); |
| @override |
| T visitForOf(ForOf node) => visitLoop(node); |
| @override |
| T visitWhile(While node) => visitLoop(node); |
| @override |
| T visitDo(Do node) => visitLoop(node); |
| @override |
| T visitContinue(Continue node) => visitJump(node); |
| @override |
| T visitBreak(Break node) => visitJump(node); |
| @override |
| T visitReturn(Return node) => visitJump(node); |
| @override |
| T visitThrow(Throw node) => visitJump(node); |
| @override |
| T visitTry(Try node) => visitStatement(node); |
| @override |
| T visitSwitch(Switch node) => visitStatement(node); |
| @override |
| T visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node); |
| @override |
| T visitLabeledStatement(LabeledStatement node) => visitStatement(node); |
| @override |
| T visitLiteralStatement(LiteralStatement node) => visitStatement(node); |
| |
| @override |
| T visitCatch(Catch node) => visitNode(node); |
| @override |
| T visitSwitchCase(SwitchCase node) => visitNode(node); |
| |
| T visitExpression(Expression node) => visitNode(node); |
| |
| @override |
| T visitLiteralExpression(LiteralExpression node) => visitExpression(node); |
| @override |
| T visitVariableDeclarationList(VariableDeclarationList node) => |
| visitExpression(node); |
| @override |
| T visitAssignment(Assignment node) => visitExpression(node); |
| @override |
| T visitVariableInitialization(VariableInitialization node) => |
| visitExpression(node); |
| |
| @override |
| T visitConditional(Conditional node) => visitExpression(node); |
| @override |
| T visitNew(New node) => visitExpression(node); |
| @override |
| T visitCall(Call node) => visitExpression(node); |
| @override |
| T visitBinary(Binary node) => visitExpression(node); |
| @override |
| T visitPrefix(Prefix node) => visitExpression(node); |
| @override |
| T visitPostfix(Postfix node) => visitExpression(node); |
| @override |
| T visitSpread(Spread node) => visitPrefix(node); |
| @override |
| T visitYield(Yield node) => visitExpression(node); |
| @override |
| T visitAccess(PropertyAccess node) => visitExpression(node); |
| |
| @override |
| T visitIdentifier(Identifier node) => visitExpression(node); |
| @override |
| T visitThis(This node) => visitExpression(node); |
| @override |
| T visitSuper(Super node) => visitExpression(node); |
| |
| @override |
| T visitRestParameter(RestParameter node) => visitNode(node); |
| |
| @override |
| T visitNamedFunction(NamedFunction node) => visitExpression(node); |
| T visitFunctionExpression(FunctionExpression node) => visitExpression(node); |
| @override |
| T visitFun(Fun node) => visitFunctionExpression(node); |
| @override |
| T visitArrowFun(ArrowFun node) => visitFunctionExpression(node); |
| |
| T visitLiteral(Literal node) => visitExpression(node); |
| |
| @override |
| T visitLiteralBool(LiteralBool node) => visitLiteral(node); |
| @override |
| T visitLiteralString(LiteralString node) => visitLiteral(node); |
| @override |
| T visitLiteralNumber(LiteralNumber node) => visitLiteral(node); |
| @override |
| T visitLiteralNull(LiteralNull node) => visitLiteral(node); |
| |
| @override |
| T visitArrayInitializer(ArrayInitializer node) => visitExpression(node); |
| @override |
| T visitArrayHole(ArrayHole node) => visitExpression(node); |
| @override |
| T visitObjectInitializer(ObjectInitializer node) => visitExpression(node); |
| @override |
| T visitProperty(Property node) => visitNode(node); |
| @override |
| T visitRegExpLiteral(RegExpLiteral node) => visitExpression(node); |
| @override |
| T visitTemplateString(TemplateString node) => visitExpression(node); |
| @override |
| T visitTaggedTemplate(TaggedTemplate node) => visitExpression(node); |
| |
| @override |
| T visitClassDeclaration(ClassDeclaration node) => visitStatement(node); |
| @override |
| T visitClassExpression(ClassExpression node) => visitExpression(node); |
| @override |
| T visitMethod(Method node) => visitProperty(node); |
| |
| T visitModuleItem(ModuleItem node) => visitNode(node); |
| @override |
| T visitImportDeclaration(ImportDeclaration node) => visitModuleItem(node); |
| @override |
| T visitExportDeclaration(ExportDeclaration node) => visitModuleItem(node); |
| @override |
| T visitExportClause(ExportClause node) => visitNode(node); |
| @override |
| T visitNameSpecifier(NameSpecifier node) => visitNode(node); |
| @override |
| T visitModule(Module node) => visitNode(node); |
| |
| T visitInterpolatedNode(InterpolatedNode node) => visitNode(node); |
| |
| @override |
| T visitInterpolatedExpression(InterpolatedExpression node) => |
| visitInterpolatedNode(node); |
| @override |
| T visitInterpolatedLiteral(InterpolatedLiteral node) => |
| visitInterpolatedNode(node); |
| @override |
| T visitInterpolatedParameter(InterpolatedParameter node) => |
| visitInterpolatedNode(node); |
| @override |
| T visitInterpolatedSelector(InterpolatedSelector node) => |
| visitInterpolatedNode(node); |
| @override |
| T visitInterpolatedStatement(InterpolatedStatement node) => |
| visitInterpolatedNode(node); |
| @override |
| T visitInterpolatedMethod(InterpolatedMethod node) => |
| visitInterpolatedNode(node); |
| @override |
| T visitInterpolatedIdentifier(InterpolatedIdentifier node) => |
| visitInterpolatedNode(node); |
| |
| // Ignore comments by default. |
| @override |
| T visitComment(Comment node) => null; |
| @override |
| T visitCommentExpression(CommentExpression node) => null; |
| |
| @override |
| T visitAwait(Await node) => visitExpression(node); |
| @override |
| T visitDartYield(DartYield node) => visitStatement(node); |
| |
| T visitBindingPattern(BindingPattern node) => visitNode(node); |
| @override |
| T visitArrayBindingPattern(ArrayBindingPattern node) => |
| visitBindingPattern(node); |
| @override |
| T visitObjectBindingPattern(ObjectBindingPattern node) => |
| visitBindingPattern(node); |
| @override |
| T visitDestructuredVariable(DestructuredVariable node) => visitNode(node); |
| @override |
| T visitSimpleBindingPattern(SimpleBindingPattern node) => visitNode(node); |
| } |
| |
| abstract class Node { |
| /// Sets the source location of this node. For performance reasons, we allow |
| /// setting this after construction. |
| Object sourceInformation; |
| |
| T accept<T>(NodeVisitor<T> visitor); |
| void visitChildren(NodeVisitor visitor); |
| |
| // Shallow clone of node. Does not clone positions since the only use of this |
| // private method is create a copy with a new position. |
| Node _clone(); |
| |
| // Returns a node equivalent to [this], but with new source position and end |
| // source position. |
| Node withSourceInformation(sourceInformation) { |
| if (sourceInformation == this.sourceInformation) { |
| return this; |
| } |
| Node clone = _clone(); |
| // TODO(sra): Should existing data be 'sticky' if we try to overwrite with |
| // `null`? |
| clone.sourceInformation = sourceInformation; |
| return clone; |
| } |
| |
| bool get isCommaOperator => false; |
| |
| Statement toStatement() { |
| throw UnsupportedError('toStatement'); |
| } |
| |
| Statement toReturn() { |
| throw UnsupportedError('toReturn'); |
| } |
| |
| // For debugging |
| @override |
| String toString() { |
| var context = SimpleJavaScriptPrintingContext(); |
| var opts = JavaScriptPrintingOptions(allowKeywordsInProperties: true); |
| context.buffer.write('js_ast `'); |
| accept(Printer(opts, context)); |
| context.buffer.write('`'); |
| return context.getText(); |
| } |
| } |
| |
| // TODO(jmesserly): rename to Module. |
| class Program extends Node { |
| /// Script tag hash-bang, e.g. `#!/usr/bin/env node` |
| final String scriptTag; |
| |
| /// Top-level statements in the program. |
| final List<ModuleItem> body; |
| |
| /// The module's own name. |
| /// |
| /// This is not used in ES6, but is provided to allow module lowering. |
| final String name; |
| |
| Program(this.body, {this.scriptTag, this.name}); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitProgram(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (ModuleItem statement in body) { |
| statement.accept(visitor); |
| } |
| } |
| |
| @override |
| Program _clone() => Program(body); |
| } |
| |
| abstract class Statement extends ModuleItem { |
| static Statement from(List<Statement> statements) { |
| // TODO(jmesserly): empty block singleton? Should this use empty statement? |
| if (statements.isEmpty) return Block([]); |
| if (statements.length == 1) return statements[0]; |
| return Block(statements); |
| } |
| |
| /// True if this declares any name from [names]. |
| /// |
| /// This predicate is true if the statement declares a variable via `let` or |
| /// `const` with any name in the set. This does not include variables nested |
| /// inside of blocks. The predicate tests whether adding a declaration of one |
| /// of the named variables to a block containing this statement will be a |
| /// JavaScript syntax error due to a redeclared identifier. |
| bool shadows(Set<String> names) => false; |
| |
| /// Whether this statement would always `return` if used as a funtion body. |
| /// |
| /// This is only well defined on the outermost block; it cannot be used for a |
| /// block inside of a loop (because of `break` and `continue`). |
| bool get alwaysReturns => false; |
| |
| /// If this statement [shadows] any name from [names], this will wrap it in a |
| /// new scoped [Block]. |
| Statement toScopedBlock(Set<String> names) { |
| return shadows(names) ? Block([this], isScope: true) : this; |
| } |
| |
| @override |
| Statement toStatement() => this; |
| @override |
| Statement toReturn() => Block([this, Return()]); |
| |
| Block toBlock() => Block([this]); |
| } |
| |
| class Block extends Statement { |
| final List<Statement> statements; |
| |
| /// True to preserve this [Block] for scoping reasons. |
| final bool isScope; |
| |
| Block(this.statements, {this.isScope = false}) { |
| assert(statements.every((s) => s is Statement)); |
| } |
| Block.empty() |
| : statements = <Statement>[], |
| isScope = false; |
| |
| @override |
| bool get alwaysReturns => |
| statements.isNotEmpty && statements.last.alwaysReturns; |
| |
| @override |
| Block toBlock() => this; |
| |
| @override |
| bool shadows(Set<String> names) => |
| !isScope && statements.any((s) => s.shadows(names)); |
| |
| @override |
| Block toScopedBlock(Set<String> names) { |
| var scoped = statements.any((s) => s.shadows(names)); |
| if (scoped == isScope) return this; |
| return Block(statements, isScope: scoped) |
| ..sourceInformation = sourceInformation; |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitBlock(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (Statement statement in statements) { |
| statement.accept(visitor); |
| } |
| } |
| |
| @override |
| Block _clone() => Block(statements); |
| } |
| |
| class ExpressionStatement extends Statement { |
| final Expression expression; |
| ExpressionStatement(this.expression); |
| |
| @override |
| bool shadows(Set<String> names) { |
| Expression expression = this.expression; |
| return expression is VariableDeclarationList && expression.shadows(names); |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitExpressionStatement(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| expression.accept(visitor); |
| } |
| |
| @override |
| ExpressionStatement _clone() => ExpressionStatement(expression); |
| } |
| |
| class EmptyStatement extends Statement { |
| EmptyStatement(); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitEmptyStatement(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| EmptyStatement _clone() => EmptyStatement(); |
| } |
| |
| class If extends Statement { |
| final Expression condition; |
| final Statement then; |
| final Statement otherwise; |
| |
| If(this.condition, this.then, this.otherwise); |
| If.noElse(this.condition, this.then) : this.otherwise = null; |
| |
| @override |
| bool get alwaysReturns => |
| hasElse && then.alwaysReturns && otherwise.alwaysReturns; |
| |
| bool get hasElse => otherwise != null; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitIf(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| condition.accept(visitor); |
| then.accept(visitor); |
| if (otherwise != null) otherwise.accept(visitor); |
| } |
| |
| @override |
| If _clone() => If(condition, then, otherwise); |
| } |
| |
| abstract class Loop extends Statement { |
| final Statement body; |
| Loop(this.body); |
| } |
| |
| class For extends Loop { |
| final Expression init; |
| final Expression condition; |
| final Expression update; |
| |
| For(this.init, this.condition, this.update, Statement body) : super(body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitFor(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| if (init != null) init.accept(visitor); |
| if (condition != null) condition.accept(visitor); |
| if (update != null) update.accept(visitor); |
| body.accept(visitor); |
| } |
| |
| @override |
| For _clone() => For(init, condition, update, body); |
| } |
| |
| class ForIn extends Loop { |
| // Note that [VariableDeclarationList] is a subclass of [Expression]. |
| // Therefore we can type the leftHandSide as [Expression]. |
| final Expression leftHandSide; |
| final Expression object; |
| |
| ForIn(this.leftHandSide, this.object, Statement body) : super(body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitForIn(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| leftHandSide.accept(visitor); |
| object.accept(visitor); |
| body.accept(visitor); |
| } |
| |
| @override |
| ForIn _clone() => ForIn(leftHandSide, object, body); |
| } |
| |
| class ForOf extends Loop { |
| // Note that [VariableDeclarationList] is a subclass of [Expression]. |
| // Therefore we can type the leftHandSide as [Expression]. |
| final Expression leftHandSide; |
| final Expression iterable; |
| |
| ForOf(this.leftHandSide, this.iterable, Statement body) : super(body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitForOf(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| leftHandSide.accept(visitor); |
| iterable.accept(visitor); |
| body.accept(visitor); |
| } |
| |
| @override |
| ForIn _clone() => ForIn(leftHandSide, iterable, body); |
| } |
| |
| class While extends Loop { |
| final Expression condition; |
| |
| While(this.condition, Statement body) : super(body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitWhile(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| condition.accept(visitor); |
| body.accept(visitor); |
| } |
| |
| @override |
| While _clone() => While(condition, body); |
| } |
| |
| class Do extends Loop { |
| final Expression condition; |
| |
| Do(Statement body, this.condition) : super(body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitDo(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| body.accept(visitor); |
| condition.accept(visitor); |
| } |
| |
| @override |
| Do _clone() => Do(body, condition); |
| } |
| |
| class Continue extends Statement { |
| final String targetLabel; // Can be null. |
| |
| Continue(this.targetLabel); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitContinue(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| |
| @override |
| Continue _clone() => Continue(targetLabel); |
| } |
| |
| class Break extends Statement { |
| final String targetLabel; // Can be null. |
| |
| Break(this.targetLabel); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitBreak(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| |
| @override |
| Break _clone() => Break(targetLabel); |
| } |
| |
| class Return extends Statement { |
| final Expression value; // Can be null. |
| |
| Return([this.value]); |
| |
| @override |
| bool get alwaysReturns => true; |
| |
| @override |
| Statement toReturn() => this; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitReturn(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| if (value != null) value.accept(visitor); |
| } |
| |
| @override |
| Return _clone() => Return(value); |
| |
| static bool foundIn(Node node) { |
| _returnFinder.found = false; |
| node.accept(_returnFinder); |
| return _returnFinder.found; |
| } |
| } |
| |
| final _returnFinder = _ReturnFinder(); |
| |
| class _ReturnFinder extends BaseVisitor { |
| bool found = false; |
| @override |
| visitReturn(Return node) { |
| found = true; |
| } |
| |
| @override |
| visitNode(Node node) { |
| if (!found) super.visitNode(node); |
| } |
| } |
| |
| class Throw extends Statement { |
| final Expression expression; |
| |
| Throw(this.expression); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitThrow(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| expression.accept(visitor); |
| } |
| |
| @override |
| Throw _clone() => Throw(expression); |
| } |
| |
| class Try extends Statement { |
| final Block body; |
| final Catch catchPart; // Can be null if [finallyPart] is non-null. |
| final Block finallyPart; // Can be null if [catchPart] is non-null. |
| |
| Try(this.body, this.catchPart, this.finallyPart) { |
| assert(catchPart != null || finallyPart != null); |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitTry(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| body.accept(visitor); |
| if (catchPart != null) catchPart.accept(visitor); |
| if (finallyPart != null) finallyPart.accept(visitor); |
| } |
| |
| @override |
| Try _clone() => Try(body, catchPart, finallyPart); |
| } |
| |
| class Catch extends Node { |
| final Identifier declaration; |
| final Block body; |
| |
| Catch(this.declaration, this.body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitCatch(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| declaration.accept(visitor); |
| body.accept(visitor); |
| } |
| |
| @override |
| Catch _clone() => Catch(declaration, body); |
| } |
| |
| class Switch extends Statement { |
| final Expression key; |
| final List<SwitchCase> cases; |
| |
| Switch(this.key, this.cases); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitSwitch(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| key.accept(visitor); |
| for (var clause in cases) { |
| clause.accept(visitor); |
| } |
| } |
| |
| @override |
| Switch _clone() => Switch(key, cases); |
| } |
| |
| class SwitchCase extends Node { |
| final Expression expression; |
| final Block body; |
| |
| SwitchCase(this.expression, this.body); |
| SwitchCase.defaultCase(this.body) : expression = null; |
| |
| bool get isDefault => expression == null; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitSwitchCase(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| expression?.accept(visitor); |
| body.accept(visitor); |
| } |
| |
| @override |
| SwitchCase _clone() => SwitchCase(expression, body); |
| } |
| |
| class FunctionDeclaration extends Statement { |
| final Identifier name; |
| final Fun function; |
| |
| FunctionDeclaration(this.name, this.function); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitFunctionDeclaration(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| name.accept(visitor); |
| function.accept(visitor); |
| } |
| |
| @override |
| FunctionDeclaration _clone() => FunctionDeclaration(name, function); |
| } |
| |
| class LabeledStatement extends Statement { |
| final String label; |
| final Statement body; |
| |
| LabeledStatement(this.label, this.body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLabeledStatement(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| body.accept(visitor); |
| } |
| |
| @override |
| LabeledStatement _clone() => LabeledStatement(label, body); |
| } |
| |
| class LiteralStatement extends Statement { |
| final String code; |
| |
| LiteralStatement(this.code); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralStatement(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| |
| @override |
| LiteralStatement _clone() => LiteralStatement(code); |
| } |
| |
| // Not a real JavaScript node, but represents the yield statement from a dart |
| // program translated to JavaScript. |
| class DartYield extends Statement { |
| final Expression expression; |
| |
| final bool hasStar; |
| |
| DartYield(this.expression, this.hasStar); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitDartYield(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| expression.accept(visitor); |
| } |
| |
| @override |
| DartYield _clone() => DartYield(expression, hasStar); |
| } |
| |
| abstract class Expression extends Node { |
| Expression(); |
| |
| factory Expression.binary(List<Expression> exprs, String op) { |
| Expression comma; |
| for (var node in exprs) { |
| comma = (comma == null) ? node : Binary(op, comma, node); |
| } |
| return comma; |
| } |
| |
| int get precedenceLevel; |
| |
| @override |
| Statement toStatement() => ExpressionStatement(toVoidExpression()); |
| @override |
| Statement toReturn() => Return(this); |
| |
| // TODO(jmesserly): make this return a Yield? |
| Statement toYieldStatement({bool star = false}) => |
| ExpressionStatement(Yield(this, star: star)); |
| |
| Expression toVoidExpression() => this; |
| Expression toAssignExpression(Expression left, [String op]) => |
| Assignment.compound(left, op, this); |
| |
| // TODO(jmesserly): make this work for more cases? |
| Statement toVariableDeclaration(VariableBinding name) => |
| VariableDeclarationList('let', [VariableInitialization(name, this)]) |
| .toStatement(); |
| } |
| |
| class LiteralExpression extends Expression { |
| final String template; |
| LiteralExpression(this.template); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralExpression(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| |
| @override |
| LiteralExpression _clone() => LiteralExpression(template); |
| |
| // Code that uses LiteralExpression must take care of operator precedences, |
| // and put parenthesis if needed. |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| /** |
| * [VariableDeclarationList] is a subclass of [Expression] to simplify the |
| * AST. |
| */ |
| class VariableDeclarationList extends Expression { |
| /** |
| * The `var` or `let` or `const` keyword used for this variable declaration |
| * list. |
| */ |
| final String keyword; |
| final List<VariableInitialization> declarations; |
| |
| VariableDeclarationList(this.keyword, this.declarations); |
| |
| /// True if this declares any name from [names]. |
| /// |
| /// Analogous to the predicate [Statement.shadows]. |
| bool shadows(Set<String> names) { |
| if (keyword == 'var') return false; |
| for (var d in declarations) { |
| if (d.declaration.shadows(names)) return true; |
| } |
| return false; |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitVariableDeclarationList(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (VariableInitialization declaration in declarations) { |
| declaration.accept(visitor); |
| } |
| } |
| |
| @override |
| VariableDeclarationList _clone() => |
| VariableDeclarationList(keyword, declarations); |
| |
| @override |
| int get precedenceLevel => EXPRESSION; |
| } |
| |
| class Assignment extends Expression { |
| final Expression leftHandSide; |
| final String op; // Null, if the assignment is not compound. |
| final Expression value; |
| |
| Assignment(this.leftHandSide, this.value) : op = null; |
| Assignment.compound(this.leftHandSide, this.op, this.value); |
| |
| @override |
| int get precedenceLevel => ASSIGNMENT; |
| |
| bool get isCompound => op != null; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitAssignment(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| leftHandSide.accept(visitor); |
| if (value != null) value.accept(visitor); |
| } |
| |
| @override |
| Assignment _clone() => Assignment.compound(leftHandSide, op, value); |
| } |
| |
| class VariableInitialization extends Expression { |
| final VariableBinding declaration; |
| final Expression value; // May be null. |
| |
| /// [value] may be null. |
| VariableInitialization(this.declaration, this.value); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitVariableInitialization(this); |
| |
| @override |
| VariableInitialization _clone() => VariableInitialization(declaration, value); |
| |
| @override |
| int get precedenceLevel => ASSIGNMENT; |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| declaration.accept(visitor); |
| if (value != null) value.accept(visitor); |
| } |
| } |
| |
| abstract class VariableBinding extends Expression { |
| /// True if this binding declares any name from [names]. |
| /// |
| /// Analogous to the predicate [Statement.shadows]. |
| bool shadows(Set<String> names); |
| } |
| |
| // TODO(jmesserly): destructuring was originally implemented in the context of |
| // Closure Compiler work. Rethink how this is represented. |
| class DestructuredVariable extends Expression implements Parameter { |
| final Identifier name; |
| |
| /// The proprety in an object binding pattern, for example: |
| /// |
| /// let key = 'z'; |
| /// let {[key]: foo} = {z: 'bar'}; |
| /// console.log(foo); // "bar" |
| /// |
| // TODO(jmesserly): parser does not support this feature. |
| final Expression property; |
| |
| final BindingPattern structure; |
| final Expression defaultValue; |
| |
| DestructuredVariable( |
| {this.name, this.property, this.structure, this.defaultValue}) { |
| assert(name != null || structure != null); |
| } |
| |
| @override |
| bool shadows(Set<String> names) { |
| return (name?.shadows(names) ?? false) || |
| (structure?.shadows(names) ?? false); |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitDestructuredVariable(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| name?.accept(visitor); |
| structure?.accept(visitor); |
| defaultValue?.accept(visitor); |
| } |
| |
| /// Avoid parenthesis when pretty-printing. |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| String get parameterName => name.name; |
| @override |
| Node _clone() => DestructuredVariable( |
| name: name, |
| property: property, |
| structure: structure, |
| defaultValue: defaultValue); |
| } |
| |
| abstract class BindingPattern extends Expression implements VariableBinding { |
| final List<DestructuredVariable> variables; |
| BindingPattern(this.variables); |
| |
| @override |
| bool shadows(Set<String> names) { |
| for (var v in variables) { |
| if (v.shadows(names)) return true; |
| } |
| return false; |
| } |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (DestructuredVariable v in variables) { |
| v.accept(visitor); |
| } |
| } |
| } |
| |
| class SimpleBindingPattern extends BindingPattern { |
| final Identifier name; |
| SimpleBindingPattern(Identifier name) |
| : name = name, |
| super([DestructuredVariable(name: name)]); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitSimpleBindingPattern(this); |
| |
| @override |
| bool shadows(Set<String> names) => names.contains(name.name); |
| |
| /// Avoid parenthesis when pretty-printing. |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| Node _clone() => SimpleBindingPattern(name); |
| } |
| |
| class ObjectBindingPattern extends BindingPattern { |
| ObjectBindingPattern(List<DestructuredVariable> variables) : super(variables); |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitObjectBindingPattern(this); |
| |
| /// Avoid parenthesis when pretty-printing. |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| Node _clone() => ObjectBindingPattern(variables); |
| } |
| |
| class ArrayBindingPattern extends BindingPattern { |
| ArrayBindingPattern(List<DestructuredVariable> variables) : super(variables); |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrayBindingPattern(this); |
| |
| /// Avoid parenthesis when pretty-printing. |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| Node _clone() => ArrayBindingPattern(variables); |
| } |
| |
| class Conditional extends Expression { |
| final Expression condition; |
| final Expression then; |
| final Expression otherwise; |
| |
| Conditional(this.condition, this.then, this.otherwise); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitConditional(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| condition.accept(visitor); |
| then.accept(visitor); |
| otherwise.accept(visitor); |
| } |
| |
| @override |
| Conditional _clone() => Conditional(condition, then, otherwise); |
| |
| @override |
| int get precedenceLevel => ASSIGNMENT; |
| } |
| |
| class Call extends Expression { |
| Expression target; |
| List<Expression> arguments; |
| |
| Call(this.target, this.arguments); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitCall(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| target.accept(visitor); |
| for (Expression arg in arguments) { |
| arg.accept(visitor); |
| } |
| } |
| |
| @override |
| Call _clone() => Call(target, arguments); |
| |
| @override |
| int get precedenceLevel => CALL; |
| } |
| |
| class New extends Call { |
| New(Expression cls, List<Expression> arguments) : super(cls, arguments); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitNew(this); |
| |
| @override |
| New _clone() => New(target, arguments); |
| |
| @override |
| int get precedenceLevel => ACCESS; |
| } |
| |
| class Binary extends Expression { |
| final String op; |
| final Expression left; |
| final Expression right; |
| |
| Binary(this.op, this.left, this.right); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitBinary(this); |
| |
| @override |
| Binary _clone() => Binary(op, left, right); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| left.accept(visitor); |
| right.accept(visitor); |
| } |
| |
| @override |
| bool get isCommaOperator => op == ','; |
| |
| @override |
| Expression toVoidExpression() { |
| if (!isCommaOperator) return super.toVoidExpression(); |
| var l = left.toVoidExpression(); |
| var r = right.toVoidExpression(); |
| if (l == left && r == right) return this; |
| return Binary(',', l, r); |
| } |
| |
| @override |
| Statement toStatement() { |
| if (!isCommaOperator) return super.toStatement(); |
| return Block([left.toStatement(), right.toStatement()]); |
| } |
| |
| @override |
| Statement toReturn() { |
| if (!isCommaOperator) return super.toReturn(); |
| return Block([left.toStatement(), right.toReturn()]); |
| } |
| |
| @override |
| Statement toYieldStatement({bool star = false}) { |
| if (!isCommaOperator) return super.toYieldStatement(star: star); |
| return Block([left.toStatement(), right.toYieldStatement(star: star)]); |
| } |
| |
| List<Expression> commaToExpressionList() { |
| if (!isCommaOperator) throw StateError('not a comma expression'); |
| var exprs = <Expression>[]; |
| _flattenComma(exprs, left); |
| _flattenComma(exprs, right); |
| return exprs; |
| } |
| |
| static void _flattenComma(List<Expression> exprs, Expression node) { |
| if (node is Binary && node.isCommaOperator) { |
| _flattenComma(exprs, node.left); |
| _flattenComma(exprs, node.right); |
| } else { |
| exprs.add(node); |
| } |
| } |
| |
| @override |
| int get precedenceLevel { |
| // TODO(floitsch): switch to constant map. |
| switch (op) { |
| case "*": |
| case "/": |
| case "%": |
| return MULTIPLICATIVE; |
| case "+": |
| case "-": |
| return ADDITIVE; |
| case "<<": |
| case ">>": |
| case ">>>": |
| return SHIFT; |
| case "<": |
| case ">": |
| case "<=": |
| case ">=": |
| case "instanceof": |
| case "in": |
| return RELATIONAL; |
| case "==": |
| case "===": |
| case "!=": |
| case "!==": |
| return EQUALITY; |
| case "&": |
| return BIT_AND; |
| case "^": |
| return BIT_XOR; |
| case "|": |
| return BIT_OR; |
| case "&&": |
| return LOGICAL_AND; |
| case "||": |
| return LOGICAL_OR; |
| case ',': |
| return EXPRESSION; |
| default: |
| throw "Internal Error: Unhandled binary operator: $op"; |
| } |
| } |
| } |
| |
| class Prefix extends Expression { |
| final String op; |
| final Expression argument; |
| |
| Prefix(this.op, this.argument); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitPrefix(this); |
| |
| @override |
| Prefix _clone() => Prefix(op, argument); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| argument.accept(visitor); |
| } |
| |
| @override |
| int get precedenceLevel => UNARY; |
| } |
| |
| // SpreadElement isn't really a prefix expression, as it can only appear in |
| // certain places such as ArgumentList and BindingPattern, but we pretend |
| // it is for simplicity's sake. |
| class Spread extends Prefix { |
| Spread(Expression operand) : super('...', operand); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitSpread(this); |
| @override |
| Spread _clone() => Spread(argument); |
| } |
| |
| class Postfix extends Expression { |
| final String op; |
| final Expression argument; |
| |
| Postfix(this.op, this.argument); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitPostfix(this); |
| |
| @override |
| Postfix _clone() => Postfix(op, argument); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| argument.accept(visitor); |
| } |
| |
| @override |
| int get precedenceLevel => UNARY; |
| } |
| |
| abstract class Parameter implements Expression, VariableBinding { |
| String get parameterName; |
| } |
| |
| class Identifier extends Expression implements Parameter { |
| final String name; |
| final bool allowRename; |
| |
| Identifier(this.name, {this.allowRename = true}) { |
| if (!_identifierRE.hasMatch(name)) { |
| throw ArgumentError.value(name, "name", "not a valid identifier"); |
| } |
| } |
| static RegExp _identifierRE = RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$'); |
| |
| @override |
| bool shadows(Set<String> names) => names.contains(name); |
| |
| @override |
| Identifier _clone() => Identifier(name, allowRename: allowRename); |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitIdentifier(this); |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| String get parameterName => name; |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| } |
| |
| // This is an expression for convenience in the AST. |
| class RestParameter extends Expression implements Parameter { |
| final Identifier parameter; |
| |
| RestParameter(this.parameter); |
| |
| @override |
| bool shadows(Set<String> names) => names.contains(parameter.name); |
| |
| @override |
| RestParameter _clone() => RestParameter(parameter); |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitRestParameter(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| parameter.accept(visitor); |
| } |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| String get parameterName => parameter.parameterName; |
| } |
| |
| class This extends Expression { |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitThis(this); |
| @override |
| This _clone() => This(); |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| } |
| |
| // `super` is more restricted in the ES6 spec, but for simplicity we accept |
| // it anywhere that `this` is accepted. |
| class Super extends Expression { |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitSuper(this); |
| @override |
| Super _clone() => Super(); |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| } |
| |
| class NamedFunction extends Expression { |
| final Identifier name; |
| final Fun function; |
| // A heuristic to force extra parens around this function. V8 and other |
| // engines use this IIFE (immediately invoked function expression) heuristic |
| // to eagerly parse a function. |
| final bool immediatelyInvoked; |
| |
| NamedFunction(this.name, this.function, [this.immediatelyInvoked = false]); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitNamedFunction(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| name.accept(visitor); |
| function.accept(visitor); |
| } |
| |
| @override |
| NamedFunction _clone() => NamedFunction(name, function, immediatelyInvoked); |
| |
| @override |
| int get precedenceLevel => |
| immediatelyInvoked ? EXPRESSION : PRIMARY_LOW_PRECEDENCE; |
| } |
| |
| abstract class FunctionExpression extends Expression { |
| Node get body; // Expression or block |
| List<Parameter> get params; |
| } |
| |
| class Fun extends FunctionExpression { |
| @override |
| final List<Parameter> params; |
| @override |
| final Block body; |
| |
| /** Whether this is a JS generator (`function*`) that may contain `yield`. */ |
| final bool isGenerator; |
| |
| final AsyncModifier asyncModifier; |
| |
| Fun(this.params, this.body, |
| {this.isGenerator = false, |
| this.asyncModifier = const AsyncModifier.sync()}); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitFun(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (Parameter param in params) { |
| param.accept(visitor); |
| } |
| body.accept(visitor); |
| } |
| |
| @override |
| Fun _clone() => |
| Fun(params, body, isGenerator: isGenerator, asyncModifier: asyncModifier); |
| |
| @override |
| int get precedenceLevel => PRIMARY_LOW_PRECEDENCE; |
| } |
| |
| class ArrowFun extends FunctionExpression { |
| @override |
| final List<Parameter> params; |
| @override |
| final Node body; // Expression or Block |
| |
| ArrowFun(this.params, this.body); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrowFun(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (Parameter param in params) { |
| param.accept(visitor); |
| } |
| body.accept(visitor); |
| } |
| |
| @override |
| int get precedenceLevel => PRIMARY_LOW_PRECEDENCE; |
| |
| @override |
| ArrowFun _clone() => ArrowFun(params, body); |
| } |
| |
| /** |
| * The Dart sync, sync*, async, and async* modifier. |
| * See [DartYield]. |
| * |
| * This is not used for JS functions. |
| */ |
| class AsyncModifier { |
| final bool isAsync; |
| final bool isYielding; |
| final String description; |
| |
| const AsyncModifier.sync() |
| : isAsync = false, |
| isYielding = false, |
| description = "sync"; |
| const AsyncModifier.async() |
| : isAsync = true, |
| isYielding = false, |
| description = "async"; |
| const AsyncModifier.asyncStar() |
| : isAsync = true, |
| isYielding = true, |
| description = "async*"; |
| const AsyncModifier.syncStar() |
| : isAsync = false, |
| isYielding = true, |
| description = "sync*"; |
| @override |
| toString() => description; |
| } |
| |
| class PropertyAccess extends Expression { |
| final Expression receiver; |
| final Expression selector; |
| |
| PropertyAccess(this.receiver, this.selector); |
| PropertyAccess.field(this.receiver, String fieldName) |
| : selector = LiteralString('"$fieldName"'); |
| PropertyAccess.indexed(this.receiver, int index) |
| : selector = LiteralNumber('$index'); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitAccess(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| receiver.accept(visitor); |
| selector.accept(visitor); |
| } |
| |
| @override |
| PropertyAccess _clone() => PropertyAccess(receiver, selector); |
| |
| @override |
| int get precedenceLevel => ACCESS; |
| } |
| |
| abstract class Literal extends Expression { |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| class LiteralBool extends Literal { |
| final bool value; |
| |
| LiteralBool(this.value); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralBool(this); |
| // [visitChildren] inherited from [Literal]. |
| @override |
| LiteralBool _clone() => LiteralBool(value); |
| } |
| |
| class LiteralNull extends Literal { |
| LiteralNull(); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralNull(this); |
| @override |
| LiteralNull _clone() => LiteralNull(); |
| } |
| |
| class LiteralString extends Literal { |
| final String value; |
| |
| /** |
| * Constructs a LiteralString from a string value. |
| * |
| * The constructor does not add the required quotes. If [value] is not |
| * surrounded by quotes and property escaped, the resulting object is invalid |
| * as a JS value. |
| * |
| * TODO(sra): Introduce variants for known valid strings that don't allocate a |
| * new string just to add quotes. |
| */ |
| LiteralString(this.value); |
| |
| /// Gets the value inside the string without the beginning and end quotes. |
| String get valueWithoutQuotes => value.substring(1, value.length - 1); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralString(this); |
| @override |
| LiteralString _clone() => LiteralString(value); |
| } |
| |
| class LiteralNumber extends Literal { |
| final String value; // Must be a valid JavaScript number literal. |
| |
| LiteralNumber(this.value); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralNumber(this); |
| @override |
| LiteralNumber _clone() => LiteralNumber(value); |
| |
| /** |
| * Use a different precedence level depending on whether the value contains a |
| * dot to ensure we generate `(1).toString()` and `1.0.toString()`. |
| */ |
| @override |
| int get precedenceLevel => value.contains('.') ? PRIMARY : UNARY; |
| } |
| |
| class ArrayInitializer extends Expression { |
| final List<Expression> elements; |
| final bool multiline; |
| |
| ArrayInitializer(this.elements, {this.multiline = false}); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrayInitializer(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (Expression element in elements) { |
| element.accept(visitor); |
| } |
| } |
| |
| @override |
| ArrayInitializer _clone() => ArrayInitializer(elements); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| /** |
| * An empty place in an [ArrayInitializer]. |
| * For example the list [1, , , 2] would contain two holes. |
| */ |
| class ArrayHole extends Expression { |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrayHole(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| |
| @override |
| ArrayHole _clone() => ArrayHole(); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| class ObjectInitializer extends Expression { |
| final List<Property> properties; |
| final bool _multiline; |
| |
| /** |
| * Constructs a new object-initializer containing the given [properties]. |
| */ |
| ObjectInitializer(this.properties, {bool multiline = false}) |
| : _multiline = multiline; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitObjectInitializer(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (Property init in properties) { |
| init.accept(visitor); |
| } |
| } |
| |
| @override |
| ObjectInitializer _clone() => ObjectInitializer(properties); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| /** |
| * If set to true, forces a vertical layout when using the [Printer]. |
| * Otherwise, layout will be vertical if and only if any [properties] |
| * are [FunctionExpression]s. |
| */ |
| bool get multiline { |
| return _multiline || properties.any((p) => p.value is FunctionExpression); |
| } |
| } |
| |
| class Property extends Node { |
| final Expression name; |
| final Expression value; |
| |
| Property(this.name, this.value); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitProperty(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| name.accept(visitor); |
| value.accept(visitor); |
| } |
| |
| @override |
| Property _clone() => Property(name, value); |
| } |
| |
| // TODO(jmesserly): parser does not support this yet. |
| class TemplateString extends Expression { |
| /** |
| * The parts of this template string: a sequence of [String]s and |
| * [Expression]s. Strings and expressions will alternate, for example: |
| * |
| * `foo${1 + 2} bar ${'hi'}` |
| * |
| * would be represented by [strings]: |
| * |
| * ['foo', ' bar ', ''] |
| * |
| * and [interpolations]: |
| * |
| * [new JS.Binary('+', js.number(1), js.number(2)), |
| * new JS.LiteralString("'hi'")] |
| * |
| * There should be exactly one more string than interpolation expression. |
| */ |
| final List<String> strings; |
| final List<Expression> interpolations; |
| |
| TemplateString(this.strings, this.interpolations) { |
| assert(strings.length == interpolations.length + 1); |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitTemplateString(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (var element in interpolations) { |
| element.accept(visitor); |
| } |
| } |
| |
| @override |
| TemplateString _clone() => TemplateString(strings, interpolations); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| // TODO(jmesserly): parser does not support this yet. |
| class TaggedTemplate extends Expression { |
| final Expression tag; |
| final TemplateString template; |
| |
| TaggedTemplate(this.tag, this.template); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitTaggedTemplate(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| tag.accept(visitor); |
| template.accept(visitor); |
| } |
| |
| @override |
| TaggedTemplate _clone() => TaggedTemplate(tag, template); |
| |
| @override |
| int get precedenceLevel => CALL; |
| } |
| |
| // TODO(jmesserly): parser does not support this yet. |
| class Yield extends Expression { |
| final Expression value; // Can be null. |
| |
| /** |
| * Whether this yield expression is a `yield*` that iterates each item in |
| * [value]. |
| */ |
| final bool star; |
| |
| Yield(this.value, {this.star = false}); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitYield(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| if (value != null) value.accept(visitor); |
| } |
| |
| @override |
| Yield _clone() => Yield(value); |
| |
| @override |
| int get precedenceLevel => YIELD; |
| } |
| |
| class ClassDeclaration extends Statement { |
| final ClassExpression classExpr; |
| |
| ClassDeclaration(this.classExpr); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitClassDeclaration(this); |
| @override |
| visitChildren(NodeVisitor visitor) => classExpr.accept(visitor); |
| @override |
| ClassDeclaration _clone() => ClassDeclaration(classExpr); |
| } |
| |
| class ClassExpression extends Expression { |
| final Identifier name; |
| final Expression heritage; // Can be null. |
| final List<Method> methods; |
| |
| ClassExpression(this.name, this.heritage, this.methods); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitClassExpression(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| name.accept(visitor); |
| if (heritage != null) heritage.accept(visitor); |
| for (Method element in methods) { |
| element.accept(visitor); |
| } |
| } |
| |
| @override |
| ClassDeclaration toStatement() => ClassDeclaration(this); |
| |
| @override |
| ClassExpression _clone() => ClassExpression(name, heritage, methods); |
| |
| @override |
| int get precedenceLevel => PRIMARY_LOW_PRECEDENCE; |
| } |
| |
| class Method extends Node implements Property { |
| @override |
| final Expression name; |
| final Fun function; |
| final bool isGetter; |
| final bool isSetter; |
| final bool isStatic; |
| |
| Method(this.name, this.function, |
| {this.isGetter = false, this.isSetter = false, this.isStatic = false}) { |
| assert(!isGetter || function.params.isEmpty); |
| assert(!isSetter || function.params.length == 1); |
| assert(!isGetter && !isSetter || !function.isGenerator); |
| } |
| |
| @override |
| Fun get value => function; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitMethod(this); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| name.accept(visitor); |
| function.accept(visitor); |
| } |
| |
| @override |
| Method _clone() => Method(name, function, |
| isGetter: isGetter, isSetter: isSetter, isStatic: isStatic); |
| } |
| |
| /// Tag class for all interpolated positions. |
| abstract class InterpolatedNode implements Node { |
| get nameOrPosition; |
| |
| bool get isNamed => nameOrPosition is String; |
| bool get isPositional => nameOrPosition is int; |
| } |
| |
| class InterpolatedExpression extends Expression with InterpolatedNode { |
| @override |
| final Object nameOrPosition; |
| |
| InterpolatedExpression(this.nameOrPosition); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitInterpolatedExpression(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedExpression _clone() => InterpolatedExpression(nameOrPosition); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| class InterpolatedLiteral extends Literal with InterpolatedNode { |
| @override |
| final Object nameOrPosition; |
| |
| InterpolatedLiteral(this.nameOrPosition); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitInterpolatedLiteral(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedLiteral _clone() => InterpolatedLiteral(nameOrPosition); |
| } |
| |
| class InterpolatedParameter extends Expression |
| with InterpolatedNode |
| implements Identifier { |
| @override |
| final Object nameOrPosition; |
| |
| @override |
| String get name { |
| throw "InterpolatedParameter.name must not be invoked"; |
| } |
| |
| @override |
| String get parameterName { |
| throw "InterpolatedParameter.parameterName must not be invoked"; |
| } |
| |
| @override |
| bool shadows(Set<String> names) => false; |
| |
| @override |
| bool get allowRename => false; |
| |
| InterpolatedParameter(this.nameOrPosition); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitInterpolatedParameter(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedParameter _clone() => InterpolatedParameter(nameOrPosition); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| class InterpolatedSelector extends Expression with InterpolatedNode { |
| @override |
| final Object nameOrPosition; |
| |
| InterpolatedSelector(this.nameOrPosition); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitInterpolatedSelector(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedSelector _clone() => InterpolatedSelector(nameOrPosition); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| class InterpolatedStatement extends Statement with InterpolatedNode { |
| @override |
| final Object nameOrPosition; |
| |
| InterpolatedStatement(this.nameOrPosition); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitInterpolatedStatement(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedStatement _clone() => InterpolatedStatement(nameOrPosition); |
| } |
| |
| // TODO(jmesserly): generalize this to InterpolatedProperty? |
| class InterpolatedMethod extends Expression |
| with InterpolatedNode |
| implements Method { |
| @override |
| final Object nameOrPosition; |
| |
| InterpolatedMethod(this.nameOrPosition); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitInterpolatedMethod(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedMethod _clone() => InterpolatedMethod(nameOrPosition); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| Expression get name => throw _unsupported; |
| @override |
| Fun get value => throw _unsupported; |
| @override |
| bool get isGetter => throw _unsupported; |
| @override |
| bool get isSetter => throw _unsupported; |
| @override |
| bool get isStatic => throw _unsupported; |
| @override |
| Fun get function => throw _unsupported; |
| Error get _unsupported => |
| UnsupportedError('$runtimeType does not support this member.'); |
| } |
| |
| class InterpolatedIdentifier extends Expression |
| with InterpolatedNode |
| implements Identifier { |
| @override |
| final Object nameOrPosition; |
| |
| InterpolatedIdentifier(this.nameOrPosition); |
| |
| @override |
| bool shadows(Set<String> names) => false; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => |
| visitor.visitInterpolatedIdentifier(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| InterpolatedIdentifier _clone() => InterpolatedIdentifier(nameOrPosition); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| String get name => throw '$runtimeType does not support this member.'; |
| @override |
| String get parameterName => |
| throw '$runtimeType does not support this member.'; |
| @override |
| bool get allowRename => false; |
| } |
| |
| /** |
| * [RegExpLiteral]s, despite being called "Literal", do not inherit from |
| * [Literal]. Indeed, regular expressions in JavaScript have a side-effect and |
| * are thus not in the same category as numbers or strings. |
| */ |
| class RegExpLiteral extends Expression { |
| /** Contains the pattern and the flags.*/ |
| final String pattern; |
| |
| RegExpLiteral(this.pattern); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitRegExpLiteral(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| RegExpLiteral _clone() => RegExpLiteral(pattern); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| } |
| |
| /** |
| * An asynchronous await. |
| * |
| * Not part of JavaScript. We desugar this expression before outputting. |
| * Should only occur in a [Fun] with `asyncModifier` async or asyncStar. |
| */ |
| class Await extends Expression { |
| /** The awaited expression. */ |
| final Expression expression; |
| |
| Await(this.expression); |
| |
| @override |
| int get precedenceLevel => UNARY; |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitAwait(this); |
| @override |
| void visitChildren(NodeVisitor visitor) => expression.accept(visitor); |
| @override |
| Await _clone() => Await(expression); |
| } |
| |
| /** |
| * A comment. |
| * |
| * Extends [Statement] so we can add comments before statements in |
| * [Block] and [Program]. |
| */ |
| class Comment extends Statement { |
| final String comment; |
| |
| Comment(this.comment); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitComment(this); |
| @override |
| Comment _clone() => Comment(comment); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| } |
| |
| /** |
| * A comment for expressions. |
| * |
| * Extends [Expression] so we can add comments before expressions. |
| * Has the highest possible precedence, so we don't add parentheses around it. |
| */ |
| class CommentExpression extends Expression { |
| final String comment; |
| final Expression expression; |
| |
| CommentExpression(this.comment, this.expression); |
| |
| @override |
| int get precedenceLevel => PRIMARY; |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitCommentExpression(this); |
| @override |
| CommentExpression _clone() => CommentExpression(comment, expression); |
| |
| @override |
| void visitChildren(NodeVisitor visitor) => expression.accept(visitor); |
| } |
| |
| class DebuggerStatement extends Statement { |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitDebuggerStatement(this); |
| @override |
| DebuggerStatement _clone() => DebuggerStatement(); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| } |
| |
| /** |
| * Represents allowed module items: |
| * [Statement], [ImportDeclaration], and [ExportDeclaration]. |
| */ |
| abstract class ModuleItem extends Node {} |
| |
| class ImportDeclaration extends ModuleItem { |
| final Identifier defaultBinding; // Can be null. |
| |
| // Can be null, a single specifier of `* as name`, or a list. |
| final List<NameSpecifier> namedImports; |
| |
| final LiteralString from; |
| |
| ImportDeclaration({this.defaultBinding, this.namedImports, this.from}) { |
| assert(from != null); |
| } |
| |
| /** The `import "name.js"` form of import */ |
| ImportDeclaration.all(LiteralString module) : this(from: module); |
| |
| /** If this import has `* as name` returns the name, otherwise null. */ |
| Identifier get importStarAs { |
| if (namedImports != null && |
| namedImports.length == 1 && |
| namedImports[0].isStar) { |
| return namedImports[0].asName; |
| } |
| return null; |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitImportDeclaration(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| if (namedImports != null) { |
| for (NameSpecifier name in namedImports) { |
| name.accept(visitor); |
| } |
| } |
| from.accept(visitor); |
| } |
| |
| @override |
| ImportDeclaration _clone() => ImportDeclaration( |
| defaultBinding: defaultBinding, namedImports: namedImports, from: from); |
| } |
| |
| class ExportDeclaration extends ModuleItem { |
| /** |
| * Exports a name from this module. |
| * |
| * This can be a [ClassDeclaration] or [FunctionDeclaration]. |
| * If [isDefault] is true, it can also be an [Expression]. |
| * Otherwise it can be a [VariableDeclarationList] or an [ExportClause]. |
| */ |
| final Node exported; |
| |
| /** True if this is an `export default`. */ |
| final bool isDefault; |
| |
| ExportDeclaration(this.exported, {this.isDefault = false}) { |
| assert(exported is ClassDeclaration || |
| exported is FunctionDeclaration || |
| isDefault |
| ? exported is Expression |
| : exported is VariableDeclarationList || exported is ExportClause); |
| } |
| |
| /// Gets the list of names exported by this export declaration, or `null` |
| /// if this is an `export *`. |
| /// |
| /// This can be useful for lowering to other module formats. |
| List<NameSpecifier> get exportedNames { |
| if (isDefault) return [NameSpecifier(Identifier('default'))]; |
| |
| var exported = this.exported; |
| if (exported is ClassDeclaration) { |
| return [NameSpecifier(exported.classExpr.name)]; |
| } |
| if (exported is FunctionDeclaration) return [NameSpecifier(exported.name)]; |
| if (exported is VariableDeclarationList) { |
| return exported.declarations |
| .map((i) => NameSpecifier(i.declaration as Identifier)) |
| .toList(); |
| } |
| if (exported is ExportClause) { |
| if (exported.exportStar) return null; |
| return exported.exports; |
| } |
| throw StateError('invalid export declaration'); |
| } |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitExportDeclaration(this); |
| @override |
| visitChildren(NodeVisitor visitor) => exported.accept(visitor); |
| @override |
| ExportDeclaration _clone() => |
| ExportDeclaration(exported, isDefault: isDefault); |
| } |
| |
| class ExportClause extends Node { |
| final List<NameSpecifier> exports; |
| final LiteralString from; // Can be null. |
| |
| ExportClause(this.exports, {this.from}); |
| |
| /** The `export * from 'name.js'` form. */ |
| ExportClause.star(LiteralString from) |
| : this([NameSpecifier.star()], from: from); |
| |
| /** True if this is an `export *`. */ |
| bool get exportStar => exports.length == 1 && exports[0].isStar; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitExportClause(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (NameSpecifier name in exports) { |
| name.accept(visitor); |
| } |
| if (from != null) from.accept(visitor); |
| } |
| |
| @override |
| ExportClause _clone() => ExportClause(exports, from: from); |
| } |
| |
| /** An import or export specifier. */ |
| class NameSpecifier extends Node { |
| final Identifier name; |
| final Identifier asName; // Can be null. |
| |
| NameSpecifier(this.name, {this.asName}); |
| NameSpecifier.star() : this(null); |
| |
| /** True if this is a `* as someName` specifier. */ |
| bool get isStar => name == null; |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitNameSpecifier(this); |
| @override |
| void visitChildren(NodeVisitor visitor) {} |
| @override |
| NameSpecifier _clone() => NameSpecifier(name, asName: asName); |
| } |
| |
| // TODO(jmesserly): should this be related to [Program]? |
| class Module extends Node { |
| /// The module's name |
| // TODO(jmesserly): this is not declared in ES6, but is known by the loader. |
| // We use this because some ES5 desugarings require it. |
| final String name; |
| |
| final List<ModuleItem> body; |
| Module(this.body, {this.name}); |
| |
| @override |
| T accept<T>(NodeVisitor<T> visitor) => visitor.visitModule(this); |
| @override |
| void visitChildren(NodeVisitor visitor) { |
| for (ModuleItem item in body) { |
| item.accept(visitor); |
| } |
| } |
| |
| @override |
| Module _clone() => Module(body); |
| } |