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 |