| 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 |