Commit 925c13d5 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #559 from kmod/threading_local4

switch to CPython's thread._local implementation
parents 73140839 d48961f6
...@@ -64,6 +64,7 @@ file(GLOB_RECURSE STDMODULE_SRCS Modules ...@@ -64,6 +64,7 @@ file(GLOB_RECURSE STDMODULE_SRCS Modules
stringio.c stringio.c
stropmodule.c stropmodule.c
textio.c textio.c
threadmodule.c
timemodule.c timemodule.c
unicodedata.c unicodedata.c
util.c util.c
......
...@@ -14,8 +14,8 @@ extern "C" { ...@@ -14,8 +14,8 @@ extern "C" {
// Pyston change: this is no longer a static object // Pyston change: this is no longer a static object
//PyAPI_DATA(PyTypeObject) PyCFunction_Type; //PyAPI_DATA(PyTypeObject) PyCFunction_Type;
PyAPI_DATA(PyTypeObject*) builtin_function_or_method_cls; PyAPI_DATA(PyTypeObject*) capifunc_cls;
#define PyCFunction_Type (*builtin_function_or_method_cls) #define PyCFunction_Type (*capifunc_cls)
#define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type) #define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
...@@ -28,6 +28,8 @@ PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *) PYSTON_NOEXCEPT; ...@@ -28,6 +28,8 @@ PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *) PYSTON_NOEXCEPT;
PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *) PYSTON_NOEXCEPT;
// Pyston change: changed to function calls
#if 0
/* Macros for direct access to these values. Type checks are *not* /* Macros for direct access to these values. Type checks are *not*
done, so use with care. */ done, so use with care. */
#define PyCFunction_GET_FUNCTION(func) \ #define PyCFunction_GET_FUNCTION(func) \
...@@ -36,6 +38,13 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *) PYSTON_NOEXCEPT; ...@@ -36,6 +38,13 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *) PYSTON_NOEXCEPT;
(((PyCFunctionObject *)func) -> m_self) (((PyCFunctionObject *)func) -> m_self)
#define PyCFunction_GET_FLAGS(func) \ #define PyCFunction_GET_FLAGS(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags) (((PyCFunctionObject *)func) -> m_ml -> ml_flags)
#endif
#define PyCFunction_GET_FUNCTION(func) \
PyCFunction_GetFunction((PyObject*)(func))
#define PyCFunction_GET_SELF(func) \
PyCFunction_GetSelf((PyObject*)(func))
#define PyCFunction_GET_FLAGS(func) \
PyCFunction_GetFlags((PyObject*)(func))
PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *) PYSTON_NOEXCEPT;
struct PyMethodDef { struct PyMethodDef {
......
...@@ -244,14 +244,14 @@ ...@@ -244,14 +244,14 @@
#define HAVE_SYS_WAIT_H 1 #define HAVE_SYS_WAIT_H 1
#define HAVE_TCGETPGRP 1 #define HAVE_TCGETPGRP 1
#define HAVE_TCSETPGRP 1 #define HAVE_TCSETPGRP 1
#define HAVE_TEMPNAM 1 //#define HAVE_TEMPNAM 1 // pyston change: we have them but I dislike the compiler warnings they generate
#define HAVE_TERM_H 1 #define HAVE_TERM_H 1
#define HAVE_TERMIOS_H 1 #define HAVE_TERMIOS_H 1
#define HAVE_TGAMMA 1 #define HAVE_TGAMMA 1
#define HAVE_TIMEGM 1 #define HAVE_TIMEGM 1
#define HAVE_TIMES 1 #define HAVE_TIMES 1
#define HAVE_TMPFILE 1 #define HAVE_TMPFILE 1
#define HAVE_TMPNAM 1 //#define HAVE_TMPNAM 1 // pyston change: we have them but I dislike the compiler warnings they generate
#define HAVE_TMPNAM_R 1 #define HAVE_TMPNAM_R 1
#define HAVE_TM_ZONE 1 #define HAVE_TM_ZONE 1
#define HAVE_TRUNCATE 1 #define HAVE_TRUNCATE 1
......
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
#include "pythread.h" #include "pythread.h"
static PyObject *ThreadError; // Pyston change: we're only using part of this file
static PyObject *str_dict; static PyObject *str_dict;
#if 0
static PyObject *ThreadError;
static long nb_threads = 0; static long nb_threads = 0;
/* Lock objects */ /* Lock objects */
...@@ -173,6 +175,7 @@ newlockobject(void) ...@@ -173,6 +175,7 @@ newlockobject(void)
} }
return self; return self;
} }
#endif
/* Thread-local objects */ /* Thread-local objects */
...@@ -236,7 +239,7 @@ static PyTypeObject localdummytype = { ...@@ -236,7 +239,7 @@ static PyTypeObject localdummytype = {
/* tp_name */ "_thread._localdummy", /* tp_name */ "_thread._localdummy",
/* tp_basicsize */ sizeof(localdummyobject), /* tp_basicsize */ sizeof(localdummyobject),
/* tp_itemsize */ 0, /* tp_itemsize */ 0,
/* tp_dealloc */ (destructor)localdummy_dealloc, /* tp_dealloc */ (destructor)/*localdummy_dealloc*/ NULL,
/* tp_print */ 0, /* tp_print */ 0,
/* tp_getattr */ 0, /* tp_getattr */ 0,
/* tp_setattr */ 0, /* tp_setattr */ 0,
...@@ -389,6 +392,10 @@ local_traverse(localobject *self, visitproc visit, void *arg) ...@@ -389,6 +392,10 @@ local_traverse(localobject *self, visitproc visit, void *arg)
static int static int
local_clear(localobject *self) local_clear(localobject *self)
{ {
// Pyston change:
Py_FatalError("unexpected call to local_clear()");
abort();
#if 0
PyThreadState *tstate; PyThreadState *tstate;
Py_CLEAR(self->args); Py_CLEAR(self->args);
Py_CLEAR(self->kw); Py_CLEAR(self->kw);
...@@ -406,6 +413,7 @@ local_clear(localobject *self) ...@@ -406,6 +413,7 @@ local_clear(localobject *self)
PyDict_DelItem(tstate->dict, self->key); PyDict_DelItem(tstate->dict, self->key);
} }
return 0; return 0;
#endif
} }
static void static void
...@@ -490,7 +498,7 @@ static PyTypeObject localtype = { ...@@ -490,7 +498,7 @@ static PyTypeObject localtype = {
/* tp_name */ "thread._local", /* tp_name */ "thread._local",
/* tp_basicsize */ sizeof(localobject), /* tp_basicsize */ sizeof(localobject),
/* tp_itemsize */ 0, /* tp_itemsize */ 0,
/* tp_dealloc */ (destructor)local_dealloc, /* tp_dealloc */ (destructor)/*local_dealloc*/ NULL,
/* tp_print */ 0, /* tp_print */ 0,
/* tp_getattr */ 0, /* tp_getattr */ 0,
/* tp_setattr */ 0, /* tp_setattr */ 0,
...@@ -589,6 +597,8 @@ _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref) ...@@ -589,6 +597,8 @@ _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
// Pyston change:
#if 0
/* Module functions */ /* Module functions */
struct bootstate { struct bootstate {
...@@ -846,8 +856,11 @@ requiring allocation in multiples of the system memory page size\n\ ...@@ -846,8 +856,11 @@ requiring allocation in multiples of the system memory page size\n\
- platform documentation should be referred to for more information\n\ - platform documentation should be referred to for more information\n\
(4kB pages are common; using multiples of 4096 for the stack size is\n\ (4kB pages are common; using multiples of 4096 for the stack size is\n\
the suggested approach in the absence of more specific information)."); the suggested approach in the absence of more specific information).");
#endif
static PyMethodDef thread_methods[] = { static PyMethodDef thread_methods[] = {
// Pyston change:
#if 0
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS, METH_VARARGS,
start_new_doc}, start_new_doc},
...@@ -871,6 +884,7 @@ static PyMethodDef thread_methods[] = { ...@@ -871,6 +884,7 @@ static PyMethodDef thread_methods[] = {
{"stack_size", (PyCFunction)thread_stack_size, {"stack_size", (PyCFunction)thread_stack_size,
METH_VARARGS, METH_VARARGS,
stack_size_doc}, stack_size_doc},
#endif
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -909,6 +923,8 @@ initthread(void) ...@@ -909,6 +923,8 @@ initthread(void)
if (m == NULL) if (m == NULL)
return; return;
// Pyston change:
#if 0
/* Add a symbolic constant */ /* Add a symbolic constant */
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
ThreadError = PyGC_AddRoot(PyErr_NewException("thread.error", NULL, NULL)); ThreadError = PyGC_AddRoot(PyErr_NewException("thread.error", NULL, NULL));
...@@ -918,17 +934,18 @@ initthread(void) ...@@ -918,17 +934,18 @@ initthread(void)
return; return;
Py_INCREF(&Locktype); Py_INCREF(&Locktype);
PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype);
#endif
Py_INCREF(&localtype); Py_INCREF(&localtype);
if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0)
return; return;
nb_threads = 0; //nb_threads = 0; // pyston change
str_dict = PyGC_AddRoot(PyString_InternFromString("__dict__")); str_dict = PyGC_AddRoot(PyString_InternFromString("__dict__"));
if (str_dict == NULL) if (str_dict == NULL)
return; return;
/* Initialize the C thread library */ /* Initialize the C thread library */
PyThread_init_thread(); //PyThread_init_thread(); // pyston change
} }
...@@ -425,8 +425,7 @@ extern "C" PyObject* Py_InitModule4(const char* name, PyMethodDef* methods, cons ...@@ -425,8 +425,7 @@ extern "C" PyObject* Py_InitModule4(const char* name, PyMethodDef* methods, cons
while (methods && methods->ml_name) { while (methods && methods->ml_name) {
RELEASE_ASSERT((methods->ml_flags & (~(METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O))) == 0, "%d", RELEASE_ASSERT((methods->ml_flags & (~(METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O))) == 0, "%d",
methods->ml_flags); methods->ml_flags);
module->giveAttr(methods->ml_name, new BoxedCApiFunction(methods->ml_flags, passthrough, methods->ml_name, module->giveAttr(methods->ml_name, new BoxedCApiFunction(methods, passthrough, boxString(name)));
methods->ml_meth, boxString(name)));
methods++; methods++;
} }
......
...@@ -27,6 +27,8 @@ static const std::string _getattr_str("__getattr__"); ...@@ -27,6 +27,8 @@ static const std::string _getattr_str("__getattr__");
static const std::string _getattribute_str("__getattribute__"); static const std::string _getattribute_str("__getattribute__");
typedef int (*update_callback)(PyTypeObject*, void*); typedef int (*update_callback)(PyTypeObject*, void*);
static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) noexcept;
extern "C" void conservativeGCHandler(GCVisitor* v, Box* b) noexcept { extern "C" void conservativeGCHandler(GCVisitor* v, Box* b) noexcept {
v->visitPotentialRange((void* const*)b, (void* const*)((char*)b + b->cls->tp_basicsize)); v->visitPotentialRange((void* const*)b, (void* const*)((char*)b + b->cls->tp_basicsize));
} }
...@@ -1591,8 +1593,6 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce ...@@ -1591,8 +1593,6 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce
else else
use_generic = 1; use_generic = 1;
} }
// TODO Pyston doesn't support PyCFunction_Type yet I think?
#if 0
} else if (Py_TYPE(descr) == &PyCFunction_Type && PyCFunction_GET_FUNCTION(descr) == (PyCFunction)tp_new_wrapper } else if (Py_TYPE(descr) == &PyCFunction_Type && PyCFunction_GET_FUNCTION(descr) == (PyCFunction)tp_new_wrapper
&& ptr == (void**)&type->tp_new) { && ptr == (void**)&type->tp_new) {
/* The __new__ wrapper is not a wrapper descriptor, /* The __new__ wrapper is not a wrapper descriptor,
...@@ -1611,7 +1611,6 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce ...@@ -1611,7 +1611,6 @@ static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) noexce
in this reasoning that requires additional in this reasoning that requires additional
sanity checks. I'll buy the first person to sanity checks. I'll buy the first person to
point out a bug in this reasoning a beer. */ point out a bug in this reasoning a beer. */
#endif
} else if (offset == offsetof(BoxedClass, tp_descr_get) && descr->cls == function_cls } else if (offset == offsetof(BoxedClass, tp_descr_get) && descr->cls == function_cls
&& static_cast<BoxedFunction*>(descr)->f->always_use_version) { && static_cast<BoxedFunction*>(descr)->f->always_use_version) {
type->tpp_descr_get = (descrgetfunc) static_cast<BoxedFunction*>(descr)->f->always_use_version->code; type->tpp_descr_get = (descrgetfunc) static_cast<BoxedFunction*>(descr)->f->always_use_version->code;
...@@ -1745,12 +1744,16 @@ static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) ...@@ -1745,12 +1744,16 @@ static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds)
return self->tp_new(subtype, new_args, kwds); return self->tp_new(subtype, new_args, kwds);
} }
static struct PyMethodDef tp_new_methoddef[] = { { "__new__", (PyCFunction)tp_new_wrapper, METH_VARARGS | METH_KEYWORDS,
PyDoc_STR("T.__new__(S, ...) -> "
"a new object with type S, a subtype of T") },
{ 0, 0, 0, 0 } };
static void add_tp_new_wrapper(BoxedClass* type) noexcept { static void add_tp_new_wrapper(BoxedClass* type) noexcept {
if (type->getattr("__new__")) if (type->getattr("__new__"))
return; return;
type->giveAttr("__new__", type->giveAttr("__new__", new BoxedCApiFunction(tp_new_methoddef, type));
new BoxedCApiFunction(METH_VARARGS | METH_KEYWORDS, type, "__new__", (PyCFunction)tp_new_wrapper));
} }
void add_operators(BoxedClass* cls) noexcept { void add_operators(BoxedClass* cls) noexcept {
...@@ -2931,7 +2934,7 @@ extern "C" void PyType_Modified(PyTypeObject* type) noexcept { ...@@ -2931,7 +2934,7 @@ extern "C" void PyType_Modified(PyTypeObject* type) noexcept {
extern "C" int PyType_Ready(PyTypeObject* cls) noexcept { extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
ASSERT(!cls->is_pyston_class, "should not call this on Pyston classes"); ASSERT(!cls->is_pyston_class, "should not call this on Pyston classes");
gc::registerNonheapRootObject(cls); gc::registerNonheapRootObject(cls, sizeof(PyTypeObject));
// unhandled fields: // unhandled fields:
int ALLOWABLE_FLAGS = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES int ALLOWABLE_FLAGS = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES
......
...@@ -37,24 +37,22 @@ struct wrapper_def { ...@@ -37,24 +37,22 @@ struct wrapper_def {
extern "C" BoxedClass* capifunc_cls, *wrapperdescr_cls, *wrapperobject_cls; extern "C" BoxedClass* capifunc_cls, *wrapperdescr_cls, *wrapperobject_cls;
class BoxedCApiFunction : public Box { class BoxedCApiFunction : public Box {
private:
int ml_flags;
Box* passthrough;
const char* name;
PyCFunction func;
public: public:
PyMethodDef* method_def;
PyObject* passthrough;
Box* module; Box* module;
public: public:
BoxedCApiFunction(int ml_flags, Box* passthrough, const char* name, PyCFunction func, Box* module = NULL) BoxedCApiFunction(PyMethodDef* method_def, Box* passthrough, Box* module = NULL)
: ml_flags(ml_flags), passthrough(passthrough), name(name), func(func), module(module) {} : method_def(method_def), passthrough(passthrough), module(module) {}
DEFAULT_CLASS(capifunc_cls); DEFAULT_CLASS(capifunc_cls);
PyCFunction getFunction() { return method_def->ml_meth; }
static BoxedString* __repr__(BoxedCApiFunction* self) { static BoxedString* __repr__(BoxedCApiFunction* self) {
assert(self->cls == capifunc_cls); assert(self->cls == capifunc_cls);
return boxStrConstant(self->name); return boxStrConstant(self->method_def->ml_name);
} }
static Box* __call__(BoxedCApiFunction* self, BoxedTuple* varargs, BoxedDict* kwargs) { static Box* __call__(BoxedCApiFunction* self, BoxedTuple* varargs, BoxedDict* kwargs) {
...@@ -66,24 +64,28 @@ public: ...@@ -66,24 +64,28 @@ public:
threading::GLPromoteRegion _gil_lock; threading::GLPromoteRegion _gil_lock;
Box* rtn; Box* rtn;
if (self->ml_flags == METH_VARARGS) {
int flags = self->method_def->ml_flags;
auto func = self->method_def->ml_meth;
if (flags == METH_VARARGS) {
assert(kwargs->d.size() == 0); assert(kwargs->d.size() == 0);
rtn = (Box*)self->func(self->passthrough, varargs); rtn = (Box*)func(self->passthrough, varargs);
} else if (self->ml_flags == (METH_VARARGS | METH_KEYWORDS)) { } else if (flags == (METH_VARARGS | METH_KEYWORDS)) {
rtn = (Box*)((PyCFunctionWithKeywords)self->func)(self->passthrough, varargs, kwargs); rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, varargs, kwargs);
} else if (self->ml_flags == METH_NOARGS) { } else if (flags == METH_NOARGS) {
assert(kwargs->d.size() == 0); assert(kwargs->d.size() == 0);
assert(varargs->size() == 0); assert(varargs->size() == 0);
rtn = (Box*)self->func(self->passthrough, NULL); rtn = (Box*)func(self->passthrough, NULL);
} else if (self->ml_flags == METH_O) { } else if (flags == METH_O) {
if (kwargs->d.size() != 0) { if (kwargs->d.size() != 0) {
raiseExcHelper(TypeError, "%s() takes no keyword arguments", self->name); raiseExcHelper(TypeError, "%s() takes no keyword arguments", self->method_def->ml_name);
} }
if (varargs->size() != 1) { if (varargs->size() != 1) {
raiseExcHelper(TypeError, "%s() takes exactly one argument (%d given)", self->name, varargs->size()); raiseExcHelper(TypeError, "%s() takes exactly one argument (%d given)", self->method_def->ml_name,
varargs->size());
} }
rtn = (Box*)self->func(self->passthrough, varargs->elts[0]); rtn = (Box*)func(self->passthrough, varargs->elts[0]);
} else if (self->ml_flags == METH_OLDARGS) { } else if (flags == METH_OLDARGS) {
/* the really old style */ /* the really old style */
if (kwargs == NULL || PyDict_Size(kwargs) == 0) { if (kwargs == NULL || PyDict_Size(kwargs) == 0) {
int size = PyTuple_GET_SIZE(varargs); int size = PyTuple_GET_SIZE(varargs);
...@@ -92,12 +94,12 @@ public: ...@@ -92,12 +94,12 @@ public:
arg = PyTuple_GET_ITEM(varargs, 0); arg = PyTuple_GET_ITEM(varargs, 0);
else if (size == 0) else if (size == 0)
arg = NULL; arg = NULL;
rtn = self->func(self->passthrough, arg); rtn = func(self->passthrough, arg);
} else { } else {
raiseExcHelper(TypeError, "%.200s() takes no keyword arguments", self->name); raiseExcHelper(TypeError, "%.200s() takes no keyword arguments", self->method_def->ml_name);
} }
} else { } else {
RELEASE_ASSERT(0, "0x%x", self->ml_flags); RELEASE_ASSERT(0, "0x%x", flags);
} }
checkAndThrowCAPIException(); checkAndThrowCAPIException();
...@@ -107,7 +109,7 @@ public: ...@@ -107,7 +109,7 @@ public:
static Box* getname(Box* b, void*) { static Box* getname(Box* b, void*) {
RELEASE_ASSERT(b->cls == capifunc_cls, ""); RELEASE_ASSERT(b->cls == capifunc_cls, "");
const char* s = static_cast<BoxedCApiFunction*>(b)->name; const char* s = static_cast<BoxedCApiFunction*>(b)->method_def->ml_name;
if (s) if (s)
return boxStrConstant(s); return boxStrConstant(s);
return None; return None;
...@@ -125,6 +127,10 @@ public: ...@@ -125,6 +127,10 @@ public:
v->visit(o->module); v->visit(o->module);
} }
}; };
static_assert(sizeof(BoxedCApiFunction) == sizeof(PyCFunctionObject), "");
static_assert(offsetof(BoxedCApiFunction, method_def) == offsetof(PyCFunctionObject, m_ml), "");
static_assert(offsetof(BoxedCApiFunction, passthrough) == offsetof(PyCFunctionObject, m_self), "");
static_assert(offsetof(BoxedCApiFunction, module) == offsetof(PyCFunctionObject, m_module), "");
class BoxedWrapperDescriptor : public Box { class BoxedWrapperDescriptor : public Box {
public: public:
......
...@@ -390,6 +390,7 @@ enum class GCKind : uint8_t { ...@@ -390,6 +390,7 @@ enum class GCKind : uint8_t {
PRECISE = 3, PRECISE = 3,
UNTRACKED = 4, UNTRACKED = 4,
HIDDEN_CLASS = 5, HIDDEN_CLASS = 5,
CONSERVATIVE_PYTHON = 6,
}; };
extern "C" void* gc_alloc(size_t nbytes, GCKind kind); extern "C" void* gc_alloc(size_t nbytes, GCKind kind);
......
...@@ -162,12 +162,13 @@ static std::unordered_set<void*> nonheap_roots; ...@@ -162,12 +162,13 @@ static std::unordered_set<void*> nonheap_roots;
// way to verify it's not a nonheap root (the full check requires a hashtable lookup). // way to verify it's not a nonheap root (the full check requires a hashtable lookup).
static void* max_nonheap_root = 0; static void* max_nonheap_root = 0;
static void* min_nonheap_root = (void*)~0; static void* min_nonheap_root = (void*)~0;
void registerNonheapRootObject(void* obj) { void registerNonheapRootObject(void* obj, int size) {
// I suppose that things could work fine even if this were true, but why would it happen? // I suppose that things could work fine even if this were true, but why would it happen?
assert(global_heap.getAllocationFromInteriorPointer(obj) == NULL); assert(global_heap.getAllocationFromInteriorPointer(obj) == NULL);
assert(nonheap_roots.count(obj) == 0); assert(nonheap_roots.count(obj) == 0);
nonheap_roots.insert(obj); nonheap_roots.insert(obj);
registerPotentialRootRange(obj, ((uint8_t*)obj) + size);
max_nonheap_root = std::max(obj, max_nonheap_root); max_nonheap_root = std::max(obj, max_nonheap_root);
min_nonheap_root = std::min(obj, min_nonheap_root); min_nonheap_root = std::min(obj, min_nonheap_root);
...@@ -179,10 +180,29 @@ bool isNonheapRoot(void* p) { ...@@ -179,10 +180,29 @@ bool isNonheapRoot(void* p) {
return nonheap_roots.count(p) != 0; return nonheap_roots.count(p) != 0;
} }
bool isValidGCObject(void* p) { bool isValidGCMemory(void* p) {
return isNonheapRoot(p) || (global_heap.getAllocationFromInteriorPointer(p)->user_data == p); return isNonheapRoot(p) || (global_heap.getAllocationFromInteriorPointer(p)->user_data == p);
} }
bool isValidGCObject(void* p) {
if (isNonheapRoot(p))
return true;
GCAllocation* al = global_heap.getAllocationFromInteriorPointer(p);
if (!al)
return false;
return al->user_data == p && (al->kind_id == GCKind::CONSERVATIVE_PYTHON || al->kind_id == GCKind::PYTHON);
}
void setIsPythonObject(Box* b) {
auto al = global_heap.getAllocationFromInteriorPointer(b);
assert(al->user_data == (char*)b);
if (al->kind_id == GCKind::CONSERVATIVE) {
al->kind_id = GCKind::CONSERVATIVE_PYTHON;
} else {
assert(al->kind_id == GCKind::PYTHON);
}
}
static std::unordered_set<GCRootHandle*>* getRootHandles() { static std::unordered_set<GCRootHandle*>* getRootHandles() {
static std::unordered_set<GCRootHandle*> root_handles; static std::unordered_set<GCRootHandle*> root_handles;
return &root_handles; return &root_handles;
...@@ -259,16 +279,6 @@ void markPhase() { ...@@ -259,16 +279,6 @@ void markPhase() {
threading::visitAllStacks(&visitor); threading::visitAllStacks(&visitor);
gatherInterpreterRoots(&visitor); gatherInterpreterRoots(&visitor);
for (void* p : nonheap_roots) {
Box* b = reinterpret_cast<Box*>(p);
BoxedClass* cls = b->cls;
if (cls) {
ASSERT(cls->gc_visit, "%s", getTypeName(b));
cls->gc_visit(&visitor, b);
}
}
for (auto h : *getRootHandles()) { for (auto h : *getRootHandles()) {
visitor.visit(h->value); visitor.visit(h->value);
} }
...@@ -289,7 +299,7 @@ void markPhase() { ...@@ -289,7 +299,7 @@ void markPhase() {
GCKind kind_id = al->kind_id; GCKind kind_id = al->kind_id;
if (kind_id == GCKind::UNTRACKED) { if (kind_id == GCKind::UNTRACKED) {
continue; continue;
} else if (kind_id == GCKind::CONSERVATIVE) { } else if (kind_id == GCKind::CONSERVATIVE || kind_id == GCKind::CONSERVATIVE_PYTHON) {
uint32_t bytes = al->kind_data; uint32_t bytes = al->kind_data;
if (DEBUG >= 2) { if (DEBUG >= 2) {
if (global_heap.small_arena.contains(p)) { if (global_heap.small_arena.contains(p)) {
......
...@@ -31,7 +31,8 @@ void deregisterPermanentRoot(void* root_obj); ...@@ -31,7 +31,8 @@ void deregisterPermanentRoot(void* root_obj);
// Register an object that was not allocated through this collector, as a root for this collector. // Register an object that was not allocated through this collector, as a root for this collector.
// The motivating usecase is statically-allocated PyTypeObject objects, which are full Python objects // The motivating usecase is statically-allocated PyTypeObject objects, which are full Python objects
// even if they are not heap allocated. // even if they are not heap allocated.
void registerNonheapRootObject(void* obj); // This memory will be scanned conservatively.
void registerNonheapRootObject(void* obj, int size);
void registerPotentialRootRange(void* start, void* end); void registerPotentialRootRange(void* start, void* end);
...@@ -59,8 +60,10 @@ void disableGC(); ...@@ -59,8 +60,10 @@ void disableGC();
void enableGC(); void enableGC();
// These are mostly for debugging: // These are mostly for debugging:
bool isValidGCObject(void* p); bool isValidGCMemory(void* p); // if p is a valid gc-allocated pointer (or a non-heap root)
bool isValidGCObject(void* p); // whether p is valid gc memory and is set to have Python destructor semantics applied
bool isNonheapRoot(void* p); bool isNonheapRoot(void* p);
void setIsPythonObject(Box* b);
// Debugging/validation helpers: if a GC should not happen in certain sections (ex during unwinding), // Debugging/validation helpers: if a GC should not happen in certain sections (ex during unwinding),
// use these functions to mark that. This is different from disableGC/enableGC, since it causes an // use these functions to mark that. This is different from disableGC/enableGC, since it causes an
......
...@@ -152,7 +152,7 @@ bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) { ...@@ -152,7 +152,7 @@ bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) {
VALGRIND_ENABLE_ERROR_REPORTING; VALGRIND_ENABLE_ERROR_REPORTING;
#endif #endif
if (alloc_kind == GCKind::PYTHON) { if (alloc_kind == GCKind::PYTHON || alloc_kind == GCKind::CONSERVATIVE_PYTHON) {
#ifndef NVALGRIND #ifndef NVALGRIND
VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_DISABLE_ERROR_REPORTING;
#endif #endif
...@@ -170,7 +170,11 @@ bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) { ...@@ -170,7 +170,11 @@ bool _doFree(GCAllocation* al, std::vector<Box*>* weakly_referenced) {
} }
} }
ASSERT(b->cls->tp_dealloc == NULL, "%s", getTypeName(b)); // XXX: we are currently ignoring destructors (tp_dealloc) for extension objects, since we have
// historically done that (whoops) and there are too many to be worth changing for now as long
// as we can get real destructor support soon.
ASSERT(b->cls->tp_dealloc == NULL || alloc_kind == GCKind::CONSERVATIVE_PYTHON, "%s", getTypeName(b));
if (b->cls->simple_destructor) if (b->cls->simple_destructor)
b->cls->simple_destructor(b); b->cls->simple_destructor(b);
} }
...@@ -208,7 +212,7 @@ struct HeapStatistics { ...@@ -208,7 +212,7 @@ struct HeapStatistics {
int num_hcls_by_attrs[HCLS_ATTRS_STAT_MAX + 1]; int num_hcls_by_attrs[HCLS_ATTRS_STAT_MAX + 1];
int num_hcls_by_attrs_exceed; int num_hcls_by_attrs_exceed;
TypeStats python, conservative, untracked, hcls, precise; TypeStats python, conservative, conservative_python, untracked, hcls, precise;
TypeStats total; TypeStats total;
HeapStatistics(bool collect_cls_stats, bool collect_hcls_stats) HeapStatistics(bool collect_cls_stats, bool collect_hcls_stats)
...@@ -247,6 +251,17 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) { ...@@ -247,6 +251,17 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
} else if (al->kind_id == GCKind::CONSERVATIVE) { } else if (al->kind_id == GCKind::CONSERVATIVE) {
stats->conservative.nallocs++; stats->conservative.nallocs++;
stats->conservative.nbytes += nbytes; stats->conservative.nbytes += nbytes;
} else if (al->kind_id == GCKind::CONSERVATIVE_PYTHON) {
stats->conservative_python.nallocs++;
stats->conservative_python.nbytes += nbytes;
if (stats->collect_cls_stats) {
Box* b = (Box*)al->user_data;
auto& t = stats->by_cls[b->cls];
t.nallocs++;
t.nbytes += nbytes;
}
} else if (al->kind_id == GCKind::UNTRACKED) { } else if (al->kind_id == GCKind::UNTRACKED) {
stats->untracked.nallocs++; stats->untracked.nallocs++;
stats->untracked.nbytes += nbytes; stats->untracked.nbytes += nbytes;
...@@ -288,6 +303,7 @@ void Heap::dumpHeapStatistics(int level) { ...@@ -288,6 +303,7 @@ void Heap::dumpHeapStatistics(int level) {
stats.python.print("python"); stats.python.print("python");
stats.conservative.print("conservative"); stats.conservative.print("conservative");
stats.conservative_python.print("conservative_python");
stats.untracked.print("untracked"); stats.untracked.print("untracked");
stats.hcls.print("hcls"); stats.hcls.print("hcls");
stats.precise.print("precise"); stats.precise.print("precise");
...@@ -454,7 +470,9 @@ SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weak ...@@ -454,7 +470,9 @@ SmallArena::Block** SmallArena::_freeChain(Block** head, std::vector<Box*>& weak
} else { } else {
if (_doFree(al, &weakly_referenced)) { if (_doFree(al, &weakly_referenced)) {
b->isfree.set(atom_idx); b->isfree.set(atom_idx);
// memset(al->user_data, 0, b->size - sizeof(GCAllocation)); #ifndef NDEBUG
memset(al->user_data, 0xbb, b->size - sizeof(GCAllocation));
#endif
} }
} }
} }
......
...@@ -498,7 +498,7 @@ void setupSys() { ...@@ -498,7 +498,7 @@ void setupSys() {
sys_flags_cls->freeze(); sys_flags_cls->freeze();
for (auto& md : sys_methods) { for (auto& md : sys_methods) {
sys_module->giveAttr(md.ml_name, new BoxedCApiFunction(md.ml_flags, sys_module, md.ml_name, md.ml_meth)); sys_module->giveAttr(md.ml_name, new BoxedCApiFunction(&md, sys_module));
} }
sys_module->giveAttr("flags", new BoxedSysFlags()); sys_module->giveAttr("flags", new BoxedSysFlags());
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
using namespace pyston::threading; using namespace pyston::threading;
extern "C" void initthread();
static int initialized; static int initialized;
static void PyThread__init_thread(void); /* Forward */ static void PyThread__init_thread(void); /* Forward */
...@@ -57,8 +59,6 @@ static size_t _pythread_stacksize = 0; ...@@ -57,8 +59,6 @@ static size_t _pythread_stacksize = 0;
namespace pyston { namespace pyston {
BoxedModule* thread_module;
static void* thread_start(Box* target, Box* varargs, Box* kwargs) { static void* thread_start(Box* target, Box* varargs, Box* kwargs) {
assert(target); assert(target);
assert(varargs); assert(varargs);
...@@ -156,73 +156,6 @@ Box* allocateLock() { ...@@ -156,73 +156,6 @@ Box* allocateLock() {
return new BoxedThreadLock(); return new BoxedThreadLock();
} }
static BoxedClass* thread_local_cls;
class BoxedThreadLocal : public Box {
public:
BoxedThreadLocal() {}
static Box* getThreadLocalObject(Box* obj) {
BoxedDict* dict = static_cast<BoxedDict*>(PyThreadState_GetDict());
Box* tls_obj = dict->getOrNull(obj);
if (tls_obj == NULL) {
tls_obj = new BoxedDict();
setitem(dict, obj, tls_obj);
}
return tls_obj;
}
static Box* setattrPyston(Box* obj, Box* name, Box* val) noexcept {
if (isSubclass(name->cls, str_cls) && static_cast<BoxedString*>(name)->s() == "__dict__") {
raiseExcHelper(AttributeError, "'%.50s' object attribute '__dict__' is read-only", Py_TYPE(obj)->tp_name);
}
Box* tls_obj = getThreadLocalObject(obj);
setitem(tls_obj, name, val);
return None;
}
static int setattro(Box* obj, Box* name, Box* val) noexcept {
try {
setattrPyston(obj, name, val);
} catch (ExcInfo e) {
setCAPIException(e);
return -1;
}
return 0;
}
static Box* getattro(Box* obj, Box* name) noexcept {
assert(name->cls == str_cls);
llvm::StringRef s = static_cast<BoxedString*>(name)->s();
Box* tls_obj = getThreadLocalObject(obj);
if (s == "__dict__")
return tls_obj;
try {
return getitem(tls_obj, name);
} catch (ExcInfo e) {
}
try {
Box* r = getattrInternalGeneric(obj, s, NULL, false, false, NULL, NULL);
if (r)
return r;
} catch (ExcInfo e) {
setCAPIException(e);
return NULL;
}
PyErr_Format(PyExc_AttributeError, "'%.50s' object has no attribute '%.400s'", Py_TYPE(obj)->tp_name, s.data());
return NULL;
}
static Box* hash(Box* obj) { return boxInt(PyThread_get_thread_ident()); }
DEFAULT_CLASS(thread_local_cls);
};
Box* getIdent() { Box* getIdent() {
return boxInt(pthread_self()); return boxInt(pthread_self());
} }
...@@ -232,7 +165,14 @@ Box* stackSize() { ...@@ -232,7 +165,14 @@ Box* stackSize() {
} }
void setupThread() { void setupThread() {
thread_module = createModule("thread"); // Hacky: we want to use some of CPython's implementation of the thread module (the threading local stuff),
// and some of ours (thread handling). Start off by calling a cut-down version of initthread, and then
// add our own attributes to the module it creates.
initthread();
RELEASE_ASSERT(!PyErr_Occurred(), "");
Box* thread_module = getSysModulesDict()->getOrNull(boxString("thread"));
assert(thread_module);
thread_module->giveAttr("start_new_thread", new BoxedBuiltinFunctionOrMethod( thread_module->giveAttr("start_new_thread", new BoxedBuiltinFunctionOrMethod(
boxRTFunction((void*)startNewThread, BOXED_INT, 3, 1, false, false), boxRTFunction((void*)startNewThread, BOXED_INT, 3, 1, false, false),
...@@ -256,21 +196,6 @@ void setupThread() { ...@@ -256,21 +196,6 @@ void setupThread() {
thread_lock_cls->giveAttr("__exit__", new BoxedFunction(boxRTFunction((void*)BoxedThreadLock::exit, NONE, 4))); thread_lock_cls->giveAttr("__exit__", new BoxedFunction(boxRTFunction((void*)BoxedThreadLock::exit, NONE, 4)));
thread_lock_cls->freeze(); thread_lock_cls->freeze();
thread_local_cls = new (0) BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedThreadLocal), false,
static_cast<BoxedString*>(boxString("_local")));
thread_local_cls->giveAttr("__module__", boxStrConstant("thread"));
thread_local_cls->giveAttr("__hash__",
new BoxedFunction(boxRTFunction((void*)BoxedThreadLocal::hash, BOXED_INT, 1)));
thread_local_cls->giveAttr("__setattr__",
new BoxedFunction(boxRTFunction((void*)BoxedThreadLocal::setattrPyston, UNKNOWN, 3)));
thread_module->giveAttr("_local", thread_local_cls);
thread_local_cls->tp_setattro = BoxedThreadLocal::setattro;
thread_local_cls->tp_getattro = BoxedThreadLocal::getattro;
add_operators(thread_local_cls);
thread_local_cls->finishInitialization();
thread_local_cls->freeze();
BoxedClass* ThreadError BoxedClass* ThreadError
= BoxedHeapClass::create(type_cls, Exception, NULL, Exception->attrs_offset, Exception->tp_weaklistoffset, = BoxedHeapClass::create(type_cls, Exception, NULL, Exception->attrs_offset, Exception->tp_weaklistoffset,
Exception->tp_basicsize, false, "error"); Exception->tp_basicsize, false, "error");
......
...@@ -85,17 +85,6 @@ extern "C" void _PyErr_BadInternalCall(const char* filename, int lineno) noexcep ...@@ -85,17 +85,6 @@ extern "C" void _PyErr_BadInternalCall(const char* filename, int lineno) noexcep
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" PyVarObject* PyObject_InitVar(PyVarObject* op, PyTypeObject* tp, Py_ssize_t size) noexcept {
assert(gc::isValidGCObject(op));
assert(gc::isValidGCObject(tp));
RELEASE_ASSERT(op, "");
RELEASE_ASSERT(tp, "");
Py_TYPE(op) = tp;
op->ob_size = size;
return op;
}
extern "C" PyObject* PyObject_Format(PyObject* obj, PyObject* format_spec) noexcept { extern "C" PyObject* PyObject_Format(PyObject* obj, PyObject* format_spec) noexcept {
PyObject* empty = NULL; PyObject* empty = NULL;
PyObject* result = NULL; PyObject* result = NULL;
...@@ -269,15 +258,215 @@ extern "C" PyObject* PyObject_GenericGetAttr(PyObject* o, PyObject* name) noexce ...@@ -269,15 +258,215 @@ extern "C" PyObject* PyObject_GenericGetAttr(PyObject* o, PyObject* name) noexce
} }
} }
// Note (kmod): I don't feel great about including an alternate code-path for lookups. I also, however, don't feel
// great about modifying our code paths to take a custom dict, and since this code is just copied from CPython
// I feel like the risk is pretty low.
extern "C" PyObject* _PyObject_GenericGetAttrWithDict(PyObject* obj, PyObject* name, PyObject* dict) noexcept { extern "C" PyObject* _PyObject_GenericGetAttrWithDict(PyObject* obj, PyObject* name, PyObject* dict) noexcept {
fatalOrError(PyExc_NotImplementedError, "unimplemented"); PyTypeObject* tp = Py_TYPE(obj);
return nullptr; PyObject* descr = NULL;
PyObject* res = NULL;
descrgetfunc f;
Py_ssize_t dictoffset;
PyObject** dictptr;
if (!PyString_Check(name)) {
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if (PyUnicode_Check(name)) {
name = PyUnicode_AsEncodedString(name, NULL, NULL);
if (name == NULL)
return NULL;
} else
#endif
{
PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", Py_TYPE(name)->tp_name);
return NULL;
}
} else
Py_INCREF(name);
if (tp->tp_dict == NULL) {
if (PyType_Ready(tp) < 0)
goto done;
}
#if 0 /* XXX this is not quite _PyType_Lookup anymore */
/* Inline _PyType_Lookup */
{
Py_ssize_t i, n;
PyObject *mro, *base, *dict;
/* Look in tp_dict of types in MRO */
mro = tp->tp_mro;
assert(mro != NULL);
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro);
for (i = 0; i < n; i++) {
base = PyTuple_GET_ITEM(mro, i);
if (PyClass_Check(base))
dict = ((PyClassObject *)base)->cl_dict;
else {
assert(PyType_Check(base));
dict = ((PyTypeObject *)base)->tp_dict;
}
assert(dict && PyDict_Check(dict));
descr = PyDict_GetItem(dict, name);
if (descr != NULL)
break;
}
}
#else
descr = _PyType_Lookup(tp, name);
#endif
Py_XINCREF(descr);
f = NULL;
if (descr != NULL && PyType_HasFeature(descr->cls, Py_TPFLAGS_HAVE_CLASS)) {
f = descr->cls->tp_descr_get;
if (f != NULL && PyDescr_IsData(descr)) {
res = f(descr, obj, (PyObject*)obj->cls);
Py_DECREF(descr);
goto done;
}
}
if (dict == NULL) {
/* Inline _PyObject_GetDictPtr */
dictoffset = tp->tp_dictoffset;
if (dictoffset != 0) {
if (dictoffset < 0) {
Py_ssize_t tsize;
size_t size;
tsize = ((PyVarObject*)obj)->ob_size;
if (tsize < 0)
tsize = -tsize;
size = _PyObject_VAR_SIZE(tp, tsize);
dictoffset += (long)size;
assert(dictoffset > 0);
assert(dictoffset % SIZEOF_VOID_P == 0);
}
dictptr = (PyObject**)((char*)obj + dictoffset);
dict = *dictptr;
}
}
if (dict != NULL) {
Py_INCREF(dict);
res = PyDict_GetItem(dict, name);
if (res != NULL) {
Py_INCREF(res);
Py_XDECREF(descr);
Py_DECREF(dict);
goto done;
}
Py_DECREF(dict);
}
if (f != NULL) {
res = f(descr, obj, (PyObject*)Py_TYPE(obj));
Py_DECREF(descr);
goto done;
}
if (descr != NULL) {
res = descr;
/* descr was already increfed above */
goto done;
}
PyErr_Format(PyExc_AttributeError, "'%.50s' object has no attribute '%.400s'", tp->tp_name,
PyString_AS_STRING(name));
done:
Py_DECREF(name);
return res;
} }
// (see note for _PyObject_GenericGetAttrWithDict)
extern "C" int _PyObject_GenericSetAttrWithDict(PyObject* obj, PyObject* name, PyObject* value, extern "C" int _PyObject_GenericSetAttrWithDict(PyObject* obj, PyObject* name, PyObject* value,
PyObject* dict) noexcept { PyObject* dict) noexcept {
fatalOrError(PyExc_NotImplementedError, "unimplemented"); PyTypeObject* tp = Py_TYPE(obj);
PyObject* descr;
descrsetfunc f;
PyObject** dictptr;
int res = -1;
if (!PyString_Check(name)) {
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if (PyUnicode_Check(name)) {
name = PyUnicode_AsEncodedString(name, NULL, NULL);
if (name == NULL)
return -1;
} else
#endif
{
PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", Py_TYPE(name)->tp_name);
return -1; return -1;
}
} else
Py_INCREF(name);
if (tp->tp_dict == NULL) {
if (PyType_Ready(tp) < 0)
goto done;
}
descr = _PyType_Lookup(tp, name);
f = NULL;
if (descr != NULL && PyType_HasFeature(descr->cls, Py_TPFLAGS_HAVE_CLASS)) {
f = descr->cls->tp_descr_set;
if (f != NULL && PyDescr_IsData(descr)) {
res = f(descr, obj, value);
goto done;
}
}
if (dict == NULL) {
dictptr = _PyObject_GetDictPtr(obj);
if (dictptr != NULL) {
dict = *dictptr;
if (dict == NULL && value != NULL) {
dict = PyDict_New();
if (dict == NULL)
goto done;
*dictptr = dict;
}
}
}
if (dict != NULL) {
Py_INCREF(dict);
if (value == NULL)
res = PyDict_DelItem(dict, name);
else
res = PyDict_SetItem(dict, name, value);
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
PyErr_SetObject(PyExc_AttributeError, name);
Py_DECREF(dict);
goto done;
}
if (f != NULL) {
res = f(descr, obj, value);
goto done;
}
if (descr == NULL) {
PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name,
PyString_AS_STRING(name));
goto done;
}
PyErr_Format(PyExc_AttributeError, "'%.50s' object attribute '%.400s' is read-only", tp->tp_name,
PyString_AS_STRING(name));
done:
Py_DECREF(name);
return res;
} }
...@@ -1265,7 +1454,7 @@ extern "C" PyObject* Py_FindMethod(PyMethodDef* methods, PyObject* self, const c ...@@ -1265,7 +1454,7 @@ extern "C" PyObject* Py_FindMethod(PyMethodDef* methods, PyObject* self, const c
extern "C" PyObject* PyCFunction_NewEx(PyMethodDef* ml, PyObject* self, PyObject* module) noexcept { extern "C" PyObject* PyCFunction_NewEx(PyMethodDef* ml, PyObject* self, PyObject* module) noexcept {
assert((ml->ml_flags & (~(METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O))) == 0); assert((ml->ml_flags & (~(METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O))) == 0);
return new BoxedCApiFunction(ml->ml_flags, self, ml->ml_name, ml->ml_meth, module); return new BoxedCApiFunction(ml, self, module);
} }
extern "C" PyCFunction PyCFunction_GetFunction(PyObject* op) noexcept { extern "C" PyCFunction PyCFunction_GetFunction(PyObject* op) noexcept {
...@@ -1273,7 +1462,15 @@ extern "C" PyCFunction PyCFunction_GetFunction(PyObject* op) noexcept { ...@@ -1273,7 +1462,15 @@ extern "C" PyCFunction PyCFunction_GetFunction(PyObject* op) noexcept {
PyErr_BadInternalCall(); PyErr_BadInternalCall();
return NULL; return NULL;
} }
return ((PyCFunctionObject*)op)->m_ml->ml_meth; return static_cast<BoxedCApiFunction*>(op)->getFunction();
}
extern "C" PyObject* PyCFunction_GetSelf(PyObject* op) noexcept {
if (!PyCFunction_Check(op)) {
PyErr_BadInternalCall();
return NULL;
}
return static_cast<BoxedCApiFunction*>(op)->passthrough;
} }
extern "C" int _PyEval_SliceIndex(PyObject* v, Py_ssize_t* pi) noexcept { extern "C" int _PyEval_SliceIndex(PyObject* v, Py_ssize_t* pi) noexcept {
...@@ -1413,18 +1610,18 @@ Box* BoxedCApiFunction::callInternal(BoxedFunctionBase* func, CallRewriteArgs* r ...@@ -1413,18 +1610,18 @@ Box* BoxedCApiFunction::callInternal(BoxedFunctionBase* func, CallRewriteArgs* r
assert(arg1->cls == capifunc_cls); assert(arg1->cls == capifunc_cls);
BoxedCApiFunction* capifunc = static_cast<BoxedCApiFunction*>(arg1); BoxedCApiFunction* capifunc = static_cast<BoxedCApiFunction*>(arg1);
if (capifunc->ml_flags != METH_O) if (capifunc->method_def->ml_flags != METH_O)
return callFunc(func, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names); return callFunc(func, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
if (rewrite_args) { if (rewrite_args) {
rewrite_args->arg1->addGuard((intptr_t)arg1); rewrite_args->arg1->addGuard((intptr_t)arg1);
RewriterVar* r_passthrough = rewrite_args->arg1->getAttr(offsetof(BoxedCApiFunction, passthrough)); RewriterVar* r_passthrough = rewrite_args->arg1->getAttr(offsetof(BoxedCApiFunction, passthrough));
rewrite_args->out_rtn rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)capifunc->method_def->ml_meth, r_passthrough,
= rewrite_args->rewriter->call(true, (void*)capifunc->func, r_passthrough, rewrite_args->arg2); rewrite_args->arg2);
rewrite_args->rewriter->call(true, (void*)checkAndThrowCAPIException); rewrite_args->rewriter->call(true, (void*)checkAndThrowCAPIException);
rewrite_args->out_success = true; rewrite_args->out_success = true;
} }
Box* r = capifunc->func(capifunc->passthrough, arg2); Box* r = capifunc->method_def->ml_meth(capifunc->passthrough, arg2);
checkAndThrowCAPIException(); checkAndThrowCAPIException();
assert(r); assert(r);
return r; return r;
...@@ -1448,7 +1645,7 @@ static Box* methodGetDoc(Box* b, void*) { ...@@ -1448,7 +1645,7 @@ static Box* methodGetDoc(Box* b, void*) {
extern "C" PyObject* _PyObject_GC_Malloc(size_t basicsize) noexcept { extern "C" PyObject* _PyObject_GC_Malloc(size_t basicsize) noexcept {
Box* r = ((PyObject*)PyObject_MALLOC(basicsize)); Box* r = ((PyObject*)PyObject_MALLOC(basicsize));
RELEASE_ASSERT(gc::isValidGCObject(r), ""); RELEASE_ASSERT(gc::isValidGCMemory(r), "");
return r; return r;
} }
......
...@@ -901,6 +901,16 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite ...@@ -901,6 +901,16 @@ void Box::setattr(const std::string& attr, Box* val, SetattrRewriteArgs* rewrite
abort(); abort();
} }
extern "C" PyObject* _PyType_Lookup(PyTypeObject* type, PyObject* name) noexcept {
RELEASE_ASSERT(name->cls == str_cls, "");
try {
return typeLookup(type, static_cast<BoxedString*>(name)->s(), NULL);
} catch (ExcInfo e) {
setCAPIException(e);
return NULL;
}
}
Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* rewrite_args) { Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* rewrite_args) {
Box* val; Box* val;
...@@ -2328,7 +2338,7 @@ extern "C" void dumpEx(void* p, int levels) { ...@@ -2328,7 +2338,7 @@ extern "C" void dumpEx(void* p, int levels) {
printf("\n"); printf("\n");
printf("Raw address: %p\n", p); printf("Raw address: %p\n", p);
bool is_gc = gc::isValidGCObject(p); bool is_gc = gc::isValidGCMemory(p);
if (!is_gc) { if (!is_gc) {
printf("non-gc memory\n"); printf("non-gc memory\n");
return; return;
...@@ -4513,23 +4523,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa ...@@ -4513,23 +4523,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
// Notably, "type" itself does not. For instance, assuming M is a subclass of // Notably, "type" itself does not. For instance, assuming M is a subclass of
// type, type.__new__(M, 1) will return the int class, which is not an instance of M. // type, type.__new__(M, 1) will return the int class, which is not an instance of M.
static Box* object_new = NULL;
static Box* object_init = NULL;
// this is ok with not using StlCompatAllocator since we will manually register these objects with the GC // this is ok with not using StlCompatAllocator since we will manually register these objects with the GC
static std::vector<Box*> allowable_news; static std::vector<Box*> allowable_news;
if (!object_new) { if (allowable_news.empty()) {
object_new = typeLookup(object_cls, new_str, NULL); for (BoxedClass* allowed_cls : { object_cls, enumerate_cls, xrange_cls }) {
assert(object_new);
// I think this is unnecessary, but good form:
gc::registerPermanentRoot(object_new);
object_init = typeLookup(object_cls, init_str, NULL);
assert(object_init);
gc::registerPermanentRoot(object_init);
allowable_news.push_back(object_new);
for (BoxedClass* allowed_cls : { enumerate_cls, xrange_cls }) {
auto new_obj = typeLookup(allowed_cls, new_str, NULL); auto new_obj = typeLookup(allowed_cls, new_str, NULL);
gc::registerPermanentRoot(new_obj); gc::registerPermanentRoot(new_obj);
allowable_news.push_back(new_obj); allowable_news.push_back(new_obj);
...@@ -4585,16 +4582,9 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa ...@@ -4585,16 +4582,9 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
RewriterVar* r_made = NULL; RewriterVar* r_made = NULL;
ArgPassSpec new_argspec = argspec; ArgPassSpec new_argspec = argspec;
if (npassed_args > 1 && new_attr == object_new) {
if (init_attr == object_init) {
raiseExcHelper(TypeError, objectNewParameterTypeErrorMsg());
} else {
new_argspec = ArgPassSpec(1);
}
}
if (rewrite_args) { if (rewrite_args) {
if (new_attr == object_new && init_attr != object_init) { if (cls->tp_new == object_cls->tp_new && cls->tp_init != object_cls->tp_init) {
// Fast case: if we are calling object_new, we normally doesn't look at the arguments at all. // Fast case: if we are calling object_new, we normally doesn't look at the arguments at all.
// (Except in the case when init_attr != object_init, in which case object_new looks at the number // (Except in the case when init_attr != object_init, in which case object_new looks at the number
// of arguments and throws an exception.) // of arguments and throws an exception.)
...@@ -4660,7 +4650,7 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa ...@@ -4660,7 +4650,7 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
} }
} }
if (init_attr && init_attr != object_init) { if (init_attr && made->cls->tp_init != object_cls->tp_init) {
// TODO apply the same descriptor special-casing as in callattr? // TODO apply the same descriptor special-casing as in callattr?
Box* initrtn; Box* initrtn;
...@@ -4722,8 +4712,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa ...@@ -4722,8 +4712,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) { Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
assert(vararg->cls == tuple_cls); assert(vararg->cls == tuple_cls);
bool pass_kwargs = (kwargs && kwargs->d.size());
int n = vararg->size(); int n = vararg->size();
int args_to_pass = n + 2; // 1 for obj, 1 for kwargs int args_to_pass = n + 1 + (pass_kwargs ? 1 : 0); // 1 for obj, 1 for kwargs
Box** args = NULL; Box** args = NULL;
if (args_to_pass > 3) if (args_to_pass > 3)
...@@ -4734,9 +4726,11 @@ Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) { ...@@ -4734,9 +4726,11 @@ Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
getArg(i + 1, arg1, arg2, arg3, args) = vararg->elts[i]; getArg(i + 1, arg1, arg2, arg3, args) = vararg->elts[i];
} }
if (pass_kwargs)
getArg(n + 1, arg1, arg2, arg3, args) = kwargs; getArg(n + 1, arg1, arg2, arg3, args) = kwargs;
return typeCallInternal(NULL, NULL, ArgPassSpec(n + 1, 0, false, true), arg1, arg2, arg3, args, NULL); return typeCallInternal(NULL, NULL, ArgPassSpec(n + 1, 0, false, pass_kwargs), arg1, arg2, arg3, args, NULL);
} }
extern "C" void delGlobal(Box* globals, const std::string* name) { extern "C" void delGlobal(Box* globals, const std::string* name) {
......
...@@ -95,7 +95,8 @@ bool IN_SHUTDOWN = false; ...@@ -95,7 +95,8 @@ bool IN_SHUTDOWN = false;
extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) noexcept { extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) noexcept {
assert(cls); assert(cls);
const size_t size = _PyObject_VAR_SIZE(cls, nitems); // See PyType_GenericAlloc for note about the +1 here:
const size_t size = _PyObject_VAR_SIZE(cls, nitems + 1);
#ifndef NDEBUG #ifndef NDEBUG
#if 0 #if 0
...@@ -144,8 +145,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -144,8 +145,11 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern "C" PyObject* PyType_GenericAlloc(PyTypeObject* type, Py_ssize_t nitems) noexcept { extern "C" PyObject* PyType_GenericAlloc(PyTypeObject* type, Py_ssize_t nitems) noexcept {
PyObject* obj; PyObject* obj;
const size_t size = _PyObject_VAR_SIZE(type, nitems); const size_t size = _PyObject_VAR_SIZE(type, nitems + 1);
// I don't understand why there is a +1 in this method; _PyObject_NewVar doesn't do that.
// CPython has the following comment:
/* note that we need to add one, for the sentinel */ /* note that we need to add one, for the sentinel */
// I think that regardless of the reasoning behind them having it, we should do what they do?
if (PyType_IS_GC(type)) if (PyType_IS_GC(type))
obj = _PyObject_GC_Malloc(size); obj = _PyObject_GC_Malloc(size);
...@@ -1731,26 +1735,45 @@ Box* objectNewNoArgs(BoxedClass* cls) { ...@@ -1731,26 +1735,45 @@ Box* objectNewNoArgs(BoxedClass* cls) {
return new (cls) Box(); return new (cls) Box();
} }
Box* objectNew(BoxedClass* cls, BoxedTuple* args, BoxedDict* kwargs) { static int excess_args(PyObject* args, PyObject* kwds) noexcept {
assert(isSubclass(cls->cls, type_cls)); return PyTuple_GET_SIZE(args) || (kwds && PyDict_Check(kwds) && PyDict_Size(kwds));
assert(args->cls == tuple_cls); }
assert(kwargs->cls == dict_cls);
// We use a different strategy from CPython: we let object.__new__ take extra static PyObject* object_new(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept;
// arguments, but raise an error if they wouldn't be handled by the corresponding init.
// TODO switch to the CPython approach?
if (args->size() != 0 || kwargs->d.size() != 0) {
// TODO slow (We already cache these in typeCall -- should use that here too?)
if (typeLookup(cls, "__new__", NULL) != typeLookup(object_cls, "__new__", NULL)
|| typeLookup(cls, "__init__", NULL) == typeLookup(object_cls, "__init__", NULL))
raiseExcHelper(TypeError, objectNewParameterTypeErrorMsg());
}
return new (cls) Box(); static int object_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept {
int err = 0;
if (excess_args(args, kwds)) {
PyTypeObject* type = Py_TYPE(self);
if (type->tp_init != object_init && type->tp_new != object_new) {
err = PyErr_WarnEx(PyExc_DeprecationWarning, "object.__init__() takes no parameters", 1);
} else if (type->tp_init != object_init || type->tp_new == object_new) {
PyErr_SetString(PyExc_TypeError, "object.__init__() takes no parameters");
err = -1;
}
}
return err;
} }
Box* objectInit(Box* b, BoxedTuple* args) { static PyObject* object_new(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept {
return None; int err = 0;
if (excess_args(args, kwds)) {
if (type->tp_new != object_new && type->tp_init != object_init) {
err = PyErr_WarnEx(PyExc_DeprecationWarning, "object() takes no parameters", 1);
} else if (type->tp_new != object_new || type->tp_init == object_init) {
PyErr_SetString(PyExc_TypeError, "object() takes no parameters");
err = -1;
}
}
if (err < 0)
return NULL;
if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
// I don't know what this is or when it happens, but
// CPython does something special with it
Py_FatalError("unimplemented");
}
return type->tp_alloc(type, 0);
} }
Box* objectRepr(Box* obj) { Box* objectRepr(Box* obj) {
...@@ -2209,13 +2232,30 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) { ...@@ -2209,13 +2232,30 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) {
extern "C" void PyCallIter_AddHasNext(); extern "C" void PyCallIter_AddHasNext();
extern "C" PyVarObject* PyObject_InitVar(PyVarObject* op, PyTypeObject* tp, Py_ssize_t size) noexcept {
assert(op);
assert(tp);
assert(gc::isValidGCMemory(op));
assert(gc::isValidGCObject(tp));
gc::setIsPythonObject(op);
Py_TYPE(op) = tp;
op->ob_size = size;
return op;
}
extern "C" PyObject* PyObject_Init(PyObject* op, PyTypeObject* tp) noexcept { extern "C" PyObject* PyObject_Init(PyObject* op, PyTypeObject* tp) noexcept {
assert(op); assert(op);
assert(tp); assert(tp);
assert(gc::isValidGCObject(op)); assert(gc::isValidGCMemory(op));
assert(gc::isValidGCObject(tp)); assert(gc::isValidGCObject(tp));
gc::setIsPythonObject(op);
Py_TYPE(op) = tp; Py_TYPE(op) = tp;
if (PyType_SUPPORTS_WEAKREFS(tp)) { if (PyType_SUPPORTS_WEAKREFS(tp)) {
...@@ -2428,6 +2468,8 @@ void setupRuntime() { ...@@ -2428,6 +2468,8 @@ void setupRuntime() {
object_cls->tp_getattro = PyObject_GenericGetAttr; object_cls->tp_getattro = PyObject_GenericGetAttr;
object_cls->tp_setattro = PyObject_GenericSetAttr; object_cls->tp_setattro = PyObject_GenericSetAttr;
object_cls->tp_init = object_init;
object_cls->tp_new = object_new;
add_operators(object_cls); add_operators(object_cls);
object_cls->finishInitialization(); object_cls->finishInitialization();
...@@ -2492,8 +2534,6 @@ void setupRuntime() { ...@@ -2492,8 +2534,6 @@ void setupRuntime() {
SET = typeFromClass(set_cls); SET = typeFromClass(set_cls);
FROZENSET = typeFromClass(frozenset_cls); FROZENSET = typeFromClass(frozenset_cls);
object_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)objectNew, UNKNOWN, 1, 0, true, true)));
object_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)objectInit, UNKNOWN, 1, 0, true, false)));
object_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)objectRepr, UNKNOWN, 1, 0, false, false))); object_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)objectRepr, UNKNOWN, 1, 0, false, false)));
object_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)objectStr, UNKNOWN, 1, 0, false, false))); object_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)objectStr, UNKNOWN, 1, 0, false, false)));
object_cls->giveAttr("__hash__", object_cls->giveAttr("__hash__",
...@@ -2546,6 +2586,8 @@ void setupRuntime() { ...@@ -2546,6 +2586,8 @@ void setupRuntime() {
} }
object_cls->giveAttr("__class__", new (pyston_getset_cls) BoxedGetsetDescriptor(objectClass, objectSetClass, NULL)); object_cls->giveAttr("__class__", new (pyston_getset_cls) BoxedGetsetDescriptor(objectClass, objectSetClass, NULL));
object_cls->freeze(); object_cls->freeze();
assert(object_cls->tp_init == object_init);
assert(object_cls->tp_new == object_new);
setupBool(); setupBool();
setupLong(); setupLong();
......
# should_error
# skip-if: sys.version_info < (2, 7, 4)
# - Error message changed in 2.7.4
# Regression test: # Regression test:
# If the init function doesn't exist, shouldn't just silently ignore any args # If the init function doesn't exist, shouldn't just silently ignore any args
# that got passed # that got passed
...@@ -16,5 +12,8 @@ print "This should have worked" ...@@ -16,5 +12,8 @@ print "This should have worked"
class D(object): class D(object):
pass pass
d = D(1) try:
print "This should have failed" d = D(1)
print "This should have failed"
except TypeError as e:
print "expected exception" # the message got changed in 2.7.4
# should_error
# skip-if: sys.version_info < (2, 7, 4)
# - Error message changed in 2.7.4
# object.__new__ doesn't complain if __init__ is overridden: # object.__new__ doesn't complain if __init__ is overridden:
class C1(object): class C1(object):
...@@ -16,4 +12,12 @@ object.__new__(C1, 1) ...@@ -16,4 +12,12 @@ object.__new__(C1, 1)
object.__new__(C1, a=1) object.__new__(C1, a=1)
print "Trying C2" print "Trying C2"
object.__new__(C2, 1) try:
object.__new__(C2, 1)
except TypeError as e:
print "caught TypeError"
# These are some tricky cases, since they can potentially look like arguments
# are being passed, but really they are not.
type.__call__(*[C2])
type.__call__(C2, **{})
...@@ -12,12 +12,14 @@ class CustomThreadingLocal(threading.local): ...@@ -12,12 +12,14 @@ class CustomThreadingLocal(threading.local):
self.n += 1 self.n += 1
print self.a, self.n print self.a, self.n
print CustomThreadingLocal().a print CustomThreadingLocal().a
print CustomThreadingLocal().a c = CustomThreadingLocal()
print c.a
def f(): def f():
print
a.x = "goodbye world" a.x = "goodbye world"
print a.x print a.x
print CustomThreadingLocal().a print c.a
print CustomThreadingLocal().a print CustomThreadingLocal().a
def test(): def test():
...@@ -36,3 +38,6 @@ print a.x ...@@ -36,3 +38,6 @@ print a.x
a.__setattr__('x', 5) a.__setattr__('x', 5)
print a.x print a.x
print sorted(a.__dict__.items()) print sorted(a.__dict__.items())
del a.x
print sorted(a.__dict__.items())
# Make sure we can support weakrefs on extension objects.
# The _sre.SRE_Pattern type is one of the few builtin types that supports weakrefs natively.
import _sre
def f(n):
if n:
return f(n-1)
p = _sre.compile('', 0, [1])
print type(p)
return weakref.ref(p)
import weakref
r = f(20)
import gc
gc.collect()
print r()
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