package org.python.core; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class PyBytecode extends PyBaseCode { // for debugging private int count = 0; // total number of opcodes run so far in this code obj private int maxCount = -1; // if -1, no cap on number of opcodes than can be run public static boolean defaultDebug = false; private static PyObject dis; private static synchronized PyObject get_dis() { if (dis == null) { dis = __builtin__.__import__("dis"); } return dis; } private static PyObject opname; private static synchronized PyObject get_opname() { if (opname == null) { opname = get_dis().__getattr__("opname"); } return opname; } private boolean debug; public static void _allDebug(boolean setting) { defaultDebug = setting; } public PyObject _debug(int maxCount) { debug = maxCount > 0; this.maxCount = maxCount; return Py.None; } // end debugging public final static int CO_MAXBLOCKS = 20; // same as in CPython public final byte[] co_code; // widened to char to avoid signed byte issues public final PyObject[] co_consts; public final String[] co_names; public final int co_stacksize; // XXX - use to convert PyStack to use PyObject[] instead of ArrayList<PyObject> public final byte[] co_lnotab; private final static int CALL_FLAG_VAR = 1; private final static int CALL_FLAG_KW = 2; // follows new.code's interface public PyBytecode(int argcount, int nlocals, int stacksize, int flags, String codestring, PyObject[] constants, String[] names, String varnames[], String filename, String name, int firstlineno, String lnotab) { this(argcount, nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab, null, null); } // XXX - intern names HERE instead of in marshal public PyBytecode(int argcount, int nlocals, int stacksize, int flags, String codestring, PyObject[] constants, String[] names, String varnames[], String filename, String name, int firstlineno, String lnotab, String[] cellvars, String[] freevars) { debug = defaultDebug; co_argcount = nargs = argcount; co_varnames = varnames; co_nlocals = nlocals; // maybe assert = varnames.length; co_filename = filename; co_firstlineno = firstlineno; co_cellvars = cellvars; co_freevars = freevars; co_name = name; co_flags = new CompilerFlags(flags); varargs = co_flags.isFlagSet(CodeFlag.CO_VARARGS); varkwargs = co_flags.isFlagSet(CodeFlag.CO_VARKEYWORDS); co_stacksize = stacksize; co_consts = constants; co_names = names; co_code = getBytes(codestring); co_lnotab = getBytes(lnotab); } private static final String[] __members__ = { "co_name", "co_argcount", "co_varnames", "co_filename", "co_firstlineno", "co_flags", "co_cellvars", "co_freevars", "co_nlocals", "co_code", "co_consts", "co_names", "co_lnotab", "co_stacksize" }; @Override public PyObject __dir__() { PyString members[] = new PyString[__members__.length]; for (int i = 0; i < __members__.length; i++) { members[i] = new PyString(__members__[i]); } return new PyList(members); } private void throwReadonly(String name) { for (int i = 0; i < __members__.length; i++) { if (__members__[i] == name) { throw Py.TypeError("readonly attribute"); } } throw Py.AttributeError(name); } public void __setattr__(String name, PyObject value) { // no writable attributes throwReadonly(name); } public void __delattr__(String name) { throwReadonly(name); } private static PyTuple toPyStringTuple(String[] ar) { if (ar == null) { return Py.EmptyTuple; } int sz = ar.length; PyString[] pystr = new PyString[sz]; for (int i = 0; i < sz; i++) { pystr[i] = new PyString(ar[i]); } return new PyTuple(pystr); } public PyObject __findattr_ex__(String name) { // have to craft co_varnames specially if (name == "co_varnames") { return toPyStringTuple(co_varnames); } if (name == "co_cellvars") { return toPyStringTuple(co_cellvars); } if (name == "co_freevars") { return toPyStringTuple(co_freevars); } if (name == "co_filename") { return new PyString(co_filename); } if (name == "co_name") { return new PyString(co_name); } if (name == "co_code") { return new PyString(getString(co_code)); } if (name == "co_lnotab") { return new PyString(getString(co_lnotab)); } if (name == "co_consts") { return new PyTuple(co_consts); } if (name == "co_flags") { return Py.newInteger(co_flags.toBits()); } return super.__findattr_ex__(name); } enum Why { NOT, /* No error */ EXCEPTION, /* Exception occurred */ RERAISE, /* Exception re-raised by 'finally' */ RETURN, /* 'return' statement */ BREAK, /* 'break' statement */ CONTINUE, /* 'continue' statement */ YIELD /* 'yield' operator */ }; // to enable why's to be stored on a PyStack private static class PyStackWhy extends PyObject { Why why; PyStackWhy(Why why) { this.why = why; } @Override public String toString() { return why.toString(); } } private static class PyStackException extends PyObject { PyException exception; PyStackException(PyException exception) { this.exception = exception; } @Override public String toString() { return String.format("PyStackException<%s,%s,%.100s>", exception.type, exception.value, exception.traceback); } } private static String stringify_blocks(PyFrame f) { if (f.f_exits == null || f.f_lineno == 0) { return "[]"; } StringBuilder buf = new StringBuilder("["); int len = f.f_lineno; for (int i = 0; i < len; i++) { buf.append(f.f_exits[i].toString()); if (i < len - 1) { buf.append(", "); } } buf.append("]"); return buf.toString(); } private void print_debug(int count, int next_instr, int line, int opcode, int oparg, PyStack stack, PyFrame f) { if (debug) { System.err.println(co_name + " " + line + ":" + count + "," + f.f_lasti + "> " + get_opname().__getitem__(Py.newInteger(opcode)) + (opcode >= Opcode.HAVE_ARGUMENT ? " " + oparg : "") + ", stack: " + stack.toString() + ", blocks: " + stringify_blocks(f)); } } // the following code exploits the fact that f_exits is only used by code compiled to Java bytecode; // in their place we implement the block stack for PBC-VM, as mapped below in the comments of pushBlock private static PyTryBlock popBlock(PyFrame f) { return (PyTryBlock)(((PyList)f.f_exits[0]).pop()); } private static void pushBlock(PyFrame f, PyTryBlock block) { if (f.f_exits == null) { // allocate in the frame where they can fit! TODO consider supporting directly in the frame f.f_exits = new PyObject[1]; // f_blockstack in CPython - a simple ArrayList might be best f.f_exits[0] = new PyList(); } ((PyList)f.f_exits[0]).append(block); } private boolean blocksLeft(PyFrame f) { if (f.f_exits != null) { return ((PyList)f.f_exits[0]).__nonzero__(); } else { return false; } } @Override protected PyObject interpret(PyFrame f, ThreadState ts) { final PyStack stack = new PyStack(); int next_instr = -1; int opcode; /* Current opcode */ int oparg = 0; /* Current opcode argument, if any */ Why why = Why.NOT; PyObject retval = null; LineCache lineCache = null; int last_line = -1; int line = 0; // XXX - optimization opportunities // 1. consider detaching the setting/getting of frame fields to improve performance, instead do this // in a shadow version of the frame that we copy back to on entry/exit and downcalls if (debug) { System.err.println(co_name + ":" + f.f_lasti + "/" + co_code.length + ", cells:" + Arrays.toString(co_cellvars) + ", free:" + Arrays.toString(co_freevars)); int i = 0; for (String cellvar : co_cellvars) { System.err.println(cellvar + " = " + f.f_env[i++]); } for (String freevar : co_freevars) { System.err.println(freevar + " = " + f.f_env[i++]); } get_dis().invoke("disassemble", this); } if (f.f_lasti >= co_code.length) { throw Py.SystemError(""); // XXX - chose an appropriate error!!! } next_instr = f.f_lasti; // the restore stack aspects should occur ONLY after a yield boolean checkGeneratorInput = false; if (f.f_savedlocals != null) { for (int i = 0; i < f.f_savedlocals.length; i++) { PyObject v = (PyObject) (f.f_savedlocals[i]); stack.push(v); } checkGeneratorInput = true; f.f_savedlocals = null; } while (!debug || (maxCount == -1 || count < maxCount)) { // XXX - replace with while(true) if (f.tracefunc != null || debug) { if (lineCache == null) { lineCache = new LineCache(); if (debug) { System.err.println("LineCache: " + lineCache.toString()); } } line = lineCache.getline(next_instr); // XXX - should also return the range this is valid to avoid an unnecessary bisect if (line != last_line) { f.setline(line); } } try { if (checkGeneratorInput) { checkGeneratorInput = false; Object generatorInput = f.getGeneratorInput(); if (generatorInput instanceof PyException) { throw (PyException) generatorInput; } stack.push((PyObject) generatorInput); } opcode = getUnsigned(co_code, next_instr); if (opcode >= Opcode.HAVE_ARGUMENT) { next_instr += 2; oparg = (getUnsigned(co_code, next_instr) << 8) + getUnsigned(co_code, next_instr - 1); } print_debug(count, next_instr, line, opcode, oparg, stack, f); count += 1; next_instr += 1; f.f_lasti = next_instr; switch (opcode) { case Opcode.NOP: break; case Opcode.LOAD_FAST: stack.push(f.getlocal(oparg)); break; case Opcode.LOAD_CONST: stack.push(co_consts[oparg]); break; case Opcode.STORE_FAST: f.setlocal(oparg, stack.pop()); break; case Opcode.POP_TOP: stack.pop(); break; case Opcode.ROT_TWO: stack.rot2(); break; case Opcode.ROT_THREE: stack.rot3(); break; case Opcode.ROT_FOUR: stack.rot4(); break; case Opcode.DUP_TOP: stack.dup(); break; case Opcode.DUP_TOPX: { if (oparg == 2 || oparg == 3) { stack.dup(oparg); } else { throw Py.RuntimeError("invalid argument to DUP_TOPX" + " (bytecode corruption?)"); } break; } case Opcode.UNARY_POSITIVE: stack.push(stack.pop().__pos__()); break; case Opcode.UNARY_NEGATIVE: stack.push(stack.pop().__neg__()); break; case Opcode.UNARY_NOT: stack.push(stack.pop().__not__()); break; case Opcode.UNARY_CONVERT: stack.push(stack.pop().__repr__()); break; case Opcode.UNARY_INVERT: stack.push(stack.pop().__invert__()); break; case Opcode.BINARY_POWER: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._pow(b)); break; } case Opcode.BINARY_MULTIPLY: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._mul(b)); break; } case Opcode.BINARY_DIVIDE: { PyObject b = stack.pop(); PyObject a = stack.pop(); if (!co_flags.isFlagSet(CodeFlag.CO_FUTURE_DIVISION)) { stack.push(a._div(b)); } else { stack.push(a._truediv(b)); } break; } case Opcode.BINARY_TRUE_DIVIDE: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._truediv(b)); break; } case Opcode.BINARY_FLOOR_DIVIDE: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._floordiv(b)); break; } case Opcode.BINARY_MODULO: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._mod(b)); break; } case Opcode.BINARY_ADD: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._add(b)); break; } case Opcode.BINARY_SUBTRACT: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._sub(b)); break; } case Opcode.BINARY_SUBSCR: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a.__getitem__(b)); break; } case Opcode.BINARY_LSHIFT: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._lshift(b)); break; } case Opcode.BINARY_RSHIFT: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._rshift(b)); break; } case Opcode.BINARY_AND: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._and(b)); break; } case Opcode.BINARY_XOR: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._xor(b)); break; } case Opcode.BINARY_OR: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._or(b)); break; } case Opcode.LIST_APPEND: { PyObject b = stack.pop(); PyList a = (PyList) (stack.pop()); a.append(b); break; } case Opcode.INPLACE_POWER: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._ipow(b)); break; } case Opcode.INPLACE_MULTIPLY: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._imul(b)); break; } case Opcode.INPLACE_DIVIDE: { PyObject b = stack.pop(); PyObject a = stack.pop(); if (!co_flags.isFlagSet(CodeFlag.CO_FUTURE_DIVISION)) { stack.push(a._idiv(b)); } else { stack.push(a._itruediv(b)); } break; } case Opcode.INPLACE_TRUE_DIVIDE: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._itruediv(b)); break; } case Opcode.INPLACE_FLOOR_DIVIDE: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._ifloordiv(b)); break; } case Opcode.INPLACE_MODULO: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._imod(b)); break; } case Opcode.INPLACE_ADD: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._iadd(b)); break; } case Opcode.INPLACE_SUBTRACT: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._isub(b)); break; } case Opcode.INPLACE_LSHIFT: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._ilshift(b)); break; } case Opcode.INPLACE_RSHIFT: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._irshift(b)); break; } case Opcode.INPLACE_AND: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._iand(b)); break; } case Opcode.INPLACE_XOR: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._ixor(b)); break; } case Opcode.INPLACE_OR: { PyObject b = stack.pop(); PyObject a = stack.pop(); stack.push(a._ior(b)); break; } case Opcode.SLICE + 0: case Opcode.SLICE + 1: case Opcode.SLICE + 2: case Opcode.SLICE + 3: { PyObject stop = (((opcode - Opcode.SLICE) & 2) != 0) ? stack.pop() : null; PyObject start = (((opcode - Opcode.SLICE) & 1) != 0) ? stack.pop() : null; PyObject obj = stack.pop(); stack.push(obj.__getslice__(start, stop)); break; } case Opcode.STORE_SLICE + 0: case Opcode.STORE_SLICE + 1: case Opcode.STORE_SLICE + 2: case Opcode.STORE_SLICE + 3: { PyObject stop = (((opcode - Opcode.STORE_SLICE) & 2) != 0) ? stack.pop() : null; PyObject start = (((opcode - Opcode.STORE_SLICE) & 1) != 0) ? stack.pop() : null; PyObject obj = stack.pop(); PyObject value = stack.pop(); obj.__setslice__(start, stop, value); break; } case Opcode.DELETE_SLICE + 0: case Opcode.DELETE_SLICE + 1: case Opcode.DELETE_SLICE + 2: case Opcode.DELETE_SLICE + 3: { PyObject stop = (((opcode - Opcode.DELETE_SLICE) & 2) != 0) ? stack.pop() : null; PyObject start = (((opcode - Opcode.DELETE_SLICE) & 1) != 0) ? stack.pop() : null; PyObject obj = stack.pop(); obj.__delslice__(start, stop); break; } case Opcode.STORE_SUBSCR: { PyObject key = stack.pop(); PyObject obj = stack.pop(); PyObject value = stack.pop(); obj.__setitem__(key, value); break; } case Opcode.DELETE_SUBSCR: { PyObject key = stack.pop(); PyObject obj = stack.pop(); obj.__delitem__(key); break; } case Opcode.PRINT_EXPR: PySystemState.displayhook(stack.pop()); break; case Opcode.PRINT_ITEM_TO: Py.printComma(stack.pop(), stack.pop()); break; case Opcode.PRINT_ITEM: Py.printComma(stack.pop()); break; case Opcode.PRINT_NEWLINE_TO: Py.printlnv(stack.pop()); break; case Opcode.PRINT_NEWLINE: Py.println(); break; case Opcode.RAISE_VARARGS: switch (oparg) { case 3: { PyTraceback tb = (PyTraceback) (stack.pop()); PyObject value = stack.pop(); PyObject type = stack.pop(); throw PyException.doRaise(type, value, tb); } case 2: { PyObject value = stack.pop(); PyObject type = stack.pop(); throw PyException.doRaise(type, value, null); } case 1: { PyObject type = stack.pop(); throw PyException.doRaise(type, null, null); } case 0: throw PyException.doRaise(null, null, null); default: throw Py.SystemError("bad RAISE_VARARGS oparg"); } case Opcode.LOAD_LOCALS: stack.push(f.f_locals); break; case Opcode.RETURN_VALUE: retval = stack.pop(); why = Why.RETURN; break; case Opcode.YIELD_VALUE: retval = stack.pop(); why = Why.YIELD; break; case Opcode.EXEC_STMT: { PyObject locals = stack.pop(); PyObject globals = stack.pop(); PyObject code = stack.pop(); Py.exec(code, globals == Py.None ? null : globals, locals == Py.None ? null : locals); break; } case Opcode.POP_BLOCK: { PyTryBlock b = popBlock(f); while (stack.size() > b.b_level) { stack.pop(); } break; } case Opcode.END_FINALLY: { PyObject v = stack.pop(); if (v instanceof PyStackWhy) { why = ((PyStackWhy) v).why; assert (why != Why.YIELD); if (why == Why.RETURN || why == Why.CONTINUE) { retval = stack.pop(); } } else if (v instanceof PyStackException) { ts.exception = ((PyStackException) v).exception; why = Why.RERAISE; } else if (v instanceof PyString) { why = Why.RERAISE; } else if (v != Py.None) { throw Py.SystemError("'finally' pops bad exception"); } break; } case Opcode.BUILD_CLASS: { PyObject methods = stack.pop(); PyObject bases[] = ((PySequenceList) (stack.pop())).getArray(); String name = stack.pop().toString(); stack.push(Py.makeClass(name, bases, methods)); break; } case Opcode.STORE_NAME: f.setlocal(co_names[oparg], stack.pop()); break; case Opcode.DELETE_NAME: f.dellocal(co_names[oparg]); break; case Opcode.UNPACK_SEQUENCE: unpack_iterable(oparg, stack); break; case Opcode.STORE_ATTR: { PyObject obj = stack.pop(); PyObject v = stack.pop(); obj.__setattr__(co_names[oparg], v); break; } case Opcode.DELETE_ATTR: stack.pop().__delattr__(co_names[oparg]); break; case Opcode.STORE_GLOBAL: f.setglobal(co_names[oparg], stack.pop()); break; case Opcode.DELETE_GLOBAL: f.delglobal(co_names[oparg]); break; case Opcode.LOAD_NAME: stack.push(f.getname(co_names[oparg])); break; case Opcode.LOAD_GLOBAL: stack.push(f.getglobal(co_names[oparg])); break; case Opcode.DELETE_FAST: f.dellocal(oparg); break; case Opcode.LOAD_CLOSURE: { PyCell cell = (PyCell) (f.getclosure(oparg)); if (cell.ob_ref == null) { String name; if (oparg >= co_cellvars.length) { name = co_freevars[oparg - co_cellvars.length]; } else { name = co_cellvars[oparg]; } // XXX - consider some efficient lookup mechanism, like a hash :), // at least if co_varnames is much greater than say a certain // size (but i would think, it's not going to happen in real code. profile?) if (f.f_fastlocals != null) { int i = 0; boolean matched = false; for (String match : co_varnames) { if (match.equals(name)) { matched = true; break; } i++; } if (matched) { cell.ob_ref = f.f_fastlocals[i]; } } else { cell.ob_ref = f.f_locals.__finditem__(name); } } stack.push(cell); break; } case Opcode.LOAD_DEREF: { // common code from LOAD_CLOSURE PyCell cell = (PyCell) (f.getclosure(oparg)); if (cell.ob_ref == null) { String name; if (oparg >= co_cellvars.length) { name = co_freevars[oparg - co_cellvars.length]; } else { name = co_cellvars[oparg]; } // XXX - consider some efficient lookup mechanism, like a hash :), // at least if co_varnames is much greater than say a certain // size (but i would think, it's not going to happen in real code. profile?) if (f.f_fastlocals != null) { int i = 0; boolean matched = false; for (String match : co_varnames) { if (match.equals(name)) { matched = true; break; } i++; } if (matched) { cell.ob_ref = f.f_fastlocals[i]; } } else { cell.ob_ref = f.f_locals.__finditem__(name); } } stack.push(cell.ob_ref); break; } case Opcode.STORE_DEREF: f.setderef(oparg, stack.pop()); break; case Opcode.BUILD_TUPLE: stack.push(new PyTuple(stack.popN(oparg))); break; case Opcode.BUILD_LIST: stack.push(new PyList(stack.popN(oparg))); break; case Opcode.BUILD_MAP: stack.push(new PyDictionary()); break; case Opcode.LOAD_ATTR: { String name = co_names[oparg]; stack.push(stack.pop().__getattr__(name)); break; } case Opcode.COMPARE_OP: { PyObject b = stack.pop(); PyObject a = stack.pop(); switch (oparg) { case Opcode.PyCmp_LT: stack.push(a._lt(b)); break; case Opcode.PyCmp_LE: stack.push(a._le(b)); break; case Opcode.PyCmp_EQ: stack.push(a._eq(b)); break; case Opcode.PyCmp_NE: stack.push(a._ne(b)); break; case Opcode.PyCmp_GT: stack.push(a._gt(b)); break; case Opcode.PyCmp_GE: stack.push(a._ge(b)); break; case Opcode.PyCmp_IN: stack.push(a._in(b)); break; case Opcode.PyCmp_NOT_IN: stack.push(a._notin(b)); break; case Opcode.PyCmp_IS: stack.push(a._is(b)); break; case Opcode.PyCmp_IS_NOT: stack.push(a._isnot(b)); break; case Opcode.PyCmp_EXC_MATCH: if (a instanceof PyStackException) { PyException pye = ((PyStackException) a).exception; stack.push(Py.newBoolean(pye.match(b))); } else { stack.push(Py.newBoolean(new PyException(a).match(b))); } break; } break; } case Opcode.IMPORT_NAME: { PyObject __import__ = f.f_builtins.__finditem__("__import__"); if (__import__ == null) { throw Py.ImportError("__import__ not found"); } PyString name = Py.newString(co_names[oparg]); PyObject fromlist = stack.pop(); PyObject level = stack.pop(); if (level.asInt() != -1) { stack.push(__import__.__call__(new PyObject[]{name, f.f_globals, f.f_locals, fromlist, level})); } else { stack.push(__import__.__call__(new PyObject[]{name, f.f_globals, f.f_locals, fromlist})); } break; } case Opcode.IMPORT_STAR: { PyObject module = stack.pop(); imp.importAll(module, f); break; } case Opcode.IMPORT_FROM: String name = co_names[oparg]; try { stack.push(stack.top().__getattr__(name)); } catch (PyException pye) { if (pye.match(Py.AttributeError)) { throw Py.ImportError(String.format("cannot import name %.230s", name)); } else { throw pye; } } break; case Opcode.JUMP_FORWARD: next_instr += oparg; break; case Opcode.JUMP_IF_FALSE: if (!stack.top().__nonzero__()) { next_instr += oparg; } break; case Opcode.JUMP_IF_TRUE: if (stack.top().__nonzero__()) { next_instr += oparg; } break; case Opcode.JUMP_ABSOLUTE: next_instr = oparg; break; case Opcode.GET_ITER: { PyObject it = stack.top().__iter__(); if (it != null) { stack.set_top(it); } break; } case Opcode.FOR_ITER: { PyObject it = stack.pop(); try { PyObject x = it.__iternext__(); if (x != null) { stack.push(it); stack.push(x); break; } } catch (PyException pye) { if (!pye.match(Py.StopIteration)) { throw pye; } } next_instr += oparg; break; } case Opcode.BREAK_LOOP: why = Why.BREAK; break; case Opcode.CONTINUE_LOOP: retval = Py.newInteger(oparg); if (retval.__nonzero__()) { why = Why.CONTINUE; } break; case Opcode.SETUP_LOOP: case Opcode.SETUP_EXCEPT: case Opcode.SETUP_FINALLY: pushBlock(f, new PyTryBlock(opcode, next_instr + oparg, stack.size())); break; case Opcode.WITH_CLEANUP: { /* TOP is the context.__exit__ bound method. Below that are 1-3 values indicating how/why we entered the finally clause: - SECOND = None - (SECOND, THIRD) = (WHY_{RETURN,CONTINUE}), retval - SECOND = WHY_*; no retval below it - (SECOND, THIRD, FOURTH) = exc_info() In the last case, we must call TOP(SECOND, THIRD, FOURTH) otherwise we must call TOP(None, None, None) In addition, if the stack represents an exception, *and* the function call returns a 'true' value, we "zap" this information, to prevent END_FINALLY from re-raising the exception. (But non-local gotos should still be resumed.) */ PyObject exit = stack.top(); PyObject u = stack.top(2); PyObject v; PyObject w; if (u == Py.None || u instanceof PyStackWhy) { u = v = w = Py.None; } else { v = stack.top(3); w = stack.top(4); } PyObject x = null; if (u instanceof PyStackException) { PyException exc = ((PyStackException) u).exception; x = exit.__call__(exc.type, exc.value, exc.traceback); } else { x = exit.__call__(u, v, w); } if (u != Py.None && x != null && x.__nonzero__()) { stack.popN(4); // XXX - consider stack.stackadj op stack.push(Py.None); } else { stack.pop(); // this should be popping off a block } break; } case Opcode.CALL_FUNCTION: { int na = oparg & 0xff; int nk = (oparg >> 8) & 0xff; if (nk == 0) { call_function(na, stack); } else { call_function(na, nk, stack); } break; } case Opcode.CALL_FUNCTION_VAR: case Opcode.CALL_FUNCTION_KW: case Opcode.CALL_FUNCTION_VAR_KW: { int na = oparg & 0xff; int nk = (oparg >> 8) & 0xff; int flags = (opcode - Opcode.CALL_FUNCTION) & 3; call_function(na, nk, (flags & CALL_FLAG_VAR) != 0, (flags & CALL_FLAG_KW) != 0, stack); break; } case Opcode.MAKE_FUNCTION: { PyCode code = (PyCode) stack.pop(); PyObject[] defaults = stack.popN(oparg); PyObject doc = null; if (code instanceof PyBytecode && ((PyBytecode) code).co_consts.length > 0) { doc = ((PyBytecode) code).co_consts[0]; } PyFunction func = new PyFunction(f.f_globals, defaults, code, doc); stack.push(func); break; } case Opcode.MAKE_CLOSURE: { PyCode code = (PyCode) stack.pop(); PyObject[] closure_cells = ((PySequenceList) (stack.pop())).getArray(); PyObject[] defaults = stack.popN(oparg); PyObject doc = null; if (code instanceof PyBytecode && ((PyBytecode) code).co_consts.length > 0) { doc = ((PyBytecode) code).co_consts[0]; } PyFunction func = new PyFunction(f.f_globals, defaults, code, doc, closure_cells); stack.push(func); break; } case Opcode.BUILD_SLICE: { PyObject step = oparg == 3 ? stack.pop() : null; PyObject stop = stack.pop(); PyObject start = stack.pop(); stack.push(new PySlice(start, stop, step)); break; } case Opcode.EXTENDED_ARG: opcode = getUnsigned(co_code, next_instr++); next_instr += 2; oparg = oparg << 16 | ((getUnsigned(co_code, next_instr) << 8) + getUnsigned(co_code, next_instr - 1)); break; default: Py.print(Py.getSystemState().stderr, Py.newString( String.format("XXX lineno: %d, opcode: %d\n", f.f_lasti, opcode))); throw Py.SystemError("unknown opcode"); } // end switch } // end try catch (Throwable t) { PyException pye = Py.setException(t, f); why = Why.EXCEPTION; ts.exception = pye; if (debug) { System.err.println("Caught exception:" + pye); } } if (why == Why.YIELD) { break; } // do some trace handling here, but for now just convert to EXCEPTION if (why == Why.RERAISE) { why = Why.EXCEPTION; } while (why != Why.NOT && blocksLeft(f)) { PyTryBlock b = popBlock(f); if (debug) { System.err.println("Processing block: " + b); } assert (why != Why.YIELD); if (b.b_type == Opcode.SETUP_LOOP && why == Why.CONTINUE) { pushBlock(f, b); why = Why.NOT; next_instr = retval.asInt(); break; } while (stack.size() > b.b_level) { stack.pop(); } if (b.b_type == Opcode.SETUP_LOOP && why == Why.BREAK) { why = Why.NOT; next_instr = b.b_handler; break; } if (b.b_type == Opcode.SETUP_FINALLY || (b.b_type == Opcode.SETUP_EXCEPT && why == Why.EXCEPTION)) { if (why == Why.EXCEPTION) { PyException exc = ts.exception; if (b.b_type == Opcode.SETUP_EXCEPT) { exc.normalize(); } stack.push(exc.traceback); stack.push(exc.value); stack.push(new PyStackException(exc)); // instead of stack.push(exc.type), like CPython } else { if (why == Why.RETURN || why == Why.CONTINUE) { stack.push(retval); } stack.push(new PyStackWhy(why)); } why = Why.NOT; next_instr = b.b_handler; break; } } // unwind block stack if (why != Why.NOT) { break; } } // end-while of the instruction loop if (why != Why.YIELD) { while (stack.size() > 0) { stack.pop(); } if (why != Why.RETURN) { retval = Py.None; } } else { // store the stack in the frame for reentry from the yield; f.f_savedlocals = stack.popN(stack.size()); } f.f_lasti = next_instr; // need to update on function entry, etc if (debug) { System.err.println(count + "," + f.f_lasti + "> Returning from " + why + ": " + retval + ", stack: " + stack.toString() + ", blocks: " + stringify_blocks(f)); } if (why == why.EXCEPTION) { throw ts.exception; } if (co_flags.isFlagSet(CodeFlag.CO_GENERATOR) && why == Why.RETURN && retval == Py.None) { f.f_lasti = -1; } return retval; } private static void call_function(int na, PyStack stack) { switch (na) { case 0: { PyObject callable = stack.pop(); stack.push(callable.__call__()); break; } case 1: { PyObject arg = stack.pop(); PyObject callable = stack.pop(); stack.push(callable.__call__(arg)); break; } case 2: { PyObject arg1 = stack.pop(); PyObject arg0 = stack.pop(); PyObject callable = stack.pop(); stack.push(callable.__call__(arg0, arg1)); break; } case 3: { PyObject arg2 = stack.pop(); PyObject arg1 = stack.pop(); PyObject arg0 = stack.pop(); PyObject callable = stack.pop(); stack.push(callable.__call__(arg0, arg1, arg2)); break; } case 4: { PyObject arg3 = stack.pop(); PyObject arg2 = stack.pop(); PyObject arg1 = stack.pop(); PyObject arg0 = stack.pop(); PyObject callable = stack.pop(); stack.push(callable.__call__(arg0, arg1, arg2, arg3)); break; } default: { PyObject args[] = stack.popN(na); PyObject callable = stack.pop(); stack.push(callable.__call__(args)); } } } private static void call_function(int na, int nk, PyStack stack) { int n = na + nk * 2; PyObject params[] = stack.popN(n); PyObject callable = stack.pop(); PyObject args[] = new PyObject[na + nk]; String keywords[] = new String[nk]; int i; for (i = 0; i < na; i++) { args[i] = params[i]; } for (int j = 0; i < n; i += 2, j++) { keywords[j] = params[i].toString(); args[na + j] = params[i + 1]; } stack.push(callable.__call__(args, keywords)); } private static void call_function(int na, int nk, boolean var, boolean kw, PyStack stack) { int n = na + nk * 2; PyObject kwargs = kw ? stack.pop() : null; PyObject starargs = var ? stack.pop() : null; PyObject params[] = stack.popN(n); PyObject callable = stack.pop(); PyObject args[] = new PyObject[na + nk]; String keywords[] = new String[nk]; int i; for (i = 0; i < na; i++) { args[i] = params[i]; } for (int j = 0; i < n; i += 2, j++) { keywords[j] = params[i].toString(); args[na + j] = params[i + 1]; } stack.push(callable._callextra(args, keywords, starargs, kwargs)); } private static void unpack_iterable(int oparg, PyStack stack) { PyObject v = stack.pop(); int i = oparg; PyObject items[] = new PyObject[oparg]; for (PyObject item : v.asIterable()) { if (i <= 0) { throw Py.ValueError("too many values to unpack"); } i--; items[i] = item; } if (i > 0) { throw Py.ValueError(String.format("need more than %d value%s to unpack", i, i == 1 ? "" : "s")); } for (i = 0; i < oparg; i++) { stack.push(items[i]); } } // XXX - perhaps add support for max stack size (presumably from co_stacksize) // and capacity hints private static class PyStack { final List<PyObject> stack; PyStack() { stack = new ArrayList<PyObject>(); } PyObject top() { return stack.get(stack.size() - 1); } PyObject top(int n) { return stack.get(stack.size() - n); } PyObject pop() { return stack.remove(stack.size() - 1); } void push(PyObject v) { stack.add(v); } void set_top(PyObject v) { stack.set(stack.size() - 1, v); } void dup() { stack.add(top()); } void dup(int n) { int length = stack.size(); for (int i = n; i > 0; i--) { stack.add(stack.get(length - i)); } } PyObject[] popN(int n) { int end = stack.size(); // exclusive PyObject ret[] = new PyObject[n]; List<PyObject> lastN = stack.subList(end - n, end); lastN.toArray(ret); lastN.clear(); return ret; } void rot2() { int length = stack.size(); PyObject v = stack.get(length - 1); PyObject w = stack.get(length - 2); stack.set(length - 1, w); stack.set(length - 2, v); } void rot3() { int length = stack.size(); PyObject v = stack.get(length - 1); PyObject w = stack.get(length - 2); PyObject x = stack.get(length - 3); stack.set(length - 1, w); stack.set(length - 2, x); stack.set(length - 3, v); } void rot4() { int length = stack.size(); PyObject u = stack.get(length - 1); PyObject v = stack.get(length - 2); PyObject w = stack.get(length - 3); PyObject x = stack.get(length - 4); stack.set(length - 1, v); stack.set(length - 2, w); stack.set(length - 3, x); stack.set(length - 4, u); } int size() { return stack.size(); } @Override public String toString() { StringBuilder buffer = new StringBuilder(); int size = stack.size(); int N = size > 4 ? 4 : size; buffer.append("["); for (int i = 0; i < N; i++) { if (i > 0) { buffer.append(", "); } PyObject item = stack.get(size - (i + 1)); buffer.append(upto(item.__repr__().toString())); } if (N < size) { buffer.append(String.format(", %d more...", size - N)); } buffer.append("]"); return buffer.toString(); } private String upto(String x) { return upto(x, 100); } private String upto(String x, int n) { x = x.replace('\n', '|'); if (x.length() > n) { StringBuilder item = new StringBuilder(x.substring(0, n)); item.append("..."); return item.toString(); } else { return x; } } } private static class PyTryBlock extends PyObject { // purely to sit on top of the existing PyFrame in f_exits!!! int b_type; /* what kind of block this is */ int b_handler; /* where to jump to find handler */ int b_level; /* value stack level to pop to */ PyTryBlock(int type, int handler, int level) { b_type = type; b_handler = handler; b_level = level; } @Override public String toString() { return "<" + get_opname().__getitem__(Py.newInteger(b_type)) + "," + b_handler + "," + b_level + ">"; } } @Override protected int getline(PyFrame f) { int addrq = f.f_lasti; int size = co_lnotab.length / 2; int p = 0; int line = co_firstlineno; int addr = 0; while (--size >= 0) { addr += getUnsigned(co_lnotab, p++); if (addr > addrq) { break; } line += getUnsigned(co_lnotab, p++); } return line; } private class LineCache { private class Pair { private final int addr; private final int line; private Pair(int a, int b) { this.addr = a; this.line = b; } public String toString() { return "(" + addr + "," + line + ")"; } } List<Integer> addr_breakpoints = new ArrayList<Integer>(); List<Integer> lines = new ArrayList<Integer>(); // length should be one more than addr_breakpoints private LineCache() { // based on dis.findlinestarts int size = co_lnotab.length / 2; int p = 0; int lastline = -1; int line = co_firstlineno; int addr = 0; while (--size >= 0) { int byte_incr = getUnsigned(co_lnotab, p++); int line_incr = getUnsigned(co_lnotab, p++); if (byte_incr > 0) { if (line != lastline) { addr_breakpoints.add(addr); lines.add(line); lastline = line; } addr += byte_incr; } line += line_incr; } if (line != lastline) { lines.add(line); } } private int getline(int addrq) { // bisect_right to the lineno int lo = 0; int hi = addr_breakpoints.size(); while (lo < hi) { int mid = (lo + hi) / 2; if (addrq < addr_breakpoints.get(mid)) { hi = mid; } else { lo = mid + 1; } } return lines.get(lo); } @Override public String toString() { return addr_breakpoints.toString() + ";" + lines.toString(); } } // Utility functions to enable storage of unsigned bytes in co_code, co_lnotab byte[] arrays private static char getUnsigned(byte[] x, int i) { byte b = x[i]; if (b < 0) { return (char) (b + 256); } else { return (char) b; } } private static String getString(byte[] x) { StringBuilder buffer = new StringBuilder(x.length); for (int i = 0; i < x.length; i++) { buffer.append(getUnsigned(x, i)); } return buffer.toString(); } private static byte[] getBytes(String s) { int len = s.length(); byte[] x = new byte[len]; for (int i = 0; i < len; i++) { x[i] = (byte) (s.charAt(i) & 0xFF); } return x; } }