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