| // ast-dump.cc -- AST debug dump. -*- C++ -*- |
| |
| // Copyright 2011 The Go 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 "go-system.h" |
| |
| #include <iostream> |
| #include <fstream> |
| #include <sstream> |
| |
| #include "gogo.h" |
| #include "expressions.h" |
| #include "statements.h" |
| #include "types.h" |
| #include "ast-dump.h" |
| #include "go-c.h" |
| #include "go-dump.h" |
| #include "go-diagnostics.h" |
| |
| // The -fgo-dump-ast flag to activate AST dumps. |
| |
| Go_dump ast_dump_flag("ast"); |
| |
| // This class is used to traverse the tree to look for blocks and |
| // function headers. |
| |
| class Ast_dump_traverse_blocks_and_functions : public Traverse |
| { |
| public: |
| Ast_dump_traverse_blocks_and_functions(Ast_dump_context* ast_dump_context) |
| : Traverse(traverse_blocks | traverse_functions | traverse_variables), |
| ast_dump_context_(ast_dump_context) |
| { } |
| |
| protected: |
| int |
| block(Block*); |
| |
| int |
| function(Named_object*); |
| |
| int |
| variable(Named_object*); |
| |
| private: |
| Ast_dump_context* ast_dump_context_; |
| }; |
| |
| // This class is used to traverse the tree to look for statements. |
| |
| class Ast_dump_traverse_statements : public Traverse |
| { |
| public: |
| Ast_dump_traverse_statements(Ast_dump_context* ast_dump_context) |
| : Traverse(traverse_statements), |
| ast_dump_context_(ast_dump_context) |
| { } |
| |
| protected: |
| int |
| statement(Block*, size_t* pindex, Statement*); |
| |
| private: |
| Ast_dump_context* ast_dump_context_; |
| }; |
| |
| // For each block we enclose it in brackets. |
| |
| int Ast_dump_traverse_blocks_and_functions::block(Block * block) |
| { |
| if (block == NULL) |
| { |
| this->ast_dump_context_->ostream() << std::endl; |
| return TRAVERSE_EXIT; |
| } |
| |
| this->ast_dump_context_->print_indent(); |
| this->ast_dump_context_->ostream() << "{" << std::endl; |
| this->ast_dump_context_->indent(); |
| |
| // Dump statememts. |
| Ast_dump_traverse_statements adts(this->ast_dump_context_); |
| block->traverse(&adts); |
| |
| this->ast_dump_context_->unindent(); |
| this->ast_dump_context_->print_indent(); |
| this->ast_dump_context_->ostream() << "}" << std::endl; |
| |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // Dump each traversed statement. |
| |
| int |
| Ast_dump_traverse_statements::statement(Block* block, size_t* pindex, |
| Statement* statement) |
| { |
| statement->dump_statement(this->ast_dump_context_); |
| |
| if (statement->is_block_statement()) |
| { |
| Ast_dump_traverse_blocks_and_functions adtbf(this->ast_dump_context_); |
| statement->traverse(block, pindex, &adtbf); |
| } |
| |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // Dump the function header. |
| |
| int |
| Ast_dump_traverse_blocks_and_functions::function(Named_object* no) |
| { |
| this->ast_dump_context_->ostream() << no->name(); |
| |
| go_assert(no->is_function()); |
| Function* func = no->func_value(); |
| |
| this->ast_dump_context_->ostream() << "("; |
| this->ast_dump_context_->dump_typed_identifier_list( |
| func->type()->parameters()); |
| |
| this->ast_dump_context_->ostream() << ")"; |
| |
| Function::Results* res = func->result_variables(); |
| if (res != NULL && !res->empty()) |
| { |
| this->ast_dump_context_->ostream() << " ("; |
| |
| for (Function::Results::const_iterator it = res->begin(); |
| it != res->end(); |
| it++) |
| { |
| if (it != res->begin()) |
| this->ast_dump_context_->ostream() << ","; |
| Named_object* rno = (*it); |
| |
| this->ast_dump_context_->ostream() << rno->name() << " "; |
| go_assert(rno->is_result_variable()); |
| Result_variable* resvar = rno->result_var_value(); |
| |
| this->ast_dump_context_->dump_type(resvar->type()); |
| |
| } |
| this->ast_dump_context_->ostream() << ")"; |
| } |
| |
| this->ast_dump_context_->ostream() << " : "; |
| this->ast_dump_context_->dump_type(func->type()); |
| this->ast_dump_context_->ostream() << std::endl; |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Dump variable preinits |
| |
| int |
| Ast_dump_traverse_blocks_and_functions::variable(Named_object* no) |
| { |
| if (!no->is_variable()) |
| return TRAVERSE_CONTINUE; |
| |
| Variable* var = no->var_value(); |
| if (var->has_pre_init()) |
| { |
| this->ast_dump_context_->ostream() << "// preinit block for var " |
| << no->message_name() << "\n"; |
| var->preinit()->traverse(this); |
| } |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| |
| |
| // Class Ast_dump_context. |
| |
| Ast_dump_context::Ast_dump_context(std::ostream* out /* = NULL */, |
| bool dump_subblocks /* = true */) |
| : indent_(0), dump_subblocks_(dump_subblocks), ostream_(out), gogo_(NULL) |
| { |
| } |
| |
| // Dump files will be named %basename%.dump.ast |
| |
| const char* kAstDumpFileExtension = ".dump.ast"; |
| |
| // Dump the internal representation. |
| |
| void |
| Ast_dump_context::dump(Gogo* gogo, const char* basename) |
| { |
| std::ofstream out; |
| std::string dumpname(basename); |
| dumpname += ".dump.ast"; |
| out.open(dumpname.c_str()); |
| |
| if (out.fail()) |
| { |
| go_error_at(Linemap::unknown_location(), |
| "cannot open %s:%m; %<-fgo-dump-ast%> ignored", |
| dumpname.c_str()); |
| return; |
| } |
| |
| this->gogo_ = gogo; |
| this->ostream_ = &out; |
| |
| Ast_dump_traverse_blocks_and_functions adtbf(this); |
| gogo->traverse(&adtbf); |
| |
| out.close(); |
| } |
| |
| // Dump a textual representation of a type to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_type(const Type* t) |
| { |
| if (t == NULL) |
| this->ostream() << "(nil type)"; |
| else |
| // FIXME: write a type pretty printer instead of |
| // using mangled names. |
| if (this->gogo_ != NULL) |
| { |
| Backend_name bname; |
| t->backend_name(this->gogo_, &bname); |
| this->ostream() << "(" << bname.name() << ")"; |
| } |
| } |
| |
| // Dump a textual representation of a block to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_block(Block* b) |
| { |
| Ast_dump_traverse_blocks_and_functions adtbf(this); |
| b->traverse(&adtbf); |
| } |
| |
| // Dump a textual representation of an expression to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_expression(const Expression* e) |
| { |
| e->dump_expression(this); |
| } |
| |
| // Dump a textual representation of an expression list to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_expression_list(const Expression_list* el, |
| bool as_pairs /* = false */) |
| { |
| if (el == NULL) |
| return; |
| |
| for (std::vector<Expression*>::const_iterator it = el->begin(); |
| it != el->end(); |
| it++) |
| { |
| if ( it != el->begin()) |
| this->ostream() << ","; |
| if (*it != NULL) |
| (*it)->dump_expression(this); |
| else |
| this->ostream() << "NULL"; |
| if (as_pairs) |
| { |
| this->ostream() << ":"; |
| ++it; |
| (*it)->dump_expression(this); |
| } |
| } |
| } |
| |
| // Dump a textual representation of a typed identifier to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_typed_identifier(const Typed_identifier* ti) |
| { |
| this->ostream() << ti->name() << " "; |
| this->dump_type(ti->type()); |
| } |
| |
| // Dump a textual representation of a typed identifier list to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_typed_identifier_list( |
| const Typed_identifier_list* ti_list) |
| { |
| if (ti_list == NULL) |
| return; |
| |
| for (Typed_identifier_list::const_iterator it = ti_list->begin(); |
| it != ti_list->end(); |
| it++) |
| { |
| if (it != ti_list->begin()) |
| this->ostream() << ","; |
| this->dump_typed_identifier(&(*it)); |
| } |
| } |
| |
| // Dump a textual representation of a temporary variable to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_temp_variable_name(const Statement* s) |
| { |
| go_assert(s->classification() == Statement::STATEMENT_TEMPORARY); |
| // Use the statement address as part of the name for the temporary variable. |
| this->ostream() << "tmp." << (uintptr_t) s; |
| } |
| |
| // Dump a textual representation of a label to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_label_name(const Unnamed_label* l) |
| { |
| // Use the unnamed label address as part of the name for the temporary |
| // variable. |
| this->ostream() << "label." << (uintptr_t) l; |
| } |
| |
| // Produce a textual representation of an operator symbol. |
| |
| static const char* |
| op_string(Operator op) |
| { |
| // FIXME: This should be in line with symbols that are parsed, |
| // exported and/or imported. |
| switch (op) |
| { |
| case OPERATOR_PLUS: |
| return "+"; |
| case OPERATOR_MINUS: |
| return "-"; |
| case OPERATOR_NOT: |
| return "!"; |
| case OPERATOR_XOR: |
| return "^"; |
| case OPERATOR_OR: |
| return "|"; |
| case OPERATOR_AND: |
| return "&"; |
| case OPERATOR_MULT: |
| return "*"; |
| case OPERATOR_OROR: |
| return "||"; |
| case OPERATOR_ANDAND: |
| return "&&"; |
| case OPERATOR_EQEQ: |
| return "=="; |
| case OPERATOR_NOTEQ: |
| return "!="; |
| case OPERATOR_LT: |
| return "<"; |
| case OPERATOR_LE: |
| return "<="; |
| case OPERATOR_GT: |
| return ">"; |
| case OPERATOR_GE: |
| return ">="; |
| case OPERATOR_DIV: |
| return "/"; |
| case OPERATOR_MOD: |
| return "%"; |
| case OPERATOR_LSHIFT: |
| return "<<"; |
| case OPERATOR_RSHIFT: |
| return "//"; |
| case OPERATOR_BITCLEAR: |
| return "&^"; |
| case OPERATOR_CHANOP: |
| return "<-"; |
| case OPERATOR_PLUSEQ: |
| return "+="; |
| case OPERATOR_MINUSEQ: |
| return "-="; |
| case OPERATOR_OREQ: |
| return "|="; |
| case OPERATOR_XOREQ: |
| return "^="; |
| case OPERATOR_MULTEQ: |
| return "*="; |
| case OPERATOR_DIVEQ: |
| return "/="; |
| case OPERATOR_MODEQ: |
| return "%="; |
| case OPERATOR_LSHIFTEQ: |
| return "<<="; |
| case OPERATOR_RSHIFTEQ: |
| return ">>="; |
| case OPERATOR_ANDEQ: |
| return "&="; |
| case OPERATOR_BITCLEAREQ: |
| return "&^="; |
| case OPERATOR_PLUSPLUS: |
| return "++"; |
| case OPERATOR_MINUSMINUS: |
| return "--"; |
| case OPERATOR_COLON: |
| return ":"; |
| case OPERATOR_COLONEQ: |
| return ":="; |
| case OPERATOR_SEMICOLON: |
| return ";"; |
| case OPERATOR_DOT: |
| return "."; |
| case OPERATOR_ELLIPSIS: |
| return "..."; |
| case OPERATOR_COMMA: |
| return ","; |
| case OPERATOR_LPAREN: |
| return "("; |
| case OPERATOR_RPAREN: |
| return ")"; |
| case OPERATOR_LCURLY: |
| return "{"; |
| case OPERATOR_RCURLY: |
| return "}"; |
| case OPERATOR_LSQUARE: |
| return "["; |
| case OPERATOR_RSQUARE: |
| return "]"; |
| default: |
| go_unreachable(); |
| } |
| return NULL; |
| } |
| |
| // Dump a textual representation of an operator to the |
| // the dump file. |
| |
| void |
| Ast_dump_context::dump_operator(Operator op) |
| { |
| this->ostream() << op_string(op); |
| } |
| |
| // Size of a single indent. |
| |
| const int Ast_dump_context::offset_ = 2; |
| |
| // Print indenting spaces to dump file. |
| |
| void |
| Ast_dump_context::print_indent() |
| { |
| for (int i = 0; i < this->indent_ * this->offset_; i++) |
| this->ostream() << " "; |
| } |
| |
| // Dump a textual representation of the ast to the |
| // the dump file. |
| |
| void Gogo::dump_ast(const char* basename) |
| { |
| if (::ast_dump_flag.is_enabled()) |
| { |
| Ast_dump_context adc; |
| adc.dump(this, basename); |
| } |
| } |
| |
| // Implementation of String_dump interface. |
| |
| void |
| Ast_dump_context::write_c_string(const char* s) |
| { |
| this->ostream() << s; |
| } |
| |
| void |
| Ast_dump_context::write_string(const std::string& s) |
| { |
| this->ostream() << s; |
| } |
| |
| // Dump statement to stream. |
| |
| void |
| Ast_dump_context::dump_to_stream(const Statement* stm, std::ostream* out) |
| { |
| Ast_dump_context adc(out, false); |
| stm->dump_statement(&adc); |
| } |
| |
| // Dump expression to stream. |
| |
| void |
| Ast_dump_context::dump_to_stream(const Expression* expr, std::ostream* out) |
| { |
| Ast_dump_context adc(out, false); |
| expr->dump_expression(&adc); |
| } |
| |
| // Dump an expression to std::cerr. This is intended to be used |
| // from within a debugging session. |
| |
| void |
| debug_go_expression(const Expression* expr) |
| { |
| if (expr == NULL) |
| std::cerr << "<null>"; |
| else |
| { |
| Ast_dump_context::dump_to_stream(expr, &std::cerr); |
| std::string lstr = Linemap::location_to_string(expr->location()); |
| std::cerr << " // loc " << lstr << std::endl; |
| } |
| } |
| |
| // Shallow dump of stmt to std::cerr. This is intended to be used |
| // from within a debugging session. |
| |
| void |
| debug_go_statement(const Statement* stmt) |
| { |
| if (stmt == NULL) |
| std::cerr << "<null>\n"; |
| else |
| { |
| std::string lstr = Linemap::location_to_string(stmt->location()); |
| Statement *ncstmt = const_cast<Statement*>(stmt); |
| Block_statement* bs = ncstmt->block_statement(); |
| if (bs != NULL) |
| std::cerr << "Block " << bs->block() |
| << " // location: " << lstr << std::endl; |
| else |
| Ast_dump_context::dump_to_stream(stmt, &std::cerr); |
| } |
| } |
| |
| // Deep dump of statement to std::cerr. This is intended to be used |
| // from within a debugging session. |
| |
| void |
| debug_go_statement_deep(const Statement* statement) |
| { |
| Ast_dump_context adc(&std::cerr, true); |
| statement->dump_statement(&adc); |
| } |
| |
| // Shallow dump of a block to std::cerr. This is intended to be used |
| // from within a debugging session. |
| |
| void |
| debug_go_block(const Block* block) |
| { |
| if (block == NULL) |
| std::cerr << "<null>"; |
| else |
| { |
| std::cerr << "Block " << block |
| << " (enclosing " << block->enclosing() << "):\n"; |
| const std::vector<Statement*>* stmts = block->statements(); |
| if (stmts != NULL) |
| { |
| for (size_t i = 0; i < stmts->size(); ++i) |
| { |
| debug_go_statement(stmts->at(i)); |
| } |
| } |
| } |
| } |
| |
| // Deep dump of a block to std:cerr. This is intended to be used |
| // from within a debugging session. |
| |
| void |
| debug_go_block_deep(const Block* block) |
| { |
| Ast_dump_context adc(&std::cerr, true); |
| Block* ncblock = const_cast<Block*>(block); |
| adc.dump_block(ncblock); |
| } |
| |
| class Type_dumper |
| { |
| typedef Unordered_map(const Type*, unsigned) idx_map; |
| public: |
| Type_dumper(const Type* type) |
| : top_(type), ntypes_(0) |
| { |
| this->worklist_.push_back(type); |
| } |
| |
| void visit(); |
| |
| std::string stringResult() { return ss_.str(); } |
| |
| private: |
| void emitpre(unsigned tag, const Type* addr); |
| void typeref(const char*, const Type*, const char *); |
| void visit_forward_declaration_type(const Forward_declaration_type* fdt); |
| void visit_function_type(const Function_type* ft); |
| void visit_struct_type(const Struct_type* st); |
| void visit_array_type(const Array_type* at); |
| void visit_map_type(const Map_type* mt); |
| void visit_channel_type(const Channel_type* mt); |
| void visit_interface_type(const Interface_type* mt); |
| void visit_methods(const Typed_identifier_list* methods, |
| const char *tag); |
| std::pair<bool, unsigned> lookup(const Type*); |
| |
| static const unsigned notag = 0xffffffff; |
| |
| private: |
| const Type* top_; |
| idx_map types_; |
| unsigned ntypes_; |
| std::list<const Type*> worklist_; |
| std::ostringstream ss_; |
| }; |
| |
| // Look up a type, installing it in 'types_'. Return is <found, N> |
| // where 'found' is true if type had been previously recorded, and N |
| // is the index/tag assigned to N. The input argument is appended to |
| // the work list if this is the first time we've seen it. |
| |
| std::pair<bool, unsigned> Type_dumper::lookup(const Type* t) |
| { |
| std::pair<const Type*, unsigned> entry = std::make_pair(t, this->ntypes_); |
| std::pair<idx_map::iterator, bool> ins = this->types_.insert(entry); |
| if (ins.second) |
| { |
| this->ntypes_++; |
| if (t != this->top_) |
| this->worklist_.push_back(t); |
| } |
| return std::make_pair(ins.second, ins.first->second); |
| } |
| |
| // Emit preamble prior to dumping a type, including the type |
| // pointer itself and the tag we've assigned it. If no |
| // tag is specified (via special "notag" value) and/or the |
| // pointer is null, then just emit an equivalent amount |
| // of spaces. |
| |
| void Type_dumper::emitpre(unsigned tag, const Type* ptr) |
| { |
| char tbuf[50], pbuf[50], buf[200]; |
| |
| tbuf[0] = '\0'; |
| if (tag != notag) |
| snprintf(tbuf, sizeof tbuf, "T%u", tag); |
| |
| pbuf[0] = '\0'; |
| if (ptr != NULL) |
| snprintf(pbuf, sizeof pbuf, "%p", (const void*) ptr); |
| |
| snprintf(buf, sizeof buf, "%8s %16s ", tbuf, pbuf); |
| this->ss_ << buf; |
| } |
| |
| // Emit a reference to a type into the dump buffer. In most cases this means |
| // just the type tag, but for named types we also emit the name, and for |
| // simple/primitive types (ex: int64) we emit the type itself. If "pref" is |
| // non-NULL, emit the string prior to the reference, and if "suf" is non-NULL, |
| // emit it following the reference. |
| |
| void Type_dumper::typeref(const char* pref, const Type* t, const char* suf) |
| { |
| if (pref != NULL) |
| this->ss_ << pref; |
| std::pair<bool, unsigned> p = this->lookup(t); |
| unsigned tag = p.second; |
| switch (t->classification()) |
| { |
| case Type::TYPE_NAMED: |
| { |
| const Named_type* nt = t->named_type(); |
| const Named_object* no = nt->named_object(); |
| this->ss_ << "'" << no->message_name() << "' -> "; |
| const Type* underlying = nt->real_type(); |
| this->typeref(NULL, underlying, NULL); |
| break; |
| } |
| case Type::TYPE_POINTER: |
| this->typeref("*", t->points_to(), NULL); |
| break; |
| case Type::TYPE_ERROR: |
| this->ss_ << "error_type"; |
| break; |
| case Type::TYPE_INTEGER: |
| { |
| const Integer_type* it = t->integer_type(); |
| if (it->is_abstract()) |
| this->ss_ << "abstract_int"; |
| else |
| this->ss_ << (it->is_unsigned() ? "u" : "") << "int" << it->bits(); |
| break; |
| } |
| case Type::TYPE_FLOAT: |
| { |
| const Float_type* ft = t->float_type(); |
| if (ft->is_abstract()) |
| this->ss_ << "abstract_float"; |
| else |
| this->ss_ << "float" << ft->bits(); |
| break; |
| } |
| case Type::TYPE_COMPLEX: |
| { |
| const Complex_type* ct = t->complex_type(); |
| if (ct->is_abstract()) |
| this->ss_ << "abstract_complex"; |
| else |
| this->ss_ << "complex" << ct->bits(); |
| break; |
| } |
| case Type::TYPE_BOOLEAN: |
| this->ss_ << "bool"; |
| break; |
| case Type::TYPE_STRING: |
| this->ss_ << "string"; |
| break; |
| case Type::TYPE_NIL: |
| this->ss_ << "nil_type"; |
| break; |
| case Type::TYPE_VOID: |
| this->ss_ << "void_type"; |
| break; |
| case Type::TYPE_FUNCTION: |
| case Type::TYPE_STRUCT: |
| case Type::TYPE_ARRAY: |
| case Type::TYPE_MAP: |
| case Type::TYPE_CHANNEL: |
| case Type::TYPE_FORWARD: |
| case Type::TYPE_INTERFACE: |
| this->ss_ << "T" << tag; |
| break; |
| |
| default: |
| // This is a debugging routine, so instead of a go_unreachable() |
| // issue a warning/error, to allow for the possibility that the |
| // compiler we're debugging is in a bad state. |
| this->ss_ << "<??? " << ((unsigned)t->classification()) << "> " |
| << "T" << tag; |
| } |
| if (suf != NULL) |
| this->ss_ << suf; |
| } |
| |
| void Type_dumper::visit_forward_declaration_type(const Forward_declaration_type* fdt) |
| { |
| this->ss_ << "forward_declaration_type "; |
| if (fdt->is_defined()) |
| this->typeref("-> ", fdt->real_type(), NULL); |
| else |
| this->ss_ << "'" << fdt->name() << "'"; |
| this->ss_ << "\n"; |
| } |
| |
| void Type_dumper::visit_function_type(const Function_type* ft) |
| { |
| this->ss_ << "function\n"; |
| const Typed_identifier* rec = ft->receiver(); |
| if (rec != NULL) |
| { |
| this->emitpre(notag, NULL); |
| this->typeref("receiver ", rec->type(), "\n"); |
| } |
| const Typed_identifier_list* parameters = ft->parameters(); |
| if (parameters != NULL) |
| { |
| for (Typed_identifier_list::const_iterator p = parameters->begin(); |
| p != parameters->end(); |
| ++p) |
| { |
| this->emitpre(notag, NULL); |
| this->typeref(" param ", p->type(), "\n"); |
| } |
| } |
| const Typed_identifier_list* results = ft->results(); |
| if (results != NULL) |
| { |
| for (Typed_identifier_list::const_iterator p = results->begin(); |
| p != results->end(); |
| ++p) |
| { |
| this->emitpre(notag, NULL); |
| this->typeref(" result ", p->type(), "\n"); |
| } |
| } |
| } |
| |
| void Type_dumper::visit_struct_type(const Struct_type* st) |
| { |
| this->ss_ << "struct\n"; |
| const Struct_field_list* fields = st->fields(); |
| if (fields != NULL) |
| { |
| for (Struct_field_list::const_iterator p = fields->begin(); |
| p != fields->end(); |
| ++p) |
| { |
| this->emitpre(notag, NULL); |
| this->typeref(" field ", p->type(), "\n"); |
| } |
| } |
| } |
| |
| void Type_dumper::visit_array_type(const Array_type* at) |
| { |
| this->ss_ << "array ["; |
| if (at->length() != NULL) |
| { |
| int64_t len = 0; |
| if (at->int_length(&len)) |
| this->ss_ << len; |
| } |
| this->typeref("] ", at->element_type(), "\n"); |
| } |
| |
| void Type_dumper::visit_map_type(const Map_type* mt) |
| { |
| this->ss_ << "map ["; |
| this->typeref(NULL, mt->key_type(), NULL); |
| this->typeref("] ", mt->val_type(), "\n"); |
| } |
| |
| void Type_dumper::visit_methods(const Typed_identifier_list* methods, |
| const char *tag) |
| { |
| if (tag != NULL) |
| { |
| this->emitpre(notag, NULL); |
| this->ss_ << tag << "\n"; |
| } |
| for (Typed_identifier_list::const_iterator p = methods->begin(); |
| p != methods->end(); |
| ++p) |
| { |
| this->emitpre(notag, NULL); |
| if (p->name().empty()) |
| this->typeref(" embedded method ", p->type(), "\n"); |
| else |
| { |
| this->ss_ << " method '" << p->name() << "' "; |
| this->typeref(NULL, p->type(), "\n"); |
| } |
| } |
| } |
| |
| void Type_dumper::visit_interface_type(const Interface_type* it) |
| { |
| const Typed_identifier_list* methods = |
| (it->methods_are_finalized() ? it->methods() : it->local_methods()); |
| if (methods == NULL) |
| { |
| this->ss_ << "empty_interface\n"; |
| return; |
| } |
| this->ss_ << "interface"; |
| if (! it->methods_are_finalized()) |
| { |
| this->ss_ << " [unfinalized]\n"; |
| visit_methods(it->local_methods(), NULL); |
| } |
| else |
| { |
| this->ss_ << "\n"; |
| visit_methods(it->local_methods(), "[parse_methods]"); |
| visit_methods(it->methods(), "[all_methods]"); |
| } |
| } |
| |
| void Type_dumper::visit_channel_type(const Channel_type* ct) |
| { |
| this->ss_ << "channel {"; |
| if (ct->may_send()) |
| this->ss_ << " send"; |
| if (ct->may_receive()) |
| this->ss_ << " receive"; |
| this->typeref(" } ", ct->element_type(), "\n"); |
| } |
| |
| void Type_dumper::visit() |
| { |
| while (! this->worklist_.empty()) { |
| const Type* t = this->worklist_.front(); |
| this->worklist_.pop_front(); |
| |
| std::pair<bool, unsigned> p = this->lookup(t); |
| unsigned tag = p.second; |
| this->emitpre(tag, t); |
| |
| switch(t->classification()) |
| { |
| case Type::TYPE_ERROR: |
| case Type::TYPE_INTEGER: |
| case Type::TYPE_FLOAT: |
| case Type::TYPE_COMPLEX: |
| case Type::TYPE_BOOLEAN: |
| case Type::TYPE_STRING: |
| case Type::TYPE_VOID: |
| case Type::TYPE_POINTER: |
| case Type::TYPE_NIL: |
| case Type::TYPE_NAMED: |
| this->typeref(NULL, t, "\n"); |
| break; |
| case Type::TYPE_FORWARD: |
| this->visit_forward_declaration_type(t->forward_declaration_type()); |
| break; |
| |
| case Type::TYPE_FUNCTION: |
| this->visit_function_type(t->function_type()); |
| break; |
| case Type::TYPE_STRUCT: |
| this->visit_struct_type(t->struct_type()); |
| break; |
| case Type::TYPE_ARRAY: |
| this->visit_array_type(t->array_type()); |
| break; |
| case Type::TYPE_MAP: |
| this->visit_map_type(t->map_type()); |
| break; |
| case Type::TYPE_CHANNEL: |
| this->visit_channel_type(t->channel_type()); |
| break; |
| case Type::TYPE_INTERFACE: |
| this->visit_interface_type(t->interface_type()); |
| break; |
| default: |
| // This is a debugging routine, so instead of a go_unreachable() |
| // issue a warning/error, to allow for the possibility that the |
| // compiler we're debugging is in a bad state. |
| this->ss_ << "<unknown/unrecognized classification " |
| << ((unsigned)t->classification()) << ">\n"; |
| } |
| } |
| } |
| |
| // Dump a Go type for debugging purposes. This is a deep as opposed |
| // to shallow dump; all of the types reachable from the specified |
| // type will be dumped in addition to the type itself. |
| |
| void debug_go_type(const Type* type) |
| { |
| if (type == NULL) |
| { |
| std::cerr << "<NULL type>\n"; |
| return; |
| } |
| Type_dumper dumper(type); |
| dumper.visit(); |
| std::cerr << dumper.stringResult(); |
| } |
| |
| void debug_go_type(Type* type) |
| { |
| const Type* ctype = type; |
| debug_go_type(ctype); |
| } |