OLD | NEW |
| (Empty) |
1 // Copyright 2012 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "src/rewriter.h" | |
6 | |
7 #include "src/ast.h" | |
8 #include "src/parser.h" | |
9 #include "src/scopes.h" | |
10 | |
11 namespace v8 { | |
12 namespace internal { | |
13 | |
14 class Processor: public AstVisitor { | |
15 public: | |
16 Processor(Isolate* isolate, Scope* scope, Variable* result, | |
17 AstValueFactory* ast_value_factory) | |
18 : result_(result), | |
19 result_assigned_(false), | |
20 replacement_(nullptr), | |
21 is_set_(false), | |
22 zone_(ast_value_factory->zone()), | |
23 scope_(scope), | |
24 factory_(ast_value_factory) { | |
25 InitializeAstVisitor(isolate); | |
26 } | |
27 | |
28 Processor(Parser* parser, Scope* scope, Variable* result, | |
29 AstValueFactory* ast_value_factory) | |
30 : result_(result), | |
31 result_assigned_(false), | |
32 replacement_(nullptr), | |
33 is_set_(false), | |
34 scope_(scope), | |
35 factory_(ast_value_factory) { | |
36 InitializeAstVisitor(parser->stack_limit()); | |
37 } | |
38 | |
39 ~Processor() override {} | |
40 | |
41 void Process(ZoneList<Statement*>* statements); | |
42 bool result_assigned() const { return result_assigned_; } | |
43 | |
44 Zone* zone() { return zone_; } | |
45 Scope* scope() { return scope_; } | |
46 AstNodeFactory* factory() { return &factory_; } | |
47 | |
48 // Returns ".result = value" | |
49 Expression* SetResult(Expression* value) { | |
50 result_assigned_ = true; | |
51 VariableProxy* result_proxy = factory()->NewVariableProxy(result_); | |
52 return factory()->NewAssignment(Token::ASSIGN, result_proxy, value, | |
53 RelocInfo::kNoPosition); | |
54 } | |
55 | |
56 // Inserts '.result = undefined' in front of the given statement. | |
57 Statement* AssignUndefinedBefore(Statement* s); | |
58 | |
59 private: | |
60 Variable* result_; | |
61 | |
62 // We are not tracking result usage via the result_'s use | |
63 // counts (we leave the accurate computation to the | |
64 // usage analyzer). Instead we simple remember if | |
65 // there was ever an assignment to result_. | |
66 bool result_assigned_; | |
67 | |
68 // When visiting a node, we "return" a replacement for that node in | |
69 // [replacement_]. In many cases this will just be the original node. | |
70 Statement* replacement_; | |
71 | |
72 // To avoid storing to .result all the time, we eliminate some of | |
73 // the stores by keeping track of whether or not we're sure .result | |
74 // will be overwritten anyway. This is a bit more tricky than what I | |
75 // was hoping for. | |
76 bool is_set_; | |
77 | |
78 Zone* zone_; | |
79 Scope* scope_; | |
80 AstNodeFactory factory_; | |
81 | |
82 // Node visitors. | |
83 #define DEF_VISIT(type) void Visit##type(type* node) override; | |
84 AST_NODE_LIST(DEF_VISIT) | |
85 #undef DEF_VISIT | |
86 | |
87 void VisitIterationStatement(IterationStatement* stmt); | |
88 | |
89 DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); | |
90 }; | |
91 | |
92 | |
93 Statement* Processor::AssignUndefinedBefore(Statement* s) { | |
94 Expression* result_proxy = factory()->NewVariableProxy(result_); | |
95 Expression* undef = factory()->NewUndefinedLiteral(RelocInfo::kNoPosition); | |
96 Expression* assignment = factory()->NewAssignment( | |
97 Token::ASSIGN, result_proxy, undef, RelocInfo::kNoPosition); | |
98 Block* b = factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition); | |
99 b->statements()->Add( | |
100 factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), | |
101 zone()); | |
102 b->statements()->Add(s, zone()); | |
103 return b; | |
104 } | |
105 | |
106 | |
107 void Processor::Process(ZoneList<Statement*>* statements) { | |
108 for (int i = statements->length() - 1; i >= 0; --i) { | |
109 Visit(statements->at(i)); | |
110 statements->Set(i, replacement_); | |
111 } | |
112 } | |
113 | |
114 | |
115 void Processor::VisitBlock(Block* node) { | |
116 // An initializer block is the rewritten form of a variable declaration | |
117 // with initialization expressions. The initializer block contains the | |
118 // list of assignments corresponding to the initialization expressions. | |
119 // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of | |
120 // a variable declaration with initialization expression is 'undefined' | |
121 // with some JS VMs: For instance, using smjs, print(eval('var x = 7')) | |
122 // returns 'undefined'. To obtain the same behavior with v8, we need | |
123 // to prevent rewriting in that case. | |
124 if (!node->ignore_completion_value()) Process(node->statements()); | |
125 replacement_ = node; | |
126 } | |
127 | |
128 | |
129 void Processor::VisitExpressionStatement(ExpressionStatement* node) { | |
130 // Rewrite : <x>; -> .result = <x>; | |
131 if (!is_set_) { | |
132 node->set_expression(SetResult(node->expression())); | |
133 is_set_ = true; | |
134 } | |
135 replacement_ = node; | |
136 } | |
137 | |
138 | |
139 void Processor::VisitIfStatement(IfStatement* node) { | |
140 // Rewrite both branches. | |
141 bool set_after = is_set_; | |
142 Visit(node->then_statement()); | |
143 node->set_then_statement(replacement_); | |
144 bool set_in_then = is_set_; | |
145 is_set_ = set_after; | |
146 Visit(node->else_statement()); | |
147 node->set_else_statement(replacement_); | |
148 is_set_ = is_set_ && set_in_then; | |
149 replacement_ = node; | |
150 | |
151 if (FLAG_harmony_completion && !is_set_) { | |
152 is_set_ = true; | |
153 replacement_ = AssignUndefinedBefore(node); | |
154 } | |
155 } | |
156 | |
157 | |
158 void Processor::VisitIterationStatement(IterationStatement* node) { | |
159 // Rewrite the body. | |
160 bool set_after = is_set_; | |
161 is_set_ = false; // We are in a loop, so we can't rely on [set_after]. | |
162 Visit(node->body()); | |
163 node->set_body(replacement_); | |
164 is_set_ = is_set_ && set_after; | |
165 replacement_ = node; | |
166 | |
167 if (FLAG_harmony_completion && !is_set_) { | |
168 is_set_ = true; | |
169 replacement_ = AssignUndefinedBefore(node); | |
170 } | |
171 } | |
172 | |
173 | |
174 void Processor::VisitDoWhileStatement(DoWhileStatement* node) { | |
175 VisitIterationStatement(node); | |
176 } | |
177 | |
178 | |
179 void Processor::VisitWhileStatement(WhileStatement* node) { | |
180 VisitIterationStatement(node); | |
181 } | |
182 | |
183 | |
184 void Processor::VisitForStatement(ForStatement* node) { | |
185 VisitIterationStatement(node); | |
186 } | |
187 | |
188 | |
189 void Processor::VisitForInStatement(ForInStatement* node) { | |
190 VisitIterationStatement(node); | |
191 } | |
192 | |
193 | |
194 void Processor::VisitForOfStatement(ForOfStatement* node) { | |
195 VisitIterationStatement(node); | |
196 } | |
197 | |
198 | |
199 void Processor::VisitTryCatchStatement(TryCatchStatement* node) { | |
200 // Rewrite both try and catch block. | |
201 bool set_after = is_set_; | |
202 Visit(node->try_block()); | |
203 node->set_try_block(static_cast<Block*>(replacement_)); | |
204 bool set_in_try = is_set_; | |
205 is_set_ = set_after; | |
206 Visit(node->catch_block()); | |
207 node->set_catch_block(static_cast<Block*>(replacement_)); | |
208 is_set_ = is_set_ && set_in_try; | |
209 replacement_ = node; | |
210 | |
211 if (FLAG_harmony_completion && !is_set_) { | |
212 is_set_ = true; | |
213 replacement_ = AssignUndefinedBefore(node); | |
214 } | |
215 } | |
216 | |
217 | |
218 void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) { | |
219 // Rewrite both try and finally block (in reverse order). | |
220 bool set_after = is_set_; | |
221 is_set_ = true; // Don't normally need to assign in finally block. | |
222 Visit(node->finally_block()); | |
223 node->set_finally_block(replacement_->AsBlock()); | |
224 { // Save .result value at the beginning of the finally block and restore it | |
225 // at the end again: ".backup = .result; ...; .result = .backup" | |
226 // This is necessary because the finally block does not normally contribute | |
227 // to the completion value. | |
228 Variable* backup = scope()->NewTemporary( | |
229 factory()->ast_value_factory()->dot_result_string()); | |
230 Expression* backup_proxy = factory()->NewVariableProxy(backup); | |
231 Expression* result_proxy = factory()->NewVariableProxy(result_); | |
232 Expression* save = factory()->NewAssignment( | |
233 Token::ASSIGN, backup_proxy, result_proxy, RelocInfo::kNoPosition); | |
234 Expression* restore = factory()->NewAssignment( | |
235 Token::ASSIGN, result_proxy, backup_proxy, RelocInfo::kNoPosition); | |
236 node->finally_block()->statements()->InsertAt( | |
237 0, factory()->NewExpressionStatement(save, RelocInfo::kNoPosition), | |
238 zone()); | |
239 node->finally_block()->statements()->Add( | |
240 factory()->NewExpressionStatement(restore, RelocInfo::kNoPosition), | |
241 zone()); | |
242 } | |
243 is_set_ = set_after; | |
244 Visit(node->try_block()); | |
245 node->set_try_block(replacement_->AsBlock()); | |
246 replacement_ = node; | |
247 | |
248 if (FLAG_harmony_completion && !is_set_) { | |
249 is_set_ = true; | |
250 replacement_ = AssignUndefinedBefore(node); | |
251 } | |
252 } | |
253 | |
254 | |
255 void Processor::VisitSwitchStatement(SwitchStatement* node) { | |
256 // Rewrite statements in all case clauses (in reverse order). | |
257 ZoneList<CaseClause*>* clauses = node->cases(); | |
258 bool set_after = is_set_; | |
259 for (int i = clauses->length() - 1; i >= 0; --i) { | |
260 CaseClause* clause = clauses->at(i); | |
261 Process(clause->statements()); | |
262 } | |
263 is_set_ = is_set_ && set_after; | |
264 replacement_ = node; | |
265 | |
266 if (FLAG_harmony_completion && !is_set_) { | |
267 is_set_ = true; | |
268 replacement_ = AssignUndefinedBefore(node); | |
269 } | |
270 } | |
271 | |
272 | |
273 void Processor::VisitContinueStatement(ContinueStatement* node) { | |
274 is_set_ = false; | |
275 replacement_ = node; | |
276 } | |
277 | |
278 | |
279 void Processor::VisitBreakStatement(BreakStatement* node) { | |
280 is_set_ = false; | |
281 replacement_ = node; | |
282 } | |
283 | |
284 | |
285 void Processor::VisitWithStatement(WithStatement* node) { | |
286 Visit(node->statement()); | |
287 node->set_statement(replacement_); | |
288 replacement_ = node; | |
289 | |
290 if (FLAG_harmony_completion && !is_set_) { | |
291 is_set_ = true; | |
292 replacement_ = AssignUndefinedBefore(node); | |
293 } | |
294 } | |
295 | |
296 | |
297 void Processor::VisitSloppyBlockFunctionStatement( | |
298 SloppyBlockFunctionStatement* node) { | |
299 Visit(node->statement()); | |
300 node->set_statement(replacement_); | |
301 replacement_ = node; | |
302 } | |
303 | |
304 | |
305 void Processor::VisitEmptyStatement(EmptyStatement* node) { | |
306 replacement_ = node; | |
307 } | |
308 | |
309 | |
310 void Processor::VisitReturnStatement(ReturnStatement* node) { | |
311 is_set_ = true; | |
312 replacement_ = node; | |
313 } | |
314 | |
315 | |
316 void Processor::VisitDebuggerStatement(DebuggerStatement* node) { | |
317 replacement_ = node; | |
318 } | |
319 | |
320 | |
321 // Expressions are never visited. | |
322 #define DEF_VISIT(type) \ | |
323 void Processor::Visit##type(type* expr) { UNREACHABLE(); } | |
324 EXPRESSION_NODE_LIST(DEF_VISIT) | |
325 #undef DEF_VISIT | |
326 | |
327 | |
328 // Declarations are never visited. | |
329 #define DEF_VISIT(type) \ | |
330 void Processor::Visit##type(type* expr) { UNREACHABLE(); } | |
331 DECLARATION_NODE_LIST(DEF_VISIT) | |
332 #undef DEF_VISIT | |
333 | |
334 | |
335 // Assumes code has been parsed. Mutates the AST, so the AST should not | |
336 // continue to be used in the case of failure. | |
337 bool Rewriter::Rewrite(ParseInfo* info) { | |
338 FunctionLiteral* function = info->literal(); | |
339 DCHECK(function != NULL); | |
340 Scope* scope = function->scope(); | |
341 DCHECK(scope != NULL); | |
342 if (!scope->is_script_scope() && !scope->is_eval_scope()) return true; | |
343 | |
344 ZoneList<Statement*>* body = function->body(); | |
345 if (!body->is_empty()) { | |
346 Variable* result = | |
347 scope->NewTemporary(info->ast_value_factory()->dot_result_string()); | |
348 // The name string must be internalized at this point. | |
349 DCHECK(!result->name().is_null()); | |
350 Processor processor(info->isolate(), scope, result, | |
351 info->ast_value_factory()); | |
352 processor.Process(body); | |
353 if (processor.HasStackOverflow()) return false; | |
354 | |
355 if (processor.result_assigned()) { | |
356 DCHECK(function->end_position() != RelocInfo::kNoPosition); | |
357 // Set the position of the assignment statement one character past the | |
358 // source code, such that it definitely is not in the source code range | |
359 // of an immediate inner scope. For example in | |
360 // eval('with ({x:1}) x = 1'); | |
361 // the end position of the function generated for executing the eval code | |
362 // coincides with the end of the with scope which is the position of '1'. | |
363 int pos = function->end_position(); | |
364 VariableProxy* result_proxy = | |
365 processor.factory()->NewVariableProxy(result, pos); | |
366 Statement* result_statement = | |
367 processor.factory()->NewReturnStatement(result_proxy, pos); | |
368 body->Add(result_statement, info->zone()); | |
369 } | |
370 } | |
371 | |
372 return true; | |
373 } | |
374 | |
375 | |
376 bool Rewriter::Rewrite(Parser* parser, DoExpression* expr, | |
377 AstValueFactory* factory) { | |
378 Block* block = expr->block(); | |
379 Scope* scope = block->scope(); | |
380 ZoneList<Statement*>* body = block->statements(); | |
381 VariableProxy* result = expr->result(); | |
382 Variable* result_var = result->var(); | |
383 | |
384 if (!body->is_empty()) { | |
385 Processor processor(parser, scope, result_var, factory); | |
386 processor.Process(body); | |
387 if (processor.HasStackOverflow()) return false; | |
388 | |
389 if (!processor.result_assigned()) { | |
390 AstNodeFactory* node_factory = processor.factory(); | |
391 Expression* undef = | |
392 node_factory->NewUndefinedLiteral(RelocInfo::kNoPosition); | |
393 Statement* completion = node_factory->NewExpressionStatement( | |
394 processor.SetResult(undef), expr->position()); | |
395 body->Add(completion, factory->zone()); | |
396 } | |
397 } | |
398 return true; | |
399 } | |
400 | |
401 | |
402 } // namespace internal | |
403 } // namespace v8 | |
OLD | NEW |