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:
Johnni Winther 2018-05-31 07:26:52 +00:00 committed by commit-bot@chromium.org
parent 947760f9b2
commit c6129f768e
14 changed files with 658 additions and 353 deletions

View file

@ -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));
}),

View file

@ -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,

View file

@ -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);
}
}
}

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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.

View file

@ -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)';
}
}

View file

@ -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;
}

View file

@ -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.

View file

@ -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.
}