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