mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:49:43 +00:00
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>
This commit is contained in:
parent
947760f9b2
commit
c6129f768e
|
@ -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));
|
||||
}),
|
||||
|
|
|
@ -440,8 +440,8 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
|||
|
||||
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<T> extends InferrerEngine<T> {
|
|||
|
||||
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<T> extends InferrerEngine<T> {
|
|||
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<T> extends InferrerEngine<T> {
|
|||
// 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<T> extends InferrerEngine<T> {
|
|||
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<T> extends InferrerEngine<T> {
|
|||
|
||||
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<T> extends InferrerEngine<T> {
|
|||
SideEffectsBuilder sideEffectsBuilder,
|
||||
bool inLoop) {
|
||||
CallSiteTypeInformation info = new StaticCallSiteTypeInformation(
|
||||
abstractValueDomain,
|
||||
types.currentMember,
|
||||
node,
|
||||
caller,
|
||||
|
@ -1020,6 +1024,7 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
|||
});
|
||||
|
||||
CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
|
||||
abstractValueDomain,
|
||||
types.currentMember,
|
||||
callType,
|
||||
node,
|
||||
|
@ -1037,16 +1042,16 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
|||
}
|
||||
|
||||
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 @@ abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
|
|||
{bool inLoop}) {
|
||||
sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
|
||||
CallSiteTypeInformation info = new ClosureCallSiteTypeInformation(
|
||||
abstractValueDomain,
|
||||
types.currentMember,
|
||||
node,
|
||||
caller,
|
||||
|
|
|
@ -268,7 +268,9 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy<ir.Node> {
|
|||
|
||||
@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 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy<ir.Node> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<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 @@ 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.
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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<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 @@ class TypeSystem<T> {
|
|||
|
||||
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<T> {
|
|||
return dynamicType;
|
||||
}
|
||||
return getConcreteTypeFor(
|
||||
firstType.type.union(secondType.type, _closedWorld));
|
||||
_abstractValueDomain.union(firstType.type, secondType.type));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -304,7 +307,7 @@ class TypeSystem<T> {
|
|||
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<T> {
|
|||
_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<T> {
|
|||
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<T> {
|
|||
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<T> {
|
|||
* 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<T> {
|
|||
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<T> {
|
|||
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<T> {
|
|||
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<T> {
|
|||
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 @@ class TypeSystem<T> {
|
|||
} 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<T> {
|
|||
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<T> {
|
|||
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 @@ class TypeSystem<T> {
|
|||
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<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 @@ 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);
|
||||
}
|
||||
|
|
|
@ -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<T> extends ForwardingTypeMask {
|
||||
class ContainerTypeMask<T> extends AllocationTypeMask<T> {
|
||||
final TypeMask forwardTo;
|
||||
|
||||
// The [Node] where this type mask was created.
|
||||
|
|
|
@ -14,7 +14,7 @@ part of masks;
|
|||
*/
|
||||
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 @@ class DictionaryTypeMask<T> extends MapTypeMask<T> {
|
|||
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 @@ class DictionaryTypeMask<T> extends MapTypeMask<T> {
|
|||
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 @@ class DictionaryTypeMask<T> extends MapTypeMask<T> {
|
|||
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)';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,3 +122,11 @@ abstract class ForwardingTypeMask implements TypeMask {
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<T> extends ForwardingTypeMask {
|
||||
class MapTypeMask<T> extends AllocationTypeMask<T> {
|
||||
final TypeMask forwardTo;
|
||||
|
||||
// The [Node] where this type mask was created.
|
||||
|
|
|
@ -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<AbstractValue> 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<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.
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue