| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of js_backend; | |
| 6 | |
| 7 /** | |
| 8 * Categorizes `noSuchMethod` implementations. | |
| 9 * | |
| 10 * If user code includes `noSuchMethod` implementations, type inference is | |
| 11 * hindered because (for instance) any selector where the type of the | |
| 12 * receiver is not known all implementations of `noSuchMethod` must be taken | |
| 13 * into account when inferring the return type. | |
| 14 * | |
| 15 * The situation can be ameliorated with some heuristics for disregarding some | |
| 16 * `noSuchMethod` implementations during type inference. We can partition | |
| 17 * `noSuchMethod` implementations into 3 categories. | |
| 18 * | |
| 19 * Implementations in category A are the default implementations | |
| 20 * `Object.noSuchMethod` and `Interceptor.noSuchMethod`. | |
| 21 * | |
| 22 * Implementations in category B syntactically immediately throw, for example: | |
| 23 * | |
| 24 * noSuchMethod(x) => throw 'not implemented' | |
| 25 * | |
| 26 * Implementations that do not fall into category A or B are in category C. They | |
| 27 * are the only category of implementation that are considered during type | |
| 28 * inference. | |
| 29 * | |
| 30 * Implementations that syntactically just forward to the super implementation, | |
| 31 * for example: | |
| 32 * | |
| 33 * noSuchMethod(x) => super.noSuchMethod(x); | |
| 34 * | |
| 35 * are in the same category as the superclass implementation. This covers a | |
| 36 * common case, where users implement `noSuchMethod` with these dummy | |
| 37 * implementations to avoid warnings. | |
| 38 */ | |
| 39 class NoSuchMethodRegistry { | |
| 40 /// The implementations that fall into category A, described above. | |
| 41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); | |
| 42 /// The implementations that fall into category B, described above. | |
| 43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); | |
| 44 /// The implementations that fall into category C, described above. | |
| 45 final Set<Element> otherImpls = new Set<Element>(); | |
| 46 | |
| 47 /// The implementations that have not yet been categorized. | |
| 48 final Set<Element> uncategorizedImpls = new Set<Element>(); | |
| 49 | |
| 50 final JavaScriptBackend backend; | |
| 51 final Compiler compiler; | |
| 52 | |
| 53 NoSuchMethodRegistry(JavaScriptBackend backend) | |
| 54 : this.backend = backend, | |
| 55 this.compiler = backend.compiler; | |
| 56 | |
| 57 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; | |
| 58 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; | |
| 59 | |
| 60 void registerNoSuchMethod(Element noSuchMethodElement) { | |
| 61 uncategorizedImpls.add(noSuchMethodElement); | |
| 62 } | |
| 63 | |
| 64 void onQueueEmpty() { | |
| 65 uncategorizedImpls.forEach(_categorizeImpl); | |
| 66 uncategorizedImpls.clear(); | |
| 67 } | |
| 68 | |
| 69 NsmCategory _categorizeImpl(Element noSuchMethodElement) { | |
| 70 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); | |
| 71 if (defaultImpls.contains(noSuchMethodElement)) { | |
| 72 return NsmCategory.DEFAULT; | |
| 73 } | |
| 74 if (throwingImpls.contains(noSuchMethodElement)) { | |
| 75 return NsmCategory.THROWING; | |
| 76 } | |
| 77 if (otherImpls.contains(noSuchMethodElement)) { | |
| 78 return NsmCategory.OTHER; | |
| 79 } | |
| 80 if (noSuchMethodElement is! FunctionElement || | |
| 81 !compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { | |
| 82 otherImpls.add(noSuchMethodElement); | |
| 83 return NsmCategory.OTHER; | |
| 84 } | |
| 85 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; | |
| 86 if (backend.isDefaultNoSuchMethodImplementation(noSuchMethodFunc)) { | |
| 87 defaultImpls.add(noSuchMethodFunc); | |
| 88 return NsmCategory.DEFAULT; | |
| 89 } else if (hasForwardingSyntax(noSuchMethodFunc)) { | |
| 90 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' | |
| 91 // then it is in the same category as the super call. | |
| 92 Element superCall = noSuchMethodFunc.enclosingClass | |
| 93 .lookupSuperSelector(compiler.noSuchMethodSelector); | |
| 94 NsmCategory category = _categorizeImpl(superCall); | |
| 95 switch(category) { | |
| 96 case NsmCategory.DEFAULT: | |
| 97 defaultImpls.add(noSuchMethodFunc); | |
| 98 break; | |
| 99 case NsmCategory.THROWING: | |
| 100 throwingImpls.add(noSuchMethodFunc); | |
| 101 break; | |
| 102 case NsmCategory.OTHER: | |
| 103 otherImpls.add(noSuchMethodFunc); | |
| 104 break; | |
| 105 } | |
| 106 return category; | |
| 107 } else if (isThrowing(noSuchMethodFunc)) { | |
| 108 throwingImpls.add(noSuchMethodFunc); | |
| 109 return NsmCategory.THROWING; | |
| 110 } else { | |
| 111 otherImpls.add(noSuchMethodFunc); | |
| 112 return NsmCategory.OTHER; | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 bool hasForwardingSyntax(FunctionElement element) { | |
| 117 String param = element.parameters.single.name; | |
| 118 Statement body = element.node.body; | |
| 119 Expression expr; | |
| 120 if (body is Return && body.isArrowBody) { | |
| 121 expr = body.expression; | |
| 122 } else if (body is Block && | |
| 123 !body.statements.isEmpty && | |
| 124 body.statements.nodes.tail.isEmpty) { | |
| 125 Statement stmt = body.statements.nodes.head; | |
| 126 if (stmt is Return && stmt.hasExpression) { | |
| 127 expr = stmt.expression; | |
| 128 } | |
| 129 } | |
| 130 if (expr is Send && | |
| 131 expr.isSuperCall && | |
| 132 expr.selector is Identifier && | |
| 133 (expr.selector as Identifier).source == Compiler.NO_SUCH_METHOD) { | |
| 134 var arg = expr.arguments.head; | |
| 135 if (arg is Send && | |
| 136 arg.argumentsNode == null && | |
| 137 arg.receiver == null && | |
| 138 arg.selector is Identifier && | |
| 139 arg.selector.source == param) { | |
| 140 return true; | |
| 141 } | |
| 142 } | |
| 143 return false; | |
| 144 } | |
| 145 | |
| 146 bool isThrowing(FunctionElement element) { | |
| 147 Statement body = element.node.body; | |
| 148 if (body is Return && body.isArrowBody) { | |
| 149 if (body.expression is Throw) { | |
| 150 return true; | |
| 151 } | |
| 152 } else if (body is Block && | |
| 153 !body.statements.isEmpty && | |
| 154 body.statements.nodes.tail.isEmpty) { | |
| 155 if (body.statements.nodes.head is Throw) { | |
| 156 return true; | |
| 157 } | |
| 158 } | |
| 159 return false; | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 enum NsmCategory { DEFAULT, THROWING, OTHER } | |
| OLD | NEW |