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 |