]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
This is my patch
authorMichael W. Hudson <mwh@python.net>
Thu, 15 Aug 2002 14:59:02 +0000 (14:59 +0000)
committerMichael W. Hudson <mwh@python.net>
Thu, 15 Aug 2002 14:59:02 +0000 (14:59 +0000)
[ 587993 ] SET_LINENO killer

Remove SET_LINENO.  Tracing is now supported by inspecting co_lnotab.

Many sundry changes to document and adapt to this change.

19 files changed:
Doc/lib/libdis.tex
Doc/lib/libtraceback.tex
Doc/tut/tut.tex
Doc/whatsnew/whatsnew23.tex
Include/opcode.h
Lib/dis.py
Lib/inspect.py
Lib/pdb.py
Lib/test/test_hotshot.py
Lib/traceback.py
Misc/NEWS
Modules/_hotshot.c
Objects/frameobject.c
Python/ceval.c
Python/compile.c
Python/frozen.c
Python/import.c
Python/traceback.c
Tools/scripts/trace.py

index 16b692d756916cca4497b93283003d3798fc8e3f..567c0eed945aca0f4ed77ddb093f6a85c382cacb 100644 (file)
@@ -23,17 +23,15 @@ the following command can be used to get the disassembly of
 
 \begin{verbatim}
 >>> dis.dis(myfunc)
-          0 SET_LINENO          1
-
-          3 SET_LINENO          2
-          6 LOAD_GLOBAL         0 (len)
-          9 LOAD_FAST           0 (alist)
-         12 CALL_FUNCTION       1
-         15 RETURN_VALUE   
-         16 LOAD_CONST          0 (None)
-         19 RETURN_VALUE   
+  2           0 LOAD_GLOBAL              0 (len)
+              3 LOAD_FAST                0 (alist)
+              6 CALL_FUNCTION            1
+              9 RETURN_VALUE        
+             10 RETURN_NONE
 \end{verbatim}
 
+(The ``2'' is a line number).
+
 The \module{dis} module defines the following functions and constants:
 
 \begin{funcdesc}{dis}{\optional{bytesource}}
@@ -56,6 +54,7 @@ Disassembles a code object, indicating the last instruction if \var{lasti}
 was provided.  The output is divided in the following columns:
 
 \begin{enumerate}
+\item the line number, for the first instruction of each line
 \item the current instruction, indicated as \samp{-->},
 \item a labelled instruction, indicated with \samp{>\code{>}},
 \item the address of the instruction,
@@ -402,6 +401,14 @@ is evaluated, the locals are passed to the class definition.
 Returns with TOS to the caller of the function.
 \end{opcodedesc}
 
+\begin{opcodedesc}{RETURN_NONE}{}
+Returns \constant{None} to the caller of the function.  This opcode is
+generated as the last opcode of every function and only then, for
+reasons to do with tracing support.  See the comments in the function
+\cfunction{maybe_call_line_trace} in \file{Python/ceval.c} for the
+gory details.  \versionadded{2.3}.
+\end{opcodedesc}
+
 \begin{opcodedesc}{YIELD_VALUE}{}
 Pops \code{TOS} and yields it from a generator.
 \end{opcodedesc}
@@ -621,7 +628,7 @@ free variable storage.
 \end{opcodedesc}
 
 \begin{opcodedesc}{SET_LINENO}{lineno}
-Sets the current line number to \var{lineno}.
+This opcode is obsolete.
 \end{opcodedesc}
 
 \begin{opcodedesc}{RAISE_VARARGS}{argc}
index 2b20b0c8a16a455c9002723911e040d8abb7e1dd..a74613bbcf762b3c82c8d9add7d4519def5bca37 100644 (file)
@@ -118,10 +118,10 @@ A shorthand for \code{format_list(extract_stack(\var{f}, \var{limit}))}.
 
 \begin{funcdesc}{tb_lineno}{tb}
 This function returns the current line number set in the traceback
-object.  This is normally the same as the \code{\var{tb}.tb_lineno}
-field of the object, but when optimization is used (the -O flag) this
-field is not updated correctly; this function calculates the correct
-value.
+object.  This function was necessary because in versions of Python
+prior to 2.3 when the \programopt{O} flag was passed to Python the
+\code{\var{tb}.tb_lineno} was not updated correctly.  This function
+has no use in versions past 2.3.
 \end{funcdesc}
 
 
index c6081c6629f6dd5460915f35fe57517a72a6c1c2..4bc571aabb46df0d52ca1e4a4b41c343bf925dd6 100644 (file)
@@ -2340,12 +2340,11 @@ Some tips for experts:
 
 \item
 When the Python interpreter is invoked with the \programopt{-O} flag,
-optimized code is generated and stored in \file{.pyo} files.
-The optimizer currently doesn't help much; it only removes
-\keyword{assert} statements and \code{SET_LINENO} instructions.
-When \programopt{-O} is used, \emph{all} bytecode is optimized;
-\code{.pyc} files are ignored and \code{.py} files are compiled to
-optimized bytecode.
+optimized code is generated and stored in \file{.pyo} files.  The
+optimizer currently doesn't help much; it only removes
+\keyword{assert} statements.  When \programopt{-O} is used, \emph{all}
+bytecode is optimized; \code{.pyc} files are ignored and \code{.py}
+files are compiled to optimized bytecode.
 
 \item
 Passing two \programopt{-O} flags to the Python interpreter
index 0d7d774ee18fb76587d6e2bf8de562cfa4bc8b56..f919d7ee518ceb8b248a5a087874f80f9f734f81 100644 (file)
@@ -658,7 +658,6 @@ In 2.3, you get this:
 
 \end{itemize}
 
-
 %======================================================================
 \section{New and Improved Modules}
 
@@ -987,9 +986,9 @@ allocate objects, and \cfunction{PyObject_GC_Del} to deallocate them.
 when running Python's \file{configure} script.  (Contributed by Ondrej
 Palkovsky.)
 
-\item The  \csimplemacro{DL_EXPORT} and \csimplemacro{DL_IMPORT} macros are now
-deprecated.  Initialization functions for Python extension modules
-should now be declared using the new macro
+\item The \csimplemacro{DL_EXPORT} and \csimplemacro{DL_IMPORT} macros
+are now deprecated.  Initialization functions for Python extension
+modules should now be declared using the new macro
 \csimplemacro{PyMODINIT_FUNC}, while the Python core will generally
 use the \csimplemacro{PyAPI_FUNC} and \csimplemacro{PyAPI_DATA}
 macros.
@@ -1076,6 +1075,29 @@ Finally, there are various miscellaneous fixes:
 \item The tools used to build the documentation now work under Cygwin
 as well as \UNIX.
 
+\item The \code{SET_LINENO} opcode has been removed.  Back in the
+mists of time, this opcode was needed to produce line numbers in
+tracebacks and support trace functions (for, e.g., \module{pdb}).
+Since Python 1.5, the line numbers in tracebacks have been computed
+using a different mechanism that works with ``python -O''.  For Python
+2.3 Michael Hudson implemented a similar scheme to determine when to
+call the trace function, removing the need for \code{SET_LINENO}
+entirely.
+
+Python code will be hard pushed to notice a difference from this
+change, apart from a slight speed up when python is run without
+\programopt{-O}.
+
+C extensions that access the \member{f_lineno} field of frame objects
+should instead call \code{PyCode_Addr2Line(f->f_code, f->f_lasti)}.
+This will have the added effect of making the code work as desired
+under ``python -O'' in earlier versions of Python.
+
+To make tracing work as expected, it was found necessary to add a new
+opcode, \cdata{RETURN_NONE}, to the VM.  If you want to know why, read
+the comments in the function \cfunction{maybe_call_line_trace} in
+\file{Python/ceval.c}.
+
 \end{itemize}
 
 
index 2e2024618f7a22780ead344fc474371ab76c494d..28d0ae43b169a00f8901e3530545af781af24b65 100644 (file)
@@ -71,6 +71,9 @@ extern "C" {
 #define INPLACE_OR     79
 #define BREAK_LOOP     80
 
+#define RETURN_NONE    81 /* *only* for function epilogues 
+                             -- see comments in 
+                             ceval.c:maybe_call_line_trace for why */
 #define LOAD_LOCALS    82
 #define RETURN_VALUE   83
 #define IMPORT_STAR    84
@@ -119,8 +122,6 @@ extern "C" {
 #define STORE_FAST     125     /* Local variable number */
 #define DELETE_FAST    126     /* Local variable number */
 
-#define SET_LINENO     127     /* Current line number */
-
 #define RAISE_VARARGS  130     /* Number of raise arguments (1, 2 or 3) */
 /* CALL_FUNCTION_XXX opcodes defined below depend on this definition */
 #define CALL_FUNCTION  131     /* #args + (#kwargs<<8) */
index 267409443447bba479827e69595a5f28a89a1e2a..a1cc215f9842e5f5d904fb44f3a6436a35404858 100644 (file)
@@ -55,6 +55,20 @@ def distb(tb=None):
 def disassemble(co, lasti=-1):
     """Disassemble a code object."""
     code = co.co_code
+
+    byte_increments = [ord(c) for c in co.co_lnotab[0::2]]
+    line_increments = [ord(c) for c in co.co_lnotab[1::2]]
+    table_length = len(byte_increments) # == len(line_increments)
+
+    lineno = co.co_firstlineno
+    table_index = 0
+    while (table_index < table_length
+           and byte_increments[table_index] == 0):
+        lineno += line_increments[table_index]
+        table_index += 1
+    addr = 0
+    line_incr = 0
+    
     labels = findlabels(code)
     n = len(code)
     i = 0
@@ -63,7 +77,23 @@ def disassemble(co, lasti=-1):
     while i < n:
         c = code[i]
         op = ord(c)
-        if op == SET_LINENO and i > 0: print # Extra blank line
+
+        if i >= addr:
+            lineno += line_incr
+            while table_index < table_length:
+                addr += byte_increments[table_index]
+                line_incr = line_increments[table_index]
+                table_index += 1
+                if line_incr:
+                    break
+            else:
+                addr = sys.maxint
+            if i > 0:
+                print
+            print "%3d"%lineno,
+        else:
+            print '   ',
+
         if i == lasti: print '-->',
         else: print '   ',
         if i in labels: print '>>',
@@ -224,6 +254,7 @@ def_op('INPLACE_XOR', 78)
 def_op('INPLACE_OR', 79)
 def_op('BREAK_LOOP', 80)
 
+def_op('RETURN_NONE', 81)
 def_op('LOAD_LOCALS', 82)
 def_op('RETURN_VALUE', 83)
 def_op('IMPORT_STAR', 84)
@@ -277,9 +308,6 @@ haslocal.append(125)
 def_op('DELETE_FAST', 126)      # Local variable number
 haslocal.append(126)
 
-def_op('SET_LINENO', 127)       # Current line number
-SET_LINENO = 127
-
 def_op('RAISE_VARARGS', 130)    # Number of raise arguments (1, 2, or 3)
 def_op('CALL_FUNCTION', 131)    # #args + (#kwargs << 8)
 def_op('MAKE_FUNCTION', 132)    # Number of args with default values
index be2da41559c1caed75c696968b69cc265c0cfece..96677b7a2369bcd08f28636e5f29d4c59feee8a1 100644 (file)
@@ -711,7 +711,7 @@ def getframeinfo(frame, context=1):
         raise TypeError, 'arg is not a frame or traceback object'
 
     filename = getsourcefile(frame) or getfile(frame)
-    lineno = getlineno(frame)
+    lineno = frame.f_lineno
     if context > 0:
         start = lineno - 1 - context//2
         try:
@@ -730,18 +730,8 @@ def getframeinfo(frame, context=1):
 
 def getlineno(frame):
     """Get the line number from a frame object, allowing for optimization."""
-    # Written by Marc-AndrĂ© Lemburg; revised by Jim Hugunin and Fredrik Lundh.
-    lineno = frame.f_lineno
-    code = frame.f_code
-    if hasattr(code, 'co_lnotab'):
-        table = code.co_lnotab
-        lineno = code.co_firstlineno
-        addr = 0
-        for i in range(0, len(table), 2):
-            addr = addr + ord(table[i])
-            if addr > frame.f_lasti: break
-            lineno = lineno + ord(table[i+1])
-    return lineno
+    # FrameType.f_lineno is now a descriptor that grovels co_lnotab
+    return frame.f_lineno
 
 def getouterframes(frame, context=1):
     """Get a list of records for a frame and all higher (calling) frames.
index 3786ed97b5ef5a8332168a7216e02f8753279c21..c6164ad08ac8fa1df41d2ff1512bded815555e65 100755 (executable)
@@ -105,7 +105,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
                 if len(line) > 0 and line[0] != '#':
                     self.onecmd(line)
 
-    # Override Bdb methods (except user_call, for now)
+    # Override Bdb methods 
+
+    def user_call(self, frame, argument_list):
+        """This method is called when there is the remote possibility
+        that we ever need to stop in this function."""
+        print '--Call--'
+        self.interaction(frame, None)
 
     def user_line(self, frame):
         """This function is called when we stop or break at this line."""
index dad2bd4f2ceaa3526db38745d0655aeb09e674c1..d410099b9e58418c9b53ffb30ab43917af29e31c 100644 (file)
@@ -91,10 +91,8 @@ class HotShotTestCase(unittest.TestCase):
         f_lineno = f.func_code.co_firstlineno
         g_lineno = g.func_code.co_firstlineno
         events = [(ENTER, ("test_hotshot", g_lineno, "g")),
-                  (LINE,  ("test_hotshot", g_lineno, "g")),
                   (LINE,  ("test_hotshot", g_lineno+1, "g")),
                   (ENTER, ("test_hotshot", f_lineno, "f")),
-                  (LINE,  ("test_hotshot", f_lineno, "f")),
                   (LINE,  ("test_hotshot", f_lineno+1, "f")),
                   (LINE,  ("test_hotshot", f_lineno+2, "f")),
                   (EXIT,  ("test_hotshot", f_lineno, "f")),
index c22f576798e059df2de8fd9d546cbeff87ef8ee7..4910a37cb06d23890ffe0e9397f1531ddd75be1f 100644 (file)
@@ -59,7 +59,7 @@ def print_tb(tb, limit=None, file=None):
     n = 0
     while tb is not None and (limit is None or n < limit):
         f = tb.tb_frame
-        lineno = tb_lineno(tb)
+        lineno = tb.tb_lineno
         co = f.f_code
         filename = co.co_filename
         name = co.co_name
@@ -92,7 +92,7 @@ def extract_tb(tb, limit = None):
     n = 0
     while tb is not None and (limit is None or n < limit):
         f = tb.tb_frame
-        lineno = tb_lineno(tb)
+        lineno = tb.tb_lineno
         co = f.f_code
         filename = co.co_filename
         name = co.co_name
@@ -263,7 +263,7 @@ def extract_stack(f=None, limit = None):
     list = []
     n = 0
     while f is not None and (limit is None or n < limit):
-        lineno = f.f_lineno     # XXX Too bad if -O is used
+        lineno = f.f_lineno
         co = f.f_code
         filename = co.co_filename
         name = co.co_name
@@ -279,23 +279,6 @@ def extract_stack(f=None, limit = None):
 def tb_lineno(tb):
     """Calculate correct line number of traceback given in tb.
 
-    Even works with -O on.
+    Obsolete in 2.3.
     """
-    # Coded by Marc-Andre Lemburg from the example of PyCode_Addr2Line()
-    # in compile.c.
-    # Revised version by Jim Hugunin to work with JPython too.
-
-    c = tb.tb_frame.f_code
-    if not hasattr(c, 'co_lnotab'):
-        return tb.tb_lineno
-
-    tab = c.co_lnotab
-    line = c.co_firstlineno
-    stopat = tb.tb_lasti
-    addr = 0
-    for i in range(0, len(tab), 2):
-        addr = addr + ord(tab[i])
-        if addr > stopat:
-            break
-        line = line + ord(tab[i+1])
-    return line
+    return tb.tb_lineno
index 5abfb42ee3a840450df3efc6db667b7120a5f5b4..2c9d83f40dbb3d8066e08526ff30a25f4f2bed15 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -57,6 +57,11 @@ Type/class unification and new-style classes
 
 Core and builtins
 
+- SET_LINENO is gone.  co_lnotab is now consulted to determine when to
+  call the trace function.  C code that accessed f_lineno should call
+  PyCode_Addr2Line instead (f_lineno is still there, but not kept up
+  to date).
+
 - There's a new warning category, FutureWarning.  This is used to warn
   about a number of situations where the value or sign of an integer
   result will change in Python 2.4 as a result of PEP 237 (integer
index 1e8f4f9f419a186388e84d8a03912dfd52ef8ab1..2ca448620ee5be376faba1c62e9c84c76ece0e87 100644 (file)
@@ -152,7 +152,7 @@ logreader_tp_iter(LogReaderObject *self)
  * Low bits:    Opcode:        Meaning:
  *       0x00         ENTER     enter a frame
  *       0x01          EXIT     exit a frame
- *       0x02        LINENO     SET_LINENO instruction was executed
+ *       0x02        LINENO     execution moved onto a different line
  *       0x03         OTHER     more bits are needed to deecode
  *
  * If the type is OTHER, the record is not packed so tightly, and the
@@ -888,9 +888,12 @@ tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what,
 
     case PyTrace_LINE:
         if (self->linetimings)
-            return pack_lineno_tdelta(self, frame->f_lineno, get_tdelta(self));
+            return pack_lineno_tdelta(self, PyCode_Addr2Line(frame->f_code, 
+                                                            frame->f_lasti),
+                                     get_tdelta(self));
         else
-            return pack_lineno(self, frame->f_lineno);
+            return pack_lineno(self, PyCode_Addr2Line(frame->f_code,
+                                                     frame->f_lasti));
 
     default:
         /* ignore PyTrace_EXCEPTION */
@@ -1227,8 +1230,8 @@ PyDoc_STRVAR(profiler_object__doc__,
 "\n"
 "closed:       True if the profiler has already been closed.\n"
 "frametimings: True if ENTER/EXIT events collect timing information.\n"
-"lineevents:   True if SET_LINENO events are reported to the profiler.\n"
-"linetimings:  True if SET_LINENO events collect timing information.");
+"lineevents:   True if line events are reported to the profiler.\n"
+"linetimings:  True if line events collect timing information.");
 
 static PyTypeObject ProfilerType = {
     PyObject_HEAD_INIT(NULL)
index dfb3a2ec532bf3e4ee31cafab726378a42a94d0c..7b6ea26488c8b4016d6318eea3a8e4f3f15a2ff6 100644 (file)
@@ -16,7 +16,6 @@ static PyMemberDef frame_memberlist[] = {
        {"f_builtins",  T_OBJECT,       OFF(f_builtins),RO},
        {"f_globals",   T_OBJECT,       OFF(f_globals), RO},
        {"f_lasti",     T_INT,          OFF(f_lasti),   RO},
-       {"f_lineno",    T_INT,          OFF(f_lineno),  RO},
        {"f_restricted",T_INT,          OFF(f_restricted),RO},
        {"f_trace",     T_OBJECT,       OFF(f_trace)},
        {"f_exc_type",  T_OBJECT,       OFF(f_exc_type)},
@@ -33,8 +32,19 @@ frame_getlocals(PyFrameObject *f, void *closure)
        return f->f_locals;
 }
 
+static PyObject *
+frame_getlineno(PyFrameObject *f, void *closure)
+{
+       int lineno;
+
+       lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
+
+       return PyInt_FromLong(lineno);
+}
+
 static PyGetSetDef frame_getsetlist[] = {
        {"f_locals",    (getter)frame_getlocals, NULL, NULL},
+       {"f_lineno",    (getter)frame_getlineno, NULL, NULL},
        {0}
 };
 
@@ -306,7 +316,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
        f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL;
        f->f_tstate = tstate;
 
-       f->f_lasti = 0;
+       f->f_lasti = -1;
        f->f_lineno = code->co_firstlineno;
        f->f_restricted = (builtins != tstate->interp->builtins);
        f->f_iblock = 0;
index 50ea9c3ff03fe7020d1339ae97400d0e1a63a9cc..6985846cc6f43f8023329aced51cec52c7d5fffd 100644 (file)
@@ -51,6 +51,9 @@ static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
 static void call_trace_protected(Py_tracefunc, PyObject *,
                                 PyFrameObject *, int);
 static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *);
+static void maybe_call_line_trace(int, Py_tracefunc, PyObject *, 
+                                 PyFrameObject *, int *, int *);
+
 static PyObject *apply_slice(PyObject *, PyObject *, PyObject *);
 static int assign_slice(PyObject *, PyObject *,
                        PyObject *, PyObject *);
@@ -499,6 +502,16 @@ eval_frame(PyFrameObject *f)
        PyObject *retval = NULL;        /* Return value */
        PyThreadState *tstate = PyThreadState_GET();
        PyCodeObject *co;
+
+       /* when tracing we set things up so that 
+
+               not (instr_lb <= current_bytecode_offset < instr_ub)
+
+          is true when the line being executed has changed.  The 
+           initial values are such as to make this false the first
+           time it is tested. */
+       int instr_ub = -1, instr_lb = 0;
+
        unsigned char *first_instr;
        PyObject *names;
        PyObject *consts;
@@ -586,7 +599,12 @@ eval_frame(PyFrameObject *f)
        fastlocals = f->f_localsplus;
        freevars = f->f_localsplus + f->f_nlocals;
        _PyCode_GETCODEPTR(co, &first_instr);
-       next_instr = first_instr + f->f_lasti;
+       if (f->f_lasti < 0) {
+               next_instr = first_instr;
+       }
+       else {
+               next_instr = first_instr + f->f_lasti;
+       }
        stack_pointer = f->f_stacktop;
        assert(stack_pointer != NULL);
        f->f_stacktop = NULL;   /* remains NULL unless yield suspends frame */
@@ -637,8 +655,9 @@ eval_frame(PyFrameObject *f)
        w = NULL;
 
        for (;;) {
-               assert(stack_pointer >= f->f_valuestack);       /* else underflow */
-               assert(STACK_LEVEL() <= f->f_stacksize);        /* else overflow */
+               assert(stack_pointer >= f->f_valuestack); /* else underflow */
+               assert(STACK_LEVEL() <= f->f_stacksize);  /* else overflow */
+
                /* Do periodic things.  Doing this every time through
                   the loop would add too much overhead, so we do it
                   only every Nth instruction.  We also do it if
@@ -658,8 +677,8 @@ eval_frame(PyFrameObject *f)
 #if !defined(HAVE_SIGNAL_H) || defined(macintosh)
                        /* If we have true signals, the signal handler
                           will call Py_AddPendingCall() so we don't
-                          have to call sigcheck().  On the Mac and
-                          DOS, alas, we have to call it. */
+                          have to call PyErr_CheckSignals().  On the 
+                          Mac and DOS, alas, we have to call it. */
                        if (PyErr_CheckSignals()) {
                                why = WHY_EXCEPTION;
                                goto on_error;
@@ -686,9 +705,7 @@ eval_frame(PyFrameObject *f)
        fast_next_opcode:
                /* Extract opcode and argument */
 
-#if defined(Py_DEBUG) || defined(LLTRACE)
                f->f_lasti = INSTR_OFFSET();
-#endif
 
                opcode = NEXTOP();
                if (HAS_ARG(opcode))
@@ -708,15 +725,26 @@ eval_frame(PyFrameObject *f)
                if (lltrace) {
                        if (HAS_ARG(opcode)) {
                                printf("%d: %d, %d\n",
-                                       (int) (INSTR_OFFSET() - 3),
-                                       opcode, oparg);
+                                      f->f_lasti, opcode, oparg);
                        }
                        else {
                                printf("%d: %d\n",
-                                       (int) (INSTR_OFFSET() - 1), opcode);
+                                      f->f_lasti, opcode);
                        }
                }
 #endif
+
+               /* line-by-line tracing support */
+
+               if (tstate->c_tracefunc != NULL && !tstate->tracing) {
+                       /* see maybe_call_line_trace
+                          for expository comments */
+                       maybe_call_line_trace(opcode, 
+                                             tstate->c_tracefunc,
+                                             tstate->c_traceobj,
+                                             f, &instr_lb, &instr_ub);
+               }
+
                /* Main switch on opcode */
 
                switch (opcode) {
@@ -728,26 +756,6 @@ eval_frame(PyFrameObject *f)
 
                /* case STOP_CODE: this is an error! */
 
-               case SET_LINENO:
-#ifdef LLTRACE
-                       if (lltrace)
-                               printf("--- %s:%d \n", filename, oparg);
-#endif
-                       f->f_lineno = oparg;
-                       if (tstate->c_tracefunc == NULL || tstate->tracing)
-                               goto fast_next_opcode;
-                       /* Trace each line of code reached */
-                       f->f_lasti = INSTR_OFFSET();
-                       /* Inline call_trace() for performance: */
-                       tstate->tracing++;
-                       tstate->use_tracing = 0;
-                       err = (tstate->c_tracefunc)(tstate->c_traceobj, f,
-                                                   PyTrace_LINE, Py_None);
-                       tstate->use_tracing = (tstate->c_tracefunc
-                                              || tstate->c_profilefunc);
-                       tstate->tracing--;
-                       break;
-
                case LOAD_FAST:
                        x = GETLOCAL(oparg);
                        if (x != NULL) {
@@ -1504,9 +1512,17 @@ eval_frame(PyFrameObject *f)
                        why = WHY_RETURN;
                        break;
 
+               case RETURN_NONE:
+                       retval = Py_None;
+                       Py_INCREF(retval);
+                       why = WHY_RETURN;
+                       break;
+
                case YIELD_VALUE:
                        retval = POP();
                        f->f_stacktop = stack_pointer;
+                       /* abuse the lasti field: here it points to 
+                          the *next* instruction */
                        f->f_lasti = INSTR_OFFSET();
                        why = WHY_YIELD;
                        break;
@@ -1954,7 +1970,6 @@ eval_frame(PyFrameObject *f)
                    int n = na + 2 * nk;
                    PyObject **pfunc = stack_pointer - n - 1;
                    PyObject *func = *pfunc;
-                   f->f_lasti = INSTR_OFFSET() - 3; /* For tracing */
 
                    /* Always dispatch PyCFunction first, because
                       these are presumed to be the most frequent
@@ -2022,7 +2037,6 @@ eval_frame(PyFrameObject *f)
                            n++;
                    pfunc = stack_pointer - n - 1;
                    func = *pfunc;
-                   f->f_lasti = INSTR_OFFSET() - 3; /* For tracing */
 
                    if (PyMethod_Check(func)
                        && PyMethod_GET_SELF(func) != NULL) {
@@ -2134,7 +2148,8 @@ eval_frame(PyFrameObject *f)
                default:
                        fprintf(stderr,
                                "XXX lineno: %d, opcode: %d\n",
-                               f->f_lineno, opcode);
+                               PyCode_Addr2Line(f->f_code, f->f_lasti),
+                               opcode);
                        PyErr_SetString(PyExc_SystemError, "unknown opcode");
                        why = WHY_EXCEPTION;
                        break;
@@ -2189,9 +2204,6 @@ eval_frame(PyFrameObject *f)
                /* Log traceback info if this is a real exception */
 
                if (why == WHY_EXCEPTION) {
-                       f->f_lasti = INSTR_OFFSET() - 1;
-                       if (HAS_ARG(opcode))
-                               f->f_lasti -= 2;
                        PyTraceBack_Here(f);
 
                        if (tstate->c_tracefunc != NULL)
@@ -2875,6 +2887,125 @@ call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
        return result;
 }
 
+static void
+maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj, 
+                     PyFrameObject *frame, int *instr_lb, int *instr_ub)
+{
+       /* The theory of SET_LINENO-less tracing.
+          
+          In a nutshell, we use the co_lnotab field of the code object
+          to tell when execution has moved onto a different line.
+
+          As mentioned above, the basic idea is so set things up so
+          that
+
+                *instr_lb <= frame->f_lasti < *instr_ub
+
+          is true so long as execution does not change lines.
+
+          This is all fairly simple.  Digging the information out of
+          co_lnotab takes some work, but is conceptually clear.
+
+          Somewhat harder to explain is why we don't call the line
+          trace function when executing a POP_TOP or RETURN_NONE
+          opcodes.  An example probably serves best.
+
+          Consider this code:
+
+          1: def f(a):
+          2:     if a:
+          3:        print 1
+          4:     else:
+          5:        print 2
+
+          which compiles to this:
+
+          2           0 LOAD_FAST                0 (a)
+                      3 JUMP_IF_FALSE            9 (to 15)
+                      6 POP_TOP             
+
+          3           7 LOAD_CONST               1 (1)
+                     10 PRINT_ITEM          
+                     11 PRINT_NEWLINE       
+                     12 JUMP_FORWARD             6 (to 21)
+                >>   15 POP_TOP             
+
+          5          16 LOAD_CONST               2 (2)
+                     19 PRINT_ITEM          
+                     20 PRINT_NEWLINE       
+                >>   21 RETURN_NONE         
+
+          If a is false, execution will jump to instruction at offset
+          15 and the co_lnotab will claim that execution has moved to
+          line 3.  This is at best misleading.  In this case we could
+          associate the POP_TOP with line 4, but that doesn't make
+          sense in all cases (I think).
+
+          On the other hand, if a is true, execution will jump from
+          instruction offset 12 to offset 21.  Then the co_lnotab would
+          imply that execution has moved to line 5, which is again
+          misleading.
+
+          This is why it is important that RETURN_NONE is *only* used
+          for the "falling off the end of the function" form of
+          returning None -- using it for code like
+
+          1: def f():
+          2:     return
+
+          would, once again, lead to misleading tracing behaviour.
+
+          It is also worth mentioning that getting tracing behaviour
+          right is the *entire* motivation for adding the RETURN_NONE
+          opcode.
+       */
+
+       if (opcode != POP_TOP && opcode != RETURN_NONE &&
+           (frame->f_lasti < *instr_lb || frame->f_lasti > *instr_ub)) {
+               PyCodeObject* co = frame->f_code;
+               int size, addr;
+               unsigned char* p;
+
+               call_trace(func, obj, frame, PyTrace_LINE, Py_None);
+
+               size = PyString_Size(co->co_lnotab) / 2;
+               p = (unsigned char*)PyString_AsString(co->co_lnotab);
+
+               /* possible optimization: if f->f_lasti == instr_ub
+                  (likely to be a common case) then we already know
+                  instr_lb -- if we stored the matching value of p
+                  somwhere we could skip the first while loop. */
+
+               addr = 0;
+
+               /* see comments in compile.c for the description of
+                  co_lnotab.  A point to remember: increments to p
+                  should come in pairs -- although we don't care about
+                  the line increments here, treating them as byte
+                  increments gets confusing, to say the least. */
+
+               while (size >= 0) {
+                       if (addr + *p > frame->f_lasti)
+                               break;
+                       addr += *p++;
+                       p++;
+                       --size;
+               }
+               *instr_lb = addr;
+               if (size > 0) {
+                       while (--size >= 0) {
+                               addr += *p++;
+                               if (*p++)
+                                       break;
+                       }
+                       *instr_ub = addr;
+               }
+               else {
+                       *instr_ub = INT_MAX;
+               }
+       }
+}
+
 void
 PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
 {
index 5b4e8e6420208ed79443361985212268fe624779..ff8f4a56dcaa4c02687c70f7190b4e596156da9c 100644 (file)
@@ -407,9 +407,10 @@ PyCode_New(int argcount, int nlocals, int stacksize, int flags,
 
 /* All about c_lnotab.
 
-c_lnotab is an array of unsigned bytes disguised as a Python string.  In -O
-mode, SET_LINENO opcodes aren't generated, and bytecode offsets are mapped
-to source code line #s (when needed for tracebacks) via c_lnotab instead.
+c_lnotab is an array of unsigned bytes disguised as a Python string.  Since
+version 2.3, SET_LINENO opcodes are never generated and bytecode offsets are
+mapped to source code line #s via c_lnotab instead.
+
 The array is conceptually a list of
     (bytecode offset increment, line number increment)
 pairs.  The details are important and delicate, best illustrated by example:
@@ -830,11 +831,6 @@ static void
 com_addoparg(struct compiling *c, int op, int arg)
 {
        int extended_arg = arg >> 16;
-       if (op == SET_LINENO) {
-               com_set_lineno(c, arg);
-               if (Py_OptimizeFlag)
-                       return;
-       }
        if (extended_arg){
                com_addbyte(c, EXTENDED_ARG);
                com_addint(c, extended_arg);
@@ -1738,7 +1734,7 @@ com_call_function(struct compiling *c, node *n)
                          break;
                        if (ch->n_lineno != lineno) {
                                lineno = ch->n_lineno;
-                               com_addoparg(c, SET_LINENO, lineno);
+                               com_set_lineno(c, lineno);
                        }
                        com_argument(c, ch, &keywords);
                        if (keywords == NULL)
@@ -3168,7 +3164,7 @@ com_if_stmt(struct compiling *c, node *n)
                        continue;
                }
                if (i > 0)
-                       com_addoparg(c, SET_LINENO, ch->n_lineno);
+                       com_set_lineno(c, ch->n_lineno);
                com_node(c, ch);
                com_addfwref(c, JUMP_IF_FALSE, &a);
                com_addbyte(c, POP_TOP);
@@ -3195,7 +3191,7 @@ com_while_stmt(struct compiling *c, node *n)
        com_addfwref(c, SETUP_LOOP, &break_anchor);
        block_push(c, SETUP_LOOP);
        c->c_begin = c->c_nexti;
-       com_addoparg(c, SET_LINENO, n->n_lineno);
+       com_set_lineno(c, n->n_lineno);
        com_node(c, CHILD(n, 1));
        com_addfwref(c, JUMP_IF_FALSE, &anchor);
        com_addbyte(c, POP_TOP);
@@ -3228,7 +3224,7 @@ com_for_stmt(struct compiling *c, node *n)
        com_node(c, CHILD(n, 3));
        com_addbyte(c, GET_ITER);
        c->c_begin = c->c_nexti;
-       com_addoparg(c, SET_LINENO, n->n_lineno);
+       com_set_lineno(c, n->n_lineno);
        com_addfwref(c, FOR_ITER, &anchor);
        com_push(c, 1);
        com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL);
@@ -3339,7 +3335,7 @@ com_try_except(struct compiling *c, node *n)
                }
                except_anchor = 0;
                com_push(c, 3); /* tb, val, exc pushed by exception */
-               com_addoparg(c, SET_LINENO, ch->n_lineno);
+               com_set_lineno(c, ch->n_lineno);
                if (NCH(ch) > 1) {
                        com_addbyte(c, DUP_TOP);
                        com_push(c, 1);
@@ -3401,7 +3397,7 @@ com_try_finally(struct compiling *c, node *n)
        com_push(c, 3);
        com_backpatch(c, finally_anchor);
        ch = CHILD(n, NCH(n)-1);
-       com_addoparg(c, SET_LINENO, ch->n_lineno);
+       com_set_lineno(c, ch->n_lineno);
        com_node(c, ch);
        com_addbyte(c, END_FINALLY);
        block_pop(c, END_FINALLY);
@@ -3727,7 +3723,7 @@ com_node(struct compiling *c, node *n)
 
        case simple_stmt:
                /* small_stmt (';' small_stmt)* [';'] NEWLINE */
-               com_addoparg(c, SET_LINENO, n->n_lineno);
+               com_set_lineno(c, n->n_lineno);
                {
                        int i;
                        for (i = 0; i < NCH(n)-1; i += 2)
@@ -3736,7 +3732,7 @@ com_node(struct compiling *c, node *n)
                break;
        
        case compound_stmt:
-               com_addoparg(c, SET_LINENO, n->n_lineno);
+               com_set_lineno(c, n->n_lineno);
                n = CHILD(n, 0);
                goto loop;
 
@@ -3990,10 +3986,7 @@ compile_funcdef(struct compiling *c, node *n)
        c->c_infunction = 1;
        com_node(c, CHILD(n, 4));
        c->c_infunction = 0;
-       com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None));
-       com_push(c, 1);
-       com_addbyte(c, RETURN_VALUE);
-       com_pop(c, 1);
+       com_addbyte(c, RETURN_NONE);
 }
 
 static void
@@ -4050,7 +4043,7 @@ compile_classdef(struct compiling *c, node *n)
 static void
 compile_node(struct compiling *c, node *n)
 {
-       com_addoparg(c, SET_LINENO, n->n_lineno);
+       com_set_lineno(c, n->n_lineno);
        
        switch (TYPE(n)) {
        
@@ -4060,19 +4053,13 @@ compile_node(struct compiling *c, node *n)
                n = CHILD(n, 0);
                if (TYPE(n) != NEWLINE)
                        com_node(c, n);
-               com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None));
-               com_push(c, 1);
-               com_addbyte(c, RETURN_VALUE);
-               com_pop(c, 1);
+               com_addbyte(c, RETURN_NONE);
                c->c_interactive--;
                break;
        
        case file_input: /* A whole file, or built-in function exec() */
                com_file_input(c, n);
-               com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None));
-               com_push(c, 1);
-               com_addbyte(c, RETURN_VALUE);
-               com_pop(c, 1);
+               com_addbyte(c, RETURN_NONE);
                break;
        
        case eval_input: /* Built-in function input() */
index 069ce3cec92173dc393ab41ac45cfac0af94046a..946d626bd5714baa00add145f97d45071b6da05f 100644 (file)
 
 static unsigned char M___hello__[] = {
        99,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
-       0,115,15,0,0,0,127,0,0,127,1,0,100,0,0,71,
-       72,100,1,0,83,40,2,0,0,0,115,14,0,0,0,72,
-       101,108,108,111,32,119,111,114,108,100,46,46,46,78,40,0,
-       0,0,0,40,0,0,0,0,40,0,0,0,0,40,0,0,
-       0,0,115,8,0,0,0,104,101,108,108,111,46,112,121,115,
-       1,0,0,0,63,1,0,0,0,115,0,0,0,0,
+       0,115,9,0,0,0,100,0,0,71,72,100,1,0,83,40,
+       2,0,0,0,115,14,0,0,0,72,101,108,108,111,32,119,
+       111,114,108,100,46,46,46,78,40,0,0,0,0,40,0,0,
+       0,0,40,0,0,0,0,40,0,0,0,0,115,8,0,0,
+       0,104,101,108,108,111,46,112,121,115,1,0,0,0,63,1,
+       0,0,0,115,0,0,0,0,
 };
 
 #define SIZE (int)sizeof(M___hello__)
index 6fd05d8786d68de5f0076b49672ecebc3325b013..36ca7057dd154194248435b5869b6523b06ab6af 100644 (file)
@@ -49,6 +49,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
        algorithm relying on the above scheme. Perhaps we should simply
        start counting in increments of 10 from now on ?!
 
+       MWH, 2002-08-03: Removed SET_LINENO.  Couldn't be bothered figuring
+       out the MAGIC schemes, so just incremented it by 10.
+
    Known values:
        Python 1.5:   20121
        Python 1.5.1: 20121
@@ -60,8 +63,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
        Python 2.1.2: 60202
        Python 2.2:   60717
        Python 2.3a0: 62011
+       Python 2.3a0: 62021
 */
-#define MAGIC (62011 | ((long)'\r'<<16) | ((long)'\n'<<24))
+#define MAGIC (62021 | ((long)'\r'<<16) | ((long)'\n'<<24))
 
 /* Magic word as global; note that _PyImport_Init() can change the
    value of this global to accommodate for alterations of how the
index de918f9a203e6304ceca12bd9c628ab9564b51b3..27c69b68e5eab7264e203b9c8453b18c09b16314 100644 (file)
@@ -103,8 +103,7 @@ PyTypeObject PyTraceBack_Type = {
 };
 
 static tracebackobject *
-newtracebackobject(tracebackobject *next, PyFrameObject *frame, int lasti,
-                  int lineno)
+newtracebackobject(tracebackobject *next, PyFrameObject *frame)
 {
        tracebackobject *tb;
        if ((next != NULL && !PyTraceBack_Check(next)) ||
@@ -118,8 +117,9 @@ newtracebackobject(tracebackobject *next, PyFrameObject *frame, int lasti,
                tb->tb_next = next;
                Py_XINCREF(frame);
                tb->tb_frame = frame;
-               tb->tb_lasti = lasti;
-               tb->tb_lineno = lineno;
+               tb->tb_lasti = frame->f_lasti;
+               tb->tb_lineno = PyCode_Addr2Line(frame->f_code, 
+                                                frame->f_lasti);
                PyObject_GC_Track(tb);
        }
        return tb;
@@ -130,8 +130,7 @@ PyTraceBack_Here(PyFrameObject *frame)
 {
        PyThreadState *tstate = frame->f_tstate;
        tracebackobject *oldtb = (tracebackobject *) tstate->curexc_traceback;
-       tracebackobject *tb = newtracebackobject(oldtb,
-                               frame, frame->f_lasti, frame->f_lineno);
+       tracebackobject *tb = newtracebackobject(oldtb, frame);
        if (tb == NULL)
                return -1;
        tstate->curexc_traceback = (PyObject *)tb;
index 9c4819f1a83bc813476b8fb1c4004d9613cee488..f96e04f50372fa66c7a3f03b1f252d501046cf48 100644 (file)
@@ -370,41 +370,29 @@ class CoverageResults:
             except IOError, err:
                 sys.stderr.write("cannot save counts files because %s" % err)
 
-# Given a code string, return the SET_LINENO information
-def _find_LINENO_from_string(co_code):
-    """return all of the SET_LINENO information from a code string"""
-    import dis
+def _find_LINENO_from_code(code):
+    """return the numbers of the lines containing the source code that
+    was compiled into code"""
     linenos = {}
 
-    # This code was filched from the `dis' module then modified
-    n = len(co_code)
-    i = 0
-    prev_op = None
-    prev_lineno = 0
-    while i < n:
-        c = co_code[i]
-        op = ord(c)
-        if op == dis.SET_LINENO:
-            if prev_op == op:
-                # two SET_LINENO in a row, so the previous didn't
-                # indicate anything.  This occurs with triple
-                # quoted strings (?).  Remove the old one.
-                del linenos[prev_lineno]
-            prev_lineno = ord(co_code[i+1]) + ord(co_code[i+2])*256
-            linenos[prev_lineno] = 1
-        if op >= dis.HAVE_ARGUMENT:
-            i = i + 3
-        else:
-            i = i + 1
-        prev_op = op
+    line_increments = [ord(c) for c in code.co_lnotab[1::2]]
+    table_length = len(line_increments)
+
+    lineno = code.co_first_lineno
+
+    for li in line_increments:
+        linenos[lineno] = 1
+        lineno += li
+    linenos[lineno] = 1
+
     return linenos
 
 def _find_LINENO(code):
-    """return all of the SET_LINENO information from a code object"""
+    """return all of the lineno information from a code object"""
     import types
 
     # get all of the lineno information from the code of this scope level
-    linenos = _find_LINENO_from_string(code.co_code)
+    linenos = _find_LINENO_from_code(code)
 
     # and check the constants for references to other code objects
     for c in code.co_consts:
@@ -416,9 +404,6 @@ def _find_LINENO(code):
 def find_executable_linenos(filename):
     """return a dict of the line numbers from executable statements in a file
 
-    Works by finding all of the code-like objects in the module then searching
-    the byte code for 'SET_LINENO' terms (so this won't work one -O files).
-
     """
     import parser
 
@@ -428,10 +413,6 @@ def find_executable_linenos(filename):
     ast = parser.suite(prog)
     code = parser.compileast(ast, filename)
 
-    # The only way I know to find line numbers is to look for the
-    # SET_LINENO instructions.  Isn't there some way to get it from
-    # the AST?
-
     return _find_LINENO(code)
 
 ### XXX because os.path.commonprefix seems broken by my way of thinking...