blob: f98c9dddeb50f705d60e54a7543b99c19ca0c804 [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 closure;
import '../common.dart';
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 '../elements/elements.dart';
import '../elements/modelx.dart' show
SynthesizedConstructorElementX,
ConstructorBodyElementX,
FunctionSignatureX;
import '../io/source_information.dart';
import '../js_backend/backend_helpers.dart' show
BackendHelpers;
import '../js_backend/js_backend.dart' show
JavaScriptBackend,
SyntheticConstantKind;
import '../resolution/tree_elements.dart' show
TreeElements;
import '../resolution/semantic_visitor.dart';
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,
HasCapturedPlaceholders;
// 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';
import 'type_mask_system.dart' show
TypeMaskSystem;
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,
TypeMaskSystem typeMaskSystem) {
return measure(() {
bailoutMessage = null;
TreeElements elementsMapping = element.resolvedAst.elements;
element = element.implementation;
return reporter.withCurrentElement(element, () {
SourceInformationBuilder sourceInformationBuilder =
sourceInformationStrategy.createBuilderForContext(element);
IrBuilderVisitor builder =
new IrBuilderVisitor(
elementsMapping, compiler, sourceInformationBuilder,
typeMaskSystem);
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].
class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
with IrBuilderMixin<ast.Node>,
SemanticSendResolvedMixin<ir.Primitive, dynamic>,
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;
final TypeMaskSystem typeMaskSystem;
/// 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,
this.typeMaskSystem);
JavaScriptBackend get backend => compiler.backend;
BackendHelpers get helpers => backend.helpers;
DiagnosticReporter get reporter => compiler.reporter;
String bailoutMessage = null;
ir.Primitive visit(ast.Node node) => node.accept(this);
@override
ir.Primitive apply(ast.Node node, _) => node.accept(this);
SemanticSendVisitor get sendVisitor => this;
/// 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].
closure.ClosureClassMap closureClassMap;
/// 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) {
// 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(closureClassMap.capturingScopes[node]);
}
/// Returns the [ClosureScope] for any function, possibly different from the
/// one currently being built.
ClosureScope getClosureScopeForFunction(FunctionElement function) {
closure.ClosureClassMap map =
compiler.closureToClassMapper.computeClosureToClassMapping(
function,
function.node,
elements);
return new ClosureScope(map.capturingScopes[function.node]);
}
/// 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() {
return new ClosureEnvironment(closureClassMap);
}
IrBuilder getBuilderFor(Element element) {
return new IrBuilder(
new GlobalProgramInformation(compiler),
backend.constants,
element);
}
/// 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) {
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.FACTORY_CONSTRUCTOR:
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:
reporter.internalError(element, "Unexpected element type $element");
}
return root;
});
}
/// 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);
}
/// 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;
}
/// 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 closure.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.
ir.Primitive typeInformation;
if (requiresTypeInformation) {
typeInformation = new ir.TypeExpression(
ir.TypeExpressionKind.INSTANCE,
classElement.thisType,
irParameters.sublist(firstTypeArgumentParameterIndex));
irBuilder.add(new ir.LetPrim(typeInformation));
} else {
typeInformation = null;
}
// -- 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(backend.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));
}
Selector selector = new Selector.call(target.memberName,
new CallStructure(bodyArguments.length));
irBuilder.addPrimitive(new ir.InvokeMethodDirectly(
instance, bodyElement, selector, bodyArguments, null));
}
// --- step 4: return the created object ----
irBuilder.buildReturn(
value: instance,
sourceInformation:
sourceInformationBuilder.buildImplicitReturn(constructor));
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.
IrBuilderVisitor makeVisitorForContext(AstElement context) {
return new IrBuilderVisitor(
context.resolvedAst.elements,
compiler,
sourceInformationBuilder.forContext(context),
typeMaskSystem);
}
/// 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) {
IrBuilderVisitor visitor = makeVisitorForContext(context);
return visitor.withBuilder(irBuilder, () => visitor.visit(expression));
}
/// 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);
IrBuilderVisitor visitor = makeVisitorForContext(target);
visitor.withBuilder(irBuilder, () {
visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
});
}
/// 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++;
});
}
/// 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) {
IrBuilderVisitor visitor = makeVisitorForContext(target);
visitor.withBuilder(irBuilder, () {
visitor.loadArguments(target, call, arguments);
visitor.evaluateConstructorFieldInitializers(target, supers, fieldValues);
});
}
/// 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 (backend.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.buildLocalGet(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 {
reporter.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) {
reporter.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);
}
TryBoxedVariables _analyzeTryBoxedVariables(ast.Node node) {
TryBoxedVariables variables = new TryBoxedVariables(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.
TryBoxedVariables variables = _analyzeTryBoxedVariables(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);
TryBoxedVariables variables = _analyzeTryBoxedVariables(node);
tryStatements = variables.tryStatements;
IrBuilder builder = getBuilderFor(element);
return withBuilder(builder,
() => _makeFunctionBody(builder, element, node));
}
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();
});
}
/// 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) {
IrBuilderVisitor visitor = makeVisitorForContext(context);
return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp));
}
/// 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);
}
}
/// Normalizes the argument list of a static invocation.
///
/// A static invocation is one where the target is known. The argument list
/// [arguments] is normalized by adding default values for optional arguments
/// that are not passed, and by sorting it in place so that named arguments
/// appear in a canonical order. A [CallStructure] reflecting this order
/// is returned.
CallStructure normalizeStaticArguments(CallStructure callStructure,
FunctionElement target,
List<ir.Primitive> arguments) {
target = target.implementation;
FunctionSignature signature = target.functionSignature;
if (!signature.optionalParametersAreNamed &&
signature.parameterCount == arguments.length) {
return callStructure;
}
if (!signature.optionalParametersAreNamed) {
int i = signature.requiredParameterCount;
signature.forEachOptionalParameter((ParameterElement element) {
if (i < callStructure.positionalArgumentCount) {
++i;
} else {
arguments.add(translateDefaultValue(element));
}
});
return new CallStructure(signature.parameterCount);
}
int offset = signature.requiredParameterCount;
List<ir.Primitive> namedArguments = arguments.sublist(offset);
arguments.length = offset;
List<String> normalizedNames = <String>[];
// Iterate over the optional parameters of the signature, and try to
// find them in the callStructure's named arguments. 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);
arguments.add(nameIndex == -1 ? translateDefaultValue(element)
: namedArguments[nameIndex]);
normalizedNames.add(element.name);
});
return new CallStructure(signature.parameterCount, normalizedNames);
}
/// Normalizes the argument list of a dynamic invocation.
///
/// A dynamic invocation is one where the target is not known. The argument
/// list [arguments] is normalized by sorting it in place so that the named
/// arguments appear in a canonical order. A [CallStructure] reflecting this
/// order is returned.
CallStructure normalizeDynamicArguments(CallStructure callStructure,
List<ir.Primitive> arguments) {
assert(arguments.length == callStructure.argumentCount);
if (callStructure.namedArguments.isEmpty) return callStructure;
int destinationIndex = callStructure.positionalArgumentCount;
List<ir.Primitive> namedArguments = arguments.sublist(destinationIndex);
for (String argName in callStructure.getOrderedNamedArguments()) {
int sourceIndex = callStructure.namedArguments.indexOf(argName);
arguments[destinationIndex++] = namedArguments[sourceIndex];
}
return new CallStructure(callStructure.argumentCount,
callStructure.getOrderedNamedArguments());
}
/// Read the value of [field].
ir.Primitive buildStaticFieldGet(FieldElement field, SourceInformation src) {
ConstantValue constant = getConstantForVariable(field);
if (constant != null && !field.isAssignable) {
typeMaskSystem.associateConstantValueWithElement(constant, field);
return irBuilder.buildConstant(constant, sourceInformation: src);
} else if (backend.constants.lazyStatics.contains(field)) {
return irBuilder.addPrimitive(new ir.GetLazyStatic(field, src));
} else {
return irBuilder.addPrimitive(new ir.GetStatic(field, src));
}
}
ir.FunctionDefinition _makeFunctionBody(
IrBuilder builder,
FunctionElement element,
ast.FunctionExpression node) {
FunctionSignature signature = element.functionSignature;
List<Local> parameters = <Local>[];
signature.orderedForEachParameter(
(LocalParameterElement e) => parameters.add(e));
bool requiresRuntimeTypes = false;
if (element.isFactoryConstructor) {
requiresRuntimeTypes =
builder.program.requiresRuntimeTypesFor(element.enclosingElement);
if (requiresRuntimeTypes) {
// Type arguments are passed in as extra parameters.
for (DartType typeVariable in element.enclosingClass.typeVariables) {
parameters.add(new closure.TypeVariableLocal(typeVariable, element));
}
}
}
irBuilder.buildFunctionHeader(parameters,
closureScope: getClosureScopeForNode(node),
env: getClosureEnvironment());
if (element == helpers.jsArrayTypedConstructor) {
// Generate a body for JSArray<E>.typed(allocation):
//
// t1 = setRuntimeTypeInfo(allocation, TypeExpression($E));
// return Refinement(t1, <JSArray>);
//
assert(parameters.length == 1 || parameters.length == 2);
ir.Primitive allocation = irBuilder.buildLocalGet(parameters[0]);
ClassElement classElement = element.enclosingElement;
// Only call setRuntimeTypeInfo if JSArray requires the type parameter.
if (requiresRuntimeTypes) {
assert(parameters.length == 2);
closure.TypeVariableLocal typeParameter = parameters[1];
ir.Primitive typeArgument =
irBuilder.buildTypeVariableAccess(typeParameter.typeVariable);
ir.Primitive typeInformation = irBuilder.addPrimitive(
new ir.TypeExpression(ir.TypeExpressionKind.INSTANCE,
element.enclosingClass.thisType,
<ir.Primitive>[typeArgument]));
MethodElement helper = helpers.setRuntimeTypeInfo;
CallStructure callStructure = CallStructure.TWO_ARGS;
Selector selector = new Selector.call(helper.memberName, callStructure);
allocation = irBuilder.buildInvokeStatic(
helper, selector, <ir.Primitive>[allocation, typeInformation],
sourceInformationBuilder.buildGeneric(node));
}
ir.Primitive refinement = irBuilder.addPrimitive(
new ir.Refinement(allocation, typeMaskSystem.arrayType));
irBuilder.buildReturn(value: refinement,
sourceInformation:
sourceInformationBuilder.buildImplicitReturn(element));
} else {
visit(node.body);
}
return irBuilder.makeFunctionDefinition();
}
/// Builds the IR for creating an instance of the closure class corresponding
/// to the given nested function.
closure.ClosureClassElement makeSubFunction(ast.FunctionExpression node) {
closure.ClosureClassMap innerMap =
compiler.closureToClassMapper.getMappingForNestedFunction(node);
closure.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));
}
// ## Statements ##
visitBlock(ast.Block node) {
irBuilder.buildBlock(node.statements.nodes, build);
}
ir.Primitive visitBreakStatement(ast.BreakStatement node) {
if (!irBuilder.buildBreak(elements.getTargetOf(node))) {
reporter.internalError(node, "'break' target not found");
}
return null;
}
ir.Primitive visitContinueStatement(ast.ContinueStatement node) {
if (!irBuilder.buildContinue(elements.getTargetOf(node))) {
reporter.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 [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);
callStructure =
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) {
assert(irBuilder.isOpen);
ir.Primitive value = visit(node.expression);
return irBuilder.addPrimitive(new ir.Await(value));
}
visitYield(ast.Yield node) {
assert(irBuilder.isOpen);
ir.Primitive value = visit(node.expression);
return irBuilder.addPrimitive(new ir.Yield(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));
}
/// If compiling with trusted type annotations, assumes that [value] is
/// now known to be `null` or an instance of [type].
///
/// This is also where we should add type checks in checked mode, but this
/// is not supported yet.
ir.Primitive checkType(ir.Primitive value, DartType dartType) {
if (!compiler.trustTypeAnnotations) return value;
TypeMask type = typeMaskSystem.subtypesOf(dartType).nullable();
return irBuilder.addPrimitive(new ir.Refinement(value, type));
}
ir.Primitive checkTypeVsElement(ir.Primitive value, TypedElement element) {
return checkType(value, element.type);
}
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);
initialValue = checkTypeVsElement(initialValue, element);
} 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(backend.isNative(function));
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 {
String name = backend.getFixedBackendName(function);
irBuilder.buildRedirectingNativeFunctionBody(function, name, source);
}
} else {
irBuilder.buildReturn(
value: build(node.expression),
sourceInformation: source);
}
}
visitSwitchStatement(ast.SwitchStatement node) {
// Dart switch cases can be labeled and be the target of continue from
// within the switch. Such cases are 'recursive'. If there are any
// recursive cases, we implement the switch using a pair of switches with
// the second one switching over a state variable in a loop. The first
// switch contains the non-recursive cases, and the second switch contains
// the recursive ones.
//
// For example, for the Dart switch:
//
// switch (E) {
// case 0:
// BODY0;
// break;
// LABEL0: case 1:
// BODY1;
// break;
// case 2:
// BODY2;
// continue LABEL1;
// LABEL1: case 3:
// BODY3;
// continue LABEL0;
// default:
// BODY4;
// }
//
// We translate it as if it were the JavaScript:
//
// var state = -1;
// switch (E) {
// case 0:
// BODY0;
// break;
// case 1:
// state = 0; // Recursive, label ID = 0.
// break;
// case 2:
// BODY2;
// state = 1; // Continue to label ID = 1.
// break;
// case 3:
// state = 1; // Recursive, label ID = 1.
// break;
// default:
// BODY4;
// }
// L: while (state != -1) {
// case 0:
// BODY1;
// break L; // Break from switch becomes break from loop.
// case 1:
// BODY2;
// state = 0; // Continue to label ID = 0.
// break;
// }
assert(irBuilder.isOpen);
// Preprocess: compute a list of cases that are the target of continue.
// These are the so-called 'recursive' cases.
List<JumpTarget> continueTargets = <JumpTarget>[];
List<ast.Node> switchCases = node.cases.nodes.toList();
for (ast.SwitchCase switchCase in switchCases) {
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.Label) {
LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
if (definition != null && definition.isContinueTarget) {
continueTargets.add(definition.target);
}
}
}
}
// If any cases are continue targets, use an anonymous local value to
// implement a state machine. The initial value is -1.
ir.Primitive initial;
int stateIndex;
if (continueTargets.isNotEmpty) {
initial = irBuilder.buildIntegerConstant(-1);
stateIndex = irBuilder.environment.length;
irBuilder.environment.extend(null, initial);
}
// Use a simple switch for the non-recursive cases. A break will go to the
// join-point after the switch. A continue to a labeled case will assign
// to the state variable and go to the join-point.
ir.Primitive value = visit(node.expression);
JumpCollector join = new ForwardJumpCollector(irBuilder.environment,
target: elements.getTargetDefinition(node));
irBuilder.state.breakCollectors.add(join);
for (int i = 0; i < continueTargets.length; ++i) {
// The state value is i, the case's position in the list of recursive
// cases.
irBuilder.state.continueCollectors.add(new GotoJumpCollector(
continueTargets[i], stateIndex, i, join));
}
// For each non-default case use a pair of functions, one to translate the
// condition and one to translate the body. For the default case use a
// function to translate the body. Use continueTargetIterator as a pointer
// to the next recursive case.
Iterator<JumpTarget> continueTargetIterator = continueTargets.iterator;
continueTargetIterator.moveNext();
List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
SubbuildFunction buildDefaultBody;
for (ast.SwitchCase switchCase in switchCases) {
JumpTarget nextContinueTarget = continueTargetIterator.current;
if (switchCase.isDefaultCase) {
if (nextContinueTarget != null &&
switchCase == nextContinueTarget.statement) {
// In this simple switch, recursive cases are as if they immediately
// continued to themselves.
buildDefaultBody = nested(() {
irBuilder.buildContinue(nextContinueTarget);
});
continueTargetIterator.moveNext();
} else {
// Non-recursive cases consist of the translation of the body.
// For the default case, there is implicitly a break if control
// flow reaches the end.
buildDefaultBody = nested(() {
irBuilder.buildSequence(switchCase.statements, visit);
if (irBuilder.isOpen) irBuilder.jumpTo(join);
});
}
continue;
}
ir.Primitive buildCondition(IrBuilder builder) {
// There can be multiple cases sharing the same body, because empty
// cases are allowed to fall through to the next one. Each case is
// a comparison, build a short-circuited disjunction of all of them.
return withBuilder(builder, () {
ir.Primitive condition;
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.CaseMatch) {
ir.Primitive buildComparison() {
ir.Primitive constant =
translateConstant(labelOrCase.expression);
return irBuilder.buildIdentical(value, constant);
}
if (condition == null) {
condition = buildComparison();
} else {
condition = irBuilder.buildLogicalOperator(condition,
nested(buildComparison), isLazyOr: true);
}
}
}
return condition;
});
}
SubbuildFunction buildBody;
if (nextContinueTarget != null &&
switchCase == nextContinueTarget.statement) {
// Recursive cases are as if they immediately continued to themselves.
buildBody = nested(() {
irBuilder.buildContinue(nextContinueTarget);
});
continueTargetIterator.moveNext();
} else {
// Non-recursive cases consist of the translation of the body. It is a
// runtime error if control-flow reaches the end of the body of any but
// the last case.
buildBody = (IrBuilder builder) {
withBuilder(builder, () {
irBuilder.buildSequence(switchCase.statements, visit);
if (irBuilder.isOpen) {
if (switchCase == switchCases.last) {
irBuilder.jumpTo(join);
} else {
Element error = helpers.fallThroughError;
ir.Primitive exception = irBuilder.buildInvokeStatic(
error,
new Selector.fromElement(error),
<ir.Primitive>[],
sourceInformationBuilder.buildGeneric(node));
irBuilder.buildThrow(exception);
}
}
});
return null;
};
}
cases.add(new SwitchCaseInfo(buildCondition, buildBody));
}
irBuilder.buildSimpleSwitch(join, cases, buildDefaultBody);
irBuilder.state.breakCollectors.removeLast();
irBuilder.state.continueCollectors.length -= continueTargets.length;
if (continueTargets.isEmpty) return;
// If there were recursive cases build a while loop whose body is a
// switch containing (only) the recursive cases. The condition is
// 'state != initialValue' so the loop is not taken when the state variable
// has not been assigned.
//
// 'loop' is the join-point of the exits from the inner switch which will
// perform another iteration of the loop. 'exit' is the join-point of the
// breaks from the switch, outside the loop.
JumpCollector loop = new ForwardJumpCollector(irBuilder.environment);
JumpCollector exit = new ForwardJumpCollector(irBuilder.environment,
target: elements.getTargetDefinition(node));
irBuilder.state.breakCollectors.add(exit);
for (int i = 0; i < continueTargets.length; ++i) {
irBuilder.state.continueCollectors.add(new GotoJumpCollector(
continueTargets[i], stateIndex, i, loop));
}
cases.clear();
for (int i = 0; i < continueTargets.length; ++i) {
// The conditions compare to the recursive case index.
ir.Primitive buildCondition(IrBuilder builder) {
ir.Primitive constant = builder.buildIntegerConstant(i);
return builder.buildIdentical(
builder.environment.index2value[stateIndex], constant);
}
ir.Primitive buildBody(IrBuilder builder) {
withBuilder(builder, () {
ast.SwitchCase switchCase = continueTargets[i].statement;
irBuilder.buildSequence(switchCase.statements, visit);
if (irBuilder.isOpen) {
if (switchCase == switchCases.last) {
irBuilder.jumpTo(exit);
} else {
Element error = helpers.fallThroughError;
ir.Primitive exception = irBuilder.buildInvokeStatic(
error,
new Selector.fromElement(error),
<ir.Primitive>[],
sourceInformationBuilder.buildGeneric(node));
irBuilder.buildThrow(exception);
}
}
});
return null;
}
cases.add(new SwitchCaseInfo(buildCondition, buildBody));
}
// A loop with a simple switch in the body.
IrBuilder whileBuilder = irBuilder.makeDelimitedBuilder();
whileBuilder.buildWhile(
buildCondition: (IrBuilder builder) {
ir.Primitive condition = builder.buildIdentical(
builder.environment.index2value[stateIndex], initial);
return builder.buildNegation(condition);
},
buildBody: (IrBuilder builder) {
builder.buildSimpleSwitch(loop, cases, null);
});
// Jump to the exit continuation. This jump is the body of the loop exit
// continuation, so the loop exit continuation can be eta-reduced. The
// jump is here for simplicity because `buildWhile` does not expose the
// loop's exit continuation directly and has already emitted all jumps
// to it anyway.
whileBuilder.jumpTo(exit);
irBuilder.add(new ir.LetCont(exit.continuation, whileBuilder.root));
irBuilder.environment = exit.environment;
irBuilder.environment.discard(1); // Discard the state variable.
irBuilder.state.breakCollectors.removeLast();
irBuilder.state.continueCollectors.length -= continueTargets.length;
}
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);
}
/// Returns the allocation site-specific type for a given allocation.
///
/// Currently, it is an error to call this with anything that is not the
/// allocation site for a List object (a literal list or a call to one
/// of the List constructors).
TypeMask getAllocationSiteType(ast.Node node) {
return compiler.typesTask.getGuaranteedTypeOfNode(
elements.analyzedElement, node);
}
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);
TypeMask allocationSiteType = getAllocationSiteType(node);
// TODO(sra): In checked mode, the elements must be checked as though
// operator[]= is called.
ir.Primitive list = irBuilder.buildListLiteral(type, values,
allocationSiteType: allocationSiteType);
if (type.treatAsRaw) return list;
// Call JSArray<E>.typed(allocation) to install the reified type.
ConstructorElement constructor = helpers.jsArrayTypedConstructor;
ir.Primitive tagged = irBuilder.buildConstructorInvocation(
constructor.effectiveTarget,
CallStructure.ONE_ARG,
constructor.computeEffectiveTargetType(type),
<ir.Primitive>[list],
sourceInformationBuilder.buildNew(node));
if (allocationSiteType == null) return tagged;
return irBuilder.addPrimitive(
new ir.Refinement(tagged, allocationSiteType));
}
ir.Primitive visitLiteralMap(ast.LiteralMap node) {
assert(irBuilder.isOpen);
if (node.isConst) {
return translateConstant(node);
}
InterfaceType type = elements.getType(node);
if (node.entries.nodes.isEmpty) {
if (type.treatAsRaw) {
return irBuilder.buildStaticFunctionInvocation(
helpers.mapLiteralUntypedEmptyMaker,
<ir.Primitive>[],
sourceInformation: sourceInformationBuilder.buildNew(node));
} else {
ConstructorElement constructor = helpers.mapLiteralConstructorEmpty;
return irBuilder.buildConstructorInvocation(
constructor.effectiveTarget,
CallStructure.NO_ARGS,
constructor.computeEffectiveTargetType(type),
<ir.Primitive>[],
sourceInformationBuilder.buildNew(node));
}
}
List<ir.Primitive> keysAndValues = <ir.Primitive>[];
for (ast.LiteralMapEntry entry in node.entries.nodes.toList()) {
keysAndValues.add(visit(entry.key));
keysAndValues.add(visit(entry.value));
}
ir.Primitive keysAndValuesList =
irBuilder.buildListLiteral(null, keysAndValues);
if (type.treatAsRaw) {
return irBuilder.buildStaticFunctionInvocation(
helpers.mapLiteralUntypedMaker,
<ir.Primitive>[keysAndValuesList],
sourceInformation: sourceInformationBuilder.buildNew(node));
} else {
ConstructorElement constructor = helpers.mapLiteralConstructor;
return irBuilder.buildConstructorInvocation(
constructor.effectiveTarget,
CallStructure.ONE_ARG,
constructor.computeEffectiveTargetType(type),
<ir.Primitive>[keysAndValuesList],
sourceInformationBuilder.buildNew(node));
}
}
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, _) {
if (prefix != null) buildCheckDeferredIsLoaded(prefix, node);
}
/// Create a call to check that a deferred import has already been loaded.
ir.Primitive buildCheckDeferredIsLoaded(PrefixElement prefix, ast.Send node) {
SourceInformation sourceInformation =
sourceInformationBuilder.buildCall(node, node.selector);
ir.Primitive name = irBuilder.buildStringConstant(
compiler.deferredLoadTask.getImportDeferName(node, prefix));
ir.Primitive uri =
irBuilder.buildStringConstant('${prefix.deferredImport.uri}');
return irBuilder.buildStaticFunctionInvocation(
helpers.checkDeferredIsLoaded,
<ir.Primitive>[name, uri],
sourceInformation: sourceInformation);
}
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 = argumentsNode.nodes.mapToList(visit);
callStructure = 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.buildLocalGet(element);
}
ir.Primitive handleLocalGet(
ast.Send node,
LocalElement element,
_) {
return irBuilder.buildLocalGet(element);
}
@override
ir.Primitive handleStaticFunctionGet(
ast.Send node,
MethodElement function,
_) {
return irBuilder.addPrimitive(new ir.GetStatic(function));
}
@override
ir.Primitive handleStaticGetterGet(
ast.Send node,
FunctionElement getter,
_) {
return buildStaticGetterGet(getter, node);
}
/// Create a getter invocation of the static getter [getter]. This also
/// handles the special case where [getter] is the `loadLibrary`
/// pseudo-function on library prefixes of deferred imports.
ir.Primitive buildStaticGetterGet(MethodElement getter, ast.Send node) {
SourceInformation sourceInformation =
sourceInformationBuilder.buildGet(node);
if (getter.isDeferredLoaderGetter) {
PrefixElement prefix = getter.enclosingElement;
ir.Primitive loadId = irBuilder.buildStringConstant(
compiler.deferredLoadTask.getImportDeferName(node, prefix));
return irBuilder.buildStaticFunctionInvocation(
compiler.loadLibraryFunction,
<ir.Primitive>[loadId],
sourceInformation: sourceInformation);
} else {
return irBuilder.buildStaticGetterGet(getter, sourceInformation);
}
}
@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) {
ir.Primitive receiver = visit(left);
Selector selector = new Selector.binaryOperator(operator.selectorName);
List<ir.Primitive> arguments = <ir.Primitive>[visit(right)];
CallStructure callStructure =
normalizeDynamicArguments(selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
receiver,
new Selector(selector.kind, selector.memberName, callStructure),
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, _) {
ir.Primitive target = visit(receiver);
Selector selector = new Selector.index();
List<ir.Primitive> arguments = <ir.Primitive>[visit(index)];
CallStructure callStructure =
normalizeDynamicArguments(selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target,
new Selector(selector.kind, selector.memberName, callStructure),
elements.getTypeMask(node),
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(receiver, node.selector));
}
ir.Primitive translateSuperBinary(FunctionElement function,
op.BinaryOperator operator,
ast.Node argument) {
List<ir.Primitive> arguments = <ir.Primitive>[visit(argument)];
return irBuilder.buildSuperMethodInvocation(function,
CallStructure.ONE_ARG, 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.
CallStructure translateDynamicArguments(ast.NodeList nodeList,
CallStructure callStructure,
List<ir.Primitive> arguments) {
assert(arguments.isEmpty);
for (ast.Node node in nodeList) arguments.add(visit(node));
return normalizeDynamicArguments(callStructure, arguments);
}
// TODO(johnniwinther): Handle this in the [IrBuilder] to ensure the correct
// semantic correlation between arguments and invocation.
CallStructure translateStaticArguments(ast.NodeList nodeList,
Element element,
CallStructure callStructure,
List<ir.Primitive> arguments) {
assert(arguments.isEmpty);
for (ast.Node node in nodeList) arguments.add(visit(node));
return normalizeStaticArguments(callStructure, element, arguments);
}
ir.Primitive translateCallInvoke(ir.Primitive target,
ast.NodeList argumentsNode,
CallStructure callStructure,
SourceInformation sourceInformation) {
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildCallInvocation(target, callStructure, arguments,
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 handleConstructorInvoke(
ast.NewExpression node,
ConstructorElement constructor,
DartType type,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
// TODO(sigmund): move these checks down after visiting arguments
// (see issue #25355)
ast.Send send = node.send;
// If an allocation refers to a type using a deferred import prefix (e.g.
// `new lib.A()`), we must ensure that the deferred import has already been
// loaded.
var prefix = compiler.deferredLoadTask.deferredPrefixElement(
send, elements);
if (prefix != null) buildCheckDeferredIsLoaded(prefix, send);
// We also emit deferred import checks when using redirecting factories that
// refer to deferred prefixes.
if (constructor.isRedirectingFactory && !constructor.isCyclicRedirection) {
ConstructorElement current = constructor;
while (current.isRedirectingFactory) {
var prefix = current.redirectionDeferredPrefix;
if (prefix != null) buildCheckDeferredIsLoaded(prefix, send);
current = current.immediateRedirectionTarget;
}
}
List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
// Use default values from the effective target, not the immediate target.
ConstructorElement target = constructor.implementation;
while (target.isRedirectingFactory && !target.isCyclicRedirection) {
target = target.effectiveTarget.implementation;
}
callStructure = normalizeStaticArguments(callStructure, target, arguments);
TypeMask allocationSiteType;
if (Elements.isFixedListConstructorCall(constructor, send, compiler) ||
Elements.isGrowableListConstructorCall(constructor, send, compiler) ||
Elements.isFilledListConstructorCall(constructor, send, compiler) ||
Elements.isConstructorOfTypedArraySubclass(constructor, compiler)) {
allocationSiteType = getAllocationSiteType(send);
}
ConstructorElement constructorImplementation = constructor.implementation;
return irBuilder.buildConstructorInvocation(
target,
callStructure,
constructorImplementation.computeEffectiveTargetType(type),
arguments,
sourceInformationBuilder.buildNew(node),
allocationSiteType: allocationSiteType);
}
@override
ir.Primitive handleDynamicInvoke(
ast.Send node,
ast.Node receiver,
ast.NodeList argumentsNode,
Selector selector,
_) {
ir.Primitive target = translateReceiver(receiver);
List<ir.Primitive> arguments = <ir.Primitive>[];
CallStructure callStructure = translateDynamicArguments(
argumentsNode, selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target,
new Selector(selector.kind, selector.memberName, callStructure),
elements.getTypeMask(node),
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
@override
ir.Primitive visitIfNotNullDynamicPropertyInvoke(
ast.Send node,
ast.Node receiver,
ast.NodeList argumentsNode,
Selector selector,
_) {
ir.Primitive target = visit(receiver);
return irBuilder.buildIfNotNullSend(
target,
nested(() {
List<ir.Primitive> arguments = <ir.Primitive>[];
CallStructure callStructure = translateDynamicArguments(
argumentsNode, selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target,
new Selector(selector.kind, selector.memberName, callStructure),
elements.getTypeMask(node),
arguments);
}));
}
ir.Primitive handleLocalInvoke(
ast.Send node,
LocalElement element,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
ir.Primitive function = irBuilder.buildLocalGet(element);
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildCallInvocation(function, callStructure, arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
@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 argumentsNode,
CallStructure callStructure,
_) {
SourceInformation src = sourceInformationBuilder.buildGet(node);
ir.Primitive target = buildStaticFieldGet(field, src);
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildCallInvocation(
target,
callStructure,
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
ir.Primitive handleStaticFunctionInvoke(ast.Send node,
MethodElement function,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
if (compiler.backend.isForeign(function)) {
return handleForeignCode(node, function, argumentsNode, callStructure);
} else {
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure = translateStaticArguments(argumentsNode, function,
callStructure, arguments);
Selector selector = new Selector.call(function.memberName, callStructure);
return irBuilder.buildInvokeStatic(function, selector, arguments,
sourceInformationBuilder.buildCall(node, node.selector));
}
}
@override
ir.Primitive handleStaticFunctionIncompatibleInvoke(
ast.Send node,
MethodElement function,
ast.NodeList arguments,
CallStructure callStructure, _) {
return irBuilder.buildStaticNoSuchMethod(
elements.getSelector(node),
arguments.nodes.mapToList(visit));
}
@override
ir.Primitive handleStaticGetterInvoke(
ast.Send node,
FunctionElement getter,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
ir.Primitive target = buildStaticGetterGet(getter, node);
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildCallInvocation(
target,
callStructure,
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
ir.Primitive visitSuperFieldInvoke(
ast.Send node,
FieldElement field,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
ir.Primitive target = irBuilder.buildSuperFieldGet(field);
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildCallInvocation(
target,
callStructure,
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
ir.Primitive visitSuperGetterInvoke(
ast.Send node,
FunctionElement getter,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
ir.Primitive target = irBuilder.buildSuperGetterGet(getter,
sourceInformationBuilder.buildGet(node));
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildCallInvocation(
target,
callStructure,
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, argumentsNode));
}
@override
ir.Primitive visitSuperMethodInvoke(
ast.Send node,
MethodElement method,
ast.NodeList argumentsNode,
CallStructure callStructure,
_) {
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure = translateStaticArguments(argumentsNode, method,
callStructure, arguments);
return irBuilder.buildSuperMethodInvocation(
method,
callStructure,
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(node, node.selector));
}
@override
ir.Primitive visitSuperMethodIncompatibleInvoke(
ast.Send node,
MethodElement method,
ast.NodeList arguments,
CallStructure callStructure, _) {
List<ir.Primitive> normalizedArguments = <ir.Primitive>[];
CallStructure normalizedCallStructure =
translateDynamicArguments(arguments, callStructure, normalizedArguments);
return buildInstanceNoSuchMethod(
new Selector.call(method.memberName, normalizedCallStructure),
elements.getTypeMask(node),
normalizedArguments);
}
@override
ir.Primitive visitUnresolvedSuperInvoke(
ast.Send node,
Element element,
ast.NodeList argumentsNode,
Selector selector, _) {
List<ir.Primitive> arguments = <ir.Primitive>[];
CallStructure callStructure = translateDynamicArguments(
argumentsNode, selector.callStructure, arguments);
// TODO(johnniwinther): Supply a member name to the visit function instead
// of looking it up in elements.
return buildInstanceNoSuchMethod(
new Selector.call(elements.getSelector(node).memberName, callStructure),
elements.getTypeMask(node),
arguments);
}
@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];
CallStructure callStructure =
normalizeDynamicArguments(operatorSelector.callStructure, arguments);
TypeMask operatorTypeMask =
elements.getOperatorTypeMaskInComplexSendSet(node);
SourceInformation operatorSourceInformation =
sourceInformationBuilder.buildCall(node, node.assignmentOperator);
ir.Primitive result = irBuilder.buildDynamicInvocation(
value,
new Selector(operatorSelector.kind, operatorSelector.memberName,
callStructure),
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,
_) {
ir.Primitive value = visit(rhs);
value = checkTypeVsElement(value, element);
return irBuilder.buildLocalVariableSet(element, value);
}
@override
ir.Primitive handleStaticFieldSet(
ast.SendSet node,
FieldElement field,
ast.Node rhs,
_) {
ir.Primitive value = visit(rhs);
irBuilder.addPrimitive(new ir.SetStatic(field, value));
return value;
}
@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) {
SourceInformation src = sourceInformationBuilder.buildGet(node);
return translateCompounds(node, () {
return buildConstantExpression(constant, src);
}, rhs, (ir.Primitive 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, () {
return irBuilder.buildDynamicGet(
target,
new Selector.getter(name),
elements.getGetterTypeMaskInComplexSendSet(node),
sourceInformationBuilder.buildGet(node));
}, rhs, (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, () {
return irBuilder.buildDynamicGet(
target,
new Selector.getter(name),
elements.getGetterTypeMaskInComplexSendSet(node),
sourceInformationBuilder.buildGet(node));
}, rhs, (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 irBuilder.buildStaticNoSuchMethod(selector, [value]);
}
@override
ir.Primitive handleLocalCompounds(
ast.SendSet node,
LocalElement local,
CompoundRhs rhs,
arg,
{bool isSetterValid}) {
return translateCompounds(node, () {
return irBuilder.buildLocalGet(local);
}, rhs, (ir.Primitive result) {
if (isSetterValid) {
irBuilder.buildLocalVariableSet(local, result);
} else {
Selector selector = new Selector.setter(
new Name(local.name, local.library, isSetter: true));
irBuilder.buildStaticNoSuchMethod(selector, <ir.Primitive>[result]);
}
});
}
@override
ir.Primitive handleLocalSetIfNulls(
ast.SendSet node,
LocalElement local,
ast.Node rhs,
_,
{bool isSetterValid}) {
return translateSetIfNull(node, () {
return irBuilder.buildLocalGet(local);
}, rhs, (ir.Primitive result) {
if (isSetterValid) {
irBuilder.buildLocalVariableSet(local, result);
} else {
Selector selector = new Selector.setter(
new Name(local.name, local.library, isSetter: true));
irBuilder.buildStaticNoSuchMethod(selector, <ir.Primitive>[result]);
}
});
}
@override
ir.Primitive handleStaticCompounds(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
CompoundRhs rhs,
arg) {
return translateCompounds(node, () {
switch (getterKind) {
case CompoundGetter.FIELD:
SourceInformation src = sourceInformationBuilder.buildGet(node);
return buildStaticFieldGet(getter, src);
case CompoundGetter.GETTER:
return buildStaticGetterGet(getter, node);
case CompoundGetter.METHOD:
return irBuilder.addPrimitive(new ir.GetStatic(getter));
case CompoundGetter.UNRESOLVED:
return irBuilder.buildStaticNoSuchMethod(
new Selector.getter(new Name(getter.name, getter.library)),
<ir.Primitive>[]);
}
}, rhs, (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
irBuilder.addPrimitive(new ir.SetStatic(setter, result));
return;
case CompoundSetter.SETTER:
irBuilder.buildStaticSetterSet(setter, result);
return;
case CompoundSetter.INVALID:
irBuilder.buildStaticNoSuchMethod(
new Selector.setter(new Name(setter.name, setter.library)),
<ir.Primitive>[result]);
return;
}
});
}
@override
ir.Primitive handleStaticSetIfNulls(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
ast.Node rhs,
_) {
return translateSetIfNull(node, () {
switch (getterKind) {
case CompoundGetter.FIELD:
SourceInformation src = sourceInformationBuilder.buildGet(node);
return buildStaticFieldGet(getter, src);
case CompoundGetter.GETTER:
return buildStaticGetterGet(getter, node);
case CompoundGetter.METHOD:
return irBuilder.addPrimitive(new ir.GetStatic(getter));
case CompoundGetter.UNRESOLVED:
return irBuilder.buildStaticNoSuchMethod(
new Selector.getter(new Name(getter.name, getter.library)),
<ir.Primitive>[]);
}
}, rhs, (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
irBuilder.addPrimitive(new ir.SetStatic(setter, result));
return;
case CompoundSetter.SETTER:
irBuilder.buildStaticSetterSet(setter, result);
return;
case CompoundSetter.INVALID:
irBuilder.buildStaticNoSuchMethod(
new Selector.setter(new Name(setter.name, setter.library)),
<ir.Primitive>[result]);
return;
}
});
}
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, () {
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, (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
irBuilder.buildSuperFieldSet(setter, result);
return;
case CompoundSetter.SETTER:
irBuilder.buildSuperSetterSet(setter, result);
return;
case CompoundSetter.INVALID:
buildSuperNoSuchSetter(
setter, elements.getTypeMask(node), result);
return;
}
});
}
@override
ir.Primitive handleSuperSetIfNulls(
ast.SendSet node,
Element getter,
CompoundGetter getterKind,
Element setter,
CompoundSetter setterKind,
ast.Node rhs,
_) {
return translateSetIfNull(node, () {
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, (ir.Primitive result) {
switch (setterKind) {
case CompoundSetter.FIELD:
irBuilder.buildSuperFieldSet(setter, result);
return;
case CompoundSetter.SETTER:
irBuilder.buildSuperSetterSet(setter, result);
return;
case CompoundSetter.INVALID:
buildSuperNoSuchSetter(
setter, elements.getTypeMask(node), result);
return;
}
});
}
@override
ir.Primitive handleTypeVariableTypeLiteralCompounds(
ast.SendSet node,
TypeVariableElement typeVariable,
CompoundRhs rhs,
arg) {
return translateCompounds(node, () {
return irBuilder.buildReifyTypeVariable(
typeVariable.type,
sourceInformationBuilder.buildGet(node));
}, rhs, (ir.Primitive 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, () {
Selector selector = new Selector.index();
List<ir.Primitive> arguments = <ir.Primitive>[indexValue];
CallStructure callStructure =
normalizeDynamicArguments(selector.callStructure, arguments);
return irBuilder.buildDynamicInvocation(
target,
new Selector(selector.kind, selector.memberName, callStructure),
elements.getGetterTypeMaskInComplexSendSet(node),
arguments,
sourceInformation:
sourceInformationBuilder.buildCall(receiver, node));
}, rhs, (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, () {
return isGetterValid
? irBuilder.buildSuperIndex(indexFunction, indexValue)
: buildInstanceNoSuchMethod(
new Selector.index(),
elements.getGetterTypeMaskInComplexSendSet(node),
<ir.Primitive>[indexValue]);
}, rhs, (ir.Primitive result) {
if (isSetterValid) {
irBuilder.buildSuperIndexSet(indexSetFunction, indexValue, result);
} else {
buildInstanceNoSuchMethod(
new Selector.indexSet(),
elements.getTypeMask(node),
<ir.Primitive>[indexValue, result]);
}
});
}
/// 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(MethodElement element,
CallStructure callStructure) {
if (element == null) {
reporter.internalError(node,
'Isolate library and compiler mismatch.');
}
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure = translateStaticArguments(argumentList, element,
callStructure, arguments);
Selector selector = new Selector.call(element.memberName, callStructure);
return irBuilder.buildInvokeStatic(element, selector, arguments,
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);
if (behavior.codeTemplate.positionalArgumentCount != arguments.length) {
reporter.reportErrorMessage(
node, MessageKind.GENERIC,
{'text':
'Mismatch between number of placeholders'
' and number of arguments.'});
return irBuilder.buildNullConstant();
}
if (HasCapturedPlaceholders.check(behavior.codeTemplate.ast)) {
reporter.reportErrorMessage(node, MessageKind.JS_PLACEHOLDER_CAPTURE);
return irBuilder.buildNullConstant();
}
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, helpers.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);
}
return internalError(argument, 'expected Type as argument');
case 'JS_EFFECT':
return irBuilder.buildNullConstant();
case 'JS_GET_NAME':
validateArgumentCount(exactly: 1);
ast.Node argument = argumentNodes.head;
JsGetName id = getEnumValue(argument, helpers.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(helpers.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.DEPENDS_OTHER);
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.CHANGES_OTHER);
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(helpers.callInIsolate,
CallStructure.TWO_ARGS);
default:
return giveup(node, 'unplemented native construct: ${function.name}');
}
}
/// 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.buildStaticFunctionInvocation(
helpers.stringInterpolationHelper,
<ir.Primitive>[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 buildInstanceNoSuchMethod(Selector selector,
TypeMask mask,
List<ir.Primitive> arguments) {
return irBuilder.buildDynamicInvocation(
irBuilder.buildThis(),
Selectors.noSuchMethod_,
mask,
[irBuilder.buildInvocationMirror(selector, arguments)]);
}
@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 irBuilder.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.
ir.Primitive message =
irBuilder.buildStringConstant("Unresolved class: '${element.name}'");
return irBuilder.buildStaticFunctionInvocation(
helpers.throwRuntimeError,
<ir.Primitive>[message]);
}
@override
ir.Primitive visitUnresolvedConstructorInvoke(
ast.NewExpression node,
Element constructor,
DartType type,
ast.NodeList argumentsNode,
Selector selector, _) {
// If the class is there but the constructor is missing, it's an NSM error.
List<ir.Primitive> arguments = <ir.Primitive>[];
CallStructure callStructure = translateDynamicArguments(
argumentsNode, selector.callStructure, arguments);
return irBuilder.buildStaticNoSuchMethod(
new Selector(selector.kind, selector.memberName, callStructure),
arguments);
}
@override
ir.Primitive visitConstructorIncompatibleInvoke(
ast.NewExpression node,
ConstructorElement constructor,
DartType type,
ast.NodeList argumentsNode,
CallStructure callStructure, _) {
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildStaticNoSuchMethod(
new Selector.call(constructor.memberName, callStructure), arguments);
}
@override
ir.Primitive visitUnresolvedGet(
ast.Send node,
Element element, _) {
return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node), []);
}
@override
ir.Primitive visitUnresolvedInvoke(
ast.Send node,
Element element,
ast.NodeList arguments,
Selector selector, _) {
return irBuilder.buildStaticNoSuchMethod(elements.getSelector(node),
arguments.nodes.mapToList(visit));
}
@override
ir.Primitive visitUnresolvedRedirectingFactoryConstructorInvoke(
ast.NewExpression node,
ConstructorElement constructor,
InterfaceType type,
ast.NodeList argumentsNode,
CallStructure callStructure, _) {
String nameString = Elements.reconstructConstructorName(constructor);
Name name = new Name(nameString, constructor.library);
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return irBuilder.buildStaticNoSuchMethod(
new Selector.call(name, callStructure),
arguments);
}
@override
ir.Primitive visitUnresolvedSet(
ast.Send node,
Element element,
ast.Node rhs, _) {
return irBuilder.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.Node node, ErroneousElement error, _) {
return irBuilder.buildNullConstant();
}
@override
ir.Primitive visitClassTypeLiteralSet(
ast.SendSet node,
TypeConstantExpression constant,
ast.Node rhs, _) {
InterfaceType type = constant.type;
ClassElement element = type.element;
return irBuilder.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 irBuilder.buildStaticNoSuchMethod(
new Selector.setter(element.memberName),
[visit(rhs)]);
}
@override
ir.Primitive visitTypeVariableTypeLiteralSet(
ast.SendSet node,
TypeVariableElement element,
ast.Node rhs, _) {
return irBuilder.buildStaticNoSuchMethod(
new Selector.setter(element.memberName), [visit(rhs)]);
}
@override
ir.Primitive visitDynamicTypeLiteralSet(
ast.SendSet node,
ConstantExpression constant,
ast.Node rhs, _) {
return irBuilder.buildStaticNoSuchMethod(
new Selector.setter(Names.dynamic_), [visit(rhs)]);
}
@override
ir.Primitive visitAbstractClassConstructorInvoke(
ast.NewExpression node,
ConstructorElement element,
InterfaceType type,
ast.NodeList arguments,
CallStructure callStructure, _) {
for (ast.Node argument in arguments) visit(argument);
ir.Primitive name =
irBuilder.buildStringConstant(element.enclosingClass.name);
return irBuilder.buildStaticFunctionInvocation(
helpers.throwAbstractClassInstantiationError,
<ir.Primitive>[name]);
}
@override
ir.Primitive handleFinalStaticFieldSet(
ast.SendSet node,
FieldElement field,
ast.Node rhs, _) {
// TODO(asgerf): Include class name somehow for static class members?
return irBuilder.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 irBuilder.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 irBuilder.buildStaticNoSuchMethod(
new Selector.setter(function.memberName),
[visit(rhs)]);
}
@override
ir.Primitive handleStaticGetterSet(
ast.SendSet node,
GetterElement getter,
ast.Node rhs,
_) {
return irBuilder.buildStaticNoSuchMethod(
new Selector.setter(getter.memberName),
[visit(rhs)]);
}
@override
ir.Primitive handleStaticSetterGet(
ast.Send node,
SetterElement setter,
_) {
return irBuilder.buildStaticNoSuchMethod(
new Selector.getter(setter.memberName),
[]);
}
@override
ir.Primitive handleStaticSetterInvoke(
ast.Send node,
SetterElement setter,
ast.NodeList argumentsNode,
CallStructure callStructure, _) {
// Translate as a method call.
List<ir.Primitive> arguments = argumentsNode.nodes.mapToList(visit);
return irBuilder.buildStaticNoSuchMethod(
new Selector.call(setter.memberName, callStructure),
arguments);
}
@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 argumentsNode,
CallStructure callStructure, _) {
List<ir.Primitive> arguments = <ir.Primitive>[];
callStructure =
translateDynamicArguments(argumentsNode, callStructure, arguments);
return buildInstanceNoSuchMethod(
new Selector.call(setter.memberName, callStructure),
elements.getTypeMask(node),
arguments);
}
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) {
reporter.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";
/// Determines which local variables should be boxed in a mutable variable
/// inside a given try block.
class TryBoxedVariables extends ast.Visitor {
final TreeElements elements;
TryBoxedVariables(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);
}
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 async* functions");
}
if (node.initializers != null) {
visit(node.initializers);
}
visit(node.body);
currentFunction = savedFunction;
}
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 throwTypeErrorHelper => _backend.helpers.throwTypeError;
Element get throwNoSuchMethod => _backend.helpers.throwNoSuchMethod;
ClassElement get nullClass => _compiler.coreClasses.nullClass;
DartType unaliasType(DartType type) => type.unaliased;
TypeMask getTypeMaskForForeign(NativeBehavior behavior) {
if (behavior == null) {
return _backend.dynamicType;
}
return TypeMaskFactory.fromNativeBehavior(behavior, _compiler);
}
bool isArrayType(TypeMask type) {
return type.satisfies(_backend.helpers.jsArrayClass, _compiler.world);
}
TypeMask getTypeMaskForNativeFunction(FunctionElement function) {
return _compiler.typesTask.getGuaranteedReturnTypeOfElement(function);
}
FieldElement locateSingleField(Selector selector, TypeMask type) {
return _compiler.world.locateSingleField(selector, type);
}
Element get closureConverter {
return _backend.helpers.closureConverter;
}
void addNativeMethod(FunctionElement function) {
_backend.emitter.nativeEmitter.nativeMethods.add(function);
}
}