Usa AbstractValue in the rest of ssa
Change-Id: Id29d7bdaaeaa0fb6dfd4f5fbba50468cd474cd8e
Reviewed-on: https://dart-review.googlesource.com/56664
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index a723729..c43f13f9 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -23,8 +23,7 @@
import 'types/masks.dart';
import 'types/types.dart'
show GlobalTypeInferenceElementResult, GlobalTypeInferenceMemberResult;
-import 'universe/world_builder.dart'
- show CodegenWorldBuilder, ReceiverConstraint;
+import 'universe/world_builder.dart' show CodegenWorldBuilder;
import 'universe/world_impact.dart'
show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl;
import 'world.dart' show ClosedWorld;
@@ -376,8 +375,8 @@
class Selection {
final Entity selectedEntity;
- final ReceiverConstraint mask;
- Selection(this.selectedEntity, this.mask);
+ final Object receiverConstraint;
+ Selection(this.selectedEntity, this.receiverConstraint);
}
/// Interface used to record information from different parts of the compiler so
@@ -462,12 +461,13 @@
entity,
impact,
new WorldImpactVisitorImpl(visitDynamicUse: (dynamicUse) {
- TypeMask mask = dynamicUse.mask;
+ TypeMask 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.mask)));
+ .map((MemberEntity e) =>
+ new Selection(e, dynamicUse.receiverConstraint)));
}, visitStaticUse: (staticUse) {
selections.add(new Selection(staticUse.element, null));
}),
@@ -570,7 +570,8 @@
// Don't register dart2js builtin functions that are not recorded.
Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
if (useInfo == null) continue;
- info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
+ info.uses.add(
+ new DependencyInfo(useInfo, '${selection.receiverConstraint}'));
}
}
@@ -584,7 +585,8 @@
for (Selection selection in uses) {
Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
if (useInfo == null) continue;
- info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
+ info.uses.add(
+ new DependencyInfo(useInfo, '${selection.receiverConstraint}'));
}
}
diff --git a/pkg/compiler/lib/src/js_backend/interceptor_data.dart b/pkg/compiler/lib/src/js_backend/interceptor_data.dart
index 079aa91..925d846 100644
--- a/pkg/compiler/lib/src/js_backend/interceptor_data.dart
+++ b/pkg/compiler/lib/src/js_backend/interceptor_data.dart
@@ -9,7 +9,7 @@
import '../elements/entities.dart';
import '../elements/types.dart';
import '../js/js.dart' as jsAst;
-import '../types/masks.dart' show TypeMask;
+import '../types/abstract_value_domain.dart';
import '../universe/selector.dart';
import '../world.dart' show ClosedWorld;
import 'namer.dart';
@@ -25,7 +25,7 @@
bool isInterceptedName(String name);
bool isInterceptedSelector(Selector selector);
bool isInterceptedMixinSelector(
- Selector selector, TypeMask mask, ClosedWorld closedWorld);
+ Selector selector, AbstractValue mask, ClosedWorld closedWorld);
Iterable<ClassEntity> get interceptedClasses;
bool isMixedIntoInterceptedClass(ClassEntity element);
@@ -117,7 +117,7 @@
/// into an intercepted class. These selectors are not eligible for the
/// 'dummy explicit receiver' optimization.
bool isInterceptedMixinSelector(
- Selector selector, TypeMask mask, ClosedWorld closedWorld) {
+ Selector selector, AbstractValue mask, ClosedWorld closedWorld) {
Set<MemberEntity> elements =
_interceptedMixinElements.putIfAbsent(selector.name, () {
Set<MemberEntity> elements = interceptedMembers[selector.name];
@@ -132,7 +132,8 @@
if (elements.isEmpty) return false;
return elements.any((element) {
return selector.applies(element) &&
- (mask == null || mask.canHit(element, selector, closedWorld));
+ (mask == null ||
+ closedWorld.abstractValueDomain.canHit(mask, element, selector));
});
}
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 87b1ceb..37288e6 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -29,7 +29,6 @@
import '../native/native.dart' as native;
import '../options.dart';
import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../universe/selector.dart' show Selector;
import '../universe/use.dart'
@@ -365,8 +364,8 @@
.visitGraph(graph);
new SsaTypeKnownRemover().visitGraph(graph);
new SsaTrustedCheckRemover(_options).visitGraph(graph);
- new SsaInstructionMerger(_closedWorld.abstractValueDomain,
- generateAtUseSite, _superMemberData)
+ new SsaInstructionMerger(
+ _abstractValueDomain, generateAtUseSite, _superMemberData)
.visitGraph(graph);
new SsaConditionMerger(generateAtUseSite, controlFlowOperators)
.visitGraph(graph);
@@ -1828,8 +1827,8 @@
_registry.registerUseInterceptor();
}
- TypeMask getOptimizedSelectorFor(
- HInvokeDynamic node, Selector selector, TypeMask mask) {
+ AbstractValue getOptimizedSelectorFor(
+ HInvokeDynamic node, Selector selector, AbstractValue mask) {
if (node.element != null) {
// Create an artificial type mask to make sure only
// [node.element] will be enqueued. We're not using the receiver
@@ -1837,7 +1836,7 @@
// invoke dynamic knows more than the receiver.
ClassEntity enclosing = node.element.enclosingClass;
if (_closedWorld.isInstantiated(enclosing)) {
- return _closedWorld.abstractValueDomain.createNonNullExact(enclosing);
+ return _abstractValueDomain.createNonNullExact(enclosing);
} else {
// The element is mixed in so a non-null subtype mask is the most
// precise we have.
@@ -1847,7 +1846,7 @@
node,
"Element ${node.element} from $enclosing expected "
"to be mixed in."));
- return _closedWorld.abstractValueDomain.createNonNullSubtype(enclosing);
+ return _abstractValueDomain.createNonNullSubtype(enclosing);
}
}
// If [JSInvocationMirror._invokeOn] is enabled, and this call
@@ -1880,7 +1879,7 @@
_registry.registerStaticUse(new StaticUse.directInvoke(
target, selector.callStructure, node.typeArguments));
} else {
- TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
+ AbstractValue mask = getOptimizedSelectorFor(node, selector, node.mask);
_registry.registerDynamicUse(
new ConstrainedDynamicUse(selector, mask, node.typeArguments));
}
@@ -1895,7 +1894,7 @@
_registry.registerStaticUse(new StaticUse.directSet(node.element));
} else {
Selector selector = node.selector;
- TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
+ AbstractValue mask = getOptimizedSelectorFor(node, selector, node.mask);
_registry.registerDynamicUse(
new ConstrainedDynamicUse(selector, mask, node.typeArguments));
}
@@ -1912,7 +1911,7 @@
_registry.registerStaticUse(new StaticUse.directGet(node.element));
} else {
Selector selector = node.selector;
- TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
+ AbstractValue mask = getOptimizedSelectorFor(node, selector, node.mask);
_registry.registerDynamicUse(
new ConstrainedDynamicUse(selector, mask, node.typeArguments));
}
@@ -2929,19 +2928,18 @@
js.Expression generateReceiverOrArgumentTypeTest(HTypeConversion node) {
HInstruction input = node.checkedInput;
- TypeMask inputType = node.inputType ?? input.instructionType;
- TypeMask checkedType = node.checkedType;
+ AbstractValue inputType = node.inputType ?? input.instructionType;
+ AbstractValue checkedType = node.checkedType;
// This path is no longer used for indexable primitive types.
- assert(
- !checkedType.satisfies(_commonElements.jsIndexableClass, _closedWorld));
+ assert(!_abstractValueDomain.isJsIndexable(checkedType));
// Figure out if it is beneficial to use a null check. V8 generally prefers
// 'typeof' checks, but for integers we cannot compile this test into a
// single typeof check so the null check is cheaper.
- bool isIntCheck = checkedType.containsOnlyInt(_closedWorld);
+ bool isIntCheck = _abstractValueDomain.isIntegerOrNull(checkedType);
bool turnIntoNumCheck =
- isIntCheck && inputType.containsOnlyInt(_closedWorld);
+ isIntCheck && _abstractValueDomain.isIntegerOrNull(inputType);
bool turnIntoNullCheck = !turnIntoNumCheck &&
- (checkedType.nullable() == inputType) &&
+ (_abstractValueDomain.includeNull(checkedType) == inputType) &&
isIntCheck;
if (turnIntoNullCheck) {
@@ -2952,15 +2950,16 @@
// input is !int
checkBigInt(input, '!==', input.sourceInformation);
return pop();
- } else if (turnIntoNumCheck || checkedType.containsOnlyNum(_closedWorld)) {
+ } else if (turnIntoNumCheck ||
+ _abstractValueDomain.isNumberOrNull(checkedType)) {
// input is !num
checkNum(input, '!==', input.sourceInformation);
return pop();
- } else if (checkedType.containsOnlyBool(_closedWorld)) {
+ } else if (_abstractValueDomain.isBooleanOrNull(checkedType)) {
// input is !bool
checkBool(input, '!==', input.sourceInformation);
return pop();
- } else if (checkedType.containsOnlyString(_closedWorld)) {
+ } else if (_abstractValueDomain.isStringOrNull(checkedType)) {
// input is !string
checkString(input, '!==', input.sourceInformation);
return pop();
@@ -3124,14 +3123,15 @@
}
bool typeVariableAccessNeedsSubstitution(
- TypeVariableEntity element, TypeMask receiverMask) {
+ TypeVariableEntity element, AbstractValue receiverMask) {
ClassEntity cls = element.typeDeclaration;
// See if the receiver type narrows the set of classes to ones that can be
// indexed.
// TODO(sra): Currently the only convenient query is [singleClass]. We
// should iterate over all the concrete classes in [receiverMask].
- ClassEntity receiverClass = receiverMask.singleClass(_closedWorld);
+ ClassEntity receiverClass =
+ _abstractValueDomain.getExactClass(receiverMask);
if (receiverClass != null) {
if (_rtiSubstitutions.isTrivialSubstitution(receiverClass, cls)) {
return false;
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index bfa7fb2..d3905d1 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -8,7 +8,6 @@
import '../js_backend/interceptor_data.dart';
import '../options.dart';
import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
import '../universe/selector.dart' show Selector;
import '../world.dart' show ClosedWorld;
import 'nodes.dart';
@@ -71,8 +70,8 @@
if (node.kind == HIs.RAW_CHECK) {
HInstruction interceptor = node.interceptor;
if (interceptor != null) {
- return new HIsViaInterceptor(node.typeExpression, interceptor,
- _closedWorld.abstractValueDomain.boolType);
+ return new HIsViaInterceptor(
+ node.typeExpression, interceptor, _abstractValueDomain.boolType);
}
}
return node;
@@ -86,9 +85,10 @@
String simpleOp(HInstruction left, HInstruction right) {
// Returns the single identity comparison (== or ===) or null if a more
// complex expression is required.
- TypeMask leftType = left.instructionType;
- TypeMask rightType = right.instructionType;
- if (leftType.isNullable && rightType.isNullable) {
+ AbstractValue leftType = left.instructionType;
+ AbstractValue rightType = right.instructionType;
+ if (_abstractValueDomain.canBeNull(leftType) &&
+ _abstractValueDomain.canBeNull(rightType)) {
if (left.isConstantNull() ||
right.isConstantNull() ||
(left.isPrimitive(_abstractValueDomain) && leftType == rightType)) {
@@ -108,14 +108,14 @@
HInstruction visitInvokeSuper(HInvokeSuper node) {
if (node.isInterceptedCall) {
- TypeMask mask = node.getDartReceiver(_closedWorld).instructionType;
+ AbstractValue mask = node.getDartReceiver(_closedWorld).instructionType;
tryReplaceInterceptorWithDummy(node, node.selector, mask);
}
return node;
}
void tryReplaceInterceptorWithDummy(
- HInvoke node, Selector selector, TypeMask mask) {
+ HInvoke node, Selector selector, AbstractValue mask) {
// Calls of the form
//
// a.foo$1(a, x)
diff --git a/pkg/compiler/lib/src/ssa/graph_builder.dart b/pkg/compiler/lib/src/ssa/graph_builder.dart
index aa99124..2a6830c 100644
--- a/pkg/compiler/lib/src/ssa/graph_builder.dart
+++ b/pkg/compiler/lib/src/ssa/graph_builder.dart
@@ -130,7 +130,7 @@
/// Pushes a boolean checking [expression] against null.
pushCheckNull(HInstruction expression) {
push(new HIdentity(expression, graph.addConstantNull(closedWorld), null,
- closedWorld.abstractValueDomain.boolType));
+ abstractValueDomain.boolType));
}
void dup() {
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index a3ec846..6d297c5 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -3,12 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
import '../common_elements.dart' show CommonElements;
-import '../constants/constant_system.dart';
import '../constants/values.dart';
import '../elements/entities.dart';
import '../js_backend/interceptor_data.dart';
import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
import '../universe/selector.dart' show Selector;
import '../world.dart' show ClosedWorld;
import 'nodes.dart';
@@ -37,23 +35,21 @@
class SsaSimplifyInterceptors extends HBaseVisitor
implements OptimizationPhase {
final String name = "SsaSimplifyInterceptors";
- final ClosedWorld closedWorld;
- final ClassEntity enclosingClass;
- HGraph graph;
+ final ClosedWorld _closedWorld;
+ final ClassEntity _enclosingClass;
+ HGraph _graph;
- SsaSimplifyInterceptors(this.closedWorld, this.enclosingClass);
+ SsaSimplifyInterceptors(this._closedWorld, this._enclosingClass);
- CommonElements get _commonElements => closedWorld.commonElements;
+ CommonElements get _commonElements => _closedWorld.commonElements;
- ConstantSystem get constantSystem => closedWorld.constantSystem;
-
- InterceptorData get interceptorData => closedWorld.interceptorData;
+ InterceptorData get _interceptorData => _closedWorld.interceptorData;
AbstractValueDomain get _abstractValueDomain =>
- closedWorld.abstractValueDomain;
+ _closedWorld.abstractValueDomain;
void visitGraph(HGraph graph) {
- this.graph = graph;
+ this._graph = graph;
visitDominatorTree(graph);
}
@@ -110,18 +106,16 @@
// All intercepted classes extend `Interceptor`, so if the receiver can't be
// a class extending `Interceptor` then it can be called directly.
- return new TypeMask.nonNullSubclass(
- _commonElements.jsInterceptorClass, closedWorld)
- .isDisjoint(receiver.instructionType, closedWorld);
+ return !_abstractValueDomain.canBeInterceptor(receiver.instructionType);
}
HInstruction tryComputeConstantInterceptor(
HInstruction input, Set<ClassEntity> interceptedClasses) {
- if (input == graph.explicitReceiverParameter) {
+ if (input == _graph.explicitReceiverParameter) {
// If `explicitReceiverParameter` is set it means the current method is an
// interceptor method, and `this` is the interceptor. The caller just did
// `getInterceptor(foo).currentMethod(foo)` to enter the current method.
- return graph.thisInstruction;
+ return _graph.thisInstruction;
}
ClassEntity constantInterceptor = tryComputeConstantInterceptorFromType(
@@ -131,32 +125,32 @@
// If we just happen to be in an instance method of the constant
// interceptor, `this` is a shorter alias.
- if (constantInterceptor == enclosingClass &&
- graph.thisInstruction != null) {
- return graph.thisInstruction;
+ if (constantInterceptor == _enclosingClass &&
+ _graph.thisInstruction != null) {
+ return _graph.thisInstruction;
}
ConstantValue constant = new InterceptorConstantValue(constantInterceptor);
- return graph.addConstant(constant, closedWorld);
+ return _graph.addConstant(constant, _closedWorld);
}
ClassEntity tryComputeConstantInterceptorFromType(
- TypeMask type, Set<ClassEntity> interceptedClasses) {
- if (type.isNullable) {
- if (type.isNull) {
+ AbstractValue type, Set<ClassEntity> interceptedClasses) {
+ if (_abstractValueDomain.canBeNull(type)) {
+ if (_abstractValueDomain.isNull(type)) {
return _commonElements.jsNullClass;
}
- } else if (type.containsOnlyInt(closedWorld)) {
+ } else if (_abstractValueDomain.isIntegerOrNull(type)) {
return _commonElements.jsIntClass;
- } else if (type.containsOnlyDouble(closedWorld)) {
+ } else if (_abstractValueDomain.isDoubleOrNull(type)) {
return _commonElements.jsDoubleClass;
- } else if (type.containsOnlyBool(closedWorld)) {
+ } else if (_abstractValueDomain.isBooleanOrNull(type)) {
return _commonElements.jsBoolClass;
- } else if (type.containsOnlyString(closedWorld)) {
+ } else if (_abstractValueDomain.isStringOrNull(type)) {
return _commonElements.jsStringClass;
- } else if (type.satisfies(_commonElements.jsArrayClass, closedWorld)) {
+ } else if (_abstractValueDomain.isArray(type)) {
return _commonElements.jsArrayClass;
- } else if (type.containsOnlyNum(closedWorld) &&
+ } else if (_abstractValueDomain.isNumberOrNull(type) &&
!interceptedClasses.contains(_commonElements.jsIntClass) &&
!interceptedClasses.contains(_commonElements.jsDoubleClass)) {
// If the method being intercepted is not defined in [int] or [double] we
@@ -175,8 +169,8 @@
// for a subclass or call methods defined on a subclass. Provided the
// code is completely insensitive to the specific instance subclasses, we
// can use the non-leaf class directly.
- ClassEntity element = type.singleClass(closedWorld);
- if (element != null && closedWorld.nativeData.isNativeClass(element)) {
+ ClassEntity element = _abstractValueDomain.getExactClass(type);
+ if (element != null && _closedWorld.nativeData.isNativeClass(element)) {
return element;
}
}
@@ -223,11 +217,11 @@
// If there is a call that dominates all other uses, we can use just the
// selector of that instruction.
if (dominator is HInvokeDynamic &&
- dominator.isCallOnInterceptor(closedWorld) &&
+ dominator.isCallOnInterceptor(_closedWorld) &&
node == dominator.receiver &&
useCount(dominator, node) == 1) {
- interceptedClasses = interceptorData.getInterceptedClassesOn(
- dominator.selector.name, closedWorld);
+ interceptedClasses = _interceptorData.getInterceptedClassesOn(
+ dominator.selector.name, _closedWorld);
// If we found that we need number, we must still go through all
// uses to check if they require int, or double.
@@ -237,8 +231,8 @@
Set<ClassEntity> required;
for (HInstruction user in node.usedBy) {
if (user is! HInvoke) continue;
- Set<ClassEntity> intercepted = interceptorData
- .getInterceptedClassesOn(user.selector.name, closedWorld);
+ Set<ClassEntity> intercepted = _interceptorData
+ .getInterceptedClassesOn(user.selector.name, _closedWorld);
if (intercepted.contains(_commonElements.jsIntClass)) {
// TODO(johnniwinther): Use type argument when all uses of
// intercepted classes expect entities instead of elements.
@@ -252,7 +246,8 @@
required.add(_commonElements.jsDoubleClass);
}
}
- // Don't modify the result of [interceptorData.getInterceptedClassesOn].
+ // Don't modify the result of
+ // [_interceptorData.getInterceptedClassesOn].
if (required != null) {
interceptedClasses = interceptedClasses.union(required);
}
@@ -263,21 +258,21 @@
interceptedClasses = new Set<ClassEntity>();
for (HInstruction user in node.usedBy) {
if (user is HInvokeDynamic &&
- user.isCallOnInterceptor(closedWorld) &&
+ user.isCallOnInterceptor(_closedWorld) &&
node == user.receiver &&
useCount(user, node) == 1) {
- interceptedClasses.addAll(interceptorData.getInterceptedClassesOn(
- user.selector.name, closedWorld));
+ interceptedClasses.addAll(_interceptorData.getInterceptedClassesOn(
+ user.selector.name, _closedWorld));
} else if (user is HInvokeSuper &&
- user.isCallOnInterceptor(closedWorld) &&
+ user.isCallOnInterceptor(_closedWorld) &&
node == user.receiver &&
useCount(user, node) == 1) {
- interceptedClasses.addAll(interceptorData.getInterceptedClassesOn(
- user.selector.name, closedWorld));
+ interceptedClasses.addAll(_interceptorData.getInterceptedClassesOn(
+ user.selector.name, _closedWorld));
} else {
// Use a most general interceptor for other instructions, example,
// is-checks and escaping interceptors.
- interceptedClasses.addAll(interceptorData.interceptedClasses);
+ interceptedClasses.addAll(_interceptorData.interceptedClasses);
break;
}
}
@@ -326,8 +321,8 @@
_abstractValueDomain.excludeNull(receiver.instructionType),
interceptedClasses);
if (interceptorClass != null) {
- HInstruction constantInstruction = graph.addConstant(
- new InterceptorConstantValue(interceptorClass), closedWorld);
+ HInstruction constantInstruction = _graph.addConstant(
+ new InterceptorConstantValue(interceptorClass), _closedWorld);
node.conditionalConstantInterceptor = constantInstruction;
constantInstruction.usedBy.add(node);
return false;
@@ -355,8 +350,8 @@
// See if we can rewrite the is-check to use 'instanceof', i.e. rewrite
// "getInterceptor(x).$isT" to "x instanceof T".
if (node == user.interceptor) {
- if (interceptorData.mayGenerateInstanceofCheck(
- user.typeExpression, closedWorld)) {
+ if (_interceptorData.mayGenerateInstanceofCheck(
+ user.typeExpression, _closedWorld)) {
HInstruction instanceofCheck = new HIs.instanceOf(user.typeExpression,
user.expression, user.instructionType, user.sourceInformation);
instanceofCheck.sourceElement = user.sourceElement;
@@ -366,11 +361,11 @@
} else if (user is HInvokeDynamic) {
if (node == user.inputs[0]) {
// Replace the user with a [HOneShotInterceptor].
- HConstant nullConstant = graph.addConstantNull(closedWorld);
+ HConstant nullConstant = _graph.addConstantNull(_closedWorld);
List<HInstruction> inputs = new List<HInstruction>.from(user.inputs);
inputs[0] = nullConstant;
HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor(
- closedWorld.abstractValueDomain,
+ _abstractValueDomain,
user.selector,
user.mask,
inputs,
@@ -409,7 +404,7 @@
if (constant == null) return false;
Selector selector = node.selector;
- TypeMask mask = node.mask;
+ AbstractValue mask = node.mask;
HInstruction instruction;
if (selector.isGetter) {
instruction = new HInvokeDynamicGetter(
diff --git a/pkg/compiler/lib/src/ssa/kernel_impact.dart b/pkg/compiler/lib/src/ssa/kernel_impact.dart
index 8e731b7..950aa78 100644
--- a/pkg/compiler/lib/src/ssa/kernel_impact.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_impact.dart
@@ -510,7 +510,7 @@
List<DartType> typeArguments = _visitArguments(node.arguments);
// TODO(johnniwinther): Restrict the dynamic use to only match the known
// target.
- ReceiverConstraint constraint;
+ Object constraint;
MemberEntity member = elementMap.getMember(node.target);
if (_options.strongMode && useStrongModeWorldStrategy) {
// TODO(johnniwinther): Restrict this to subclasses?
@@ -610,7 +610,7 @@
}
} else {
visitNode(node.receiver);
- ReceiverConstraint constraint;
+ Object constraint;
if (_options.strongMode && useStrongModeWorldStrategy) {
DartType receiverType = elementMap.getStaticType(node.receiver);
if (receiverType is InterfaceType) {
@@ -626,7 +626,7 @@
@override
void visitPropertyGet(ir.PropertyGet node) {
visitNode(node.receiver);
- ReceiverConstraint constraint;
+ Object constraint;
if (_options.strongMode && useStrongModeWorldStrategy) {
DartType receiverType = elementMap.getStaticType(node.receiver);
if (receiverType is InterfaceType) {
@@ -642,7 +642,7 @@
void visitPropertySet(ir.PropertySet node) {
visitNode(node.receiver);
visitNode(node.value);
- ReceiverConstraint constraint;
+ Object constraint;
if (_options.strongMode && useStrongModeWorldStrategy) {
DartType receiverType = elementMap.getStaticType(node.receiver);
if (receiverType is InterfaceType) {
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index a0ce7be..4c03def 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -18,7 +18,6 @@
import '../native/native.dart' as native;
import '../options.dart';
import '../types/abstract_value_domain.dart';
-//import '../types/masks.dart';
import '../types/types.dart';
import '../universe/selector.dart' show Selector;
import '../universe/side_effects.dart' show SideEffects;
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index d0a0f44..00d06ae 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -6,7 +6,6 @@
import '../elements/entities.dart';
import '../options.dart';
import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
import '../types/types.dart';
import '../universe/selector.dart' show Selector;
import '../world.dart' show ClosedWorld;
@@ -47,7 +46,7 @@
AbstractValueDomain get abstractValueDomain =>
closedWorld.abstractValueDomain;
- TypeMask computeType(HInstruction instruction) {
+ AbstractValue computeType(HInstruction instruction) {
return instruction.accept(this);
}
@@ -55,8 +54,8 @@
// whether or not the type was changed.
bool updateType(HInstruction instruction) {
// Compute old and new types.
- TypeMask oldType = instruction.instructionType;
- TypeMask newType = computeType(instruction);
+ AbstractValue oldType = instruction.instructionType;
+ AbstractValue newType = computeType(instruction);
assert(newType != null);
// We unconditionally replace the propagated type with the new type. The
// computeType must make sure that we eventually reach a stable state.
@@ -127,109 +126,111 @@
}
}
- TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) {
+ AbstractValue visitBinaryArithmetic(HBinaryArithmetic instruction) {
HInstruction left = instruction.left;
HInstruction right = instruction.right;
- if (left.isInteger(closedWorld.abstractValueDomain) &&
- right.isInteger(closedWorld.abstractValueDomain)) {
- return closedWorld.abstractValueDomain.intType;
+ if (left.isInteger(abstractValueDomain) &&
+ right.isInteger(abstractValueDomain)) {
+ return abstractValueDomain.intType;
}
- if (left.isDouble(closedWorld.abstractValueDomain)) {
- return closedWorld.abstractValueDomain.doubleType;
+ if (left.isDouble(abstractValueDomain)) {
+ return abstractValueDomain.doubleType;
}
- return closedWorld.abstractValueDomain.numType;
+ return abstractValueDomain.numType;
}
- TypeMask checkPositiveInteger(HBinaryArithmetic instruction) {
+ AbstractValue checkPositiveInteger(HBinaryArithmetic instruction) {
HInstruction left = instruction.left;
HInstruction right = instruction.right;
- if (left.isPositiveInteger(closedWorld.abstractValueDomain) &&
- right.isPositiveInteger(closedWorld.abstractValueDomain)) {
- return closedWorld.abstractValueDomain.positiveIntType;
+ if (left.isPositiveInteger(abstractValueDomain) &&
+ right.isPositiveInteger(abstractValueDomain)) {
+ return abstractValueDomain.positiveIntType;
}
return visitBinaryArithmetic(instruction);
}
- TypeMask visitMultiply(HMultiply instruction) {
+ AbstractValue visitMultiply(HMultiply instruction) {
return checkPositiveInteger(instruction);
}
- TypeMask visitAdd(HAdd instruction) {
+ AbstractValue visitAdd(HAdd instruction) {
return checkPositiveInteger(instruction);
}
- TypeMask visitDivide(HDivide instruction) {
+ AbstractValue visitDivide(HDivide instruction) {
// Always double, as initialized.
return instruction.instructionType;
}
- TypeMask visitTruncatingDivide(HTruncatingDivide instruction) {
+ AbstractValue visitTruncatingDivide(HTruncatingDivide instruction) {
// Always as initialized.
return instruction.instructionType;
}
- TypeMask visitRemainder(HRemainder instruction) {
+ AbstractValue visitRemainder(HRemainder instruction) {
// Always as initialized.
return instruction.instructionType;
}
- TypeMask visitNegate(HNegate instruction) {
+ AbstractValue visitNegate(HNegate instruction) {
HInstruction operand = instruction.operand;
// We have integer subclasses that represent ranges, so widen any int
// subclass to full integer.
- if (operand.isInteger(closedWorld.abstractValueDomain)) {
- return closedWorld.abstractValueDomain.intType;
+ if (operand.isInteger(abstractValueDomain)) {
+ return abstractValueDomain.intType;
}
return instruction.operand.instructionType;
}
- TypeMask visitAbs(HAbs instruction) {
+ AbstractValue visitAbs(HAbs instruction) {
// TODO(sra): Can narrow to non-negative type for integers.
return instruction.operand.instructionType;
}
- TypeMask visitInstruction(HInstruction instruction) {
+ AbstractValue visitInstruction(HInstruction instruction) {
assert(instruction.instructionType != null);
return instruction.instructionType;
}
- TypeMask visitPhi(HPhi phi) {
- TypeMask candidateType = closedWorld.abstractValueDomain.emptyType;
+ AbstractValue visitPhi(HPhi phi) {
+ AbstractValue candidateType = abstractValueDomain.emptyType;
for (int i = 0, length = phi.inputs.length; i < length; i++) {
- TypeMask inputType = phi.inputs[i].instructionType;
- candidateType = candidateType.union(inputType, closedWorld);
+ AbstractValue inputType = phi.inputs[i].instructionType;
+ candidateType = abstractValueDomain.union(candidateType, inputType);
}
return candidateType;
}
- TypeMask visitTypeConversion(HTypeConversion instruction) {
+ AbstractValue visitTypeConversion(HTypeConversion instruction) {
HInstruction input = instruction.checkedInput;
- TypeMask inputType = input.instructionType;
- TypeMask checkedType = instruction.checkedType;
+ AbstractValue inputType = input.instructionType;
+ AbstractValue checkedType = instruction.checkedType;
if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) {
// We must make sure a type conversion for receiver or argument check
// does not try to do an int check, because an int check is not enough.
// We only do an int check if the input is integer or null.
- if (checkedType.containsOnlyNum(closedWorld) &&
- !checkedType.containsOnlyDouble(closedWorld) &&
- input.isIntegerOrNull(closedWorld.abstractValueDomain)) {
- instruction.checkedType = closedWorld.abstractValueDomain.intType;
- } else if (checkedType.containsOnlyInt(closedWorld) &&
- !input.isIntegerOrNull(closedWorld.abstractValueDomain)) {
- instruction.checkedType = closedWorld.abstractValueDomain.numType;
+ if (abstractValueDomain.isNumberOrNull(checkedType) &&
+ !abstractValueDomain.isDoubleOrNull(checkedType) &&
+ input.isIntegerOrNull(abstractValueDomain)) {
+ instruction.checkedType = abstractValueDomain.intType;
+ } else if (abstractValueDomain.isIntegerOrNull(checkedType) &&
+ !input.isIntegerOrNull(abstractValueDomain)) {
+ instruction.checkedType = abstractValueDomain.numType;
}
}
- TypeMask outputType = checkedType.intersection(inputType, closedWorld);
- if (outputType.isEmpty) {
+ AbstractValue outputType =
+ abstractValueDomain.intersection(checkedType, inputType);
+ if (abstractValueDomain.isEmpty(outputType)) {
// Intersection of double and integer conflicts (is empty), but JS numbers
// can be both int and double at the same time. For example, the input
// can be a literal double '8.0' that is marked as an integer (because 'is
// int' will return 'true'). What we really need to do is make the
// overlap between int and double values explicit in the TypeMask system.
- if (inputType.containsOnlyInt(closedWorld) &&
- checkedType.containsOnlyDouble(closedWorld)) {
- if (inputType.isNullable && checkedType.isNullable) {
+ if (abstractValueDomain.isIntegerOrNull(inputType) &&
+ abstractValueDomain.isDoubleOrNull(checkedType)) {
+ if (abstractValueDomain.canBeNull(inputType) &&
+ abstractValueDomain.canBeNull(checkedType)) {
outputType =
abstractValueDomain.includeNull(abstractValueDomain.doubleType);
} else {
@@ -255,10 +256,10 @@
return outputType;
}
- TypeMask visitTypeKnown(HTypeKnown instruction) {
+ AbstractValue visitTypeKnown(HTypeKnown instruction) {
HInstruction input = instruction.checkedInput;
- TypeMask inputType = input.instructionType;
- TypeMask outputType =
+ AbstractValue inputType = input.instructionType;
+ AbstractValue outputType =
abstractValueDomain.intersection(instruction.knownType, inputType);
if (inputType != outputType) {
input.replaceAllUsersDominatedBy(instruction.next, instruction);
@@ -266,8 +267,8 @@
return outputType;
}
- void convertInput(
- HInvokeDynamic instruction, HInstruction input, TypeMask type, int kind) {
+ void convertInput(HInvokeDynamic instruction, HInstruction input,
+ AbstractValue type, int kind) {
Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK)
? instruction.selector
: null;
@@ -278,15 +279,15 @@
input.replaceAllUsersDominatedBy(instruction, converted);
}
- bool isCheckEnoughForNsmOrAe(HInstruction instruction, TypeMask type) {
+ bool isCheckEnoughForNsmOrAe(HInstruction instruction, AbstractValue type) {
// In some cases, we want the receiver to be an integer,
// but that does not mean we will get a NoSuchMethodError
// if it's not: the receiver could be a double.
- if (type.containsOnlyInt(closedWorld)) {
+ if (abstractValueDomain.isIntegerOrNull(type)) {
// If the instruction's type is integer or null, the codegen
// will emit a null check, which is enough to know if it will
// hit a noSuchMethod.
- return instruction.isIntegerOrNull(closedWorld.abstractValueDomain);
+ return instruction.isIntegerOrNull(abstractValueDomain);
}
return true;
}
@@ -297,12 +298,12 @@
bool checkReceiver(HInvokeDynamic instruction) {
assert(instruction.isInterceptedCall);
HInstruction receiver = instruction.inputs[1];
- if (receiver.isNumber(closedWorld.abstractValueDomain)) return false;
- if (receiver.isNumberOrNull(closedWorld.abstractValueDomain)) {
+ if (receiver.isNumber(abstractValueDomain)) return false;
+ if (receiver.isNumberOrNull(abstractValueDomain)) {
convertInput(
instruction,
receiver,
- closedWorld.abstractValueDomain.excludeNull(receiver.instructionType),
+ abstractValueDomain.excludeNull(receiver.instructionType),
HTypeConversion.RECEIVER_TYPE_CHECK);
return true;
} else if (instruction.element == null) {
@@ -315,10 +316,10 @@
if (targets.length == 1) {
MemberEntity target = targets.first;
ClassEntity cls = target.enclosingClass;
- TypeMask type = new TypeMask.nonNullSubclass(cls, closedWorld);
+ AbstractValue type = abstractValueDomain.createNonNullSubclass(cls);
// We currently only optimize on some primitive types.
- if (!type.containsOnlyNum(closedWorld) &&
- !type.containsOnlyBool(closedWorld)) {
+ if (!abstractValueDomain.isNumberOrNull(type) &&
+ !abstractValueDomain.isBooleanOrNull(type)) {
return false;
}
if (!isCheckEnoughForNsmOrAe(receiver, type)) return false;
@@ -341,11 +342,11 @@
HInstruction right = instruction.inputs[2];
Selector selector = instruction.selector;
- if (selector.isOperator && left.isNumber(closedWorld.abstractValueDomain)) {
- if (right.isNumber(closedWorld.abstractValueDomain)) return false;
- TypeMask type = right.isIntegerOrNull(closedWorld.abstractValueDomain)
- ? closedWorld.abstractValueDomain.excludeNull(right.instructionType)
- : closedWorld.abstractValueDomain.numType;
+ if (selector.isOperator && left.isNumber(abstractValueDomain)) {
+ if (right.isNumber(abstractValueDomain)) return false;
+ AbstractValue type = right.isIntegerOrNull(abstractValueDomain)
+ ? abstractValueDomain.excludeNull(right.instructionType)
+ : abstractValueDomain.numType;
// TODO(ngeoffray): Some number operations don't have a builtin
// variant and will do the check in their method anyway. We
// still add a check because it allows to GVN these operations,
@@ -376,7 +377,7 @@
});
}
- TypeMask visitInvokeDynamic(HInvokeDynamic instruction) {
+ AbstractValue visitInvokeDynamic(HInvokeDynamic instruction) {
if (instruction.isInterceptedCall) {
// We cannot do the following optimization now, because we have to wait
// for the type propagation to be stable. The receiver of [instruction]
@@ -403,7 +404,7 @@
}
HInstruction receiver = instruction.getDartReceiver(closedWorld);
- TypeMask receiverType = receiver.instructionType;
+ AbstractValue receiverType = receiver.instructionType;
instruction.mask = receiverType;
// Try to specialize the receiver after this call by inserting a refinement
@@ -414,11 +415,11 @@
// we try to do the least expensive test first.
const int _MAX_QUICK_USERS = 50;
if (!instruction.selector.isClosureCall) {
- TypeMask newType;
- TypeMask computeNewType() {
+ AbstractValue newType;
+ AbstractValue computeNewType() {
newType = closedWorld.computeReceiverType(
instruction.selector, instruction.mask);
- newType = newType.intersection(receiverType, closedWorld);
+ newType = abstractValueDomain.intersection(newType, receiverType);
return newType;
}
diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart
index bbc5153..ed28667 100644
--- a/pkg/compiler/lib/src/types/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart
@@ -146,6 +146,10 @@
/// Returns `true` if [value] is an exact class or `null` at runtime.
bool isExact(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);
@@ -312,7 +316,10 @@
/// Returns `null` if 0 or more than 1 member can be hit at runtime.
MemberEntity locateSingleMember(AbstractValue receiver, Selector selector);
- /// Returns `true` if [value] is a indexable and iterable JavaScript value at
+ /// Returns `true` if [value] is an indexable JavaScript value at runtime.
+ bool isJsIndexable(covariant AbstractValue value);
+
+ /// Returns `true` if [value] is an indexable and iterable JavaScript value at
/// runtime.
///
/// JavaScript arrays are both indexable and iterable whereas JavaScript
diff --git a/pkg/compiler/lib/src/types/masks.dart b/pkg/compiler/lib/src/types/masks.dart
index aba1f0a..6e5d2c8 100644
--- a/pkg/compiler/lib/src/types/masks.dart
+++ b/pkg/compiler/lib/src/types/masks.dart
@@ -10,11 +10,9 @@
import '../elements/entities.dart';
import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer;
import '../universe/selector.dart' show Selector;
+import '../universe/use.dart' show DynamicUse;
import '../universe/world_builder.dart'
- show
- ReceiverConstraint,
- UniverseSelectorConstraints,
- SelectorConstraintsStrategy;
+ show UniverseSelectorConstraints, SelectorConstraintsStrategy;
import '../util/util.dart';
import '../world.dart' show ClassQuery, ClosedWorld;
import 'abstract_value_domain.dart';
@@ -289,6 +287,11 @@
bool isExact(TypeMask value) => value.isExact || isNull(value);
@override
+ ClassEntity getExactClass(TypeMask mask) {
+ return mask.singleClass(_closedWorld);
+ }
+
+ @override
bool isPrimitiveValue(TypeMask value) => value.isValue;
@override
@@ -557,6 +560,12 @@
}
@override
+ bool isJsIndexable(TypeMask mask) {
+ return mask.satisfies(
+ _closedWorld.commonElements.jsIndexableClass, _closedWorld);
+ }
+
+ @override
bool isJsIndexableAndIterable(covariant TypeMask mask) {
return mask != null &&
mask.satisfies(
diff --git a/pkg/compiler/lib/src/types/type_mask.dart b/pkg/compiler/lib/src/types/type_mask.dart
index d7c9c01..0458cb9 100644
--- a/pkg/compiler/lib/src/types/type_mask.dart
+++ b/pkg/compiler/lib/src/types/type_mask.dart
@@ -66,6 +66,15 @@
UniverseSelectorConstraints createSelectorConstraints(Selector selector) {
return new IncreasingTypeMaskSet();
}
+
+ @override
+ bool appliedUnnamed(
+ DynamicUse dynamicUse, MemberEntity member, covariant ClosedWorld world) {
+ Selector selector = dynamicUse.selector;
+ TypeMask mask = dynamicUse.receiverConstraint;
+ return selector.appliesUnnamed(member) &&
+ (mask == null || mask.canHit(member, selector, world));
+ }
}
/**
@@ -73,7 +82,7 @@
* operations on it are not guaranteed to be precise and they may
* yield conservative answers that contain too many classes.
*/
-abstract class TypeMask implements ReceiverConstraint, AbstractValue {
+abstract class TypeMask implements AbstractValue {
factory TypeMask(
ClassEntity base, int kind, bool isNullable, ClosedWorld closedWorld) {
return new FlatTypeMask.normalized(
@@ -361,6 +370,10 @@
*/
bool canHit(MemberEntity element, Selector selector, ClosedWorld closedWorld);
+ /// Returns whether this [TypeMask] applied to [selector] can hit a
+ /// [noSuchMethod].
+ bool needsNoSuchMethodHandling(Selector selector, ClosedWorld world);
+
/**
* Returns the [element] that is known to always be hit at runtime
* on this mask. Returns null if there is none.
diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
index 2476b2b..8301869 100644
--- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
@@ -256,7 +256,8 @@
void _process(Map<String, Set<_MemberUsage>> memberMap,
EnumSet<MemberUse> action(_MemberUsage usage)) {
_processSet(memberMap, methodName, (_MemberUsage usage) {
- if (dynamicUse.appliesUnnamed(usage.entity, _world)) {
+ if (selectorConstraintsStrategy.appliedUnnamed(
+ dynamicUse, usage.entity, _world)) {
memberUsed(usage.entity, action(usage));
return true;
}
@@ -294,14 +295,14 @@
Map<String, Map<Selector, SelectorConstraints>> selectorMap) {
Selector selector = dynamicUse.selector;
String name = selector.name;
- ReceiverConstraint mask = dynamicUse.mask;
+ Object constraint = dynamicUse.receiverConstraint;
Map<Selector, SelectorConstraints> selectors = selectorMap.putIfAbsent(
name, () => new Maplet<Selector, SelectorConstraints>());
UniverseSelectorConstraints constraints =
selectors.putIfAbsent(selector, () {
return selectorConstraintsStrategy.createSelectorConstraints(selector);
});
- return constraints.addReceiverConstraint(mask);
+ return constraints.addReceiverConstraint(constraint);
}
Map<Selector, SelectorConstraints> _asUnmodifiable(
diff --git a/pkg/compiler/lib/src/universe/resolution_world_builder.dart b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
index f408104..0a77ecc 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -600,7 +600,8 @@
void _process(Map<String, Set<_MemberUsage>> memberMap,
EnumSet<MemberUse> action(_MemberUsage usage)) {
_processSet(memberMap, methodName, (_MemberUsage usage) {
- if (dynamicUse.appliesUnnamed(usage.entity, this)) {
+ if (selectorConstraintsStrategy.appliedUnnamed(
+ dynamicUse, usage.entity, this)) {
memberUsed(usage.entity, action(usage));
return true;
}
@@ -634,7 +635,7 @@
Map<String, Map<Selector, SelectorConstraints>> selectorMap) {
Selector selector = dynamicUse.selector;
String name = selector.name;
- ReceiverConstraint constraint = dynamicUse.mask;
+ Object constraint = dynamicUse.receiverConstraint;
Map<Selector, SelectorConstraints> selectors = selectorMap.putIfAbsent(
name, () => new Maplet<Selector, SelectorConstraints>());
UniverseSelectorConstraints constraints =
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 0fcec7e..8788342 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -22,10 +22,8 @@
import '../elements/entities.dart';
import '../js_model/closure.dart';
import '../util/util.dart' show equalElements, Hashing;
-import '../world.dart' show World;
import 'call_structure.dart' show CallStructure;
import 'selector.dart' show Selector;
-import 'world_builder.dart' show ReceiverConstraint;
enum DynamicUseKind {
INVOKE,
@@ -34,7 +32,7 @@
}
/// The use of a dynamic property. [selector] defined the name and kind of the
-/// property and [mask] defines the known constraint for the object on which
+/// property and [receiverConstraint] defines the known constraint for the object on which
/// the property is accessed.
class DynamicUse {
final Selector selector;
@@ -64,12 +62,7 @@
return sb.toString();
}
- bool appliesUnnamed(MemberEntity element, World world) {
- return selector.appliesUnnamed(element) &&
- (mask == null || mask.canHit(element, selector, world));
- }
-
- ReceiverConstraint get mask => null;
+ Object get receiverConstraint => null;
DynamicUseKind get kind {
if (selector.isGetter) {
@@ -83,18 +76,18 @@
List<DartType> get typeArguments => const <DartType>[];
- int get hashCode =>
- Hashing.listHash(typeArguments, Hashing.objectsHash(selector, mask));
+ int get hashCode => Hashing.listHash(
+ typeArguments, Hashing.objectsHash(selector, receiverConstraint));
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! DynamicUse) return false;
return selector == other.selector &&
- mask == other.mask &&
+ receiverConstraint == other.receiverConstraint &&
equalElements(typeArguments, other.typeArguments);
}
- String toString() => '$selector,$mask';
+ String toString() => '$selector,$receiverConstraint';
}
class GenericDynamicUse extends DynamicUse {
@@ -118,10 +111,11 @@
/// This is used in the codegen phase where receivers are constrained to a
/// type mask or similar.
class ConstrainedDynamicUse extends DynamicUse {
- final ReceiverConstraint mask;
+ final Object receiverConstraint;
final List<DartType> _typeArguments;
- ConstrainedDynamicUse(Selector selector, this.mask, this._typeArguments)
+ ConstrainedDynamicUse(
+ Selector selector, this.receiverConstraint, this._typeArguments)
: super(selector) {
assert(
selector.callStructure.typeArgumentCount ==
diff --git a/pkg/compiler/lib/src/universe/world_builder.dart b/pkg/compiler/lib/src/universe/world_builder.dart
index 44d098b..04f422e 100644
--- a/pkg/compiler/lib/src/universe/world_builder.dart
+++ b/pkg/compiler/lib/src/universe/world_builder.dart
@@ -42,27 +42,6 @@
part 'member_usage.dart';
part 'resolution_world_builder.dart';
-/// The known constraint on receiver for a dynamic call site.
-///
-/// This can for instance be used to constrain this dynamic call to `foo` to
-/// 'receivers of the exact instance `Bar`':
-///
-/// class Bar {
-/// void foo() {}
-/// }
-/// main() => new Bar().foo();
-///
-abstract class ReceiverConstraint {
- /// Returns whether [element] is a potential target when being
- /// invoked on a receiver with this constraint. [selector] is used to ensure
- /// library privacy is taken into account.
- bool canHit(MemberEntity element, Selector selector, covariant World world);
-
- /// Returns whether this [TypeMask] applied to [selector] can hit a
- /// [noSuchMethod].
- bool needsNoSuchMethodHandling(Selector selector, covariant World world);
-}
-
/// The combined constraints on receivers all the dynamic call sites of the same
/// selector.
///
@@ -118,7 +97,7 @@
abstract class UniverseSelectorConstraints extends SelectorConstraints {
/// Adds [constraint] to these selector constraints. Return `true` if the set
/// of potential receivers expanded due to the new constraint.
- bool addReceiverConstraint(covariant ReceiverConstraint constraint);
+ bool addReceiverConstraint(covariant Object constraint);
}
/// Strategy for computing the constraints on potential receivers of dynamic
@@ -127,6 +106,9 @@
/// Create a [UniverseSelectorConstraints] to represent the global receiver
/// constraints for dynamic call sites with [selector].
UniverseSelectorConstraints createSelectorConstraints(Selector selector);
+
+ /// Returns `true` if [member] is a potential target of [dynamicUse].
+ bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member, World world);
}
class OpenWorldStrategy implements SelectorConstraintsStrategy {
@@ -135,6 +117,12 @@
OpenWorldConstraints createSelectorConstraints(Selector selector) {
return new OpenWorldConstraints();
}
+
+ @override
+ bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member, World world) {
+ Selector selector = dynamicUse.selector;
+ return selector.appliesUnnamed(member);
+ }
}
class OpenWorldConstraints extends UniverseSelectorConstraints {
@@ -147,7 +135,7 @@
bool needsNoSuchMethodHandling(Selector selector, World world) => isAll;
@override
- bool addReceiverConstraint(ReceiverConstraint constraint) {
+ bool addReceiverConstraint(Object constraint) {
if (isAll) return false;
isAll = true;
return true;
@@ -174,6 +162,15 @@
StrongModeWorldConstraints createSelectorConstraints(Selector selector) {
return new StrongModeWorldConstraints();
}
+
+ @override
+ bool appliedUnnamed(
+ DynamicUse dynamicUse, MemberEntity member, covariant OpenWorld world) {
+ Selector selector = dynamicUse.selector;
+ StrongModeConstraint constraint = dynamicUse.receiverConstraint;
+ return selector.appliesUnnamed(member) &&
+ (constraint == null || constraint.canHit(member, selector, world));
+ }
}
class StrongModeWorldConstraints extends UniverseSelectorConstraints {
@@ -230,15 +227,13 @@
}
}
-class StrongModeConstraint implements ReceiverConstraint {
+class StrongModeConstraint {
final ClassEntity cls;
const StrongModeConstraint(this.cls);
- @override
bool needsNoSuchMethodHandling(Selector selector, World world) => true;
- @override
bool canHit(MemberEntity element, Selector selector, OpenWorld world) {
return world.isInheritedInSubtypeOf(element, cls);
}
diff --git a/tests/compiler/dart2js/model/enqueuer_test.dart b/tests/compiler/dart2js/model/enqueuer_test.dart
index 1912168..78dd3fe 100644
--- a/tests/compiler/dart2js/model/enqueuer_test.dart
+++ b/tests/compiler/dart2js/model/enqueuer_test.dart
@@ -188,7 +188,7 @@
ElementEnvironment elementEnvironment,
String className,
String methodName,
- ReceiverConstraint Function(ClassEntity cls) createConstraint) {
+ Object Function(ClassEntity cls) createConstraint) {
ClassEntity cls = elementEnvironment.lookupClass(
elementEnvironment.mainLibrary, className);
Selector selector = new Selector.call(
@@ -200,11 +200,8 @@
enqueuer.applyImpact(impact);
}
- void applyImpact(
- Enqueuer enqueuer,
- ElementEnvironment elementEnvironment,
- Impact impact,
- ReceiverConstraint Function(ClassEntity cls) createConstraint) {
+ void applyImpact(Enqueuer enqueuer, ElementEnvironment elementEnvironment,
+ Impact impact, Object Function(ClassEntity cls) createConstraint) {
switch (impact.kind) {
case ImpactKind.instantiate:
instantiate(enqueuer, elementEnvironment, impact.clsName);
@@ -253,7 +250,7 @@
compiler.frontendStrategy.elementEnvironment;
checkInvariant(enqueuer, elementEnvironment);
- ReceiverConstraint createConstraint(ClassEntity cls) {
+ Object createConstraint(ClassEntity cls) {
return new StrongModeConstraint(cls);
}
@@ -268,7 +265,7 @@
compiler.backendClosedWorldForTesting.elementEnvironment;
checkInvariant(enqueuer, elementEnvironment);
- ReceiverConstraint createConstraint(ClassEntity cls) {
+ Object createConstraint(ClassEntity cls) {
return new TypeMask.subtype(cls, closedWorld);
}