| OLD | NEW |
| 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_elements.dart' show CommonElements; | 6 import '../common_elements.dart' show CommonElements; |
| 7 import '../common/names.dart' show Identifiers, Names, Selectors; | 7 import '../common/names.dart' show Identifiers, Selectors; |
| 8 import '../elements/elements.dart'; | |
| 9 import '../elements/entities.dart'; | 8 import '../elements/entities.dart'; |
| 10 import '../types/types.dart'; | 9 import '../types/types.dart'; |
| 11 import '../tree/tree.dart'; | |
| 12 | 10 |
| 13 /** | 11 /** |
| 14 * Categorizes `noSuchMethod` implementations. | 12 * Categorizes `noSuchMethod` implementations. |
| 15 * | 13 * |
| 16 * If user code includes `noSuchMethod` implementations, type inference is | 14 * If user code includes `noSuchMethod` implementations, type inference is |
| 17 * hindered because (for instance) any selector where the type of the | 15 * hindered because (for instance) any selector where the type of the |
| 18 * receiver is not known all implementations of `noSuchMethod` must be taken | 16 * receiver is not known all implementations of `noSuchMethod` must be taken |
| 19 * into account when inferring the return type. | 17 * into account when inferring the return type. |
| 20 * | 18 * |
| 21 * The situation can be ameliorated with some heuristics for disregarding some | 19 * The situation can be ameliorated with some heuristics for disregarding some |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 final Set<FunctionEntity> complexReturningImpls = new Set<FunctionEntity>(); | 65 final Set<FunctionEntity> complexReturningImpls = new Set<FunctionEntity>(); |
| 68 | 66 |
| 69 /// The implementations that have not yet been categorized. | 67 /// The implementations that have not yet been categorized. |
| 70 final Set<FunctionEntity> _uncategorizedImpls = new Set<FunctionEntity>(); | 68 final Set<FunctionEntity> _uncategorizedImpls = new Set<FunctionEntity>(); |
| 71 | 69 |
| 72 final CommonElements _commonElements; | 70 final CommonElements _commonElements; |
| 73 final NoSuchMethodResolver _resolver; | 71 final NoSuchMethodResolver _resolver; |
| 74 | 72 |
| 75 NoSuchMethodRegistry(this._commonElements, this._resolver); | 73 NoSuchMethodRegistry(this._commonElements, this._resolver); |
| 76 | 74 |
| 75 NoSuchMethodResolver get internalResolverForTesting => _resolver; |
| 76 |
| 77 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; | 77 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; |
| 78 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; | 78 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; |
| 79 | 79 |
| 80 void registerNoSuchMethod(FunctionEntity noSuchMethodElement) { | 80 void registerNoSuchMethod(FunctionEntity noSuchMethodElement) { |
| 81 _uncategorizedImpls.add(noSuchMethodElement); | 81 _uncategorizedImpls.add(noSuchMethodElement); |
| 82 } | 82 } |
| 83 | 83 |
| 84 void onQueueEmpty() { | 84 void onQueueEmpty() { |
| 85 _uncategorizedImpls.forEach(_categorizeImpl); | 85 _uncategorizedImpls.forEach(_categorizeImpl); |
| 86 _uncategorizedImpls.clear(); | 86 _uncategorizedImpls.clear(); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 | 199 |
| 200 /// Computes whether [method] is of the form | 200 /// Computes whether [method] is of the form |
| 201 /// | 201 /// |
| 202 /// noSuchMethod(i) => throw new Error(); | 202 /// noSuchMethod(i) => throw new Error(); |
| 203 /// | 203 /// |
| 204 bool hasThrowingSyntax(FunctionEntity method); | 204 bool hasThrowingSyntax(FunctionEntity method); |
| 205 | 205 |
| 206 /// Returns the `noSuchMethod` that [method] overrides. | 206 /// Returns the `noSuchMethod` that [method] overrides. |
| 207 FunctionEntity getSuperNoSuchMethod(FunctionEntity method); | 207 FunctionEntity getSuperNoSuchMethod(FunctionEntity method); |
| 208 } | 208 } |
| 209 | |
| 210 /// AST-based implementation of [NoSuchMethodResolver]. | |
| 211 class NoSuchMethodResolverImpl implements NoSuchMethodResolver { | |
| 212 bool hasForwardingSyntax(MethodElement element) { | |
| 213 // At this point we know that this is signature-compatible with | |
| 214 // Object.noSuchMethod, but it may have more than one argument as long as | |
| 215 // it only has one required argument. | |
| 216 if (!element.hasResolvedAst) { | |
| 217 // TODO(johnniwinther): Why do we see unresolved elements here? | |
| 218 return false; | |
| 219 } | |
| 220 ResolvedAst resolvedAst = element.resolvedAst; | |
| 221 if (resolvedAst.kind != ResolvedAstKind.PARSED) { | |
| 222 return false; | |
| 223 } | |
| 224 String param = element.parameters.first.name; | |
| 225 Statement body = resolvedAst.body; | |
| 226 Expression expr; | |
| 227 if (body is Return && body.isArrowBody) { | |
| 228 expr = body.expression; | |
| 229 } else if (body is Block && | |
| 230 !body.statements.isEmpty && | |
| 231 body.statements.nodes.tail.isEmpty) { | |
| 232 Statement stmt = body.statements.nodes.head; | |
| 233 if (stmt is Return && stmt.hasExpression) { | |
| 234 expr = stmt.expression; | |
| 235 } | |
| 236 } | |
| 237 if (expr is Send && expr.isTypeCast) { | |
| 238 Send sendExpr = expr; | |
| 239 var typeAnnotation = sendExpr.typeAnnotationFromIsCheckOrCast; | |
| 240 var typeName = typeAnnotation.asNominalTypeAnnotation()?.typeName; | |
| 241 if (typeName is Identifier && typeName.source == "dynamic") { | |
| 242 expr = sendExpr.receiver; | |
| 243 } | |
| 244 } | |
| 245 if (expr is Send && | |
| 246 expr.isSuperCall && | |
| 247 expr.selector is Identifier && | |
| 248 (expr.selector as Identifier).source == Identifiers.noSuchMethod_) { | |
| 249 var arg = expr.arguments.head; | |
| 250 if (expr.arguments.tail.isEmpty && | |
| 251 arg is Send && | |
| 252 arg.argumentsNode == null && | |
| 253 arg.receiver == null && | |
| 254 arg.selector is Identifier && | |
| 255 arg.selector.source == param) { | |
| 256 return true; | |
| 257 } | |
| 258 } | |
| 259 return false; | |
| 260 } | |
| 261 | |
| 262 bool hasThrowingSyntax(MethodElement element) { | |
| 263 if (!element.hasResolvedAst) { | |
| 264 // TODO(johnniwinther): Why do we see unresolved elements here? | |
| 265 return false; | |
| 266 } | |
| 267 ResolvedAst resolvedAst = element.resolvedAst; | |
| 268 if (resolvedAst.kind != ResolvedAstKind.PARSED) { | |
| 269 return false; | |
| 270 } | |
| 271 Statement body = resolvedAst.body; | |
| 272 if (body is Return && body.isArrowBody) { | |
| 273 if (body.expression is Throw) { | |
| 274 return true; | |
| 275 } | |
| 276 } else if (body is Block && | |
| 277 !body.statements.isEmpty && | |
| 278 body.statements.nodes.tail.isEmpty) { | |
| 279 if (body.statements.nodes.head is ExpressionStatement) { | |
| 280 ExpressionStatement stmt = body.statements.nodes.head; | |
| 281 return stmt.expression is Throw; | |
| 282 } | |
| 283 } | |
| 284 return false; | |
| 285 } | |
| 286 | |
| 287 MethodElement getSuperNoSuchMethod(MethodElement method) { | |
| 288 return method.enclosingClass.lookupSuperByName(Names.noSuchMethod_); | |
| 289 } | |
| 290 } | |
| OLD | NEW |