Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart |
diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
index 7fd2153184a842c4c8fcec1039c8b22f15ccc75e..3e8f483883404231b25638b08000fbb11f3e9a5d 100644 |
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
@@ -666,6 +666,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
JavaScriptBackend get backend => compiler.backend; |
TypeMaskSystem get typeSystem => lattice.typeSystem; |
types.DartTypes get dartTypes => lattice.dartTypes; |
+ World get classWorld => typeSystem.classWorld; |
Map<Variable, ConstantValue> get values => analyzer.values; |
final InternalErrorFunction internalError; |
@@ -2195,39 +2196,80 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
Primitive visitInterceptor(Interceptor node) { |
AbstractValue value = getValue(node.input.definition); |
- // If the exact class of the input is known, replace with a constant |
- // or the input itself. |
- ClassElement singleClass; |
- if (lattice.isDefinitelyInt(value)) { |
- // Classes like JSUInt31 and JSUInt32 do not exist at runtime, so ensure |
- // all the int classes get mapped tor their runtime class. |
- singleClass = backend.jsIntClass; |
- } else if (lattice.isDefinitelyNum(value)) { |
- if (jsNumberClassSuffices(node)) { |
- singleClass = backend.jsNumberClass; |
- } |
- } else if (lattice.isDefinitelyNativeList(value)) { |
- // Ensure all the array subclasses get mapped to the array class. |
- singleClass = backend.jsArrayClass; |
+ TypeMask interceptedInputs = |
+ value.type.intersection(typeSystem.interceptorType, classWorld); |
+ bool catchNull = |
sra1
2015/10/21 00:46:25
'include' might be a better name than 'catch'.
asgerf
2015/10/21 11:52:46
Changed to 'interceptNull'. Hope that's better.
|
+ node.interceptedClasses.contains(backend.jsNullClass) || |
+ node.interceptedClasses.contains(backend.jsInterceptorClass); |
+ |
+ if (lattice.isDefinitelyInt(value, allowNull: !catchNull)) { |
+ node.interceptedClasses.clear(); |
+ node.interceptedClasses.add(backend.jsIntClass); |
sra1
2015/10/21 00:46:25
Maybe:
node.interceptedClasses..clear()..add(back
asgerf
2015/10/21 11:52:46
Done.
|
+ } else if (lattice.isDefinitelyNum(value, allowNull: !catchNull) && |
+ jsNumberClassSuffices(node)) { |
+ node.interceptedClasses.clear(); |
+ node.interceptedClasses.add(backend.jsNumberClass); |
} else { |
- singleClass = typeSystem.singleClass(value.type); |
- } |
- if (singleClass != null && |
- singleClass.isSubclassOf(backend.jsInterceptorClass)) { |
- node.constantValue = new InterceptorConstantValue(singleClass.rawType); |
+ // Filter out intercepted classes that do not match the input type. |
+ node.interceptedClasses.retainWhere((ClassElement clazz) { |
+ return !typeSystem.areDisjoint( |
+ value.type, |
+ typeSystem.getInterceptorSubtypes(clazz)); |
+ }); |
+ |
+ // The interceptor root class will usually not be filtered out because all |
+ // intercepted values are subtypes of it. But it can be "shadowed" by a |
+ // more specific interceptor classes if all possible intercepted values |
+ // will hit one of the more specific classes. |
+ // We only do this optimization if the resulting interceptor call is |
+ // sufficiently specialized to be worth it (#classes <= 2). |
+ // TODO(asgerf): Reconsider when TypeTest interceptors don't intercept |
+ // ALL interceptor classes. |
+ if (node.interceptedClasses.length > 1 && |
+ node.interceptedClasses.length < 4 && |
+ node.interceptedClasses.contains(backend.jsInterceptorClass)) { |
+ TypeMask specificInterceptors = new TypeMask.unionOf( |
+ node.interceptedClasses |
+ .where((cl) => cl != backend.jsInterceptorClass) |
+ .map(typeSystem.getInterceptorSubtypes), |
+ classWorld); |
+ if (specificInterceptors.containsMask(interceptedInputs, classWorld)) { |
+ // All possible inputs are caught an Interceptor subclass (or are |
+ // self-interceptors), so there is no need to have the check for |
+ // the Interceptor root class (which is expensive). |
+ node.interceptedClasses.remove(backend.jsInterceptorClass); |
+ } |
+ } |
+ |
+ // Remove the interceptor call if it can only return its input. |
+ if (node.interceptedClasses.isEmpty) { |
+ node.input.definition.substituteFor(node); |
+ return null; |
+ } |
} |
- // Filter out intercepted classes that do not match the input type. |
- node.interceptedClasses.retainWhere((ClassElement clazz) { |
- if (clazz == typeSystem.jsNullClass) { |
- return value.isNullable; |
+ |
+ node.flags = Interceptor.ALL_FLAGS; |
+ |
+ // If there is only one caught interceptor class, determine more precisely |
+ // how this might resolve at runtime. Later optimizations depend on this, |
+ // but do not have refined type information available. |
+ if (node.interceptedClasses.length == 1) { |
+ if (value.isDefinitelyNotNull) { |
+ node.clearFlag(Interceptor.NULL); |
+ } else if (catchNull) { |
+ node.clearFlag(Interceptor.NULL_BYPASS); |
} else { |
- TypeMask classMask = typeSystem.nonNullSubclass(clazz); |
- return !typeSystem.areDisjoint(value.type, classMask); |
+ node.clearFlag(Interceptor.NULL_INTERCEPT); |
+ } |
+ if (typeSystem.isDefinitelyIntercepted(value.type, allowNull: true)) { |
+ node.clearFlag(Interceptor.SELF_INTERCEPT); |
+ } |
+ TypeMask interceptedType = |
+ typeSystem.getInterceptorSubtypes(node.interceptedClasses.single); |
+ if (interceptedType.containsMask(interceptedInputs.nonNullable(), |
+ classWorld)) { |
+ node.clearFlag(Interceptor.NON_NULL_BYPASS); |
} |
- }); |
- // Remove the interceptor call if it can only return its input. |
- if (node.interceptedClasses.isEmpty) { |
- node.input.definition.substituteFor(node); |
} |
return null; |
} |
@@ -2899,7 +2941,6 @@ class TypePropagationVisitor implements Visitor { |
void visitGetLazyStatic(GetLazyStatic node) { |
setResult(node, nonConstant(typeSystem.getFieldType(node.element))); |
} |
- |
void visitInterceptor(Interceptor node) { |
push(node.input.definition); |
AbstractValue value = getValue(node.input.definition); |