普通文本  |  1404行  |  38.72 KB

// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/ast/prettyprinter.h"

#include <stdarg.h>

#include "src/ast/ast-value-factory.h"
#include "src/ast/scopes.h"
#include "src/base/platform/platform.h"
#include "src/globals.h"
#include "src/objects-inl.h"
#include "src/string-builder-inl.h"

namespace v8 {
namespace internal {

CallPrinter::CallPrinter(Isolate* isolate, bool is_user_js)
    : builder_(new IncrementalStringBuilder(isolate)) {
  isolate_ = isolate;
  position_ = 0;
  num_prints_ = 0;
  found_ = false;
  done_ = false;
  is_call_error_ = false;
  is_iterator_error_ = false;
  is_async_iterator_error_ = false;
  is_user_js_ = is_user_js;
  function_kind_ = kNormalFunction;
  InitializeAstVisitor(isolate);
}

CallPrinter::~CallPrinter() {}

CallPrinter::ErrorHint CallPrinter::GetErrorHint() const {
  if (is_call_error_) {
    if (is_iterator_error_) return ErrorHint::kCallAndNormalIterator;
    if (is_async_iterator_error_) return ErrorHint::kCallAndAsyncIterator;
  } else {
    if (is_iterator_error_) return ErrorHint::kNormalIterator;
    if (is_async_iterator_error_) return ErrorHint::kAsyncIterator;
  }
  return ErrorHint::kNone;
}

Handle<String> CallPrinter::Print(FunctionLiteral* program, int position) {
  num_prints_ = 0;
  position_ = position;
  Find(program);
  return builder_->Finish().ToHandleChecked();
}


void CallPrinter::Find(AstNode* node, bool print) {
  if (found_) {
    if (print) {
      int prev_num_prints = num_prints_;
      Visit(node);
      if (prev_num_prints != num_prints_) return;
    }
    Print("(intermediate value)");
  } else {
    Visit(node);
  }
}

void CallPrinter::Print(const char* str) {
  if (!found_ || done_) return;
  num_prints_++;
  builder_->AppendCString(str);
}

void CallPrinter::Print(Handle<String> str) {
  if (!found_ || done_) return;
  num_prints_++;
  builder_->AppendString(str);
}

void CallPrinter::VisitBlock(Block* node) {
  FindStatements(node->statements());
}


void CallPrinter::VisitVariableDeclaration(VariableDeclaration* node) {}


void CallPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) {}


void CallPrinter::VisitExpressionStatement(ExpressionStatement* node) {
  Find(node->expression());
}


void CallPrinter::VisitEmptyStatement(EmptyStatement* node) {}


void CallPrinter::VisitSloppyBlockFunctionStatement(
    SloppyBlockFunctionStatement* node) {
  Find(node->statement());
}


void CallPrinter::VisitIfStatement(IfStatement* node) {
  Find(node->condition());
  Find(node->then_statement());
  if (node->HasElseStatement()) {
    Find(node->else_statement());
  }
}


void CallPrinter::VisitContinueStatement(ContinueStatement* node) {}


void CallPrinter::VisitBreakStatement(BreakStatement* node) {}


void CallPrinter::VisitReturnStatement(ReturnStatement* node) {
  Find(node->expression());
}


void CallPrinter::VisitWithStatement(WithStatement* node) {
  Find(node->expression());
  Find(node->statement());
}


void CallPrinter::VisitSwitchStatement(SwitchStatement* node) {
  Find(node->tag());
  for (CaseClause* clause : *node->cases()) {
    if (!clause->is_default()) Find(clause->label());
    FindStatements(clause->statements());
  }
}


void CallPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
  Find(node->body());
  Find(node->cond());
}


void CallPrinter::VisitWhileStatement(WhileStatement* node) {
  Find(node->cond());
  Find(node->body());
}


void CallPrinter::VisitForStatement(ForStatement* node) {
  if (node->init() != nullptr) {
    Find(node->init());
  }
  if (node->cond() != nullptr) Find(node->cond());
  if (node->next() != nullptr) Find(node->next());
  Find(node->body());
}


void CallPrinter::VisitForInStatement(ForInStatement* node) {
  Find(node->each());
  Find(node->enumerable());
  Find(node->body());
}


void CallPrinter::VisitForOfStatement(ForOfStatement* node) {
  Find(node->assign_iterator());
  Find(node->next_result());
  Find(node->result_done());
  Find(node->assign_each());
  Find(node->body());
}


void CallPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
  Find(node->try_block());
  Find(node->catch_block());
}


void CallPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) {
  Find(node->try_block());
  Find(node->finally_block());
}


void CallPrinter::VisitDebuggerStatement(DebuggerStatement* node) {}


void CallPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
  FunctionKind last_function_kind = function_kind_;
  function_kind_ = node->kind();
  FindStatements(node->body());
  function_kind_ = last_function_kind;
}


void CallPrinter::VisitClassLiteral(ClassLiteral* node) {
  if (node->extends()) Find(node->extends());
  for (int i = 0; i < node->properties()->length(); i++) {
    Find(node->properties()->at(i)->value());
  }
}

void CallPrinter::VisitInitializeClassFieldsStatement(
    InitializeClassFieldsStatement* node) {
  for (int i = 0; i < node->fields()->length(); i++) {
    Find(node->fields()->at(i)->value());
  }
}

void CallPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {}


void CallPrinter::VisitDoExpression(DoExpression* node) { Find(node->block()); }


void CallPrinter::VisitConditional(Conditional* node) {
  Find(node->condition());
  Find(node->then_expression());
  Find(node->else_expression());
}


void CallPrinter::VisitLiteral(Literal* node) {
  // TODO(adamk): Teach Literal how to print its values without
  // allocating on the heap.
  PrintLiteral(node->BuildValue(isolate_), true);
}


void CallPrinter::VisitRegExpLiteral(RegExpLiteral* node) {
  Print("/");
  PrintLiteral(node->pattern(), false);
  Print("/");
  if (node->flags() & RegExp::kGlobal) Print("g");
  if (node->flags() & RegExp::kIgnoreCase) Print("i");
  if (node->flags() & RegExp::kMultiline) Print("m");
  if (node->flags() & RegExp::kUnicode) Print("u");
  if (node->flags() & RegExp::kSticky) Print("y");
}


void CallPrinter::VisitObjectLiteral(ObjectLiteral* node) {
  Print("{");
  for (int i = 0; i < node->properties()->length(); i++) {
    Find(node->properties()->at(i)->value());
  }
  Print("}");
}


void CallPrinter::VisitArrayLiteral(ArrayLiteral* node) {
  Print("[");
  for (int i = 0; i < node->values()->length(); i++) {
    if (i != 0) Print(",");
    Expression* subexpr = node->values()->at(i);
    Spread* spread = subexpr->AsSpread();
    if (spread != nullptr && !found_ &&
        position_ == spread->expression()->position()) {
      found_ = true;
      is_iterator_error_ = true;
      Find(spread->expression(), true);
      done_ = true;
      return;
    }
    Find(subexpr, true);
  }
  Print("]");
}


void CallPrinter::VisitVariableProxy(VariableProxy* node) {
  if (is_user_js_) {
    PrintLiteral(node->name(), false);
  } else {
    // Variable names of non-user code are meaningless due to minification.
    Print("(var)");
  }
}


void CallPrinter::VisitAssignment(Assignment* node) {
  Find(node->target());
  Find(node->value());
}

void CallPrinter::VisitCompoundAssignment(CompoundAssignment* node) {
  VisitAssignment(node);
}

void CallPrinter::VisitYield(Yield* node) { Find(node->expression()); }

void CallPrinter::VisitYieldStar(YieldStar* node) {
  if (!found_ && position_ == node->expression()->position()) {
    found_ = true;
    if (IsAsyncFunction(function_kind_))
      is_async_iterator_error_ = true;
    else
      is_iterator_error_ = true;
    Print("yield* ");
  }
  Find(node->expression());
}

void CallPrinter::VisitAwait(Await* node) { Find(node->expression()); }

void CallPrinter::VisitThrow(Throw* node) { Find(node->exception()); }


void CallPrinter::VisitProperty(Property* node) {
  Expression* key = node->key();
  Literal* literal = key->AsLiteral();
  if (literal != nullptr &&
      literal->BuildValue(isolate_)->IsInternalizedString()) {
    Find(node->obj(), true);
    Print(".");
    // TODO(adamk): Teach Literal how to print its values without
    // allocating on the heap.
    PrintLiteral(literal->BuildValue(isolate_), false);
  } else {
    Find(node->obj(), true);
    Print("[");
    Find(key, true);
    Print("]");
  }
}

void CallPrinter::VisitResolvedProperty(ResolvedProperty* node) {}

void CallPrinter::VisitCall(Call* node) {
  bool was_found = false;
  if (node->position() == position_) {
    is_call_error_ = true;
    was_found = !found_;
  }
  if (was_found) {
    // Bail out if the error is caused by a direct call to a variable in
    // non-user JS code. The variable name is meaningless due to minification.
    if (!is_user_js_ && node->expression()->IsVariableProxy()) {
      done_ = true;
      return;
    }
    found_ = true;
  }
  Find(node->expression(), true);
  if (!was_found) Print("(...)");
  FindArguments(node->arguments());
  if (was_found) {
    done_ = true;
    found_ = false;
  }
}


void CallPrinter::VisitCallNew(CallNew* node) {
  bool was_found = false;
  if (node->position() == position_) {
    is_call_error_ = true;
    was_found = !found_;
  }
  if (was_found) {
    // Bail out if the error is caused by a direct call to a variable in
    // non-user JS code. The variable name is meaningless due to minification.
    if (!is_user_js_ && node->expression()->IsVariableProxy()) {
      done_ = true;
      return;
    }
    found_ = true;
  }
  Find(node->expression(), was_found);
  FindArguments(node->arguments());
  if (was_found) {
    done_ = true;
    found_ = false;
  }
}


void CallPrinter::VisitCallRuntime(CallRuntime* node) {
  FindArguments(node->arguments());
}


void CallPrinter::VisitUnaryOperation(UnaryOperation* node) {
  Token::Value op = node->op();
  bool needsSpace =
      op == Token::DELETE || op == Token::TYPEOF || op == Token::VOID;
  Print("(");
  Print(Token::String(op));
  if (needsSpace) Print(" ");
  Find(node->expression(), true);
  Print(")");
}


void CallPrinter::VisitCountOperation(CountOperation* node) {
  Print("(");
  if (node->is_prefix()) Print(Token::String(node->op()));
  Find(node->expression(), true);
  if (node->is_postfix()) Print(Token::String(node->op()));
  Print(")");
}


void CallPrinter::VisitBinaryOperation(BinaryOperation* node) {
  Print("(");
  Find(node->left(), true);
  Print(" ");
  Print(Token::String(node->op()));
  Print(" ");
  Find(node->right(), true);
  Print(")");
}

void CallPrinter::VisitNaryOperation(NaryOperation* node) {
  Print("(");
  Find(node->first(), true);
  for (size_t i = 0; i < node->subsequent_length(); ++i) {
    Print(" ");
    Print(Token::String(node->op()));
    Print(" ");
    Find(node->subsequent(i), true);
  }
  Print(")");
}

void CallPrinter::VisitCompareOperation(CompareOperation* node) {
  Print("(");
  Find(node->left(), true);
  Print(" ");
  Print(Token::String(node->op()));
  Print(" ");
  Find(node->right(), true);
  Print(")");
}


void CallPrinter::VisitSpread(Spread* node) {
  Print("(...");
  Find(node->expression(), true);
  Print(")");
}

void CallPrinter::VisitStoreInArrayLiteral(StoreInArrayLiteral* node) {
  Find(node->array());
  Find(node->index());
  Find(node->value());
}

void CallPrinter::VisitEmptyParentheses(EmptyParentheses* node) {
  UNREACHABLE();
}

void CallPrinter::VisitGetIterator(GetIterator* node) {
  bool was_found = false;
  if (node->position() == position_) {
    is_async_iterator_error_ = node->hint() == IteratorType::kAsync;
    is_iterator_error_ = !is_async_iterator_error_;
    was_found = !found_;
    if (was_found) {
      found_ = true;
    }
  }
  Find(node->iterable_for_call_printer(), true);
  if (was_found) {
    done_ = true;
    found_ = false;
  }
}

void CallPrinter::VisitGetTemplateObject(GetTemplateObject* node) {}

void CallPrinter::VisitTemplateLiteral(TemplateLiteral* node) {
  for (Expression* substitution : *node->substitutions()) {
    Find(substitution, true);
  }
}

void CallPrinter::VisitImportCallExpression(ImportCallExpression* node) {
  Print("ImportCall(");
  Find(node->argument(), true);
  Print(")");
}

void CallPrinter::VisitThisFunction(ThisFunction* node) {}


void CallPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) {}


void CallPrinter::VisitSuperCallReference(SuperCallReference* node) {
  Print("super");
}


void CallPrinter::VisitRewritableExpression(RewritableExpression* node) {
  Find(node->expression());
}

void CallPrinter::FindStatements(ZonePtrList<Statement>* statements) {
  if (statements == nullptr) return;
  for (int i = 0; i < statements->length(); i++) {
    Find(statements->at(i));
  }
}

void CallPrinter::FindArguments(ZonePtrList<Expression>* arguments) {
  if (found_) return;
  for (int i = 0; i < arguments->length(); i++) {
    Find(arguments->at(i));
  }
}

void CallPrinter::PrintLiteral(Handle<Object> value, bool quote) {
  if (value->IsString()) {
    if (quote) Print("\"");
    Print(Handle<String>::cast(value));
    if (quote) Print("\"");
  } else if (value->IsNull(isolate_)) {
    Print("null");
  } else if (value->IsTrue(isolate_)) {
    Print("true");
  } else if (value->IsFalse(isolate_)) {
    Print("false");
  } else if (value->IsUndefined(isolate_)) {
    Print("undefined");
  } else if (value->IsNumber()) {
    Print(isolate_->factory()->NumberToString(value));
  } else if (value->IsSymbol()) {
    // Symbols can only occur as literals if they were inserted by the parser.
    PrintLiteral(handle(Handle<Symbol>::cast(value)->name(), isolate_), false);
  }
}


void CallPrinter::PrintLiteral(const AstRawString* value, bool quote) {
  PrintLiteral(value->string(), quote);
}


//-----------------------------------------------------------------------------


#ifdef DEBUG

const char* AstPrinter::Print(AstNode* node) {
  Init();
  Visit(node);
  return output_;
}

void AstPrinter::Init() {
  if (size_ == 0) {
    DCHECK_NULL(output_);
    const int initial_size = 256;
    output_ = NewArray<char>(initial_size);
    size_ = initial_size;
  }
  output_[0] = '\0';
  pos_ = 0;
}

void AstPrinter::Print(const char* format, ...) {
  for (;;) {
    va_list arguments;
    va_start(arguments, format);
    int n = VSNPrintF(Vector<char>(output_, size_) + pos_,
                      format,
                      arguments);
    va_end(arguments);

    if (n >= 0) {
      // there was enough space - we are done
      pos_ += n;
      return;
    } else {
      // there was not enough space - allocate more and try again
      const int slack = 32;
      int new_size = size_ + (size_ >> 1) + slack;
      char* new_output = NewArray<char>(new_size);
      MemCopy(new_output, output_, pos_);
      DeleteArray(output_);
      output_ = new_output;
      size_ = new_size;
    }
  }
}

void AstPrinter::PrintLabels(ZonePtrList<const AstRawString>* labels) {
  if (labels != nullptr) {
    for (int i = 0; i < labels->length(); i++) {
      PrintLiteral(labels->at(i), false);
      Print(": ");
    }
  }
}

void AstPrinter::PrintLiteral(Literal* literal, bool quote) {
  switch (literal->type()) {
    case Literal::kString:
      PrintLiteral(literal->AsRawString(), quote);
      break;
    case Literal::kSymbol:
      const char* symbol;
      switch (literal->AsSymbol()) {
        case AstSymbol::kHomeObjectSymbol:
          symbol = "HomeObjectSymbol";
      }
      Print("%s", symbol);
      break;
    case Literal::kSmi:
      Print("%d", Smi::ToInt(literal->AsSmiLiteral()));
      break;
    case Literal::kHeapNumber:
      Print("%g", literal->AsNumber());
      break;
    case Literal::kBigInt:
      Print("%sn", literal->AsBigInt().c_str());
      break;
    case Literal::kNull:
      Print("null");
      break;
    case Literal::kUndefined:
      Print("undefined");
      break;
    case Literal::kTheHole:
      Print("the hole");
      break;
    case Literal::kBoolean:
      if (literal->ToBooleanIsTrue()) {
        Print("true");
      } else {
        Print("false");
      }
      break;
  }
}

void AstPrinter::PrintLiteral(const AstRawString* value, bool quote) {
  if (quote) Print("\"");
  if (value != nullptr) {
    const char* format = value->is_one_byte() ? "%c" : "%lc";
    const int increment = value->is_one_byte() ? 1 : 2;
    const unsigned char* raw_bytes = value->raw_data();
    for (int i = 0; i < value->length(); i += increment) {
      Print(format, raw_bytes[i]);
    }
  }
  if (quote) Print("\"");
}

void AstPrinter::PrintLiteral(const AstConsString* value, bool quote) {
  if (quote) Print("\"");
  if (value != nullptr) {
    std::forward_list<const AstRawString*> strings = value->ToRawStrings();
    for (const AstRawString* string : strings) {
      PrintLiteral(string, false);
    }
  }
  if (quote) Print("\"");
}

//-----------------------------------------------------------------------------

class IndentedScope BASE_EMBEDDED {
 public:
  IndentedScope(AstPrinter* printer, const char* txt)
      : ast_printer_(printer) {
    ast_printer_->PrintIndented(txt);
    ast_printer_->Print("\n");
    ast_printer_->inc_indent();
  }

  IndentedScope(AstPrinter* printer, const char* txt, int pos)
      : ast_printer_(printer) {
    ast_printer_->PrintIndented(txt);
    ast_printer_->Print(" at %d\n", pos);
    ast_printer_->inc_indent();
  }

  virtual ~IndentedScope() {
    ast_printer_->dec_indent();
  }

 private:
  AstPrinter* ast_printer_;
};


//-----------------------------------------------------------------------------

AstPrinter::AstPrinter(uintptr_t stack_limit)
    : output_(nullptr), size_(0), pos_(0), indent_(0) {
  InitializeAstVisitor(stack_limit);
}

AstPrinter::~AstPrinter() {
  DCHECK_EQ(indent_, 0);
  DeleteArray(output_);
}


void AstPrinter::PrintIndented(const char* txt) {
  for (int i = 0; i < indent_; i++) {
    Print(". ");
  }
  Print("%s", txt);
}

void AstPrinter::PrintLiteralIndented(const char* info, Literal* literal,
                                      bool quote) {
  PrintIndented(info);
  Print(" ");
  PrintLiteral(literal, quote);
  Print("\n");
}

void AstPrinter::PrintLiteralIndented(const char* info,
                                      const AstRawString* value, bool quote) {
  PrintIndented(info);
  Print(" ");
  PrintLiteral(value, quote);
  Print("\n");
}

void AstPrinter::PrintLiteralIndented(const char* info,
                                      const AstConsString* value, bool quote) {
  PrintIndented(info);
  Print(" ");
  PrintLiteral(value, quote);
  Print("\n");
}

void AstPrinter::PrintLiteralWithModeIndented(const char* info, Variable* var,
                                              const AstRawString* value) {
  if (var == nullptr) {
    PrintLiteralIndented(info, value, true);
  } else {
    EmbeddedVector<char, 256> buf;
    int pos =
        SNPrintF(buf, "%s (%p) (mode = %s", info, reinterpret_cast<void*>(var),
                 VariableMode2String(var->mode()));
    SNPrintF(buf + pos, ")");
    PrintLiteralIndented(buf.start(), value, true);
  }
}

void AstPrinter::PrintLabelsIndented(ZonePtrList<const AstRawString>* labels,
                                     const char* prefix) {
  if (labels == nullptr || labels->length() == 0) return;
  PrintIndented(prefix);
  Print("LABELS ");
  PrintLabels(labels);
  Print("\n");
}


void AstPrinter::PrintIndentedVisit(const char* s, AstNode* node) {
  if (node != nullptr) {
    IndentedScope indent(this, s, node->position());
    Visit(node);
  }
}


const char* AstPrinter::PrintProgram(FunctionLiteral* program) {
  Init();
  { IndentedScope indent(this, "FUNC", program->position());
    PrintIndented("KIND");
    Print(" %d\n", program->kind());
    PrintIndented("SUSPEND COUNT");
    Print(" %d\n", program->suspend_count());
    PrintLiteralIndented("NAME", program->raw_name(), true);
    if (program->raw_inferred_name()) {
      PrintLiteralIndented("INFERRED NAME", program->raw_inferred_name(), true);
    }
    if (program->requires_instance_fields_initializer()) {
      Print(" REQUIRES INSTANCE FIELDS INITIALIZER\n");
    }
    PrintParameters(program->scope());
    PrintDeclarations(program->scope()->declarations());
    PrintStatements(program->body());
  }
  return output_;
}


void AstPrinter::PrintOut(Isolate* isolate, AstNode* node) {
  AstPrinter printer(isolate->stack_guard()->real_climit());
  printer.Init();
  printer.Visit(node);
  PrintF("%s", printer.output_);
}

void AstPrinter::PrintDeclarations(Declaration::List* declarations) {
  if (!declarations->is_empty()) {
    IndentedScope indent(this, "DECLS");
    for (Declaration* decl : *declarations) Visit(decl);
  }
}

void AstPrinter::PrintParameters(DeclarationScope* scope) {
  if (scope->num_parameters() > 0) {
    IndentedScope indent(this, "PARAMS");
    for (int i = 0; i < scope->num_parameters(); i++) {
      PrintLiteralWithModeIndented("VAR", scope->parameter(i),
                                   scope->parameter(i)->raw_name());
    }
  }
}

void AstPrinter::PrintStatements(ZonePtrList<Statement>* statements) {
  for (int i = 0; i < statements->length(); i++) {
    Visit(statements->at(i));
  }
}

void AstPrinter::PrintArguments(ZonePtrList<Expression>* arguments) {
  for (int i = 0; i < arguments->length(); i++) {
    Visit(arguments->at(i));
  }
}


void AstPrinter::VisitBlock(Block* node) {
  const char* block_txt =
      node->ignore_completion_value() ? "BLOCK NOCOMPLETIONS" : "BLOCK";
  IndentedScope indent(this, block_txt, node->position());
  PrintLabelsIndented(node->labels());
  PrintStatements(node->statements());
}


// TODO(svenpanne) Start with IndentedScope.
void AstPrinter::VisitVariableDeclaration(VariableDeclaration* node) {
  PrintLiteralWithModeIndented("VARIABLE", node->proxy()->var(),
                               node->proxy()->raw_name());
}


// TODO(svenpanne) Start with IndentedScope.
void AstPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) {
  PrintIndented("FUNCTION ");
  PrintLiteral(node->proxy()->raw_name(), true);
  Print(" = function ");
  PrintLiteral(node->fun()->raw_name(), false);
  Print("\n");
}


void AstPrinter::VisitExpressionStatement(ExpressionStatement* node) {
  IndentedScope indent(this, "EXPRESSION STATEMENT", node->position());
  Visit(node->expression());
}


void AstPrinter::VisitEmptyStatement(EmptyStatement* node) {
  IndentedScope indent(this, "EMPTY", node->position());
}


void AstPrinter::VisitSloppyBlockFunctionStatement(
    SloppyBlockFunctionStatement* node) {
  Visit(node->statement());
}


void AstPrinter::VisitIfStatement(IfStatement* node) {
  IndentedScope indent(this, "IF", node->position());
  PrintIndentedVisit("CONDITION", node->condition());
  PrintIndentedVisit("THEN", node->then_statement());
  if (node->HasElseStatement()) {
    PrintIndentedVisit("ELSE", node->else_statement());
  }
}


void AstPrinter::VisitContinueStatement(ContinueStatement* node) {
  IndentedScope indent(this, "CONTINUE", node->position());
  PrintLabelsIndented(node->target()->labels());
}


void AstPrinter::VisitBreakStatement(BreakStatement* node) {
  IndentedScope indent(this, "BREAK", node->position());
  PrintLabelsIndented(node->target()->labels());
}


void AstPrinter::VisitReturnStatement(ReturnStatement* node) {
  IndentedScope indent(this, "RETURN", node->position());
  Visit(node->expression());
}


void AstPrinter::VisitWithStatement(WithStatement* node) {
  IndentedScope indent(this, "WITH", node->position());
  PrintIndentedVisit("OBJECT", node->expression());
  PrintIndentedVisit("BODY", node->statement());
}


void AstPrinter::VisitSwitchStatement(SwitchStatement* node) {
  IndentedScope indent(this, "SWITCH", node->position());
  PrintLabelsIndented(node->labels());
  PrintIndentedVisit("TAG", node->tag());
  for (CaseClause* clause : *node->cases()) {
    if (clause->is_default()) {
      IndentedScope indent(this, "DEFAULT");
      PrintStatements(clause->statements());
    } else {
      IndentedScope indent(this, "CASE");
      Visit(clause->label());
      PrintStatements(clause->statements());
    }
  }
}


void AstPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
  IndentedScope indent(this, "DO", node->position());
  PrintLabelsIndented(node->labels());
  PrintLabelsIndented(node->own_labels(), "OWN ");
  PrintIndentedVisit("BODY", node->body());
  PrintIndentedVisit("COND", node->cond());
}


void AstPrinter::VisitWhileStatement(WhileStatement* node) {
  IndentedScope indent(this, "WHILE", node->position());
  PrintLabelsIndented(node->labels());
  PrintLabelsIndented(node->own_labels(), "OWN ");
  PrintIndentedVisit("COND", node->cond());
  PrintIndentedVisit("BODY", node->body());
}


void AstPrinter::VisitForStatement(ForStatement* node) {
  IndentedScope indent(this, "FOR", node->position());
  PrintLabelsIndented(node->labels());
  PrintLabelsIndented(node->own_labels(), "OWN ");
  if (node->init()) PrintIndentedVisit("INIT", node->init());
  if (node->cond()) PrintIndentedVisit("COND", node->cond());
  PrintIndentedVisit("BODY", node->body());
  if (node->next()) PrintIndentedVisit("NEXT", node->next());
}


void AstPrinter::VisitForInStatement(ForInStatement* node) {
  IndentedScope indent(this, "FOR IN", node->position());
  PrintLabelsIndented(node->labels());
  PrintLabelsIndented(node->own_labels(), "OWN ");
  PrintIndentedVisit("FOR", node->each());
  PrintIndentedVisit("IN", node->enumerable());
  PrintIndentedVisit("BODY", node->body());
}


void AstPrinter::VisitForOfStatement(ForOfStatement* node) {
  IndentedScope indent(this, "FOR OF", node->position());
  PrintLabelsIndented(node->labels());
  PrintLabelsIndented(node->own_labels(), "OWN ");
  PrintIndentedVisit("INIT", node->assign_iterator());
  PrintIndentedVisit("NEXT", node->next_result());
  PrintIndentedVisit("DONE", node->result_done());
  PrintIndentedVisit("EACH", node->assign_each());
  PrintIndentedVisit("BODY", node->body());
}


void AstPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
  IndentedScope indent(this, "TRY CATCH", node->position());
  PrintIndentedVisit("TRY", node->try_block());
  PrintIndented("CATCH PREDICTION");
  const char* prediction = "";
  switch (node->GetCatchPrediction(HandlerTable::UNCAUGHT)) {
    case HandlerTable::UNCAUGHT:
      prediction = "UNCAUGHT";
      break;
    case HandlerTable::CAUGHT:
      prediction = "CAUGHT";
      break;
    case HandlerTable::DESUGARING:
      prediction = "DESUGARING";
      break;
    case HandlerTable::ASYNC_AWAIT:
      prediction = "ASYNC_AWAIT";
      break;
    case HandlerTable::PROMISE:
      // Catch prediction resulting in promise rejections aren't
      // parsed by the parser.
      UNREACHABLE();
  }
  Print(" %s\n", prediction);
  if (node->scope()) {
    PrintLiteralWithModeIndented("CATCHVAR", node->scope()->catch_variable(),
                                 node->scope()->catch_variable()->raw_name());
  }
  PrintIndentedVisit("CATCH", node->catch_block());
}

void AstPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) {
  IndentedScope indent(this, "TRY FINALLY", node->position());
  PrintIndentedVisit("TRY", node->try_block());
  PrintIndentedVisit("FINALLY", node->finally_block());
}

void AstPrinter::VisitDebuggerStatement(DebuggerStatement* node) {
  IndentedScope indent(this, "DEBUGGER", node->position());
}


void AstPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
  IndentedScope indent(this, "FUNC LITERAL", node->position());
  PrintLiteralIndented("NAME", node->raw_name(), false);
  PrintLiteralIndented("INFERRED NAME", node->raw_inferred_name(), false);
  PrintParameters(node->scope());
  // We don't want to see the function literal in this case: it
  // will be printed via PrintProgram when the code for it is
  // generated.
  // PrintStatements(node->body());
}


void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
  IndentedScope indent(this, "CLASS LITERAL", node->position());
  PrintLiteralIndented("NAME", node->constructor()->raw_name(), false);
  if (node->extends() != nullptr) {
    PrintIndentedVisit("EXTENDS", node->extends());
  }
  if (node->static_fields_initializer() != nullptr) {
    PrintIndentedVisit("STATIC FIELDS INITIALIZER",
                       node->static_fields_initializer());
  }
  if (node->instance_fields_initializer_function() != nullptr) {
    PrintIndentedVisit("INSTANCE FIELDS INITIALIZER",
                       node->instance_fields_initializer_function());
  }
  PrintClassProperties(node->properties());
}

void AstPrinter::VisitInitializeClassFieldsStatement(
    InitializeClassFieldsStatement* node) {
  IndentedScope indent(this, "INITIALIZE CLASS FIELDS", node->position());
  PrintClassProperties(node->fields());
}

void AstPrinter::PrintClassProperties(
    ZonePtrList<ClassLiteral::Property>* properties) {
  for (int i = 0; i < properties->length(); i++) {
    ClassLiteral::Property* property = properties->at(i);
    const char* prop_kind = nullptr;
    switch (property->kind()) {
      case ClassLiteral::Property::METHOD:
        prop_kind = "METHOD";
        break;
      case ClassLiteral::Property::GETTER:
        prop_kind = "GETTER";
        break;
      case ClassLiteral::Property::SETTER:
        prop_kind = "SETTER";
        break;
      case ClassLiteral::Property::PUBLIC_FIELD:
        prop_kind = "PUBLIC FIELD";
        break;
      case ClassLiteral::Property::PRIVATE_FIELD:
        prop_kind = "PRIVATE FIELD";
        break;
    }
    EmbeddedVector<char, 128> buf;
    SNPrintF(buf, "PROPERTY%s - %s", property->is_static() ? " - STATIC" : "",
             prop_kind);
    IndentedScope prop(this, buf.start());
    PrintIndentedVisit("KEY", properties->at(i)->key());
    PrintIndentedVisit("VALUE", properties->at(i)->value());
  }
}


void AstPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {
  IndentedScope indent(this, "NATIVE FUNC LITERAL", node->position());
  PrintLiteralIndented("NAME", node->raw_name(), false);
}


void AstPrinter::VisitDoExpression(DoExpression* node) {
  IndentedScope indent(this, "DO EXPRESSION", node->position());
  PrintStatements(node->block()->statements());
}


void AstPrinter::VisitConditional(Conditional* node) {
  IndentedScope indent(this, "CONDITIONAL", node->position());
  PrintIndentedVisit("CONDITION", node->condition());
  PrintIndentedVisit("THEN", node->then_expression());
  PrintIndentedVisit("ELSE", node->else_expression());
}


void AstPrinter::VisitLiteral(Literal* node) {
  PrintLiteralIndented("LITERAL", node, true);
}


void AstPrinter::VisitRegExpLiteral(RegExpLiteral* node) {
  IndentedScope indent(this, "REGEXP LITERAL", node->position());
  PrintLiteralIndented("PATTERN", node->raw_pattern(), false);
  int i = 0;
  EmbeddedVector<char, 128> buf;
  if (node->flags() & RegExp::kGlobal) buf[i++] = 'g';
  if (node->flags() & RegExp::kIgnoreCase) buf[i++] = 'i';
  if (node->flags() & RegExp::kMultiline) buf[i++] = 'm';
  if (node->flags() & RegExp::kUnicode) buf[i++] = 'u';
  if (node->flags() & RegExp::kSticky) buf[i++] = 'y';
  buf[i] = '\0';
  PrintIndented("FLAGS ");
  Print("%s", buf.start());
  Print("\n");
}


void AstPrinter::VisitObjectLiteral(ObjectLiteral* node) {
  IndentedScope indent(this, "OBJ LITERAL", node->position());
  PrintObjectProperties(node->properties());
}

void AstPrinter::PrintObjectProperties(
    ZonePtrList<ObjectLiteral::Property>* properties) {
  for (int i = 0; i < properties->length(); i++) {
    ObjectLiteral::Property* property = properties->at(i);
    const char* prop_kind = nullptr;
    switch (property->kind()) {
      case ObjectLiteral::Property::CONSTANT:
        prop_kind = "CONSTANT";
        break;
      case ObjectLiteral::Property::COMPUTED:
        prop_kind = "COMPUTED";
        break;
      case ObjectLiteral::Property::MATERIALIZED_LITERAL:
        prop_kind = "MATERIALIZED_LITERAL";
        break;
      case ObjectLiteral::Property::PROTOTYPE:
        prop_kind = "PROTOTYPE";
        break;
      case ObjectLiteral::Property::GETTER:
        prop_kind = "GETTER";
        break;
      case ObjectLiteral::Property::SETTER:
        prop_kind = "SETTER";
        break;
      case ObjectLiteral::Property::SPREAD:
        prop_kind = "SPREAD";
        break;
    }
    EmbeddedVector<char, 128> buf;
    SNPrintF(buf, "PROPERTY - %s", prop_kind);
    IndentedScope prop(this, buf.start());
    PrintIndentedVisit("KEY", properties->at(i)->key());
    PrintIndentedVisit("VALUE", properties->at(i)->value());
  }
}


void AstPrinter::VisitArrayLiteral(ArrayLiteral* node) {
  IndentedScope indent(this, "ARRAY LITERAL", node->position());
  if (node->values()->length() > 0) {
    IndentedScope indent(this, "VALUES", node->position());
    for (int i = 0; i < node->values()->length(); i++) {
      Visit(node->values()->at(i));
    }
  }
}


void AstPrinter::VisitVariableProxy(VariableProxy* node) {
  EmbeddedVector<char, 128> buf;
  int pos = SNPrintF(buf, "VAR PROXY");

  if (!node->is_resolved()) {
    SNPrintF(buf + pos, " unresolved");
    PrintLiteralWithModeIndented(buf.start(), nullptr, node->raw_name());
  } else {
    Variable* var = node->var();
    switch (var->location()) {
      case VariableLocation::UNALLOCATED:
        SNPrintF(buf + pos, " unallocated");
        break;
      case VariableLocation::PARAMETER:
        SNPrintF(buf + pos, " parameter[%d]", var->index());
        break;
      case VariableLocation::LOCAL:
        SNPrintF(buf + pos, " local[%d]", var->index());
        break;
      case VariableLocation::CONTEXT:
        SNPrintF(buf + pos, " context[%d]", var->index());
        break;
      case VariableLocation::LOOKUP:
        SNPrintF(buf + pos, " lookup");
        break;
      case VariableLocation::MODULE:
        SNPrintF(buf + pos, " module");
        break;
    }
    PrintLiteralWithModeIndented(buf.start(), var, node->raw_name());
  }
}


void AstPrinter::VisitAssignment(Assignment* node) {
  IndentedScope indent(this, Token::Name(node->op()), node->position());
  Visit(node->target());
  Visit(node->value());
}

void AstPrinter::VisitCompoundAssignment(CompoundAssignment* node) {
  VisitAssignment(node);
}

void AstPrinter::VisitYield(Yield* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "YIELD");
  IndentedScope indent(this, buf.start(), node->position());
  Visit(node->expression());
}

void AstPrinter::VisitYieldStar(YieldStar* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "YIELD_STAR");
  IndentedScope indent(this, buf.start(), node->position());
  Visit(node->expression());
}

void AstPrinter::VisitAwait(Await* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "AWAIT");
  IndentedScope indent(this, buf.start(), node->position());
  Visit(node->expression());
}

void AstPrinter::VisitThrow(Throw* node) {
  IndentedScope indent(this, "THROW", node->position());
  Visit(node->exception());
}

void AstPrinter::VisitProperty(Property* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "PROPERTY");
  IndentedScope indent(this, buf.start(), node->position());

  Visit(node->obj());
  LhsKind property_kind = Property::GetAssignType(node);
  if (property_kind == NAMED_PROPERTY ||
      property_kind == NAMED_SUPER_PROPERTY) {
    PrintLiteralIndented("NAME", node->key()->AsLiteral(), false);
  } else {
    DCHECK(property_kind == KEYED_PROPERTY ||
           property_kind == KEYED_SUPER_PROPERTY);
    PrintIndentedVisit("KEY", node->key());
  }
}

void AstPrinter::VisitResolvedProperty(ResolvedProperty* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "RESOLVED-PROPERTY");
  IndentedScope indent(this, buf.start(), node->position());

  PrintIndentedVisit("RECEIVER", node->object());
  PrintIndentedVisit("PROPERTY", node->property());
}

void AstPrinter::VisitCall(Call* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "CALL");
  IndentedScope indent(this, buf.start());

  Visit(node->expression());
  PrintArguments(node->arguments());
}


void AstPrinter::VisitCallNew(CallNew* node) {
  IndentedScope indent(this, "CALL NEW", node->position());
  Visit(node->expression());
  PrintArguments(node->arguments());
}


void AstPrinter::VisitCallRuntime(CallRuntime* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "CALL RUNTIME %s%s", node->debug_name(),
           node->is_jsruntime() ? " (JS function)" : "");
  IndentedScope indent(this, buf.start(), node->position());
  PrintArguments(node->arguments());
}


void AstPrinter::VisitUnaryOperation(UnaryOperation* node) {
  IndentedScope indent(this, Token::Name(node->op()), node->position());
  Visit(node->expression());
}


void AstPrinter::VisitCountOperation(CountOperation* node) {
  EmbeddedVector<char, 128> buf;
  SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
           Token::Name(node->op()));
  IndentedScope indent(this, buf.start(), node->position());
  Visit(node->expression());
}


void AstPrinter::VisitBinaryOperation(BinaryOperation* node) {
  IndentedScope indent(this, Token::Name(node->op()), node->position());
  Visit(node->left());
  Visit(node->right());
}

void AstPrinter::VisitNaryOperation(NaryOperation* node) {
  IndentedScope indent(this, Token::Name(node->op()), node->position());
  Visit(node->first());
  for (size_t i = 0; i < node->subsequent_length(); ++i) {
    Visit(node->subsequent(i));
  }
}

void AstPrinter::VisitCompareOperation(CompareOperation* node) {
  IndentedScope indent(this, Token::Name(node->op()), node->position());
  Visit(node->left());
  Visit(node->right());
}


void AstPrinter::VisitSpread(Spread* node) {
  IndentedScope indent(this, "SPREAD", node->position());
  Visit(node->expression());
}

void AstPrinter::VisitStoreInArrayLiteral(StoreInArrayLiteral* node) {
  IndentedScope indent(this, "STORE IN ARRAY LITERAL", node->position());
  PrintIndentedVisit("ARRAY", node->array());
  PrintIndentedVisit("INDEX", node->index());
  PrintIndentedVisit("VALUE", node->value());
}

void AstPrinter::VisitEmptyParentheses(EmptyParentheses* node) {
  IndentedScope indent(this, "()", node->position());
}

void AstPrinter::VisitGetIterator(GetIterator* node) {
  IndentedScope indent(this, "GET-ITERATOR", node->position());
  Visit(node->iterable());
}

void AstPrinter::VisitGetTemplateObject(GetTemplateObject* node) {
  IndentedScope indent(this, "GET-TEMPLATE-OBJECT", node->position());
}

void AstPrinter::VisitTemplateLiteral(TemplateLiteral* node) {
  IndentedScope indent(this, "TEMPLATE-LITERAL", node->position());
  const AstRawString* string = node->string_parts()->first();
  if (!string->IsEmpty()) PrintLiteralIndented("SPAN", string, true);
  for (int i = 0; i < node->substitutions()->length();) {
    PrintIndentedVisit("EXPR", node->substitutions()->at(i++));
    if (i < node->string_parts()->length()) {
      string = node->string_parts()->at(i);
      if (!string->IsEmpty()) PrintLiteralIndented("SPAN", string, true);
    }
  }
}

void AstPrinter::VisitImportCallExpression(ImportCallExpression* node) {
  IndentedScope indent(this, "IMPORT-CALL", node->position());
  Visit(node->argument());
}

void AstPrinter::VisitThisFunction(ThisFunction* node) {
  IndentedScope indent(this, "THIS-FUNCTION", node->position());
}


void AstPrinter::VisitSuperPropertyReference(SuperPropertyReference* node) {
  IndentedScope indent(this, "SUPER-PROPERTY-REFERENCE", node->position());
}


void AstPrinter::VisitSuperCallReference(SuperCallReference* node) {
  IndentedScope indent(this, "SUPER-CALL-REFERENCE", node->position());
}


void AstPrinter::VisitRewritableExpression(RewritableExpression* node) {
  Visit(node->expression());
}


#endif  // DEBUG

}  // namespace internal
}  // namespace v8