| 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 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 * | 37 * |
| 38 * noSuchMethod(x) => super.noSuchMethod(x); | 38 * noSuchMethod(x) => super.noSuchMethod(x); |
| 39 * | 39 * |
| 40 * 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 |
| 41 * common case, where users implement `noSuchMethod` with these dummy | 41 * common case, where users implement `noSuchMethod` with these dummy |
| 42 * implementations to avoid warnings. | 42 * implementations to avoid warnings. |
| 43 */ | 43 */ |
| 44 class NoSuchMethodRegistry { | 44 class NoSuchMethodRegistry { |
| 45 /// The implementations that fall into category A, described above. | 45 /// The implementations that fall into category A, described above. |
| 46 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); | 46 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); |
| 47 |
| 47 /// The implementations that fall into category B, described above. | 48 /// The implementations that fall into category B, described above. |
| 48 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); | 49 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); |
| 50 |
| 49 /// The implementations that fall into category C, described above. | 51 /// The implementations that fall into category C, described above. |
| 50 final Set<FunctionElement> notApplicableImpls = new Set<FunctionElement>(); | 52 final Set<FunctionElement> notApplicableImpls = new Set<FunctionElement>(); |
| 53 |
| 51 /// The implementations that fall into category D, described above. | 54 /// The implementations that fall into category D, described above. |
| 52 final Set<FunctionElement> otherImpls = new Set<FunctionElement>(); | 55 final Set<FunctionElement> otherImpls = new Set<FunctionElement>(); |
| 53 | 56 |
| 54 /// The implementations that fall into category D1 | 57 /// The implementations that fall into category D1 |
| 55 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>(); | 58 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>(); |
| 59 |
| 56 /// The implementations that fall into category D2 | 60 /// The implementations that fall into category D2 |
| 57 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>(); | 61 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>(); |
| 58 | 62 |
| 59 /// The implementations that have not yet been categorized. | 63 /// The implementations that have not yet been categorized. |
| 60 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>(); | 64 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>(); |
| 61 | 65 |
| 62 final JavaScriptBackend _backend; | 66 final JavaScriptBackend _backend; |
| 63 final Compiler _compiler; | 67 final Compiler _compiler; |
| 64 | 68 |
| 65 NoSuchMethodRegistry(JavaScriptBackend backend) | 69 NoSuchMethodRegistry(JavaScriptBackend backend) |
| (...skipping 17 matching lines...) Expand all Loading... |
| 83 /// Now that type inference is complete, split category D into two | 87 /// Now that type inference is complete, split category D into two |
| 84 /// subcategories: D1, those that have no return type, and D2, those | 88 /// subcategories: D1, those that have no return type, and D2, those |
| 85 /// that have a return type. | 89 /// that have a return type. |
| 86 void onTypeInferenceComplete() { | 90 void onTypeInferenceComplete() { |
| 87 otherImpls.forEach(_subcategorizeOther); | 91 otherImpls.forEach(_subcategorizeOther); |
| 88 } | 92 } |
| 89 | 93 |
| 90 /// Emits a diagnostic | 94 /// Emits a diagnostic |
| 91 void emitDiagnostic() { | 95 void emitDiagnostic() { |
| 92 throwingImpls.forEach((e) { | 96 throwingImpls.forEach((e) { |
| 93 if (!_hasForwardingSyntax(e)) { | 97 if (!_hasForwardingSyntax(e)) { |
| 94 reporter.reportHintMessage( | 98 reporter.reportHintMessage(e, MessageKind.DIRECTLY_THROWING_NSM); |
| 95 e, MessageKind.DIRECTLY_THROWING_NSM); | 99 } |
| 96 } | 100 }); |
| 97 }); | |
| 98 complexNoReturnImpls.forEach((e) { | 101 complexNoReturnImpls.forEach((e) { |
| 99 if (!_hasForwardingSyntax(e)) { | 102 if (!_hasForwardingSyntax(e)) { |
| 100 reporter.reportHintMessage( | 103 reporter.reportHintMessage(e, MessageKind.COMPLEX_THROWING_NSM); |
| 101 e, MessageKind.COMPLEX_THROWING_NSM); | 104 } |
| 102 } | 105 }); |
| 103 }); | |
| 104 complexReturningImpls.forEach((e) { | 106 complexReturningImpls.forEach((e) { |
| 105 if (!_hasForwardingSyntax(e)) { | 107 if (!_hasForwardingSyntax(e)) { |
| 106 reporter.reportHintMessage( | 108 reporter.reportHintMessage(e, MessageKind.COMPLEX_RETURNING_NSM); |
| 107 e, MessageKind.COMPLEX_RETURNING_NSM); | 109 } |
| 108 } | 110 }); |
| 109 }); | |
| 110 } | 111 } |
| 111 | 112 |
| 112 /// Returns [true] if the given element is a complex [noSuchMethod] | 113 /// Returns [true] if the given element is a complex [noSuchMethod] |
| 113 /// implementation. An implementation is complex if it falls into | 114 /// implementation. An implementation is complex if it falls into |
| 114 /// category D, as described above. | 115 /// category D, as described above. |
| 115 bool isComplex(FunctionElement element) { | 116 bool isComplex(FunctionElement element) { |
| 116 assert(element.name == Identifiers.noSuchMethod_); | 117 assert(element.name == Identifiers.noSuchMethod_); |
| 117 return otherImpls.contains(element); | 118 return otherImpls.contains(element); |
| 118 } | 119 } |
| 119 | 120 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 147 } | 148 } |
| 148 if (_isDefaultNoSuchMethodImplementation(element)) { | 149 if (_isDefaultNoSuchMethodImplementation(element)) { |
| 149 defaultImpls.add(element); | 150 defaultImpls.add(element); |
| 150 return NsmCategory.DEFAULT; | 151 return NsmCategory.DEFAULT; |
| 151 } else if (_hasForwardingSyntax(element)) { | 152 } else if (_hasForwardingSyntax(element)) { |
| 152 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' | 153 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' |
| 153 // then it is in the same category as the super call. | 154 // then it is in the same category as the super call. |
| 154 Element superCall = | 155 Element superCall = |
| 155 element.enclosingClass.lookupSuperByName(Names.noSuchMethod_); | 156 element.enclosingClass.lookupSuperByName(Names.noSuchMethod_); |
| 156 NsmCategory category = _categorizeImpl(superCall); | 157 NsmCategory category = _categorizeImpl(superCall); |
| 157 switch(category) { | 158 switch (category) { |
| 158 case NsmCategory.DEFAULT: | 159 case NsmCategory.DEFAULT: |
| 159 defaultImpls.add(element); | 160 defaultImpls.add(element); |
| 160 break; | 161 break; |
| 161 case NsmCategory.THROWING: | 162 case NsmCategory.THROWING: |
| 162 throwingImpls.add(element); | 163 throwingImpls.add(element); |
| 163 break; | 164 break; |
| 164 case NsmCategory.OTHER: | 165 case NsmCategory.OTHER: |
| 165 otherImpls.add(element); | 166 otherImpls.add(element); |
| 166 break; | 167 break; |
| 167 case NsmCategory.NOT_APPLICABLE: | 168 case NsmCategory.NOT_APPLICABLE: |
| 168 // If the super method is not applicable, the call is redirected to | 169 // If the super method is not applicable, the call is redirected to |
| 169 // `Object.noSuchMethod`. | 170 // `Object.noSuchMethod`. |
| 170 defaultImpls.add(element); | 171 defaultImpls.add(element); |
| 171 category = NsmCategory.DEFAULT; | 172 category = NsmCategory.DEFAULT; |
| 172 break; | 173 break; |
| 173 } | 174 } |
| 174 return category; | 175 return category; |
| 175 } else if (_hasThrowingSyntax(element)) { | 176 } else if (_hasThrowingSyntax(element)) { |
| 176 throwingImpls.add(element); | 177 throwingImpls.add(element); |
| 177 return NsmCategory.THROWING; | 178 return NsmCategory.THROWING; |
| 178 } else { | 179 } else { |
| 179 otherImpls.add(element); | 180 otherImpls.add(element); |
| 180 return NsmCategory.OTHER; | 181 return NsmCategory.OTHER; |
| 181 } | 182 } |
| 182 } | 183 } |
| 183 | 184 |
| 184 bool _isDefaultNoSuchMethodImplementation(FunctionElement element) { | 185 bool _isDefaultNoSuchMethodImplementation(FunctionElement element) { |
| 185 ClassElement classElement = element.enclosingClass; | 186 ClassElement classElement = element.enclosingClass; |
| 186 return classElement == _compiler.coreClasses.objectClass | 187 return classElement == _compiler.coreClasses.objectClass || |
| 187 || classElement == _backend.helpers.jsInterceptorClass | 188 classElement == _backend.helpers.jsInterceptorClass || |
| 188 || classElement == _backend.helpers.jsNullClass; | 189 classElement == _backend.helpers.jsNullClass; |
| 189 } | 190 } |
| 190 | 191 |
| 191 bool _hasForwardingSyntax(FunctionElement element) { | 192 bool _hasForwardingSyntax(FunctionElement element) { |
| 192 // At this point we know that this is signature-compatible with | 193 // At this point we know that this is signature-compatible with |
| 193 // Object.noSuchMethod, but it may have more than one argument as long as | 194 // Object.noSuchMethod, but it may have more than one argument as long as |
| 194 // it only has one required argument. | 195 // it only has one required argument. |
| 195 String param = element.parameters.first.name; | 196 String param = element.parameters.first.name; |
| 196 Statement body = element.node.body; | 197 Statement body = element.node.body; |
| 197 Expression expr; | 198 Expression expr; |
| 198 if (body is Return && body.isArrowBody) { | 199 if (body is Return && body.isArrowBody) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 body.statements.nodes.tail.isEmpty) { | 234 body.statements.nodes.tail.isEmpty) { |
| 234 if (body.statements.nodes.head is ExpressionStatement) { | 235 if (body.statements.nodes.head is ExpressionStatement) { |
| 235 ExpressionStatement stmt = body.statements.nodes.head; | 236 ExpressionStatement stmt = body.statements.nodes.head; |
| 236 return stmt.expression is Throw; | 237 return stmt.expression is Throw; |
| 237 } | 238 } |
| 238 } | 239 } |
| 239 return false; | 240 return false; |
| 240 } | 241 } |
| 241 } | 242 } |
| 242 | 243 |
| 243 enum NsmCategory { | 244 enum NsmCategory { DEFAULT, THROWING, NOT_APPLICABLE, OTHER, } |
| 244 DEFAULT, | |
| 245 THROWING, | |
| 246 NOT_APPLICABLE, | |
| 247 OTHER, | |
| 248 } | |
| OLD | NEW |