| 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 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 4 categories. | 17 * `noSuchMethod` implementations into 3 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 in category C are not applicable, for example: | 26 * Implementations that do not fall into category A or B are in category C. They |
| 27 * | 27 * are the only category of implementation that are considered during type |
| 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 | |
| 33 * inference. | 28 * inference. |
| 34 * | 29 * |
| 35 * Implementations that syntactically just forward to the super implementation, | 30 * Implementations that syntactically just forward to the super implementation, |
| 36 * for example: | 31 * for example: |
| 37 * | 32 * |
| 38 * noSuchMethod(x) => super.noSuchMethod(x); | 33 * noSuchMethod(x) => super.noSuchMethod(x); |
| 39 * | 34 * |
| 40 * are in the same category as the superclass implementation. This covers a | 35 * are in the same category as the superclass implementation. This covers a |
| 41 * common case, where users implement `noSuchMethod` with these dummy | 36 * common case, where users implement `noSuchMethod` with these dummy |
| 42 * implementations to avoid warnings. | 37 * implementations to avoid warnings. |
| 43 */ | 38 */ |
| 44 class NoSuchMethodRegistry { | 39 class NoSuchMethodRegistry { |
| 45 /// The implementations that fall into category A, described above. | 40 /// The implementations that fall into category A, described above. |
| 46 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); | 41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); |
| 47 /// The implementations that fall into category B, described above. | 42 /// The implementations that fall into category B, described above. |
| 48 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); | 43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); |
| 49 /// The implementations that fall into category C, described above. | 44 /// 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. | |
| 52 final Set<FunctionElement> otherImpls = new Set<FunctionElement>(); | 45 final Set<FunctionElement> otherImpls = new Set<FunctionElement>(); |
| 53 | 46 |
| 54 /// The implementations that fall into category D1 | 47 /// The implementations that fall into category C1 |
| 55 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>(); | 48 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>(); |
| 56 /// The implementations that fall into category D2 | 49 /// The implementations that fall into category C2 |
| 57 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>(); | 50 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>(); |
| 58 | 51 |
| 59 /// The implementations that have not yet been categorized. | 52 /// The implementations that have not yet been categorized. |
| 60 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>(); | 53 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>(); |
| 61 | 54 |
| 62 final JavaScriptBackend _backend; | 55 final JavaScriptBackend _backend; |
| 63 final Compiler _compiler; | 56 final Compiler _compiler; |
| 64 | 57 |
| 65 NoSuchMethodRegistry(JavaScriptBackend backend) | 58 NoSuchMethodRegistry(JavaScriptBackend backend) |
| 66 : this._backend = backend, | 59 : this._backend = backend, |
| 67 this._compiler = backend.compiler; | 60 this._compiler = backend.compiler; |
| 68 | 61 |
| 69 DiagnosticReporter get reporter => _compiler.reporter; | 62 DiagnosticReporter get reporter => _compiler.reporter; |
| 70 | 63 |
| 71 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; | 64 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; |
| 72 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; | 65 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; |
| 73 | 66 |
| 74 void registerNoSuchMethod(FunctionElement noSuchMethodElement) { | 67 void registerNoSuchMethod(FunctionElement noSuchMethodElement) { |
| 75 _uncategorizedImpls.add(noSuchMethodElement); | 68 _uncategorizedImpls.add(noSuchMethodElement); |
| 76 } | 69 } |
| 77 | 70 |
| 78 void onQueueEmpty() { | 71 void onQueueEmpty() { |
| 79 _uncategorizedImpls.forEach(_categorizeImpl); | 72 _uncategorizedImpls.forEach(_categorizeImpl); |
| 80 _uncategorizedImpls.clear(); | 73 _uncategorizedImpls.clear(); |
| 81 } | 74 } |
| 82 | 75 |
| 83 /// Now that type inference is complete, split category D into two | 76 /// Now that type inference is complete, split category C into two |
| 84 /// subcategories: D1, those that have no return type, and D2, those | 77 /// subcategories: C1, those that have no return type, and C2, those |
| 85 /// that have a return type. | 78 /// that have a return type. |
| 86 void onTypeInferenceComplete() { | 79 void onTypeInferenceComplete() { |
| 87 otherImpls.forEach(_subcategorizeOther); | 80 otherImpls.forEach(_subcategorizeOther); |
| 88 } | 81 } |
| 89 | 82 |
| 90 /// Emits a diagnostic | 83 /// Emits a diagnostic |
| 91 void emitDiagnostic() { | 84 void emitDiagnostic() { |
| 92 throwingImpls.forEach((e) { | 85 throwingImpls.forEach((e) { |
| 93 if (!_hasForwardingSyntax(e)) { | 86 if (!_hasForwardingSyntax(e)) { |
| 94 reporter.reportHintMessage( | 87 reporter.reportHintMessage( |
| 95 e, MessageKind.DIRECTLY_THROWING_NSM); | 88 e, MessageKind.DIRECTLY_THROWING_NSM); |
| 96 } | 89 } |
| 97 }); | 90 }); |
| 98 complexNoReturnImpls.forEach((e) { | 91 complexNoReturnImpls.forEach((e) { |
| 99 if (!_hasForwardingSyntax(e)) { | 92 if (!_hasForwardingSyntax(e)) { |
| 100 reporter.reportHintMessage( | 93 reporter.reportHintMessage( |
| 101 e, MessageKind.COMPLEX_THROWING_NSM); | 94 e, MessageKind.COMPLEX_THROWING_NSM); |
| 102 } | 95 } |
| 103 }); | 96 }); |
| 104 complexReturningImpls.forEach((e) { | 97 complexReturningImpls.forEach((e) { |
| 105 if (!_hasForwardingSyntax(e)) { | 98 if (!_hasForwardingSyntax(e)) { |
| 106 reporter.reportHintMessage( | 99 reporter.reportHintMessage( |
| 107 e, MessageKind.COMPLEX_RETURNING_NSM); | 100 e, MessageKind.COMPLEX_RETURNING_NSM); |
| 108 } | 101 } |
| 109 }); | 102 }); |
| 110 } | 103 } |
| 111 | 104 |
| 112 /// Returns [true] if the given element is a complex [noSuchMethod] | 105 /// Returns [true] if the given element is a complex [noSuchMethod] |
| 113 /// implementation. An implementation is complex if it falls into | 106 /// implementation. An implementation is complex if it falls into |
| 114 /// category D, as described above. | 107 /// category C, as described above. |
| 115 bool isComplex(FunctionElement element) { | 108 bool isComplex(FunctionElement element) { |
| 116 assert(element.name == Identifiers.noSuchMethod_); | 109 assert(element.name == Identifiers.noSuchMethod_); |
| 117 return otherImpls.contains(element); | 110 return otherImpls.contains(element); |
| 118 } | 111 } |
| 119 | 112 |
| 120 _subcategorizeOther(FunctionElement element) { | 113 _subcategorizeOther(FunctionElement element) { |
| 121 TypeMask returnType = | 114 TypeMask returnType = |
| 122 _compiler.typesTask.getGuaranteedReturnTypeOfElement(element); | 115 _compiler.typesTask.getGuaranteedReturnTypeOfElement(element); |
| 123 if (returnType == const TypeMask.nonNullEmpty()) { | 116 if (returnType == const TypeMask.nonNullEmpty()) { |
| 124 complexNoReturnImpls.add(element); | 117 complexNoReturnImpls.add(element); |
| 125 } else { | 118 } else { |
| 126 complexReturningImpls.add(element); | 119 complexReturningImpls.add(element); |
| 127 } | 120 } |
| 128 } | 121 } |
| 129 | 122 |
| 130 NsmCategory _categorizeImpl(FunctionElement element) { | 123 NsmCategory _categorizeImpl(FunctionElement element) { |
| 131 assert(element.name == Identifiers.noSuchMethod_); | 124 assert(element.name == Identifiers.noSuchMethod_); |
| 132 if (defaultImpls.contains(element)) { | 125 if (defaultImpls.contains(element)) { |
| 133 return NsmCategory.DEFAULT; | 126 return NsmCategory.DEFAULT; |
| 134 } | 127 } |
| 135 if (throwingImpls.contains(element)) { | 128 if (throwingImpls.contains(element)) { |
| 136 return NsmCategory.THROWING; | 129 return NsmCategory.THROWING; |
| 137 } | 130 } |
| 138 if (otherImpls.contains(element)) { | 131 if (otherImpls.contains(element)) { |
| 139 return NsmCategory.OTHER; | 132 return NsmCategory.OTHER; |
| 140 } | 133 } |
| 141 if (notApplicableImpls.contains(element)) { | |
| 142 return NsmCategory.NOT_APPLICABLE; | |
| 143 } | |
| 144 if (!Selectors.noSuchMethod_.signatureApplies(element)) { | 134 if (!Selectors.noSuchMethod_.signatureApplies(element)) { |
| 145 notApplicableImpls.add(element); | 135 otherImpls.add(element); |
| 146 return NsmCategory.NOT_APPLICABLE; | 136 return NsmCategory.OTHER; |
| 147 } | 137 } |
| 148 if (_isDefaultNoSuchMethodImplementation(element)) { | 138 if (_isDefaultNoSuchMethodImplementation(element)) { |
| 149 defaultImpls.add(element); | 139 defaultImpls.add(element); |
| 150 return NsmCategory.DEFAULT; | 140 return NsmCategory.DEFAULT; |
| 151 } else if (_hasForwardingSyntax(element)) { | 141 } else if (_hasForwardingSyntax(element)) { |
| 152 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' | 142 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' |
| 153 // then it is in the same category as the super call. | 143 // then it is in the same category as the super call. |
| 154 Element superCall = | 144 Element superCall = element.enclosingClass |
| 155 element.enclosingClass.lookupSuperByName(Names.noSuchMethod_); | 145 .lookupSuperByName(Selectors.noSuchMethod_.memberName); |
| 156 NsmCategory category = _categorizeImpl(superCall); | 146 NsmCategory category = _categorizeImpl(superCall); |
| 157 switch(category) { | 147 switch(category) { |
| 158 case NsmCategory.DEFAULT: | 148 case NsmCategory.DEFAULT: |
| 159 defaultImpls.add(element); | 149 defaultImpls.add(element); |
| 160 break; | 150 break; |
| 161 case NsmCategory.THROWING: | 151 case NsmCategory.THROWING: |
| 162 throwingImpls.add(element); | 152 throwingImpls.add(element); |
| 163 break; | 153 break; |
| 164 case NsmCategory.OTHER: | 154 case NsmCategory.OTHER: |
| 165 otherImpls.add(element); | 155 otherImpls.add(element); |
| 166 break; | 156 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; | |
| 173 } | 157 } |
| 174 return category; | 158 return category; |
| 175 } else if (_hasThrowingSyntax(element)) { | 159 } else if (_hasThrowingSyntax(element)) { |
| 176 throwingImpls.add(element); | 160 throwingImpls.add(element); |
| 177 return NsmCategory.THROWING; | 161 return NsmCategory.THROWING; |
| 178 } else { | 162 } else { |
| 179 otherImpls.add(element); | 163 otherImpls.add(element); |
| 180 return NsmCategory.OTHER; | 164 return NsmCategory.OTHER; |
| 181 } | 165 } |
| 182 } | 166 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 body.statements.nodes.tail.isEmpty) { | 217 body.statements.nodes.tail.isEmpty) { |
| 234 if (body.statements.nodes.head is ExpressionStatement) { | 218 if (body.statements.nodes.head is ExpressionStatement) { |
| 235 ExpressionStatement stmt = body.statements.nodes.head; | 219 ExpressionStatement stmt = body.statements.nodes.head; |
| 236 return stmt.expression is Throw; | 220 return stmt.expression is Throw; |
| 237 } | 221 } |
| 238 } | 222 } |
| 239 return false; | 223 return false; |
| 240 } | 224 } |
| 241 } | 225 } |
| 242 | 226 |
| 243 enum NsmCategory { | 227 enum NsmCategory { DEFAULT, THROWING, OTHER } |
| 244 DEFAULT, | |
| 245 THROWING, | |
| 246 NOT_APPLICABLE, | |
| 247 OTHER, | |
| 248 } | |
| OLD | NEW |