Initial implementation of printing the AST as a JSON string. This
authorkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 12 Oct 2009 15:06:28 +0000 (15:06 +0000)
committerkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 12 Oct 2009 15:06:28 +0000 (15:06 +0000)
implementation is not yet complete (it prints only the node name for
some AST constructs) and does not serialize everything needed to
reconstitute the AST.

It is motivated by a desire to prototype source-to-source
transformations in JavaScript itself (or anything else that can grok
JSON), but it should have other uses too.

Feedback is welcome.
Review URL: http://codereview.chromium.org/131101

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3051 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ast.cc
src/ast.h
src/codegen.cc
src/flag-definitions.h
src/prettyprinter.cc
src/prettyprinter.h

index 52bef62..f6864b8 100644 (file)
@@ -173,6 +173,13 @@ void TargetCollector::AddTarget(BreakTarget* target) {
 // Implementation of AstVisitor
 
 
+void AstVisitor::VisitDeclarations(ZoneList<Declaration*>* declarations) {
+  for (int i = 0; i < declarations->length(); i++) {
+    Visit(declarations->at(i));
+  }
+}
+
+
 void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) {
   for (int i = 0; i < statements->length(); i++) {
     Visit(statements->at(i));
index 5f5b1cb..584a9e1 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1732,6 +1732,7 @@ class AstVisitor BASE_EMBEDDED {
   void Visit(AstNode* node) { node->Accept(this); }
 
   // Iteration
+  virtual void VisitDeclarations(ZoneList<Declaration*>* declarations);
   virtual void VisitStatements(ZoneList<Statement*>* statements);
   virtual void VisitExpressions(ZoneList<Expression*>* expressions);
 
index eb363bd..b70dd2d 100644 (file)
@@ -140,15 +140,18 @@ Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit,
 #ifdef DEBUG
   bool print_source = false;
   bool print_ast = false;
+  bool print_json_ast = false;
   const char* ftype;
 
   if (Bootstrapper::IsActive()) {
     print_source = FLAG_print_builtin_source;
     print_ast = FLAG_print_builtin_ast;
+    print_json_ast = FLAG_print_builtin_json_ast;
     ftype = "builtin";
   } else {
     print_source = FLAG_print_source;
     print_ast = FLAG_print_ast;
+    print_json_ast = FLAG_print_json_ast;
     ftype = "user-defined";
   }
 
@@ -165,6 +168,11 @@ Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit,
   if (print_ast) {
     PrintF("--- AST ---\n%s\n", AstPrinter().PrintProgram(flit));
   }
+
+  if (print_json_ast) {
+    JsonAstBuilder builder;
+    PrintF("%s", builder.BuildProgram(flit));
+  }
 #endif  // DEBUG
 
   // Generate code.
index 54e1cd4..46ec28f 100644 (file)
@@ -273,6 +273,9 @@ DEFINE_bool(print_builtin_source, false,
             "pretty print source code for builtins")
 DEFINE_bool(print_ast, false, "print source AST")
 DEFINE_bool(print_builtin_ast, false, "print source AST for builtins")
+DEFINE_bool(print_json_ast, false, "print source AST as JSON")
+DEFINE_bool(print_builtin_json_ast, false,
+            "print source AST for builtins as JSON")
 DEFINE_bool(trace_calls, false, "trace calls")
 DEFINE_bool(trace_builtin_calls, false, "trace builtins calls")
 DEFINE_string(stop_at, "", "function name where to insert a breakpoint")
index 50fe958..10c1ea8 100644 (file)
@@ -1100,6 +1100,414 @@ void AstPrinter::VisitThisFunction(ThisFunction* node) {
 }
 
 
+TagScope::TagScope(JsonAstBuilder* builder, const char* name)
+    : builder_(builder), next_(builder->tag()), has_body_(false) {
+  if (next_ != NULL) {
+    next_->use();
+    builder->Print(",\n");
+  }
+  builder->set_tag(this);
+  builder->PrintIndented("[");
+  builder->Print("\"%s\"", name);
+  builder->increase_indent(JsonAstBuilder::kTagIndentSize);
+}
+
+
+TagScope::~TagScope() {
+  builder_->decrease_indent(JsonAstBuilder::kTagIndentSize);
+  if (has_body_) {
+    builder_->Print("\n");
+    builder_->PrintIndented("]");
+  } else {
+    builder_->Print("]");
+  }
+  builder_->set_tag(next_);
+}
+
+
+AttributesScope::AttributesScope(JsonAstBuilder* builder)
+    : builder_(builder), attribute_count_(0) {
+  builder->set_attributes(this);
+  builder->tag()->use();
+  builder->Print(",\n");
+  builder->PrintIndented("{");
+  builder->increase_indent(JsonAstBuilder::kAttributesIndentSize);
+}
+
+
+AttributesScope::~AttributesScope() {
+  builder_->decrease_indent(JsonAstBuilder::kAttributesIndentSize);
+  if (attribute_count_ > 1) {
+    builder_->Print("\n");
+    builder_->PrintIndented("}");
+  } else {
+    builder_->Print("}");
+  }
+  builder_->set_attributes(NULL);
+}
+
+
+const char* JsonAstBuilder::BuildProgram(FunctionLiteral* program) {
+  Init();
+  Visit(program);
+  Print("\n");
+  return Output();
+}
+
+
+void JsonAstBuilder::AddAttributePrefix(const char* name) {
+  if (attributes()->is_used()) {
+    Print(",\n");
+    PrintIndented("\"");
+  } else {
+    Print("\"");
+  }
+  Print("%s\":", name);
+  attributes()->use();
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, Handle<String> value) {
+  SmartPointer<char> value_string = value->ToCString();
+  AddAttributePrefix(name);
+  Print("\"%s\"", *value_string);
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, const char* value) {
+  AddAttributePrefix(name);
+  Print("\"%s\"", value);
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, int value) {
+  AddAttributePrefix(name);
+  Print("%d", value);
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, bool value) {
+  AddAttributePrefix(name);
+  Print(value ? "true" : "false");
+}
+
+
+void JsonAstBuilder::VisitBlock(Block* stmt) {
+  TagScope tag(this, "Block");
+  VisitStatements(stmt->statements());
+}
+
+
+void JsonAstBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
+  TagScope tag(this, "ExpressionStatement");
+  Visit(stmt->expression());
+}
+
+
+void JsonAstBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
+  TagScope tag(this, "EmptyStatement");
+}
+
+
+void JsonAstBuilder::VisitIfStatement(IfStatement* stmt) {
+  TagScope tag(this, "IfStatement");
+  Visit(stmt->condition());
+  Visit(stmt->then_statement());
+  Visit(stmt->else_statement());
+}
+
+
+void JsonAstBuilder::VisitContinueStatement(ContinueStatement* stmt) {
+  TagScope tag(this, "ContinueStatement");
+}
+
+
+void JsonAstBuilder::VisitBreakStatement(BreakStatement* stmt) {
+  TagScope tag(this, "BreakStatement");
+}
+
+
+void JsonAstBuilder::VisitReturnStatement(ReturnStatement* stmt) {
+  TagScope tag(this, "ReturnStatement");
+  Visit(stmt->expression());
+}
+
+
+void JsonAstBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
+  TagScope tag(this, "WithEnterStatement");
+  Visit(stmt->expression());
+}
+
+
+void JsonAstBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
+  TagScope tag(this, "WithExitStatement");
+}
+
+
+void JsonAstBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
+  TagScope tag(this, "SwitchStatement");
+}
+
+
+void JsonAstBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
+  TagScope tag(this, "DoWhileStatement");
+  Visit(stmt->body());
+  Visit(stmt->cond());
+}
+
+
+void JsonAstBuilder::VisitWhileStatement(WhileStatement* stmt) {
+  TagScope tag(this, "WhileStatement");
+  Visit(stmt->cond());
+  Visit(stmt->body());
+}
+
+
+void JsonAstBuilder::VisitForStatement(ForStatement* stmt) {
+  TagScope tag(this, "ForStatement");
+  if (stmt->init() != NULL) Visit(stmt->init());
+  if (stmt->cond() != NULL) Visit(stmt->cond());
+  Visit(stmt->body());
+  if (stmt->next() != NULL) Visit(stmt->next());
+}
+
+
+void JsonAstBuilder::VisitForInStatement(ForInStatement* stmt) {
+  TagScope tag(this, "ForInStatement");
+  Visit(stmt->each());
+  Visit(stmt->enumerable());
+  Visit(stmt->body());
+}
+
+
+void JsonAstBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
+  TagScope tag(this, "TryCatchStatement");
+  Visit(stmt->try_block());
+  Visit(stmt->catch_var());
+  Visit(stmt->catch_block());
+}
+
+
+void JsonAstBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+  TagScope tag(this, "TryFinallyStatement");
+  Visit(stmt->try_block());
+  Visit(stmt->finally_block());
+}
+
+
+void JsonAstBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
+  TagScope tag(this, "DebuggerStatement");
+}
+
+
+void JsonAstBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
+  TagScope tag(this, "FunctionLiteral");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("name", expr->name());
+  }
+  VisitDeclarations(expr->scope()->declarations());
+  VisitStatements(expr->body());
+}
+
+
+void JsonAstBuilder::VisitFunctionBoilerplateLiteral(
+    FunctionBoilerplateLiteral* expr) {
+  TagScope tag(this, "FunctionBoilerplateLiteral");
+}
+
+
+void JsonAstBuilder::VisitConditional(Conditional* expr) {
+  TagScope tag(this, "Conditional");
+}
+
+
+void JsonAstBuilder::VisitSlot(Slot* expr) {
+  TagScope tag(this, "Slot");
+  {
+    AttributesScope attributes(this);
+    switch (expr->type()) {
+      case Slot::PARAMETER:
+        AddAttribute("type", "PARAMETER");
+        break;
+      case Slot::LOCAL:
+        AddAttribute("type", "LOCAL");
+        break;
+      case Slot::CONTEXT:
+        AddAttribute("type", "CONTEXT");
+        break;
+      case Slot::LOOKUP:
+        AddAttribute("type", "LOOKUP");
+        break;
+      case Slot::GLOBAL:
+        AddAttribute("type", "GLOBAL");
+        break;
+    }
+    AddAttribute("index", expr->index());
+  }
+}
+
+
+void JsonAstBuilder::VisitVariableProxy(VariableProxy* expr) {
+  if (expr->var()->rewrite() == NULL) {
+    TagScope tag(this, "VariableProxy");
+    {
+      AttributesScope attributes(this);
+      AddAttribute("name", expr->name());
+      AddAttribute("mode", Variable::Mode2String(expr->var()->mode()));
+    }
+  } else {
+    Visit(expr->var()->rewrite());
+  }
+}
+
+
+void JsonAstBuilder::VisitLiteral(Literal* expr) {
+  TagScope tag(this, "Literal");
+  {
+    AttributesScope attributes(this);
+    Handle<Object> handle = expr->handle();
+    if (handle->IsString()) {
+      AddAttribute("handle", Handle<String>(String::cast(*handle)));
+    } else if (handle->IsSmi()) {
+      AddAttribute("handle", Smi::cast(*handle)->value());
+    }
+  }
+}
+
+
+void JsonAstBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
+  TagScope tag(this, "RegExpLiteral");
+}
+
+
+void JsonAstBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
+  TagScope tag(this, "ObjectLiteral");
+}
+
+
+void JsonAstBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
+  TagScope tag(this, "ArrayLiteral");
+}
+
+
+void JsonAstBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
+  TagScope tag(this, "CatchExtensionObject");
+  Visit(expr->key());
+  Visit(expr->value());
+}
+
+
+void JsonAstBuilder::VisitAssignment(Assignment* expr) {
+  TagScope tag(this, "Assignment");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("op", Token::Name(expr->op()));
+  }
+  Visit(expr->target());
+  Visit(expr->value());
+}
+
+
+void JsonAstBuilder::VisitThrow(Throw* expr) {
+  TagScope tag(this, "Throw");
+  Visit(expr->exception());
+}
+
+
+void JsonAstBuilder::VisitProperty(Property* expr) {
+  TagScope tag(this, "Property");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("type", expr->is_synthetic() ? "SYNTHETIC" : "NORMAL");
+  }
+  Visit(expr->obj());
+  Visit(expr->key());
+}
+
+
+void JsonAstBuilder::VisitCall(Call* expr) {
+  TagScope tag(this, "Call");
+  Visit(expr->expression());
+  VisitExpressions(expr->arguments());
+}
+
+
+void JsonAstBuilder::VisitCallNew(CallNew* expr) {
+  TagScope tag(this, "CallNew");
+  Visit(expr->expression());
+  VisitExpressions(expr->arguments());
+}
+
+
+void JsonAstBuilder::VisitCallRuntime(CallRuntime* expr) {
+  TagScope tag(this, "CallRuntime");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("name", expr->name());
+  }
+  VisitExpressions(expr->arguments());
+}
+
+
+void JsonAstBuilder::VisitUnaryOperation(UnaryOperation* expr) {
+  TagScope tag(this, "UnaryOperation");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("op", Token::Name(expr->op()));
+  }
+  Visit(expr->expression());
+}
+
+
+void JsonAstBuilder::VisitCountOperation(CountOperation* expr) {
+  TagScope tag(this, "CountOperation");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("is_prefix", expr->is_prefix());
+    AddAttribute("op", Token::Name(expr->op()));
+  }
+  Visit(expr->expression());
+}
+
+
+void JsonAstBuilder::VisitBinaryOperation(BinaryOperation* expr) {
+  TagScope tag(this, "BinaryOperation");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("op", Token::Name(expr->op()));
+  }
+  Visit(expr->left());
+  Visit(expr->right());
+}
+
+
+void JsonAstBuilder::VisitCompareOperation(CompareOperation* expr) {
+  TagScope tag(this, "CompareOperation");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("op", Token::Name(expr->op()));
+  }
+  Visit(expr->left());
+  Visit(expr->right());
+}
+
+
+void JsonAstBuilder::VisitThisFunction(ThisFunction* expr) {
+  TagScope tag(this, "ThisFunction");
+}
+
+
+void JsonAstBuilder::VisitDeclaration(Declaration* decl) {
+  TagScope tag(this, "Declaration");
+  {
+    AttributesScope attributes(this);
+    AddAttribute("mode", Variable::Mode2String(decl->mode()));
+  }
+  Visit(decl->proxy());
+  if (decl->fun() != NULL) Visit(decl->fun());
+}
+
 
 #endif  // DEBUG
 
index 8a6d1fb..f885cb3 100644 (file)
@@ -46,14 +46,15 @@ class PrettyPrinter: public AstVisitor {
   const char* PrintExpression(FunctionLiteral* program);
   const char* PrintProgram(FunctionLiteral* program);
 
+  void Print(const char* format, ...);
+
   // Print a node to stdout.
   static void PrintOut(AstNode* node);
 
   // Individual nodes
-#define DEF_VISIT(type)                         \
-  virtual void Visit##type(type* node);
-  AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+  AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
 
  private:
   char* output_;  // output string buffer
@@ -62,7 +63,6 @@ class PrettyPrinter: public AstVisitor {
 
  protected:
   void Init();
-  void Print(const char* format, ...);
   const char* Output() const { return output_; }
 
   virtual void PrintStatements(ZoneList<Statement*>* statements);
@@ -85,10 +85,9 @@ class AstPrinter: public PrettyPrinter {
   const char* PrintProgram(FunctionLiteral* program);
 
   // Individual nodes
-#define DEF_VISIT(type)                         \
-  virtual void Visit##type(type* node);
-  AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+  AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
  private:
   friend class IndentedScope;
   void PrintIndented(const char* txt);
@@ -112,6 +111,107 @@ class AstPrinter: public PrettyPrinter {
   static int indent_;
 };
 
+
+// Forward declaration of helper classes.
+class TagScope;
+class AttributesScope;
+
+// Build a C string containing a JSON representation of a function's
+// AST. The representation is based on JsonML (www.jsonml.org).
+class JsonAstBuilder: public PrettyPrinter {
+ public:
+  JsonAstBuilder()
+      : indent_(0), top_tag_scope_(NULL), attributes_scope_(NULL) {
+  }
+  virtual ~JsonAstBuilder() {}
+
+  // Controls the indentation of subsequent lines of a tag body after
+  // the first line.
+  static const int kTagIndentSize = 2;
+
+  // Controls the indentation of subsequent lines of an attributes
+  // blocks's body after the first line.
+  static const int kAttributesIndentSize = 1;
+
+  // Construct a JSON representation of a function literal.
+  const char* BuildProgram(FunctionLiteral* program);
+
+  // Print text indented by the current indentation level.
+  void PrintIndented(const char* text) { Print("%*s%s", indent_, "", text); }
+
+  // Change the indentation level.
+  void increase_indent(int amount) { indent_ += amount; }
+  void decrease_indent(int amount) { indent_ -= amount; }
+
+  // The builder maintains a stack of opened AST node constructors.
+  // Each node constructor corresponds to a JsonML tag.
+  TagScope* tag() { return top_tag_scope_; }
+  void set_tag(TagScope* scope) { top_tag_scope_ = scope; }
+
+  // The builder maintains a pointer to the currently opened attributes
+  // of current AST node or NULL if the attributes are not opened.
+  AttributesScope* attributes() { return attributes_scope_; }
+  void set_attributes(AttributesScope* scope) { attributes_scope_ = scope; }
+
+  // Add an attribute to the currently opened attributes.
+  void AddAttribute(const char* name, Handle<String> value);
+  void AddAttribute(const char* name, const char* value);
+  void AddAttribute(const char* name, int value);
+  void AddAttribute(const char* name, bool value);
+
+  // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+  AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ private:
+  int indent_;
+  TagScope* top_tag_scope_;
+  AttributesScope* attributes_scope_;
+
+  // Utility function used by AddAttribute implementations.
+  void AddAttributePrefix(const char* name);
+};
+
+
+// The JSON AST builder keeps a stack of open element tags (AST node
+// constructors from the current iteration point to the root of the
+// AST).  TagScope is a helper class to manage the opening and closing
+// of tags, the indentation of their bodies, and comma separating their
+// contents.
+class TagScope BASE_EMBEDDED {
+ public:
+  TagScope(JsonAstBuilder* builder, const char* name);
+  ~TagScope();
+
+  void use() { has_body_ = true; }
+
+ private:
+  JsonAstBuilder* builder_;
+  TagScope* next_;
+  bool has_body_;
+};
+
+
+// AttributesScope is a helper class to manage the opening and closing
+// of attribute blocks, the indentation of their bodies, and comma
+// separating their contents. JsonAstBuilder::AddAttribute adds an
+// attribute to the currently open AttributesScope. They cannot be
+// nested so the builder keeps an optional single scope rather than a
+// stack.
+class AttributesScope BASE_EMBEDDED {
+ public:
+  explicit AttributesScope(JsonAstBuilder* builder);
+  ~AttributesScope();
+
+  bool is_used() { return attribute_count_ > 0; }
+  void use() { ++attribute_count_; }
+
+ private:
+  JsonAstBuilder* builder_;
+  int attribute_count_;
+};
+
 #endif  // DEBUG
 
 } }  // namespace v8::internal