968 lines
24 KiB
C++
968 lines
24 KiB
C++
// 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);
|
|
}
|