Commit 97b5b389 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'capi_subclassing'

parents 49ee07fa e5a0e465
...@@ -869,6 +869,10 @@ runpy_%: %.py ext_python ...@@ -869,6 +869,10 @@ runpy_%: %.py ext_python
PYTHONPATH=test/test_extension/build/lib.linux-x86_64-2.7 python $< PYTHONPATH=test/test_extension/build/lib.linux-x86_64-2.7 python $<
$(call make_search,runpy_%) $(call make_search,runpy_%)
dbgpy_%: %.py ext_pythondbg
export PYTHON_VERSION=$$(python2.7-dbg -V 2>&1 | awk '{print $$2}'); PYTHONPATH=test/test_extension/build/lib.linux-x86_64-2.7-pydebug $(GDB) --ex "dir $(DEPS_DIR)/python-src/python2.7-$$PYTHON_VERSION/debian" $(GDB_CMDS) --args python2.7-dbg $<
$(call make_search,dbgpy_%)
# "kill valgrind": # "kill valgrind":
kv: kv:
ps aux | awk '/[v]algrind/ {print $$2}' | xargs kill -9; true ps aux | awk '/[v]algrind/ {print $$2}' | xargs kill -9; true
...@@ -991,9 +995,11 @@ ext_pyston_selfhost_release: pyston_release $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR ...@@ -991,9 +995,11 @@ ext_pyston_selfhost_release: pyston_release $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR
cd $(TEST_DIR)/test_extension; DISTUTILS_DEBUG=1 time ../../pyston_release setup.py build cd $(TEST_DIR)/test_extension; DISTUTILS_DEBUG=1 time ../../pyston_release setup.py build
cd $(TEST_DIR)/test_extension; ln -sf $(TEST_EXT_MODULE_NAMES:%=build/lib.unknown-2.7/%.pyston.so) . cd $(TEST_DIR)/test_extension; ln -sf $(TEST_EXT_MODULE_NAMES:%=build/lib.unknown-2.7/%.pyston.so) .
.PHONY: ext_python .PHONY: ext_python ext_pythondbg
ext_python: $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR)/test_extension/*.c) ext_python: $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR)/test_extension/*.c)
cd $(TEST_DIR)/test_extension; python setup.py build cd $(TEST_DIR)/test_extension; python setup.py build
ext_pythondbg: $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR)/test_extension/*.c)
cd $(TEST_DIR)/test_extension; python2.7-dbg setup.py build
$(FROM_CPYTHON_SRCS:.c=.o): %.o: %.c $(BUILD_SYSTEM_DEPS) $(FROM_CPYTHON_SRCS:.c=.o): %.o: %.c $(BUILD_SYSTEM_DEPS)
$(ECHO) Compiling C file to $@ $(ECHO) Compiling C file to $@
......
...@@ -248,36 +248,19 @@ download from http://code.google.com/p/gperftools/downloads/list ...@@ -248,36 +248,19 @@ download from http://code.google.com/p/gperftools/downloads/list
standard ./configure, make, make install standard ./configure, make, make install
``` ```
### ninja-based LLVM build ### Debug build of CPython
Ninja is supposed to be faster than make; I've only tried it very briefly, and it does seem to be faster when modifying LLVM files. May or may not be worth using; thought I'd jot down my notes though: Having a debug-enabled CPython can be useful for debugging issues with our extension modules. To get it set up:
You may or may not need a more-recent version of ninja than your package manager provides:
``` ```
sudo apt-get install python2.7-dbg
cd ~/pyston_deps cd ~/pyston_deps
git clone https://github.com/martine/ninja.git mkdir python-src
cd ninja cd python-src
git checkout v1.4.0 apt-get source python2.7-dbg
./bootstrap.py
```
```
cd ~/pyston_deps
wget http://www.cmake.org/files/v3.0/cmake-3.0.0.tar.gz
tar zxvf cmake-3.0.0.tar.gz
cd cmake-3.0.0
./configure
make -j4
```
```
cd ~/pyston_deps
mkdir llvm-trunk-cmake
cd llvm-trunk-cmake
CXX=g++ CC=gcc PATH=~/pyston_deps/gcc-4.8.2-install/bin:$PATH:~/pyston_deps/ninja CMAKE_MAKE_PROGRAM=~/pyston_deps/ninja/ninja ~/pyston_deps/cmake-3.0.0/bin/cmake ../llvm-trunk -G Ninja -DLLVM_TARGETS_TO_BUILD=host -DCMAKE_BUILD_TYPE=RELEASE -DLLVM_ENABLE_ASSERTIONS=ON
~/pyston_deps/ninja/ninja # runs in parallel
``` ```
Then, run `make dbgpy_TESTNAME` and it will launch the test under gdb. There's also a [wiki page](https://wiki.python.org/moin/DebuggingWithGdb) with some extra Python-specific GDB commands.
# (Experimental) CMake build system # (Experimental) CMake build system
......
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
#include "classobject.h" #include "classobject.h"
#include "fileobject.h" #include "fileobject.h"
#include "pycapsule.h" #include "pycapsule.h"
#include "traceback.h"
#include "sliceobject.h" #include "sliceobject.h"
#include "iterobject.h" #include "iterobject.h"
#include "descrobject.h" #include "descrobject.h"
...@@ -79,6 +80,7 @@ ...@@ -79,6 +80,7 @@
#include "modsupport.h" #include "modsupport.h"
#include "pythonrun.h" #include "pythonrun.h"
#include "ceval.h" #include "ceval.h"
#include "sysmodule.h"
#include "intrcheck.h" #include "intrcheck.h"
#include "import.h" #include "import.h"
......
...@@ -52,7 +52,8 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void); ...@@ -52,7 +52,8 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void);
_Py_CheckRecursiveCall(where)) _Py_CheckRecursiveCall(where))
#define Py_LeaveRecursiveCall() \ #define Py_LeaveRecursiveCall() \
(--PyThreadState_GET()->recursion_depth) (--PyThreadState_GET()->recursion_depth)
PyAPI_FUNC(int) _Py_CheckRecursiveCall(char *where); // Pyston change: changed this to const char*
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
PyAPI_DATA(int) _Py_CheckRecursionLimit; PyAPI_DATA(int) _Py_CheckRecursionLimit;
#ifdef USE_STACKCHECK #ifdef USE_STACKCHECK
# define _Py_MakeRecCheck(x) (++(x) > --_Py_CheckRecursionLimit) # define _Py_MakeRecCheck(x) (++(x) > --_Py_CheckRecursionLimit)
......
...@@ -37,7 +37,8 @@ PyAPI_DATA(PyTypeObject*) int_cls; ...@@ -37,7 +37,8 @@ PyAPI_DATA(PyTypeObject*) int_cls;
#define PyInt_Type (*int_cls) #define PyInt_Type (*int_cls)
// Pyston changes: these aren't direct macros any more [they potentially could be though] // Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyInt_Check(PyObject*); PyAPI_FUNC(bool) _PyInt_Check(PyObject*);
#define PyInt_Check(op) _PyInt_Check((PyObject*)op)
#if 0 #if 0
#define PyInt_Check(op) \ #define PyInt_Check(op) \
PyType_FastSubclass((op)->ob_type, Py_TPFLAGS_INT_SUBCLASS) PyType_FastSubclass((op)->ob_type, Py_TPFLAGS_INT_SUBCLASS)
......
...@@ -412,7 +412,7 @@ typedef PyObject *(*allocfunc)(PyTypeObject *, Py_ssize_t); ...@@ -412,7 +412,7 @@ typedef PyObject *(*allocfunc)(PyTypeObject *, Py_ssize_t);
struct PyMethodDef *tp_methods;\ struct PyMethodDef *tp_methods;\
struct PyMemberDef *tp_members;\ struct PyMemberDef *tp_members;\
struct PyGetSetDef *tp_getset;\ struct PyGetSetDef *tp_getset;\
struct _typeobject *tp_base;\ PyTypeObject *tp_base;\
PyObject *tp_dict;\ PyObject *tp_dict;\
descrgetfunc tp_descr_get;\ descrgetfunc tp_descr_get;\
descrsetfunc tp_descr_set;\ descrsetfunc tp_descr_set;\
...@@ -456,7 +456,6 @@ struct _typeobject { ...@@ -456,7 +456,6 @@ struct _typeobject {
void* _hcattrs; void* _hcattrs;
char _dep_getattrs[56]; // FIXME: this is hardcoding the size of this particular implementation of std::unordered_map char _dep_getattrs[56]; // FIXME: this is hardcoding the size of this particular implementation of std::unordered_map
char _ics[32]; char _ics[32];
void* _base;
void* _gcvisit_func; void* _gcvisit_func;
int _attrs_offset; int _attrs_offset;
bool _flags[2]; bool _flags[2];
......
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
#define HAVE_INT32_T 1 #define HAVE_INT32_T 1
#define HAVE_INT64_T 1 #define HAVE_INT64_T 1
#define PY_FORMAT_LONG_LONG "ll"
#define PY_FORMAT_SIZE_T "z"
#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1 #define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1
#define WITH_THREAD #define WITH_THREAD
......
...@@ -100,6 +100,8 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**); ...@@ -100,6 +100,8 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**);
/* */ /* */
// Pyston change: made these function calls for now
#if 0
#define PyExceptionClass_Check(x) \ #define PyExceptionClass_Check(x) \
(PyClass_Check((x)) || (PyType_Check((x)) && \ (PyClass_Check((x)) || (PyType_Check((x)) && \
PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS))) PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS)))
...@@ -117,7 +119,12 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**); ...@@ -117,7 +119,12 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**);
((PyInstance_Check((x)) \ ((PyInstance_Check((x)) \
? (PyObject*)((PyInstanceObject*)(x))->in_class \ ? (PyObject*)((PyInstanceObject*)(x))->in_class \
: (PyObject*)((x)->ob_type))) : (PyObject*)((x)->ob_type)))
#endif
// (We might have to make these wrapper macros that do appropriate casting to PyObject)
PyAPI_FUNC(int) PyExceptionClass_Check(PyObject*);
PyAPI_FUNC(int) PyExceptionInstance_Check(PyObject*);
PyAPI_FUNC(const char*) PyExceptionClass_Name(PyObject*);
PyAPI_FUNC(PyObject*) PyExceptionInstance_Class(PyObject*);
/* Predefined exceptions */ /* Predefined exceptions */
...@@ -212,10 +219,9 @@ PyAPI_DATA(PyTypeObject *) VMSError; ...@@ -212,10 +219,9 @@ PyAPI_DATA(PyTypeObject *) VMSError;
#define PyExc_BufferError ((PyObject*)BufferError) #define PyExc_BufferError ((PyObject*)BufferError)
PyAPI_DATA(PyTypeObject *) BufferError; PyAPI_DATA(PyTypeObject *) BufferError;
#define PyExc_MemoryErrorInst ((PyObject*)MemoryErrorInst) PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
PyAPI_DATA(PyTypeObject *) MemoryErrorInst; PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
#define PyExc_RecursionErrorInst ((PyObject*)RecursionErrorInst)
PyAPI_DATA(PyTypeObject *) RecursionErrorInst;
/* Predefined warning categories */ /* Predefined warning categories */
#define PyExc_Warning ((PyObject*)Warning) #define PyExc_Warning ((PyObject*)Warning)
......
...@@ -110,8 +110,13 @@ typedef struct _ts { ...@@ -110,8 +110,13 @@ typedef struct _ts {
} PyThreadState; } PyThreadState;
#endif #endif
struct _PyThreadState; typedef struct _ts {
typedef struct _PyThreadState PyThreadState; int recursion_depth;
PyObject *curexc_type;
PyObject *curexc_value;
PyObject *curexc_traceback;
} PyThreadState;
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void); PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
...@@ -135,7 +140,10 @@ PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *); ...@@ -135,7 +140,10 @@ PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *);
/* Variable and macro for in-line access to current thread state */ /* Variable and macro for in-line access to current thread state */
PyAPI_DATA(PyThreadState *) _PyThreadState_Current; // Pyston change: use our internal name for this
//PyAPI_DATA(PyThreadState *) _PyThreadState_Current;
PyAPI_DATA(__thread PyThreadState) cur_thread_state;
#define _PyThreadState_Current (&cur_thread_state)
#ifdef Py_DEBUG #ifdef Py_DEBUG
#define PyThreadState_GET() PyThreadState_Get() #define PyThreadState_GET() PyThreadState_Get()
......
...@@ -65,7 +65,8 @@ PyAPI_DATA(PyTypeObject*) str_cls; ...@@ -65,7 +65,8 @@ PyAPI_DATA(PyTypeObject*) str_cls;
#define PyString_Type (*str_cls) #define PyString_Type (*str_cls)
// Pyston changes: these aren't direct macros any more [they potentially could be though] // Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyString_Check(PyObject*); PyAPI_FUNC(bool) _PyString_Check(PyObject*);
#define PyString_Check(op) _PyString_Check((PyObject*)op)
#if 0 #if 0
#define PyString_Check(op) \ #define PyString_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_STRING_SUBCLASS) PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_STRING_SUBCLASS)
......
// This file is originally from CPython 2.7, with modifications for Pyston
/* System module interface */
#ifndef Py_SYSMODULE_H
#define Py_SYSMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
// Pyston change: changed most of these to const char*
PyAPI_FUNC(PyObject *) PySys_GetObject(const char *);
PyAPI_FUNC(int) PySys_SetObject(const char *, PyObject *);
PyAPI_FUNC(FILE *) PySys_GetFile(char *, FILE *);
PyAPI_FUNC(void) PySys_SetArgv(int, char **);
PyAPI_FUNC(void) PySys_SetArgvEx(int, char **, int);
PyAPI_FUNC(void) PySys_SetPath(char *);
PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...)
Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...)
Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
PyAPI_FUNC(void) PySys_ResetWarnOptions(void);
PyAPI_FUNC(void) PySys_AddWarnOption(char *);
PyAPI_FUNC(int) PySys_HasWarnOptions(void);
#ifdef __cplusplus
}
#endif
#endif /* !Py_SYSMODULE_H */
// This file is originally from CPython 2.7, with modifications for Pyston
#ifndef Py_TRACEBACK_H
#define Py_TRACEBACK_H
#ifdef __cplusplus
extern "C" {
#endif
struct _frame;
/* Traceback interface */
// Pyston change: not necessarily our object format
#if 0
typedef struct _traceback {
PyObject_HEAD
struct _traceback *tb_next;
struct _frame *tb_frame;
int tb_lasti;
int tb_lineno;
} PyTracebackObject;
#endif
typedef struct _PyTracebackObject PyTracebackObject;
PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *);
PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *);
PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, const char *, int, int);
/* Reveal traceback type so we can typecheck traceback objects */
// Pyston change: not a static type any more
PyAPI_DATA(PyTypeObject*) traceback_cls;
#define PyTraceback_Type (*traceback_cls)
// PyAPI_DATA(PyTypeObject) PyTraceBack_Type;
#define PyTraceBack_Check(v) (Py_TYPE(v) == &PyTraceBack_Type)
#ifdef __cplusplus
}
#endif
#endif /* !Py_TRACEBACK_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "capi/types.h" #include "capi/types.h"
#include "core/threading.h" #include "core/threading.h"
#include "core/types.h" #include "core/types.h"
#include "runtime/classobj.h"
#include "runtime/import.h" #include "runtime/import.h"
#include "runtime/objmodel.h" #include "runtime/objmodel.h"
#include "runtime/types.h" #include "runtime/types.h"
...@@ -65,6 +66,205 @@ static PyObject* objargs_mktuple(va_list va) noexcept { ...@@ -65,6 +66,205 @@ static PyObject* objargs_mktuple(va_list va) noexcept {
return result; return result;
} }
/* isinstance(), issubclass() */
/* abstract_get_bases() has logically 4 return states, with a sort of 0th
* state that will almost never happen.
*
* 0. creating the __bases__ static string could get a MemoryError
* 1. getattr(cls, '__bases__') could raise an AttributeError
* 2. getattr(cls, '__bases__') could raise some other exception
* 3. getattr(cls, '__bases__') could return a tuple
* 4. getattr(cls, '__bases__') could return something other than a tuple
*
* Only state #3 is a non-error state and only it returns a non-NULL object
* (it returns the retrieved tuple).
*
* Any raised AttributeErrors are masked by clearing the exception and
* returning NULL. If an object other than a tuple comes out of __bases__,
* then again, the return value is NULL. So yes, these two situations
* produce exactly the same results: NULL is returned and no error is set.
*
* If some exception other than AttributeError is raised, then NULL is also
* returned, but the exception is not cleared. That's because we want the
* exception to be propagated along.
*
* Callers are expected to test for PyErr_Occurred() when the return value
* is NULL to decide whether a valid exception should be propagated or not.
* When there's no exception to propagate, it's customary for the caller to
* set a TypeError.
*/
static PyObject* abstract_get_bases(PyObject* cls) noexcept {
PyObject* bases;
/*
static PyObject* __bases__ = NULL;
if (__bases__ == NULL) {
__bases__ = PyString_InternFromString("__bases__");
if (__bases__ == NULL)
return NULL;
}
*/
PyObject* __bases__ = boxStrConstant("__bases__");
bases = PyObject_GetAttr(cls, __bases__);
if (bases == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError))
PyErr_Clear();
return NULL;
}
if (!PyTuple_Check(bases)) {
Py_DECREF(bases);
return NULL;
}
return bases;
}
static int abstract_issubclass(PyObject* derived, PyObject* cls) noexcept {
PyObject* bases = NULL;
Py_ssize_t i, n;
int r = 0;
while (1) {
if (derived == cls)
return 1;
bases = abstract_get_bases(derived);
if (bases == NULL) {
if (PyErr_Occurred())
return -1;
return 0;
}
n = PyTuple_GET_SIZE(bases);
if (n == 0) {
Py_DECREF(bases);
return 0;
}
/* Avoid recursivity in the single inheritance case */
if (n == 1) {
derived = PyTuple_GET_ITEM(bases, 0);
Py_DECREF(bases);
continue;
}
for (i = 0; i < n; i++) {
r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls);
if (r != 0)
break;
}
Py_DECREF(bases);
return r;
}
}
static int check_class(PyObject* cls, const char* error) noexcept {
PyObject* bases = abstract_get_bases(cls);
if (bases == NULL) {
/* Do not mask errors. */
if (!PyErr_Occurred())
PyErr_SetString(PyExc_TypeError, error);
return 0;
}
Py_DECREF(bases);
return -1;
}
static int recursive_isinstance(PyObject* inst, PyObject* cls) noexcept {
PyObject* icls;
int retval = 0;
/*
static PyObject* __class__ = NULL;
if (__class__ == NULL) {
__class__ = PyString_InternFromString("__class__");
if (__class__ == NULL)
return -1;
}
*/
PyObject* __class__ = boxStrConstant("__class__");
if (PyClass_Check(cls) && PyInstance_Check(inst)) {
PyObject* inclass = static_cast<BoxedInstance*>(inst)->inst_cls;
retval = PyClass_IsSubclass(inclass, cls);
} else if (PyType_Check(cls)) {
retval = PyObject_TypeCheck(inst, (PyTypeObject*)cls);
if (retval == 0) {
PyObject* c = PyObject_GetAttr(inst, __class__);
if (c == NULL) {
PyErr_Clear();
} else {
if (c != (PyObject*)(inst->cls) && PyType_Check(c))
retval = PyType_IsSubtype((PyTypeObject*)c, (PyTypeObject*)cls);
Py_DECREF(c);
}
}
} else {
if (!check_class(cls, "isinstance() arg 2 must be a class, type,"
" or tuple of classes and types"))
return -1;
icls = PyObject_GetAttr(inst, __class__);
if (icls == NULL) {
PyErr_Clear();
retval = 0;
} else {
retval = abstract_issubclass(icls, cls);
Py_DECREF(icls);
}
}
return retval;
}
extern "C" int PyObject_IsInstance(PyObject* inst, PyObject* cls) {
static PyObject* name = NULL;
/* Quick test for an exact match */
if (Py_TYPE(inst) == (PyTypeObject*)cls)
return 1;
if (PyTuple_Check(cls)) {
Py_ssize_t i;
Py_ssize_t n;
int r = 0;
if (Py_EnterRecursiveCall(" in __instancecheck__"))
return -1;
n = PyTuple_GET_SIZE(cls);
for (i = 0; i < n; ++i) {
PyObject* item = PyTuple_GET_ITEM(cls, i);
r = PyObject_IsInstance(inst, item);
if (r != 0)
/* either found it, or got an error */
break;
}
Py_LeaveRecursiveCall();
return r;
}
if (!(PyClass_Check(cls) || PyInstance_Check(cls))) {
PyObject* checker;
checker = _PyObject_LookupSpecial(cls, "__instancecheck__", &name);
if (checker != NULL) {
PyObject* res;
int ok = -1;
if (Py_EnterRecursiveCall(" in __instancecheck__")) {
Py_DECREF(checker);
return ok;
}
res = PyObject_CallFunctionObjArgs(checker, inst, NULL);
Py_LeaveRecursiveCall();
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Py_DECREF(res);
}
return ok;
} else if (PyErr_Occurred())
return -1;
}
return recursive_isinstance(inst, cls);
}
extern "C" PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, ...) { extern "C" PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, ...) {
PyObject* args, *tmp; PyObject* args, *tmp;
va_list vargs; va_list vargs;
...@@ -83,4 +283,74 @@ extern "C" PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, ...) { ...@@ -83,4 +283,74 @@ extern "C" PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, ...) {
return tmp; return tmp;
} }
static int recursive_issubclass(PyObject* derived, PyObject* cls) noexcept {
int retval;
if (PyType_Check(cls) && PyType_Check(derived)) {
/* Fast path (non-recursive) */
return PyType_IsSubtype((PyTypeObject*)derived, (PyTypeObject*)cls);
}
if (!PyClass_Check(derived) || !PyClass_Check(cls)) {
if (!check_class(derived, "issubclass() arg 1 must be a class"))
return -1;
if (!check_class(cls, "issubclass() arg 2 must be a class"
" or tuple of classes"))
return -1;
retval = abstract_issubclass(derived, cls);
} else {
/* shortcut */
if (!(retval = (derived == cls)))
retval = PyClass_IsSubclass(derived, cls);
}
return retval;
}
extern "C" int PyObject_IsSubclass(PyObject* derived, PyObject* cls) {
static PyObject* name = NULL;
if (PyTuple_Check(cls)) {
Py_ssize_t i;
Py_ssize_t n;
int r = 0;
if (Py_EnterRecursiveCall(" in __subclasscheck__"))
return -1;
n = PyTuple_GET_SIZE(cls);
for (i = 0; i < n; ++i) {
PyObject* item = PyTuple_GET_ITEM(cls, i);
r = PyObject_IsSubclass(derived, item);
if (r != 0)
/* either found it, or got an error */
break;
}
Py_LeaveRecursiveCall();
return r;
}
if (!(PyClass_Check(cls) || PyInstance_Check(cls))) {
PyObject* checker;
checker = _PyObject_LookupSpecial(cls, "__subclasscheck__", &name);
if (checker != NULL) {
PyObject* res;
int ok = -1;
if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
Py_DECREF(checker);
return ok;
}
res = PyObject_CallFunctionObjArgs(checker, derived, NULL);
Py_LeaveRecursiveCall();
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Py_DECREF(res);
}
return ok;
} else if (PyErr_Occurred()) {
return -1;
}
}
return recursive_issubclass(derived, cls);
}
} }
...@@ -117,7 +117,240 @@ extern "C" PyObject* PyErr_SetFromErrnoWithUnicodeFilename(PyObject* exc, const ...@@ -117,7 +117,240 @@ extern "C" PyObject* PyErr_SetFromErrnoWithUnicodeFilename(PyObject* exc, const
} }
#endif /* MS_WINDOWS */ #endif /* MS_WINDOWS */
extern "C" void PyErr_Fetch(PyObject** p_type, PyObject** p_value, PyObject** p_traceback) {
PyThreadState* tstate = PyThreadState_GET();
*p_type = tstate->curexc_type;
*p_value = tstate->curexc_value;
*p_traceback = tstate->curexc_traceback;
tstate->curexc_type = NULL;
tstate->curexc_value = NULL;
tstate->curexc_traceback = NULL;
}
extern "C" PyObject* PyErr_SetFromErrno(PyObject* exc) { extern "C" PyObject* PyErr_SetFromErrno(PyObject* exc) {
return PyErr_SetFromErrnoWithFilenameObject(exc, NULL); return PyErr_SetFromErrnoWithFilenameObject(exc, NULL);
} }
/* Call when an exception has occurred but there is no way for Python
to handle it. Examples: exception in __del__ or during GC. */
extern "C" void PyErr_WriteUnraisable(PyObject* obj) {
PyObject* f, *t, *v, *tb;
PyErr_Fetch(&t, &v, &tb);
f = PySys_GetObject("stderr");
if (f != NULL) {
PyFile_WriteString("Exception ", f);
if (t) {
PyObject* moduleName;
const char* className;
assert(PyExceptionClass_Check(t));
className = PyExceptionClass_Name(t);
if (className != NULL) {
const char* dot = strrchr(className, '.');
if (dot != NULL)
className = dot + 1;
}
moduleName = PyObject_GetAttrString(t, "__module__");
if (moduleName == NULL)
PyFile_WriteString("<unknown>", f);
else {
char* modstr = PyString_AsString(moduleName);
if (modstr && strcmp(modstr, "exceptions") != 0) {
PyFile_WriteString(modstr, f);
PyFile_WriteString(".", f);
}
}
if (className == NULL)
PyFile_WriteString("<unknown>", f);
else
PyFile_WriteString(className, f);
if (v && v != Py_None) {
PyFile_WriteString(": ", f);
PyFile_WriteObject(v, f, 0);
}
Py_XDECREF(moduleName);
}
PyFile_WriteString(" in ", f);
PyFile_WriteObject(obj, f, 0);
PyFile_WriteString(" ignored\n", f);
PyErr_Clear(); /* Just in case */
}
Py_XDECREF(t);
Py_XDECREF(v);
Py_XDECREF(tb);
}
static int parse_syntax_error(PyObject* err, PyObject** message, const char** filename, int* lineno, int* offset,
const char** text) noexcept {
Py_FatalError("unimplemented");
}
static void print_error_text(PyObject* f, int offset, const char* text) noexcept {
Py_FatalError("unimplemented");
}
extern "C" void PyErr_Display(PyObject* exception, PyObject* value, PyObject* tb) {
int err = 0;
PyObject* f = PySys_GetObject("stderr");
Py_INCREF(value);
if (f == NULL || f == Py_None)
fprintf(stderr, "lost sys.stderr\n");
else {
if (Py_FlushLine())
PyErr_Clear();
fflush(stdout);
if (tb && tb != Py_None)
err = PyTraceBack_Print(tb, f);
if (err == 0 && PyObject_HasAttrString(value, "print_file_and_line")) {
PyObject* message;
const char* filename, *text;
int lineno, offset;
if (!parse_syntax_error(value, &message, &filename, &lineno, &offset, &text))
PyErr_Clear();
else {
char buf[10];
PyFile_WriteString(" File \"", f);
if (filename == NULL)
PyFile_WriteString("<string>", f);
else
PyFile_WriteString(filename, f);
PyFile_WriteString("\", line ", f);
PyOS_snprintf(buf, sizeof(buf), "%d", lineno);
PyFile_WriteString(buf, f);
PyFile_WriteString("\n", f);
if (text != NULL)
print_error_text(f, offset, text);
Py_DECREF(value);
value = message;
/* Can't be bothered to check all those
PyFile_WriteString() calls */
if (PyErr_Occurred())
err = -1;
}
}
if (err) {
/* Don't do anything else */
} else if (PyExceptionClass_Check(exception)) {
PyObject* moduleName;
const char* className = PyExceptionClass_Name(exception);
if (className != NULL) {
const char* dot = strrchr(className, '.');
if (dot != NULL)
className = dot + 1;
}
moduleName = PyObject_GetAttrString(exception, "__module__");
if (moduleName == NULL)
err = PyFile_WriteString("<unknown>", f);
else {
char* modstr = PyString_AsString(moduleName);
if (modstr && strcmp(modstr, "exceptions")) {
err = PyFile_WriteString(modstr, f);
err += PyFile_WriteString(".", f);
}
Py_DECREF(moduleName);
}
if (err == 0) {
if (className == NULL)
err = PyFile_WriteString("<unknown>", f);
else
err = PyFile_WriteString(className, f);
}
} else
err = PyFile_WriteObject(exception, f, Py_PRINT_RAW);
if (err == 0 && (value != Py_None)) {
PyObject* s = PyObject_Str(value);
/* only print colon if the str() of the
object is not the empty string
*/
if (s == NULL)
err = -1;
else if (!PyString_Check(s) || PyString_GET_SIZE(s) != 0)
err = PyFile_WriteString(": ", f);
if (err == 0)
err = PyFile_WriteObject(s, f, Py_PRINT_RAW);
Py_XDECREF(s);
}
/* try to write a newline in any case */
err += PyFile_WriteString("\n", f);
}
Py_DECREF(value);
/* If an error happened here, don't show it.
XXX This is wrong, but too many callers rely on this behavior. */
if (err != 0)
PyErr_Clear();
}
static void handle_system_exit(void) noexcept {
Py_FatalError("unimplemented");
}
extern "C" void PyErr_PrintEx(int set_sys_last_vars) {
PyObject* exception, *v, *tb, *hook;
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
handle_system_exit();
}
PyErr_Fetch(&exception, &v, &tb);
if (exception == NULL)
return;
PyErr_NormalizeException(&exception, &v, &tb);
if (exception == NULL)
return;
/* Now we know v != NULL too */
if (set_sys_last_vars) {
PySys_SetObject("last_type", exception);
PySys_SetObject("last_value", v);
PySys_SetObject("last_traceback", tb);
}
hook = PySys_GetObject("excepthook");
if (hook && hook != Py_None) {
PyObject* args = PyTuple_Pack(3, exception, v, tb ? tb : Py_None);
PyObject* result = PyEval_CallObject(hook, args);
if (result == NULL) {
PyObject* exception2, *v2, *tb2;
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
handle_system_exit();
}
PyErr_Fetch(&exception2, &v2, &tb2);
PyErr_NormalizeException(&exception2, &v2, &tb2);
/* It should not be possible for exception2 or v2
to be NULL. However PyErr_Display() can't
tolerate NULLs, so just be safe. */
if (exception2 == NULL) {
exception2 = Py_None;
Py_INCREF(exception2);
}
if (v2 == NULL) {
v2 = Py_None;
Py_INCREF(v2);
}
if (Py_FlushLine())
PyErr_Clear();
fflush(stdout);
PySys_WriteStderr("Error in sys.excepthook:\n");
PyErr_Display(exception2, v2, tb2);
PySys_WriteStderr("\nOriginal exception was:\n");
PyErr_Display(exception, v, tb);
Py_DECREF(exception2);
Py_DECREF(v2);
Py_XDECREF(tb2);
}
Py_XDECREF(result);
Py_XDECREF(args);
} else {
PySys_WriteStderr("sys.excepthook is missing\n");
PyErr_Display(exception, v, tb);
}
Py_XDECREF(exception);
Py_XDECREF(v);
Py_XDECREF(tb);
}
extern "C" void PyErr_Print() {
PyErr_PrintEx(1);
}
} }
...@@ -72,6 +72,29 @@ extern "C" int PyObject_GenericSetAttr(PyObject* obj, PyObject* name, PyObject* ...@@ -72,6 +72,29 @@ extern "C" int PyObject_GenericSetAttr(PyObject* obj, PyObject* name, PyObject*
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" PyObject* PyObject_GetAttrString(PyObject* o, const char* attr) {
// TODO do something like this? not sure if this is safe; will people expect that calling into a known function
// won't end up doing a GIL check?
// threading::GLDemoteRegion _gil_demote;
try {
return getattr(o, attr);
} catch (Box* b) {
PyErr_SetObject(b->cls, b);
return NULL;
}
}
extern "C" int PyObject_HasAttrString(PyObject* v, const char* name) {
PyObject* res = PyObject_GetAttrString(v, name);
if (res != NULL) {
Py_DECREF(res);
return 1;
}
PyErr_Clear();
return 0;
}
extern "C" int PyObject_AsWriteBuffer(PyObject* obj, void** buffer, Py_ssize_t* buffer_len) { extern "C" int PyObject_AsWriteBuffer(PyObject* obj, void** buffer, Py_ssize_t* buffer_len) {
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
......
...@@ -447,7 +447,8 @@ PyObject* slot_tp_repr(PyObject* self) noexcept { ...@@ -447,7 +447,8 @@ PyObject* slot_tp_repr(PyObject* self) noexcept {
try { try {
return repr(self); return repr(self);
} catch (Box* e) { } catch (Box* e) {
abort(); PyErr_SetObject(e->cls, e);
return NULL;
} }
} }
...@@ -455,7 +456,8 @@ PyObject* slot_tp_str(PyObject* self) noexcept { ...@@ -455,7 +456,8 @@ PyObject* slot_tp_str(PyObject* self) noexcept {
try { try {
return str(self); return str(self);
} catch (Box* e) { } catch (Box* e) {
abort(); PyErr_SetObject(e->cls, e);
return NULL;
} }
} }
...@@ -503,7 +505,8 @@ PyObject* slot_tp_call(PyObject* self, PyObject* args, PyObject* kwds) noexcept ...@@ -503,7 +505,8 @@ PyObject* slot_tp_call(PyObject* self, PyObject* args, PyObject* kwds) noexcept
// TODO: runtime ICs? // TODO: runtime ICs?
return runtimeCall(self, ArgPassSpec(0, 0, true, true), args, kwds, NULL, NULL, NULL); return runtimeCall(self, ArgPassSpec(0, 0, true, true), args, kwds, NULL, NULL, NULL);
} catch (Box* e) { } catch (Box* e) {
abort(); PyErr_SetObject(e->cls, e);
return NULL;
} }
} }
...@@ -561,7 +564,8 @@ PyObject* slot_tp_new(PyTypeObject* self, PyObject* args, PyObject* kwds) noexce ...@@ -561,7 +564,8 @@ PyObject* slot_tp_new(PyTypeObject* self, PyObject* args, PyObject* kwds) noexce
return runtimeCall(new_attr, ArgPassSpec(1, 0, true, true), self, args, kwds, NULL, NULL); return runtimeCall(new_attr, ArgPassSpec(1, 0, true, true), self, args, kwds, NULL, NULL);
} catch (Box* e) { } catch (Box* e) {
abort(); PyErr_SetObject(e->cls, e);
return NULL;
} }
} }
...@@ -589,7 +593,8 @@ PyObject* slot_sq_item(PyObject* self, Py_ssize_t i) noexcept { ...@@ -589,7 +593,8 @@ PyObject* slot_sq_item(PyObject* self, Py_ssize_t i) noexcept {
try { try {
return getitem(self, boxInt(i)); return getitem(self, boxInt(i));
} catch (Box* e) { } catch (Box* e) {
abort(); PyErr_SetObject(e->cls, e);
return NULL;
} }
} }
...@@ -891,27 +896,6 @@ static void** slotptr(BoxedClass* type, int offset) { ...@@ -891,27 +896,6 @@ static void** slotptr(BoxedClass* type, int offset) {
return (void**)ptr; return (void**)ptr;
} }
static void update_one_slot(BoxedClass* self, const slotdef& p) {
// TODO: CPython version is significantly more sophisticated
void** ptr = slotptr(self, p.offset);
Box* attr = typeLookup(self, p.name, NULL);
if (!ptr) {
assert(!attr && "I don't think this case should happen? CPython handles it though");
return;
}
if (attr) {
if (attr == None && ptr == (void**)&self->tp_hash) {
*ptr = (void*)&PyObject_HashNotImplemented;
} else {
*ptr = p.function;
}
} else {
*ptr = NULL;
}
}
// Copied from CPython: // Copied from CPython:
#define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ #define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
{ NAME, offsetof(PyTypeObject, SLOT), (void*)(FUNCTION), WRAPPER, PyDoc_STR(DOC), 0 } { NAME, offsetof(PyTypeObject, SLOT), (void*)(FUNCTION), WRAPPER, PyDoc_STR(DOC), 0 }
...@@ -935,91 +919,91 @@ static void update_one_slot(BoxedClass* self, const slotdef& p) { ...@@ -935,91 +919,91 @@ static void update_one_slot(BoxedClass* self, const slotdef& p) {
#define RBINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \ #define RBINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \
ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, "x." NAME "(y) <==> " DOC) ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, "x." NAME "(y) <==> " DOC)
static slotdef slotdefs[] = { static slotdef slotdefs[]
TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc, "x.__repr__() <==> repr(x)"), = { TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc, "x.__repr__() <==> repr(x)"),
TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc, "x.__hash__() <==> hash(x)"), TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc, "x.__hash__() <==> hash(x)"),
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, "x.__call__(...) <==> x(...)", FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, "x.__call__(...) <==> x(...)",
PyWrapperFlag_KEYWORDS), PyWrapperFlag_KEYWORDS),
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc, "x.__str__() <==> str(x)"), TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc, "x.__str__() <==> str(x)"),
TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt, "x.__lt__(y) <==> x<y"), TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt, "x.__lt__(y) <==> x<y"),
TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le, "x.__le__(y) <==> x<=y"), TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le, "x.__le__(y) <==> x<=y"),
TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq, "x.__eq__(y) <==> x==y"), TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq, "x.__eq__(y) <==> x==y"),
TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne, "x.__ne__(y) <==> x!=y"), TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne, "x.__ne__(y) <==> x!=y"),
TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt, "x.__gt__(y) <==> x>y"), TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt, "x.__gt__(y) <==> x>y"),
TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge, "x.__ge__(y) <==> x>=y"), TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge, "x.__ge__(y) <==> x>=y"),
FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init, "x.__init__(...) initializes x; " FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init, "x.__init__(...) initializes x; "
"see help(type(x)) for signature", "see help(type(x)) for signature",
PyWrapperFlag_KEYWORDS), PyWrapperFlag_KEYWORDS),
TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""), TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break] BINSLOT("__add__", nb_add, slot_nb_add, "+"), // [force clang-format to line break]
RBINSLOT("__radd__", nb_add, slot_nb_add, "+"), // RBINSLOT("__radd__", nb_add, slot_nb_add, "+"), //
BINSLOT("__sub__", nb_subtract, slot_nb_subtract, "-"), // BINSLOT("__sub__", nb_subtract, slot_nb_subtract, "-"), //
RBINSLOT("__rsub__", nb_subtract, slot_nb_subtract, "-"), // RBINSLOT("__rsub__", nb_subtract, slot_nb_subtract, "-"), //
BINSLOT("__mul__", nb_multiply, slot_nb_multiply, "*"), // BINSLOT("__mul__", nb_multiply, slot_nb_multiply, "*"), //
RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply, "*"), // RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply, "*"), //
BINSLOT("__div__", nb_divide, slot_nb_divide, "/"), // BINSLOT("__div__", nb_divide, slot_nb_divide, "/"), //
RBINSLOT("__rdiv__", nb_divide, slot_nb_divide, "/"), // RBINSLOT("__rdiv__", nb_divide, slot_nb_divide, "/"), //
BINSLOT("__mod__", nb_remainder, slot_nb_remainder, "%"), // BINSLOT("__mod__", nb_remainder, slot_nb_remainder, "%"), //
RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder, "%"), // RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder, "%"), //
BINSLOTNOTINFIX("__divmod__", nb_divmod, slot_nb_divmod, "divmod(x, y)"), BINSLOTNOTINFIX("__divmod__", nb_divmod, slot_nb_divmod, "divmod(x, y)"),
RBINSLOTNOTINFIX("__rdivmod__", nb_divmod, slot_nb_divmod, "divmod(y, x)"), RBINSLOTNOTINFIX("__rdivmod__", nb_divmod, slot_nb_divmod, "divmod(y, x)"),
NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc, "x.__pow__(y[, z]) <==> pow(x, y[, z])"), NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc, "x.__pow__(y[, z]) <==> pow(x, y[, z])"),
NBSLOT("__rpow__", nb_power, slot_nb_power, wrap_ternaryfunc_r, "y.__rpow__(x[, z]) <==> pow(x, y[, z])"), NBSLOT("__rpow__", nb_power, slot_nb_power, wrap_ternaryfunc_r, "y.__rpow__(x[, z]) <==> pow(x, y[, z])"),
UNSLOT("__neg__", nb_negative, slot_nb_negative, wrap_unaryfunc, "-x"), // UNSLOT("__neg__", nb_negative, slot_nb_negative, wrap_unaryfunc, "-x"), //
UNSLOT("__pos__", nb_positive, slot_nb_positive, wrap_unaryfunc, "+x"), // UNSLOT("__pos__", nb_positive, slot_nb_positive, wrap_unaryfunc, "+x"), //
UNSLOT("__abs__", nb_absolute, slot_nb_absolute, wrap_unaryfunc, "abs(x)"), // UNSLOT("__abs__", nb_absolute, slot_nb_absolute, wrap_unaryfunc, "abs(x)"), //
UNSLOT("__nonzero__", nb_nonzero, slot_nb_nonzero, wrap_inquirypred, "x != 0"), // UNSLOT("__nonzero__", nb_nonzero, slot_nb_nonzero, wrap_inquirypred, "x != 0"), //
UNSLOT("__invert__", nb_invert, slot_nb_invert, wrap_unaryfunc, "~x"), // UNSLOT("__invert__", nb_invert, slot_nb_invert, wrap_unaryfunc, "~x"), //
BINSLOT("__lshift__", nb_lshift, slot_nb_lshift, "<<"), // BINSLOT("__lshift__", nb_lshift, slot_nb_lshift, "<<"), //
RBINSLOT("__rlshift__", nb_lshift, slot_nb_lshift, "<<"), // RBINSLOT("__rlshift__", nb_lshift, slot_nb_lshift, "<<"), //
BINSLOT("__rshift__", nb_rshift, slot_nb_rshift, ">>"), // BINSLOT("__rshift__", nb_rshift, slot_nb_rshift, ">>"), //
RBINSLOT("__rrshift__", nb_rshift, slot_nb_rshift, ">>"), // RBINSLOT("__rrshift__", nb_rshift, slot_nb_rshift, ">>"), //
BINSLOT("__and__", nb_and, slot_nb_and, "&"), // BINSLOT("__and__", nb_and, slot_nb_and, "&"), //
RBINSLOT("__rand__", nb_and, slot_nb_and, "&"), // RBINSLOT("__rand__", nb_and, slot_nb_and, "&"), //
BINSLOT("__xor__", nb_xor, slot_nb_xor, "^"), // BINSLOT("__xor__", nb_xor, slot_nb_xor, "^"), //
RBINSLOT("__rxor__", nb_xor, slot_nb_xor, "^"), // RBINSLOT("__rxor__", nb_xor, slot_nb_xor, "^"), //
BINSLOT("__or__", nb_or, slot_nb_or, "|"), // BINSLOT("__or__", nb_or, slot_nb_or, "|"), //
RBINSLOT("__ror__", nb_or, slot_nb_or, "|"), // RBINSLOT("__ror__", nb_or, slot_nb_or, "|"), //
UNSLOT("__int__", nb_int, slot_nb_int, wrap_unaryfunc, "int(x)"), // UNSLOT("__int__", nb_int, slot_nb_int, wrap_unaryfunc, "int(x)"), //
UNSLOT("__long__", nb_long, slot_nb_long, wrap_unaryfunc, "long(x)"), // UNSLOT("__long__", nb_long, slot_nb_long, wrap_unaryfunc, "long(x)"), //
UNSLOT("__float__", nb_float, slot_nb_float, wrap_unaryfunc, "float(x)"), // UNSLOT("__float__", nb_float, slot_nb_float, wrap_unaryfunc, "float(x)"), //
UNSLOT("__oct__", nb_oct, slot_nb_oct, wrap_unaryfunc, "oct(x)"), // UNSLOT("__oct__", nb_oct, slot_nb_oct, wrap_unaryfunc, "oct(x)"), //
UNSLOT("__hex__", nb_hex, slot_nb_hex, wrap_unaryfunc, "hex(x)"), // UNSLOT("__hex__", nb_hex, slot_nb_hex, wrap_unaryfunc, "hex(x)"), //
MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc, "x.__len__() <==> len(x)"), MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc, "x.__len__() <==> len(x)"),
MPSLOT("__getitem__", mp_subscript, slot_mp_subscript, wrap_binaryfunc, "x.__getitem__(y) <==> x[y]"), MPSLOT("__getitem__", mp_subscript, slot_mp_subscript, wrap_binaryfunc, "x.__getitem__(y) <==> x[y]"),
MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_objobjargproc, MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_objobjargproc,
"x.__setitem__(i, y) <==> x[i]=y"), "x.__setitem__(i, y) <==> x[i]=y"),
MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_delitem, "x.__delitem__(y) <==> del x[y]"), MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript, wrap_delitem, "x.__delitem__(y) <==> del x[y]"),
SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc, "x.__len__() <==> len(x)"), SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc, "x.__len__() <==> len(x)"),
/* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL. /* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
The logic in abstract.c always falls back to nb_add/nb_multiply in The logic in abstract.c always falls back to nb_add/nb_multiply in
this case. Defining both the nb_* and the sq_* slots to call the this case. Defining both the nb_* and the sq_* slots to call the
user-defined methods has unexpected side-effects, as shown by user-defined methods has unexpected side-effects, as shown by
test_descr.notimplemented() */ test_descr.notimplemented() */
SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, "x.__add__(y) <==> x+y"), SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc, "x.__add__(y) <==> x+y"),
SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, "x.__mul__(n) <==> x*n"), SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc, "x.__mul__(n) <==> x*n"),
SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc, "x.__rmul__(n) <==> n*x"), SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc, "x.__rmul__(n) <==> n*x"),
SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, "x.__getitem__(y) <==> x[y]"), SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, "x.__getitem__(y) <==> x[y]"),
SQSLOT("__getslice__", sq_slice, slot_sq_slice, wrap_ssizessizeargfunc, "x.__getslice__(i, j) <==> x[i:j]\n\ SQSLOT("__getslice__", sq_slice, slot_sq_slice, wrap_ssizessizeargfunc, "x.__getslice__(i, j) <==> x[i:j]\n\
\n\ \n\
Use of negative indices is not supported."), Use of negative indices is not supported."),
SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem, "x.__setitem__(i, y) <==> x[i]=y"), SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem, "x.__setitem__(i, y) <==> x[i]=y"),
SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem, "x.__delitem__(y) <==> del x[y]"), SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem, "x.__delitem__(y) <==> del x[y]"),
SQSLOT("__setslice__", sq_ass_slice, slot_sq_ass_slice, wrap_ssizessizeobjargproc, SQSLOT("__setslice__", sq_ass_slice, slot_sq_ass_slice, wrap_ssizessizeobjargproc,
"x.__setslice__(i, j, y) <==> x[i:j]=y\n\ "x.__setslice__(i, j, y) <==> x[i:j]=y\n\
\n\ \n\
Use of negative indices is not supported."), Use of negative indices is not supported."),
SQSLOT("__delslice__", sq_ass_slice, slot_sq_ass_slice, wrap_delslice, "x.__delslice__(i, j) <==> del x[i:j]\n\ SQSLOT("__delslice__", sq_ass_slice, slot_sq_ass_slice, wrap_delslice, "x.__delslice__(i, j) <==> del x[i:j]\n\
\n\ \n\
Use of negative indices is not supported."), Use of negative indices is not supported."),
SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc, "x.__contains__(y) <==> y in x"), SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc, "x.__contains__(y) <==> y in x"),
SQSLOT("__iadd__", sq_inplace_concat, NULL, wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"), SQSLOT("__iadd__", sq_inplace_concat, NULL, wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"),
SQSLOT("__imul__", sq_inplace_repeat, NULL, wrap_indexargfunc, "x.__imul__(y) <==> x*=y"), SQSLOT("__imul__", sq_inplace_repeat, NULL, wrap_indexargfunc, "x.__imul__(y) <==> x*=y"),
}; { NULL, 0, NULL, NULL, NULL, 0 } };
static void init_slotdefs() { static void init_slotdefs() {
static bool initialized = false; static bool initialized = false;
...@@ -1028,6 +1012,9 @@ static void init_slotdefs() { ...@@ -1028,6 +1012,9 @@ static void init_slotdefs() {
for (int i = 0; i < sizeof(slotdefs) / sizeof(slotdefs[0]); i++) { for (int i = 0; i < sizeof(slotdefs) / sizeof(slotdefs[0]); i++) {
if (i > 0) { if (i > 0) {
if (!slotdefs[i].name)
continue;
#ifndef NDEBUG #ifndef NDEBUG
if (slotdefs[i - 1].offset > slotdefs[i].offset) { if (slotdefs[i - 1].offset > slotdefs[i].offset) {
printf("slotdef for %s in the wrong place\n", slotdefs[i - 1].name); printf("slotdef for %s in the wrong place\n", slotdefs[i - 1].name);
...@@ -1047,12 +1034,132 @@ static void init_slotdefs() { ...@@ -1047,12 +1034,132 @@ static void init_slotdefs() {
initialized = true; initialized = true;
} }
/* Length of array of slotdef pointers used to store slots with the
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
the same __name__, for any __name__. Since that's a static property, it is
appropriate to declare fixed-size arrays for this. */
#define MAX_EQUIV 10
/* Return a slot pointer for a given name, but ONLY if the attribute has
exactly one slot function. The name must be an interned string. */
static void** resolve_slotdups(PyTypeObject* type, const std::string& name) {
/* XXX Maybe this could be optimized more -- but is it worth it? */
/* pname and ptrs act as a little cache */
static std::string pname;
static slotdef* ptrs[MAX_EQUIV];
slotdef* p, **pp;
void** res, **ptr;
if (pname != name) {
/* Collect all slotdefs that match name into ptrs. */
pname = name;
pp = ptrs;
for (p = slotdefs; p->name; p++) {
if (p->name == name)
*pp++ = p;
}
*pp = NULL;
}
/* Look in all matching slots of the type; if exactly one of these has
a filled-in slot, return its value. Otherwise return NULL. */
res = NULL;
for (pp = ptrs; *pp; pp++) {
ptr = slotptr(type, (*pp)->offset);
if (ptr == NULL || *ptr == NULL)
continue;
if (res != NULL)
return NULL;
res = ptr;
}
return res;
}
static const slotdef* update_one_slot(BoxedClass* type, const slotdef* p) {
assert(p->name);
PyObject* descr;
BoxedWrapperDescriptor* d;
void* generic = NULL, * specific = NULL;
int use_generic = 0;
int offset = p->offset;
void** ptr = slotptr(type, offset);
if (ptr == NULL) {
do {
++p;
} while (p->offset == offset);
return p;
}
do {
descr = typeLookup(type, p->name, NULL);
if (descr == NULL) {
if (ptr == (void**)&type->tp_iternext) {
specific = (void*)_PyObject_NextNotImplemented;
}
continue;
}
if (Py_TYPE(descr) == wrapperdescr_cls
&& ((BoxedWrapperDescriptor*)descr)->wrapper->name == std::string(p->name)) {
void** tptr = resolve_slotdups(type, p->name);
if (tptr == NULL || tptr == ptr)
generic = p->function;
d = (BoxedWrapperDescriptor*)descr;
if (d->wrapper->wrapper == p->wrapper && PyType_IsSubtype(type, d->type)) {
if (specific == NULL || specific == d->wrapped)
specific = d->wrapped;
else
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
&& ptr == (void**)&type->tp_new) {
/* The __new__ wrapper is not a wrapper descriptor,
so must be special-cased differently.
If we don't do this, creating an instance will
always use slot_tp_new which will look up
__new__ in the MRO which will call tp_new_wrapper
which will look through the base classes looking
for a static base and call its tp_new (usually
PyType_GenericNew), after performing various
sanity checks and constructing a new argument
list. Cut all that nonsense short -- this speeds
up instance creation tremendously. */
specific = (void*)type->tp_new;
/* XXX I'm not 100% sure that there isn't a hole
in this reasoning that requires additional
sanity checks. I'll buy the first person to
point out a bug in this reasoning a beer. */
#endif
} else if (descr == Py_None && ptr == (void**)&type->tp_hash) {
/* We specifically allow __hash__ to be set to None
to prevent inheritance of the default
implementation from object.__hash__ */
specific = (void*)PyObject_HashNotImplemented;
} else {
use_generic = 1;
generic = p->function;
}
} while ((++p)->offset == offset);
if (specific && !use_generic)
*ptr = specific;
else
*ptr = generic;
return p;
}
bool update_slot(BoxedClass* self, const std::string& attr) { bool update_slot(BoxedClass* self, const std::string& attr) {
bool updated = false; bool updated = false;
for (const slotdef& p : slotdefs) { for (const slotdef& p : slotdefs) {
if (!p.name)
continue;
if (p.name == attr) { if (p.name == attr) {
// TODO update subclasses; // TODO update subclasses;
update_one_slot(self, p); update_one_slot(self, &p);
updated = true; updated = true;
} }
} }
...@@ -1062,9 +1169,9 @@ bool update_slot(BoxedClass* self, const std::string& attr) { ...@@ -1062,9 +1169,9 @@ bool update_slot(BoxedClass* self, const std::string& attr) {
void fixup_slot_dispatchers(BoxedClass* self) { void fixup_slot_dispatchers(BoxedClass* self) {
init_slotdefs(); init_slotdefs();
for (const slotdef& p : slotdefs) { const slotdef* p = slotdefs;
update_one_slot(self, p); while (p->name)
} p = update_one_slot(self, p);
// TODO: CPython handles this by having the __name__ attribute wrap (via a getset object) // TODO: CPython handles this by having the __name__ attribute wrap (via a getset object)
// the tp_name field, whereas we're (needlessly?) doing the opposite. // the tp_name field, whereas we're (needlessly?) doing the opposite.
...@@ -1131,6 +1238,321 @@ extern "C" int PyType_IsSubtype(PyTypeObject* a, PyTypeObject* b) { ...@@ -1131,6 +1238,321 @@ extern "C" int PyType_IsSubtype(PyTypeObject* a, PyTypeObject* b) {
return isSubclass(a, b); return isSubclass(a, b);
} }
#define BUFFER_FLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER)
// This is copied from CPython with some modifications:
static void inherit_special(PyTypeObject* type, PyTypeObject* base) {
Py_ssize_t oldsize, newsize;
/* Special flag magic */
if (!type->tp_as_buffer && base->tp_as_buffer) {
type->tp_flags &= ~BUFFER_FLAGS;
type->tp_flags |= base->tp_flags & BUFFER_FLAGS;
}
if (!type->tp_as_sequence && base->tp_as_sequence) {
type->tp_flags &= ~Py_TPFLAGS_HAVE_SEQUENCE_IN;
type->tp_flags |= base->tp_flags & Py_TPFLAGS_HAVE_SEQUENCE_IN;
}
if ((type->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS) != (base->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS)) {
if ((!type->tp_as_number && base->tp_as_number) || (!type->tp_as_sequence && base->tp_as_sequence)) {
type->tp_flags &= ~Py_TPFLAGS_HAVE_INPLACEOPS;
if (!type->tp_as_number && !type->tp_as_sequence) {
type->tp_flags |= base->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS;
}
}
/* Wow */
}
if (!type->tp_as_number && base->tp_as_number) {
type->tp_flags &= ~Py_TPFLAGS_CHECKTYPES;
type->tp_flags |= base->tp_flags & Py_TPFLAGS_CHECKTYPES;
}
/* Copying basicsize is connected to the GC flags */
oldsize = base->tp_basicsize;
newsize = type->tp_basicsize ? type->tp_basicsize : oldsize;
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC) && (base->tp_flags & Py_TPFLAGS_HAVE_GC)
&& (type->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE /*GC slots exist*/)
&& (!type->tp_traverse && !type->tp_clear)) {
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
if (type->tp_traverse == NULL)
type->tp_traverse = base->tp_traverse;
if (type->tp_clear == NULL)
type->tp_clear = base->tp_clear;
}
if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) {
/* The condition below could use some explanation.
It appears that tp_new is not inherited for static types
whose base class is 'object'; this seems to be a precaution
so that old extension types don't suddenly become
callable (object.__new__ wouldn't insure the invariants
that the extension type's own factory function ensures).
Heap types, of course, are under our control, so they do
inherit tp_new; static extension types that specify some
other built-in type as the default are considered
new-style-aware so they also inherit object.__new__. */
if (base != object_cls || (type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
if (type->tp_new == NULL)
type->tp_new = base->tp_new;
}
}
type->tp_basicsize = newsize;
/* Copy other non-function slots */
#undef COPYVAL
#define COPYVAL(SLOT) \
if (type->SLOT == 0) \
type->SLOT = base->SLOT
COPYVAL(tp_itemsize);
if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_WEAKREFS) {
COPYVAL(tp_weaklistoffset);
}
if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) {
COPYVAL(tp_dictoffset);
}
// Pyston change: are not using these for now:
#if 0
/* Setup fast subclass flags */
if (PyType_IsSubtype(base, (PyTypeObject*)PyExc_BaseException))
type->tp_flags |= Py_TPFLAGS_BASE_EXC_SUBCLASS;
else if (PyType_IsSubtype(base, &PyType_Type))
type->tp_flags |= Py_TPFLAGS_TYPE_SUBCLASS;
else if (PyType_IsSubtype(base, &PyInt_Type))
type->tp_flags |= Py_TPFLAGS_INT_SUBCLASS;
else if (PyType_IsSubtype(base, &PyLong_Type))
type->tp_flags |= Py_TPFLAGS_LONG_SUBCLASS;
else if (PyType_IsSubtype(base, &PyString_Type))
type->tp_flags |= Py_TPFLAGS_STRING_SUBCLASS;
#ifdef Py_USING_UNICODE
else if (PyType_IsSubtype(base, &PyUnicode_Type))
type->tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS;
#endif
else if (PyType_IsSubtype(base, &PyTuple_Type))
type->tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS;
else if (PyType_IsSubtype(base, &PyList_Type))
type->tp_flags |= Py_TPFLAGS_LIST_SUBCLASS;
else if (PyType_IsSubtype(base, &PyDict_Type))
type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS;
#endif
}
static int overrides_name(PyTypeObject* type, const char* name) {
PyObject* dict = type->tp_dict;
assert(dict != NULL);
if (PyDict_GetItemString(dict, name) != NULL) {
return 1;
}
return 0;
}
#define OVERRIDES_HASH(x) overrides_name(x, "__hash__")
#define OVERRIDES_EQ(x) overrides_name(x, "__eq__")
static void inherit_slots(PyTypeObject* type, PyTypeObject* base) {
// Pyston addition:
if (base->tp_base == NULL)
assert(base == object_cls);
PyTypeObject* basebase;
#undef SLOTDEFINED
#undef COPYSLOT
#undef COPYNUM
#undef COPYSEQ
#undef COPYMAP
#undef COPYBUF
#define SLOTDEFINED(SLOT) (base->SLOT != 0 && (basebase == NULL || base->SLOT != basebase->SLOT))
#define COPYSLOT(SLOT) \
if (!type->SLOT && SLOTDEFINED(SLOT)) \
type->SLOT = base->SLOT
#define COPYNUM(SLOT) COPYSLOT(tp_as_number->SLOT)
#define COPYSEQ(SLOT) COPYSLOT(tp_as_sequence->SLOT)
#define COPYMAP(SLOT) COPYSLOT(tp_as_mapping->SLOT)
#define COPYBUF(SLOT) COPYSLOT(tp_as_buffer->SLOT)
/* This won't inherit indirect slots (from tp_as_number etc.)
if type doesn't provide the space. */
if (type->tp_as_number != NULL && base->tp_as_number != NULL) {
basebase = base->tp_base;
if (basebase->tp_as_number == NULL)
basebase = NULL;
COPYNUM(nb_add);
COPYNUM(nb_subtract);
COPYNUM(nb_multiply);
COPYNUM(nb_divide);
COPYNUM(nb_remainder);
COPYNUM(nb_divmod);
COPYNUM(nb_power);
COPYNUM(nb_negative);
COPYNUM(nb_positive);
COPYNUM(nb_absolute);
COPYNUM(nb_nonzero);
COPYNUM(nb_invert);
COPYNUM(nb_lshift);
COPYNUM(nb_rshift);
COPYNUM(nb_and);
COPYNUM(nb_xor);
COPYNUM(nb_or);
COPYNUM(nb_coerce);
COPYNUM(nb_int);
COPYNUM(nb_long);
COPYNUM(nb_float);
COPYNUM(nb_oct);
COPYNUM(nb_hex);
COPYNUM(nb_inplace_add);
COPYNUM(nb_inplace_subtract);
COPYNUM(nb_inplace_multiply);
COPYNUM(nb_inplace_divide);
COPYNUM(nb_inplace_remainder);
COPYNUM(nb_inplace_power);
COPYNUM(nb_inplace_lshift);
COPYNUM(nb_inplace_rshift);
COPYNUM(nb_inplace_and);
COPYNUM(nb_inplace_xor);
COPYNUM(nb_inplace_or);
if (base->tp_flags & Py_TPFLAGS_CHECKTYPES) {
COPYNUM(nb_true_divide);
COPYNUM(nb_floor_divide);
COPYNUM(nb_inplace_true_divide);
COPYNUM(nb_inplace_floor_divide);
}
if (base->tp_flags & Py_TPFLAGS_HAVE_INDEX) {
COPYNUM(nb_index);
}
}
if (type->tp_as_sequence != NULL && base->tp_as_sequence != NULL) {
basebase = base->tp_base;
if (basebase->tp_as_sequence == NULL)
basebase = NULL;
COPYSEQ(sq_length);
COPYSEQ(sq_concat);
COPYSEQ(sq_repeat);
COPYSEQ(sq_item);
COPYSEQ(sq_slice);
COPYSEQ(sq_ass_item);
COPYSEQ(sq_ass_slice);
COPYSEQ(sq_contains);
COPYSEQ(sq_inplace_concat);
COPYSEQ(sq_inplace_repeat);
}
if (type->tp_as_mapping != NULL && base->tp_as_mapping != NULL) {
basebase = base->tp_base;
if (basebase->tp_as_mapping == NULL)
basebase = NULL;
COPYMAP(mp_length);
COPYMAP(mp_subscript);
COPYMAP(mp_ass_subscript);
}
if (type->tp_as_buffer != NULL && base->tp_as_buffer != NULL) {
basebase = base->tp_base;
if (basebase->tp_as_buffer == NULL)
basebase = NULL;
COPYBUF(bf_getreadbuffer);
COPYBUF(bf_getwritebuffer);
COPYBUF(bf_getsegcount);
COPYBUF(bf_getcharbuffer);
COPYBUF(bf_getbuffer);
COPYBUF(bf_releasebuffer);
}
basebase = base->tp_base;
COPYSLOT(tp_dealloc);
COPYSLOT(tp_print);
if (type->tp_getattr == NULL && type->tp_getattro == NULL) {
type->tp_getattr = base->tp_getattr;
type->tp_getattro = base->tp_getattro;
}
if (type->tp_setattr == NULL && type->tp_setattro == NULL) {
type->tp_setattr = base->tp_setattr;
type->tp_setattro = base->tp_setattro;
}
/* tp_compare see tp_richcompare */
COPYSLOT(tp_repr);
/* tp_hash see tp_richcompare */
COPYSLOT(tp_call);
COPYSLOT(tp_str);
if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE) {
if (type->tp_compare == NULL && type->tp_richcompare == NULL && type->tp_hash == NULL) {
type->tp_compare = base->tp_compare;
type->tp_richcompare = base->tp_richcompare;
type->tp_hash = base->tp_hash;
/* Check for changes to inherited methods in Py3k*/
if (Py_Py3kWarningFlag) {
if (base->tp_hash && (base->tp_hash != PyObject_HashNotImplemented) && !OVERRIDES_HASH(type)) {
if (OVERRIDES_EQ(type)) {
if (PyErr_WarnPy3k("Overriding "
"__eq__ blocks inheritance "
"of __hash__ in 3.x",
1) < 0)
/* XXX This isn't right. If the warning is turned
into an exception, we should be communicating
the error back to the caller, but figuring out
how to clean up in that case is tricky. See
issue 8627 for more. */
PyErr_Clear();
}
}
}
}
} else {
COPYSLOT(tp_compare);
}
if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_ITER) {
COPYSLOT(tp_iter);
COPYSLOT(tp_iternext);
}
if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) {
COPYSLOT(tp_descr_get);
COPYSLOT(tp_descr_set);
COPYSLOT(tp_dictoffset);
COPYSLOT(tp_init);
COPYSLOT(tp_alloc);
COPYSLOT(tp_is_gc);
if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) == (base->tp_flags & Py_TPFLAGS_HAVE_GC)) {
/* They agree about gc. */
COPYSLOT(tp_free);
} else if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) && type->tp_free == NULL && base->tp_free == _PyObject_Del) {
/* A bit of magic to plug in the correct default
* tp_free function when a derived class adds gc,
* didn't define tp_free, and the base uses the
* default non-gc tp_free.
*/
type->tp_free = PyObject_GC_Del;
}
/* else they didn't agree about gc, and there isn't something
* obvious to be done -- the type is on its own.
*/
}
}
// PystonType_Ready is for the common code between PyType_Ready (which is just for extension classes)
// and our internal type-creation endpoints (BoxedClass::BoxedClass()).
// TODO: Move more of the duplicated logic into here.
void PystonType_Ready(BoxedClass* cls) {
inherit_special(cls, cls->tp_base);
// This is supposed to be over the MRO but we don't support multiple inheritance yet:
BoxedClass* b = cls->tp_base;
while (b) {
// Not sure when this could fail; maybe not in Pyston right now but apparently it can in CPython:
if (PyType_Check(b))
inherit_slots(cls, b);
b = b->tp_base;
}
}
extern "C" int PyType_Ready(PyTypeObject* cls) { extern "C" int PyType_Ready(PyTypeObject* cls) {
gc::registerNonheapRootObject(cls); gc::registerNonheapRootObject(cls);
...@@ -1174,13 +1596,11 @@ extern "C" int PyType_Ready(PyTypeObject* cls) { ...@@ -1174,13 +1596,11 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
RELEASE_ASSERT(cls->tp_iter == NULL, ""); RELEASE_ASSERT(cls->tp_iter == NULL, "");
RELEASE_ASSERT(cls->tp_iternext == NULL, ""); RELEASE_ASSERT(cls->tp_iternext == NULL, "");
RELEASE_ASSERT(cls->tp_base == NULL, "");
RELEASE_ASSERT(cls->tp_descr_get == NULL, ""); RELEASE_ASSERT(cls->tp_descr_get == NULL, "");
RELEASE_ASSERT(cls->tp_descr_set == NULL, ""); RELEASE_ASSERT(cls->tp_descr_set == NULL, "");
RELEASE_ASSERT(cls->tp_alloc == NULL || cls->tp_alloc == PyType_GenericAlloc, ""); RELEASE_ASSERT(cls->tp_alloc == NULL || cls->tp_alloc == PyType_GenericAlloc, "");
RELEASE_ASSERT(cls->tp_free == NULL || cls->tp_free == PyObject_Del, ""); RELEASE_ASSERT(cls->tp_free == NULL || cls->tp_free == PyObject_Del, "");
RELEASE_ASSERT(cls->tp_is_gc == NULL, ""); RELEASE_ASSERT(cls->tp_is_gc == NULL, "");
RELEASE_ASSERT(cls->tp_base == NULL, "");
RELEASE_ASSERT(cls->tp_mro == NULL, ""); RELEASE_ASSERT(cls->tp_mro == NULL, "");
RELEASE_ASSERT(cls->tp_cache == NULL, ""); RELEASE_ASSERT(cls->tp_cache == NULL, "");
RELEASE_ASSERT(cls->tp_subclasses == NULL, ""); RELEASE_ASSERT(cls->tp_subclasses == NULL, "");
...@@ -1198,9 +1618,11 @@ extern "C" int PyType_Ready(PyTypeObject* cls) { ...@@ -1198,9 +1618,11 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
INITIALIZE(cls->dependent_icgetattrs); INITIALIZE(cls->dependent_icgetattrs);
#undef INITIALIZE #undef INITIALIZE
BoxedClass* base = cls->base = object_cls; BoxedClass* base = cls->tp_base;
if (base == NULL)
base = cls->tp_base = object_cls;
if (!cls->cls) if (!cls->cls)
cls->cls = cls->base->cls; cls->cls = cls->tp_base->cls;
assert(cls->tp_dict == NULL); assert(cls->tp_dict == NULL);
cls->tp_dict = makeAttrWrapper(cls); cls->tp_dict = makeAttrWrapper(cls);
...@@ -1237,6 +1659,8 @@ extern "C" int PyType_Ready(PyTypeObject* cls) { ...@@ -1237,6 +1659,8 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
printf("warning: ignoring tp_getset for now\n"); printf("warning: ignoring tp_getset for now\n");
} }
PystonType_Ready(cls);
cls->gc_visit = &conservativeGCHandler; cls->gc_visit = &conservativeGCHandler;
cls->is_user_defined = true; cls->is_user_defined = true;
......
...@@ -23,6 +23,7 @@ namespace pyston { ...@@ -23,6 +23,7 @@ namespace pyston {
bool update_slot(BoxedClass* self, const std::string& attr); bool update_slot(BoxedClass* self, const std::string& attr);
void fixup_slot_dispatchers(BoxedClass* self); void fixup_slot_dispatchers(BoxedClass* self);
void PystonType_Ready(BoxedClass* cls);
} }
#endif #endif
...@@ -205,6 +205,10 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E ...@@ -205,6 +205,10 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
long us = _t.end(); long us = _t.end();
static StatCounter us_compiling("us_compiling"); static StatCounter us_compiling("us_compiling");
us_compiling.log(us); us_compiling.log(us);
if (VERBOSITY() >= 1 && us > 100000) {
printf("Took %ldms to compile %s::%s!\n", us / 1000, source->parent_module->fn.c_str(), name.c_str());
}
static StatCounter num_compiles("num_compiles"); static StatCounter num_compiles("num_compiles");
num_compiles.log(); num_compiles.log();
......
...@@ -34,8 +34,10 @@ ...@@ -34,8 +34,10 @@
namespace pyston { namespace pyston {
namespace threading { namespace threading {
__thread ThreadState cur_thread_state extern "C" {
= { NULL, NULL, NULL }; // not sure if we need to explicitly request zero-initialization __thread PyThreadState cur_thread_state
= { 0, NULL, NULL, NULL }; // not sure if we need to explicitly request zero-initialization
}
PthreadFastMutex threading_lock; PthreadFastMutex threading_lock;
...@@ -75,9 +77,9 @@ public: ...@@ -75,9 +77,9 @@ public:
std::vector<StackInfo> previous_stacks; std::vector<StackInfo> previous_stacks;
pthread_t pthread_id; pthread_t pthread_id;
ThreadState* public_thread_state; PyThreadState* public_thread_state;
ThreadStateInternal(void* stack_start, pthread_t pthread_id, ThreadState* public_thread_state) ThreadStateInternal(void* stack_start, pthread_t pthread_id, PyThreadState* public_thread_state)
: saved(false), stack_start(stack_start), pthread_id(pthread_id), public_thread_state(public_thread_state) {} : saved(false), stack_start(stack_start), pthread_id(pthread_id), public_thread_state(public_thread_state) {}
void saveCurrent() { void saveCurrent() {
...@@ -111,12 +113,12 @@ public: ...@@ -111,12 +113,12 @@ public:
void accept(gc::GCVisitor* v) { void accept(gc::GCVisitor* v) {
auto pub_state = public_thread_state; auto pub_state = public_thread_state;
if (pub_state->exc_type) if (pub_state->curexc_type)
v->visit(pub_state->exc_type); v->visit(pub_state->curexc_type);
if (pub_state->exc_value) if (pub_state->curexc_value)
v->visit(pub_state->exc_value); v->visit(pub_state->curexc_value);
if (pub_state->exc_traceback) if (pub_state->curexc_traceback)
v->visit(pub_state->exc_traceback); v->visit(pub_state->curexc_traceback);
for (auto& stack_info : previous_stacks) { for (auto& stack_info : previous_stacks) {
v->visit(stack_info.next_generator); v->visit(stack_info.next_generator);
......
...@@ -36,11 +36,6 @@ namespace threading { ...@@ -36,11 +36,6 @@ namespace threading {
// Whether or not a second thread was ever started: // Whether or not a second thread was ever started:
bool threadWasStarted(); bool threadWasStarted();
struct ThreadState {
Box* exc_type, *exc_value, *exc_traceback;
};
extern __thread ThreadState cur_thread_state;
// returns a thread id (currently, the pthread_t id) // returns a thread id (currently, the pthread_t id)
intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg2, Box* arg3); intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg2, Box* arg3);
...@@ -49,7 +44,7 @@ void registerMainThread(); ...@@ -49,7 +44,7 @@ void registerMainThread();
void finishMainThread(); void finishMainThread();
// Hook for the GC; will visit all the threads (including the current one), visiting their // Hook for the GC; will visit all the threads (including the current one), visiting their
// stacks and thread-local ThreadState objects // stacks and thread-local PyThreadState objects
void visitAllStacks(gc::GCVisitor* v); void visitAllStacks(gc::GCVisitor* v);
// Some hooks to keep track of the list of stacks that this thread has been using. // Some hooks to keep track of the list of stacks that this thread has been using.
......
...@@ -521,6 +521,9 @@ BoxedClass* BaseException, *Exception, *StandardError, *AssertionError, *Attribu ...@@ -521,6 +521,9 @@ BoxedClass* BaseException, *Exception, *StandardError, *AssertionError, *Attribu
*RuntimeError, *ImportError, *StopIteration, *Warning, *SyntaxError, *OverflowError, *DeprecationWarning, *RuntimeError, *ImportError, *StopIteration, *Warning, *SyntaxError, *OverflowError, *DeprecationWarning,
*MemoryError, *LookupError, *EnvironmentError, *ArithmeticError, *BufferError, *KeyboardInterrupt, *SystemExit, *MemoryError, *LookupError, *EnvironmentError, *ArithmeticError, *BufferError, *KeyboardInterrupt, *SystemExit,
*SystemError, *NotImplementedError, *PendingDeprecationWarning; *SystemError, *NotImplementedError, *PendingDeprecationWarning;
Box* PyExc_RecursionErrorInst;
Box* PyExc_MemoryErrorInst;
} }
Box* exceptionNew1(BoxedClass* cls) { Box* exceptionNew1(BoxedClass* cls) {
...@@ -1086,5 +1089,11 @@ void setupBuiltins() { ...@@ -1086,5 +1089,11 @@ void setupBuiltins() {
builtins_module->giveAttr("property", property_cls); builtins_module->giveAttr("property", property_cls);
builtins_module->giveAttr("staticmethod", staticmethod_cls); builtins_module->giveAttr("staticmethod", staticmethod_cls);
builtins_module->giveAttr("classmethod", classmethod_cls); builtins_module->giveAttr("classmethod", classmethod_cls);
PyExc_RecursionErrorInst = new BoxedException(RuntimeError);
gc::registerPermanentRoot(PyExc_RecursionErrorInst);
PyExc_MemoryErrorInst = new BoxedException(MemoryError);
gc::registerPermanentRoot(PyExc_MemoryErrorInst);
} }
} }
...@@ -32,10 +32,9 @@ BoxedModule* sys_module; ...@@ -32,10 +32,9 @@ BoxedModule* sys_module;
BoxedDict* sys_modules_dict; BoxedDict* sys_modules_dict;
Box* sysExcInfo() { Box* sysExcInfo() {
return new BoxedTuple( return new BoxedTuple({ cur_thread_state.curexc_type ? cur_thread_state.curexc_type : None,
{ threading::cur_thread_state.exc_type ? threading::cur_thread_state.exc_type : None, cur_thread_state.curexc_value ? cur_thread_state.curexc_value : None,
threading::cur_thread_state.exc_value ? threading::cur_thread_state.exc_value : None, cur_thread_state.curexc_traceback ? cur_thread_state.curexc_traceback : None });
threading::cur_thread_state.exc_traceback ? threading::cur_thread_state.exc_traceback : None });
} }
static Box* sysExit(Box* arg) { static Box* sysExit(Box* arg) {
...@@ -76,6 +75,65 @@ Box* getSysStdout() { ...@@ -76,6 +75,65 @@ Box* getSysStdout() {
return sys_stdout; return sys_stdout;
} }
extern "C" int PySys_SetObject(const char* name, PyObject* v) {
try {
if (!v) {
if (sys_module->getattr(name))
sys_module->delattr(name, NULL);
} else
sys_module->setattr(name, v, NULL);
} catch (Box* b) {
abort();
}
return 0;
}
extern "C" PyObject* PySys_GetObject(const char* name) {
return sys_module->getattr(name);
}
static void mywrite(const char* name, FILE* fp, const char* format, va_list va) noexcept {
PyObject* file;
PyObject* error_type, *error_value, *error_traceback;
PyErr_Fetch(&error_type, &error_value, &error_traceback);
file = PySys_GetObject(name);
if (file == NULL || PyFile_AsFile(file) == fp)
vfprintf(fp, format, va);
else {
char buffer[1001];
const int written = PyOS_vsnprintf(buffer, sizeof(buffer), format, va);
if (PyFile_WriteString(buffer, file) != 0) {
PyErr_Clear();
fputs(buffer, fp);
}
if (written < 0 || (size_t)written >= sizeof(buffer)) {
const char* truncated = "... truncated";
if (PyFile_WriteString(truncated, file) != 0) {
PyErr_Clear();
fputs(truncated, fp);
}
}
}
PyErr_Restore(error_type, error_value, error_traceback);
}
extern "C" void PySys_WriteStdout(const char* format, ...) {
va_list va;
va_start(va, format);
mywrite("stdout", stdout, format, va);
va_end(va);
}
extern "C" void PySys_WriteStderr(const char* format, ...) {
va_list va;
va_start(va, format);
mywrite("stderr", stderr, format, va);
va_end(va);
}
void addToSysArgv(const char* str) { void addToSysArgv(const char* str) {
Box* sys_argv = sys_module->getattr("argv"); Box* sys_argv = sys_module->getattr("argv");
assert(sys_argv); assert(sys_argv);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "capi/types.h" #include "capi/types.h"
#include "core/threading.h" #include "core/threading.h"
#include "core/types.h" #include "core/types.h"
#include "runtime/classobj.h"
#include "runtime/import.h" #include "runtime/import.h"
#include "runtime/objmodel.h" #include "runtime/objmodel.h"
#include "runtime/types.h" #include "runtime/types.h"
...@@ -32,20 +33,24 @@ BoxedClass* method_cls; ...@@ -32,20 +33,24 @@ BoxedClass* method_cls;
#define MAKE_CHECK(NAME, cls_name) \ #define MAKE_CHECK(NAME, cls_name) \
extern "C" bool Py##NAME##_Check(PyObject* op) { return isSubclass(op->cls, cls_name); } extern "C" bool Py##NAME##_Check(PyObject* op) { return isSubclass(op->cls, cls_name); }
#define MAKE_CHECK2(NAME, cls_name) \
extern "C" bool _Py##NAME##_Check(PyObject* op) { return isSubclass(op->cls, cls_name); }
MAKE_CHECK(Int, int_cls) MAKE_CHECK2(Int, int_cls)
MAKE_CHECK(String, str_cls) MAKE_CHECK2(String, str_cls)
MAKE_CHECK(Long, long_cls) MAKE_CHECK(Long, long_cls)
MAKE_CHECK(List, list_cls) MAKE_CHECK(List, list_cls)
MAKE_CHECK(Tuple, tuple_cls) MAKE_CHECK(Tuple, tuple_cls)
MAKE_CHECK(Dict, dict_cls) MAKE_CHECK(Dict, dict_cls)
MAKE_CHECK(Slice, slice_cls) MAKE_CHECK(Slice, slice_cls)
MAKE_CHECK(Type, type_cls)
#ifdef Py_USING_UNICODE #ifdef Py_USING_UNICODE
MAKE_CHECK(Unicode, unicode_cls) MAKE_CHECK(Unicode, unicode_cls)
#endif #endif
#undef MAKE_CHECK #undef MAKE_CHECK
#undef MAKE_CHECK2
extern "C" bool _PyIndex_Check(PyObject* op) { extern "C" bool _PyIndex_Check(PyObject* op) {
// TODO this is wrong (the CPython version checks for things that can be coerced to a number): // TODO this is wrong (the CPython version checks for things that can be coerced to a number):
...@@ -230,18 +235,6 @@ extern "C" PyObject* _PyObject_CallMethod_SizeT(PyObject* o, char* name, char* f ...@@ -230,18 +235,6 @@ extern "C" PyObject* _PyObject_CallMethod_SizeT(PyObject* o, char* name, char* f
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" PyObject* PyObject_GetAttrString(PyObject* o, const char* attr) {
// TODO do something like this? not sure if this is safe; will people expect that calling into a known function
// won't end up doing a GIL check?
// threading::GLDemoteRegion _gil_demote;
try {
return getattr(o, attr);
} catch (Box* b) {
Py_FatalError("unimplemented");
}
}
extern "C" Py_ssize_t PyObject_Size(PyObject* o) { extern "C" Py_ssize_t PyObject_Size(PyObject* o) {
try { try {
return len(o)->n; return len(o)->n;
...@@ -463,6 +456,11 @@ extern "C" long PyObject_HashNotImplemented(PyObject* self) { ...@@ -463,6 +456,11 @@ extern "C" long PyObject_HashNotImplemented(PyObject* self) {
return -1; return -1;
} }
extern "C" PyObject* _PyObject_NextNotImplemented(PyObject* self) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not iterable", Py_TYPE(self)->tp_name);
return NULL;
}
extern "C" long _Py_HashPointer(void* p) { extern "C" long _Py_HashPointer(void* p) {
long x; long x;
size_t y = (size_t)p; size_t y = (size_t)p;
...@@ -629,11 +627,118 @@ extern "C" int PyCallable_Check(PyObject* x) { ...@@ -629,11 +627,118 @@ extern "C" int PyCallable_Check(PyObject* x) {
return typeLookup(x->cls, call_attr, NULL) != NULL; return typeLookup(x->cls, call_attr, NULL) != NULL;
} }
extern "C" int Py_FlushLine(void) {
PyObject* f = PySys_GetObject("stdout");
if (f == NULL)
return 0;
if (!PyFile_SoftSpace(f, 0))
return 0;
return PyFile_WriteString("\n", f);
}
extern "C" void PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObject** tb) {
PyObject* type = *exc;
PyObject* value = *val;
PyObject* inclass = NULL;
PyObject* initial_tb = NULL;
PyThreadState* tstate = NULL;
if (type == NULL) {
/* There was no exception, so nothing to do. */
return;
}
/* If PyErr_SetNone() was used, the value will have been actually
set to NULL.
*/
if (!value) {
value = Py_None;
Py_INCREF(value);
}
if (PyExceptionInstance_Check(value))
inclass = PyExceptionInstance_Class(value);
/* Normalize the exception so that if the type is a class, the
value will be an instance.
*/
if (PyExceptionClass_Check(type)) {
/* if the value was not an instance, or is not an instance
whose class is (or is derived from) type, then use the
value as an argument to instantiation of the type
class.
*/
if (!inclass || !PyObject_IsSubclass(inclass, type)) {
PyObject* args, *res;
if (value == Py_None)
args = PyTuple_New(0);
else if (PyTuple_Check(value)) {
Py_INCREF(value);
args = value;
} else
args = PyTuple_Pack(1, value);
if (args == NULL)
goto finally;
res = PyEval_CallObject(type, args);
Py_DECREF(args);
if (res == NULL)
goto finally;
Py_DECREF(value);
value = res;
}
/* if the class of the instance doesn't exactly match the
class of the type, believe the instance
*/
else if (inclass != type) {
Py_DECREF(type);
type = inclass;
Py_INCREF(type);
}
}
*exc = type;
*val = value;
return;
finally:
Py_DECREF(type);
Py_DECREF(value);
/* If the new exception doesn't set a traceback and the old
exception had a traceback, use the old traceback for the
new exception. It's better than nothing.
*/
initial_tb = *tb;
PyErr_Fetch(exc, val, tb);
if (initial_tb != NULL) {
if (*tb == NULL)
*tb = initial_tb;
else
Py_DECREF(initial_tb);
}
/* normalize recursively */
tstate = PyThreadState_GET();
if (++tstate->recursion_depth > Py_GetRecursionLimit()) {
--tstate->recursion_depth;
/* throw away the old exception... */
Py_DECREF(*exc);
Py_DECREF(*val);
/* ... and use the recursion error instead */
*exc = PyExc_RuntimeError;
*val = PyExc_RecursionErrorInst;
Py_INCREF(*exc);
Py_INCREF(*val);
/* just keeping the old traceback */
return;
}
PyErr_NormalizeException(exc, val, tb);
--tstate->recursion_depth;
}
void checkAndThrowCAPIException() { void checkAndThrowCAPIException() {
Box* value = threading::cur_thread_state.exc_value; Box* value = cur_thread_state.curexc_value;
if (value) { if (value) {
RELEASE_ASSERT(threading::cur_thread_state.exc_traceback == NULL, "unsupported"); RELEASE_ASSERT(cur_thread_state.curexc_traceback == NULL, "unsupported");
Box* _type = threading::cur_thread_state.exc_type; Box* _type = cur_thread_state.curexc_type;
BoxedClass* type = static_cast<BoxedClass*>(_type); BoxedClass* type = static_cast<BoxedClass*>(_type);
assert(isInstance(_type, type_cls) && isSubclass(static_cast<BoxedClass*>(type), BaseException) assert(isInstance(_type, type_cls) && isSubclass(static_cast<BoxedClass*>(type), BaseException)
&& "Only support throwing subclass of BaseException for now"); && "Only support throwing subclass of BaseException for now");
...@@ -641,13 +746,12 @@ void checkAndThrowCAPIException() { ...@@ -641,13 +746,12 @@ void checkAndThrowCAPIException() {
// This is similar to PyErr_NormalizeException: // This is similar to PyErr_NormalizeException:
if (!isInstance(value, type)) { if (!isInstance(value, type)) {
if (value->cls == tuple_cls) { if (value->cls == tuple_cls) {
value = runtimeCall(threading::cur_thread_state.exc_type, ArgPassSpec(0, 0, true, false), value, NULL, value = runtimeCall(cur_thread_state.curexc_type, ArgPassSpec(0, 0, true, false), value, NULL, NULL,
NULL, NULL, NULL); NULL, NULL);
} else if (value == None) { } else if (value == None) {
value = runtimeCall(threading::cur_thread_state.exc_type, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL); value = runtimeCall(cur_thread_state.curexc_type, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
} else { } else {
value value = runtimeCall(cur_thread_state.curexc_type, ArgPassSpec(1), value, NULL, NULL, NULL, NULL);
= runtimeCall(threading::cur_thread_state.exc_type, ArgPassSpec(1), value, NULL, NULL, NULL, NULL);
} }
} }
...@@ -659,9 +763,9 @@ void checkAndThrowCAPIException() { ...@@ -659,9 +763,9 @@ void checkAndThrowCAPIException() {
} }
extern "C" void PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback) { extern "C" void PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback) {
threading::cur_thread_state.exc_type = type; cur_thread_state.curexc_type = type;
threading::cur_thread_state.exc_value = value; cur_thread_state.curexc_value = value;
threading::cur_thread_state.exc_traceback = traceback; cur_thread_state.curexc_traceback = traceback;
} }
extern "C" void PyErr_Clear() { extern "C" void PyErr_Clear() {
...@@ -688,12 +792,117 @@ extern "C" int PyErr_CheckSignals() { ...@@ -688,12 +792,117 @@ extern "C" int PyErr_CheckSignals() {
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" int PyErr_ExceptionMatches(PyObject* exc) { extern "C" int PyExceptionClass_Check(PyObject* o) {
return PyClass_Check(o) || (PyType_Check(o) && isSubclass(static_cast<BoxedClass*>(o), BaseException));
}
extern "C" int PyExceptionInstance_Check(PyObject* o) {
return PyInstance_Check(o) || isSubclass(o->cls, BaseException);
}
extern "C" const char* PyExceptionClass_Name(PyObject* o) {
return PyClass_Check(o) ? PyString_AS_STRING(static_cast<BoxedClassobj*>(o)->name)
: static_cast<BoxedClass*>(o)->tp_name;
}
extern "C" PyObject* PyExceptionInstance_Class(PyObject* o) {
return PyInstance_Check(o) ? (Box*)static_cast<BoxedInstance*>(o)->inst_cls : o->cls;
}
extern "C" int PyTraceBack_Print(PyObject* v, PyObject* f) {
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
#define Py_DEFAULT_RECURSION_LIMIT 1000
static int recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
extern "C" {
int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
}
/* the macro Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall()
if the recursion_depth reaches _Py_CheckRecursionLimit.
If USE_STACKCHECK, the macro decrements _Py_CheckRecursionLimit
to guarantee that _Py_CheckRecursiveCall() is regularly called.
Without USE_STACKCHECK, there is no need for this. */
extern "C" int _Py_CheckRecursiveCall(const char* where) {
PyThreadState* tstate = PyThreadState_GET();
#ifdef USE_STACKCHECK
if (PyOS_CheckStack()) {
--tstate->recursion_depth;
PyErr_SetString(PyExc_MemoryError, "Stack overflow");
return -1;
}
#endif
if (tstate->recursion_depth > recursion_limit) {
--tstate->recursion_depth;
PyErr_Format(PyExc_RuntimeError, "maximum recursion depth exceeded%s", where);
return -1;
}
_Py_CheckRecursionLimit = recursion_limit;
return 0;
}
extern "C" int Py_GetRecursionLimit(void) {
return recursion_limit;
}
extern "C" void Py_SetRecursionLimit(int new_limit) {
recursion_limit = new_limit;
_Py_CheckRecursionLimit = recursion_limit;
}
extern "C" int PyErr_GivenExceptionMatches(PyObject* err, PyObject* exc) {
if (err == NULL || exc == NULL) {
/* maybe caused by "import exceptions" that failed early on */
return 0;
}
if (PyTuple_Check(exc)) {
Py_ssize_t i, n;
n = PyTuple_Size(exc);
for (i = 0; i < n; i++) {
/* Test recursively */
if (PyErr_GivenExceptionMatches(err, PyTuple_GET_ITEM(exc, i))) {
return 1;
}
}
return 0;
}
/* err might be an instance, so check its class. */
if (PyExceptionInstance_Check(err))
err = PyExceptionInstance_Class(err);
if (PyExceptionClass_Check(err) && PyExceptionClass_Check(exc)) {
int res = 0, reclimit;
PyObject* exception, *value, *tb;
PyErr_Fetch(&exception, &value, &tb);
/* Temporarily bump the recursion limit, so that in the most
common case PyObject_IsSubclass will not raise a recursion
error we have to ignore anyway. Don't do it when the limit
is already insanely high, to avoid overflow */
reclimit = Py_GetRecursionLimit();
if (reclimit < (1 << 30))
Py_SetRecursionLimit(reclimit + 5);
res = PyObject_IsSubclass(err, exc);
Py_SetRecursionLimit(reclimit);
/* This function must not fail, so print the error here */
if (res == -1) {
PyErr_WriteUnraisable(err);
res = 0;
}
PyErr_Restore(exception, value, tb);
return res;
}
return err == exc;
}
extern "C" int PyErr_ExceptionMatches(PyObject* exc) {
return PyErr_GivenExceptionMatches(PyErr_Occurred(), exc);
}
extern "C" PyObject* PyErr_Occurred() { extern "C" PyObject* PyErr_Occurred() {
return threading::cur_thread_state.exc_type; return cur_thread_state.curexc_type;
} }
extern "C" int PyErr_WarnEx(PyObject* category, const char* text, Py_ssize_t stacklevel) { extern "C" int PyErr_WarnEx(PyObject* category, const char* text, Py_ssize_t stacklevel) {
......
...@@ -57,6 +57,10 @@ static Box* classLookup(BoxedClassobj* cls, const std::string& attr) { ...@@ -57,6 +57,10 @@ static Box* classLookup(BoxedClassobj* cls, const std::string& attr) {
return NULL; return NULL;
} }
extern "C" int PyClass_IsSubclass(PyObject* klass, PyObject* base) {
Py_FatalError("unimplemented");
}
Box* classobjNew(Box* _cls, Box* _name, Box* _bases, Box** _args) { Box* classobjNew(Box* _cls, Box* _name, Box* _bases, Box** _args) {
if (!isSubclass(_cls->cls, type_cls)) if (!isSubclass(_cls->cls, type_cls))
raiseExcHelper(TypeError, "classobj.__new__(X): X is not a type object (%s)", getTypeName(_cls)->c_str()); raiseExcHelper(TypeError, "classobj.__new__(X): X is not a type object (%s)", getTypeName(_cls)->c_str());
......
...@@ -191,6 +191,71 @@ extern "C" PyObject* PyFile_FromFile(FILE* fp, char* name, char* mode, int (*clo ...@@ -191,6 +191,71 @@ extern "C" PyObject* PyFile_FromFile(FILE* fp, char* name, char* mode, int (*clo
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" FILE* PyFile_AsFile(PyObject* f) {
if (!f || !PyFile_Check(f))
return NULL;
return static_cast<BoxedFile*>(f)->f;
}
extern "C" int PyFile_WriteObject(PyObject* v, PyObject* f, int flags) {
if (f->cls != file_cls || v->cls != str_cls || flags != Py_PRINT_RAW)
Py_FatalError("unimplemented");
try {
Box* r = fileWrite(static_cast<BoxedFile*>(f), v);
assert(r == None);
return 0;
} catch (Box* b) {
PyErr_SetObject(b->cls, b);
return -1;
}
}
#define FILE_BEGIN_ALLOW_THREADS(fobj) \
{ \
/*fobj->unlocked_count++;*/ \
Py_BEGIN_ALLOW_THREADS
#define FILE_END_ALLOW_THREADS(fobj) \
Py_END_ALLOW_THREADS \
/*fobj->unlocked_count--;*/ \
/*assert(fobj->unlocked_count >= 0);*/ \
}
static PyObject* err_closed(void) noexcept {
PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
return NULL;
}
extern "C" int PyFile_WriteString(const char* s, PyObject* f) {
if (f == NULL) {
/* Should be caused by a pre-existing error */
if (!PyErr_Occurred())
PyErr_SetString(PyExc_SystemError, "null file for PyFile_WriteString");
return -1;
} else if (PyFile_Check(f)) {
PyFileObject* fobj = (PyFileObject*)f;
FILE* fp = PyFile_AsFile(f);
if (fp == NULL) {
err_closed();
return -1;
}
FILE_BEGIN_ALLOW_THREADS(fobj)
fputs(s, fp);
FILE_END_ALLOW_THREADS(fobj)
return 0;
} else if (!PyErr_Occurred()) {
PyObject* v = PyString_FromString(s);
int err;
if (v == NULL)
return -1;
err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
Py_DECREF(v);
return err;
} else
return -1;
}
extern "C" void PyFile_SetBufSize(PyObject* f, int bufsize) { extern "C" void PyFile_SetBufSize(PyObject* f, int bufsize) {
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
...@@ -203,6 +268,14 @@ extern "C" int PyObject_AsFileDescriptor(PyObject* o) { ...@@ -203,6 +268,14 @@ extern "C" int PyObject_AsFileDescriptor(PyObject* o) {
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" int PyFile_SoftSpace(PyObject* f, int newflag) {
try {
return softspace(f, newflag);
} catch (Box* b) {
abort();
}
}
void setupFile() { void setupFile() {
file_cls->giveAttr("__name__", boxStrConstant("file")); file_cls->giveAttr("__name__", boxStrConstant("file"));
......
...@@ -253,7 +253,7 @@ extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent) { ...@@ -253,7 +253,7 @@ extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent) {
while (child) { while (child) {
if (child == parent) if (child == parent)
return true; return true;
child = child->base; child = child->tp_base;
} }
return false; return false;
} }
...@@ -345,7 +345,7 @@ void BoxedClass::freeze() { ...@@ -345,7 +345,7 @@ void BoxedClass::freeze() {
BoxedClass::BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, BoxedClass::BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
int instance_size, bool is_user_defined) int instance_size, bool is_user_defined)
: BoxVar(metaclass, 0), base(base), gc_visit(gc_visit), attrs_offset(attrs_offset), is_constant(false), : BoxVar(metaclass, 0), gc_visit(gc_visit), attrs_offset(attrs_offset), is_constant(false),
is_user_defined(is_user_defined) { is_user_defined(is_user_defined) {
// Zero out the CPython tp_* slots: // Zero out the CPython tp_* slots:
...@@ -354,6 +354,9 @@ BoxedClass::BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_ ...@@ -354,6 +354,9 @@ BoxedClass::BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_
tp_flags |= Py_TPFLAGS_HEAPTYPE; tp_flags |= Py_TPFLAGS_HEAPTYPE;
tp_flags |= Py_TPFLAGS_CHECKTYPES; tp_flags |= Py_TPFLAGS_CHECKTYPES;
tp_flags |= Py_TPFLAGS_BASETYPE;
tp_base = base;
if (metaclass == NULL) { if (metaclass == NULL) {
assert(type_cls == NULL); assert(type_cls == NULL);
...@@ -639,16 +642,16 @@ Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* re ...@@ -639,16 +642,16 @@ Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* re
val = cls->getattr(attr, rewrite_args); val = cls->getattr(attr, rewrite_args);
assert(rewrite_args->out_success); assert(rewrite_args->out_success);
if (!val and cls->base) { if (!val and cls->tp_base) {
rewrite_args->out_success = false; rewrite_args->out_success = false;
rewrite_args->obj = obj_saved->getAttr(offsetof(BoxedClass, base)); rewrite_args->obj = obj_saved->getAttr(offsetof(BoxedClass, tp_base));
val = typeLookup(cls->base, attr, rewrite_args); val = typeLookup(cls->tp_base, attr, rewrite_args);
} }
return val; return val;
} else { } else {
val = cls->getattr(attr, NULL); val = cls->getattr(attr, NULL);
if (!val and cls->base) if (!val and cls->tp_base)
return typeLookup(cls->base, attr, NULL); return typeLookup(cls->tp_base, attr, NULL);
return val; return val;
} }
} }
...@@ -3304,6 +3307,8 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) { ...@@ -3304,6 +3307,8 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
RELEASE_ASSERT(_base->cls == type_cls, ""); RELEASE_ASSERT(_base->cls == type_cls, "");
base = static_cast<BoxedClass*>(_base); base = static_cast<BoxedClass*>(_base);
if ((base->tp_flags & Py_TPFLAGS_BASETYPE) == 0)
raiseExcHelper(TypeError, "type '%.100s' is not an acceptable base type", base->tp_name);
BoxedClass* made; BoxedClass* made;
...@@ -3329,6 +3334,8 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) { ...@@ -3329,6 +3334,8 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
// TODO should this function (typeNew) call PyType_Ready? // TODO should this function (typeNew) call PyType_Ready?
made->tp_new = base->tp_new; made->tp_new = base->tp_new;
made->tp_alloc = reinterpret_cast<decltype(cls->tp_alloc)>(PyType_GenericAlloc); made->tp_alloc = reinterpret_cast<decltype(cls->tp_alloc)>(PyType_GenericAlloc);
PystonType_Ready(made);
fixup_slot_dispatchers(made); fixup_slot_dispatchers(made);
return made; return made;
......
...@@ -32,8 +32,242 @@ ...@@ -32,8 +32,242 @@
namespace pyston { namespace pyston {
extern "C" PyObject* PyString_FromFormatV(const char* format, va_list vargs) {
va_list count;
Py_ssize_t n = 0;
const char* f;
char* s;
PyObject* string;
#ifdef VA_LIST_IS_ARRAY
Py_MEMCPY(count, vargs, sizeof(va_list));
#else
#ifdef __va_copy
__va_copy(count, vargs);
#else
count = vargs;
#endif
#endif
/* step 1: figure out how large a buffer we need */
for (f = format; *f; f++) {
if (*f == '%') {
#ifdef HAVE_LONG_LONG
int longlongflag = 0;
#endif
const char* p = f;
while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
;
/* skip the 'l' or 'z' in {%ld, %zd, %lu, %zu} since
* they don't affect the amount of space we reserve.
*/
if (*f == 'l') {
if (f[1] == 'd' || f[1] == 'u') {
++f;
}
#ifdef HAVE_LONG_LONG
else if (f[1] == 'l' && (f[2] == 'd' || f[2] == 'u')) {
longlongflag = 1;
f += 2;
}
#endif
} else if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) {
++f;
}
switch (*f) {
case 'c':
(void)va_arg(count, int);
/* fall through... */
case '%':
n++;
break;
case 'd':
case 'u':
case 'i':
case 'x':
(void)va_arg(count, int);
#ifdef HAVE_LONG_LONG
/* Need at most
ceil(log10(256)*SIZEOF_LONG_LONG) digits,
plus 1 for the sign. 53/22 is an upper
bound for log10(256). */
if (longlongflag)
n += 2 + (SIZEOF_LONG_LONG * 53 - 1) / 22;
else
#endif
/* 20 bytes is enough to hold a 64-bit
integer. Decimal takes the most
space. This isn't enough for
octal. */
n += 20;
break;
case 's':
s = va_arg(count, char*);
n += strlen(s);
break;
case 'p':
(void)va_arg(count, int);
/* maximum 64-bit pointer representation:
* 0xffffffffffffffff
* so 19 characters is enough.
* XXX I count 18 -- what's the extra for?
*/
n += 19;
break;
default:
/* if we stumble upon an unknown
formatting code, copy the rest of
the format string to the output
string. (we cannot just skip the
code, since there's no way to know
what's in the argument list) */
n += strlen(p);
goto expand;
}
} else
n++;
}
expand:
/* step 2: fill the buffer */
/* Since we've analyzed how much space we need for the worst case,
use sprintf directly instead of the slower PyOS_snprintf. */
string = PyString_FromStringAndSize(NULL, n);
if (!string)
return NULL;
s = PyString_AsString(string);
for (f = format; *f; f++) {
if (*f == '%') {
const char* p = f++;
Py_ssize_t i;
int longflag = 0;
#ifdef HAVE_LONG_LONG
int longlongflag = 0;
#endif
int size_tflag = 0;
/* parse the width.precision part (we're only
interested in the precision value, if any) */
n = 0;
while (isdigit(Py_CHARMASK(*f)))
n = (n * 10) + *f++ - '0';
if (*f == '.') {
f++;
n = 0;
while (isdigit(Py_CHARMASK(*f)))
n = (n * 10) + *f++ - '0';
}
while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
f++;
/* Handle %ld, %lu, %lld and %llu. */
if (*f == 'l') {
if (f[1] == 'd' || f[1] == 'u') {
longflag = 1;
++f;
}
#ifdef HAVE_LONG_LONG
else if (f[1] == 'l' && (f[2] == 'd' || f[2] == 'u')) {
longlongflag = 1;
f += 2;
}
#endif
}
/* handle the size_t flag. */
else if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) {
size_tflag = 1;
++f;
}
switch (*f) {
case 'c':
*s++ = va_arg(vargs, int);
break;
case 'd':
if (longflag)
sprintf(s, "%ld", va_arg(vargs, long));
#ifdef HAVE_LONG_LONG
else if (longlongflag)
sprintf(s, "%" PY_FORMAT_LONG_LONG "d", va_arg(vargs, PY_LONG_LONG));
#endif
else if (size_tflag)
sprintf(s, "%" PY_FORMAT_SIZE_T "d", va_arg(vargs, Py_ssize_t));
else
sprintf(s, "%d", va_arg(vargs, int));
s += strlen(s);
break;
case 'u':
if (longflag)
sprintf(s, "%lu", va_arg(vargs, unsigned long));
#ifdef HAVE_LONG_LONG
else if (longlongflag)
sprintf(s, "%" PY_FORMAT_LONG_LONG "u", va_arg(vargs, PY_LONG_LONG));
#endif
else if (size_tflag)
sprintf(s, "%" PY_FORMAT_SIZE_T "u", va_arg(vargs, size_t));
else
sprintf(s, "%u", va_arg(vargs, unsigned int));
s += strlen(s);
break;
case 'i':
sprintf(s, "%i", va_arg(vargs, int));
s += strlen(s);
break;
case 'x':
sprintf(s, "%x", va_arg(vargs, int));
s += strlen(s);
break;
case 's':
p = va_arg(vargs, char*);
i = strlen(p);
if (n > 0 && i > n)
i = n;
Py_MEMCPY(s, p, i);
s += i;
break;
case 'p':
sprintf(s, "%p", va_arg(vargs, void*));
/* %p is ill-defined: ensure leading 0x. */
if (s[1] == 'X')
s[1] = 'x';
else if (s[1] != 'x') {
memmove(s + 2, s, strlen(s) + 1);
s[0] = '0';
s[1] = 'x';
}
s += strlen(s);
break;
case '%':
*s++ = '%';
break;
default:
strcpy(s, p);
s += strlen(s);
goto end;
}
} else
*s++ = *f;
}
end:
if (_PyString_Resize(&string, s - PyString_AS_STRING(string)))
return NULL;
return string;
}
extern "C" PyObject* PyString_FromFormat(const char* format, ...) { extern "C" PyObject* PyString_FromFormat(const char* format, ...) {
Py_FatalError("unimplemented"); PyObject* ret;
va_list vargs;
#ifdef HAVE_STDARG_PROTOTYPES
va_start(vargs, format);
#else
va_start(vargs);
#endif
ret = PyString_FromFormatV(format, vargs);
va_end(vargs);
return ret;
} }
extern "C" BoxedString* strAdd(BoxedString* lhs, Box* _rhs) { extern "C" BoxedString* strAdd(BoxedString* lhs, Box* _rhs) {
......
...@@ -66,7 +66,7 @@ Box* superGetattribute(Box* _s, Box* _attr) { ...@@ -66,7 +66,7 @@ Box* superGetattribute(Box* _s, Box* _attr) {
if (!skip) { if (!skip) {
// We don't support multiple inheritance yet, so the lookup order is simple: // We don't support multiple inheritance yet, so the lookup order is simple:
Box* r = typeLookup(s->type->base, attr->s, NULL); Box* r = typeLookup(s->type->tp_base, attr->s, NULL);
if (r) { if (r) {
return processDescriptor(r, (s->obj == s->obj_type ? None : s->obj), s->obj_type); return processDescriptor(r, (s->obj == s->obj_type ? None : s->obj), s->obj_type);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "core/stats.h" #include "core/stats.h"
#include "core/types.h" #include "core/types.h"
#include "gc/collector.h" #include "gc/collector.h"
#include "runtime/capi.h"
#include "runtime/classobj.h" #include "runtime/classobj.h"
#include "runtime/ics.h" #include "runtime/ics.h"
#include "runtime/iterobject.h" #include "runtime/iterobject.h"
...@@ -284,8 +285,8 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) { ...@@ -284,8 +285,8 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) {
BoxedClass* cls = (BoxedClass*)b; BoxedClass* cls = (BoxedClass*)b;
if (cls->base) if (cls->tp_base)
v->visit(cls->base); v->visit(cls->tp_base);
if (cls->tp_dict) if (cls->tp_dict)
v->visit(cls->tp_dict); v->visit(cls->tp_dict);
} }
...@@ -433,9 +434,30 @@ extern "C" Box* createUserClass(std::string* name, Box* _bases, Box* _attr_dict) ...@@ -433,9 +434,30 @@ extern "C" Box* createUserClass(std::string* name, Box* _bases, Box* _attr_dict)
} }
assert(metaclass); assert(metaclass);
Box* r = runtimeCall(metaclass, ArgPassSpec(3), boxStringPtr(name), _bases, _attr_dict, NULL, NULL); try {
RELEASE_ASSERT(r, ""); Box* r = runtimeCall(metaclass, ArgPassSpec(3), boxStringPtr(name), _bases, _attr_dict, NULL, NULL);
return r; RELEASE_ASSERT(r, "");
return r;
} catch (Box* b) {
// TODO [CAPI] bad error handling...
RELEASE_ASSERT(isSubclass(b->cls, BaseException), "");
Box* msg = b->getattr("message");
RELEASE_ASSERT(msg, "");
RELEASE_ASSERT(msg->cls == str_cls, "");
PyObject* newmsg;
newmsg = PyString_FromFormat("Error when calling the metaclass bases\n"
" %s",
PyString_AS_STRING(msg));
PyErr_Restore(b->cls, newmsg, NULL);
checkAndThrowCAPIException();
// Should not reach here
abort();
}
} }
extern "C" Box* boxInstanceMethod(Box* obj, Box* func) { extern "C" Box* boxInstanceMethod(Box* obj, Box* func) {
...@@ -804,7 +826,7 @@ void setupRuntime() { ...@@ -804,7 +826,7 @@ void setupRuntime() {
root_hcls = HiddenClass::makeRoot(); root_hcls = HiddenClass::makeRoot();
gc::registerPermanentRoot(root_hcls); gc::registerPermanentRoot(root_hcls);
object_cls = new BoxedHeapClass(NULL, NULL, &boxGCHandler, 0, sizeof(Box), false); object_cls = new BoxedClass(NULL, NULL, &boxGCHandler, 0, sizeof(Box), false);
type_cls type_cls
= new BoxedHeapClass(NULL, object_cls, &typeGCHandler, offsetof(BoxedClass, attrs), sizeof(BoxedClass), false); = new BoxedHeapClass(NULL, object_cls, &typeGCHandler, offsetof(BoxedClass, attrs), sizeof(BoxedClass), false);
type_cls->cls = type_cls; type_cls->cls = type_cls;
......
...@@ -174,10 +174,6 @@ class BoxedClass : public BoxVar { ...@@ -174,10 +174,6 @@ class BoxedClass : public BoxVar {
public: public:
typedef void (*gcvisit_func)(GCVisitor*, Box*); typedef void (*gcvisit_func)(GCVisitor*, Box*);
protected:
BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, int instance_size,
bool is_user_defined);
public: public:
PyTypeObject_BODY; PyTypeObject_BODY;
...@@ -197,10 +193,6 @@ public: ...@@ -197,10 +193,6 @@ public:
Box* callReprIC(Box* obj); Box* callReprIC(Box* obj);
bool callNonzeroIC(Box* obj); bool callNonzeroIC(Box* obj);
// Only a single base supported for now.
// Is NULL iff this is object_cls
BoxedClass* base;
gcvisit_func gc_visit; gcvisit_func gc_visit;
// Offset of the HCAttrs object or 0 if there are no hcattrs. // Offset of the HCAttrs object or 0 if there are no hcattrs.
...@@ -223,6 +215,9 @@ public: ...@@ -223,6 +215,9 @@ public:
bool hasGenericGetattr() { return true; } bool hasGenericGetattr() { return true; }
void freeze(); void freeze();
BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, int instance_size,
bool is_user_defined);
}; };
class BoxedHeapClass : public BoxedClass { class BoxedHeapClass : public BoxedClass {
...@@ -244,11 +239,9 @@ static_assert(offsetof(pyston::BoxedClass, cls) == offsetof(struct _typeobject, ...@@ -244,11 +239,9 @@ static_assert(offsetof(pyston::BoxedClass, cls) == offsetof(struct _typeobject,
static_assert(offsetof(pyston::BoxedClass, tp_name) == offsetof(struct _typeobject, tp_name), ""); static_assert(offsetof(pyston::BoxedClass, tp_name) == offsetof(struct _typeobject, tp_name), "");
static_assert(offsetof(pyston::BoxedClass, attrs) == offsetof(struct _typeobject, _hcls), ""); static_assert(offsetof(pyston::BoxedClass, attrs) == offsetof(struct _typeobject, _hcls), "");
static_assert(offsetof(pyston::BoxedClass, dependent_icgetattrs) == offsetof(struct _typeobject, _dep_getattrs), ""); static_assert(offsetof(pyston::BoxedClass, dependent_icgetattrs) == offsetof(struct _typeobject, _dep_getattrs), "");
static_assert(offsetof(pyston::BoxedClass, base) == offsetof(struct _typeobject, _base), "");
static_assert(offsetof(pyston::BoxedClass, gc_visit) == offsetof(struct _typeobject, _gcvisit_func), ""); static_assert(offsetof(pyston::BoxedClass, gc_visit) == offsetof(struct _typeobject, _gcvisit_func), "");
static_assert(sizeof(pyston::BoxedClass) == sizeof(struct _typeobject), ""); static_assert(sizeof(pyston::BoxedClass) == sizeof(struct _typeobject), "");
static_assert(offsetof(pyston::BoxedHeapClass, base) == offsetof(PyHeapTypeObject, ht_type._base), "");
static_assert(offsetof(pyston::BoxedHeapClass, as_number) == offsetof(PyHeapTypeObject, as_number), ""); static_assert(offsetof(pyston::BoxedHeapClass, as_number) == offsetof(PyHeapTypeObject, as_number), "");
static_assert(offsetof(pyston::BoxedHeapClass, as_mapping) == offsetof(PyHeapTypeObject, as_mapping), ""); static_assert(offsetof(pyston::BoxedHeapClass, as_mapping) == offsetof(PyHeapTypeObject, as_mapping), "");
static_assert(offsetof(pyston::BoxedHeapClass, as_sequence) == offsetof(PyHeapTypeObject, as_sequence), ""); static_assert(offsetof(pyston::BoxedHeapClass, as_sequence) == offsetof(PyHeapTypeObject, as_sequence), "");
......
...@@ -20,8 +20,7 @@ slots_tester_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ...@@ -20,8 +20,7 @@ slots_tester_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
printf("slots_tester_seq.__new__, %d\n", n); printf("slots_tester_seq.__new__, %d\n", n);
/* create attrgetterobject structure */ obj = (slots_tester_object*)type->tp_alloc(type, 0);
obj = PyObject_New(slots_tester_object, type);
if (obj == NULL) if (obj == NULL)
return NULL; return NULL;
...@@ -123,7 +122,7 @@ static PyTypeObject slots_tester_seq = { ...@@ -123,7 +122,7 @@ static PyTypeObject slots_tester_seq = {
0, /* tp_getattro */ 0, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
slots_tester_seq_doc, /* tp_doc */ slots_tester_seq_doc, /* tp_doc */
0, /* tp_traverse */ 0, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
...@@ -145,6 +144,49 @@ static PyTypeObject slots_tester_seq = { ...@@ -145,6 +144,49 @@ static PyTypeObject slots_tester_seq = {
0, /* tp_free */ 0, /* tp_free */
}; };
static PyTypeObject slots_tester_nonsubclassable = {
PyVarObject_HEAD_INIT(NULL, 0)
"slots_test.slots_tester_nonsubclassable", /* tp_name */
sizeof(slots_tester_object), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
(reprfunc)slots_tester_seq_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
slots_tester_init, /* tp_init */
0, /* tp_alloc */
slots_tester_new, /* tp_new */
0, /* tp_free */
};
static Py_ssize_t slots_tester_map_length(slots_tester_object* obj) { static Py_ssize_t slots_tester_map_length(slots_tester_object* obj) {
return obj->n; return obj->n;
} }
...@@ -347,6 +389,56 @@ static PyTypeObject slots_tester_num = { ...@@ -347,6 +389,56 @@ static PyTypeObject slots_tester_num = {
0, /* tp_free */ 0, /* tp_free */
}; };
typedef struct {
slots_tester_object base;
int n2;
} slots_tester_object_sub;
static PyTypeObject slots_tester_sub = {
PyVarObject_HEAD_INIT(NULL, 0)
"slots_test.slots_tester_sub", /* tp_name */
sizeof(slots_tester_object_sub), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
&slots_tester_seq, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
};
// Tests the correctness of the CAPI slots when the attributes get set in Python code: // Tests the correctness of the CAPI slots when the attributes get set in Python code:
static PyObject * static PyObject *
call_funcs(PyObject* _module, PyObject* args) { call_funcs(PyObject* _module, PyObject* args) {
...@@ -367,8 +459,13 @@ call_funcs(PyObject* _module, PyObject* args) { ...@@ -367,8 +459,13 @@ call_funcs(PyObject* _module, PyObject* args) {
if (cls->tp_new) { if (cls->tp_new) {
PyObject* rtn = cls->tp_new(cls, PyTuple_New(0), PyDict_New()); PyObject* rtn = cls->tp_new(cls, PyTuple_New(0), PyDict_New());
printf("tp_new exists and returned an object of type: '%s'\n", Py_TYPE(rtn)->tp_name); if (!rtn) {
Py_DECREF(rtn); printf("tp_new_exists but returned an error!\n");
PyErr_Print();
} else {
printf("tp_new exists and returned an object of type: '%s'\n", Py_TYPE(rtn)->tp_name);
Py_DECREF(rtn);
}
} }
if (cls->tp_new) { if (cls->tp_new) {
...@@ -519,10 +616,20 @@ initslots_test(void) ...@@ -519,10 +616,20 @@ initslots_test(void)
if (res < 0) if (res < 0)
return; return;
res = PyType_Ready(&slots_tester_sub);
if (res < 0)
return;
res = PyType_Ready(&slots_tester_nonsubclassable);
if (res < 0)
return;
// Not sure if the result of PyInt_FromLong needs to be decref'd // Not sure if the result of PyInt_FromLong needs to be decref'd
PyDict_SetItemString(slots_tester_seq.tp_dict, "set_through_tpdict", PyInt_FromLong(123)); PyDict_SetItemString(slots_tester_seq.tp_dict, "set_through_tpdict", PyInt_FromLong(123));
PyModule_AddObject(m, "SlotsTesterSeq", (PyObject *)&slots_tester_seq); PyModule_AddObject(m, "SlotsTesterSeq", (PyObject *)&slots_tester_seq);
PyModule_AddObject(m, "SlotsTesterMap", (PyObject *)&slots_tester_map); PyModule_AddObject(m, "SlotsTesterMap", (PyObject *)&slots_tester_map);
PyModule_AddObject(m, "SlotsTesterNum", (PyObject *)&slots_tester_num); PyModule_AddObject(m, "SlotsTesterNum", (PyObject *)&slots_tester_num);
PyModule_AddObject(m, "SlotsTesterSub", (PyObject *)&slots_tester_sub);
PyModule_AddObject(m, "SlotsTesterNonsubclassable", (PyObject *)&slots_tester_nonsubclassable);
} }
...@@ -39,6 +39,9 @@ for i in xrange(3): ...@@ -39,6 +39,9 @@ for i in xrange(3):
print hex(t) print hex(t)
print oct(t) print oct(t)
su = slots_test.SlotsTesterSub(5)
print su
class C(object): class C(object):
def __repr__(self): def __repr__(self):
print "__repr__()" print "__repr__()"
...@@ -69,3 +72,19 @@ def add(self, rhs): ...@@ -69,3 +72,19 @@ def add(self, rhs):
C.__add__ = add C.__add__ = add
slots_test.call_funcs(C()) slots_test.call_funcs(C())
class C2(C):
pass
slots_test.call_funcs(C2())
class C3(slots_test.SlotsTesterSeq):
pass
slots_test.call_funcs(C3(5))
try:
class C4(slots_test.SlotsTesterNonsubclassable):
pass
except TypeError, e:
print e
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