Use AbstractValue in the rest of type inference
Change-Id: Ic782af55e7b9ab9942eab9809f180e3979d2b326
Reviewed-on: https://dart-review.googlesource.com/56920
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index c43f13f9..e0b8077 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -20,7 +20,7 @@
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 @@
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 @@
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 0764bff..5abfae9 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -440,8 +440,8 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
TypeInformation getDefaultTypeOfParameter(Local parameter) {
return defaultTypeOfParameter.putIfAbsent(parameter, () {
- return new PlaceholderTypeInformation(types.currentMember);
+ return new PlaceholderTypeInformation(
+ abstractValueDomain, types.currentMember);
});
}
@@ -967,6 +970,7 @@
SideEffectsBuilder sideEffectsBuilder,
bool inLoop) {
CallSiteTypeInformation info = new StaticCallSiteTypeInformation(
+ abstractValueDomain,
types.currentMember,
node,
caller,
@@ -1020,6 +1024,7 @@
});
CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
+ abstractValueDomain,
types.currentMember,
callType,
node,
@@ -1037,16 +1042,16 @@
}
TypeInformation registerAwait(T node, TypeInformation argument) {
- AwaitTypeInformation info =
- new AwaitTypeInformation<T>(types.currentMember, node);
+ AwaitTypeInformation info = new AwaitTypeInformation<T>(
+ abstractValueDomain, types.currentMember, node);
info.addAssignment(argument);
types.allocatedTypes.add(info);
return info;
}
TypeInformation registerYield(T node, TypeInformation argument) {
- YieldTypeInformation info =
- new YieldTypeInformation<T>(types.currentMember, node);
+ YieldTypeInformation info = new YieldTypeInformation<T>(
+ abstractValueDomain, types.currentMember, node);
info.addAssignment(argument);
types.allocatedTypes.add(info);
return info;
@@ -1063,6 +1068,7 @@
{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 7b49435..a5f708c 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 @@
@override
ParameterTypeInformation createParameterTypeInformation(
- covariant JLocal parameter, TypeSystem<ir.Node> types) {
+ AbstractValueDomain abstractValueDomain,
+ covariant JLocal parameter,
+ TypeSystem<ir.Node> types) {
MemberEntity context = parameter.memberContext;
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(context);
ir.FunctionNode functionNode =
@@ -289,40 +291,48 @@
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 032b254..b09dfcc 100644
--- a/pkg/compiler/lib/src/inferrer/node_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/node_tracer.dart
@@ -6,7 +6,7 @@
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 @@
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 @@
// "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 @@
// 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 @@
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 d8242e9..52ff120 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 @@
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 @@
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 @@
final Set<TypeInformation> seen = new Set<TypeInformation>();
final List<TypeInformation> worklist = new List<TypeInformation>();
final Map<TypeInformation, int> nodeId = <TypeInformation, int>{};
+ 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 @@
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 cb9971a..2353af8 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 '../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 @@
/// 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 @@
bool get isConcrete => false;
- TypeInformation(this.context)
+ TypeInformation(this.type, this.context)
: _assignments = <TypeInformation>[],
users = new Setlet<TypeInformation>();
- TypeInformation.noAssignments(this.context)
+ TypeInformation.noAssignments(this.type, this.context)
: _assignments = const <TypeInformation>[],
users = new Setlet<TypeInformation>();
- TypeInformation.untracked()
+ TypeInformation.untracked(this.type)
: _assignments = const <TypeInformation>[],
users = const ImmutableEmptySet(),
context = null;
- TypeInformation.withAssignments(this.context, this._assignments)
+ TypeInformation.withAssignments(this.type, this.context, this._assignments)
: users = new Setlet<TypeInformation>();
void addUser(TypeInformation user) {
@@ -152,7 +145,7 @@
}
}
- TypeMask refine(InferrerEngine inferrer) {
+ AbstractValue refine(InferrerEngine inferrer) {
return abandonInferencing ? safeType(inferrer) : computeType(inferrer);
}
@@ -160,13 +153,13 @@
* 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 @@
bool reset(InferrerEngine inferrer) {
if (abandonInferencing) return false;
- type = const TypeMask.nonNullEmpty();
+ type = inferrer.abstractValueDomain.emptyType;
refineCount = 0;
return true;
}
@@ -259,13 +252,15 @@
* [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 @@
/// 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 @@
*/
Map<MemberEntity, Setlet<Object>> _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 @@
// 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 @@
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 @@
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 @@
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 @@
return null;
}
- TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
+ AbstractValue _potentiallyNarrowType(
+ AbstractValue mask, InferrerEngine inferrer) {
return _narrowType(inferrer.closedWorld, mask, _type);
}
@@ -558,14 +563,16 @@
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 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 @@
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 @@
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 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 @@
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 @@
: _isInstanceMemberParameter = true,
_isClosureParameter = false,
_isInitializingFormal = false,
- super._withAssignments(context, assignments);
+ super._withAssignments(abstractValueDomain, context, assignments);
FunctionEntity get method => _method;
@@ -719,7 +740,7 @@
}
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
Iterable<MemberEntity> _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 @@
Iterable<MemberEntity> 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 @@
* 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 @@
}
}
- TypeMask computeType(InferrerEngine inferrer) {
+ AbstractValue computeType(InferrerEngine inferrer) {
+ ClosedWorld closedWorld = inferrer.closedWorld;
+ AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
Iterable<MemberEntity> 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<MemberEntity> 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 @@
// 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 @@
// `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 @@
}
}
}
- 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 @@
}
}));
}
- 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 @@
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<MemberEntity> get callees {
throw new UnsupportedError("Cannot compute callees of a closure call.");
@@ -1358,8 +1396,7 @@
* 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 @@
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 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 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 @@
* 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 @@
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 @@
/** 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 @@
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 @@
*/
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 @@
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 @@
* 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 @@
// 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 @@
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 @@
return newInfo;
}
- List<TypeInformation> addMapAssignment(MapTypeInformation other) {
+ List<TypeInformation> addMapAssignment(
+ AbstractValueDomain abstractValueDomain, MapTypeInformation other) {
List<TypeInformation> newInfos = <TypeInformation>[];
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 @@
return visitor.visitMapTypeInformation(this);
}
- TypeMask toTypeMask(InferrerEngine inferrer) {
+ AbstractValue toTypeMask(InferrerEngine inferrer) {
+ AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
if (inDictionaryMode) {
- Map<String, TypeMask> mappings = new Map<String, TypeMask>();
+ Map<String, AbstractValue> mappings = new Map<String, AbstractValue>();
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 @@
return type;
}
- TypeMask safeType(InferrerEngine inferrer) => originalType;
+ AbstractValue safeType(InferrerEngine inferrer) => originalType;
bool hasStableType(InferrerEngine inferrer) {
return keyType.isStable &&
@@ -1750,9 +1801,9 @@
* 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 @@
// 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 @@
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 @@
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 @@
class AwaitTypeInformation<T> 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 YieldTypeInformation<T> 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 @@
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 @@
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 @@
// 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 eb3bc06..672ebbe 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -6,7 +6,6 @@
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 @@
/// information for nodes.
abstract class TypeSystemStrategy<T> {
/// Creates [MemberTypeInformation] for [member].
- MemberTypeInformation createMemberTypeInformation(MemberEntity member);
+ MemberTypeInformation createMemberTypeInformation(
+ AbstractValueDomain abstractValueDomain, MemberEntity member);
/// Creates [ParameterTypeInformation] for [parameter].
ParameterTypeInformation createParameterTypeInformation(
- Local parameter, TypeSystem<T> types);
+ AbstractValueDomain abstractValueDomain,
+ Local parameter,
+ TypeSystem<T> types);
/// Calls [f] for each parameter in [function].
void forEachParameter(FunctionEntity function, void f(Local parameter));
@@ -261,11 +263,12 @@
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 @@
return dynamicType;
}
return getConcreteTypeFor(
- firstType.type.union(secondType.type, _closedWorld));
+ _abstractValueDomain.union(firstType.type, secondType.type));
}
/**
@@ -304,7 +307,7 @@
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 @@
_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 @@
if (isNullable) {
return type;
}
- otherType = dynamicType.type.nonNullable();
+ otherType = _abstractValueDomain.excludeNull(dynamicType.type);
} else {
otherType =
_abstractValueDomain.createNonNullSubtype(interface.element);
@@ -358,10 +362,11 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
int inferredLength = isFixed ? length : null;
AbstractValue elementTypeMask =
isElementInferred ? elementType.type : dynamicType.type;
- ContainerTypeMask<T> mask = new ContainerTypeMask<T>(
+ 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 @@
} 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 @@
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 @@
TypeInformation allocateDiamondPhi(
TypeInformation firstInput, TypeInformation secondInput) {
PhiElementTypeInformation<T> result = new PhiElementTypeInformation<T>(
- currentMember, null, null,
+ _abstractValueDomain, currentMember, null, null,
isTry: false);
result.addAssignment(firstInput);
result.addAssignment(secondInput);
@@ -548,7 +556,7 @@
PhiElementTypeInformation<T> _addPhi(
T node, Local variable, TypeInformation inputType, bool isTry) {
PhiElementTypeInformation<T> result = new PhiElementTypeInformation<T>(
- 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 67bd1e0..c9e3647 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1010,7 +1010,8 @@
/// 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 95c7a90..2534ec8 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 @@
/// 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 @@
/// 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<String, AbstractValue> 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<MemberEntity> members);
@@ -346,4 +441,7 @@
/// 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 fbbd59f..f389c66 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 @@
/// 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<T> extends ForwardingTypeMask {
+class ContainerTypeMask<T> extends AllocationTypeMask<T> {
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 3d92fcc..3a620c4 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 @@
*/
class DictionaryTypeMask<T> extends MapTypeMask<T> {
// The underlying key/value map of this dictionary.
- final Map<String, TypeMask> typeMap;
+ final Map<String, AbstractValue> _typeMap;
DictionaryTypeMask(
TypeMask forwardTo,
@@ -22,34 +22,38 @@
MemberEntity allocationElement,
TypeMask keyType,
TypeMask valueType,
- this.typeMap)
+ this._typeMap)
: super(forwardTo, allocationNode, allocationElement, keyType, valueType);
TypeMask nullable() {
return isNullable
? this
: new DictionaryTypeMask<T>(forwardTo.nullable(), allocationNode,
- allocationElement, keyType, valueType, typeMap);
+ allocationElement, keyType, valueType, _typeMap);
}
TypeMask nonNullable() {
return isNullable
? new DictionaryTypeMask<T>(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 @@
TypeMask newKeyType = keyType.union(other.keyType, closedWorld);
TypeMask newValueType = valueType.union(other.valueType, closedWorld);
Map<String, TypeMask> mappings = <String, TypeMask>{};
- 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 @@
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 0bfed426..a24095d 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 @@
int get hashCode => throw "Subclass should implement hashCode getter";
}
+
+abstract class AllocationTypeMask<T> 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 b1ded1d..7c47b4c 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 @@
* 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<T> extends ForwardingTypeMask {
+class MapTypeMask<T> extends AllocationTypeMask<T> {
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 9959309..a7da7b1 100644
--- a/pkg/compiler/lib/src/types/masks.dart
+++ b/pkg/compiler/lib/src/types/masks.dart
@@ -287,7 +287,10 @@
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 @@
bool isPrimitiveValue(TypeMask value) => value.isValue;
@override
- ConstantValue getPrimitiveValue(TypeMask mask) {
+ PrimitiveConstantValue getPrimitiveValue(TypeMask mask) {
if (mask.isValue) {
ValueTypeMask valueMask = mask;
return valueMask.value;
@@ -307,6 +310,12 @@
}
@override
+ AbstractValue createPrimitiveValue(
+ covariant TypeMask originalValue, PrimitiveConstantValue value) {
+ return new ValueTypeMask(originalValue, value);
+ }
+
+ @override
bool canBeNull(TypeMask value) => value.isNullable;
@override
@@ -489,8 +498,17 @@
}
@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;
@@ -505,6 +523,22 @@
}
@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<AbstractValue> values) {
TypeMask result = const TypeMask.nonNullEmpty();
for (TypeMask value in values) {
@@ -609,4 +643,113 @@
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<String, AbstractValue> 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.
}