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