|
|
|
@ -0,0 +1,283 @@
|
|
|
|
|
/*
|
|
|
|
|
dot_type.c
|
|
|
|
|
|
|
|
|
|
"emit" types to dot (graphvis).
|
|
|
|
|
|
|
|
|
|
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
|
|
|
|
|
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
|
|
|
Date: 2020/03/28
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
|
# include <string.h>
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
|
# include <strings.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#include <QF/dstring.h>
|
|
|
|
|
#include <QF/mathlib.h>
|
|
|
|
|
#include <QF/quakeio.h>
|
|
|
|
|
#include <QF/va.h>
|
|
|
|
|
|
|
|
|
|
#include "class.h"
|
|
|
|
|
#include "symtab.h"
|
|
|
|
|
#include "type.h"
|
|
|
|
|
#include "qc-parse.h"
|
|
|
|
|
#include "strpool.h"
|
|
|
|
|
|
|
|
|
|
typedef void (*print_f) (dstring_t *dstr, type_t *, int, int);
|
|
|
|
|
static void dot_print_type (dstring_t *dstr, type_t *t, int level, int id);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_pointer (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
type_t *aux = t->t.fldptr.type;
|
|
|
|
|
|
|
|
|
|
dot_print_type (dstr, aux, level, id);
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, aux);
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"%c\"];\n", indent, "", t,
|
|
|
|
|
t->type == ev_pointer ? '*' : '.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_ellipsis (dstring_t *dstr, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
static int ellipsis_id;
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
|
|
|
|
|
if (ellipsis_id == id) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ellipsis_id = id;
|
|
|
|
|
dasprintf (dstr, "%*st_ellipsis [label=\"...\"];\n", indent, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_function (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
const ty_func_t *func = &t->t.func;
|
|
|
|
|
type_t *ret = func->type;
|
|
|
|
|
type_t *param;
|
|
|
|
|
|
|
|
|
|
dot_print_type (dstr, ret, level + 1, id);
|
|
|
|
|
if (func->num_params < 0) {
|
|
|
|
|
for (int i = 0; i < ~func->num_params; i++) {
|
|
|
|
|
param = func->param_types[i];
|
|
|
|
|
dot_print_type (dstr, param, level + 1, id);
|
|
|
|
|
}
|
|
|
|
|
print_ellipsis (dstr, level, id);
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < func->num_params; i++) {
|
|
|
|
|
param = func->param_types[i];
|
|
|
|
|
dot_print_type (dstr, param, level + 1, id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\" [label=\"r\"];\n", indent, "",
|
|
|
|
|
t, ret);
|
|
|
|
|
if (func->num_params < 0) {
|
|
|
|
|
for (int i = 0; i < ~func->num_params; i++) {
|
|
|
|
|
param = func->param_types[i];
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, param);
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_ellipsis\";\n", indent, "", t);
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < func->num_params; i++) {
|
|
|
|
|
param = func->param_types[i];
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, param);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"( )\"];\n", indent, "", t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_basic (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
if (t->type == ev_pointer || t->type == ev_field) {
|
|
|
|
|
print_pointer (dstr, t, level, id);
|
|
|
|
|
} else if (t->type == ev_func) {
|
|
|
|
|
print_function (dstr, t, level, id);
|
|
|
|
|
} else {
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"%s\"];\n", indent, "", t, t->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_struct (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
const symtab_t *symtab = t->t.symtab;
|
|
|
|
|
const symbol_t *sym;
|
|
|
|
|
int pnum;
|
|
|
|
|
static const char *struct_type_names[3] = {"struct", "union", "enum"};
|
|
|
|
|
const char *struct_type = struct_type_names[t->meta - ty_struct];
|
|
|
|
|
|
|
|
|
|
if (!symtab) {
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"%s %s\"];\n", indent, "", t,
|
|
|
|
|
struct_type, quote_string (t->name));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (t->meta != ty_enum) {
|
|
|
|
|
for (sym = symtab->symbols; sym; sym = sym->next) {
|
|
|
|
|
if (sym->sy_type != sy_var) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
dot_print_type (dstr, sym->type, level, id);
|
|
|
|
|
}
|
|
|
|
|
for (pnum = 0, sym = symtab->symbols; sym; sym = sym->next) {
|
|
|
|
|
if (sym->sy_type != sy_var) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*st_%p:f%d -> \"t_%p\";\n", indent, "",
|
|
|
|
|
t, pnum++, sym->type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*st_%p [shape=none,label=<\n", indent, "", t);
|
|
|
|
|
dasprintf (dstr, "%*s<table border=\"0\" cellborder=\"1\" "
|
|
|
|
|
"cellspacing=\"0\">\n",
|
|
|
|
|
indent + 2, "");
|
|
|
|
|
dasprintf (dstr, "%*s<tr><td colspan=\"2\">%s %s</td></tr>\n",
|
|
|
|
|
indent + 4, "",
|
|
|
|
|
struct_type, quote_string (t->name));
|
|
|
|
|
for (pnum = 0, sym = symtab->symbols; sym; sym = sym->next) {
|
|
|
|
|
int val;
|
|
|
|
|
const char *port = "";
|
|
|
|
|
if (sym->sy_type == sy_const) {
|
|
|
|
|
val = sym->s.value->v.integer_val;
|
|
|
|
|
} else {
|
|
|
|
|
if (sym->sy_type != sy_var) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
val = sym->s.offset;
|
|
|
|
|
port = va (" port=\"f%d\"", pnum++);
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*s<tr><td>%s</td><td%s>%d</td></tr>\n",
|
|
|
|
|
indent + 4, "",
|
|
|
|
|
quote_string (sym->name), port, val);
|
|
|
|
|
}
|
|
|
|
|
dasprintf (dstr, "%*s</table>\n", indent + 2, "");
|
|
|
|
|
dasprintf (dstr, "%*s>];\n", indent, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_array (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
type_t *type = t->t.array.type;
|
|
|
|
|
|
|
|
|
|
dot_print_type (dstr, type, level, id);
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, type);
|
|
|
|
|
if (t->t.array.base) {
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"[%d..%d]\"];\n", indent, "", t,
|
|
|
|
|
t->t.array.base,
|
|
|
|
|
t->t.array.base + t->t.array.size - 1);
|
|
|
|
|
} else {
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"[%d]\"];\n", indent, "", t,
|
|
|
|
|
t->t.array.size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_class (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"class '%s'\"];\n", indent, "", t,
|
|
|
|
|
t->t.class->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_alias (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
type_t *aux = t->t.alias.aux_type;
|
|
|
|
|
type_t *full = t->t.alias.full_type;
|
|
|
|
|
|
|
|
|
|
dot_print_type (dstr, aux, level, id);
|
|
|
|
|
dot_print_type (dstr, full, level, id);
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, aux);
|
|
|
|
|
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, full);
|
|
|
|
|
dasprintf (dstr, "%*st_%p [label=\"alias '%s'\"];\n", indent, "", t,
|
|
|
|
|
t->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
dot_print_type (dstring_t *dstr, type_t *t, int level, int id)
|
|
|
|
|
{
|
|
|
|
|
static print_f print_funcs[] = {
|
|
|
|
|
print_basic,
|
|
|
|
|
print_struct,
|
|
|
|
|
print_struct,
|
|
|
|
|
print_struct,
|
|
|
|
|
print_array,
|
|
|
|
|
print_class,
|
|
|
|
|
print_alias,
|
|
|
|
|
};
|
|
|
|
|
int indent = level * 2 + 2;
|
|
|
|
|
|
|
|
|
|
if (!t) {
|
|
|
|
|
dasprintf (dstr, "%*s\"e_%p\" [label=\"(null)\"];\n", indent, "", t);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (t->printid == id) // already printed this type
|
|
|
|
|
return;
|
|
|
|
|
t->printid = id;
|
|
|
|
|
|
|
|
|
|
if ((unsigned) t->meta >= sizeof (print_funcs) / sizeof (print_funcs[0])) {
|
|
|
|
|
dasprintf (dstr, "%*se_%p [label=\"(bad type meta)\\n%d\"];\n",
|
|
|
|
|
indent, "", t, t->meta);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
print_funcs [t->meta] (dstr, t, level, id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
dump_dot_type (void *_t, const char *filename)
|
|
|
|
|
{
|
|
|
|
|
static int id = 0;
|
|
|
|
|
dstring_t *dstr = dstring_newstr ();
|
|
|
|
|
type_t *t = (type_t *) _t;
|
|
|
|
|
|
|
|
|
|
dasprintf (dstr, "digraph type_%p {\n", t);
|
|
|
|
|
dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename));
|
|
|
|
|
dasprintf (dstr, " layout=dot; rankdir=TB; compound=true;\n");
|
|
|
|
|
dot_print_type (dstr, t, 0, ++id);
|
|
|
|
|
dasprintf (dstr, "}\n");
|
|
|
|
|
|
|
|
|
|
if (filename) {
|
|
|
|
|
QFile *file;
|
|
|
|
|
|
|
|
|
|
file = Qopen (filename, "wt");
|
|
|
|
|
Qwrite (file, dstr->str, dstr->size - 1);
|
|
|
|
|
Qclose (file);
|
|
|
|
|
} else {
|
|
|
|
|
fputs (dstr->str, stdout);
|
|
|
|
|
}
|
|
|
|
|
dstring_delete (dstr);
|
|
|
|
|
}
|