OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of js_backend; | |
6 | |
7 const VERBOSE_OPTIMIZER_HINTS = false; | |
8 | |
9 class JavaScriptItemCompilationContext extends ItemCompilationContext { | |
10 final Set<HInstruction> boundsChecked = new Set<HInstruction>(); | |
11 final Set<HInstruction> allocatedFixedLists = new Set<HInstruction>(); | |
12 } | |
13 | |
14 /* | |
15 * Invariants: | |
16 * canInline(function) implies canInline(function, insideLoop:true) | |
17 * !canInline(function, insideLoop: true) implies !canInline(function) | |
18 */ | |
19 class FunctionInlineCache { | |
20 final Map<FunctionElement, bool> canBeInlined = | |
21 new Map<FunctionElement, bool>(); | |
22 | |
23 final Map<FunctionElement, bool> canBeInlinedInsideLoop = | |
24 new Map<FunctionElement, bool>(); | |
25 | |
26 // Returns [:true:]/[:false:] if we have a cached decision. | |
27 // Returns [:null:] otherwise. | |
28 bool canInline(FunctionElement element, {bool insideLoop}) { | |
29 return insideLoop ? canBeInlinedInsideLoop[element] : canBeInlined[element]; | |
30 } | |
31 | |
32 void markAsInlinable(FunctionElement element, {bool insideLoop}) { | |
33 if (insideLoop) { | |
34 canBeInlinedInsideLoop[element] = true; | |
35 } else { | |
36 // If we can inline a function outside a loop then we should do it inside | |
37 // a loop as well. | |
38 canBeInlined[element] = true; | |
39 canBeInlinedInsideLoop[element] = true; | |
40 } | |
41 } | |
42 | |
43 void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { | |
44 if (insideLoop == null || insideLoop) { | |
45 // If we can't inline a function inside a loop, then we should not inline | |
46 // it outside a loop either. | |
47 canBeInlined[element] = false; | |
48 canBeInlinedInsideLoop[element] = false; | |
49 } else { | |
50 canBeInlined[element] = false; | |
51 } | |
52 } | |
53 } | |
54 | |
55 class JavaScriptBackend extends Backend { | |
56 static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper'); | |
57 static final Uri DART_INTERCEPTORS = | |
58 new Uri(scheme: 'dart', path: '_interceptors'); | |
59 static final Uri DART_INTERNAL = | |
60 new Uri(scheme: 'dart', path: '_internal'); | |
61 static final Uri DART_FOREIGN_HELPER = | |
62 new Uri(scheme: 'dart', path: '_foreign_helper'); | |
63 static final Uri DART_JS_MIRRORS = | |
64 new Uri(scheme: 'dart', path: '_js_mirrors'); | |
65 static final Uri DART_JS_NAMES = | |
66 new Uri(scheme: 'dart', path: '_js_names'); | |
67 static final Uri DART_ISOLATE_HELPER = | |
68 new Uri(scheme: 'dart', path: '_isolate_helper'); | |
69 static final Uri DART_HTML = | |
70 new Uri(scheme: 'dart', path: 'html'); | |
71 | |
72 static const String INVOKE_ON = '_getCachedInvocation'; | |
73 static const String START_ROOT_ISOLATE = 'startRootIsolate'; | |
74 | |
75 | |
76 /// The list of functions for classes in the [internalLibrary] that we want | |
77 /// to inline always. Any function in this list must be inlinable with | |
78 /// respect to the conditions used in [InlineWeeder.canInline], except for | |
79 /// size/complexity heuristics. | |
80 static const Map<String, List<String>> ALWAYS_INLINE = | |
81 const <String, List<String>> { | |
82 'IterableMixinWorkaround': const <String>['forEach'], | |
83 }; | |
84 | |
85 /// List of [FunctionElement]s that we want to inline always. This list is | |
86 /// filled when resolution is complete by looking up in [internalLibrary]. | |
87 List<FunctionElement> functionsToAlwaysInline; | |
88 | |
89 /// Reference to the internal library to lookup functions to always inline. | |
90 LibraryElement internalLibrary; | |
91 | |
92 | |
93 /// Set of classes that need to be considered for reflection although not | |
94 /// otherwise visible during resolution. | |
95 Iterable<ClassElement> get classesRequiredForReflection { | |
96 // TODO(herhut): Clean this up when classes needed for rti are tracked. | |
97 return [closureClass, jsIndexableClass]; | |
98 } | |
99 | |
100 SsaBuilderTask builder; | |
101 SsaOptimizerTask optimizer; | |
102 SsaCodeGeneratorTask generator; | |
103 CodeEmitterTask emitter; | |
104 | |
105 /** | |
106 * The generated code as a js AST for compiled methods. | |
107 */ | |
108 Map<Element, jsAst.Expression> get generatedCode { | |
109 return compiler.enqueuer.codegen.generatedCode; | |
110 } | |
111 | |
112 FunctionInlineCache inlineCache = new FunctionInlineCache(); | |
113 | |
114 LibraryElement jsHelperLibrary; | |
115 LibraryElement interceptorsLibrary; | |
116 LibraryElement foreignLibrary; | |
117 LibraryElement isolateHelperLibrary; | |
118 | |
119 ClassElement closureClass; | |
120 ClassElement boundClosureClass; | |
121 Element assertMethod; | |
122 Element invokeOnMethod; | |
123 | |
124 ClassElement jsInterceptorClass; | |
125 ClassElement jsStringClass; | |
126 ClassElement jsArrayClass; | |
127 ClassElement jsNumberClass; | |
128 ClassElement jsIntClass; | |
129 ClassElement jsDoubleClass; | |
130 ClassElement jsNullClass; | |
131 ClassElement jsBoolClass; | |
132 ClassElement jsPlainJavaScriptObjectClass; | |
133 ClassElement jsUnknownJavaScriptObjectClass; | |
134 | |
135 ClassElement jsIndexableClass; | |
136 ClassElement jsMutableIndexableClass; | |
137 | |
138 ClassElement jsMutableArrayClass; | |
139 ClassElement jsFixedArrayClass; | |
140 ClassElement jsExtendableArrayClass; | |
141 ClassElement jsPositiveIntClass; | |
142 ClassElement jsUInt32Class; | |
143 ClassElement jsUInt31Class; | |
144 | |
145 Element jsIndexableLength; | |
146 Element jsArrayTypedConstructor; | |
147 Element jsArrayRemoveLast; | |
148 Element jsArrayAdd; | |
149 Element jsStringSplit; | |
150 Element jsStringToString; | |
151 Element jsStringOperatorAdd; | |
152 Element objectEquals; | |
153 | |
154 ClassElement typeLiteralClass; | |
155 ClassElement mapLiteralClass; | |
156 ClassElement constMapLiteralClass; | |
157 ClassElement typeVariableClass; | |
158 ConstructorElement mapLiteralConstructor; | |
159 ConstructorElement mapLiteralConstructorEmpty; | |
160 | |
161 ClassElement noSideEffectsClass; | |
162 ClassElement noThrowsClass; | |
163 ClassElement noInlineClass; | |
164 ClassElement irRepresentationClass; | |
165 | |
166 Element getInterceptorMethod; | |
167 Element interceptedNames; | |
168 | |
169 ClassElement jsInvocationMirrorClass; | |
170 | |
171 /// If [true], the compiler will emit code that writes the name of the current | |
172 /// method together with its class and library to the console the first time | |
173 /// the method is called. | |
174 static const bool TRACE_CALLS = false; | |
175 Element traceHelper; | |
176 | |
177 /** | |
178 * This element is a top-level variable (in generated output) that the | |
179 * compiler initializes to a datastructure used to map from a Type to the | |
180 * interceptor. See declaration of `mapTypeToInterceptor` in | |
181 * `interceptors.dart`. | |
182 */ | |
183 Element mapTypeToInterceptor; | |
184 | |
185 TypeMask get stringType => compiler.typesTask.stringType; | |
186 TypeMask get doubleType => compiler.typesTask.doubleType; | |
187 TypeMask get intType => compiler.typesTask.intType; | |
188 TypeMask get uint32Type => compiler.typesTask.uint32Type; | |
189 TypeMask get uint31Type => compiler.typesTask.uint31Type; | |
190 TypeMask get positiveIntType => compiler.typesTask.positiveIntType; | |
191 TypeMask get numType => compiler.typesTask.numType; | |
192 TypeMask get boolType => compiler.typesTask.boolType; | |
193 TypeMask get dynamicType => compiler.typesTask.dynamicType; | |
194 TypeMask get nullType => compiler.typesTask.nullType; | |
195 TypeMask get emptyType => const TypeMask.nonNullEmpty(); | |
196 | |
197 TypeMask _indexablePrimitiveTypeCache; | |
198 TypeMask get indexablePrimitiveType { | |
199 if (_indexablePrimitiveTypeCache == null) { | |
200 _indexablePrimitiveTypeCache = | |
201 new TypeMask.nonNullSubtype(jsIndexableClass, compiler.world); | |
202 } | |
203 return _indexablePrimitiveTypeCache; | |
204 } | |
205 | |
206 TypeMask _readableArrayTypeCache; | |
207 TypeMask get readableArrayType { | |
208 if (_readableArrayTypeCache == null) { | |
209 _readableArrayTypeCache = new TypeMask.nonNullSubclass(jsArrayClass, | |
210 compiler.world); | |
211 } | |
212 return _readableArrayTypeCache; | |
213 } | |
214 | |
215 TypeMask _mutableArrayTypeCache; | |
216 TypeMask get mutableArrayType { | |
217 if (_mutableArrayTypeCache == null) { | |
218 _mutableArrayTypeCache = new TypeMask.nonNullSubclass(jsMutableArrayClass, | |
219 compiler.world); | |
220 } | |
221 return _mutableArrayTypeCache; | |
222 } | |
223 | |
224 TypeMask _fixedArrayTypeCache; | |
225 TypeMask get fixedArrayType { | |
226 if (_fixedArrayTypeCache == null) { | |
227 _fixedArrayTypeCache = new TypeMask.nonNullExact(jsFixedArrayClass, | |
228 compiler.world); | |
229 } | |
230 return _fixedArrayTypeCache; | |
231 } | |
232 | |
233 TypeMask _extendableArrayTypeCache; | |
234 TypeMask get extendableArrayType { | |
235 if (_extendableArrayTypeCache == null) { | |
236 _extendableArrayTypeCache = | |
237 new TypeMask.nonNullExact(jsExtendableArrayClass, compiler.world); | |
238 } | |
239 return _extendableArrayTypeCache; | |
240 } | |
241 | |
242 TypeMask _nonNullTypeCache; | |
243 TypeMask get nonNullType { | |
244 if (_nonNullTypeCache == null) { | |
245 _nonNullTypeCache = | |
246 compiler.typesTask.dynamicType.nonNullable(); | |
247 } | |
248 return _nonNullTypeCache; | |
249 } | |
250 | |
251 /// Maps special classes to their implementation (JSXxx) class. | |
252 Map<ClassElement, ClassElement> implementationClasses; | |
253 | |
254 Element getNativeInterceptorMethod; | |
255 bool needToInitializeIsolateAffinityTag = false; | |
256 bool needToInitializeDispatchProperty = false; | |
257 | |
258 /// Holds the method "getIsolateAffinityTag" when dart:_js_helper has been | |
259 /// loaded. | |
260 FunctionElement getIsolateAffinityTagMarker; | |
261 | |
262 final Namer namer; | |
263 | |
264 /** | |
265 * Interface used to determine if an object has the JavaScript | |
266 * indexing behavior. The interface is only visible to specific | |
267 * libraries. | |
268 */ | |
269 ClassElement jsIndexingBehaviorInterface; | |
270 | |
271 /** | |
272 * A collection of selectors that must have a one shot interceptor | |
273 * generated. | |
274 */ | |
275 final Map<String, Selector> oneShotInterceptors; | |
276 | |
277 /** | |
278 * The members of instantiated interceptor classes: maps a member name to the | |
279 * list of members that have that name. This map is used by the codegen to | |
280 * know whether a send must be intercepted or not. | |
281 */ | |
282 final Map<String, Set<Element>> interceptedElements; | |
283 | |
284 /** | |
285 * The members of mixin classes that are mixed into an instantiated | |
286 * interceptor class. This is a cached subset of [interceptedElements]. | |
287 * | |
288 * Mixin methods are not specialized for the class they are mixed into. | |
289 * Methods mixed into intercepted classes thus always make use of the explicit | |
290 * receiver argument, even when mixed into non-interceptor classes. | |
291 * | |
292 * These members must be invoked with a correct explicit receiver even when | |
293 * the receiver is not an intercepted class. | |
294 */ | |
295 final Map<String, Set<Element>> interceptedMixinElements = | |
296 new Map<String, Set<Element>>(); | |
297 | |
298 /** | |
299 * A map of specialized versions of the [getInterceptorMethod]. | |
300 * Since [getInterceptorMethod] is a hot method at runtime, we're | |
301 * always specializing it based on the incoming type. The keys in | |
302 * the map are the names of these specialized versions. Note that | |
303 * the generic version that contains all possible type checks is | |
304 * also stored in this map. | |
305 */ | |
306 final Map<String, Set<ClassElement>> specializedGetInterceptors; | |
307 | |
308 /** | |
309 * Set of classes whose methods are intercepted. | |
310 */ | |
311 final Set<ClassElement> _interceptedClasses = new Set<ClassElement>(); | |
312 | |
313 /** | |
314 * Set of classes used as mixins on intercepted (native and primitive) | |
315 * classes. Methods on these classes might also be mixed in to regular Dart | |
316 * (unintercepted) classes. | |
317 */ | |
318 final Set<ClassElement> classesMixedIntoInterceptedClasses = | |
319 new Set<ClassElement>(); | |
320 | |
321 /** | |
322 * Set of classes whose `operator ==` methods handle `null` themselves. | |
323 */ | |
324 final Set<ClassElement> specialOperatorEqClasses = new Set<ClassElement>(); | |
325 | |
326 List<CompilerTask> get tasks { | |
327 return <CompilerTask>[builder, optimizer, generator, emitter]; | |
328 } | |
329 | |
330 final RuntimeTypes rti; | |
331 | |
332 /// Holds the method "disableTreeShaking" in js_mirrors when | |
333 /// dart:mirrors has been loaded. | |
334 FunctionElement disableTreeShakingMarker; | |
335 | |
336 /// Holds the method "preserveNames" in js_mirrors when | |
337 /// dart:mirrors has been loaded. | |
338 FunctionElement preserveNamesMarker; | |
339 | |
340 /// Holds the method "preserveMetadata" in js_mirrors when | |
341 /// dart:mirrors has been loaded. | |
342 FunctionElement preserveMetadataMarker; | |
343 | |
344 /// Holds the method "preserveUris" in js_mirrors when | |
345 /// dart:mirrors has been loaded. | |
346 FunctionElement preserveUrisMarker; | |
347 | |
348 /// Holds the method "preserveLibraryNames" in js_mirrors when | |
349 /// dart:mirrors has been loaded. | |
350 FunctionElement preserveLibraryNamesMarker; | |
351 | |
352 /// Holds the method "requiresPreamble" in _js_helper. | |
353 FunctionElement requiresPreambleMarker; | |
354 | |
355 /// True if a call to preserveMetadataMarker has been seen. This means that | |
356 /// metadata must be retained for dart:mirrors to work correctly. | |
357 bool mustRetainMetadata = false; | |
358 | |
359 /// True if any metadata has been retained. This is slightly different from | |
360 /// [mustRetainMetadata] and tells us if any metadata was retained. For | |
361 /// example, if [mustRetainMetadata] is true but there is no metadata in the | |
362 /// program, this variable will stil be false. | |
363 bool hasRetainedMetadata = false; | |
364 | |
365 /// True if a call to preserveUris has been seen and the preserve-uris flag | |
366 /// is set. | |
367 bool mustPreserveUris = false; | |
368 | |
369 /// True if a call to preserveLibraryNames has been seen. | |
370 bool mustRetainLibraryNames = false; | |
371 | |
372 /// True if a call to preserveNames has been seen. | |
373 bool mustPreserveNames = false; | |
374 | |
375 /// True if a call to disableTreeShaking has been seen. | |
376 bool isTreeShakingDisabled = false; | |
377 | |
378 /// True if there isn't sufficient @MirrorsUsed data. | |
379 bool hasInsufficientMirrorsUsed = false; | |
380 | |
381 /// True if a core-library function requires the preamble file to function. | |
382 bool requiresPreamble = false; | |
383 | |
384 /// True if the html library has been loaded. | |
385 bool htmlLibraryIsLoaded = false; | |
386 | |
387 /// List of constants from metadata. If metadata must be preserved, | |
388 /// these constants must be registered. | |
389 final List<Dependency> metadataConstants = <Dependency>[]; | |
390 | |
391 /// List of elements that the user has requested for reflection. | |
392 final Set<Element> targetsUsed = new Set<Element>(); | |
393 | |
394 /// List of annotations provided by user that indicate that the annotated | |
395 /// element must be retained. | |
396 final Set<Element> metaTargetsUsed = new Set<Element>(); | |
397 | |
398 /// Set of methods that are needed by reflection. Computed using | |
399 /// [computeMembersNeededForReflection] on first use. | |
400 Set<Element> _membersNeededForReflection = null; | |
401 Iterable<Element> get membersNeededForReflection { | |
402 assert(_membersNeededForReflection != null); | |
403 return _membersNeededForReflection; | |
404 } | |
405 | |
406 /// List of symbols that the user has requested for reflection. | |
407 final Set<String> symbolsUsed = new Set<String>(); | |
408 | |
409 /// List of elements that the backend may use. | |
410 final Set<Element> helpersUsed = new Set<Element>(); | |
411 | |
412 /// All the checked mode helpers. | |
413 static const checkedModeHelpers = CheckedModeHelper.helpers; | |
414 | |
415 // Checked mode helpers indexed by name. | |
416 Map<String, CheckedModeHelper> checkedModeHelperByName = | |
417 new Map<String, CheckedModeHelper>.fromIterable( | |
418 checkedModeHelpers, | |
419 key: (helper) => helper.name); | |
420 | |
421 TypeVariableHandler typeVariableHandler; | |
422 | |
423 /// Number of methods compiled before considering reflection. | |
424 int preMirrorsMethodCount = 0; | |
425 | |
426 /// Resolution and codegen support for generating table of interceptors and | |
427 /// constructors for custom elements. | |
428 CustomElementsAnalysis customElementsAnalysis; | |
429 | |
430 JavaScriptConstantTask constantCompilerTask; | |
431 | |
432 JavaScriptResolutionCallbacks resolutionCallbacks; | |
433 | |
434 JavaScriptBackend(Compiler compiler, bool generateSourceMap) | |
435 : namer = determineNamer(compiler), | |
436 oneShotInterceptors = new Map<String, Selector>(), | |
437 interceptedElements = new Map<String, Set<Element>>(), | |
438 rti = new RuntimeTypes(compiler), | |
439 specializedGetInterceptors = new Map<String, Set<ClassElement>>(), | |
440 super(compiler) { | |
441 emitter = new CodeEmitterTask(compiler, namer, generateSourceMap); | |
442 builder = new SsaBuilderTask(this); | |
443 optimizer = new SsaOptimizerTask(this); | |
444 generator = new SsaCodeGeneratorTask(this); | |
445 typeVariableHandler = new TypeVariableHandler(this); | |
446 customElementsAnalysis = new CustomElementsAnalysis(this); | |
447 constantCompilerTask = new JavaScriptConstantTask(compiler); | |
448 resolutionCallbacks = new JavaScriptResolutionCallbacks(this); | |
449 } | |
450 | |
451 ConstantSystem get constantSystem => constants.constantSystem; | |
452 | |
453 /// Returns constant environment for the JavaScript interpretation of the | |
454 /// constants. | |
455 JavaScriptConstantCompiler get constants { | |
456 return constantCompilerTask.jsConstantCompiler; | |
457 } | |
458 | |
459 // TODO(karlklose): Split into findHelperFunction and findHelperClass and | |
460 // add a check that the element has the expected kind. | |
461 Element findHelper(String name) => find(jsHelperLibrary, name); | |
462 Element findInterceptor(String name) => find(interceptorsLibrary, name); | |
463 | |
464 Element find(LibraryElement library, String name) { | |
465 Element element = library.findLocal(name); | |
466 assert(invariant(library, element != null, | |
467 message: "Element '$name' not found in '${library.canonicalUri}'.")); | |
468 return element; | |
469 } | |
470 | |
471 bool isForeign(Element element) => element.library == foreignLibrary; | |
472 | |
473 bool isBackendLibrary(LibraryElement library) { | |
474 return library == interceptorsLibrary || | |
475 library == jsHelperLibrary; | |
476 } | |
477 | |
478 static Namer determineNamer(Compiler compiler) { | |
479 return compiler.enableMinification ? | |
480 new MinifyNamer(compiler) : | |
481 new Namer(compiler); | |
482 } | |
483 | |
484 bool usedByBackend(Element element) { | |
485 if (element.isParameter | |
486 || element.isInitializingFormal | |
487 || element.isField) { | |
488 if (usedByBackend(element.enclosingElement)) return true; | |
489 } | |
490 return helpersUsed.contains(element.declaration); | |
491 } | |
492 | |
493 bool invokedReflectively(Element element) { | |
494 if (element.isParameter || element.isInitializingFormal) { | |
495 ParameterElement parameter = element; | |
496 if (invokedReflectively(parameter.functionDeclaration)) return true; | |
497 } | |
498 | |
499 if (element.isField) { | |
500 if (Elements.isStaticOrTopLevel(element) | |
501 && (element.isFinal || element.isConst)) { | |
502 return false; | |
503 } | |
504 } | |
505 | |
506 return isAccessibleByReflection(element.declaration); | |
507 } | |
508 | |
509 bool canBeUsedForGlobalOptimizations(Element element) { | |
510 return !usedByBackend(element) && !invokedReflectively(element); | |
511 } | |
512 | |
513 bool isInterceptorClass(ClassElement element) { | |
514 if (element == null) return false; | |
515 if (Elements.isNativeOrExtendsNative(element)) return true; | |
516 if (interceptedClasses.contains(element)) return true; | |
517 if (classesMixedIntoInterceptedClasses.contains(element)) return true; | |
518 return false; | |
519 } | |
520 | |
521 String registerOneShotInterceptor(Selector selector) { | |
522 Set<ClassElement> classes = getInterceptedClassesOn(selector.name); | |
523 String name = namer.getOneShotInterceptorName(selector, classes); | |
524 if (!oneShotInterceptors.containsKey(name)) { | |
525 registerSpecializedGetInterceptor(classes); | |
526 oneShotInterceptors[name] = selector; | |
527 } | |
528 return name; | |
529 } | |
530 | |
531 bool isInterceptedMethod(Element element) { | |
532 if (!element.isInstanceMember) return false; | |
533 if (element.isGenerativeConstructorBody) { | |
534 return Elements.isNativeOrExtendsNative(element.enclosingClass); | |
535 } | |
536 return interceptedElements[element.name] != null; | |
537 } | |
538 | |
539 bool fieldHasInterceptedGetter(Element element) { | |
540 assert(element.isField); | |
541 return interceptedElements[element.name] != null; | |
542 } | |
543 | |
544 bool fieldHasInterceptedSetter(Element element) { | |
545 assert(element.isField); | |
546 return interceptedElements[element.name] != null; | |
547 } | |
548 | |
549 bool isInterceptedName(String name) { | |
550 return interceptedElements[name] != null; | |
551 } | |
552 | |
553 bool isInterceptedSelector(Selector selector) { | |
554 return interceptedElements[selector.name] != null; | |
555 } | |
556 | |
557 /** | |
558 * Returns `true` iff [selector] matches an element defined in a class mixed | |
559 * into an intercepted class. These selectors are not eligible for the 'dummy | |
560 * explicit receiver' optimization. | |
561 */ | |
562 bool isInterceptedMixinSelector(Selector selector) { | |
563 Set<Element> elements = interceptedMixinElements.putIfAbsent( | |
564 selector.name, | |
565 () { | |
566 Set<Element> elements = interceptedElements[selector.name]; | |
567 if (elements == null) return null; | |
568 return elements | |
569 .where((element) => | |
570 classesMixedIntoInterceptedClasses.contains( | |
571 element.enclosingClass)) | |
572 .toSet(); | |
573 }); | |
574 | |
575 if (elements == null) return false; | |
576 if (elements.isEmpty) return false; | |
577 return elements.any((element) => selector.applies(element, compiler.world)); | |
578 } | |
579 | |
580 final Map<String, Set<ClassElement>> interceptedClassesCache = | |
581 new Map<String, Set<ClassElement>>(); | |
582 | |
583 /** | |
584 * Returns a set of interceptor classes that contain a member named | |
585 * [name]. Returns [:null:] if there is no class. | |
586 */ | |
587 Set<ClassElement> getInterceptedClassesOn(String name) { | |
588 Set<Element> intercepted = interceptedElements[name]; | |
589 if (intercepted == null) return null; | |
590 return interceptedClassesCache.putIfAbsent(name, () { | |
591 // Populate the cache by running through all the elements and | |
592 // determine if the given selector applies to them. | |
593 Set<ClassElement> result = new Set<ClassElement>(); | |
594 for (Element element in intercepted) { | |
595 ClassElement classElement = element.enclosingClass; | |
596 if (Elements.isNativeOrExtendsNative(classElement) | |
597 || interceptedClasses.contains(classElement)) { | |
598 result.add(classElement); | |
599 } | |
600 if (classesMixedIntoInterceptedClasses.contains(classElement)) { | |
601 Set<ClassElement> nativeSubclasses = | |
602 nativeSubclassesOfMixin(classElement); | |
603 if (nativeSubclasses != null) result.addAll(nativeSubclasses); | |
604 } | |
605 } | |
606 return result; | |
607 }); | |
608 } | |
609 | |
610 Set<ClassElement> nativeSubclassesOfMixin(ClassElement mixin) { | |
611 ClassWorld classWorld = compiler.world; | |
612 Iterable<MixinApplicationElement> uses = classWorld.mixinUsesOf(mixin); | |
613 Set<ClassElement> result = null; | |
614 for (MixinApplicationElement use in uses) { | |
615 Iterable<ClassElement> subclasses = classWorld.subclassesOf(use); | |
616 for (ClassElement subclass in subclasses) { | |
617 if (Elements.isNativeOrExtendsNative(subclass)) { | |
618 if (result == null) result = new Set<ClassElement>(); | |
619 result.add(subclass); | |
620 } | |
621 } | |
622 } | |
623 return result; | |
624 } | |
625 | |
626 bool operatorEqHandlesNullArgument(FunctionElement operatorEqfunction) { | |
627 return specialOperatorEqClasses.contains( | |
628 operatorEqfunction.enclosingClass); | |
629 } | |
630 | |
631 void validateInterceptorImplementsAllObjectMethods( | |
632 ClassElement interceptorClass) { | |
633 if (interceptorClass == null) return; | |
634 interceptorClass.ensureResolved(compiler); | |
635 compiler.objectClass.forEachMember((_, Element member) { | |
636 if (member.isGenerativeConstructor) return; | |
637 Element interceptorMember = interceptorClass.lookupMember(member.name); | |
638 // Interceptors must override all Object methods due to calling convention | |
639 // differences. | |
640 assert(interceptorMember.enclosingClass == interceptorClass); | |
641 }); | |
642 } | |
643 | |
644 void addInterceptorsForNativeClassMembers( | |
645 ClassElement cls, Enqueuer enqueuer) { | |
646 if (enqueuer.isResolutionQueue) { | |
647 cls.ensureResolved(compiler); | |
648 cls.forEachMember((ClassElement classElement, Element member) { | |
649 if (member.name == Compiler.CALL_OPERATOR_NAME) { | |
650 compiler.reportError( | |
651 member, | |
652 MessageKind.CALL_NOT_SUPPORTED_ON_NATIVE_CLASS); | |
653 return; | |
654 } | |
655 if (member.isSynthesized) return; | |
656 // All methods on [Object] are shadowed by [Interceptor]. | |
657 if (classElement == compiler.objectClass) return; | |
658 Set<Element> set = interceptedElements.putIfAbsent( | |
659 member.name, () => new Set<Element>()); | |
660 set.add(member); | |
661 }, | |
662 includeSuperAndInjectedMembers: true); | |
663 | |
664 // Walk superclass chain to find mixins. | |
665 for (; cls != null; cls = cls.superclass) { | |
666 if (cls.isMixinApplication) { | |
667 MixinApplicationElement mixinApplication = cls; | |
668 classesMixedIntoInterceptedClasses.add(mixinApplication.mixin); | |
669 } | |
670 } | |
671 } | |
672 } | |
673 | |
674 void addInterceptors(ClassElement cls, | |
675 Enqueuer enqueuer, | |
676 Registry registry) { | |
677 if (enqueuer.isResolutionQueue) { | |
678 _interceptedClasses.add(jsInterceptorClass); | |
679 _interceptedClasses.add(cls); | |
680 cls.ensureResolved(compiler); | |
681 cls.forEachMember((ClassElement classElement, Element member) { | |
682 // All methods on [Object] are shadowed by [Interceptor]. | |
683 if (classElement == compiler.objectClass) return; | |
684 Set<Element> set = interceptedElements.putIfAbsent( | |
685 member.name, () => new Set<Element>()); | |
686 set.add(member); | |
687 }, | |
688 includeSuperAndInjectedMembers: true); | |
689 } | |
690 enqueueClass(enqueuer, cls, registry); | |
691 } | |
692 | |
693 Set<ClassElement> get interceptedClasses { | |
694 assert(compiler.enqueuer.resolution.queueIsClosed); | |
695 return _interceptedClasses; | |
696 } | |
697 | |
698 void registerSpecializedGetInterceptor(Set<ClassElement> classes) { | |
699 String name = namer.getInterceptorName(getInterceptorMethod, classes); | |
700 if (classes.contains(jsInterceptorClass)) { | |
701 // We can't use a specialized [getInterceptorMethod], so we make | |
702 // sure we emit the one with all checks. | |
703 specializedGetInterceptors[name] = interceptedClasses; | |
704 } else { | |
705 specializedGetInterceptors[name] = classes; | |
706 } | |
707 } | |
708 | |
709 void registerCompileTimeConstant(ConstantValue constant, Registry registry) { | |
710 registerCompileTimeConstantInternal(constant, registry); | |
711 for (ConstantValue dependency in constant.getDependencies()) { | |
712 registerCompileTimeConstant(dependency, registry); | |
713 } | |
714 } | |
715 | |
716 void registerCompileTimeConstantInternal(ConstantValue constant, | |
717 Registry registry) { | |
718 DartType type = constant.computeType(compiler); | |
719 registerInstantiatedConstantType(type, registry); | |
720 | |
721 if (constant.isFunction) { | |
722 FunctionConstantValue function = constant; | |
723 registry.registerGetOfStaticFunction(function.element); | |
724 } else if (constant.isInterceptor) { | |
725 // An interceptor constant references the class's prototype chain. | |
726 InterceptorConstantValue interceptor = constant; | |
727 registerInstantiatedConstantType(interceptor.dispatchedType, registry); | |
728 } else if (constant.isType) { | |
729 enqueueInResolution(getCreateRuntimeType(), registry); | |
730 registry.registerInstantiation(typeImplementation.rawType); | |
731 } | |
732 } | |
733 | |
734 void registerInstantiatedConstantType(DartType type, Registry registry) { | |
735 DartType instantiatedType = | |
736 type.isFunctionType ? compiler.functionClass.rawType : type; | |
737 if (type is InterfaceType) { | |
738 registry.registerInstantiation(instantiatedType); | |
739 if (!type.treatAsRaw && classNeedsRti(type.element)) { | |
740 registry.registerStaticInvocation(getSetRuntimeTypeInfo()); | |
741 } | |
742 if (type.element == typeImplementation) { | |
743 // If we use a type literal in a constant, the compile time | |
744 // constant emitter will generate a call to the createRuntimeType | |
745 // helper so we register a use of that. | |
746 registry.registerStaticInvocation(getCreateRuntimeType()); | |
747 } | |
748 } | |
749 } | |
750 | |
751 void registerMetadataConstant(MetadataAnnotation metadata, | |
752 Element annotatedElement, | |
753 Registry registry) { | |
754 assert(registry.isForResolution); | |
755 ConstantValue constant = constants.getConstantForMetadata(metadata).value; | |
756 registerCompileTimeConstant(constant, registry); | |
757 metadataConstants.add(new Dependency(constant, annotatedElement)); | |
758 } | |
759 | |
760 void registerInstantiatedClass(ClassElement cls, | |
761 Enqueuer enqueuer, | |
762 Registry registry) { | |
763 if (!cls.typeVariables.isEmpty) { | |
764 typeVariableHandler.registerClassWithTypeVariables(cls); | |
765 } | |
766 | |
767 // Register any helper that will be needed by the backend. | |
768 if (enqueuer.isResolutionQueue) { | |
769 if (cls == compiler.intClass | |
770 || cls == compiler.doubleClass | |
771 || cls == compiler.numClass) { | |
772 // The backend will try to optimize number operations and use the | |
773 // `iae` helper directly. | |
774 enqueue(enqueuer, findHelper('iae'), registry); | |
775 } else if (cls == compiler.listClass | |
776 || cls == compiler.stringClass) { | |
777 // The backend will try to optimize array and string access and use the | |
778 // `ioore` and `iae` helpers directly. | |
779 enqueue(enqueuer, findHelper('ioore'), registry); | |
780 enqueue(enqueuer, findHelper('iae'), registry); | |
781 } else if (cls == compiler.functionClass) { | |
782 enqueueClass(enqueuer, closureClass, registry); | |
783 } else if (cls == compiler.mapClass) { | |
784 // The backend will use a literal list to initialize the entries | |
785 // of the map. | |
786 enqueueClass(enqueuer, compiler.listClass, registry); | |
787 enqueueClass(enqueuer, mapLiteralClass, registry); | |
788 // For map literals, the dependency between the implementation class | |
789 // and [Map] is not visible, so we have to add it manually. | |
790 rti.registerRtiDependency(mapLiteralClass, cls); | |
791 } else if (cls == boundClosureClass) { | |
792 // TODO(johnniwinther): Is this a noop? | |
793 enqueueClass(enqueuer, boundClosureClass, registry); | |
794 } else if (Elements.isNativeOrExtendsNative(cls)) { | |
795 enqueue(enqueuer, getNativeInterceptorMethod, registry); | |
796 enqueueClass(enqueuer, jsInterceptorClass, compiler.globalDependencies); | |
797 enqueueClass(enqueuer, jsPlainJavaScriptObjectClass, registry); | |
798 } else if (cls == mapLiteralClass) { | |
799 // For map literals, the dependency between the implementation class | |
800 // and [Map] is not visible, so we have to add it manually. | |
801 Element getFactory(String name, int arity) { | |
802 // The constructor is on the patch class, but dart2js unit tests don't | |
803 // have a patch class. | |
804 ClassElement implementation = cls.patch != null ? cls.patch : cls; | |
805 return implementation.lookupConstructor( | |
806 new Selector.callConstructor( | |
807 name, mapLiteralClass.library, arity), | |
808 (element) { | |
809 compiler.internalError(mapLiteralClass, | |
810 "Map literal class $mapLiteralClass missing " | |
811 "'$name' constructor" | |
812 " ${mapLiteralClass.constructors}"); | |
813 }); | |
814 } | |
815 mapLiteralConstructor = getFactory('_literal', 1); | |
816 mapLiteralConstructorEmpty = getFactory('_empty', 0); | |
817 enqueueInResolution(mapLiteralConstructor, registry); | |
818 enqueueInResolution(mapLiteralConstructorEmpty, registry); | |
819 } | |
820 } | |
821 if (cls == closureClass) { | |
822 enqueue(enqueuer, findHelper('closureFromTearOff'), registry); | |
823 } | |
824 ClassElement result = null; | |
825 if (cls == compiler.stringClass || cls == jsStringClass) { | |
826 addInterceptors(jsStringClass, enqueuer, registry); | |
827 } else if (cls == compiler.listClass || | |
828 cls == jsArrayClass || | |
829 cls == jsFixedArrayClass || | |
830 cls == jsExtendableArrayClass) { | |
831 addInterceptors(jsArrayClass, enqueuer, registry); | |
832 addInterceptors(jsMutableArrayClass, enqueuer, registry); | |
833 addInterceptors(jsFixedArrayClass, enqueuer, registry); | |
834 addInterceptors(jsExtendableArrayClass, enqueuer, registry); | |
835 } else if (cls == compiler.intClass || cls == jsIntClass) { | |
836 addInterceptors(jsIntClass, enqueuer, registry); | |
837 addInterceptors(jsPositiveIntClass, enqueuer, registry); | |
838 addInterceptors(jsUInt32Class, enqueuer, registry); | |
839 addInterceptors(jsUInt31Class, enqueuer, registry); | |
840 addInterceptors(jsNumberClass, enqueuer, registry); | |
841 } else if (cls == compiler.doubleClass || cls == jsDoubleClass) { | |
842 addInterceptors(jsDoubleClass, enqueuer, registry); | |
843 addInterceptors(jsNumberClass, enqueuer, registry); | |
844 } else if (cls == compiler.boolClass || cls == jsBoolClass) { | |
845 addInterceptors(jsBoolClass, enqueuer, registry); | |
846 } else if (cls == compiler.nullClass || cls == jsNullClass) { | |
847 addInterceptors(jsNullClass, enqueuer, registry); | |
848 } else if (cls == compiler.numClass || cls == jsNumberClass) { | |
849 addInterceptors(jsIntClass, enqueuer, registry); | |
850 addInterceptors(jsPositiveIntClass, enqueuer, registry); | |
851 addInterceptors(jsUInt32Class, enqueuer, registry); | |
852 addInterceptors(jsUInt31Class, enqueuer, registry); | |
853 addInterceptors(jsDoubleClass, enqueuer, registry); | |
854 addInterceptors(jsNumberClass, enqueuer, registry); | |
855 } else if (cls == jsPlainJavaScriptObjectClass) { | |
856 addInterceptors(jsPlainJavaScriptObjectClass, enqueuer, registry); | |
857 } else if (cls == jsUnknownJavaScriptObjectClass) { | |
858 addInterceptors(jsUnknownJavaScriptObjectClass, enqueuer, registry); | |
859 } else if (Elements.isNativeOrExtendsNative(cls)) { | |
860 addInterceptorsForNativeClassMembers(cls, enqueuer); | |
861 } else if (cls == jsIndexingBehaviorInterface) { | |
862 // These two helpers are used by the emitter and the codegen. | |
863 // Because we cannot enqueue elements at the time of emission, | |
864 // we make sure they are always generated. | |
865 enqueue(enqueuer, findHelper('isJsIndexable'), registry); | |
866 } | |
867 | |
868 customElementsAnalysis.registerInstantiatedClass(cls, enqueuer); | |
869 } | |
870 | |
871 void registerUseInterceptor(Enqueuer enqueuer) { | |
872 assert(!enqueuer.isResolutionQueue); | |
873 if (!enqueuer.nativeEnqueuer.hasInstantiatedNativeClasses()) return; | |
874 Registry registry = compiler.globalDependencies; | |
875 enqueue(enqueuer, getNativeInterceptorMethod, registry); | |
876 enqueueClass(enqueuer, jsPlainJavaScriptObjectClass, registry); | |
877 needToInitializeIsolateAffinityTag = true; | |
878 needToInitializeDispatchProperty = true; | |
879 } | |
880 | |
881 JavaScriptItemCompilationContext createItemCompilationContext() { | |
882 return new JavaScriptItemCompilationContext(); | |
883 } | |
884 | |
885 void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { | |
886 assert(interceptorsLibrary != null); | |
887 // TODO(ngeoffray): Not enqueuing those two classes currently make | |
888 // the compiler potentially crash. However, any reasonable program | |
889 // will instantiate those two classes. | |
890 addInterceptors(jsBoolClass, world, registry); | |
891 addInterceptors(jsNullClass, world, registry); | |
892 if (compiler.enableTypeAssertions) { | |
893 // Unconditionally register the helper that checks if the | |
894 // expression in an if/while/for is a boolean. | |
895 // TODO(ngeoffray): Should we have the resolver register those instead? | |
896 Element e = findHelper('boolConversionCheck'); | |
897 if (e != null) enqueue(world, e, registry); | |
898 } | |
899 if (TRACE_CALLS) { | |
900 traceHelper = findHelper('traceHelper'); | |
901 assert(traceHelper != null); | |
902 enqueueInResolution(traceHelper, registry); | |
903 } | |
904 registerCheckedModeHelpers(registry); | |
905 } | |
906 | |
907 onResolutionComplete() { | |
908 super.onResolutionComplete(); | |
909 computeMembersNeededForReflection(); | |
910 rti.computeClassesNeedingRti(); | |
911 computeFunctionsToAlwaysInline(); | |
912 } | |
913 | |
914 void computeFunctionsToAlwaysInline() { | |
915 functionsToAlwaysInline = <FunctionElement>[]; | |
916 if (internalLibrary == null) return; | |
917 | |
918 // Try to find all functions intended to always inline. If their enclosing | |
919 // class is not resolved we skip the methods, but it is an error to mention | |
920 // a function or class that cannot be found. | |
921 for (String className in ALWAYS_INLINE.keys) { | |
922 ClassElement cls = find(internalLibrary, className); | |
923 if (cls.resolutionState != STATE_DONE) continue; | |
924 for (String functionName in ALWAYS_INLINE[className]) { | |
925 Element function = cls.lookupMember(functionName); | |
926 assert(invariant(cls, function is FunctionElement, | |
927 message: 'unable to find function $functionName in $className')); | |
928 functionsToAlwaysInline.add(function); | |
929 } | |
930 } | |
931 } | |
932 | |
933 void registerGetRuntimeTypeArgument(Registry registry) { | |
934 enqueueInResolution(getGetRuntimeTypeArgument(), registry); | |
935 enqueueInResolution(getGetTypeArgumentByIndex(), registry); | |
936 enqueueInResolution(getCopyTypeArguments(), registry); | |
937 } | |
938 | |
939 void registerCallMethodWithFreeTypeVariables( | |
940 Element callMethod, | |
941 Enqueuer enqueuer, | |
942 Registry registry) { | |
943 if (enqueuer.isResolutionQueue || methodNeedsRti(callMethod)) { | |
944 registerComputeSignature(enqueuer, registry); | |
945 } | |
946 } | |
947 | |
948 void registerClosureWithFreeTypeVariables( | |
949 Element closure, | |
950 Enqueuer enqueuer, | |
951 Registry registry) { | |
952 if (enqueuer.isResolutionQueue || methodNeedsRti(closure)) { | |
953 registerComputeSignature(enqueuer, registry); | |
954 } | |
955 } | |
956 | |
957 void registerBoundClosure(Enqueuer enqueuer) { | |
958 enqueuer.registerInstantiatedClass( | |
959 boundClosureClass, | |
960 // Precise dependency is not important here. | |
961 compiler.globalDependencies); | |
962 } | |
963 | |
964 void registerGetOfStaticFunction(Enqueuer enqueuer) { | |
965 enqueuer.registerInstantiatedClass(closureClass, | |
966 compiler.globalDependencies); | |
967 } | |
968 | |
969 void registerComputeSignature(Enqueuer enqueuer, Registry registry) { | |
970 // Calls to [:computeSignature:] are generated by the emitter and we | |
971 // therefore need to enqueue the used elements in the codegen enqueuer as | |
972 // well as in the resolution enqueuer. | |
973 enqueue(enqueuer, getSetRuntimeTypeInfo(), registry); | |
974 enqueue(enqueuer, getGetRuntimeTypeInfo(), registry); | |
975 enqueue(enqueuer, getComputeSignature(), registry); | |
976 enqueue(enqueuer, getGetRuntimeTypeArguments(), registry); | |
977 enqueueClass(enqueuer, compiler.listClass, registry); | |
978 } | |
979 | |
980 void registerRuntimeType(Enqueuer enqueuer, Registry registry) { | |
981 registerComputeSignature(enqueuer, registry); | |
982 enqueueInResolution(getSetRuntimeTypeInfo(), registry); | |
983 enqueueInResolution(getGetRuntimeTypeInfo(), registry); | |
984 registerGetRuntimeTypeArgument(registry); | |
985 enqueueClass(enqueuer, compiler.listClass, registry); | |
986 } | |
987 | |
988 void registerIsCheckForCodegen(DartType type, | |
989 Enqueuer world, | |
990 Registry registry) { | |
991 assert(!registry.isForResolution); | |
992 type = type.unalias(compiler); | |
993 enqueueClass(world, compiler.boolClass, registry); | |
994 bool inCheckedMode = compiler.enableTypeAssertions; | |
995 // [registerIsCheck] is also called for checked mode checks, so we | |
996 // need to register checked mode helpers. | |
997 if (inCheckedMode) { | |
998 // All helpers are added to resolution queue in enqueueHelpers. These | |
999 // calls to enqueueInResolution serve as assertions that the helper was | |
1000 // in fact added. | |
1001 // TODO(13155): Find a way to enqueue helpers lazily. | |
1002 CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: false); | |
1003 if (helper != null) { | |
1004 enqueue(world, helper.getElement(compiler), registry); | |
1005 } | |
1006 // We also need the native variant of the check (for DOM types). | |
1007 helper = getNativeCheckedModeHelper(type, typeCast: false); | |
1008 if (helper != null) { | |
1009 enqueue(world, helper.getElement(compiler), registry); | |
1010 } | |
1011 } | |
1012 if (!type.treatAsRaw || type.containsTypeVariables) { | |
1013 enqueueClass(world, compiler.listClass, registry); | |
1014 } | |
1015 if (type.element != null && type.element.isNative) { | |
1016 // We will neeed to add the "$is" and "$as" properties on the | |
1017 // JavaScript object prototype, so we make sure | |
1018 // [:defineProperty:] is compiled. | |
1019 enqueue(world, findHelper('defineProperty'), registry); | |
1020 } | |
1021 } | |
1022 | |
1023 void registerTypeVariableBoundsSubtypeCheck(DartType typeArgument, | |
1024 DartType bound) { | |
1025 rti.registerTypeVariableBoundsSubtypeCheck(typeArgument, bound); | |
1026 } | |
1027 | |
1028 void registerCheckDeferredIsLoaded(Registry registry) { | |
1029 enqueueInResolution(getCheckDeferredIsLoaded(), registry); | |
1030 // Also register the types of the arguments passed to this method. | |
1031 enqueueClass(compiler.enqueuer.resolution, compiler.stringClass, registry); | |
1032 } | |
1033 | |
1034 void enableNoSuchMethod(Element context, Enqueuer world) { | |
1035 enqueue(world, getCreateInvocationMirror(), compiler.globalDependencies); | |
1036 world.registerInvocation(compiler.noSuchMethodSelector); | |
1037 // TODO(tyoverby): Send the context element to DumpInfoTask to be | |
1038 // blamed. | |
1039 } | |
1040 | |
1041 void enableIsolateSupport(Enqueuer enqueuer) { | |
1042 // TODO(floitsch): We should also ensure that the class IsolateMessage is | |
1043 // instantiated. Currently, just enabling isolate support works. | |
1044 if (compiler.mainFunction != null) { | |
1045 // The JavaScript backend implements [Isolate.spawn] by looking up | |
1046 // top-level functions by name. So all top-level function tear-off | |
1047 // closures have a private name field. | |
1048 // | |
1049 // The JavaScript backend of [Isolate.spawnUri] uses the same internal | |
1050 // implementation as [Isolate.spawn], and fails if it cannot look main up | |
1051 // by name. | |
1052 enqueuer.registerGetOfStaticFunction(compiler.mainFunction); | |
1053 } | |
1054 if (enqueuer.isResolutionQueue) { | |
1055 for (String name in const [START_ROOT_ISOLATE, | |
1056 '_currentIsolate', | |
1057 '_callInIsolate']) { | |
1058 Element element = find(isolateHelperLibrary, name); | |
1059 enqueuer.addToWorkList(element); | |
1060 compiler.globalDependencies.registerDependency(element); | |
1061 } | |
1062 } else { | |
1063 enqueuer.addToWorkList(find(isolateHelperLibrary, START_ROOT_ISOLATE)); | |
1064 } | |
1065 } | |
1066 | |
1067 bool isAssertMethod(Element element) => element == assertMethod; | |
1068 | |
1069 void registerRequiredType(DartType type, Element enclosingElement) { | |
1070 // If [argument] has type variables or is a type variable, this method | |
1071 // registers a RTI dependency between the class where the type variable is | |
1072 // defined (that is the enclosing class of the current element being | |
1073 // resolved) and the class of [type]. If the class of [type] requires RTI, | |
1074 // then the class of the type variable does too. | |
1075 ClassElement contextClass = Types.getClassContext(type); | |
1076 if (contextClass != null) { | |
1077 assert(contextClass == enclosingElement.enclosingClass.declaration); | |
1078 rti.registerRtiDependency(type.element, contextClass); | |
1079 } | |
1080 } | |
1081 | |
1082 void registerClassUsingVariableExpression(ClassElement cls) { | |
1083 rti.classesUsingTypeVariableExpression.add(cls); | |
1084 } | |
1085 | |
1086 bool classNeedsRti(ClassElement cls) { | |
1087 return rti.classesNeedingRti.contains(cls.declaration) || | |
1088 compiler.enabledRuntimeType; | |
1089 } | |
1090 | |
1091 bool isDefaultNoSuchMethodImplementation(Element element) { | |
1092 assert(element.name == Compiler.NO_SUCH_METHOD); | |
1093 ClassElement classElement = element.enclosingClass; | |
1094 return classElement == compiler.objectClass | |
1095 || classElement == jsInterceptorClass | |
1096 || classElement == jsNullClass; | |
1097 } | |
1098 | |
1099 bool isDefaultEqualityImplementation(Element element) { | |
1100 assert(element.name == '=='); | |
1101 ClassElement classElement = element.enclosingClass; | |
1102 return classElement == compiler.objectClass | |
1103 || classElement == jsInterceptorClass | |
1104 || classElement == jsNullClass; | |
1105 } | |
1106 | |
1107 bool methodNeedsRti(FunctionElement function) { | |
1108 return rti.methodsNeedingRti.contains(function) || | |
1109 compiler.enabledRuntimeType; | |
1110 } | |
1111 | |
1112 /// The backend must *always* call this method when enqueuing an | |
1113 /// element. Calls done by the backend are not seen by global | |
1114 /// optimizations, so they would make these optimizations unsound. | |
1115 /// Therefore we need to collect the list of helpers the backend may | |
1116 /// use. | |
1117 Element registerBackendUse(Element element) { | |
1118 if (element != null) { | |
1119 helpersUsed.add(element.declaration); | |
1120 if (element.isClass && element.isPatched) { | |
1121 // Both declaration and implementation may declare fields, so we | |
1122 // add both to the list of helpers. | |
1123 helpersUsed.add(element.implementation); | |
1124 } | |
1125 } | |
1126 return element; | |
1127 } | |
1128 | |
1129 /// Enqueue [e] in [enqueuer]. | |
1130 /// | |
1131 /// This method calls [registerBackendUse]. | |
1132 void enqueue(Enqueuer enqueuer, Element e, Registry registry) { | |
1133 if (e == null) return; | |
1134 registerBackendUse(e); | |
1135 enqueuer.addToWorkList(e); | |
1136 registry.registerDependency(e); | |
1137 } | |
1138 | |
1139 /// Enqueue [e] in the resolution enqueuer. | |
1140 /// | |
1141 /// This method calls [registerBackendUse]. | |
1142 void enqueueInResolution(Element e, Registry registry) { | |
1143 if (e == null) return; | |
1144 ResolutionEnqueuer enqueuer = compiler.enqueuer.resolution; | |
1145 enqueue(enqueuer, e, registry); | |
1146 } | |
1147 | |
1148 /// Register instantiation of [cls] in [enqueuer]. | |
1149 /// | |
1150 /// This method calls [registerBackendUse]. | |
1151 void enqueueClass(Enqueuer enqueuer, Element cls, Registry registry) { | |
1152 if (cls == null) return; | |
1153 registerBackendUse(cls); | |
1154 helpersUsed.add(cls.declaration); | |
1155 if (cls.declaration != cls.implementation) { | |
1156 helpersUsed.add(cls.implementation); | |
1157 } | |
1158 enqueuer.registerInstantiatedClass(cls, registry); | |
1159 } | |
1160 | |
1161 void codegen(CodegenWorkItem work) { | |
1162 Element element = work.element; | |
1163 var kind = element.kind; | |
1164 if (kind == ElementKind.TYPEDEF) return; | |
1165 if (element.isConstructor && element.enclosingClass == jsNullClass) { | |
1166 // Work around a problem compiling JSNull's constructor. | |
1167 return; | |
1168 } | |
1169 if (kind.category == ElementCategory.VARIABLE) { | |
1170 ConstantExpression initialValue = constants.getConstantForVariable(element
); | |
1171 if (initialValue != null) { | |
1172 registerCompileTimeConstant(initialValue.value, work.registry); | |
1173 constants.addCompileTimeConstantForEmission(initialValue.value); | |
1174 // We don't need to generate code for static or top-level | |
1175 // variables. For instance variables, we may need to generate | |
1176 // the checked setter. | |
1177 if (Elements.isStaticOrTopLevel(element)) return; | |
1178 } else { | |
1179 // If the constant-handler was not able to produce a result we have to | |
1180 // go through the builder (below) to generate the lazy initializer for | |
1181 // the static variable. | |
1182 // We also need to register the use of the cyclic-error helper. | |
1183 compiler.enqueuer.codegen.registerStaticUse(getCyclicThrowHelper()); | |
1184 } | |
1185 } | |
1186 HGraph graph = builder.build(work); | |
1187 optimizer.optimize(work, graph); | |
1188 jsAst.Expression code = generator.generateCode(work, graph); | |
1189 generatedCode[element] = code; | |
1190 } | |
1191 | |
1192 native.NativeEnqueuer nativeResolutionEnqueuer(Enqueuer world) { | |
1193 return new native.NativeResolutionEnqueuer(world, compiler); | |
1194 } | |
1195 | |
1196 native.NativeEnqueuer nativeCodegenEnqueuer(Enqueuer world) { | |
1197 return new native.NativeCodegenEnqueuer(world, compiler, emitter); | |
1198 } | |
1199 | |
1200 ClassElement defaultSuperclass(ClassElement element) { | |
1201 // Native classes inherit from Interceptor. | |
1202 return element.isNative ? jsInterceptorClass : compiler.objectClass; | |
1203 } | |
1204 | |
1205 /** | |
1206 * Unit test hook that returns code of an element as a String. | |
1207 * | |
1208 * Invariant: [element] must be a declaration element. | |
1209 */ | |
1210 String assembleCode(Element element) { | |
1211 assert(invariant(element, element.isDeclaration)); | |
1212 return jsAst.prettyPrint(generatedCode[element], compiler).getText(); | |
1213 } | |
1214 | |
1215 void assembleProgram() { | |
1216 emitter.assembleProgram(); | |
1217 int totalMethodCount = generatedCode.length; | |
1218 if (totalMethodCount != preMirrorsMethodCount) { | |
1219 int mirrorCount = totalMethodCount - preMirrorsMethodCount; | |
1220 double percentage = (mirrorCount / totalMethodCount) * 100; | |
1221 compiler.reportHint( | |
1222 compiler.mainApp, MessageKind.MIRROR_BLOAT, | |
1223 {'count': mirrorCount, | |
1224 'total': totalMethodCount, | |
1225 'percentage': percentage.round()}); | |
1226 for (LibraryElement library in compiler.libraryLoader.libraries) { | |
1227 if (library.isInternalLibrary) continue; | |
1228 for (LibraryTag tag in library.tags) { | |
1229 Import importTag = tag.asImport(); | |
1230 if (importTag == null) continue; | |
1231 LibraryElement importedLibrary = library.getLibraryFromTag(tag); | |
1232 if (importedLibrary != compiler.mirrorsLibrary) continue; | |
1233 MessageKind kind = | |
1234 compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(library) | |
1235 ? MessageKind.MIRROR_IMPORT | |
1236 : MessageKind.MIRROR_IMPORT_NO_USAGE; | |
1237 compiler.withCurrentElement(library, () { | |
1238 compiler.reportInfo(importTag, kind); | |
1239 }); | |
1240 } | |
1241 } | |
1242 } | |
1243 } | |
1244 | |
1245 Element getDartClass(Element element) { | |
1246 for (ClassElement dartClass in implementationClasses.keys) { | |
1247 if (element == implementationClasses[dartClass]) { | |
1248 return dartClass; | |
1249 } | |
1250 } | |
1251 return element; | |
1252 } | |
1253 | |
1254 /** | |
1255 * Returns the checked mode helper that will be needed to do a type check/type | |
1256 * cast on [type] at runtime. Note that this method is being called both by | |
1257 * the resolver with interface types (int, String, ...), and by the SSA | |
1258 * backend with implementation types (JSInt, JSString, ...). | |
1259 */ | |
1260 CheckedModeHelper getCheckedModeHelper(DartType type, {bool typeCast}) { | |
1261 return getCheckedModeHelperInternal( | |
1262 type, typeCast: typeCast, nativeCheckOnly: false); | |
1263 } | |
1264 | |
1265 /** | |
1266 * Returns the native checked mode helper that will be needed to do a type | |
1267 * check/type cast on [type] at runtime. If no native helper exists for | |
1268 * [type], [:null:] is returned. | |
1269 */ | |
1270 CheckedModeHelper getNativeCheckedModeHelper(DartType type, {bool typeCast}) { | |
1271 return getCheckedModeHelperInternal( | |
1272 type, typeCast: typeCast, nativeCheckOnly: true); | |
1273 } | |
1274 | |
1275 /** | |
1276 * Returns the checked mode helper for the type check/type cast for [type]. If | |
1277 * [nativeCheckOnly] is [:true:], only names for native helpers are returned. | |
1278 */ | |
1279 CheckedModeHelper getCheckedModeHelperInternal(DartType type, | |
1280 {bool typeCast, | |
1281 bool nativeCheckOnly}) { | |
1282 String name = getCheckedModeHelperNameInternal(type, | |
1283 typeCast: typeCast, nativeCheckOnly: nativeCheckOnly); | |
1284 if (name == null) return null; | |
1285 CheckedModeHelper helper = checkedModeHelperByName[name]; | |
1286 assert(helper != null); | |
1287 return helper; | |
1288 } | |
1289 | |
1290 String getCheckedModeHelperNameInternal(DartType type, | |
1291 {bool typeCast, | |
1292 bool nativeCheckOnly}) { | |
1293 assert(type.kind != TypeKind.TYPEDEF); | |
1294 if (type.isMalformed) { | |
1295 // The same error is thrown for type test and type cast of a malformed | |
1296 // type so we only need one check method. | |
1297 return 'checkMalformedType'; | |
1298 } | |
1299 Element element = type.element; | |
1300 bool nativeCheck = nativeCheckOnly || | |
1301 emitter.nativeEmitter.requiresNativeIsCheck(element); | |
1302 | |
1303 // TODO(13955), TODO(9731). The test for non-primitive types should use an | |
1304 // interceptor. The interceptor should be an argument to HTypeConversion so | |
1305 // that it can be optimized by standard interceptor optimizations. | |
1306 nativeCheck = true; | |
1307 | |
1308 if (type.isVoid) { | |
1309 assert(!typeCast); // Cannot cast to void. | |
1310 if (nativeCheckOnly) return null; | |
1311 return 'voidTypeCheck'; | |
1312 } else if (element == jsStringClass || element == compiler.stringClass) { | |
1313 if (nativeCheckOnly) return null; | |
1314 return typeCast | |
1315 ? 'stringTypeCast' | |
1316 : 'stringTypeCheck'; | |
1317 } else if (element == jsDoubleClass || element == compiler.doubleClass) { | |
1318 if (nativeCheckOnly) return null; | |
1319 return typeCast | |
1320 ? 'doubleTypeCast' | |
1321 : 'doubleTypeCheck'; | |
1322 } else if (element == jsNumberClass || element == compiler.numClass) { | |
1323 if (nativeCheckOnly) return null; | |
1324 return typeCast | |
1325 ? 'numTypeCast' | |
1326 : 'numTypeCheck'; | |
1327 } else if (element == jsBoolClass || element == compiler.boolClass) { | |
1328 if (nativeCheckOnly) return null; | |
1329 return typeCast | |
1330 ? 'boolTypeCast' | |
1331 : 'boolTypeCheck'; | |
1332 } else if (element == jsIntClass || element == compiler.intClass | |
1333 || element == jsUInt32Class || element == jsUInt31Class | |
1334 || element == jsPositiveIntClass) { | |
1335 if (nativeCheckOnly) return null; | |
1336 return typeCast | |
1337 ? 'intTypeCast' | |
1338 : 'intTypeCheck'; | |
1339 } else if (Elements.isNumberOrStringSupertype(element, compiler)) { | |
1340 if (nativeCheck) { | |
1341 return typeCast | |
1342 ? 'numberOrStringSuperNativeTypeCast' | |
1343 : 'numberOrStringSuperNativeTypeCheck'; | |
1344 } else { | |
1345 return typeCast | |
1346 ? 'numberOrStringSuperTypeCast' | |
1347 : 'numberOrStringSuperTypeCheck'; | |
1348 } | |
1349 } else if (Elements.isStringOnlySupertype(element, compiler)) { | |
1350 if (nativeCheck) { | |
1351 return typeCast | |
1352 ? 'stringSuperNativeTypeCast' | |
1353 : 'stringSuperNativeTypeCheck'; | |
1354 } else { | |
1355 return typeCast | |
1356 ? 'stringSuperTypeCast' | |
1357 : 'stringSuperTypeCheck'; | |
1358 } | |
1359 } else if ((element == compiler.listClass || element == jsArrayClass) && | |
1360 type.treatAsRaw) { | |
1361 if (nativeCheckOnly) return null; | |
1362 return typeCast | |
1363 ? 'listTypeCast' | |
1364 : 'listTypeCheck'; | |
1365 } else { | |
1366 if (Elements.isListSupertype(element, compiler)) { | |
1367 if (nativeCheck) { | |
1368 return typeCast | |
1369 ? 'listSuperNativeTypeCast' | |
1370 : 'listSuperNativeTypeCheck'; | |
1371 } else { | |
1372 return typeCast | |
1373 ? 'listSuperTypeCast' | |
1374 : 'listSuperTypeCheck'; | |
1375 } | |
1376 } else { | |
1377 if (type.isInterfaceType && !type.treatAsRaw) { | |
1378 return typeCast | |
1379 ? 'subtypeCast' | |
1380 : 'assertSubtype'; | |
1381 } else if (type.isTypeVariable) { | |
1382 return typeCast | |
1383 ? 'subtypeOfRuntimeTypeCast' | |
1384 : 'assertSubtypeOfRuntimeType'; | |
1385 } else if (type.isFunctionType) { | |
1386 return null; | |
1387 } else { | |
1388 if (nativeCheck) { | |
1389 // TODO(karlklose): can we get rid of this branch when we use | |
1390 // interceptors? | |
1391 return typeCast | |
1392 ? 'interceptedTypeCast' | |
1393 : 'interceptedTypeCheck'; | |
1394 } else { | |
1395 return typeCast | |
1396 ? 'propertyTypeCast' | |
1397 : 'propertyTypeCheck'; | |
1398 } | |
1399 } | |
1400 } | |
1401 } | |
1402 } | |
1403 | |
1404 void registerCheckedModeHelpers(Registry registry) { | |
1405 // We register all the helpers in the resolution queue. | |
1406 // TODO(13155): Find a way to register fewer helpers. | |
1407 for (CheckedModeHelper helper in checkedModeHelpers) { | |
1408 enqueueInResolution(helper.getElement(compiler), registry); | |
1409 } | |
1410 } | |
1411 | |
1412 /** | |
1413 * Returns [:true:] if the checking of [type] is performed directly on the | |
1414 * object and not on an interceptor. | |
1415 */ | |
1416 bool hasDirectCheckFor(DartType type) { | |
1417 Element element = type.element; | |
1418 return element == compiler.stringClass || | |
1419 element == compiler.boolClass || | |
1420 element == compiler.numClass || | |
1421 element == compiler.intClass || | |
1422 element == compiler.doubleClass || | |
1423 element == jsArrayClass || | |
1424 element == jsMutableArrayClass || | |
1425 element == jsExtendableArrayClass || | |
1426 element == jsFixedArrayClass; | |
1427 } | |
1428 | |
1429 Element getExceptionUnwrapper() { | |
1430 return findHelper('unwrapException'); | |
1431 } | |
1432 | |
1433 Element getThrowRuntimeError() { | |
1434 return findHelper('throwRuntimeError'); | |
1435 } | |
1436 | |
1437 Element getThrowTypeError() { | |
1438 return findHelper('throwTypeError'); | |
1439 } | |
1440 | |
1441 Element getThrowAbstractClassInstantiationError() { | |
1442 return findHelper('throwAbstractClassInstantiationError'); | |
1443 } | |
1444 | |
1445 Element getStringInterpolationHelper() { | |
1446 return findHelper('S'); | |
1447 } | |
1448 | |
1449 Element getWrapExceptionHelper() { | |
1450 return findHelper(r'wrapException'); | |
1451 } | |
1452 | |
1453 Element getThrowExpressionHelper() { | |
1454 return findHelper('throwExpression'); | |
1455 } | |
1456 | |
1457 Element getClosureConverter() { | |
1458 return findHelper('convertDartClosureToJS'); | |
1459 } | |
1460 | |
1461 Element getTraceFromException() { | |
1462 return findHelper('getTraceFromException'); | |
1463 } | |
1464 | |
1465 Element getSetRuntimeTypeInfo() { | |
1466 return findHelper('setRuntimeTypeInfo'); | |
1467 } | |
1468 | |
1469 Element getGetRuntimeTypeInfo() { | |
1470 return findHelper('getRuntimeTypeInfo'); | |
1471 } | |
1472 | |
1473 Element getGetTypeArgumentByIndex() { | |
1474 return findHelper('getTypeArgumentByIndex'); | |
1475 } | |
1476 | |
1477 Element getCopyTypeArguments() { | |
1478 return findHelper('copyTypeArguments'); | |
1479 } | |
1480 | |
1481 Element getComputeSignature() { | |
1482 return findHelper('computeSignature'); | |
1483 } | |
1484 | |
1485 Element getGetRuntimeTypeArguments() { | |
1486 return findHelper('getRuntimeTypeArguments'); | |
1487 } | |
1488 | |
1489 Element getGetRuntimeTypeArgument() { | |
1490 return findHelper('getRuntimeTypeArgument'); | |
1491 } | |
1492 | |
1493 Element getRuntimeTypeToString() { | |
1494 return findHelper('runtimeTypeToString'); | |
1495 } | |
1496 | |
1497 Element getAssertIsSubtype() { | |
1498 return findHelper('assertIsSubtype'); | |
1499 } | |
1500 | |
1501 Element getCheckSubtype() { | |
1502 return findHelper('checkSubtype'); | |
1503 } | |
1504 | |
1505 Element getAssertSubtype() { | |
1506 return findHelper('assertSubtype'); | |
1507 } | |
1508 | |
1509 Element getCheckSubtypeOfRuntimeType() { | |
1510 return findHelper('checkSubtypeOfRuntimeType'); | |
1511 } | |
1512 | |
1513 Element getCheckDeferredIsLoaded() { | |
1514 return findHelper('checkDeferredIsLoaded'); | |
1515 } | |
1516 | |
1517 Element getAssertSubtypeOfRuntimeType() { | |
1518 return findHelper('assertSubtypeOfRuntimeType'); | |
1519 } | |
1520 | |
1521 Element getThrowNoSuchMethod() { | |
1522 return findHelper('throwNoSuchMethod'); | |
1523 } | |
1524 | |
1525 Element getCreateRuntimeType() { | |
1526 return findHelper('createRuntimeType'); | |
1527 } | |
1528 | |
1529 Element getFallThroughError() { | |
1530 return findHelper("getFallThroughError"); | |
1531 } | |
1532 | |
1533 Element getCreateInvocationMirror() { | |
1534 return findHelper(Compiler.CREATE_INVOCATION_MIRROR); | |
1535 } | |
1536 | |
1537 Element getCyclicThrowHelper() { | |
1538 return findHelper("throwCyclicInit"); | |
1539 } | |
1540 | |
1541 bool isNullImplementation(ClassElement cls) { | |
1542 return cls == jsNullClass; | |
1543 } | |
1544 | |
1545 ClassElement get intImplementation => jsIntClass; | |
1546 ClassElement get uint32Implementation => jsUInt32Class; | |
1547 ClassElement get uint31Implementation => jsUInt31Class; | |
1548 ClassElement get positiveIntImplementation => jsPositiveIntClass; | |
1549 ClassElement get doubleImplementation => jsDoubleClass; | |
1550 ClassElement get numImplementation => jsNumberClass; | |
1551 ClassElement get stringImplementation => jsStringClass; | |
1552 ClassElement get listImplementation => jsArrayClass; | |
1553 ClassElement get constListImplementation => jsArrayClass; | |
1554 ClassElement get fixedListImplementation => jsFixedArrayClass; | |
1555 ClassElement get growableListImplementation => jsExtendableArrayClass; | |
1556 ClassElement get mapImplementation => mapLiteralClass; | |
1557 ClassElement get constMapImplementation => constMapLiteralClass; | |
1558 ClassElement get typeImplementation => typeLiteralClass; | |
1559 ClassElement get boolImplementation => jsBoolClass; | |
1560 ClassElement get nullImplementation => jsNullClass; | |
1561 | |
1562 void registerStaticUse(Element element, Enqueuer enqueuer) { | |
1563 if (element == disableTreeShakingMarker) { | |
1564 compiler.disableTypeInferenceForMirrors = true; | |
1565 isTreeShakingDisabled = true; | |
1566 typeVariableHandler.onTreeShakingDisabled(enqueuer); | |
1567 } else if (element == preserveNamesMarker) { | |
1568 mustPreserveNames = true; | |
1569 } else if (element == preserveMetadataMarker) { | |
1570 mustRetainMetadata = true; | |
1571 } else if (element == preserveUrisMarker) { | |
1572 if (compiler.preserveUris) mustPreserveUris = true; | |
1573 } else if (element == preserveLibraryNamesMarker) { | |
1574 mustRetainLibraryNames = true; | |
1575 } else if (element == getIsolateAffinityTagMarker) { | |
1576 needToInitializeIsolateAffinityTag = true; | |
1577 } else if (element.isDeferredLoaderGetter) { | |
1578 // TODO(sigurdm): Create a function registerLoadLibraryAccess. | |
1579 if (compiler.loadLibraryFunction == null) { | |
1580 compiler.loadLibraryFunction = | |
1581 findHelper("_loadLibraryWrapper"); | |
1582 enqueueInResolution(compiler.loadLibraryFunction, | |
1583 compiler.globalDependencies); | |
1584 } | |
1585 } else if (element == requiresPreambleMarker) { | |
1586 requiresPreamble = true; | |
1587 } | |
1588 customElementsAnalysis.registerStaticUse(element, enqueuer); | |
1589 } | |
1590 | |
1591 /// Called when [:const Symbol(name):] is seen. | |
1592 void registerConstSymbol(String name, Registry registry) { | |
1593 symbolsUsed.add(name); | |
1594 if (name.endsWith('=')) { | |
1595 symbolsUsed.add(name.substring(0, name.length - 1)); | |
1596 } | |
1597 } | |
1598 | |
1599 /// Called when [:new Symbol(...):] is seen. | |
1600 void registerNewSymbol(Registry registry) { | |
1601 } | |
1602 | |
1603 /// Should [element] (a getter) that would normally not be generated due to | |
1604 /// treeshaking be retained for reflection? | |
1605 bool shouldRetainGetter(Element element) { | |
1606 return isTreeShakingDisabled && isAccessibleByReflection(element); | |
1607 } | |
1608 | |
1609 /// Should [element] (a setter) hat would normally not be generated due to | |
1610 /// treeshaking be retained for reflection? | |
1611 bool shouldRetainSetter(Element element) { | |
1612 return isTreeShakingDisabled && isAccessibleByReflection(element); | |
1613 } | |
1614 | |
1615 /// Should [name] be retained for reflection? | |
1616 bool shouldRetainName(String name) { | |
1617 if (hasInsufficientMirrorsUsed) return mustPreserveNames; | |
1618 if (name == '') return false; | |
1619 return symbolsUsed.contains(name); | |
1620 } | |
1621 | |
1622 bool retainMetadataOf(Element element) { | |
1623 if (mustRetainMetadata) hasRetainedMetadata = true; | |
1624 if (mustRetainMetadata && referencedFromMirrorSystem(element)) { | |
1625 for (MetadataAnnotation metadata in element.metadata) { | |
1626 metadata.ensureResolved(compiler); | |
1627 ConstantValue constant = | |
1628 constants.getConstantForMetadata(metadata).value; | |
1629 constants.addCompileTimeConstantForEmission(constant); | |
1630 } | |
1631 return true; | |
1632 } | |
1633 return false; | |
1634 } | |
1635 | |
1636 void onLibraryCreated(LibraryElement library) { | |
1637 Uri uri = library.canonicalUri; | |
1638 if (uri == DART_JS_HELPER) { | |
1639 jsHelperLibrary = library; | |
1640 } else if (uri == DART_INTERNAL) { | |
1641 internalLibrary = library; | |
1642 } else if (uri == DART_INTERCEPTORS) { | |
1643 interceptorsLibrary = library; | |
1644 } else if (uri == DART_FOREIGN_HELPER) { | |
1645 foreignLibrary = library; | |
1646 } else if (uri == DART_ISOLATE_HELPER) { | |
1647 isolateHelperLibrary = library; | |
1648 } | |
1649 } | |
1650 | |
1651 void initializeHelperClasses() { | |
1652 final List missingHelperClasses = []; | |
1653 ClassElement lookupHelperClass(String name) { | |
1654 ClassElement result = findHelper(name); | |
1655 if (result == null) { | |
1656 missingHelperClasses.add(name); | |
1657 } | |
1658 return result; | |
1659 } | |
1660 jsInvocationMirrorClass = lookupHelperClass('JSInvocationMirror'); | |
1661 boundClosureClass = lookupHelperClass('BoundClosure'); | |
1662 closureClass = lookupHelperClass('Closure'); | |
1663 if (!missingHelperClasses.isEmpty) { | |
1664 compiler.internalError(jsHelperLibrary, | |
1665 'dart:_js_helper library does not contain required classes: ' | |
1666 '$missingHelperClasses'); | |
1667 } | |
1668 } | |
1669 | |
1670 Future onLibraryScanned(LibraryElement library, LibraryLoader loader) { | |
1671 return super.onLibraryScanned(library, loader).then((_) { | |
1672 Uri uri = library.canonicalUri; | |
1673 | |
1674 VariableElement findVariable(String name) { | |
1675 return find(library, name); | |
1676 } | |
1677 | |
1678 FunctionElement findMethod(String name) { | |
1679 return find(library, name); | |
1680 } | |
1681 | |
1682 ClassElement findClass(String name) { | |
1683 return find(library, name); | |
1684 } | |
1685 | |
1686 if (uri == DART_INTERCEPTORS) { | |
1687 getInterceptorMethod = findMethod('getInterceptor'); | |
1688 interceptedNames = findVariable('interceptedNames'); | |
1689 mapTypeToInterceptor = findVariable('mapTypeToInterceptor'); | |
1690 getNativeInterceptorMethod = findMethod('getNativeInterceptor'); | |
1691 | |
1692 List<ClassElement> classes = [ | |
1693 jsInterceptorClass = findClass('Interceptor'), | |
1694 jsStringClass = findClass('JSString'), | |
1695 jsArrayClass = findClass('JSArray'), | |
1696 // The int class must be before the double class, because the | |
1697 // emitter relies on this list for the order of type checks. | |
1698 jsIntClass = findClass('JSInt'), | |
1699 jsPositiveIntClass = findClass('JSPositiveInt'), | |
1700 jsUInt32Class = findClass('JSUInt32'), | |
1701 jsUInt31Class = findClass('JSUInt31'), | |
1702 jsDoubleClass = findClass('JSDouble'), | |
1703 jsNumberClass = findClass('JSNumber'), | |
1704 jsNullClass = findClass('JSNull'), | |
1705 jsBoolClass = findClass('JSBool'), | |
1706 jsMutableArrayClass = findClass('JSMutableArray'), | |
1707 jsFixedArrayClass = findClass('JSFixedArray'), | |
1708 jsExtendableArrayClass = findClass('JSExtendableArray'), | |
1709 jsPlainJavaScriptObjectClass = findClass('PlainJavaScriptObject'), | |
1710 jsUnknownJavaScriptObjectClass = findClass('UnknownJavaScriptObject'), | |
1711 ]; | |
1712 | |
1713 jsIndexableClass = findClass('JSIndexable'); | |
1714 jsMutableIndexableClass = findClass('JSMutableIndexable'); | |
1715 } else if (uri == DART_JS_HELPER) { | |
1716 initializeHelperClasses(); | |
1717 assertMethod = findHelper('assertHelper'); | |
1718 | |
1719 typeLiteralClass = findClass('TypeImpl'); | |
1720 constMapLiteralClass = findClass('ConstantMap'); | |
1721 typeVariableClass = findClass('TypeVariable'); | |
1722 | |
1723 jsIndexingBehaviorInterface = findClass('JavaScriptIndexingBehavior'); | |
1724 | |
1725 noSideEffectsClass = findClass('NoSideEffects'); | |
1726 noThrowsClass = findClass('NoThrows'); | |
1727 noInlineClass = findClass('NoInline'); | |
1728 irRepresentationClass = findClass('IrRepresentation'); | |
1729 | |
1730 getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); | |
1731 | |
1732 requiresPreambleMarker = findMethod('requiresPreamble'); | |
1733 } else if (uri == DART_JS_MIRRORS) { | |
1734 disableTreeShakingMarker = find(library, 'disableTreeShaking'); | |
1735 preserveMetadataMarker = find(library, 'preserveMetadata'); | |
1736 preserveUrisMarker = find(library, 'preserveUris'); | |
1737 preserveLibraryNamesMarker = find(library, 'preserveLibraryNames'); | |
1738 } else if (uri == DART_JS_NAMES) { | |
1739 preserveNamesMarker = find(library, 'preserveNames'); | |
1740 } else if (uri == DART_HTML) { | |
1741 htmlLibraryIsLoaded = true; | |
1742 } | |
1743 }); | |
1744 } | |
1745 | |
1746 Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { | |
1747 if (!loadedLibraries.containsKey(Compiler.DART_CORE)) { | |
1748 return new Future.value(); | |
1749 } | |
1750 | |
1751 assert(loadedLibraries.containsKey(Compiler.DART_CORE)); | |
1752 assert(loadedLibraries.containsKey(DART_INTERCEPTORS)); | |
1753 assert(loadedLibraries.containsKey(DART_JS_HELPER)); | |
1754 | |
1755 if (jsInvocationMirrorClass != null) { | |
1756 jsInvocationMirrorClass.ensureResolved(compiler); | |
1757 invokeOnMethod = jsInvocationMirrorClass.lookupLocalMember(INVOKE_ON); | |
1758 } | |
1759 | |
1760 // [LinkedHashMap] is reexported from dart:collection and can therefore not | |
1761 // be loaded from dart:core in [onLibraryScanned]. | |
1762 mapLiteralClass = compiler.coreLibrary.find('LinkedHashMap'); | |
1763 assert(invariant(compiler.coreLibrary, mapLiteralClass != null, | |
1764 message: "Element 'LinkedHashMap' not found in 'dart:core'.")); | |
1765 | |
1766 implementationClasses = <ClassElement, ClassElement>{}; | |
1767 implementationClasses[compiler.intClass] = jsIntClass; | |
1768 implementationClasses[compiler.boolClass] = jsBoolClass; | |
1769 implementationClasses[compiler.numClass] = jsNumberClass; | |
1770 implementationClasses[compiler.doubleClass] = jsDoubleClass; | |
1771 implementationClasses[compiler.stringClass] = jsStringClass; | |
1772 implementationClasses[compiler.listClass] = jsArrayClass; | |
1773 implementationClasses[compiler.nullClass] = jsNullClass; | |
1774 | |
1775 // These methods are overwritten with generated versions. | |
1776 inlineCache.markAsNonInlinable(getInterceptorMethod, insideLoop: true); | |
1777 | |
1778 // TODO(kasperl): Some tests do not define the special JSArray | |
1779 // subclasses, so we check to see if they are defined before | |
1780 // trying to resolve them. | |
1781 if (jsFixedArrayClass != null) { | |
1782 jsFixedArrayClass.ensureResolved(compiler); | |
1783 } | |
1784 if (jsExtendableArrayClass != null) { | |
1785 jsExtendableArrayClass.ensureResolved(compiler); | |
1786 } | |
1787 | |
1788 jsIndexableClass.ensureResolved(compiler); | |
1789 jsIndexableLength = compiler.lookupElementIn( | |
1790 jsIndexableClass, 'length'); | |
1791 if (jsIndexableLength != null && jsIndexableLength.isAbstractField) { | |
1792 AbstractFieldElement element = jsIndexableLength; | |
1793 jsIndexableLength = element.getter; | |
1794 } | |
1795 | |
1796 jsArrayClass.ensureResolved(compiler); | |
1797 jsArrayTypedConstructor = compiler.lookupElementIn(jsArrayClass, 'typed'); | |
1798 jsArrayRemoveLast = compiler.lookupElementIn(jsArrayClass, 'removeLast'); | |
1799 jsArrayAdd = compiler.lookupElementIn(jsArrayClass, 'add'); | |
1800 | |
1801 jsStringClass.ensureResolved(compiler); | |
1802 jsStringSplit = compiler.lookupElementIn(jsStringClass, 'split'); | |
1803 jsStringOperatorAdd = compiler.lookupElementIn(jsStringClass, '+'); | |
1804 jsStringToString = compiler.lookupElementIn(jsStringClass, 'toString'); | |
1805 | |
1806 objectEquals = compiler.lookupElementIn(compiler.objectClass, '=='); | |
1807 | |
1808 specialOperatorEqClasses | |
1809 ..add(compiler.objectClass) | |
1810 ..add(jsInterceptorClass) | |
1811 ..add(jsNullClass); | |
1812 | |
1813 validateInterceptorImplementsAllObjectMethods(jsInterceptorClass); | |
1814 // The null-interceptor must also implement *all* methods. | |
1815 validateInterceptorImplementsAllObjectMethods(jsNullClass); | |
1816 | |
1817 return new Future.value(); | |
1818 } | |
1819 | |
1820 void registerMirrorUsage(Set<String> symbols, | |
1821 Set<Element> targets, | |
1822 Set<Element> metaTargets) { | |
1823 if (symbols == null && targets == null && metaTargets == null) { | |
1824 // The user didn't specify anything, or there are imports of | |
1825 // 'dart:mirrors' without @MirrorsUsed. | |
1826 hasInsufficientMirrorsUsed = true; | |
1827 return; | |
1828 } | |
1829 if (symbols != null) symbolsUsed.addAll(symbols); | |
1830 if (targets != null) { | |
1831 for (Element target in targets) { | |
1832 if (target.isAbstractField) { | |
1833 AbstractFieldElement field = target; | |
1834 targetsUsed.add(field.getter); | |
1835 targetsUsed.add(field.setter); | |
1836 } else { | |
1837 targetsUsed.add(target); | |
1838 } | |
1839 } | |
1840 } | |
1841 if (metaTargets != null) metaTargetsUsed.addAll(metaTargets); | |
1842 } | |
1843 | |
1844 /** | |
1845 * Returns `true` if [element] can be accessed through reflection, that is, | |
1846 * is in the set of elements covered by a `MirrorsUsed` annotation. | |
1847 * | |
1848 * This property is used to tag emitted elements with a marker which is | |
1849 * checked by the runtime system to throw an exception if an element is | |
1850 * accessed (invoked, get, set) that is not accessible for the reflective | |
1851 * system. | |
1852 */ | |
1853 bool isAccessibleByReflection(Element element) { | |
1854 if (element.isClass) { | |
1855 element = getDartClass(element); | |
1856 } | |
1857 return membersNeededForReflection.contains(element); | |
1858 } | |
1859 | |
1860 /** | |
1861 * Returns true if the element has to be resolved due to a mirrorsUsed | |
1862 * annotation. If we have insufficient mirrors used annotations, we only | |
1863 * keep additonal elements if treeshaking has been disabled. | |
1864 */ | |
1865 bool requiredByMirrorSystem(Element element) { | |
1866 return hasInsufficientMirrorsUsed && isTreeShakingDisabled || | |
1867 matchesMirrorsMetaTarget(element) || | |
1868 targetsUsed.contains(element); | |
1869 } | |
1870 | |
1871 /** | |
1872 * Returns true if the element matches a mirrorsUsed annotation. If | |
1873 * we have insufficient mirrorsUsed information, this returns true for | |
1874 * all elements, as they might all be potentially referenced. | |
1875 */ | |
1876 bool referencedFromMirrorSystem(Element element, [recursive = true]) { | |
1877 Element enclosing = recursive ? element.enclosingElement : null; | |
1878 | |
1879 return hasInsufficientMirrorsUsed || | |
1880 matchesMirrorsMetaTarget(element) || | |
1881 targetsUsed.contains(element) || | |
1882 (enclosing != null && referencedFromMirrorSystem(enclosing)); | |
1883 } | |
1884 | |
1885 /** | |
1886 * Returns `true` if the element is needed because it has an annotation | |
1887 * of a type that is used as a meta target for reflection. | |
1888 */ | |
1889 bool matchesMirrorsMetaTarget(Element element) { | |
1890 if (metaTargetsUsed.isEmpty) return false; | |
1891 for (Link link = element.metadata; !link.isEmpty; link = link.tail) { | |
1892 MetadataAnnotation metadata = link.head; | |
1893 // TODO(kasperl): It would be nice if we didn't have to resolve | |
1894 // all metadata but only stuff that potentially would match one | |
1895 // of the used meta targets. | |
1896 metadata.ensureResolved(compiler); | |
1897 ConstantValue value = metadata.constant.value; | |
1898 if (value == null) continue; | |
1899 DartType type = value.computeType(compiler); | |
1900 if (metaTargetsUsed.contains(type.element)) return true; | |
1901 } | |
1902 return false; | |
1903 } | |
1904 | |
1905 /** | |
1906 * Visits all classes and computes whether its members are needed for | |
1907 * reflection. | |
1908 * | |
1909 * We have to precompute this set as we cannot easily answer the need for | |
1910 * reflection locally when looking at the member: We lack the information by | |
1911 * which classes a member is inherited. Called after resolution is complete. | |
1912 * | |
1913 * We filter out private libraries here, as their elements should not | |
1914 * be visible by reflection unless some other interfaces makes them | |
1915 * accessible. | |
1916 */ | |
1917 computeMembersNeededForReflection() { | |
1918 if (_membersNeededForReflection != null) return; | |
1919 if (compiler.mirrorsLibrary == null) { | |
1920 _membersNeededForReflection = const ImmutableEmptySet<Element>(); | |
1921 return; | |
1922 } | |
1923 // Compute a mapping from class to the closures it contains, so we | |
1924 // can include the correct ones when including the class. | |
1925 Map<ClassElement, List<LocalFunctionElement>> closureMap = | |
1926 new Map<ClassElement, List<LocalFunctionElement>>(); | |
1927 for (LocalFunctionElement closure in compiler.resolverWorld.allClosures) { | |
1928 closureMap.putIfAbsent(closure.enclosingClass, () => []).add(closure); | |
1929 } | |
1930 bool foundClosure = false; | |
1931 Set<Element> reflectableMembers = new Set<Element>(); | |
1932 ResolutionEnqueuer resolution = compiler.enqueuer.resolution; | |
1933 for (ClassElement cls in resolution.universe.directlyInstantiatedClasses) { | |
1934 // Do not process internal classes. | |
1935 if (cls.library.isInternalLibrary || cls.isInjected) continue; | |
1936 if (referencedFromMirrorSystem(cls)) { | |
1937 Set<Name> memberNames = new Set<Name>(); | |
1938 // 1) the class (should be resolved) | |
1939 assert(invariant(cls, cls.isResolved)); | |
1940 reflectableMembers.add(cls); | |
1941 // 2) its constructors (if resolved) | |
1942 cls.constructors.forEach((Element constructor) { | |
1943 if (resolution.hasBeenResolved(constructor)) { | |
1944 reflectableMembers.add(constructor); | |
1945 } | |
1946 }); | |
1947 // 3) all members, including fields via getter/setters (if resolved) | |
1948 cls.forEachClassMember((Member member) { | |
1949 if (resolution.hasBeenResolved(member.element)) { | |
1950 memberNames.add(member.name); | |
1951 reflectableMembers.add(member.element); | |
1952 } | |
1953 }); | |
1954 // 4) all overriding members of subclasses/subtypes (should be resolved) | |
1955 if (compiler.world.hasAnySubtype(cls)) { | |
1956 for (ClassElement subcls in compiler.world.subtypesOf(cls)) { | |
1957 subcls.forEachClassMember((Member member) { | |
1958 if (memberNames.contains(member.name)) { | |
1959 // TODO(20993): find out why this assertion fails. | |
1960 // assert(invariant(member.element, | |
1961 // resolution.hasBeenResolved(member.element))); | |
1962 if (resolution.hasBeenResolved(member.element)) { | |
1963 reflectableMembers.add(member.element); | |
1964 } | |
1965 } | |
1966 }); | |
1967 } | |
1968 } | |
1969 // 5) all its closures | |
1970 List<LocalFunctionElement> closures = closureMap[cls]; | |
1971 if (closures != null) { | |
1972 reflectableMembers.addAll(closures); | |
1973 foundClosure = true; | |
1974 } | |
1975 } else { | |
1976 // check members themselves | |
1977 cls.constructors.forEach((ConstructorElement element) { | |
1978 if (!resolution.hasBeenResolved(element)) return; | |
1979 if (referencedFromMirrorSystem(element, false)) { | |
1980 reflectableMembers.add(element); | |
1981 } | |
1982 }); | |
1983 cls.forEachClassMember((Member member) { | |
1984 if (!resolution.hasBeenResolved(member.element)) return; | |
1985 if (referencedFromMirrorSystem(member.element, false)) { | |
1986 reflectableMembers.add(member.element); | |
1987 } | |
1988 }); | |
1989 // Also add in closures. Those might be reflectable is their enclosing | |
1990 // member is. | |
1991 List<LocalFunctionElement> closures = closureMap[cls]; | |
1992 if (closures != null) { | |
1993 for (LocalFunctionElement closure in closures) { | |
1994 if (referencedFromMirrorSystem(closure.memberContext, false)) { | |
1995 reflectableMembers.add(closure); | |
1996 foundClosure = true; | |
1997 } | |
1998 } | |
1999 } | |
2000 } | |
2001 } | |
2002 // We also need top-level non-class elements like static functions and | |
2003 // global fields. We use the resolution queue to decide which elements are | |
2004 // part of the live world. | |
2005 for (LibraryElement lib in compiler.libraryLoader.libraries) { | |
2006 if (lib.isInternalLibrary) continue; | |
2007 lib.forEachLocalMember((Element member) { | |
2008 if (!member.isClass && | |
2009 resolution.hasBeenResolved(member) && | |
2010 referencedFromMirrorSystem(member)) { | |
2011 reflectableMembers.add(member); | |
2012 } | |
2013 }); | |
2014 } | |
2015 // And closures inside top-level elements that do not have a surrounding | |
2016 // class. These will be in the [:null:] bucket of the [closureMap]. | |
2017 if (closureMap.containsKey(null)) { | |
2018 for (Element closure in closureMap[null]) { | |
2019 if (referencedFromMirrorSystem(closure)) { | |
2020 reflectableMembers.add(closure); | |
2021 foundClosure = true; | |
2022 } | |
2023 } | |
2024 } | |
2025 // As we do not think about closures as classes, yet, we have to make sure | |
2026 // their superclasses are available for reflection manually. | |
2027 if (foundClosure) { | |
2028 reflectableMembers.add(closureClass); | |
2029 } | |
2030 Set<Element> closurizedMembers = compiler.resolverWorld.closurizedMembers; | |
2031 if (closurizedMembers.any(reflectableMembers.contains)) { | |
2032 reflectableMembers.add(boundClosureClass); | |
2033 } | |
2034 // Add typedefs. | |
2035 reflectableMembers | |
2036 .addAll(compiler.world.allTypedefs.where(referencedFromMirrorSystem)); | |
2037 // Register all symbols of reflectable elements | |
2038 for (Element element in reflectableMembers) { | |
2039 symbolsUsed.add(element.name); | |
2040 } | |
2041 _membersNeededForReflection = reflectableMembers; | |
2042 } | |
2043 | |
2044 // TODO(20791): compute closure classes after resolution and move this code to | |
2045 // [computeMembersNeededForReflection]. | |
2046 void maybeMarkClosureAsNeededForReflection( | |
2047 ClosureClassElement globalizedElement, | |
2048 FunctionElement callFunction, | |
2049 FunctionElement function) { | |
2050 if (!_membersNeededForReflection.contains(function)) return; | |
2051 _membersNeededForReflection.add(callFunction); | |
2052 _membersNeededForReflection.add(globalizedElement); | |
2053 } | |
2054 | |
2055 jsAst.Call generateIsJsIndexableCall(jsAst.Expression use1, | |
2056 jsAst.Expression use2) { | |
2057 String dispatchPropertyName = embeddedNames.DISPATCH_PROPERTY_NAME; | |
2058 jsAst.Expression dispatchProperty = | |
2059 emitter.generateEmbeddedGlobalAccess(dispatchPropertyName); | |
2060 | |
2061 // We pass the dispatch property record to the isJsIndexable | |
2062 // helper rather than reading it inside the helper to increase the | |
2063 // chance of making the dispatch record access monomorphic. | |
2064 jsAst.PropertyAccess record = | |
2065 new jsAst.PropertyAccess(use2, dispatchProperty); | |
2066 | |
2067 List<jsAst.Expression> arguments = <jsAst.Expression>[use1, record]; | |
2068 FunctionElement helper = findHelper('isJsIndexable'); | |
2069 jsAst.Expression helperExpression = namer.elementAccess(helper); | |
2070 return new jsAst.Call(helperExpression, arguments); | |
2071 } | |
2072 | |
2073 bool isTypedArray(TypeMask mask) { | |
2074 // Just checking for [:TypedData:] is not sufficient, as it is an | |
2075 // abstract class any user-defined class can implement. So we also | |
2076 // check for the interface [JavaScriptIndexingBehavior]. | |
2077 return | |
2078 compiler.typedDataClass != null && | |
2079 compiler.world.isInstantiated(compiler.typedDataClass) && | |
2080 mask.satisfies(compiler.typedDataClass, compiler.world) && | |
2081 mask.satisfies(jsIndexingBehaviorInterface, compiler.world); | |
2082 } | |
2083 | |
2084 bool couldBeTypedArray(TypeMask mask) { | |
2085 bool intersects(TypeMask type1, TypeMask type2) => | |
2086 !type1.intersection(type2, compiler.world).isEmpty; | |
2087 // TODO(herhut): Maybe cache the TypeMask for typedDataClass and | |
2088 // jsIndexingBehaviourInterface. | |
2089 return | |
2090 compiler.typedDataClass != null && | |
2091 compiler.world.isInstantiated(compiler.typedDataClass) && | |
2092 intersects(mask, | |
2093 new TypeMask.subtype(compiler.typedDataClass, compiler.world)) && | |
2094 intersects(mask, | |
2095 new TypeMask.subtype(jsIndexingBehaviorInterface, compiler.world)); | |
2096 } | |
2097 | |
2098 /// Returns all static fields that are referenced through [targetsUsed]. | |
2099 /// If the target is a library or class all nested static fields are | |
2100 /// included too. | |
2101 Iterable<Element> _findStaticFieldTargets() { | |
2102 List staticFields = []; | |
2103 | |
2104 void addFieldsInContainer(ScopeContainerElement container) { | |
2105 container.forEachLocalMember((Element member) { | |
2106 if (!member.isInstanceMember && member.isField) { | |
2107 staticFields.add(member); | |
2108 } else if (member.isClass) { | |
2109 addFieldsInContainer(member); | |
2110 } | |
2111 }); | |
2112 } | |
2113 | |
2114 for (Element target in targetsUsed) { | |
2115 if (target == null) continue; | |
2116 if (target.isField) { | |
2117 staticFields.add(target); | |
2118 } else if (target.isLibrary || target.isClass) { | |
2119 addFieldsInContainer(target); | |
2120 } | |
2121 } | |
2122 return staticFields; | |
2123 } | |
2124 | |
2125 /// Called when [enqueuer] is empty, but before it is closed. | |
2126 bool onQueueEmpty(Enqueuer enqueuer, Iterable<ClassElement> recentClasses) { | |
2127 // Add elements referenced only via custom elements. Return early if any | |
2128 // elements are added to avoid counting the elements as due to mirrors. | |
2129 customElementsAnalysis.onQueueEmpty(enqueuer); | |
2130 if (!enqueuer.queueIsEmpty) return false; | |
2131 | |
2132 if (!enqueuer.isResolutionQueue && preMirrorsMethodCount == 0) { | |
2133 preMirrorsMethodCount = generatedCode.length; | |
2134 } | |
2135 | |
2136 if (isTreeShakingDisabled) { | |
2137 enqueuer.enqueueReflectiveElements(recentClasses); | |
2138 } else if (!targetsUsed.isEmpty && enqueuer.isResolutionQueue) { | |
2139 // Add all static elements (not classes) that have been requested for | |
2140 // reflection. If there is no mirror-usage these are probably not | |
2141 // necessary, but the backend relies on them being resolved. | |
2142 enqueuer.enqueueReflectiveStaticFields(_findStaticFieldTargets()); | |
2143 } | |
2144 | |
2145 if (mustPreserveNames) compiler.log('Preserving names.'); | |
2146 | |
2147 if (mustRetainMetadata) { | |
2148 compiler.log('Retaining metadata.'); | |
2149 | |
2150 compiler.libraryLoader.libraries.forEach(retainMetadataOf); | |
2151 if (!enqueuer.isResolutionQueue) { | |
2152 for (Dependency dependency in metadataConstants) { | |
2153 registerCompileTimeConstant( | |
2154 dependency.constant, | |
2155 new CodegenRegistry(compiler, | |
2156 dependency.annotatedElement.analyzableElement.treeElements)); | |
2157 } | |
2158 metadataConstants.clear(); | |
2159 } | |
2160 } | |
2161 return true; | |
2162 } | |
2163 | |
2164 void onElementResolved(Element element, TreeElements elements) { | |
2165 LibraryElement library = element.library; | |
2166 if (!library.isPlatformLibrary && !library.canUseNative) return; | |
2167 bool hasNoInline = false; | |
2168 bool hasNoThrows = false; | |
2169 bool hasNoSideEffects = false; | |
2170 for (MetadataAnnotation metadata in element.metadata) { | |
2171 metadata.ensureResolved(compiler); | |
2172 if (!metadata.constant.value.isConstructedObject) continue; | |
2173 ObjectConstantValue value = metadata.constant.value; | |
2174 ClassElement cls = value.type.element; | |
2175 if (cls == noInlineClass) { | |
2176 hasNoInline = true; | |
2177 if (VERBOSE_OPTIMIZER_HINTS) { | |
2178 compiler.reportHint(element, | |
2179 MessageKind.GENERIC, | |
2180 {'text': "Cannot inline"}); | |
2181 } | |
2182 inlineCache.markAsNonInlinable(element); | |
2183 } else if (cls == noThrowsClass) { | |
2184 hasNoThrows = true; | |
2185 if (!Elements.isStaticOrTopLevelFunction(element)) { | |
2186 compiler.internalError(element, | |
2187 "@NoThrows() is currently limited to top-level" | |
2188 " or static functions"); | |
2189 } | |
2190 if (VERBOSE_OPTIMIZER_HINTS) { | |
2191 compiler.reportHint(element, | |
2192 MessageKind.GENERIC, | |
2193 {'text': "Cannot throw"}); | |
2194 } | |
2195 compiler.world.registerCannotThrow(element); | |
2196 } else if (cls == noSideEffectsClass) { | |
2197 hasNoSideEffects = true; | |
2198 if (VERBOSE_OPTIMIZER_HINTS) { | |
2199 compiler.reportHint(element, | |
2200 MessageKind.GENERIC, | |
2201 {'text': "Has no side effects"}); | |
2202 } | |
2203 compiler.world.registerSideEffectsFree(element); | |
2204 } | |
2205 } | |
2206 if (hasNoThrows && !hasNoInline) { | |
2207 compiler.internalError(element, | |
2208 "@NoThrows() should always be combined with @NoInline."); | |
2209 } | |
2210 if (hasNoSideEffects && !hasNoInline) { | |
2211 compiler.internalError(element, | |
2212 "@NoSideEffects() should always be combined with @NoInline."); | |
2213 } | |
2214 if (element == invokeOnMethod) { | |
2215 compiler.enabledInvokeOn = true; | |
2216 } | |
2217 } | |
2218 | |
2219 CodeBuffer codeOf(Element element) { | |
2220 return generatedCode.containsKey(element) | |
2221 ? jsAst.prettyPrint(generatedCode[element], compiler) | |
2222 : null; | |
2223 } | |
2224 | |
2225 FunctionElement helperForBadMain() => findHelper('badMain'); | |
2226 | |
2227 FunctionElement helperForMissingMain() => findHelper('missingMain'); | |
2228 | |
2229 FunctionElement helperForMainArity() { | |
2230 return findHelper('mainHasTooManyParameters'); | |
2231 } | |
2232 | |
2233 void forgetElement(Element element) { | |
2234 constants.forgetElement(element); | |
2235 constantCompilerTask.dartConstantCompiler.forgetElement(element); | |
2236 } | |
2237 | |
2238 void registerMainHasArguments(Enqueuer enqueuer) { | |
2239 // If the main method takes arguments, this compilation could be the target | |
2240 // of Isolate.spawnUri. Strictly speaking, that can happen also if main | |
2241 // takes no arguments, but in this case the spawned isolate can't | |
2242 // communicate with the spawning isolate. | |
2243 enqueuer.enableIsolateSupport(); | |
2244 } | |
2245 } | |
2246 | |
2247 class JavaScriptResolutionCallbacks extends ResolutionCallbacks { | |
2248 final JavaScriptBackend backend; | |
2249 | |
2250 JavaScriptResolutionCallbacks(this.backend); | |
2251 | |
2252 void registerBackendStaticInvocation(Element element, Registry registry) { | |
2253 registry.registerStaticInvocation(backend.registerBackendUse(element)); | |
2254 } | |
2255 | |
2256 void registerBackendInstantiation(ClassElement element, Registry registry) { | |
2257 backend.registerBackendUse(element); | |
2258 element.ensureResolved(backend.compiler); | |
2259 registry.registerInstantiation(element.rawType); | |
2260 } | |
2261 | |
2262 void onAssert(Send node, Registry registry) { | |
2263 registerBackendStaticInvocation(backend.assertMethod, registry); | |
2264 } | |
2265 | |
2266 void onStringInterpolation(Registry registry) { | |
2267 assert(registry.isForResolution); | |
2268 registerBackendStaticInvocation( | |
2269 backend.getStringInterpolationHelper(), registry); | |
2270 } | |
2271 | |
2272 void onCatchStatement(Registry registry) { | |
2273 assert(registry.isForResolution); | |
2274 registerBackendStaticInvocation(backend.getExceptionUnwrapper(), registry); | |
2275 registerBackendInstantiation( | |
2276 backend.jsPlainJavaScriptObjectClass, registry); | |
2277 registerBackendInstantiation( | |
2278 backend.jsUnknownJavaScriptObjectClass, registry); | |
2279 } | |
2280 | |
2281 void onThrowExpression(Registry registry) { | |
2282 assert(registry.isForResolution); | |
2283 // We don't know ahead of time whether we will need the throw in a | |
2284 // statement context or an expression context, so we register both | |
2285 // here, even though we may not need the throwExpression helper. | |
2286 registerBackendStaticInvocation(backend.getWrapExceptionHelper(), registry); | |
2287 registerBackendStaticInvocation( | |
2288 backend.getThrowExpressionHelper(), registry); | |
2289 } | |
2290 | |
2291 void onLazyField(Registry registry) { | |
2292 assert(registry.isForResolution); | |
2293 registerBackendStaticInvocation(backend.getCyclicThrowHelper(), registry); | |
2294 } | |
2295 | |
2296 void onTypeLiteral(DartType type, Registry registry) { | |
2297 assert(registry.isForResolution); | |
2298 registerBackendInstantiation(backend.typeImplementation, registry); | |
2299 registerBackendStaticInvocation(backend.getCreateRuntimeType(), registry); | |
2300 // TODO(ahe): Might want to register [element] as an instantiated class | |
2301 // when reflection is used. However, as long as we disable tree-shaking | |
2302 // eagerly it doesn't matter. | |
2303 if (type.isTypedef) { | |
2304 backend.compiler.world.allTypedefs.add(type.element); | |
2305 } | |
2306 backend.customElementsAnalysis.registerTypeLiteral(type, registry); | |
2307 } | |
2308 | |
2309 void onStackTraceInCatch(Registry registry) { | |
2310 assert(registry.isForResolution); | |
2311 registerBackendStaticInvocation(backend.getTraceFromException(), registry); | |
2312 } | |
2313 | |
2314 | |
2315 void onTypeVariableExpression(Registry registry) { | |
2316 assert(registry.isForResolution); | |
2317 registerBackendStaticInvocation(backend.getSetRuntimeTypeInfo(), registry); | |
2318 registerBackendStaticInvocation(backend.getGetRuntimeTypeInfo(), registry); | |
2319 backend.registerGetRuntimeTypeArgument(registry); | |
2320 registerBackendInstantiation(backend.compiler.listClass, registry); | |
2321 registerBackendStaticInvocation(backend.getRuntimeTypeToString(), registry); | |
2322 registerBackendStaticInvocation(backend.getCreateRuntimeType(), registry); | |
2323 } | |
2324 | |
2325 // TODO(johnniwinther): Maybe split this into [onAssertType] and [onTestType]. | |
2326 void onIsCheck(DartType type, Registry registry) { | |
2327 assert(registry.isForResolution); | |
2328 type = type.unalias(backend.compiler); | |
2329 registerBackendInstantiation(backend.compiler.boolClass, registry); | |
2330 bool inCheckedMode = backend.compiler.enableTypeAssertions; | |
2331 if (inCheckedMode) { | |
2332 registerBackendStaticInvocation(backend.getThrowRuntimeError(), registry); | |
2333 } | |
2334 if (type.isMalformed) { | |
2335 registerBackendStaticInvocation(backend.getThrowTypeError(), registry); | |
2336 } | |
2337 if (!type.treatAsRaw || type.containsTypeVariables) { | |
2338 // TODO(johnniwinther): Investigate why this is needed. | |
2339 registerBackendStaticInvocation( | |
2340 backend.getSetRuntimeTypeInfo(), registry); | |
2341 registerBackendStaticInvocation( | |
2342 backend.getGetRuntimeTypeInfo(), registry); | |
2343 backend.registerGetRuntimeTypeArgument(registry); | |
2344 if (inCheckedMode) { | |
2345 registerBackendStaticInvocation(backend.getAssertSubtype(), registry); | |
2346 } | |
2347 registerBackendStaticInvocation(backend.getCheckSubtype(), registry); | |
2348 if (type.isTypeVariable) { | |
2349 registerBackendStaticInvocation( | |
2350 backend.getCheckSubtypeOfRuntimeType(), registry); | |
2351 if (inCheckedMode) { | |
2352 registerBackendStaticInvocation( | |
2353 backend.getAssertSubtypeOfRuntimeType(), registry); | |
2354 } | |
2355 } | |
2356 registerBackendInstantiation(backend.compiler.listClass, registry); | |
2357 } | |
2358 if (type is FunctionType) { | |
2359 registerBackendStaticInvocation( | |
2360 backend.find(backend.jsHelperLibrary, 'functionTypeTestMetaHelper'), | |
2361 registry); | |
2362 } | |
2363 if (type.element != null && type.element.isNative) { | |
2364 // We will neeed to add the "$is" and "$as" properties on the | |
2365 // JavaScript object prototype, so we make sure | |
2366 // [:defineProperty:] is compiled. | |
2367 registerBackendStaticInvocation( | |
2368 backend.find(backend.jsHelperLibrary, 'defineProperty'), registry); | |
2369 } | |
2370 } | |
2371 | |
2372 void onTypeVariableBoundCheck(Registry registry) { | |
2373 assert(registry.isForResolution); | |
2374 registerBackendStaticInvocation(backend.getThrowTypeError(), registry); | |
2375 registerBackendStaticInvocation(backend.getAssertIsSubtype(), registry); | |
2376 } | |
2377 | |
2378 void onAbstractClassInstantiation(Registry registry) { | |
2379 assert(registry.isForResolution); | |
2380 registerBackendStaticInvocation( | |
2381 backend.getThrowAbstractClassInstantiationError(), registry); | |
2382 // Also register the types of the arguments passed to this method. | |
2383 registerBackendInstantiation(backend.compiler.stringClass, registry); | |
2384 } | |
2385 | |
2386 void onFallThroughError(Registry registry) { | |
2387 assert(registry.isForResolution); | |
2388 registerBackendStaticInvocation(backend.getFallThroughError(), registry); | |
2389 } | |
2390 | |
2391 void onAsCheck(DartType type, Registry registry) { | |
2392 assert(registry.isForResolution); | |
2393 registerBackendStaticInvocation(backend.getThrowRuntimeError(), registry); | |
2394 } | |
2395 | |
2396 void onThrowNoSuchMethod(Registry registry) { | |
2397 assert(registry.isForResolution); | |
2398 registerBackendStaticInvocation(backend.getThrowNoSuchMethod(), registry); | |
2399 // Also register the types of the arguments passed to this method. | |
2400 registerBackendInstantiation(backend.compiler.listClass, registry); | |
2401 registerBackendInstantiation(backend.compiler.stringClass, registry); | |
2402 } | |
2403 | |
2404 void onThrowRuntimeError(Registry registry) { | |
2405 assert(registry.isForResolution); | |
2406 registerBackendStaticInvocation(backend.getThrowRuntimeError(), registry); | |
2407 // Also register the types of the arguments passed to this method. | |
2408 registerBackendInstantiation(backend.compiler.stringClass, registry); | |
2409 } | |
2410 | |
2411 void onSuperNoSuchMethod(Registry registry) { | |
2412 assert(registry.isForResolution); | |
2413 registerBackendStaticInvocation( | |
2414 backend.getCreateInvocationMirror(), registry); | |
2415 registerBackendStaticInvocation( | |
2416 backend.compiler.objectClass.lookupLocalMember(Compiler.NO_SUCH_METHOD), | |
2417 registry); | |
2418 registerBackendInstantiation(backend.compiler.listClass, registry); | |
2419 } | |
2420 | |
2421 void onConstantMap(Registry registry) { | |
2422 assert(registry.isForResolution); | |
2423 void enqueue(String name) { | |
2424 Element e = backend.find(backend.jsHelperLibrary, name); | |
2425 registerBackendInstantiation(e, registry); | |
2426 } | |
2427 | |
2428 enqueue(JavaScriptMapConstant.DART_CLASS); | |
2429 enqueue(JavaScriptMapConstant.DART_PROTO_CLASS); | |
2430 enqueue(JavaScriptMapConstant.DART_STRING_CLASS); | |
2431 enqueue(JavaScriptMapConstant.DART_GENERAL_CLASS); | |
2432 } | |
2433 | |
2434 /// Called when resolving the `Symbol` constructor. | |
2435 void onSymbolConstructor(Registry registry) { | |
2436 assert(registry.isForResolution); | |
2437 // Make sure that _internals.Symbol.validated is registered. | |
2438 assert(backend.compiler.symbolValidatedConstructor != null); | |
2439 registerBackendStaticInvocation( | |
2440 backend.compiler.symbolValidatedConstructor, registry); | |
2441 } | |
2442 } | |
2443 | |
2444 /// Records that [constant] is used by the element behind [registry]. | |
2445 class Dependency { | |
2446 final ConstantValue constant; | |
2447 final Element annotatedElement; | |
2448 | |
2449 const Dependency(this.constant, this.annotatedElement); | |
2450 } | |
OLD | NEW |