| 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 24 matching lines...) Expand all Loading... |
| 35 * 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 |
| 36 * common case, where users implement `noSuchMethod` with these dummy | 36 * common case, where users implement `noSuchMethod` with these dummy |
| 37 * implementations to avoid warnings. | 37 * implementations to avoid warnings. |
| 38 */ | 38 */ |
| 39 class NoSuchMethodRegistry { | 39 class NoSuchMethodRegistry { |
| 40 /// The implementations that fall into category A, described above. | 40 /// The implementations that fall into category A, described above. |
| 41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); | 41 final Set<FunctionElement> defaultImpls = new Set<FunctionElement>(); |
| 42 /// The implementations that fall into category B, described above. | 42 /// The implementations that fall into category B, described above. |
| 43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); | 43 final Set<FunctionElement> throwingImpls = new Set<FunctionElement>(); |
| 44 /// The implementations that fall into category C, described above. | 44 /// The implementations that fall into category C, described above. |
| 45 final Set<Element> otherImpls = new Set<Element>(); | 45 final Set<FunctionElement> otherImpls = new Set<FunctionElement>(); |
| 46 |
| 47 /// The implementations that fall into category C1 |
| 48 final Set<FunctionElement> complexNoReturnImpls = new Set<FunctionElement>(); |
| 49 /// The implementations that fall into category C2 |
| 50 final Set<FunctionElement> complexReturningImpls = new Set<FunctionElement>(); |
| 46 | 51 |
| 47 /// The implementations that have not yet been categorized. | 52 /// The implementations that have not yet been categorized. |
| 48 final Set<Element> _uncategorizedImpls = new Set<Element>(); | 53 final Set<FunctionElement> _uncategorizedImpls = new Set<FunctionElement>(); |
| 49 | 54 |
| 50 final JavaScriptBackend _backend; | 55 final JavaScriptBackend _backend; |
| 51 final Compiler _compiler; | 56 final Compiler _compiler; |
| 52 | 57 |
| 53 NoSuchMethodRegistry(JavaScriptBackend backend) | 58 NoSuchMethodRegistry(JavaScriptBackend backend) |
| 54 : this._backend = backend, | 59 : this._backend = backend, |
| 55 this._compiler = backend.compiler; | 60 this._compiler = backend.compiler; |
| 56 | 61 |
| 57 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; | 62 bool get hasThrowingNoSuchMethod => throwingImpls.isNotEmpty; |
| 58 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; | 63 bool get hasComplexNoSuchMethod => otherImpls.isNotEmpty; |
| 59 | 64 |
| 60 void registerNoSuchMethod(Element noSuchMethodElement) { | 65 void registerNoSuchMethod(FunctionElement noSuchMethodElement) { |
| 61 _uncategorizedImpls.add(noSuchMethodElement); | 66 _uncategorizedImpls.add(noSuchMethodElement); |
| 62 } | 67 } |
| 63 | 68 |
| 64 void onQueueEmpty() { | 69 void onQueueEmpty() { |
| 65 _uncategorizedImpls.forEach(_categorizeImpl); | 70 _uncategorizedImpls.forEach(_categorizeImpl); |
| 66 _uncategorizedImpls.clear(); | 71 _uncategorizedImpls.clear(); |
| 67 } | 72 } |
| 68 | 73 |
| 74 /// Now that type inference is complete, split category C into two |
| 75 /// subcategories: C1, those that have no return type, and C2, those |
| 76 /// that have a return type. |
| 77 void onInferenceComplete() { |
| 78 otherImpls.forEach(_subcategorizeOther); |
| 79 } |
| 80 |
| 81 /// Emits a diagnostic |
| 82 void emitDiagnostic() { |
| 83 throwingImpls.forEach((e) { |
| 84 if (!_hasForwardingSyntax(e)) { |
| 85 _compiler.reportHint(e, |
| 86 MessageKind.DIRECTLY_THROWING_NSM); |
| 87 } |
| 88 }); |
| 89 complexNoReturnImpls.forEach((e) { |
| 90 if (!_hasForwardingSyntax(e)) { |
| 91 _compiler.reportHint(e, |
| 92 MessageKind.COMPLEX_THROWING_NSM); |
| 93 } |
| 94 }); |
| 95 complexReturningImpls.forEach((e) { |
| 96 if (!_hasForwardingSyntax(e)) { |
| 97 _compiler.reportHint(e, |
| 98 MessageKind.COMPLEX_RETURNING_NSM); |
| 99 } |
| 100 }); |
| 101 } |
| 102 |
| 69 /// Returns [true] if the given element is a complex [noSuchMethod] | 103 /// Returns [true] if the given element is a complex [noSuchMethod] |
| 70 /// implementation. An implementation is complex if it falls into | 104 /// implementation. An implementation is complex if it falls into |
| 71 /// category C, as described above. | 105 /// category C, as described above. |
| 72 bool isComplex(FunctionElement element) { | 106 bool isComplex(FunctionElement element) { |
| 73 assert(element.name == Compiler.NO_SUCH_METHOD); | 107 assert(element.name == Compiler.NO_SUCH_METHOD); |
| 74 return otherImpls.contains(element); | 108 return otherImpls.contains(element); |
| 75 } | 109 } |
| 76 | 110 |
| 77 NsmCategory _categorizeImpl(Element noSuchMethodElement) { | 111 _subcategorizeOther(FunctionElement element) { |
| 78 assert(noSuchMethodElement.name == Compiler.NO_SUCH_METHOD); | 112 TypeMask returnType = |
| 79 if (defaultImpls.contains(noSuchMethodElement)) { | 113 _compiler.typesTask.getGuaranteedReturnTypeOfElement(element); |
| 114 if (returnType == const TypeMask.nonNullEmpty()) { |
| 115 complexNoReturnImpls.add(element); |
| 116 } else { |
| 117 complexReturningImpls.add(element); |
| 118 } |
| 119 } |
| 120 |
| 121 NsmCategory _categorizeImpl(FunctionElement element) { |
| 122 assert(element.name == Compiler.NO_SUCH_METHOD); |
| 123 if (defaultImpls.contains(element)) { |
| 80 return NsmCategory.DEFAULT; | 124 return NsmCategory.DEFAULT; |
| 81 } | 125 } |
| 82 if (throwingImpls.contains(noSuchMethodElement)) { | 126 if (throwingImpls.contains(element)) { |
| 83 return NsmCategory.THROWING; | 127 return NsmCategory.THROWING; |
| 84 } | 128 } |
| 85 if (otherImpls.contains(noSuchMethodElement)) { | 129 if (otherImpls.contains(element)) { |
| 86 return NsmCategory.OTHER; | 130 return NsmCategory.OTHER; |
| 87 } | 131 } |
| 88 if (noSuchMethodElement is! FunctionElement || | 132 if (!_compiler.noSuchMethodSelector.signatureApplies(element)) { |
| 89 !_compiler.noSuchMethodSelector.signatureApplies(noSuchMethodElement)) { | 133 otherImpls.add(element); |
| 90 otherImpls.add(noSuchMethodElement); | |
| 91 return NsmCategory.OTHER; | 134 return NsmCategory.OTHER; |
| 92 } | 135 } |
| 93 FunctionElement noSuchMethodFunc = noSuchMethodElement as FunctionElement; | 136 if (_isDefaultNoSuchMethodImplementation(element)) { |
| 94 if (_isDefaultNoSuchMethodImplementation(noSuchMethodFunc)) { | 137 defaultImpls.add(element); |
| 95 defaultImpls.add(noSuchMethodFunc); | |
| 96 return NsmCategory.DEFAULT; | 138 return NsmCategory.DEFAULT; |
| 97 } else if (_hasForwardingSyntax(noSuchMethodFunc)) { | 139 } else if (_hasForwardingSyntax(element)) { |
| 98 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' | 140 // If the implementation is 'noSuchMethod(x) => super.noSuchMethod(x);' |
| 99 // then it is in the same category as the super call. | 141 // then it is in the same category as the super call. |
| 100 Element superCall = noSuchMethodFunc.enclosingClass | 142 Element superCall = element.enclosingClass |
| 101 .lookupSuperSelector(_compiler.noSuchMethodSelector); | 143 .lookupSuperSelector(_compiler.noSuchMethodSelector); |
| 102 NsmCategory category = _categorizeImpl(superCall); | 144 NsmCategory category = _categorizeImpl(superCall); |
| 103 switch(category) { | 145 switch(category) { |
| 104 case NsmCategory.DEFAULT: | 146 case NsmCategory.DEFAULT: |
| 105 defaultImpls.add(noSuchMethodFunc); | 147 defaultImpls.add(element); |
| 106 break; | 148 break; |
| 107 case NsmCategory.THROWING: | 149 case NsmCategory.THROWING: |
| 108 throwingImpls.add(noSuchMethodFunc); | 150 throwingImpls.add(element); |
| 109 break; | 151 break; |
| 110 case NsmCategory.OTHER: | 152 case NsmCategory.OTHER: |
| 111 otherImpls.add(noSuchMethodFunc); | 153 otherImpls.add(element); |
| 112 break; | 154 break; |
| 113 } | 155 } |
| 114 return category; | 156 return category; |
| 115 } else if (_hasThrowingSyntax(noSuchMethodFunc)) { | 157 } else if (_hasThrowingSyntax(element)) { |
| 116 throwingImpls.add(noSuchMethodFunc); | 158 throwingImpls.add(element); |
| 117 return NsmCategory.THROWING; | 159 return NsmCategory.THROWING; |
| 118 } else { | 160 } else { |
| 119 otherImpls.add(noSuchMethodFunc); | 161 otherImpls.add(element); |
| 120 return NsmCategory.OTHER; | 162 return NsmCategory.OTHER; |
| 121 } | 163 } |
| 122 } | 164 } |
| 123 | 165 |
| 124 bool _isDefaultNoSuchMethodImplementation(Element element) { | 166 bool _isDefaultNoSuchMethodImplementation(FunctionElement element) { |
| 125 ClassElement classElement = element.enclosingClass; | 167 ClassElement classElement = element.enclosingClass; |
| 126 return classElement == _compiler.objectClass | 168 return classElement == _compiler.objectClass |
| 127 || classElement == _backend.jsInterceptorClass | 169 || classElement == _backend.jsInterceptorClass |
| 128 || classElement == _backend.jsNullClass; | 170 || classElement == _backend.jsNullClass; |
| 129 } | 171 } |
| 130 | 172 |
| 131 bool _hasForwardingSyntax(FunctionElement element) { | 173 bool _hasForwardingSyntax(FunctionElement element) { |
| 132 // At this point we know that this is signature-compatible with | 174 // At this point we know that this is signature-compatible with |
| 133 // Object.noSuchMethod, but it may have more than one argument as long as | 175 // Object.noSuchMethod, but it may have more than one argument as long as |
| 134 // it only has one required argument. | 176 // it only has one required argument. |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 if (body.statements.nodes.head is ExpressionStatement) { | 216 if (body.statements.nodes.head is ExpressionStatement) { |
| 175 ExpressionStatement stmt = body.statements.nodes.head; | 217 ExpressionStatement stmt = body.statements.nodes.head; |
| 176 return stmt.expression is Throw; | 218 return stmt.expression is Throw; |
| 177 } | 219 } |
| 178 } | 220 } |
| 179 return false; | 221 return false; |
| 180 } | 222 } |
| 181 } | 223 } |
| 182 | 224 |
| 183 enum NsmCategory { DEFAULT, THROWING, OTHER } | 225 enum NsmCategory { DEFAULT, THROWING, OTHER } |
| OLD | NEW |