diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart index c43f13f9d20b..e0b807741d51 100644 --- a/pkg/compiler/lib/src/dump_info.dart +++ b/pkg/compiler/lib/src/dump_info.dart @@ -20,7 +20,7 @@ import 'deferred_load.dart' show OutputUnit; import 'elements/entities.dart'; import 'js/js.dart' as jsAst; import 'js_backend/js_backend.dart' show JavaScriptBackend; -import 'types/masks.dart'; +import 'types/abstract_value_domain.dart'; import 'types/types.dart' show GlobalTypeInferenceElementResult, GlobalTypeInferenceMemberResult; import 'universe/world_builder.dart' show CodegenWorldBuilder; @@ -118,9 +118,12 @@ class ElementInfoCollector { if (!isInInstantiatedClass && !_hasBeenResolved(field)) { return null; } - TypeMask inferredType = _resultOfMember(field).type; + AbstractValue inferredType = _resultOfMember(field).type; // If a field has an empty inferred type it is never used. - if (inferredType == null || inferredType.isEmpty) return null; + if (inferredType == null || + closedWorld.abstractValueDomain.isEmpty(inferredType)) { + return null; + } int size = compiler.dumpInfoTask.sizeOf(field); String code = compiler.dumpInfoTask.codeOf(field); @@ -461,13 +464,12 @@ class DumpInfoTask extends CompilerTask implements InfoReporter { entity, impact, new WorldImpactVisitorImpl(visitDynamicUse: (dynamicUse) { - TypeMask mask = dynamicUse.receiverConstraint; + AbstractValue mask = dynamicUse.receiverConstraint; selections.addAll(closedWorld // TODO(het): Handle `call` on `Closure` through // `world.includesClosureCall`. .locateMembers(dynamicUse.selector, mask) - .map((MemberEntity e) => - new Selection(e, dynamicUse.receiverConstraint))); + .map((MemberEntity e) => new Selection(e, mask))); }, visitStaticUse: (staticUse) { selections.add(new Selection(staticUse.element, null)); }), diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart index 0764bff1bc5c..5abfae9717a0 100644 --- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart +++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart @@ -440,8 +440,8 @@ abstract class InferrerEngineImpl extends InferrerEngine { info.bailedOut = false; info.elementType.inferred = true; - AbstractValue fixedListType = abstractValueDomain.fixedListType; - if (info.originalType.forwardTo == fixedListType) { + if (abstractValueDomain.isSpecializationOf( + info.originalType, abstractValueDomain.fixedListType)) { info.checksGrowable = tracer.callsGrowableMethod; } tracer.assignments.forEach(info.elementType.addAssignment); @@ -460,12 +460,12 @@ abstract class InferrerEngineImpl extends InferrerEngine { info.bailedOut = false; for (int i = 0; i < tracer.keyAssignments.length; ++i) { - TypeInformation newType = info.addEntryAssignment( + TypeInformation newType = info.addEntryAssignment(abstractValueDomain, tracer.keyAssignments[i], tracer.valueAssignments[i]); if (newType != null) workQueue.add(newType); } for (TypeInformation map in tracer.mapAssignments) { - workQueue.addAll(info.addMapAssignment(map)); + workQueue.addAll(info.addMapAssignment(abstractValueDomain, map)); } info.markAsInferred(); @@ -596,15 +596,15 @@ abstract class InferrerEngineImpl extends InferrerEngine { types.allocatedLists.values.forEach((_info) { ListTypeInformation info = _info; print('${info.type} ' - 'for ${info.originalType.allocationNode} ' - 'at ${info.originalType.allocationElement} ' + 'for ${abstractValueDomain.getAllocationNode(info.originalType)} ' + 'at ${abstractValueDomain.getAllocationElement(info.originalType)}' 'after ${info.refineCount}'); }); types.allocatedMaps.values.forEach((_info) { MapTypeInformation info = _info; print('${info.type} ' - 'for ${info.originalType.allocationNode} ' - 'at ${info.originalType.allocationElement} ' + 'for ${abstractValueDomain.getAllocationNode(info.originalType)} ' + 'at ${abstractValueDomain.getAllocationElement(info.originalType)}' 'after ${info.refineCount}'); }); types.allocatedClosures.forEach((TypeInformation info) { @@ -711,7 +711,8 @@ abstract class InferrerEngineImpl extends InferrerEngine { // the old type around to ensure that we get a complete view // of the type graph and do not drop any flow edges. AbstractValue refinedType = computeTypeMask(closedWorld, value); - type = new NarrowTypeInformation(type, refinedType); + type = new NarrowTypeInformation( + abstractValueDomain, type, refinedType); types.allocatedTypes.add(type); } } @@ -766,7 +767,8 @@ abstract class InferrerEngineImpl extends InferrerEngine { if (info is StaticCallSiteTypeInformation) { MemberEntity member = info.calledElement; closedWorldRefiner.addFunctionCalledInLoop(member); - } else if (info.mask != null && !info.mask.containsAll(closedWorld)) { + } else if (info.mask != null && + !abstractValueDomain.containsAll(info.mask)) { // For instance methods, we only register a selector called in a // loop if it is a typed selector, to avoid marking too many // methods as being called from within a loop. This cuts down @@ -906,7 +908,8 @@ abstract class InferrerEngineImpl extends InferrerEngine { TypeInformation getDefaultTypeOfParameter(Local parameter) { return defaultTypeOfParameter.putIfAbsent(parameter, () { - return new PlaceholderTypeInformation(types.currentMember); + return new PlaceholderTypeInformation( + abstractValueDomain, types.currentMember); }); } @@ -967,6 +970,7 @@ abstract class InferrerEngineImpl extends InferrerEngine { SideEffectsBuilder sideEffectsBuilder, bool inLoop) { CallSiteTypeInformation info = new StaticCallSiteTypeInformation( + abstractValueDomain, types.currentMember, node, caller, @@ -1020,6 +1024,7 @@ abstract class InferrerEngineImpl extends InferrerEngine { }); CallSiteTypeInformation info = new DynamicCallSiteTypeInformation( + abstractValueDomain, types.currentMember, callType, node, @@ -1037,16 +1042,16 @@ abstract class InferrerEngineImpl extends InferrerEngine { } TypeInformation registerAwait(T node, TypeInformation argument) { - AwaitTypeInformation info = - new AwaitTypeInformation(types.currentMember, node); + AwaitTypeInformation info = new AwaitTypeInformation( + abstractValueDomain, types.currentMember, node); info.addAssignment(argument); types.allocatedTypes.add(info); return info; } TypeInformation registerYield(T node, TypeInformation argument) { - YieldTypeInformation info = - new YieldTypeInformation(types.currentMember, node); + YieldTypeInformation info = new YieldTypeInformation( + abstractValueDomain, types.currentMember, node); info.addAssignment(argument); types.allocatedTypes.add(info); return info; @@ -1063,6 +1068,7 @@ abstract class InferrerEngineImpl extends InferrerEngine { {bool inLoop}) { sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething(); CallSiteTypeInformation info = new ClosureCallSiteTypeInformation( + abstractValueDomain, types.currentMember, node, caller, diff --git a/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart index 7b494354bb05..a5f708cef610 100644 --- a/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart +++ b/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart @@ -268,7 +268,9 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy { @override ParameterTypeInformation createParameterTypeInformation( - covariant JLocal parameter, TypeSystem types) { + AbstractValueDomain abstractValueDomain, + covariant JLocal parameter, + TypeSystem types) { MemberEntity context = parameter.memberContext; KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(context); ir.FunctionNode functionNode = @@ -289,40 +291,48 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy { types.getInferredTypeOfMember(member); if (isClosure) { return new ParameterTypeInformation.localFunction( - memberTypeInformation, parameter, type, member); + abstractValueDomain, memberTypeInformation, parameter, type, member); } else if (member.isInstanceMember) { - return new ParameterTypeInformation.instanceMember(memberTypeInformation, - parameter, type, member, new ParameterAssignments()); + return new ParameterTypeInformation.instanceMember( + abstractValueDomain, + memberTypeInformation, + parameter, + type, + member, + new ParameterAssignments()); } else { return new ParameterTypeInformation.static( - memberTypeInformation, parameter, type, member); + abstractValueDomain, memberTypeInformation, parameter, type, member); } } @override - MemberTypeInformation createMemberTypeInformation(MemberEntity member) { + MemberTypeInformation createMemberTypeInformation( + AbstractValueDomain abstractValueDomain, MemberEntity member) { if (member.isField) { FieldEntity field = member; DartType type = _elementEnvironment.getFieldType(field); - return new FieldTypeInformation(field, type); + return new FieldTypeInformation(abstractValueDomain, field, type); } else if (member.isGetter) { FunctionEntity getter = member; DartType type = _elementEnvironment.getFunctionType(getter); - return new GetterTypeInformation(getter, type); + return new GetterTypeInformation(abstractValueDomain, getter, type); } else if (member.isSetter) { FunctionEntity setter = member; - return new SetterTypeInformation(setter); + return new SetterTypeInformation(abstractValueDomain, setter); } else if (member.isFunction) { FunctionEntity method = member; DartType type = _elementEnvironment.getFunctionType(method); - return new MethodTypeInformation(method, type); + return new MethodTypeInformation(abstractValueDomain, method, type); } else { ConstructorEntity constructor = member; if (constructor.isFactoryConstructor) { DartType type = _elementEnvironment.getFunctionType(constructor); - return new FactoryConstructorTypeInformation(constructor, type); + return new FactoryConstructorTypeInformation( + abstractValueDomain, constructor, type); } else { - return new GenerativeConstructorTypeInformation(constructor); + return new GenerativeConstructorTypeInformation( + abstractValueDomain, constructor); } } } diff --git a/pkg/compiler/lib/src/inferrer/node_tracer.dart b/pkg/compiler/lib/src/inferrer/node_tracer.dart index 032b254103cc..b09dfcc32d41 100644 --- a/pkg/compiler/lib/src/inferrer/node_tracer.dart +++ b/pkg/compiler/lib/src/inferrer/node_tracer.dart @@ -6,7 +6,7 @@ library compiler.src.inferrer.node_tracer; import '../common/names.dart' show Identifiers; import '../elements/entities.dart'; -import '../types/masks.dart' show ContainerTypeMask, MapTypeMask; +import '../types/abstract_value_domain.dart'; import '../util/util.dart' show Setlet; import 'debug.dart' as debug; import 'inferrer_engine.dart'; @@ -306,31 +306,33 @@ abstract class TracerVisitor implements TypeInformationVisitor { void visitDynamicCallSiteTypeInformation( DynamicCallSiteTypeInformation info) { - void addsToContainer(ContainerTypeMask mask) { - if (mask.allocationNode != null) { + void addsToContainer(AbstractValue mask) { + Object allocationNode = + inferrer.abstractValueDomain.getAllocationNode(mask); + if (allocationNode != null) { ListTypeInformation list = - inferrer.types.allocatedLists[mask.allocationNode]; + inferrer.types.allocatedLists[allocationNode]; listsToAnalyze.add(list); } else { - // The [ContainerTypeMask] is a union of two containers, and we lose - // track of where these containers have been allocated at this point. + // The [mask] is a union of two containers, and we lose track of where + // these containers have been allocated at this point. bailout('Stored in too many containers'); } } - void addsToMapValue(MapTypeMask mask) { - if (mask.allocationNode != null) { - MapTypeInformation map = - inferrer.types.allocatedMaps[mask.allocationNode]; + void addsToMapValue(AbstractValue mask) { + Object allocationNode = + inferrer.abstractValueDomain.getAllocationNode(mask); + if (allocationNode != null) { + MapTypeInformation map = inferrer.types.allocatedMaps[allocationNode]; mapsToAnalyze.add(map); } else { - // The [MapTypeMask] is a union. See comment for [ContainerTypeMask] - // above. + // The [mask] is a union. See comment for [mask] above. bailout('Stored in too many maps'); } } - void addsToMapKey(MapTypeMask mask) { + void addsToMapKey(AbstractValue mask) { // We do not track the use of keys from a map, so we have to bail. bailout('Used as key in Map'); } @@ -338,9 +340,9 @@ abstract class TracerVisitor implements TypeInformationVisitor { // "a[...] = x" could be a list (container) or map assignemnt. if (isIndexSetValue(info)) { var receiverType = info.receiver.type; - if (receiverType is ContainerTypeMask) { + if (inferrer.abstractValueDomain.isContainer(receiverType)) { addsToContainer(receiverType); - } else if (receiverType is MapTypeMask) { + } else if (inferrer.abstractValueDomain.isMap(receiverType)) { addsToMapValue(receiverType); } else { // Not a container or map, so the targets could be any methods. There @@ -363,7 +365,7 @@ abstract class TracerVisitor implements TypeInformationVisitor { // Could be: m[x] = ...; if (isIndexSetKey(info)) { var receiverType = info.receiver.type; - if (receiverType is MapTypeMask) { + if (inferrer.abstractValueDomain.isMap(receiverType)) { addsToMapKey(receiverType); } else { bailoutIfReaches(isParameterOfListAddingMethod); @@ -373,7 +375,7 @@ abstract class TracerVisitor implements TypeInformationVisitor { if (mightAddToContainer(info)) { var receiverType = info.receiver.type; - if (receiverType is ContainerTypeMask) { + if (inferrer.abstractValueDomain.isContainer(receiverType)) { addsToContainer(receiverType); } else { // Not a container, see note above. diff --git a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart index d8242e97c6b8..52ff120dc382 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart @@ -6,7 +6,7 @@ library dart2js.inferrer.type_graph_dump; import '../../compiler_new.dart'; import '../elements/entities.dart'; import '../elements/entity_utils.dart' as utils; -import '../types/masks.dart'; +import '../types/abstract_value_domain.dart'; import 'inferrer_engine.dart'; import 'type_graph_nodes.dart'; import 'debug.dart'; @@ -80,7 +80,8 @@ class TypeGraphDump { String name = filenameFromElement(element); output = compilerOutput.createOutputSink( '$outputDir/$name', 'dot', OutputType.debug); - _GraphGenerator visitor = new _GraphGenerator(this, element, output); + _GraphGenerator visitor = new _GraphGenerator( + this, element, output, inferrer.abstractValueDomain.getCompactText); for (TypeInformation node in nodes[element]) { visitor.visit(node); } @@ -137,12 +138,13 @@ class _GraphGenerator extends TypeInformationVisitor { final Set seen = new Set(); final List worklist = new List(); final Map nodeId = {}; + final String Function(AbstractValue) formatType; int usedIds = 0; final OutputSink output; final MemberEntity element; TypeInformation returnValue; - _GraphGenerator(this.global, this.element, this.output) { + _GraphGenerator(this.global, this.element, this.output, this.formatType) { returnValue = global.inferrer.types.getInferredTypeOfMember(element); getNode(returnValue); // Ensure return value is part of graph. append('digraph {'); @@ -413,41 +415,3 @@ class _GraphGenerator extends TypeInformationVisitor { addNode(info, 'Yield\n$text'); } } - -/// Convert the given TypeMask to a compact string format. -/// -/// The default format is too verbose for the graph format since long strings -/// create oblong nodes that obstruct the graph layout. -String formatType(TypeMask type) { - if (type is FlatTypeMask) { - // TODO(asgerf): Disambiguate classes whose name is not unique. Using the - // library name for all classes is not a good idea, since library names - // can be really long and mess up the layout. - // Capitalize Null to emphasize that it's the null type mask and not - // a null value we accidentally printed out. - if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty'; - String nullFlag = type.isNullable ? '?' : ''; - String subFlag = type.isExact ? '' : type.isSubclass ? '+' : '*'; - return '${type.base.name}$nullFlag$subFlag'; - } - if (type is UnionTypeMask) { - return type.disjointMasks.map(formatType).join(' | '); - } - if (type is ContainerTypeMask) { - String container = formatType(type.forwardTo); - String member = formatType(type.elementType); - return '$container<$member>'; - } - if (type is MapTypeMask) { - String container = formatType(type.forwardTo); - String key = formatType(type.keyType); - String value = formatType(type.valueType); - return '$container<$key,$value>'; - } - if (type is ValueTypeMask) { - String baseType = formatType(type.forwardTo); - String value = type.value.toStructuredText(); - return '$baseType=$value'; - } - return '$type'; // Fall back on toString if not supported here. -} diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart index cb9971acf8b3..2353af8e2127 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -12,14 +12,7 @@ import '../common/names.dart' show Identifiers; import '../constants/values.dart'; import '../elements/entities.dart'; import '../elements/types.dart'; -import '../types/masks.dart' - show - CommonMasks, - ContainerTypeMask, - DictionaryTypeMask, - MapTypeMask, - TypeMask, - ValueTypeMask; +import '../types/abstract_value_domain.dart'; import '../universe/selector.dart' show Selector; import '../util/util.dart' show ImmutableEmptySet, Setlet; import '../world.dart' show ClosedWorld; @@ -50,7 +43,7 @@ abstract class TypeInformation { /// The type the inferrer has found for this [TypeInformation]. /// Initially empty. - TypeMask type = const TypeMask.nonNullEmpty(); + AbstractValue type; /// The graph node of the member this [TypeInformation] node belongs to. final MemberTypeInformation context; @@ -92,20 +85,20 @@ abstract class TypeInformation { bool get isConcrete => false; - TypeInformation(this.context) + TypeInformation(this.type, this.context) : _assignments = [], users = new Setlet(); - TypeInformation.noAssignments(this.context) + TypeInformation.noAssignments(this.type, this.context) : _assignments = const [], users = new Setlet(); - TypeInformation.untracked() + TypeInformation.untracked(this.type) : _assignments = const [], users = const ImmutableEmptySet(), context = null; - TypeInformation.withAssignments(this.context, this._assignments) + TypeInformation.withAssignments(this.type, this.context, this._assignments) : users = new Setlet(); void addUser(TypeInformation user) { @@ -152,7 +145,7 @@ abstract class TypeInformation { } } - TypeMask refine(InferrerEngine inferrer) { + AbstractValue refine(InferrerEngine inferrer) { return abandonInferencing ? safeType(inferrer) : computeType(inferrer); } @@ -160,13 +153,13 @@ abstract class TypeInformation { * Computes a new type for this [TypeInformation] node depending on its * potentially updated inputs. */ - TypeMask computeType(InferrerEngine inferrer); + AbstractValue computeType(InferrerEngine inferrer); /** * Returns an approximation for this [TypeInformation] node that is always * safe to use. Used when abandoning inference on a node. */ - TypeMask safeType(InferrerEngine inferrer) { + AbstractValue safeType(InferrerEngine inferrer) { return inferrer.types.dynamicType.type; } @@ -189,7 +182,7 @@ abstract class TypeInformation { bool reset(InferrerEngine inferrer) { if (abandonInferencing) return false; - type = const TypeMask.nonNullEmpty(); + type = inferrer.abstractValueDomain.emptyType; refineCount = 0; return true; } @@ -259,13 +252,15 @@ abstract class ApplyableTypeInformation implements TypeInformation { * [getDefaultTypeOfParameter] and [setDefaultTypeOfParameter] for details. */ class PlaceholderTypeInformation extends TypeInformation { - PlaceholderTypeInformation(MemberTypeInformation context) : super(context); + PlaceholderTypeInformation( + AbstractValueDomain abstractValueDomain, MemberTypeInformation context) + : super(abstractValueDomain.emptyType, context); void accept(TypeInformationVisitor visitor) { throw new UnsupportedError("Cannot visit placeholder"); } - TypeMask computeType(InferrerEngine inferrer) { + AbstractValue computeType(InferrerEngine inferrer) { throw new UnsupportedError("Cannot refine placeholder"); } @@ -350,11 +345,15 @@ abstract class ElementTypeInformation extends TypeInformation { /// Marker to disable inference for closures in [handleSpecialCases]. bool disableInferenceForClosures = true; - ElementTypeInformation._internal(MemberTypeInformation context) - : super(context); + ElementTypeInformation._internal( + AbstractValueDomain abstractValueDomain, MemberTypeInformation context) + : super(abstractValueDomain.emptyType, context); ElementTypeInformation._withAssignments( - MemberTypeInformation context, ParameterAssignments assignments) - : super.withAssignments(context, assignments); + AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, + ParameterAssignments assignments) + : super.withAssignments( + abstractValueDomain.emptyType, context, assignments); String getInferredSignature(TypeSystem types); @@ -399,7 +398,9 @@ abstract class MemberTypeInformation extends ElementTypeInformation */ Map> _callers; - MemberTypeInformation._internal(this._member) : super._internal(null); + MemberTypeInformation._internal( + AbstractValueDomain abstractValueDomain, this._member) + : super._internal(abstractValueDomain, null); MemberEntity get member => _member; @@ -452,9 +453,9 @@ abstract class MemberTypeInformation extends ElementTypeInformation // state of the [isStable] field inherited from [TypeInformation]. bool get isStable => super.isStable && !isClosurized; - TypeMask handleSpecialCases(InferrerEngine inferrer); + AbstractValue handleSpecialCases(InferrerEngine inferrer); - TypeMask _handleFunctionCase( + AbstractValue _handleFunctionCase( FunctionEntity function, InferrerEngine inferrer) { if (inferrer.closedWorld.nativeData.isNativeMember(function)) { // Use the type annotation as the type for native elements. We @@ -469,7 +470,8 @@ abstract class MemberTypeInformation extends ElementTypeInformation return null; } - TypeMask potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { if (inferrer.options.assignmentCheckPolicy.isTrusted || inferrer.options.assignmentCheckPolicy.isEmitted || inferrer.trustTypeAnnotations(_member)) { @@ -478,16 +480,17 @@ abstract class MemberTypeInformation extends ElementTypeInformation return mask; } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer); + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer); - TypeMask computeType(InferrerEngine inferrer) { - TypeMask special = handleSpecialCases(inferrer); + AbstractValue computeType(InferrerEngine inferrer) { + AbstractValue special = handleSpecialCases(inferrer); if (special != null) return potentiallyNarrowType(special, inferrer); return potentiallyNarrowType( inferrer.types.computeTypeMask(assignments), inferrer); } - TypeMask safeType(InferrerEngine inferrer) { + AbstractValue safeType(InferrerEngine inferrer) { return potentiallyNarrowType(super.safeType(inferrer), inferrer); } @@ -515,10 +518,11 @@ class FieldTypeInformation extends MemberTypeInformation { FieldEntity get _field => _member; final DartType _type; - FieldTypeInformation(FieldEntity element, this._type) - : super._internal(element); + FieldTypeInformation( + AbstractValueDomain abstractValueDomain, FieldEntity element, this._type) + : super._internal(abstractValueDomain, element); - TypeMask handleSpecialCases(InferrerEngine inferrer) { + AbstractValue handleSpecialCases(InferrerEngine inferrer) { if (!inferrer.canFieldBeUsedForGlobalOptimizations(_field) || inferrer.assumeDynamic(_field)) { // Do not infer types for fields that have a corresponding annotation or @@ -540,7 +544,8 @@ class FieldTypeInformation extends MemberTypeInformation { return null; } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { return _narrowType(inferrer.closedWorld, mask, _type); } @@ -558,14 +563,16 @@ class GetterTypeInformation extends MemberTypeInformation { FunctionEntity get _getter => _member; final FunctionType _type; - GetterTypeInformation(FunctionEntity element, this._type) - : super._internal(element); + GetterTypeInformation(AbstractValueDomain abstractValueDomain, + FunctionEntity element, this._type) + : super._internal(abstractValueDomain, element); - TypeMask handleSpecialCases(InferrerEngine inferrer) { + AbstractValue handleSpecialCases(InferrerEngine inferrer) { return _handleFunctionCase(_getter, inferrer); } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { return _narrowType(inferrer.closedWorld, mask, _type.returnType); } } @@ -573,13 +580,16 @@ class GetterTypeInformation extends MemberTypeInformation { class SetterTypeInformation extends MemberTypeInformation { FunctionEntity get _setter => _member; - SetterTypeInformation(FunctionEntity element) : super._internal(element); + SetterTypeInformation( + AbstractValueDomain abstractValueDomain, FunctionEntity element) + : super._internal(abstractValueDomain, element); - TypeMask handleSpecialCases(InferrerEngine inferrer) { + AbstractValue handleSpecialCases(InferrerEngine inferrer) { return _handleFunctionCase(_setter, inferrer); } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { return mask; } } @@ -588,14 +598,16 @@ class MethodTypeInformation extends MemberTypeInformation { FunctionEntity get _method => _member; final FunctionType _type; - MethodTypeInformation(FunctionEntity element, this._type) - : super._internal(element); + MethodTypeInformation(AbstractValueDomain abstractValueDomain, + FunctionEntity element, this._type) + : super._internal(abstractValueDomain, element); - TypeMask handleSpecialCases(InferrerEngine inferrer) { + AbstractValue handleSpecialCases(InferrerEngine inferrer) { return _handleFunctionCase(_method, inferrer); } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { return _narrowType(inferrer.closedWorld, mask, _type.returnType); } @@ -606,29 +618,31 @@ class FactoryConstructorTypeInformation extends MemberTypeInformation { ConstructorEntity get _constructor => _member; final FunctionType _type; - FactoryConstructorTypeInformation(ConstructorEntity element, this._type) - : super._internal(element); + FactoryConstructorTypeInformation(AbstractValueDomain abstractValueDomain, + ConstructorEntity element, this._type) + : super._internal(abstractValueDomain, element); - TypeMask handleSpecialCases(InferrerEngine inferrer) { - CommonMasks abstractValueDomain = inferrer.abstractValueDomain; + AbstractValue handleSpecialCases(InferrerEngine inferrer) { + AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain; if (_constructor.isFromEnvironmentConstructor) { if (_constructor.enclosingClass == inferrer.commonElements.intClass) { giveUp(inferrer); - return abstractValueDomain.intType.nullable(); + return abstractValueDomain.includeNull(abstractValueDomain.intType); } else if (_constructor.enclosingClass == inferrer.commonElements.boolClass) { giveUp(inferrer); - return abstractValueDomain.boolType.nullable(); + return abstractValueDomain.includeNull(abstractValueDomain.boolType); } else if (_constructor.enclosingClass == inferrer.commonElements.stringClass) { giveUp(inferrer); - return abstractValueDomain.stringType.nullable(); + return abstractValueDomain.includeNull(abstractValueDomain.stringType); } } return _handleFunctionCase(_constructor, inferrer); } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { return _narrowType(inferrer.closedWorld, mask, _type.returnType); } @@ -640,14 +654,16 @@ class FactoryConstructorTypeInformation extends MemberTypeInformation { class GenerativeConstructorTypeInformation extends MemberTypeInformation { ConstructorEntity get _constructor => _member; - GenerativeConstructorTypeInformation(ConstructorEntity element) - : super._internal(element); + GenerativeConstructorTypeInformation( + AbstractValueDomain abstractValueDomain, ConstructorEntity element) + : super._internal(abstractValueDomain, element); - TypeMask handleSpecialCases(InferrerEngine inferrer) { + AbstractValue handleSpecialCases(InferrerEngine inferrer) { return _handleFunctionCase(_constructor, inferrer); } - TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue _potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { return mask; } @@ -675,21 +691,26 @@ class ParameterTypeInformation extends ElementTypeInformation { bool _isTearOffClosureParameter = false; ParameterTypeInformation.localFunction( - MemberTypeInformation context, this._parameter, this._type, this._method) + AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, + this._parameter, + this._type, + this._method) : _isInstanceMemberParameter = false, _isClosureParameter = true, _isInitializingFormal = false, - super._internal(context); + super._internal(abstractValueDomain, context); - ParameterTypeInformation.static( + ParameterTypeInformation.static(AbstractValueDomain abstractValueDomain, MemberTypeInformation context, this._parameter, this._type, this._method, {bool isInitializingFormal: false}) : _isInstanceMemberParameter = false, _isClosureParameter = false, _isInitializingFormal = isInitializingFormal, - super._internal(context); + super._internal(abstractValueDomain, context); ParameterTypeInformation.instanceMember( + AbstractValueDomain abstractValueDomain, MemberTypeInformation context, this._parameter, this._type, @@ -698,7 +719,7 @@ class ParameterTypeInformation extends ElementTypeInformation { : _isInstanceMemberParameter = true, _isClosureParameter = false, _isInitializingFormal = false, - super._withAssignments(context, assignments); + super._withAssignments(abstractValueDomain, context, assignments); FunctionEntity get method => _method; @@ -719,7 +740,7 @@ class ParameterTypeInformation extends ElementTypeInformation { } // TODO(herhut): Cleanup into one conditional. - TypeMask handleSpecialCases(InferrerEngine inferrer) { + AbstractValue handleSpecialCases(InferrerEngine inferrer) { if (!inferrer.canFunctionParametersBeUsedForGlobalOptimizations(_method) || inferrer.assumeDynamic(_method)) { // Do not infer types for parameters that have a corresponding annotation @@ -767,7 +788,8 @@ class ParameterTypeInformation extends ElementTypeInformation { return null; } - TypeMask potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) { + AbstractValue potentiallyNarrowType( + AbstractValue mask, InferrerEngine inferrer) { if (inferrer.options.parameterCheckPolicy.isTrusted || inferrer.trustTypeAnnotations(_method)) { // In checked or strong mode we don't trust the types of the arguments @@ -804,14 +826,14 @@ class ParameterTypeInformation extends ElementTypeInformation { return mask; } - TypeMask computeType(InferrerEngine inferrer) { - TypeMask special = handleSpecialCases(inferrer); + AbstractValue computeType(InferrerEngine inferrer) { + AbstractValue special = handleSpecialCases(inferrer); if (special != null) return special; return potentiallyNarrowType( inferrer.types.computeTypeMask(assignments), inferrer); } - TypeMask safeType(InferrerEngine inferrer) { + AbstractValue safeType(InferrerEngine inferrer) { return potentiallyNarrowType(super.safeType(inferrer), inferrer); } @@ -866,13 +888,20 @@ abstract class CallSiteTypeInformation extends TypeInformation final Object _call; final MemberEntity caller; final Selector selector; - final TypeMask mask; + final AbstractValue mask; final ArgumentsTypes arguments; final bool inLoop; - CallSiteTypeInformation(MemberTypeInformation context, this._call, - this.caller, this.selector, this.mask, this.arguments, this.inLoop) - : super.noAssignments(context) { + CallSiteTypeInformation( + AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, + this._call, + this.caller, + this.selector, + this.mask, + this.arguments, + this.inLoop) + : super.noAssignments(abstractValueDomain.emptyType, context) { assert(_call is ir.Node); } @@ -891,15 +920,17 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation { final MemberEntity calledElement; StaticCallSiteTypeInformation( + AbstractValueDomain abstractValueDomain, MemberTypeInformation context, Object call, MemberEntity enclosing, this.calledElement, Selector selector, - TypeMask mask, + AbstractValue mask, ArgumentsTypes arguments, bool inLoop) - : super(context, call, enclosing, selector, mask, arguments, inLoop); + : super(abstractValueDomain, context, call, enclosing, selector, mask, + arguments, inLoop); MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) { return inferrer.types.getInferredTypeOfMember(calledElement); @@ -929,7 +960,7 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation { return inferrer.typeOfMemberWithSelector(calledElement, selector); } - TypeMask computeType(InferrerEngine inferrer) { + AbstractValue computeType(InferrerEngine inferrer) { if (isSynthesized) { assert(arguments != null); return _getCalledTypeInfo(inferrer).type; @@ -971,23 +1002,25 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { Iterable _concreteTargets; DynamicCallSiteTypeInformation( + AbstractValueDomain abstractValueDomain, MemberTypeInformation context, this._callType, T call, MemberEntity enclosing, Selector selector, - TypeMask mask, + AbstractValue mask, this.receiver, ArgumentsTypes arguments, bool inLoop, this.isConditional) - : super(context, call, enclosing, selector, mask, arguments, inLoop) { + : super(abstractValueDomain, context, call, enclosing, selector, mask, + arguments, inLoop) { assert(validCallType(_callType, _call)); } void addToGraph(InferrerEngine inferrer) { assert(receiver != null); - TypeMask typeMask = computeTypedSelector(inferrer); + AbstractValue typeMask = computeTypedSelector(inferrer); _hasClosureCallTargets = inferrer.closedWorld.includesClosureCall(selector, typeMask); _concreteTargets = inferrer.closedWorld.locateMembers(selector, typeMask); @@ -1016,8 +1049,8 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { Iterable get callees => _concreteTargets; - TypeMask computeTypedSelector(InferrerEngine inferrer) { - TypeMask receiverType = receiver.type; + AbstractValue computeTypedSelector(InferrerEngine inferrer) { + AbstractValue receiverType = receiver.type; if (mask != receiverType) { return receiverType == inferrer.abstractValueDomain.dynamicType @@ -1047,10 +1080,10 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { * code. */ TypeInformation handleIntrisifiedSelector( - Selector selector, TypeMask mask, InferrerEngine inferrer) { + Selector selector, AbstractValue mask, InferrerEngine inferrer) { ClosedWorld closedWorld = inferrer.closedWorld; if (mask == null) return null; - if (!mask.containsOnlyInt(closedWorld)) { + if (!inferrer.abstractValueDomain.isIntegerOrNull(mask)) { return null; } if (!selector.isCall && !selector.isOperator) return null; @@ -1143,26 +1176,28 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { } } - TypeMask computeType(InferrerEngine inferrer) { + AbstractValue computeType(InferrerEngine inferrer) { + ClosedWorld closedWorld = inferrer.closedWorld; + AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; Iterable oldTargets = _concreteTargets; - TypeMask typeMask = computeTypedSelector(inferrer); + AbstractValue typeMask = computeTypedSelector(inferrer); inferrer.updateSelectorInMember( caller, _callType, _call, selector, typeMask); - TypeMask maskToUse = - inferrer.closedWorld.extendMaskIfReachesAll(selector, typeMask); - bool canReachAll = inferrer.closedWorld.backendUsage.isInvokeOnUsed && - (maskToUse != typeMask); + AbstractValue maskToUse = + closedWorld.extendMaskIfReachesAll(selector, typeMask); + bool canReachAll = + closedWorld.backendUsage.isInvokeOnUsed && (maskToUse != typeMask); // If this call could potentially reach all methods that satisfy // the untyped selector (through noSuchMethod's `Invocation` // and a call to `delegate`), we iterate over all these methods to // update their parameter types. _hasClosureCallTargets = - inferrer.closedWorld.includesClosureCall(selector, maskToUse); - _concreteTargets = inferrer.closedWorld.locateMembers(selector, maskToUse); + closedWorld.includesClosureCall(selector, maskToUse); + _concreteTargets = closedWorld.locateMembers(selector, maskToUse); Iterable typedTargets = canReachAll - ? inferrer.closedWorld.locateMembers(selector, typeMask) + ? closedWorld.locateMembers(selector, typeMask) : _concreteTargets; // Update the call graph if the targets could have changed. @@ -1196,9 +1231,9 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { // Walk over the found targets, and compute the joined union type mask // for all these targets. - TypeMask result; + AbstractValue result; if (_hasClosureCallTargets) { - result = inferrer.abstractValueDomain.dynamicType; + result = abstractValueDomain.dynamicType; } else { result = inferrer.types .joinTypeMasks(_concreteTargets.map((MemberEntity element) { @@ -1208,24 +1243,24 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { // `Invocation.delegate`. Note that the `noSuchMethod` targets // are included in [typedTargets]. if (canReachAll && !typedTargets.contains(element)) { - return const TypeMask.nonNullEmpty(); + return abstractValueDomain.emptyType; } if (inferrer.returnsListElementType(selector, typeMask)) { - ContainerTypeMask containerTypeMask = receiver.type; - return containerTypeMask.elementType; + return abstractValueDomain.getContainerElementType(receiver.type); } else if (inferrer.returnsMapValueType(selector, typeMask)) { - if (typeMask.isDictionary) { - TypeMask arg = arguments.positional[0].type; - if (arg is ValueTypeMask && arg.value.isString) { - DictionaryTypeMask dictionaryTypeMask = typeMask; - StringConstantValue value = arg.value; + if (abstractValueDomain.isDictionary(typeMask)) { + AbstractValue arg = arguments.positional[0].type; + ConstantValue value = abstractValueDomain.getPrimitiveValue(arg); + if (value is StringConstantValue) { String key = value.stringValue; - if (dictionaryTypeMask.typeMap.containsKey(key)) { + if (abstractValueDomain.containsDictionaryKey(typeMask, key)) { if (debug.VERBOSE) { print("Dictionary lookup for $key yields " - "${dictionaryTypeMask.typeMap[key]}."); + "${abstractValueDomain. + getDictionaryValueForKey(typeMask, key)}."); } - return dictionaryTypeMask.typeMap[key]; + return abstractValueDomain.getDictionaryValueForKey( + typeMask, key); } else { // The typeMap is precise, so if we do not find the key, the lookup // will be [null] at runtime. @@ -1236,11 +1271,12 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { } } } - MapTypeMask mapTypeMask = typeMask; + assert(abstractValueDomain.isMap(typeMask)); if (debug.VERBOSE) { - print("Map lookup for $selector yields ${mapTypeMask.valueType}."); + print("Map lookup for $selector yields " + "${abstractValueDomain.getMapValueType(typeMask)}."); } - return mapTypeMask.valueType; + return abstractValueDomain.getMapValueType(typeMask); } else { TypeInformation info = handleIntrisifiedSelector(selector, typeMask, inferrer); @@ -1249,10 +1285,10 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { } })); } - if (isConditional && receiver.type.isNullable) { + if (isConditional && abstractValueDomain.canBeNull(receiver.type)) { // Conditional call sites (e.g. `a?.b`) may be null if the receiver is // null. - result = result.nullable(); + result = abstractValueDomain.includeNull(result); } return result; } @@ -1309,22 +1345,24 @@ class ClosureCallSiteTypeInformation extends CallSiteTypeInformation { final TypeInformation closure; ClosureCallSiteTypeInformation( + AbstractValueDomain abstractValueDomain, MemberTypeInformation context, Object call, MemberEntity enclosing, Selector selector, - TypeMask mask, + AbstractValue mask, this.closure, ArgumentsTypes arguments, bool inLoop) - : super(context, call, enclosing, selector, mask, arguments, inLoop); + : super(abstractValueDomain, context, call, enclosing, selector, mask, + arguments, inLoop); void addToGraph(InferrerEngine inferrer) { arguments.forEach((info) => info.addUser(this)); closure.addUser(this); } - TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer); + AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer); Iterable get callees { throw new UnsupportedError("Cannot compute callees of a closure call."); @@ -1358,8 +1396,7 @@ class ClosureCallSiteTypeInformation extends CallSiteTypeInformation { * type. */ class ConcreteTypeInformation extends TypeInformation { - ConcreteTypeInformation(TypeMask type) : super.untracked() { - this.type = type; + ConcreteTypeInformation(AbstractValue type) : super.untracked(type) { this.isStable = true; } @@ -1385,7 +1422,7 @@ class ConcreteTypeInformation extends TypeInformation { throw "Not supported"; } - TypeMask computeType(InferrerEngine inferrer) => type; + AbstractValue computeType(InferrerEngine inferrer) => type; bool reset(InferrerEngine inferrer) { throw "Not supported"; @@ -1403,8 +1440,10 @@ class ConcreteTypeInformation extends TypeInformation { class StringLiteralTypeInformation extends ConcreteTypeInformation { final String value; - StringLiteralTypeInformation(this.value, TypeMask mask) - : super(new ValueTypeMask(mask, new StringConstantValue(value))); + StringLiteralTypeInformation( + AbstractValueDomain abstractValueDomain, this.value, AbstractValue mask) + : super(abstractValueDomain.createPrimitiveValue( + mask, new StringConstantValue(value))); String asString() => value; String toString() => 'Type $type value ${value}'; @@ -1417,8 +1456,9 @@ class StringLiteralTypeInformation extends ConcreteTypeInformation { class BoolLiteralTypeInformation extends ConcreteTypeInformation { final bool value; - BoolLiteralTypeInformation(this.value, TypeMask mask) - : super(new ValueTypeMask( + BoolLiteralTypeInformation( + AbstractValueDomain abstractValueDomain, this.value, AbstractValue mask) + : super(abstractValueDomain.createPrimitiveValue( mask, value ? new TrueConstantValue() : new FalseConstantValue())); String toString() => 'Type $type value ${value}'; @@ -1448,10 +1488,11 @@ class BoolLiteralTypeInformation extends ConcreteTypeInformation { * information on the type of a local. */ class NarrowTypeInformation extends TypeInformation { - final TypeMask typeAnnotation; + final AbstractValue typeAnnotation; - NarrowTypeInformation(TypeInformation narrowedType, this.typeAnnotation) - : super(narrowedType.context) { + NarrowTypeInformation(AbstractValueDomain abstractValueDomain, + TypeInformation narrowedType, this.typeAnnotation) + : super(abstractValueDomain.emptyType, narrowedType.context) { addAssignment(narrowedType); } @@ -1460,13 +1501,14 @@ class NarrowTypeInformation extends TypeInformation { assert(assignments.length == 1); } - TypeMask computeType(InferrerEngine inferrer) { - TypeMask input = assignments.first.type; - TypeMask intersection = - input.intersection(typeAnnotation, inferrer.closedWorld); + AbstractValue computeType(InferrerEngine inferrer) { + AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain; + AbstractValue input = assignments.first.type; + AbstractValue intersection = + abstractValueDomain.intersection(input, typeAnnotation); if (debug.ANOMALY_WARN) { - if (!input.containsMask(intersection, inferrer.closedWorld) || - !typeAnnotation.containsMask(intersection, inferrer.closedWorld)) { + if (!abstractValueDomain.contains(input, intersection) || + !abstractValueDomain.contains(typeAnnotation, intersection)) { print("ANOMALY WARNING: narrowed $input to $intersection via " "$typeAnnotation"); } @@ -1493,13 +1535,13 @@ abstract class InferredTypeInformation extends TypeInformation { /** Whether the element type in that container has been inferred. */ bool inferred = false; - InferredTypeInformation( + InferredTypeInformation(AbstractValueDomain abstractValueDomain, MemberTypeInformation context, TypeInformation parentType) - : super(context) { + : super(abstractValueDomain.emptyType, context) { if (parentType != null) addAssignment(parentType); } - TypeMask computeType(InferrerEngine inferrer) { + AbstractValue computeType(InferrerEngine inferrer) { if (!inferred) return safeType(inferrer); return inferrer.types.computeTypeMask(assignments); } @@ -1517,7 +1559,7 @@ class ListTypeInformation extends TypeInformation with TracedTypeInformation { final ElementInContainerTypeInformation elementType; /** The container type before it is inferred. */ - final ContainerTypeMask originalType; + final AbstractValue originalType; /** The length at the allocation site. */ final int originalLength; @@ -1531,11 +1573,14 @@ class ListTypeInformation extends TypeInformation with TracedTypeInformation { */ bool checksGrowable = true; - ListTypeInformation(MemberTypeInformation context, this.originalType, - this.elementType, this.originalLength) - : super(context) { - type = originalType; - inferredLength = originalType.length; + ListTypeInformation( + AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, + this.originalType, + this.elementType, + this.originalLength) + : super(originalType, context) { + inferredLength = abstractValueDomain.getContainerLength(originalType); elementType.addUser(this); } @@ -1549,22 +1594,23 @@ class ListTypeInformation extends TypeInformation with TracedTypeInformation { return elementType.isStable && super.hasStableType(inferrer); } - TypeMask computeType(InferrerEngine inferrer) { - dynamic mask = type; - if (!mask.isContainer || - mask.elementType != elementType.type || - mask.length != inferredLength) { - return new ContainerTypeMask( - originalType.forwardTo, - originalType.allocationNode, - originalType.allocationElement, + AbstractValue computeType(InferrerEngine inferrer) { + AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain; + AbstractValue mask = type; + if (!abstractValueDomain.isContainer(type) || + abstractValueDomain.getContainerElementType(type) != elementType.type || + abstractValueDomain.getContainerLength(type) != inferredLength) { + return abstractValueDomain.createContainerValue( + abstractValueDomain.getGeneralization(originalType), + abstractValueDomain.getAllocationNode(originalType), + abstractValueDomain.getAllocationElement(originalType), elementType.type, inferredLength); } return mask; } - TypeMask safeType(InferrerEngine inferrer) => originalType; + AbstractValue safeType(InferrerEngine inferrer) => originalType; void cleanup() { super.cleanup(); @@ -1578,8 +1624,9 @@ class ListTypeInformation extends TypeInformation with TracedTypeInformation { * elements in a [ListTypeInformation]. */ class ElementInContainerTypeInformation extends InferredTypeInformation { - ElementInContainerTypeInformation(MemberTypeInformation context, elementType) - : super(context, elementType); + ElementInContainerTypeInformation(AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, elementType) + : super(abstractValueDomain, context, elementType); String toString() => 'Element in container $type'; @@ -1599,7 +1646,7 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { // These fields track the overall type of the keys/values in the map. final KeyInMapTypeInformation keyType; final ValueInMapTypeInformation valueType; - final MapTypeMask originalType; + final AbstractValue originalType; // Set to false if a statically unknown key flows into this map. bool _allKeysAreStrings = true; @@ -1608,19 +1655,20 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { MapTypeInformation(MemberTypeInformation context, this.originalType, this.keyType, this.valueType) - : super(context) { + : super(originalType, context) { keyType.addUser(this); valueType.addUser(this); - type = originalType; } - TypeInformation addEntryAssignment(TypeInformation key, TypeInformation value, + TypeInformation addEntryAssignment(AbstractValueDomain abstractValueDomain, + TypeInformation key, TypeInformation value, [bool nonNull = false]) { TypeInformation newInfo = null; if (_allKeysAreStrings && key is StringLiteralTypeInformation) { String keyString = key.asString(); typeInfoMap.putIfAbsent(keyString, () { - newInfo = new ValueInMapTypeInformation(context, null, nonNull); + newInfo = new ValueInMapTypeInformation( + abstractValueDomain, context, null, nonNull); return newInfo; }); typeInfoMap[keyString].addAssignment(value); @@ -1635,13 +1683,14 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { return newInfo; } - List addMapAssignment(MapTypeInformation other) { + List addMapAssignment( + AbstractValueDomain abstractValueDomain, MapTypeInformation other) { List newInfos = []; if (_allKeysAreStrings && other.inDictionaryMode) { other.typeInfoMap.forEach((keyString, value) { typeInfoMap.putIfAbsent(keyString, () { - TypeInformation newInfo = - new ValueInMapTypeInformation(context, null, false); + TypeInformation newInfo = new ValueInMapTypeInformation( + abstractValueDomain, context, null, false); newInfos.add(newInfo); return newInfo; }); @@ -1670,49 +1719,51 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { return visitor.visitMapTypeInformation(this); } - TypeMask toTypeMask(InferrerEngine inferrer) { + AbstractValue toTypeMask(InferrerEngine inferrer) { + AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain; if (inDictionaryMode) { - Map mappings = new Map(); + Map mappings = new Map(); for (var key in typeInfoMap.keys) { mappings[key] = typeInfoMap[key].type; } - return new DictionaryTypeMask( - originalType.forwardTo, - originalType.allocationNode, - originalType.allocationElement, + return inferrer.abstractValueDomain.createDictionaryValue( + abstractValueDomain.getGeneralization(originalType), + abstractValueDomain.getAllocationNode(originalType), + abstractValueDomain.getAllocationElement(originalType), keyType.type, valueType.type, mappings); } else { - return new MapTypeMask( - originalType.forwardTo, - originalType.allocationNode, - originalType.allocationElement, + return inferrer.abstractValueDomain.createMapValue( + abstractValueDomain.getGeneralization(originalType), + abstractValueDomain.getAllocationNode(originalType), + abstractValueDomain.getAllocationElement(originalType), keyType.type, valueType.type); } } - TypeMask computeType(InferrerEngine inferrer) { - if (type.isDictionary != inDictionaryMode) { + AbstractValue computeType(InferrerEngine inferrer) { + AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain; + if (abstractValueDomain.isDictionary(type) != inDictionaryMode) { return toTypeMask(inferrer); - } else if (type.isDictionary) { + } else if (abstractValueDomain.isDictionary(type)) { assert(inDictionaryMode); - DictionaryTypeMask mask = type; - for (var key in typeInfoMap.keys) { + for (String key in typeInfoMap.keys) { TypeInformation value = typeInfoMap[key]; - if (!mask.typeMap.containsKey(key) && - !value.type.containsAll(inferrer.closedWorld) && - !value.type.isNullable) { + if (!abstractValueDomain.containsDictionaryKey(type, key) && + !abstractValueDomain.containsAll(value.type) && + !abstractValueDomain.canBeNull(value.type)) { return toTypeMask(inferrer); } - if (mask.typeMap[key] != typeInfoMap[key].type) { + if (abstractValueDomain.getDictionaryValueForKey(type, key) != + typeInfoMap[key].type) { return toTypeMask(inferrer); } } - } else if (type.isMap) { - MapTypeMask mask = type; - if (mask.keyType != keyType.type || mask.valueType != valueType.type) { + } else if (abstractValueDomain.isMap(type)) { + if (abstractValueDomain.getMapKeyType(type) != keyType.type || + abstractValueDomain.getMapValueType(type) != valueType.type) { return toTypeMask(inferrer); } } else { @@ -1722,7 +1773,7 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { return type; } - TypeMask safeType(InferrerEngine inferrer) => originalType; + AbstractValue safeType(InferrerEngine inferrer) => originalType; bool hasStableType(InferrerEngine inferrer) { return keyType.isStable && @@ -1750,9 +1801,9 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { * for the keys in a [MapTypeInformation] */ class KeyInMapTypeInformation extends InferredTypeInformation { - KeyInMapTypeInformation( + KeyInMapTypeInformation(AbstractValueDomain abstractValueDomain, MemberTypeInformation context, TypeInformation keyType) - : super(context, keyType); + : super(abstractValueDomain, context, keyType); accept(TypeInformationVisitor visitor) { return visitor.visitKeyInMapTypeInformation(this); @@ -1771,19 +1822,19 @@ class ValueInMapTypeInformation extends InferredTypeInformation { // mode can ever be marked as [nonNull]. final bool nonNull; - ValueInMapTypeInformation( + ValueInMapTypeInformation(AbstractValueDomain abstractValueDomain, MemberTypeInformation context, TypeInformation valueType, [this.nonNull = false]) - : super(context, valueType); + : super(abstractValueDomain, context, valueType); accept(TypeInformationVisitor visitor) { return visitor.visitValueInMapTypeInformation(this); } - TypeMask computeType(InferrerEngine inferrer) { + AbstractValue computeType(InferrerEngine inferrer) { return nonNull ? super.computeType(inferrer) - : super.computeType(inferrer).nullable(); + : inferrer.abstractValueDomain.includeNull(super.computeType(inferrer)); } String toString() => 'Value in Map $type'; @@ -1798,12 +1849,12 @@ class PhiElementTypeInformation extends TypeInformation { final Local variable; final bool isTry; - PhiElementTypeInformation( + PhiElementTypeInformation(AbstractValueDomain abstractValueDomain, MemberTypeInformation context, this.branchNode, this.variable, {this.isTry}) - : super(context); + : super(abstractValueDomain.emptyType, context); - TypeMask computeType(InferrerEngine inferrer) { + AbstractValue computeType(InferrerEngine inferrer) { return inferrer.types.computeTypeMask(assignments); } @@ -1835,14 +1886,15 @@ class ClosureTypeInformation extends TypeInformation with ApplyableTypeInformation { final FunctionEntity _element; - ClosureTypeInformation(MemberTypeInformation context, this._element) - : super(context); + ClosureTypeInformation(AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, this._element) + : super(abstractValueDomain.emptyType, context); FunctionEntity get closure => _element; - TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer); + AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer); - TypeMask safeType(InferrerEngine inferrer) { + AbstractValue safeType(InferrerEngine inferrer) { return inferrer.types.functionType.type; } @@ -1900,11 +1952,12 @@ abstract class TracedTypeInformation implements TypeInformation { class AwaitTypeInformation extends TypeInformation { final T _node; - AwaitTypeInformation(MemberTypeInformation context, this._node) - : super(context); + AwaitTypeInformation(AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, this._node) + : super(abstractValueDomain.emptyType, context); // TODO(22894): Compute a better type here. - TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer); + AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer); String get debugName => '$_node'; @@ -1918,10 +1971,11 @@ class AwaitTypeInformation extends TypeInformation { class YieldTypeInformation extends TypeInformation { final T _node; - YieldTypeInformation(MemberTypeInformation context, this._node) - : super(context); + YieldTypeInformation(AbstractValueDomain abstractValueDomain, + MemberTypeInformation context, this._node) + : super(abstractValueDomain.emptyType, context); - TypeMask computeType(InferrerEngine inferrer) => safeType(inferrer); + AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer); String get debugName => '$_node'; @@ -1954,10 +2008,11 @@ abstract class TypeInformationVisitor { T visitYieldTypeInformation(YieldTypeInformation info); } -TypeMask _narrowType( - ClosedWorld closedWorld, TypeMask type, DartType annotation, +AbstractValue _narrowType( + ClosedWorld closedWorld, AbstractValue type, DartType annotation, {bool isNullable: true}) { - TypeMask otherType; + AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; + AbstractValue otherType; if (annotation.treatAsDynamic) { return type; } else if (annotation.isInterfaceType) { @@ -1965,7 +2020,7 @@ TypeMask _narrowType( if (interfaceType.element == closedWorld.commonElements.objectClass) { return type; } - otherType = new TypeMask.nonNullSubtype(interfaceType.element, closedWorld); + otherType = abstractValueDomain.createNonNullSubtype(interfaceType.element); } else if (annotation.isVoid) { return type; } else if (annotation.isTypedef || annotation.isFunctionType) { @@ -1978,7 +2033,11 @@ TypeMask _narrowType( // TODO(ngeoffray): Narrow to bound. return type; } - if (isNullable) otherType = otherType.nullable(); - if (type == null) return otherType; - return type.intersection(otherType, closedWorld); + if (isNullable) { + otherType = abstractValueDomain.includeNull(otherType); + } + if (type == null) { + return otherType; + } + return abstractValueDomain.intersection(type, otherType); } diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart index eb3bc06aa81d..672ebbe11a94 100644 --- a/pkg/compiler/lib/src/inferrer/type_system.dart +++ b/pkg/compiler/lib/src/inferrer/type_system.dart @@ -6,7 +6,6 @@ import '../common.dart'; import '../elements/entities.dart'; import '../elements/types.dart'; import '../types/abstract_value_domain.dart'; -import '../types/masks.dart' show ContainerTypeMask, MapTypeMask; import '../universe/selector.dart'; import '../world.dart'; import 'type_graph_nodes.dart'; @@ -15,11 +14,14 @@ import 'type_graph_nodes.dart'; /// information for nodes. abstract class TypeSystemStrategy { /// Creates [MemberTypeInformation] for [member]. - MemberTypeInformation createMemberTypeInformation(MemberEntity member); + MemberTypeInformation createMemberTypeInformation( + AbstractValueDomain abstractValueDomain, MemberEntity member); /// Creates [ParameterTypeInformation] for [parameter]. ParameterTypeInformation createParameterTypeInformation( - Local parameter, TypeSystem types); + AbstractValueDomain abstractValueDomain, + Local parameter, + TypeSystem types); /// Calls [f] for each parameter in [function]. void forEachParameter(FunctionEntity function, void f(Local parameter)); @@ -261,11 +263,12 @@ class TypeSystem { TypeInformation stringLiteralType(String value) { return new StringLiteralTypeInformation( - value, _abstractValueDomain.stringType); + _abstractValueDomain, value, _abstractValueDomain.stringType); } TypeInformation boolLiteralType(bool value) { - return new BoolLiteralTypeInformation(value, _abstractValueDomain.boolType); + return new BoolLiteralTypeInformation( + _abstractValueDomain, value, _abstractValueDomain.boolType); } /** @@ -282,7 +285,7 @@ class TypeSystem { return dynamicType; } return getConcreteTypeFor( - firstType.type.union(secondType.type, _closedWorld)); + _abstractValueDomain.union(firstType.type, secondType.type)); } /** @@ -304,7 +307,7 @@ class TypeSystem { TypeInformation refineReceiver( Selector selector, AbstractValue mask, TypeInformation receiver, {bool isConditional}) { - if (receiver.type.isExact) return receiver; + if (_abstractValueDomain.isExact(receiver.type)) return receiver; AbstractValue otherType = _closedWorld.computeReceiverType(selector, mask); // Conditional sends (a?.b) can still narrow the possible types of `a`, // however, we still need to consider that `a` may be null. @@ -319,7 +322,8 @@ class TypeSystem { _abstractValueDomain.containsAll(otherType)) { return receiver; } - TypeInformation newType = new NarrowTypeInformation(receiver, otherType); + TypeInformation newType = + new NarrowTypeInformation(_abstractValueDomain, receiver, otherType); allocatedTypes.add(newType); return newType; } @@ -340,7 +344,7 @@ class TypeSystem { if (isNullable) { return type; } - otherType = dynamicType.type.nonNullable(); + otherType = _abstractValueDomain.excludeNull(dynamicType.type); } else { otherType = _abstractValueDomain.createNonNullSubtype(interface.element); @@ -358,10 +362,11 @@ class TypeSystem { if (isNullable) { otherType = _abstractValueDomain.includeNull(otherType); } - if (type.type.isExact) { + if (_abstractValueDomain.isExact(type.type)) { return type; } else { - TypeInformation newType = new NarrowTypeInformation(type, otherType); + TypeInformation newType = + new NarrowTypeInformation(_abstractValueDomain, type, otherType); allocatedTypes.add(newType); return newType; } @@ -371,11 +376,11 @@ class TypeSystem { * Returns the non-nullable type of [type]. */ TypeInformation narrowNotNull(TypeInformation type) { - if (type.type.isExact && !type.type.isNullable) { + if (_abstractValueDomain.isExact(type.type)) { return type; } - TypeInformation newType = - new NarrowTypeInformation(type, dynamicType.type.nonNullable()); + TypeInformation newType = new NarrowTypeInformation(_abstractValueDomain, + type, _abstractValueDomain.excludeNull(dynamicType.type)); allocatedTypes.add(newType); return newType; } @@ -383,7 +388,8 @@ class TypeSystem { ParameterTypeInformation getInferredTypeOfParameter(Local parameter) { return parameterTypeInformations.putIfAbsent(parameter, () { ParameterTypeInformation typeInformation = - strategy.createParameterTypeInformation(parameter, this); + strategy.createParameterTypeInformation( + _abstractValueDomain, parameter, this); _orderedTypeInformations.add(typeInformation); return typeInformation; }); @@ -394,7 +400,7 @@ class TypeSystem { failedAt(member, "Unexpected abstract member $member.")); return memberTypeInformations.putIfAbsent(member, () { MemberTypeInformation typeInformation = - strategy.createMemberTypeInformation(member); + strategy.createMemberTypeInformation(_abstractValueDomain, member); _orderedTypeInformations.add(typeInformation); return typeInformation; }); @@ -451,7 +457,7 @@ class TypeSystem { ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass; bool isTypedArray = typedDataClass != null && _closedWorld.isInstantiated(typedDataClass) && - type.type.satisfies(typedDataClass, _closedWorld); + _abstractValueDomain.isInstanceOfOrNull(type.type, typedDataClass); bool isConst = (type.type == _abstractValueDomain.constListType); bool isFixed = (type.type == _abstractValueDomain.fixedListType) || isConst || @@ -461,22 +467,24 @@ class TypeSystem { int inferredLength = isFixed ? length : null; AbstractValue elementTypeMask = isElementInferred ? elementType.type : dynamicType.type; - ContainerTypeMask mask = new ContainerTypeMask( + AbstractValue mask = _abstractValueDomain.createContainerValue( type.type, node, enclosing, elementTypeMask, inferredLength); ElementInContainerTypeInformation element = - new ElementInContainerTypeInformation(currentMember, elementType); + new ElementInContainerTypeInformation( + _abstractValueDomain, currentMember, elementType); element.inferred = isElementInferred; allocatedTypes.add(element); - return allocatedLists[node] = - new ListTypeInformation(currentMember, mask, element, length); + return allocatedLists[node] = new ListTypeInformation( + _abstractValueDomain, currentMember, mask, element, length); } /// Creates a [TypeInformation] object either for the closurization of a /// static or top-level method [element] used as a function constant or for /// the synthesized 'call' method [element] created for a local function. TypeInformation allocateClosure(FunctionEntity element) { - TypeInformation result = new ClosureTypeInformation(currentMember, element); + TypeInformation result = new ClosureTypeInformation( + _abstractValueDomain, currentMember, element); allocatedClosures.add(result); return result; } @@ -497,13 +505,13 @@ class TypeSystem { } else { keyType = valueType = dynamicType.type; } - MapTypeMask mask = - new MapTypeMask(type.type, node, element, keyType, valueType); + AbstractValue mask = _abstractValueDomain.createMapValue( + type.type, node, element, keyType, valueType); TypeInformation keyTypeInfo = - new KeyInMapTypeInformation(currentMember, null); - TypeInformation valueTypeInfo = - new ValueInMapTypeInformation(currentMember, null); + new KeyInMapTypeInformation(_abstractValueDomain, currentMember, null); + TypeInformation valueTypeInfo = new ValueInMapTypeInformation( + _abstractValueDomain, currentMember, null); allocatedTypes.add(keyTypeInfo); allocatedTypes.add(valueTypeInfo); @@ -511,8 +519,8 @@ class TypeSystem { new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo); for (int i = 0; i < keyTypes.length; ++i) { - TypeInformation newType = - map.addEntryAssignment(keyTypes[i], valueTypes[i], true); + TypeInformation newType = map.addEntryAssignment( + _abstractValueDomain, keyTypes[i], valueTypes[i], true); if (newType != null) allocatedTypes.add(newType); } @@ -537,7 +545,7 @@ class TypeSystem { TypeInformation allocateDiamondPhi( TypeInformation firstInput, TypeInformation secondInput) { PhiElementTypeInformation result = new PhiElementTypeInformation( - currentMember, null, null, + _abstractValueDomain, currentMember, null, null, isTry: false); result.addAssignment(firstInput); result.addAssignment(secondInput); @@ -548,7 +556,7 @@ class TypeSystem { PhiElementTypeInformation _addPhi( T node, Local variable, TypeInformation inputType, bool isTry) { PhiElementTypeInformation result = new PhiElementTypeInformation( - currentMember, node, variable, + _abstractValueDomain, currentMember, node, variable, isTry: isTry); allocatedTypes.add(result); result.addAssignment(inputType); diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index 67bd1e0bbdde..c9e36476b065 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -1010,7 +1010,8 @@ abstract class HInstruction implements Spannable { /// Does this node potentially affect control flow. bool isControlFlow() => false; - bool isExact(AbstractValueDomain domain) => domain.isExact(instructionType); + bool isExact(AbstractValueDomain domain) => + domain.isExactOrNull(instructionType); bool isValue(AbstractValueDomain domain) => domain.isPrimitiveValue(instructionType); diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart index 95c7a9027caf..2534ec895251 100644 --- a/pkg/compiler/lib/src/types/abstract_value_domain.dart +++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart @@ -4,7 +4,7 @@ library dart2js.abstract_value_domain; -import '../constants/values.dart' show ConstantValue; +import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue; import '../elements/entities.dart'; import '../universe/selector.dart'; @@ -155,16 +155,16 @@ abstract class AbstractValueDomain { /// Returns `true` if [value] is empty set of runtime values. bool isEmpty(covariant AbstractValue value); - /// Returns `true` if [value] is an exact class or `null` at runtime. + /// Returns `true` if [value] is a non-null exact class at runtime. bool isExact(covariant AbstractValue value); + /// Returns `true` if [value] is an exact class or `null` at runtime. + bool isExactOrNull(covariant AbstractValue value); + /// Returns the [ClassEntity] if this [value] is a non-null instance of an /// exact class at runtime, and `null` otherwise. ClassEntity getExactClass(covariant AbstractValue value); - /// Returns `true` if [value] a known primitive JavaScript value at runtime. - bool isPrimitiveValue(covariant AbstractValue value); - /// Returns `true` if [value] can be `null` at runtime. bool canBeNull(covariant AbstractValue value); @@ -292,21 +292,116 @@ abstract class AbstractValueDomain { /// Returns `true` if [value] represents a container value at runtime. bool isContainer(covariant AbstractValue value); + /// Creates a container value specialization of [originalValue] with the + /// inferred [element] runtime value and inferred runtime [length]. + /// + /// The [allocationNode] is used to identify this particular map allocation. + /// The [allocationElement] is used only for debugging. + AbstractValue createContainerValue( + AbstractValue originalValue, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue elementType, + int length); + /// Returns the element type of [value] if it represents a container value /// at runtime. Returns [dynamicType] otherwise. AbstractValue getContainerElementType(AbstractValue value); + /// Return the known length of [value] if it represents a container value + /// at runtime. Returns `null` if the length is unknown or if [value] doesn't + /// represent a container value at runtime. + int getContainerLength(AbstractValue value); + /// Returns `true` if [value] represents a map value at runtime. bool isMap(covariant AbstractValue value); + /// Creates a map value specialization of [originalValue] with the inferred + /// [key] and [value] runtime values. + /// + /// The [allocationNode] is used to identify this particular map allocation. + /// The [allocationElement] is used only for debugging. + AbstractValue createMapValue( + AbstractValue originalValue, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue key, + AbstractValue value); + + /// Returns the key type of [value] if it represents a map value at runtime. + /// Returns [dynamicType] otherwise. + AbstractValue getMapKeyType(AbstractValue value); + /// Returns the value type of [value] if it represents a map value at runtime. /// Returns [dynamicType] otherwise. AbstractValue getMapValueType(AbstractValue value); + /// Returns `true` if [value] represents a dictionary value, that is, a map + /// with strings as keys, at runtime. + bool isDictionary(covariant AbstractValue value); + + /// Creates a dictionary value specialization of [originalValue] with the + /// inferred [key] and [value] runtime values. + /// + /// The [allocationNode] is used to identify this particular map allocation. + /// The [allocationElement] is used only for debugging. + AbstractValue createDictionaryValue( + AbstractValue originalValue, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue key, + AbstractValue value, + Map mappings); + + /// Returns `true` if [value] is a dictionary value which contains [key] as + /// a key. + bool containsDictionaryKey(AbstractValue value, String key); + + /// Returns the value type for [key] in [value] if it represents a dictionary + /// value at runtime. Returns [dynamicType] otherwise. + AbstractValue getDictionaryValueForKey(AbstractValue value, String key); + + /// Returns `true` if [specialization] is a specialization of + /// [generalization]. + /// + /// Specializations are created through [createPrimitiveValue], + /// [createMapValue], [createDictionaryValue] and [createContainerValue]. + bool isSpecializationOf( + AbstractValue specialization, AbstractValue generalization); + + /// Returns the value of which [value] is a specialization. Return `null` if + /// [value] is not a specialization. + /// + /// Specializations are created through [createPrimitiveValue], + /// [createMapValue], [createDictionaryValue] and [createContainerValue]. + AbstractValue getGeneralization(AbstractValue value); + + /// Return the object identifying the allocation of [value] if it is an + /// allocation based specialization. Otherwise returns `null`. + /// + /// Allocation based specializations are created through [createMapValue], + /// [createDictionaryValue] and [createContainerValue] + Object getAllocationNode(AbstractValue value); + + /// Return the allocation element of [value] if it is an allocation based + /// specialization. Otherwise returns `null`. + /// + /// Allocation based specializations are created through [createMapValue], + /// [createDictionaryValue] and [createContainerValue] + MemberEntity getAllocationElement(AbstractValue value); + + /// Returns `true` if [value] a known primitive JavaScript value at runtime. + bool isPrimitiveValue(covariant AbstractValue value); + + /// Creates a primitive value specialization of [originalValue] with the + /// inferred primitive constant [value]. + AbstractValue createPrimitiveValue( + AbstractValue originalValue, PrimitiveConstantValue value); + /// Returns the primitive JavaScript value of [value] if it represents a /// primitive JavaScript value at runtime, value at runtime. Returns `null` /// otherwise. - ConstantValue getPrimitiveValue(covariant AbstractValue value); + PrimitiveConstantValue getPrimitiveValue(covariant AbstractValue value); /// Compute the type of all potential receivers of the set of live [members]. AbstractValue computeReceiver(Iterable members); @@ -346,4 +441,7 @@ abstract class AbstractValueDomain { /// Returns `true` if [value] is an JavaScript indexable of fixed length. bool isFixedLengthJsIndexable(AbstractValue value); + + /// Returns compact a textual representation for [value] used for debugging. + String getCompactText(AbstractValue value); } diff --git a/pkg/compiler/lib/src/types/container_type_mask.dart b/pkg/compiler/lib/src/types/container_type_mask.dart index fbbd59f3b334..f389c6650b6c 100644 --- a/pkg/compiler/lib/src/types/container_type_mask.dart +++ b/pkg/compiler/lib/src/types/container_type_mask.dart @@ -7,7 +7,7 @@ part of masks; /// A [ContainerTypeMask] is a [TypeMask] for a specific allocation /// site of a container (currently only List) that will get specialized /// once the [TypeGraphInferrer] phase finds an element type for it. -class ContainerTypeMask extends ForwardingTypeMask { +class ContainerTypeMask extends AllocationTypeMask { final TypeMask forwardTo; // The [Node] where this type mask was created. diff --git a/pkg/compiler/lib/src/types/dictionary_type_mask.dart b/pkg/compiler/lib/src/types/dictionary_type_mask.dart index 3d92fcc923d3..3a620c4148d7 100644 --- a/pkg/compiler/lib/src/types/dictionary_type_mask.dart +++ b/pkg/compiler/lib/src/types/dictionary_type_mask.dart @@ -14,7 +14,7 @@ part of masks; */ class DictionaryTypeMask extends MapTypeMask { // The underlying key/value map of this dictionary. - final Map typeMap; + final Map _typeMap; DictionaryTypeMask( TypeMask forwardTo, @@ -22,34 +22,38 @@ class DictionaryTypeMask extends MapTypeMask { MemberEntity allocationElement, TypeMask keyType, TypeMask valueType, - this.typeMap) + this._typeMap) : super(forwardTo, allocationNode, allocationElement, keyType, valueType); TypeMask nullable() { return isNullable ? this : new DictionaryTypeMask(forwardTo.nullable(), allocationNode, - allocationElement, keyType, valueType, typeMap); + allocationElement, keyType, valueType, _typeMap); } TypeMask nonNullable() { return isNullable ? new DictionaryTypeMask(forwardTo.nonNullable(), allocationNode, - allocationElement, keyType, valueType, typeMap) + allocationElement, keyType, valueType, _typeMap) : this; } bool get isDictionary => true; bool get isExact => true; + bool containsKey(String key) => _typeMap.containsKey(key); + + TypeMask getValueForKey(String key) => _typeMap[key]; + bool equalsDisregardNull(other) { if (other is! DictionaryTypeMask) return false; return allocationNode == other.allocationNode && keyType == other.keyType && valueType == other.valueType && - typeMap.keys.every((k) => other.typeMap.containsKey(k)) && - other.typeMap.keys.every( - (k) => typeMap.containsKey(k) && typeMap[k] == other.typeMap[k]); + _typeMap.keys.every((k) => other._typeMap.containsKey(k)) && + other._typeMap.keys.every( + (k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]); } TypeMask intersection(TypeMask other, ClosedWorld closedWorld) { @@ -70,14 +74,14 @@ class DictionaryTypeMask extends MapTypeMask { TypeMask newKeyType = keyType.union(other.keyType, closedWorld); TypeMask newValueType = valueType.union(other.valueType, closedWorld); Map mappings = {}; - typeMap.forEach((k, v) { - if (!other.typeMap.containsKey(k)) { + _typeMap.forEach((k, dynamic v) { + if (!other._typeMap.containsKey(k)) { mappings[k] = v.nullable(); } }); - other.typeMap.forEach((k, v) { - if (typeMap.containsKey(k)) { - mappings[k] = v.union(typeMap[k], closedWorld); + other._typeMap.forEach((k, v) { + if (_typeMap.containsKey(k)) { + mappings[k] = v.union(_typeMap[k], closedWorld); } else { mappings[k] = v.nullable(); } @@ -100,11 +104,11 @@ class DictionaryTypeMask extends MapTypeMask { bool operator ==(other) => super == other; int get hashCode { - return computeHashCode(allocationNode, isNullable, typeMap, forwardTo); + return computeHashCode(allocationNode, isNullable, _typeMap, forwardTo); } String toString() { return 'Dictionary($forwardTo, key: $keyType, ' - 'value: $valueType, map: $typeMap)'; + 'value: $valueType, map: $_typeMap)'; } } diff --git a/pkg/compiler/lib/src/types/forwarding_type_mask.dart b/pkg/compiler/lib/src/types/forwarding_type_mask.dart index 0bfed426fa15..a24095d8924d 100644 --- a/pkg/compiler/lib/src/types/forwarding_type_mask.dart +++ b/pkg/compiler/lib/src/types/forwarding_type_mask.dart @@ -122,3 +122,11 @@ abstract class ForwardingTypeMask implements TypeMask { int get hashCode => throw "Subclass should implement hashCode getter"; } + +abstract class AllocationTypeMask extends ForwardingTypeMask { + // The [Node] where this type mask was created. + T get allocationNode; + + // The [Entity] where this type mask was created. + MemberEntity get allocationElement; +} diff --git a/pkg/compiler/lib/src/types/map_type_mask.dart b/pkg/compiler/lib/src/types/map_type_mask.dart index b1ded1d0e19d..7c47b4c73ef9 100644 --- a/pkg/compiler/lib/src/types/map_type_mask.dart +++ b/pkg/compiler/lib/src/types/map_type_mask.dart @@ -9,7 +9,7 @@ part of masks; * site of a map (currently only internal Map class) that will get specialized * once the [TypeGraphInferrer] phase finds a key and/or value type for it. */ -class MapTypeMask extends ForwardingTypeMask { +class MapTypeMask extends AllocationTypeMask { final TypeMask forwardTo; // The [Node] where this type mask was created. diff --git a/pkg/compiler/lib/src/types/masks.dart b/pkg/compiler/lib/src/types/masks.dart index 9959309fc20a..a7da7b110abe 100644 --- a/pkg/compiler/lib/src/types/masks.dart +++ b/pkg/compiler/lib/src/types/masks.dart @@ -287,7 +287,10 @@ class CommonMasks implements AbstractValueDomain { bool isEmpty(TypeMask value) => value.isEmpty; @override - bool isExact(TypeMask value) => value.isExact || isNull(value); + bool isExact(TypeMask value) => value.isExact && !value.isNullable; + + @override + bool isExactOrNull(TypeMask value) => value.isExact || isNull(value); @override ClassEntity getExactClass(TypeMask mask) { @@ -298,7 +301,7 @@ class CommonMasks implements AbstractValueDomain { bool isPrimitiveValue(TypeMask value) => value.isValue; @override - ConstantValue getPrimitiveValue(TypeMask mask) { + PrimitiveConstantValue getPrimitiveValue(TypeMask mask) { if (mask.isValue) { ValueTypeMask valueMask = mask; return valueMask.value; @@ -306,6 +309,12 @@ class CommonMasks implements AbstractValueDomain { return null; } + @override + AbstractValue createPrimitiveValue( + covariant TypeMask originalValue, PrimitiveConstantValue value) { + return new ValueTypeMask(originalValue, value); + } + @override bool canBeNull(TypeMask value) => value.isNullable; @@ -488,9 +497,18 @@ class CommonMasks implements AbstractValueDomain { return computeTypeMask(_closedWorld, value); } + @override + AbstractValue getMapKeyType(AbstractValue value) { + if (value is MapTypeMask) { + return value.keyType; + } + return dynamicType; + } + @override AbstractValue getMapValueType(AbstractValue value) { if (value is MapTypeMask) { + // TODO(johnniwinther): Assert the `value.valueType` is not null. return value.valueType ?? dynamicType; } return dynamicType; @@ -504,6 +522,22 @@ class CommonMasks implements AbstractValueDomain { return dynamicType; } + @override + int getContainerLength(AbstractValue value) { + return value is ContainerTypeMask ? value.length : null; + } + + @override + AbstractValue createContainerValue( + AbstractValue forwardTo, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue elementType, + int length) { + return new ContainerTypeMask( + forwardTo, allocationNode, allocationElement, elementType, length); + } + @override AbstractValue unionOfMany(Iterable values) { TypeMask result = const TypeMask.nonNullEmpty(); @@ -609,4 +643,113 @@ class CommonMasks implements AbstractValueDomain { bool isContainer(TypeMask value) { return value.isContainer; } + + @override + bool isDictionary(TypeMask value) { + return value.isDictionary; + } + + @override + bool containsDictionaryKey(AbstractValue value, String key) { + return value is DictionaryTypeMask && value.containsKey(key); + } + + @override + AbstractValue getDictionaryValueForKey(AbstractValue value, String key) { + if (value is DictionaryTypeMask) return value.getValueForKey(key); + return dynamicType; + } + + @override + AbstractValue createMapValue(AbstractValue forwardTo, Object allocationNode, + MemberEntity allocationElement, AbstractValue key, AbstractValue value) { + return new MapTypeMask( + forwardTo, allocationNode, allocationElement, key, value); + } + + @override + AbstractValue createDictionaryValue( + AbstractValue forwardTo, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue key, + AbstractValue value, + Map mappings) { + return new DictionaryTypeMask( + forwardTo, allocationNode, allocationElement, key, value, mappings); + } + + @override + bool isSpecializationOf( + AbstractValue specialization, AbstractValue generalization) { + return specialization is ForwardingTypeMask && + specialization.forwardTo == generalization; + } + + @override + Object getAllocationNode(AbstractValue value) { + if (value is AllocationTypeMask) { + return value.allocationNode; + } + return null; + } + + @override + MemberEntity getAllocationElement(AbstractValue value) { + if (value is AllocationTypeMask) { + return value.allocationElement; + } + return null; + } + + @override + AbstractValue getGeneralization(AbstractValue value) { + if (value is AllocationTypeMask) { + return value.forwardTo; + } + return null; + } + + @override + String getCompactText(AbstractValue value) { + return formatType(value); + } +} + +/// Convert the given TypeMask to a compact string format. +/// +/// The default format is too verbose for the graph format since long strings +/// create oblong nodes that obstruct the graph layout. +String formatType(TypeMask type) { + if (type is FlatTypeMask) { + // TODO(asgerf): Disambiguate classes whose name is not unique. Using the + // library name for all classes is not a good idea, since library names + // can be really long and mess up the layout. + // Capitalize Null to emphasize that it's the null type mask and not + // a null value we accidentally printed out. + if (type.isEmptyOrNull) return type.isNullable ? 'Null' : 'Empty'; + String nullFlag = type.isNullable ? '?' : ''; + String subFlag = type.isExact ? '' : type.isSubclass ? '+' : '*'; + return '${type.base.name}$nullFlag$subFlag'; + } + if (type is UnionTypeMask) { + return type.disjointMasks.map(formatType).join(' | '); + } + if (type is ContainerTypeMask) { + String container = formatType(type.forwardTo); + String member = formatType(type.elementType); + return '$container<$member>'; + } + if (type is MapTypeMask) { + String container = formatType(type.forwardTo); + String key = formatType(type.keyType); + String value = formatType(type.valueType); + return '$container<$key,$value>'; + } + if (type is ValueTypeMask) { + String baseType = formatType(type.forwardTo); + String value = type.value.toStructuredText(); + return '$baseType=$value'; + } + return '$type'; // Fall back on toString if not supported here. }