Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(251)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/native/enqueue.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698