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