void print(std::ostream &os) const { os << "function("; for (int i = 0; i < tf->code[1]; i++) { os << localname(tf, i); if (i + 1 < tf->code[1]) os << ", "; } os << ") -- line " << tf->lineDefined << std::endl; decompile(os, tf, indent_str + std::string(4, ' '), upvals, num_upvals); os << indent_str << "end"; }
static const char *getobjname (Proto *p, int lastpc, int reg, const char **name) { int pc = findsetreg(p, lastpc, reg); if (pc != -1) { /* could find instruction? */ Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); switch (op) { case OP_GETTABUP: case OP_GETTABLE: { int k = GETARG_C(i); /* key index */ int t = GETARG_B(i); /* table index */ const char *vn = (op == OP_GETTABLE) /* name of indexed variable */ ? luaF_getlocalname(p, t + 1) : upvalname(p, t); kname(p, pc, k, name); return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field"; } case OP_GETLOCAL: { *name = localname(p, GETARG_B(i)); return "local"; } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); return "upvalue"; } case OP_LOADK: case OP_LOADKX: { int b = (op == OP_LOADK) ? GETARG_Bx(i) : GETARG_Ax(p->code[pc + 1]); if (ttisstring(&p->k[b])) { *name = svalue(&p->k[b]); return "constant"; } break; } case OP_SELF: { int k = GETARG_C(i); /* key index */ kname(p, pc, k, name); return "method"; } default: break; /* go through to return NULL */ } } return NULL; /* could not find reasonable name */ }
void Decompiler::decompileRange(Byte *start, Byte *end) { // First, scan for IFFUPJMP, which is used for repeat/until, so // we can recognize the start of such loops. We only keep the // last value to match each address, which represents the outermost // repeat/until loop starting at that point. std::map<Byte *, Byte *> rev_iffupjmp_map; for (Byte *scan = start; end == NULL || scan < end; scan += get_instr_len(*scan)) { if (*scan == IFFUPJMP) rev_iffupjmp_map[scan + 2 - scan[1]] = scan; else if (*scan == IFFUPJMPW) rev_iffupjmp_map[scan + 3 - (scan[1] | (scan[2] << 8))] = scan; else if (*scan == ENDCODE) break; } while (end == NULL || start < end) { int locs_here = local_var_defs->count(start); if (locs_here > 0) { // There were local variable slots just pushed onto the stack // Print them out (in the second pass) // First, if there are multiple defined, it must be from // local x, y, z = f() or local a, b. So just ignore the extra // entries. for (int i = 1; i < locs_here; i++) { delete stk->top(); stk->pop(); } Expression *def = stk->top(); stk->pop(); // Print the local variable names, and at the same time push // fake values onto the stack *os << indent_str << "local "; for (int i = 0; i < locs_here; i++) { std::string locname = localname(tf, tf->code[1] + stk->size()); *os << locname; if (i + 1 < locs_here) *os << ", "; stk->push(new VarExpr(start, "<" + locname + " stack slot>")); } // Print the definition, unless it's nil VarExpr *v = dynamic_cast<VarExpr *>(def); if (v == NULL || v->name != "nil") *os << " = " << *def; *os << std::endl; delete def; local_var_defs->erase(start); } if (rev_iffupjmp_map.find(start) != rev_iffupjmp_map.end()) { // aha, do a repeat/until loop *os << indent_str << "repeat\n"; Decompiler indented_dc = *this; indented_dc.indent_str += std::string(4, ' '); indented_dc.break_pos = rev_iffupjmp_map[start]; indented_dc.break_pos += get_instr_len(*indented_dc.break_pos); indented_dc.decompileRange(start, rev_iffupjmp_map[start]); Expression *e = stk->top(); stk->pop(); *os << indent_str << "until " << *e << std::endl; delete e; start = indented_dc.break_pos; continue; } Byte opc = *start++; int aux; switch (opc) { case ENDCODE: return; case PUSHNIL: aux = *start++; goto pushnil; case PUSHNIL0: aux = 0; pushnil: for (int i = 0; i <= aux; i++) stk->push(new VarExpr(start, "nil")); // Cheat a little :) break; case PUSHNUMBER: aux = *start++; goto pushnumber; case PUSHNUMBER0: case PUSHNUMBER1: case PUSHNUMBER2: aux = opc - PUSHNUMBER0; goto pushnumber; case PUSHNUMBERW: aux = start[0] | (start[1] << 8); start += 2; pushnumber: stk->push(new NumberExpr(start, aux)); break; case PUSHCONSTANT: aux = *start++; goto pushconst; case PUSHCONSTANT0: case PUSHCONSTANT1: case PUSHCONSTANT2: case PUSHCONSTANT3: case PUSHCONSTANT4: case PUSHCONSTANT5: case PUSHCONSTANT6: case PUSHCONSTANT7: aux = opc - PUSHCONSTANT0; goto pushconst; case PUSHCONSTANTW: aux = start[0] | (start[1] << 8); start += 2; pushconst: switch (ttype(tf->consts + aux)) { case LUA_T_STRING: stk->push(new StringExpr(start, tsvalue(tf->consts + aux))); break; case LUA_T_NUMBER: stk->push(new NumberExpr(start, nvalue(tf->consts + aux))); break; case LUA_T_PROTO: stk->push(new FuncExpr(start, tfvalue(tf->consts + aux), indent_str)); break; default: *os << indent_str << "error: invalid constant type " << int(ttype(tf->consts + aux)) << std::endl; } break; case PUSHUPVALUE: aux = *start++; goto pushupvalue; case PUSHUPVALUE0: case PUSHUPVALUE1: aux = opc - PUSHUPVALUE0; pushupvalue: { if (aux >= num_upvals) { *os << indent_str << "error: invalid upvalue #" << aux << std::endl; } std::ostringstream s; s << "%" << *upvals[aux]; stk->push(new VarExpr(start, s.str())); } break; case PUSHLOCAL: aux = *start++; goto pushlocal; case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2: case PUSHLOCAL3: case PUSHLOCAL4: case PUSHLOCAL5: case PUSHLOCAL6: case PUSHLOCAL7: aux = opc - PUSHLOCAL0; pushlocal: stk->push(new VarExpr(start, localname(tf, aux))); break; case GETGLOBAL: aux = *start++; goto getglobal; case GETGLOBAL0: case GETGLOBAL1: case GETGLOBAL2: case GETGLOBAL3: case GETGLOBAL4: case GETGLOBAL5: case GETGLOBAL6: case GETGLOBAL7: aux = opc - GETGLOBAL0; goto getglobal; case GETGLOBALW: aux = start[0] | (start[1] << 8); start += 2; getglobal: stk->push(new VarExpr(start, svalue(tf->consts + aux))); break; case GETTABLE: { Expression *index = stk->top(); stk->pop(); Expression *table = stk->top(); stk->pop(); stk->push(new BracketsIndexExpr(start, table, index)); } break; case GETDOTTED: aux = *start++; goto getdotted; case GETDOTTED0: case GETDOTTED1: case GETDOTTED2: case GETDOTTED3: case GETDOTTED4: case GETDOTTED5: case GETDOTTED6: case GETDOTTED7: aux = opc - GETDOTTED0; goto getdotted; case GETDOTTEDW: aux = start[0] | (start[1] << 8); start += 2; getdotted: { Expression *tbl = stk->top(); stk->pop(); stk->push(new DotIndexExpr(start, tbl, new StringExpr (start, tsvalue(tf->consts + aux)))); } break; case PUSHSELF: aux = *start++; goto pushself; case PUSHSELF0: case PUSHSELF1: case PUSHSELF2: case PUSHSELF3: case PUSHSELF4: case PUSHSELF5: case PUSHSELF6: case PUSHSELF7: aux = opc - PUSHSELF0; goto pushself; case PUSHSELFW: aux = start[0] | (start[1] << 8); start += 2; pushself: { Expression *tbl = stk->top(); stk->pop(); stk->push(new SelfExpr(start, tbl, new StringExpr (start, tsvalue(tf->consts + aux)))); stk->push(new VarExpr(start, "<self>")); // Fake value, FuncCallExpr will handle it } break; case CREATEARRAY: start++; goto createarray; case CREATEARRAY0: case CREATEARRAY1: goto createarray; case CREATEARRAYW: start += 2; createarray: stk->push(new ArrayExpr(start)); break; case SETLOCAL: case SETLOCAL0: case SETLOCAL1: case SETLOCAL2: case SETLOCAL3: case SETLOCAL4: case SETLOCAL5: case SETLOCAL6: case SETLOCAL7: case SETGLOBAL: case SETGLOBAL0: case SETGLOBAL1: case SETGLOBAL2: case SETGLOBAL3: case SETGLOBAL4: case SETGLOBAL5: case SETGLOBAL6: case SETGLOBAL7: case SETGLOBALW: case SETTABLE0: case SETTABLE: start--; do_multi_assign(start); break; case SETLIST: start++; // assume offset is correct goto setlist; case SETLISTW: start += 2; case SETLIST0: setlist: aux = *start++; { ArrayExpr::mapping_list new_mappings; for (int i = 0; i < aux; i++) { Expression *val = stk->top(); stk->pop(); new_mappings.push_front(std::make_pair((Expression *) NULL, val)); } ArrayExpr *a = dynamic_cast<ArrayExpr *>(stk->top()); if (a == NULL) { *os << indent_str << "error: attempt to setlist a non-array object\n"; } // Append the new list a->mappings.splice(a->mappings.end(), new_mappings); a->pos = start; } break; case SETMAP: aux = *start++; goto setmap; case SETMAP0: aux = 0; setmap: { ArrayExpr::mapping_list new_mappings; for (int i = 0; i <= aux; i++) { Expression *val = stk->top(); stk->pop(); Expression *key = stk->top(); stk->pop(); new_mappings.push_front(std::make_pair(key, val)); } ArrayExpr *a = dynamic_cast<ArrayExpr *>(stk->top()); if (a == NULL) { *os << indent_str << "error: attempt to setmap a non-array object\n"; } // Append the new list a->mappings.splice(a->mappings.end(), new_mappings); a->pos = start; } break; case EQOP: do_binary_op(start, 1, false, " == "); break; case NEQOP: do_binary_op(start, 1, false, " ~= "); break; case LTOP: do_binary_op(start, 1, false, " < "); break; case LEOP: do_binary_op(start, 1, false, " <= "); break; case GTOP: do_binary_op(start, 1, false, " > "); break; case GEOP: do_binary_op(start, 1, false, " >= "); break; case ADDOP: do_binary_op(start, 3, false, " + "); break; case SUBOP: do_binary_op(start, 3, false, " - "); break; case MULTOP: do_binary_op(start, 4, false, " * "); break; case DIVOP: do_binary_op(start, 4, false, " / "); break; case POWOP: do_binary_op(start, 6, true, " ^ "); break; case CONCOP: do_binary_op(start, 2, false, " .. "); break; case MINUSOP: do_unary_op(start, 5, "-"); break; case NOTOP: do_unary_op(start, 5, "not "); break; case ONTJMP: aux = *start++; goto ontjmp; case ONTJMPW: aux = start[0] | (start[1] << 8); start += 2; ontjmp: // push_expr_1 ontjmp(label) push_expr_2 label: -> expr_1 || expr_2 decompileRange(start, start + aux); do_binary_op(start + aux, 0, false, " or "); start = start + aux; break; case ONFJMP: aux = *start++; goto onfjmp; case ONFJMPW: aux = start[0] | (start[1] << 8); start += 2; onfjmp: // push_expr_1 onfjmp(label) push_expr_2 label: -> expr_2 && expr_2 decompileRange(start, start + aux); do_binary_op(start + aux, 0, false, " and "); start = start + aux; break; case JMP: aux = *start++; goto jmp; case JMPW: aux = start[0] | (start[1] << 8); start += 2; jmp: { Byte *dest = start + aux; if (dest == break_pos) { *os << indent_str << "break\n"; break; } // otherwise, must be the start of a while statement Byte *while_cond_end; for (while_cond_end = dest; end == NULL || while_cond_end < end; while_cond_end += get_instr_len(*while_cond_end)) if (*while_cond_end == IFTUPJMP || *while_cond_end == IFTUPJMPW) break; if (end != NULL && while_cond_end >= end) { *os << indent_str << "error: JMP not in break, while, if/else\n"; } // push the while condition onto the stack decompileRange(dest, while_cond_end); *os << indent_str << "while " << *stk->top() << " do\n"; delete stk->top(); stk->pop(); // decompile the while body Decompiler indented_dc = *this; indented_dc.indent_str += std::string(4, ' '); indented_dc.break_pos = while_cond_end + get_instr_len(*while_cond_end); indented_dc.decompileRange(start, dest); *os << indent_str << "end\n"; start = indented_dc.break_pos; } break; case IFFJMP: aux = *start++; goto iffjmp; case IFFJMPW: aux = start[0] | (start[1] << 8); start += 2; iffjmp: { // Output an if/end, if/else/end, if/elseif/else/end, ... statement Byte *if_part_end = start + aux; Decompiler indented_dc = *this; indented_dc.indent_str += std::string(4, ' '); *os << indent_str << "if " << *stk->top(); delete stk->top(); stk->pop(); *os << " then\n"; bool has_else; Byte *else_part_end; get_else_part(start, if_part_end, has_else, else_part_end); // Output the if part output_if: indented_dc.decompileRange(start, if_part_end); start = start + aux; if (has_else) { // Check whether the entire else part is a single // if or if/else statement Byte *instr_scan = start; while (is_expr_opc(*instr_scan) && (end == NULL || instr_scan < else_part_end)) instr_scan += get_instr_len(*instr_scan); if ((end == NULL || instr_scan < else_part_end) && (*instr_scan == IFFJMP || *instr_scan == IFFJMPW)) { // OK, first line will be if, check if it will go all // the way through Byte *new_start, *new_if_part_end, *new_else_part_end; bool new_has_else; if (*instr_scan == IFFJMP) { aux = instr_scan[1]; new_start = instr_scan + 2; } else { aux = instr_scan[1] | (instr_scan[2] << 8); new_start = instr_scan + 3; } new_if_part_end = new_start + aux; get_else_part(new_start, new_if_part_end, new_has_else, new_else_part_end); if (new_if_part_end == else_part_end || (new_has_else && new_else_part_end == else_part_end)) { // Yes, output an elseif decompileRange(start, instr_scan); // push condition *os << indent_str << "elseif " << *stk->top() << " then\n"; delete stk->top(); stk->pop(); start = new_start; if_part_end = new_if_part_end; has_else = new_has_else; else_part_end = new_else_part_end; goto output_if; } } *os << indent_str << "else\n"; indented_dc.decompileRange(start, else_part_end); start = else_part_end; } *os << indent_str << "end\n"; } break; case CLOSURE: aux = *start++; goto closure; case CLOSURE0: case CLOSURE1: aux = opc - CLOSURE0; closure: { FuncExpr *f = dynamic_cast<FuncExpr *>(stk->top()); if (f == NULL) { *os << indent_str << "error: closure requires a function\n"; } stk->pop(); f->num_upvals = aux; f->upvals = new Expression*[aux]; for (int i = aux - 1; i >= 0; i--) { f->upvals[i] = stk->top(); stk->pop(); } stk->push(f); } break; case CALLFUNC: aux = *start++; goto callfunc; case CALLFUNC0: case CALLFUNC1: aux = opc - CALLFUNC0; callfunc: { int num_args = *start++; FuncCallExpr *e = new FuncCallExpr(start); e->num_args = num_args; e->args = new Expression*[num_args]; for (int i = num_args - 1; i >= 0; i--) { e->args[i] = stk->top(); stk->pop(); } e->func = stk->top(); stk->pop(); if (aux == 0) { *os << indent_str << *e << std::endl; delete e; } else if (aux == 1 || aux == 255) // 255 for return f() stk->push(e); else { stk->push(e); for (int i = 1; i < aux; i++) stk->push(new VarExpr(start, "<extra result>")); } } break; case RETCODE: { int num_rets = stk->size() + tf->code[1] - *start++; ExprStack rets; for (int i = 0; i < num_rets; i++) { rets.push(stk->top()); stk->pop(); } *os << indent_str << "return"; for (int i = 0; i < num_rets; i++) { *os << " " << *rets.top(); delete rets.top(); rets.pop(); if (i + 1 < num_rets) *os << ","; } *os << std::endl; } break; case SETLINE: aux = *start++; goto setline; case SETLINEW: aux = start[0] | (start[1] << 8); start += 2; setline: break; // ignore line info case POP: aux = *start++; goto pop; case POP0: case POP1: aux = opc - POP0; pop: for (int i = 0; i <= aux; i++) { local_var_defs->insert(stk->top()->pos); delete stk->top(); stk->pop(); } break; //Nop default: break; } } }
// Scan for a series of assignments void Decompiler::do_multi_assign(Byte *&start) { std::queue<Expression *> results; ExprStack values; bool done; int num_tables = 0; do { int aux, opc; done = false; opc = *start++; switch (opc) { case SETLOCAL: aux = *start++; goto setlocal; case SETLOCAL0: case SETLOCAL1: case SETLOCAL2: case SETLOCAL3: case SETLOCAL4: case SETLOCAL5: case SETLOCAL6: case SETLOCAL7: aux = opc - SETLOCAL0; setlocal: results.push(new VarExpr(start, localname(tf, aux))); break; case SETGLOBAL: aux = *start++; goto setglobal; case SETGLOBAL0: case SETGLOBAL1: case SETGLOBAL2: case SETGLOBAL3: case SETGLOBAL4: case SETGLOBAL5: case SETGLOBAL6: case SETGLOBAL7: aux = opc - SETGLOBAL0; goto setglobal; case SETGLOBALW: aux = start[0] | (start[1] << 8); start += 2; setglobal: results.push(new VarExpr(start, svalue(tf->consts + aux))); break; case SETTABLE: start++; // assume offset is correct num_tables++; // this needs stuff from farther up the stack, wait until // it's available case SETTABLE0: results.push(new IndexExpr(start, NULL, NULL)); break; default: start--; done = true; } if (! done) { Expression *e = stk->top(); // Check for fake result from function calls with multiple return values VarExpr *v = dynamic_cast<VarExpr *>(e); if (v != NULL && v->name == "<extra result>") delete e; else values.push(e); stk->pop(); } } while (! done); // Check for popping tables and indices if (num_tables > 0 && (*start == POP || *start == POP0 || *start == POP1)) { start++; if (start[-1] == POP) start++; } // Now get actual tables and indices from the stack, reversing // the list to the right order at the same time ExprStack results2; while (! results.empty()) { Expression *var = results.front(); results.pop(); IndexExpr *tbl = dynamic_cast<IndexExpr *>(var); if (tbl != NULL) { tbl->index = stk->top(); stk->pop(); tbl->table = stk->top(); stk->pop(); } results2.push(var); } *os << indent_str; while (! results2.empty()) { Expression *var = results2.top(); results2.pop(); *os << *var; delete var; if (! results2.empty()) *os << ", "; } *os << " = "; while (! values.empty()) { Expression *val = values.top(); values.pop(); *os << *val; delete val; if (! values.empty()) *os << ", "; } *os << std::endl; }
zorba::diagnostic::kind ZorbaWarningQName::kind() const { return get_standard_error_kind( localname() ); }
zorba::diagnostic::kind JSONiqErrorQName::kind() const { return get_standard_error_kind( localname() ); }