mirror of
https://github.com/wrog/lambdamoo
synced 2026-08-13 00:26:05 -04:00
Add in bitwise AND ".&.", OR ".|.", and XOR ".^."
Add arithmetic left shift "<<", arithmetic right shift ">>",
and logical right shift operators ">>>"
This retains the original parsing of something like "9.^.5" to be
interpreted as "square root of 9" However, "9.^. 5" is interpreted as
"9 XOR 5".
( The number parsing code moving to parse_number() is the only
significant change in adapting this extension to the new server;
the original behavior of the extension is retained in this commit
--wrog )
Change-Id: I99291bed3d07d68ced204ea27bf684dc09a861fc
1057 lines
28 KiB
C
1057 lines
28 KiB
C
/******************************************************************************
|
|
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 unsigned 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 SKIP_BYTES(n) ((void)(ptr += n))
|
|
#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() SKIP_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;
|
|
#ifdef BYTECODE_REDUCE_REF
|
|
} else if (IS_PUSH_CLEAR_n(op)) {
|
|
e = alloc_expr(EXPR_ID);
|
|
e->e.id = PUSH_CLEAR_n_INDEX(op);
|
|
push_expr(HOT_OP(e));
|
|
continue;
|
|
#endif /* BYTECODE_REDUCE_REF */
|
|
} 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);
|
|
(void) 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));
|
|
(void) 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 : (int)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!");
|
|
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 = ≻
|
|
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();
|
|
(void) 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;
|
|
(void) 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++;
|
|
}
|
|
(void) 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();
|
|
{
|
|
unsigned label = READ_LABEL();
|
|
if (label < ptr - bc.vector)
|
|
s->kind = STMT_CONTINUE;
|
|
}
|
|
ADD_STMT(HOT_OP(s));
|
|
break;
|
|
case EOP_BITAND:
|
|
kind = EXPR_BITAND;
|
|
goto finish_extended_binary;
|
|
case EOP_BITXOR:
|
|
kind = EXPR_BITXOR;
|
|
goto finish_extended_binary;
|
|
case EOP_BITOR:
|
|
kind = EXPR_BITOR;
|
|
goto finish_extended_binary;
|
|
case EOP_SHL:
|
|
kind = EXPR_SHL;
|
|
goto finish_extended_binary;
|
|
case EOP_SHR:
|
|
kind = EXPR_SHR;
|
|
goto finish_extended_binary;
|
|
case EOP_LSHR:
|
|
kind = EXPR_LSHR;
|
|
finish_extended_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 EOP_COMPLEMENT:
|
|
e = alloc_expr(EXPR_COMPLEMENT);
|
|
e->e.expr = pop_expr();
|
|
push_expr(HOT_OP1(e->e.expr, e));
|
|
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, unsigned pcnext)
|
|
{
|
|
Stmt *result;
|
|
Bytecodes bc;
|
|
unsigned i, sum;
|
|
|
|
program = prog;
|
|
bc = (pc_vector == MAIN_VECTOR
|
|
? program->main_vector
|
|
: program->fork_vectors[pc_vector]);
|
|
|
|
if (!pcnext)
|
|
hot_byte = 0;
|
|
else if (pcnext <= bc.size)
|
|
hot_byte = bc.vector + pcnext - 1;
|
|
else
|
|
panic("Illegal PC in FIND_LINE_NUMBER!");
|
|
|
|
hot_node = 0;
|
|
hot_position = (pcnext == bc.size ? 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, 0);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
unsigned
|
|
find_line_number(Program * prog, int vector, unsigned pc)
|
|
{
|
|
Stmt *tree;
|
|
|
|
if (prog->cached_lineno_pc == pc && prog->cached_lineno_vec == vector)
|
|
return prog->cached_lineno;
|
|
|
|
tree = program_to_tree(prog, MAIN_VECTOR, vector, pc + 1);
|
|
|
|
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!");
|
|
|
|
prog->cached_lineno_vec = vector;
|
|
prog->cached_lineno_pc = pc;
|
|
prog->cached_lineno = lineno;
|
|
return lineno;
|
|
}
|
|
|
|
|
|
/*
|
|
* $Log$
|
|
* 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
|
|
*/
|