| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 | 5 |
| 6 /** | 6 /** |
| 7 * If true, print a warning for each method that was resolved, but not | 7 * If true, print a warning for each method that was resolved, but not |
| 8 * compiled. | 8 * compiled. |
| 9 */ | 9 */ |
| 10 final bool REPORT_EXCESS_RESOLUTION = false; | 10 final bool REPORT_EXCESS_RESOLUTION = false; |
| (...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 514 LibraryElement patchLibrary = | 514 LibraryElement patchLibrary = |
| 515 patchParser.loadPatchLibrary(patchUri); | 515 patchParser.loadPatchLibrary(patchUri); |
| 516 // We allow foreign functions in patched libraries. | 516 // We allow foreign functions in patched libraries. |
| 517 addForeignFunctions(library); // Is safe even if already added. | 517 addForeignFunctions(library); // Is safe even if already added. |
| 518 applyLibraryPatch(library, patchLibrary); | 518 applyLibraryPatch(library, patchLibrary); |
| 519 } | 519 } |
| 520 } | 520 } |
| 521 | 521 |
| 522 void applyLibraryPatch(LibraryElement original, LibraryElement patch) { | 522 void applyLibraryPatch(LibraryElement original, LibraryElement patch) { |
| 523 Link<Element> patches = patch.topLevelElements; | 523 Link<Element> patches = patch.topLevelElements; |
| 524 applyContainerPatch(original, patches, original.findLocal); |
| 524 | 525 |
| 525 // Copy/patch top-level elements. | 526 // Copy imports from patch to original library. |
| 527 Map<String, LibraryElement> delayedPatches = <LibraryElement>{}; |
| 528 Uri patchBase = patch.script.uri; |
| 529 for (ScriptTag tag in patch.tags.reverse()) { |
| 530 if (tag.isImport()) { |
| 531 StringNode argument = tag.argument; |
| 532 Uri resolved = patchBase.resolve(argument.dartString.slowToString()); |
| 533 LibraryElement importedLibrary = |
| 534 scanner.loadLibrary(resolved, argument); |
| 535 scanner.importLibrary(original, importedLibrary, tag, patch); |
| 536 if (resolved.scheme == "dart") { |
| 537 delayedPatches[resolved.path] = importedLibrary; |
| 538 } |
| 539 } |
| 540 } |
| 541 |
| 542 // Mark library as already patched. |
| 543 original.patch = patch; |
| 544 |
| 545 // We patch imported libraries after marking the current library as |
| 546 // patched, to avoid problems with cyclic dependencies. |
| 547 delayedPatches.forEach((String path, LibraryElement importedLibrary) { |
| 548 patchDartLibrary(importedLibrary, path); |
| 549 }); |
| 550 } |
| 551 |
| 552 void applyContainerPatch(ContainerElement original, Link<Element> patches, |
| 553 Element lookup(SourceString name)) { |
| 526 while (!patches.isEmpty()) { | 554 while (!patches.isEmpty()) { |
| 527 Element patchElement = patches.head; | 555 Element patchElement = patches.head; |
| 528 Element originalElement = original.elements[patchElement.name]; | 556 Element originalElement = lookup(patchElement.name); |
| 529 // Getters and setters are kept inside a synthetic field. | 557 // Getters and setters are kept inside a synthetic field. |
| 530 if (patchElement.kind === ElementKind.ABSTRACT_FIELD) { | 558 if (patchElement.kind === ElementKind.ABSTRACT_FIELD) { |
| 531 if (originalElement !== null && | 559 if (originalElement !== null && |
| 532 originalElement.kind !== ElementKind.ABSTRACT_FIELD) { | 560 originalElement.kind !== ElementKind.ABSTRACT_FIELD) { |
| 533 internalError("Cannot patch non-getter/setter with getter/setter", | 561 internalError("Cannot patch non-getter/setter with getter/setter", |
| 534 element: originalElement); | 562 element: originalElement); |
| 535 } | 563 } |
| 536 AbstractFieldElement patchField = patchElement; | 564 AbstractFieldElement patchField = patchElement; |
| 537 AbstractFieldElement originalField = originalElement; | 565 AbstractFieldElement originalField = originalElement; |
| 538 if (patchField.getter !== null) { | 566 if (patchField.getter !== null) { |
| 539 if (originalField === null || originalField.getter === null) { | 567 if (originalField === null || originalField.getter === null) { |
| 540 original.addGetterOrSetter(clonePatch(patchField.getter)); | 568 original.addGetterOrSetter(clonePatch(patchField.getter), |
| 569 originalField, |
| 570 this); |
| 571 if (originalField === null && patchField.setter !== null) { |
| 572 // It exists now, so find it for the setter patching. |
| 573 originalField = lookup(patchElement.name); |
| 574 } |
| 541 } else { | 575 } else { |
| 542 patchMember(originalField.getter, patchField.getter); | 576 patchMember(originalField.getter, patchField.getter); |
| 543 } | 577 } |
| 544 } | 578 } |
| 545 if (patchField.setter !== null) { | 579 if (patchField.setter !== null) { |
| 546 if (originalField === null || originalField.setter === null) { | 580 if (originalField === null || originalField.setter === null) { |
| 547 original.addGetterOrSetter(clonePatch(patchField.setter)); | 581 original.addGetterOrSetter(clonePatch(patchField.setter), |
| 582 originalField, |
| 583 this); |
| 548 } else { | 584 } else { |
| 549 patchMember(originalField.setter, patchField.setter); | 585 patchMember(originalField.setter, patchField.setter); |
| 550 } | 586 } |
| 551 } | 587 } |
| 552 } else if (originalElement === null) { | 588 } else if (originalElement === null) { |
| 553 original.addMember(clonePatch(patchElement)); | 589 if (isPatchElement(patchElement)) { |
| 590 internalError("Cannot patch non-existing member '" |
| 591 "${patchElement.name.slowToString()}'."); |
| 592 } |
| 593 original.addMember(clonePatch(patchElement), this); |
| 554 } else { | 594 } else { |
| 555 patchMember(originalElement, patchElement); | 595 patchMember(originalElement, patchElement); |
| 556 } | 596 } |
| 557 patches = patches.tail; | 597 patches = patches.tail; |
| 558 } | 598 } |
| 559 | |
| 560 // Copy imports. | |
| 561 Map<String, LibraryElement> delayedPatches = <LibraryElement>{}; | |
| 562 Uri patchBase = patch.script.uri; | |
| 563 for (ScriptTag tag in patch.tags.reverse()) { | |
| 564 if (tag.isImport()) { | |
| 565 StringNode argument = tag.argument; | |
| 566 Uri resolved = patchBase.resolve(argument.dartString.slowToString()); | |
| 567 LibraryElement importedLibrary = | |
| 568 scanner.loadLibrary(resolved, argument); | |
| 569 scanner.importLibrary(original, importedLibrary, tag, patch); | |
| 570 if (resolved.scheme == "dart") { | |
| 571 delayedPatches[resolved.path] = importedLibrary; | |
| 572 } | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 // Mark library as already patched. | |
| 577 original.patch = patch; | |
| 578 | |
| 579 // We patch imported libraries after marking the current library as | |
| 580 // patched, to avoid problems with cyclic dependencies. | |
| 581 delayedPatches.forEach((String path, LibraryElement importedLibrary) { | |
| 582 patchDartLibrary(importedLibrary, path); | |
| 583 }); | |
| 584 } | 599 } |
| 585 | 600 |
| 586 bool isPatchElement(Element element) { | 601 bool isPatchElement(Element element) { |
| 587 // TODO(lrn): More checks needed if we introduce metadata for real. | 602 // TODO(lrn): More checks needed if we introduce metadata for real. |
| 588 // In that case, it must have the identifier "native" as metadata. | 603 // In that case, it must have the identifier "native" as metadata. |
| 589 return !element.metadata.isEmpty(); | 604 return !element.metadata.isEmpty(); |
| 590 } | 605 } |
| 591 | 606 |
| 592 Element clonePatch(Element patchElement) { | 607 Element clonePatch(Element patchElement) { |
| 593 // The original library does not have an element with the same name | 608 // The original library does not have an element with the same name |
| 594 // as the patch library element. | 609 // as the patch library element. |
| 595 // In this case, the patch library element must not be marked as "patch", | 610 // In this case, the patch library element must not be marked as "patch", |
| 596 // and its name must make it private. | 611 // and its name must make it private. |
| 597 if (isPatchElement(patchElement)) { | |
| 598 internalError("Cannot patch non-existing member '" | |
| 599 "${patchElement.name.slowToString()}'."); | |
| 600 | |
| 601 } | |
| 602 if (!patchElement.name.isPrivate()) { | 612 if (!patchElement.name.isPrivate()) { |
| 603 internalError("Cannot add non-private member '" | 613 internalError("Cannot add non-private member '" |
| 604 "${patchElement.name.slowToString()}' from patch."); | 614 "${patchElement.name.slowToString()}' from patch."); |
| 605 } | 615 } |
| 606 // TODO(lrn): Create a copy of patchElement that isn't added to anything, | 616 // TODO(lrn): Create a copy of patchElement that isn't added to any |
| 607 // but which takes its source from patchElement. | 617 // object/library yet, but which takes its source from patchElement. |
| 608 throw "Adding members from patch is unsupported"; | 618 throw "Adding members from patch is unsupported"; |
| 609 } | 619 } |
| 610 | 620 |
| 611 void patchMember(Element originalElement, | 621 void patchMember(Element originalElement, Element patchElement) { |
| 612 Element patchElement) { | |
| 613 // The original library has an element with the same name as the patch | 622 // The original library has an element with the same name as the patch |
| 614 // library element. | 623 // library element. |
| 615 // In this case, the patch library element must be a function marked as | 624 // In this case, the patch library element must be a function marked as |
| 616 // "patch" and it must have the same signature as the function it patches. | 625 // "patch" and it must have the same signature as the function it patches. |
| 617 if (!isPatchElement(patchElement)) { | 626 if (!isPatchElement(patchElement)) { |
| 618 internalError("Cannot overwrite existing '" | 627 internalError("Cannot overwrite existing '" |
| 619 "${originalElement.name.slowToString()}' with non-patch."); | 628 "${originalElement.name.slowToString()}' with non-patch."); |
| 620 } | 629 } |
| 630 if (originalElement is PartialClassElement) { |
| 631 // Only happens when patching a library. Dart does not, yet, have nested |
| 632 // classes. |
| 633 if (patchElement is! PartialClassElement) { |
| 634 internalError("Trying to patch class with non-class", |
| 635 element:originalElement); |
| 636 } |
| 637 applyClassPatch(originalElement, patchElement); |
| 638 return; |
| 639 } |
| 621 if (originalElement is! FunctionElement) { | 640 if (originalElement is! FunctionElement) { |
| 622 // TODO(lrn): Handle class declarations too. | 641 // TODO(lrn): Handle class declarations too. |
| 623 internalError("Can only patch functions", element: originalElement); | 642 internalError("Can only patch functions", element: originalElement); |
| 624 } | 643 } |
| 625 // TODO(lrn): Abort if the original isn't marked external, when | 644 FunctionElement original = originalElement; |
| 626 // that is added to the language. | 645 if (!original.modifiers.isExternal()) { |
| 646 internalError("Can only patch external functions.", element: original); |
| 647 } |
| 627 if (patchElement is! FunctionElement || | 648 if (patchElement is! FunctionElement || |
| 628 !patchSignatureMatches(originalElement, patchElement)) { | 649 !patchSignatureMatches(original, patchElement)) { |
| 629 internalError("Can only patch functions with matching signatures", | 650 internalError("Can only patch functions with matching signatures", |
| 630 element: originalElement); | 651 element: original); |
| 631 } | 652 } |
| 632 applyFunctionPatch(originalElement, patchElement); | 653 applyFunctionPatch(original, patchElement); |
| 633 } | 654 } |
| 634 | 655 |
| 635 bool patchSignatureMatches(FunctionElement original, FunctionElement patch) { | 656 bool patchSignatureMatches(FunctionElement original, FunctionElement patch) { |
| 636 // TODO(lrn): Check that patches actually match the signature of | 657 // TODO(lrn): Check that patches actually match the signature of |
| 637 // the function it's patching. | 658 // the function it's patching. |
| 638 return true; | 659 return true; |
| 639 } | 660 } |
| 640 | 661 |
| 641 void applyFunctionPatch(FunctionElement element, | 662 void applyFunctionPatch(FunctionElement element, |
| 642 FunctionElement patchElement) { | 663 FunctionElement patchElement) { |
| 643 // Don't just assign the patch field. This also updates the cachedNode. | |
| 644 if (element.isPatched) { | 664 if (element.isPatched) { |
| 645 internalError("Trying to patch a function more than once.", | 665 internalError("Trying to patch a function more than once.", |
| 646 element: element); | 666 element: element); |
| 647 } | 667 } |
| 648 if (element.cachedNode !== null) { | 668 if (element.cachedNode !== null) { |
| 649 internalError("Trying to patch an already compiled function.", | 669 internalError("Trying to patch an already compiled function.", |
| 650 element: element); | 670 element: element); |
| 651 } | 671 } |
| 672 // Don't just assign the patch field. This also updates the cachedNode. |
| 652 element.setPatch(patchElement); | 673 element.setPatch(patchElement); |
| 653 } | 674 } |
| 654 | 675 |
| 676 void applyClassPatch(PartialClassElement original, |
| 677 PartialClassElement patch) { |
| 678 // Eagerly parse the class so we can patch it. |
| 679 // TODO(lrn): Perhaps find a way to delay parsing until the class is needed, |
| 680 // i.e., until [parseNode] is called on [original]. |
| 681 ClassNode node = original.parseNode(this); |
| 682 // Parse patch class with "patch" parser. |
| 683 ClassNode patchNode = patchParser.parsePatchClassNode(patch); |
| 684 Link<Element> patches = patch.members; |
| 685 Element lookupMemberOrConstructor(SourceString name) { |
| 686 Element result = original.lookupLocalMember(name); |
| 687 if (result !== null) return result; |
| 688 return original.lookupConstructor(name); |
| 689 } |
| 690 applyContainerPatch(original, patches, lookupMemberOrConstructor); |
| 691 } |
| 692 |
| 655 /** | 693 /** |
| 656 * Get an [Uri] pointing to a patch for the dart: library with | 694 * Get an [Uri] pointing to a patch for the dart: library with |
| 657 * the given path. Returns null if there is no patch. | 695 * the given path. Returns null if there is no patch. |
| 658 */ | 696 */ |
| 659 abstract Uri resolvePatchUri(String dartLibraryPath); | 697 abstract Uri resolvePatchUri(String dartLibraryPath); |
| 660 | 698 |
| 661 /** Define the JS helper functions in the given library. */ | 699 /** Define the JS helper functions in the given library. */ |
| 662 void addForeignFunctions(LibraryElement library) { | 700 void addForeignFunctions(LibraryElement library) { |
| 663 library.define(new ForeignElement( | 701 library.define(new ForeignElement( |
| 664 const SourceString('JS'), library), this); | 702 const SourceString('JS'), library), this); |
| (...skipping 378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1043 f(int beginOffset, int endOffset)) { | 1081 f(int beginOffset, int endOffset)) { |
| 1044 final beginOffset = begin.charOffset; | 1082 final beginOffset = begin.charOffset; |
| 1045 final endOffset = end.charOffset + end.slowCharCount; | 1083 final endOffset = end.charOffset + end.slowCharCount; |
| 1046 | 1084 |
| 1047 // [begin] and [end] might be the same for the same empty token. This | 1085 // [begin] and [end] might be the same for the same empty token. This |
| 1048 // happens for instance when scanning '$$'. | 1086 // happens for instance when scanning '$$'. |
| 1049 assert(endOffset >= beginOffset); | 1087 assert(endOffset >= beginOffset); |
| 1050 return f(beginOffset, endOffset); | 1088 return f(beginOffset, endOffset); |
| 1051 } | 1089 } |
| 1052 } | 1090 } |
| OLD | NEW |