// Copyright (c) Corporation for National Research Initiatives package org.python.core; /** * An implementation of PyCode where the actual executable content * is stored as a PyFunctionTable instance and an integer index. */ final public class PyTableCode extends PyCode { public int co_argcount; int nargs; public int co_firstlineno = -1; public String co_varnames[]; public String co_cellvars[]; public int jy_npurecell; // internal: jython specific public String co_freevars[]; public String co_filename; public int co_flags; public int co_nlocals; public boolean args, keywords; PyFunctionTable funcs; int func_id; final public static int CO_OPTIMIZED = 0x0001; //final public static int CO_NEWLOCALS = 0x0002 final public static int CO_VARARGS = 0x0004; final public static int CO_VARKEYWORDS = 0x0008; final public static int CO_GENERATOR = 0x0020; final public static int CO_NESTED = 0x0010; final public static int CO_GENERATOR_ALLOWED = 0x1000; final public static int CO_FUTUREDIVISION = 0x2000; final public static int CO_ALL_FEATURES = CO_NESTED | CO_GENERATOR_ALLOWED | CO_FUTUREDIVISION; public PyTableCode(int argcount, String varnames[], String filename, String name, int firstlineno, boolean args, boolean keywords, PyFunctionTable funcs, int func_id) { this(argcount, varnames, filename, name, firstlineno, args, keywords, funcs, func_id, null, null, 0, 0); } public PyTableCode(int argcount, String varnames[], String filename, String name, int firstlineno, boolean args, boolean keywords, PyFunctionTable funcs, int func_id, String[] cellvars, String[] freevars, int npurecell, int moreflags) // may change { co_argcount = nargs = argcount; co_varnames = varnames; co_nlocals = varnames.length; co_filename = filename; co_firstlineno = firstlineno; co_cellvars = cellvars; co_freevars = freevars; this.jy_npurecell = npurecell; this.args = args; co_name = name; if (args) { co_argcount -= 1; co_flags |= CO_VARARGS; } this.keywords = keywords; if (keywords) { co_argcount -= 1; co_flags |= CO_VARKEYWORDS; } co_flags |= moreflags; this.funcs = funcs; this.func_id = func_id; } private static final String[] __members__ = { "co_name", "co_argcount", "co_varnames", "co_filename", "co_firstlineno", "co_flags", "co_cellvars", "co_freevars", "co_nlocals" // not supported: co_code, co_consts, co_names, // co_lnotab, co_stacksize }; 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); } public boolean hasFreevars() { return co_freevars != null && co_freevars.length > 0; } 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__(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); return super.__findattr__(name); } public PyObject call(PyFrame frame, PyObject closure) { // System.err.println("tablecode call: "+co_name); ThreadState ts = Py.getThreadState(); if (ts.systemState == null) { ts.systemState = Py.defaultSystemState; } //System.err.println("got ts: "+ts+", "+ts.systemState); // Cache previously defined exception PyException previous_exception = ts.exception; // Push frame frame.f_back = ts.frame; if (frame.f_builtins == null) { if (frame.f_back != null) { frame.f_builtins = frame.f_back.f_builtins; } else { //System.err.println("ts: "+ts); //System.err.println("ss: "+ts.systemState); frame.f_builtins = ts.systemState.builtins; } } // nested scopes: setup env with closure int env_j = 0; int ncells = frame.f_ncells; int nfreevars = frame.f_nfreevars; PyCell[] env = frame.f_env; PyTuple freevars = (PyTuple) closure; for (int i = 0; i < ncells; i++, env_j++) { env[env_j] = new PyCell(); } for (int i = 0; i < nfreevars; i++, env_j++) { env[env_j] = (PyCell) freevars.pyget(i); } ts.frame = frame; // Handle trace function for debugging PySystemState ss = ts.systemState; if (ss.tracefunc != null) { // Jython and CPython differ here. CPython actually lays down // an extra SET_LINENO bytecode for function definition line. // This is ostensibly so that a tuple unpacking failure in // argument passing gets the right line number in the // traceback. It also means that when tracing a function, // you'll see two 'line' events, one for the def line and then // immediately after, one for the first line of the function. // // Jython on the other hand only lays down a call in the // generated Java function to set the line number for the first // line of the function (i.e. not the def line). This // difference in behavior doesn't seem to affect arg tuple // unpacking tracebacks, but it does mean that function tracing // gives slightly different behavior. Is this bad? Until // someone complains... no. // // The second commented out line fixes this but it is probably // not the right solution. Better would be to fix the code // generator to lay down two calls to setline() in the // classfile. This would allow that call to be optimized out // when using the -O option. I suppose on the other hand we // could test that flag here and not call the setline below. // In either case, it probably doesn't make sense to slow down // function calling even by this miniscule amount until it's // shown to have a detrimental effect. // // Note also that if you were to print out frame.f_lineno in // the `call' event handler of your trace function, you'd see // zero instead of the line of the def. That's what the first // commented line fixes. // // 9-Sep-1999 baw // // frame.f_lineno = co_firstlineno; frame.tracefunc = ss.tracefunc.traceCall(frame); frame.setline(co_firstlineno); } // Handle trace function for profiling if (ss.profilefunc != null) { ss.profilefunc.traceCall(frame); } PyObject ret; try { ret = funcs.call_function(func_id, frame); } catch (Throwable t) { //t.printStackTrace(); //Convert exceptions that occured in Java code to PyExceptions PyException e = Py.JavaError(t); //Add another traceback object to the exception if needed if (e.traceback.tb_frame != frame) { PyTraceback tb; // If f_back is null, we've jumped threads so use the current // threadstate's frame. Bug #1533624 if (e.traceback.tb_frame.f_back == null) { tb = new PyTraceback(ts.frame); } else { tb = new PyTraceback(e.traceback.tb_frame.f_back); } tb.tb_next = e.traceback; e.traceback = tb; } frame.f_lasti = -1; if (frame.tracefunc != null) { frame.tracefunc.traceException(frame, e); } if (ss.profilefunc != null) { ss.profilefunc.traceException(frame, e); } //Rethrow the exception to the next stack frame ts.exception = previous_exception; ts.frame = ts.frame.f_back; throw e; } if (frame.tracefunc != null) { frame.tracefunc.traceReturn(frame, ret); } // Handle trace function for profiling if (ss.profilefunc != null) { ss.profilefunc.traceReturn(frame, ret); } // Restore previously defined exception ts.exception = previous_exception; ts.frame = ts.frame.f_back; return ret; } public PyObject call(PyObject globals, PyObject[] defaults, PyObject closure) { if (co_argcount != 0 || args || keywords) return call(Py.EmptyObjects, Py.NoKeywords, globals, defaults, closure); PyFrame frame = new PyFrame(this, globals); if ((co_flags & CO_GENERATOR) != 0) { return new PyGenerator(frame, closure); } return call(frame, closure); } public PyObject call(PyObject arg1, PyObject globals, PyObject[] defaults, PyObject closure) { if (co_argcount != 1 || args || keywords) return call(new PyObject[] { arg1 }, Py.NoKeywords, globals, defaults, closure); PyFrame frame = new PyFrame(this, globals); frame.f_fastlocals[0] = arg1; if ((co_flags & CO_GENERATOR) != 0) { return new PyGenerator(frame, closure); } return call(frame, closure); } public PyObject call(PyObject arg1, PyObject arg2, PyObject globals, PyObject[] defaults, PyObject closure) { if (co_argcount != 2 || args || keywords) return call(new PyObject[] { arg1, arg2 }, Py.NoKeywords, globals, defaults, closure); PyFrame frame = new PyFrame(this, globals); frame.f_fastlocals[0] = arg1; frame.f_fastlocals[1] = arg2; if ((co_flags & CO_GENERATOR) != 0) { return new PyGenerator(frame, closure); } return call(frame, closure); } public PyObject call(PyObject arg1, PyObject arg2, PyObject arg3, PyObject globals, PyObject[] defaults, PyObject closure) { if (co_argcount != 3 || args || keywords) return call(new PyObject[] { arg1, arg2, arg3 }, Py.NoKeywords, globals, defaults, closure); PyFrame frame = new PyFrame(this, globals); frame.f_fastlocals[0] = arg1; frame.f_fastlocals[1] = arg2; frame.f_fastlocals[2] = arg3; if ((co_flags & CO_GENERATOR) != 0) { return new PyGenerator(frame, closure); } return call(frame, closure); } public PyObject call(PyObject self, PyObject call_args[], String call_keywords[], PyObject globals, PyObject[] defaults, PyObject closure) { PyObject[] os = new PyObject[call_args.length + 1]; os[0] = (PyObject) self; System.arraycopy(call_args, 0, os, 1, call_args.length); return call(os, call_keywords, globals, defaults, closure); } private String prefix() { return co_name.toString() + "() "; } public PyObject call(PyObject call_args[], String call_keywords[], PyObject globals, PyObject[] defaults, PyObject closure) { //Needs try except finally blocks PyFrame my_frame = new PyFrame(this, globals); PyObject actual_args[], extra_args[] = null; PyDictionary extra_keywords = null; int plain_args = call_args.length - call_keywords.length; int i; if (plain_args > co_argcount) plain_args = co_argcount; actual_args = my_frame.f_fastlocals; if (plain_args > 0) System.arraycopy(call_args, 0, actual_args, 0, plain_args); if (!((call_keywords == null || call_keywords.length == 0) && call_args.length == co_argcount && !keywords && !args)) { if (keywords) extra_keywords = new PyDictionary(); for (i = 0; i < call_keywords.length; i++) { int index = 0; while (index < co_argcount) { if (co_varnames[index].equals(call_keywords[i])) break; index++; } if (index < co_argcount) { if (actual_args[index] != null) { throw Py.TypeError(prefix() + "got multiple values for " + "keyword argument '" + call_keywords[i] + "'"); } actual_args[index] = call_args[i + (call_args.length - call_keywords.length)]; } else { if (extra_keywords == null) { throw Py.TypeError(prefix() + "got an unexpected keyword " + "argument '" + call_keywords[i] + "'"); } extra_keywords.__setitem__(call_keywords[i], call_args[i + (call_args.length - call_keywords.length)]); } } if (call_args.length - call_keywords.length > co_argcount) { if (!args) throw Py.TypeError(prefix() + "too many arguments; expected " + co_argcount + " got " + (call_args.length - call_keywords.length)); extra_args = new PyObject[call_args.length - call_keywords.length - co_argcount]; for (i = 0; i < extra_args.length; i++) { extra_args[i] = call_args[i + co_argcount]; } } for (i = plain_args; i < co_argcount; i++) { if (actual_args[i] == null) { if (co_argcount - i > defaults.length) { int min = co_argcount - defaults.length; throw Py.TypeError(prefix() + "takes at least " + min + (min == 1 ? " argument (" : " arguments (") + (call_args.length - call_keywords.length) + " given)"); } actual_args[i] = defaults[defaults.length - (co_argcount - i)]; } } if (args) { if (extra_args == null) actual_args[co_argcount] = Py.EmptyTuple; else actual_args[co_argcount] = new PyTuple(extra_args); } if (extra_keywords != null) { actual_args[nargs - 1] = extra_keywords; } } if ((co_flags & CO_GENERATOR) != 0) { return new PyGenerator(my_frame, closure); } return call(my_frame, closure); } public String toString() { return "<code object " + co_name + " " + Py.idstr(this) + ", file \"" + co_filename + "\", line " + co_firstlineno + ">"; } }