| 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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 55 | 55 |
| 56 import 'package:compiler/src/js_backend/js_backend.dart' show | 56 import 'package:compiler/src/js_backend/js_backend.dart' show |
| 57 JavaScriptBackend, | 57 JavaScriptBackend, |
| 58 Namer; | 58 Namer; |
| 59 | 59 |
| 60 import 'package:compiler/src/util/util.dart' show | 60 import 'package:compiler/src/util/util.dart' show |
| 61 Link, | 61 Link, |
| 62 LinkBuilder; | 62 LinkBuilder; |
| 63 | 63 |
| 64 import 'package:compiler/src/elements/modelx.dart' show | 64 import 'package:compiler/src/elements/modelx.dart' show |
| 65 ClassElementX, |
| 65 DeclarationSite, | 66 DeclarationSite, |
| 66 ElementX; | 67 ElementX, |
| 68 LibraryElementX; |
| 67 | 69 |
| 68 import 'diff.dart' show | 70 import 'diff.dart' show |
| 69 Difference, | 71 Difference, |
| 70 computeDifference; | 72 computeDifference; |
| 71 | 73 |
| 72 typedef void Logger(message); | 74 typedef void Logger(message); |
| 73 | 75 |
| 74 typedef bool Reuser( | 76 typedef bool Reuser( |
| 75 Token diffToken, | 77 Token diffToken, |
| 76 PartialElement before, | 78 PartialElement before, |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 | 176 |
| 175 bool canReuseScopeContainerElement( | 177 bool canReuseScopeContainerElement( |
| 176 ScopeContainerElement element, | 178 ScopeContainerElement element, |
| 177 ScopeContainerElement newElement) { | 179 ScopeContainerElement newElement) { |
| 178 List<Difference> differences = computeDifference(element, newElement); | 180 List<Difference> differences = computeDifference(element, newElement); |
| 179 logTime('Differences computed.'); | 181 logTime('Differences computed.'); |
| 180 for (Difference difference in differences) { | 182 for (Difference difference in differences) { |
| 181 logTime('Looking at difference: $difference'); | 183 logTime('Looking at difference: $difference'); |
| 182 | 184 |
| 183 if (difference.before == null && difference.after is PartialElement) { | 185 if (difference.before == null && difference.after is PartialElement) { |
| 184 canReuseAddedElement(difference.after); | 186 canReuseAddedElement(difference.after, element); |
| 185 continue; | 187 continue; |
| 186 } | 188 } |
| 187 if (difference.after == null && difference.before is PartialElement) { | 189 if (difference.after == null && difference.before is PartialElement) { |
| 188 canReuseRemovedElement(difference.before); | 190 canReuseRemovedElement(difference.before); |
| 189 continue; | 191 continue; |
| 190 } | 192 } |
| 191 Token diffToken = difference.token; | 193 Token diffToken = difference.token; |
| 192 if (diffToken == null) { | 194 if (diffToken == null) { |
| 193 cannotReuse(difference, "No difference token."); | 195 cannotReuse(difference, "No difference token."); |
| 194 continue; | 196 continue; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 213 } | 215 } |
| 214 if (!reuser(diffToken, before, after)) { | 216 if (!reuser(diffToken, before, after)) { |
| 215 assert(!_failedUpdates.isEmpty); | 217 assert(!_failedUpdates.isEmpty); |
| 216 continue; | 218 continue; |
| 217 } | 219 } |
| 218 } | 220 } |
| 219 | 221 |
| 220 return _failedUpdates.isEmpty; | 222 return _failedUpdates.isEmpty; |
| 221 } | 223 } |
| 222 | 224 |
| 223 bool canReuseAddedElement(PartialElement element) { | 225 bool canReuseAddedElement( |
| 224 return cannotReuse(element, "Scope changed, element added."); | 226 PartialElement element, |
| 227 ScopeContainerElement container) { |
| 228 if (element is PartialFunctionElement) { |
| 229 addFunction(element, container); |
| 230 return true; |
| 231 } |
| 232 return cannotReuse(element, "Added element that isn't a function."); |
| 233 } |
| 234 |
| 235 void addFunction( |
| 236 PartialFunctionElement element, |
| 237 ScopeContainerElement container) { |
| 238 invalidateScopesAffectedBy(element, container); |
| 239 |
| 240 updates.add(new AddedFunctionUpdate(compiler, element, container)); |
| 225 } | 241 } |
| 226 | 242 |
| 227 bool canReuseRemovedElement(PartialElement element) { | 243 bool canReuseRemovedElement(PartialElement element) { |
| 228 if (element is PartialFunctionElement) { | 244 if (element is PartialFunctionElement) { |
| 229 return canReuseRemovedFunction(element); | 245 removeFunction(element); |
| 246 return true; |
| 230 } | 247 } |
| 231 return cannotReuse( | 248 return cannotReuse(element, "Removed element that isn't a function."); |
| 232 element, "Removed element that isn't a method."); | |
| 233 } | 249 } |
| 234 | 250 |
| 235 bool canReuseRemovedFunction(PartialFunctionElement element) { | 251 void removeFunction(PartialFunctionElement element) { |
| 236 logVerbose("Removed method $element."); | 252 logVerbose("Removed method $element."); |
| 237 | 253 |
| 238 ScopeContainerElement container = element.enclosingElement; | 254 invalidateScopesAffectedBy(element, element.enclosingElement); |
| 255 |
| 256 _removedElements.add(element); |
| 257 |
| 258 updates.add(new RemovedFunctionUpdate(compiler, element)); |
| 259 } |
| 260 |
| 261 void invalidateScopesAffectedBy( |
| 262 ElementX element, |
| 263 ScopeContainerElement container) { |
| 239 for (ScopeContainerElement scope in scopesAffectedBy(element, container)) { | 264 for (ScopeContainerElement scope in scopesAffectedBy(element, container)) { |
| 240 scanSites(scope, (Element member, DeclarationSite site) { | 265 scanSites(scope, (Element member, DeclarationSite site) { |
| 241 // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. | 266 // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. |
| 242 Map<String, List<String>> names = qualifiedNamesIn(site); | 267 Map<String, List<String>> names = qualifiedNamesIn(site); |
| 243 if (canNamesResolveStaticallyTo(names, element, container)) { | 268 if (canNamesResolveStaticallyTo(names, element, container)) { |
| 244 _elementsToInvalidate.add(member); | 269 _elementsToInvalidate.add(member); |
| 245 } | 270 } |
| 246 }); | 271 }); |
| 247 } | 272 } |
| 248 | |
| 249 _removedElements.add(element); | |
| 250 | |
| 251 updates.add(new RemovedFunctionUpdate(compiler, element)); | |
| 252 | |
| 253 return true; | |
| 254 } | 273 } |
| 255 | 274 |
| 256 /// Invoke [f] on each [DeclarationSite] in [element]. If [element] is a | 275 /// Invoke [f] on each [DeclarationSite] in [element]. If [element] is a |
| 257 /// [ScopeContainerElement], invoke f on all local members as well. | 276 /// [ScopeContainerElement], invoke f on all local members as well. |
| 258 void scanSites( | 277 void scanSites( |
| 259 Element element, | 278 Element element, |
| 260 void f(ElementX element, DeclarationSite site)) { | 279 void f(ElementX element, DeclarationSite site)) { |
| 261 DeclarationSite site = declarationSite(element); | 280 DeclarationSite site = declarationSite(element); |
| 262 if (site != null) { | 281 if (site != null) { |
| 263 f(element, site); | 282 f(element, site); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 FunctionExpression node = | 317 FunctionExpression node = |
| 299 after.parseNode(compiler).asFunctionExpression(); | 318 after.parseNode(compiler).asFunctionExpression(); |
| 300 if (node == null) { | 319 if (node == null) { |
| 301 return cannotReuse(after, "Not a function expression: '$node'"); | 320 return cannotReuse(after, "Not a function expression: '$node'"); |
| 302 } | 321 } |
| 303 Token last = after.endToken; | 322 Token last = after.endToken; |
| 304 if (node.body != null) { | 323 if (node.body != null) { |
| 305 last = node.body.getBeginToken(); | 324 last = node.body.getBeginToken(); |
| 306 } | 325 } |
| 307 if (isTokenBetween(diffToken, after.beginToken, last)) { | 326 if (isTokenBetween(diffToken, after.beginToken, last)) { |
| 308 return cannotReuse(after, 'Signature changed.'); | 327 removeFunction(before); |
| 328 addFunction(after, before.enclosingElement); |
| 329 return true; |
| 309 } | 330 } |
| 310 logVerbose('Simple modification of ${after} detected'); | 331 logVerbose('Simple modification of ${after} detected'); |
| 311 updates.add(new FunctionUpdate(compiler, before, after)); | 332 updates.add(new FunctionUpdate(compiler, before, after)); |
| 312 return true; | 333 return true; |
| 313 } | 334 } |
| 314 | 335 |
| 315 bool canReuseClass( | 336 bool canReuseClass( |
| 316 Token diffToken, | 337 Token diffToken, |
| 317 PartialClassElement before, | 338 PartialClassElement before, |
| 318 PartialClassElement after) { | 339 PartialClassElement after) { |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 439 jsAst.Node superAccess = namer.elementAccess(superclass); | 460 jsAst.Node superAccess = namer.elementAccess(superclass); |
| 440 inherits.add( | 461 inherits.add( |
| 441 js.statement( | 462 js.statement( |
| 442 r'self.$dart_unsafe_eval.inheritFrom(#, #)', | 463 r'self.$dart_unsafe_eval.inheritFrom(#, #)', |
| 443 [classAccess, superAccess])); | 464 [classAccess, superAccess])); |
| 444 } | 465 } |
| 445 } | 466 } |
| 446 | 467 |
| 447 updates.addAll(inherits); | 468 updates.addAll(inherits); |
| 448 | 469 |
| 470 for (RemovedFunctionUpdate update in removals) { |
| 471 update.writeUpdateJsOn(updates); |
| 472 } |
| 449 for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) { | 473 for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) { |
| 450 if (!element.isField) { | 474 if (!element.isField) { |
| 451 updates.add(computeMemberUpdateJs(element)); | 475 updates.add(computeMemberUpdateJs(element)); |
| 452 } | 476 } |
| 453 } | 477 } |
| 454 for (RemovedFunctionUpdate update in removals) { | |
| 455 update.writeUpdateJsOn(updates); | |
| 456 } | |
| 457 | 478 |
| 458 if (updates.length == 1) { | 479 if (updates.length == 1) { |
| 459 return prettyPrintJs(updates.single); | 480 return prettyPrintJs(updates.single); |
| 460 } else { | 481 } else { |
| 461 return prettyPrintJs(js.statement('{#}', [updates])); | 482 return prettyPrintJs(js.statement('{#}', [updates])); |
| 462 } | 483 } |
| 463 } | 484 } |
| 464 | 485 |
| 465 jsAst.Node computeMemberUpdateJs(Element element) { | 486 jsAst.Node computeMemberUpdateJs(Element element) { |
| 466 MemberInfo info = emitter.oldEmitter.containerBuilder | 487 MemberInfo info = emitter.oldEmitter.containerBuilder |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 abstract class Update { | 548 abstract class Update { |
| 528 final Compiler compiler; | 549 final Compiler compiler; |
| 529 | 550 |
| 530 PartialElement get before; | 551 PartialElement get before; |
| 531 | 552 |
| 532 PartialElement get after; | 553 PartialElement get after; |
| 533 | 554 |
| 534 Update(this.compiler); | 555 Update(this.compiler); |
| 535 | 556 |
| 536 /// Applies the update to [before] and returns that element. | 557 /// Applies the update to [before] and returns that element. |
| 537 PartialElement apply(); | 558 Element apply(); |
| 538 | 559 |
| 539 bool get isRemoval => false; | 560 bool get isRemoval => false; |
| 540 | 561 |
| 541 /// Called before any patches are applied to capture any state that is needed | 562 /// Called before any patches are applied to capture any state that is needed |
| 542 /// later. | 563 /// later. |
| 543 void captureState() { | 564 void captureState() { |
| 544 } | 565 } |
| 545 } | 566 } |
| 546 | 567 |
| 547 /// Represents an update of a function element. | 568 /// Represents an update of a function element. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 561 | 582 |
| 562 /// Destructively change the tokens in [before] to match those of [after]. | 583 /// Destructively change the tokens in [before] to match those of [after]. |
| 563 void patchElement() { | 584 void patchElement() { |
| 564 before.beginToken = after.beginToken; | 585 before.beginToken = after.beginToken; |
| 565 before.endToken = after.endToken; | 586 before.endToken = after.endToken; |
| 566 before.getOrSet = after.getOrSet; | 587 before.getOrSet = after.getOrSet; |
| 567 } | 588 } |
| 568 } | 589 } |
| 569 | 590 |
| 570 abstract class ReuseFunction { | 591 abstract class ReuseFunction { |
| 592 Compiler get compiler; |
| 593 |
| 571 PartialFunctionElement get before; | 594 PartialFunctionElement get before; |
| 572 | 595 |
| 573 /// Reset various caches and remove this element from the compiler's internal | 596 /// Reset various caches and remove this element from the compiler's internal |
| 574 /// state. | 597 /// state. |
| 575 void reuseElement() { | 598 void reuseElement() { |
| 576 compiler.forgetElement(before); | 599 compiler.forgetElement(before); |
| 577 before.reuseElement(); | 600 before.reuseElement(); |
| 578 } | 601 } |
| 579 } | 602 } |
| 580 | 603 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 613 if (backend.isAliasedSuperMember(element)) { | 636 if (backend.isAliasedSuperMember(element)) { |
| 614 superName = namer.getNameOfAliasedSuperMember(element); | 637 superName = namer.getNameOfAliasedSuperMember(element); |
| 615 } | 638 } |
| 616 } else { | 639 } else { |
| 617 elementAccess = namer.elementAccess(element); | 640 elementAccess = namer.elementAccess(element); |
| 618 } | 641 } |
| 619 | 642 |
| 620 wasStateCaptured = true; | 643 wasStateCaptured = true; |
| 621 } | 644 } |
| 622 | 645 |
| 623 PartialElement apply() { | 646 PartialFunctionElement apply() { |
| 624 if (!wasStateCaptured) throw "captureState must be called before apply."; | 647 if (!wasStateCaptured) throw "captureState must be called before apply."; |
| 625 removeFromEnclosing(); | 648 removeFromEnclosing(); |
| 626 reuseElement(); | 649 reuseElement(); |
| 627 return null; | 650 return null; |
| 628 } | 651 } |
| 629 | 652 |
| 630 void removeFromEnclosing() { | 653 void removeFromEnclosing() { |
| 631 PartialClassElement cls = element.enclosingClass; | 654 PartialClassElement cls = element.enclosingClass; |
| 632 if (cls == null) { | 655 if (cls == null) { |
| 633 removeFromLibrary(element.library); | 656 removeFromLibrary(element.library); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 676 if (superName != null) { | 699 if (superName != null) { |
| 677 updates.add( | 700 updates.add( |
| 678 js.statement('delete #.prototype.#', [elementAccess, superName])); | 701 js.statement('delete #.prototype.#', [elementAccess, superName])); |
| 679 } | 702 } |
| 680 } else { | 703 } else { |
| 681 updates.add(js.statement('delete #', [elementAccess])); | 704 updates.add(js.statement('delete #', [elementAccess])); |
| 682 } | 705 } |
| 683 } | 706 } |
| 684 } | 707 } |
| 685 | 708 |
| 709 class AddedFunctionUpdate extends Update with JsFeatures { |
| 710 final PartialFunctionElement element; |
| 711 |
| 712 final ScopeContainerElement container; |
| 713 |
| 714 AddedFunctionUpdate(Compiler compiler, this.element, this.container) |
| 715 : super(compiler) { |
| 716 if (container == null) { |
| 717 throw "container is null"; |
| 718 } |
| 719 } |
| 720 |
| 721 PartialFunctionElement get before => null; |
| 722 |
| 723 PartialElement get after => element; |
| 724 |
| 725 PartialFunctionElement apply() { |
| 726 Element enclosing = container; |
| 727 if (enclosing.isLibrary) { |
| 728 // TODO(ahe): Reuse compilation unit instead? |
| 729 enclosing = enclosing.compilationUnit; |
| 730 } |
| 731 PartialFunctionElement copy = element.copyWithEnclosing(enclosing); |
| 732 container.addMember(copy, compiler); |
| 733 return copy; |
| 734 } |
| 735 } |
| 736 |
| 686 /// Returns all qualified names in [element] with less than four identifiers. A | 737 /// Returns all qualified names in [element] with less than four identifiers. A |
| 687 /// qualified name is an identifier followed by a sequence of dots and | 738 /// qualified name is an identifier followed by a sequence of dots and |
| 688 /// identifiers, for example, "x", and "x.y.z". But not "x.y.z.w" ("w" is the | 739 /// identifiers, for example, "x", and "x.y.z". But not "x.y.z.w" ("w" is the |
| 689 /// fourth identifier). | 740 /// fourth identifier). |
| 690 /// | 741 /// |
| 691 /// The longest possible name that can be resolved is three identifiers, for | 742 /// The longest possible name that can be resolved is three identifiers, for |
| 692 /// example, "prefix.MyClass.staticMethod". Since four or more identifiers | 743 /// example, "prefix.MyClass.staticMethod". Since four or more identifiers |
| 693 /// cannot resolve to anything statically, they're not included in the returned | 744 /// cannot resolve to anything statically, they're not included in the returned |
| 694 /// value of this method. | 745 /// value of this method. |
| 695 Set<String> qualifiedNamesIn(PartialElement element) { | 746 Set<String> qualifiedNamesIn(PartialElement element) { |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 776 | 827 |
| 777 ClassEmitter get classEmitter => backend.emitter.oldEmitter.classEmitter; | 828 ClassEmitter get classEmitter => backend.emitter.oldEmitter.classEmitter; |
| 778 | 829 |
| 779 List<String> computeFields(ClassElement cls) { | 830 List<String> computeFields(ClassElement cls) { |
| 780 // TODO(ahe): Rewrite for new emitter. | 831 // TODO(ahe): Rewrite for new emitter. |
| 781 ClassBuilder builder = new ClassBuilder(cls, namer); | 832 ClassBuilder builder = new ClassBuilder(cls, namer); |
| 782 classEmitter.emitFields(cls, builder, ""); | 833 classEmitter.emitFields(cls, builder, ""); |
| 783 return builder.fields; | 834 return builder.fields; |
| 784 } | 835 } |
| 785 } | 836 } |
| OLD | NEW |