| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Assigns JavaScript identifiers to Dart variables, class-names and members. | 8 * Assigns JavaScript identifiers to Dart variables, class-names and members. |
| 9 * | 9 * |
| 10 * Names are generated through three stages: | 10 * Names are generated through three stages: |
| (...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 489 * If the [originalName] is not private returns [originalName]. Otherwise | 489 * If the [originalName] is not private returns [originalName]. Otherwise |
| 490 * mangles the [originalName] so that each library has its own distinguished | 490 * mangles the [originalName] so that each library has its own distinguished |
| 491 * version of the name. | 491 * version of the name. |
| 492 * | 492 * |
| 493 * Although the name is not guaranteed to be unique within any namespace, | 493 * Although the name is not guaranteed to be unique within any namespace, |
| 494 * clashes are very unlikely in practice. Therefore, it can be used in cases | 494 * clashes are very unlikely in practice. Therefore, it can be used in cases |
| 495 * where uniqueness is nice but not a strict requirement. | 495 * where uniqueness is nice but not a strict requirement. |
| 496 * | 496 * |
| 497 * The resulting name is a *proposed name* and is never minified. | 497 * The resulting name is a *proposed name* and is never minified. |
| 498 */ | 498 */ |
| 499 String privateName(Name originalName) { | 499 String privateName(LibraryElement library, String originalName) { |
| 500 String text = originalName.text; | |
| 501 | |
| 502 // Public names are easy. | 500 // Public names are easy. |
| 503 if (!originalName.isPrivate) return text; | 501 if (!isPrivateName(originalName)) return originalName; |
| 504 | |
| 505 LibraryElement library = originalName.library; | |
| 506 | 502 |
| 507 // The first library asking for a short private name wins. | 503 // The first library asking for a short private name wins. |
| 508 LibraryElement owner = | 504 LibraryElement owner = |
| 509 shortPrivateNameOwners.putIfAbsent(text, () => library); | 505 shortPrivateNameOwners.putIfAbsent(originalName, () => library); |
| 510 | 506 |
| 511 if (owner == library) { | 507 if (owner == library) { |
| 512 return text; | 508 return originalName; |
| 513 } else { | 509 } else { |
| 514 // Make sure to return a private name that starts with _ so it | 510 // Make sure to return a private name that starts with _ so it |
| 515 // cannot clash with any public names. | 511 // cannot clash with any public names. |
| 516 // The name is still not guaranteed to be unique, since both the library | 512 // The name is still not guaranteed to be unique, since both the library |
| 517 // name and originalName could contain $ symbols. | 513 // name and originalName could contain $ symbols. |
| 518 String libraryName = _disambiguateGlobal(library); | 514 String libraryName = _disambiguateGlobal(library); |
| 519 return '_$libraryName\$${text}'; | 515 return '_$libraryName\$${originalName}'; |
| 520 } | 516 } |
| 521 } | 517 } |
| 522 | 518 |
| 523 String _proposeNameForConstructorBody(ConstructorBodyElement method) { | 519 String _proposeNameForConstructorBody(ConstructorBodyElement method) { |
| 524 String name = Elements.reconstructConstructorNameSourceString(method); | 520 String name = Elements.reconstructConstructorNameSourceString(method); |
| 525 // We include the method suffix on constructor bodies. It has no purpose, | 521 // We include the method suffix on constructor bodies. It has no purpose, |
| 526 // but this way it produces the same names as previous versions of the | 522 // but this way it produces the same names as previous versions of the |
| 527 // Namer class did. | 523 // Namer class did. |
| 528 List<String> suffix = callSuffixForSignature(method.functionSignature); | 524 List<String> suffix = callSuffixForSignature(method.functionSignature); |
| 529 return '$name\$${suffix.join(r'$')}'; | 525 return '$name\$${suffix.join(r'$')}'; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 555 // TODO(asgerf): Avoid clashes when named parameters contain $ symbols. | 551 // TODO(asgerf): Avoid clashes when named parameters contain $ symbols. |
| 556 return '$callPrefix\$${suffix.join(r'$')}'; | 552 return '$callPrefix\$${suffix.join(r'$')}'; |
| 557 } | 553 } |
| 558 | 554 |
| 559 /// The suffix list for the pattern: | 555 /// The suffix list for the pattern: |
| 560 /// | 556 /// |
| 561 /// $<N>$namedParam1...$namedParam<M> | 557 /// $<N>$namedParam1...$namedParam<M> |
| 562 /// | 558 /// |
| 563 /// This is used for the annotated names of `call`, and for the proposed name | 559 /// This is used for the annotated names of `call`, and for the proposed name |
| 564 /// for other instance methods. | 560 /// for other instance methods. |
| 565 List<String> callSuffixForStructure(CallStructure callStructure) { | 561 List<String> callSuffixForSelector(Selector selector) { |
| 566 List<String> suffixes = ['${callStructure.argumentCount}']; | 562 List<String> suffixes = ['${selector.argumentCount}']; |
| 567 suffixes.addAll(callStructure.getOrderedNamedArguments()); | 563 suffixes.addAll(selector.getOrderedNamedArguments()); |
| 568 return suffixes; | 564 return suffixes; |
| 569 } | 565 } |
| 570 | 566 |
| 571 /// The suffix list for the pattern: | 567 /// The suffix list for the pattern: |
| 572 /// | 568 /// |
| 573 /// $<N>$namedParam1...$namedParam<M> | 569 /// $<N>$namedParam1...$namedParam<M> |
| 574 /// | 570 /// |
| 575 /// This is used for the annotated names of `call`, and for the proposed name | 571 /// This is used for the annotated names of `call`, and for the proposed name |
| 576 /// for other instance methods. | 572 /// for other instance methods. |
| 577 List<String> callSuffixForSignature(FunctionSignature sig) { | 573 List<String> callSuffixForSignature(FunctionSignature sig) { |
| 578 List<String> suffixes = ['${sig.parameterCount}']; | 574 List<String> suffixes = ['${sig.parameterCount}']; |
| 579 if (sig.optionalParametersAreNamed) { | 575 if (sig.optionalParametersAreNamed) { |
| 580 for (FormalElement param in sig.orderedOptionalParameters) { | 576 for (FormalElement param in sig.orderedOptionalParameters) { |
| 581 suffixes.add(param.name); | 577 suffixes.add(param.name); |
| 582 } | 578 } |
| 583 } | 579 } |
| 584 return suffixes; | 580 return suffixes; |
| 585 } | 581 } |
| 586 | 582 |
| 587 /// Annotated name for the member being invoked by [selector]. | 583 /// Annotated name for the member being invoked by [selector]. |
| 588 String invocationName(Selector selector) { | 584 String invocationName(Selector selector) { |
| 585 LibraryElement library = selector.library; |
| 589 switch (selector.kind) { | 586 switch (selector.kind) { |
| 590 case SelectorKind.GETTER: | 587 case SelectorKind.GETTER: |
| 591 String disambiguatedName = _disambiguateMember(selector.memberName); | 588 String disambiguatedName = _disambiguateMember(library, selector.name); |
| 592 return deriveGetterName(disambiguatedName); | 589 return deriveGetterName(disambiguatedName); |
| 593 | 590 |
| 594 case SelectorKind.SETTER: | 591 case SelectorKind.SETTER: |
| 595 String disambiguatedName = _disambiguateMember(selector.memberName); | 592 String disambiguatedName = _disambiguateMember(library, selector.name); |
| 596 return deriveSetterName(disambiguatedName); | 593 return deriveSetterName(disambiguatedName); |
| 597 | 594 |
| 598 case SelectorKind.OPERATOR: | 595 case SelectorKind.OPERATOR: |
| 599 case SelectorKind.INDEX: | 596 case SelectorKind.INDEX: |
| 600 String operatorIdentifier = operatorNameToIdentifier(selector.name); | 597 String operatorIdentifier = operatorNameToIdentifier(selector.name); |
| 601 String disambiguatedName = _disambiguateOperator(operatorIdentifier); | 598 String disambiguatedName = _disambiguateOperator(operatorIdentifier); |
| 602 return disambiguatedName; // Operators are not annotated. | 599 return disambiguatedName; // Operators are not annotated. |
| 603 | 600 |
| 604 case SelectorKind.CALL: | 601 case SelectorKind.CALL: |
| 605 List<String> suffix = callSuffixForStructure(selector.callStructure); | 602 List<String> suffix = callSuffixForSelector(selector); |
| 606 if (selector.name == Compiler.CALL_OPERATOR_NAME) { | 603 if (selector.name == Compiler.CALL_OPERATOR_NAME) { |
| 607 // Derive the annotated name for this variant of 'call'. | 604 // Derive the annotated name for this variant of 'call'. |
| 608 return deriveCallMethodName(suffix); | 605 return deriveCallMethodName(suffix); |
| 609 } | 606 } |
| 610 String disambiguatedName = | 607 String disambiguatedName = |
| 611 _disambiguateMember(selector.memberName, suffix); | 608 _disambiguateMember(library, selector.name, suffix); |
| 612 return disambiguatedName; // Methods other than call are not annotated. | 609 return disambiguatedName; // Methods other than call are not annotated. |
| 613 | 610 |
| 614 default: | 611 default: |
| 615 compiler.internalError(compiler.currentElement, | 612 compiler.internalError(compiler.currentElement, |
| 616 'Unexpected selector kind: ${selector.kind}'); | 613 'Unexpected selector kind: ${selector.kind}'); |
| 617 return null; | 614 return null; |
| 618 } | 615 } |
| 619 } | 616 } |
| 620 | 617 |
| 621 /** | 618 /** |
| 622 * Returns the internal name used for an invocation mirror of this selector. | 619 * Returns the internal name used for an invocation mirror of this selector. |
| 623 */ | 620 */ |
| 624 String invocationMirrorInternalName(Selector selector) | 621 String invocationMirrorInternalName(Selector selector) |
| 625 => invocationName(selector); | 622 => invocationName(selector); |
| 626 | 623 |
| 627 /** | 624 /** |
| 628 * Returns the disambiguated name for the given field, used for constructing | 625 * Returns the disambiguated name for the given field, used for constructing |
| 629 * the getter and setter names. | 626 * the getter and setter names. |
| 630 */ | 627 */ |
| 631 String fieldAccessorName(FieldElement element) { | 628 String fieldAccessorName(Element element) { |
| 632 return element.isInstanceMember | 629 return element.isInstanceMember |
| 633 ? _disambiguateMember(element.memberName) | 630 ? _disambiguateMember(element.library, element.name) |
| 634 : _disambiguateGlobal(element); | 631 : _disambiguateGlobal(element); |
| 635 } | 632 } |
| 636 | 633 |
| 637 /** | 634 /** |
| 638 * Returns name of the JavaScript property used to store a static or instance | 635 * Returns name of the JavaScript property used to store a static or instance |
| 639 * field. | 636 * field. |
| 640 */ | 637 */ |
| 641 String fieldPropertyName(FieldElement element) { | 638 String fieldPropertyName(Element element) { |
| 642 return element.isInstanceMember | 639 return element.isInstanceMember |
| 643 ? instanceFieldPropertyName(element) | 640 ? instanceFieldPropertyName(element) |
| 644 : _disambiguateGlobal(element); | 641 : _disambiguateGlobal(element); |
| 645 } | 642 } |
| 646 | 643 |
| 647 /** | 644 /** |
| 648 * Returns name of the JavaScript property used to store the | 645 * Returns name of the JavaScript property used to store the |
| 649 * `readTypeVariable` function for the given type variable. | 646 * `readTypeVariable` function for the given type variable. |
| 650 */ | 647 */ |
| 651 String nameForReadTypeVariable(TypeVariableElement element) { | 648 String nameForReadTypeVariable(TypeVariableElement element) { |
| 652 return _disambiguateInternalMember(element, () => element.name); | 649 return _disambiguateInternalMember(element, () => element.name); |
| 653 } | 650 } |
| 654 | 651 |
| 655 /** | 652 /** |
| 656 * Returns a JavaScript property name used to store [element] on one | 653 * Returns a JavaScript property name used to store [element] on one |
| 657 * of the global objects. | 654 * of the global objects. |
| 658 * | 655 * |
| 659 * Should be used together with [globalObjectFor], which denotes the object | 656 * Should be used together with [globalObjectFor], which denotes the object |
| 660 * on which the returned property name should be used. | 657 * on which the returned property name should be used. |
| 661 */ | 658 */ |
| 662 String globalPropertyName(Element element) { | 659 String globalPropertyName(Element element) { |
| 663 return _disambiguateGlobal(element); | 660 return _disambiguateGlobal(element); |
| 664 } | 661 } |
| 665 | 662 |
| 666 /** | 663 /** |
| 667 * Returns the JavaScript property name used to store an instance field. | 664 * Returns the JavaScript property name used to store an instance field. |
| 668 */ | 665 */ |
| 669 String instanceFieldPropertyName(FieldElement element) { | 666 String instanceFieldPropertyName(Element element) { |
| 670 ClassElement enclosingClass = element.enclosingClass; | 667 ClassElement enclosingClass = element.enclosingClass; |
| 671 | 668 |
| 672 if (element.hasFixedBackendName) { | 669 if (element.hasFixedBackendName) { |
| 673 // Certain native fields must be given a specific name. Native names must | 670 // Certain native fields must be given a specific name. Native names must |
| 674 // not contain '$'. We rely on this to avoid clashes. | 671 // not contain '$'. We rely on this to avoid clashes. |
| 675 assert(enclosingClass.isNative && | 672 assert(enclosingClass.isNative && |
| 676 !element.fixedBackendName.contains(r'$')); | 673 !element.fixedBackendName.contains(r'$')); |
| 677 | 674 |
| 678 return element.fixedBackendName; | 675 return element.fixedBackendName; |
| 679 } | 676 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 699 if (classWorld.isUsedAsMixin(enclosingClass) || | 696 if (classWorld.isUsedAsMixin(enclosingClass) || |
| 700 _isShadowingSuperField(element) || | 697 _isShadowingSuperField(element) || |
| 701 _isUserClassExtendingNative(enclosingClass)) { | 698 _isUserClassExtendingNative(enclosingClass)) { |
| 702 String proposeName() => '${enclosingClass.name}_${element.name}'; | 699 String proposeName() => '${enclosingClass.name}_${element.name}'; |
| 703 return _disambiguateInternalMember(element, proposeName); | 700 return _disambiguateInternalMember(element, proposeName); |
| 704 } | 701 } |
| 705 | 702 |
| 706 // No superclass uses the disambiguated name as a property name, so we can | 703 // No superclass uses the disambiguated name as a property name, so we can |
| 707 // use it for this field. This generates nicer field names since otherwise | 704 // use it for this field. This generates nicer field names since otherwise |
| 708 // the field name would have to be mangled. | 705 // the field name would have to be mangled. |
| 709 return _disambiguateMember(element.memberName); | 706 return _disambiguateMember(element.library, element.name); |
| 710 } | 707 } |
| 711 | 708 |
| 712 bool _isShadowingSuperField(Element element) { | 709 bool _isShadowingSuperField(Element element) { |
| 713 return element.enclosingClass.hasFieldShadowedBy(element); | 710 return element.enclosingClass.hasFieldShadowedBy(element); |
| 714 } | 711 } |
| 715 | 712 |
| 716 /// True if [class_] is a non-native class that inherits from a native class. | 713 /// True if [class_] is a non-native class that inherits from a native class. |
| 717 bool _isUserClassExtendingNative(ClassElement class_) { | 714 bool _isUserClassExtendingNative(ClassElement class_) { |
| 718 return !class_.isNative && | 715 return !class_.isNative && |
| 719 Elements.isNativeOrExtendsNative(class_.superclass); | 716 Elements.isNativeOrExtendsNative(class_.superclass); |
| 720 } | 717 } |
| 721 | 718 |
| 722 /// Annotated name for the setter of [element]. | 719 /// Annotated name for the setter of [element]. |
| 723 String setterForElement(MemberElement element) { | 720 String setterForElement(Element element) { |
| 724 // We dynamically create setters from the field-name. The setter name must | 721 // We dynamically create setters from the field-name. The setter name must |
| 725 // therefore be derived from the instance field-name. | 722 // therefore be derived from the instance field-name. |
| 726 String name = _disambiguateMember(element.memberName); | 723 String name = _disambiguateMember(element.library, element.name); |
| 727 return deriveSetterName(name); | 724 return deriveSetterName(name); |
| 728 } | 725 } |
| 729 | 726 |
| 730 /// Annotated name for the setter of any member with [disambiguatedName]. | 727 /// Annotated name for the setter of any member with [disambiguatedName]. |
| 731 String deriveSetterName(String disambiguatedName) { | 728 String deriveSetterName(String disambiguatedName) { |
| 732 // We dynamically create setters from the field-name. The setter name must | 729 // We dynamically create setters from the field-name. The setter name must |
| 733 // therefore be derived from the instance field-name. | 730 // therefore be derived from the instance field-name. |
| 734 return '$setterPrefix$disambiguatedName'; | 731 return '$setterPrefix$disambiguatedName'; |
| 735 } | 732 } |
| 736 | 733 |
| 737 /// Annotated name for the setter of any member with [disambiguatedName]. | 734 /// Annotated name for the setter of any member with [disambiguatedName]. |
| 738 String deriveGetterName(String disambiguatedName) { | 735 String deriveGetterName(String disambiguatedName) { |
| 739 // We dynamically create getters from the field-name. The getter name must | 736 // We dynamically create getters from the field-name. The getter name must |
| 740 // therefore be derived from the instance field-name. | 737 // therefore be derived from the instance field-name. |
| 741 return '$getterPrefix$disambiguatedName'; | 738 return '$getterPrefix$disambiguatedName'; |
| 742 } | 739 } |
| 743 | 740 |
| 744 /// Annotated name for the getter of [element]. | 741 /// Annotated name for the getter of [element]. |
| 745 String getterForElement(MemberElement element) { | 742 String getterForElement(Element element) { |
| 746 // We dynamically create getters from the field-name. The getter name must | 743 // We dynamically create getters from the field-name. The getter name must |
| 747 // therefore be derived from the instance field-name. | 744 // therefore be derived from the instance field-name. |
| 748 String name = _disambiguateMember(element.memberName); | 745 String name = _disambiguateMember(element.library, element.name); |
| 749 return deriveGetterName(name); | 746 return deriveGetterName(name); |
| 750 } | 747 } |
| 751 | 748 |
| 752 /// Property name for the getter of an instance member with [originalName]. | 749 /// Property name for the getter of an instance member with [originalName] |
| 753 String getterForMember(Name originalName) { | 750 /// in [library]. |
| 754 String disambiguatedName = _disambiguateMember(originalName); | 751 /// |
| 752 /// [library] may be `null` if [originalName] is known to be public. |
| 753 String getterForMember(LibraryElement library, String originalName) { |
| 754 String disambiguatedName = _disambiguateMember(library, originalName); |
| 755 return deriveGetterName(disambiguatedName); | 755 return deriveGetterName(disambiguatedName); |
| 756 } | 756 } |
| 757 | 757 |
| 758 /// Property name for the getter or a public instance member with |
| 759 /// [originalName]. |
| 760 String getterForPublicMember(String originalName) { |
| 761 return getterForMember(null, originalName); |
| 762 } |
| 763 |
| 758 /// Disambiguated name for a compiler-owned global variable. | 764 /// Disambiguated name for a compiler-owned global variable. |
| 759 /// | 765 /// |
| 760 /// The resulting name is unique within the global-member namespace. | 766 /// The resulting name is unique within the global-member namespace. |
| 761 String _disambiguateInternalGlobal(String name) { | 767 String _disambiguateInternalGlobal(String name) { |
| 762 String newName = internalGlobals[name]; | 768 String newName = internalGlobals[name]; |
| 763 if (newName == null) { | 769 if (newName == null) { |
| 764 newName = getFreshName(name, usedGlobalNames, suggestedGlobalNames); | 770 newName = getFreshName(name, usedGlobalNames, suggestedGlobalNames); |
| 765 internalGlobals[name] = newName; | 771 internalGlobals[name] = newName; |
| 766 } | 772 } |
| 767 return newName; | 773 return newName; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 809 /// and setters) and as property name for storing methods and method stubs. | 815 /// and setters) and as property name for storing methods and method stubs. |
| 810 /// | 816 /// |
| 811 /// [suffixes] denote an extension of [originalName] to distiguish it from | 817 /// [suffixes] denote an extension of [originalName] to distiguish it from |
| 812 /// other members with that name. These are used to encode the arity and | 818 /// other members with that name. These are used to encode the arity and |
| 813 /// named parameters to a method. Disambiguating the same [originalName] with | 819 /// named parameters to a method. Disambiguating the same [originalName] with |
| 814 /// different [suffixes] will yield different disambiguated names. | 820 /// different [suffixes] will yield different disambiguated names. |
| 815 /// | 821 /// |
| 816 /// The resulting name, and its associated annotated names, are unique | 822 /// The resulting name, and its associated annotated names, are unique |
| 817 /// to the ([originalName], [suffixes]) pair within the instance-member | 823 /// to the ([originalName], [suffixes]) pair within the instance-member |
| 818 /// namespace. | 824 /// namespace. |
| 819 String _disambiguateMember(Name originalName, | 825 String _disambiguateMember(LibraryElement library, |
| 826 String originalName, |
| 820 [List<String> suffixes = const []]) { | 827 [List<String> suffixes = const []]) { |
| 828 // For private names, a library must be given. |
| 829 assert(isPublicName(originalName) || library != null); |
| 830 |
| 821 // Build a string encoding the library name, if the name is private. | 831 // Build a string encoding the library name, if the name is private. |
| 822 String libraryKey = originalName.isPrivate | 832 String libraryKey = isPrivateName(originalName) |
| 823 ? _disambiguateGlobal(originalName.library) | 833 ? _disambiguateGlobal(library) |
| 824 : ''; | 834 : ''; |
| 825 | 835 |
| 826 // In the unique key, separate the name parts by '@'. | 836 // In the unique key, separate the name parts by '@'. |
| 827 // This avoids clashes since the original names cannot contain that symbol. | 837 // This avoids clashes since the original names cannot contain that symbol. |
| 828 String key = '$libraryKey@${originalName.text}@${suffixes.join('@')}'; | 838 String key = '$libraryKey@$originalName@${suffixes.join('@')}'; |
| 829 String newName = userInstanceMembers[key]; | 839 String newName = userInstanceMembers[key]; |
| 830 if (newName == null) { | 840 if (newName == null) { |
| 831 String proposedName = privateName(originalName); | 841 String proposedName = privateName(library, originalName); |
| 832 if (!suffixes.isEmpty) { | 842 if (!suffixes.isEmpty) { |
| 833 // In the proposed name, separate the name parts by '$', because the | 843 // In the proposed name, separate the name parts by '$', because the |
| 834 // proposed name must be a valid identifier, but not necessarily unique. | 844 // proposed name must be a valid identifier, but not necessarily unique. |
| 835 proposedName += r'$' + suffixes.join(r'$'); | 845 proposedName += r'$' + suffixes.join(r'$'); |
| 836 } | 846 } |
| 837 newName = getFreshName(proposedName, | 847 newName = getFreshName(proposedName, |
| 838 usedInstanceNames, suggestedInstanceNames, | 848 usedInstanceNames, suggestedInstanceNames, |
| 839 sanitizeForAnnotations: true); | 849 sanitizeForAnnotations: true); |
| 840 userInstanceMembers[key] = newName; | 850 userInstanceMembers[key] = newName; |
| 841 } | 851 } |
| (...skipping 916 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1758 if (!first) { | 1768 if (!first) { |
| 1759 sb.write('_'); | 1769 sb.write('_'); |
| 1760 } | 1770 } |
| 1761 sb.write('_'); | 1771 sb.write('_'); |
| 1762 visit(parameter); | 1772 visit(parameter); |
| 1763 first = true; | 1773 first = true; |
| 1764 } | 1774 } |
| 1765 } | 1775 } |
| 1766 } | 1776 } |
| 1767 } | 1777 } |
| OLD | NEW |