4.2. Types of code and frame Object

4.2.1. Several Types of Code

An object that is merely of type code to Python, should be capable of representing at least:

  • Java byte code (compiled from Python). As in Jython 2, we expect to produce Java byte code for performance.

  • CPython byte code (of more than one vintage?). We have found it necessary in Jython 2 to support CPython byte code because large modules and functions exceed the maximum size for a Java class definition.

An extensible approach is to allow sub-classes of PyCode, one for each compiled form.

These two cases already make it desirable to have more than one kind of PyFrame. The idea briefly, explored more fully below, is to regard the code object as a factory for the particular type of frame that it needs.

4.2.2. Old Notes (needs rewrite)

PyCode

PyCodeObject (type code) holds compiled code (CPython byte code), such as from a module or function body. It holds information that may be deduced statically from the source, such as the names of local variables and function arguments. For the TreePython version, “compiled code” is the AST itself, or possibly a transformed version of it, and the other information can be derived from the AST. Later we want the compiled code to be a JVM class definition, and to answer the architectural questions that may raise.

PyFrame

PyFrameObject is a Python object (type frame) that provides the execution context for one invocation of a function, or of a module or class definition while it executes. It holds the values of local variables named in the associated code object, the Python VM arithmetic stack, and all local execution state. A chain of frames forms the Python execution stack of one thread.

The PyFrame holds argument values and local variables, and any state associated with a particular execution of the code, if it is not in the local variables of the interpreter itself. In CPython it provides space for the arithmetic stack.

A PyFrame also exists apart from representing the current state of a thread:

  • when it is new (e.g. during a function call before the push);

  • as part of a trace-back (in an exception, say); or

  • as the state of a generator or coroutine.

The actions of the interpreter are, essentially, operations on the current frame, and a call creates a new frame to act upon, leaving interpreter state suspended in the calling frame. It is therefore attractive to identify interpreter actions as methods on the frame object, rather than as static methods following CPython.

The architectural question for Python compiled to JVM byte code will be the extent to which program state information should be in the JVM frame, and the variables it holds, rather than in the frame. Information in the frame is accessible to Python in tracebacks and debugging.