blob: 664388d95695dc251cd24d4460ae316d5e7f7092 [file] [log] [blame]
// Copyright (c) 2016, 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.md file.
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/frontend/accessors.dart'
show
Accessor,
IndexAccessor,
NullAwarePropertyAccessor,
PropertyAccessor,
ReadOnlyAccessor,
StaticAccessor,
SuperIndexAccessor,
SuperPropertyAccessor,
ThisPropertyAccessor,
VariableAccessor,
buildIsNull,
makeBinary,
makeLet,
makeOrReuseVariable;
import 'package:kernel/transformations/flags.dart';
import '../common.dart';
import '../common/names.dart';
import '../constants/expressions.dart'
show
BoolFromEnvironmentConstantExpression,
ConstantExpression,
ConstructedConstantExpression,
IntFromEnvironmentConstantExpression,
StringFromEnvironmentConstantExpression,
TypeConstantExpression;
import '../dart_types.dart' show DartType, InterfaceType;
import '../diagnostics/spannable.dart' show Spannable;
import '../elements/elements.dart'
show
AstElement,
AsyncMarker,
ClassElement,
ConstructorElement,
Element,
FieldElement,
FunctionElement,
FunctionSignature,
GetterElement,
InitializingFormalElement,
JumpTarget,
LocalElement,
LocalFunctionElement,
LocalVariableElement,
MethodElement,
Name,
ParameterElement,
PrefixElement,
TypeVariableElement;
import '../resolution/operators.dart'
show AssignmentOperator, BinaryOperator, IncDecOperator, UnaryOperator;
import '../resolution/semantic_visitor.dart'
show
BaseImplementationOfCompoundsMixin,
BaseImplementationOfLocalsMixin,
BaseImplementationOfSetIfNullsMixin,
BaseImplementationOfStaticsMixin,
CompoundGetter,
CompoundKind,
CompoundRhs,
CompoundSetter,
SemanticDeclarationResolvedMixin,
SemanticDeclarationVisitor,
SemanticSendResolvedMixin,
SemanticSendVisitor,
SemanticVisitor;
import '../resolution/send_resolver.dart' show DeclarationResolverMixin;
import '../resolution/send_structure.dart'
show
InitializerStructure,
InitializersStructure,
ParameterStructure,
VariableStructure;
import '../resolution/tree_elements.dart' show TreeElements;
import '../tree/tree.dart'
show
Assert,
AsyncForIn,
Await,
Block,
BreakStatement,
Cascade,
CascadeReceiver,
CaseMatch,
CatchBlock,
Conditional,
ConditionalUri,
ContinueStatement,
DoWhile,
DottedName,
EmptyStatement,
Enum,
Expression,
ExpressionStatement,
For,
ForIn,
FunctionDeclaration,
FunctionExpression,
Identifier,
If,
Label,
LabeledStatement,
LiteralBool,
LiteralDouble,
LiteralInt,
LiteralList,
LiteralMap,
LiteralMapEntry,
LiteralNull,
LiteralString,
LiteralSymbol,
Metadata,
NamedArgument,
NewExpression,
Node,
NodeList,
Operator,
ParenthesizedExpression,
RedirectingFactoryBody,
Rethrow,
Return,
Send,
SendSet,
Statement,
StringInterpolation,
StringInterpolationPart,
StringJuxtaposition,
SwitchCase,
SwitchStatement,
SyncForIn,
Throw,
TryStatement,
TypeAnnotation,
TypeVariable,
VariableDefinitions,
While,
Yield;
import '../universe/call_structure.dart' show CallStructure;
import '../universe/selector.dart' show Selector;
import '../util/util.dart' show Link;
import 'error.dart' show KernelError;
import 'kernel.dart' show ConstructorTarget, Kernel;
import 'unavailable.dart' show UnavailableVisitor;
import 'unresolved.dart' show UnresolvedVisitor;
/// Translates dart2js AST nodes [Node] into Kernel IR [ir.TreeNode].
///
/// Most methods in this class have a prefix that follows these conventions:
///
/// * `visit` for overridden visitor methods.
/// * `handle` for methods that implement common behavior for several `visit`
/// methods. These methods are called by `visit` methods implemented by
/// various mixins below.
/// * `build` helper method that builds a new Kernel IR tree.
/// * `get` helper method that use a cache to build exactly one Kernel IR
/// tree for a given element.
///
/// We reserve the prefixes `visit` and `handle` for superclasses of this
/// class. So those methods should always have an @override annotation. Use
/// `build` instead of `handle` when adding a new helper method to this class.
class KernelVisitor extends Object
with
SemanticSendResolvedMixin,
BaseImplementationOfStaticsMixin,
BaseImplementationOfLocalsMixin,
BaseImplementationOfCompoundsMixin,
BaseImplementationOfSetIfNullsMixin,
SemanticDeclarationResolvedMixin,
DeclarationResolverMixin,
UnavailableVisitor,
UnresolvedVisitor,
KernelError
implements
SemanticVisitor,
SemanticSendVisitor,
SemanticDeclarationVisitor {
TreeElements elements;
AstElement currentElement;
final Kernel kernel;
int transformerFlags = 0;
final Map<JumpTarget, ir.LabeledStatement> continueTargets =
<JumpTarget, ir.LabeledStatement>{};
final Map<JumpTarget, ir.SwitchCase> continueSwitchTargets =
<JumpTarget, ir.SwitchCase>{};
final Map<JumpTarget, ir.LabeledStatement> breakTargets =
<JumpTarget, ir.LabeledStatement>{};
final Map<LocalElement, ir.VariableDeclaration> locals =
<LocalElement, ir.VariableDeclaration>{};
final Map<CascadeReceiver, ir.VariableGet> cascadeReceivers =
<CascadeReceiver, ir.VariableGet>{};
ir.Node associateElement(ir.Node node, Element element) {
kernel.nodeToElement[node] = element;
return node;
}
ir.Node associateNode(ir.Node node, Node ast) {
kernel.nodeToAst[node] = ast;
return node;
}
bool isVoidContext = false;
KernelVisitor(this.currentElement, this.elements, this.kernel);
KernelVisitor get sendVisitor => this;
KernelVisitor get declVisitor => this;
ir.TreeNode visitForValue(Expression node) {
bool wasVoidContext = isVoidContext;
isVoidContext = false;
try {
return node?.accept(this);
} finally {
isVoidContext = wasVoidContext;
}
}
ir.TreeNode visitForEffect(Expression node) {
bool wasVoidContext = isVoidContext;
isVoidContext = true;
try {
return node?.accept(this);
} finally {
isVoidContext = wasVoidContext;
}
}
ir.TreeNode visitWithCurrentContext(Expression node) => node?.accept(this);
withCurrentElement(AstElement element, f()) {
assert(element.library == kernel.compiler.currentElement.library);
Element previousElement = currentElement;
currentElement = element;
try {
return f();
} finally {
currentElement = previousElement;
}
}
ir.DartType computeType(TypeAnnotation node) {
if (node == null) return const ir.DynamicType();
return kernel.typeToIr(elements.getType(node));
}
// This works around a bug in dart2js.
// TODO(ahe): Fix the bug in dart2js and remove this function.
ir.DartType typeToIrHack(DartType type) {
if (currentElement.isSynthesized &&
currentElement.enclosingClass.isMixinApplication &&
!kernel.hasHierarchyProblem(currentElement.enclosingClass)) {
// Dart2js doesn't compute the correct functionSignature for synthetic
// constructors in mixin applications. So we compute the correct type:
// First, find the first superclass that isn't a mixin.
ClassElement superclass = currentElement.enclosingClass.superclass;
while (superclass.isMixinApplication) {
superclass = superclass.superclass;
}
// Then translate the "this type" of the mixin application to its
// supertype with the correct type arguments.
//
// Consider this example:
//
// class Super<S> {}
// class Sub<T> extends Object with Super<T> {}
//
// Here the problem is that dart2js has created a constructor that refers
// to S (not T) in Sub (for example, the return type of the constructor
// is Super<S> and it should be Sub<T>, but we settle for Super<T> for
// now). So we need to translate Sub<T> to an instance of Super, which is
// Super<T> (not Super<S>).
InterfaceType supertype =
currentElement.enclosingClass.asInstanceOf(superclass);
// Once we have [supertype], we know how to substitute S with T: the type
// arguments of [supertype] corresponds to T, and the type variables of
// its element correspond to S.
type =
type.subst(supertype.typeArguments, supertype.element.typeVariables);
}
return kernel.typeToIr(type);
}
// TODO(ahe): Hack. Fix dart2js instead.
ir.Name nameToIrName(Name name) {
assert(!name.isPrivate ||
name.library.implementation == currentElement.library.implementation);
return kernel.irName(name.text, currentElement);
}
List<ir.DartType> computeTypesFromTypes(NodeList nodes, {int expected}) {
if (expected == null) {
throw "[expected] is null";
}
List<ir.DartType> types = new List<ir.DartType>(expected);
Iterator<Node> iterator = nodes?.iterator;
for (int i = 0; i < expected; i++) {
TypeAnnotation type = null;
if (iterator != null && iterator.moveNext()) {
type = iterator.current;
}
types[i] = computeType(type);
}
if (iterator != null && iterator.moveNext()) {
// Should already have been reported by resolution.
// TODO(ahe): Delete this debug message.
kernel.debugMessage(iterator.current, "Extra type arguments.");
}
return types;
}
ir.DartType computeTypeFromTypes(NodeList node) {
return computeTypesFromTypes(node, expected: 1).single;
}
ir.MethodInvocation buildInvokeSelector(
ir.Expression receiver, Selector selector, ir.Arguments arguments) {
return new ir.MethodInvocation(
receiver, nameToIrName(selector.memberName), arguments);
}
ir.MethodInvocation buildCall(
ir.Expression receiver, CallStructure callStructure, NodeList arguments) {
return buildInvokeSelector(
receiver, callStructure.callSelector, buildArguments(arguments));
}
@override
ir.Expression visitIdentifier(Identifier node) {
// TODO(ahe): Shouldn't have to override this method, but
// [SemanticSendResolvedMixin.visitIdentifier] may return `null` on errors.
if (node.isThis()) {
return sendVisitor.visitThisGet(node, null);
} else {
return new ir.InvalidExpression();
}
}
@override
ir.InvalidExpression handleUnresolved(Node node) {
return new ir.InvalidExpression();
}
@override
ir.Expression handleError(Node node) => new ir.InvalidExpression();
@override
void apply(Node node, _) {
throw new UnsupportedError("apply");
}
@override
void previsitDeferredAccess(Send node, PrefixElement prefix, _) {}
@override
internalError(Spannable spannable, String message) {
kernel.internalError(spannable, message);
}
@override
applyParameters(NodeList parameters, _) {
throw new UnsupportedError("applyParameters");
}
@override
applyInitializers(FunctionExpression constructor, _) {
throw new UnsupportedError("applyInitializers");
}
@override
ir.AssertStatement visitAssert(Assert node) {
return new ir.AssertStatement(
visitForValue(node.condition), visitForValue(node.message));
}
ir.LabeledStatement getBreakTarget(JumpTarget target) {
return breakTargets.putIfAbsent(
target, () => new ir.LabeledStatement(null));
}
ir.LabeledStatement getContinueTarget(JumpTarget target) {
return continueTargets.putIfAbsent(
target, () => new ir.LabeledStatement(null));
}
ir.SwitchCase getContinueSwitchTarget(JumpTarget target) {
return continueSwitchTargets[target];
}
ir.Statement buildBreakTarget(
ir.Statement statement, Node node, JumpTarget jumpTarget) {
assert(node.isValidBreakTarget());
assert(jumpTarget == elements.getTargetDefinition(node));
if (jumpTarget != null && jumpTarget.isBreakTarget) {
ir.LabeledStatement breakTarget = getBreakTarget(jumpTarget);
breakTarget.body = statement;
statement.parent = breakTarget;
return breakTarget;
} else {
return statement;
}
}
ir.Statement buildContinueTarget(
ir.Statement statement, Node node, JumpTarget jumpTarget) {
assert(node.isValidContinueTarget());
assert(jumpTarget == elements.getTargetDefinition(node));
if (jumpTarget != null && jumpTarget.isContinueTarget) {
ir.LabeledStatement continueTarget = getContinueTarget(jumpTarget);
continueTarget.body = statement;
statement.parent = continueTarget;
return continueTarget;
} else {
return statement;
}
}
ir.Statement buildForInCommon(
ForIn node, ir.VariableDeclaration variable, ir.Statement body,
{bool isAsync}) {
ir.Expression iterable = visitForValue(node.expression);
JumpTarget jumpTarget = elements.getTargetDefinition(node);
body = buildContinueTarget(body, node, jumpTarget);
return buildBreakTarget(
associateNode(
new ir.ForInStatement(variable, iterable, body, isAsync: isAsync),
node),
node,
jumpTarget);
}
/// Builds a for-in statement for this case:
///
/// for (constOrVarOrType loopVariable in expression) body
ir.Statement buildForInWithDeclaration(
ForIn node, VariableDefinitions declaration,
{bool isAsync}) {
if (declaration.definitions.slowLength() != 1) {
// It's not legal to declare more than one variable in a for-in loop.
return new ir.InvalidStatement();
}
ir.VariableDeclaration variable = declaration.accept(this);
return buildForInCommon(node, variable, buildStatementInBlock(node.body),
isAsync: isAsync);
}
Accessor buildStaticAccessor(Element getter, [Element setter]) {
if (setter == null &&
getter != null &&
getter.isField &&
!getter.isFinal &&
!getter.isConst) {
setter = getter;
}
return new StaticAccessor(
(getter == null) ? null : kernel.elementToIr(getter),
(setter == null) ? null : kernel.elementToIr(setter));
}
Accessor computeAccessor(ForIn node, Element element) {
if (element == null) {
Send send = node.declaredIdentifier.asSend();
if (send == null) {
return buildStaticAccessor(null);
}
// This should be the situation where `node.declaredIdentifier` is
// unresolved, but in an instance method context. If it is some different
// situation, the assignment to [ir.PropertyGet] should act as an
// assertion.
ir.PropertyGet expression = visitForValue(send);
return PropertyAccessor.make(
expression.receiver, expression.name, null, null);
} else if (kernel.isSyntheticError(element)) {
return buildStaticAccessor(null);
} else if (element.isGetter) {
if (element.isInstanceMember) {
return new ThisPropertyAccessor(
kernel.irName(element.name, element), null, null);
} else {
GetterElement getter = element;
Element setter = getter.setter;
return buildStaticAccessor(getter, setter);
}
} else if (element.isLocal) {
return new VariableAccessor(getLocal(element));
} else if (element.isField) {
return buildStaticAccessor(element);
} else {
return buildStaticAccessor(null);
}
}
/// Builds a for-in statement for this case:
///
/// for (element in expression) body
///
/// This is normalized to:
///
/// for (final #t in expression) {
/// element = #t;
/// body;
/// }
ir.Statement buildForInWithoutDeclaration(ForIn node, Element element,
{bool isAsync}) {
Accessor accessor = computeAccessor(node, elements.getForInVariable(node));
// Since we've created [variable], we know it's only assigned to in the
// loop header and can be final.
ir.VariableDeclaration variable =
new ir.VariableDeclaration.forValue(null, isFinal: true);
ir.Statement assigment = new ir.ExpressionStatement(accessor
.buildAssignment(new ir.VariableGet(variable), voidContext: true));
ir.Block body = buildStatementInBlock(node.body, forceBlock: true);
List<ir.Statement> statements = <ir.Statement>[assigment]
..addAll(body.statements);
return buildForInCommon(node, variable, new ir.Block(statements),
isAsync: isAsync);
}
ir.Statement buildForIn(ForIn node, {bool isAsync}) {
VariableDefinitions declaration =
node.declaredIdentifier.asVariableDefinitions();
if (declaration != null) {
return buildForInWithDeclaration(node, declaration, isAsync: isAsync);
} else {
Element element = elements.getForInVariable(node);
return buildForInWithoutDeclaration(node, element, isAsync: isAsync);
}
}
@override
ir.Statement visitAsyncForIn(AsyncForIn node) {
return buildForIn(node, isAsync: true);
}
@override
ir.AwaitExpression visitAwait(Await node) {
return new ir.AwaitExpression(visitForValue(node.expression));
}
@override
ir.Statement visitBlock(Block node) {
return buildBreakTarget(
buildStatementInBlock(node), node, elements.getTargetDefinition(node));
}
bool buildStatement(Statement statement, List<ir.Statement> statements) {
ir.Node irNode = statement.accept(this);
bool hasVariableDeclaration = false;
if (irNode is VariableDeclarations) {
statements.addAll(irNode.variables);
hasVariableDeclaration = true;
} else {
statements.add(irNode);
if (irNode is ir.VariableDeclaration) {
hasVariableDeclaration = true;
}
}
return hasVariableDeclaration;
}
ir.Statement buildStatementInBlock(Statement node, {bool forceBlock: false}) {
if (node == null) return null;
List<ir.Statement> statements = <ir.Statement>[];
if (node is Block) {
for (Node statement in node.statements.nodes) {
buildStatement(statement, statements);
}
} else {
if (buildStatement(node, statements)) forceBlock = true;
if (!forceBlock && statements.length == 1) {
return statements.single;
}
// One VariableDefinitions statement node (dart2js AST) may generate
// multiple statements in Kernel IR so we sometimes fall through here.
}
return new ir.Block(statements);
}
@override
ir.Statement visitBreakStatement(BreakStatement node) {
JumpTarget target = elements.getTargetOf(node);
if (target == null || !target.statement.isValidBreakTarget()) {
// This is a break in an invalid position.
return new ir.InvalidStatement();
}
// A break can break to itself in the degenerate case `label: break
// label'`.
return buildBreakTarget(new ir.BreakStatement(getBreakTarget(target)), node,
elements.getTargetDefinition(node));
}
CascadeReceiver computeCascadeReceiver(Cascade cascade) {
CascadeReceiver receiver;
Expression send = cascade.expression.asSend();
while (send != null && (receiver = send.asCascadeReceiver()) == null) {
Expression possibleReceiver = send.asSend()?.receiver;
if (possibleReceiver != null) {
send = possibleReceiver;
} else {
// Can happen in this case: `a..add(foo)('WHAT')`.
send = send.asSend()?.selector;
}
}
if (receiver == null) {
internalError(cascade, "Can't find cascade receiver");
}
return receiver;
}
@override
ir.Let visitCascade(Cascade node) {
// Given this cascade expression `receiver..cascade1()..cascade2()`, the
// parser has produced a tree like this:
// Cascade(Send(
// CascadeReceiver(Cascade(Send(
// CascadeRecevier(receiver), 'cascade1', []))),
// 'cascade2', []))
// If viewed as a tree, `CascadeReceiver(receiver)` is the left-most leaf
// node. Below, we create this:
// cascades = [
// Cascade(Send(CascadeReceiver(...), 'cascade2', [])),
// Cascade(Send(CascadeReceiver(...), 'cascade1', []))]
// Notice that the cascades are in reverse order, which we use to build a
// `let` expression bottom up.
// First iteration of the loop produces:
// let dummy = rcv.cascade2() in
// rcv
// Second iteration:
// let dummy = rcv.cascade1() in
// let dummy = rcv.cascade2() in
// rcv
// Finally we return:
// let rcv = receiver in
// let dummy = rcv.cascade1() in
// let dummy = rcv.cascade2() in
// rcv
int startLength;
assert((startLength = cascadeReceivers.length) >= 0);
Cascade cascade = node;
List<Cascade> cascades = <Cascade>[];
CascadeReceiver receiver;
ir.VariableDeclaration receiverVariable = makeOrReuseVariable(null);
do {
cascades.add(cascade);
receiver = computeCascadeReceiver(cascade);
cascadeReceivers[receiver] = new ir.VariableGet(receiverVariable);
cascade = receiver.expression.asCascade();
} while (cascade != null);
// At this point, all nested [Cascades] targeting the same receiver have
// been collected in [cascades] in reverse order. [receiver] is the
// left-most receiver. [receiverVariable] will hold the value of evaluating
// [receiver]. Each [CascadeReceiver] has a getter for [receiverVariable]
// in [cascadeReceivers].
receiverVariable.initializer = visitForValue(receiver.expression);
receiverVariable.initializer.parent = receiverVariable;
ir.Expression result = new ir.VariableGet(receiverVariable); // rcv.
for (Cascade cascade in cascades) {
// When evaluating `cascade.expression`, we stop the recursion at
// [visitCascadeReceiver] and instead returns an [ir.VariableGet].
// TODO(ahe): Use visitForEffect here?
ir.Expression value = visitForValue(cascade.expression);
result = new ir.Let(makeOrReuseVariable(value), result);
}
assert(startLength == cascadeReceivers.length);
return new ir.Let(receiverVariable, result);
}
@override
ir.VariableGet visitCascadeReceiver(CascadeReceiver node) {
return cascadeReceivers.remove(node);
}
@override
visitCaseMatch(CaseMatch node) {
// Shouldn't be called. Handled by [visitSwitchCase].
return internalError(node, "CaseMatch");
}
@override
ir.Catch visitCatchBlock(CatchBlock node) {
ir.VariableDeclaration exception =
(node.exception == null) ? null : getLocal(elements[node.exception]);
ir.VariableDeclaration trace =
(node.trace == null) ? null : getLocal(elements[node.trace]);
ir.DartType guard = computeType(node.type);
return new ir.Catch(exception, buildStatementInBlock(node.block),
guard: guard, stackTrace: trace);
}
@override
ir.ConditionalExpression visitConditional(Conditional node) {
return new ir.ConditionalExpression(
visitForValue(node.condition),
visitWithCurrentContext(node.thenExpression),
visitWithCurrentContext(node.elseExpression),
null);
}
@override
ir.Statement visitContinueStatement(ContinueStatement node) {
JumpTarget target = elements.getTargetOf(node);
if (target == null || !target.statement.isValidContinueTarget()) {
// This is a continue in an invalid position.
return new ir.InvalidStatement();
}
ir.SwitchCase switchCase = getContinueSwitchTarget(target);
return (switchCase == null)
? new ir.BreakStatement(getContinueTarget(target))
: new ir.ContinueSwitchStatement(switchCase);
}
@override
ir.Statement visitDoWhile(DoWhile node) {
JumpTarget jumpTarget = elements.getTargetDefinition(node);
ir.Statement body =
buildContinueTarget(buildStatementInBlock(node.body), node, jumpTarget);
ir.Expression condition = visitForValue(node.condition);
return buildBreakTarget(
new ir.DoStatement(body, condition), node, jumpTarget);
}
@override
ir.EmptyStatement visitEmptyStatement(EmptyStatement node) {
return new ir.EmptyStatement();
}
@override
visitEnum(Enum node) {
// Not called normally. In dart2js, enums are represented as class
// elements, so `classToIr` handles enums. All the synthetic members of an
// enum class have already been installed by dart2js and we don't have to
// do anything special.
return internalError(node, "Enum");
}
@override
ir.ExpressionStatement visitExpressionStatement(ExpressionStatement node) {
return new ir.ExpressionStatement(visitForEffect(node.expression));
}
@override
ir.Statement visitFor(For node) {
VariableDefinitions initializers =
node.initializer?.asVariableDefinitions();
ir.Expression initializer;
List<ir.VariableDeclaration> variables;
if (initializers != null) {
ir.Block block = buildStatementInBlock(initializers, forceBlock: true);
variables = new List<ir.VariableDeclaration>.from(block.statements);
} else {
if (node.initializer != null) {
initializer = visitForValue(node.initializer);
}
variables = const <ir.VariableDeclaration>[];
}
ir.Expression condition = visitForValue(node.condition);
List<ir.Expression> updates = <ir.Expression>[];
for (Expression update in node.update) {
updates.add(visitForEffect(update));
}
JumpTarget jumpTarget = elements.getTargetDefinition(node);
ir.Statement body =
buildContinueTarget(buildStatementInBlock(node.body), node, jumpTarget);
ir.ForStatement forStatement = associateNode(
new ir.ForStatement(variables, condition, updates, body), node);
ir.Statement result = buildBreakTarget(forStatement, node, jumpTarget);
if (initializer != null) {
result = new ir.Block(
<ir.Statement>[new ir.ExpressionStatement(initializer), result]);
}
return result;
}
@override
ir.FunctionDeclaration visitFunctionDeclaration(FunctionDeclaration node) {
return node.function.accept(this);
}
@override
ir.Statement visitIf(If node) {
return buildBreakTarget(
new ir.IfStatement(
visitForValue(node.condition),
buildStatementInBlock(node.thenPart),
buildStatementInBlock(node.elsePart)),
node,
elements.getTargetDefinition(node));
}
@override
visitLabel(Label node) {
// Shouldn't be called. Handled by visitLabeledStatement and
// visitSwitchCase.
return internalError(node, "Label");
}
@override
ir.Statement visitLabeledStatement(LabeledStatement node) {
Statement statement = node.statement;
ir.Statement result = (statement is Block)
// If [statement] is a Block, we need to ensure that we don't bypass
// its visit method (so it can build break targets correctly).
? statement.accept(this)
: buildStatementInBlock(statement);
// A [LabeledStatement] isn't the actual jump target, instead, [statement]
// is the target. This allows uniform handling of break and continue in
// loops. The following code simply assert that [result] has been generated
// correctly with respect to jump targets.
JumpTarget jumpTarget = elements.getTargetDefinition(node.statement);
if (jumpTarget != null) {
if (jumpTarget.isBreakTarget) {
ir.LabeledStatement target = breakTargets[jumpTarget];
if (target != null && target != result && target.parent == null) {
internalError(node, "no parent");
}
}
if (jumpTarget.isContinueTarget) {
ir.LabeledStatement target = continueTargets[jumpTarget];
if (target != null && target != result && target.parent == null) {
internalError(node, "no parent");
}
}
}
return result;
}
@override
ir.BoolLiteral visitLiteralBool(LiteralBool node) {
return new ir.BoolLiteral(node.value);
}
@override
ir.DoubleLiteral visitLiteralDouble(LiteralDouble node) {
return new ir.DoubleLiteral(node.value);
}
@override
ir.IntLiteral visitLiteralInt(LiteralInt node) {
return new ir.IntLiteral(node.value);
}
@override
ir.ListLiteral visitLiteralList(LiteralList node) {
// TODO(ahe): Type arguments.
List<ir.Expression> elements = <ir.Expression>[];
for (Expression element in node.elements.nodes) {
elements.add(visitForValue(element));
}
return associateNode(
new ir.ListLiteral(elements,
typeArgument: computeTypeFromTypes(node.typeArguments),
// TODO(ahe): Should constness be validated?
isConst: node.isConst),
node);
}
@override
ir.MapLiteral visitLiteralMap(LiteralMap node) {
// TODO(ahe): Type arguments.
List<ir.MapEntry> entries = <ir.MapEntry>[];
for (LiteralMapEntry entry in node.entries.nodes) {
entries.add(new ir.MapEntry(
visitForValue(entry.key), visitForValue(entry.value)));
}
List<ir.DartType> typeArguments =
computeTypesFromTypes(node.typeArguments, expected: 2);
return associateNode(
new ir.MapLiteral(entries,
keyType: typeArguments.first,
valueType: typeArguments.last,
// TODO(ahe): Should Constness be validated?
isConst: node.isConst),
node);
}
@override
visitLiteralMapEntry(LiteralMapEntry node) {
// Shouldn't be called. Handled by [visitLiteralMap].
return internalError(node, "LiteralMapEntry");
}
@override
ir.NullLiteral visitLiteralNull(LiteralNull node) {
return new ir.NullLiteral();
}
@override
ir.Expression visitLiteralString(LiteralString node) {
if (node.dartString == null) return new ir.InvalidExpression();
return new ir.StringLiteral(node.dartString.slowToString());
}
@override
ir.SymbolLiteral visitLiteralSymbol(LiteralSymbol node) {
var result = new ir.SymbolLiteral(node.slowNameString);
return associateNode(result, node);
}
@override
visitMetadata(Metadata node) {
// Shouldn't be called. Metadata should already have been analyzed and
// converted to a constant expression in the resolver.
return internalError(node, "Metadata not handled as constant.");
}
@override
ir.NamedExpression visitNamedArgument(NamedArgument node) {
return new ir.NamedExpression(
node.name.source, visitForValue(node.expression));
}
@override
visitOperator(Operator node) {
// This is a special subclass of [Identifier], and we should never see that
// in the semantic visitor.
return internalError(node, "Operator");
}
@override
ir.Expression visitParenthesizedExpression(ParenthesizedExpression node) {
return visitWithCurrentContext(node.expression);
}
@override
ir.InvalidStatement visitRedirectingFactoryBody(RedirectingFactoryBody node) {
// Not implemented yet, only serves to recover from parser errors as
// dart2js is lenient in parsing something that looks like a redirecting
// factory method.
return new ir.InvalidStatement();
}
@override
ir.ExpressionStatement visitRethrow(Rethrow node) {
return new ir.ExpressionStatement(new ir.Rethrow());
}
@override
ir.ReturnStatement visitReturn(Return node) {
return new ir.ReturnStatement(visitForValue(node.expression));
}
@override
ir.StringConcatenation visitStringInterpolation(StringInterpolation node) {
List<ir.Expression> expressions = <ir.Expression>[];
expressions.add(visitForValue(node.string));
for (StringInterpolationPart part in node.parts) {
expressions.add(visitForValue(part.expression));
expressions.add(visitForValue(part.string));
}
return new ir.StringConcatenation(expressions);
}
@override
ir.StringConcatenation visitStringJuxtaposition(StringJuxtaposition node) {
return new ir.StringConcatenation(
<ir.Expression>[visitForValue(node.first), visitForValue(node.second)]);
}
@override
ir.SwitchCase visitSwitchCase(SwitchCase node) {
List<ir.Expression> expressions = <ir.Expression>[];
for (var labelOrCase in node.labelsAndCases.nodes) {
CaseMatch match = labelOrCase.asCaseMatch();
if (match != null) {
expressions.add(visitForValue(match.expression));
} else {
// Assert that labelOrCase is one of two known types: [CaseMatch] or
// [Label]. We ignore cases, as any users have been resolved to use the
// case directly.
assert(labelOrCase.asLabel() != null);
}
}
// We ignore the node's statements here, they're generated below in
// [visitSwitchStatement] once we've set up all the jump targets.
return new ir.SwitchCase(expressions, null, isDefault: node.isDefaultCase);
}
/// Returns true if [node] would let execution reach the next node (aka
/// fall-through in switch cases).
bool fallsThrough(ir.Statement node) {
return !(node is ir.BreakStatement ||
node is ir.ReturnStatement ||
node is ir.ContinueSwitchStatement ||
(node is ir.ExpressionStatement && node.expression is ir.Throw));
}
@override
ir.Statement visitSwitchStatement(SwitchStatement node) {
ir.Expression expression = visitForValue(node.expression);
List<ir.SwitchCase> cases = <ir.SwitchCase>[];
for (SwitchCase caseNode in node.cases.nodes) {
cases.add(caseNode.accept(this));
JumpTarget jumpTarget = elements.getTargetDefinition(caseNode);
if (jumpTarget != null) {
assert(jumpTarget.isContinueTarget);
assert(!continueSwitchTargets.containsKey(jumpTarget));
continueSwitchTargets[jumpTarget] = cases.last;
}
}
Iterator<ir.SwitchCase> casesIterator = cases.iterator;
for (Link<Node> link = node.cases.nodes;
link.isNotEmpty;
link = link.tail) {
SwitchCase caseNode = link.head;
bool isLastCase = link.tail.isEmpty;
if (!casesIterator.moveNext()) {
internalError(caseNode, "case node mismatch");
}
ir.SwitchCase irCase = casesIterator.current;
List<ir.Statement> statements = <ir.Statement>[];
bool hasVariableDeclaration = false;
for (Statement statement in caseNode.statements.nodes) {
if (buildStatement(statement, statements)) {
hasVariableDeclaration = true;
}
}
if (statements.isEmpty || fallsThrough(statements.last)) {
if (isLastCase) {
statements.add(new ir.BreakStatement(
getBreakTarget(elements.getTargetDefinition(node))));
} else {
statements.add(new ir.ExpressionStatement(new ir.Throw(
new ir.ConstructorInvocation(
kernel.getFallThroughErrorConstructor(),
new ir.Arguments.empty()))));
}
}
ir.Statement body = new ir.Block(statements);
irCase.body = body;
body.parent = irCase;
}
assert(!casesIterator.moveNext());
return buildBreakTarget(new ir.SwitchStatement(expression, cases), node,
elements.getTargetDefinition(node));
}
@override
ir.Statement visitSyncForIn(SyncForIn node) {
return buildForIn(node, isAsync: false);
}
@override
ir.Throw visitThrow(Throw node) {
return new ir.Throw(visitForValue(node?.expression));
}
@override
ir.Statement visitTryStatement(TryStatement node) {
ir.Statement result = buildStatementInBlock(node.tryBlock);
if (node.catchBlocks != null && !node.catchBlocks.isEmpty) {
List<ir.Catch> catchBlocks = <ir.Catch>[];
for (CatchBlock block in node.catchBlocks.nodes) {
catchBlocks.add(block.accept(this));
}
result = new ir.TryCatch(result, catchBlocks);
}
if (node.finallyBlock != null) {
result =
new ir.TryFinally(result, buildStatementInBlock(node.finallyBlock));
}
return buildBreakTarget(result, node, elements.getTargetDefinition(node));
}
@override
visitTypeAnnotation(TypeAnnotation node) {
// Shouldn't be called, as the resolver have already resolved types and
// created [DartType] objects.
return internalError(node, "TypeAnnotation");
}
@override
visitTypeVariable(TypeVariable node) {
// Shouldn't be called, as the resolver have already resolved types and
// created [DartType] objects.
return internalError(node, "TypeVariable");
}
@override
ir.Statement visitWhile(While node) {
ir.Expression condition = visitForValue(node.condition);
JumpTarget jumpTarget = elements.getTargetDefinition(node);
ir.Statement body =
buildContinueTarget(buildStatementInBlock(node.body), node, jumpTarget);
return buildBreakTarget(
associateNode(new ir.WhileStatement(condition, body), node),
node,
jumpTarget);
}
@override
ir.YieldStatement visitYield(Yield node) {
return new ir.YieldStatement(visitForValue(node.expression),
isYieldStar: node.hasStar);
}
@override
ir.InvalidExpression visitAbstractClassConstructorInvoke(
NewExpression node,
ConstructorElement element,
InterfaceType type,
NodeList arguments,
CallStructure callStructure,
_) {
return new ir.InvalidExpression();
}
IrFunction buildIrFunction(
ir.ProcedureKind kind, FunctionElement function, Node body) {
return new IrFunction.procedure(kind, buildFunctionNode(function, body));
}
@override
IrFunction visitAbstractGetterDeclaration(
FunctionExpression node, MethodElement getter, _) {
return buildIrFunction(ir.ProcedureKind.Getter, getter, null);
}
@override
IrFunction visitAbstractSetterDeclaration(
FunctionExpression node, MethodElement setter, NodeList parameters, _) {
return buildIrFunction(ir.ProcedureKind.Setter, setter, null);
}
@override
ir.AsExpression visitAs(Send node, Node expression, DartType type, _) {
return new ir.AsExpression(
visitForValue(expression), kernel.typeToIr(type));
}
@override
ir.MethodInvocation visitBinary(
Send node, Node left, BinaryOperator operator, Node right, _) {
return associateNode(
buildBinaryOperator(left, operator.selectorName, right), node);
}
ir.Expression buildConstructorInvoke(NewExpression node, {bool isConst}) {
ConstructorElement constructor = elements[node.send];
ConstructorTarget target =
kernel.computeEffectiveTarget(constructor, elements.getType(node));
NodeList arguments = node.send.argumentsNode;
if (kernel.isSyntheticError(target.element)) {
return new ir.MethodInvocation(new ir.InvalidExpression(),
kernel.irName("call", currentElement), buildArguments(arguments));
}
ir.InvocationExpression invoke = target.element.isGenerativeConstructor
? buildGenerativeConstructorInvoke(target.element, arguments,
isConst: isConst)
: buildStaticInvoke(target.element, arguments, isConst: isConst);
if (target.type.isInterfaceType) {
InterfaceType type = target.type;
if (type.isGeneric) {
invoke.arguments.types.addAll(kernel.typesToIr(type.typeArguments));
}
}
return invoke;
}
@override
ir.InvocationExpression visitBoolFromEnvironmentConstructorInvoke(
NewExpression node, BoolFromEnvironmentConstantExpression constant, _) {
return buildConstructorInvoke(node, isConst: true);
}
ir.TypeLiteral buildTypeLiteral(TypeConstantExpression constant) {
return new ir.TypeLiteral(kernel.typeLiteralToIr(constant));
}
@override
ir.TypeLiteral visitClassTypeLiteralGet(
Send node, ConstantExpression constant, _) {
return buildTypeLiteral(constant);
}
@override
ir.MethodInvocation visitClassTypeLiteralInvoke(
Send node,
ConstantExpression constant,
NodeList arguments,
CallStructure callStructure,
_) {
return buildCall(buildTypeLiteral(constant), callStructure, arguments);
}
ir.Expression buildTypeLiteralSet(TypeConstantExpression constant, Node rhs) {
return new ReadOnlyAccessor(buildTypeLiteral(constant))
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression visitClassTypeLiteralSet(
SendSet node, ConstantExpression constant, Node rhs, _) {
return buildTypeLiteralSet(constant, rhs);
}
@override
ir.FunctionExpression visitClosureDeclaration(FunctionExpression node,
LocalFunctionElement closure, NodeList parameters, Node body, _) {
return withCurrentElement(closure, () {
ir.FunctionExpression function =
new ir.FunctionExpression(buildFunctionNode(closure, body));
kernel.localFunctions[closure] = function;
return function;
});
}
@override
ir.Expression visitCompoundIndexSet(SendSet node, Node receiver, Node index,
AssignmentOperator operator, Node rhs, _) {
// TODO(sra): Find binary operator.
return buildIndexAccessor(receiver, index).buildCompoundAssignment(
kernel.irName(operator.selectorName, currentElement),
visitForValue(rhs),
voidContext: isVoidContext);
}
@override
ir.InvocationExpression visitConstConstructorInvoke(
NewExpression node, ConstructedConstantExpression constant, _) {
return buildConstructorInvoke(node, isConst: true);
}
@override
visitConstantGet(Send node, ConstantExpression constant, _) {
// TODO(ahe): This method is never called. Is it a bug in semantic visitor?
return internalError(node, "ConstantGet");
}
@override
visitConstantInvoke(Send node, ConstantExpression constant,
NodeList arguments, CallStructure callStructure, _) {
// TODO(ahe): This method is never called. Is it a bug in semantic visitor?
return internalError(node, "ConstantInvoke");
}
@override
ir.InvalidExpression visitConstructorIncompatibleInvoke(
NewExpression node,
ConstructorElement constructor,
InterfaceType type,
NodeList arguments,
CallStructure callStructure,
_) {
return new ir.InvalidExpression();
}
@override
ir.PropertyGet visitDynamicPropertyGet(
Send node, Node receiver, Name name, _) {
return associateNode(
new ir.PropertyGet(visitForValue(receiver), nameToIrName(name)), node);
}
@override
ir.MethodInvocation visitDynamicPropertyInvoke(
Send node, Node receiver, NodeList arguments, Selector selector, _) {
return associateNode(
buildInvokeSelector(
visitForValue(receiver), selector, buildArguments(arguments)),
node);
}
@override
ir.Expression handleDynamicCompounds(
Send node, Node receiver, Name name, CompoundRhs rhs, _) {
ir.Expression receiverNode =
receiver == null ? new ir.ThisExpression() : visitForValue(receiver);
ir.Expression compound = buildCompound(
PropertyAccessor.make(receiverNode, nameToIrName(name), null, null),
rhs,
node);
if (compound is ir.VariableSet) {
associateNode(compound.value, node);
} else {
associateNode(compound, node);
}
return compound;
}
@override
ir.PropertySet visitDynamicPropertySet(
SendSet node, Node receiver, Name name, Node rhs, _) {
ir.Expression value = visitForValue(rhs);
return new ir.PropertySet(
visitForValue(receiver), nameToIrName(name), value);
}
@override
ir.Expression handleDynamicSetIfNulls(
Send node, Node receiver, Name name, Node rhs, _) {
ir.Name irName = nameToIrName(name);
Accessor accessor = (receiver == null)
? new ThisPropertyAccessor(irName, null, null)
: PropertyAccessor.make(visitForValue(receiver), irName, null, null);
return accessor.buildNullAwareAssignment(visitForValue(rhs), null,
voidContext: isVoidContext);
}
@override
ir.TypeLiteral visitDynamicTypeLiteralGet(
Send node, ConstantExpression constant, _) {
return buildTypeLiteral(constant);
}
@override
ir.MethodInvocation visitDynamicTypeLiteralInvoke(
Send node,
ConstantExpression constant,
NodeList arguments,
CallStructure callStructure,
_) {
return buildCall(buildTypeLiteral(constant), callStructure, arguments);
}
@override
ir.Expression visitDynamicTypeLiteralSet(
SendSet node, ConstantExpression constant, Node rhs, _) {
return buildTypeLiteralSet(constant, rhs);
}
ir.MethodInvocation buildBinaryOperator(
Node left, String operator, Node right) {
ir.Name name = kernel.irName(operator, currentElement);
return makeBinary(visitForValue(left), name, null, visitForValue(right));
}
@override
ir.MethodInvocation visitEquals(Send node, Node left, Node right, _) {
return associateNode(buildBinaryOperator(left, '==', right), node);
}
@override
ir.MethodInvocation visitExpressionInvoke(Send node, Node expression,
NodeList arguments, CallStructure callStructure, _) {
return associateNode(
buildCall(visitForValue(expression), callStructure, arguments), node);
}
@override
IrFunction visitFactoryConstructorDeclaration(FunctionExpression node,
ConstructorElement constructor, NodeList parameters, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Factory, constructor, body);
}
@override
ir.InvocationExpression visitFactoryConstructorInvoke(
NewExpression node,
ConstructorElement constructor,
InterfaceType type,
NodeList arguments,
CallStructure callStructure,
_) {
return buildConstructorInvoke(node, isConst: false);
}
@override
ir.Initializer visitFieldInitializer(
SendSet node, FieldElement field, Node expression, _) {
if (kernel.isSyntheticError(field)) {
return new ir.InvalidInitializer();
} else {
return new ir.FieldInitializer(
kernel.fieldToIr(field), visitForValue(expression));
}
}
ir.Expression buildStaticFieldSet(FieldElement field, Node rhs) {
return buildStaticAccessor(field)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression handleFinalStaticFieldSet(
SendSet node, FieldElement field, Node rhs, _) {
return buildStaticFieldSet(field, rhs);
}
@override
ir.Expression visitFinalSuperFieldSet(
SendSet node, FieldElement field, Node rhs, _) {
return buildSuperPropertyAccessor(field)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
IrFunction buildGenerativeConstructor(
ConstructorElement constructor, NodeList parameters, Node body) {
List<ir.Initializer> constructorInitializers = <ir.Initializer>[];
if (kernel.hasHierarchyProblem(constructor.enclosingClass)) {
constructorInitializers.add(new ir.InvalidInitializer());
} else if (constructor.isSynthesized) {
List<ir.Expression> arguments = const <ir.Expression>[];
List<ir.NamedExpression> named = const <ir.NamedExpression>[];
FunctionSignature signature = constructor.functionSignature;
if (signature.parameterCount != 0) {
// Mixin application implicit super call.
arguments = <ir.Expression>[];
named = <ir.NamedExpression>[];
signature.orderedForEachParameter((ParameterElement parameter) {
ir.VariableGet argument = buildLocalGet(parameter);
if (parameter.isNamed) {
named.add(new ir.NamedExpression(parameter.name, argument));
} else {
arguments.add(argument);
}
});
}
if (kernel.isSyntheticError(constructor.definingConstructor)) {
constructorInitializers.add(new ir.InvalidInitializer());
} else {
constructorInitializers.add(new ir.SuperInitializer(
kernel.functionToIr(constructor.definingConstructor),
new ir.Arguments(arguments, named: named, types: null)));
}
} else {
if (parameters != null) {
// TODO(ahe): the following is a (modified) copy of
// [SemanticDeclarationResolvedMixin.visitParameters].
List<ParameterStructure> structures =
computeParameterStructures(parameters);
for (ParameterStructure structure in structures) {
if (structure.parameter.isInitializingFormal) {
constructorInitializers.add(structure.dispatch(declVisitor, null));
}
}
}
// TODO(ahe): the following is a (modified) copy of
// [SemanticDeclarationResolvedMixin.visitInitializers].
InitializersStructure initializers =
computeInitializersStructure(constructor.node);
for (InitializerStructure structure in initializers.initializers) {
constructorInitializers.add(structure.dispatch(declVisitor, null));
}
}
return new IrFunction.constructor(
buildFunctionNode(constructor, body), constructorInitializers);
}
@override
IrFunction visitGenerativeConstructorDeclaration(
FunctionExpression node,
ConstructorElement constructor,
NodeList parameters,
NodeList initializers,
Node body,
_) {
return buildGenerativeConstructor(constructor, parameters, body);
}
ir.ConstructorInvocation buildGenerativeConstructorInvoke(
ConstructorElement constructor, NodeList arguments,
{bool isConst}) {
if (const bool.fromEnvironment("require_kernel_arguments")) {
// Check that all constructors from kernel/ast.dart (that are invoked
// from this package) provide all arguments (including optional
// arguments).
// TODO(ahe): Remove this when the implementation has matured.
if (("package:kernel/ast.dart" ==
"${constructor.library.canonicalUri}") &&
"${currentElement.library.canonicalUri}"
.startsWith("package:rasta/")) {
if (constructor.functionSignature.parameterCount !=
arguments.slowLength()) {
kernel.debugMessage(arguments, "Missing arguments");
kernel.debugMessage(constructor, "When calling the constructor");
}
}
}
ir.Arguments argumentsNode = buildArguments(arguments);
ir.Constructor target = kernel.functionToIr(constructor);
return new ir.ConstructorInvocation(target, argumentsNode,
isConst: isConst);
}
@override
ir.InvocationExpression visitGenerativeConstructorInvoke(
NewExpression node,
ConstructorElement constructor,
InterfaceType type,
NodeList arguments,
CallStructure callStructure,
_) {
return buildConstructorInvoke(node, isConst: false);
}
Accessor buildNullAwarePropertyAccessor(Node receiver, Name name) {
return new NullAwarePropertyAccessor(
visitForValue(receiver), nameToIrName(name), null, null, null);
}
@override
ir.Expression visitIfNotNullDynamicPropertyGet(
Send node, Node receiver, Name name, _) {
return buildNullAwarePropertyAccessor(receiver, name).buildSimpleRead();
}
@override
ir.Let visitIfNotNullDynamicPropertyInvoke(
Send node, Node receiverNode, NodeList arguments, Selector selector, _) {
ir.VariableDeclaration receiver =
makeOrReuseVariable(visitForValue(receiverNode));
return makeLet(
receiver,
new ir.ConditionalExpression(
buildIsNull(new ir.VariableGet(receiver)),
new ir.NullLiteral(),
buildInvokeSelector(new ir.VariableGet(receiver), selector,
buildArguments(arguments)),
null));
}
@override
ir.Expression visitIfNotNullDynamicPropertySet(
SendSet node, Node receiver, Name name, Node rhs, _) {
return buildNullAwarePropertyAccessor(receiver, name)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression visitIfNotNullDynamicPropertySetIfNull(
Send node, Node receiver, Name name, Node rhs, _) {
return buildNullAwarePropertyAccessor(receiver, name)
.buildNullAwareAssignment(visitForValue(rhs), null,
voidContext: isVoidContext);
}
ir.LogicalExpression buildLogicalExpression(
Node left, Operator operator, Node right) {
return new ir.LogicalExpression(
visitForValue(left), operator.source, visitForValue(right));
}
@override
ir.Expression visitIfNull(Send node, Node left, Node right, _) {
var leftValue = new ir.VariableDeclaration.forValue(visitForValue(left));
return new ir.Let(
leftValue,
new ir.ConditionalExpression(buildIsNull(new ir.VariableGet(leftValue)),
visitForValue(right), new ir.VariableGet(leftValue), null));
}
@override
ir.Initializer visitImplicitSuperConstructorInvoke(FunctionExpression node,
ConstructorElement superConstructor, InterfaceType type, _) {
if (superConstructor == null) {
// TODO(ahe): Semantic visitor shouldn't call this.
return new ir.InvalidInitializer();
}
return new ir.SuperInitializer(
kernel.functionToIr(superConstructor), new ir.Arguments.empty());
}
Accessor buildIndexAccessor(Node receiver, Node index) {
return IndexAccessor.make(
visitForValue(receiver), visitForValue(index), null, null);
}
@override
ir.Expression visitIndex(Send node, Node receiver, Node index, _) {
return associateNode(
buildIndexAccessor(receiver, index).buildSimpleRead(), node);
}
ir.Expression buildIndexPostfix(Accessor accessor, IncDecOperator operator) {
ir.Name name = kernel.irName(operator.selectorName, currentElement);
return accessor.buildPostfixIncrement(name, voidContext: isVoidContext);
}
@override
ir.Expression visitIndexPostfix(
Send node, Node receiver, Node index, IncDecOperator operator, _) {
return buildIndexPostfix(buildIndexAccessor(receiver, index), operator);
}
ir.Expression buildIndexPrefix(Accessor accessor, IncDecOperator operator) {
ir.Name name = kernel.irName(operator.selectorName, currentElement);
return accessor.buildPrefixIncrement(name, voidContext: isVoidContext);
}
@override
ir.Expression visitIndexPrefix(
Send node, Node receiver, Node index, IncDecOperator operator, _) {
return buildIndexPrefix(buildIndexAccessor(receiver, index), operator);
}
@override
ir.Expression visitIndexSet(
SendSet node, Node receiver, Node index, Node rhs, _) {
return associateNode(
buildIndexAccessor(receiver, index)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext),
node);
}
ir.Initializer buildInitializingFormal(InitializingFormalElement parameter) {
FieldElement field = parameter.fieldElement;
if (kernel.isSyntheticError(field)) {
return new ir.InvalidInitializer();
} else {
return new ir.FieldInitializer(
kernel.fieldToIr(field), buildLocalGet(parameter));
}
}
@override
ir.Initializer visitInitializingFormalDeclaration(VariableDefinitions node,
Node definition, InitializingFormalElement parameter, int index, _) {
return buildInitializingFormal(parameter);
}
@override
visitInstanceFieldDeclaration(VariableDefinitions node, Node definition,
FieldElement field, Node initializer, _) {
// Shouldn't be called, handled by fieldToIr.
return internalError(node, "InstanceFieldDeclaration");
}
@override
IrFunction visitInstanceGetterDeclaration(
FunctionExpression node, MethodElement getter, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Getter, getter, body);
}
@override
IrFunction visitInstanceSetterDeclaration(FunctionExpression node,
MethodElement setter, NodeList parameters, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Setter, setter, body);
}
@override
ir.InvocationExpression visitIntFromEnvironmentConstructorInvoke(
NewExpression node, IntFromEnvironmentConstantExpression constant, _) {
return buildConstructorInvoke(node, isConst: true);
}
ir.IsExpression buildIs(Node expression, DartType type) {
return new ir.IsExpression(
visitForValue(expression), kernel.typeToIr(type));
}
@override
ir.IsExpression visitIs(Send node, Node expression, DartType type, _) {
return buildIs(expression, type);
}
@override
ir.Not visitIsNot(Send node, Node expression, DartType type, _) {
return new ir.Not(buildIs(expression, type));
}
ir.VariableDeclaration buildLocalVariableDeclaration(
LocalVariableElement variable, Node initializer) {
ir.Expression initializerNode = visitForValue(initializer);
ir.VariableDeclaration local = getLocal(variable);
if (initializer != null) {
local.initializer = initializerNode;
initializerNode.parent = local;
}
return local;
}
@override
ir.VariableDeclaration visitLocalConstantDeclaration(
VariableDefinitions node,
Node definition,
LocalVariableElement variable,
ConstantExpression constant,
_) {
// TODO(ahe): Use [constant]?
return buildLocalVariableDeclaration(variable, variable.initializer)
..isConst = true;
}
@override
ir.FunctionDeclaration visitLocalFunctionDeclaration(FunctionExpression node,
LocalFunctionElement localFunction, NodeList parameters, Node body, _) {
return withCurrentElement(localFunction, () {
ir.VariableDeclaration local = getLocal(localFunction)..isFinal = true;
ir.FunctionDeclaration function = new ir.FunctionDeclaration(
local, buildFunctionNode(localFunction, body));
// Closures can escape their context and we must therefore store them
// globally to include them in the world computation.
kernel.localFunctions[localFunction] = function;
return function;
});
}
@override
ir.VariableDeclaration visitLocalVariableDeclaration(VariableDefinitions node,
Node definition, LocalVariableElement variable, Node initializer, _) {
return buildLocalVariableDeclaration(variable, initializer);
}
ir.VariableGet buildLocalGet(LocalElement local) {
return new ir.VariableGet(getLocal(local));
}
@override
ir.VariableGet handleLocalGet(Send node, LocalElement element, _) {
return buildLocalGet(element);
}
ir.Expression buildCompound(
Accessor accessor, CompoundRhs rhs, SendSet node) {
ir.Name name = kernel.irName(rhs.operator.selectorName, currentElement);
ir.Expression result;
switch (rhs.kind) {
case CompoundKind.POSTFIX:
result =
accessor.buildPostfixIncrement(name, voidContext: isVoidContext);
break;
case CompoundKind.PREFIX:
result =
accessor.buildPrefixIncrement(name, voidContext: isVoidContext);
break;
case CompoundKind.ASSIGNMENT:
result = accessor.buildCompoundAssignment(name, visitForValue(rhs.rhs),
voidContext: isVoidContext);
break;
}
assert(accessor.builtBinary != null);
kernel.nodeToAstOperator[accessor.builtBinary] = node;
if (accessor.builtGetter != null) {
kernel.nodeToAst[accessor.builtGetter] = node;
}
return result;
}
@override
ir.Expression handleLocalCompounds(
SendSet node, LocalElement local, CompoundRhs rhs, _,
{bool isSetterValid}) {
ir.Expression compound =
buildCompound(new VariableAccessor(getLocal(local)), rhs, node);
if (compound is ir.VariableSet) {
associateNode(compound.value, node);
} else {
associateNode(compound, node);
}
return compound;
}
@override
ir.VariableSet handleLocalSet(
SendSet node, LocalElement element, Node rhs, _) {
return new ir.VariableSet(getLocal(element), visitForValue(rhs));
}
@override
ir.VariableSet handleImmutableLocalSet(
SendSet node, LocalElement element, Node rhs, _) {
// TODO(ahe): Build invalid?
return handleLocalSet(node, element, rhs, _);
}
@override
ir.LogicalExpression visitLogicalAnd(Send node, Node left, Node right, _) {
return buildLogicalExpression(left, node.selector, right);
}
@override
ir.LogicalExpression visitLogicalOr(Send node, Node left, Node right, _) {
return buildLogicalExpression(left, node.selector, right);
}
@override
ir.Initializer visitNamedInitializingFormalDeclaration(
VariableDefinitions node,
Node definition,
InitializingFormalElement parameter,
ConstantExpression defaultValue,
_) {
return buildInitializingFormal(parameter);
}
@override
visitNamedParameterDeclaration(VariableDefinitions node, Node definition,
ParameterElement parameter, ConstantExpression defaultValue, _) {
// Shouldn't be called, we handle parameters via [FunctionSignture].
return internalError(node, "NamedParameterDeclaration");
}
@override
ir.Not visitNot(Send node, Node expression, _) {
return new ir.Not(visitForValue(expression));
}
@override
ir.Not visitNotEquals(Send node, Node left, Node right, _) {
return associateNode(
new ir.Not(associateNode(buildBinaryOperator(left, '==', right), node)),
node);
}
@override
ir.Initializer visitOptionalInitializingFormalDeclaration(
VariableDefinitions node,
Node definition,
InitializingFormalElement parameter,
ConstantExpression defaultValue,
int index,
_) {
return buildInitializingFormal(parameter);
}
@override
visitOptionalParameterDeclaration(
VariableDefinitions node,
Node definition,
ParameterElement parameter,
ConstantExpression defaultValue,
int index,
_) {
// Shouldn't be called, we handle parameters via [FunctionSignture].
return internalError(node, "OptionalParameterDeclaration");
}
@override
visitParameterDeclaration(VariableDefinitions node, Node definition,
ParameterElement parameter, int index, _) {
// Shouldn't be called, we handle parameters via [FunctionSignture].
return internalError(node, "ParameterDeclaration");
}
@override
ir.MethodInvocation handleLocalInvoke(Send node, LocalElement element,
NodeList arguments, CallStructure callStructure, _) {
return associateNode(
buildCall(buildLocalGet(element), callStructure, arguments), node);
}
@override
ir.Expression handleLocalSetIfNulls(
SendSet node, LocalElement local, Node rhs, _,
{bool isSetterValid}) {
return new VariableAccessor(getLocal(local)).buildNullAwareAssignment(
visitForValue(rhs), null,
voidContext: isVoidContext);
}
@override
IrFunction visitRedirectingFactoryConstructorDeclaration(
FunctionExpression node,
ConstructorElement constructor,
NodeList parameters,
DartType redirectionType, // TODO(ahe): Should be InterfaceType.
ConstructorElement redirectionTarget,
_) {
if (!constructor.isFactoryConstructor) {
// TODO(ahe): This seems like a bug in semantic visitor and how it
// recovers from a bad constructor.
return new IrFunction.constructor(buildFunctionNode(constructor, null),
<ir.Initializer>[new ir.InvalidInitializer()]);
}
ir.Statement body = null;
if (kernel.isSyntheticError(redirectionTarget)) {
body = new ir.InvalidStatement();
} else {
// TODO(ahe): This should be implemented, but doesn't matter much unless
// we support reflection. At the call-site, we bypass this factory and
// call its effective target directly. So this factory is only necessary
// for reflection.
body = new ir.InvalidStatement();
}
IrFunction function =
buildIrFunction(ir.ProcedureKind.Factory, constructor, null);
function.node.body = body..parent = function.node;
return function;
}
@override
ir.InvocationExpression visitRedirectingFactoryConstructorInvoke(
NewExpression node,
ConstructorElement constructor,
InterfaceType type,
ConstructorElement effectiveTarget,
InterfaceType effectiveTargetType,
NodeList arguments,
CallStructure callStructure,
_) {
return buildConstructorInvoke(node, isConst: false);
}
@override
IrFunction visitRedirectingGenerativeConstructorDeclaration(
FunctionExpression node,
ConstructorElement constructor,
NodeList parameters,
NodeList initializers,
_) {
return buildGenerativeConstructor(constructor, parameters, null);
}
@override
ir.InvocationExpression visitRedirectingGenerativeConstructorInvoke(
NewExpression node,
ConstructorElement constructor,
InterfaceType type,
NodeList arguments,
CallStructure callStructure,
_) {
return buildConstructorInvoke(node, isConst: false);
}
@override
visitStaticConstantDeclaration(VariableDefinitions node, Node definition,
FieldElement field, ConstantExpression constant, _) {
// Shouldn't be called, handled by fieldToIr.
return internalError(node, "StaticConstantDeclaration");
}
@override
visitStaticFieldDeclaration(VariableDefinitions node, Node definition,
FieldElement field, Node initializer, _) {
// Shouldn't be called, handled by fieldToIr.
return internalError(node, "StaticFieldDeclaration");
}
ir.Expression buildStaticGet(Element element) {
return buildStaticAccessor(element).buildSimpleRead();
}
@override
ir.Expression handleStaticFieldGet(Send node, FieldElement field, _) {
return associateNode(buildStaticGet(field), node);
}
@override
ir.MethodInvocation handleStaticFieldInvoke(Send node, FieldElement field,
NodeList arguments, CallStructure callStructure, _) {
return associateNode(
buildCall(buildStaticGet(field), callStructure, arguments), node);
}
@override
ir.Expression handleStaticFieldSet(
SendSet node, FieldElement field, Node rhs, _) {
return buildStaticFieldSet(field, rhs);
}
@override
ir.Expression handleStaticSetIfNulls(
SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
Node rhs,
_) {
if (setterKind == CompoundSetter.INVALID) {
setter = null;
}
return buildStaticAccessor(getter, setter).buildNullAwareAssignment(
visitForValue(rhs), null,
voidContext: isVoidContext);
}
ir.VariableDeclaration getLocal(LocalElement local) {
return locals.putIfAbsent(local, () {
// Currently, initializing formals are not final.
bool isFinal = local.isFinal && !local.isInitializingFormal;
return associateElement(
new ir.VariableDeclaration(local.name,
initializer: null,
type: typeToIrHack(local.type),
isFinal: isFinal,
isConst: local.isConst),
local);
});
}
ir.FunctionNode buildFunctionNode(FunctionElement function, Node bodyNode) {
List<ir.TypeParameter> typeParameters =
kernel.typeParametersNotImplemented();
List<ir.VariableDeclaration> positionalParameters =
<ir.VariableDeclaration>[];
List<ir.VariableDeclaration> namedParameters = <ir.VariableDeclaration>[];
int requiredParameterCount = 0;
ir.DartType returnType = const ir.DynamicType();
if (function.hasFunctionSignature) {
FunctionSignature signature = function.functionSignature;
requiredParameterCount = signature.requiredParameterCount;
signature.forEachParameter((ParameterElement parameter) {
ir.VariableDeclaration variable = getLocal(parameter);
if (parameter.isNamed) {
namedParameters.add(variable);
} else {
positionalParameters.add(variable);
}
});
signature.forEachParameter((ParameterElement parameter) {
if (!parameter.isOptional) return;
ir.Expression initializer = visitForValue(parameter.initializer);
ir.VariableDeclaration variable = getLocal(parameter);
if (initializer != null) {
variable.initializer = initializer;
initializer.parent = variable;
}
});
if (function.isGenerativeConstructor) {
returnType = const ir.VoidType();
} else {
returnType = typeToIrHack(signature.type.returnType);
}
if (function.isFactoryConstructor) {
InterfaceType type = function.enclosingClass.thisType;
if (type.isGeneric) {
typeParameters = new List<ir.TypeParameter>();
for (DartType parameter in type.typeArguments) {
typeParameters.add(kernel.typeVariableToIr(parameter.element));
}
}
}
}
ir.AsyncMarker asyncMarker = ir.AsyncMarker.Sync;
if (!kernel.isSyntheticError(function)) {
switch (function.asyncMarker) {
case AsyncMarker.SYNC:
asyncMarker = ir.AsyncMarker.Sync;
break;
case AsyncMarker.SYNC_STAR:
asyncMarker = ir.AsyncMarker.SyncStar;
break;
case AsyncMarker.ASYNC:
asyncMarker = ir.AsyncMarker.Async;
break;
case AsyncMarker.ASYNC_STAR:
asyncMarker = ir.AsyncMarker.AsyncStar;
break;
default:
internalError(
function, "Unknown async maker: ${function.asyncMarker}");
break;
}
}
ir.Statement body;
if (function.isExternal) {
// [body] must be `null`.
} else if (function.isConstructor) {
// TODO(johnniwinther): Clean this up pending kernel issue #28.
ConstructorElement constructor = function;
if (bodyNode == null || bodyNode.asEmptyStatement() != null) {
body = new ir.EmptyStatement();
} else {
body = buildStatementInBlock(bodyNode);
}
} else if (bodyNode != null) {
Return returnStatement = bodyNode.asReturn();
if ((function.isSetter || function.name == Names.INDEX_SET_NAME.text) &&
returnStatement != null) {
// Avoid encoding the implicit return of setters with arrow body:
// set setter(value) => this.value = value;
// operator []=(index, value) => this[index] = value;
body = new ir.ExpressionStatement(
visitForEffect(returnStatement.expression));
} else {
body = buildStatementInBlock(bodyNode);
}
}
return associateElement(
new ir.FunctionNode(body,
asyncMarker: asyncMarker,
returnType: returnType,
typeParameters: typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount: requiredParameterCount),
function);
}
@override
IrFunction visitStaticFunctionDeclaration(FunctionExpression node,
MethodElement function, NodeList parameters, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Method, function, body);
}
ir.ProcedureKind computeInstanceMethodKind(MethodElement method) {
assert(method.isFunction);
return method.isOperator
? ir.ProcedureKind.Operator
: ir.ProcedureKind.Method;
}
@override
IrFunction visitInstanceMethodDeclaration(FunctionExpression node,
MethodElement method, NodeList parameters, Node body, _) {
return buildIrFunction(
computeInstanceMethodKind(currentElement), currentElement, body);
}
@override
IrFunction visitAbstractMethodDeclaration(
FunctionExpression node, MethodElement method, NodeList parameters, _) {
return buildIrFunction(
computeInstanceMethodKind(currentElement), currentElement, null);
}
@override
ir.Expression handleStaticFunctionGet(Send node, MethodElement function, _) {
return buildStaticGet(function);
}
@override
ir.StaticInvocation handleStaticFunctionIncompatibleInvoke(
Send node,
MethodElement function,
NodeList arguments,
CallStructure callStructure,
_) {
return buildStaticInvoke(function, arguments, isConst: false);
}
ir.StaticInvocation buildStaticInvoke(
FunctionElement function, NodeList arguments,
{bool isConst}) {
ir.Arguments argumentsNode = buildArguments(arguments);
return new ir.StaticInvocation(kernel.functionToIr(function), argumentsNode,
isConst: isConst);
}
@override
ir.StaticInvocation handleStaticFunctionInvoke(
Send node,
MethodElement function,
NodeList arguments,
CallStructure callStructure,
_) {
return associateNode(
buildStaticInvoke(function, arguments, isConst: false), node);
}
@override
ir.Expression handleStaticFunctionSet(
Send node, MethodElement function, Node rhs, _) {
return buildStaticAccessor(function)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
IrFunction visitStaticGetterDeclaration(
FunctionExpression node, MethodElement getter, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Getter, getter, body);
}
@override
ir.Expression handleStaticGetterGet(Send node, FunctionElement getter, _) {
if (getter.isDeferredLoaderGetter) {
// TODO(ahe): Support deferred load.
return new ir.InvalidExpression();
}
return buildStaticGet(getter);
}
@override
ir.Expression handleStaticGetterInvoke(Send node, FunctionElement getter,
NodeList arguments, CallStructure callStructure, _) {
if (getter.isDeferredLoaderGetter) {
// TODO(ahe): Support deferred load.
return new ir.InvalidExpression();
}
return buildCall(buildStaticGet(getter), callStructure, arguments);
}
@override
ir.Expression handleStaticGetterSet(
SendSet node, FunctionElement getter, Node rhs, _) {
return buildStaticAccessor(getter)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
IrFunction visitStaticSetterDeclaration(FunctionExpression node,
MethodElement setter, NodeList parameters, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Setter, setter, body);
}
@override
ir.Expression handleStaticSetterGet(Send node, FunctionElement setter, _) {
return buildStaticAccessor(null, setter).buildSimpleRead();
}
@override
ir.MethodInvocation handleStaticSetterInvoke(
Send node,
FunctionElement setter,
NodeList arguments,
CallStructure callStructure,
_) {
return buildCall(buildStaticAccessor(null, setter).buildSimpleRead(),
callStructure, arguments);
}
@override
ir.Expression handleStaticSetterSet(
SendSet node, FunctionElement setter, Node rhs, _) {
return buildStaticAccessor(null, setter)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.InvocationExpression visitStringFromEnvironmentConstructorInvoke(
NewExpression node, StringFromEnvironmentConstantExpression constant, _) {
return buildConstructorInvoke(node, isConst: true);
}
@override
ir.SuperMethodInvocation visitSuperBinary(Send node, FunctionElement function,
BinaryOperator operator, Node argument, _) {
transformerFlags |= TransformerFlag.superCalls;
return new ir.SuperMethodInvocation(
kernel.irName(operator.selectorName, currentElement),
new ir.Arguments(<ir.Expression>[visitForValue(argument)]),
kernel.functionToIr(function));
}
@override
ir.Expression visitSuperCompoundIndexSet(
SendSet node,
MethodElement getter,
MethodElement setter,
Node index,
AssignmentOperator operator,
Node rhs,
_) {
// TODO(sra): Find binary operator.
return buildSuperIndexAccessor(index, getter, setter)
.buildCompoundAssignment(
kernel.irName(operator.selectorName, currentElement),
visitForValue(rhs),
voidContext: isVoidContext);
}
@override
ir.Initializer visitSuperConstructorInvoke(
Send node,
ConstructorElement superConstructor,
InterfaceType type,
NodeList arguments,
CallStructure callStructure,
_) {
if (kernel.isSyntheticError(superConstructor)) {
// TODO(ahe): Semantic visitor shouldn't call in this case.
return new ir.InvalidInitializer();
}
return new ir.SuperInitializer(
kernel.functionToIr(superConstructor), buildArguments(arguments));
}
ir.SuperMethodInvocation buildSuperEquals(
FunctionElement function, Node argument) {
transformerFlags |= TransformerFlag.superCalls;
return new ir.SuperMethodInvocation(
kernel.irName(function.name, function),
new ir.Arguments(<ir.Expression>[visitForValue(argument)],
types: null, named: null),
kernel.functionToIr(function));
}
@override
ir.SuperMethodInvocation visitSuperEquals(
Send node, FunctionElement function, Node argument, _) {
return buildSuperEquals(function, argument);
}
@override
ir.Expression handleSuperCompounds(
SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
CompoundRhs rhs,
_) {
if (setterKind == CompoundSetter.INVALID) {
setter = null;
}
return buildCompound(buildSuperPropertyAccessor(getter, setter), rhs, node);
}
@override
ir.Expression handleStaticCompounds(
SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
CompoundRhs rhs,
_) {
if (setterKind == CompoundSetter.INVALID) {
setter = null;
}
return buildCompound(buildStaticAccessor(getter, setter), rhs, node);
}
@override
ir.Expression handleTypeLiteralConstantCompounds(
SendSet node, ConstantExpression constant, CompoundRhs rhs, _) {
return buildCompound(
new ReadOnlyAccessor(buildTypeLiteral(constant)), rhs, node);
}
ir.TypeLiteral buildTypeVariable(TypeVariableElement element) {
return new ir.TypeLiteral(kernel.typeToIr(element.type));
}
@override
ir.Expression handleTypeVariableTypeLiteralCompounds(
SendSet node, TypeVariableElement element, CompoundRhs rhs, _) {
return buildCompound(
new ReadOnlyAccessor(buildTypeVariable(element)), rhs, node);
}
@override
ir.SuperPropertyGet visitSuperFieldGet(Send node, FieldElement field, _) {
return buildSuperPropertyAccessor(field).buildSimpleRead();
}
@override
ir.MethodInvocation visitSuperFieldInvoke(Send node, FieldElement field,
NodeList arguments, CallStructure callStructure, _) {
return buildCall(buildSuperPropertyAccessor(field).buildSimpleRead(),
callStructure, arguments);
}
@override
ir.Expression visitSuperFieldSet(
SendSet node, FieldElement field, Node rhs, _) {
return buildSuperPropertyAccessor(field)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
Accessor buildSuperPropertyAccessor(Element getter, [Element setter]) {
transformerFlags |= TransformerFlag.superCalls;
if (setter == null &&
getter.isField &&
!getter.isFinal &&
!getter.isConst) {
setter = getter;
}
Element element = getter ?? setter;
return new SuperPropertyAccessor(
kernel.irName(element.name, element),
(getter == null) ? null : kernel.elementToIr(getter),
(setter == null) ? null : kernel.elementToIr(setter));
}
Accessor buildSuperIndexAccessor(Expression index, Element getter,
[Element setter]) {
if (setter == null &&
getter.isField &&
!getter.isFinal &&
!getter.isConst) {
setter = getter;
}
return new SuperIndexAccessor(
visitForValue(index),
(getter == null) ? null : kernel.elementToIr(getter),
(setter == null) ? null : kernel.elementToIr(setter));
}
@override
ir.SuperPropertyGet visitSuperGetterGet(
Send node, FunctionElement getter, _) {
return buildSuperPropertyAccessor(getter).buildSimpleRead();
}
@override
ir.MethodInvocation visitSuperGetterInvoke(Send node, FunctionElement getter,
NodeList arguments, CallStructure callStructure, _) {
return buildCall(buildSuperPropertyAccessor(getter).buildSimpleRead(),
callStructure, arguments);
}
@override
ir.Expression visitSuperGetterSet(
SendSet node, FunctionElement getter, Node rhs, _) {
return buildSuperPropertyAccessor(getter)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression handleSuperSetIfNulls(
SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
Node rhs,
_) {
if (setterKind == CompoundSetter.INVALID) {
setter = null;
}
return buildSuperPropertyAccessor(getter, setter).buildNullAwareAssignment(
visitForValue(rhs), null,
voidContext: isVoidContext);
}
@override
ir.SuperMethodInvocation visitSuperIndex(
Send node, FunctionElement function, Node index, _) {
return buildSuperIndexAccessor(index, function).buildSimpleRead();
}
@override
ir.Expression visitSuperIndexPostfix(Send node, MethodElement indexFunction,
MethodElement indexSetFunction, Node index, IncDecOperator operator, _) {
Accessor accessor =
buildSuperIndexAccessor(index, indexFunction, indexSetFunction);
return buildIndexPostfix(accessor, operator);
}
@override
ir.Expression visitSuperIndexPrefix(Send node, MethodElement indexFunction,
MethodElement indexSetFunction, Node index, IncDecOperator operator, _) {
Accessor accessor =
buildSuperIndexAccessor(index, indexFunction, indexSetFunction);
return buildIndexPrefix(accessor, operator);
}
@override
ir.Expression visitSuperIndexSet(
SendSet node, FunctionElement function, Node index, Node rhs, _) {
return buildSuperIndexAccessor(index, null, function)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression visitSuperMethodGet(Send node, MethodElement method, _) {
return buildSuperPropertyAccessor(method).buildSimpleRead();
}
ir.SuperMethodInvocation buildSuperMethodInvoke(
MethodElement method, NodeList arguments) {
transformerFlags |= TransformerFlag.superCalls;
return new ir.SuperMethodInvocation(kernel.irName(method.name, method),
buildArguments(arguments), kernel.functionToIr(method));
}
@override
ir.SuperMethodInvocation visitSuperMethodIncompatibleInvoke(
Send node,
MethodElement method,
NodeList arguments,
CallStructure callStructure,
_) {
return buildSuperMethodInvoke(method, arguments);
}
@override
ir.SuperMethodInvocation visitSuperMethodInvoke(
Send node,
MethodElement method,
NodeList arguments,
CallStructure callStructure,
_) {
return associateNode(buildSuperMethodInvoke(method, arguments), node);
}
@override
ir.Expression visitSuperMethodSet(
Send node, MethodElement method, Node rhs, _) {
return buildSuperPropertyAccessor(method)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Not visitSuperNotEquals(
Send node, FunctionElement function, Node argument, _) {
return new ir.Not(buildSuperEquals(function, argument));
}
@override
ir.Expression visitSuperSetterGet(Send node, FunctionElement setter, _) {
return buildSuperPropertyAccessor(null, setter).buildSimpleRead();
}
@override
ir.MethodInvocation visitSuperSetterInvoke(Send node, FunctionElement setter,
NodeList arguments, CallStructure callStructure, _) {
return buildCall(buildSuperPropertyAccessor(null, setter).buildSimpleRead(),
callStructure, arguments);
}
@override
ir.Expression visitSuperSetterSet(
SendSet node, FunctionElement setter, Node rhs, _) {
return buildSuperPropertyAccessor(null, setter)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.SuperMethodInvocation visitSuperUnary(
Send node, UnaryOperator operator, FunctionElement function, _) {
transformerFlags |= TransformerFlag.superCalls;
return new ir.SuperMethodInvocation(kernel.irName(function.name, function),
new ir.Arguments.empty(), kernel.functionToIr(function));
}
@override
ir.Initializer visitThisConstructorInvoke(
Send node,
ConstructorElement thisConstructor,
NodeList arguments,
CallStructure callStructure,
_) {
if (kernel.isSyntheticError(thisConstructor)) {
return new ir.InvalidInitializer();
} else {
return new ir.RedirectingInitializer(
kernel.functionToIr(thisConstructor), buildArguments(arguments));
}
}
@override
ir.ThisExpression visitThisGet(Identifier node, _) {
return new ir.ThisExpression();
}
@override
ir.MethodInvocation visitThisInvoke(
Send node, NodeList arguments, CallStructure callStructure, _) {
return buildCall(new ir.ThisExpression(), callStructure, arguments);
}
Accessor buildThisPropertyAccessor(Name name) {
return new ThisPropertyAccessor(nameToIrName(name), null, null);
}
@override
ir.Expression visitThisPropertyGet(Send node, Name name, _) {
return associateNode(
buildThisPropertyAccessor(name).buildSimpleRead(), node);
}
@override
ir.MethodInvocation visitThisPropertyInvoke(
Send node, NodeList arguments, Selector selector, _) {
return associateNode(
buildInvokeSelector(
new ir.ThisExpression(), selector, buildArguments(arguments)),
node);
}
@override
ir.Expression visitThisPropertySet(SendSet node, Name name, Node rhs, _) {
return buildThisPropertyAccessor(name)
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression visitTopLevelConstantDeclaration(VariableDefinitions node,
Node definition, FieldElement field, ConstantExpression constant, _) {
// Shouldn't be called, handled by fieldToIr.
return internalError(node, "TopLevelFieldDeclaration");
}
@override
ir.Expression visitTopLevelFieldDeclaration(VariableDefinitions node,
Node definition, FieldElement field, Node initializer, _) {
// Shouldn't be called, handled by fieldToIr.
return internalError(node, "TopLevelFieldDeclaration");
}
@override
IrFunction visitTopLevelFunctionDeclaration(FunctionExpression node,
MethodElement function, NodeList parameters, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Method, function, body);
}
ir.Arguments buildArguments(NodeList arguments) {
List<ir.Expression> positional = <ir.Expression>[];
List<ir.NamedExpression> named = <ir.NamedExpression>[];
for (Expression expression in arguments.nodes) {
ir.TreeNode argument = visitForValue(expression);
if (argument is ir.NamedExpression) {
named.add(argument);
} else {
positional.add(argument);
}
}
return new ir.Arguments(positional, named: named, types: null);
}
@override
IrFunction visitTopLevelGetterDeclaration(
FunctionExpression node, MethodElement getter, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Getter, getter, body);
}
@override
IrFunction visitTopLevelSetterDeclaration(FunctionExpression node,
MethodElement setter, NodeList parameters, Node body, _) {
return buildIrFunction(ir.ProcedureKind.Setter, setter, body);
}
@override
ir.TypeLiteral visitTypeVariableTypeLiteralGet(
Send node, TypeVariableElement element, _) {
return buildTypeVariable(element);
}
@override
ir.MethodInvocation visitTypeVariableTypeLiteralInvoke(
Send node,
TypeVariableElement element,
NodeList arguments,
CallStructure callStructure,
_) {
return buildCall(buildTypeVariable(element), callStructure, arguments);
}
@override
ir.Expression visitTypeVariableTypeLiteralSet(
SendSet node, TypeVariableElement element, Node rhs, _) {
return new ReadOnlyAccessor(buildTypeVariable(element))
.buildAssignment(visitForValue(rhs), voidContext: isVoidContext);
}
@override
ir.Expression visitTypeVariableTypeLiteralSetIfNull(
Send node, TypeVariableElement element, Node rhs, _) {
return new ReadOnlyAccessor(buildTypeVariable(element))
.buildNullAwareAssignment(visitForValue(rhs), null,
voidContext: isVoidContext);
}
@override
ir.TypeLiteral visitTypedefTypeLiteralGet(
Send node, ConstantExpression constant, _) {
return buildTypeLiteral(constant);
}
@override
ir.MethodInvocation visitTypedefTypeLiteralInvoke(
Send node,
ConstantExpression constant,
NodeList arguments,
CallStructure callStructure,
_) {
return buildCall(buildTypeLiteral(constant), callStructure, arguments);
}
@override
ir.Expression visitTypedefTypeLiteralSet(
SendSet node, ConstantExpression constant, Node rhs, _) {
return buildTypeLiteralSet(constant, rhs);
}
@override
ir.Expression handleTypeLiteralConstantSetIfNulls(
SendSet node, ConstantExpression constant, Node rhs, _) {
// Degenerate case: ignores [rhs] as a type literal is never null.
return buildTypeLiteral(constant);
}
@override
ir.MethodInvocation visitUnary(
Send node, UnaryOperator operator, Node expression, _) {
return associateNode(
new ir.MethodInvocation(
visitForValue(expression),
kernel.irName(operator.selectorName, currentElement),
new ir.Arguments.empty()),
node);
}
@override
visitConditionalUri(ConditionalUri node) {
// Shouldn't be called, handled by library loader.
return internalError(node, "ConditionalUri");
}
@override
visitDottedName(DottedName node) {
// Shouldn't be called, handled by library loader.
return internalError(node, "DottedName");
}
@override
visitForIn(ForIn node) {
// Shouldn't be called, handled by [visitAsyncForIn] or [visitSyncForIn].
return internalError(node, "ForIn");
}
@override
ir.Expression visitIndexSetIfNull(
SendSet node, Node receiver, Node index, Node rhs, _) {
return buildIndexAccessor(receiver, index).buildNullAwareAssignment(
visitForValue(rhs), null,
voidContext: isVoidContext);
}
@override
ir.Expression visitSuperIndexSetIfNull(SendSet node, MethodElement getter,
MethodElement setter, Node index, Node rhs, _) {
return buildSuperIndexAccessor(index, getter, setter)
.buildNullAwareAssignment(visitForValue(rhs), null,
voidContext: isVoidContext);
}
@override
ir.Node visitVariableDefinitions(VariableDefinitions definitions) {
// TODO(ahe): This method is copied from [SemanticDeclarationResolvedMixin]
// and modified. Perhaps we can find a way to avoid code duplication.
List<ir.VariableDeclaration> variables = <ir.VariableDeclaration>[];
computeVariableStructures(definitions,
(Node node, VariableStructure structure) {
if (structure == null) {
return internalError(node, 'No structure for $node');
} else {
ir.VariableDeclaration variable =
structure.dispatch(declVisitor, node, null);
variables.add(variable);
return variable;
}
});
if (variables.length == 1) return variables.single;
return new VariableDeclarations(variables);
}
IrFunction buildFunction() {
return kernel.compiler.reporter.withCurrentElement(currentElement, () {
if (kernel.isSyntheticError(currentElement)) {
kernel.internalError(currentElement,
"Can't build synthetic function element: $currentElement");
} else if (currentElement.isMalformed) {
ir.FunctionNode node = buildFunctionNode(currentElement, null);
if (currentElement.isGenerativeConstructor) {
return new IrFunction.constructor(
node, <ir.Initializer>[new ir.InvalidInitializer()]);
} else {
node.body = new ir.InvalidStatement()..parent = node;
return new IrFunction.procedure(ir.ProcedureKind.Method, node);
}
} else if (currentElement.isSynthesized) {
if (currentElement.isGenerativeConstructor) {
return buildGenerativeConstructor(currentElement, null, null);
} else {
return internalError(currentElement, "Unhandled synthetic function.");
}
} else {
Node node = currentElement.node;
if (node.isErroneous) {
return internalError(currentElement, "Unexpected syntax error.");
} else {
return node.accept(this);
}
}
});
}
ir.Expression buildInitializer() {
return kernel.compiler.reporter.withCurrentElement(currentElement, () {
FieldElement field = currentElement;
return field.isMalformed
? new ir.InvalidExpression()
: associateNode(visitForValue(field.initializer), field.initializer);
});
}
}
class VariableDeclarations implements ir.Node {
final List<ir.VariableDeclaration> variables;
VariableDeclarations(this.variables);
accept(ir.Visitor v) => throw "unsupported";
visitChildren(ir.Visitor v) => throw "unsupported";
String toString() => "VariableDeclarations($variables)";
}
class IrFunction implements ir.Node {
final ir.ProcedureKind kind;
final bool isConstructor;
final ir.FunctionNode node;
final List<ir.Initializer> initializers;
IrFunction(this.kind, this.isConstructor, this.node, this.initializers);
IrFunction.procedure(ir.ProcedureKind kind, ir.FunctionNode node)
: this(kind, false, node, null);
IrFunction.constructor(
ir.FunctionNode node, List<ir.Initializer> initializers)
: this(null, true, node, initializers);
accept(ir.Visitor v) => throw "unsupported";
visitChildren(ir.Visitor v) => throw "unsupported";
String toString() {
return "IrFunction($kind, $isConstructor, $node, $initializers)";
}
}