001: // Copyright (c) Corporation for National Research Initiatives
002: package org.python.core;
003:
004: /**
005: * An implementation of PyCode where the actual executable content
006: * is stored as a PyFunctionTable instance and an integer index.
007: */
008:
009: public class PyTableCode extends PyCode {
010: public int co_argcount;
011: int nargs;
012: public int co_firstlineno = -1;
013: public String co_varnames[];
014: public String co_cellvars[];
015: public int jy_npurecell; // internal: jython specific
016: public String co_freevars[];
017: public String co_filename;
018: public int co_flags;
019: public int co_nlocals;
020: public boolean args, keywords;
021: PyFunctionTable funcs;
022: int func_id;
023:
024: final public static int CO_OPTIMIZED = 0x0001;
025: //final public static int CO_NEWLOCALS = 0x0002
026: final public static int CO_VARARGS = 0x0004;
027: final public static int CO_VARKEYWORDS = 0x0008;
028: final public static int CO_GENERATOR = 0x0020;
029:
030: final public static int CO_NESTED = 0x0010;
031: final public static int CO_GENERATOR_ALLOWED = 0x1000;
032: final public static int CO_FUTUREDIVISION = 0x2000;
033: final public static int CO_ALL_FEATURES = CO_NESTED
034: | CO_GENERATOR_ALLOWED | CO_FUTUREDIVISION;
035:
036: public PyTableCode(int argcount, String varnames[],
037: String filename, String name, int firstlineno,
038: boolean args, boolean keywords, PyFunctionTable funcs,
039: int func_id) {
040: this (argcount, varnames, filename, name, firstlineno, args,
041: keywords, funcs, func_id, null, null, 0, 0);
042: }
043:
044: public PyTableCode(int argcount, String varnames[],
045: String filename, String name, int firstlineno,
046: boolean args, boolean keywords, PyFunctionTable funcs,
047: int func_id, String[] cellvars, String[] freevars,
048: int npurecell, int moreflags) // may change
049: {
050: co_argcount = nargs = argcount;
051: co_varnames = varnames;
052: co_nlocals = varnames.length;
053: co_filename = filename;
054: co_firstlineno = firstlineno;
055: co_cellvars = cellvars;
056: co_freevars = freevars;
057: this .jy_npurecell = npurecell;
058: this .args = args;
059: co_name = name;
060: if (args) {
061: co_argcount -= 1;
062: co_flags |= CO_VARARGS;
063: }
064: this .keywords = keywords;
065: if (keywords) {
066: co_argcount -= 1;
067: co_flags |= CO_VARKEYWORDS;
068: }
069: co_flags |= moreflags;
070: this .funcs = funcs;
071: this .func_id = func_id;
072: }
073:
074: private static final String[] __members__ = { "co_name",
075: "co_argcount", "co_varnames", "co_filename",
076: "co_firstlineno", "co_flags", "co_cellvars", "co_freevars",
077: "co_nlocals"
078: // not supported: co_code, co_consts, co_names,
079: // co_lnotab, co_stacksize
080: };
081:
082: public PyObject __dir__() {
083: PyString members[] = new PyString[__members__.length];
084: for (int i = 0; i < __members__.length; i++)
085: members[i] = new PyString(__members__[i]);
086: return new PyList(members);
087: }
088:
089: public boolean hasFreevars() {
090: return co_freevars != null && co_freevars.length > 0;
091: }
092:
093: private void throwReadonly(String name) {
094: for (int i = 0; i < __members__.length; i++)
095: if (__members__[i] == name)
096: throw Py.TypeError("readonly attribute");
097: throw Py.AttributeError(name);
098: }
099:
100: public void __setattr__(String name, PyObject value) {
101: // no writable attributes
102: throwReadonly(name);
103: }
104:
105: public void __delattr__(String name) {
106: throwReadonly(name);
107: }
108:
109: private static PyTuple toPyStringTuple(String[] ar) {
110: if (ar == null)
111: return Py.EmptyTuple;
112: int sz = ar.length;
113: PyString[] pystr = new PyString[sz];
114: for (int i = 0; i < sz; i++) {
115: pystr[i] = new PyString(ar[i]);
116: }
117: return new PyTuple(pystr);
118: }
119:
120: public PyObject __findattr__(String name) {
121: // have to craft co_varnames specially
122: if (name == "co_varnames")
123: return toPyStringTuple(co_varnames);
124: if (name == "co_cellvars")
125: return toPyStringTuple(co_cellvars);
126: if (name == "co_freevars")
127: return toPyStringTuple(co_freevars);
128: return super .__findattr__(name);
129: }
130:
131: public PyObject call(PyFrame frame, PyObject closure) {
132: // System.err.println("tablecode call: "+co_name);
133: ThreadState ts = Py.getThreadState();
134: if (ts.systemState == null) {
135: ts.systemState = Py.defaultSystemState;
136: }
137: //System.err.println("got ts: "+ts+", "+ts.systemState);
138:
139: // Cache previously defined exception
140: PyException previous_exception = ts.exception;
141:
142: // Push frame
143: frame.f_back = ts.frame;
144: if (frame.f_builtins == null) {
145: if (frame.f_back != null) {
146: frame.f_builtins = frame.f_back.f_builtins;
147: } else {
148: //System.err.println("ts: "+ts);
149: //System.err.println("ss: "+ts.systemState);
150: frame.f_builtins = ts.systemState.builtins;
151: }
152: }
153: // nested scopes: setup env with closure
154: int env_j = 0;
155: int ncells = frame.f_ncells;
156: int nfreevars = frame.f_nfreevars;
157: PyCell[] env = frame.f_env;
158: PyTuple freevars = (PyTuple) closure;
159: for (int i = 0; i < ncells; i++, env_j++) {
160: env[env_j] = new PyCell();
161: }
162: for (int i = 0; i < nfreevars; i++, env_j++) {
163: env[env_j] = (PyCell) freevars.pyget(i);
164: }
165:
166: ts.frame = frame;
167:
168: // Handle trace function for debugging
169: PySystemState ss = ts.systemState;
170: if (ss.tracefunc != null) {
171: // JPython and CPython differ here. CPython actually lays down
172: // an extra SET_LINENO bytecode for function definition line.
173: // This is ostensibly so that a tuple unpacking failure in
174: // argument passing gets the right line number in the
175: // traceback. It also means that when tracing a function,
176: // you'll see two 'line' events, one for the def line and then
177: // immediately after, one for the first line of the function.
178: //
179: // JPython on the other hand only lays down a call in the
180: // generated Java function to set the line number for the first
181: // line of the function (i.e. not the def line). This
182: // difference in behavior doesn't seem to affect arg tuple
183: // unpacking tracebacks, but it does mean that function tracing
184: // gives slightly different behavior. Is this bad? Until
185: // someone complains... no.
186: //
187: // The second commented out line fixes this but it is probably
188: // not the right solution. Better would be to fix the code
189: // generator to lay down two calls to setline() in the
190: // classfile. This would allow that call to be optimized out
191: // when using the -O option. I suppose on the other hand we
192: // could test that flag here and not call the setline below.
193: // In either case, it probably doesn't make sense to slow down
194: // function calling even by this miniscule amount until it's
195: // shown to have a detrimental effect.
196: //
197: // Note also that if you were to print out frame.f_lineno in
198: // the `call' event handler of your trace function, you'd see
199: // zero instead of the line of the def. That's what the first
200: // commented line fixes.
201: //
202: // 9-Sep-1999 baw
203: //
204: // frame.f_lineno = co_firstlineno;
205: frame.tracefunc = ss.tracefunc.traceCall(frame);
206: frame.setline(co_firstlineno);
207: }
208:
209: // Handle trace function for profiling
210: if (ss.profilefunc != null) {
211: ss.profilefunc.traceCall(frame);
212: }
213:
214: PyObject ret;
215: try {
216: ret = funcs.call_function(func_id, frame);
217: } catch (Throwable t) {
218: //t.printStackTrace();
219: //Convert exceptions that occured in Java code to PyExceptions
220: PyException e = Py.JavaError(t);
221:
222: //Add another traceback object to the exception if needed
223: if (e.traceback.tb_frame != frame) {
224: PyTraceback tb;
225: // If f_back is null, we've jumped threads so use the current
226: // threadstate's frame. Bug #1533624
227: if (e.traceback.tb_frame.f_back == null) {
228: tb = new PyTraceback(ts.frame);
229: } else {
230: tb = new PyTraceback(e.traceback.tb_frame.f_back);
231: }
232: tb.tb_next = e.traceback;
233: e.traceback = tb;
234: }
235:
236: frame.f_lasti = -1;
237:
238: if (frame.tracefunc != null) {
239: frame.tracefunc.traceException(frame, e);
240: }
241: if (ss.profilefunc != null) {
242: ss.profilefunc.traceException(frame, e);
243: }
244:
245: //Rethrow the exception to the next stack frame
246: ts.exception = previous_exception;
247: ts.frame = ts.frame.f_back;
248: throw e;
249: }
250:
251: if (frame.tracefunc != null) {
252: frame.tracefunc.traceReturn(frame, ret);
253: }
254: // Handle trace function for profiling
255: if (ss.profilefunc != null) {
256: ss.profilefunc.traceReturn(frame, ret);
257: }
258:
259: // Restore previously defined exception
260: ts.exception = previous_exception;
261:
262: ts.frame = ts.frame.f_back;
263: return ret;
264: }
265:
266: public PyObject call(PyObject globals, PyObject[] defaults,
267: PyObject closure) {
268: if (co_argcount != 0 || args || keywords)
269: return call(Py.EmptyObjects, Py.NoKeywords, globals,
270: defaults, closure);
271: PyFrame frame = new PyFrame(this , globals);
272: if ((co_flags & CO_GENERATOR) != 0) {
273: return new PyGenerator(frame, closure);
274: }
275: return call(frame, closure);
276: }
277:
278: public PyObject call(PyObject arg1, PyObject globals,
279: PyObject[] defaults, PyObject closure) {
280: if (co_argcount != 1 || args || keywords)
281: return call(new PyObject[] { arg1 }, Py.NoKeywords,
282: globals, defaults, closure);
283: PyFrame frame = new PyFrame(this , globals);
284: frame.f_fastlocals[0] = arg1;
285: if ((co_flags & CO_GENERATOR) != 0) {
286: return new PyGenerator(frame, closure);
287: }
288: return call(frame, closure);
289: }
290:
291: public PyObject call(PyObject arg1, PyObject arg2,
292: PyObject globals, PyObject[] defaults, PyObject closure) {
293: if (co_argcount != 2 || args || keywords)
294: return call(new PyObject[] { arg1, arg2 }, Py.NoKeywords,
295: globals, defaults, closure);
296: PyFrame frame = new PyFrame(this , globals);
297: frame.f_fastlocals[0] = arg1;
298: frame.f_fastlocals[1] = arg2;
299: if ((co_flags & CO_GENERATOR) != 0) {
300: return new PyGenerator(frame, closure);
301: }
302: return call(frame, closure);
303: }
304:
305: public PyObject call(PyObject arg1, PyObject arg2, PyObject arg3,
306: PyObject globals, PyObject[] defaults, PyObject closure) {
307: if (co_argcount != 3 || args || keywords)
308: return call(new PyObject[] { arg1, arg2, arg3 },
309: Py.NoKeywords, globals, defaults, closure);
310: PyFrame frame = new PyFrame(this , globals);
311: frame.f_fastlocals[0] = arg1;
312: frame.f_fastlocals[1] = arg2;
313: frame.f_fastlocals[2] = arg3;
314: if ((co_flags & CO_GENERATOR) != 0) {
315: return new PyGenerator(frame, closure);
316: }
317: return call(frame, closure);
318: }
319:
320: public PyObject call(PyObject self, PyObject call_args[],
321: String call_keywords[], PyObject globals,
322: PyObject[] defaults, PyObject closure) {
323: PyObject[] os = new PyObject[call_args.length + 1];
324: os[0] = (PyObject) self;
325: System.arraycopy(call_args, 0, os, 1, call_args.length);
326: return call(os, call_keywords, globals, defaults, closure);
327: }
328:
329: private String prefix() {
330: return co_name.toString() + "() ";
331: }
332:
333: public PyObject call(PyObject call_args[], String call_keywords[],
334: PyObject globals, PyObject[] defaults, PyObject closure) {
335: //Needs try except finally blocks
336: PyFrame my_frame = new PyFrame(this , globals);
337:
338: PyObject actual_args[], extra_args[] = null;
339: PyDictionary extra_keywords = null;
340: int plain_args = call_args.length - call_keywords.length;
341: int i;
342:
343: if (plain_args > co_argcount)
344: plain_args = co_argcount;
345:
346: actual_args = my_frame.f_fastlocals;
347: if (plain_args > 0)
348: System.arraycopy(call_args, 0, actual_args, 0, plain_args);
349:
350: if (!((call_keywords == null || call_keywords.length == 0)
351: && call_args.length == co_argcount && !keywords && !args)) {
352: if (keywords)
353: extra_keywords = new PyDictionary();
354:
355: for (i = 0; i < call_keywords.length; i++) {
356: int index = 0;
357: while (index < co_argcount) {
358: if (co_varnames[index].equals(call_keywords[i]))
359: break;
360: index++;
361: }
362: if (index < co_argcount) {
363: if (actual_args[index] != null) {
364: throw Py.TypeError(prefix()
365: + "got multiple values for "
366: + "keyword argument '"
367: + call_keywords[i] + "'");
368: }
369: actual_args[index] = call_args[i
370: + (call_args.length - call_keywords.length)];
371: } else {
372: if (extra_keywords == null) {
373: throw Py
374: .TypeError(prefix()
375: + "got an unexpected keyword "
376: + "argument '"
377: + call_keywords[i] + "'");
378: }
379: extra_keywords
380: .__setitem__(
381: call_keywords[i],
382: call_args[i
383: + (call_args.length - call_keywords.length)]);
384: }
385: }
386: if (call_args.length - call_keywords.length > co_argcount) {
387: if (!args)
388: throw Py
389: .TypeError(prefix()
390: + "too many arguments; expected "
391: + co_argcount
392: + " got "
393: + (call_args.length - call_keywords.length));
394: extra_args = new PyObject[call_args.length
395: - call_keywords.length - co_argcount];
396:
397: for (i = 0; i < extra_args.length; i++) {
398: extra_args[i] = call_args[i + co_argcount];
399: }
400: }
401: for (i = plain_args; i < co_argcount; i++) {
402: if (actual_args[i] == null) {
403: if (co_argcount - i > defaults.length) {
404: int min = co_argcount - defaults.length;
405: throw Py
406: .TypeError(prefix()
407: + "takes at least "
408: + min
409: + (min == 1 ? " argument ("
410: : " arguments (")
411: + (call_args.length - call_keywords.length)
412: + " given)");
413: }
414: actual_args[i] = defaults[defaults.length
415: - (co_argcount - i)];
416: }
417: }
418: if (args) {
419: if (extra_args == null)
420: actual_args[co_argcount] = Py.EmptyTuple;
421: else
422: actual_args[co_argcount] = new PyTuple(extra_args);
423: }
424: if (extra_keywords != null) {
425: actual_args[nargs - 1] = extra_keywords;
426: }
427: }
428: if ((co_flags & CO_GENERATOR) != 0) {
429: return new PyGenerator(my_frame, closure);
430: }
431: return call(my_frame, closure);
432: }
433:
434: public String toString() {
435: return "<code object " + co_name + " " + Py.idstr(this )
436: + ", file \"" + co_filename + "\", line "
437: + co_firstlineno + ">";
438: }
439: }
|