| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 js_backend.backend; | 5 library js_backend.backend; |
| 6 | 6 |
| 7 import '../common.dart'; | 7 import '../common.dart'; |
| 8 import '../common/backend_api.dart' | 8 import '../common/backend_api.dart' |
| 9 show ForeignResolver, NativeRegistry, ImpactTransformer; | 9 show ForeignResolver, NativeRegistry, ImpactTransformer; |
| 10 import '../common/codegen.dart' show CodegenImpact, CodegenWorkItem; | 10 import '../common/codegen.dart' show CodegenImpact, CodegenWorkItem; |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 static const int _canInlineInLoopMustNotOutside = 2; | 105 static const int _canInlineInLoopMustNotOutside = 2; |
| 106 // May-inline means that we know that it can be inlined inside a loop, but | 106 // May-inline means that we know that it can be inlined inside a loop, but |
| 107 // don't know about the general case yet. | 107 // don't know about the general case yet. |
| 108 static const int _canInlineInLoopMayInlineOutside = 3; | 108 static const int _canInlineInLoopMayInlineOutside = 3; |
| 109 static const int _canInline = 4; | 109 static const int _canInline = 4; |
| 110 static const int _mustInline = 5; | 110 static const int _mustInline = 5; |
| 111 | 111 |
| 112 final Map<FunctionEntity, int> _cachedDecisions = | 112 final Map<FunctionEntity, int> _cachedDecisions = |
| 113 new Map<FunctionEntity, int>(); | 113 new Map<FunctionEntity, int>(); |
| 114 | 114 |
| 115 /// Checks that [method] is the canonical representative for this method. |
| 116 /// |
| 117 /// For a [MethodElement] this means it must be the declaration element. |
| 118 bool checkFunction(FunctionEntity method) { |
| 119 if (method is MethodElement) return method.isDeclaration; |
| 120 return true; |
| 121 } |
| 122 |
| 115 /// Returns the current cache decision. This should only be used for testing. | 123 /// Returns the current cache decision. This should only be used for testing. |
| 116 int getCurrentCacheDecisionForTesting(Element element) { | 124 int getCurrentCacheDecisionForTesting(FunctionEntity element) { |
| 125 assert(checkFunction(element)); |
| 117 return _cachedDecisions[element]; | 126 return _cachedDecisions[element]; |
| 118 } | 127 } |
| 119 | 128 |
| 120 // Returns `true`/`false` if we have a cached decision. | 129 // Returns `true`/`false` if we have a cached decision. |
| 121 // Returns `null` otherwise. | 130 // Returns `null` otherwise. |
| 122 bool canInline(FunctionEntity element, {bool insideLoop}) { | 131 bool canInline(FunctionEntity element, {bool insideLoop}) { |
| 132 assert(checkFunction(element)); |
| 123 int decision = _cachedDecisions[element]; | 133 int decision = _cachedDecisions[element]; |
| 124 | 134 |
| 125 if (decision == null) { | 135 if (decision == null) { |
| 126 // These synthetic elements are not yet present when we initially compute | 136 // These synthetic elements are not yet present when we initially compute |
| 127 // this cache from metadata annotations, so look for their parent. | 137 // this cache from metadata annotations, so look for their parent. |
| 128 if (element is ConstructorBodyElement) { | 138 if (element is ConstructorBodyElement) { |
| 129 ConstructorBodyElement body = element; | 139 ConstructorBodyElement body = element; |
| 130 decision = _cachedDecisions[body.constructor]; | 140 decision = _cachedDecisions[body.constructor]; |
| 131 } | 141 } |
| 132 if (decision == null) { | 142 if (decision == null) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 case _mustInline: | 178 case _mustInline: |
| 169 return true; | 179 return true; |
| 170 } | 180 } |
| 171 } | 181 } |
| 172 | 182 |
| 173 // Quiet static checker. | 183 // Quiet static checker. |
| 174 return null; | 184 return null; |
| 175 } | 185 } |
| 176 | 186 |
| 177 void markAsInlinable(FunctionEntity element, {bool insideLoop}) { | 187 void markAsInlinable(FunctionEntity element, {bool insideLoop}) { |
| 188 assert(checkFunction(element)); |
| 178 int oldDecision = _cachedDecisions[element]; | 189 int oldDecision = _cachedDecisions[element]; |
| 179 | 190 |
| 180 if (oldDecision == null) { | 191 if (oldDecision == null) { |
| 181 oldDecision = _unknown; | 192 oldDecision = _unknown; |
| 182 } | 193 } |
| 183 | 194 |
| 184 if (insideLoop) { | 195 if (insideLoop) { |
| 185 switch (oldDecision) { | 196 switch (oldDecision) { |
| 186 case _mustNotInline: | 197 case _mustNotInline: |
| 187 throw new SpannableAssertionFailure( | 198 throw new SpannableAssertionFailure( |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 | 234 |
| 224 case _canInline: | 235 case _canInline: |
| 225 case _mustInline: | 236 case _mustInline: |
| 226 // Do nothing. | 237 // Do nothing. |
| 227 break; | 238 break; |
| 228 } | 239 } |
| 229 } | 240 } |
| 230 } | 241 } |
| 231 | 242 |
| 232 void markAsNonInlinable(FunctionEntity element, {bool insideLoop: true}) { | 243 void markAsNonInlinable(FunctionEntity element, {bool insideLoop: true}) { |
| 244 assert(checkFunction(element)); |
| 233 int oldDecision = _cachedDecisions[element]; | 245 int oldDecision = _cachedDecisions[element]; |
| 234 | 246 |
| 235 if (oldDecision == null) { | 247 if (oldDecision == null) { |
| 236 oldDecision = _unknown; | 248 oldDecision = _unknown; |
| 237 } | 249 } |
| 238 | 250 |
| 239 if (insideLoop) { | 251 if (insideLoop) { |
| 240 switch (oldDecision) { | 252 switch (oldDecision) { |
| 241 case _canInlineInLoopMustNotOutside: | 253 case _canInlineInLoopMustNotOutside: |
| 242 case _canInlineInLoopMayInlineOutside: | 254 case _canInlineInLoopMayInlineOutside: |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 case _mayInlineInLoopMustNotOutside: | 293 case _mayInlineInLoopMustNotOutside: |
| 282 case _canInlineInLoopMustNotOutside: | 294 case _canInlineInLoopMustNotOutside: |
| 283 case _mustNotInline: | 295 case _mustNotInline: |
| 284 // Do nothing. | 296 // Do nothing. |
| 285 break; | 297 break; |
| 286 } | 298 } |
| 287 } | 299 } |
| 288 } | 300 } |
| 289 | 301 |
| 290 void markAsMustInline(FunctionEntity element) { | 302 void markAsMustInline(FunctionEntity element) { |
| 303 assert(checkFunction(element)); |
| 291 _cachedDecisions[element] = _mustInline; | 304 _cachedDecisions[element] = _mustInline; |
| 292 } | 305 } |
| 293 } | 306 } |
| 294 | 307 |
| 295 enum SyntheticConstantKind { | 308 enum SyntheticConstantKind { |
| 296 DUMMY_INTERCEPTOR, | 309 DUMMY_INTERCEPTOR, |
| 297 EMPTY_VALUE, | 310 EMPTY_VALUE, |
| 298 TYPEVARIABLE_REFERENCE, // Reference to a type in reflection data. | 311 TYPEVARIABLE_REFERENCE, // Reference to a type in reflection data. |
| 299 NAME | 312 NAME |
| 300 } | 313 } |
| 301 | 314 |
| 302 class JavaScriptBackend { | 315 class JavaScriptBackend { |
| 303 static const String JS = 'JS'; | 316 static const String JS = 'JS'; |
| 304 static const String JS_BUILTIN = 'JS_BUILTIN'; | 317 static const String JS_BUILTIN = 'JS_BUILTIN'; |
| 305 static const String JS_EMBEDDED_GLOBAL = 'JS_EMBEDDED_GLOBAL'; | 318 static const String JS_EMBEDDED_GLOBAL = 'JS_EMBEDDED_GLOBAL'; |
| 306 static const String JS_INTERCEPTOR_CONSTANT = 'JS_INTERCEPTOR_CONSTANT'; | 319 static const String JS_INTERCEPTOR_CONSTANT = 'JS_INTERCEPTOR_CONSTANT'; |
| 307 | 320 |
| 308 final Compiler compiler; | 321 final Compiler compiler; |
| 309 | 322 |
| 310 /// Returns true if the backend supports reflection. | 323 /// Returns true if the backend supports reflection. |
| 311 bool get supportsReflection => emitter.supportsReflection; | 324 bool get supportsReflection => emitter.supportsReflection; |
| 312 | 325 |
| 313 final OptimizerHintsForTests annotations; | 326 final OptimizerHintsForTests optimizerHints; |
| 314 | 327 |
| 315 /// Set of classes that need to be considered for reflection although not | 328 /// Set of classes that need to be considered for reflection although not |
| 316 /// otherwise visible during resolution. | 329 /// otherwise visible during resolution. |
| 317 Iterable<ClassEntity> get classesRequiredForReflection { | 330 Iterable<ClassEntity> get classesRequiredForReflection { |
| 318 // TODO(herhut): Clean this up when classes needed for rti are tracked. | 331 // TODO(herhut): Clean this up when classes needed for rti are tracked. |
| 319 return [commonElements.closureClass, commonElements.jsIndexableClass]; | 332 return [commonElements.closureClass, commonElements.jsIndexableClass]; |
| 320 } | 333 } |
| 321 | 334 |
| 322 FunctionCompiler functionCompiler; | 335 FunctionCompiler functionCompiler; |
| 323 | 336 |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 471 } | 484 } |
| 472 } | 485 } |
| 473 | 486 |
| 474 JavaScriptBackend(this.compiler, | 487 JavaScriptBackend(this.compiler, |
| 475 {bool generateSourceMap: true, | 488 {bool generateSourceMap: true, |
| 476 bool useStartupEmitter: false, | 489 bool useStartupEmitter: false, |
| 477 bool useMultiSourceInfo: false, | 490 bool useMultiSourceInfo: false, |
| 478 bool useNewSourceInfo: false, | 491 bool useNewSourceInfo: false, |
| 479 bool useKernel: false}) | 492 bool useKernel: false}) |
| 480 : _rti = new _RuntimeTypes(compiler), | 493 : _rti = new _RuntimeTypes(compiler), |
| 481 annotations = new OptimizerHintsForTests(compiler), | 494 optimizerHints = new OptimizerHintsForTests( |
| 495 compiler.elementEnvironment, compiler.commonElements), |
| 482 this.sourceInformationStrategy = createSourceInformationStrategy( | 496 this.sourceInformationStrategy = createSourceInformationStrategy( |
| 483 generateSourceMap: generateSourceMap, | 497 generateSourceMap: generateSourceMap, |
| 484 useMultiSourceInfo: useMultiSourceInfo, | 498 useMultiSourceInfo: useMultiSourceInfo, |
| 485 useNewSourceInfo: useNewSourceInfo), | 499 useNewSourceInfo: useNewSourceInfo), |
| 486 constantCompilerTask = new JavaScriptConstantTask(compiler), | 500 constantCompilerTask = new JavaScriptConstantTask(compiler), |
| 487 _nativeDataResolver = new NativeDataResolverImpl(compiler), | 501 _nativeDataResolver = new NativeDataResolverImpl(compiler), |
| 488 _rtiNeedBuilder = | 502 _rtiNeedBuilder = |
| 489 compiler.frontEndStrategy.createRuntimeTypesNeedBuilder() { | 503 compiler.frontEndStrategy.createRuntimeTypesNeedBuilder() { |
| 490 _target = new JavaScriptBackendTarget(this); | 504 _target = new JavaScriptBackendTarget(this); |
| 491 impacts = new BackendImpacts(compiler.options, commonElements); | 505 impacts = new BackendImpacts(compiler.options, commonElements); |
| (...skipping 680 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1172 bool isTargetSpecificLibrary(LibraryElement library) { | 1186 bool isTargetSpecificLibrary(LibraryElement library) { |
| 1173 Uri canonicalUri = library.canonicalUri; | 1187 Uri canonicalUri = library.canonicalUri; |
| 1174 if (canonicalUri == Uris.dart__js_helper || | 1188 if (canonicalUri == Uris.dart__js_helper || |
| 1175 canonicalUri == Uris.dart__interceptors) { | 1189 canonicalUri == Uris.dart__interceptors) { |
| 1176 return true; | 1190 return true; |
| 1177 } | 1191 } |
| 1178 return false; | 1192 return false; |
| 1179 } | 1193 } |
| 1180 | 1194 |
| 1181 /// Process backend specific annotations. | 1195 /// Process backend specific annotations. |
| 1196 // TODO(johnniwinther): Merge this with [AnnotationProcessor] and use |
| 1197 // [ElementEnvironment.getMemberMetadata] in [AnnotationProcessor]. |
| 1182 void processAnnotations( | 1198 void processAnnotations( |
| 1183 MemberElement element, ClosedWorldRefiner closedWorldRefiner) { | 1199 MemberEntity element, ClosedWorldRefiner closedWorldRefiner) { |
| 1184 if (element.isMalformed) { | 1200 if (element is MemberElement && element.isMalformed) { |
| 1185 // Elements that are marked as malformed during parsing or resolution | 1201 // Elements that are marked as malformed during parsing or resolution |
| 1186 // might be registered here. These should just be ignored. | 1202 // might be registered here. These should just be ignored. |
| 1187 return; | 1203 return; |
| 1188 } | 1204 } |
| 1189 | 1205 |
| 1190 if (element.isFunction || element.isConstructor) { | 1206 if (element.isFunction || element.isConstructor) { |
| 1191 MethodElement method = element.implementation; | 1207 if (optimizerHints.noInline(element)) { |
| 1192 if (annotations.noInline(method)) { | 1208 inlineCache.markAsNonInlinable(element); |
| 1193 inlineCache.markAsNonInlinable(method); | |
| 1194 } | 1209 } |
| 1195 } | 1210 } |
| 1196 if (element.isField) return; | 1211 if (element.isField) return; |
| 1197 MethodElement method = element; | 1212 FunctionEntity method = element; |
| 1198 | 1213 |
| 1199 LibraryElement library = method.library; | 1214 LibraryEntity library = method.library; |
| 1200 if (!library.isPlatformLibrary && !canLibraryUseNative(library)) return; | 1215 if (library.canonicalUri.scheme != 'dart' && |
| 1216 !canLibraryUseNative(library)) { |
| 1217 return; |
| 1218 } |
| 1201 bool hasNoInline = false; | 1219 bool hasNoInline = false; |
| 1202 bool hasForceInline = false; | 1220 bool hasForceInline = false; |
| 1203 bool hasNoThrows = false; | 1221 bool hasNoThrows = false; |
| 1204 bool hasNoSideEffects = false; | 1222 bool hasNoSideEffects = false; |
| 1205 for (MetadataAnnotation metadata in method.implementation.metadata) { | 1223 for (ConstantValue constantValue |
| 1206 metadata.ensureResolved(resolution); | 1224 in compiler.elementEnvironment.getMemberMetadata(method)) { |
| 1207 ConstantValue constantValue = | |
| 1208 compiler.constants.getConstantValue(metadata.constant); | |
| 1209 if (!constantValue.isConstructedObject) continue; | 1225 if (!constantValue.isConstructedObject) continue; |
| 1210 ObjectConstantValue value = constantValue; | 1226 ObjectConstantValue value = constantValue; |
| 1211 ClassElement cls = value.type.element; | 1227 ClassEntity cls = value.type.element; |
| 1212 if (cls == commonElements.forceInlineClass) { | 1228 if (cls == commonElements.forceInlineClass) { |
| 1213 hasForceInline = true; | 1229 hasForceInline = true; |
| 1214 if (VERBOSE_OPTIMIZER_HINTS) { | 1230 if (VERBOSE_OPTIMIZER_HINTS) { |
| 1215 reporter.reportHintMessage( | 1231 reporter.reportHintMessage( |
| 1216 method, MessageKind.GENERIC, {'text': "Must inline"}); | 1232 method, MessageKind.GENERIC, {'text': "Must inline"}); |
| 1217 } | 1233 } |
| 1218 inlineCache.markAsMustInline(method); | 1234 inlineCache.markAsMustInline(method); |
| 1219 } else if (cls == commonElements.noInlineClass) { | 1235 } else if (cls == commonElements.noInlineClass) { |
| 1220 hasNoInline = true; | 1236 hasNoInline = true; |
| 1221 if (VERBOSE_OPTIMIZER_HINTS) { | 1237 if (VERBOSE_OPTIMIZER_HINTS) { |
| 1222 reporter.reportHintMessage( | 1238 reporter.reportHintMessage( |
| 1223 method, MessageKind.GENERIC, {'text': "Cannot inline"}); | 1239 method, MessageKind.GENERIC, {'text': "Cannot inline"}); |
| 1224 } | 1240 } |
| 1225 inlineCache.markAsNonInlinable(method); | 1241 inlineCache.markAsNonInlinable(method); |
| 1226 } else if (cls == commonElements.noThrowsClass) { | 1242 } else if (cls == commonElements.noThrowsClass) { |
| 1227 hasNoThrows = true; | 1243 hasNoThrows = true; |
| 1228 if (!Elements.isStaticOrTopLevelFunction(method) && | 1244 bool isValid = true; |
| 1229 !method.isFactoryConstructor) { | 1245 if (method.isTopLevel) { |
| 1246 isValid = true; |
| 1247 } else if (method.isStatic) { |
| 1248 isValid = true; |
| 1249 } else if (method is ConstructorEntity && method.isFactoryConstructor) { |
| 1250 isValid = true; |
| 1251 } |
| 1252 if (!isValid) { |
| 1230 reporter.internalError( | 1253 reporter.internalError( |
| 1231 method, | 1254 method, |
| 1232 "@NoThrows() is currently limited to top-level" | 1255 "@NoThrows() is currently limited to top-level" |
| 1233 " or static functions and factory constructors."); | 1256 " or static functions and factory constructors."); |
| 1234 } | 1257 } |
| 1235 if (VERBOSE_OPTIMIZER_HINTS) { | 1258 if (VERBOSE_OPTIMIZER_HINTS) { |
| 1236 reporter.reportHintMessage( | 1259 reporter.reportHintMessage( |
| 1237 method, MessageKind.GENERIC, {'text': "Cannot throw"}); | 1260 method, MessageKind.GENERIC, {'text': "Cannot throw"}); |
| 1238 } | 1261 } |
| 1239 closedWorldRefiner.registerCannotThrow(method); | 1262 closedWorldRefiner.registerCannotThrow(method); |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1452 | 1475 |
| 1453 bool canUseAliasedSuperMember(MemberEntity member, Selector selector) { | 1476 bool canUseAliasedSuperMember(MemberEntity member, Selector selector) { |
| 1454 return !selector.isGetter; | 1477 return !selector.isGetter; |
| 1455 } | 1478 } |
| 1456 | 1479 |
| 1457 /// Returns `true` if [member] is called from a subclass via `super`. | 1480 /// Returns `true` if [member] is called from a subclass via `super`. |
| 1458 bool isAliasedSuperMember(MemberEntity member) { | 1481 bool isAliasedSuperMember(MemberEntity member) { |
| 1459 return _aliasedSuperMembers.contains(member); | 1482 return _aliasedSuperMembers.contains(member); |
| 1460 } | 1483 } |
| 1461 } | 1484 } |
| OLD | NEW |