/****************************************************************************** Copyright (c) 1994, 1995, 1996 Xerox Corporation. All rights reserved. Portions of this code were written by Stephen White, aka ghond. Use and copying of this software and preparation of derivative works based upon this software are permitted. Any distribution of this software or derivative works must comply with all applicable United States export control laws. This software is made available AS IS, and Xerox Corporation makes no warranty about the software, its performance or its conformity to any specification. Any person obtaining a copy of this software is requested to send their name and post office or electronic mail address to: Pavel Curtis Xerox PARC 3333 Coyote Hill Rd. Palo Alto, CA 94304 Pavel@Xerox.Com *****************************************************************************/ #include "my-stdio.h" #include "bf_register.h" #include "config.h" #include "db.h" #include "functions.h" #include "list.h" #include "opcode.h" #include "program.h" #include "storage.h" #include "streams.h" #include "unparse.h" #include "utils.h" #include "verbs.h" const char *mnemonics[256], *ext_mnemonics[256]; struct mapping { unsigned value; const char *name; }; struct mapping mappings[] = { {OP_IF, "IF"}, {OP_WHILE, "WHILE"}, {OP_EIF, "ELSEIF"}, {OP_FORK, "FORK"}, {OP_FORK_WITH_ID, "FORK_NAMED"}, {OP_FOR_LIST, "FOR_LIST"}, {OP_FOR_RANGE, "FOR_RANGE"}, {OP_INDEXSET, "INDEXSET"}, {OP_PUSH_GET_PROP, "PUSH_GET_PROP"}, {OP_GET_PROP, "GET_PROP"}, {OP_CALL_VERB, "CALL_VERB"}, {OP_PUT_PROP, "PUT_PROP"}, {OP_BI_FUNC_CALL, "CALL_FUNC"}, {OP_IF_QUES, "IF_EXPR"}, {OP_REF, "INDEX"}, {OP_RANGE_REF, "RANGE"}, {OP_MAKE_SINGLETON_LIST, "MAKE_SINGLETON_LIST"}, {OP_CHECK_LIST_FOR_SPLICE, "CHECK_LIST_FOR_SPLICE"}, {OP_MULT, "MULTIPLY"}, {OP_DIV, "DIVIDE"}, {OP_MOD, "MOD"}, {OP_ADD, "ADD"}, {OP_MINUS, "SUBTRACT"}, {OP_EQ, "EQ"}, {OP_NE, "NE"}, {OP_LT, "LT"}, {OP_LE, "LE"}, {OP_GT, "GT"}, {OP_GE, "GE"}, {OP_IN, "IN"}, {OP_AND, "AND"}, {OP_OR, "OR"}, {OP_UNARY_MINUS, "NEGATE"}, {OP_NOT, "NOT"}, {OP_G_PUT, "PUT"}, {OP_G_PUSH, "PUSH"}, {OP_IMM, "PUSH_LITERAL"}, {OP_MAKE_EMPTY_LIST, "MAKE_EMPTY_LIST"}, {OP_LIST_ADD_TAIL, "LIST_ADD_TAIL"}, {OP_LIST_APPEND, "LIST_APPEND"}, {OP_PUSH_REF, "PUSH_INDEX"}, {OP_PUT_TEMP, "PUT_TEMP"}, {OP_PUSH_TEMP, "PUSH_TEMP"}, {OP_JUMP, "JUMP"}, {OP_RETURN, "RETURN"}, {OP_RETURN0, "RETURN 0"}, {OP_DONE, "DONE"}, {OP_POP, "POP"}}; struct mapping ext_mappings[] = { {EOP_RANGESET, "RANGESET"}, {EOP_LENGTH, "LENGTH"}, {EOP_PUSH_LABEL, "PUSH_LABEL"}, {EOP_SCATTER, "SCATTER"}, {EOP_EXP, "EXPONENT"}, {EOP_CATCH, "CATCH"}, {EOP_END_CATCH, "END_CATCH"}, {EOP_TRY_EXCEPT, "TRY_EXCEPT"}, {EOP_END_EXCEPT, "END_EXCEPT"}, {EOP_TRY_FINALLY, "TRY_FINALLY"}, {EOP_END_FINALLY, "END_FINALLY"}, {EOP_CONTINUE, "CONTINUE"}, {EOP_WHILE_ID, "WHILE_ID"}, {EOP_EXIT, "EXIT"}, {EOP_EXIT_ID, "EXIT_ID"}}; static void initialize_tables(void) { static int tables_initialized = 0; unsigned i; if (tables_initialized) return; for (i = 0; i < 256; i++) { mnemonics[i] = "*** Unknown opcode ***"; ext_mnemonics[i] = "*** Unknown extended opcode ***"; } for (i = 0; i < Arraysize(mappings); i++) mnemonics[mappings[i].value] = mappings[i].name; for (i = 0; i < Arraysize(ext_mappings); i++) ext_mnemonics[ext_mappings[i].value] = ext_mappings[i].name; tables_initialized = 1; } typedef void (*Printer)(const char *, void *); static Printer print; static void *print_data; static int bytes_width, max_bytes_width; static void output(Stream *s) { (*print)(reset_stream(s), print_data); } static void new_insn(Stream *s, unsigned pc) { stream_printf(s, "%3d:", pc); bytes_width = max_bytes_width; } static unsigned add_bytes(Stream *s, Byte *vector, unsigned pc, unsigned length) { unsigned arg = 0, b; while (length--) { if (bytes_width == 0) { output(s); stream_add_string(s, " "); bytes_width = max_bytes_width; } b = vector[pc++]; stream_printf(s, " %03d", b); arg = (arg << 8) + b; bytes_width--; } return arg; } static void finish_insn(Stream *s, Stream *insn) { while (bytes_width--) stream_add_string(s, " "); stream_add_string(s, reset_stream(insn)); output(s); } static void disassemble(Program *prog, Printer p, void *data) { Stream *s = new_stream(100); Stream *insn = new_stream(50); int i, l; unsigned pc; Bytecodes bc; const char *ptr; const char **names = prog->var_names; unsigned tmp, num_names = prog->num_var_names; # define NAMES(i) (tmp = i, \ tmp < num_names ? names[tmp] \ : "*** Unknown variable ***") Var *literals = prog->literals; initialize_tables(); print = p; print_data = data; stream_printf(s, "Language version number: %d", (int) prog->version); output(s); stream_printf(s, "First line number: %d", prog->first_lineno); output(s); for (i = -1; i < 0 || i < prog->fork_vectors_size; i++) { output(s); if (i == -1) { stream_printf(s, "Main code vector:"); output(s); stream_printf(s, "================="); output(s); bc = prog->main_vector; } else { stream_printf(s, "Forked code vector %d:", i); l = stream_length(s); output(s); while (l--) stream_add_char(s, '='); output(s); bc = prog->fork_vectors[i]; } stream_printf(s, "[Bytes for labels = %d, literals = %d, ", bc.numbytes_label, bc.numbytes_literal); stream_printf(s, "forks = %d, variables = %d, stack refs = %d]", bc.numbytes_fork, bc.numbytes_var_name, bc.numbytes_stack); output(s); stream_printf(s, "[Maximum stack size = %d]", bc.max_stack); output(s); max_bytes_width = 5; for (pc = 0; pc < bc.size;) { Byte b; unsigned arg; # define ADD_BYTES(n) (arg = add_bytes(s, bc.vector, pc, n), \ pc += n, \ arg) unsigned a1, a2; new_insn(s, pc); b = add_bytes(s, bc.vector, pc++, 1); if (b != OP_EXTENDED) stream_add_string(insn, COUNT_TICK(b) ? " * " : " "); if (IS_OPTIM_NUM_OPCODE(b)) stream_printf(insn, "NUM %d", OPCODE_TO_OPTIM_NUM(b)); else if (IS_PUSH_n(b)) stream_printf(insn, "PUSH %s", NAMES(PUSH_n_INDEX(b))); else if (IS_PUT_n(b)) stream_printf(insn, "PUT %s", NAMES(PUT_n_INDEX(b))); else if (b == OP_EXTENDED) { b = ADD_BYTES(1); stream_add_string(insn, COUNT_EOP_TICK(b) ? " * " : " "); stream_add_string(insn, ext_mnemonics[b]); switch ((Extended_Opcode) b) { case EOP_WHILE_ID: a1 = ADD_BYTES(bc.numbytes_var_name); a2 = ADD_BYTES(bc.numbytes_label); stream_printf(insn, " %s %d", NAMES(a1), a2); break; case EOP_EXIT_ID: stream_printf(insn, " %s", NAMES(ADD_BYTES(bc.numbytes_var_name))); /* fall thru */ case EOP_EXIT: a1 = ADD_BYTES(bc.numbytes_stack); a2 = ADD_BYTES(bc.numbytes_label); stream_printf(insn, " %d %d", a1, a2); break; case EOP_PUSH_LABEL: case EOP_END_CATCH: case EOP_END_EXCEPT: case EOP_TRY_FINALLY: stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_label)); break; case EOP_TRY_EXCEPT: stream_printf(insn, " %d", ADD_BYTES(1)); break; case EOP_LENGTH: stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_stack)); break; case EOP_SCATTER: { int i, nargs = ADD_BYTES(1); a1 = ADD_BYTES(1); a2 = ADD_BYTES(1); stream_printf(insn, " %d/%d/%d:", nargs, a1, a2); for (i = 0; i < nargs; i++) { a1 = ADD_BYTES(bc.numbytes_var_name); a2 = ADD_BYTES(bc.numbytes_label); stream_printf(insn, " %s/%d", NAMES(a1), a2); } stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_label)); } break; default: break; } } else { stream_add_string(insn, mnemonics[b]); switch ((Opcode) b) { case OP_IF: case OP_IF_QUES: case OP_EIF: case OP_AND: case OP_OR: case OP_JUMP: case OP_WHILE: stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_label)); break; case OP_FORK: stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_fork)); break; case OP_FORK_WITH_ID: a1 = ADD_BYTES(bc.numbytes_fork); a2 = ADD_BYTES(bc.numbytes_var_name); stream_printf(insn, " %d %s", a1, NAMES(a2)); break; case OP_FOR_LIST: case OP_FOR_RANGE: a1 = ADD_BYTES(bc.numbytes_var_name); a2 = ADD_BYTES(bc.numbytes_label); stream_printf(insn, " %s %d", NAMES(a1), a2); break; case OP_G_PUSH: case OP_G_PUT: stream_printf(insn, " %s", NAMES(ADD_BYTES(bc.numbytes_var_name))); break; case OP_IMM: { Var v; v = literals[ADD_BYTES(bc.numbytes_literal)]; switch (v.type) { case TYPE_OBJ: stream_printf(insn, " #%d", v.v.obj); break; case TYPE_INT: stream_printf(insn, " %d", v.v.num); break; case TYPE_STR: stream_add_string(insn, " \""); for (ptr = v.v.str; *ptr; ptr++) { if (*ptr == '"' || *ptr == '\\') stream_add_char(insn, '\\'); stream_add_char(insn, *ptr); } stream_add_char(insn, '"'); break; case TYPE_ERR: stream_printf(insn, " %s", error_name(v.v.err)); break; default: stream_printf(insn, " <literal type = %d>", v.type); break; } } break; case OP_BI_FUNC_CALL: stream_printf(insn, " %s", name_func_by_num(ADD_BYTES(1))); default: break; } } finish_insn(s, insn); } } free_stream(s); free_stream(insn); } static void print_line(const char *line, void *data) { FILE *f = data; fprintf(f, "%s\n", line); } void disassemble_to_file(FILE *fp, Program *prog) { disassemble(prog, print_line, fp); } void disassemble_to_stderr(Program *prog) { disassemble_to_file(stderr, prog); } struct data { char **lines; int used, max; }; static void add_line(const char *line, void *data) { struct data *d = data; if (d->used >= d->max) { int new_max = (d->max == 0 ? 20 : d->max * 2); char **new = mymalloc(sizeof(char **) * new_max, M_DISASSEMBLE); int i; for (i = 0; i < d->used; i++) new[i] = d->lines[i]; if (d->lines) myfree(d->lines, M_DISASSEMBLE); d->lines = new; d->max = new_max; } d->lines[d->used++] = str_dup(line); } static package bf_disassemble(Var arglist, Byte next, void *vdata, Objid progr) { Objid oid = arglist.v.list[1].v.obj; Var desc = arglist.v.list[2]; db_verb_handle h; struct data data; Var r; int i; enum error e; if ((e = validate_verb_descriptor(desc)) != E_NONE || (e = E_INVARG, !valid(oid))) { free_var(arglist); return make_error_pack(e); } h = find_described_verb(oid, desc); free_var(arglist); if (!h.ptr) return make_error_pack(E_VERBNF); if (!db_verb_allows(h, progr, VF_READ)) return make_error_pack(E_PERM); data.lines = 0; data.used = data.max = 0; disassemble(db_verb_program(h), add_line, &data); r = new_list(data.used); for (i = 1; i <= data.used; i++) { r.v.list[i].type = TYPE_STR; r.v.list[i].v.str = data.lines[i - 1]; } if (data.lines) myfree(data.lines, M_DISASSEMBLE); return make_var_pack(r); } void register_disassemble(void) { register_function("disassemble", 2, 2, bf_disassemble, TYPE_OBJ, TYPE_ANY); } char rcsid_disassemble[] = "$Id"; /* $Log: disassemble.c,v $ * Revision 2.6 1996/04/08 01:09:40 pavel * Added missing mnemonic-table entry for EOP_PUSH_LABEL. Release 1.8.0p3. * * Revision 2.5 1996/02/08 07:15:40 pavel * Added support for exponentiation expression, named WHILE loop, and BREAK * and CONTINUE statements. Removed redundant error-value unparsing code. * Added printing of language version number. Added support for EOPs that * cost ticks. Renamed TYPE_NUM to TYPE_INT. Added disassemble_to_file(). * Updated copyright notice for 1996. Release 1.8.0beta1. * * Revision 2.4 1996/01/16 07:16:38 pavel * Add EOP_SCATTER handling. Added support for line-wrapping in the display * of the bytes of an instruction. Release 1.8.0alpha6. * * Revision 2.3 1995/12/31 00:07:49 pavel * Added support for EOP_LENGTH and stack references in general. * Release 1.8.0alpha4. * * Revision 2.2 1995/12/28 00:31:29 pavel * Added support for numeric verb descriptors in disassemble() built-in. * Release 1.8.0alpha3. * * Revision 2.1 1995/12/11 08:10:32 pavel * Accounted for verb programs never being NULL any more. * Release 1.8.0alpha2. * * Revision 2.0 1995/11/30 05:00:54 pavel * New baseline version, corresponding to release 1.8.0alpha1. * * Revision 1.1 1995/11/30 05:00:43 pavel * Initial revision */