| 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 library dart2js.js_emitter.runtime_type_generator; | 5 library dart2js.js_emitter.runtime_type_generator; |
| 6 | 6 |
| 7 import '../closure.dart' | 7 import '../closure.dart' |
| 8 show ClosureRepresentationInfo, ClosureFieldElement, ClosureConversionTask; | 8 show ClosureRepresentationInfo, ClosureFieldElement, ClosureConversionTask; |
| 9 import '../common.dart'; | 9 import '../common.dart'; |
| 10 import '../common/names.dart' show Identifiers; | 10 import '../common/names.dart' show Identifiers; |
| 11 import '../common_elements.dart' show CommonElements, ElementEnvironment; | 11 import '../common_elements.dart' show CommonElements, ElementEnvironment; |
| 12 import '../elements/elements.dart' | 12 import '../elements/elements.dart' |
| 13 show ClassElement, MethodElement, MixinApplicationElement; | 13 show ClassElement, MethodElement, MixinApplicationElement; |
| 14 import '../elements/entities.dart'; | 14 import '../elements/entities.dart'; |
| 15 import '../elements/types.dart'; | 15 import '../elements/types.dart'; |
| 16 import '../js/js.dart' as jsAst; | 16 import '../js/js.dart' as jsAst; |
| 17 import '../js/js.dart' show js; | 17 import '../js/js.dart' show js; |
| 18 import '../js_backend/js_interop_analysis.dart'; | 18 import '../js_backend/js_interop_analysis.dart'; |
| 19 import '../js_backend/native_data.dart'; | 19 import '../js_backend/native_data.dart'; |
| 20 import '../js_backend/namer.dart' show Namer; | 20 import '../js_backend/namer.dart' show Namer; |
| 21 import '../js_backend/runtime_types.dart' | 21 import '../js_backend/runtime_types.dart' |
| 22 show | 22 show |
| 23 RuntimeTypesChecks, | 23 RuntimeTypesChecks, |
| 24 RuntimeTypesNeed, | 24 RuntimeTypesNeed, |
| 25 RuntimeTypesEncoder, | 25 RuntimeTypesEncoder, |
| 26 RuntimeTypesSubstitutions, | 26 RuntimeTypesSubstitutions, |
| 27 Substitution, | 27 Substitution, |
| 28 TypeCheck, | 28 TypeCheck, |
| 29 TypeChecks; | 29 TypeChecks; |
| 30 import '../js_emitter/sorter.dart'; |
| 30 import '../util/util.dart' show Setlet; | 31 import '../util/util.dart' show Setlet; |
| 31 import '../world.dart'; | 32 import '../world.dart'; |
| 32 | 33 |
| 33 import 'code_emitter_task.dart' show CodeEmitterTask; | 34 import 'code_emitter_task.dart' show CodeEmitterTask; |
| 34 import 'type_test_registry.dart' show TypeTestRegistry; | 35 import 'type_test_registry.dart' show TypeTestRegistry; |
| 35 | 36 |
| 36 // Function signatures used in the generation of runtime type information. | 37 // Function signatures used in the generation of runtime type information. |
| 37 typedef void FunctionTypeSignatureEmitter( | 38 typedef void FunctionTypeSignatureEmitter( |
| 38 FunctionEntity method, FunctionType methodType); | 39 FunctionEntity method, FunctionType methodType); |
| 39 | 40 |
| 40 typedef void SubstitutionEmitter(ClassEntity element, {bool emitNull}); | 41 typedef void SubstitutionEmitter(ClassEntity element, {bool emitNull}); |
| 41 | 42 |
| 43 class TypeTest { |
| 44 final jsAst.Name name; |
| 45 final jsAst.Node expression; |
| 46 |
| 47 TypeTest(this.name, this.expression); |
| 48 } |
| 49 |
| 50 class TypeTests { |
| 51 TypeTest isTest; |
| 52 TypeTest substitution; |
| 53 TypeTest signature; |
| 54 } |
| 55 |
| 42 class TypeTestProperties { | 56 class TypeTestProperties { |
| 43 /// The index of the function type into the metadata. | 57 /// The index of the function type into the metadata. |
| 44 /// | 58 /// |
| 45 /// If the class doesn't have a function type this field is `null`. | 59 /// If the class doesn't have a function type this field is `null`. |
| 46 /// | 60 /// |
| 47 /// If the is tests were generated with `storeFunctionTypeInMetadata` set to | 61 /// If the is tests were generated with `storeFunctionTypeInMetadata` set to |
| 48 /// `false`, this field is `null`, and the [properties] contain a property | 62 /// `false`, this field is `null`, and the [properties] contain a property |
| 49 /// that encodes the function type. | 63 /// that encodes the function type. |
| 50 jsAst.Expression functionTypeIndex; | 64 jsAst.Expression functionTypeIndex; |
| 51 | 65 |
| 52 /// The properties that must be installed on the prototype of the | 66 /// The properties that must be installed on the prototype of the |
| 53 /// JS constructor of the [ClassEntity] for which the is checks were | 67 /// JS constructor of the [ClassEntity] for which the is checks were |
| 54 /// generated. | 68 /// generated. |
| 55 final Map<jsAst.Name, jsAst.Node> properties = <jsAst.Name, jsAst.Node>{}; | 69 final Map<ClassEntity, TypeTests> _properties = <ClassEntity, TypeTests>{}; |
| 70 |
| 71 void addIsTest(ClassEntity cls, jsAst.Name name, jsAst.Node expression) { |
| 72 TypeTests typeTests = _properties.putIfAbsent(cls, () => new TypeTests()); |
| 73 typeTests.isTest = new TypeTest(name, expression); |
| 74 } |
| 75 |
| 76 void addSubstitution( |
| 77 ClassEntity cls, jsAst.Name name, jsAst.Node expression) { |
| 78 TypeTests typeTests = _properties.putIfAbsent(cls, () => new TypeTests()); |
| 79 typeTests.substitution = new TypeTest(name, expression); |
| 80 } |
| 81 |
| 82 void addSignature(ClassEntity cls, jsAst.Name name, jsAst.Node expression) { |
| 83 TypeTests typeTests = _properties.putIfAbsent(cls, () => new TypeTests()); |
| 84 typeTests.signature = new TypeTest(name, expression); |
| 85 } |
| 86 |
| 87 void forEachProperty( |
| 88 Sorter sorter, void f(jsAst.Name name, jsAst.Node expression)) { |
| 89 void handleTypeTest(TypeTest typeTest) { |
| 90 if (typeTest == null) return; |
| 91 f(typeTest.name, typeTest.expression); |
| 92 } |
| 93 |
| 94 for (ClassEntity cls in sorter.sortClasses(_properties.keys)) { |
| 95 TypeTests typeTests = _properties[cls]; |
| 96 handleTypeTest(typeTests.isTest); |
| 97 handleTypeTest(typeTests.substitution); |
| 98 handleTypeTest(typeTests.signature); |
| 99 } |
| 100 } |
| 56 } | 101 } |
| 57 | 102 |
| 58 class RuntimeTypeGenerator { | 103 class RuntimeTypeGenerator { |
| 59 final ElementEnvironment _elementEnvironment; | 104 final ElementEnvironment _elementEnvironment; |
| 60 final CommonElements _commonElements; | 105 final CommonElements _commonElements; |
| 61 final DartTypes _types; | 106 final DartTypes _types; |
| 62 final ClosedWorld _closedWorld; | 107 final ClosedWorld _closedWorld; |
| 63 final ClosureConversionTask _closureDataLookup; | 108 final ClosureConversionTask _closureDataLookup; |
| 64 final CodeEmitterTask emitterTask; | 109 final CodeEmitterTask emitterTask; |
| 65 final Namer _namer; | 110 final Namer _namer; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 failedAt(classElement)); | 158 failedAt(classElement)); |
| 114 | 159 |
| 115 /// Generates an is-test if the test is not inherited from a superclass | 160 /// Generates an is-test if the test is not inherited from a superclass |
| 116 /// This assumes that for every class an is-tests is generated | 161 /// This assumes that for every class an is-tests is generated |
| 117 /// dynamically at runtime. We also always generate tests against | 162 /// dynamically at runtime. We also always generate tests against |
| 118 /// native classes. | 163 /// native classes. |
| 119 /// TODO(herhut): Generate tests for native classes dynamically, as well. | 164 /// TODO(herhut): Generate tests for native classes dynamically, as well. |
| 120 void generateIsTest(ClassEntity other) { | 165 void generateIsTest(ClassEntity other) { |
| 121 if (_nativeData.isNativeClass(classElement) || | 166 if (_nativeData.isNativeClass(classElement) || |
| 122 !_closedWorld.isSubclassOf(classElement, other)) { | 167 !_closedWorld.isSubclassOf(classElement, other)) { |
| 123 result.properties[_namer.operatorIs(other)] = js('1'); | 168 result.addIsTest(other, _namer.operatorIs(other), js('1')); |
| 124 } | 169 } |
| 125 } | 170 } |
| 126 | 171 |
| 127 void generateFunctionTypeSignature( | 172 void generateFunctionTypeSignature( |
| 128 FunctionEntity method, FunctionType type) { | 173 FunctionEntity method, FunctionType type) { |
| 129 assert(!(method is MethodElement && !method.isImplementation)); | 174 assert(!(method is MethodElement && !method.isImplementation)); |
| 130 jsAst.Expression thisAccess = new jsAst.This(); | 175 jsAst.Expression thisAccess = new jsAst.This(); |
| 131 if (!method.isAbstract) { | 176 if (!method.isAbstract) { |
| 132 ClosureRepresentationInfo closureData = | 177 ClosureRepresentationInfo closureData = |
| 133 _closureDataLookup.getClosureRepresentationInfo(method); | 178 _closureDataLookup.getClosureRepresentationInfo(method); |
| 134 if (closureData != null) { | 179 if (closureData != null) { |
| 135 ClosureFieldElement thisLocal = closureData.thisFieldEntity; | 180 ClosureFieldElement thisLocal = closureData.thisFieldEntity; |
| 136 if (thisLocal != null) { | 181 if (thisLocal != null) { |
| 137 jsAst.Name thisName = _namer.instanceFieldPropertyName(thisLocal); | 182 jsAst.Name thisName = _namer.instanceFieldPropertyName(thisLocal); |
| 138 thisAccess = js('this.#', thisName); | 183 thisAccess = js('this.#', thisName); |
| 139 } | 184 } |
| 140 } | 185 } |
| 141 } | 186 } |
| 142 | 187 |
| 143 if (storeFunctionTypeInMetadata && !type.containsTypeVariables) { | 188 if (storeFunctionTypeInMetadata && !type.containsTypeVariables) { |
| 144 result.functionTypeIndex = | 189 result.functionTypeIndex = |
| 145 emitterTask.metadataCollector.reifyType(type); | 190 emitterTask.metadataCollector.reifyType(type); |
| 146 } else { | 191 } else { |
| 147 jsAst.Expression encoding = _rtiEncoder.getSignatureEncoding( | 192 jsAst.Expression encoding = _rtiEncoder.getSignatureEncoding( |
| 148 emitterTask.emitter, type, thisAccess); | 193 emitterTask.emitter, type, thisAccess); |
| 149 jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature); | 194 jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature); |
| 150 result.properties[operatorSignature] = encoding; | 195 result.addSignature(classElement, operatorSignature, encoding); |
| 151 } | 196 } |
| 152 } | 197 } |
| 153 | 198 |
| 154 void generateSubstitution(ClassEntity cls, {bool emitNull: false}) { | 199 void generateSubstitution(ClassEntity cls, {bool emitNull: false}) { |
| 155 if (!_elementEnvironment.isGenericClass(cls)) return; | 200 if (!_elementEnvironment.isGenericClass(cls)) return; |
| 156 jsAst.Expression expression; | 201 jsAst.Expression expression; |
| 157 bool needsNativeCheck = | 202 bool needsNativeCheck = |
| 158 emitterTask.nativeEmitter.requiresNativeIsCheck(cls); | 203 emitterTask.nativeEmitter.requiresNativeIsCheck(cls); |
| 159 Substitution substitution = | 204 Substitution substitution = |
| 160 _rtiSubstitutions.getSubstitution(classElement, cls); | 205 _rtiSubstitutions.getSubstitution(classElement, cls); |
| 161 if (substitution != null) { | 206 if (substitution != null) { |
| 162 expression = | 207 expression = |
| 163 _rtiEncoder.getSubstitutionCode(emitterTask.emitter, substitution); | 208 _rtiEncoder.getSubstitutionCode(emitterTask.emitter, substitution); |
| 164 } | 209 } |
| 165 if (expression == null && (emitNull || needsNativeCheck)) { | 210 if (expression == null && (emitNull || needsNativeCheck)) { |
| 166 expression = new jsAst.LiteralNull(); | 211 expression = new jsAst.LiteralNull(); |
| 167 } | 212 } |
| 168 if (expression != null) { | 213 if (expression != null) { |
| 169 result.properties[_namer.substitutionName(cls)] = expression; | 214 result.addSubstitution(cls, _namer.substitutionName(cls), expression); |
| 170 } | 215 } |
| 171 } | 216 } |
| 172 | 217 |
| 173 void generateTypeCheck(TypeCheck check) { | 218 void generateTypeCheck(TypeCheck check) { |
| 174 ClassEntity checkedClass = check.cls; | 219 ClassEntity checkedClass = check.cls; |
| 175 generateIsTest(checkedClass); | 220 generateIsTest(checkedClass); |
| 176 Substitution substitution = check.substitution; | 221 Substitution substitution = check.substitution; |
| 177 if (substitution != null) { | 222 if (substitution != null) { |
| 178 jsAst.Expression body = | 223 jsAst.Expression body = |
| 179 _rtiEncoder.getSubstitutionCode(emitterTask.emitter, substitution); | 224 _rtiEncoder.getSubstitutionCode(emitterTask.emitter, substitution); |
| 180 result.properties[_namer.substitutionName(checkedClass)] = body; | 225 result.addSubstitution( |
| 226 checkedClass, _namer.substitutionName(checkedClass), body); |
| 181 } | 227 } |
| 182 } | 228 } |
| 183 | 229 |
| 184 _generateIsTestsOn( | 230 _generateIsTestsOn( |
| 185 classElement, | 231 classElement, |
| 186 generateIsTest, | 232 generateIsTest, |
| 187 generateFunctionTypeSignature, | 233 generateFunctionTypeSignature, |
| 188 (ClassEntity e, {bool emitNull: false}) => | 234 (ClassEntity e, {bool emitNull: false}) => |
| 189 generateSubstitution(e, emitNull: emitNull), | 235 generateSubstitution(e, emitNull: emitNull), |
| 190 generateTypeCheck); | 236 generateTypeCheck); |
| 191 | 237 |
| 192 if (classElement == _commonElements.jsJavaScriptFunctionClass) { | 238 if (classElement == _commonElements.jsJavaScriptFunctionClass) { |
| 193 var type = _jsInteropAnalysis.buildJsFunctionType(); | 239 var type = _jsInteropAnalysis.buildJsFunctionType(); |
| 194 if (type != null) { | 240 if (type != null) { |
| 195 jsAst.Expression thisAccess = new jsAst.This(); | 241 jsAst.Expression thisAccess = new jsAst.This(); |
| 196 jsAst.Expression encoding = _rtiEncoder.getSignatureEncoding( | 242 jsAst.Expression encoding = _rtiEncoder.getSignatureEncoding( |
| 197 emitterTask.emitter, type, thisAccess); | 243 emitterTask.emitter, type, thisAccess); |
| 198 jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature); | 244 jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature); |
| 199 result.properties[operatorSignature] = encoding; | 245 result.addSignature(classElement, operatorSignature, encoding); |
| 200 } | 246 } |
| 201 } | 247 } |
| 202 return result; | 248 return result; |
| 203 } | 249 } |
| 204 | 250 |
| 205 /** | 251 /** |
| 206 * Generate "is tests" for [cls] itself, and the "is tests" for the | 252 * Generate "is tests" for [cls] itself, and the "is tests" for the |
| 207 * classes it implements and type argument substitution functions for these | 253 * classes it implements and type argument substitution functions for these |
| 208 * tests. We don't need to add the "is tests" of the super class because | 254 * tests. We don't need to add the "is tests" of the super class because |
| 209 * they will be inherited at runtime, but we may need to generate the | 255 * they will be inherited at runtime, but we may need to generate the |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 343 | 389 |
| 344 // We need to also emit "is checks" for the superclass and its supertypes. | 390 // We need to also emit "is checks" for the superclass and its supertypes. |
| 345 ClassEntity superclass = _elementEnvironment.getSuperClass(cls); | 391 ClassEntity superclass = _elementEnvironment.getSuperClass(cls); |
| 346 if (superclass != null) { | 392 if (superclass != null) { |
| 347 tryEmitTest(superclass); | 393 tryEmitTest(superclass); |
| 348 _generateInterfacesIsTests( | 394 _generateInterfacesIsTests( |
| 349 superclass, generateIsTest, generateSubstitution, alreadyGenerated); | 395 superclass, generateIsTest, generateSubstitution, alreadyGenerated); |
| 350 } | 396 } |
| 351 } | 397 } |
| 352 } | 398 } |
| OLD | NEW |