111
|
1 // ast-dump.cc -- AST debug dump. -*- C++ -*-
|
|
2
|
|
3 // Copyright 2011 The Go Authors. All rights reserved.
|
|
4 // Use of this source code is governed by a BSD-style
|
|
5 // license that can be found in the LICENSE file.
|
|
6
|
|
7 #include "go-system.h"
|
|
8
|
|
9 #include <iostream>
|
|
10 #include <fstream>
|
145
|
11 #include <sstream>
|
111
|
12
|
|
13 #include "gogo.h"
|
|
14 #include "expressions.h"
|
|
15 #include "statements.h"
|
|
16 #include "types.h"
|
|
17 #include "ast-dump.h"
|
|
18 #include "go-c.h"
|
|
19 #include "go-dump.h"
|
|
20 #include "go-diagnostics.h"
|
|
21
|
|
22 // The -fgo-dump-ast flag to activate AST dumps.
|
|
23
|
|
24 Go_dump ast_dump_flag("ast");
|
|
25
|
|
26 // This class is used to traverse the tree to look for blocks and
|
|
27 // function headers.
|
|
28
|
|
29 class Ast_dump_traverse_blocks_and_functions : public Traverse
|
|
30 {
|
|
31 public:
|
|
32 Ast_dump_traverse_blocks_and_functions(Ast_dump_context* ast_dump_context)
|
131
|
33 : Traverse(traverse_blocks | traverse_functions | traverse_variables),
|
111
|
34 ast_dump_context_(ast_dump_context)
|
|
35 { }
|
|
36
|
|
37 protected:
|
|
38 int
|
|
39 block(Block*);
|
|
40
|
|
41 int
|
|
42 function(Named_object*);
|
|
43
|
131
|
44 int
|
|
45 variable(Named_object*);
|
|
46
|
111
|
47 private:
|
|
48 Ast_dump_context* ast_dump_context_;
|
|
49 };
|
|
50
|
|
51 // This class is used to traverse the tree to look for statements.
|
|
52
|
|
53 class Ast_dump_traverse_statements : public Traverse
|
|
54 {
|
|
55 public:
|
|
56 Ast_dump_traverse_statements(Ast_dump_context* ast_dump_context)
|
|
57 : Traverse(traverse_statements),
|
|
58 ast_dump_context_(ast_dump_context)
|
|
59 { }
|
|
60
|
|
61 protected:
|
|
62 int
|
|
63 statement(Block*, size_t* pindex, Statement*);
|
|
64
|
|
65 private:
|
|
66 Ast_dump_context* ast_dump_context_;
|
|
67 };
|
|
68
|
|
69 // For each block we enclose it in brackets.
|
|
70
|
|
71 int Ast_dump_traverse_blocks_and_functions::block(Block * block)
|
|
72 {
|
|
73 if (block == NULL)
|
|
74 {
|
|
75 this->ast_dump_context_->ostream() << std::endl;
|
|
76 return TRAVERSE_EXIT;
|
|
77 }
|
|
78
|
|
79 this->ast_dump_context_->print_indent();
|
|
80 this->ast_dump_context_->ostream() << "{" << std::endl;
|
|
81 this->ast_dump_context_->indent();
|
|
82
|
|
83 // Dump statememts.
|
|
84 Ast_dump_traverse_statements adts(this->ast_dump_context_);
|
|
85 block->traverse(&adts);
|
|
86
|
|
87 this->ast_dump_context_->unindent();
|
|
88 this->ast_dump_context_->print_indent();
|
|
89 this->ast_dump_context_->ostream() << "}" << std::endl;
|
|
90
|
|
91 return TRAVERSE_SKIP_COMPONENTS;
|
|
92 }
|
|
93
|
|
94 // Dump each traversed statement.
|
|
95
|
|
96 int
|
|
97 Ast_dump_traverse_statements::statement(Block* block, size_t* pindex,
|
|
98 Statement* statement)
|
|
99 {
|
|
100 statement->dump_statement(this->ast_dump_context_);
|
|
101
|
|
102 if (statement->is_block_statement())
|
|
103 {
|
|
104 Ast_dump_traverse_blocks_and_functions adtbf(this->ast_dump_context_);
|
|
105 statement->traverse(block, pindex, &adtbf);
|
|
106 }
|
|
107
|
|
108 return TRAVERSE_SKIP_COMPONENTS;
|
|
109 }
|
|
110
|
|
111 // Dump the function header.
|
|
112
|
|
113 int
|
|
114 Ast_dump_traverse_blocks_and_functions::function(Named_object* no)
|
|
115 {
|
|
116 this->ast_dump_context_->ostream() << no->name();
|
|
117
|
|
118 go_assert(no->is_function());
|
|
119 Function* func = no->func_value();
|
|
120
|
|
121 this->ast_dump_context_->ostream() << "(";
|
|
122 this->ast_dump_context_->dump_typed_identifier_list(
|
|
123 func->type()->parameters());
|
|
124
|
|
125 this->ast_dump_context_->ostream() << ")";
|
|
126
|
|
127 Function::Results* res = func->result_variables();
|
|
128 if (res != NULL && !res->empty())
|
|
129 {
|
|
130 this->ast_dump_context_->ostream() << " (";
|
|
131
|
|
132 for (Function::Results::const_iterator it = res->begin();
|
|
133 it != res->end();
|
|
134 it++)
|
|
135 {
|
|
136 if (it != res->begin())
|
|
137 this->ast_dump_context_->ostream() << ",";
|
145
|
138 Named_object* rno = (*it);
|
111
|
139
|
145
|
140 this->ast_dump_context_->ostream() << rno->name() << " ";
|
|
141 go_assert(rno->is_result_variable());
|
|
142 Result_variable* resvar = rno->result_var_value();
|
111
|
143
|
|
144 this->ast_dump_context_->dump_type(resvar->type());
|
|
145
|
|
146 }
|
|
147 this->ast_dump_context_->ostream() << ")";
|
|
148 }
|
|
149
|
|
150 this->ast_dump_context_->ostream() << " : ";
|
|
151 this->ast_dump_context_->dump_type(func->type());
|
|
152 this->ast_dump_context_->ostream() << std::endl;
|
|
153
|
|
154 return TRAVERSE_CONTINUE;
|
|
155 }
|
|
156
|
131
|
157 // Dump variable preinits
|
|
158
|
|
159 int
|
|
160 Ast_dump_traverse_blocks_and_functions::variable(Named_object* no)
|
|
161 {
|
|
162 if (!no->is_variable())
|
|
163 return TRAVERSE_CONTINUE;
|
|
164
|
|
165 Variable* var = no->var_value();
|
|
166 if (var->has_pre_init())
|
|
167 {
|
|
168 this->ast_dump_context_->ostream() << "// preinit block for var "
|
|
169 << no->message_name() << "\n";
|
|
170 var->preinit()->traverse(this);
|
|
171 }
|
|
172
|
|
173 return TRAVERSE_CONTINUE;
|
|
174 }
|
|
175
|
|
176
|
|
177
|
111
|
178 // Class Ast_dump_context.
|
|
179
|
|
180 Ast_dump_context::Ast_dump_context(std::ostream* out /* = NULL */,
|
|
181 bool dump_subblocks /* = true */)
|
|
182 : indent_(0), dump_subblocks_(dump_subblocks), ostream_(out), gogo_(NULL)
|
|
183 {
|
|
184 }
|
|
185
|
|
186 // Dump files will be named %basename%.dump.ast
|
|
187
|
|
188 const char* kAstDumpFileExtension = ".dump.ast";
|
|
189
|
|
190 // Dump the internal representation.
|
|
191
|
|
192 void
|
|
193 Ast_dump_context::dump(Gogo* gogo, const char* basename)
|
|
194 {
|
|
195 std::ofstream out;
|
|
196 std::string dumpname(basename);
|
|
197 dumpname += ".dump.ast";
|
|
198 out.open(dumpname.c_str());
|
|
199
|
|
200 if (out.fail())
|
|
201 {
|
|
202 go_error_at(Linemap::unknown_location(),
|
145
|
203 "cannot open %s:%m; %<-fgo-dump-ast%> ignored",
|
|
204 dumpname.c_str());
|
111
|
205 return;
|
|
206 }
|
|
207
|
|
208 this->gogo_ = gogo;
|
|
209 this->ostream_ = &out;
|
|
210
|
|
211 Ast_dump_traverse_blocks_and_functions adtbf(this);
|
|
212 gogo->traverse(&adtbf);
|
|
213
|
|
214 out.close();
|
|
215 }
|
|
216
|
|
217 // Dump a textual representation of a type to the
|
|
218 // the dump file.
|
|
219
|
|
220 void
|
|
221 Ast_dump_context::dump_type(const Type* t)
|
|
222 {
|
|
223 if (t == NULL)
|
|
224 this->ostream() << "(nil type)";
|
|
225 else
|
|
226 // FIXME: write a type pretty printer instead of
|
|
227 // using mangled names.
|
|
228 if (this->gogo_ != NULL)
|
|
229 this->ostream() << "(" << t->mangled_name(this->gogo_) << ")";
|
|
230 }
|
|
231
|
|
232 // Dump a textual representation of a block to the
|
|
233 // the dump file.
|
|
234
|
|
235 void
|
|
236 Ast_dump_context::dump_block(Block* b)
|
|
237 {
|
|
238 Ast_dump_traverse_blocks_and_functions adtbf(this);
|
|
239 b->traverse(&adtbf);
|
|
240 }
|
|
241
|
|
242 // Dump a textual representation of an expression to the
|
|
243 // the dump file.
|
|
244
|
|
245 void
|
|
246 Ast_dump_context::dump_expression(const Expression* e)
|
|
247 {
|
|
248 e->dump_expression(this);
|
|
249 }
|
|
250
|
|
251 // Dump a textual representation of an expression list to the
|
|
252 // the dump file.
|
|
253
|
|
254 void
|
|
255 Ast_dump_context::dump_expression_list(const Expression_list* el,
|
|
256 bool as_pairs /* = false */)
|
|
257 {
|
|
258 if (el == NULL)
|
|
259 return;
|
|
260
|
|
261 for (std::vector<Expression*>::const_iterator it = el->begin();
|
|
262 it != el->end();
|
|
263 it++)
|
|
264 {
|
|
265 if ( it != el->begin())
|
|
266 this->ostream() << ",";
|
|
267 if (*it != NULL)
|
|
268 (*it)->dump_expression(this);
|
|
269 else
|
|
270 this->ostream() << "NULL";
|
|
271 if (as_pairs)
|
|
272 {
|
|
273 this->ostream() << ":";
|
|
274 ++it;
|
|
275 (*it)->dump_expression(this);
|
|
276 }
|
|
277 }
|
|
278 }
|
|
279
|
|
280 // Dump a textual representation of a typed identifier to the
|
|
281 // the dump file.
|
|
282
|
|
283 void
|
|
284 Ast_dump_context::dump_typed_identifier(const Typed_identifier* ti)
|
|
285 {
|
|
286 this->ostream() << ti->name() << " ";
|
|
287 this->dump_type(ti->type());
|
|
288 }
|
|
289
|
|
290 // Dump a textual representation of a typed identifier list to the
|
|
291 // the dump file.
|
|
292
|
|
293 void
|
|
294 Ast_dump_context::dump_typed_identifier_list(
|
|
295 const Typed_identifier_list* ti_list)
|
|
296 {
|
|
297 if (ti_list == NULL)
|
|
298 return;
|
|
299
|
|
300 for (Typed_identifier_list::const_iterator it = ti_list->begin();
|
|
301 it != ti_list->end();
|
|
302 it++)
|
|
303 {
|
|
304 if (it != ti_list->begin())
|
|
305 this->ostream() << ",";
|
|
306 this->dump_typed_identifier(&(*it));
|
|
307 }
|
|
308 }
|
|
309
|
|
310 // Dump a textual representation of a temporary variable to the
|
|
311 // the dump file.
|
|
312
|
|
313 void
|
|
314 Ast_dump_context::dump_temp_variable_name(const Statement* s)
|
|
315 {
|
|
316 go_assert(s->classification() == Statement::STATEMENT_TEMPORARY);
|
|
317 // Use the statement address as part of the name for the temporary variable.
|
|
318 this->ostream() << "tmp." << (uintptr_t) s;
|
|
319 }
|
|
320
|
|
321 // Dump a textual representation of a label to the
|
|
322 // the dump file.
|
|
323
|
|
324 void
|
|
325 Ast_dump_context::dump_label_name(const Unnamed_label* l)
|
|
326 {
|
|
327 // Use the unnamed label address as part of the name for the temporary
|
|
328 // variable.
|
|
329 this->ostream() << "label." << (uintptr_t) l;
|
|
330 }
|
|
331
|
|
332 // Produce a textual representation of an operator symbol.
|
|
333
|
|
334 static const char*
|
|
335 op_string(Operator op)
|
|
336 {
|
|
337 // FIXME: This should be in line with symbols that are parsed,
|
|
338 // exported and/or imported.
|
|
339 switch (op)
|
|
340 {
|
|
341 case OPERATOR_PLUS:
|
|
342 return "+";
|
|
343 case OPERATOR_MINUS:
|
|
344 return "-";
|
|
345 case OPERATOR_NOT:
|
|
346 return "!";
|
|
347 case OPERATOR_XOR:
|
|
348 return "^";
|
|
349 case OPERATOR_OR:
|
|
350 return "|";
|
|
351 case OPERATOR_AND:
|
|
352 return "&";
|
|
353 case OPERATOR_MULT:
|
|
354 return "*";
|
|
355 case OPERATOR_OROR:
|
|
356 return "||";
|
|
357 case OPERATOR_ANDAND:
|
|
358 return "&&";
|
|
359 case OPERATOR_EQEQ:
|
|
360 return "==";
|
|
361 case OPERATOR_NOTEQ:
|
|
362 return "!=";
|
|
363 case OPERATOR_LT:
|
|
364 return "<";
|
|
365 case OPERATOR_LE:
|
|
366 return "<=";
|
|
367 case OPERATOR_GT:
|
|
368 return ">";
|
|
369 case OPERATOR_GE:
|
|
370 return ">=";
|
|
371 case OPERATOR_DIV:
|
|
372 return "/";
|
|
373 case OPERATOR_MOD:
|
|
374 return "%";
|
|
375 case OPERATOR_LSHIFT:
|
|
376 return "<<";
|
|
377 case OPERATOR_RSHIFT:
|
|
378 return "//";
|
|
379 case OPERATOR_BITCLEAR:
|
|
380 return "&^";
|
|
381 case OPERATOR_CHANOP:
|
|
382 return "<-";
|
|
383 case OPERATOR_PLUSEQ:
|
|
384 return "+=";
|
|
385 case OPERATOR_MINUSEQ:
|
|
386 return "-=";
|
|
387 case OPERATOR_OREQ:
|
|
388 return "|=";
|
|
389 case OPERATOR_XOREQ:
|
|
390 return "^=";
|
|
391 case OPERATOR_MULTEQ:
|
|
392 return "*=";
|
|
393 case OPERATOR_DIVEQ:
|
|
394 return "/=";
|
|
395 case OPERATOR_MODEQ:
|
|
396 return "%=";
|
|
397 case OPERATOR_LSHIFTEQ:
|
|
398 return "<<=";
|
|
399 case OPERATOR_RSHIFTEQ:
|
|
400 return ">>=";
|
|
401 case OPERATOR_ANDEQ:
|
|
402 return "&=";
|
|
403 case OPERATOR_BITCLEAREQ:
|
|
404 return "&^=";
|
|
405 case OPERATOR_PLUSPLUS:
|
|
406 return "++";
|
|
407 case OPERATOR_MINUSMINUS:
|
|
408 return "--";
|
|
409 case OPERATOR_COLON:
|
|
410 return ":";
|
|
411 case OPERATOR_COLONEQ:
|
|
412 return ":=";
|
|
413 case OPERATOR_SEMICOLON:
|
|
414 return ";";
|
|
415 case OPERATOR_DOT:
|
|
416 return ".";
|
|
417 case OPERATOR_ELLIPSIS:
|
|
418 return "...";
|
|
419 case OPERATOR_COMMA:
|
|
420 return ",";
|
|
421 case OPERATOR_LPAREN:
|
|
422 return "(";
|
|
423 case OPERATOR_RPAREN:
|
|
424 return ")";
|
|
425 case OPERATOR_LCURLY:
|
|
426 return "{";
|
|
427 case OPERATOR_RCURLY:
|
|
428 return "}";
|
|
429 case OPERATOR_LSQUARE:
|
|
430 return "[";
|
|
431 case OPERATOR_RSQUARE:
|
|
432 return "]";
|
|
433 default:
|
|
434 go_unreachable();
|
|
435 }
|
|
436 return NULL;
|
|
437 }
|
|
438
|
|
439 // Dump a textual representation of an operator to the
|
|
440 // the dump file.
|
|
441
|
|
442 void
|
|
443 Ast_dump_context::dump_operator(Operator op)
|
|
444 {
|
|
445 this->ostream() << op_string(op);
|
|
446 }
|
|
447
|
|
448 // Size of a single indent.
|
|
449
|
|
450 const int Ast_dump_context::offset_ = 2;
|
|
451
|
|
452 // Print indenting spaces to dump file.
|
|
453
|
|
454 void
|
|
455 Ast_dump_context::print_indent()
|
|
456 {
|
|
457 for (int i = 0; i < this->indent_ * this->offset_; i++)
|
|
458 this->ostream() << " ";
|
|
459 }
|
|
460
|
|
461 // Dump a textual representation of the ast to the
|
|
462 // the dump file.
|
|
463
|
|
464 void Gogo::dump_ast(const char* basename)
|
|
465 {
|
|
466 if (::ast_dump_flag.is_enabled())
|
|
467 {
|
|
468 Ast_dump_context adc;
|
|
469 adc.dump(this, basename);
|
|
470 }
|
|
471 }
|
|
472
|
|
473 // Implementation of String_dump interface.
|
|
474
|
|
475 void
|
|
476 Ast_dump_context::write_c_string(const char* s)
|
|
477 {
|
|
478 this->ostream() << s;
|
|
479 }
|
|
480
|
|
481 void
|
|
482 Ast_dump_context::write_string(const std::string& s)
|
|
483 {
|
|
484 this->ostream() << s;
|
|
485 }
|
|
486
|
145
|
487 // Dump statement to stream.
|
111
|
488
|
|
489 void
|
|
490 Ast_dump_context::dump_to_stream(const Statement* stm, std::ostream* out)
|
|
491 {
|
|
492 Ast_dump_context adc(out, false);
|
|
493 stm->dump_statement(&adc);
|
|
494 }
|
|
495
|
|
496 // Dump expression to stream.
|
|
497
|
|
498 void
|
|
499 Ast_dump_context::dump_to_stream(const Expression* expr, std::ostream* out)
|
|
500 {
|
|
501 Ast_dump_context adc(out, false);
|
|
502 expr->dump_expression(&adc);
|
|
503 }
|
145
|
504
|
|
505 // Dump an expression to std::cerr. This is intended to be used
|
|
506 // from within a debugging session.
|
|
507
|
|
508 void
|
|
509 debug_go_expression(const Expression* expr)
|
|
510 {
|
|
511 if (expr == NULL)
|
|
512 std::cerr << "<null>";
|
|
513 else
|
|
514 {
|
|
515 Ast_dump_context::dump_to_stream(expr, &std::cerr);
|
|
516 std::string lstr = Linemap::location_to_string(expr->location());
|
|
517 std::cerr << " // loc " << lstr << std::endl;
|
|
518 }
|
|
519 }
|
|
520
|
|
521 // Shallow dump of stmt to std::cerr. This is intended to be used
|
|
522 // from within a debugging session.
|
|
523
|
|
524 void
|
|
525 debug_go_statement(const Statement* stmt)
|
|
526 {
|
|
527 if (stmt == NULL)
|
|
528 std::cerr << "<null>\n";
|
|
529 else
|
|
530 {
|
|
531 std::string lstr = Linemap::location_to_string(stmt->location());
|
|
532 Statement *ncstmt = const_cast<Statement*>(stmt);
|
|
533 Block_statement* bs = ncstmt->block_statement();
|
|
534 if (bs != NULL)
|
|
535 std::cerr << "Block " << bs->block()
|
|
536 << " // location: " << lstr << std::endl;
|
|
537 else
|
|
538 Ast_dump_context::dump_to_stream(stmt, &std::cerr);
|
|
539 }
|
|
540 }
|
|
541
|
|
542 // Deep dump of statement to std::cerr. This is intended to be used
|
|
543 // from within a debugging session.
|
|
544
|
|
545 void
|
|
546 debug_go_statement_deep(const Statement* statement)
|
|
547 {
|
|
548 Ast_dump_context adc(&std::cerr, true);
|
|
549 statement->dump_statement(&adc);
|
|
550 }
|
|
551
|
|
552 // Shallow dump of a block to std::cerr. This is intended to be used
|
|
553 // from within a debugging session.
|
|
554
|
|
555 void
|
|
556 debug_go_block(const Block* block)
|
|
557 {
|
|
558 if (block == NULL)
|
|
559 std::cerr << "<null>";
|
|
560 else
|
|
561 {
|
|
562 std::cerr << "Block " << block
|
|
563 << " (enclosing " << block->enclosing() << "):\n";
|
|
564 const std::vector<Statement*>* stmts = block->statements();
|
|
565 if (stmts != NULL)
|
|
566 {
|
|
567 for (size_t i = 0; i < stmts->size(); ++i)
|
|
568 {
|
|
569 debug_go_statement(stmts->at(i));
|
|
570 }
|
|
571 }
|
|
572 }
|
|
573 }
|
|
574
|
|
575 // Deep dump of a block to std:cerr. This is intended to be used
|
|
576 // from within a debugging session.
|
|
577
|
|
578 void
|
|
579 debug_go_block_deep(const Block* block)
|
|
580 {
|
|
581 Ast_dump_context adc(&std::cerr, true);
|
|
582 Block* ncblock = const_cast<Block*>(block);
|
|
583 adc.dump_block(ncblock);
|
|
584 }
|
|
585
|
|
586 class Type_dumper
|
|
587 {
|
|
588 typedef Unordered_map(const Type*, unsigned) idx_map;
|
|
589 public:
|
|
590 Type_dumper(const Type* type)
|
|
591 : top_(type), ntypes_(0)
|
|
592 {
|
|
593 this->worklist_.push_back(type);
|
|
594 }
|
|
595
|
|
596 void visit();
|
|
597
|
|
598 std::string stringResult() { return ss_.str(); }
|
|
599
|
|
600 private:
|
|
601 void emitpre(unsigned tag, const Type* addr);
|
|
602 void typeref(const char*, const Type*, const char *);
|
|
603 void visit_forward_declaration_type(const Forward_declaration_type* fdt);
|
|
604 void visit_function_type(const Function_type* ft);
|
|
605 void visit_struct_type(const Struct_type* st);
|
|
606 void visit_array_type(const Array_type* at);
|
|
607 void visit_map_type(const Map_type* mt);
|
|
608 void visit_channel_type(const Channel_type* mt);
|
|
609 void visit_interface_type(const Interface_type* mt);
|
|
610 void visit_methods(const Typed_identifier_list* methods,
|
|
611 const char *tag);
|
|
612 std::pair<bool, unsigned> lookup(const Type*);
|
|
613
|
|
614 static const unsigned notag = 0xffffffff;
|
|
615
|
|
616 private:
|
|
617 const Type* top_;
|
|
618 idx_map types_;
|
|
619 unsigned ntypes_;
|
|
620 std::list<const Type*> worklist_;
|
|
621 std::ostringstream ss_;
|
|
622 };
|
|
623
|
|
624 // Look up a type, installing it in 'types_'. Return is <found, N>
|
|
625 // where 'found' is true if type had been previously recorded, and N
|
|
626 // is the index/tag assigned to N. The input argument is appended to
|
|
627 // the work list if this is the first time we've seen it.
|
|
628
|
|
629 std::pair<bool, unsigned> Type_dumper::lookup(const Type* t)
|
|
630 {
|
|
631 std::pair<const Type*, unsigned> entry = std::make_pair(t, this->ntypes_);
|
|
632 std::pair<idx_map::iterator, bool> ins = this->types_.insert(entry);
|
|
633 if (ins.second)
|
|
634 {
|
|
635 this->ntypes_++;
|
|
636 if (t != this->top_)
|
|
637 this->worklist_.push_back(t);
|
|
638 }
|
|
639 return std::make_pair(ins.second, ins.first->second);
|
|
640 }
|
|
641
|
|
642 // Emit preamble prior to dumping a type, including the type
|
|
643 // pointer itself and the tag we've assigned it. If no
|
|
644 // tag is specified (via special "notag" value) and/or the
|
|
645 // pointer is null, then just emit an equivalent amount
|
|
646 // of spaces.
|
|
647
|
|
648 void Type_dumper::emitpre(unsigned tag, const Type* ptr)
|
|
649 {
|
|
650 char tbuf[50], pbuf[50], buf[200];
|
|
651
|
|
652 tbuf[0] = '\0';
|
|
653 if (tag != notag)
|
|
654 snprintf(tbuf, sizeof tbuf, "T%u", tag);
|
|
655
|
|
656 pbuf[0] = '\0';
|
|
657 if (ptr != NULL)
|
|
658 snprintf(pbuf, sizeof pbuf, "%p", (const void*) ptr);
|
|
659
|
|
660 snprintf(buf, sizeof buf, "%8s %16s ", tbuf, pbuf);
|
|
661 this->ss_ << buf;
|
|
662 }
|
|
663
|
|
664 // Emit a reference to a type into the dump buffer. In most cases this means
|
|
665 // just the type tag, but for named types we also emit the name, and for
|
|
666 // simple/primitive types (ex: int64) we emit the type itself. If "pref" is
|
|
667 // non-NULL, emit the string prior to the reference, and if "suf" is non-NULL,
|
|
668 // emit it following the reference.
|
|
669
|
|
670 void Type_dumper::typeref(const char* pref, const Type* t, const char* suf)
|
|
671 {
|
|
672 if (pref != NULL)
|
|
673 this->ss_ << pref;
|
|
674 std::pair<bool, unsigned> p = this->lookup(t);
|
|
675 unsigned tag = p.second;
|
|
676 switch (t->classification())
|
|
677 {
|
|
678 case Type::TYPE_NAMED:
|
|
679 {
|
|
680 const Named_type* nt = t->named_type();
|
|
681 const Named_object* no = nt->named_object();
|
|
682 this->ss_ << "'" << no->message_name() << "' -> ";
|
|
683 const Type* underlying = nt->real_type();
|
|
684 this->typeref(NULL, underlying, NULL);
|
|
685 break;
|
|
686 }
|
|
687 case Type::TYPE_POINTER:
|
|
688 this->typeref("*", t->points_to(), NULL);
|
|
689 break;
|
|
690 case Type::TYPE_ERROR:
|
|
691 this->ss_ << "error_type";
|
|
692 break;
|
|
693 case Type::TYPE_INTEGER:
|
|
694 {
|
|
695 const Integer_type* it = t->integer_type();
|
|
696 if (it->is_abstract())
|
|
697 this->ss_ << "abstract_int";
|
|
698 else
|
|
699 this->ss_ << (it->is_unsigned() ? "u" : "") << "int" << it->bits();
|
|
700 break;
|
|
701 }
|
|
702 case Type::TYPE_FLOAT:
|
|
703 {
|
|
704 const Float_type* ft = t->float_type();
|
|
705 if (ft->is_abstract())
|
|
706 this->ss_ << "abstract_float";
|
|
707 else
|
|
708 this->ss_ << "float" << ft->bits();
|
|
709 break;
|
|
710 }
|
|
711 case Type::TYPE_COMPLEX:
|
|
712 {
|
|
713 const Complex_type* ct = t->complex_type();
|
|
714 if (ct->is_abstract())
|
|
715 this->ss_ << "abstract_complex";
|
|
716 else
|
|
717 this->ss_ << "complex" << ct->bits();
|
|
718 break;
|
|
719 }
|
|
720 case Type::TYPE_BOOLEAN:
|
|
721 this->ss_ << "bool";
|
|
722 break;
|
|
723 case Type::TYPE_STRING:
|
|
724 this->ss_ << "string";
|
|
725 break;
|
|
726 case Type::TYPE_NIL:
|
|
727 this->ss_ << "nil_type";
|
|
728 break;
|
|
729 case Type::TYPE_VOID:
|
|
730 this->ss_ << "void_type";
|
|
731 break;
|
|
732 case Type::TYPE_FUNCTION:
|
|
733 case Type::TYPE_STRUCT:
|
|
734 case Type::TYPE_ARRAY:
|
|
735 case Type::TYPE_MAP:
|
|
736 case Type::TYPE_CHANNEL:
|
|
737 case Type::TYPE_FORWARD:
|
|
738 case Type::TYPE_INTERFACE:
|
|
739 this->ss_ << "T" << tag;
|
|
740 break;
|
|
741
|
|
742 default:
|
|
743 // This is a debugging routine, so instead of a go_unreachable()
|
|
744 // issue a warning/error, to allow for the possibility that the
|
|
745 // compiler we're debugging is in a bad state.
|
|
746 this->ss_ << "<??? " << ((unsigned)t->classification()) << "> "
|
|
747 << "T" << tag;
|
|
748 }
|
|
749 if (suf != NULL)
|
|
750 this->ss_ << suf;
|
|
751 }
|
|
752
|
|
753 void Type_dumper::visit_forward_declaration_type(const Forward_declaration_type* fdt)
|
|
754 {
|
|
755 this->ss_ << "forward_declaration_type ";
|
|
756 if (fdt->is_defined())
|
|
757 this->typeref("-> ", fdt->real_type(), NULL);
|
|
758 else
|
|
759 this->ss_ << "'" << fdt->name() << "'";
|
|
760 this->ss_ << "\n";
|
|
761 }
|
|
762
|
|
763 void Type_dumper::visit_function_type(const Function_type* ft)
|
|
764 {
|
|
765 this->ss_ << "function\n";
|
|
766 const Typed_identifier* rec = ft->receiver();
|
|
767 if (rec != NULL)
|
|
768 {
|
|
769 this->emitpre(notag, NULL);
|
|
770 this->typeref("receiver ", rec->type(), "\n");
|
|
771 }
|
|
772 const Typed_identifier_list* parameters = ft->parameters();
|
|
773 if (parameters != NULL)
|
|
774 {
|
|
775 for (Typed_identifier_list::const_iterator p = parameters->begin();
|
|
776 p != parameters->end();
|
|
777 ++p)
|
|
778 {
|
|
779 this->emitpre(notag, NULL);
|
|
780 this->typeref(" param ", p->type(), "\n");
|
|
781 }
|
|
782 }
|
|
783 const Typed_identifier_list* results = ft->results();
|
|
784 if (results != NULL)
|
|
785 {
|
|
786 for (Typed_identifier_list::const_iterator p = results->begin();
|
|
787 p != results->end();
|
|
788 ++p)
|
|
789 {
|
|
790 this->emitpre(notag, NULL);
|
|
791 this->typeref(" result ", p->type(), "\n");
|
|
792 }
|
|
793 }
|
|
794 }
|
|
795
|
|
796 void Type_dumper::visit_struct_type(const Struct_type* st)
|
|
797 {
|
|
798 this->ss_ << "struct\n";
|
|
799 const Struct_field_list* fields = st->fields();
|
|
800 if (fields != NULL)
|
|
801 {
|
|
802 for (Struct_field_list::const_iterator p = fields->begin();
|
|
803 p != fields->end();
|
|
804 ++p)
|
|
805 {
|
|
806 this->emitpre(notag, NULL);
|
|
807 this->typeref(" field ", p->type(), "\n");
|
|
808 }
|
|
809 }
|
|
810 }
|
|
811
|
|
812 void Type_dumper::visit_array_type(const Array_type* at)
|
|
813 {
|
|
814 this->ss_ << "array [";
|
|
815 if (at->length() != NULL)
|
|
816 {
|
|
817 int64_t len = 0;
|
|
818 if (at->int_length(&len))
|
|
819 this->ss_ << len;
|
|
820 }
|
|
821 this->typeref("] ", at->element_type(), "\n");
|
|
822 }
|
|
823
|
|
824 void Type_dumper::visit_map_type(const Map_type* mt)
|
|
825 {
|
|
826 this->ss_ << "map [";
|
|
827 this->typeref(NULL, mt->key_type(), NULL);
|
|
828 this->typeref("] ", mt->val_type(), "\n");
|
|
829 }
|
|
830
|
|
831 void Type_dumper::visit_methods(const Typed_identifier_list* methods,
|
|
832 const char *tag)
|
|
833 {
|
|
834 if (tag != NULL)
|
|
835 {
|
|
836 this->emitpre(notag, NULL);
|
|
837 this->ss_ << tag << "\n";
|
|
838 }
|
|
839 for (Typed_identifier_list::const_iterator p = methods->begin();
|
|
840 p != methods->end();
|
|
841 ++p)
|
|
842 {
|
|
843 this->emitpre(notag, NULL);
|
|
844 if (p->name().empty())
|
|
845 this->typeref(" embedded method ", p->type(), "\n");
|
|
846 else
|
|
847 {
|
|
848 this->ss_ << " method '" << p->name() << "' ";
|
|
849 this->typeref(NULL, p->type(), "\n");
|
|
850 }
|
|
851 }
|
|
852 }
|
|
853
|
|
854 void Type_dumper::visit_interface_type(const Interface_type* it)
|
|
855 {
|
|
856 const Typed_identifier_list* methods =
|
|
857 (it->methods_are_finalized() ? it->methods() : it->local_methods());
|
|
858 if (methods == NULL)
|
|
859 {
|
|
860 this->ss_ << "empty_interface\n";
|
|
861 return;
|
|
862 }
|
|
863 this->ss_ << "interface";
|
|
864 if (! it->methods_are_finalized())
|
|
865 {
|
|
866 this->ss_ << " [unfinalized]\n";
|
|
867 visit_methods(it->local_methods(), NULL);
|
|
868 }
|
|
869 else
|
|
870 {
|
|
871 this->ss_ << "\n";
|
|
872 visit_methods(it->local_methods(), "[parse_methods]");
|
|
873 visit_methods(it->methods(), "[all_methods]");
|
|
874 }
|
|
875 }
|
|
876
|
|
877 void Type_dumper::visit_channel_type(const Channel_type* ct)
|
|
878 {
|
|
879 this->ss_ << "channel {";
|
|
880 if (ct->may_send())
|
|
881 this->ss_ << " send";
|
|
882 if (ct->may_receive())
|
|
883 this->ss_ << " receive";
|
|
884 this->typeref(" } ", ct->element_type(), "\n");
|
|
885 }
|
|
886
|
|
887 void Type_dumper::visit()
|
|
888 {
|
|
889 while (! this->worklist_.empty()) {
|
|
890 const Type* t = this->worklist_.front();
|
|
891 this->worklist_.pop_front();
|
|
892
|
|
893 std::pair<bool, unsigned> p = this->lookup(t);
|
|
894 unsigned tag = p.second;
|
|
895 this->emitpre(tag, t);
|
|
896
|
|
897 switch(t->classification())
|
|
898 {
|
|
899 case Type::TYPE_ERROR:
|
|
900 case Type::TYPE_INTEGER:
|
|
901 case Type::TYPE_FLOAT:
|
|
902 case Type::TYPE_COMPLEX:
|
|
903 case Type::TYPE_BOOLEAN:
|
|
904 case Type::TYPE_STRING:
|
|
905 case Type::TYPE_VOID:
|
|
906 case Type::TYPE_POINTER:
|
|
907 case Type::TYPE_NIL:
|
|
908 case Type::TYPE_NAMED:
|
|
909 this->typeref(NULL, t, "\n");
|
|
910 break;
|
|
911 case Type::TYPE_FORWARD:
|
|
912 this->visit_forward_declaration_type(t->forward_declaration_type());
|
|
913 break;
|
|
914
|
|
915 case Type::TYPE_FUNCTION:
|
|
916 this->visit_function_type(t->function_type());
|
|
917 break;
|
|
918 case Type::TYPE_STRUCT:
|
|
919 this->visit_struct_type(t->struct_type());
|
|
920 break;
|
|
921 case Type::TYPE_ARRAY:
|
|
922 this->visit_array_type(t->array_type());
|
|
923 break;
|
|
924 case Type::TYPE_MAP:
|
|
925 this->visit_map_type(t->map_type());
|
|
926 break;
|
|
927 case Type::TYPE_CHANNEL:
|
|
928 this->visit_channel_type(t->channel_type());
|
|
929 break;
|
|
930 case Type::TYPE_INTERFACE:
|
|
931 this->visit_interface_type(t->interface_type());
|
|
932 break;
|
|
933 default:
|
|
934 // This is a debugging routine, so instead of a go_unreachable()
|
|
935 // issue a warning/error, to allow for the possibility that the
|
|
936 // compiler we're debugging is in a bad state.
|
|
937 this->ss_ << "<unknown/unrecognized classification "
|
|
938 << ((unsigned)t->classification()) << ">\n";
|
|
939 }
|
|
940 }
|
|
941 }
|
|
942
|
|
943 // Dump a Go type for debugging purposes. This is a deep as opposed
|
|
944 // to shallow dump; all of the types reachable from the specified
|
|
945 // type will be dumped in addition to the type itself.
|
|
946
|
|
947 void debug_go_type(const Type* type)
|
|
948 {
|
|
949 if (type == NULL)
|
|
950 {
|
|
951 std::cerr << "<NULL type>\n";
|
|
952 return;
|
|
953 }
|
|
954 Type_dumper dumper(type);
|
|
955 dumper.visit();
|
|
956 std::cerr << dumper.stringResult();
|
|
957 }
|
|
958
|
|
959 void debug_go_type(Type* type)
|
|
960 {
|
|
961 const Type* ctype = type;
|
|
962 debug_go_type(ctype);
|
|
963 }
|