Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: pkg/compiler/lib/src/js_backend/no_such_method_registry.dart

Issue 1678053002: Fix super noSuchMethod handling. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Fix. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 * Categorizes `noSuchMethod` implementations. 8 * Categorizes `noSuchMethod` implementations.
9 * 9 *
10 * If user code includes `noSuchMethod` implementations, type inference is 10 * If user code includes `noSuchMethod` implementations, type inference is
11 * hindered because (for instance) any selector where the type of the 11 * hindered because (for instance) any selector where the type of the
12 * receiver is not known all implementations of `noSuchMethod` must be taken 12 * receiver is not known all implementations of `noSuchMethod` must be taken
13 * into account when inferring the return type. 13 * into account when inferring the return type.
14 * 14 *
15 * The situation can be ameliorated with some heuristics for disregarding some 15 * The situation can be ameliorated with some heuristics for disregarding some
16 * `noSuchMethod` implementations during type inference. We can partition 16 * `noSuchMethod` implementations during type inference. We can partition
17 * `noSuchMethod` implementations into 3 categories. 17 * `noSuchMethod` implementations into 4 categories.
18 * 18 *
19 * Implementations in category A are the default implementations 19 * Implementations in category A are the default implementations
20 * `Object.noSuchMethod` and `Interceptor.noSuchMethod`. 20 * `Object.noSuchMethod` and `Interceptor.noSuchMethod`.
21 * 21 *
22 * Implementations in category B syntactically immediately throw, for example: 22 * Implementations in category B syntactically immediately throw, for example:
23 * 23 *
24 * noSuchMethod(x) => throw 'not implemented' 24 * noSuchMethod(x) => throw 'not implemented'
25 * 25 *
26 * Implementations that do not fall into category A or B are in category C. They 26 * Implementations in category C are not applicable, for example:
27 * are the only category of implementation that are considered during type 27 *
28 * noSuchMethod() { /* missing parameter */ }
29 * noSuchMethod(a, b) { /* too many parameters */ }
30 *
31 * Implementations that do not fall into category A, B or C are in category D.
32 * They are the only category of implementation that are considered during type
28 * inference. 33 * inference.
29 * 34 *
30 * Implementations that syntactically just forward to the super implementation, 35 * Implementations that syntactically just forward to the super implementation,
31 * for example: 36 * for example:
32 * 37 *
33 * noSuchMethod(x) => super.noSuchMethod(x); 38 * noSuchMethod(x) => super.noSuchMethod(x);
34 * 39 *
35 * are in the same category as the superclass implementation. This covers a 40 * are in the same category as the superclass implementation. This covers a
36 * common case, where users implement `noSuchMethod` with these dummy 41 * common case, where users implement `noSuchMethod` with these dummy
37 * implementations to avoid warnings. 42 * implementations to avoid warnings.
38 */ 43 */
39 class NoSuchMethodRegistry { 44 class NoSuchMethodRegistry {
40 /// The implementations that fall into category A, described above. 45 /// The implementations that fall into category A, described above.
41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); 46 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>();
42 /// The implementations that fall into category B, described above. 47 /// The implementations that fall into category B, described above.
43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); 48 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>();
44 /// The implementations that fall into category C, described above. 49 /// The implementations that fall into category C, described above.
50 final Set<FunctionElement> notApplicableImpls = new Set<FunctionElement>();
51 /// The implementations that fall into category D, described above.
45 final Set<FunctionElement> otherImpls = new Set<FunctionElement>(); 52 final Set<FunctionElement> otherImpls = new Set<FunctionElement>();
46 53
47 /// The implementations that fall into category C1 54 /// The implementations that fall into category D1
48 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>(); 55 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>();
49 /// The implementations that fall into category C2 56 /// The implementations that fall into category D2
50 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>(); 57 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>();
51 58
52 /// The implementations that have not yet been categorized. 59 /// The implementations that have not yet been categorized.
53 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>(); 60 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>();
54 61
55 final JavaScriptBackend _backend; 62 final JavaScriptBackend _backend;
56 final Compiler _compiler; 63 final Compiler _compiler;
57 64
58 NoSuchMethodRegistry(JavaScriptBackend backend) 65 NoSuchMethodRegistry(JavaScriptBackend backend)
59 : this._backend = backend, 66 : this._backend = backend,
60 this._compiler = backend.compiler; 67 this._compiler = backend.compiler;
61 68
62 DiagnosticReporter get reporter => _compiler.reporter; 69 DiagnosticReporter get reporter => _compiler.reporter;
63 70
64 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; 71 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty;
65 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; 72 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty;
66 73
67 void registerNoSuchMethod(FunctionElement noSuchMethodElement) { 74 void registerNoSuchMethod(FunctionElement noSuchMethodElement) {
68 _uncategorizedImpls.add(noSuchMethodElement); 75 _uncategorizedImpls.add(noSuchMethodElement);
69 } 76 }
70 77
71 void onQueueEmpty() { 78 void onQueueEmpty() {
72 _uncategorizedImpls.forEach(_categorizeImpl); 79 _uncategorizedImpls.forEach(_categorizeImpl);
73 _uncategorizedImpls.clear(); 80 _uncategorizedImpls.clear();
74 } 81 }
75 82
76 /// Now that type inference is complete, split category C into two 83 /// Now that type inference is complete, split category D into two
77 /// subcategories: C1, those that have no return type, and C2, those 84 /// subcategories: D1, those that have no return type, and D2, those
78 /// that have a return type. 85 /// that have a return type.
79 void onTypeInferenceComplete() { 86 void onTypeInferenceComplete() {
80 otherImpls.forEach(_subcategorizeOther); 87 otherImpls.forEach(_subcategorizeOther);
81 } 88 }
82 89
83 /// Emits a diagnostic 90 /// Emits a diagnostic
84 void emitDiagnostic() { 91 void emitDiagnostic() {
85 throwingImpls.forEach((e) { 92 throwingImpls.forEach((e) {
86 if (!_hasForwardingSyntax(e)) { 93 if (!_hasForwardingSyntax(e)) {
87 reporter.reportHintMessage( 94 reporter.reportHintMessage(
88 e, MessageKind.DIRECTLY_THROWING_NSM); 95 e, MessageKind.DIRECTLY_THROWING_NSM);
89 } 96 }
90 }); 97 });
91 complexNoReturnImpls.forEach((e) { 98 complexNoReturnImpls.forEach((e) {
92 if (!_hasForwardingSyntax(e)) { 99 if (!_hasForwardingSyntax(e)) {
93 reporter.reportHintMessage( 100 reporter.reportHintMessage(
94 e, MessageKind.COMPLEX_THROWING_NSM); 101 e, MessageKind.COMPLEX_THROWING_NSM);
95 } 102 }
96 }); 103 });
97 complexReturningImpls.forEach((e) { 104 complexReturningImpls.forEach((e) {
98 if (!_hasForwardingSyntax(e)) { 105 if (!_hasForwardingSyntax(e)) {
99 reporter.reportHintMessage( 106 reporter.reportHintMessage(
100 e, MessageKind.COMPLEX_RETURNING_NSM); 107 e, MessageKind.COMPLEX_RETURNING_NSM);
101 } 108 }
102 }); 109 });
103 } 110 }
104 111
105 /// Returns [true] if the given element is a complex [noSuchMethod] 112 /// Returns [true] if the given element is a complex [noSuchMethod]
106 /// implementation. An implementation is complex if it falls into 113 /// implementation. An implementation is complex if it falls into
107 /// category C, as described above. 114 /// category D, as described above.
108 bool isComplex(FunctionElement element) { 115 bool isComplex(FunctionElement element) {
109 assert(element.name == Identifiers.noSuchMethod_); 116 assert(element.name == Identifiers.noSuchMethod_);
110 return otherImpls.contains(element); 117 return otherImpls.contains(element);
111 } 118 }
112 119
113 _subcategorizeOther(FunctionElement element) { 120 _subcategorizeOther(FunctionElement element) {
114 TypeMask returnType = 121 TypeMask returnType =
115 _compiler.typesTask.getGuaranteedReturnTypeOfElement(element); 122 _compiler.typesTask.getGuaranteedReturnTypeOfElement(element);
116 if (returnType == const TypeMask.nonNullEmpty()) { 123 if (returnType == const TypeMask.nonNullEmpty()) {
117 complexNoReturnImpls.add(element); 124 complexNoReturnImpls.add(element);
118 } else { 125 } else {
119 complexReturningImpls.add(element); 126 complexReturningImpls.add(element);
120 } 127 }
121 } 128 }
122 129
123 NsmCategory _categorizeImpl(FunctionElement element) { 130 NsmCategory _categorizeImpl(FunctionElement element) {
124 assert(element.name == Identifiers.noSuchMethod_); 131 assert(element.name == Identifiers.noSuchMethod_);
125 if (defaultImpls.contains(element)) { 132 if (defaultImpls.contains(element)) {
126 return NsmCategory.DEFAULT; 133 return NsmCategory.DEFAULT;
127 } 134 }
128 if (throwingImpls.contains(element)) { 135 if (throwingImpls.contains(element)) {
129 return NsmCategory.THROWING; 136 return NsmCategory.THROWING;
130 } 137 }
131 if (otherImpls.contains(element)) { 138 if (otherImpls.contains(element)) {
132 return NsmCategory.OTHER; 139 return NsmCategory.OTHER;
133 } 140 }
141 if (notApplicableImpls.contains(element)) {
142 return NsmCategory.NOT_APPLICABLE;
143 }
134 if (!Selectors.noSuchMethod_.signatureApplies(element)) { 144 if (!Selectors.noSuchMethod_.signatureApplies(element)) {
135 otherImpls.add(element); 145 notApplicableImpls.add(element);
136 return NsmCategory.OTHER; 146 return NsmCategory.NOT_APPLICABLE;
137 } 147 }
138 if (_isDefaultNoSuchMethodImplementation(element)) { 148 if (_isDefaultNoSuchMethodImplementation(element)) {
139 defaultImpls.add(element); 149 defaultImpls.add(element);
140 return NsmCategory.DEFAULT; 150 return NsmCategory.DEFAULT;
141 } else if (_hasForwardingSyntax(element)) { 151 } else if (_hasForwardingSyntax(element)) {
142 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' 152 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);'
143 // then it is in the same category as the super call. 153 // then it is in the same category as the super call.
144 Element superCall = element.enclosingClass 154 Element superCall =
145 .lookupSuperByName(Selectors.noSuchMethod_.memberName); 155 element.enclosingClass.lookupSuperByName(Names.noSuchMethod_);
146 NsmCategory category = _categorizeImpl(superCall); 156 NsmCategory category = _categorizeImpl(superCall);
147 switch(category) { 157 switch(category) {
148 case NsmCategory.DEFAULT: 158 case NsmCategory.DEFAULT:
149 defaultImpls.add(element); 159 defaultImpls.add(element);
150 break; 160 break;
151 case NsmCategory.THROWING: 161 case NsmCategory.THROWING:
152 throwingImpls.add(element); 162 throwingImpls.add(element);
153 break; 163 break;
154 case NsmCategory.OTHER: 164 case NsmCategory.OTHER:
155 otherImpls.add(element); 165 otherImpls.add(element);
156 break; 166 break;
167 case NsmCategory.NOT_APPLICABLE:
168 // If the super method is not applicable, the call is redirected to
169 // `Object.noSuchMethod`.
170 defaultImpls.add(element);
171 category = NsmCategory.DEFAULT;
172 break;
157 } 173 }
158 return category; 174 return category;
159 } else if (_hasThrowingSyntax(element)) { 175 } else if (_hasThrowingSyntax(element)) {
160 throwingImpls.add(element); 176 throwingImpls.add(element);
161 return NsmCategory.THROWING; 177 return NsmCategory.THROWING;
162 } else { 178 } else {
163 otherImpls.add(element); 179 otherImpls.add(element);
164 return NsmCategory.OTHER; 180 return NsmCategory.OTHER;
165 } 181 }
166 } 182 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 body.statements.nodes.tail.isEmpty) { 233 body.statements.nodes.tail.isEmpty) {
218 if (body.statements.nodes.head is ExpressionStatement) { 234 if (body.statements.nodes.head is ExpressionStatement) {
219 ExpressionStatement stmt = body.statements.nodes.head; 235 ExpressionStatement stmt = body.statements.nodes.head;
220 return stmt.expression is Throw; 236 return stmt.expression is Throw;
221 } 237 }
222 } 238 }
223 return false; 239 return false;
224 } 240 }
225 } 241 }
226 242
227 enum NsmCategory { DEFAULT, THROWING, OTHER } 243 enum NsmCategory {
244 DEFAULT,
245 THROWING,
246 NOT_APPLICABLE,
247 OTHER,
248 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/js_backend.dart ('k') | pkg/compiler/lib/src/resolution/semantic_visitor.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698