Commit 74f7ff8a authored by Marius Wachtler's avatar Marius Wachtler

tracebacks: implement PyFrame_New and PyCode_New / custom traceback entries from the CAPI

it only allows creating empty code objects but this is enough to allow ctypes and cython to create custom traceback entries.

It's not enough for removing the pyexpat and all cython code object workarounds (because they need 'real' code object / frames)
parent 98dfb53b
...@@ -74,7 +74,6 @@ ...@@ -74,7 +74,6 @@
#include "classobject.h" #include "classobject.h"
#include "cobject.h" #include "cobject.h"
#include "fileobject.h" #include "fileobject.h"
#include "frameobject.h"
#include "pycapsule.h" #include "pycapsule.h"
#include "traceback.h" #include "traceback.h"
#include "sliceobject.h" #include "sliceobject.h"
......
...@@ -58,12 +58,11 @@ PyAPI_DATA(PyTypeObject) PyFrame_Type; ...@@ -58,12 +58,11 @@ PyAPI_DATA(PyTypeObject) PyFrame_Type;
#define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type) #define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type)
#define PyFrame_IsRestricted(f) \ #define PyFrame_IsRestricted(f) \
((f)->f_builtins != (f)->f_tstate->interp->builtins) ((f)->f_builtins != (f)->f_tstate->interp->builtins)
PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
PyObject *, PyObject *); PyObject *, PyObject *);
/* The rest of the interface is specific for frame objects */ /* The rest of the interface is specific for frame objects */
/* Block management functions */ /* Block management functions */
...@@ -89,8 +88,12 @@ PyAPI_DATA(PyTypeObject*) frame_cls; ...@@ -89,8 +88,12 @@ PyAPI_DATA(PyTypeObject*) frame_cls;
#define PyFrame_Type (*frame_cls) #define PyFrame_Type (*frame_cls)
#define PyFrame_Check(op) (((PyObject*)op)->ob_type == &PyFrame_Type) #define PyFrame_Check(op) (((PyObject*)op)->ob_type == &PyFrame_Type)
PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *,
PyObject *, PyObject *) PYSTON_NOEXCEPT;
/* Return the line of code the frame is currently executing. */ /* Return the line of code the frame is currently executing. */
PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) PyFrame_SetLineNumber(PyFrameObject *, int line_number) PYSTON_NOEXCEPT;
// Pyston changes: add a function to get globals // Pyston changes: add a function to get globals
PyAPI_FUNC(PyObject *) PyFrame_GetGlobals(PyFrameObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(PyObject *) PyFrame_GetGlobals(PyFrameObject *) PYSTON_NOEXCEPT;
// Pyston changes: add a function to get the code object // Pyston changes: add a function to get the code object
......
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
#include "Python.h" #include "Python.h"
#include "compile.h" /* required only for 2.3, as it seems */ #include "compile.h" /* required only for 2.3, as it seems */
// Pyston change: We don't have this file and commented out the function that needs it, but #include "frameobject.h"
// we may want to support that function in the future.
//#include "frameobject.h"
#include <ffi.h> #include <ffi.h>
#ifdef MS_WIN32 #ifdef MS_WIN32
...@@ -152,9 +150,6 @@ failed: ...@@ -152,9 +150,6 @@ failed:
/* after code that pyrex generates */ /* after code that pyrex generates */
void _ctypes_add_traceback(char *funcname, char *filename, int lineno) void _ctypes_add_traceback(char *funcname, char *filename, int lineno)
{ {
// TODO: Pyston change:
// Supporting this will require frameobject.h
#if 0
PyObject *py_globals = 0; PyObject *py_globals = 0;
PyCodeObject *py_code = 0; PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0; PyFrameObject *py_frame = 0;
...@@ -170,15 +165,16 @@ void _ctypes_add_traceback(char *funcname, char *filename, int lineno) ...@@ -170,15 +165,16 @@ void _ctypes_add_traceback(char *funcname, char *filename, int lineno)
0 /*PyObject *locals*/ 0 /*PyObject *locals*/
); );
if (!py_frame) goto bad; if (!py_frame) goto bad;
py_frame->f_lineno = lineno;
// Pyston change:
// py_frame->f_lineno = lineno;
PyFrame_SetLineNumber(py_frame, lineno);
PyTraceBack_Here(py_frame); PyTraceBack_Here(py_frame);
bad: bad:
Py_XDECREF(py_globals); Py_XDECREF(py_globals);
Py_XDECREF(py_code); Py_XDECREF(py_code);
Py_XDECREF(py_frame); Py_XDECREF(py_frame);
#else
assert(false);
#endif
} }
#ifdef MS_WIN32 #ifdef MS_WIN32
......
...@@ -1656,7 +1656,10 @@ extern "C" void PyEval_ReleaseThread(PyThreadState* tstate) noexcept { ...@@ -1656,7 +1656,10 @@ extern "C" void PyEval_ReleaseThread(PyThreadState* tstate) noexcept {
} }
extern "C" PyThreadState* PyThreadState_Get(void) noexcept { extern "C" PyThreadState* PyThreadState_Get(void) noexcept {
Py_FatalError("Unimplemented"); if (_PyThreadState_Current == NULL)
Py_FatalError("PyThreadState_Get: no current thread");
return _PyThreadState_Current;
} }
extern "C" PyThreadState* PyEval_SaveThread(void) noexcept { extern "C" PyThreadState* PyEval_SaveThread(void) noexcept {
......
...@@ -29,16 +29,26 @@ BoxedClass* code_cls; ...@@ -29,16 +29,26 @@ BoxedClass* code_cls;
void BoxedCode::gcHandler(GCVisitor* v, Box* b) { void BoxedCode::gcHandler(GCVisitor* v, Box* b) {
assert(b->cls == code_cls); assert(b->cls == code_cls);
Box::gcHandler(v, b); Box::gcHandler(v, b);
BoxedCode* code = (BoxedCode*)b;
v->visit(&code->_filename);
v->visit(&code->_name);
} }
Box* BoxedCode::name(Box* b, void*) { Box* BoxedCode::name(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, ""); RELEASE_ASSERT(b->cls == code_cls, "");
return static_cast<BoxedCode*>(b)->f->source->getName(); BoxedCode* code = static_cast<BoxedCode*>(b);
if (code->_name)
return code->_name;
return code->f->source->getName();
} }
Box* BoxedCode::filename(Box* b, void*) { Box* BoxedCode::filename(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, ""); RELEASE_ASSERT(b->cls == code_cls, "");
return static_cast<BoxedCode*>(b)->f->source->getFn(); BoxedCode* code = static_cast<BoxedCode*>(b);
if (code->_filename)
return code->_filename;
return code->f->source->getFn();
} }
Box* BoxedCode::firstlineno(Box* b, void*) { Box* BoxedCode::firstlineno(Box* b, void*) {
...@@ -46,11 +56,8 @@ Box* BoxedCode::firstlineno(Box* b, void*) { ...@@ -46,11 +56,8 @@ Box* BoxedCode::firstlineno(Box* b, void*) {
BoxedCode* code = static_cast<BoxedCode*>(b); BoxedCode* code = static_cast<BoxedCode*>(b);
FunctionMetadata* md = code->f; FunctionMetadata* md = code->f;
if (!md->source) { if (!md || !md->source)
// I don't think it really matters what we return here; return boxInt(code->_firstline);
// in CPython, builtin functions don't have code objects.
return boxInt(-1);
}
if (md->source->ast->lineno == (uint32_t)-1) if (md->source->ast->lineno == (uint32_t)-1)
return boxInt(-1); return boxInt(-1);
...@@ -105,9 +112,68 @@ FunctionMetadata* metadataFromCode(Box* code) { ...@@ -105,9 +112,68 @@ FunctionMetadata* metadataFromCode(Box* code) {
return static_cast<BoxedCode*>(code)->f; return static_cast<BoxedCode*>(code)->f;
} }
extern "C" PyCodeObject* PyCode_New(int, int, int, int, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, extern "C" PyCodeObject* PyCode_New(int argcount, int nlocals, int stacksize, int flags, PyObject* code,
PyObject*, PyObject*, PyObject*, int, PyObject*) noexcept { PyObject* consts, PyObject* names, PyObject* varnames, PyObject* freevars,
RELEASE_ASSERT(0, "not implemented"); PyObject* cellvars, PyObject* filename, PyObject* name, int firstlineno,
PyObject* lnotab) noexcept {
// Check if this is a dummy code object like PyCode_NewEmpty generates.
// Because we currently support dummy ones only.
bool is_dummy = argcount == 0 && nlocals == 0 && stacksize == 0 && flags == 0;
is_dummy = is_dummy && code == EmptyString && lnotab == EmptyString;
for (auto&& var : { consts, names, varnames, freevars, cellvars })
is_dummy = is_dummy && var == EmptyTuple;
RELEASE_ASSERT(is_dummy, "not implemented");
// ok this is an empty/dummy code object
RELEASE_ASSERT(PyString_Check(filename), "");
RELEASE_ASSERT(PyString_Check(name), "");
return (PyCodeObject*)new BoxedCode(filename, name, firstlineno);
}
extern "C" PyCodeObject* PyCode_NewEmpty(const char* filename, const char* funcname, int firstlineno) noexcept {
static PyObject* emptystring = NULL;
static PyObject* nulltuple = NULL;
PyObject* filename_ob = NULL;
PyObject* funcname_ob = NULL;
PyCodeObject* result = NULL;
if (emptystring == NULL) {
emptystring = PyString_FromString("");
if (emptystring == NULL)
goto failed;
}
if (nulltuple == NULL) {
nulltuple = PyTuple_New(0);
if (nulltuple == NULL)
goto failed;
}
funcname_ob = PyString_FromString(funcname);
if (funcname_ob == NULL)
goto failed;
filename_ob = PyString_FromString(filename);
if (filename_ob == NULL)
goto failed;
result = PyCode_New(0, /* argcount */
0, /* nlocals */
0, /* stacksize */
0, /* flags */
emptystring, /* code */
nulltuple, /* consts */
nulltuple, /* names */
nulltuple, /* varnames */
nulltuple, /* freevars */
nulltuple, /* cellvars */
filename_ob, /* filename */
funcname_ob, /* name */
firstlineno, /* firstlineno */
emptystring /* lnotab */
);
failed:
Py_XDECREF(funcname_ob);
Py_XDECREF(filename_ob);
return result;
} }
extern "C" int PyCode_GetArgCount(PyCodeObject* op) noexcept { extern "C" int PyCode_GetArgCount(PyCodeObject* op) noexcept {
......
...@@ -24,8 +24,13 @@ namespace pyston { ...@@ -24,8 +24,13 @@ namespace pyston {
class BoxedCode : public Box { class BoxedCode : public Box {
public: public:
FunctionMetadata* f; FunctionMetadata* f;
Box* _filename;
Box* _name;
int _firstline;
BoxedCode(FunctionMetadata* f) : f(f) {} BoxedCode(FunctionMetadata* f) : f(f), _filename(NULL), _name(NULL), _firstline(-1) {}
BoxedCode(Box* filename, Box* name, int firstline)
: f(NULL), _filename(filename), _name(name), _firstline(firstline) {}
DEFAULT_CLASS(code_cls); DEFAULT_CLASS(code_cls);
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
// limitations under the License. // limitations under the License.
#include "Python.h" #include "Python.h"
#include "frameobject.h"
#include "pythread.h" #include "pythread.h"
#include "codegen/unwinding.h" #include "codegen/unwinding.h"
...@@ -33,9 +35,7 @@ class BoxedFrame : public Box { ...@@ -33,9 +35,7 @@ class BoxedFrame : public Box {
private: private:
// Call boxFrame to get a BoxedFrame object. // Call boxFrame to get a BoxedFrame object.
BoxedFrame(FrameInfo* frame_info) __attribute__((visibility("default"))) BoxedFrame(FrameInfo* frame_info) __attribute__((visibility("default")))
: frame_info(frame_info), _back(NULL), _code(NULL), _globals(NULL), _locals(NULL), _stmt(NULL) { : frame_info(frame_info), _back(NULL), _code(NULL), _globals(NULL), _locals(NULL), _linenumber(-1) {}
assert(frame_info);
}
public: public:
FrameInfo* frame_info; FrameInfo* frame_info;
...@@ -45,7 +45,7 @@ public: ...@@ -45,7 +45,7 @@ public:
Box* _globals; Box* _globals;
Box* _locals; Box* _locals;
AST_stmt* _stmt; int _linenumber;
bool hasExited() const { return frame_info == NULL; } bool hasExited() const { return frame_info == NULL; }
...@@ -132,7 +132,7 @@ public: ...@@ -132,7 +132,7 @@ public:
auto f = static_cast<BoxedFrame*>(obj); auto f = static_cast<BoxedFrame*>(obj);
if (f->hasExited()) if (f->hasExited())
return boxInt(f->_stmt->lineno); return boxInt(f->_linenumber);
AST_stmt* stmt = f->frame_info->stmt; AST_stmt* stmt = f->frame_info->stmt;
return boxInt(stmt->lineno); return boxInt(stmt->lineno);
...@@ -146,7 +146,7 @@ public: ...@@ -146,7 +146,7 @@ public:
_code = code(this, NULL); _code = code(this, NULL);
_globals = globals(this, NULL); _globals = globals(this, NULL);
_locals = locals(this, NULL); _locals = locals(this, NULL);
_stmt = frame_info->stmt; _linenumber = frame_info->stmt->lineno;
frame_info = NULL; // this means exited == true frame_info = NULL; // this means exited == true
assert(hasExited()); assert(hasExited());
...@@ -160,6 +160,16 @@ public: ...@@ -160,6 +160,16 @@ public:
assert(fi->frame_obj->cls == frame_cls); assert(fi->frame_obj->cls == frame_cls);
return fi->frame_obj; return fi->frame_obj;
} }
static Box* boxFrame(Box* back, BoxedCode* code, Box* globals, Box* locals) {
BoxedFrame* frame = new BoxedFrame(NULL);
frame->_back = back;
frame->_code = (Box*)code;
frame->_globals = globals;
frame->_locals = locals;
frame->_linenumber = -1;
return frame;
}
}; };
...@@ -196,6 +206,23 @@ extern "C" int PyFrame_GetLineNumber(PyFrameObject* _f) noexcept { ...@@ -196,6 +206,23 @@ extern "C" int PyFrame_GetLineNumber(PyFrameObject* _f) noexcept {
return lineno->n; return lineno->n;
} }
extern "C" void PyFrame_SetLineNumber(PyFrameObject* _f, int linenumber) noexcept {
BoxedFrame* f = (BoxedFrame*)_f;
RELEASE_ASSERT(f->hasExited(),
"if this frame did not exit yet the line number may get overwriten, may be a problem?");
f->_linenumber = linenumber;
}
extern "C" PyFrameObject* PyFrame_New(PyThreadState* tstate, PyCodeObject* code, PyObject* globals,
PyObject* locals) noexcept {
RELEASE_ASSERT(tstate == &cur_thread_state, "");
RELEASE_ASSERT(PyCode_Check((Box*)code), "");
RELEASE_ASSERT(!globals || PyDict_Check(globals) || globals->cls == attrwrapper_cls, "%s", globals->cls->tp_name);
RELEASE_ASSERT(!locals || PyDict_Check(locals), "%s", locals->cls->tp_name);
return (PyFrameObject*)BoxedFrame::boxFrame(getFrame(0), (BoxedCode*)code, globals, locals);
}
extern "C" PyObject* PyFrame_GetGlobals(PyFrameObject* f) noexcept { extern "C" PyObject* PyFrame_GetGlobals(PyFrameObject* f) noexcept {
return BoxedFrame::globals((Box*)f, NULL); return BoxedFrame::globals((Box*)f, NULL);
} }
......
...@@ -16,7 +16,7 @@ def install_and_test_lxml(): ...@@ -16,7 +16,7 @@ def install_and_test_lxml():
subprocess.check_call(["tar", "-zxf", "Cython-0.22.tar.gz"], cwd=SRC_DIR) subprocess.check_call(["tar", "-zxf", "Cython-0.22.tar.gz"], cwd=SRC_DIR)
CYTHON_DIR = os.path.abspath(os.path.join(SRC_DIR, "Cython-0.22")) CYTHON_DIR = os.path.abspath(os.path.join(SRC_DIR, "Cython-0.22"))
PATCH_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "integration", "Cython_0001-Pyston-change-we-don-t-support-custom-traceback-entr.patch")) PATCH_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "integration", "Cython-0.22.patch"))
subprocess.check_call(["patch", "-p1", "--input=" + PATCH_FILE], cwd=CYTHON_DIR) subprocess.check_call(["patch", "-p1", "--input=" + PATCH_FILE], cwd=CYTHON_DIR)
print "Applied Cython patch" print "Applied Cython patch"
subprocess.check_call([PYTHON_EXE, "setup.py", "install"], cwd=CYTHON_DIR) subprocess.check_call([PYTHON_EXE, "setup.py", "install"], cwd=CYTHON_DIR)
......
...@@ -74,27 +74,20 @@ index 9cc38f0..ab05ad1 100644 ...@@ -74,27 +74,20 @@ index 9cc38f0..ab05ad1 100644
// PyPy does not have this function // PyPy does not have this function
static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) {
diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c diff --git a/Cython/Utility/Exceptions.c b/Cython/Utility/Exceptions.c
index 354a776..8af3cb7 100644 index 354a776..8af3cb7 100644 static void __Pyx_AddTraceback(const char *funcname, int c_line,
--- a/Cython/Utility/Exceptions.c --- a/Cython/Utility/Exceptions.c
+++ b/Cython/Utility/Exceptions.c +++ b/Cython/Utility/Exceptions.c
@@ -450,7 +450,8 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line, @@ -528,7 +528,9 @@
/////////////// AddTraceback /////////////// 0 /*PyObject *locals*/
//@requires: ModuleSetupCode.c::CodeObjectCache );
//@substitute: naming if (!py_frame) goto bad;
- - py_frame->f_lineno = py_line;
+// Pyston change: We don't support custom traceback entries currently + // Pyston change:
+#if 0 + // py_frame->f_lineno = py_line;
#include "compile.h" + PyFrame_SetLineNumber(py_frame, py_line);
#include "frameobject.h" PyTraceBack_Here(py_frame);
#include "traceback.h" bad:
@@ -534,3 +535,7 @@ bad:
Py_XDECREF(py_code); Py_XDECREF(py_code);
Py_XDECREF(py_frame);
}
+#else
+static void __Pyx_AddTraceback(const char *funcname, int c_line, int py_line, const char *filename) {
+}
+#endif
diff --git a/Cython/Utility/Generator.c b/Cython/Utility/Generator.c diff --git a/Cython/Utility/Generator.c b/Cython/Utility/Generator.c
index 0310570..70e550c 100644 index 0310570..70e550c 100644
--- a/Cython/Utility/Generator.c --- a/Cython/Utility/Generator.c
...@@ -205,3 +198,4 @@ index 6477fb2..75dcdda 100644 ...@@ -205,3 +198,4 @@ index 6477fb2..75dcdda 100644
-- --
1.9.1 1.9.1
...@@ -69,7 +69,7 @@ if not os.path.exists(CYTHON_DIR): ...@@ -69,7 +69,7 @@ if not os.path.exists(CYTHON_DIR):
subprocess.check_call(["wget", url], cwd=SRC_DIR) subprocess.check_call(["wget", url], cwd=SRC_DIR)
subprocess.check_call(["tar", "-zxf", "Cython-0.22.tar.gz"], cwd=SRC_DIR) subprocess.check_call(["tar", "-zxf", "Cython-0.22.tar.gz"], cwd=SRC_DIR)
PATCH_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "Cython_0001-Pyston-change-we-don-t-support-custom-traceback-entr.patch")) PATCH_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "Cython-0.22.patch"))
subprocess.check_call(["patch", "-p1", "--input=" + PATCH_FILE], cwd=CYTHON_DIR) subprocess.check_call(["patch", "-p1", "--input=" + PATCH_FILE], cwd=CYTHON_DIR)
print ">>> Applied Cython patch" print ">>> Applied Cython patch"
......
from ctypes import *
libc = CDLL("libc.so.6")
qsort = libc.qsort
qsort.restype = None
CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
def py_cmp_func(a, b):
1/0
cmp_func = CMPFUNC(py_cmp_func)
IntArray3 = c_int * 3
ia = IntArray3(1, 2, 3)
qsort(ia, len(ia), sizeof(c_int), cmp_func)
print "finished"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment