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 import 'package:async_helper/async_helper.dart'; | 5 import 'package:async_helper/async_helper.dart'; |
6 import 'package:expect/expect.dart'; | 6 import 'package:expect/expect.dart'; |
7 import 'package:compiler/src/js_backend/backend_helpers.dart'; | 7 import 'package:compiler/src/js_backend/backend_helpers.dart'; |
8 import 'package:compiler/src/js_backend/js_backend.dart'; | 8 import 'package:compiler/src/js_backend/js_backend.dart'; |
9 import 'package:compiler/src/types/types.dart'; | 9 import 'package:compiler/src/types/types.dart'; |
10 import 'package:compiler/src/world.dart'; | 10 import 'package:compiler/src/world.dart'; |
(...skipping 29 matching lines...) Expand all Loading... |
40 TypeMask jsString; | 40 TypeMask jsString; |
41 TypeMask jsStringOrNull; | 41 TypeMask jsStringOrNull; |
42 TypeMask jsArrayOrNull; | 42 TypeMask jsArrayOrNull; |
43 TypeMask jsMutableArrayOrNull; | 43 TypeMask jsMutableArrayOrNull; |
44 TypeMask jsFixedArrayOrNull; | 44 TypeMask jsFixedArrayOrNull; |
45 TypeMask jsExtendableArrayOrNull; | 45 TypeMask jsExtendableArrayOrNull; |
46 TypeMask jsUnmodifiableArrayOrNull; | 46 TypeMask jsUnmodifiableArrayOrNull; |
47 TypeMask jsIndexableOrNull; | 47 TypeMask jsIndexableOrNull; |
48 TypeMask jsInterceptorOrNull; | 48 TypeMask jsInterceptorOrNull; |
49 | 49 |
50 | |
51 class Pair { | 50 class Pair { |
52 final first; | 51 final first; |
53 final second; | 52 final second; |
54 Pair(this.first, this.second); | 53 Pair(this.first, this.second); |
55 int get hashCode => first.hashCode * 47 + second.hashCode; | 54 int get hashCode => first.hashCode * 47 + second.hashCode; |
56 bool operator==(Pair other) => | 55 bool operator ==(Pair other) => |
57 identical(first, other.first) && identical(second, other.second); | 56 identical(first, other.first) && identical(second, other.second); |
58 } | 57 } |
59 | 58 |
60 class RuleSet { | 59 class RuleSet { |
61 final name; | 60 final name; |
62 final operate; | 61 final operate; |
63 final Set typesSeen = new Set(); | 62 final Set typesSeen = new Set(); |
64 final Set pairsSeen = new Set(); | 63 final Set pairsSeen = new Set(); |
65 | 64 |
66 RuleSet(this.name, this.operate); | 65 RuleSet(this.name, this.operate); |
67 | 66 |
68 void rule(type1, type2, result) { | 67 void rule(type1, type2, result) { |
69 typesSeen..add(type1)..add(type2); | 68 typesSeen..add(type1)..add(type2); |
70 var pair1 = new Pair(type1, type2); | 69 var pair1 = new Pair(type1, type2); |
71 var pair2 = new Pair(type2, type1); | 70 var pair2 = new Pair(type2, type1); |
72 if (pairsSeen.contains(pair1)) { | 71 if (pairsSeen.contains(pair1)) { |
73 Expect.isFalse(true, 'Redundant rule ($type1, $type2, ...)'); | 72 Expect.isFalse(true, 'Redundant rule ($type1, $type2, ...)'); |
74 } | 73 } |
75 pairsSeen..add(pair1)..add(pair2); | 74 pairsSeen..add(pair1)..add(pair2); |
76 | 75 |
77 var r1 = operate(type1, type2); | 76 var r1 = operate(type1, type2); |
78 var r2 = operate(type2, type1); | 77 var r2 = operate(type2, type1); |
79 Expect.equals(result, r1, | 78 Expect.equals(result, r1, "Unexpected result of $name($type1,$type2)"); |
80 "Unexpected result of $name($type1,$type2)"); | |
81 Expect.equals(r1, r2, 'Symmetry violation of $name($type1,$type2)'); | 79 Expect.equals(r1, r2, 'Symmetry violation of $name($type1,$type2)'); |
82 } | 80 } |
83 | 81 |
84 void check(type1, type2, predicate) { | 82 void check(type1, type2, predicate) { |
85 typesSeen..add(type1)..add(type2); | 83 typesSeen..add(type1)..add(type2); |
86 var pair = new Pair(type1, type2); | 84 var pair = new Pair(type1, type2); |
87 pairsSeen..add(pair); | 85 pairsSeen..add(pair); |
88 var result = operate(type1, type2); | 86 var result = operate(type1, type2); |
89 Expect.isTrue(predicate(result)); | 87 Expect.isTrue(predicate(result)); |
90 } | 88 } |
91 | 89 |
92 void validateCoverage() { | 90 void validateCoverage() { |
93 for (var type1 in typesSeen) { | 91 for (var type1 in typesSeen) { |
94 for (var type2 in typesSeen) { | 92 for (var type2 in typesSeen) { |
95 var pair = new Pair(type1, type2); | 93 var pair = new Pair(type1, type2); |
96 if (!pairsSeen.contains(pair)) { | 94 if (!pairsSeen.contains(pair)) { |
97 Expect.isTrue(false, 'Missing rule: $name($type1, $type2)'); | 95 Expect.isTrue(false, 'Missing rule: $name($type1, $type2)'); |
98 } | 96 } |
99 } | 97 } |
100 } | 98 } |
101 } | 99 } |
102 } | 100 } |
103 | 101 |
104 void testUnion(MockCompiler compiler) { | 102 void testUnion(MockCompiler compiler) { |
105 RuleSet ruleSet = new RuleSet('union', | 103 RuleSet ruleSet = new RuleSet( |
106 (t1, t2) => simplify(t1.union(t2, compiler.world), compiler)); | 104 'union', (t1, t2) => simplify(t1.union(t2, compiler.world), compiler)); |
107 rule(type1, type2, result) => ruleSet.rule(type1, type2, result); | 105 rule(type1, type2, result) => ruleSet.rule(type1, type2, result); |
108 check(type1, type2, predicate) => ruleSet.check(type1, type2, predicate); | 106 check(type1, type2, predicate) => ruleSet.check(type1, type2, predicate); |
109 | 107 |
110 rule(emptyType, emptyType, emptyType); | 108 rule(emptyType, emptyType, emptyType); |
111 rule(emptyType, dynamicType, dynamicType); | 109 rule(emptyType, dynamicType, dynamicType); |
112 rule(emptyType, jsBoolean, jsBoolean); | 110 rule(emptyType, jsBoolean, jsBoolean); |
113 rule(emptyType, jsNumber, jsNumber); | 111 rule(emptyType, jsNumber, jsNumber); |
114 rule(emptyType, jsInteger, jsInteger); | 112 rule(emptyType, jsInteger, jsInteger); |
115 rule(emptyType, jsDouble, jsDouble); | 113 rule(emptyType, jsDouble, jsDouble); |
116 rule(emptyType, jsIndexable, jsIndexable); | 114 rule(emptyType, jsIndexable, jsIndexable); |
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 check(nonPrimitive2, nullType, (type) => type == nonPrimitive2.nullable()); | 406 check(nonPrimitive2, nullType, (type) => type == nonPrimitive2.nullable()); |
409 check(nullType, nonPrimitive1, (type) => type == nonPrimitive1.nullable()); | 407 check(nullType, nonPrimitive1, (type) => type == nonPrimitive1.nullable()); |
410 check(nullType, nonPrimitive2, (type) => type == nonPrimitive2.nullable()); | 408 check(nullType, nonPrimitive2, (type) => type == nonPrimitive2.nullable()); |
411 | 409 |
412 ruleSet.validateCoverage(); | 410 ruleSet.validateCoverage(); |
413 } | 411 } |
414 | 412 |
415 void testIntersection(MockCompiler compiler) { | 413 void testIntersection(MockCompiler compiler) { |
416 JavaScriptBackend backend = compiler.backend; | 414 JavaScriptBackend backend = compiler.backend; |
417 BackendHelpers helpers = backend.helpers; | 415 BackendHelpers helpers = backend.helpers; |
418 RuleSet ruleSet = new RuleSet('intersection', | 416 RuleSet ruleSet = new RuleSet( |
419 (t1, t2) => t1.intersection(t2, compiler.world)); | 417 'intersection', (t1, t2) => t1.intersection(t2, compiler.world)); |
420 rule(type1, type2, result) => ruleSet.rule(type1, type2, result); | 418 rule(type1, type2, result) => ruleSet.rule(type1, type2, result); |
421 | 419 |
422 rule(emptyType, emptyType, emptyType); | 420 rule(emptyType, emptyType, emptyType); |
423 rule(emptyType, dynamicType, emptyType); | 421 rule(emptyType, dynamicType, emptyType); |
424 rule(emptyType, jsBoolean, emptyType); | 422 rule(emptyType, jsBoolean, emptyType); |
425 rule(emptyType, jsNumber, emptyType); | 423 rule(emptyType, jsNumber, emptyType); |
426 rule(emptyType, jsInteger, emptyType); | 424 rule(emptyType, jsInteger, emptyType); |
427 rule(emptyType, jsDouble, emptyType); | 425 rule(emptyType, jsDouble, emptyType); |
428 rule(emptyType, jsIndexable, emptyType); | 426 rule(emptyType, jsIndexable, emptyType); |
429 rule(emptyType, jsString, emptyType); | 427 rule(emptyType, jsString, emptyType); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
549 rule(jsDouble, jsFixedArray, emptyType); | 547 rule(jsDouble, jsFixedArray, emptyType); |
550 | 548 |
551 rule(jsIndexable, jsIndexable, jsIndexable); | 549 rule(jsIndexable, jsIndexable, jsIndexable); |
552 rule(jsIndexable, jsString, jsString); | 550 rule(jsIndexable, jsString, jsString); |
553 rule(jsIndexable, jsReadableArray, jsReadableArray); | 551 rule(jsIndexable, jsReadableArray, jsReadableArray); |
554 rule(jsIndexable, jsMutableArray, jsMutableArray); | 552 rule(jsIndexable, jsMutableArray, jsMutableArray); |
555 rule(jsIndexable, jsExtendableArray, jsExtendableArray); | 553 rule(jsIndexable, jsExtendableArray, jsExtendableArray); |
556 rule(jsIndexable, jsUnmodifiableArray, jsUnmodifiableArray); | 554 rule(jsIndexable, jsUnmodifiableArray, jsUnmodifiableArray); |
557 rule(jsIndexable, nonPrimitive1, emptyType); | 555 rule(jsIndexable, nonPrimitive1, emptyType); |
558 rule(jsIndexable, nonPrimitive2, emptyType); | 556 rule(jsIndexable, nonPrimitive2, emptyType); |
559 rule(jsIndexable, potentialArray, new TypeMask.nonNullSubtype( | 557 rule(jsIndexable, potentialArray, |
560 helpers.jsArrayClass, compiler.world)); | 558 new TypeMask.nonNullSubtype(helpers.jsArrayClass, compiler.world)); |
561 rule(jsIndexable, potentialString, new TypeMask.nonNullSubtype( | 559 rule(jsIndexable, potentialString, |
562 helpers.jsStringClass, compiler.world)); | 560 new TypeMask.nonNullSubtype(helpers.jsStringClass, compiler.world)); |
563 rule(jsIndexable, jsBooleanOrNull, emptyType); | 561 rule(jsIndexable, jsBooleanOrNull, emptyType); |
564 rule(jsIndexable, jsNumberOrNull, emptyType); | 562 rule(jsIndexable, jsNumberOrNull, emptyType); |
565 rule(jsIndexable, jsIntegerOrNull, emptyType); | 563 rule(jsIndexable, jsIntegerOrNull, emptyType); |
566 rule(jsIndexable, jsDoubleOrNull, emptyType); | 564 rule(jsIndexable, jsDoubleOrNull, emptyType); |
567 rule(jsIndexable, jsStringOrNull, jsString); | 565 rule(jsIndexable, jsStringOrNull, jsString); |
568 rule(jsIndexable, nullType, emptyType); | 566 rule(jsIndexable, nullType, emptyType); |
569 rule(jsIndexable, jsFixedArray, jsFixedArray); | 567 rule(jsIndexable, jsFixedArray, jsFixedArray); |
570 | 568 |
571 rule(jsString, jsString, jsString); | 569 rule(jsString, jsString, jsString); |
572 rule(jsString, jsReadableArray, emptyType); | 570 rule(jsString, jsReadableArray, emptyType); |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 | 715 |
718 rule(nullType, nullType, nullType); | 716 rule(nullType, nullType, nullType); |
719 rule(nullType, jsFixedArray, emptyType); | 717 rule(nullType, jsFixedArray, emptyType); |
720 | 718 |
721 rule(jsFixedArray, jsFixedArray, jsFixedArray); | 719 rule(jsFixedArray, jsFixedArray, jsFixedArray); |
722 | 720 |
723 ruleSet.validateCoverage(); | 721 ruleSet.validateCoverage(); |
724 } | 722 } |
725 | 723 |
726 void testRegressions(MockCompiler compiler) { | 724 void testRegressions(MockCompiler compiler) { |
727 TypeMask nonNullPotentialString = new TypeMask.nonNullSubtype( | 725 TypeMask nonNullPotentialString = |
728 patternClass, compiler.world); | 726 new TypeMask.nonNullSubtype(patternClass, compiler.world); |
729 Expect.equals( | 727 Expect.equals(potentialString, |
730 potentialString, jsStringOrNull.union( | 728 jsStringOrNull.union(nonNullPotentialString, compiler.world)); |
731 nonNullPotentialString, compiler.world)); | |
732 } | 729 } |
733 | 730 |
734 void main() { | 731 void main() { |
735 asyncTest(() async { | 732 asyncTest(() async { |
736 MockCompiler compiler = new MockCompiler.internal(); | 733 MockCompiler compiler = new MockCompiler.internal(); |
737 await compiler.init(""" | 734 await compiler.init(""" |
738 class PatternImpl implements Pattern {} | 735 class PatternImpl implements Pattern {} |
739 """); | 736 """); |
740 JavaScriptBackend backend = compiler.backend; | 737 JavaScriptBackend backend = compiler.backend; |
741 BackendHelpers helpers = backend.helpers; | 738 BackendHelpers helpers = backend.helpers; |
742 World world = compiler.world; | 739 World world = compiler.world; |
743 helpers.interceptorsLibrary.forEachLocalMember((element) { | 740 helpers.interceptorsLibrary.forEachLocalMember((element) { |
744 if (element.isClass) { | 741 if (element.isClass) { |
745 element.ensureResolved(compiler.resolution); | 742 element.ensureResolved(compiler.resolution); |
746 backend.registerInstantiatedType( | 743 backend.registerInstantiatedType(element.rawType, |
747 element.rawType, | 744 compiler.enqueuer.resolution, compiler.globalDependencies); |
748 compiler.enqueuer.resolution, | |
749 compiler.globalDependencies); | |
750 } | 745 } |
751 }); | 746 }); |
752 ClassElement patternImplClass = compiler.mainApp.find('PatternImpl'); | 747 ClassElement patternImplClass = compiler.mainApp.find('PatternImpl'); |
753 patternImplClass.ensureResolved(compiler.resolution); | 748 patternImplClass.ensureResolved(compiler.resolution); |
754 | 749 |
755 backend.registerInstantiatedType( | 750 backend.registerInstantiatedType(compiler.coreTypes.mapType(), |
756 compiler.coreTypes.mapType(), | 751 compiler.enqueuer.resolution, compiler.globalDependencies); |
757 compiler.enqueuer.resolution, | 752 backend.registerInstantiatedType(compiler.coreTypes.functionType, |
758 compiler.globalDependencies); | 753 compiler.enqueuer.resolution, compiler.globalDependencies); |
759 backend.registerInstantiatedType( | 754 backend.registerInstantiatedType(patternImplClass.rawType, |
760 compiler.coreTypes.functionType, | 755 compiler.enqueuer.resolution, compiler.globalDependencies); |
761 compiler.enqueuer.resolution, | |
762 compiler.globalDependencies); | |
763 backend.registerInstantiatedType( | |
764 patternImplClass.rawType, | |
765 compiler.enqueuer.resolution, | |
766 compiler.globalDependencies); | |
767 compiler.world.populate(); | 756 compiler.world.populate(); |
768 | 757 |
769 // Grab hold of a supertype for String so we can produce potential | 758 // Grab hold of a supertype for String so we can produce potential |
770 // string types. | 759 // string types. |
771 patternClass = compiler.commonElements.coreLibrary.find('Pattern'); | 760 patternClass = compiler.commonElements.coreLibrary.find('Pattern'); |
772 | 761 |
773 nonPrimitive1 = new TypeMask.nonNullSubtype( | 762 nonPrimitive1 = |
774 compiler.coreClasses.mapClass, world); | 763 new TypeMask.nonNullSubtype(compiler.coreClasses.mapClass, world); |
775 nonPrimitive2 = new TypeMask.nonNullSubtype( | 764 nonPrimitive2 = |
776 compiler.coreClasses.functionClass, world); | 765 new TypeMask.nonNullSubtype(compiler.coreClasses.functionClass, world); |
777 potentialArray = new TypeMask.subtype( | 766 potentialArray = |
778 compiler.coreClasses.listClass, world); | 767 new TypeMask.subtype(compiler.coreClasses.listClass, world); |
779 potentialString = new TypeMask.subtype(patternClass, world); | 768 potentialString = new TypeMask.subtype(patternClass, world); |
780 jsInterceptor = new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, | 769 jsInterceptor = |
781 world); | 770 new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, world); |
782 jsArrayOrNull = new TypeMask.subclass(helpers.jsArrayClass, world); | 771 jsArrayOrNull = new TypeMask.subclass(helpers.jsArrayClass, world); |
783 jsReadableArray = new TypeMask.nonNullSubclass(helpers.jsArrayClass, | 772 jsReadableArray = new TypeMask.nonNullSubclass(helpers.jsArrayClass, world); |
784 world); | 773 jsMutableArrayOrNull = |
785 jsMutableArrayOrNull = new TypeMask.subclass(helpers.jsMutableArrayClass, | 774 new TypeMask.subclass(helpers.jsMutableArrayClass, world); |
786 world); | 775 jsMutableArray = |
787 jsMutableArray = new TypeMask.nonNullSubclass(helpers.jsMutableArrayClass, | 776 new TypeMask.nonNullSubclass(helpers.jsMutableArrayClass, world); |
788 world); | |
789 jsFixedArrayOrNull = new TypeMask.exact(helpers.jsFixedArrayClass, world); | 777 jsFixedArrayOrNull = new TypeMask.exact(helpers.jsFixedArrayClass, world); |
790 jsFixedArray = new TypeMask.nonNullExact(helpers.jsFixedArrayClass, world); | 778 jsFixedArray = new TypeMask.nonNullExact(helpers.jsFixedArrayClass, world); |
791 jsExtendableArrayOrNull = new TypeMask.exact(helpers.jsExtendableArrayClass, | 779 jsExtendableArrayOrNull = |
792 world); | 780 new TypeMask.exact(helpers.jsExtendableArrayClass, world); |
793 jsExtendableArray = new TypeMask.nonNullExact( | 781 jsExtendableArray = |
794 helpers.jsExtendableArrayClass, world); | 782 new TypeMask.nonNullExact(helpers.jsExtendableArrayClass, world); |
795 jsUnmodifiableArrayOrNull = | 783 jsUnmodifiableArrayOrNull = |
796 new TypeMask.exact(helpers.jsUnmodifiableArrayClass, world); | 784 new TypeMask.exact(helpers.jsUnmodifiableArrayClass, world); |
797 jsUnmodifiableArray = | 785 jsUnmodifiableArray = |
798 new TypeMask.nonNullExact(helpers.jsUnmodifiableArrayClass, world); | 786 new TypeMask.nonNullExact(helpers.jsUnmodifiableArrayClass, world); |
799 jsIndexableOrNull = new TypeMask.subtype(helpers.jsIndexableClass, world); | 787 jsIndexableOrNull = new TypeMask.subtype(helpers.jsIndexableClass, world); |
800 jsIndexable = new TypeMask.nonNullSubtype(helpers.jsIndexableClass, world); | 788 jsIndexable = new TypeMask.nonNullSubtype(helpers.jsIndexableClass, world); |
801 jsInterceptorOrNull = new TypeMask.subclass(helpers.jsInterceptorClass, | 789 jsInterceptorOrNull = |
802 world); | 790 new TypeMask.subclass(helpers.jsInterceptorClass, world); |
803 jsStringOrNull = new TypeMask.exact(helpers.jsStringClass, world); | 791 jsStringOrNull = new TypeMask.exact(helpers.jsStringClass, world); |
804 jsString = new TypeMask.nonNullExact(helpers.jsStringClass, world); | 792 jsString = new TypeMask.nonNullExact(helpers.jsStringClass, world); |
805 jsBoolean = new TypeMask.nonNullExact(helpers.jsBoolClass, world); | 793 jsBoolean = new TypeMask.nonNullExact(helpers.jsBoolClass, world); |
806 jsNumber = new TypeMask.nonNullSubclass(helpers.jsNumberClass, world); | 794 jsNumber = new TypeMask.nonNullSubclass(helpers.jsNumberClass, world); |
807 jsInteger = new TypeMask.nonNullExact(helpers.jsIntClass, world); | 795 jsInteger = new TypeMask.nonNullExact(helpers.jsIntClass, world); |
808 jsDouble = new TypeMask.nonNullExact(helpers.jsDoubleClass, world); | 796 jsDouble = new TypeMask.nonNullExact(helpers.jsDoubleClass, world); |
809 jsBooleanOrNull = new TypeMask.exact(helpers.jsBoolClass, world); | 797 jsBooleanOrNull = new TypeMask.exact(helpers.jsBoolClass, world); |
810 jsNumberOrNull = new TypeMask.subclass(helpers.jsNumberClass, world); | 798 jsNumberOrNull = new TypeMask.subclass(helpers.jsNumberClass, world); |
811 jsIntegerOrNull = new TypeMask.exact(helpers.jsIntClass, world); | 799 jsIntegerOrNull = new TypeMask.exact(helpers.jsIntClass, world); |
812 jsDoubleOrNull = new TypeMask.exact(helpers.jsDoubleClass, world); | 800 jsDoubleOrNull = new TypeMask.exact(helpers.jsDoubleClass, world); |
813 nullType = const TypeMask.empty(); | 801 nullType = const TypeMask.empty(); |
814 objectType = new TypeMask.nonNullSubclass( | 802 objectType = |
815 compiler.coreClasses.objectClass, world); | 803 new TypeMask.nonNullSubclass(compiler.coreClasses.objectClass, world); |
816 emptyType = const TypeMask.nonNullEmpty(); | 804 emptyType = const TypeMask.nonNullEmpty(); |
817 dynamicType = new TypeMask.subclass( | 805 dynamicType = |
818 compiler.coreClasses.objectClass, world); | 806 new TypeMask.subclass(compiler.coreClasses.objectClass, world); |
819 | 807 |
820 Expect.notEquals(emptyType, nonPrimitive1, | 808 Expect.notEquals( |
821 "nonPrimitive1 expected to be non-empty."); | 809 emptyType, nonPrimitive1, "nonPrimitive1 expected to be non-empty."); |
822 Expect.notEquals(jsStringOrNull, potentialString, | 810 Expect.notEquals(jsStringOrNull, potentialString, |
823 "potentialString expected not to be exact JSString"); | 811 "potentialString expected not to be exact JSString"); |
824 Expect.notEquals(jsArrayOrNull, potentialArray, | 812 Expect.notEquals(jsArrayOrNull, potentialArray, |
825 "potentialArray expected not to be JSArray subclass"); | 813 "potentialArray expected not to be JSArray subclass"); |
826 | 814 |
827 testUnion(compiler); | 815 testUnion(compiler); |
828 testIntersection(compiler); | 816 testIntersection(compiler); |
829 testRegressions(compiler); | 817 testRegressions(compiler); |
830 }); | 818 }); |
831 } | 819 } |
OLD | NEW |