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 |