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
PYTHONPATH=test/test_extension/build/lib.linux-x86_64-2.7 python $<
$(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":
kv:
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
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) .
.PHONY: ext_python
.PHONY: ext_python ext_pythondbg
ext_python: $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR)/test_extension/*.c)
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)
$(ECHO) Compiling C file to $@
......
......@@ -248,36 +248,19 @@ download from http://code.google.com/p/gperftools/downloads/list
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
git clone https://github.com/martine/ninja.git
cd ninja
git checkout v1.4.0
./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
mkdir python-src
cd python-src
apt-get source python2.7-dbg
```
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
......
......@@ -66,6 +66,7 @@
#include "classobject.h"
#include "fileobject.h"
#include "pycapsule.h"
#include "traceback.h"
#include "sliceobject.h"
#include "iterobject.h"
#include "descrobject.h"
......@@ -79,6 +80,7 @@
#include "modsupport.h"
#include "pythonrun.h"
#include "ceval.h"
#include "sysmodule.h"
#include "intrcheck.h"
#include "import.h"
......
......@@ -52,7 +52,8 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void);
_Py_CheckRecursiveCall(where))
#define Py_LeaveRecursiveCall() \
(--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;
#ifdef USE_STACKCHECK
# define _Py_MakeRecCheck(x) (++(x) > --_Py_CheckRecursionLimit)
......
......@@ -37,7 +37,8 @@ PyAPI_DATA(PyTypeObject*) int_cls;
#define PyInt_Type (*int_cls)
// 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
#define PyInt_Check(op) \
PyType_FastSubclass((op)->ob_type, Py_TPFLAGS_INT_SUBCLASS)
......
......@@ -412,7 +412,7 @@ typedef PyObject *(*allocfunc)(PyTypeObject *, Py_ssize_t);
struct PyMethodDef *tp_methods;\
struct PyMemberDef *tp_members;\
struct PyGetSetDef *tp_getset;\
struct _typeobject *tp_base;\
PyTypeObject *tp_base;\
PyObject *tp_dict;\
descrgetfunc tp_descr_get;\
descrsetfunc tp_descr_set;\
......@@ -456,7 +456,6 @@ struct _typeobject {
void* _hcattrs;
char _dep_getattrs[56]; // FIXME: this is hardcoding the size of this particular implementation of std::unordered_map
char _ics[32];
void* _base;
void* _gcvisit_func;
int _attrs_offset;
bool _flags[2];
......
......@@ -42,6 +42,9 @@
#define HAVE_INT32_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 WITH_THREAD
......
......@@ -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) \
(PyClass_Check((x)) || (PyType_Check((x)) && \
PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS)))
......@@ -117,7 +119,12 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**);
((PyInstance_Check((x)) \
? (PyObject*)((PyInstanceObject*)(x))->in_class \
: (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 */
......@@ -212,10 +219,9 @@ PyAPI_DATA(PyTypeObject *) VMSError;
#define PyExc_BufferError ((PyObject*)BufferError)
PyAPI_DATA(PyTypeObject *) BufferError;
#define PyExc_MemoryErrorInst ((PyObject*)MemoryErrorInst)
PyAPI_DATA(PyTypeObject *) MemoryErrorInst;
#define PyExc_RecursionErrorInst ((PyObject*)RecursionErrorInst)
PyAPI_DATA(PyTypeObject *) RecursionErrorInst;
PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
/* Predefined warning categories */
#define PyExc_Warning ((PyObject*)Warning)
......
......@@ -110,8 +110,13 @@ typedef struct _ts {
} PyThreadState;
#endif
struct _PyThreadState;
typedef struct _PyThreadState PyThreadState;
typedef struct _ts {
int recursion_depth;
PyObject *curexc_type;
PyObject *curexc_value;
PyObject *curexc_traceback;
} PyThreadState;
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
......@@ -135,7 +140,10 @@ PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *);
/* 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
#define PyThreadState_GET() PyThreadState_Get()
......
......@@ -65,7 +65,8 @@ PyAPI_DATA(PyTypeObject*) str_cls;
#define PyString_Type (*str_cls)
// 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
#define PyString_Check(op) \
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 @@
#include "capi/types.h"
#include "core/threading.h"
#include "core/types.h"
#include "runtime/classobj.h"
#include "runtime/import.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
......@@ -65,6 +66,205 @@ static PyObject* objargs_mktuple(va_list va) noexcept {
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, ...) {
PyObject* args, *tmp;
va_list vargs;
......@@ -83,4 +283,74 @@ extern "C" PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, ...) {
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
}
#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) {
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*
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) {
Py_FatalError("unimplemented");
}
......
This diff is collapsed.
......@@ -23,6 +23,7 @@ namespace pyston {
bool update_slot(BoxedClass* self, const std::string& attr);
void fixup_slot_dispatchers(BoxedClass* self);
void PystonType_Ready(BoxedClass* cls);
}
#endif
......@@ -205,6 +205,10 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
long us = _t.end();
static StatCounter us_compiling("us_compiling");
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");
num_compiles.log();
......
......@@ -34,8 +34,10 @@
namespace pyston {
namespace threading {
__thread ThreadState cur_thread_state
= { NULL, NULL, NULL }; // not sure if we need to explicitly request zero-initialization
extern "C" {
__thread PyThreadState cur_thread_state
= { 0, NULL, NULL, NULL }; // not sure if we need to explicitly request zero-initialization
}
PthreadFastMutex threading_lock;
......@@ -75,9 +77,9 @@ public:
std::vector<StackInfo> previous_stacks;
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) {}
void saveCurrent() {
......@@ -111,12 +113,12 @@ public:
void accept(gc::GCVisitor* v) {
auto pub_state = public_thread_state;
if (pub_state->exc_type)
v->visit(pub_state->exc_type);
if (pub_state->exc_value)
v->visit(pub_state->exc_value);
if (pub_state->exc_traceback)
v->visit(pub_state->exc_traceback);
if (pub_state->curexc_type)
v->visit(pub_state->curexc_type);
if (pub_state->curexc_value)
v->visit(pub_state->curexc_value);
if (pub_state->curexc_traceback)
v->visit(pub_state->curexc_traceback);
for (auto& stack_info : previous_stacks) {
v->visit(stack_info.next_generator);
......
......@@ -36,11 +36,6 @@ namespace threading {
// Whether or not a second thread was ever started:
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)
intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg2, Box* arg3);
......@@ -49,7 +44,7 @@ void registerMainThread();
void finishMainThread();
// 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);
// 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
*RuntimeError, *ImportError, *StopIteration, *Warning, *SyntaxError, *OverflowError, *DeprecationWarning,
*MemoryError, *LookupError, *EnvironmentError, *ArithmeticError, *BufferError, *KeyboardInterrupt, *SystemExit,
*SystemError, *NotImplementedError, *PendingDeprecationWarning;
Box* PyExc_RecursionErrorInst;
Box* PyExc_MemoryErrorInst;
}
Box* exceptionNew1(BoxedClass* cls) {
......@@ -1086,5 +1089,11 @@ void setupBuiltins() {
builtins_module->giveAttr("property", property_cls);
builtins_module->giveAttr("staticmethod", staticmethod_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;
BoxedDict* sys_modules_dict;
Box* sysExcInfo() {
return new BoxedTuple(
{ threading::cur_thread_state.exc_type ? threading::cur_thread_state.exc_type : None,
threading::cur_thread_state.exc_value ? threading::cur_thread_state.exc_value : None,
threading::cur_thread_state.exc_traceback ? threading::cur_thread_state.exc_traceback : None });
return new BoxedTuple({ cur_thread_state.curexc_type ? cur_thread_state.curexc_type : None,
cur_thread_state.curexc_value ? cur_thread_state.curexc_value : None,
cur_thread_state.curexc_traceback ? cur_thread_state.curexc_traceback : None });
}
static Box* sysExit(Box* arg) {
......@@ -76,6 +75,65 @@ Box* getSysStdout() {
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) {
Box* sys_argv = sys_module->getattr("argv");
assert(sys_argv);
......
This diff is collapsed.
......@@ -57,6 +57,10 @@ static Box* classLookup(BoxedClassobj* cls, const std::string& attr) {
return NULL;
}
extern "C" int PyClass_IsSubclass(PyObject* klass, PyObject* base) {
Py_FatalError("unimplemented");
}
Box* classobjNew(Box* _cls, Box* _name, Box* _bases, Box** _args) {
if (!isSubclass(_cls->cls, type_cls))
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
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) {
Py_FatalError("unimplemented");
}
......@@ -203,6 +268,14 @@ extern "C" int PyObject_AsFileDescriptor(PyObject* o) {
Py_FatalError("unimplemented");
}
extern "C" int PyFile_SoftSpace(PyObject* f, int newflag) {
try {
return softspace(f, newflag);
} catch (Box* b) {
abort();
}
}
void setupFile() {
file_cls->giveAttr("__name__", boxStrConstant("file"));
......
......@@ -253,7 +253,7 @@ extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent) {
while (child) {
if (child == parent)
return true;
child = child->base;
child = child->tp_base;
}
return false;
}
......@@ -345,7 +345,7 @@ void BoxedClass::freeze() {
BoxedClass::BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_visit, int attrs_offset,
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) {
// Zero out the CPython tp_* slots:
......@@ -354,6 +354,9 @@ BoxedClass::BoxedClass(BoxedClass* metaclass, BoxedClass* base, gcvisit_func gc_
tp_flags |= Py_TPFLAGS_HEAPTYPE;
tp_flags |= Py_TPFLAGS_CHECKTYPES;
tp_flags |= Py_TPFLAGS_BASETYPE;
tp_base = base;
if (metaclass == NULL) {
assert(type_cls == NULL);
......@@ -639,16 +642,16 @@ Box* typeLookup(BoxedClass* cls, const std::string& attr, GetattrRewriteArgs* re
val = cls->getattr(attr, rewrite_args);
assert(rewrite_args->out_success);
if (!val and cls->base) {
if (!val and cls->tp_base) {
rewrite_args->out_success = false;
rewrite_args->obj = obj_saved->getAttr(offsetof(BoxedClass, base));
val = typeLookup(cls->base, attr, rewrite_args);
rewrite_args->obj = obj_saved->getAttr(offsetof(BoxedClass, tp_base));
val = typeLookup(cls->tp_base, attr, rewrite_args);
}
return val;
} else {
val = cls->getattr(attr, NULL);
if (!val and cls->base)
return typeLookup(cls->base, attr, NULL);
if (!val and cls->tp_base)
return typeLookup(cls->tp_base, attr, NULL);
return val;
}
}
......@@ -3304,6 +3307,8 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
RELEASE_ASSERT(_base->cls == type_cls, "");
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;
......@@ -3329,6 +3334,8 @@ Box* typeNew(Box* _cls, Box* arg1, Box* arg2, Box** _args) {
// TODO should this function (typeNew) call PyType_Ready?
made->tp_new = base->tp_new;
made->tp_alloc = reinterpret_cast<decltype(cls->tp_alloc)>(PyType_GenericAlloc);
PystonType_Ready(made);
fixup_slot_dispatchers(made);
return made;
......
......@@ -32,8 +32,242 @@
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, ...) {
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) {
......
......@@ -66,7 +66,7 @@ Box* superGetattribute(Box* _s, Box* _attr) {
if (!skip) {
// 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) {
return processDescriptor(r, (s->obj == s->obj_type ? None : s->obj), s->obj_type);
......
......@@ -25,6 +25,7 @@
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/capi.h"
#include "runtime/classobj.h"
#include "runtime/ics.h"
#include "runtime/iterobject.h"
......@@ -284,8 +285,8 @@ extern "C" void typeGCHandler(GCVisitor* v, Box* b) {
BoxedClass* cls = (BoxedClass*)b;
if (cls->base)
v->visit(cls->base);
if (cls->tp_base)
v->visit(cls->tp_base);
if (cls->tp_dict)
v->visit(cls->tp_dict);
}
......@@ -433,9 +434,30 @@ extern "C" Box* createUserClass(std::string* name, Box* _bases, Box* _attr_dict)
}
assert(metaclass);
Box* r = runtimeCall(metaclass, ArgPassSpec(3), boxStringPtr(name), _bases, _attr_dict, NULL, NULL);
RELEASE_ASSERT(r, "");
return r;
try {
Box* r = runtimeCall(metaclass, ArgPassSpec(3), boxStringPtr(name), _bases, _attr_dict, NULL, NULL);
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) {
......@@ -804,7 +826,7 @@ void setupRuntime() {
root_hcls = HiddenClass::makeRoot();
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
= new BoxedHeapClass(NULL, object_cls, &typeGCHandler, offsetof(BoxedClass, attrs), sizeof(BoxedClass), false);
type_cls->cls = type_cls;
......
......@@ -174,10 +174,6 @@ class BoxedClass : public BoxVar {
public:
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:
PyTypeObject_BODY;
......@@ -197,10 +193,6 @@ public:
Box* callReprIC(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;
// Offset of the HCAttrs object or 0 if there are no hcattrs.
......@@ -223,6 +215,9 @@ public:
bool hasGenericGetattr() { return true; }
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 {
......@@ -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, attrs) == offsetof(struct _typeobject, _hcls), "");
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(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_mapping) == offsetof(PyHeapTypeObject, as_mapping), "");
static_assert(offsetof(pyston::BoxedHeapClass, as_sequence) == offsetof(PyHeapTypeObject, as_sequence), "");
......
......@@ -20,8 +20,7 @@ slots_tester_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
printf("slots_tester_seq.__new__, %d\n", n);
/* create attrgetterobject structure */
obj = PyObject_New(slots_tester_object, type);
obj = (slots_tester_object*)type->tp_alloc(type, 0);
if (obj == NULL)
return NULL;
......@@ -123,7 +122,7 @@ static PyTypeObject slots_tester_seq = {
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
slots_tester_seq_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
......@@ -145,6 +144,49 @@ static PyTypeObject slots_tester_seq = {
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) {
return obj->n;
}
......@@ -347,6 +389,56 @@ static PyTypeObject slots_tester_num = {
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:
static PyObject *
call_funcs(PyObject* _module, PyObject* args) {
......@@ -367,8 +459,13 @@ call_funcs(PyObject* _module, PyObject* args) {
if (cls->tp_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);
Py_DECREF(rtn);
if (!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) {
......@@ -519,10 +616,20 @@ initslots_test(void)
if (res < 0)
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
PyDict_SetItemString(slots_tester_seq.tp_dict, "set_through_tpdict", PyInt_FromLong(123));
PyModule_AddObject(m, "SlotsTesterSeq", (PyObject *)&slots_tester_seq);
PyModule_AddObject(m, "SlotsTesterMap", (PyObject *)&slots_tester_map);
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):
print hex(t)
print oct(t)
su = slots_test.SlotsTesterSub(5)
print su
class C(object):
def __repr__(self):
print "__repr__()"
......@@ -69,3 +72,19 @@ def add(self, rhs):
C.__add__ = add
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