• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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