blob: d721e6e818ca03be4fc967d26dc767b230681ed2 [file] [log] [blame]
// 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);
}