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