OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 /** | |
6 * Traverses the entire code of a function to find whether it contains awaits, | |
7 * ensuring they occur only in valid locations. This visitor will *not* recurse | |
8 * inside nested function declarations. | |
9 */ | |
10 class AwaitChecker implements TreeVisitor { | |
11 | |
12 /** Functions found within the body of the entry function. */ | |
13 List<FunctionDefinition> nestedFunctions; | |
14 | |
15 /** Helper bool to ensure that only the top-level function is analyzed. */ | |
16 bool _entryFunction = true; | |
17 | |
18 /** AST nodes that contain await expressions. */ | |
19 NodeSet haveAwait; | |
20 | |
21 // TODO: track haveExit (return/throw) and haveBreak (break/continue) to | |
22 // property transform nodes that don't contain await, but may be affected | |
23 AwaitChecker() : nestedFunctions = [], haveAwait = new NodeSet(); | |
24 | |
25 visitVariableDefinition(VariableDefinition node) { | |
26 bool awaitSeen = _visitList(node.values); | |
27 if (awaitSeen) haveAwait.add(node); | |
28 return awaitSeen; | |
29 } | |
30 | |
31 visitFunctionDefinition(FunctionDefinition node) { | |
32 if (_entryFunction) { | |
33 _entryFunction = false; | |
34 if (node.initializers != null) { | |
35 for (Expression e in node.initializers) { | |
36 if (e.visit(this)) { | |
37 world.error('Await expressions are not allowed in initializers.', | |
38 e.span); | |
39 } | |
40 } | |
41 } | |
42 if (_visit(node.body)) { | |
43 haveAwait.add(node); | |
44 // TODO(sigmund) check that return type is Dynamic or a future. | |
45 return true; | |
46 } | |
47 return false; | |
48 } else { | |
49 // Do not analyze nested functions now, use a separate checker for that. | |
50 nestedFunctions.add(node); | |
51 return false; | |
52 } | |
53 } | |
54 | |
55 _notSupportedStmt(name, node) { | |
56 world.error("Await is not supported in '$name' statements yet.", node.span); | |
57 } | |
58 | |
59 _notSupported(name, node) { | |
60 world.error( | |
61 "Await is not supported in $name yet, try pulling into a tmp var.", | |
62 node.span); | |
63 } | |
64 | |
65 visitReturnStatement(ReturnStatement node) { | |
66 bool awaitSeen = _visit(node.value); | |
67 if (awaitSeen) haveAwait.add(node); | |
68 return awaitSeen; | |
69 } | |
70 | |
71 visitThrowStatement(ThrowStatement node) { | |
72 bool awaitSeen = _visit(node.value); | |
73 if (awaitSeen) haveAwait.add(node); | |
74 return awaitSeen; | |
75 } | |
76 | |
77 visitAssertStatement(AssertStatement node) { | |
78 bool awaitSeen = node.test.visit(this); | |
79 if (awaitSeen) { | |
80 haveAwait.add(node); | |
81 _notSupportedStmt("assert", node); | |
82 } | |
83 return awaitSeen; | |
84 } | |
85 | |
86 visitBreakStatement(BreakStatement node) { | |
87 return false; | |
88 } | |
89 | |
90 visitContinueStatement(ContinueStatement node) { | |
91 return false; | |
92 } | |
93 | |
94 visitIfStatement(IfStatement node) { | |
95 bool awaitSeen = node.test.visit(this); | |
96 if (node.trueBranch.visit(this)) { | |
97 awaitSeen = true; | |
98 } | |
99 if (_visit(node.falseBranch)) { | |
100 awaitSeen = true; | |
101 } | |
102 if (awaitSeen) haveAwait.add(node); | |
103 return awaitSeen; | |
104 } | |
105 | |
106 visitWhileStatement(WhileStatement node) { | |
107 bool awaitSeen = node.test.visit(this); | |
108 if (_visit(node.body)) awaitSeen = true; | |
109 if (awaitSeen) haveAwait.add(node); | |
110 return awaitSeen; | |
111 } | |
112 | |
113 visitDoStatement(DoStatement node) { | |
114 bool awaitSeen = node.test.visit(this); | |
115 if (_visit(node.body)) awaitSeen = true; | |
116 if (awaitSeen) { | |
117 haveAwait.add(node); | |
118 _notSupportedStmt("do while", node); | |
119 } | |
120 return awaitSeen; | |
121 } | |
122 | |
123 visitForStatement(ForStatement node) { | |
124 bool awaitSeen = node.test.visit(this); | |
125 if (_visit(node.body)) awaitSeen = true; | |
126 if (_visit(node.init)) awaitSeen = true; | |
127 if (_visitList(node.step)) awaitSeen = true; | |
128 if (awaitSeen) { | |
129 haveAwait.add(node); | |
130 _notSupportedStmt("for", node); | |
131 } | |
132 return awaitSeen; | |
133 } | |
134 | |
135 visitForInStatement(ForInStatement node) { | |
136 bool awaitSeen = node.list.visit(this); | |
137 if (_visit(node.body)) awaitSeen = true; | |
138 if (awaitSeen) { | |
139 haveAwait.add(node); | |
140 _notSupportedStmt("for-in", node); | |
141 } | |
142 return awaitSeen; | |
143 } | |
144 | |
145 visitTryStatement(TryStatement node) { | |
146 bool awaitSeen = (_visit(node.body)); | |
147 if (_visitList(node.catches)) awaitSeen = true; | |
148 if (_visit(node.finallyBlock)) { | |
149 awaitSeen = true; | |
150 } | |
151 if (awaitSeen) haveAwait.add(node); | |
152 return awaitSeen; | |
153 } | |
154 | |
155 visitSwitchStatement(SwitchStatement node) { | |
156 bool awaitSeen = node.test.visit(this); | |
157 if (_visitList(node.cases)) awaitSeen = true; | |
158 if (awaitSeen) { | |
159 haveAwait.add(node); | |
160 _notSupportedStmt("switch", node); | |
161 } | |
162 return awaitSeen; | |
163 } | |
164 | |
165 visitBlockStatement(BlockStatement node) { | |
166 bool awaitSeen = _visitList(node.body); | |
167 if (awaitSeen) haveAwait.add(node); | |
168 return awaitSeen; | |
169 } | |
170 | |
171 visitLabeledStatement(LabeledStatement node) { | |
172 bool awaitSeen = node.body.visit(this); | |
173 if (awaitSeen) haveAwait.add(node); | |
174 return awaitSeen; | |
175 } | |
176 | |
177 visitExpressionStatement(ExpressionStatement node) { | |
178 bool awaitSeen = node.body.visit(this); | |
179 if (awaitSeen) haveAwait.add(node); | |
180 return awaitSeen; | |
181 } | |
182 | |
183 visitEmptyStatement(EmptyStatement node) { | |
184 return false; | |
185 } | |
186 | |
187 visitLambdaExpression(LambdaExpression node) { | |
188 _entryFunction = false; | |
189 return node.func.visit(this); | |
190 } | |
191 | |
192 visitCallExpression(CallExpression node) { | |
193 bool awaitSeen = node.target.visit(this); | |
194 if (_visitList(node.arguments)) awaitSeen = true; | |
195 if (awaitSeen) haveAwait.add(node); | |
196 return awaitSeen; | |
197 } | |
198 | |
199 visitIndexExpression(IndexExpression node) { | |
200 bool awaitSeen = node.target.visit(this); | |
201 if (node.index.visit(this)) awaitSeen = true; | |
202 if (awaitSeen) haveAwait.add(node); | |
203 return awaitSeen; | |
204 } | |
205 | |
206 visitBinaryExpression(BinaryExpression node) { | |
207 bool awaitSeen = node.x.visit(this); | |
208 if (node.y.visit(this)) awaitSeen = true; | |
209 if (awaitSeen) haveAwait.add(node); | |
210 return awaitSeen; | |
211 } | |
212 | |
213 visitUnaryExpression(UnaryExpression node) { | |
214 // TODO(sigmund): issue errors for ++/-- cases where we expect an l-value. | |
215 bool awaitSeen = node.self.visit(this); | |
216 if (awaitSeen) { | |
217 haveAwait.add(node); | |
218 _notSupported("unary expressions", node); | |
219 } | |
220 return awaitSeen; | |
221 } | |
222 | |
223 visitPostfixExpression(PostfixExpression node) { | |
224 // TODO(sigmund): issue errors for ++/-- cases where we expect an l-value. | |
225 bool awaitSeen = node.body.visit(this); | |
226 if (awaitSeen) { | |
227 haveAwait.add(node); | |
228 _notSupported("postfix expressions", node); | |
229 } | |
230 return awaitSeen; | |
231 } | |
232 | |
233 visitNewExpression(NewExpression node) { | |
234 bool awaitSeen = _visitList(node.arguments); | |
235 if (awaitSeen) { | |
236 haveAwait.add(node); | |
237 _notSupported("new expressions", node); | |
238 } | |
239 return awaitSeen; | |
240 } | |
241 | |
242 visitListExpression(ListExpression node) { | |
243 bool awaitSeen = _visitList(node.values); | |
244 if (awaitSeen) { | |
245 haveAwait.add(node); | |
246 _notSupported("list literals", node); | |
247 } | |
248 return awaitSeen; | |
249 } | |
250 | |
251 visitMapExpression(MapExpression node) { | |
252 bool awaitSeen = _visitList(node.items); | |
253 if (awaitSeen) { | |
254 haveAwait.add(node); | |
255 _notSupported("map literals", node); | |
256 } | |
257 return awaitSeen; | |
258 } | |
259 | |
260 visitConditionalExpression(ConditionalExpression node) { | |
261 bool awaitSeen = node.test.visit(this); | |
262 if (node.trueBranch.visit(this)) awaitSeen = true; | |
263 if (node.falseBranch.visit(this)) awaitSeen = true; | |
264 if (awaitSeen) { | |
265 haveAwait.add(node); | |
266 _notSupported("ternary expressions", node); | |
267 } | |
268 return awaitSeen; | |
269 } | |
270 | |
271 visitIsExpression(IsExpression node) { | |
272 bool awaitSeen = node.x.visit(this); | |
273 if (awaitSeen) { | |
274 haveAwait.add(node); | |
275 _notSupported("'is' checks", node); | |
276 } | |
277 return awaitSeen; | |
278 } | |
279 | |
280 visitParenExpression(ParenExpression node) { | |
281 bool awaitSeen = node.body.visit(this); | |
282 if (awaitSeen) haveAwait.add(node); | |
283 return awaitSeen; | |
284 } | |
285 | |
286 visitAwaitExpression(AwaitExpression node) { | |
287 haveAwait.add(node); | |
288 return true; | |
289 } | |
290 | |
291 visitDotExpression(DotExpression node) { | |
292 bool awaitSeen = node.self.visit(this); | |
293 if (awaitSeen) haveAwait.add(node); | |
294 return awaitSeen; | |
295 } | |
296 | |
297 visitVarExpression(VarExpression node) { | |
298 return false; | |
299 } | |
300 | |
301 visitThisExpression(ThisExpression node) { | |
302 return false; | |
303 } | |
304 | |
305 visitSuperExpression(SuperExpression node) { | |
306 return false; | |
307 } | |
308 | |
309 visitStringInterpExpression(StringInterpExpression node) { | |
310 return false; | |
311 } | |
312 | |
313 visitLiteralExpression(LiteralExpression node) { | |
314 return false; | |
315 } | |
316 | |
317 visitArgumentNode(ArgumentNode node) { | |
318 bool awaitSeen = node.value.visit(this); | |
319 if (awaitSeen) haveAwait.add(node); | |
320 return awaitSeen; | |
321 } | |
322 | |
323 visitCatchNode(CatchNode node) { | |
324 bool awaitSeen = false; | |
325 if (_visit(node.body)) awaitSeen = true; | |
326 if (awaitSeen) { | |
327 haveAwait.add(node); | |
328 _notSupported("catch blocks", node); | |
329 } | |
330 return awaitSeen; | |
331 } | |
332 | |
333 visitCaseNode(CaseNode node) { | |
334 bool awaitSeen = false; | |
335 for (Expression e in node.cases) { | |
336 if (_visit(e)) { | |
337 world.error( | |
338 'Await is not allowed in case expressions of switch statements.', | |
339 e.span); | |
340 awaitSeen = true; | |
341 } | |
342 } | |
343 if (_visitList(node.statements)) awaitSeen = true; | |
344 if (awaitSeen) { | |
345 haveAwait.add(node); | |
346 _notSupported("case blocks", node); | |
347 } | |
348 return awaitSeen; | |
349 } | |
350 | |
351 /** | |
352 * Recurses on every element in the list, returns whether any of them has an | |
353 * await. | |
354 */ | |
355 _visitList(List nodes) { | |
356 bool awaitSeen = false; | |
357 if (nodes != null) { | |
358 for (final n in nodes) { | |
359 if (_visit(n)) awaitSeen = true; | |
360 } | |
361 } | |
362 return awaitSeen; | |
363 } | |
364 | |
365 _visit(node) { | |
366 if (node == null) return false; | |
367 return node.visit(this); | |
368 } | |
369 } | |
OLD | NEW |