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