| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js_incremental.library_updater; | 5 library dart2js_incremental.library_updater; |
| 6 | 6 |
| 7 import 'dart:async' show | 7 import 'dart:async' show |
| 8 Future; | 8 Future; |
| 9 | 9 |
| 10 import 'dart:convert' show | 10 import 'dart:convert' show |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 if (element is PartialFunctionElement) { | 225 if (element is PartialFunctionElement) { |
| 226 return canReuseRemovedFunction(element); | 226 return canReuseRemovedFunction(element); |
| 227 } | 227 } |
| 228 return cannotReuse( | 228 return cannotReuse( |
| 229 element, "Removed element that isn't a method."); | 229 element, "Removed element that isn't a method."); |
| 230 } | 230 } |
| 231 | 231 |
| 232 bool canReuseRemovedFunction(PartialFunctionElement element) { | 232 bool canReuseRemovedFunction(PartialFunctionElement element) { |
| 233 logVerbose("Removed method $element."); | 233 logVerbose("Removed method $element."); |
| 234 | 234 |
| 235 PartialClassElement cls = element.enclosingClass; | 235 ScopeContainerElement container = element.enclosingElement; |
| 236 for (ScopeContainerElement scope in scopesAffectedBy(element, cls)) { | 236 for (ScopeContainerElement scope in scopesAffectedBy(element, container)) { |
| 237 scanSites(scope, (Element member, DeclarationSite site) { | 237 scanSites(scope, (Element member, DeclarationSite site) { |
| 238 // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. | 238 // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. |
| 239 Map<String, List<String>> names = qualifiedNamesIn(site); | 239 Map<String, List<String>> names = qualifiedNamesIn(site); |
| 240 if (canNamesResolveTo(names, element, cls)) { | 240 if (canNamesResolveStaticallyTo(names, element, container)) { |
| 241 _elementsToInvalidate.add(member); | 241 _elementsToInvalidate.add(member); |
| 242 } | 242 } |
| 243 }); | 243 }); |
| 244 } | 244 } |
| 245 | 245 |
| 246 _removedElements.add(element); | 246 _removedElements.add(element); |
| 247 | 247 |
| 248 updates.add(new RemovedFunctionUpdate(compiler, element)); | 248 updates.add(new RemovedFunctionUpdate(compiler, element)); |
| 249 | 249 |
| 250 return true; | 250 return true; |
| 251 } | 251 } |
| 252 | 252 |
| 253 /// Invoke [f] on each [DeclarationSite] in [element]. If [element] is a |
| 254 /// [ScopeContainerElement], invoke f on all local members as well. |
| 253 void scanSites( | 255 void scanSites( |
| 254 Element element, | 256 Element element, |
| 255 void f(ElementX element, DeclarationSite site)) { | 257 void f(ElementX element, DeclarationSite site)) { |
| 256 DeclarationSite site = declarationSite(element); | 258 DeclarationSite site = declarationSite(element); |
| 257 if (site != null) { | 259 if (site != null) { |
| 258 f(element, site); | 260 f(element, site); |
| 259 } | 261 } |
| 260 if (element is ScopeContainerElement) { | 262 if (element is ScopeContainerElement) { |
| 261 element.forEachLocalMember((member) { scanSites(member, f); }); | 263 element.forEachLocalMember((member) { scanSites(member, f); }); |
| 262 } | 264 } |
| 263 } | 265 } |
| 264 | 266 |
| 267 /// Assume [element] is either removed from or added to [container], and |
| 268 /// return all [ScopeContainerElement] that can see this change. |
| 265 List<ScopeContainerElement> scopesAffectedBy( | 269 List<ScopeContainerElement> scopesAffectedBy( |
| 266 Element element, | 270 Element element, |
| 267 ClassElement cls) { | 271 ScopeContainerElement container) { |
| 268 // TODO(ahe): Use library export graph to compute this. | 272 // TODO(ahe): Use library export graph to compute this. |
| 269 // TODO(ahe): Should return all user-defined libraries and packages. | 273 // TODO(ahe): Should return all user-defined libraries and packages. |
| 270 LibraryElement library = element.library; | 274 LibraryElement library = container.library; |
| 271 List<ScopeContainerElement> result = <ScopeContainerElement>[library]; | 275 List<ScopeContainerElement> result = <ScopeContainerElement>[library]; |
| 272 | 276 |
| 273 if (cls == null) return result; | 277 if (!container.isClass) return result; |
| 278 |
| 279 ClassElement cls = container; |
| 274 | 280 |
| 275 var externalSubtypes = | 281 var externalSubtypes = |
| 276 compiler.world.subtypesOf(cls).where((e) => e.library != library); | 282 compiler.world.subtypesOf(cls).where((e) => e.library != library); |
| 277 | 283 |
| 278 return result..addAll(externalSubtypes); | 284 return result..addAll(externalSubtypes); |
| 279 } | 285 } |
| 280 | 286 |
| 281 /// Returns true if function [before] can be reused to reflect the changes in | 287 /// Returns true if function [before] can be reused to reflect the changes in |
| 282 /// [after]. | 288 /// [after]. |
| 283 /// | 289 /// |
| (...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 653 if (superName != null) { | 659 if (superName != null) { |
| 654 updates.add( | 660 updates.add( |
| 655 js.statement('delete #.prototype.#', [elementAccess, superName])); | 661 js.statement('delete #.prototype.#', [elementAccess, superName])); |
| 656 } | 662 } |
| 657 } else { | 663 } else { |
| 658 updates.add(js.statement('delete #', [elementAccess])); | 664 updates.add(js.statement('delete #', [elementAccess])); |
| 659 } | 665 } |
| 660 } | 666 } |
| 661 } | 667 } |
| 662 | 668 |
| 663 Map<String, List<String>> qualifiedNamesIn(PartialElement element) { | 669 /// Returns all qualified names in [element] with less than four identifiers. A |
| 670 /// qualified name is an identifier followed by a sequence of dots and |
| 671 /// identifiers, for example, "x", and "x.y.z". But not "x.y.z.w" ("w" is the |
| 672 /// fourth identifier). |
| 673 /// |
| 674 /// The longest possible name that can be resolved is three identifiers, for |
| 675 /// example, "prefix.MyClass.staticMethod". Since four or more identifiers |
| 676 /// cannot resolve to anything statically, they're not included in the returned |
| 677 /// value of this method. |
| 678 Set<String> qualifiedNamesIn(PartialElement element) { |
| 664 Token beginToken = element.beginToken; | 679 Token beginToken = element.beginToken; |
| 665 Token endToken = element.endToken; | 680 Token endToken = element.endToken; |
| 666 Token token = beginToken; | 681 Token token = beginToken; |
| 667 if (element is PartialClassElement) { | 682 if (element is PartialClassElement) { |
| 668 ClassNode node = element.cachedNode; | 683 ClassNode node = element.cachedNode; |
| 669 if (node != null) { | 684 if (node != null) { |
| 670 NodeList body = node.body; | 685 NodeList body = node.body; |
| 671 if (body != null) { | 686 if (body != null) { |
| 672 endToken = body.beginToken; | 687 endToken = body.beginToken; |
| 673 } | 688 } |
| 674 } | 689 } |
| 675 } | 690 } |
| 676 Map<String, List<String>> names = new Map<String, List<String>>(); | 691 Set<String> names = new Set<String>(); |
| 677 List<List<Token>> qualifieds = <List<Token>>[]; | |
| 678 do { | 692 do { |
| 679 if (token.isIdentifier()) { | 693 if (token.isIdentifier()) { |
| 680 List<String> name = names.putIfAbsent(token.value, () => <String>[]); | 694 String name = token.value; |
| 681 while (identical('.', token.next.stringValue) && | 695 // [name] is a single "identifier". |
| 682 token.next.next.isIdentifier()) { | 696 names.add(name); |
| 697 if (identical('.', token.next.stringValue) && |
| 698 token.next.next.isIdentifier()) { |
| 683 token = token.next.next; | 699 token = token.next.next; |
| 684 name.add(token.value); | 700 name += '.${token.value}'; |
| 701 // [name] is "idenfifier.idenfifier". |
| 702 names.add(name); |
| 703 |
| 704 if (identical('.', token.next.stringValue) && |
| 705 token.next.next.isIdentifier()) { |
| 706 token = token.next.next; |
| 707 name += '.${token.value}'; |
| 708 // [name] is "idenfifier.idenfifier.idenfifier". |
| 709 names.add(name); |
| 710 |
| 711 while (identical('.', token.next.stringValue) && |
| 712 token.next.next.isIdentifier()) { |
| 713 // Skip remaining identifiers, they cannot statically resolve to |
| 714 // anything, and must be dynamic sends. |
| 715 token = token.next.next; |
| 716 } |
| 717 } |
| 685 } | 718 } |
| 686 } | 719 } |
| 687 token = token.next; | 720 token = token.next; |
| 688 } while (token.kind != EOF_TOKEN && token != endToken); | 721 } while (token.kind != EOF_TOKEN && token != endToken); |
| 689 return names; | 722 return names; |
| 690 } | 723 } |
| 691 | 724 |
| 692 bool canNamesResolveTo( | 725 /// Returns true if one of the qualified names in names (as computed by |
| 693 Map<String, List<String>> names, | 726 /// [qualifiedNamesIn]) could be a static reference to [element]. |
| 727 bool canNamesResolveStaticallyTo( |
| 728 Set<String> names, |
| 694 Element element, | 729 Element element, |
| 695 ClassElement cls) { | 730 ScopeContainerElement container) { |
| 696 if (names.containsKey(element.name)) { | 731 if (names.contains(element.name)) return true; |
| 697 return true; | 732 if (container != null && container.isClass) { |
| 733 // [names] contains C.m, where C is the name of [container], and m is the |
| 734 // name of [element]. |
| 735 if (names.contains("${container.name}.${element.name}")) return true; |
| 698 } | 736 } |
| 699 if (cls != null) { | 737 // TODO(ahe): Check for prefixes as well. |
| 700 List<String> rest = names[cls.name]; | |
| 701 if (rest != null && rest.contains(element.name)) { | |
| 702 // [names] contains C.m, where C is the name of [cls], and m is the name | |
| 703 // of [element]. | |
| 704 return true; | |
| 705 } | |
| 706 } | |
| 707 return false; | 738 return false; |
| 708 } | 739 } |
| 709 | 740 |
| 710 DeclarationSite declarationSite(Element element) { | 741 DeclarationSite declarationSite(Element element) { |
| 711 return element is ElementX ? element.declarationSite : null; | 742 return element is ElementX ? element.declarationSite : null; |
| 712 } | 743 } |
| 713 | 744 |
| 714 abstract class JsFeatures { | 745 abstract class JsFeatures { |
| 715 Compiler get compiler; | 746 Compiler get compiler; |
| 716 | 747 |
| 717 JavaScriptBackend get backend => compiler.backend; | 748 JavaScriptBackend get backend => compiler.backend; |
| 718 | 749 |
| 719 Namer get namer => backend.namer; | 750 Namer get namer => backend.namer; |
| 720 | 751 |
| 721 CodeEmitterTask get emitter => backend.emitter; | 752 CodeEmitterTask get emitter => backend.emitter; |
| 722 } | 753 } |
| OLD | NEW |