// 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