/* * Copyright (c) Corporation for National Research Initiatives * Copyright (c) Jython Developers */ package org.python.core; import org.python.expose.ExposedDelete; import org.python.expose.ExposedGet; import org.python.expose.ExposedSet; import org.python.expose.ExposedType; /** * A Python frame object. */ @ExposedType(name = "frame", isBaseType = false) public class PyFrame extends PyObject { public static final PyType TYPE = PyType.fromClass(PyFrame.class); /** Previous frame or null. */ @ExposedGet public PyFrame f_back; /** The underyling code object. */ @ExposedGet public PyBaseCode f_code; /** builtin symbol table. */ @ExposedGet public PyObject f_builtins; /** Global symbol table. */ @ExposedGet public PyObject f_globals; /** Local symbol table. */ public PyObject f_locals; /** Current line number. */ public int f_lineno; public PyObject[] f_fastlocals; /** Nested scopes: cell + free env. */ public PyCell[] f_env; private int env_j = 0; public int f_ncells; public int f_nfreevars; @ExposedGet public int f_lasti; public Object[] f_savedlocals; private Object generatorInput = Py.None; /** with context exits - used by generated bytecode */ public PyObject[] f_exits; /** An interface to functions suitable for tracing, e.g. via sys.settrace(). */ public TraceFunction tracefunc; private static final String NAME_ERROR_MSG = "name '%.200s' is not defined"; private static final String GLOBAL_NAME_ERROR_MSG = "global name '%.200s' is not defined"; private static final String UNBOUNDLOCAL_ERROR_MSG = "local variable '%.200s' referenced before assignment"; public PyFrame(PyBaseCode code, PyObject locals, PyObject globals, PyObject builtins) { super(TYPE); f_code = code; f_locals = locals; f_globals = globals; f_builtins = builtins; // This needs work to be efficient with multiple interpreter states if (locals == null && code != null) { // ! f_fastlocals needed for arg passing too if (code.co_flags.isFlagSet(CodeFlag.CO_OPTIMIZED) || code.nargs > 0) { if (code.co_nlocals > 0) { // internal: may change f_fastlocals = new PyObject[code.co_nlocals - code.jy_npurecell]; } } else { f_locals = new PyStringMap(); } } if (code != null) { // reserve space for env int env_sz = 0; if (code.co_freevars != null) { env_sz += (f_nfreevars = code.co_freevars.length); } if (code.co_cellvars != null) { env_sz += (f_ncells = code.co_cellvars.length); } if (env_sz > 0) { f_env = new PyCell[env_sz]; } } } public PyFrame(PyBaseCode code, PyObject globals) { this(code, null, globals, null); } /** * Populate the frame with closure variables, but at most once. * * @param freevars a <code>PyTuple</code> value */ void setupEnv(PyTuple freevars) { int ntotal = f_ncells + f_nfreevars; // add space for the cellvars for (; env_j < f_ncells; env_j++) { f_env[env_j] = new PyCell(); } // inherit the freevars for (int i = 0; env_j < ntotal; i++, env_j++) { f_env[env_j] = (PyCell)freevars.pyget(i); } } void setGeneratorInput(Object value) { generatorInput = value; } public Object getGeneratorInput() { Object input = generatorInput; generatorInput = Py.None; return input; } public Object checkGeneratorInput() { return generatorInput; } /** * Return the locals dict. First merges the fast locals into * f_locals, then returns the updated f_locals. * * @return a PyObject mapping of locals */ @ExposedGet(name = "f_locals") public PyObject getLocals() { if (f_locals == null) { f_locals = new PyStringMap(); } if (f_code != null && (f_code.co_nlocals > 0 || f_nfreevars > 0)) { int i; if (f_fastlocals != null) { for (i = 0; i < f_fastlocals.length; i++) { PyObject o = f_fastlocals[i]; if (o != null) f_locals.__setitem__(f_code.co_varnames[i], o); } if (!f_code.co_flags.isFlagSet(CodeFlag.CO_OPTIMIZED)) { f_fastlocals = null; } } int j = 0; for (i = 0; i < f_ncells; i++, j++) { PyObject v = f_env[j].ob_ref; if (v != null) { f_locals.__setitem__(f_code.co_cellvars[i], v); } } for (i = 0; i < f_nfreevars; i++, j++) { PyObject v = f_env[j].ob_ref; if (v != null) { f_locals.__setitem__(f_code.co_freevars[i], v); } } } return f_locals; } @ExposedGet(name = "f_trace") public PyObject getTrace() { return tracefunc instanceof PythonTraceFunction ? ((PythonTraceFunction)tracefunc).tracefunc : Py.None; } @ExposedSet(name = "f_trace") public void setTrace(PyObject trace) { tracefunc = new PythonTraceFunction(trace); } @ExposedDelete(name = "f_trace") public void delTrace() { tracefunc = null; } /** * Return the current f_locals dict. * * @return a PyObject mapping of locals */ public PyObject getf_locals() { // XXX: This could be deprecated, grab f_locals directly // instead. only the compiler calls this return f_locals; } /** * Track the current line number. Called by generated code. * * This is not to be confused with the CPython method * frame_setlineno() which causes the interpreter to jump to * the given line. */ public void setline(int line) { f_lineno = line; if (tracefunc != null) { tracefunc = tracefunc.traceLine(this, line); } } @ExposedGet(name = "f_lineno") public int getline() { return tracefunc != null ? f_lineno : f_code.getline(this); } public PyObject getlocal(int index) { if (f_fastlocals != null) { PyObject ret = f_fastlocals[index]; if (ret != null) { return ret; } } String name = f_code.co_varnames[index]; if (f_locals != null) { PyObject ret = f_locals.__finditem__(name); if (ret != null) { return ret; } } throw Py.UnboundLocalError(String.format(UNBOUNDLOCAL_ERROR_MSG, name)); } public PyObject getname(String index) { PyObject ret; if (f_locals == null || f_locals == f_globals) { ret = doGetglobal(index); } else { ret = f_locals.__finditem__(index); if (ret != null) { return ret; } ret = doGetglobal(index); } if (ret != null) { return ret; } throw Py.NameError(String.format(NAME_ERROR_MSG, index)); } public PyObject getglobal(String index) { PyObject ret = doGetglobal(index); if (ret != null) { return ret; } throw Py.NameError(String.format(GLOBAL_NAME_ERROR_MSG, index)); } private PyObject doGetglobal(String index) { PyObject ret = f_globals.__finditem__(index); if (ret != null) { return ret; } // Set up f_builtins if not already set if (f_builtins == null) { f_builtins = PySystemState.builtins; } return f_builtins.__finditem__(index); } public void setlocal(int index, PyObject value) { if (f_fastlocals != null) { f_fastlocals[index] = value; } else { setlocal(f_code.co_varnames[index], value); } } public void setlocal(String index, PyObject value) { if (f_locals != null) { f_locals.__setitem__(index, value); } else { throw Py.SystemError(String.format("no locals found when storing '%s'", value)); } } public void setglobal(String index, PyObject value) { f_globals.__setitem__(index, value); } public void dellocal(int index) { if (f_fastlocals != null) { if (f_fastlocals[index] == null) { throw Py.UnboundLocalError(String.format(UNBOUNDLOCAL_ERROR_MSG, f_code.co_varnames[index])); } f_fastlocals[index] = null; } else { dellocal(f_code.co_varnames[index]); } } public void dellocal(String index) { if (f_locals != null) { try { f_locals.__delitem__(index); } catch (PyException pye) { if (pye.match(Py.KeyError)) { throw Py.NameError(String.format(NAME_ERROR_MSG, index)); } throw pye; } } else { throw Py.SystemError(String.format("no locals when deleting '%s'", index)); } } public void delglobal(String index) { try { f_globals.__delitem__(index); } catch (PyException pye) { if (pye.match(Py.KeyError)) { throw Py.NameError(String.format(GLOBAL_NAME_ERROR_MSG, index)); } throw pye; } } // nested scopes helpers public PyObject getclosure(int index) { return f_env[index]; } public PyObject getderef(int index) { PyObject obj = f_env[index].ob_ref; if (obj != null) { return obj; } String name; if (index >= f_ncells) { name = f_code.co_freevars[index - f_ncells]; } else { name = f_code.co_cellvars[index]; } throw Py.UnboundLocalError(String.format(UNBOUNDLOCAL_ERROR_MSG, name)); } public void setderef(int index, PyObject value) { f_env[index].ob_ref = value; } public void to_cell(int parm_index, int env_index) { f_env[env_index].ob_ref = f_fastlocals[parm_index]; } }