| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of native; | |
| 6 | |
| 7 /** | |
| 8 * This could be an abstract class but we use it as a stub for the dart_backend. | |
| 9 */ | |
| 10 class NativeEnqueuer { | |
| 11 /// Initial entry point to native enqueuer. | |
| 12 void processNativeClasses(Iterable<LibraryElement> libraries) {} | |
| 13 | |
| 14 /// Notification of a main Enqueuer worklist element. For methods, adds | |
| 15 /// information from metadata attributes, and computes types instantiated due | |
| 16 /// to calling the method. | |
| 17 void registerElement(Element element) {} | |
| 18 | |
| 19 /// Notification of native field. Adds information from metadata attributes. | |
| 20 void handleFieldAnnotations(Element field) {} | |
| 21 | |
| 22 /// Computes types instantiated due to getting a native field. | |
| 23 void registerFieldLoad(Element field) {} | |
| 24 | |
| 25 /// Computes types instantiated due to setting a native field. | |
| 26 void registerFieldStore(Element field) {} | |
| 27 | |
| 28 NativeBehavior getNativeBehaviorOf(Send node) => NativeBehavior.NONE; | |
| 29 | |
| 30 /// Returns whether native classes are being used. | |
| 31 bool hasInstantiatedNativeClasses() => false; | |
| 32 | |
| 33 /** | |
| 34 * Handles JS-calls, which can be an instantiation point for types. | |
| 35 * | |
| 36 * For example, the following code instantiates and returns native classes | |
| 37 * that are `_DOMWindowImpl` or a subtype. | |
| 38 * | |
| 39 * JS('_DOMWindowImpl', 'window') | |
| 40 * | |
| 41 */ | |
| 42 // TODO(sra): The entry from codegen will not have a resolver. | |
| 43 void registerJsCall(Send node, ResolverVisitor resolver) {} | |
| 44 | |
| 45 /** | |
| 46 * Handles JS-embedded global calls, which can be an instantiation point for | |
| 47 * types. | |
| 48 * | |
| 49 * For example, the following code instantiates and returns a String class | |
| 50 * | |
| 51 * JS_EMBEDDED_GLOBAL('String', 'foo') | |
| 52 * | |
| 53 */ | |
| 54 // TODO(sra): The entry from codegen will not have a resolver. | |
| 55 void registerJsEmbeddedGlobalCall(Send node, ResolverVisitor resolver) {} | |
| 56 | |
| 57 /// Emits a summary information using the [log] function. | |
| 58 void logSummary(log(message)) {} | |
| 59 | |
| 60 // Do not use annotations in dart2dart. | |
| 61 ClassElement get annotationCreatesClass => null; | |
| 62 ClassElement get annotationReturnsClass => null; | |
| 63 ClassElement get annotationJsNameClass => null; | |
| 64 } | |
| 65 | |
| 66 | |
| 67 abstract class NativeEnqueuerBase implements NativeEnqueuer { | |
| 68 | |
| 69 /** | |
| 70 * The set of all native classes. Each native class is in [nativeClasses] and | |
| 71 * exactly one of [unusedClasses], [pendingClasses] and [registeredClasses]. | |
| 72 */ | |
| 73 final Set<ClassElement> nativeClasses = new Set<ClassElement>(); | |
| 74 | |
| 75 final Set<ClassElement> registeredClasses = new Set<ClassElement>(); | |
| 76 final Set<ClassElement> pendingClasses = new Set<ClassElement>(); | |
| 77 final Set<ClassElement> unusedClasses = new Set<ClassElement>(); | |
| 78 | |
| 79 final Set<LibraryElement> processedLibraries; | |
| 80 | |
| 81 bool hasInstantiatedNativeClasses() => !registeredClasses.isEmpty; | |
| 82 | |
| 83 final Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); | |
| 84 | |
| 85 final Map<ClassElement, Set<ClassElement>> nonNativeSubclasses = | |
| 86 new Map<ClassElement, Set<ClassElement>>(); | |
| 87 | |
| 88 /** | |
| 89 * Records matched constraints ([SpecialType] or [DartType]). Once a type | |
| 90 * constraint has been matched, there is no need to match it again. | |
| 91 */ | |
| 92 final Set matchedTypeConstraints = new Set(); | |
| 93 | |
| 94 /// Pending actions. Classes in [pendingClasses] have action thunks in | |
| 95 /// [queue] to register the class. | |
| 96 final queue = new Queue(); | |
| 97 bool flushing = false; | |
| 98 | |
| 99 /// Maps JS foreign calls to their computed native behavior. | |
| 100 final Map<Node, NativeBehavior> nativeBehaviors = | |
| 101 new Map<Node, NativeBehavior>(); | |
| 102 | |
| 103 final Enqueuer world; | |
| 104 final Compiler compiler; | |
| 105 final bool enableLiveTypeAnalysis; | |
| 106 | |
| 107 ClassElement _annotationCreatesClass; | |
| 108 ClassElement _annotationReturnsClass; | |
| 109 ClassElement _annotationJsNameClass; | |
| 110 | |
| 111 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | |
| 112 NativeEnqueuerBase(this.world, Compiler compiler, this.enableLiveTypeAnalysis) | |
| 113 : this.compiler = compiler, | |
| 114 processedLibraries = compiler.cacheStrategy.newSet(); | |
| 115 | |
| 116 JavaScriptBackend get backend => compiler.backend; | |
| 117 | |
| 118 void processNativeClasses(Iterable<LibraryElement> libraries) { | |
| 119 if (compiler.hasIncrementalSupport) { | |
| 120 // Since [Set.add] returns bool if an element was added, this restricts | |
| 121 // [libraries] to ones that haven't already been processed. This saves | |
| 122 // time during incremental compiles. | |
| 123 libraries = libraries.where(processedLibraries.add); | |
| 124 } | |
| 125 libraries.forEach(processNativeClassesInLibrary); | |
| 126 if (backend.isolateHelperLibrary != null) { | |
| 127 processNativeClassesInLibrary(backend.isolateHelperLibrary); | |
| 128 } | |
| 129 processSubclassesOfNativeClasses(libraries); | |
| 130 if (!enableLiveTypeAnalysis) { | |
| 131 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); | |
| 132 flushQueue(); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 void processNativeClassesInLibrary(LibraryElement library) { | |
| 137 // Use implementation to ensure the inclusion of injected members. | |
| 138 library.implementation.forEachLocalMember((Element element) { | |
| 139 if (element.isClass && element.isNative) { | |
| 140 processNativeClass(element); | |
| 141 } | |
| 142 }); | |
| 143 } | |
| 144 | |
| 145 void processNativeClass(ClassElement classElement) { | |
| 146 nativeClasses.add(classElement); | |
| 147 unusedClasses.add(classElement); | |
| 148 // Resolve class to ensure the class has valid inheritance info. | |
| 149 classElement.ensureResolved(compiler); | |
| 150 } | |
| 151 | |
| 152 void processSubclassesOfNativeClasses(Iterable<LibraryElement> libraries) { | |
| 153 // Collect potential subclasses, e.g. | |
| 154 // | |
| 155 // class B extends foo.A {} | |
| 156 // | |
| 157 // String "A" has a potential subclass B. | |
| 158 | |
| 159 var potentialExtends = new Map<String, Set<ClassElement>>(); | |
| 160 | |
| 161 libraries.forEach((library) { | |
| 162 library.implementation.forEachLocalMember((element) { | |
| 163 if (element.isClass) { | |
| 164 String name = element.name; | |
| 165 String extendsName = findExtendsNameOfClass(element); | |
| 166 if (extendsName != null) { | |
| 167 Set<ClassElement> potentialSubclasses = | |
| 168 potentialExtends.putIfAbsent( | |
| 169 extendsName, | |
| 170 () => new Set<ClassElement>()); | |
| 171 potentialSubclasses.add(element); | |
| 172 } | |
| 173 } | |
| 174 }); | |
| 175 }); | |
| 176 | |
| 177 // Resolve all the native classes and any classes that might extend them in | |
| 178 // [potentialExtends], and then check that the properly resolved class is in | |
| 179 // fact a subclass of a native class. | |
| 180 | |
| 181 ClassElement nativeSuperclassOf(ClassElement classElement) { | |
| 182 if (classElement.isNative) return classElement; | |
| 183 if (classElement.superclass == null) return null; | |
| 184 return nativeSuperclassOf(classElement.superclass); | |
| 185 } | |
| 186 | |
| 187 void walkPotentialSubclasses(ClassElement element) { | |
| 188 if (nativeClassesAndSubclasses.contains(element)) return; | |
| 189 element.ensureResolved(compiler); | |
| 190 ClassElement nativeSuperclass = nativeSuperclassOf(element); | |
| 191 if (nativeSuperclass != null) { | |
| 192 nativeClassesAndSubclasses.add(element); | |
| 193 if (!element.isNative) { | |
| 194 nonNativeSubclasses.putIfAbsent(nativeSuperclass, | |
| 195 () => new Set<ClassElement>()) | |
| 196 .add(element); | |
| 197 } | |
| 198 Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; | |
| 199 if (potentialSubclasses != null) { | |
| 200 potentialSubclasses.forEach(walkPotentialSubclasses); | |
| 201 } | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 nativeClasses.forEach(walkPotentialSubclasses); | |
| 206 | |
| 207 nativeClasses.addAll(nativeClassesAndSubclasses); | |
| 208 unusedClasses.addAll(nativeClassesAndSubclasses); | |
| 209 } | |
| 210 | |
| 211 /** | |
| 212 * Returns the source string of the class named in the extends clause, or | |
| 213 * `null` if there is no extends clause. | |
| 214 */ | |
| 215 String findExtendsNameOfClass(ClassElement classElement) { | |
| 216 // "class B extends A ... {}" --> "A" | |
| 217 // "class B extends foo.A ... {}" --> "A" | |
| 218 // "class B<T> extends foo.A<T,T> with M1, M2 ... {}" --> "A" | |
| 219 | |
| 220 // We want to avoid calling classElement.parseNode on every class. Doing so | |
| 221 // will slightly increase parse time and size and cause compiler errors and | |
| 222 // warnings to me emitted in more unused code. | |
| 223 | |
| 224 // An alternative to this code is to extend the API of ClassElement to | |
| 225 // expose the name of the extended element. | |
| 226 | |
| 227 // Pattern match the above cases in the token stream. | |
| 228 // [abstract] class X extends [id.]* id | |
| 229 | |
| 230 Token skipTypeParameters(Token token) { | |
| 231 BeginGroupToken beginGroupToken = token; | |
| 232 Token endToken = beginGroupToken.endGroup; | |
| 233 return endToken.next; | |
| 234 //for (;;) { | |
| 235 // token = token.next; | |
| 236 // if (token.stringValue == '>') return token.next; | |
| 237 // if (token.stringValue == '<') return skipTypeParameters(token); | |
| 238 //} | |
| 239 } | |
| 240 | |
| 241 String scanForExtendsName(Token token) { | |
| 242 if (token.stringValue == 'abstract') token = token.next; | |
| 243 if (token.stringValue != 'class') return null; | |
| 244 token = token.next; | |
| 245 if (!token.isIdentifier()) return null; | |
| 246 token = token.next; | |
| 247 // class F<X extends B<X>> extends ... | |
| 248 if (token.stringValue == '<') { | |
| 249 token = skipTypeParameters(token); | |
| 250 } | |
| 251 if (token.stringValue != 'extends') return null; | |
| 252 token = token.next; | |
| 253 Token id = token; | |
| 254 while (token.kind != EOF_TOKEN) { | |
| 255 token = token.next; | |
| 256 if (token.stringValue != '.') break; | |
| 257 token = token.next; | |
| 258 if (!token.isIdentifier()) return null; | |
| 259 id = token; | |
| 260 } | |
| 261 // Should be at '{', 'with', 'implements', '<' or 'native'. | |
| 262 return id.value; | |
| 263 } | |
| 264 | |
| 265 return compiler.withCurrentElement(classElement, () { | |
| 266 return scanForExtendsName(classElement.position); | |
| 267 }); | |
| 268 } | |
| 269 | |
| 270 ClassElement get annotationCreatesClass { | |
| 271 findAnnotationClasses(); | |
| 272 return _annotationCreatesClass; | |
| 273 } | |
| 274 | |
| 275 ClassElement get annotationReturnsClass { | |
| 276 findAnnotationClasses(); | |
| 277 return _annotationReturnsClass; | |
| 278 } | |
| 279 | |
| 280 ClassElement get annotationJsNameClass { | |
| 281 findAnnotationClasses(); | |
| 282 return _annotationJsNameClass; | |
| 283 } | |
| 284 | |
| 285 void findAnnotationClasses() { | |
| 286 if (_annotationCreatesClass != null) return; | |
| 287 ClassElement find(name) { | |
| 288 Element e = backend.findHelper(name); | |
| 289 if (e == null || e is! ClassElement) { | |
| 290 compiler.internalError(NO_LOCATION_SPANNABLE, | |
| 291 "Could not find implementation class '${name}'."); | |
| 292 } | |
| 293 return e; | |
| 294 } | |
| 295 _annotationCreatesClass = find('Creates'); | |
| 296 _annotationReturnsClass = find('Returns'); | |
| 297 _annotationJsNameClass = find('JSName'); | |
| 298 } | |
| 299 | |
| 300 /// Returns the JSName annotation string or `null` if no JSName annotation is | |
| 301 /// present. | |
| 302 String findJsNameFromAnnotation(Element element) { | |
| 303 String name = null; | |
| 304 ClassElement annotationClass = annotationJsNameClass; | |
| 305 for (Link<MetadataAnnotation> link = element.metadata; | |
| 306 !link.isEmpty; | |
| 307 link = link.tail) { | |
| 308 MetadataAnnotation annotation = link.head.ensureResolved(compiler); | |
| 309 ConstantValue value = annotation.constant.value; | |
| 310 if (!value.isConstructedObject) continue; | |
| 311 ConstructedConstantValue constructedObject = value; | |
| 312 if (constructedObject.type.element != annotationClass) continue; | |
| 313 | |
| 314 List<ConstantValue> fields = constructedObject.fields; | |
| 315 // TODO(sra): Better validation of the constant. | |
| 316 if (fields.length != 1 || fields[0] is! StringConstantValue) { | |
| 317 PartialMetadataAnnotation partial = annotation; | |
| 318 compiler.internalError(annotation, | |
| 319 'Annotations needs one string: ${partial.parseNode(compiler)}'); | |
| 320 } | |
| 321 StringConstantValue specStringConstant = fields[0]; | |
| 322 String specString = specStringConstant.toDartString().slowToString(); | |
| 323 if (name == null) { | |
| 324 name = specString; | |
| 325 } else { | |
| 326 PartialMetadataAnnotation partial = annotation; | |
| 327 compiler.internalError(annotation, | |
| 328 'Too many JSName annotations: ${partial.parseNode(compiler)}'); | |
| 329 } | |
| 330 } | |
| 331 return name; | |
| 332 } | |
| 333 | |
| 334 enqueueClass(ClassElement classElement, cause) { | |
| 335 assert(unusedClasses.contains(classElement)); | |
| 336 unusedClasses.remove(classElement); | |
| 337 pendingClasses.add(classElement); | |
| 338 queue.add(() { processClass(classElement, cause); }); | |
| 339 } | |
| 340 | |
| 341 void flushQueue() { | |
| 342 if (flushing) return; | |
| 343 flushing = true; | |
| 344 while (!queue.isEmpty) { | |
| 345 (queue.removeFirst())(); | |
| 346 } | |
| 347 flushing = false; | |
| 348 } | |
| 349 | |
| 350 processClass(ClassElementX classElement, cause) { | |
| 351 // TODO(ahe): Fix this assertion to work in incremental compilation. | |
| 352 assert(compiler.hasIncrementalSupport || | |
| 353 !registeredClasses.contains(classElement)); | |
| 354 | |
| 355 bool firstTime = registeredClasses.isEmpty; | |
| 356 pendingClasses.remove(classElement); | |
| 357 registeredClasses.add(classElement); | |
| 358 | |
| 359 // TODO(ahe): Is this really a global dependency? | |
| 360 world.registerInstantiatedClass(classElement, compiler.globalDependencies); | |
| 361 | |
| 362 // Also parse the node to know all its methods because otherwise it will | |
| 363 // only be parsed if there is a call to one of its constructors. | |
| 364 classElement.parseNode(compiler); | |
| 365 | |
| 366 if (firstTime) { | |
| 367 queue.add(onFirstNativeClass); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 registerElement(Element element) { | |
| 372 compiler.withCurrentElement(element, () { | |
| 373 if (element.isFunction || element.isGetter || element.isSetter) { | |
| 374 handleMethodAnnotations(element); | |
| 375 if (element.isNative) { | |
| 376 registerMethodUsed(element); | |
| 377 } | |
| 378 } else if (element.isField) { | |
| 379 handleFieldAnnotations(element); | |
| 380 if (element.isNative) { | |
| 381 registerFieldLoad(element); | |
| 382 registerFieldStore(element); | |
| 383 } | |
| 384 } | |
| 385 }); | |
| 386 } | |
| 387 | |
| 388 handleFieldAnnotations(Element element) { | |
| 389 if (element.enclosingElement.isNative) { | |
| 390 // Exclude non-instance (static) fields - they not really native and are | |
| 391 // compiled as isolate globals. Access of a property of a constructor | |
| 392 // function or a non-method property in the prototype chain, must be coded | |
| 393 // using a JS-call. | |
| 394 if (element.isInstanceMember) { | |
| 395 setNativeName(element); | |
| 396 } | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 handleMethodAnnotations(Element method) { | |
| 401 if (isNativeMethod(method)) { | |
| 402 setNativeName(method); | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 /// Sets the native name of [element], either from an annotation, or | |
| 407 /// defaulting to the Dart name. | |
| 408 void setNativeName(Element element) { | |
| 409 String name = findJsNameFromAnnotation(element); | |
| 410 if (name == null) name = element.name; | |
| 411 element.setNative(name); | |
| 412 } | |
| 413 | |
| 414 bool isNativeMethod(FunctionElementX element) { | |
| 415 if (!element.library.canUseNative) return false; | |
| 416 // Native method? | |
| 417 return compiler.withCurrentElement(element, () { | |
| 418 Node node = element.parseNode(compiler); | |
| 419 if (node is! FunctionExpression) return false; | |
| 420 FunctionExpression functionExpression = node; | |
| 421 node = functionExpression.body; | |
| 422 Token token = node.getBeginToken(); | |
| 423 if (identical(token.stringValue, 'native')) return true; | |
| 424 return false; | |
| 425 }); | |
| 426 } | |
| 427 | |
| 428 void registerMethodUsed(Element method) { | |
| 429 processNativeBehavior( | |
| 430 NativeBehavior.ofMethod(method, compiler), | |
| 431 method); | |
| 432 flushQueue(); | |
| 433 } | |
| 434 | |
| 435 void registerFieldLoad(Element field) { | |
| 436 processNativeBehavior( | |
| 437 NativeBehavior.ofFieldLoad(field, compiler), | |
| 438 field); | |
| 439 flushQueue(); | |
| 440 } | |
| 441 | |
| 442 void registerFieldStore(Element field) { | |
| 443 processNativeBehavior( | |
| 444 NativeBehavior.ofFieldStore(field, compiler), | |
| 445 field); | |
| 446 flushQueue(); | |
| 447 } | |
| 448 | |
| 449 void registerJsCall(Send node, ResolverVisitor resolver) { | |
| 450 NativeBehavior behavior = NativeBehavior.ofJsCall(node, compiler, resolver); | |
| 451 processNativeBehavior(behavior, node); | |
| 452 nativeBehaviors[node] = behavior; | |
| 453 flushQueue(); | |
| 454 } | |
| 455 | |
| 456 void registerJsEmbeddedGlobalCall(Send node, ResolverVisitor resolver) { | |
| 457 NativeBehavior behavior = | |
| 458 NativeBehavior.ofJsEmbeddedGlobalCall(node, compiler, resolver); | |
| 459 processNativeBehavior(behavior, node); | |
| 460 nativeBehaviors[node] = behavior; | |
| 461 flushQueue(); | |
| 462 } | |
| 463 | |
| 464 NativeBehavior getNativeBehaviorOf(Send node) => nativeBehaviors[node]; | |
| 465 | |
| 466 processNativeBehavior(NativeBehavior behavior, cause) { | |
| 467 // TODO(ahe): Is this really a global dependency? | |
| 468 Registry registry = compiler.globalDependencies; | |
| 469 bool allUsedBefore = unusedClasses.isEmpty; | |
| 470 for (var type in behavior.typesInstantiated) { | |
| 471 if (matchedTypeConstraints.contains(type)) continue; | |
| 472 matchedTypeConstraints.add(type); | |
| 473 if (type is SpecialType) { | |
| 474 if (type == SpecialType.JsObject) { | |
| 475 world.registerInstantiatedClass(compiler.objectClass, registry); | |
| 476 } | |
| 477 continue; | |
| 478 } | |
| 479 if (type is InterfaceType) { | |
| 480 if (type.element == compiler.intClass) { | |
| 481 world.registerInstantiatedClass(compiler.intClass, registry); | |
| 482 } else if (type.element == compiler.doubleClass) { | |
| 483 world.registerInstantiatedClass(compiler.doubleClass, registry); | |
| 484 } else if (type.element == compiler.numClass) { | |
| 485 world.registerInstantiatedClass(compiler.doubleClass, registry); | |
| 486 world.registerInstantiatedClass(compiler.intClass, registry); | |
| 487 } else if (type.element == compiler.stringClass) { | |
| 488 world.registerInstantiatedClass(compiler.stringClass, registry); | |
| 489 } else if (type.element == compiler.nullClass) { | |
| 490 world.registerInstantiatedClass(compiler.nullClass, registry); | |
| 491 } else if (type.element == compiler.boolClass) { | |
| 492 world.registerInstantiatedClass(compiler.boolClass, registry); | |
| 493 } else if (compiler.types.isSubtype( | |
| 494 type, backend.listImplementation.rawType)) { | |
| 495 world.registerInstantiatedClass(type.element, registry); | |
| 496 } | |
| 497 } | |
| 498 assert(type is DartType); | |
| 499 enqueueUnusedClassesMatching( | |
| 500 (nativeClass) => compiler.types.isSubtype(nativeClass.thisType, type), | |
| 501 cause, | |
| 502 'subtypeof($type)'); | |
| 503 } | |
| 504 | |
| 505 // Give an info so that library developers can compile with -v to find why | |
| 506 // all the native classes are included. | |
| 507 if (unusedClasses.isEmpty && !allUsedBefore) { | |
| 508 compiler.log('All native types marked as used due to $cause.'); | |
| 509 } | |
| 510 } | |
| 511 | |
| 512 enqueueUnusedClassesMatching(bool predicate(classElement), | |
| 513 cause, | |
| 514 [String reason]) { | |
| 515 Iterable matches = unusedClasses.where(predicate); | |
| 516 matches.toList().forEach((c) => enqueueClass(c, cause)); | |
| 517 } | |
| 518 | |
| 519 onFirstNativeClass() { | |
| 520 staticUse(name) { | |
| 521 backend.enqueue( | |
| 522 world, backend.findHelper(name), compiler.globalDependencies); | |
| 523 } | |
| 524 | |
| 525 staticUse('defineProperty'); | |
| 526 staticUse('toStringForNativeObject'); | |
| 527 staticUse('hashCodeForNativeObject'); | |
| 528 staticUse('convertDartClosureToJS'); | |
| 529 addNativeExceptions(); | |
| 530 } | |
| 531 | |
| 532 addNativeExceptions() { | |
| 533 enqueueUnusedClassesMatching((classElement) { | |
| 534 // TODO(sra): Annotate exception classes in dart:html. | |
| 535 String name = classElement.name; | |
| 536 if (name.contains('Exception')) return true; | |
| 537 if (name.contains('Error')) return true; | |
| 538 return false; | |
| 539 }, | |
| 540 'native exception'); | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 | |
| 545 class NativeResolutionEnqueuer extends NativeEnqueuerBase { | |
| 546 | |
| 547 Map<String, ClassElement> tagOwner = new Map<String, ClassElement>(); | |
| 548 | |
| 549 NativeResolutionEnqueuer(Enqueuer world, Compiler compiler) | |
| 550 : super(world, compiler, compiler.enableNativeLiveTypeAnalysis); | |
| 551 | |
| 552 void processNativeClass(ClassElement classElement) { | |
| 553 super.processNativeClass(classElement); | |
| 554 | |
| 555 // Since we map from dispatch tags to classes, a dispatch tag must be used | |
| 556 // on only one native class. | |
| 557 for (String tag in nativeTagsOfClass(classElement)) { | |
| 558 ClassElement owner = tagOwner[tag]; | |
| 559 if (owner != null) { | |
| 560 if (owner != classElement) { | |
| 561 compiler.internalError( | |
| 562 classElement, "Tag '$tag' already in use by '${owner.name}'"); | |
| 563 } | |
| 564 } else { | |
| 565 tagOwner[tag] = classElement; | |
| 566 } | |
| 567 } | |
| 568 } | |
| 569 | |
| 570 void logSummary(log(message)) { | |
| 571 log('Resolved ${registeredClasses.length} native elements used, ' | |
| 572 '${unusedClasses.length} native elements dead.'); | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 | |
| 577 class NativeCodegenEnqueuer extends NativeEnqueuerBase { | |
| 578 | |
| 579 final CodeEmitterTask emitter; | |
| 580 | |
| 581 final Set<ClassElement> doneAddSubtypes = new Set<ClassElement>(); | |
| 582 | |
| 583 NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter) | |
| 584 : super(world, compiler, compiler.enableNativeLiveTypeAnalysis); | |
| 585 | |
| 586 void processNativeClasses(Iterable<LibraryElement> libraries) { | |
| 587 super.processNativeClasses(libraries); | |
| 588 | |
| 589 // HACK HACK - add all the resolved classes. | |
| 590 NativeEnqueuerBase enqueuer = compiler.enqueuer.resolution.nativeEnqueuer; | |
| 591 for (final classElement in enqueuer.registeredClasses) { | |
| 592 if (unusedClasses.contains(classElement)) { | |
| 593 enqueueClass(classElement, 'was resolved'); | |
| 594 } | |
| 595 } | |
| 596 flushQueue(); | |
| 597 } | |
| 598 | |
| 599 processClass(ClassElement classElement, cause) { | |
| 600 super.processClass(classElement, cause); | |
| 601 // Add the information that this class is a subtype of its supertypes. The | |
| 602 // code emitter and the ssa builder use that information. | |
| 603 addSubtypes(classElement, emitter.nativeEmitter); | |
| 604 } | |
| 605 | |
| 606 void addSubtypes(ClassElement cls, NativeEmitter emitter) { | |
| 607 if (!cls.isNative) return; | |
| 608 if (doneAddSubtypes.contains(cls)) return; | |
| 609 doneAddSubtypes.add(cls); | |
| 610 | |
| 611 // Walk the superclass chain since classes on the superclass chain might not | |
| 612 // be instantiated (abstract or simply unused). | |
| 613 addSubtypes(cls.superclass, emitter); | |
| 614 | |
| 615 for (DartType type in cls.allSupertypes) { | |
| 616 List<Element> subtypes = emitter.subtypes.putIfAbsent( | |
| 617 type.element, | |
| 618 () => <ClassElement>[]); | |
| 619 subtypes.add(cls); | |
| 620 } | |
| 621 | |
| 622 // Skip through all the mixin applications in the super class | |
| 623 // chain. That way, the direct subtypes set only contain the | |
| 624 // natives classes. | |
| 625 ClassElement superclass = cls.superclass; | |
| 626 while (superclass != null && superclass.isMixinApplication) { | |
| 627 assert(!superclass.isNative); | |
| 628 superclass = superclass.superclass; | |
| 629 } | |
| 630 | |
| 631 List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( | |
| 632 superclass, | |
| 633 () => <ClassElement>[]); | |
| 634 directSubtypes.add(cls); | |
| 635 } | |
| 636 | |
| 637 void logSummary(log(message)) { | |
| 638 log('Compiled ${registeredClasses.length} native classes, ' | |
| 639 '${unusedClasses.length} native classes omitted.'); | |
| 640 } | |
| 641 } | |
| OLD | NEW |