| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 // Function signatures used in the generation of runtime type information. | 7 // Function signatures used in the generation of runtime type information. |
| 8 typedef void FunctionTypeSignatureEmitter(Element method, | 8 typedef void FunctionTypeSignatureEmitter( |
| 9 FunctionType methodType); | 9 Element method, FunctionType methodType); |
| 10 | 10 |
| 11 typedef void SubstitutionEmitter(Element element, {bool emitNull}); | 11 typedef void SubstitutionEmitter(Element element, {bool emitNull}); |
| 12 | 12 |
| 13 class TypeTestProperties { | 13 class TypeTestProperties { |
| 14 /// The index of the function type into the metadata. | 14 /// The index of the function type into the metadata. |
| 15 /// | 15 /// |
| 16 /// If the class doesn't have a function type this field is `null`. | 16 /// If the class doesn't have a function type this field is `null`. |
| 17 /// | 17 /// |
| 18 /// If the is tests were generated with `storeFunctionTypeInMetadata` set to | 18 /// If the is tests were generated with `storeFunctionTypeInMetadata` set to |
| 19 /// `false`, this field is `null`, and the [properties] contain a property | 19 /// `false`, this field is `null`, and the [properties] contain a property |
| (...skipping 10 matching lines...) Expand all Loading... |
| 30 final Compiler compiler; | 30 final Compiler compiler; |
| 31 final CodeEmitterTask emitterTask; | 31 final CodeEmitterTask emitterTask; |
| 32 final Namer namer; | 32 final Namer namer; |
| 33 | 33 |
| 34 RuntimeTypeGenerator(this.compiler, this.emitterTask, this.namer); | 34 RuntimeTypeGenerator(this.compiler, this.emitterTask, this.namer); |
| 35 | 35 |
| 36 JavaScriptBackend get backend => compiler.backend; | 36 JavaScriptBackend get backend => compiler.backend; |
| 37 TypeTestRegistry get typeTestRegistry => emitterTask.typeTestRegistry; | 37 TypeTestRegistry get typeTestRegistry => emitterTask.typeTestRegistry; |
| 38 CoreClasses get coreClasses => compiler.coreClasses; | 38 CoreClasses get coreClasses => compiler.coreClasses; |
| 39 | 39 |
| 40 Set<ClassElement> get checkedClasses => | 40 Set<ClassElement> get checkedClasses => typeTestRegistry.checkedClasses; |
| 41 typeTestRegistry.checkedClasses; | |
| 42 | 41 |
| 43 Iterable<ClassElement> get classesUsingTypeVariableTests => | 42 Iterable<ClassElement> get classesUsingTypeVariableTests => |
| 44 typeTestRegistry.classesUsingTypeVariableTests; | 43 typeTestRegistry.classesUsingTypeVariableTests; |
| 45 | 44 |
| 46 Set<FunctionType> get checkedFunctionTypes => | 45 Set<FunctionType> get checkedFunctionTypes => |
| 47 typeTestRegistry.checkedFunctionTypes; | 46 typeTestRegistry.checkedFunctionTypes; |
| 48 | 47 |
| 49 /// Generates all properties necessary for is-checks on the [classElement]. | 48 /// Generates all properties necessary for is-checks on the [classElement]. |
| 50 /// | 49 /// |
| 51 /// Returns an instance of [TypeTestProperties] that contains the properties | 50 /// Returns an instance of [TypeTestProperties] that contains the properties |
| 52 /// that must be installed on the prototype of the JS constructor of the | 51 /// that must be installed on the prototype of the JS constructor of the |
| 53 /// [classElement]. | 52 /// [classElement]. |
| 54 /// | 53 /// |
| 55 /// If [storeFunctionTypeInMetadata] is `true`, stores the reified function | 54 /// If [storeFunctionTypeInMetadata] is `true`, stores the reified function |
| 56 /// type (if class has one) in the metadata object and stores its index in | 55 /// type (if class has one) in the metadata object and stores its index in |
| 57 /// the result. This is only possible for function types that do not contain | 56 /// the result. This is only possible for function types that do not contain |
| 58 /// type variables. | 57 /// type variables. |
| 59 TypeTestProperties generateIsTests( | 58 TypeTestProperties generateIsTests(ClassElement classElement, |
| 60 ClassElement classElement, | 59 {bool storeFunctionTypeInMetadata: true}) { |
| 61 { bool storeFunctionTypeInMetadata: true}) { | |
| 62 assert(invariant(classElement, classElement.isDeclaration)); | 60 assert(invariant(classElement, classElement.isDeclaration)); |
| 63 | 61 |
| 64 TypeTestProperties result = new TypeTestProperties(); | 62 TypeTestProperties result = new TypeTestProperties(); |
| 65 | 63 |
| 66 /// Generates an is-test if the test is not inherited from a superclass | 64 /// Generates an is-test if the test is not inherited from a superclass |
| 67 /// This assumes that for every class an is-tests is generated | 65 /// This assumes that for every class an is-tests is generated |
| 68 /// dynamically at runtime. We also always generate tests against | 66 /// dynamically at runtime. We also always generate tests against |
| 69 /// native classes. | 67 /// native classes. |
| 70 /// TODO(herhut): Generate tests for native classes dynamically, as well. | 68 /// TODO(herhut): Generate tests for native classes dynamically, as well. |
| 71 void generateIsTest(Element other) { | 69 void generateIsTest(Element other) { |
| 72 if (backend.isNative(classElement) || | 70 if (backend.isNative(classElement) || !classElement.isSubclassOf(other)) { |
| 73 !classElement.isSubclassOf(other)) { | |
| 74 result.properties[namer.operatorIs(other)] = js('1'); | 71 result.properties[namer.operatorIs(other)] = js('1'); |
| 75 } | 72 } |
| 76 } | 73 } |
| 77 | 74 |
| 78 void generateFunctionTypeSignature(FunctionElement method, | 75 void generateFunctionTypeSignature( |
| 79 FunctionType type) { | 76 FunctionElement method, FunctionType type) { |
| 80 assert(method.isImplementation); | 77 assert(method.isImplementation); |
| 81 jsAst.Expression thisAccess = new jsAst.This(); | 78 jsAst.Expression thisAccess = new jsAst.This(); |
| 82 ClosureClassMap closureData = | 79 ClosureClassMap closureData = |
| 83 compiler.closureToClassMapper.closureMappingCache[method.node]; | 80 compiler.closureToClassMapper.closureMappingCache[method.node]; |
| 84 if (closureData != null) { | 81 if (closureData != null) { |
| 85 ClosureFieldElement thisLocal = | 82 ClosureFieldElement thisLocal = |
| 86 closureData.freeVariableMap[closureData.thisLocal]; | 83 closureData.freeVariableMap[closureData.thisLocal]; |
| 87 if (thisLocal != null) { | 84 if (thisLocal != null) { |
| 88 jsAst.Name thisName = namer.instanceFieldPropertyName(thisLocal); | 85 jsAst.Name thisName = namer.instanceFieldPropertyName(thisLocal); |
| 89 thisAccess = js('this.#', thisName); | 86 thisAccess = js('this.#', thisName); |
| 90 } | 87 } |
| 91 } | 88 } |
| 92 | 89 |
| 93 if (storeFunctionTypeInMetadata && !type.containsTypeVariables) { | 90 if (storeFunctionTypeInMetadata && !type.containsTypeVariables) { |
| 94 result.functionTypeIndex = | 91 result.functionTypeIndex = |
| 95 emitterTask.metadataCollector.reifyType(type); | 92 emitterTask.metadataCollector.reifyType(type); |
| 96 } else { | 93 } else { |
| 97 RuntimeTypesEncoder rtiEncoder = backend.rtiEncoder; | 94 RuntimeTypesEncoder rtiEncoder = backend.rtiEncoder; |
| 98 jsAst.Expression encoding = | 95 jsAst.Expression encoding = |
| 99 rtiEncoder.getSignatureEncoding(type, thisAccess); | 96 rtiEncoder.getSignatureEncoding(type, thisAccess); |
| 100 jsAst.Name operatorSignature = | 97 jsAst.Name operatorSignature = namer.asName(namer.operatorSignature); |
| 101 namer.asName(namer.operatorSignature); | |
| 102 result.properties[operatorSignature] = encoding; | 98 result.properties[operatorSignature] = encoding; |
| 103 } | 99 } |
| 104 } | 100 } |
| 105 | 101 |
| 106 void generateSubstitution(ClassElement cls, {bool emitNull: false}) { | 102 void generateSubstitution(ClassElement cls, {bool emitNull: false}) { |
| 107 if (cls.typeVariables.isEmpty) return; | 103 if (cls.typeVariables.isEmpty) return; |
| 108 RuntimeTypes rti = backend.rti; | 104 RuntimeTypes rti = backend.rti; |
| 109 RuntimeTypesEncoder rtiEncoder = backend.rtiEncoder; | 105 RuntimeTypesEncoder rtiEncoder = backend.rtiEncoder; |
| 110 jsAst.Expression expression; | 106 jsAst.Expression expression; |
| 111 bool needsNativeCheck = | 107 bool needsNativeCheck = |
| (...skipping 15 matching lines...) Expand all Loading... |
| 127 generateIsTest(checkedClass); | 123 generateIsTest(checkedClass); |
| 128 Substitution substitution = check.substitution; | 124 Substitution substitution = check.substitution; |
| 129 if (substitution != null) { | 125 if (substitution != null) { |
| 130 jsAst.Expression body = | 126 jsAst.Expression body = |
| 131 backend.rtiEncoder.getSubstitutionCode(substitution); | 127 backend.rtiEncoder.getSubstitutionCode(substitution); |
| 132 result.properties[namer.substitutionName(checkedClass)] = body; | 128 result.properties[namer.substitutionName(checkedClass)] = body; |
| 133 } | 129 } |
| 134 } | 130 } |
| 135 | 131 |
| 136 _generateIsTestsOn(classElement, generateIsTest, | 132 _generateIsTestsOn(classElement, generateIsTest, |
| 137 generateFunctionTypeSignature, | 133 generateFunctionTypeSignature, generateSubstitution, generateTypeCheck); |
| 138 generateSubstitution, | |
| 139 generateTypeCheck); | |
| 140 | 134 |
| 141 return result; | 135 return result; |
| 142 } | 136 } |
| 143 | 137 |
| 144 /** | 138 /** |
| 145 * Generate "is tests" for [cls] itself, and the "is tests" for the | 139 * Generate "is tests" for [cls] itself, and the "is tests" for the |
| 146 * classes it implements and type argument substitution functions for these | 140 * classes it implements and type argument substitution functions for these |
| 147 * tests. We don't need to add the "is tests" of the super class because | 141 * tests. We don't need to add the "is tests" of the super class because |
| 148 * they will be inherited at runtime, but we may need to generate the | 142 * they will be inherited at runtime, but we may need to generate the |
| 149 * substitutions, because they may have changed. | 143 * substitutions, because they may have changed. |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 | 198 |
| 205 if (classesUsingTypeVariableTests.contains(superclass) || | 199 if (classesUsingTypeVariableTests.contains(superclass) || |
| 206 checkedClasses.contains(superclass)) { | 200 checkedClasses.contains(superclass)) { |
| 207 // Generate substitution. If no substitution is necessary, emit | 201 // Generate substitution. If no substitution is necessary, emit |
| 208 // `null` to overwrite a (possibly) existing substitution from the | 202 // `null` to overwrite a (possibly) existing substitution from the |
| 209 // super classes. | 203 // super classes. |
| 210 generateSubstitution(superclass, emitNull: true); | 204 generateSubstitution(superclass, emitNull: true); |
| 211 } | 205 } |
| 212 } | 206 } |
| 213 | 207 |
| 214 void emitNothing(_, {emitNull}) {}; | 208 void emitNothing(_, {emitNull}) {} |
| 209 ; |
| 215 | 210 |
| 216 generateSubstitution = emitNothing; | 211 generateSubstitution = emitNothing; |
| 217 } | 212 } |
| 218 | 213 |
| 219 // A class that defines a `call` method implicitly implements | 214 // A class that defines a `call` method implicitly implements |
| 220 // [Function] and needs checks for all typedefs that are used in is-checks. | 215 // [Function] and needs checks for all typedefs that are used in is-checks. |
| 221 if (checkedClasses.contains(coreClasses.functionClass) || | 216 if (checkedClasses.contains(coreClasses.functionClass) || |
| 222 checkedFunctionTypes.isNotEmpty) { | 217 checkedFunctionTypes.isNotEmpty) { |
| 223 Element call = cls.lookupLocalMember(Identifiers.call); | 218 Element call = cls.lookupLocalMember(Identifiers.call); |
| 224 if (call == null) { | 219 if (call == null) { |
| 225 // If [cls] is a closure, it has a synthetic call operator method. | 220 // If [cls] is a closure, it has a synthetic call operator method. |
| 226 call = cls.lookupBackendMember(Identifiers.call); | 221 call = cls.lookupBackendMember(Identifiers.call); |
| 227 } | 222 } |
| 228 if (call != null && call.isFunction) { | 223 if (call != null && call.isFunction) { |
| 229 FunctionElement callFunction = call; | 224 FunctionElement callFunction = call; |
| 230 // A superclass might already implement the Function interface. In such | 225 // A superclass might already implement the Function interface. In such |
| 231 // a case, we can avoid emiting the is test here. | 226 // a case, we can avoid emiting the is test here. |
| 232 if (!cls.superclass.implementsFunction(coreClasses)) { | 227 if (!cls.superclass.implementsFunction(coreClasses)) { |
| 233 _generateInterfacesIsTests(coreClasses.functionClass, | 228 _generateInterfacesIsTests(coreClasses.functionClass, generateIsTest, |
| 234 generateIsTest, | 229 generateSubstitution, generated); |
| 235 generateSubstitution, | |
| 236 generated); | |
| 237 } | 230 } |
| 238 FunctionType callType = callFunction.computeType(compiler.resolution); | 231 FunctionType callType = callFunction.computeType(compiler.resolution); |
| 239 generateFunctionTypeSignature(callFunction, callType); | 232 generateFunctionTypeSignature(callFunction, callType); |
| 240 } | 233 } |
| 241 } | 234 } |
| 242 | 235 |
| 243 for (DartType interfaceType in cls.interfaces) { | 236 for (DartType interfaceType in cls.interfaces) { |
| 244 _generateInterfacesIsTests(interfaceType.element, generateIsTest, | 237 _generateInterfacesIsTests(interfaceType.element, generateIsTest, |
| 245 generateSubstitution, generated); | 238 generateSubstitution, generated); |
| 246 } | 239 } |
| 247 } | 240 } |
| 248 | 241 |
| 249 /** | 242 /** |
| 250 * Generate "is tests" where [cls] is being implemented. | 243 * Generate "is tests" where [cls] is being implemented. |
| 251 */ | 244 */ |
| 252 void _generateInterfacesIsTests(ClassElement cls, | 245 void _generateInterfacesIsTests( |
| 253 void generateIsTest(ClassElement element), | 246 ClassElement cls, |
| 254 SubstitutionEmitter generateSubstitution, | 247 void generateIsTest(ClassElement element), |
| 255 Set<Element> alreadyGenerated) { | 248 SubstitutionEmitter generateSubstitution, |
| 249 Set<Element> alreadyGenerated) { |
| 256 void tryEmitTest(ClassElement check) { | 250 void tryEmitTest(ClassElement check) { |
| 257 if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) { | 251 if (!alreadyGenerated.contains(check) && checkedClasses.contains(check)) { |
| 258 alreadyGenerated.add(check); | 252 alreadyGenerated.add(check); |
| 259 generateIsTest(check); | 253 generateIsTest(check); |
| 260 generateSubstitution(check); | 254 generateSubstitution(check); |
| 261 } | 255 } |
| 262 }; | 256 } |
| 257 ; |
| 263 | 258 |
| 264 tryEmitTest(cls); | 259 tryEmitTest(cls); |
| 265 | 260 |
| 266 for (DartType interfaceType in cls.interfaces) { | 261 for (DartType interfaceType in cls.interfaces) { |
| 267 Element element = interfaceType.element; | 262 Element element = interfaceType.element; |
| 268 tryEmitTest(element); | 263 tryEmitTest(element); |
| 269 _generateInterfacesIsTests(element, generateIsTest, generateSubstitution, | 264 _generateInterfacesIsTests( |
| 270 alreadyGenerated); | 265 element, generateIsTest, generateSubstitution, alreadyGenerated); |
| 271 } | 266 } |
| 272 | 267 |
| 273 // We need to also emit "is checks" for the superclass and its supertypes. | 268 // We need to also emit "is checks" for the superclass and its supertypes. |
| 274 ClassElement superclass = cls.superclass; | 269 ClassElement superclass = cls.superclass; |
| 275 if (superclass != null) { | 270 if (superclass != null) { |
| 276 tryEmitTest(superclass); | 271 tryEmitTest(superclass); |
| 277 _generateInterfacesIsTests(superclass, generateIsTest, | 272 _generateInterfacesIsTests( |
| 278 generateSubstitution, alreadyGenerated); | 273 superclass, generateIsTest, generateSubstitution, alreadyGenerated); |
| 279 } | 274 } |
| 280 } | 275 } |
| 281 | 276 |
| 282 List<StubMethod> generateTypeVariableReaderStubs(ClassElement classElement) { | 277 List<StubMethod> generateTypeVariableReaderStubs(ClassElement classElement) { |
| 283 List<StubMethod> stubs = <StubMethod>[]; | 278 List<StubMethod> stubs = <StubMethod>[]; |
| 284 ClassElement superclass = classElement; | 279 ClassElement superclass = classElement; |
| 285 while (superclass != null) { | 280 while (superclass != null) { |
| 286 for (TypeVariableType parameter in superclass.typeVariables) { | 281 for (TypeVariableType parameter in superclass.typeVariables) { |
| 287 if (backend.emitter.readTypeVariables.contains(parameter.element)) { | 282 if (backend.emitter.readTypeVariables.contains(parameter.element)) { |
| 288 stubs.add( | 283 stubs.add( |
| 289 _generateTypeVariableReader(classElement, parameter.element)); | 284 _generateTypeVariableReader(classElement, parameter.element)); |
| 290 } | |
| 291 } | 285 } |
| 292 superclass = superclass.superclass; | |
| 293 } | 286 } |
| 287 superclass = superclass.superclass; |
| 288 } |
| 294 | 289 |
| 295 return stubs; | 290 return stubs; |
| 296 } | 291 } |
| 297 | 292 |
| 298 StubMethod _generateTypeVariableReader(ClassElement cls, | 293 StubMethod _generateTypeVariableReader( |
| 299 TypeVariableElement element) { | 294 ClassElement cls, TypeVariableElement element) { |
| 300 jsAst.Name name = namer.nameForReadTypeVariable(element); | 295 jsAst.Name name = namer.nameForReadTypeVariable(element); |
| 301 int index = element.index; | 296 int index = element.index; |
| 302 jsAst.Expression computeTypeVariable; | 297 jsAst.Expression computeTypeVariable; |
| 303 | 298 |
| 304 Substitution substitution = | 299 Substitution substitution = |
| 305 backend.rti.getSubstitution(cls, element.typeDeclaration); | 300 backend.rti.getSubstitution(cls, element.typeDeclaration); |
| 306 if (substitution != null) { | 301 if (substitution != null) { |
| 307 computeTypeVariable = | 302 computeTypeVariable = js( |
| 308 js(r'#.apply(null, this.$builtinTypeInfo)', | 303 r'#.apply(null, this.$builtinTypeInfo)', |
| 309 backend.rtiEncoder.getSubstitutionCodeForVariable( | 304 backend.rtiEncoder |
| 310 substitution, index)); | 305 .getSubstitutionCodeForVariable(substitution, index)); |
| 311 } else { | 306 } else { |
| 312 // TODO(ahe): These can be generated dynamically. | 307 // TODO(ahe): These can be generated dynamically. |
| 313 computeTypeVariable = | 308 computeTypeVariable = js( |
| 314 js(r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]', | 309 r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]', |
| 315 js.number(index)); | 310 js.number(index)); |
| 316 } | 311 } |
| 317 jsAst.Expression convertRtiToRuntimeType = backend.emitter | 312 jsAst.Expression convertRtiToRuntimeType = backend.emitter |
| 318 .staticFunctionAccess(backend.helpers.convertRtiToRuntimeType); | 313 .staticFunctionAccess(backend.helpers.convertRtiToRuntimeType); |
| 319 | 314 |
| 320 return new StubMethod(name, | 315 return new StubMethod( |
| 321 js('function () { return #(#) }', | 316 name, |
| 322 [convertRtiToRuntimeType, computeTypeVariable])); | 317 js('function () { return #(#) }', |
| 318 [convertRtiToRuntimeType, computeTypeVariable])); |
| 323 } | 319 } |
| 324 } | 320 } |
| OLD | NEW |