aboutsummaryrefslogtreecommitdiffstats
path: root/decompile.c
diff options
context:
space:
mode:
authornop <nop>1997-03-03 03:44:59 +0000
committernop <nop>1997-03-03 03:44:59 +0000
commita515162931c35db517995e3427cb41cee2a63a0a (patch)
tree8e3f82edf617adebc676d878af284c0678dad555 /decompile.c
downloadmoo-cvs-a515162931c35db517995e3427cb41cee2a63a0a.tar.gz
moo-cvs-a515162931c35db517995e3427cb41cee2a63a0a.tar.xz
moo-cvs-a515162931c35db517995e3427cb41cee2a63a0a.zip
Initial revision
Diffstat (limited to 'decompile.c')
-rw-r--r--decompile.c1015
1 files changed, 1015 insertions, 0 deletions
diff --git a/decompile.c b/decompile.c
new file mode 100644
index 0000000..bb94116
--- /dev/null
+++ b/decompile.c
@@ -0,0 +1,1015 @@
+/******************************************************************************
+ Copyright (c) 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 "ast.h"
+#include "decompile.h"
+#include "exceptions.h"
+#include "opcode.h"
+#include "program.h"
+#include "storage.h"
+#include "utils.h"
+
+static Program *program;
+static Expr **expr_stack;
+static int top_expr_stack;
+
+static Byte *hot_byte;
+static void *hot_node;
+static enum { TOP, ENDBODY, BOTTOM, DONE } hot_position;
+
+static int lineno;
+
+static void
+push_expr(Expr *expr)
+{
+ expr_stack[top_expr_stack++] = expr;
+}
+
+static Expr *
+pop_expr(void)
+{
+ if (!top_expr_stack)
+ panic("Empty expr stack in DECOMPILE!");
+ return expr_stack[--top_expr_stack];
+}
+
+#define ADD_STMT(stmt) \
+do { \
+ Stmt *temp = stmt; \
+ \
+ *stmt_sink = temp; \
+ stmt_sink = &(temp->next); \
+ stmt_start = ptr - bc.vector;\
+} while (0);
+
+#define ADD_ARM(arm) \
+do { \
+ Cond_Arm *temp = arm; \
+ \
+ *arm_sink = temp; \
+ arm_sink = &(temp->next); \
+} while (0);
+
+#define READ_BYTES(n) \
+ (ptr += n, \
+ (n == 1 \
+ ? ptr[-1] \
+ : (n == 2 \
+ ? ((unsigned) ptr[-2] << 8) \
+ + ptr[-1] \
+ : ((unsigned) ptr[-4] << 24) \
+ + ((unsigned) ptr[-3] << 16) \
+ + ((unsigned) ptr[-2] << 8) \
+ + ptr[-1])))
+
+#define READ_LABEL() READ_BYTES(bc.numbytes_label)
+#define READ_LITERAL() program->literals[READ_BYTES(bc.numbytes_literal)]
+#define READ_FORK() program->fork_vectors[READ_BYTES(bc.numbytes_fork)]
+#define READ_ID() READ_BYTES(bc.numbytes_var_name)
+#define READ_STACK() READ_BYTES(bc.numbytes_stack)
+
+#define READ_JUMP(is_hot) read_jump(bc.numbytes_label, &ptr, &is_hot)
+
+static unsigned
+read_jump(unsigned numbytes_label, Byte **p, int *is_hot)
+{
+ Byte *ptr = *p;
+ unsigned label;
+
+ *is_hot = (ptr == hot_byte);
+ if (*ptr++ != OP_JUMP)
+ panic("Missing JUMP in DECOMPILE!");
+
+ label = READ_BYTES(numbytes_label);
+ *p = ptr;
+
+ return label;
+}
+
+#define HOT(is_hot, n) (node = n, is_hot ? (hot_node = node) : node)
+#define HOT1(is_hot, kid, n) HOT(is_hot || hot_node == kid, n)
+#define HOT2(is_hot, kid1, kid2, n) \
+ HOT1(is_hot || hot_node == kid1, kid2, n)
+#define HOT3(is_hot, kid1, kid2, kid3, n) \
+ HOT2(is_hot || hot_node == kid1, kid2, kid3, n)
+#define HOT4(is_hot, kid1, kid2, kid3, kid4, n) \
+ HOT3(is_hot || hot_node == kid1, kid2, kid3, kid4, n)
+
+#define HOT_OP(n) HOT(op_hot, n)
+#define HOT_OP1(kid, n) HOT1(op_hot, kid, n)
+#define HOT_OP2(kid1, kid2, n) HOT2(op_hot, kid1, kid2, n)
+#define HOT_OP3(kid1, kid2, kid3, n) \
+ HOT3(op_hot, kid1, kid2, kid3, n)
+
+#define HOT_POS(is_hot, n, pos) \
+ if (is_hot) { hot_node = n; hot_position = pos; }
+#define HOT_BOTTOM(is_hot, n) HOT_POS(is_hot, n, BOTTOM)
+
+#define DECOMPILE(bc, start, end, stmt_sink, arm_sink) \
+ (ptr = decompile(bc, start, end, stmt_sink, arm_sink))
+
+static Byte *
+decompile(Bytecodes bc, Byte *start, Byte *end, Stmt **stmt_sink,
+ Cond_Arm **arm_sink)
+{
+ /*** The reader will likely find it useful to consult the file
+ *** `MOOCodeSequences.txt' in this directory while reading this function.
+ ***/
+
+ Byte *ptr = start;
+ unsigned stmt_start = start - bc.vector;
+ unsigned jump_len = bc.numbytes_label + 1;
+ Stmt *s;
+ Expr *e;
+ enum Expr_Kind kind;
+ void *node;
+ int asgn_hot = 0;
+
+ if (stmt_sink)
+ *stmt_sink = 0;
+ if (arm_sink)
+ *arm_sink = 0;
+
+ while (ptr < end) {
+ int op_hot = (ptr == hot_byte);
+ Opcode op = *ptr++;
+
+ if (IS_PUSH_n(op)) {
+ e = alloc_expr(EXPR_ID);
+ e->e.id = PUSH_n_INDEX(op);
+ push_expr(HOT_OP(e));
+ continue;
+ } else if (IS_PUT_n(op)) {
+ e = alloc_expr(EXPR_ID);
+ e->e.id = PUT_n_INDEX(op);
+ e = alloc_binary(EXPR_ASGN, e, pop_expr());
+ push_expr(HOT_OP1(e->e.bin.rhs, e));
+ continue;
+ } else if (IS_OPTIM_NUM_OPCODE(op)) {
+ e = alloc_var(TYPE_INT);
+ e->e.var.v.num = OPCODE_TO_OPTIM_NUM(op);
+ push_expr(HOT_OP(e));
+ continue;
+ }
+
+ switch (op) {
+ case OP_IF:
+ {
+ unsigned next = READ_LABEL();
+ Expr *condition = pop_expr();
+ Stmt *arm_stmts;
+ Cond_Arm *arm;
+ unsigned done;
+ int jump_hot;
+
+ s = alloc_stmt(STMT_COND);
+ DECOMPILE(bc, ptr, bc.vector + next - jump_len, &arm_stmts, 0);
+ arm = s->s.cond.arms = alloc_cond_arm(condition, arm_stmts);
+ HOT_OP1(condition, arm);
+ done = READ_JUMP(jump_hot);
+ HOT_BOTTOM(jump_hot, arm);
+ DECOMPILE(bc, ptr, bc.vector + done, &(s->s.cond.otherwise),
+ &(arm->next));
+
+ ADD_STMT(s);
+ }
+ break;
+ case OP_EIF:
+ {
+ unsigned next = READ_LABEL();
+ Expr *condition = pop_expr();
+ Stmt *arm_stmts;
+ Cond_Arm *arm;
+ unsigned done;
+ int jump_hot;
+
+ DECOMPILE(bc, ptr, bc.vector + next - jump_len, &arm_stmts, 0);
+ ADD_ARM(arm = alloc_cond_arm(condition, arm_stmts));
+ HOT_OP1(condition, arm);
+ done = READ_JUMP(jump_hot);
+ HOT_BOTTOM(jump_hot, arm);
+ if (bc.vector + done != end)
+ panic("ELSEIF jumps to wrong place in DECOMPILE!");
+ stmt_start = ptr - bc.vector;
+ }
+ break;
+ case OP_FOR_LIST:
+ {
+ unsigned top = (ptr - 1) - bc.vector;
+ int id = READ_ID();
+ unsigned done = READ_LABEL();
+ Expr *one = pop_expr();
+ Expr *list = pop_expr();
+ int jump_hot;
+
+ if (one->kind != EXPR_VAR
+ || one->e.var.type != TYPE_INT
+ || one->e.var.v.num != 1)
+ panic("Not a literal one in DECOMPILE!");
+ else
+ dealloc_node(one);
+ s = alloc_stmt(STMT_LIST);
+ s->s.list.id = id;
+ s->s.list.expr = list;
+ DECOMPILE(bc, ptr, bc.vector + done - jump_len,
+ &(s->s.list.body), 0);
+ if (top != READ_JUMP(jump_hot))
+ panic("FOR_LIST jumps to wrong place in DECOMPILE!");
+ HOT_BOTTOM(jump_hot, s);
+ ADD_STMT(HOT_OP2(one, list, s));
+ }
+ break;
+ case OP_FOR_RANGE:
+ {
+ unsigned top = (ptr - 1) - bc.vector;
+ int id = READ_ID();
+ unsigned done = READ_LABEL();
+ Expr *to = pop_expr();
+ Expr *from = pop_expr();
+ int jump_hot;
+
+ s = alloc_stmt(STMT_RANGE);
+ s->s.range.id = id;
+ s->s.range.from = from;
+ s->s.range.to = to;
+ DECOMPILE(bc, ptr, bc.vector + done - jump_len,
+ &(s->s.range.body), 0);
+ if (top != READ_JUMP(jump_hot))
+ panic("FOR_RANGE jumps to wrong place in DECOMPILE!");
+ HOT_BOTTOM(jump_hot, s);
+ ADD_STMT(HOT_OP2(from, to, s));
+ }
+ break;
+ case OP_WHILE:
+ s = alloc_stmt(STMT_WHILE);
+ s->s.loop.id = -1;
+ finish_while:
+ {
+ unsigned top = stmt_start;
+ unsigned done = READ_LABEL();
+ Expr *condition = pop_expr();
+ int jump_hot;
+
+ s->s.loop.condition = condition;
+ DECOMPILE(bc, ptr, bc.vector + done - jump_len,
+ &(s->s.loop.body), 0);
+ if (top != READ_JUMP(jump_hot))
+ panic("WHILE jumps to wrong place in DECOMPILE!");
+ HOT_BOTTOM(jump_hot, s);
+ ADD_STMT(HOT_OP1(condition, s));
+ }
+ break;
+ case OP_FORK:
+ case OP_FORK_WITH_ID:
+ {
+ Bytecodes fbc;
+ int id;
+ Expr *time = pop_expr();
+
+ fbc = READ_FORK();
+ id = (op == OP_FORK ? -1 : READ_ID());
+ s = alloc_stmt(STMT_FORK);
+ s->s.fork.id = id;
+ s->s.fork.time = time;
+ (void) decompile(fbc, fbc.vector, fbc.vector + fbc.size,
+ &(s->s.fork.body), 0);
+ HOT_BOTTOM(hot_byte == fbc.vector + fbc.size - 1, s);
+ ADD_STMT(HOT_OP1(time, s));
+ }
+ break;
+ case OP_POP:
+ s = alloc_stmt(STMT_EXPR);
+ e = s->s.expr = pop_expr();
+ ADD_STMT(HOT_OP1(e, s));
+ break;
+ case OP_RETURN:
+ case OP_RETURN0:
+ s = alloc_stmt(STMT_RETURN);
+ e = s->s.expr = (op == OP_RETURN ? pop_expr() : 0);
+ ADD_STMT(HOT(op_hot || (e && e == hot_node), s));
+ break;
+ case OP_DONE:
+ if (ptr != end)
+ panic("DONE not at end in DECOMPILE!");
+ break;
+ case OP_IMM:
+ e = alloc_expr(EXPR_VAR);
+ e->e.var = var_ref(READ_LITERAL());
+ push_expr(HOT_OP(e));
+ break;
+ case OP_G_PUSH:
+ e = alloc_expr(EXPR_ID);
+ e->e.id = READ_ID();
+ push_expr(HOT_OP(e));
+ break;
+ case OP_AND:
+ case OP_OR:
+ {
+ unsigned done = READ_LABEL();
+
+ e = pop_expr();
+ DECOMPILE(bc, ptr, bc.vector + done, 0, 0);
+ if (ptr != bc.vector + done)
+ panic("AND/OR jumps to wrong place in DECOMPILE!");
+ e = alloc_binary(op == OP_AND ? EXPR_AND : EXPR_OR,
+ e, pop_expr());
+ push_expr(HOT_OP2(e->e.bin.lhs, e->e.bin.rhs, e));
+ }
+ break;
+ case OP_UNARY_MINUS:
+ case OP_NOT:
+ e = alloc_expr(op == OP_NOT ? EXPR_NOT : EXPR_NEGATE);
+ e->e.expr = pop_expr();
+ push_expr(HOT_OP1(e->e.expr, e));
+ break;
+ case OP_GET_PROP:
+ case OP_PUSH_GET_PROP:
+ kind = EXPR_PROP;
+ goto finish_binary;
+ case OP_EQ:
+ kind = EXPR_EQ;
+ goto finish_binary;
+ case OP_NE:
+ kind = EXPR_NE;
+ goto finish_binary;
+ case OP_LT:
+ kind = EXPR_LT;
+ goto finish_binary;
+ case OP_LE:
+ kind = EXPR_LE;
+ goto finish_binary;
+ case OP_GT:
+ kind = EXPR_GT;
+ goto finish_binary;
+ case OP_GE:
+ kind = EXPR_GE;
+ goto finish_binary;
+ case OP_IN:
+ kind = EXPR_IN;
+ goto finish_binary;
+ case OP_ADD:
+ kind = EXPR_PLUS;
+ goto finish_binary;
+ case OP_MINUS:
+ kind = EXPR_MINUS;
+ goto finish_binary;
+ case OP_MULT:
+ kind = EXPR_TIMES;
+ goto finish_binary;
+ case OP_DIV:
+ kind = EXPR_DIVIDE;
+ goto finish_binary;
+ case OP_MOD:
+ kind = EXPR_MOD;
+ goto finish_binary;
+ case OP_REF:
+ case OP_PUSH_REF:
+ kind = EXPR_INDEX;
+ finish_binary:
+ e = pop_expr();
+ e = alloc_binary(kind, pop_expr(), e);
+ push_expr(HOT_OP2(e->e.bin.lhs, e->e.bin.rhs, e));
+ break;
+ case OP_RANGE_REF:
+ {
+ Expr *e1 = pop_expr();
+ Expr *e2 = pop_expr();
+
+ e = alloc_expr(EXPR_RANGE);
+ e->e.range.base = pop_expr();
+ e->e.range.from = e2;
+ e->e.range.to = e1;
+ push_expr(HOT_OP3(e1, e2, e->e.range.base, e));
+ }
+ break;
+ case OP_BI_FUNC_CALL:
+ {
+ Expr *a = pop_expr();
+
+ if (a->kind != EXPR_LIST)
+ panic("Missing arglist for BI_FUNC_CALL in DECOMPILE!");
+ e = alloc_expr(EXPR_CALL);
+ e->e.call.args = a->e.list;
+ dealloc_node(a);
+ e->e.call.func = READ_BYTES(1);
+ push_expr(HOT_OP1(a, e));
+ }
+ break;
+ case OP_CALL_VERB:
+ {
+ Expr *a = pop_expr();
+ Expr *e2 = pop_expr();
+
+ if (a->kind != EXPR_LIST)
+ panic("Missing arglist for CALL_VERB in DECOMPILE!");
+ e = alloc_verb(pop_expr(), e2, a->e.list);
+ dealloc_node(a);
+ push_expr(HOT_OP3(e->e.verb.obj, a, e2, e));
+ }
+ break;
+ case OP_IF_QUES:
+ {
+ unsigned label = READ_LABEL();
+ int jump_hot;
+
+ e = alloc_expr(EXPR_COND);
+ e->e.cond.condition = pop_expr();
+ DECOMPILE(bc, ptr, bc.vector + label - jump_len, 0, 0);
+ label = READ_JUMP(jump_hot);
+ e->e.cond.consequent = pop_expr();
+ DECOMPILE(bc, ptr, bc.vector + label, 0, 0);
+ if (ptr != bc.vector + label)
+ panic("THEN jumps to wrong place in DECOMPILE!");
+ e->e.cond.alternate = pop_expr();
+ push_expr(HOT3(op_hot || jump_hot, e->e.cond.condition,
+ e->e.cond.consequent, e->e.cond.alternate,
+ e));
+ }
+ break;
+ case OP_PUT_TEMP:
+ /* Ignore; following RANGESET or INDEXSET does the work */
+ if (op_hot)
+ asgn_hot = 1;
+ break;
+ case OP_INDEXSET:
+ /* Most of the lvalue has already been constructed on the stack.
+ * Add the final indexing.
+ */
+ {
+ Expr *rvalue = pop_expr();
+ Expr *index = pop_expr();
+
+ e = alloc_binary(EXPR_INDEX, pop_expr(), index);
+ push_expr(HOT3(op_hot || asgn_hot,
+ e->e.bin.lhs, index, rvalue,
+ alloc_binary(EXPR_ASGN, e, rvalue)));
+ }
+ finish_indexed_assignment:
+ /* The remainder of this complex assignment code sequence is
+ * useless for the purpose of decompilation.
+ */
+ asgn_hot = 0;
+ while (*ptr != OP_PUSH_TEMP) {
+ if (ptr == hot_byte) /* it's our assignment expression */
+ hot_node = expr_stack[top_expr_stack];
+ ptr++;
+ }
+ ptr++;
+ break;
+ case OP_G_PUT:
+ e = alloc_expr(EXPR_ID);
+ e->e.id = READ_ID();
+ e = alloc_binary(EXPR_ASGN, e, pop_expr());
+ push_expr(HOT_OP1(e->e.bin.rhs, e));
+ break;
+ case OP_PUT_PROP:
+ {
+ Expr *rvalue = pop_expr();
+
+ e = pop_expr();
+ e = alloc_binary(EXPR_PROP, pop_expr(), e);
+ push_expr(HOT_OP3(e->e.bin.lhs, e->e.bin.rhs, rvalue,
+ alloc_binary(EXPR_ASGN, e, rvalue)));
+ }
+ break;
+ case OP_MAKE_EMPTY_LIST:
+ e = alloc_expr(EXPR_LIST);
+ e->e.list = 0;
+ push_expr(HOT_OP(e));
+ break;
+ case OP_MAKE_SINGLETON_LIST:
+ e = alloc_expr(EXPR_LIST);
+ e->e.list = alloc_arg_list(ARG_NORMAL, pop_expr());
+ push_expr(HOT_OP1(e->e.list->expr, e));
+ break;
+ case OP_CHECK_LIST_FOR_SPLICE:
+ e = alloc_expr(EXPR_LIST);
+ e->e.list = alloc_arg_list(ARG_SPLICE, pop_expr());
+ push_expr(HOT_OP1(e->e.list->expr, e));
+ break;
+ case OP_LIST_ADD_TAIL:
+ case OP_LIST_APPEND:
+ {
+ Expr *list;
+ Arg_List *a;
+
+ e = pop_expr();
+ list = pop_expr();
+ if (list->kind != EXPR_LIST)
+ panic("Missing list expression in DECOMPILE!");
+ for (a = list->e.list; a->next; a = a->next)
+ ;
+ a->next = alloc_arg_list(op == OP_LIST_APPEND ? ARG_SPLICE
+ : ARG_NORMAL, e);
+ push_expr(HOT_OP1(e, list));
+ }
+ break;
+ case OP_PUSH_TEMP:
+ case OP_JUMP:
+ panic("Unexpected opcode in DECOMPILE!");
+ break;
+ case OP_EXTENDED:
+ {
+ Extended_Opcode eop = *ptr++;
+
+ switch (eop) {
+ case EOP_RANGESET:
+ /* Most of the lvalue has already been constructed on the
+ * stack. Add the final subranging.
+ */
+ {
+ Expr *rvalue = pop_expr();
+
+ e = alloc_expr(EXPR_RANGE);
+ e->e.range.to = pop_expr();
+ e->e.range.from = pop_expr();
+ e->e.range.base = pop_expr();
+ push_expr(HOT4(op_hot || asgn_hot,
+ e->e.range.base, e->e.range.from,
+ e->e.range.to, rvalue,
+ alloc_binary(EXPR_ASGN, e, rvalue)));
+ }
+ goto finish_indexed_assignment;
+ case EOP_LENGTH:
+ READ_STACK();
+ e = alloc_expr(EXPR_LENGTH);
+ push_expr(HOT_OP(e));
+ break;
+ case EOP_EXP:
+ kind = EXPR_EXP;
+ goto finish_binary;
+ case EOP_SCATTER:
+ {
+ Scatter *sc, **scp;
+ int nargs = *ptr++;
+ int rest = (ptr++, *ptr++); /* skip nreq */
+ int *next_label = 0;
+ int i, done, is_hot = op_hot;
+
+ for (i = 1, scp = &sc;
+ i <= nargs;
+ i++, scp = &((*scp)->next)) {
+ int id = READ_ID();
+ int label = READ_LABEL();
+
+ *scp = alloc_scatter(i == rest ? SCAT_REST :
+ label == 0 ? SCAT_REQUIRED
+ : SCAT_OPTIONAL,
+ id, 0);
+ if (label > 1) {
+ (*scp)->label = label;
+ if (next_label)
+ *next_label = label;
+ next_label = &((*scp)->next_label);
+ } else
+ (*scp)->label = 0;
+ }
+ done = READ_LABEL();
+ if (next_label)
+ *next_label = done;
+ e = alloc_expr(EXPR_SCATTER);
+ e->e.scatter = sc;
+ for (sc = e->e.scatter; sc; sc = sc->next)
+ if (sc->label) {
+ Expr *defallt;
+
+ if (ptr != bc.vector + sc->label)
+ panic("Misplaced default in DECOMPILE!");
+ DECOMPILE(bc, ptr,
+ bc.vector + sc->next_label - 1,
+ 0, 0);
+ defallt = pop_expr();
+ HOT1(is_hot, defallt, e);
+ if (defallt->kind != EXPR_ASGN
+ || defallt->e.bin.lhs->kind != EXPR_ID
+ || defallt->e.bin.lhs->e.id != sc->id)
+ panic("Wrong variable in DECOMPILE!");
+ sc->expr = defallt->e.bin.rhs;
+ dealloc_node(defallt->e.bin.lhs);
+ dealloc_node(defallt);
+ is_hot = (is_hot || ptr == hot_byte);
+ if (*ptr++ != OP_POP)
+ panic("Missing default POP in DECOMPILE!");
+ }
+ e = alloc_binary(EXPR_ASGN, e, pop_expr());
+ push_expr(HOT2(is_hot, e->e.bin.lhs, e->e.bin.rhs, e));
+ if (ptr != bc.vector + done)
+ panic("Not at end of scatter in DECOMPILE!");
+ }
+ break;
+ case EOP_PUSH_LABEL:
+ e = alloc_var(TYPE_INT);
+ e->e.var.v.num = READ_LABEL();
+ push_expr(HOT_OP(e));
+ break;
+ case EOP_CATCH:
+ {
+ Expr *label_expr = pop_expr();
+ Expr *codes = pop_expr();
+ Expr *try_expr, *default_expr = 0;
+ Arg_List *a = 0; /* silence warning */
+ int label, done;
+ int is_hot = op_hot;
+
+ if (label_expr->kind != EXPR_VAR
+ || label_expr->e.var.type != TYPE_INT)
+ panic("Not a catch label in DECOMPILE!");
+ label = label_expr->e.var.v.num;
+ dealloc_node(label_expr);
+ if (codes->kind == EXPR_LIST)
+ a = codes->e.list;
+ else if (codes->kind == EXPR_VAR
+ && codes->e.var.type == TYPE_INT
+ && codes->e.var.v.num == 0)
+ a = 0;
+ else
+ panic("Not a codes expression in DECOMPILE!");
+ dealloc_node(codes);
+ DECOMPILE(bc, ptr, end, 0, 0);
+ is_hot = (is_hot || ptr++ == hot_byte);
+ if (*ptr++ != EOP_END_CATCH)
+ panic("Missing END_CATCH in DECOMPILE!");
+ done = READ_LABEL();
+ try_expr = pop_expr();
+ if (ptr != bc.vector + label)
+ panic("Misplaced handler in DECOMPILE!");
+ is_hot = (is_hot || ptr == hot_byte);
+ op = *ptr++;
+ if (op == OPTIM_NUM_TO_OPCODE(1)) {
+ /* No default expression */
+ is_hot = (is_hot || ptr == hot_byte);
+ if (*ptr++ != OP_REF)
+ panic("Missing REF in DECOMPILE!");
+ default_expr = 0;
+ } else if (op == OP_POP) {
+ /* There's a default expression */
+ DECOMPILE(bc, ptr, bc.vector + done, 0, 0);
+ default_expr = pop_expr();
+ } else
+ panic("Bad default expression in DECOMPILE!");
+ if (ptr != bc.vector + done)
+ panic("CATCH ends in wrong place in DECOMPILE!");
+ e = alloc_expr(EXPR_CATCH);
+ e->e.catch.try = try_expr;
+ e->e.catch.codes = a;
+ e->e.catch.except = default_expr;
+ push_expr(HOT3(is_hot || (default_expr
+ && default_expr == hot_node),
+ label_expr, codes, try_expr,
+ e));
+ }
+ break;
+ case EOP_END_CATCH:
+ /* Early exit; main logic is in CATCH case, above. */
+ return ptr - 2;
+ case EOP_TRY_EXCEPT:
+ {
+ Expr *label_expr;
+ Except_Arm *ex;
+ Arg_List *a = 0; /* silence warning */
+ int count = *ptr++, label;
+ unsigned done;
+
+ s = HOT_OP(alloc_stmt(STMT_TRY_EXCEPT));
+ s->s.catch.excepts = 0;
+ while (count--) {
+ label_expr = pop_expr();
+ if (label_expr->kind != EXPR_VAR
+ || label_expr->e.var.type != TYPE_INT)
+ panic("Not an except label in DECOMPILE!");
+ label = label_expr->e.var.v.num;
+ dealloc_node(label_expr);
+ e = pop_expr();
+ if (e->kind == EXPR_LIST)
+ a = e->e.list;
+ else if (e->kind == EXPR_VAR
+ && e->e.var.type == TYPE_INT
+ && e->e.var.v.num == 0)
+ a = 0;
+ else
+ panic("Not a codes expression in DECOMPILE!");
+ dealloc_node(e);
+ ex = alloc_except(-1, a, 0);
+ ex->label = label;
+ ex->next = s->s.catch.excepts;
+ HOT2(0, label_expr, e, ex);
+ s->s.catch.excepts = ex;
+ }
+ DECOMPILE(bc, ptr, end, &(s->s.catch.body), 0);
+ HOT_POS(ptr++ == hot_byte, s, ENDBODY);
+ if (*ptr++ != EOP_END_EXCEPT)
+ panic("Missing END_EXCEPT in DECOMPILE!");
+ done = READ_LABEL();
+ for (ex = s->s.catch.excepts; ex; ex = ex->next) {
+ Byte *stop;
+ int jump_hot = 0;
+
+ if (ex->label != ptr - bc.vector)
+ panic("Not at start of handler in DECOMPILE!");
+ op_hot = (ptr == hot_byte);
+ op = *ptr++;
+ if (op == OP_G_PUT) {
+ ex->id = READ_ID();
+ op = *ptr++;
+ } else if (IS_PUT_n(op)) {
+ ex->id = PUT_n_INDEX(op);
+ op = *ptr++;
+ }
+ HOT(op_hot || ptr - 1 == hot_byte, ex);
+ if (op != OP_POP)
+ panic("Missing POP in DECOMPILE!");
+ if (ex->next)
+ stop = bc.vector + ex->next->label - jump_len;
+ else
+ stop = bc.vector + done;
+ DECOMPILE(bc, ptr, stop, &(ex->stmt), 0);
+ if (ex->next && READ_JUMP(jump_hot) != done)
+ panic("EXCEPT jumps to wrong place in "
+ "DECOMPILE!");
+ HOT_BOTTOM(jump_hot, ex);
+ }
+ if (ptr - bc.vector != done)
+ panic("EXCEPTS end in wrong place in DECOMPILE!");
+ ADD_STMT(s);
+ }
+ break;
+ case EOP_END_EXCEPT:
+ /* Early exit; main logic is in TRY_EXCEPT case, above. */
+ return ptr - 2;
+ case EOP_TRY_FINALLY:
+ {
+ int label = READ_LABEL();
+
+ s = HOT_OP(alloc_stmt(STMT_TRY_FINALLY));
+ DECOMPILE(bc, ptr, end, &(s->s.finally.body), 0);
+ HOT_POS(ptr++ == hot_byte, s, ENDBODY);
+ if (*ptr++ != EOP_END_FINALLY)
+ panic("Missing END_FINALLY in DECOMPILE!");
+ if (ptr - bc.vector != label)
+ panic("FINALLY handler in wrong place in "
+ "DECOMPILE!");
+ DECOMPILE(bc, ptr, end, &(s->s.finally.handler), 0);
+ HOT_BOTTOM(ptr++ == hot_byte, s);
+ if (*ptr++ != EOP_CONTINUE)
+ panic("Missing CONTINUE in DECOMPILE!");
+ ADD_STMT(s);
+ }
+ break;
+ case EOP_END_FINALLY:
+ case EOP_CONTINUE:
+ /* Early exit; main logic is in TRY_FINALLY case, above. */
+ return ptr - 2;
+ case EOP_WHILE_ID:
+ s = alloc_stmt(STMT_WHILE);
+ s->s.loop.id = READ_ID();
+ goto finish_while;
+ case EOP_EXIT:
+ case EOP_EXIT_ID:
+ s = alloc_stmt(STMT_BREAK);
+ if (eop == EOP_EXIT_ID)
+ s->s.exit = READ_ID();
+ else
+ s->s.exit = -1;
+ READ_STACK();
+ if (READ_LABEL() < ptr - bc.vector)
+ s->kind = STMT_CONTINUE;
+ ADD_STMT(HOT_OP(s));
+ break;
+ default:
+ panic("Unknown extended opcode in DECOMPILE!");
+ }
+ }
+ break;
+ default:
+ panic("Unknown opcode in DECOMPILE!");
+ }
+ }
+
+ if (ptr != end)
+ panic("Overshot end in DECOMPILE!");
+
+ return ptr;
+}
+
+static Stmt *
+program_to_tree(Program *prog, int vector, int pc_vector, int pc)
+{
+ Stmt *result;
+ Bytecodes bc;
+ int i, sum;
+
+ program = prog;
+ bc = (pc_vector == MAIN_VECTOR
+ ? program->main_vector
+ : program->fork_vectors[pc_vector]);
+
+ if (pc < 0)
+ hot_byte = 0;
+ else if (pc < bc.size)
+ hot_byte = bc.vector + pc;
+ else
+ panic("Illegal PC in FIND_LINE_NUMBER!");
+
+ hot_node = 0;
+ hot_position = (pc == bc.size - 1 ? DONE : TOP);
+
+ sum = program->main_vector.max_stack;
+ for (i = 0; i < program->fork_vectors_size; i++)
+ sum += program->fork_vectors[i].max_stack;
+ expr_stack = mymalloc(sum * sizeof(Expr *), M_DECOMPILE);
+ top_expr_stack = 0;
+
+ bc = (vector == MAIN_VECTOR
+ ? program->main_vector
+ : program->fork_vectors[vector]);
+
+ begin_code_allocation();
+ decompile(bc, bc.vector, bc.vector + bc.size, &result, 0);
+ end_code_allocation(0);
+
+ myfree(expr_stack, M_DECOMPILE);
+
+ return result;
+}
+
+Stmt *
+decompile_program(Program *prog, int vector)
+{
+ return program_to_tree(prog, vector, MAIN_VECTOR, -1);
+}
+
+static int
+find_hot_node(Stmt *stmt)
+{
+ /* Invariants: on entry, lineno is number of first line of STMT
+ * on exit, if result is true,
+ * lineno is line number of hot_node/position
+ * else, lineno is number of first line after STMT
+ */
+
+ for (; stmt; stmt = stmt->next) {
+ if (stmt == hot_node && hot_position == TOP)
+ return 1;
+
+ switch (stmt->kind) {
+ case STMT_COND:
+ {
+ Cond_Arm *arm;
+
+ for (arm = stmt->s.cond.arms; arm; arm = arm->next) {
+ if (arm == hot_node && hot_position == TOP)
+ return 1;
+ lineno++; /* advance to arm body */
+ if (find_hot_node(arm->stmt))
+ return 1;
+ else if (arm == hot_node && hot_position == BOTTOM) {
+ lineno--; /* Blame last line of arm */
+ return 1;
+ }
+ }
+ if (stmt->s.cond.otherwise) {
+ lineno++; /* Skip `else' line */
+ if (find_hot_node(stmt->s.cond.otherwise))
+ return 1;
+ }
+ }
+ break;
+ case STMT_LIST:
+ lineno++; /* Skip `for' line */
+ if (find_hot_node(stmt->s.list.body))
+ return 1;
+ break;
+ case STMT_RANGE:
+ lineno++; /* Skip `for' line */
+ if (find_hot_node(stmt->s.range.body))
+ return 1;
+ break;
+ case STMT_WHILE:
+ lineno++; /* Skip `while' line */
+ if (find_hot_node(stmt->s.loop.body))
+ return 1;
+ break;
+ case STMT_FORK:
+ lineno++; /* Skip `fork' line */
+ if (find_hot_node(stmt->s.fork.body))
+ return 1;
+ break;
+ case STMT_EXPR:
+ case STMT_RETURN:
+ case STMT_BREAK:
+ case STMT_CONTINUE:
+ /* Nothing more to do */
+ break;
+ case STMT_TRY_EXCEPT:
+ {
+ Except_Arm *ex;
+
+ lineno++; /* Skip `try' line */
+ if (find_hot_node(stmt->s.catch.body))
+ return 1;
+ if (stmt == hot_node && hot_position == ENDBODY) {
+ lineno--; /* blame it on last line of body */
+ return 1;
+ }
+ for (ex = stmt->s.catch.excepts; ex; ex = ex->next) {
+ if (ex == hot_node && hot_position == TOP)
+ return 1;
+ lineno++; /* skip `except' line */
+ if (find_hot_node(ex->stmt))
+ return 1;
+ if (ex == hot_node && hot_position == BOTTOM) {
+ lineno--; /* blame last line of handler */
+ return 1;
+ }
+ }
+ }
+ break;
+ case STMT_TRY_FINALLY:
+ lineno++; /* Skip `try' line */
+ if (find_hot_node(stmt->s.finally.body)
+ || (stmt == hot_node && hot_position == ENDBODY))
+ return 1;
+ lineno++; /* Skip `finally' line */
+ if (find_hot_node(stmt->s.finally.handler))
+ return 1;
+ break;
+ default:
+ panic("Unknown statement kind in FIND_HOT_NODE!");
+ }
+
+ if (stmt == hot_node && hot_position == BOTTOM)
+ return 1;
+
+ lineno++; /* Count this statement */
+ }
+
+ return 0;
+}
+
+int
+find_line_number(Program *prog, int vector, int pc)
+{
+ Stmt *tree = program_to_tree(prog, MAIN_VECTOR, vector, pc);
+
+ lineno = prog->first_lineno;
+ find_hot_node(tree);
+ free_stmt(tree);
+
+ if (!hot_node && hot_position != DONE)
+ panic("Can't do job in FIND_LINE_NUMBER!");
+
+ return lineno;
+}
+
+char rcsid_decompile[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.6 1996/03/10 01:17:48 pavel
+ * Removed a automatic structure initialization. Release 1.8.0.
+ *
+ * Revision 2.5 1996/02/08 07:17:28 pavel
+ * Renamed TYPE_NUM to TYPE_INT. Fixed bug in handling of OP_EIF
+ * vs. stmt_start. Added support for exponentiation expression, named WHILE
+ * loop and BREAK and CONTINUE statement. Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.4 1996/01/16 07:12:42 pavel
+ * Add EOP_SCATTER handling. Release 1.8.0alpha6.
+ *
+ * Revision 2.3 1995/12/31 03:11:29 pavel
+ * Added support for the `$' expression. Release 1.8.0alpha4.
+ *
+ * Revision 2.2 1995/12/28 00:37:38 pavel
+ * Fixed line number bug in `if' statements. Fixed memory leak in line-number
+ * finder. Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 07:58:23 pavel
+ * Fixed bug in decompilation of try-except-endtry. Added distinction between
+ * vector and pc_vector in program_to_tree(), to clear up bugs in
+ * forked-program accounting.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:25:59 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:25:40 pavel
+ * Initial revision
+ */