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

Side by Side Diff: pkg/dart2js_incremental/lib/library_updater.dart

Issue 2647343003: Update dart2js unit tests to dart_parser and dart_scanner. (Closed)
Patch Set: Address review comments. Created 3 years, 11 months 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
« no previous file with comments | « pkg/dart2js_incremental/lib/diff.dart ('k') | pkg/dart2js_incremental/lib/server.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 library dart2js_incremental.library_updater;
6
7 import 'dart:async' show
8 Future;
9
10 import 'package:compiler/compiler.dart' as api;
11
12 import 'package:compiler/src/compiler.dart' show
13 Compiler;
14
15 import 'package:compiler/src/diagnostics/messages.dart' show
16 MessageKind;
17
18 import 'package:compiler/src/elements/elements.dart' show
19 ClassElement,
20 CompilationUnitElement,
21 Element,
22 FunctionElement,
23 LibraryElement,
24 STATE_NOT_STARTED,
25 ScopeContainerElement;
26
27 import 'package:compiler/src/enqueue.dart' show
28 EnqueueTask;
29
30 import 'package:compiler/src/parser/listener.dart' show
31 Listener;
32
33 import 'package:compiler/src/parser/node_listener.dart' show
34 NodeListener;
35
36 import 'package:compiler/src/parser/partial_elements.dart' show
37 PartialClassElement,
38 PartialElement,
39 PartialFieldList,
40 PartialFunctionElement;
41
42 import 'package:compiler/src/parser/parser.dart' show
43 Parser;
44
45 import 'package:compiler/src/scanner/scanner.dart' show
46 Scanner;
47
48 import 'package:compiler/src/tokens/token.dart' show
49 Token;
50
51 import 'package:compiler/src/tokens/token_constants.dart' show
52 EOF_TOKEN;
53
54 import 'package:compiler/src/script.dart' show
55 Script;
56
57 import 'package:compiler/src/io/source_file.dart' show
58 CachingUtf8BytesSourceFile,
59 SourceFile,
60 StringSourceFile;
61
62 import 'package:compiler/src/tree/tree.dart' show
63 ClassNode,
64 FunctionExpression,
65 LibraryTag,
66 NodeList,
67 Part,
68 StringNode,
69 unparse;
70
71 import 'package:compiler/src/js/js.dart' show
72 js;
73
74 import 'package:compiler/src/js/js.dart' as jsAst;
75
76 import 'package:compiler/src/js_emitter/js_emitter.dart' show
77 CodeEmitterTask,
78 computeMixinClass;
79
80 import 'package:compiler/src/js_emitter/full_emitter/emitter.dart'
81 as full show Emitter;
82
83 import 'package:compiler/src/js_emitter/model.dart' show
84 Class,
85 Method;
86
87 import 'package:compiler/src/js_emitter/program_builder/program_builder.dart'
88 show ProgramBuilder;
89
90 import 'package:js_runtime/shared/embedded_names.dart'
91 as embeddedNames;
92
93 import 'package:compiler/src/js_backend/js_backend.dart' show
94 JavaScriptBackend,
95 Namer;
96
97 import 'package:compiler/src/util/util.dart' show
98 Link,
99 LinkBuilder;
100
101 import 'package:compiler/src/elements/modelx.dart' show
102 ClassElementX,
103 CompilationUnitElementX,
104 DeclarationSite,
105 ElementX,
106 FieldElementX,
107 LibraryElementX;
108
109 import 'package:compiler/src/universe/selector.dart' show
110 Selector;
111
112 import 'package:compiler/src/constants/values.dart' show
113 ConstantValue;
114
115 import 'package:compiler/src/library_loader.dart' show
116 TagState;
117
118 import 'diff.dart' show
119 Difference,
120 computeDifference;
121
122 import 'dart2js_incremental.dart' show
123 IncrementalCompilationFailed,
124 IncrementalCompiler;
125
126 typedef void Logger(message);
127
128 typedef bool Reuser(
129 Token diffToken,
130 PartialElement before,
131 PartialElement after);
132
133 class FailedUpdate {
134 /// Either an [Element] or a [Difference].
135 final context;
136 final String message;
137
138 FailedUpdate(this.context, this.message);
139
140 String toString() {
141 if (context == null) return '$message';
142 return 'In $context:\n $message';
143 }
144 }
145
146 abstract class _IncrementalCompilerContext {
147 IncrementalCompiler incrementalCompiler;
148
149 Set<ClassElementX> _emittedClasses;
150
151 Set<ClassElementX> _directlyInstantiatedClasses;
152
153 Set<ConstantValue> _compiledConstants;
154 }
155
156 class IncrementalCompilerContext extends _IncrementalCompilerContext {
157 final Set<Uri> _uriWithUpdates = new Set<Uri>();
158
159 void set incrementalCompiler(IncrementalCompiler value) {
160 if (super.incrementalCompiler != null) {
161 throw new StateError("Can't set [incrementalCompiler] more than once.");
162 }
163 super.incrementalCompiler = value;
164 }
165
166 void registerUriWithUpdates(Iterable<Uri> uris) {
167 _uriWithUpdates.addAll(uris);
168 }
169
170 void _captureState(Compiler compiler) {
171 JavaScriptBackend backend = compiler.backend;
172 Set neededClasses = backend.emitter.neededClasses;
173 if (neededClasses == null) {
174 neededClasses = new Set();
175 }
176 _emittedClasses = new Set.from(neededClasses);
177
178 _directlyInstantiatedClasses =
179 new Set.from(compiler.codegenWorld.directlyInstantiatedClasses);
180
181 // This breaks constant tracking of the incremental compiler. It would need
182 // to capture the emitted constants.
183 List<ConstantValue> constants = null;
184 if (constants == null) constants = <ConstantValue>[];
185 _compiledConstants = new Set<ConstantValue>.identity()..addAll(constants);
186 }
187
188 bool _uriHasUpdate(Uri uri) => _uriWithUpdates.contains(uri);
189 }
190
191 class LibraryUpdater extends JsFeatures {
192 final Compiler compiler;
193
194 final api.CompilerInputProvider inputProvider;
195
196 final Logger logTime;
197
198 final Logger logVerbose;
199
200 final List<Update> updates = <Update>[];
201
202 final List<FailedUpdate> _failedUpdates = <FailedUpdate>[];
203
204 final Set<ElementX> _elementsToInvalidate = new Set<ElementX>();
205
206 final Set<ElementX> _removedElements = new Set<ElementX>();
207
208 final Set<ClassElementX> _classesWithSchemaChanges =
209 new Set<ClassElementX>();
210
211 final IncrementalCompilerContext _context;
212
213 final Map<Uri, Future> _sources = <Uri, Future>{};
214
215 /// Cached tokens of entry compilation units.
216 final Map<LibraryElementX, Token> _entryUnitTokens =
217 <LibraryElementX, Token>{};
218
219 /// Cached source files for entry compilation units.
220 final Map<LibraryElementX, SourceFile> _entrySourceFiles =
221 <LibraryElementX, SourceFile>{};
222
223 bool _hasComputedNeeds = false;
224
225 bool _hasCapturedCompilerState = false;
226
227 LibraryUpdater(
228 this.compiler,
229 this.inputProvider,
230 this.logTime,
231 this.logVerbose,
232 this._context) {
233 // TODO(ahe): Would like to remove this from the constructor. However, the
234 // state must be captured before calling [reuseCompiler].
235 // Proper solution might be: [reuseCompiler] should not clear the sets that
236 // are captured in [IncrementalCompilerContext._captureState].
237 _ensureCompilerStateCaptured();
238 }
239
240 /// Returns the classes emitted by [compiler].
241 Set<ClassElementX> get _emittedClasses => _context._emittedClasses;
242
243 /// Returns the directly instantantiated classes seen by [compiler] (this
244 /// includes interfaces and may be different from [_emittedClasses] that only
245 /// includes interfaces used in type tests).
246 Set<ClassElementX> get _directlyInstantiatedClasses {
247 return _context._directlyInstantiatedClasses;
248 }
249
250 /// Returns the constants emitted by [compiler].
251 Set<ConstantValue> get _compiledConstants => _context._compiledConstants;
252
253 /// When [true], updates must be applied (using [applyUpdates]) before the
254 /// [compiler]'s state correctly reflects the updated program.
255 bool get hasPendingUpdates => !updates.isEmpty;
256
257 bool get failed => !_failedUpdates.isEmpty;
258
259 /// Used as tear-off passed to [LibraryLoaderTask.resetAsync].
260 Future<bool> reuseLibrary(LibraryElement library) {
261 _ensureCompilerStateCaptured();
262 assert(compiler != null);
263 if (library.isPlatformLibrary) {
264 logTime('Reusing $library (assumed read-only).');
265 return new Future.value(true);
266 }
267 return _haveTagsChanged(library).then((bool haveTagsChanged) {
268 if (haveTagsChanged) {
269 cannotReuse(
270 library,
271 "Changes to library, import, export, or part declarations not"
272 " supported.");
273 return true;
274 }
275
276 bool isChanged = false;
277 List<Future<Script>> futureScripts = <Future<Script>>[];
278
279 for (CompilationUnitElementX unit in library.compilationUnits) {
280 Uri uri = unit.script.resourceUri;
281 if (_context._uriHasUpdate(uri)) {
282 isChanged = true;
283 futureScripts.add(_updatedScript(unit.script, library));
284 } else {
285 futureScripts.add(new Future.value(unit.script));
286 }
287 }
288
289 if (!isChanged) {
290 logTime("Reusing $library, source didn't change.");
291 return true;
292 }
293
294 return Future.wait(futureScripts).then(
295 (List<Script> scripts) => canReuseLibrary(library, scripts));
296 }).whenComplete(() => _cleanUp(library));
297 }
298
299 void _cleanUp(LibraryElementX library) {
300 _entryUnitTokens.remove(library);
301 _entrySourceFiles.remove(library);
302 }
303
304 Future<Script> _updatedScript(Script before, LibraryElementX library) {
305 if (before == library.entryCompilationUnit.script &&
306 _entrySourceFiles.containsKey(library)) {
307 return new Future.value(before.copyWithFile(_entrySourceFiles[library]));
308 }
309
310 return _readUri(before.resourceUri).then((bytes) {
311 Uri uri = before.file.uri;
312 String filename = before.file.filename;
313 SourceFile sourceFile = bytes is String
314 ? new StringSourceFile(uri, filename, bytes)
315 : new CachingUtf8BytesSourceFile(uri, filename, bytes);
316 return before.copyWithFile(sourceFile);
317 });
318 }
319
320 Future<bool> _haveTagsChanged(LibraryElement library) {
321 Script before = library.entryCompilationUnit.script;
322 if (!_context._uriHasUpdate(before.resourceUri)) {
323 // The entry compilation unit hasn't been updated. So the tags aren't
324 // changed.
325 return new Future<bool>.value(false);
326 }
327
328 return _updatedScript(before, library).then((Script script) {
329 _entrySourceFiles[library] = script.file;
330 Token token = new Scanner(_entrySourceFiles[library]).tokenize();
331 _entryUnitTokens[library] = token;
332 // Using two parsers to only create the nodes we want ([LibraryTag]).
333 Parser parser = new Parser(new Listener(), compiler.options);
334 NodeListener listener = new NodeListener(
335 compiler, library.entryCompilationUnit);
336 Parser nodeParser = new Parser(listener, compiler.options);
337 Iterator<LibraryTag> tags = library.tags.iterator;
338 while (token.kind != EOF_TOKEN) {
339 token = parser.parseMetadataStar(token);
340 if (parser.optional('library', token) ||
341 parser.optional('import', token) ||
342 parser.optional('export', token) ||
343 parser.optional('part', token)) {
344 if (!tags.moveNext()) return true;
345 token = nodeParser.parseTopLevelDeclaration(token);
346 LibraryTag tag = listener.popNode();
347 assert(listener.nodes.isEmpty);
348 if (unparse(tags.current) != unparse(tag)) {
349 return true;
350 }
351 } else {
352 break;
353 }
354 }
355 return tags.moveNext();
356 });
357 }
358
359 Future _readUri(Uri uri) {
360 return _sources.putIfAbsent(uri, () => inputProvider(uri));
361 }
362
363 void _ensureCompilerStateCaptured() {
364 // TODO(ahe): [compiler] shouldn't be null, remove the following line.
365 if (compiler == null) return;
366
367 if (_hasCapturedCompilerState) return;
368 _context._captureState(compiler);
369 _hasCapturedCompilerState = true;
370 }
371
372 /// Returns true if [library] can be reused.
373 ///
374 /// This methods also computes the [updates] (patches) needed to have
375 /// [library] reflect the modifications in [scripts].
376 bool canReuseLibrary(LibraryElement library, List<Script> scripts) {
377 logTime('Attempting to reuse ${library}.');
378
379 Uri entryUri = library.entryCompilationUnit.script.resourceUri;
380 Script entryScript =
381 scripts.singleWhere((Script script) => script.resourceUri == entryUri);
382 LibraryElement newLibrary =
383 new LibraryElementX(entryScript, library.canonicalUri);
384 if (_entryUnitTokens.containsKey(library)) {
385 compiler.dietParser.dietParse(
386 newLibrary.entryCompilationUnit, _entryUnitTokens[library]);
387 } else {
388 compiler.scanner.scanLibrary(newLibrary);
389 }
390
391 TagState tagState = new TagState();
392 for (LibraryTag tag in newLibrary.tags) {
393 if (tag.isImport) {
394 tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler);
395 } else if (tag.isExport) {
396 tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler);
397 } else if (tag.isLibraryName) {
398 tagState.checkTag(TagState.LIBRARY, tag, compiler);
399 if (newLibrary.libraryTag == null) {
400 // Use the first if there are multiple (which is reported as an
401 // error in [TagState.checkTag]).
402 newLibrary.libraryTag = tag;
403 }
404 } else if (tag.isPart) {
405 tagState.checkTag(TagState.PART, tag, compiler);
406 }
407 }
408
409 // TODO(ahe): Process tags using TagState, not
410 // LibraryLoaderTask.processLibraryTags.
411 Link<CompilationUnitElement> units = library.compilationUnits;
412 for (Script script in scripts) {
413 CompilationUnitElementX unit = units.head;
414 units = units.tail;
415 if (script != entryScript) {
416 // TODO(ahe): Copied from library_loader.
417 CompilationUnitElement newUnit =
418 new CompilationUnitElementX(script, newLibrary);
419 compiler.withCurrentElement(newUnit, () {
420 compiler.scanner.scan(newUnit);
421 if (unit.partTag == null) {
422 compiler.reportError(unit, MessageKind.MISSING_PART_OF_TAG);
423 }
424 });
425 }
426 }
427
428 logTime('New library synthesized.');
429 return canReuseScopeContainerElement(library, newLibrary);
430 }
431
432 bool cannotReuse(context, String message) {
433 _failedUpdates.add(new FailedUpdate(context, message));
434 logVerbose(message);
435 return false;
436 }
437
438 bool canReuseScopeContainerElement(
439 ScopeContainerElement element,
440 ScopeContainerElement newElement) {
441 List<Difference> differences = computeDifference(element, newElement);
442 logTime('Differences computed.');
443 for (Difference difference in differences) {
444 logTime('Looking at difference: $difference');
445
446 if (difference.before == null && difference.after is PartialElement) {
447 canReuseAddedElement(difference.after, element, newElement);
448 continue;
449 }
450 if (difference.after == null && difference.before is PartialElement) {
451 canReuseRemovedElement(difference.before, element);
452 continue;
453 }
454 Token diffToken = difference.token;
455 if (diffToken == null) {
456 cannotReuse(difference, "No difference token.");
457 continue;
458 }
459 if (difference.after is! PartialElement &&
460 difference.before is! PartialElement) {
461 cannotReuse(difference, "Don't know how to recompile.");
462 continue;
463 }
464 PartialElement before = difference.before;
465 PartialElement after = difference.after;
466
467 Reuser reuser;
468
469 if (before is PartialFunctionElement && after is PartialFunctionElement) {
470 reuser = canReuseFunction;
471 } else if (before is PartialClassElement &&
472 after is PartialClassElement) {
473 reuser = canReuseClass;
474 } else {
475 reuser = unableToReuse;
476 }
477 if (!reuser(diffToken, before, after)) {
478 assert(!_failedUpdates.isEmpty);
479 continue;
480 }
481 }
482
483 return _failedUpdates.isEmpty;
484 }
485
486 bool canReuseAddedElement(
487 PartialElement element,
488 ScopeContainerElement container,
489 ScopeContainerElement syntheticContainer) {
490 if (element is PartialFunctionElement) {
491 addFunction(element, container);
492 return true;
493 } else if (element is PartialClassElement) {
494 addClass(element, container);
495 return true;
496 } else if (element is PartialFieldList) {
497 addFields(element, container, syntheticContainer);
498 return true;
499 }
500 return cannotReuse(element, "Adding ${element.runtimeType} not supported.");
501 }
502
503 void addFunction(
504 PartialFunctionElement element,
505 /* ScopeContainerElement */ container) {
506 invalidateScopesAffectedBy(element, container);
507
508 updates.add(new AddedFunctionUpdate(compiler, element, container));
509 }
510
511 void addClass(
512 PartialClassElement element,
513 LibraryElementX library) {
514 invalidateScopesAffectedBy(element, library);
515
516 updates.add(new AddedClassUpdate(compiler, element, library));
517 }
518
519 /// Called when a field in [definition] has changed.
520 ///
521 /// There's no direct link from a [PartialFieldList] to its implied
522 /// [FieldElementX], so instead we use [syntheticContainer], the (synthetic)
523 /// container created by [canReuseLibrary], or [canReuseClass] (through
524 /// [PartialClassElement.parseNode]). This container is scanned looking for
525 /// fields whose declaration site is [definition].
526 // TODO(ahe): It would be nice if [computeDifference] returned this
527 // information directly.
528 void addFields(
529 PartialFieldList definition,
530 ScopeContainerElement container,
531 ScopeContainerElement syntheticContainer) {
532 List<FieldElementX> fields = <FieldElementX>[];
533 syntheticContainer.forEachLocalMember((ElementX member) {
534 if (member.declarationSite == definition) {
535 fields.add(member);
536 }
537 });
538 for (FieldElementX field in fields) {
539 // TODO(ahe): This only works when there's one field per
540 // PartialFieldList.
541 addField(field, container);
542 }
543 }
544
545 void addField(FieldElementX element, ScopeContainerElement container) {
546 invalidateScopesAffectedBy(element, container);
547 if (element.isInstanceMember) {
548 _classesWithSchemaChanges.add(container);
549 }
550 updates.add(new AddedFieldUpdate(compiler, element, container));
551 }
552
553 bool canReuseRemovedElement(
554 PartialElement element,
555 ScopeContainerElement container) {
556 if (element is PartialFunctionElement) {
557 removeFunction(element);
558 return true;
559 } else if (element is PartialClassElement) {
560 removeClass(element);
561 return true;
562 } else if (element is PartialFieldList) {
563 removeFields(element, container);
564 return true;
565 }
566 return cannotReuse(
567 element, "Removing ${element.runtimeType} not supported.");
568 }
569
570 void removeFunction(PartialFunctionElement element) {
571 logVerbose("Removed method $element.");
572
573 invalidateScopesAffectedBy(element, element.enclosingElement);
574
575 _removedElements.add(element);
576
577 updates.add(new RemovedFunctionUpdate(compiler, element));
578 }
579
580 void removeClass(PartialClassElement element) {
581 logVerbose("Removed class $element.");
582
583 invalidateScopesAffectedBy(element, element.library);
584
585 _removedElements.add(element);
586 element.forEachLocalMember((ElementX member) {
587 _removedElements.add(member);
588 });
589
590 updates.add(new RemovedClassUpdate(compiler, element));
591 }
592
593 void removeFields(
594 PartialFieldList definition,
595 ScopeContainerElement container) {
596 List<FieldElementX> fields = <FieldElementX>[];
597 container.forEachLocalMember((ElementX member) {
598 if (member.declarationSite == definition) {
599 fields.add(member);
600 }
601 });
602 for (FieldElementX field in fields) {
603 // TODO(ahe): This only works when there's one field per
604 // PartialFieldList.
605 removeField(field);
606 }
607 }
608
609 void removeField(FieldElementX element) {
610 logVerbose("Removed field $element.");
611 if (!element.isInstanceMember) {
612 cannotReuse(element, "Not an instance field.");
613 } else {
614 removeInstanceField(element);
615 }
616 }
617
618 void removeInstanceField(FieldElementX element) {
619 PartialClassElement cls = element.enclosingClass;
620
621 _classesWithSchemaChanges.add(cls);
622 invalidateScopesAffectedBy(element, cls);
623
624 _removedElements.add(element);
625
626 updates.add(new RemovedFieldUpdate(compiler, element));
627 }
628
629 void invalidateScopesAffectedBy(
630 ElementX element,
631 /* ScopeContainerElement */ container) {
632 for (ScopeContainerElement scope in scopesAffectedBy(element, container)) {
633 scanSites(scope, (Element member, DeclarationSite site) {
634 // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior.
635 Set<String> names = qualifiedNamesIn(site);
636 if (canNamesResolveStaticallyTo(names, element, container)) {
637 _elementsToInvalidate.add(member);
638 }
639 });
640 }
641 }
642
643 /// Invoke [f] on each [DeclarationSite] in [element]. If [element] is a
644 /// [ScopeContainerElement], invoke f on all local members as well.
645 void scanSites(
646 Element element,
647 void f(ElementX element, DeclarationSite site)) {
648 DeclarationSite site = declarationSite(element);
649 if (site != null) {
650 f(element, site);
651 }
652 if (element is ScopeContainerElement) {
653 element.forEachLocalMember((member) { scanSites(member, f); });
654 }
655 }
656
657 /// Assume [element] is either removed from or added to [container], and
658 /// return all [ScopeContainerElement] that can see this change.
659 List<ScopeContainerElement> scopesAffectedBy(
660 Element element,
661 /* ScopeContainerElement */ container) {
662 // TODO(ahe): Use library export graph to compute this.
663 // TODO(ahe): Should return all user-defined libraries and packages.
664 LibraryElement library = container.library;
665 List<ScopeContainerElement> result = <ScopeContainerElement>[library];
666
667 if (!container.isClass) return result;
668
669 ClassElement cls = container;
670
671 var externalSubtypes =
672 compiler.world.subtypesOf(cls).where((e) => e.library != library);
673
674 return result..addAll(externalSubtypes);
675 }
676
677 /// Returns true if function [before] can be reused to reflect the changes in
678 /// [after].
679 ///
680 /// If [before] can be reused, an update (patch) is added to [updates].
681 bool canReuseFunction(
682 Token diffToken,
683 PartialFunctionElement before,
684 PartialFunctionElement after) {
685 FunctionExpression node =
686 after.parseNode(compiler.parsingContext).asFunctionExpression();
687 if (node == null) {
688 return cannotReuse(after, "Not a function expression: '$node'");
689 }
690 Token last = after.endToken;
691 if (node.body != null) {
692 last = node.body.getBeginToken();
693 }
694 if (isTokenBetween(diffToken, after.beginToken, last)) {
695 removeFunction(before);
696 addFunction(after, before.enclosingElement);
697 return true;
698 }
699 logVerbose('Simple modification of ${after} detected');
700 updates.add(new FunctionUpdate(compiler, before, after));
701 return true;
702 }
703
704 bool canReuseClass(
705 Token diffToken,
706 PartialClassElement before,
707 PartialClassElement after) {
708 ClassNode node = after.parseNode(compiler.parsingContext).asClassNode();
709 if (node == null) {
710 return cannotReuse(after, "Not a ClassNode: '$node'");
711 }
712 NodeList body = node.body;
713 if (body == null) {
714 return cannotReuse(after, "Class has no body.");
715 }
716 if (isTokenBetween(diffToken, node.beginToken, body.beginToken)) {
717 logVerbose('Class header modified in ${after}');
718 updates.add(new ClassUpdate(compiler, before, after));
719 before.forEachLocalMember((ElementX member) {
720 // TODO(ahe): Quadratic.
721 invalidateScopesAffectedBy(member, before);
722 });
723 }
724 return canReuseScopeContainerElement(before, after);
725 }
726
727 bool isTokenBetween(Token token, Token first, Token last) {
728 Token current = first;
729 while (current != last && current.kind != EOF_TOKEN) {
730 if (current == token) {
731 return true;
732 }
733 current = current.next;
734 }
735 return false;
736 }
737
738 bool unableToReuse(
739 Token diffToken,
740 PartialElement before,
741 PartialElement after) {
742 return cannotReuse(
743 after,
744 'Unhandled change:'
745 ' ${before} (${before.runtimeType} -> ${after.runtimeType}).');
746 }
747
748 /// Apply the collected [updates]. Return a list of elements that needs to be
749 /// recompiled after applying the updates. Any elements removed as a
750 /// consequence of applying the patches are added to [removals] if provided.
751 List<Element> applyUpdates([List<Update> removals]) {
752 for (Update update in updates) {
753 update.captureState();
754 }
755 if (!_failedUpdates.isEmpty) {
756 throw new IncrementalCompilationFailed(_failedUpdates.join('\n\n'));
757 }
758 for (ElementX element in _elementsToInvalidate) {
759 compiler.forgetElement(element);
760 element.reuseElement();
761 }
762 List<Element> elementsToInvalidate = <Element>[];
763 for (ElementX element in _elementsToInvalidate) {
764 if (!_removedElements.contains(element)) {
765 elementsToInvalidate.add(element);
766 }
767 }
768 for (Update update in updates) {
769 Element element = update.apply();
770 if (update.isRemoval) {
771 if (removals != null) {
772 removals.add(update);
773 }
774 } else {
775 elementsToInvalidate.add(element);
776 }
777 }
778 return elementsToInvalidate;
779 }
780
781 String computeUpdateJs() {
782 List<Update> removals = <Update>[];
783 List<Element> updatedElements = applyUpdates(removals);
784 if (compiler.progress != null) {
785 compiler.progress.reset();
786 }
787 for (Element element in updatedElements) {
788 if (!element.isClass) {
789 enqueuer.resolution.addToWorkList(element);
790 } else {
791 NO_WARN(element).ensureResolved(compiler);
792 }
793 }
794 compiler.processQueue(enqueuer.resolution, null);
795
796 compiler.phase = Compiler.PHASE_DONE_RESOLVING;
797
798 // TODO(ahe): Clean this up. Don't call this method in analyze-only mode.
799 if (compiler.options.analyzeOnly) return "/* analyze only */";
800
801 Set<ClassElementX> changedClasses =
802 new Set<ClassElementX>.from(_classesWithSchemaChanges);
803 for (Element element in updatedElements) {
804 if (!element.isClass) {
805 enqueuer.codegen.addToWorkList(element);
806 } else {
807 changedClasses.add(element);
808 }
809 }
810 compiler.processQueue(enqueuer.codegen, null);
811
812 // Run through all compiled methods and see if they may apply to
813 // newlySeenSelectors.
814 for (Element e in enqueuer.codegen.generatedCode.keys) {
815 if (e.isFunction && !e.isConstructor &&
816 e.functionSignature.hasOptionalParameters) {
817 for (Selector selector in enqueuer.codegen.newlySeenSelectors) {
818 // TODO(ahe): Group selectors by name at this point for improved
819 // performance.
820 if (e.isInstanceMember && selector.applies(e, compiler.world)) {
821 // TODO(ahe): Don't use
822 // enqueuer.codegen.newlyEnqueuedElements directly like
823 // this, make a copy.
824 enqueuer.codegen.newlyEnqueuedElements.add(e);
825 }
826 if (selector.name == namer.closureInvocationSelectorName) {
827 selector = new Selector.call(
828 e.name, e.library,
829 selector.argumentCount, selector.namedArguments);
830 if (selector.appliesUnnamed(e, compiler.world)) {
831 // TODO(ahe): Also make a copy here.
832 enqueuer.codegen.newlyEnqueuedElements.add(e);
833 }
834 }
835 }
836 }
837 }
838
839 List<jsAst.Statement> updates = <jsAst.Statement>[];
840
841 Set<ClassElementX> newClasses = new Set.from(
842 compiler.codegenWorld.directlyInstantiatedClasses);
843 newClasses.removeAll(_directlyInstantiatedClasses);
844
845 if (!newClasses.isEmpty) {
846 // Ask the emitter to compute "needs" (only) if new classes were
847 // instantiated.
848 _ensureAllNeededEntitiesComputed();
849 newClasses = new Set.from(emitter.neededClasses);
850 newClasses.removeAll(_emittedClasses);
851 } else {
852 // Make sure that the set of emitted classes is preserved for subsequent
853 // updates.
854 // TODO(ahe): This is a bit convoluted, find a better approach.
855 emitter.neededClasses
856 ..clear()
857 ..addAll(_emittedClasses);
858 }
859
860 List<jsAst.Statement> inherits = <jsAst.Statement>[];
861
862 for (ClassElementX cls in newClasses) {
863 jsAst.Node classAccess = emitter.constructorAccess(cls);
864 String name = namer.className(cls);
865
866 updates.add(
867 js.statement(
868 r'# = #', [classAccess, invokeDefineClass(cls)]));
869
870 ClassElement superclass = cls.superclass;
871 if (superclass != null) {
872 jsAst.Node superAccess = emitter.constructorAccess(superclass);
873 inherits.add(
874 js.statement(
875 r'this.inheritFrom(#, #)', [classAccess, superAccess]));
876 }
877 }
878
879 // Call inheritFrom after all classes have been created. This way we don't
880 // need to sort the classes by having superclasses defined before their
881 // subclasses.
882 updates.addAll(inherits);
883
884 for (ClassElementX cls in changedClasses) {
885 ClassElement superclass = cls.superclass;
886 jsAst.Node superAccess =
887 superclass == null ? js('null')
888 : emitter.constructorAccess(superclass);
889 jsAst.Node classAccess = emitter.constructorAccess(cls);
890 updates.add(
891 js.statement(
892 r'# = this.schemaChange(#, #, #)',
893 [classAccess, invokeDefineClass(cls), classAccess, superAccess]));
894 }
895
896 for (RemovalUpdate update in removals) {
897 update.writeUpdateJsOn(updates);
898 }
899 for (Element element in enqueuer.codegen.newlyEnqueuedElements) {
900 if (element.isField) {
901 updates.addAll(computeFieldUpdateJs(element));
902 } else {
903 updates.add(computeMethodUpdateJs(element));
904 }
905 }
906
907 Set<ConstantValue> newConstants = new Set<ConstantValue>.identity()..addAll(
908 compiler.backend.constants.compiledConstants);
909 newConstants.removeAll(_compiledConstants);
910
911 if (!newConstants.isEmpty) {
912 _ensureAllNeededEntitiesComputed();
913 List<ConstantValue> constants =
914 emitter.outputConstantLists[compiler.deferredLoadTask.mainOutputUnit];
915 if (constants != null) {
916 for (ConstantValue constant in constants) {
917 if (!_compiledConstants.contains(constant)) {
918 full.Emitter fullEmitter = emitter.emitter;
919 jsAst.Statement constantInitializer =
920 fullEmitter.buildConstantInitializer(constant).toStatement();
921 updates.add(constantInitializer);
922 }
923 }
924 }
925 }
926
927 updates.add(js.statement(r'''
928 if (this.pendingStubs) {
929 this.pendingStubs.map(function(e) { return e(); });
930 this.pendingStubs = void 0;
931 }
932 '''));
933
934 if (updates.length == 1) {
935 return prettyPrintJs(updates.single);
936 } else {
937 return prettyPrintJs(js.statement('{#}', [updates]));
938 }
939 }
940
941 jsAst.Expression invokeDefineClass(ClassElementX cls) {
942 String name = namer.className(cls);
943 var descriptor = js('Object.create(null)');
944 return js(
945 r'''
946 (new Function(
947 "$collectedClasses", "$desc",
948 this.defineClass(#name, #computeFields) +"\n;return " + #name))(
949 {#name: [,#descriptor]})''',
950 {'name': js.string(name),
951 'computeFields': js.stringArray(computeFields(cls)),
952 'descriptor': descriptor});
953 }
954
955 jsAst.Node computeMethodUpdateJs(Element element) {
956 Method member = new ProgramBuilder(compiler, namer, emitter)
957 .buildMethodHackForIncrementalCompilation(element);
958 if (member == null) {
959 compiler.internalError(element, '${element.runtimeType}');
960 }
961 ClassBuilder builder = new ClassBuilder(element, namer);
962 containerBuilder.addMemberMethod(member, builder);
963 jsAst.Node partialDescriptor =
964 builder.toObjectInitializer(emitClassDescriptor: false);
965
966 String name = member.name;
967 jsAst.Node function = member.code;
968 bool isStatic = !element.isInstanceMember;
969
970 /// Either a global object (non-instance members) or a prototype (instance
971 /// members).
972 jsAst.Node holder;
973
974 if (element.isInstanceMember) {
975 holder = emitter.prototypeAccess(element.enclosingClass);
976 } else {
977 holder = js('#', namer.globalObjectFor(element));
978 }
979
980 jsAst.Expression globalFunctionsAccess =
981 emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS);
982
983 return js.statement(
984 r'this.addMethod(#, #, #, #, #)',
985 [partialDescriptor, js.string(name), holder,
986 new jsAst.LiteralBool(isStatic), globalFunctionsAccess]);
987 }
988
989 List<jsAst.Statement> computeFieldUpdateJs(FieldElementX element) {
990 if (element.isInstanceMember) {
991 // Any initializers are inlined in factory methods, and the field is
992 // declared by adding its class to [_classesWithSchemaChanges].
993 return const <jsAst.Statement>[];
994 }
995 // A static (or top-level) field.
996 if (backend.constants.lazyStatics.contains(element)) {
997 full.Emitter fullEmitter = emitter.emitter;
998 jsAst.Expression init =
999 fullEmitter.buildLazilyInitializedStaticField(
1000 element, isolateProperties: namer.staticStateHolder);
1001 if (init == null) {
1002 throw new StateError("Initializer optimized away for $element");
1003 }
1004 return <jsAst.Statement>[init.toStatement()];
1005 } else {
1006 // TODO(ahe): When a field is referenced it is enqueued. If the field has
1007 // no initializer, it will not have any associated code, so it will
1008 // appear as if it was newly enqueued.
1009 if (element.initializer == null) {
1010 return const <jsAst.Statement>[];
1011 } else {
1012 throw new StateError("Don't know how to compile $element");
1013 }
1014 }
1015 }
1016
1017 String prettyPrintJs(jsAst.Node node) {
1018 jsAst.JavaScriptPrintingOptions options =
1019 new jsAst.JavaScriptPrintingOptions();
1020 jsAst.JavaScriptPrintingContext context =
1021 new jsAst.Dart2JSJavaScriptPrintingContext(compiler, null);
1022 jsAst.Printer printer = new jsAst.Printer(options, context);
1023 printer.blockOutWithoutBraces(node);
1024 return context.outBuffer.getText();
1025 }
1026
1027 String callNameFor(FunctionElement element) {
1028 // TODO(ahe): Call a method in the compiler to obtain this name.
1029 String callPrefix = namer.callPrefix;
1030 int parameterCount = element.functionSignature.parameterCount;
1031 return '$callPrefix\$$parameterCount';
1032 }
1033
1034 List<String> computeFields(ClassElement cls) {
1035 return new EmitterHelper(compiler).computeFields(cls);
1036 }
1037
1038 void _ensureAllNeededEntitiesComputed() {
1039 if (_hasComputedNeeds) return;
1040 emitter.computeAllNeededEntities();
1041 _hasComputedNeeds = true;
1042 }
1043 }
1044
1045 /// Represents an update (aka patch) of [before] to [after]. We use the word
1046 /// "update" to avoid confusion with the compiler feature of "patch" methods.
1047 abstract class Update {
1048 final Compiler compiler;
1049
1050 PartialElement get before;
1051
1052 PartialElement get after;
1053
1054 Update(this.compiler);
1055
1056 /// Applies the update to [before] and returns that element.
1057 Element apply();
1058
1059 bool get isRemoval => false;
1060
1061 /// Called before any patches are applied to capture any state that is needed
1062 /// later.
1063 void captureState() {
1064 }
1065 }
1066
1067 /// Represents an update of a function element.
1068 class FunctionUpdate extends Update with ReuseFunction {
1069 final PartialFunctionElement before;
1070
1071 final PartialFunctionElement after;
1072
1073 FunctionUpdate(Compiler compiler, this.before, this.after)
1074 : super(compiler);
1075
1076 PartialFunctionElement apply() {
1077 patchElement();
1078 reuseElement();
1079 return before;
1080 }
1081
1082 /// Destructively change the tokens in [before] to match those of [after].
1083 void patchElement() {
1084 before.beginToken = after.beginToken;
1085 before.endToken = after.endToken;
1086 before.getOrSet = after.getOrSet;
1087 }
1088 }
1089
1090 abstract class ReuseFunction {
1091 Compiler get compiler;
1092
1093 PartialFunctionElement get before;
1094
1095 /// Reset various caches and remove this element from the compiler's internal
1096 /// state.
1097 void reuseElement() {
1098 compiler.forgetElement(before);
1099 before.reuseElement();
1100 }
1101 }
1102
1103 abstract class RemovalUpdate extends Update {
1104 ElementX get element;
1105
1106 RemovalUpdate(Compiler compiler)
1107 : super(compiler);
1108
1109 bool get isRemoval => true;
1110
1111 void writeUpdateJsOn(List<jsAst.Statement> updates);
1112
1113 void removeFromEnclosing() {
1114 // TODO(ahe): Need to recompute duplicated elements logic again. Simplest
1115 // solution is probably to remove all elements from enclosing scope and add
1116 // them back.
1117 if (element.isTopLevel) {
1118 removeFromLibrary(element.library);
1119 } else {
1120 removeFromEnclosingClass(element.enclosingClass);
1121 }
1122 }
1123
1124 void removeFromEnclosingClass(PartialClassElement cls) {
1125 cls.localMembersCache = null;
1126 cls.localMembersReversed = cls.localMembersReversed.copyWithout(element);
1127 cls.localScope.contents.remove(element.name);
1128 }
1129
1130 void removeFromLibrary(LibraryElementX library) {
1131 library.localMembers = library.localMembers.copyWithout(element);
1132 library.localScope.contents.remove(element.name);
1133 }
1134 }
1135
1136 class RemovedFunctionUpdate extends RemovalUpdate
1137 with JsFeatures, ReuseFunction {
1138 final PartialFunctionElement element;
1139
1140 /// Name of property to remove using JavaScript "delete". Null for
1141 /// non-instance methods.
1142 String name;
1143
1144 /// For instance methods, access to class object. Otherwise, access to the
1145 /// method itself.
1146 jsAst.Node elementAccess;
1147
1148 bool wasStateCaptured = false;
1149
1150 RemovedFunctionUpdate(Compiler compiler, this.element)
1151 : super(compiler);
1152
1153 PartialFunctionElement get before => element;
1154
1155 PartialFunctionElement get after => null;
1156
1157 void captureState() {
1158 if (wasStateCaptured) throw "captureState was called twice.";
1159 wasStateCaptured = true;
1160
1161 if (element.isInstanceMember) {
1162 elementAccess = emitter.constructorAccess(element.enclosingClass);
1163 name = namer.instanceMethodName(element);
1164 } else {
1165 elementAccess = emitter.staticFunctionAccess(element);
1166 }
1167 }
1168
1169 PartialFunctionElement apply() {
1170 if (!wasStateCaptured) throw "captureState must be called before apply.";
1171 removeFromEnclosing();
1172 reuseElement();
1173 return null;
1174 }
1175
1176 void writeUpdateJsOn(List<jsAst.Statement> updates) {
1177 if (elementAccess == null) {
1178 compiler.internalError(
1179 element, 'No elementAccess for ${element.runtimeType}');
1180 }
1181 if (element.isInstanceMember) {
1182 if (name == null) {
1183 compiler.internalError(element, 'No name for ${element.runtimeType}');
1184 }
1185 updates.add(
1186 js.statement('delete #.prototype.#', [elementAccess, name]));
1187 } else {
1188 updates.add(js.statement('delete #', [elementAccess]));
1189 }
1190 }
1191 }
1192
1193 class RemovedClassUpdate extends RemovalUpdate with JsFeatures {
1194 final PartialClassElement element;
1195
1196 bool wasStateCaptured = false;
1197
1198 final List<jsAst.Node> accessToStatics = <jsAst.Node>[];
1199
1200 RemovedClassUpdate(Compiler compiler, this.element)
1201 : super(compiler);
1202
1203 PartialClassElement get before => element;
1204
1205 PartialClassElement get after => null;
1206
1207 void captureState() {
1208 if (wasStateCaptured) throw "captureState was called twice.";
1209 wasStateCaptured = true;
1210 accessToStatics.add(emitter.constructorAccess(element));
1211
1212 element.forEachLocalMember((ElementX member) {
1213 if (!member.isInstanceMember) {
1214 accessToStatics.add(emitter.staticFunctionAccess(member));
1215 }
1216 });
1217 }
1218
1219 PartialClassElement apply() {
1220 if (!wasStateCaptured) {
1221 throw new StateError("captureState must be called before apply.");
1222 }
1223
1224 removeFromEnclosing();
1225
1226 element.forEachLocalMember((ElementX member) {
1227 compiler.forgetElement(member);
1228 member.reuseElement();
1229 });
1230
1231 compiler.forgetElement(element);
1232 element.reuseElement();
1233
1234 return null;
1235 }
1236
1237 void writeUpdateJsOn(List<jsAst.Statement> updates) {
1238 if (accessToStatics.isEmpty) {
1239 throw
1240 new StateError("captureState must be called before writeUpdateJsOn.");
1241 }
1242
1243 for (jsAst.Node access in accessToStatics) {
1244 updates.add(js.statement('delete #', [access]));
1245 }
1246 }
1247 }
1248
1249 class RemovedFieldUpdate extends RemovalUpdate with JsFeatures {
1250 final FieldElementX element;
1251
1252 bool wasStateCaptured = false;
1253
1254 jsAst.Node prototypeAccess;
1255
1256 String getterName;
1257
1258 String setterName;
1259
1260 RemovedFieldUpdate(Compiler compiler, this.element)
1261 : super(compiler);
1262
1263 PartialFieldList get before => element.declarationSite;
1264
1265 PartialFieldList get after => null;
1266
1267 void captureState() {
1268 if (wasStateCaptured) throw "captureState was called twice.";
1269 wasStateCaptured = true;
1270
1271 prototypeAccess = emitter.prototypeAccess(element.enclosingClass);
1272 getterName = namer.getterForElement(element);
1273 setterName = namer.setterForElement(element);
1274 }
1275
1276 FieldElementX apply() {
1277 if (!wasStateCaptured) {
1278 throw new StateError("captureState must be called before apply.");
1279 }
1280
1281 removeFromEnclosing();
1282
1283 return element;
1284 }
1285
1286 void writeUpdateJsOn(List<jsAst.Statement> updates) {
1287 if (!wasStateCaptured) {
1288 throw new StateError(
1289 "captureState must be called before writeUpdateJsOn.");
1290 }
1291
1292 updates.add(
1293 js.statement('delete #.#', [prototypeAccess, getterName]));
1294 updates.add(
1295 js.statement('delete #.#', [prototypeAccess, setterName]));
1296 }
1297 }
1298
1299 class AddedFunctionUpdate extends Update with JsFeatures {
1300 final PartialFunctionElement element;
1301
1302 final /* ScopeContainerElement */ container;
1303
1304 AddedFunctionUpdate(Compiler compiler, this.element, this.container)
1305 : super(compiler) {
1306 if (container == null) {
1307 throw "container is null";
1308 }
1309 }
1310
1311 PartialFunctionElement get before => null;
1312
1313 PartialFunctionElement get after => element;
1314
1315 PartialFunctionElement apply() {
1316 Element enclosing = container;
1317 if (enclosing.isLibrary) {
1318 // TODO(ahe): Reuse compilation unit of element instead?
1319 enclosing = enclosing.compilationUnit;
1320 }
1321 PartialFunctionElement copy = element.copyWithEnclosing(enclosing);
1322 NO_WARN(container).addMember(copy, compiler);
1323 return copy;
1324 }
1325 }
1326
1327 class AddedClassUpdate extends Update with JsFeatures {
1328 final PartialClassElement element;
1329
1330 final LibraryElementX library;
1331
1332 AddedClassUpdate(Compiler compiler, this.element, this.library)
1333 : super(compiler);
1334
1335 PartialClassElement get before => null;
1336
1337 PartialClassElement get after => element;
1338
1339 PartialClassElement apply() {
1340 // TODO(ahe): Reuse compilation unit of element instead?
1341 CompilationUnitElementX compilationUnit = library.compilationUnit;
1342 PartialClassElement copy = element.copyWithEnclosing(compilationUnit);
1343 compilationUnit.addMember(copy, compiler);
1344 return copy;
1345 }
1346 }
1347
1348 class AddedFieldUpdate extends Update with JsFeatures {
1349 final FieldElementX element;
1350
1351 final ScopeContainerElement container;
1352
1353 AddedFieldUpdate(Compiler compiler, this.element, this.container)
1354 : super(compiler);
1355
1356 PartialFieldList get before => null;
1357
1358 PartialFieldList get after => element.declarationSite;
1359
1360 FieldElementX apply() {
1361 Element enclosing = container;
1362 if (enclosing.isLibrary) {
1363 // TODO(ahe): Reuse compilation unit of element instead?
1364 enclosing = enclosing.compilationUnit;
1365 }
1366 FieldElementX copy = element.copyWithEnclosing(enclosing);
1367 NO_WARN(container).addMember(copy, compiler);
1368 return copy;
1369 }
1370 }
1371
1372
1373 class ClassUpdate extends Update with JsFeatures {
1374 final PartialClassElement before;
1375
1376 final PartialClassElement after;
1377
1378 ClassUpdate(Compiler compiler, this.before, this.after)
1379 : super(compiler);
1380
1381 PartialClassElement apply() {
1382 patchElement();
1383 reuseElement();
1384 return before;
1385 }
1386
1387 /// Destructively change the tokens in [before] to match those of [after].
1388 void patchElement() {
1389 before.cachedNode = after.cachedNode;
1390 before.beginToken = after.beginToken;
1391 before.endToken = after.endToken;
1392 }
1393
1394 void reuseElement() {
1395 before.supertype = null;
1396 before.interfaces = null;
1397 before.nativeTagInfo = null;
1398 before.supertypeLoadState = STATE_NOT_STARTED;
1399 before.resolutionState = STATE_NOT_STARTED;
1400 before.isProxy = false;
1401 before.hasIncompleteHierarchy = false;
1402 before.backendMembers = const Link<Element>();
1403 before.allSupertypesAndSelf = null;
1404 }
1405 }
1406
1407 /// Returns all qualified names in [element] with less than four identifiers. A
1408 /// qualified name is an identifier followed by a sequence of dots and
1409 /// identifiers, for example, "x", and "x.y.z". But not "x.y.z.w" ("w" is the
1410 /// fourth identifier).
1411 ///
1412 /// The longest possible name that can be resolved is three identifiers, for
1413 /// example, "prefix.MyClass.staticMethod". Since four or more identifiers
1414 /// cannot resolve to anything statically, they're not included in the returned
1415 /// value of this method.
1416 Set<String> qualifiedNamesIn(PartialElement element) {
1417 Token beginToken = element.beginToken;
1418 Token endToken = element.endToken;
1419 Token token = beginToken;
1420 if (element is PartialClassElement) {
1421 ClassNode node = element.cachedNode;
1422 if (node != null) {
1423 NodeList body = node.body;
1424 if (body != null) {
1425 endToken = body.beginToken;
1426 }
1427 }
1428 }
1429 Set<String> names = new Set<String>();
1430 do {
1431 if (token.isIdentifier()) {
1432 String name = token.value;
1433 // [name] is a single "identifier".
1434 names.add(name);
1435 if (identical('.', token.next.stringValue) &&
1436 token.next.next.isIdentifier()) {
1437 token = token.next.next;
1438 name += '.${token.value}';
1439 // [name] is "idenfifier.idenfifier".
1440 names.add(name);
1441
1442 if (identical('.', token.next.stringValue) &&
1443 token.next.next.isIdentifier()) {
1444 token = token.next.next;
1445 name += '.${token.value}';
1446 // [name] is "idenfifier.idenfifier.idenfifier".
1447 names.add(name);
1448
1449 while (identical('.', token.next.stringValue) &&
1450 token.next.next.isIdentifier()) {
1451 // Skip remaining identifiers, they cannot statically resolve to
1452 // anything, and must be dynamic sends.
1453 token = token.next.next;
1454 }
1455 }
1456 }
1457 }
1458 token = token.next;
1459 } while (token.kind != EOF_TOKEN && token != endToken);
1460 return names;
1461 }
1462
1463 /// Returns true if one of the qualified names in names (as computed by
1464 /// [qualifiedNamesIn]) could be a static reference to [element].
1465 bool canNamesResolveStaticallyTo(
1466 Set<String> names,
1467 Element element,
1468 /* ScopeContainerElement */ container) {
1469 if (names.contains(element.name)) return true;
1470 if (container != null && container.isClass) {
1471 // [names] contains C.m, where C is the name of [container], and m is the
1472 // name of [element].
1473 if (names.contains("${container.name}.${element.name}")) return true;
1474 }
1475 // TODO(ahe): Check for prefixes as well.
1476 return false;
1477 }
1478
1479 DeclarationSite declarationSite(Element element) {
1480 return element is ElementX ? element.declarationSite : null;
1481 }
1482
1483 abstract class JsFeatures {
1484 Compiler get compiler;
1485
1486 JavaScriptBackend get backend => compiler.backend;
1487
1488 Namer get namer => backend.namer;
1489
1490 CodeEmitterTask get emitter => backend.emitter;
1491
1492 ContainerBuilder get containerBuilder {
1493 full.Emitter fullEmitter = emitter.emitter;
1494 return fullEmitter.containerBuilder;
1495 }
1496
1497 EnqueueTask get enqueuer => compiler.enqueuer;
1498 }
1499
1500 class EmitterHelper extends JsFeatures {
1501 final Compiler compiler;
1502
1503 EmitterHelper(this.compiler);
1504
1505 ClassEmitter get classEmitter {
1506 full.Emitter fullEmitter = emitter.emitter;
1507 return fullEmitter.classEmitter;
1508 }
1509
1510 List<String> computeFields(ClassElement classElement) {
1511 Class cls = new ProgramBuilder(compiler, namer, emitter)
1512 .buildFieldsHackForIncrementalCompilation(classElement);
1513 // TODO(ahe): Rewrite for new emitter.
1514 ClassBuilder builder = new ClassBuilder(classElement, namer);
1515 classEmitter.emitFields(cls, builder);
1516 return builder.fields;
1517 }
1518 }
1519
1520 // TODO(ahe): Remove this method.
1521 NO_WARN(x) => x;
OLDNEW
« no previous file with comments | « pkg/dart2js_incremental/lib/diff.dart ('k') | pkg/dart2js_incremental/lib/server.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698