blob: 09c6b0d7795990869bf0460d5270cf00fd2a417b [file] [log] [blame]
// Copyright (c) 2013, 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.
library dart2js.ir_builder_task;
import '../closure.dart' as closurelib;
import '../closure.dart' hide ClosureScope;
import '../common/names.dart' show
Names,
Selectors;
import '../common/tasks.dart' show
CompilerTask;
import '../compiler.dart' show
Compiler;
import '../constants/expressions.dart';
import '../dart_types.dart';
import '../diagnostics/invariant.dart' show
invariant;
import '../elements/elements.dart';
import '../elements/modelx.dart' show
SynthesizedConstructorElementX,
ConstructorBodyElementX,
FunctionSignatureX;
import '../io/source_information.dart';
import '../js_backend/js_backend.dart' show
JavaScriptBackend,
SyntheticConstantKind;
import '../resolution/tree_elements.dart' show
TreeElements;
import '../resolution/semantic_visitor.dart';
import '../resolution/send_resolver.dart' show
SendResolverMixin;
import '../resolution/operators.dart' as op;
import '../tree/tree.dart' as ast;
import '../types/types.dart' show
TypeMask;
import '../universe/call_structure.dart' show
CallStructure;
import '../universe/selector.dart' show
Selector;
import '../constants/values.dart' show
ConstantValue;
import 'cps_ir_nodes.dart' as ir;
import 'cps_ir_builder.dart';
import '../native/native.dart' show
NativeBehavior;
// TODO(karlklose): remove.
import '../js/js.dart' as js show js, Template, Expression, Name;
import '../ssa/ssa.dart' show TypeMaskFactory;
import '../util/util.dart';
import 'package:js_runtime/shared/embedded_names.dart'
show JsBuiltin, JsGetName;
import '../constants/values.dart';
typedef void IrBuilderCallback(Element element, ir.FunctionDefinition irNode);
/// This task provides the interface to build IR nodes from [ast.Node]s, which
/// is used from the [CpsFunctionCompiler] to generate code.
///
/// This class is mainly there to correctly measure how long building the IR
/// takes.
class IrBuilderTask extends CompilerTask {
final SourceInformationStrategy sourceInformationStrategy;
String bailoutMessage = null;
/// If not null, this function will be called with for each
/// [ir.FunctionDefinition] node that has been built.
IrBuilderCallback builderCallback;
IrBuilderTask(Compiler compiler, this.sourceInformationStrategy,
[this.builderCallback])
: super(compiler);
String get name => 'CPS builder';
ir.FunctionDefinition buildNode(AstElement element) {
return measure(() {
bailoutMessage = null;
TreeElements elementsMapping = element.resolvedAst.elements;
element = element.implementation;
return compiler.withCurrentElement(element, () {
SourceInformationBuilder sourceInformationBuilder =
sourceInformationStrategy.createBuilderForContext(element);
IrBuilderVisitor builder =
new JsIrBuilderVisitor(
elementsMapping, compiler, sourceInformationBuilder);
ir.FunctionDefinition irNode = builder.buildExecutable(element);
if (irNode == null) {
bailoutMessage = builder.bailoutMessage;
} else if (builderCallback != null) {
builderCallback(element, irNode);
}
return irNode;
});
});
}
}
/// Translates the frontend AST of a method to its CPS IR.
///
/// The visitor has an [IrBuilder] which contains an IR fragment to build upon
/// and the current reaching definition of local variables.
///
/// Visiting a statement or expression extends the IR builder's fragment.
/// For expressions, the primitive holding the resulting value is returned.
/// For statements, `null` is returned.
// TODO(johnniwinther): Implement [SemanticDeclVisitor].
abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
with IrBuilderMixin<ast.Node>,
SemanticSendResolvedMixin<ir.Primitive, dynamic>,
SendResolverMixin,
ErrorBulkMixin<ir.Primitive, dynamic>,
BaseImplementationOfStaticsMixin<ir.Primitive, dynamic>,
BaseImplementationOfLocalsMixin<ir.Primitive, dynamic>,
BaseImplementationOfDynamicsMixin<ir.Primitive, dynamic>,
BaseImplementationOfConstantsMixin<ir.Primitive, dynamic>,
BaseImplementationOfNewMixin<ir.Primitive, dynamic>,
BaseImplementationOfCompoundsMixin<ir.Primitive, dynamic>,
BaseImplementationOfSetIfNullsMixin<ir.Primitive, dynamic>,
BaseImplementationOfIndexCompoundsMixin<ir.Primitive, dynamic>
implements SemanticSendVisitor<ir.Primitive, dynamic> {
final TreeElements elements;
final Compiler compiler;
final SourceInformationBuilder sourceInformationBuilder;
/// A map from try statements in the source to analysis information about
/// them.
///
/// The analysis information includes the set of variables that must be
/// copied into [ir.MutableVariable]s on entry to the try and copied out on
/// exit.
Map<ast.Node, TryStatementInfo> tryStatements = null;
// In SSA terms, join-point continuation parameters are the phis and the
// continuation invocation arguments are the corresponding phi inputs. To
// support name introduction and renaming for source level variables, we use
// nested (delimited) visitors for constructing subparts of the IR that will
// need renaming. Each source variable is assigned an index.
//
// Each nested visitor maintains a list of free variable uses in the body.
// These are implemented as a list of parameters, each with their own use
// list of references. When the delimited subexpression is plugged into the
// surrounding context, the free occurrences can be captured or become free
// occurrences in the next outer delimited subexpression.
//
// Each nested visitor maintains a list that maps indexes of variables
// assigned in the delimited subexpression to their reaching definition ---
// that is, the definition in effect at the hole in 'current'. These are
// used to determine if a join-point continuation needs to be passed
// arguments, and what the arguments are.
/// Construct a top-level visitor.
IrBuilderVisitor(this.elements,
this.compiler,
this.sourceInformationBuilder);
String bailoutMessage = null;
@override
ir.Primitive apply(ast.Node node, _) => node.accept(this);
@override
SemanticSendVisitor get sendVisitor => this;
/**
* Builds the [ir.FunctionDefinition] for an executable element. In case the
* function uses features that cannot be expressed in the IR, this element
* returns `null`.
*/
ir.FunctionDefinition buildExecutable(ExecutableElement element);
ClosureClassMap get closureClassMap;
ClosureScope getClosureScopeForNode(ast.Node node);
ClosureEnvironment getClosureEnvironment();
/// Normalizes the argument list to a static invocation (i.e. where the target
/// element is known).
///
/// For the JS backend, inserts default arguments and normalizes order of
/// named arguments.
///
/// For the Dart backend, returns [arguments].
List<ir.Primitive> normalizeStaticArguments(
CallStructure callStructure,
FunctionElement target,
List<ir.Primitive> arguments);
/// Normalizes the argument list of a dynamic invocation (i.e. where the
/// target element is unknown).
///
/// For the JS backend, normalizes order of named arguments.
///
/// For the Dart backend, returns [arguments].
List<ir.Primitive> normalizeDynamicArguments(
CallStructure callStructure,
List<ir.Primitive> arguments);
/// Read the value of [field].
ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src);
ir.FunctionDefinition _makeFunctionBody(FunctionElement element,
ast.FunctionExpression node) {
FunctionSignature signature = element.functionSignature;
List<Local> parameters = <Local>[];
signature.orderedForEachParameter(
(LocalParameterElement e) => parameters.add(e));
if (element.isFactoryConstructor) {
// Type arguments are passed in as extra parameters.
for (DartType typeVariable in element.enclosingClass.typeVariables) {
parameters.add(new TypeVariableLocal(typeVariable, element));
}
}
irBuilder.buildFunctionHeader(parameters,
closureScope: getClosureScopeForNode(node),
env: getClosureEnvironment());
visit(node.body);
return irBuilder.makeFunctionDefinition();
}
ir.Primitive visit(ast.Node node) => node.accept(this);
// ## Statements ##
visitBlock(ast.Block node) {
irBuilder.buildBlock(node.statements.nodes, build);
}
ir.Primitive visitBreakStatement(ast.BreakStatement node) {
if (!irBuilder.buildBreak(elements.getTargetOf(node))) {
compiler.internalError(node, "'break' target not found");
}
return null;
}
ir.Primitive visitContinueStatement(ast.ContinueStatement node) {
if (!irBuilder.buildContinue(elements.getTargetOf(node))) {
compiler.internalError(node, "'continue' target not found");
}
return null;
}
// Build(EmptyStatement, C) = C
ir.Primitive visitEmptyStatement(ast.EmptyStatement node) {
assert(irBuilder.isOpen);
return null;
}
// Build(ExpressionStatement(e), C) = C'
// where (C', _) = Build(e, C)
ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) {
assert(irBuilder.isOpen);
if (node.expression is ast.Throw) {
// Throw expressions that occur as statements are translated differently
// from ones that occur as subexpressions. This is achieved by peeking
// at statement-level expressions here.
irBuilder.buildThrow(visit(node.expression));
} else {
visit(node.expression);
}
return null;
}
ir.Primitive visitRethrow(ast.Rethrow node) {
assert(irBuilder.isOpen);
irBuilder.buildRethrow();
return null;
}
/// Construct a method that executes the forwarding call to the target
/// constructor. This is only required, if the forwarding factory
/// constructor can potentially be the target of a reflective call, because
/// the builder shortcuts calls to redirecting factories at the call site
/// (see [JsIrBuilderVisitor.handleConstructorInvoke]).
visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
ConstructorElement targetConstructor =
elements.getRedirectingTargetConstructor(node).implementation;
ConstructorElement redirectingConstructor =
irBuilder.state.currentElement.implementation;
List<ir.Primitive> arguments = <ir.Primitive>[];
FunctionSignature redirectingSignature =
redirectingConstructor.functionSignature;
List<String> namedParameters = <String>[];
redirectingSignature.forEachParameter((ParameterElement parameter) {
arguments.add(irBuilder.environment.lookup(parameter));
if (parameter.isNamed) {
namedParameters.add(parameter.name);
}
});
ClassElement cls = redirectingConstructor.enclosingClass;
InterfaceType targetType =
redirectingConstructor.computeEffectiveTargetType(cls.thisType);
CallStructure callStructure = new CallStructure(
redirectingSignature.parameterCount,
namedParameters);
arguments = normalizeStaticArguments(callStructure, targetConstructor,
arguments);
ir.Primitive instance = irBuilder.buildConstructorInvocation(
targetConstructor,
callStructure,
targetType,
arguments,
sourceInformationBuilder.buildNew(node));
irBuilder.buildReturn(
value: instance,
sourceInformation: sourceInformationBuilder.buildReturn(node));
}
visitFor(ast.For node) {
List<LocalElement> loopVariables = <LocalElement>[];
if (node.initializer is ast.VariableDefinitions) {
ast.VariableDefinitions definitions = node.initializer;
for (ast.Node node in definitions.definitions.nodes) {
LocalElement loopVariable = elements[node];
loopVariables.add(loopVariable);
}
}
JumpTarget target = elements.getTargetDefinition(node);
irBuilder.buildFor(
buildInitializer: subbuild(node.initializer),
buildCondition: subbuild(node.condition),
buildBody: subbuild(node.body),
buildUpdate: subbuildSequence(node.update),
closureScope: getClosureScopeForNode(node),
loopVariables: loopVariables,
target: target);
}
visitIf(ast.If node) {
irBuilder.buildIf(
build(node.condition),
subbuild(node.thenPart),
subbuild(node.elsePart));
}
visitLabeledStatement(ast.LabeledStatement node) {
ast.Statement body = node.statement;
if (body is ast.Loop) {
visit(body);
} else {
JumpTarget target = elements.getTargetDefinition(body);
irBuilder.buildLabeledStatement(
buildBody: subbuild(body),
target: target);
}
}
visitDoWhile(ast.DoWhile node) {
irBuilder.buildDoWhile(
buildBody: subbuild(node.body),
buildCondition: subbuild(node.condition),
target: elements.getTargetDefinition(node),
closureScope: getClosureScopeForNode(node));
}
visitWhile(ast.While node) {
irBuilder.buildWhile(
buildCondition: subbuild(node.condition),
buildBody: subbuild(node.body),
target: elements.getTargetDefinition(node),
closureScope: getClosureScopeForNode(node));
}
visitAsyncForIn(ast.AsyncForIn node) {
// await for is not yet implemented.
return giveup(node, 'await for');
}
visitAwait(ast.Await node) {
ir.Primitive value = visit(node.expression);
return irBuilder.buildAwait(value);
}
visitYield(ast.Yield node) {
ir.Primitive value = visit(node.expression);
return irBuilder.buildYield(value, node.hasStar);
}
visitSyncForIn(ast.SyncForIn node) {
// [node.declaredIdentifier] can be either an [ast.VariableDefinitions]
// (defining a new local variable) or a send designating some existing
// variable.
ast.Node identifier = node.declaredIdentifier;
ast.VariableDefinitions variableDeclaration =
identifier.asVariableDefinitions();
Element variableElement = elements.getForInVariable(node);
Selector selector = elements.getSelector(identifier);
irBuilder.buildForIn(
buildExpression: subbuild(node.expression),
buildVariableDeclaration: subbuild(variableDeclaration),
variableElement: variableElement,
variableSelector: selector,
variableMask: elements.getTypeMask(identifier),
currentMask: elements.getCurrentTypeMask(node),
moveNextMask: elements.getMoveNextTypeMask(node),
iteratorMask: elements.getIteratorTypeMask(node),
buildBody: subbuild(node.body),
target: elements.getTargetDefinition(node),
closureScope: getClosureScopeForNode(node));
}
ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) {
assert(irBuilder.isOpen);
for (ast.Node definition in node.definitions.nodes) {
Element element = elements[definition];
ir.Primitive initialValue;
// Definitions are either SendSets if there is an initializer, or
// Identifiers if there is no initializer.
if (definition is ast.SendSet) {
assert(!definition.arguments.isEmpty);
assert(definition.arguments.tail.isEmpty);
initialValue = visit(definition.arguments.head);
} else {
assert(definition is ast.Identifier);
}
irBuilder.declareLocalVariable(element, initialValue: initialValue);
}
return null;
}
static final RegExp nativeRedirectionRegExp =
new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$');
// Build(Return(e), C) = C'[InvokeContinuation(return, x)]
// where (C', x) = Build(e, C)
//
// Return without a subexpression is translated as if it were return null.
visitReturn(ast.Return node) {
assert(irBuilder.isOpen);
SourceInformation source = sourceInformationBuilder.buildReturn(node);
if (node.beginToken.value == 'native') {
FunctionElement function = irBuilder.state.currentElement;
assert(function.isNative);
ast.Node nativeBody = node.expression;
if (nativeBody != null) {
ast.LiteralString jsCode = nativeBody.asLiteralString();
String javaScriptCode = jsCode.dartString.slowToString();
assert(invariant(nativeBody,
!nativeRedirectionRegExp.hasMatch(javaScriptCode),
message: "Deprecated syntax, use @JSName('name') instead."));
assert(invariant(nativeBody,
function.functionSignature.parameterCount == 0,
message: 'native "..." syntax is restricted to '
'functions with zero parameters.'));
irBuilder.buildNativeFunctionBody(function, javaScriptCode);
} else {
irBuilder.buildRedirectingNativeFunctionBody(function, source);
}
} else {
irBuilder.buildReturn(
value: build(node.expression),
sourceInformation: source);
}
}
visitSwitchStatement(ast.SwitchStatement node) {
assert(irBuilder.isOpen);
// We do not handle switch statements with continue to labeled cases.
for (ast.SwitchCase switchCase in node.cases) {
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.Label) {
LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
if (definition != null && definition.isContinueTarget) {
return giveup(node, "continue to a labeled switch case");
}
}
}
}
// Each switch case contains a list of interleaved labels and expressions
// and a non-empty body. We can ignore the labels because they are not
// jump targets.
List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
SwitchCaseInfo defaultCase;
for (ast.SwitchCase switchCase in node.cases) {
SwitchCaseInfo caseInfo =
new SwitchCaseInfo(subbuildSequence(switchCase.statements));
if (switchCase.isDefaultCase) {
defaultCase = caseInfo;
} else {
cases.add(caseInfo);
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.CaseMatch) {
ir.Primitive constant = translateConstant(labelOrCase.expression);
caseInfo.addConstant(constant);
}
}
}
}
ir.Primitive value = visit(node.expression);
JumpTarget target = elements.getTargetDefinition(node);
Element error =
(compiler.backend as JavaScriptBackend).getFallThroughError();
irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error,
sourceInformationBuilder.buildGeneric(node));
}
visitTryStatement(ast.TryStatement node) {
List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[];
for (ast.CatchBlock catchClause in node.catchBlocks.nodes) {
LocalVariableElement exceptionVariable;
if (catchClause.exception != null) {
exceptionVariable = elements[catchClause.exception];
}
LocalVariableElement stackTraceVariable;
if (catchClause.trace != null) {
stackTraceVariable = elements[catchClause.trace];
}
DartType type;
if (catchClause.onKeyword != null) {
type = elements.getType(catchClause.type);
}
catchClauseInfos.add(new CatchClauseInfo(
type: type,
exceptionVariable: exceptionVariable,
stackTraceVariable: stackTraceVariable,
buildCatchBlock: subbuild(catchClause.block)));
}
assert(!node.catchBlocks.isEmpty || node.finallyBlock != null);
if (!node.catchBlocks.isEmpty && node.finallyBlock != null) {
// Try/catch/finally is encoded in terms of try/catch and try/finally:
//
// try tryBlock catch (ex, st) catchBlock finally finallyBlock
// ==>
// try { try tryBlock catch (ex, st) catchBlock } finally finallyBlock
irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
(IrBuilder inner) {
inner.buildTryCatch(tryStatements[node.catchBlocks],
subbuild(node.tryBlock),
catchClauseInfos);
},
subbuild(node.finallyBlock));
} else if (!node.catchBlocks.isEmpty) {
irBuilder.buildTryCatch(tryStatements[node.catchBlocks],
subbuild(node.tryBlock),
catchClauseInfos);
} else {
irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
subbuild(node.tryBlock),
subbuild(node.finallyBlock));
}
}
// ## Expressions ##
ir.Primitive visitConditional(ast.Conditional node) {
return irBuilder.buildConditional(
build(node.condition),
subbuild(node.thenExpression),
subbuild(node.elseExpression));
}
// For all simple literals:
// Build(Literal(c), C) = C[let val x = Constant(c) in [], x]
ir.Primitive visitLiteralBool(ast.LiteralBool node) {
assert(irBuilder.isOpen);
return irBuilder.buildBooleanConstant(node.value);
}
ir.Primitive visitLiteralDouble(ast.LiteralDouble node) {
assert(irBuilder.isOpen);
return irBuilder.buildDoubleConstant(node.value);
}
ir.Primitive visitLiteralInt(ast.LiteralInt node) {
assert(irBuilder.isOpen);
return irBuilder.buildIntegerConstant(node.value);
}
ir.Primitive visitLiteralNull(ast.LiteralNull node) {
assert(irBuilder.isOpen);
return irBuilder.buildNullConstant();
}
ir.Primitive visitLiteralString(ast.LiteralString node) {
assert(irBuilder.isOpen);
return irBuilder.buildDartStringConstant(node.dartString);
}
ConstantValue getConstantForNode(ast.Node node) {
return irBuilder.state.constants.getConstantValueForNode(node, elements);
}
ConstantValue getConstantForVariable(VariableElement element) {
return irBuilder.state.constants.getConstantValueForVariable(element);
}
ir.Primitive buildConstantExpression(ConstantExpression expression,
SourceInformation sourceInformation) {
return irBuilder.buildConstant(
irBuilder.state.constants.getConstantValue(expression),
sourceInformation: sourceInformation);
}
ir.Primitive visitLiteralList(ast.LiteralList node) {
if (node.isConst) {
return translateConstant(node);
}
List<ir.Primitive> values = node.elements.nodes.mapToList(visit);
InterfaceType type = elements.getType(node);
return irBuilder.buildListLiteral(type, values);
}
ir.Primitive visitLiteralMap(ast.LiteralMap node) {
if (node.isConst) {
return translateConstant(node);
}
InterfaceType type = elements.getType(node);
return irBuilder.buildMapLiteral(
type,
node.entries.nodes.map((e) => e.key),
node.entries.nodes.map((e) => e.value),
build);
}
ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) {
assert(irBuilder.isOpen);
return translateConstant(node);
}
ir.Primitive visitParenthesizedExpression(
ast.ParenthesizedExpression node) {
assert(irBuilder.isOpen);
return visit(node.expression);
}
// Stores the result of visiting a CascadeReceiver, so we can return it from
// its enclosing Cascade.
ir.Primitive _currentCascadeReceiver;
ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) {
assert(irBuilder.isOpen);
return _currentCascadeReceiver = visit(node.expression);
}
ir.Primitive visitCascade(ast.Cascade node) {
assert(irBuilder.isOpen);
var oldCascadeReceiver = _currentCascadeReceiver;
// Throw away the result of visiting the expression.
// Instead we return the result of visiting the CascadeReceiver.
visit(node.expression);
ir.Primitive receiver = _currentCascadeReceiver;
_currentCascadeReceiver = oldCascadeReceiver;
return receiver;
}
@override
ir.Primitive visitAssert(ast.Assert node) {
assert(irBuilder.isOpen);
if (compiler.enableUserAssertions) {
return giveup(node, 'assert in checked mode not implemented');
} else {
// The call to assert and its argument expression must be ignored
// in production mode.
// Assertions can only occur in expression statements, so no value needs
// to be returned.
return null;
}
}
// ## Sends ##
@override
void previsitDeferredAccess(ast.Send node, PrefixElement prefix, _) {
giveup(node, 'deferred access is not implemented');
}
ir.Primitive visitNamedArgument(ast.NamedArgument node) {
assert(irBuilder.isOpen);
return visit(node.expression);
}
@override
ir.Primitive visitExpressionInvoke(ast.Send node,
ast.Node expression,
ast.NodeList argumentsNode,
CallStructure callStructure, _) {
ir.Primitive receiver = visit(expression);
List<ir.Primitive> arguments = node.arguments.mapToList(visit);
arguments = normalizeDynamicArguments(callStructure, arguments);
return irBuilder.buildCallInvocation(
receiver, callStructure, arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
/// Returns `true` if [node] is a super call.
// TODO(johnniwinther): Remove the need for this.
bool isSuperCall(ast.Send node) {
return node != null && node.receiver != null && node.receiver.isSuper();
}
@override
ir.Primitive handleConstantGet(
ast.Node node,
ConstantExpression constant, _) {
return buildConstantExpression(constant,
sourceInformationBuilder.buildGet(node));
}
/// If [node] is null, returns this.
/// Otherwise visits [node] and returns the result.
ir.Primitive translateReceiver(ast.Expression node) {
return node != null ? visit(node) : irBuilder.buildThis();
}
@override
ir.Primitive handleDynamicGet(
ast.Send node,
ast.Node receiver,
Name name,
_) {
return irBuilder.buildDynamicGet(
translateReceiver(receiver),
new Selector.getter(name),
elements.getTypeMask(node),
sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive visitIfNotNullDynamicPropertyGet(
ast.Send node,
ast.Node receiver,
Name name,
_) {
ir.Primitive target = visit(receiver);
return irBuilder.buildIfNotNullSend(
target,
nested(() => irBuilder.buildDynamicGet(
target,
new Selector.getter(name),
elements.getTypeMask(node),
sourceInformationBuilder.buildGet(node))));
}
@override
ir.Primitive visitDynamicTypeLiteralGet(
ast.Send node,
ConstantExpression constant,
_) {
return buildConstantExpression(constant,
sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive visitLocalVariableGet(
ast.Send node,
LocalVariableElement element,
_) {
return element.isConst
? irBuilder.buildConstant(getConstantForVariable(element),
sourceInformation: sourceInformationBuilder.buildGet(node))
: irBuilder.buildLocalVariableGet(element);
}
@override
ir.Primitive handleLocalGet(
ast.Send node,
LocalElement element,
_) {
return irBuilder.buildLocalVariableGet(element);
}
@override
ir.Primitive visitLocalFunctionGet(
ast.Send node,
LocalFunctionElement function,
_) {
return irBuilder.buildLocalFunctionGet(function);
}
@override
ir.Primitive handleStaticFunctionGet(
ast.Send node,
MethodElement function,
_) {
return irBuilder.buildStaticFunctionGet(function);
}
@override
ir.Primitive handleStaticGetterGet(
ast.Send node,
FunctionElement getter,
_) {
return irBuilder.buildStaticGetterGet(
getter, sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive visitSuperFieldGet(
ast.Send node,
FieldElement field,
_) {
return irBuilder.buildSuperFieldGet(field);
}
@override
ir.Primitive visitSuperGetterGet(
ast.Send node,
FunctionElement getter,
_) {
return irBuilder.buildSuperGetterGet(
getter, sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive visitSuperMethodGet(
ast.Send node,
MethodElement method,
_) {
return irBuilder.buildSuperMethodGet(method);
}
@override
ir.Primitive visitUnresolvedSuperGet(
ast.Send node,
Element element, _) {
return buildInstanceNoSuchMethod(
elements.getSelector(node), elements.getTypeMask(node), []);
}
@override
ir.Primitive visitThisGet(ast.Identifier node, _) {
if (irBuilder.state.thisParameter == null) {
// TODO(asgerf,johnniwinther): Should be in a visitInvalidThis method.
// 'this' in static context. Just translate to null.
assert(compiler.compilationFailed);
return irBuilder.buildNullConstant();
}
return irBuilder.buildThis();
}
ir.Primitive translateTypeVariableTypeLiteral(
TypeVariableElement element,
SourceInformation sourceInformation) {
return irBuilder.buildReifyTypeVariable(element.type, sourceInformation);
}
@override
ir.Primitive visitTypeVariableTypeLiteralGet(ast.Send node,
TypeVariableElement element, _) {
return translateTypeVariableTypeLiteral(element,
sourceInformationBuilder.buildGet(node));
}
ir.Primitive translateLogicalOperator(ast.Expression left,
ast.Expression right,
{bool isLazyOr}) {
ir.Primitive leftValue = visit(left);
ir.Primitive buildRightValue(IrBuilder rightBuilder) {
return withBuilder(rightBuilder, () => visit(right));
}
return irBuilder.buildLogicalOperator(
leftValue, buildRightValue, isLazyOr: isLazyOr);
}
@override
ir.Primitive visitIfNull(
ast.Send node, ast.Node left, ast.Node right, _) {
return irBuilder.buildIfNull(build(left), subbuild(right));
}
@override
ir.Primitive visitLogicalAnd(
ast.Send node, ast.Node left, ast.Node right, _) {
return translateLogicalOperator(left, right, isLazyOr: false);
}
@override
ir.Primitive visitLogicalOr(
ast.Send node, ast.Node left, ast.Node right, _) {
return translateLogicalOperator(left, right, isLazyOr: true);
}
@override
ir.Primitive visitAs(
ast.Send node,
ast.Node expression,
DartType type,
_) {
ir.Primitive receiver = visit(expression);
return irBuilder.buildTypeOperator(receiver, type, isTypeTest: false);
}
@override
ir.Primitive visitIs(
ast.Send node,
ast.Node expression,
DartType type,
_) {
ir.Primitive value = visit(expression);
return irBuilder.buildTypeOperator(value, type, isTypeTest: true);
}
@override
ir.Primitive visitIsNot(ast.Send node,
ast.Node expression, DartType type, _) {
ir.Primitive value = visit(expression);
ir.Primitive check = irBuilder.buildTypeOperator(
value, type, isTypeTest: true);
return irBuilder.buildNegation(check);
}
ir.Primitive translateBinary(ast.Send node,
ast.Node left,
op.BinaryOperator operator,
ast.Node right) {
Selector selector = new Selector.binaryOperator(operator.selectorName);
ir.Primitive receiver = visit(left);
List<ir.Primitive> arguments = <ir.Primitive>[visit(right)];
arguments = normalizeDynamicArguments(selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
receiver, selector, elements.getTypeMask(node), arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
@override
ir.Primitive visitBinary(ast.Send node,
ast.Node left,
op.BinaryOperator operator,
ast.Node right, _) {
return translateBinary(node, left, operator, right);
}
@override
ir.Primitive visitIndex(ast.Send node,
ast.Node receiver,
ast.Node index, _) {
Selector selector = new Selector.index();
ir.Primitive target = visit(receiver);
List<ir.Primitive> arguments = <ir.Primitive>[visit(index)];
arguments = normalizeDynamicArguments(selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target, selector, elements.getTypeMask(node), arguments,
sourceInformation:
sourceInformationBuilder.buildCall(receiver, node.selector));
}
ir.Primitive translateSuperBinary(FunctionElement function,
op.BinaryOperator operator,
ast.Node argument) {
CallStructure callStructure = CallStructure.ONE_ARG;
List<ir.Primitive> arguments = <ir.Primitive>[visit(argument)];
arguments = normalizeDynamicArguments(callStructure, arguments);
return irBuilder.buildSuperMethodInvocation(
function, callStructure, arguments);
}
@override
ir.Primitive visitSuperBinary(
ast.Send node,
FunctionElement function,
op.BinaryOperator operator,
ast.Node argument,
_) {
return translateSuperBinary(function, operator, argument);
}
@override
ir.Primitive visitSuperIndex(
ast.Send node,
FunctionElement function,
ast.Node index,
_) {
return irBuilder.buildSuperIndex(function, visit(index));
}
@override
ir.Primitive visitEquals(
ast.Send node,
ast.Node left,
ast.Node right,
_) {
return translateBinary(node, left, op.BinaryOperator.EQ, right);
}
@override
ir.Primitive visitSuperEquals(
ast.Send node,
FunctionElement function,
ast.Node argument,
_) {
return translateSuperBinary(function, op.BinaryOperator.EQ, argument);
}
@override
ir.Primitive visitNot(
ast.Send node,
ast.Node expression,
_) {
return irBuilder.buildNegation(visit(expression));
}
@override
ir.Primitive visitNotEquals(
ast.Send node,
ast.Node left,
ast.Node right,
_) {
return irBuilder.buildNegation(
translateBinary(node, left, op.BinaryOperator.NOT_EQ, right));
}
@override
ir.Primitive visitSuperNotEquals(
ast.Send node,
FunctionElement function,
ast.Node argument,
_) {
return irBuilder.buildNegation(
translateSuperBinary(function, op.BinaryOperator.NOT_EQ, argument));
}
@override
ir.Primitive visitUnary(ast.Send node,
op.UnaryOperator operator, ast.Node expression, _) {
// TODO(johnniwinther): Clean up the creation of selectors.
Selector selector = operator.selector;
ir.Primitive receiver = translateReceiver(expression);
return irBuilder.buildDynamicInvocation(
receiver, selector, elements.getTypeMask(node), const [],
sourceInformation: sourceInformationBuilder.buildCall(
expression, node));
}
@override
ir.Primitive visitSuperUnary(
ast.Send node,
op.UnaryOperator operator,
FunctionElement function,
_) {
return irBuilder.buildSuperMethodInvocation(
function, CallStructure.NO_ARGS, const []);
}
// TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
// semantic correlation between arguments and invocation.
List<ir.Primitive> translateDynamicArguments(ast.NodeList nodeList,
CallStructure callStructure) {
List<ir.Primitive> arguments = nodeList.nodes.mapToList(visit);
return normalizeDynamicArguments(callStructure, arguments);
}
// TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
// semantic correlation between arguments and invocation.
List<ir.Primitive> translateStaticArguments(ast.NodeList nodeList,
Element element,
CallStructure callStructure) {
List<ir.Primitive> arguments = nodeList.nodes.mapToList(visit);
return normalizeStaticArguments(callStructure, element, arguments);
}
ir.Primitive translateCallInvoke(ir.Primitive target,
ast.NodeList arguments,
CallStructure callStructure,
SourceInformation sourceInformation) {
return irBuilder.buildCallInvocation(target, callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformation: sourceInformation);
}
@override
ir.Primitive handleConstantInvoke(
ast.Send node,
ConstantExpression constant,
ast.NodeList arguments,
CallStructure callStructure,
_) {
ir.Primitive target = buildConstantExpression(constant,
sourceInformationBuilder.buildGet(node));
return translateCallInvoke(target, arguments, callStructure,
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive handleDynamicInvoke(
ast.Send node,
ast.Node receiver,
ast.NodeList arguments,
Selector selector,
_) {
return irBuilder.buildDynamicInvocation(
translateReceiver(receiver), selector, elements.getTypeMask(node),
translateDynamicArguments(arguments, selector.callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
@override
ir.Primitive visitIfNotNullDynamicPropertyInvoke(
ast.Send node,
ast.Node receiver,
ast.NodeList arguments,
Selector selector,
_) {
ir.Primitive target = visit(receiver);
return irBuilder.buildIfNotNullSend(
target,
nested(() => irBuilder.buildDynamicInvocation(
target, selector, elements.getTypeMask(node),
translateDynamicArguments(arguments, selector.callStructure))));
}
ir.Primitive handleLocalInvoke(
ast.Send node,
LocalElement element,
ast.NodeList arguments,
CallStructure callStructure,
_) {
return irBuilder.buildLocalVariableInvocation(element, callStructure,
translateDynamicArguments(arguments, callStructure),
callSourceInformation:
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive visitLocalFunctionInvoke(
ast.Send node,
LocalFunctionElement function,
ast.NodeList arguments,
CallStructure callStructure,
_) {
return irBuilder.buildLocalFunctionInvocation(function, callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) {
return buildStaticFieldGet(field, sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive handleStaticFieldInvoke(
ast.Send node,
FieldElement field,
ast.NodeList arguments,
CallStructure callStructure,
_) {
SourceInformation src = sourceInformationBuilder.buildGet(node);
ir.Primitive target = buildStaticFieldGet(field, src);
return irBuilder.buildCallInvocation(target,
callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive handleStaticFunctionInvoke(
ast.Send node,
MethodElement function,
ast.NodeList arguments,
CallStructure callStructure,
_);
@override
ir.Primitive handleStaticFunctionIncompatibleInvoke(
ast.Send node,
MethodElement function,
ast.NodeList arguments,
CallStructure callStructure, _) {
return buildStaticNoSuchMethod(
elements.getSelector(node),
arguments.nodes.mapToList(visit));
}
@override
ir.Primitive handleStaticGetterInvoke(
ast.Send node,
FunctionElement getter,
ast.NodeList arguments,
CallStructure callStructure,
_) {
ir.Primitive target = irBuilder.buildStaticGetterGet(
getter, sourceInformationBuilder.buildGet(node));
return irBuilder.buildCallInvocation(target,
callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive visitSuperFieldInvoke(
ast.Send node,
FieldElement field,
ast.NodeList arguments,
CallStructure callStructure,
_) {
ir.Primitive target = irBuilder.buildSuperFieldGet(field);
return irBuilder.buildCallInvocation(target,
callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive visitSuperGetterInvoke(
ast.Send node,
FunctionElement getter,
ast.NodeList arguments,
CallStructure callStructure,
_) {
ir.Primitive target = irBuilder.buildSuperGetterGet(
getter, sourceInformationBuilder.buildGet(node));
return irBuilder.buildCallInvocation(target,
callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive visitSuperMethodInvoke(
ast.Send node,
MethodElement method,
ast.NodeList arguments,
CallStructure callStructure,
_) {
return irBuilder.buildSuperMethodInvocation(method, callStructure,
translateDynamicArguments(arguments, callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
@override
ir.Primitive visitSuperMethodIncompatibleInvoke(
ast.Send node,
MethodElement method,
ast.NodeList arguments,
CallStructure callStructure, _) {
return buildInstanceNoSuchMethod(
elements.getSelector(node),
elements.getTypeMask(node),
translateDynamicArguments(arguments, callStructure));
}
@override
ir.Primitive visitUnresolvedSuperInvoke(
ast.Send node,
Element element,
ast.NodeList arguments,
Selector selector, _) {
return buildInstanceNoSuchMethod(
elements.getSelector(node),
elements.getTypeMask(node),
translateDynamicArguments(arguments, selector.callStructure));
}
@override
ir.Primitive visitThisInvoke(
ast.Send node,
ast.NodeList arguments,
CallStructure callStructure,
_) {
return translateCallInvoke(
irBuilder.buildThis(),
arguments,
callStructure,
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive visitTypeVariableTypeLiteralInvoke(
ast.Send node,
TypeVariableElement element,
ast.NodeList arguments,
CallStructure callStructure,
_) {
return translateCallInvoke(
translateTypeVariableTypeLiteral(
element, sourceInformationBuilder.buildGet(node)),
arguments,
callStructure,
sourceInformationBuilder.buildCall(node, arguments));
}
@override
ir.Primitive visitIndexSet(
ast.SendSet node,
ast.Node receiver,
ast.Node index,
ast.Node rhs,
_) {
return irBuilder.buildDynamicIndexSet(
visit(receiver), elements.getTypeMask(node), visit(index), visit(rhs));
}
@override
ir.Primitive visitSuperIndexSet(
ast.SendSet node,
FunctionElement function,
ast.Node index,
ast.Node rhs,
_) {
return irBuilder.buildSuperIndexSet(function, visit(index), visit(rhs));
}
ir.Primitive translateCompounds(
ast.SendSet node,
{ir.Primitive getValue(),
CompoundRhs rhs,
void setValue(ir.Primitive value)}) {
ir.Primitive value = getValue();
op.BinaryOperator operator = rhs.operator;
if (operator.kind == op.BinaryOperatorKind.IF_NULL) {
// Unlike other compound operators if-null conditionally will not do the
// assignment operation.
return irBuilder.buildIfNull(value, nested(() {
ir.Primitive newValue = build(rhs.rhs);
setValue(newValue);
return newValue;
}));
}
Selector operatorSelector =
new Selector.binaryOperator(operator.selectorName);
ir.Primitive rhsValue;
if (rhs.kind == CompoundKind.ASSIGNMENT) {
rhsValue = visit(rhs.rhs);
} else {
rhsValue = irBuilder.buildIntegerConstant(1);
}
List<ir.Primitive> arguments = <ir.Primitive>[rhsValue];
arguments = normalizeDynamicArguments(
operatorSelector.callStructure, arguments);
TypeMask operatorTypeMask =
elements.getOperatorTypeMaskInComplexSendSet(node);
SourceInformation operatorSourceInformation =
sourceInformationBuilder.buildCall(node, node.assignmentOperator);
ir.Primitive result = irBuilder.buildDynamicInvocation(
value, operatorSelector, operatorTypeMask, arguments,
sourceInformation: operatorSourceInformation);
setValue(result);
return rhs.kind == CompoundKind.POSTFIX ? value : result;
}
ir.Primitive translateSetIfNull(
ast.SendSet node,
{ir.Primitive getValue(),
ast.Node rhs,
void setValue(ir.Primitive value)}) {
ir.Primitive value = getValue();
// Unlike other compound operators if-null conditionally will not do the
// assignment operation.
return irBuilder.buildIfNull(value, nested(() {
ir.Primitive newValue = build(rhs);
setValue(newValue);
return newValue;
}));
}
@override
ir.Primitive handleDynamicSet(
ast.SendSet node,
ast.Node receiver,
Name name,
ast.Node rhs,
_) {
return irBuilder.buildDynamicSet(
translateReceiver(receiver),
new Selector.setter(name),
elements.getTypeMask(node),
visit(rhs));
}
@override
ir.Primitive visitIfNotNullDynamicPropertySet(
ast.SendSet node,
ast.Node receiver,
Name name,
ast.Node rhs,
_) {
ir.Primitive target = visit(receiver);
return irBuilder.buildIfNotNullSend(
target,
nested(() => irBuilder.buildDynamicSet(
target,
new Selector.setter(name),
elements.getTypeMask(node),
visit(rhs))));
}
@override
ir.Primitive handleLocalSet(
ast.SendSet node,
LocalElement element,
ast.Node rhs,
_) {
return irBuilder.buildLocalVariableSet(element, visit(rhs));
}
@override
ir.Primitive handleStaticFieldSet(
ast.SendSet node,
FieldElement field,
ast.Node rhs,
_) {
return irBuilder.buildStaticFieldSet(field, visit(rhs));
}
@override
ir.Primitive visitSuperFieldSet(
ast.SendSet node,
FieldElement field,
ast.Node rhs,
_) {
return irBuilder.buildSuperFieldSet(field, visit(rhs));
}
@override
ir.Primitive visitSuperSetterSet(
ast.SendSet node,
FunctionElement setter,
ast.Node rhs,
_) {
return irBuilder.buildSuperSetterSet(setter, visit(rhs));
}
@override
ir.Primitive visitUnresolvedSuperIndexSet(
ast.Send node,
Element element,
ast.Node index,
ast.Node rhs,
arg) {
return giveup(node, 'visitUnresolvedSuperIndexSet');
}
@override
ir.Primitive handleStaticSetterSet(
ast.SendSet node,
FunctionElement setter,
ast.Node rhs,
_) {
return irBuilder.buildStaticSetterSet(setter, visit(rhs));
}
@override
ir.Primitive handleTypeLiteralConstantCompounds(
ast.SendSet node,
ConstantExpression constant,
CompoundRhs rhs,
arg) {
return translateCompounds(
node,
getValue: () {
return buildConstantExpression(constant,
sourceInformationBuilder.buildGet(node));
},
rhs: rhs,
setValue: (value) {}); // The binary operator will throw before this.
}
@override
ir.Primitive handleTypeLiteralConstantSetIfNulls(
ast.SendSet node,
ConstantExpression constant,
ast.Node rhs,
_) {
// The type literal is never `null`.
return buildConstantExpression(constant,
sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive handleDynamicCompounds(
ast.SendSet node,
ast.Node receiver,
Name name,
CompoundRhs rhs,
arg) {
ir.Primitive target = translateReceiver(receiver);
ir.Primitive helper() {
return translateCompounds(
node,
getValue: () => irBuilder.buildDynamicGet(
target,
new Selector.getter(name),
elements.getGetterTypeMaskInComplexSendSet(node),
sourceInformationBuilder.buildGet(node)),
rhs: rhs,
setValue: (ir.Primitive result) {
irBuilder.buildDynamicSet(
target,
new Selector.setter(name),
elements.getTypeMask(node),
result);
});
}
return node.isConditional
? irBuilder.buildIfNotNullSend(target, nested(helper))
: helper();
}
@override
ir.Primitive handleDynamicSetIfNulls(
ast.Send node,
ast.Node receiver,
Name name,
ast.Node rhs,
_) {
ir.Primitive target = translateReceiver(receiver);
ir.Primitive helper() {
return translateSetIfNull(
node,
getValue: () => irBuilder.buildDynamicGet(
target,
new Selector.getter(name),
elements.getGetterTypeMaskInComplexSendSet(node),
sourceInformationBuilder.buildGet(node)),
rhs: rhs,
setValue: (ir.Primitive result) {
irBuilder.buildDynamicSet(
target,
new Selector.setter(name),
elements.getTypeMask(node),
result);
});
}
return node.isConditional
? irBuilder.buildIfNotNullSend(target, nested(helper))
: helper();
}
ir.Primitive buildLocalNoSuchSetter(LocalElement local, ir.Primitive value) {
Selector selector = new Selector.setter(
new Name(local.name, local.library, isSetter: true));
return buildStaticNoSuchMethod(selector, [value]);
}
@override
ir.Primitive handleLocalCompounds(
ast.SendSet node,
LocalElement local,
CompoundRhs rhs,
arg,
{bool isSetterValid}) {
return translateCompounds(
node,
getValue: () {
if (local.isFunction) {
return irBuilder.buildLocalFunctionGet(local);
} else {
return irBuilder.buildLocalVariableGet(local);
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
if (isSetterValid) {
irBuilder.buildLocalVariableSet(local, result);
} else {
return buildLocalNoSuchSetter(local, result);
}
});
}
@override
ir.Primitive handleLocalSetIfNulls(
ast.SendSet node,
LocalElement local,
ast.Node rhs,
_,
{bool isSetterValid}) {
return translateSetIfNull(
node,
getValue: () {
if (local.isFunction) {
return irBuilder.buildLocalFunctionGet(local);
} else {
return irBuilder.buildLocalVariableGet(local);
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
if (isSetterValid) {
irBuilder.buildLocalVariableSet(local, result);
} else {
return buildLocalNoSuchSetter(local, result);
}
});
}
ir.Primitive buildStaticNoSuchGetter(Element element) {
return buildStaticNoSuchMethod(
new Selector.getter(new Name(element.name, element.library)),
const <ir.Primitive>[]);
}
ir.Primitive buildStaticNoSuchSetter(Element element, ir.Primitive value) {
return buildStaticNoSuchMethod(
new Selector.setter(new Name(element.name, element.library)),
<ir.Primitive>[value]);
}
@override
ir.Primitive handleStaticCompounds(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
CompoundRhs rhs,
arg) {
return translateCompounds(
node,
getValue: () {
switch (getterKind) {
case CompoundGetter.FIELD:
SourceInformation src = sourceInformationBuilder.buildGet(node);
return irBuilder.buildStaticFieldGet(getter, src);
case CompoundGetter.GETTER:
return irBuilder.buildStaticGetterGet(
getter, sourceInformationBuilder.buildGet(node));
case CompoundGetter.METHOD:
return irBuilder.buildStaticFunctionGet(getter);
case CompoundGetter.UNRESOLVED:
return buildStaticNoSuchGetter(getter);
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
return irBuilder.buildStaticFieldSet(setter, result);
case CompoundSetter.SETTER:
return irBuilder.buildStaticSetterSet(setter, result);
case CompoundSetter.INVALID:
return buildStaticNoSuchSetter(setter, result);
}
});
}
@override
ir.Primitive handleStaticSetIfNulls(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
ast.Node rhs,
_) {
return translateSetIfNull(
node,
getValue: () {
switch (getterKind) {
case CompoundGetter.FIELD:
SourceInformation src = sourceInformationBuilder.buildGet(node);
return irBuilder.buildStaticFieldGet(getter, src);
case CompoundGetter.GETTER:
return irBuilder.buildStaticGetterGet(
getter, sourceInformationBuilder.buildGet(node));
case CompoundGetter.METHOD:
return irBuilder.buildStaticFunctionGet(getter);
case CompoundGetter.UNRESOLVED:
return buildStaticNoSuchGetter(getter);
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
return irBuilder.buildStaticFieldSet(setter, result);
case CompoundSetter.SETTER:
return irBuilder.buildStaticSetterSet(setter, result);
case CompoundSetter.INVALID:
return buildStaticNoSuchSetter(setter, result);
}
});
}
ir.Primitive buildSuperNoSuchGetter(Element element, TypeMask mask) {
return buildInstanceNoSuchMethod(
new Selector.getter(new Name(element.name, element.library)),
mask,
const <ir.Primitive>[]);
}
ir.Primitive buildSuperNoSuchSetter(Element element,
TypeMask mask,
ir.Primitive value) {
return buildInstanceNoSuchMethod(
new Selector.setter(new Name(element.name, element.library)),
mask,
<ir.Primitive>[value]);
}
@override
ir.Primitive handleSuperCompounds(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
CompoundRhs rhs,
arg) {
return translateCompounds(
node,
getValue: () {
switch (getterKind) {
case CompoundGetter.FIELD:
return irBuilder.buildSuperFieldGet(getter);
case CompoundGetter.GETTER:
return irBuilder.buildSuperGetterGet(
getter, sourceInformationBuilder.buildGet(node));
case CompoundGetter.METHOD:
return irBuilder.buildSuperMethodGet(getter);
case CompoundGetter.UNRESOLVED:
return buildSuperNoSuchGetter(
getter,
elements.getGetterTypeMaskInComplexSendSet(node));
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
return irBuilder.buildSuperFieldSet(setter, result);
case CompoundSetter.SETTER:
return irBuilder.buildSuperSetterSet(setter, result);
case CompoundSetter.INVALID:
return buildSuperNoSuchSetter(
setter, elements.getTypeMask(node), result);
}
});
}
@override
ir.Primitive handleSuperSetIfNulls(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
ast.Node rhs,
_) {
return translateSetIfNull(
node,
getValue: () {
switch (getterKind) {
case CompoundGetter.FIELD:
return irBuilder.buildSuperFieldGet(getter);
case CompoundGetter.GETTER:
return irBuilder.buildSuperGetterGet(
getter, sourceInformationBuilder.buildGet(node));
case CompoundGetter.METHOD:
return irBuilder.buildSuperMethodGet(getter);
case CompoundGetter.UNRESOLVED:
return buildSuperNoSuchGetter(
getter,
elements.getGetterTypeMaskInComplexSendSet(node));
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
return irBuilder.buildSuperFieldSet(setter, result);
case CompoundSetter.SETTER:
return irBuilder.buildSuperSetterSet(setter, result);
case CompoundSetter.INVALID:
return buildSuperNoSuchSetter(
setter, elements.getTypeMask(node), result);
}
});
}
@override
ir.Primitive handleTypeVariableTypeLiteralCompounds(
ast.SendSet node,
TypeVariableElement typeVariable,
CompoundRhs rhs,
arg) {
return translateCompounds(
node,
getValue: () {
return irBuilder.buildReifyTypeVariable(
typeVariable.type,
sourceInformationBuilder.buildGet(node));
},
rhs: rhs,
setValue: (value) {}); // The binary operator will throw before this.
}
@override
ir.Primitive visitTypeVariableTypeLiteralSetIfNull(
ast.Send node,
TypeVariableElement element,
ast.Node rhs,
_) {
// The type variable is never `null`.
return translateTypeVariableTypeLiteral(element,
sourceInformationBuilder.buildGet(node));
}
@override
ir.Primitive handleIndexCompounds(
ast.SendSet node,
ast.Node receiver,
ast.Node index,
CompoundRhs rhs,
arg) {
ir.Primitive target = visit(receiver);
ir.Primitive indexValue = visit(index);
return translateCompounds(
node,
getValue: () {
Selector selector = new Selector.index();
List<ir.Primitive> arguments = <ir.Primitive>[indexValue];
arguments =
normalizeDynamicArguments(selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target,
selector,
elements.getGetterTypeMaskInComplexSendSet(node),
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(receiver, node));
},
rhs: rhs,
setValue: (ir.Primitive result) {
irBuilder.buildDynamicIndexSet(
target,
elements.getTypeMask(node),
indexValue,
result);
});
}
@override
ir.Primitive handleSuperIndexCompounds(
ast.SendSet node,
Element indexFunction,
Element indexSetFunction,
ast.Node index,
CompoundRhs rhs,
arg,
{bool isGetterValid,
bool isSetterValid}) {
ir.Primitive indexValue = visit(index);
return translateCompounds(
node,
getValue: () {
if (isGetterValid) {
return irBuilder.buildSuperIndex(indexFunction, indexValue);
} else {
return buildInstanceNoSuchMethod(
new Selector.index(),
elements.getGetterTypeMaskInComplexSendSet(node),
<ir.Primitive>[indexValue]);
}
},
rhs: rhs,
setValue: (ir.Primitive result) {
if (isSetterValid) {
irBuilder.buildSuperIndexSet(indexSetFunction, indexValue, result);
} else {
buildInstanceNoSuchMethod(
new Selector.indexSet(),
elements.getTypeMask(node),
<ir.Primitive>[indexValue, result]);
}
});
}
/// Evaluates a string interpolation and appends each part to [accumulator]
/// (after stringify conversion).
void buildStringParts(ast.Node node, List<ir.Primitive> accumulator) {
if (node is ast.StringJuxtaposition) {
buildStringParts(node.first, accumulator);
buildStringParts(node.second, accumulator);
} else if (node is ast.StringInterpolation) {
buildStringParts(node.string, accumulator);
for (ast.StringInterpolationPart part in node.parts) {
buildStringParts(part.expression, accumulator);
buildStringParts(part.string, accumulator);
}
} else if (node is ast.LiteralString) {
// Empty strings often occur at the end of a string interpolation,
// do not bother to include them.
if (!node.dartString.isEmpty) {
accumulator.add(irBuilder.buildDartStringConstant(node.dartString));
}
} else if (node is ast.ParenthesizedExpression) {
buildStringParts(node.expression, accumulator);
} else {
ir.Primitive value = visit(node);
accumulator.add(irBuilder.buildStringify(value));
}
}
ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) {
assert(irBuilder.isOpen);
List<ir.Primitive> parts = <ir.Primitive>[];
buildStringParts(node, parts);
return irBuilder.buildStringConcatenation(parts);
}
ir.Primitive visitStringInterpolation(ast.StringInterpolation node) {
assert(irBuilder.isOpen);
List<ir.Primitive> parts = <ir.Primitive>[];
buildStringParts(node, parts);
return irBuilder.buildStringConcatenation(parts);
}
ir.Primitive translateConstant(ast.Node node) {
assert(irBuilder.isOpen);
return irBuilder.buildConstant(
getConstantForNode(node),
sourceInformation: sourceInformationBuilder.buildGet(node));
}
ir.Primitive visitThrow(ast.Throw node) {
assert(irBuilder.isOpen);
// This function is not called for throw expressions occurring as
// statements.
return irBuilder.buildNonTailThrow(visit(node.expression));
}
ir.Primitive buildStaticNoSuchMethod(
Selector selector,
List<ir.Primitive> arguments);
ir.Primitive buildInstanceNoSuchMethod(
Selector selector,
TypeMask mask,
List<ir.Primitive> arguments);
ir.Primitive buildRuntimeError(String message);
ir.Primitive buildAbstractClassInstantiationError(ClassElement element);
@override
ir.Primitive visitUnresolvedCompound(
ast.Send node,
Element element,
op.AssignmentOperator operator,
ast.Node rhs, _) {
// TODO(asgerf): What is unresolved? The getter and/or the setter?
// If it was the setter, we must evaluate the right-hand side.
return buildStaticNoSuchMethod(elements.getSelector(node), []);
}
@override
ir.Primitive visitUnresolvedClassConstructorInvoke(
ast.NewExpression node,
Element element,
DartType type,
ast.NodeList arguments,
Selector selector, _) {
// If the class is missing it's a runtime error.
return buildRuntimeError("Unresolved class: '${element.name}'");
}
@override
ir.Primitive visitUnresolvedConstructorInvoke(
ast.NewExpression node,
Element constructor,
DartType type,
ast.NodeList arguments,
Selector selector, _) {
// If the class is there but the constructor is missing, it's an NSM error.
return buildStaticNoSuchMethod(selector,
translateDynamicArguments(arguments, selector.callStructure));
}
@override
ir.Primitive visitConstructorIncompatibleInvoke(
ast.NewExpression node,
Element constructor,
DartType type,
ast.NodeList arguments,
CallStructure callStructure, _) {
return buildStaticNoSuchMethod(elements.getSelector(node.send),
translateDynamicArguments(arguments, callStructure));
}
@override
ir.Primitive visitUnresolvedGet(
ast.Send node,
Element element, _) {
return buildStaticNoSuchMethod(elements.getSelector(node), []);
}
@override
ir.Primitive visitUnresolvedInvoke(
ast.Send node,
Element element,
ast.NodeList arguments,
Selector selector, _) {
return buildStaticNoSuchMethod(elements.getSelector(node),
arguments.nodes.mapToList(visit));
}
@override
ir.Primitive visitUnresolvedRedirectingFactoryConstructorInvoke(
ast.NewExpression node,
ConstructorElement constructor,
InterfaceType type,
ast.NodeList arguments,
CallStructure callStructure, _) {
String nameString = Elements.reconstructConstructorName(constructor);
Name name = new Name(nameString, constructor.library);
return buildStaticNoSuchMethod(
new Selector.call(name, callStructure),
translateDynamicArguments(arguments, callStructure));
}
@override
ir.Primitive visitUnresolvedSet(
ast.Send node,
Element element,
ast.Node rhs, _) {
return buildStaticNoSuchMethod(elements.getSelector(node), [visit(rhs)]);
}
@override
ir.Primitive visitUnresolvedSuperIndex(
ast.Send node,
Element function,
ast.Node index, _) {
// Assume the index getter is missing.
return buildInstanceNoSuchMethod(
new Selector.index(), elements.getTypeMask(node), [visit(index)]);
}
@override
ir.Primitive visitUnresolvedSuperBinary(
ast.Send node,
Element element,
op.BinaryOperator operator,
ast.Node argument, _) {
return buildInstanceNoSuchMethod(
elements.getSelector(node),
elements.getTypeMask(node),
[visit(argument)]);
}
@override
ir.Primitive visitUnresolvedSuperUnary(
ast.Send node,
op.UnaryOperator operator,
Element element, _) {
return buildInstanceNoSuchMethod(
elements.getSelector(node), elements.getTypeMask(node), []);
}
@override
ir.Primitive bulkHandleNode(ast.Node node, String message, _) {
return giveup(node, "Unhandled node: ${message.replaceAll('#', '$node')}");
}
@override
ir.Primitive bulkHandleError(ast.Send node, ErroneousElement error, _) {
assert(compiler.compilationFailed);
return irBuilder.buildNullConstant();
}
@override
ir.Primitive visitClassTypeLiteralSet(
ast.SendSet node,
TypeConstantExpression constant,
ast.Node rhs, _) {
InterfaceType type = constant.type;
ClassElement element = type.element;
return buildStaticNoSuchMethod(
new Selector.setter(element.memberName),
[visit(rhs)]);
}
@override
ir.Primitive visitTypedefTypeLiteralSet(
ast.SendSet node,
TypeConstantExpression constant,
ast.Node rhs, _) {
TypedefType type = constant.type;
TypedefElement element = type.element;
return buildStaticNoSuchMethod(
new Selector.setter(element.memberName),
[visit(rhs)]);
}
@override
ir.Primitive visitTypeVariableTypeLiteralSet(
ast.SendSet node,
TypeVariableElement element,
ast.Node rhs, _) {
return buildStaticNoSuchMethod(
new Selector.setter(element.memberName), [visit(rhs)]);
}
@override
ir.Primitive visitDynamicTypeLiteralSet(
ast.SendSet node,
ConstantExpression constant,
ast.Node rhs, _) {
return buildStaticNoSuchMethod(
new Selector.setter(Names.dynamic_), [visit(rhs)]);
}
@override
ir.Primitive visitAbstractClassConstructorInvoke(
ast.NewExpression node,
ConstructorElement element,
InterfaceType type,
ast.NodeList arguments,
CallStructure callStructure, _) {
return buildAbstractClassInstantiationError(element.enclosingClass);
}
@override
ir.Primitive handleFinalStaticFieldSet(
ast.SendSet node,
FieldElement field,
ast.Node rhs, _) {
// TODO(asgerf): Include class name somehow for static class members?
return buildStaticNoSuchMethod(
new Selector.setter(field.memberName),
[visit(rhs)]);
}
@override
ir.Primitive visitFinalSuperFieldSet(
ast.SendSet node,
FieldElement field,
ast.Node rhs, _) {
return buildInstanceNoSuchMethod(
new Selector.setter(field.memberName),
elements.getTypeMask(node),
[visit(rhs)]);
}
@override
ir.Primitive handleImmutableLocalSet(
ast.SendSet node,
LocalElement local,
ast.Node rhs, _) {
return buildStaticNoSuchMethod(
new Selector.setter(new Name(local.name, local.library)),
[visit(rhs)]);
}
@override
ir.Primitive handleStaticFunctionSet(
ast.Send node,
MethodElement function,
ast.Node rhs,
_) {
return buildStaticNoSuchMethod(
new Selector.setter(function.memberName),
[visit(rhs)]);
}
@override
ir.Primitive handleStaticGetterSet(
ast.SendSet node,
GetterElement getter,
ast.Node rhs,
_) {
return buildStaticNoSuchMethod(
new Selector.setter(getter.memberName),
[visit(rhs)]);
}
@override
ir.Primitive handleStaticSetterGet(
ast.Send node,
SetterElement setter,
_) {
return buildStaticNoSuchMethod(
new Selector.getter(setter.memberName),
[]);
}
@override
ir.Primitive handleStaticSetterInvoke(
ast.Send node,
SetterElement setter,
ast.NodeList arguments,
CallStructure callStructure, _) {
// Translate as a method call.
List<ir.Primitive> args = arguments.nodes.mapToList(visit);
return buildStaticNoSuchMethod(
new Selector.call(setter.memberName, callStructure),
args);
}
@override
ir.Primitive visitSuperGetterSet(
ast.SendSet node,
GetterElement getter,
ast.Node rhs,
_) {
return buildInstanceNoSuchMethod(
new Selector.setter(getter.memberName),
elements.getTypeMask(node),
[visit(rhs)]);
}
@override
ir.Primitive visitSuperMethodSet(
ast.Send node,
MethodElement method,
ast.Node rhs,
_) {
return buildInstanceNoSuchMethod(
new Selector.setter(method.memberName),
elements.getTypeMask(node),
[visit(rhs)]);
}
@override
ir.Primitive visitSuperSetterGet(
ast.Send node,
SetterElement setter, _) {
return buildInstanceNoSuchMethod(
new Selector.setter(setter.memberName),
elements.getTypeMask(node),
[]);
}
@override
ir.Primitive visitSuperSetterInvoke(
ast.Send node,
SetterElement setter,
ast.NodeList arguments,
CallStructure callStructure, _) {
List<ir.Primitive> args =
translateDynamicArguments(arguments, callStructure);
return buildInstanceNoSuchMethod(
new Selector.call(setter.memberName, callStructure),
elements.getTypeMask(node),
args);
}
ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) {
try {
return action();
} catch(e) {
if (e == ABORT_IRNODE_BUILDER) {
return null;
}
rethrow;
}
}
internalError(ast.Node node, String message) {
compiler.internalError(node, message);
}
@override
visitNode(ast.Node node) {
giveup(node, "Unhandled node");
}
dynamic giveup(ast.Node node, [String reason]) {
bailoutMessage = '($node): $reason';
throw ABORT_IRNODE_BUILDER;
}
}
final String ABORT_IRNODE_BUILDER = "IrNode builder aborted";
/// Classifies local variables and local functions as captured, if they
/// are accessed from within a nested function.
///
/// This class is specific to the [DartIrBuilder], in that it gives up if it
/// sees a feature that is currently unsupport by that builder. In particular,
/// loop variables captured in a for-loop initializer, condition, or update
/// expression are unsupported.
class DartCapturedVariables extends ast.Visitor {
final TreeElements elements;
DartCapturedVariables(this.elements);
FunctionElement currentFunction;
bool insideInitializer = false;
Set<Local> capturedVariables = new Set<Local>();
/// A map containing variables boxed inside try blocks.
///
/// The map is keyed by the [NodeList] of catch clauses for try/catch and
/// by the finally block for try/finally. try/catch/finally is treated
/// as a try/catch nested in the try block of a try/finally.
Map<ast.Node, TryStatementInfo> tryStatements =
<ast.Node, TryStatementInfo>{};
List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[];
bool get inTryStatement => tryNestingStack.isNotEmpty;
String bailoutMessage = null;
giveup(ast.Node node, [String reason]) {
bailoutMessage = '($node): $reason';
throw ABORT_IRNODE_BUILDER;
}
void markAsCaptured(Local local) {
capturedVariables.add(local);
}
analyze(ast.Node node) {
visit(node);
// Variables that are captured by a closure are boxed for their entire
// lifetime, so they never need to be boxed on entry to a try block.
// They are not filtered out before this because we cannot identify all
// of them in the same pass (they may be captured by a closure after the
// try statement).
for (TryStatementInfo info in tryStatements.values) {
info.boxedOnEntry.removeAll(capturedVariables);
}
}
visit(ast.Node node) => node.accept(this);
visitNode(ast.Node node) {
node.visitChildren(this);
}
visitFor(ast.For node) {
if (node.initializer != null) visit(node.initializer);
if (node.condition != null) visit(node.condition);
if (node.update != null) visit(node.update);
// Give up if a variable was captured outside of the loop body.
if (node.initializer is ast.VariableDefinitions) {
ast.VariableDefinitions definitions = node.initializer;
for (ast.Node node in definitions.definitions.nodes) {
LocalElement loopVariable = elements[node];
if (capturedVariables.contains(loopVariable)) {
return giveup(node, 'For-loop variable captured in loop header');
}
}
}
if (node.body != null) visit(node.body);
}
void handleSend(ast.Send node) {
Element element = elements[node];
if (Elements.isLocal(element) &&
!element.isConst &&
element.enclosingElement != currentFunction) {
LocalElement local = element;
markAsCaptured(local);
}
}
visitSend(ast.Send node) {
handleSend(node);
node.visitChildren(this);
}
visitSendSet(ast.SendSet node) {
handleSend(node);
Element element = elements[node];
if (Elements.isLocal(element)) {
LocalElement local = element;
if (insideInitializer &&
local.isParameter &&
local.enclosingElement == currentFunction) {
assert(local.enclosingElement.isConstructor);
// Initializers in an initializer-list can communicate via parameters.
// If a parameter is stored in an initializer list we box it.
// TODO(sigurdm): Fix this.
// Though these variables do not outlive the activation of the
// function, they still need to be boxed. As a simplification, we
// treat them as if they are captured by a closure (i.e., they do
// outlive the activation of the function).
markAsCaptured(local);
} else if (inTryStatement) {
assert(local.isParameter || local.isVariable);
// Search for the position of the try block containing the variable
// declaration, or -1 if it is declared outside the outermost try.
int i = tryNestingStack.length - 1;
while (i >= 0 && !tryNestingStack[i].declared.contains(local)) {
--i;
}
// If there is a next inner try, then the variable should be boxed on
// entry to it.
if (i + 1 < tryNestingStack.length) {
tryNestingStack[i + 1].boxedOnEntry.add(local);
}
}
}
node.visitChildren(this);
}
visitFunctionExpression(ast.FunctionExpression node) {
FunctionElement savedFunction = currentFunction;
currentFunction = elements[node];
if (currentFunction.asyncMarker != AsyncMarker.SYNC &&
currentFunction.asyncMarker != AsyncMarker.SYNC_STAR &&
currentFunction.asyncMarker != AsyncMarker.ASYNC) {
giveup(node, "cannot handle sync*/async* functions");
}
bool savedInsideInitializer = insideInitializer;
if (node.initializers != null) {
insideInitializer = true;
visit(node.initializers);
}
insideInitializer = false;
visit(node.body);
currentFunction = savedFunction;
insideInitializer = savedInsideInitializer;
}
visitTryStatement(ast.TryStatement node) {
// Try/catch/finally is treated as two simpler constructs: try/catch and
// try/finally. The encoding is:
//
// try S0 catch (ex, st) S1 finally S2
// ==>
// try { try S0 catch (ex, st) S1 } finally S2
//
// The analysis associates variables assigned in S0 with the catch clauses
// and variables assigned in S0 and S1 with the finally block.
TryStatementInfo enterTryFor(ast.Node node) {
TryStatementInfo info = new TryStatementInfo();
tryStatements[node] = info;
tryNestingStack.add(info);
return info;
}
void leaveTryFor(TryStatementInfo info) {
assert(tryNestingStack.last == info);
tryNestingStack.removeLast();
}
bool hasCatch = !node.catchBlocks.isEmpty;
bool hasFinally = node.finallyBlock != null;
TryStatementInfo catchInfo, finallyInfo;
// There is a nesting stack of try blocks, so the outer try/finally block
// is added first.
if (hasFinally) finallyInfo = enterTryFor(node.finallyBlock);
if (hasCatch) catchInfo = enterTryFor(node.catchBlocks);
visit(node.tryBlock);
if (hasCatch) {
leaveTryFor(catchInfo);
visit(node.catchBlocks);
}
if (hasFinally) {
leaveTryFor(finallyInfo);
visit(node.finallyBlock);
}
}
visitVariableDefinitions(ast.VariableDefinitions node) {
if (inTryStatement) {
for (ast.Node definition in node.definitions.nodes) {
LocalVariableElement local = elements[definition];
assert(local != null);
// In the closure conversion pass we check for isInitializingFormal,
// but I'm not sure it can arise.
assert(!local.isInitializingFormal);
tryNestingStack.last.declared.add(local);
}
}
node.visitChildren(this);
}
}
/// The [IrBuilder]s view on the information about the program that has been
/// computed in resolution and and type interence.
class GlobalProgramInformation {
final Compiler _compiler;
JavaScriptBackend get _backend => _compiler.backend;
GlobalProgramInformation(this._compiler);
/// Returns [true], if the analysis could not determine that the type
/// arguments for the class [cls] are never used in the program.
bool requiresRuntimeTypesFor(ClassElement cls) {
return cls.typeVariables.isNotEmpty && _backend.classNeedsRti(cls);
}
FunctionElement get stringifyFunction {
return _backend.getStringInterpolationHelper();
}
FunctionElement get throwTypeErrorHelper => _backend.getThrowTypeError();
ClassElement get nullClass => _compiler.nullClass;
DartType unaliasType(DartType type) => type.unalias(_compiler);
TypeMask getTypeMaskForForeign(NativeBehavior behavior) {
if (behavior == null) {
return _backend.dynamicType;
}
return TypeMaskFactory.fromNativeBehavior(behavior, _compiler);
}
FieldElement locateSingleField(Selector selector, TypeMask type) {
return _compiler.world.locateSingleField(selector, type);
}
Element get closureConverter {
return _backend.getClosureConverter();
}
void addNativeMethod(FunctionElement function) {
_backend.emitter.nativeEmitter.nativeMethods.add(function);
}
}
/// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder].
class JsIrBuilderVisitor extends IrBuilderVisitor {
JavaScriptBackend get backend => compiler.backend;
/// Result of closure conversion for the current body of code.
///
/// Will be initialized upon entering the body of a function.
/// It is computed by the [ClosureTranslator].
ClosureClassMap closureClassMap;
JsIrBuilderVisitor(TreeElements elements,
Compiler compiler,
SourceInformationBuilder sourceInformationBuilder)
: super(elements, compiler, sourceInformationBuilder);
/// Builds the IR for creating an instance of the closure class corresponding
/// to the given nested function.
ClosureClassElement makeSubFunction(ast.FunctionExpression node) {
ClosureClassMap innerMap =
compiler.closureToClassMapper.getMappingForNestedFunction(node);
ClosureClassElement closureClass = innerMap.closureClassElement;
return closureClass;
}
ir.Primitive visitFunctionExpression(ast.FunctionExpression node) {
return irBuilder.buildFunctionExpression(makeSubFunction(node),
sourceInformationBuilder.buildCreate(node));
}
visitFunctionDeclaration(ast.FunctionDeclaration node) {
LocalFunctionElement element = elements[node.function];
Object inner = makeSubFunction(node.function);
irBuilder.declareLocalFunction(element, inner,
sourceInformationBuilder.buildCreate(node.function));
}
Map mapValues(Map map, dynamic fn(dynamic)) {
Map result = {};
map.forEach((key, value) {
result[key] = fn(value);
});
return result;
}
/// Converts closure.dart's CapturedVariable into a ClosureLocation.
/// There is a 1:1 corresponce between these; we do this because the
/// IR builder should not depend on synthetic elements.
ClosureLocation getLocation(CapturedVariable v) {
if (v is BoxFieldElement) {
return new ClosureLocation(v.box, v);
} else {
ClosureFieldElement field = v;
return new ClosureLocation(null, field);
}
}
/// If the current function is a nested function with free variables (or a
/// captured reference to `this`), returns a [ClosureEnvironment]
/// indicating how to access these.
ClosureEnvironment getClosureEnvironment() {
if (closureClassMap.closureElement == null) return null;
return new ClosureEnvironment(
closureClassMap.closureElement,
closureClassMap.thisLocal,
mapValues(closureClassMap.freeVariableMap, getLocation));
}
/// If [node] has declarations for variables that should be boxed,
/// returns a [ClosureScope] naming a box to create, and enumerating the
/// variables that should be stored in the box.
///
/// Also see [ClosureScope].
ClosureScope getClosureScopeForNode(ast.Node node) {
closurelib.ClosureScope scope = closureClassMap.capturingScopes[node];
if (scope == null) return null;
// We translate a ClosureScope from closure.dart into IR builder's variant
// because the IR builder should not depend on the synthetic elements
// created in closure.dart.
return new ClosureScope(scope.boxElement,
mapValues(scope.capturedVariables, getLocation),
scope.boxedLoopVariables);
}
/// Returns the [ClosureScope] for any function, possibly different from the
/// one currently being built.
ClosureScope getClosureScopeForFunction(FunctionElement function) {
ClosureClassMap map =
compiler.closureToClassMapper.computeClosureToClassMapping(
function,
function.node,
elements);
closurelib.ClosureScope scope = map.capturingScopes[function.node];
if (scope == null) return null;
return new ClosureScope(scope.boxElement,
mapValues(scope.capturedVariables, getLocation),
scope.boxedLoopVariables);
}
ir.FunctionDefinition buildExecutable(ExecutableElement element) {
return nullIfGiveup(() {
ir.FunctionDefinition root;
switch (element.kind) {
case ElementKind.GENERATIVE_CONSTRUCTOR:
root = buildConstructor(element);
break;
case ElementKind.GENERATIVE_CONSTRUCTOR_BODY:
root = buildConstructorBody(element);
break;
case ElementKind.FUNCTION:
case ElementKind.GETTER:
case ElementKind.SETTER:
root = buildFunction(element);
break;
case ElementKind.FIELD:
if (Elements.isStaticOrTopLevel(element)) {
root = buildStaticFieldInitializer(element);
} else {
// Instance field initializers are inlined in the constructor,
// so we shouldn't need to build anything here.
// TODO(asgerf): But what should we return?
return null;
}
break;
default:
compiler.internalError(element, "Unexpected element type $element");
}
return root;
});
}
ir.FunctionDefinition buildStaticFieldInitializer(FieldElement element) {
if (!backend.constants.lazyStatics.contains(element)) {
return null; // Nothing to do.
}
closureClassMap =
compiler.closureToClassMapper.computeClosureToClassMapping(
element,
element.node,
elements);
IrBuilder builder = getBuilderFor(element);
return withBuilder(builder, () {
irBuilder.buildFunctionHeader(<Local>[]);
ir.Primitive initialValue = visit(element.initializer);
ast.VariableDefinitions node = element.node;
ast.SendSet sendSet = node.definitions.nodes.head;
irBuilder.buildReturn(
value: initialValue,
sourceInformation:
sourceInformationBuilder.buildReturn(sendSet.assignmentOperator));
return irBuilder.makeFunctionDefinition();
});
}
/// Make a visitor suitable for translating ASTs taken from [context].
///
/// Every visitor can only be applied to nodes in one context, because
/// the [elements] field is specific to that context.
JsIrBuilderVisitor makeVisitorForContext(AstElement context) {
return new JsIrBuilderVisitor(
context.resolvedAst.elements,
compiler,
sourceInformationBuilder.forContext(context));
}
/// Builds the IR for an [expression] taken from a different [context].
///
/// Such expressions need to be compiled with a different [sourceFile] and
/// [elements] mapping.
ir.Primitive inlineExpression(AstElement context, ast.Expression expression) {
JsIrBuilderVisitor visitor = makeVisitorForContext(context);
return visitor.withBuilder(irBuilder, () => visitor.visit(expression));
}
/// Builds the IR for a constant taken from a different [context].
///
/// Such constants need to be compiled with a different [sourceFile] and
/// [elements] mapping.
ir.Primitive inlineConstant(AstElement context, ast.Expression exp) {
JsIrBuilderVisitor visitor = makeVisitorForContext(context);
return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp));
}
IrBuilder getBuilderFor(Element element) {
return new IrBuilder(
new GlobalProgramInformation(compiler),
compiler.backend.constants,
element);
}
/// Builds the IR for a given constructor.
///
/// 1. Computes the type held in all own or "inherited" type variables.
/// 2. Evaluates all own or inherited field initializers.
/// 3. Creates the object and assigns its fields and runtime type.
/// 4. Calls constructor body and super constructor bodies.
/// 5. Returns the created object.
ir.FunctionDefinition buildConstructor(ConstructorElement constructor) {
// TODO(asgerf): Optimization: If constructor is redirecting, then just
// evaluate arguments and call the target constructor.
constructor = constructor.implementation;
ClassElement classElement = constructor.enclosingClass.implementation;
IrBuilder builder = getBuilderFor(constructor);
final bool requiresTypeInformation =
builder.program.requiresRuntimeTypesFor(classElement);
return withBuilder(builder, () {
// Setup parameters and create a box if anything is captured.
List<Local> parameters = <Local>[];
constructor.functionSignature.orderedForEachParameter(
(ParameterElement p) => parameters.add(p));
int firstTypeArgumentParameterIndex;
// If instances of the class may need runtime type information, we add a
// synthetic parameter for each type parameter.
if (requiresTypeInformation) {
firstTypeArgumentParameterIndex = parameters.length;
classElement.typeVariables.forEach((TypeVariableType variable) {
parameters.add(new TypeVariableLocal(variable, constructor));
});
} else {
classElement.typeVariables.forEach((TypeVariableType variable) {
irBuilder.declareTypeVariable(variable, const DynamicType());
});
}
// Create IR parameters and setup the environment.
List<ir.Parameter> irParameters = builder.buildFunctionHeader(parameters,
closureScope: getClosureScopeForFunction(constructor));
// Create a list of the values of all type argument parameters, if any.
List<ir.Primitive> typeInformation;
if (requiresTypeInformation) {
typeInformation = irParameters.sublist(firstTypeArgumentParameterIndex);
} else {
typeInformation = const <ir.Primitive>[];
}
// -- Load values for type variables declared on super classes --
// Field initializers for super classes can reference these, so they
// must be available before evaluating field initializers.
// This could be interleaved with field initialization, but we choose do
// get it out of the way here to avoid complications with mixins.
loadTypeVariablesForSuperClasses(classElement);
/// Maps each field from this class or a superclass to its initial value.
Map<FieldElement, ir.Primitive> fieldValues =
<FieldElement, ir.Primitive>{};
// -- Evaluate field initializers ---
// Evaluate field initializers in constructor and super constructors.
List<ConstructorElement> constructorList = <ConstructorElement>[];
evaluateConstructorFieldInitializers(
constructor, constructorList, fieldValues);
// All parameters in all constructors are now bound in the environment.
// BoxLocals for captured parameters are also in the environment.
// The initial value of all fields are now bound in [fieldValues].
// --- Create the object ---
// Get the initial field values in the canonical order.
List<ir.Primitive> instanceArguments = <ir.Primitive>[];
classElement.forEachInstanceField((ClassElement c, FieldElement field) {
ir.Primitive value = fieldValues[field];
if (value != null) {
instanceArguments.add(value);
} else {
assert(Elements.isNativeOrExtendsNative(c));
// Native fields are initialized elsewhere.
}
}, includeSuperAndInjectedMembers: true);
ir.Primitive instance = new ir.CreateInstance(
classElement,
instanceArguments,
typeInformation,
constructor.hasNode
? sourceInformationBuilder.buildCreate(constructor.node)
// TODO(johnniwinther): Provide source information for creation
// through synthetic constructors.
: null);
irBuilder.add(new ir.LetPrim(instance));
// --- Call constructor bodies ---
for (ConstructorElement target in constructorList) {
ConstructorBodyElement bodyElement = getConstructorBody(target);
if (bodyElement == null) continue; // Skip if constructor has no body.
List<ir.Primitive> bodyArguments = <ir.Primitive>[];
for (Local param in getConstructorBodyParameters(bodyElement)) {
bodyArguments.add(irBuilder.environment.lookup(param));
}
irBuilder.buildInvokeDirectly(bodyElement, instance, bodyArguments);
}
// --- step 4: return the created object ----
irBuilder.buildReturn(
value: instance,
sourceInformation:
sourceInformationBuilder.buildImplicitReturn(constructor));
return irBuilder.makeFunctionDefinition();
});
}
/// Evaluates all field initializers on [constructor] and all constructors
/// invoked through `this()` or `super()` ("superconstructors").
///
/// The resulting field values will be available in [fieldValues]. The values
/// are not stored in any fields.
///
/// This procedure assumes that the parameters to [constructor] are available
/// in the IR builder's environment.
///
/// The parameters to superconstructors are, however, assumed *not* to be in
/// the environment, but will be put there by this procedure.
///
/// All constructors will be added to [supers], with superconstructors first.
void evaluateConstructorFieldInitializers(
ConstructorElement constructor,
List<ConstructorElement> supers,
Map<FieldElement, ir.Primitive> fieldValues) {
assert(constructor.isImplementation);
assert(constructor == elements.analyzedElement);
ClassElement enclosingClass = constructor.enclosingClass.implementation;
// Evaluate declaration-site field initializers, unless this constructor
// redirects to another using a `this()` initializer. In that case, these
// will be initialized by the effective target constructor.
if (!constructor.isRedirectingGenerative) {
enclosingClass.forEachInstanceField((ClassElement c, FieldElement field) {
if (field.initializer != null) {
fieldValues[field] = inlineExpression(field, field.initializer);
} else {
if (Elements.isNativeOrExtendsNative(c)) {
// Native field is initialized elsewhere.
} else {
// Fields without an initializer default to null.
// This value will be overwritten below if an initializer is found.
fieldValues[field] = irBuilder.buildNullConstant();
}
}
});
}
// If this is a mixin constructor, it does not have its own parameter list
// or initializer list. Directly forward to the super constructor.
// Note that the declaration-site initializers originating from the
// mixed-in class were handled above.
if (enclosingClass.isMixinApplication) {
forwardSynthesizedMixinConstructor(constructor, supers, fieldValues);
return;
}
// Evaluate initializing parameters, e.g. `Foo(this.x)`.
constructor.functionSignature.orderedForEachParameter(
(ParameterElement parameter) {
if (parameter.isInitializingFormal) {
InitializingFormalElement fieldParameter = parameter;
fieldValues[fieldParameter.fieldElement] =
irBuilder.buildLocalVariableGet(parameter);
}
});
// Evaluate constructor initializers, e.g. `Foo() : x = 50`.
ast.FunctionExpression node = constructor.node;
bool hasConstructorCall = false; // Has this() or super() initializer?
if (node != null && node.initializers != null) {
for(ast.Node initializer in node.initializers) {
if (initializer is ast.SendSet) {
// Field initializer.
FieldElement field = elements[initializer];
fieldValues[field] = visit(initializer.arguments.head);
} else if (initializer is ast.Send) {
// Super or this initializer.
ConstructorElement target = elements[initializer].implementation;
Selector selector = elements.getSelector(initializer);
List<ir.Primitive> arguments = initializer.arguments.mapToList(visit);
evaluateConstructorCallFromInitializer(
target,
selector.callStructure,
arguments,
supers,
fieldValues);
hasConstructorCall = true;
} else {
compiler.internalError(initializer,
"Unexpected initializer type $initializer");
}
}
}
// If no super() or this() was found, also call default superconstructor.
if (!hasConstructorCall && !enclosingClass.isObject) {
ClassElement superClass = enclosingClass.superclass;
FunctionElement target = superClass.lookupDefaultConstructor();
if (target == null) {
compiler.internalError(superClass, "No default constructor available.");
}
target = target.implementation;
evaluateConstructorCallFromInitializer(
target,
CallStructure.NO_ARGS,
const [],
supers,
fieldValues);
}
// Add this constructor after the superconstructors.
supers.add(constructor);
}
/// Evaluates a call to the given constructor from an initializer list.
///
/// Calls [loadArguments] and [evaluateConstructorFieldInitializers] in a
/// visitor that has the proper [TreeElements] mapping.
void evaluateConstructorCallFromInitializer(
ConstructorElement target,
CallStructure call,
List<ir.Primitive> arguments,
List<ConstructorElement> supers,
Map<FieldElement, ir.Primitive> fieldValues) {
JsIrBuilderVisitor visitor = makeVisitorForContext(target);
visitor.withBuilder(irBuilder, () {
visitor.loadArguments(target, call, arguments);
visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
});
}
/// Evaluate the implicit super call in the given mixin constructor.
void forwardSynthesizedMixinConstructor(
ConstructorElement constructor,
List<ConstructorElement> supers,
Map<FieldElement, ir.Primitive> fieldValues) {
assert(constructor.enclosingClass.implementation.isMixinApplication);
assert(constructor.isSynthesized);
ConstructorElement target =
constructor.definingConstructor.implementation;
// The resolver gives us the exact same FunctionSignature for the two
// constructors. The parameters for the synthesized constructor
// are already in the environment, so the target constructor's parameters
// are also in the environment since their elements are the same.
assert(constructor.functionSignature == target.functionSignature);
JsIrBuilderVisitor visitor = makeVisitorForContext(target);
visitor.withBuilder(irBuilder, () {
visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
});
}
/// Loads the type variables for all super classes of [superClass] into the
/// IR builder's environment with their corresponding values.
///
/// The type variables for [currentClass] must already be in the IR builder's
/// environment.
///
/// Type variables are stored as [TypeVariableLocal] in the environment.
///
/// This ensures that access to type variables mentioned inside the
/// constructors and initializers will happen through the local environment
/// instead of using 'this'.
void loadTypeVariablesForSuperClasses(ClassElement currentClass) {
if (currentClass.isObject) return;
loadTypeVariablesForType(currentClass.supertype);
if (currentClass is MixinApplicationElement) {
loadTypeVariablesForType(currentClass.mixinType);
}
}
/// Loads all type variables for [type] and all of its super classes into
/// the environment. All type variables mentioned in [type] must already
/// be in the environment.
void loadTypeVariablesForType(InterfaceType type) {
ClassElement clazz = type.element;
assert(clazz.typeVariables.length == type.typeArguments.length);
for (int i = 0; i < clazz.typeVariables.length; ++i) {
irBuilder.declareTypeVariable(clazz.typeVariables[i],
type.typeArguments[i]);
}
loadTypeVariablesForSuperClasses(clazz);
}
/// In preparation of inlining (part of) [target], the [arguments] are moved
/// into the environment bindings for the corresponding parameters.
///
/// Defaults for optional arguments are evaluated in order to ensure
/// all parameters are available in the environment.
void loadArguments(ConstructorElement target,
CallStructure call,
List<ir.Primitive> arguments) {
assert(target.isImplementation);
assert(target == elements.analyzedElement);
FunctionSignature signature = target.functionSignature;
// Establish a scope in case parameters are captured.
ClosureScope scope = getClosureScopeForFunction(target);
irBuilder.enterScope(scope);
// Load required parameters
int index = 0;
signature.forEachRequiredParameter((ParameterElement param) {
irBuilder.declareLocalVariable(param, initialValue: arguments[index]);
index++;
});
// Load optional parameters, evaluating default values for omitted ones.
signature.forEachOptionalParameter((ParameterElement param) {
ir.Primitive value;
// Load argument if provided.
if (signature.optionalParametersAreNamed) {
int nameIndex = call.namedArguments.indexOf(param.name);
if (nameIndex != -1) {
int translatedIndex = call.positionalArgumentCount + nameIndex;
value = arguments[translatedIndex];
}
} else if (index < arguments.length) {
value = arguments[index];
}
// Load default if argument was not provided.
if (value == null) {
if (param.initializer != null) {
value = visit(param.initializer);
} else {
value = irBuilder.buildNullConstant();
}
}
irBuilder.declareLocalVariable(param, initialValue: value);
index++;
});
}
/**
* Returns the constructor body associated with the given constructor or
* creates a new constructor body, if none can be found.
*
* Returns `null` if the constructor does not have a body.
*/
ConstructorBodyElement getConstructorBody(FunctionElement constructor) {
// TODO(asgerf): This is largely inherited from the SSA builder.
// The ConstructorBodyElement has an invalid function signature, but we
// cannot add a BoxLocal as parameter, because BoxLocal is not an element.
// Instead of forging ParameterElements to forge a FunctionSignature, we
// need a way to create backend methods without creating more fake elements.
assert(constructor.isGenerativeConstructor);
assert(constructor.isImplementation);
if (constructor.isSynthesized) return null;
ast.FunctionExpression node = constructor.node;
// If we know the body doesn't have any code, we don't generate it.
if (!node.hasBody()) return null;
if (node.hasEmptyBody()) return null;
ClassElement classElement = constructor.enclosingClass;
ConstructorBodyElement bodyElement;
classElement.forEachBackendMember((Element backendMember) {
if (backendMember.isGenerativeConstructorBody) {
ConstructorBodyElement body = backendMember;
if (body.constructor == constructor) {
bodyElement = backendMember;
}
}
});
if (bodyElement == null) {
bodyElement = new ConstructorBodyElementX(constructor);
classElement.addBackendMember(bodyElement);
if (constructor.isPatch) {
// Create origin body element for patched constructors.
ConstructorBodyElementX patch = bodyElement;
ConstructorBodyElementX origin =
new ConstructorBodyElementX(constructor.origin);
origin.applyPatch(patch);
classElement.origin.addBackendMember(bodyElement.origin);
}
}
assert(bodyElement.isGenerativeConstructorBody);
return bodyElement;
}
/// The list of parameters to send from the generative constructor
/// to the generative constructor body.
///
/// Boxed parameters are not in the list, instead, a [BoxLocal] is passed
/// containing the boxed parameters.
///
/// For example, given the following constructor,
///
/// Foo(x, y) : field = (() => ++x) { print(x + y) }
///
/// the argument `x` would be replaced by a [BoxLocal]:
///
/// Foo_body(box0, y) { print(box0.x + y) }
///
List<Local> getConstructorBodyParameters(ConstructorBodyElement body) {
List<Local> parameters = <Local>[];
ClosureScope scope = getClosureScopeForFunction(body.constructor);
if (scope != null) {
parameters.add(scope.box);
}
body.functionSignature.orderedForEachParameter((ParameterElement param) {
if (scope != null && scope.capturedVariables.containsKey(param)) {
// Do not pass this parameter; the box will carry its value.
} else {
parameters.add(param);
}
});
return parameters;
}
DartCapturedVariables _analyzeCapturedVariables(ast.Node node) {
DartCapturedVariables variables = new DartCapturedVariables(elements);
try {
variables.analyze(node);
} catch (e) {
bailoutMessage = variables.bailoutMessage;
rethrow;
}
return variables;
}
/// Builds the IR for the body of a constructor.
///
/// This function is invoked from one or more "factory" constructors built by
/// [buildConstructor].
ir.FunctionDefinition buildConstructorBody(ConstructorBodyElement body) {
ConstructorElement constructor = body.constructor;
ast.FunctionExpression node = constructor.node;
closureClassMap =
compiler.closureToClassMapper.computeClosureToClassMapping(
constructor,
node,
elements);
// We compute variables boxed in mutable variables on entry to each try
// block, not including variables captured by a closure (which are boxed
// in the heap). This duplicates some of the work of closure conversion
// without directly using the results. This duplication is wasteful and
// error-prone.
// TODO(kmillikin): We should combine closure conversion and try/catch
// variable analysis in some way.
DartCapturedVariables variables = _analyzeCapturedVariables(node);
tryStatements = variables.tryStatements;
IrBuilder builder = getBuilderFor(body);
return withBuilder(builder, () {
irBuilder.buildConstructorBodyHeader(getConstructorBodyParameters(body),
getClosureScopeForNode(node));
visit(node.body);
return irBuilder.makeFunctionDefinition();
});
}
ir.FunctionDefinition buildFunction(FunctionElement element) {
assert(invariant(element, element.isImplementation));
ast.FunctionExpression node = element.node;
assert(!element.isSynthesized);
assert(node != null);
assert(elements[node] != null);
closureClassMap =
compiler.closureToClassMapper.computeClosureToClassMapping(
element,
node,
elements);
DartCapturedVariables variables = _analyzeCapturedVariables(node);
tryStatements = variables.tryStatements;
IrBuilder builder = getBuilderFor(element);
return withBuilder(builder, () => _makeFunctionBody(element, node));
}
/// Creates a primitive for the default value of [parameter].
ir.Primitive translateDefaultValue(ParameterElement parameter) {
if (parameter.initializer == null) {
return irBuilder.buildNullConstant();
} else {
return inlineConstant(parameter.executableContext, parameter.initializer);
}
}
/// Inserts default arguments and normalizes order of named arguments.
List<ir.Primitive> normalizeStaticArguments(
CallStructure callStructure,
FunctionElement target,
List<ir.Primitive> arguments) {
target = target.implementation;
FunctionSignature signature = target.functionSignature;
if (!signature.optionalParametersAreNamed &&
signature.parameterCount == arguments.length) {
// Optimization: don't copy the argument list for trivial cases.
return arguments;
}
List<ir.Primitive> result = <ir.Primitive>[];
int i = 0;
signature.forEachRequiredParameter((ParameterElement element) {
result.add(arguments[i]);
++i;
});
if (!signature.optionalParametersAreNamed) {
signature.forEachOptionalParameter((ParameterElement element) {
if (i < arguments.length) {
result.add(arguments[i]);
++i;
} else {
result.add(translateDefaultValue(element));
}
});
} else {
int offset = i;
// Iterate over the optional parameters of the signature, and try to
// find them in [compiledNamedArguments]. If found, we use the
// value in the temporary list, otherwise the default value.
signature.orderedOptionalParameters.forEach((ParameterElement element) {
int nameIndex = callStructure.namedArguments.indexOf(element.name);
if (nameIndex != -1) {
int translatedIndex = offset + nameIndex;
result.add(arguments[translatedIndex]);
} else {
result.add(translateDefaultValue(element));
}
});
}
return result;
}
/// Normalizes order of named arguments.
List<ir.Primitive> normalizeDynamicArguments(
CallStructure callStructure,
List<ir.Primitive> arguments) {
assert(arguments.length == callStructure.argumentCount);
// Optimization: don't copy the argument list for trivial cases.
if (callStructure.namedArguments.isEmpty) return arguments;
List<ir.Primitive> result = <ir.Primitive>[];
for (int i=0; i < callStructure.positionalArgumentCount; i++) {
result.add(arguments[i]);
}
for (String argName in callStructure.getOrderedNamedArguments()) {
int nameIndex = callStructure.namedArguments.indexOf(argName);
int translatedIndex = callStructure.positionalArgumentCount + nameIndex;
result.add(arguments[translatedIndex]);
}
return result;
}
@override
ir.Primitive handleConstructorInvoke(
ast.NewExpression node,
ConstructorElement constructor,
DartType type,
ast.NodeList arguments,
CallStructure callStructure,
_) {
List<ir.Primitive> arguments =
node.send.arguments.mapToList(visit, growable:false);
// Use default values from the effective target, not the immediate target.
ConstructorElement target = constructor.effectiveTarget;
arguments = normalizeStaticArguments(callStructure, target, arguments);
return irBuilder.buildConstructorInvocation(
target,
callStructure,
constructor.computeEffectiveTargetType(type),
arguments,
sourceInformationBuilder.buildNew(node));
}
@override
ir.Primitive buildStaticNoSuchMethod(Selector selector,
List<ir.Primitive> arguments) {
Element thrower = backend.getThrowNoSuchMethod();
ir.Primitive receiver = irBuilder.buildStringConstant('');
ir.Primitive name = irBuilder.buildStringConstant(selector.name);
ir.Primitive argumentList = irBuilder.buildListLiteral(null, arguments);
ir.Primitive expectedArgumentNames = irBuilder.buildNullConstant();
return irBuilder.buildStaticFunctionInvocation(
thrower,
new CallStructure.unnamed(4),
[receiver, name, argumentList, expectedArgumentNames]);
}
@override
ir.Primitive buildInstanceNoSuchMethod(Selector selector,
TypeMask mask,
List<ir.Primitive> arguments) {
return irBuilder.buildDynamicInvocation(
irBuilder.buildThis(),
Selectors.noSuchMethod_,
mask,
[irBuilder.buildInvocationMirror(selector, arguments)]);
}
@override
ir.Primitive buildRuntimeError(String message) {
return irBuilder.buildStaticFunctionInvocation(
backend.getThrowRuntimeError(),
new CallStructure.unnamed(1),
[irBuilder.buildStringConstant(message)]);
}
@override
ir.Primitive buildAbstractClassInstantiationError(ClassElement element) {
return irBuilder.buildStaticFunctionInvocation(
backend.getThrowAbstractClassInstantiationError(),
new CallStructure.unnamed(1),
[irBuilder.buildStringConstant(element.name)]);
}
@override
ir.Primitive handleStaticFieldGet(ast.Send node, FieldElement field, _) {
SourceInformation src = sourceInformationBuilder.buildGet(node);
return buildStaticFieldGet(field, src);
}
ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src) {
ConstantValue constant = getConstantForVariable(field);
if (constant != null && !field.isAssignable) {
return irBuilder.buildConstant(constant, sourceInformation: src);
} else if (backend.constants.lazyStatics.contains(field)) {
return irBuilder.buildStaticFieldLazyGet(field, src);
} else {
return irBuilder.buildStaticFieldGet(field, src);
}
}
/// Build code to handle foreign code, that is, native JavaScript code, or
/// builtin values and operations of the backend.
ir.Primitive handleForeignCode(ast.Send node,
MethodElement function,
ast.NodeList argumentList,
CallStructure callStructure) {
void validateArgumentCount({int minimum, int exactly}) {
assert((minimum == null) != (exactly == null));
int count = 0;
int maximum;
if (exactly != null) {
minimum = exactly;
maximum = exactly;
}
for (ast.Node argument in argumentList) {
count++;
if (maximum != null && count > maximum) {
internalError(argument, 'Additional argument.');
}
}
if (count < minimum) {
internalError(node, 'Expected at least $minimum arguments.');
}
}
/// Call a helper method from the isolate library. The isolate library uses
/// its own isolate structure, that encapsulates dart2js's isolate.
ir.Primitive buildIsolateHelperInvocation(String helperName,
CallStructure callStructure) {
Element element = backend.isolateHelperLibrary.find(helperName);
if (element == null) {
compiler.internalError(node,
'Isolate library and compiler mismatch.');
}
List<ir.Primitive> arguments = translateStaticArguments(argumentList,
element, callStructure);
return irBuilder.buildStaticFunctionInvocation(element,
callStructure, arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
/// Lookup the value of the enum described by [node].
getEnumValue(ast.Node node, EnumClassElement enumClass, List values) {
Element element = elements[node];
if (element is! FieldElement || element.enclosingClass != enumClass) {
internalError(node, 'expected a JsBuiltin enum value');
}
int index = enumClass.enumValues.indexOf(element);
return values[index];
}
/// Returns the String the node evaluates to, or throws an error if the
/// result is not a string constant.
String expectStringConstant(ast.Node node) {
ir.Primitive nameValue = visit(node);
if (nameValue is ir.Constant && nameValue.value.isString) {
StringConstantValue constantValue = nameValue.value;
return constantValue.primitiveValue.slowToString();
} else {
return internalError(node, 'expected a literal string');
}
}
Link<ast.Node> argumentNodes = argumentList.nodes;
NativeBehavior behavior =
compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
switch (function.name) {
case 'JS':
validateArgumentCount(minimum: 2);
// The first two arguments are the type and the foreign code template,
// which already have been analyzed by the resolver and can be retrieved
// using [NativeBehavior]. We can ignore these arguments in the backend.
List<ir.Primitive> arguments =
argumentNodes.skip(2).mapToList(visit, growable: false);
return irBuilder.buildForeignCode(behavior.codeTemplate, arguments,
behavior);
case 'DART_CLOSURE_TO_JS':
// TODO(ahe): This should probably take care to wrap the closure in
// another closure that saves the current isolate.
case 'RAW_DART_FUNCTION_REF':
validateArgumentCount(exactly: 1);
ast.Node argument = node.arguments.single;
FunctionElement closure = elements[argument].implementation;
if (!Elements.isStaticOrTopLevelFunction(closure)) {
internalError(argument,
'only static or toplevel function supported');
}
if (closure.functionSignature.hasOptionalParameters) {
internalError(argument,
'closures with optional parameters not supported');
}
return irBuilder.buildForeignCode(
js.js.expressionTemplateYielding(
backend.emitter.staticFunctionAccess(closure)),
<ir.Primitive>[],
NativeBehavior.PURE,
dependency: closure);
case 'JS_BUILTIN':
// The first argument is a description of the type and effect of the
// builtin, which has already been analyzed in the frontend. The second
// argument must be a [JsBuiltin] value. All other arguments are
// values used by the JavaScript template that is associated with the
// builtin.
validateArgumentCount(minimum: 2);
ast.Node builtin = argumentNodes.tail.head;
JsBuiltin value = getEnumValue(builtin, backend.jsBuiltinEnum,
JsBuiltin.values);
js.Template template = backend.emitter.builtinTemplateFor(value);
List<ir.Primitive> arguments =
argumentNodes.skip(2).mapToList(visit, growable: false);
return irBuilder.buildForeignCode(template, arguments, behavior);
case 'JS_EMBEDDED_GLOBAL':
validateArgumentCount(exactly: 2);
String name = expectStringConstant(argumentNodes.tail.head);
js.Expression access =
backend.emitter.generateEmbeddedGlobalAccess(name);
js.Template template = js.js.expressionTemplateYielding(access);
return irBuilder.buildForeignCode(template, <ir.Primitive>[], behavior);
case 'JS_INTERCEPTOR_CONSTANT':
validateArgumentCount(exactly: 1);
ast.Node argument = argumentNodes.head;
ir.Primitive argumentValue = visit(argument);
if (argumentValue is ir.Constant && argumentValue.value.isType) {
TypeConstantValue constant = argumentValue.value;
ConstantValue interceptorValue =
new InterceptorConstantValue(constant.representedType);
return irBuilder.buildConstant(interceptorValue);
} else {
internalError(argument, 'expected Type as argument');
}
break;
case 'JS_EFFECT':
return irBuilder.buildNullConstant();
case 'JS_GET_NAME':
validateArgumentCount(exactly: 1);
ast.Node argument = argumentNodes.head;
JsGetName id = getEnumValue(argument, backend.jsGetNameEnum,
JsGetName.values);
js.Name name = backend.namer.getNameForJsGetName(argument, id);
ConstantValue nameConstant =
new SyntheticConstantValue(SyntheticConstantKind.NAME,
js.js.quoteName(name));
return irBuilder.buildConstant(nameConstant);
case 'JS_GET_FLAG':
validateArgumentCount(exactly: 1);
String name = expectStringConstant(argumentNodes.first);
bool value = false;
switch (name) {
case 'MUST_RETAIN_METADATA':
value = backend.mustRetainMetadata;
break;
case 'USE_CONTENT_SECURITY_POLICY':
value = compiler.useContentSecurityPolicy;
break;
default:
internalError(node, 'Unknown internal flag "$name".');
}
return irBuilder.buildBooleanConstant(value);
case 'JS_STRING_CONCAT':
validateArgumentCount(exactly: 2);
List<ir.Primitive> arguments = argumentNodes.mapToList(visit);
return irBuilder.buildStringConcatenation(arguments);
case 'JS_CURRENT_ISOLATE_CONTEXT':
validateArgumentCount(exactly: 0);
if (!compiler.hasIsolateSupport) {
// If the isolate library is not used, we just generate code
// to fetch the current isolate.
continue getStaticState;
}
return buildIsolateHelperInvocation('_currentIsolate',
CallStructure.NO_ARGS);
getStaticState: case 'JS_GET_STATIC_STATE':
validateArgumentCount(exactly: 0);
return irBuilder.buildForeignCode(
js.js.parseForeignJS(backend.namer.staticStateHolder),
const <ir.Primitive>[],
NativeBehavior.PURE);
case 'JS_SET_STATIC_STATE':
validateArgumentCount(exactly: 1);
ir.Primitive value = visit(argumentNodes.single);
String isolateName = backend.namer.staticStateHolder;
return irBuilder.buildForeignCode(
js.js.parseForeignJS("$isolateName = #"),
<ir.Primitive>[value],
NativeBehavior.PURE);
case 'JS_CALL_IN_ISOLATE':
validateArgumentCount(exactly: 2);
if (!compiler.hasIsolateSupport) {
ir.Primitive closure = visit(argumentNodes.tail.head);
return irBuilder.buildCallInvocation(closure, CallStructure.NO_ARGS,
const <ir.Primitive>[]);
}
return buildIsolateHelperInvocation('_callInIsolate',
CallStructure.TWO_ARGS);
default:
giveup(node, 'unplemented native construct: ${function.name}');
break;
}
}
@override
ir.Primitive handleStaticFunctionInvoke(ast.Send node,
MethodElement function,
ast.NodeList argumentList,
CallStructure callStructure,
_) {
if (compiler.backend.isForeign(function)) {
return handleForeignCode(node, function, argumentList, callStructure);
} else {
return irBuilder.buildStaticFunctionInvocation(function, callStructure,
translateStaticArguments(argumentList, function, callStructure),
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
}
}