blob: 8d2e088f5126a3bb619355c7d208261df174f2c1 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart' as ir;
import '../closure.dart';
import '../common.dart';
import '../compiler.dart';
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../common_elements.dart';
import '../elements/elements.dart';
import '../elements/entities.dart';
import '../elements/jumps.dart';
import '../elements/modelx.dart';
import '../elements/resolution_types.dart';
import '../elements/types.dart';
import '../js_backend/js_backend.dart';
import '../js_backend/native_data.dart';
import '../js_model/closure.dart' show KernelScopeInfo, JRecordField;
import '../kernel/element_map.dart';
import '../kernel/element_map_mixins.dart';
import '../kernel/kernel.dart';
import '../native/native.dart' as native;
import '../resolution/tree_elements.dart';
import '../ssa/type_builder.dart';
import '../tree/tree.dart' as ast;
import '../types/masks.dart';
import '../types/types.dart';
import '../universe/selector.dart';
import '../world.dart';
import 'graph_builder.dart';
import 'jump_handler.dart' show SwitchCaseJumpHandler;
import 'locals_handler.dart';
import 'types.dart';
/// A helper class that abstracts all accesses of the AST from Kernel nodes.
///
/// The goal is to remove all need for the AST from the Kernel SSA builder.
class KernelAstAdapter extends KernelToElementMapBaseMixin
with KernelToElementMapForBuildingMixin, KernelToElementMapForImpactMixin
implements KernelToLocalsMap {
final Kernel kernel;
final JavaScriptBackend _backend;
final Map<ir.Node, ast.Node> _nodeToAst;
final Map<ir.Node, Element> _nodeToElement;
final Map<ir.VariableDeclaration, SyntheticLocal> _syntheticLocals =
<ir.VariableDeclaration, SyntheticLocal>{};
// TODO(efortuna): In an ideal world the TreeNodes should be some common
// interface we create for both ir.Statements and ir.SwitchCase (the
// ContinueSwitchStatement's target is a SwitchCase) rather than general
// TreeNode. Talking to Asger about this.
final Map<ir.TreeNode, KernelJumpTarget> _jumpTargets =
<ir.TreeNode, KernelJumpTarget>{};
DartTypeConverter _typeConverter;
ResolvedAst _resolvedAst;
/// Sometimes for resolution the resolved AST element needs to change (for
/// example, if we're inlining, or if we're in a constructor, but then also
/// constructing the field values). We keep track of this with a stack.
final List<ResolvedAst> _resolvedAstStack = <ResolvedAst>[];
final native.BehaviorBuilder nativeBehaviorBuilder;
KernelAstAdapter(this.kernel, this._backend, this._resolvedAst,
this._nodeToAst, this._nodeToElement)
: nativeBehaviorBuilder = new native.ResolverBehaviorBuilder(
_backend.compiler, _backend.frontendStrategy.nativeBasicData) {
KernelJumpTarget.index = 0;
// TODO(het): Maybe just use all of the kernel maps directly?
for (FieldElement fieldElement in kernel.fields.keys) {
_nodeToElement[kernel.fields[fieldElement]] = fieldElement;
}
for (FunctionElement functionElement in kernel.functions.keys) {
_nodeToElement[kernel.functions[functionElement]] = functionElement;
}
for (ClassElement classElement in kernel.classes.keys) {
_nodeToElement[kernel.classes[classElement]] = classElement;
}
for (LibraryElement libraryElement in kernel.libraries.keys) {
_nodeToElement[kernel.libraries[libraryElement]] = libraryElement;
}
for (LocalFunctionElement localFunction in kernel.localFunctions.keys) {
_nodeToElement[kernel.localFunctions[localFunction]] = localFunction;
}
for (TypeVariableElement typeVariable in kernel.typeParameters.keys) {
_nodeToElement[kernel.typeParameters[typeVariable]] = typeVariable;
}
_typeConverter = new DartTypeConverter(this);
}
@override
NativeBasicData get nativeBasicData =>
_backend.frontendStrategy.nativeBasicData;
void addProgram(ir.Program node) {
throw new UnsupportedError('KernelAstAdapter.addProgram');
}
@override
ConstantValue computeConstantValue(
Spannable spannable, ConstantExpression constant,
{bool requireConstant: true}) {
_compiler.backend.constants.evaluate(constant);
ConstantValue value =
_compiler.backend.constants.getConstantValue(constant);
if (value == null && requireConstant) {
throw new UnsupportedError(
'No constant value for ${constant.toStructuredText()}');
}
return value;
}
@override
ConstantValue getFieldConstantValue(covariant FieldElement element) {
if (element.constant != null) {
return computeConstantValue(element, element.constant);
}
return null;
}
/// Called to find the corresponding Kernel element for a particular Element
/// before traversing over it with a Kernel visitor.
ir.Node getMemberNode(MemberElement originTarget) {
ir.Node target;
if (originTarget.isPatch) {
originTarget = originTarget.origin;
}
if (originTarget is MethodElement) {
if (originTarget is ConstructorBodyElement) {
ConstructorBodyElement body = originTarget;
originTarget = body.constructor;
}
target = kernel.functions[originTarget];
// Closures require a lookup one level deeper in the closure class mapper.
if (target == null) {
SynthesizedCallMethodElementX originTargetFunction = originTarget;
target = kernel.localFunctions[originTargetFunction.expression];
}
} else if (originTarget is FieldElement) {
target = kernel.fields[originTarget];
}
assert(target != null);
return target;
}
MemberDefinition getMemberDefinition(MemberElement member) {
ir.Node node = getMemberNode(member);
if (member is ConstructorBodyElement) {
return new SpecialMemberDefinition(
member, node, MemberKind.constructorBody);
} else if (node is ir.Constructor) {
return new SpecialMemberDefinition(member, node, MemberKind.constructor);
} else if (node is ir.FunctionDeclaration ||
node is ir.FunctionExpression) {
return new SpecialMemberDefinition(member, node, MemberKind.closureCall);
}
return new RegularMemberDefinition(member, node);
}
InterfaceType getMemberThisType(MemberElement member) {
return member.contextClass?.thisType;
}
ClassTypeVariableAccess getClassTypeVariableAccessForMember(
MemberEntity member) {
throw new UnsupportedError('KernelAstAdapter.getMemberTypeVariableAccess');
}
ir.Node getClassNode(ClassElement cls) {
throw new UnsupportedError('KernelAstAdapter.getClassNode');
}
ClassDefinition getClassDefinition(ClassElement cls) {
throw new UnsupportedError('KernelAstAdapter.getClassDefinition');
}
@override
CommonElements get commonElements => _compiler.resolution.commonElements;
@override
DartTypes get types => _compiler.resolution.types;
@override
ElementEnvironment get elementEnvironment =>
_compiler.resolution.elementEnvironment;
MemberElement get currentMember => _resolvedAst.element;
/// Push the existing resolved AST on the stack and shift the current resolved
/// AST to the AST that this kernel node points to.
void enterInlinedMember(MemberElement member) {
_resolvedAstStack.add(_resolvedAst);
_resolvedAst = member.resolvedAst;
}
/// Pop the resolved AST stack to reset it to the previous resolved AST node.
void leaveInlinedMember(MemberElement member) {
assert(_resolvedAstStack.isNotEmpty);
assert(_resolvedAst.element == member);
_resolvedAst = _resolvedAstStack.removeLast();
}
Compiler get _compiler => _backend.compiler;
TreeElements get elements => _resolvedAst.elements;
DiagnosticReporter get reporter => _compiler.reporter;
// TODO(johnniwinther): Use the more precise functions below.
Element getElement(ir.Node node) {
Element result = _nodeToElement[node];
assert(result != null,
failedAt(CURRENT_ELEMENT_SPANNABLE, "No element found for $node."));
return result;
}
ConstructorElement getConstructor(ir.Member node) =>
getElement(node).declaration;
@override
ConstructorEntity getSuperConstructor(
ir.Constructor constructor, ir.Member target) {
assert(target != null);
return getConstructor(target);
}
@override
MemberEntity getSuperMember(ir.Member context, ir.Name name, ir.Member target,
{bool setter: false}) {
assert(target != null);
return getMember(target);
}
MemberElement getMember(ir.Member node) => getElement(node).declaration;
MethodElement getMethod(ir.Procedure node) => getElement(node).declaration;
FieldElement getField(ir.Field node) => getElement(node).declaration;
ClassElement getClass(ir.Class node) => getElement(node).declaration;
LibraryElement getLibrary(ir.Library node) => getElement(node).declaration;
LocalFunctionElement getLocalFunction(ir.TreeNode node) {
assert(
node is ir.FunctionDeclaration || node is ir.FunctionExpression,
failedAt(
CURRENT_ELEMENT_SPANNABLE, 'Invalid local function node: $node'));
return getElement(node);
}
/// Returns the uri for the deferred import [node].
String getDeferredUri(ir.LibraryDependency node) {
PrefixElement prefixElement = getElement(node);
return prefixElement.deferredImport.uri.toString();
}
ast.Node getNode(ir.Node node) {
ast.Node result = _nodeToAst[node];
assert(result != null,
failedAt(CURRENT_ELEMENT_SPANNABLE, "No node found for $node"));
return result;
}
ast.Node getNodeOrNull(ir.Node node) {
return _nodeToAst[node];
}
void assertNodeIsSynthetic(ir.Node node) {
assert(
kernel.syntheticNodes.contains(node),
failedAt(
CURRENT_ELEMENT_SPANNABLE, "No synthetic marker found for $node"));
}
@override
Local getLocalVariable(ir.VariableDeclaration variable,
{bool isClosureCallMethod}) {
// If this is a synthetic local, return the synthetic local
if (variable.name == null) {
return _syntheticLocals.putIfAbsent(
variable, () => new SyntheticLocal("x", null, null));
}
return getElement(variable) as LocalElement;
}
@override
Local getLocalTypeVariable(
ir.TypeParameterType node, KernelToElementMap elementMap) {
return getElement(node) as LocalElement;
}
@override
ir.FunctionNode getFunctionNodeForParameter(Local parameter) {
throw new UnsupportedError('KernelAstAdapter.getFunctionNodeForParameter');
}
@override
DartType getLocalType(KernelToElementMap elementMap, Local local) {
throw new UnsupportedError('KernelAstAdapter.getLocalType');
}
@override
JumpTarget getJumpTargetForBreak(ir.BreakStatement node) {
return getJumpTarget(node.target);
}
@override
bool generateContinueForBreak(ir.BreakStatement node) => false;
@override
JumpTarget getJumpTargetForLabel(ir.LabeledStatement node) {
return getJumpTarget(node);
}
@override
JumpTarget getJumpTargetForSwitch(ir.SwitchStatement node) {
return getJumpTarget(node);
}
@override
JumpTarget getJumpTargetForContinueSwitch(ir.ContinueSwitchStatement node) {
return getJumpTarget(node.target);
}
@override
JumpTarget getJumpTargetForSwitchCase(ir.SwitchCase node) {
return getJumpTarget(node, isContinueTarget: true);
}
@override
JumpTarget getJumpTargetForDo(ir.DoStatement node) {
return getJumpTarget(node);
}
@override
JumpTarget getJumpTargetForFor(ir.ForStatement node) {
return getJumpTarget(node);
}
@override
JumpTarget getJumpTargetForForIn(ir.ForInStatement node) {
return getJumpTarget(node);
}
@override
JumpTarget getJumpTargetForWhile(ir.WhileStatement node) {
return getJumpTarget(node);
}
KernelJumpTarget getJumpTarget(ir.TreeNode node,
{bool isContinueTarget: false}) {
return _jumpTargets.putIfAbsent(node, () {
if (node is ir.LabeledStatement && _jumpTargets.containsKey(node.body)) {
return _jumpTargets[node.body];
}
return new KernelJumpTarget(node, this,
makeContinueLabel: isContinueTarget);
});
}
DartType getDartType(ir.DartType type) {
return _typeConverter.convert(type);
}
List<DartType> getDartTypes(List<ir.DartType> types) {
return types.map(getDartType).toList();
}
/// Computes the function type corresponding the signature of [node].
FunctionType getFunctionType(ir.FunctionNode node) {
ResolutionDartType returnType = getDartType(node.returnType);
List<ResolutionDartType> parameterTypes = <ResolutionDartType>[];
List<ResolutionDartType> optionalParameterTypes = <ResolutionDartType>[];
for (ir.VariableDeclaration variable in node.positionalParameters) {
if (parameterTypes.length == node.requiredParameterCount) {
optionalParameterTypes.add(getDartType(variable.type));
} else {
parameterTypes.add(getDartType(variable.type));
}
}
List<String> namedParameters = <String>[];
List<ResolutionDartType> namedParameterTypes = <ResolutionDartType>[];
List<ir.VariableDeclaration> sortedNamedParameters =
node.namedParameters.toList()..sort((a, b) => a.name.compareTo(b.name));
for (ir.VariableDeclaration variable in sortedNamedParameters) {
namedParameters.add(variable.name);
namedParameterTypes.add(getDartType(variable.type));
}
return new ResolutionFunctionType.synthesized(returnType, parameterTypes,
optionalParameterTypes, namedParameters, namedParameterTypes);
}
InterfaceType getInterfaceType(ir.InterfaceType type) => getDartType(type);
TypeVariableType getTypeVariableType(ir.TypeParameterType type) =>
getDartType(type);
InterfaceType getThisType(ClassElement cls) => cls.thisType;
InterfaceType createInterfaceType(
ir.Class cls, List<ir.DartType> typeArguments) {
return new ResolutionInterfaceType(
getClass(cls), getDartTypes(typeArguments));
}
@override
TypedefType getTypedefType(ir.Typedef node) {
throw new UnsupportedError('KernelAstAdapter.getTypedefType');
}
FunctionEntity getConstructorBody(ir.Constructor constructor) {
AstElement element = getElement(constructor);
MemberEntity constructorBody =
ConstructorBodyElementX.createFromResolvedAst(element.resolvedAst);
assert(constructorBody != null);
return constructorBody;
}
@override
Spannable getSpannable(MemberEntity member, ir.Node node) {
return getNode(node);
}
@override
CapturedLoopScope getCapturedLoopScope(
ClosureDataLookup closureLookup, ir.TreeNode node) {
return closureLookup.getCapturedLoopScope(getNode(node));
}
@override
ClosureRepresentationInfo getClosureRepresentationInfo(
ClosureDataLookup closureLookup, ir.TreeNode node) {
LocalFunctionElement localFunction = getElement(node);
return closureLookup.getClosureInfo(localFunction.node);
}
@override
Map<Local, JRecordField> makeRecordContainer(
KernelScopeInfo info, MemberEntity member, KernelToLocalsMap localsMap) {
throw new UnsupportedError('KernelAstAdapter.makeRecordContainer');
}
}
/// Visitor that converts kernel dart types into [ResolutionDartType].
class DartTypeConverter extends ir.DartTypeVisitor<ResolutionDartType> {
final KernelAstAdapter astAdapter;
bool topLevel = true;
DartTypeConverter(this.astAdapter);
ResolutionDartType convert(ir.DartType type) {
topLevel = true;
return type.accept(this);
}
/// Visit a inner type.
ResolutionDartType visitType(ir.DartType type) {
topLevel = false;
return type.accept(this);
}
List<ResolutionDartType> visitTypes(List<ir.DartType> types) {
topLevel = false;
return new List.generate(
types.length, (int index) => types[index].accept(this));
}
@override
ResolutionDartType visitTypeParameterType(ir.TypeParameterType node) {
if (node.parameter.parent is ir.Class) {
ir.Class cls = node.parameter.parent;
int index = cls.typeParameters.indexOf(node.parameter);
ClassElement classElement = astAdapter.getElement(cls);
return classElement.typeVariables[index];
} else if (node.parameter.parent is ir.FunctionNode) {
ir.FunctionNode func = node.parameter.parent;
int index = func.typeParameters.indexOf(node.parameter);
Element element = astAdapter.getElement(func);
if (element.isConstructor) {
ClassElement classElement = element.enclosingClass;
return classElement.typeVariables[index];
} else {
GenericElement genericElement = element;
return genericElement.typeVariables[index];
}
}
throw new UnsupportedError('Unsupported type parameter type node $node.');
}
@override
ResolutionDartType visitFunctionType(ir.FunctionType node) {
return new ResolutionFunctionType.synthesized(
visitType(node.returnType),
visitTypes(node.positionalParameters
.take(node.requiredParameterCount)
.toList()),
visitTypes(node.positionalParameters
.skip(node.requiredParameterCount)
.toList()),
node.namedParameters.map((n) => n.name).toList(),
node.namedParameters.map((n) => visitType(n.type)).toList());
}
@override
ResolutionDartType visitInterfaceType(ir.InterfaceType node) {
ClassElement cls = astAdapter.getClass(node.classNode);
return new ResolutionInterfaceType(cls, visitTypes(node.typeArguments));
}
@override
ResolutionDartType visitVoidType(ir.VoidType node) {
return const ResolutionVoidType();
}
@override
ResolutionDartType visitDynamicType(ir.DynamicType node) {
return const ResolutionDynamicType();
}
@override
ResolutionDartType visitInvalidType(ir.InvalidType node) {
// Root uses such a `o is Unresolved` and `o as Unresolved` must be special
// cased in the builder, nested invalid types are treated as `dynamic`.
return const ResolutionDynamicType();
}
}
class KernelJumpTarget implements JumpTargetX {
static int index = 0;
/// Pointer to the actual executable statements that a jump target refers to.
/// If this jump target was not initially constructed with a LabeledStatement,
/// this value is identical to originalStatement. This Node is actually of
/// type either ir.Statement or ir.SwitchCase.
ir.Node targetStatement;
/// The original statement used to construct this jump target.
/// If this jump target was not initially constructed with a LabeledStatement,
/// this value is identical to targetStatement. This Node is actually of
/// type either ir.Statement or ir.SwitchCase.
ir.Node originalStatement;
/// Used to provide unique numbers to labels that would otherwise be duplicate
/// if one JumpTarget is inside another.
int nestingLevel;
@override
bool isBreakTarget = false;
@override
bool isContinueTarget = false;
KernelJumpTarget(this.targetStatement, KernelAstAdapter adapter,
{bool makeContinueLabel = false}) {
originalStatement = targetStatement;
this.labels = <LabelDefinition<ast.Node>>[];
if (targetStatement is ir.WhileStatement ||
targetStatement is ir.DoStatement ||
targetStatement is ir.ForStatement ||
targetStatement is ir.ForInStatement) {
// Currently these labels are set at resolution on the element itself.
// Once that gets updated, this logic can change downstream.
JumpTarget<ast.Node> target = adapter.elements
.getTargetDefinition(adapter.getNode(targetStatement));
if (target != null) {
labels.addAll(target.labels);
isBreakTarget = target.isBreakTarget;
isContinueTarget = target.isContinueTarget;
}
} else if (targetStatement is ir.LabeledStatement) {
targetStatement = (targetStatement as ir.LabeledStatement).body;
labels.add(
new LabelDefinitionX(null, 'L${index++}', this)..setBreakTarget());
isBreakTarget = true;
}
var originalNode = adapter.getNode(originalStatement);
var originalTarget = adapter.elements.getTargetDefinition(originalNode);
if (originalTarget != null) {
nestingLevel = originalTarget.nestingLevel;
} else {
nestingLevel = 0;
}
if (makeContinueLabel) {
labels.add(
new LabelDefinitionX(null, 'L${index++}', this)..setContinueTarget());
isContinueTarget = true;
}
}
@override
LabelDefinition<ast.Node> addLabel(ast.Label label, String labelName,
{bool isBreakTarget: false, bool isContinueTarget: false}) {
LabelDefinitionX result = new LabelDefinitionX(label, labelName, this);
labels.add(result);
if (isBreakTarget) {
result.setBreakTarget();
}
if (isContinueTarget) {
result.setContinueTarget();
}
return result;
}
@override
ExecutableElement get executableContext => null;
@override
MemberElement get memberContext => null;
@override
bool get isSwitch => targetStatement is ir.SwitchStatement;
bool get isSwitchCase => targetStatement is ir.SwitchCase;
@override
bool get isTarget => isBreakTarget || isContinueTarget;
@override
List<LabelDefinition<ast.Node>> labels;
@override
String get name => 'target';
@override
ast.Label get statement => null;
String toString() => 'Target:$targetStatement';
}
/// Special [JumpHandler] implementation used to handle continue statements
/// targeting switch cases.
class KernelSwitchCaseJumpHandler extends SwitchCaseJumpHandler {
KernelSwitchCaseJumpHandler(GraphBuilder builder, JumpTarget target,
ir.SwitchStatement switchStatement, KernelToLocalsMap localsMap)
: super(builder, target) {
// The switch case indices must match those computed in
// [KernelSsaBuilder.buildSwitchCaseConstants].
// Switch indices are 1-based so we can bypass the synthetic loop when no
// cases match simply by branching on the index (which defaults to null).
// TODO
int switchIndex = 1;
for (ir.SwitchCase switchCase in switchStatement.cases) {
JumpTarget continueTarget =
localsMap.getJumpTargetForSwitchCase(switchCase);
if (continueTarget != null) {
targetIndexMap[continueTarget] = switchIndex;
assert(builder.jumpTargets[continueTarget] == null);
builder.jumpTargets[continueTarget] = this;
}
switchIndex++;
}
}
}
class KernelAstTypeInferenceMap implements KernelToTypeInferenceMap {
final KernelAstAdapter _astAdapter;
KernelAstTypeInferenceMap(this._astAdapter);
MemberElement get _target => _astAdapter._resolvedAst.element;
GlobalTypeInferenceResults get _globalInferenceResults =>
_astAdapter._compiler.globalInference.results;
GlobalTypeInferenceElementResult _resultOf(MemberElement e) =>
_globalInferenceResults
.resultOfMember(e is ConstructorBodyElementX ? e.constructor : e);
TypeMask getReturnTypeOf(FunctionEntity function) {
return TypeMaskFactory.inferredReturnTypeForElement(
function, _globalInferenceResults);
}
TypeMask typeOfInvocation(ir.MethodInvocation send, ClosedWorld closedWorld) {
ast.Node operatorNode = _astAdapter.kernel.nodeToAstOperator[send];
if (operatorNode != null) {
return _resultOf(_target).typeOfOperator(operatorNode);
}
if (send.name.name == '[]=') {
return closedWorld.commonMasks.dynamicType;
}
ast.Node node = _astAdapter.getNodeOrNull(send);
if (node == null) {
assert(send.name.name == '==');
return closedWorld.commonMasks.dynamicType;
}
return _resultOf(_target).typeOfSend(node);
}
TypeMask typeOfGet(ir.PropertyGet getter) {
return _resultOf(_target).typeOfSend(_astAdapter.getNode(getter));
}
TypeMask typeOfSet(ir.PropertySet setter, ClosedWorld closedWorld) {
return closedWorld.commonMasks.dynamicType;
}
TypeMask typeOfDirectGet(ir.DirectPropertyGet getter) {
return _resultOf(_target).typeOfSend(_astAdapter.getNode(getter));
}
TypeMask typeOfListLiteral(MemberElement owner, ir.ListLiteral listLiteral,
ClosedWorld closedWorld) {
ast.Node node = _astAdapter.getNodeOrNull(listLiteral);
if (node == null) {
_astAdapter.assertNodeIsSynthetic(listLiteral);
return closedWorld.commonMasks.growableListType;
}
return _resultOf(owner)
.typeOfListLiteral(_astAdapter.getNode(listLiteral)) ??
closedWorld.commonMasks.dynamicType;
}
TypeMask typeOfIterator(ir.ForInStatement forInStatement) {
return _resultOf(_target)
.typeOfIterator(_astAdapter.getNode(forInStatement));
}
TypeMask typeOfIteratorCurrent(ir.ForInStatement forInStatement) {
return _resultOf(_target)
.typeOfIteratorCurrent(_astAdapter.getNode(forInStatement));
}
TypeMask typeOfIteratorMoveNext(ir.ForInStatement forInStatement) {
return _resultOf(_target)
.typeOfIteratorMoveNext(_astAdapter.getNode(forInStatement));
}
bool isJsIndexableIterator(
ir.ForInStatement forInStatement, ClosedWorld closedWorld) {
TypeMask mask = typeOfIterator(forInStatement);
return mask != null &&
mask.satisfies(
closedWorld.commonElements.jsIndexableClass, closedWorld) &&
// String is indexable but not iterable.
!mask.satisfies(closedWorld.commonElements.jsStringClass, closedWorld);
}
bool isFixedLength(TypeMask mask, ClosedWorld closedWorld) {
if (mask.isContainer && (mask as ContainerTypeMask).length != null) {
// A container on which we have inferred the length.
return true;
}
// TODO(sra): Recognize any combination of fixed length indexables.
if (mask.containsOnly(closedWorld.commonElements.jsFixedArrayClass) ||
mask.containsOnly(
closedWorld.commonElements.jsUnmodifiableArrayClass) ||
mask.containsOnlyString(closedWorld) ||
closedWorld.commonMasks.isTypedArray(mask)) {
return true;
}
return false;
}
TypeMask inferredIndexType(ir.ForInStatement forInStatement) {
return TypeMaskFactory.inferredTypeForSelector(new Selector.index(),
typeOfIterator(forInStatement), _globalInferenceResults);
}
TypeMask getInferredTypeOf(MemberEntity member) {
return TypeMaskFactory.inferredTypeForMember(
member, _globalInferenceResults);
}
TypeMask getInferredTypeOfParameter(Local parameter) {
return TypeMaskFactory.inferredTypeForParameter(
parameter, _globalInferenceResults);
}
TypeMask selectorTypeOf(Selector selector, TypeMask mask) {
return TypeMaskFactory.inferredTypeForSelector(
selector, mask, _globalInferenceResults);
}
TypeMask typeFromNativeBehavior(
native.NativeBehavior nativeBehavior, ClosedWorld closedWorld) {
return TypeMaskFactory.fromNativeBehavior(nativeBehavior, closedWorld);
}
}