Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
index be37c5daea62adebfd90ddd7547fe647d576cced..413fbb9cc54d728797adcc1ef284bd21d8a09bbc 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
@@ -98,6 +98,17 @@ class CodeEmitterTask extends CompilerTask { |
Set<ClassElement> checkedClasses; |
/** |
+ * Set of JS native classes (or 'Object') that need a [:$nativeCheck:] method, |
+ * because they could be used as a type argument in an is-check. |
+ * |
+ * For example, in the following program, the class int needs a native check |
+ * to correctly match integers in the is-check: |
+ * class Check<T> { foo(o) => o is T; } |
+ * main() => new Check<int>().foo(3); |
+ */ |
+ Set<ClassElement> requiredNativeChecks; |
+ |
+ /** |
* Raw Typedef symbols occuring in is-checks and type assertions. If the |
* program contains `x is F<int>` and `x is F<bool>` then the TypedefElement |
* `F` will occur once in [checkedTypedefs]. |
@@ -118,7 +129,44 @@ class CodeEmitterTask extends CompilerTask { |
} |
void computeRequiredTypeChecks() { |
- assert(checkedClasses == null); |
+ assert(checkedClasses == null && |
+ checkedTypedefs == null && |
+ requiredNativeChecks == null); |
+ |
+ // Compute type arguments of classes that potentially use their type |
+ // variables in is-checks and compute the (small) set of classes that |
+ // need native check methods (consult the documentation of |
+ // [requiredNativeChecks] for more information). |
+ requiredNativeChecks = new Set<ClassElement>(); |
+ Link<ClassElement> classes = compiler.world.classesUsingTypeVariableTests; |
+ if (!classes.isEmpty) { |
+ // Find all instantiated types that are a subtype of a class that uses |
+ // one of its type arguments in an is-check and add the arguments to the |
+ // set of is-checks. |
+ for (DartType type in compiler.codegenWorld.instantiatedTypes) { |
+ if (type.kind != TypeKind.INTERFACE) continue; |
+ InterfaceType classType = type; |
+ for (classes = compiler.world.classesUsingTypeVariableTests; |
+ !classes.isEmpty; |
+ classes = classes.tail) { |
+ // We need the type as instance of its superclass anyway, so we just |
+ // try to compute the substitution; if the result is [:null:], the |
+ // classes are not related. |
+ InterfaceType instance = classType.asInstanceOf(classes.head); |
+ if (instance == null) continue; |
+ Link<DartType> typeArguments = instance.typeArguments; |
+ for (DartType argument in typeArguments) { |
+ Element element = argument.element; |
+ JavaScriptBackend backend = compiler.backend; |
+ if (backend.rti.needsNativeCheck(element)) { |
+ requiredNativeChecks.add(element); |
+ } |
+ compiler.codegenWorld.isChecks.add(argument); |
+ } |
+ } |
+ } |
+ } |
+ |
checkedClasses = new Set<ClassElement>(); |
checkedTypedefs = new Set<TypedefElement>(); |
compiler.codegenWorld.isChecks.forEach((DartType t) { |
@@ -1074,7 +1122,7 @@ class CodeEmitterTask extends CompilerTask { |
void generateIsTest(Element other) { |
jsAst.Expression code; |
- if (compiler.objectClass == other) return; |
+ if (other == compiler.objectClass && classElement != other) return; |
karlklose
2013/02/19 08:57:38
Actually we do not need to emit it, because we gen
|
if (nativeEmitter.requiresNativeIsCheck(other)) { |
code = js.fun([], [js.return_(true)]); |
} else { |
@@ -1135,7 +1183,7 @@ class CodeEmitterTask extends CompilerTask { |
} |
} |
- void emitRuntimeClassesAndTests(CodeBuffer buffer) { |
+ void emitRuntimeTypeSupport(CodeBuffer buffer) { |
RuntimeTypeInformation rti = backend.rti; |
TypeChecks typeChecks = rti.getRequiredChecks(); |
@@ -1154,8 +1202,7 @@ class CodeEmitterTask extends CompilerTask { |
if (!needsHolder(cls)) return; |
String holder = namer.isolateAccess(cls); |
String name = namer.getName(cls); |
- buffer.add("$holder$_=$_{builtin\$cls:$_'$name'"); |
- buffer.add('}$N'); |
+ buffer.add("$holder$_=$_{builtin\$cls:$_'$name'}$N"); |
} |
// Create representation objects for classes that we do not have a class |
@@ -1176,6 +1223,16 @@ class CodeEmitterTask extends CompilerTask { |
} |
}; |
} |
+ |
+ // Emit native check methods for the class representations of native types |
+ // that could be used in an is-check against a type variable. |
+ requiredNativeChecks.forEach((ClassElement cls) { |
+ jsAst.Expression nativeCheck = rti.getNativeCheck(cls); |
+ String holder = namer.isolateAccess(cls); |
+ buffer.add('$holder.${namer.getNativeCheckName()}$_=$_'); |
+ buffer.addBuffer(jsAst.prettyPrint(nativeCheck, compiler)); |
+ buffer.add('$N'); |
+ }); |
} |
void visitNativeMixins(ClassElement classElement, |
@@ -1558,6 +1615,11 @@ class CodeEmitterTask extends CompilerTask { |
emitted.add(superclass); |
} |
for (DartType supertype in cls.allSupertypes) { |
+ ClassElement superclass = supertype.element; |
+ if (compiler.world.classesUsingTypeVariableTests.contains(superclass)) { |
+ emitSubstitution(superclass, emitNull: true); |
+ emitted.add(superclass); |
+ } |
for (ClassElement check in checkedClasses) { |
if (supertype.element == check && !emitted.contains(check)) { |
// Generate substitution. If no substitution is necessary, emit |
@@ -2605,7 +2667,7 @@ if (typeof document !== 'undefined' && document.readyState !== 'complete') { |
// We need to finish the classes before we construct compile time |
// constants. |
emitFinishClassesInvocationIfNecessary(mainBuffer); |
- emitRuntimeClassesAndTests(mainBuffer); |
+ emitRuntimeTypeSupport(mainBuffer); |
emitCompileTimeConstants(mainBuffer); |
// Static field initializations require the classes and compile-time |
// constants to be set up. |