rewriter.cc 13.8 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/parsing/rewriter.h"
6

7 8
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
9
#include "src/objects/objects-inl.h"
10
#include "src/parsing/parse-info.h"
11
#include "src/parsing/parser.h"
12
#include "src/zone/zone-list-inl.h"
13

14 15
namespace v8 {
namespace internal {
16

17
class Processor final : public AstVisitor<Processor> {
18
 public:
19
  Processor(uintptr_t stack_limit, DeclarationScope* closure_scope,
20
            Variable* result, AstValueFactory* ast_value_factory, Zone* zone)
21
      : result_(result),
22
        replacement_(nullptr),
23
        zone_(zone),
24
        closure_scope_(closure_scope),
25
        factory_(ast_value_factory, zone),
26 27 28
        result_assigned_(false),
        is_set_(false),
        breakable_(false) {
29
    DCHECK_EQ(closure_scope, closure_scope->GetClosureScope());
30
    InitializeAstVisitor(stack_limit);
31
  }
32

33
  Processor(Parser* parser, DeclarationScope* closure_scope, Variable* result,
34
            AstValueFactory* ast_value_factory, Zone* zone)
35 36
      : result_(result),
        replacement_(nullptr),
37
        zone_(zone),
38
        closure_scope_(closure_scope),
39 40 41 42
        factory_(ast_value_factory, zone_),
        result_assigned_(false),
        is_set_(false),
        breakable_(false) {
43
    DCHECK_EQ(closure_scope, closure_scope->GetClosureScope());
44 45 46
    InitializeAstVisitor(parser->stack_limit());
  }

47
  void Process(ZonePtrList<Statement>* statements);
48
  bool result_assigned() const { return result_assigned_; }
49

50
  Zone* zone() { return zone_; }
51
  DeclarationScope* closure_scope() { return closure_scope_; }
52
  AstNodeFactory* factory() { return &factory_; }
53

54 55 56 57 58
  // Returns ".result = value"
  Expression* SetResult(Expression* value) {
    result_assigned_ = true;
    VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
    return factory()->NewAssignment(Token::ASSIGN, result_proxy, value,
yangguo's avatar
yangguo committed
59
                                    kNoSourcePosition);
60 61 62 63 64
  }

  // Inserts '.result = undefined' in front of the given statement.
  Statement* AssignUndefinedBefore(Statement* s);

65
 private:
66
  Variable* result_;
67

68 69 70 71
  // When visiting a node, we "return" a replacement for that node in
  // [replacement_].  In many cases this will just be the original node.
  Statement* replacement_;

72
  class V8_NODISCARD BreakableScope final {
73 74 75 76 77 78 79 80 81 82 83 84 85
   public:
    explicit BreakableScope(Processor* processor, bool breakable = true)
        : processor_(processor), previous_(processor->breakable_) {
      processor->breakable_ = processor->breakable_ || breakable;
    }

    ~BreakableScope() { processor_->breakable_ = previous_; }

   private:
    Processor* processor_;
    bool previous_;
  };

86
  Zone* zone_;
87
  DeclarationScope* closure_scope_;
88
  AstNodeFactory factory_;
89

90
  // Node visitors.
91
#define DEF_VISIT(type) void Visit##type(type* node);
92
  AST_NODE_LIST(DEF_VISIT)
93
#undef DEF_VISIT
94 95

  void VisitIterationStatement(IterationStatement* stmt);
96 97

  DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
98 99 100 101 102 103 104 105 106 107 108 109 110 111

  // We are not tracking result usage via the result_'s use
  // counts (we leave the accurate computation to the
  // usage analyzer). Instead we simple remember if
  // there was ever an assignment to result_.
  bool result_assigned_;

  // To avoid storing to .result all the time, we eliminate some of
  // the stores by keeping track of whether or not we're sure .result
  // will be overwritten anyway. This is a bit more tricky than what I
  // was hoping for.
  bool is_set_;

  bool breakable_;
112 113 114
};


115
Statement* Processor::AssignUndefinedBefore(Statement* s) {
yangguo's avatar
yangguo committed
116
  Expression* undef = factory()->NewUndefinedLiteral(kNoSourcePosition);
117
  Expression* assignment = SetResult(undef);
118
  Block* b = factory()->NewBlock(2, false);
119
  b->statements()->Add(
yangguo's avatar
yangguo committed
120
      factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
121 122 123 124
  b->statements()->Add(s, zone());
  return b;
}

125
void Processor::Process(ZonePtrList<Statement>* statements) {
126 127 128 129 130 131 132
  // If we're in a breakable scope (named block, iteration, or switch), we walk
  // all statements. The last value producing statement before the break needs
  // to assign to .result. If we're not in a breakable scope, only the last
  // value producing statement in the block assigns to .result, so we can stop
  // early.
  for (int i = statements->length() - 1; i >= 0 && (breakable_ || !is_set_);
       --i) {
133
    Visit(statements->at(i));
134
    statements->Set(i, replacement_);
135 136 137 138 139 140 141 142 143 144 145 146 147
  }
}


void Processor::VisitBlock(Block* node) {
  // An initializer block is the rewritten form of a variable declaration
  // with initialization expressions. The initializer block contains the
  // list of assignments corresponding to the initialization expressions.
  // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of
  // a variable declaration with initialization expression is 'undefined'
  // with some JS VMs: For instance, using smjs, print(eval('var x = 7'))
  // returns 'undefined'. To obtain the same behavior with v8, we need
  // to prevent rewriting in that case.
148
  if (!node->ignore_completion_value()) {
149
    BreakableScope scope(this, node->is_breakable());
150 151
    Process(node->statements());
  }
152
  replacement_ = node;
153 154 155 156 157
}


void Processor::VisitExpressionStatement(ExpressionStatement* node) {
  // Rewrite : <x>; -> .result = <x>;
158
  if (!is_set_) {
159
    node->set_expression(SetResult(node->expression()));
160
    is_set_ = true;
161
  }
162
  replacement_ = node;
163 164 165 166
}


void Processor::VisitIfStatement(IfStatement* node) {
167 168
  // Rewrite both branches.
  bool set_after = is_set_;
169

170
  Visit(node->then_statement());
171
  node->set_then_statement(replacement_);
172
  bool set_in_then = is_set_;
173

174 175
  is_set_ = set_after;
  Visit(node->else_statement());
176
  node->set_else_statement(replacement_);
177

178 179
  replacement_ = set_in_then && is_set_ ? node : AssignUndefinedBefore(node);
  is_set_ = true;
180 181 182
}


183
void Processor::VisitIterationStatement(IterationStatement* node) {
184 185 186 187 188
  // The statement may have to produce a value, so always assign undefined
  // before.
  // TODO(verwaest): Omit it if we know that there's no break/continue leaving
  // it early.
  DCHECK(breakable_ || !is_set_);
189
  BreakableScope scope(this);
190

191
  Visit(node->body());
192
  node->set_body(replacement_);
193

194 195
  replacement_ = AssignUndefinedBefore(node);
  is_set_ = true;
196 197 198
}


199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
  VisitIterationStatement(node);
}


void Processor::VisitWhileStatement(WhileStatement* node) {
  VisitIterationStatement(node);
}


void Processor::VisitForStatement(ForStatement* node) {
  VisitIterationStatement(node);
}


214
void Processor::VisitForInStatement(ForInStatement* node) {
215
  VisitIterationStatement(node);
216 217 218
}


219 220 221 222 223
void Processor::VisitForOfStatement(ForOfStatement* node) {
  VisitIterationStatement(node);
}


224
void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
225 226
  // Rewrite both try and catch block.
  bool set_after = is_set_;
227

228
  Visit(node->try_block());
229
  node->set_try_block(static_cast<Block*>(replacement_));
230
  bool set_in_try = is_set_;
231

232 233
  is_set_ = set_after;
  Visit(node->catch_block());
234
  node->set_catch_block(static_cast<Block*>(replacement_));
235

236 237
  replacement_ = is_set_ && set_in_try ? node : AssignUndefinedBefore(node);
  is_set_ = true;
238 239 240
}


241
void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
242 243 244 245 246 247 248 249
  // Only rewrite finally if it could contain 'break' or 'continue'. Always
  // rewrite try.
  if (breakable_) {
    // Only set result before a 'break' or 'continue'.
    is_set_ = true;
    Visit(node->finally_block());
    node->set_finally_block(replacement_->AsBlock());
    CHECK_NOT_NULL(closure_scope());
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
    if (is_set_) {
      // Save .result value at the beginning of the finally block and restore it
      // at the end again: ".backup = .result; ...; .result = .backup" This is
      // necessary because the finally block does not normally contribute to the
      // completion value.
      Variable* backup = closure_scope()->NewTemporary(
          factory()->ast_value_factory()->dot_result_string());
      Expression* backup_proxy = factory()->NewVariableProxy(backup);
      Expression* result_proxy = factory()->NewVariableProxy(result_);
      Expression* save = factory()->NewAssignment(
          Token::ASSIGN, backup_proxy, result_proxy, kNoSourcePosition);
      Expression* restore = factory()->NewAssignment(
          Token::ASSIGN, result_proxy, backup_proxy, kNoSourcePosition);
      node->finally_block()->statements()->InsertAt(
          0, factory()->NewExpressionStatement(save, kNoSourcePosition),
          zone());
      node->finally_block()->statements()->Add(
          factory()->NewExpressionStatement(restore, kNoSourcePosition),
          zone());
    } else {
      // If is_set_ is false, it means the finally block has a 'break' or a
      // 'continue' and was not preceded by a statement that assigned to
      // .result. Try-finally statements return the abrupt completions from the
      // finally block, meaning this case should get an undefined.
      //
      // Since the finally block will definitely result in an abrupt completion,
      // there's no need to save and restore the .result.
      Expression* undef = factory()->NewUndefinedLiteral(kNoSourcePosition);
      Expression* assignment = SetResult(undef);
      node->finally_block()->statements()->InsertAt(
          0, factory()->NewExpressionStatement(assignment, kNoSourcePosition),
          zone());
    }
283 284 285
    // We can't tell whether the finally-block is guaranteed to set .result, so
    // reset is_set_ before visiting the try-block.
    is_set_ = false;
neis's avatar
neis committed
286 287
  }
  Visit(node->try_block());
288
  node->set_try_block(replacement_->AsBlock());
289

290 291
  replacement_ = is_set_ ? node : AssignUndefinedBefore(node);
  is_set_ = true;
292 293 294 295
}


void Processor::VisitSwitchStatement(SwitchStatement* node) {
296 297 298 299 300
  // The statement may have to produce a value, so always assign undefined
  // before.
  // TODO(verwaest): Omit it if we know that there's no break/continue leaving
  // it early.
  DCHECK(breakable_ || !is_set_);
301
  BreakableScope scope(this);
302
  // Rewrite statements in all case clauses.
303
  ZonePtrList<CaseClause>* clauses = node->cases();
304 305 306 307
  for (int i = clauses->length() - 1; i >= 0; --i) {
    CaseClause* clause = clauses->at(i);
    Process(clause->statements());
  }
308

309 310
  replacement_ = AssignUndefinedBefore(node);
  is_set_ = true;
311 312 313 314 315
}


void Processor::VisitContinueStatement(ContinueStatement* node) {
  is_set_ = false;
316
  replacement_ = node;
317 318 319 320 321
}


void Processor::VisitBreakStatement(BreakStatement* node) {
  is_set_ = false;
322
  replacement_ = node;
323 324 325
}


326 327
void Processor::VisitWithStatement(WithStatement* node) {
  Visit(node->statement());
328
  node->set_statement(replacement_);
329

330 331
  replacement_ = is_set_ ? node : AssignUndefinedBefore(node);
  is_set_ = true;
332 333 334
}


335 336 337
void Processor::VisitSloppyBlockFunctionStatement(
    SloppyBlockFunctionStatement* node) {
  Visit(node->statement());
338 339
  node->set_statement(replacement_);
  replacement_ = node;
340 341 342
}


343 344 345
void Processor::VisitEmptyStatement(EmptyStatement* node) {
  replacement_ = node;
}
346 347


348 349 350 351 352 353 354 355 356
void Processor::VisitReturnStatement(ReturnStatement* node) {
  is_set_ = true;
  replacement_ = node;
}


void Processor::VisitDebuggerStatement(DebuggerStatement* node) {
  replacement_ = node;
}
357

358 359
void Processor::VisitInitializeClassMembersStatement(
    InitializeClassMembersStatement* node) {
360 361
  replacement_ = node;
}
362

363 364 365 366 367
void Processor::VisitInitializeClassStaticElementsStatement(
    InitializeClassStaticElementsStatement* node) {
  replacement_ = node;
}

368
// Expressions are never visited.
369 370 371 372
#define DEF_VISIT(type)                                         \
  void Processor::Visit##type(type* expr) { UNREACHABLE(); }
EXPRESSION_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
373 374


375 376 377 378 379 380 381
// Declarations are never visited.
#define DEF_VISIT(type) \
  void Processor::Visit##type(type* expr) { UNREACHABLE(); }
DECLARATION_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT


382 383
// Assumes code has been parsed.  Mutates the AST, so the AST should not
// continue to be used in the case of failure.
384
bool Rewriter::Rewrite(ParseInfo* info) {
385 386 387
  RCS_SCOPE(info->runtime_call_stats(),
            RuntimeCallCounterId::kCompileRewriteReturnResult,
            RuntimeCallStats::kThreadSpecific);
388

389
  FunctionLiteral* function = info->literal();
390
  DCHECK_NOT_NULL(function);
391
  Scope* scope = function->scope();
392
  DCHECK_NOT_NULL(scope);
393
  DCHECK_EQ(scope, scope->GetClosureScope());
394

395
  if (scope->is_repl_mode_scope()) return true;
396 397 398 399
  if (!(scope->is_script_scope() || scope->is_eval_scope() ||
        scope->is_module_scope())) {
    return true;
  }
400

401
  ZonePtrList<Statement>* body = function->body();
402 403 404 405 406
  return RewriteBody(info, scope, body).has_value();
}

base::Optional<VariableProxy*> Rewriter::RewriteBody(
    ParseInfo* info, Scope* scope, ZonePtrList<Statement>* body) {
407
  DisallowGarbageCollection no_gc;
408 409 410
  DisallowHandleAllocation no_handles;
  DisallowHandleDereference no_deref;

411
  DCHECK_IMPLIES(scope->is_module_scope(), !body->is_empty());
412
  if (!body->is_empty()) {
413
    Variable* result = scope->AsDeclarationScope()->NewTemporary(
414
        info->ast_value_factory()->dot_result_string());
415
    Processor processor(info->stack_limit(), scope->AsDeclarationScope(),
416
                        result, info->ast_value_factory(), info->zone());
417
    processor.Process(body);
418

419 420 421
    DCHECK_IMPLIES(scope->is_module_scope(), processor.result_assigned());
    if (processor.result_assigned()) {
      int pos = kNoSourcePosition;
422
      VariableProxy* result_value =
423
          processor.factory()->NewVariableProxy(result, pos);
424
      if (!info->flags().is_repl_mode()) {
425 426 427 428 429
        Statement* result_statement =
            processor.factory()->NewReturnStatement(result_value, pos);
        body->Add(result_statement, info->zone());
      }
      return result_value;
430 431
    }

432 433 434 435
    if (processor.HasStackOverflow()) {
      info->pending_error_handler()->set_stack_overflow();
      return base::nullopt;
    }
436
  }
437
  return nullptr;
438 439
}

440 441
}  // namespace internal
}  // namespace v8