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

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

Issue 2782583003: Extract the AST specific part of NoSuchMethodRegistry (Closed)
Patch Set: Created 3 years, 8 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
« no previous file with comments | « pkg/compiler/lib/src/js_backend/backend_helpers.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import '../common.dart'; 5 import '../common.dart';
6 import '../common/names.dart' show Identifiers, Names, Selectors; 6 import '../common/names.dart' show Identifiers, Names, Selectors;
7 import '../compiler.dart' show Compiler; 7 import '../common_elements.dart';
8 import '../elements/elements.dart'; 8 import '../elements/elements.dart';
9 import '../types/types.dart';
9 import '../tree/tree.dart'; 10 import '../tree/tree.dart';
10 import 'backend.dart'; 11 import 'backend_helpers.dart';
11 12
12 /** 13 /**
13 * Categorizes `noSuchMethod` implementations. 14 * Categorizes `noSuchMethod` implementations.
14 * 15 *
15 * If user code includes `noSuchMethod` implementations, type inference is 16 * If user code includes `noSuchMethod` implementations, type inference is
16 * hindered because (for instance) any selector where the type of the 17 * hindered because (for instance) any selector where the type of the
17 * receiver is not known all implementations of `noSuchMethod` must be taken 18 * receiver is not known all implementations of `noSuchMethod` must be taken
18 * into account when inferring the return type. 19 * into account when inferring the return type.
19 * 20 *
20 * The situation can be ameliorated with some heuristics for disregarding some 21 * The situation can be ameliorated with some heuristics for disregarding some
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 62
62 /// The implementations that fall into category D1 63 /// The implementations that fall into category D1
63 final Set<MethodElement> complexNoReturnImpls = new Set<MethodElement>(); 64 final Set<MethodElement> complexNoReturnImpls = new Set<MethodElement>();
64 65
65 /// The implementations that fall into category D2 66 /// The implementations that fall into category D2
66 final Set<MethodElement> complexReturningImpls = new Set<MethodElement>(); 67 final Set<MethodElement> complexReturningImpls = new Set<MethodElement>();
67 68
68 /// The implementations that have not yet been categorized. 69 /// The implementations that have not yet been categorized.
69 final Set<MethodElement> _uncategorizedImpls = new Set<MethodElement>(); 70 final Set<MethodElement> _uncategorizedImpls = new Set<MethodElement>();
70 71
71 final JavaScriptBackend _backend; 72 final BackendHelpers _helpers;
72 final Compiler _compiler; 73 final NoSuchMethodResolver _resolver;
73 74
74 NoSuchMethodRegistry(JavaScriptBackend backend) 75 NoSuchMethodRegistry(this._helpers, this._resolver);
75 : this._backend = backend,
76 this._compiler = backend.compiler;
77
78 DiagnosticReporter get reporter => _compiler.reporter;
79 76
80 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; 77 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty;
81 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; 78 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty;
82 79
83 void registerNoSuchMethod(MethodElement noSuchMethodElement) { 80 void registerNoSuchMethod(MethodElement noSuchMethodElement) {
84 _uncategorizedImpls.add(noSuchMethodElement); 81 _uncategorizedImpls.add(noSuchMethodElement);
85 } 82 }
86 83
87 void onQueueEmpty() { 84 void onQueueEmpty() {
88 _uncategorizedImpls.forEach(_categorizeImpl); 85 _uncategorizedImpls.forEach(_categorizeImpl);
89 _uncategorizedImpls.clear(); 86 _uncategorizedImpls.clear();
90 } 87 }
91 88
92 /// Now that type inference is complete, split category D into two 89 /// Now that type inference is complete, split category D into two
93 /// subcategories: D1, those that have no return type, and D2, those 90 /// subcategories: D1, those that have no return type, and D2, those
94 /// that have a return type. 91 /// that have a return type.
95 void onTypeInferenceComplete() { 92 void onTypeInferenceComplete(GlobalTypeInferenceResults results) {
96 otherImpls.forEach(_subcategorizeOther); 93 otherImpls.forEach((MethodElement element) {
94 if (results.resultOf(element).throwsAlways) {
95 complexNoReturnImpls.add(element);
96 } else {
97 complexReturningImpls.add(element);
98 }
99 });
97 } 100 }
98 101
99 /// Emits a diagnostic 102 /// Emits a diagnostic
100 void emitDiagnostic() { 103 void emitDiagnostic(DiagnosticReporter reporter) {
101 throwingImpls.forEach((e) { 104 throwingImpls.forEach((e) {
102 if (!_hasForwardingSyntax(e)) { 105 if (!_resolver.hasForwardingSyntax(e)) {
103 reporter.reportHintMessage(e, MessageKind.DIRECTLY_THROWING_NSM); 106 reporter.reportHintMessage(e, MessageKind.DIRECTLY_THROWING_NSM);
104 } 107 }
105 }); 108 });
106 complexNoReturnImpls.forEach((e) { 109 complexNoReturnImpls.forEach((e) {
107 if (!_hasForwardingSyntax(e)) { 110 if (!_resolver.hasForwardingSyntax(e)) {
108 reporter.reportHintMessage(e, MessageKind.COMPLEX_THROWING_NSM); 111 reporter.reportHintMessage(e, MessageKind.COMPLEX_THROWING_NSM);
109 } 112 }
110 }); 113 });
111 complexReturningImpls.forEach((e) { 114 complexReturningImpls.forEach((e) {
112 if (!_hasForwardingSyntax(e)) { 115 if (!_resolver.hasForwardingSyntax(e)) {
113 reporter.reportHintMessage(e, MessageKind.COMPLEX_RETURNING_NSM); 116 reporter.reportHintMessage(e, MessageKind.COMPLEX_RETURNING_NSM);
114 } 117 }
115 }); 118 });
116 } 119 }
117 120
118 /// Returns [true] if the given element is a complex [noSuchMethod] 121 /// Returns [true] if the given element is a complex [noSuchMethod]
119 /// implementation. An implementation is complex if it falls into 122 /// implementation. An implementation is complex if it falls into
120 /// category D, as described above. 123 /// category D, as described above.
121 bool isComplex(MethodElement element) { 124 bool isComplex(MethodElement element) {
122 assert(element.name == Identifiers.noSuchMethod_); 125 assert(element.name == Identifiers.noSuchMethod_);
123 return otherImpls.contains(element); 126 return otherImpls.contains(element);
124 } 127 }
125 128
126 _subcategorizeOther(MethodElement element) {
127 if (_compiler.globalInference.results.resultOf(element).throwsAlways) {
128 complexNoReturnImpls.add(element);
129 } else {
130 complexReturningImpls.add(element);
131 }
132 }
133
134 NsmCategory _categorizeImpl(MethodElement element) { 129 NsmCategory _categorizeImpl(MethodElement element) {
135 assert(element.name == Identifiers.noSuchMethod_); 130 assert(element.name == Identifiers.noSuchMethod_);
136 if (defaultImpls.contains(element)) { 131 if (defaultImpls.contains(element)) {
137 return NsmCategory.DEFAULT; 132 return NsmCategory.DEFAULT;
138 } 133 }
139 if (throwingImpls.contains(element)) { 134 if (throwingImpls.contains(element)) {
140 return NsmCategory.THROWING; 135 return NsmCategory.THROWING;
141 } 136 }
142 if (otherImpls.contains(element)) { 137 if (otherImpls.contains(element)) {
143 return NsmCategory.OTHER; 138 return NsmCategory.OTHER;
144 } 139 }
145 if (notApplicableImpls.contains(element)) { 140 if (notApplicableImpls.contains(element)) {
146 return NsmCategory.NOT_APPLICABLE; 141 return NsmCategory.NOT_APPLICABLE;
147 } 142 }
148 if (!Selectors.noSuchMethod_.signatureApplies(element)) { 143 if (!Selectors.noSuchMethod_.signatureApplies(element)) {
149 notApplicableImpls.add(element); 144 notApplicableImpls.add(element);
150 return NsmCategory.NOT_APPLICABLE; 145 return NsmCategory.NOT_APPLICABLE;
151 } 146 }
152 if (isDefaultNoSuchMethodImplementation(element)) { 147 if (_helpers.isDefaultNoSuchMethodImplementation(element)) {
153 defaultImpls.add(element); 148 defaultImpls.add(element);
154 return NsmCategory.DEFAULT; 149 return NsmCategory.DEFAULT;
155 } else if (_hasForwardingSyntax(element)) { 150 } else if (_resolver.hasForwardingSyntax(element)) {
156 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' 151 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);'
157 // then it is in the same category as the super call. 152 // then it is in the same category as the super call.
158 Element superCall = 153 Element superCall =
159 element.enclosingClass.lookupSuperByName(Names.noSuchMethod_); 154 element.enclosingClass.lookupSuperByName(Names.noSuchMethod_);
160 NsmCategory category = _categorizeImpl(superCall); 155 NsmCategory category = _categorizeImpl(superCall);
161 switch (category) { 156 switch (category) {
162 case NsmCategory.DEFAULT: 157 case NsmCategory.DEFAULT:
163 defaultImpls.add(element); 158 defaultImpls.add(element);
164 break; 159 break;
165 case NsmCategory.THROWING: 160 case NsmCategory.THROWING:
166 throwingImpls.add(element); 161 throwingImpls.add(element);
167 break; 162 break;
168 case NsmCategory.OTHER: 163 case NsmCategory.OTHER:
169 otherImpls.add(element); 164 otherImpls.add(element);
170 break; 165 break;
171 case NsmCategory.NOT_APPLICABLE: 166 case NsmCategory.NOT_APPLICABLE:
172 // If the super method is not applicable, the call is redirected to 167 // If the super method is not applicable, the call is redirected to
173 // `Object.noSuchMethod`. 168 // `Object.noSuchMethod`.
174 defaultImpls.add(element); 169 defaultImpls.add(element);
175 category = NsmCategory.DEFAULT; 170 category = NsmCategory.DEFAULT;
176 break; 171 break;
177 } 172 }
178 return category; 173 return category;
179 } else if (_hasThrowingSyntax(element)) { 174 } else if (_resolver.hasThrowingSyntax(element)) {
180 throwingImpls.add(element); 175 throwingImpls.add(element);
181 return NsmCategory.THROWING; 176 return NsmCategory.THROWING;
182 } else { 177 } else {
183 otherImpls.add(element); 178 otherImpls.add(element);
184 return NsmCategory.OTHER; 179 return NsmCategory.OTHER;
185 } 180 }
186 } 181 }
182 }
187 183
188 bool isDefaultNoSuchMethodImplementation(MethodElement element) { 184 enum NsmCategory {
189 ClassElement classElement = element.enclosingClass; 185 DEFAULT,
190 return classElement == _compiler.commonElements.objectClass || 186 THROWING,
191 classElement == _backend.helpers.jsInterceptorClass || 187 NOT_APPLICABLE,
192 classElement == _backend.helpers.jsNullClass; 188 OTHER,
193 } 189 }
194 190
195 bool _hasForwardingSyntax(MethodElement element) { 191 /// Interface for determining the form of a `noSuchMethod` implementation.
192 abstract class NoSuchMethodResolver {
193 /// Computes whether [method] is of the form
194 ///
195 /// noSuchMethod(i) => super.noSuchMethod(i);
196 ///
197 bool hasForwardingSyntax(MethodElement method);
198
199 /// Computes whether [method] is of the form
200 ///
201 /// noSuchMethod(i) => throw new Error();
202 ///
203 bool hasThrowingSyntax(MethodElement method);
204 }
205
206 /// AST-based implementation of [NoSuchMethodResolver].
207 class NoSuchMethodResolverImpl implements NoSuchMethodResolver {
208 bool hasForwardingSyntax(MethodElement element) {
196 // At this point we know that this is signature-compatible with 209 // At this point we know that this is signature-compatible with
197 // Object.noSuchMethod, but it may have more than one argument as long as 210 // Object.noSuchMethod, but it may have more than one argument as long as
198 // it only has one required argument. 211 // it only has one required argument.
199 if (!element.hasResolvedAst) { 212 if (!element.hasResolvedAst) {
200 // TODO(johnniwinther): Why do we see unresolved elements here? 213 // TODO(johnniwinther): Why do we see unresolved elements here?
201 return false; 214 return false;
202 } 215 }
203 ResolvedAst resolvedAst = element.resolvedAst; 216 ResolvedAst resolvedAst = element.resolvedAst;
204 if (resolvedAst.kind != ResolvedAstKind.PARSED) { 217 if (resolvedAst.kind != ResolvedAstKind.PARSED) {
205 return false; 218 return false;
(...skipping 29 matching lines...) Expand all
235 arg.argumentsNode == null && 248 arg.argumentsNode == null &&
236 arg.receiver == null && 249 arg.receiver == null &&
237 arg.selector is Identifier && 250 arg.selector is Identifier &&
238 arg.selector.source == param) { 251 arg.selector.source == param) {
239 return true; 252 return true;
240 } 253 }
241 } 254 }
242 return false; 255 return false;
243 } 256 }
244 257
245 bool _hasThrowingSyntax(MethodElement element) { 258 bool hasThrowingSyntax(MethodElement element) {
246 if (!element.hasResolvedAst) { 259 if (!element.hasResolvedAst) {
247 // TODO(johnniwinther): Why do we see unresolved elements here? 260 // TODO(johnniwinther): Why do we see unresolved elements here?
248 return false; 261 return false;
249 } 262 }
250 ResolvedAst resolvedAst = element.resolvedAst; 263 ResolvedAst resolvedAst = element.resolvedAst;
251 if (resolvedAst.kind != ResolvedAstKind.PARSED) { 264 if (resolvedAst.kind != ResolvedAstKind.PARSED) {
252 return false; 265 return false;
253 } 266 }
254 Statement body = resolvedAst.body; 267 Statement body = resolvedAst.body;
255 if (body is Return && body.isArrowBody) { 268 if (body is Return && body.isArrowBody) {
256 if (body.expression is Throw) { 269 if (body.expression is Throw) {
257 return true; 270 return true;
258 } 271 }
259 } else if (body is Block && 272 } else if (body is Block &&
260 !body.statements.isEmpty && 273 !body.statements.isEmpty &&
261 body.statements.nodes.tail.isEmpty) { 274 body.statements.nodes.tail.isEmpty) {
262 if (body.statements.nodes.head is ExpressionStatement) { 275 if (body.statements.nodes.head is ExpressionStatement) {
263 ExpressionStatement stmt = body.statements.nodes.head; 276 ExpressionStatement stmt = body.statements.nodes.head;
264 return stmt.expression is Throw; 277 return stmt.expression is Throw;
265 } 278 }
266 } 279 }
267 return false; 280 return false;
268 } 281 }
269 } 282 }
270
271 enum NsmCategory {
272 DEFAULT,
273 THROWING,
274 NOT_APPLICABLE,
275 OTHER,
276 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/backend_helpers.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698