| 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 d014da84d46ff6cc79e20af2c967da923399e495..d3e391f8327d5f24eb4e3aeb30d36e5509473537 100644 | 
| --- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart | 
| +++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart | 
| @@ -108,7 +108,23 @@ class CodeEmitterTask extends CompilerTask { | 
| * program contains `x is F<int>` and `x is F<bool>` then the TypedefElement | 
| * `F` will occur once in [checkedTypedefs]. | 
| */ | 
| -  Set<TypedefElement> checkedTypedefs; | 
| +  Set<FunctionType> checkedFunctionTypes; | 
| + | 
| +  Map<ClassElement, Set<FunctionType>> checkedGenericFunctionTypes | 
| +      = new Map<ClassElement, Set<FunctionType>>(); | 
| + | 
| +  Set<FunctionType> checkedNonGenericFunctionTypes | 
| +      = new Set<FunctionType>(); | 
| + | 
| +  void registerDynamicFunctionTypeCheck(FunctionType functionType) { | 
| +    ClassElement classElement = Types.getClassContext(functionType); | 
| +    if (classElement != null) { | 
| +      checkedGenericFunctionTypes.putIfAbsent(classElement, | 
| +          () => new Set<FunctionType>()).add(functionType); | 
| +    } else { | 
| +      checkedNonGenericFunctionTypes.add(functionType); | 
| +    } | 
| +  } | 
|  | 
| final bool generateSourceMap; | 
|  | 
| @@ -139,17 +155,17 @@ class CodeEmitterTask extends CompilerTask { | 
| } | 
|  | 
| void computeRequiredTypeChecks() { | 
| -    assert(checkedClasses == null && checkedTypedefs == null); | 
| +    assert(checkedClasses == null && checkedFunctionTypes == null); | 
|  | 
| compiler.codegenWorld.addImplicitChecks(classesUsingTypeVariableTests); | 
|  | 
| checkedClasses = new Set<ClassElement>(); | 
| -    checkedTypedefs = new Set<TypedefElement>(); | 
| +    checkedFunctionTypes = new Set<FunctionType>(); | 
| compiler.codegenWorld.isChecks.forEach((DartType t) { | 
| if (t is InterfaceType) { | 
| checkedClasses.add(t.element); | 
| -      } else if (t is TypedefType) { | 
| -        checkedTypedefs.add(t.element); | 
| +      } else if (t is FunctionType) { | 
| +        checkedFunctionTypes.add(t); | 
| } | 
| }); | 
| } | 
| @@ -974,6 +990,19 @@ class CodeEmitterTask extends CompilerTask { | 
| builder.addProperty(namer.operatorIs(other), code); | 
| } | 
|  | 
| +    void generateIsFunctionTypeTest(FunctionType type) { | 
| +      builder.addProperty(namer.operatorIsFunctionType(type), | 
| +          new jsAst.LiteralBool(true)); | 
| +    } | 
| + | 
| +    void generateFunctionTypeSignature(FunctionType type) { | 
| +      RuntimeTypeInformation rti = backend.rti; | 
| +      String encoding = rti.getTypeEncoding(type, alwaysGenerateFunction: true); | 
| +      String operatorSignature = namer.operatorSignature(); | 
| +      builder.addProperty(operatorSignature, | 
| +          new jsAst.LiteralExpression(encoding)); | 
| +    } | 
| + | 
| void generateSubstitution(Element other, {bool emitNull: false}) { | 
| RuntimeTypeInformation rti = backend.rti; | 
| // TODO(karlklose): support typedefs with variables. | 
| @@ -996,7 +1025,9 @@ class CodeEmitterTask extends CompilerTask { | 
| } | 
| } | 
|  | 
| -    generateIsTestsOn(classElement, generateIsTest, generateSubstitution); | 
| +    generateIsTestsOn(classElement, generateIsTest, | 
| +        generateIsFunctionTypeTest, generateFunctionTypeSignature, | 
| +        generateSubstitution); | 
|  | 
| if (identical(classElement, compiler.objectClass) | 
| && compiler.enabledNoSuchMethod) { | 
| @@ -1054,6 +1085,19 @@ class CodeEmitterTask extends CompilerTask { | 
| } | 
| }; | 
| } | 
| + | 
| +    checkedNonGenericFunctionTypes.forEach((FunctionType type) { | 
| +      String encoding = rti.getTypeEncoding(type); | 
| +      buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); | 
| +    }); | 
| + | 
| +    checkedGenericFunctionTypes.forEach( | 
| +        (ClassElement cls, Set<FunctionType> functionTypes) { | 
| +          for (FunctionType type in functionTypes) { | 
| +            String encoding = rti.getTypeEncoding(type); | 
| +            buffer.add('${namer.signatureName(type)}$_=${_}$encoding$N'); | 
| +          } | 
| +        }); | 
| } | 
|  | 
| void visitNativeMixins(ClassElement classElement, | 
| @@ -1389,14 +1433,26 @@ class CodeEmitterTask extends CompilerTask { | 
| return _selectorRank(selector1) - _selectorRank(selector2); | 
| } | 
|  | 
| -  Iterable<Element> getTypedefChecksOn(DartType type) { | 
| -    bool isSubtype(TypedefElement typedef) { | 
| -      FunctionType typedefType = | 
| -          typedef.computeType(compiler).unalias(compiler); | 
| -      return compiler.types.isSubtype(type, typedefType); | 
| +  /** | 
| +   * Returns a mapping containing all checked function types for which [type] | 
| +   * can be a subtype. A function type is mapped to [:true:] if [type] is | 
| +   * statically known to be a subtype of it. | 
| +   */ | 
| +  // TODO(johnniwinther): Change to return a mapping from function types to | 
| +  // a set of variable points and use this to detect statically/dynamically | 
| +  // known subtype relations. | 
| +  Map<FunctionType, bool> getFunctionTypeChecksOn(DartType type) { | 
| +    Map<FunctionType, bool> functionTypeMap = | 
| +        new LinkedHashMap<FunctionType, bool>(); | 
| +    for (FunctionType functionType in checkedFunctionTypes) { | 
| +      if (compiler.types.isSubtype(type, functionType)) { | 
| +        functionTypeMap[functionType] = true; | 
| +      } else if (compiler.types.isPotentialSubtype(type, functionType)) { | 
| +        functionTypeMap[functionType] = false; | 
| +      } | 
| } | 
| -    return checkedTypedefs.where(isSubtype).toList() | 
| -        ..sort(Elements.compareByPosition); | 
| +    // TODO(johnniwinther): Ensure stable ordering of the keys. | 
| +    return functionTypeMap; | 
| } | 
|  | 
| /** | 
| @@ -1408,6 +1464,8 @@ class CodeEmitterTask extends CompilerTask { | 
| */ | 
| void generateIsTestsOn(ClassElement cls, | 
| void emitIsTest(Element element), | 
| +                         void emitIsFunctionTypeTest(FunctionType type), | 
| +                         void emitFunctionTypeSignature(FunctionType type), | 
| void emitSubstitution(Element element, {emitNull})) { | 
| if (checkedClasses.contains(cls)) { | 
| emitIsTest(cls); | 
| @@ -1459,7 +1517,7 @@ class CodeEmitterTask extends CompilerTask { | 
| // A class that defines a [:call:] method implicitly implements | 
| // [Function] and needs checks for all typedefs that are used in is-checks. | 
| if (checkedClasses.contains(compiler.functionClass) || | 
| -        !checkedTypedefs.isEmpty) { | 
| +        !checkedFunctionTypes.isEmpty) { | 
| FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME); | 
| if (call == null) { | 
| // If [cls] is a closure, it has a synthetic call operator method. | 
| @@ -1470,7 +1528,21 @@ class CodeEmitterTask extends CompilerTask { | 
| emitIsTest, | 
| emitSubstitution, | 
| generated); | 
| -        getTypedefChecksOn(call.computeType(compiler)).forEach(emitIsTest); | 
| +        FunctionType callType = call.computeType(compiler); | 
| +        Map<FunctionType,bool> functionTypeChecks = | 
| +            getFunctionTypeChecksOn(callType); | 
| +        bool hasDynamicFunctionTypeCheck = false; | 
| +        functionTypeChecks.forEach((FunctionType functionType, | 
| +                                    bool knownSubtype) { | 
| +          emitIsFunctionTypeTest(functionType); | 
| +          if (!knownSubtype) { | 
| +            registerDynamicFunctionTypeCheck(functionType); | 
| +            hasDynamicFunctionTypeCheck = true; | 
| +          } | 
| +        }); | 
| +        if (hasDynamicFunctionTypeCheck) { | 
| +          emitFunctionTypeSignature(callType); | 
| +        } | 
| } | 
| } | 
|  | 
| @@ -1653,12 +1725,28 @@ class CodeEmitterTask extends CompilerTask { | 
| // in case it is used in spawnFunction. | 
| String fieldName = namer.STATIC_CLOSURE_NAME_NAME; | 
| buffer.write('$fieldAccess.$fieldName$_=$_"$staticName"$N'); | 
| -      getTypedefChecksOn(element.computeType(compiler)).forEach( | 
| -        (Element typedef) { | 
| -          String operator = namer.operatorIs(typedef); | 
| -          buffer.write('$fieldAccess.$operator$_=${_}true$N'); | 
| +      FunctionType elementType = element.computeType(compiler); | 
| +      Map<FunctionType,bool> functionTypeChecks = | 
| +          getFunctionTypeChecksOn(elementType); | 
| +      bool hasDynamicFunctionTypeCheck = false; | 
| +      functionTypeChecks.forEach((FunctionType functionType, | 
| +                                  bool knownSubtype) { | 
| +        String operator = namer.operatorIsFunctionType(functionType); | 
| +        buffer.write('$fieldAccess.$operator$_=${_}true$N'); | 
| +        if (!knownSubtype) { | 
| +          TypeVariableType typeVariable = functionType.typeVariableOccurrence; | 
| +          registerDynamicFunctionTypeCheck(functionType); | 
| +          hasDynamicFunctionTypeCheck = true; | 
| } | 
| -      ); | 
| +      }); | 
| +      if (hasDynamicFunctionTypeCheck) { | 
| +        RuntimeTypeInformation rti = backend.rti; | 
| +        String encoding = rti.getTypeEncoding(elementType, | 
| +            alwaysGenerateFunction: true); | 
| +        String operatorSignature = namer.operatorSignature(); | 
| +        buffer.add('$fieldAccess.$operatorSignature$_=${_}$encoding$N'); | 
| +      } | 
| + | 
| } | 
| } | 
|  | 
| @@ -1719,12 +1807,14 @@ class CodeEmitterTask extends CompilerTask { | 
| : inInterceptor ? const ['self', 'target', 'receiver'] | 
| : const ['self', 'target']; | 
|  | 
| -    Iterable<Element> typedefChecks = | 
| -        getTypedefChecksOn(member.computeType(compiler)); | 
| -    bool hasTypedefChecks = !typedefChecks.isEmpty; | 
| +    DartType memberType = member.computeType(compiler); | 
| +    Map<FunctionType, bool> functionTypeChecks = | 
| +        getFunctionTypeChecksOn(memberType); | 
| +    bool hasFunctionTypeChecks = !functionTypeChecks.isEmpty; | 
|  | 
| -    bool canBeShared = !hasOptionalParameters && !hasTypedefChecks; | 
| +    bool canBeShared = !hasOptionalParameters && !hasFunctionTypeChecks; | 
|  | 
| +    ClassElement classElement = member.getEnclosingClass(); | 
| String closureClass = canBeShared ? cache[parameterCount] : null; | 
| if (closureClass == null) { | 
| // Either the class was not cached yet, or there are optional parameters. | 
| @@ -1781,10 +1871,24 @@ class CodeEmitterTask extends CompilerTask { | 
| boundClosureBuilder.addProperty(invocationName, fun); | 
|  | 
| addParameterStubs(callElement, boundClosureBuilder.addProperty); | 
| -      typedefChecks.forEach((Element typedef) { | 
| -        String operator = namer.operatorIs(typedef); | 
| +      bool hasDynamicFunctionTypeCheck = false; | 
| +      functionTypeChecks.forEach((FunctionType functionType, | 
| +                                  bool knownSubtype) { | 
| +        String operator = namer.operatorIsFunctionType(functionType); | 
| boundClosureBuilder.addProperty(operator, new jsAst.LiteralBool(true)); | 
| +        if (!knownSubtype) { | 
| +          registerDynamicFunctionTypeCheck(functionType); | 
| +          hasDynamicFunctionTypeCheck = true; | 
| +        } | 
| }); | 
| +      if (hasDynamicFunctionTypeCheck) { | 
| +        RuntimeTypeInformation rti = backend.rti; | 
| +        String encoding = rti.getTypeEncoding(memberType, | 
| +            alwaysGenerateFunction: true); | 
| +        String operatorSignature = namer.operatorSignature(); | 
| +        boundClosureBuilder.addProperty(operatorSignature, | 
| +            new jsAst.LiteralExpression(encoding)); | 
| +      } | 
|  | 
| boundClosures.add( | 
| js[classesCollector][mangledName].assign( | 
| @@ -1811,14 +1915,34 @@ class CodeEmitterTask extends CompilerTask { | 
| arguments.add(js[extraArg]); | 
| } | 
|  | 
| +    jsAst.Expression newClosure = js[closureClass].newWith(arguments); | 
| jsAst.Expression getterFunction = js.fun( | 
| parameters, | 
| -        js.return_(js[closureClass].newWith(arguments))); | 
| +        forwardRuntimeTypeInfo(newClosure, memberType, classElement)); | 
|  | 
| defineStub(getterName, getterFunction); | 
| } | 
|  | 
| /** | 
| +   * Wraps the [newExpression] of a closure with code for setting the runtime | 
| +   * type information if needed. | 
| +   */ | 
| +  forwardRuntimeTypeInfo(jsAst.Expression newExpression, | 
| +                         DartType memberType, | 
| +                         ClassElement thisClass) { | 
| +    if (compiler.world.needsRti(thisClass) && | 
| +        memberType.containsTypeVariables) { | 
| +      String forwardRuntimeTypeInfo = | 
| +          namer.isolateAccess(backend.getForwardRuntimeTypeInfo()); | 
| +      String substitution = namer.substitutionName(thisClass); | 
| +      return js.return_(new jsAst.Call(js[forwardRuntimeTypeInfo], | 
| +          [newExpression, js['this.$substitution'], js['this']])); | 
| +    } else { | 
| +      return js.return_(newExpression); | 
| +    } | 
| +  } | 
| + | 
| +  /** | 
| * Documentation wanted -- johnniwinther | 
| * | 
| * Invariant: [member] must be a declaration element. | 
| @@ -1853,7 +1977,7 @@ class CodeEmitterTask extends CompilerTask { | 
| // identical stubs for each we track untyped selectors which already have | 
| // stubs. | 
| Set<Selector> generatedSelectors = new Set<Selector>(); | 
| - | 
| +    DartType memberType = member.computeType(compiler); | 
| for (Selector selector in selectors) { | 
| if (selector.applies(member, compiler)) { | 
| selector = selector.asUntyped; | 
|  |