Commit bd2ee538 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #852 from kmod/perf4

more small exceptions optimizations
parents 93ff229e dbada809
......@@ -86,6 +86,36 @@
#include "warnings.h"
#include "weakrefobject.h"
// Pyston additions:
// These new APIS give access to our fast hidden-class-based attributes implementation.
// Ideally in the future this will just be "storage strategy" of dicts and all Python
// dicts will benefit from it, but for now classes have to explicitly opt-in to having
// these kinds of attrs.
struct _hcattrs {
char _data[16];
};
#ifndef _PYSTON_API
typedef struct _hcattrs PyHcAttrs;
#else
namespace pyston {
class HCAttrs;
}
typedef int PyHcAttrs;
#endif
PyAPI_FUNC(void) PyObject_InitHcAttrs(PyHcAttrs*) PYSTON_NOEXCEPT;
PyAPI_FUNC(PyObject*) PyObject_GetAttrWrapper(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) PyType_RequestHcAttrs(PyTypeObject*, int offset) PYSTON_NOEXCEPT;
// Sets a descriptor on the type so that the attrs are available via __dict__
PyAPI_FUNC(void) PyType_GiveHcAttrsDictDescr(PyTypeObject*) PYSTON_NOEXCEPT;
// These functions directly manipulate the hcattrs storage, bypassing any getattro
// or descriptor logic. This is the equivallent of callling PyDict_GetItemString
// on an instance's dict.
// These functions try to mimic the Dict versions as much as possible, so for example
// the PyObject_GetHcAttrString function does not set an exception.
PyAPI_FUNC(PyObject*) PyObject_GetHcAttrString(PyObject*, const char*) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyObject_SetHcAttrString(PyObject*, const char*, PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyObject_DelHcAttrString(PyObject*, const char*) PYSTON_NOEXCEPT;
#include "codecs.h"
#include "pyerrors.h"
......
......@@ -10,14 +10,18 @@ extern "C" {
typedef struct {
PyObject_HEAD
PyObject *dict;
// Pyston change: changed from dict to hcattrs
// PyObject *dict;
struct _hcattrs hcattrs;
PyObject *args;
PyObject *message;
} PyBaseExceptionObject;
typedef struct {
PyObject_HEAD
PyObject *dict;
// Pyston change: changed from dict to hcattrs
// PyObject *dict;
struct _hcattrs hcattrs;
PyObject *args;
PyObject *message;
PyObject *msg;
......@@ -31,7 +35,9 @@ typedef struct {
#ifdef Py_USING_UNICODE
typedef struct {
PyObject_HEAD
PyObject *dict;
// Pyston change: changed from dict to hcattrs
// PyObject *dict;
struct _hcattrs hcattrs;
PyObject *args;
PyObject *message;
PyObject *encoding;
......@@ -44,7 +50,9 @@ typedef struct {
typedef struct {
PyObject_HEAD
PyObject *dict;
// Pyston change: changed from dict to hcattrs
// PyObject *dict;
struct _hcattrs hcattrs;
PyObject *args;
PyObject *message;
PyObject *code;
......@@ -52,7 +60,9 @@ typedef struct {
typedef struct {
PyObject_HEAD
PyObject *dict;
// Pyston change: changed from dict to hcattrs
// PyObject *dict;
struct _hcattrs hcattrs;
PyObject *args;
PyObject *message;
PyObject *myerrno;
......@@ -63,7 +73,9 @@ typedef struct {
#ifdef MS_WINDOWS
typedef struct {
PyObject_HEAD
PyObject *dict;
// Pyston change: changed from dict to hcattrs
// PyObject *dict;
struct _hcattrs hcattrs;
PyObject *args;
PyObject *message;
PyObject *myerrno;
......
......@@ -36,8 +36,8 @@ BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
if (!self)
return NULL;
/* the dict is created on the fly in PyObject_GenericSetAttr */
self->message = self->dict = NULL;
self->message = NULL;
PyObject_InitHcAttrs(&self->hcattrs);
self->args = PyTuple_New(0);
if (!self->args) {
......@@ -84,7 +84,7 @@ PyObject* PyErr_CreateExceptionInstance(PyObject* _type, PyObject* arg) {
if (!self)
return NULL;
self->dict = NULL;
PyObject_InitHcAttrs(&self->hcattrs);
if (arg) {
self->args = PyTuple_Pack(1, arg);
if (!self->args)
......@@ -115,7 +115,8 @@ PyObject* PyErr_CreateExceptionInstance(PyObject* _type, PyObject* arg) {
static int
BaseException_clear(PyBaseExceptionObject *self)
{
Py_CLEAR(self->dict);
// Pyston change:
// Py_CLEAR(self->dict);
Py_CLEAR(self->args);
Py_CLEAR(self->message);
return 0;
......@@ -132,7 +133,8 @@ BaseException_dealloc(PyBaseExceptionObject *self)
static int
BaseException_traverse(PyBaseExceptionObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->dict);
// Pyston change:
// Py_VISIT(self->dict);
Py_VISIT(self->args);
Py_VISIT(self->message);
return 0;
......@@ -225,10 +227,16 @@ BaseException_repr(PyBaseExceptionObject *self)
static PyObject *
BaseException_reduce(PyBaseExceptionObject *self)
{
/* Pyston change:
if (self->args && self->dict)
return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict);
else
return PyTuple_Pack(2, Py_TYPE(self), self->args);
*/
PyObject* attr_wrapper = PyObject_GetAttrWrapper((PyObject*)self);
if (!attr_wrapper)
return NULL;
return PyTuple_Pack(3, Py_TYPE(self), self->args, attr_wrapper);
}
/*
......@@ -299,6 +307,7 @@ static PySequenceMethods BaseException_as_sequence = {
0 /* sq_inplace_repeat; */
};
/* Pyston change: we use the standard dict descriptor for these types now
static PyObject *
BaseException_get_dict(PyBaseExceptionObject *self)
{
......@@ -327,6 +336,7 @@ BaseException_set_dict(PyBaseExceptionObject *self, PyObject *val)
self->dict = val;
return 0;
}
*/
static PyObject *
BaseException_get_args(PyBaseExceptionObject *self)
......@@ -361,8 +371,7 @@ BaseException_get_message(PyBaseExceptionObject *self)
PyObject *msg;
/* if "message" is in self->dict, accessing a user-set message attribute */
if (self->dict &&
(msg = PyDict_GetItemString(self->dict, "message"))) {
if ((msg = PyObject_GetHcAttrString((PyObject*)self, "message"))) {
Py_INCREF(msg);
return msg;
}
......@@ -387,25 +396,18 @@ BaseException_set_message(PyBaseExceptionObject *self, PyObject *val)
{
/* if val is NULL, delete the message attribute */
if (val == NULL) {
if (self->dict && PyDict_GetItemString(self->dict, "message")) {
if (PyDict_DelItemString(self->dict, "message") < 0)
if (PyObject_GetHcAttrString((PyObject*)self, "message")) {
if (PyObject_DelHcAttrString((PyObject*)self, "message") < 0)
return -1;
}
Py_CLEAR(self->message);
return 0;
}
/* else set it in __dict__, but may need to create the dict first */
if (self->dict == NULL) {
self->dict = PyDict_New();
if (!self->dict)
return -1;
}
return PyDict_SetItemString(self->dict, "message", val);
return PyObject_SetHcAttrString((PyObject*)self, "message", val);
}
static PyGetSetDef BaseException_getset[] = {
{"__dict__", (getter)BaseException_get_dict, (setter)BaseException_set_dict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
{"message", (getter)BaseException_get_message,
(setter)BaseException_set_message},
......@@ -450,7 +452,7 @@ static PyTypeObject _PyExc_BaseException = {
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
offsetof(PyBaseExceptionObject, dict), /* tp_dictoffset */
/* Pyston change: offsetof(PyBaseExceptionObject, dict)*/ 0, /* tp_dictoffset */
(initproc)BaseException_init, /* tp_init */
0, /* tp_alloc */
BaseException_new, /* tp_new */
......@@ -474,7 +476,7 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
PyDoc_STR(EXCDOC), (traverseproc)BaseException_traverse, \
(inquiry)BaseException_clear, 0, 0, 0, 0, 0, 0, 0, &_ ## EXCBASE, \
0, 0, 0, offsetof(PyBaseExceptionObject, dict), \
0, 0, 0, /* Pyston change: offsetof(PyBaseExceptionObject, dict) */ 0, \
(initproc)BaseException_init, 0, BaseException_new,\
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
......@@ -490,7 +492,7 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
(inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, 0, 0, 0, &_ ## EXCBASE, \
0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
0, 0, 0, /* Pyston change: offsetof(Py ## EXCSTORE ## Object, dict) */ 0, \
(initproc)EXCSTORE ## _init, 0, BaseException_new,\
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
......@@ -507,7 +509,7 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \
PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
(inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, EXCMETHODS, \
EXCMEMBERS, 0, &_ ## EXCBASE, \
0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
0, 0, 0, /* Pyston change: offsetof(Py ## EXCSTORE ## Object, dict) */ 0, \
(initproc)EXCSTORE ## _init, 0, BaseException_new,\
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
......@@ -831,12 +833,17 @@ EnvironmentError_reduce(PyEnvironmentErrorObject *self)
} else
Py_INCREF(args);
/* Pyston change:
if (self->dict)
res = PyTuple_Pack(3, Py_TYPE(self), args, self->dict);
else
res = PyTuple_Pack(2, Py_TYPE(self), args);
Py_DECREF(args);
return res;
return res; */
PyObject* attr_wrapper = PyObject_GetAttrWrapper((PyObject*)self);
if (!attr_wrapper)
return NULL;
return PyTuple_Pack(3, Py_TYPE(self), args, attr_wrapper);
}
......@@ -1743,7 +1750,7 @@ static PyTypeObject _PyExc_UnicodeEncodeError = {
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
PyDoc_STR("Unicode encoding error."), (traverseproc)UnicodeError_traverse,
(inquiry)UnicodeError_clear, 0, 0, 0, 0, 0, UnicodeError_members,
0, &_PyExc_UnicodeError, 0, 0, 0, offsetof(PyUnicodeErrorObject, dict),
0, &_PyExc_UnicodeError, 0, 0, 0, /* Pyston change: offsetof(PyUnicodeErrorObject, dict) */ 0,
(initproc)UnicodeEncodeError_init, 0, BaseException_new,
};
PyObject *PyExc_UnicodeEncodeError = (PyObject *)&_PyExc_UnicodeEncodeError;
......@@ -1828,7 +1835,7 @@ static PyTypeObject _PyExc_UnicodeDecodeError = {
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
PyDoc_STR("Unicode decoding error."), (traverseproc)UnicodeError_traverse,
(inquiry)UnicodeError_clear, 0, 0, 0, 0, 0, UnicodeError_members,
0, &_PyExc_UnicodeError, 0, 0, 0, offsetof(PyUnicodeErrorObject, dict),
0, &_PyExc_UnicodeError, 0, 0, 0, /* Pyston change: offsetof(PyUnicodeErrorObject, dict) */ 0,
(initproc)UnicodeDecodeError_init, 0, BaseException_new,
};
PyObject *PyExc_UnicodeDecodeError = (PyObject *)&_PyExc_UnicodeDecodeError;
......@@ -1926,7 +1933,7 @@ static PyTypeObject _PyExc_UnicodeTranslateError = {
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
PyDoc_STR("Unicode translation error."), (traverseproc)UnicodeError_traverse,
(inquiry)UnicodeError_clear, 0, 0, 0, 0, 0, UnicodeError_members,
0, &_PyExc_UnicodeError, 0, 0, 0, offsetof(PyUnicodeErrorObject, dict),
0, &_PyExc_UnicodeError, 0, 0, 0, /* Pyston change: offsetof(PyUnicodeErrorObject, dict) */ 0,
(initproc)UnicodeTranslateError_init, 0, BaseException_new,
};
PyObject *PyExc_UnicodeTranslateError = (PyObject *)&_PyExc_UnicodeTranslateError;
......@@ -2110,7 +2117,10 @@ _PyExc_Init(void)
{
PyObject *m, *bltinmod, *bdict;
PyType_RequestHcAttrs(&_PyExc_BaseException, offsetof(PyBaseExceptionObject, hcattrs));
PRE_INIT(BaseException)
PyType_GiveHcAttrsDictDescr(&_PyExc_BaseException);
PRE_INIT(Exception)
PRE_INIT(StandardError)
PRE_INIT(TypeError)
......
......@@ -797,8 +797,15 @@ extern "C" int PyObject_IsSubclass(PyObject* derived, PyObject* cls) noexcept {
return r;
}
if (!(PyClass_Check(cls) || PyInstance_Check(cls))) {
PyObject* checker;
checker = _PyObject_LookupSpecial(cls, "__subclasscheck__", &name);
PyObject* checker = NULL;
if (cls->cls->has_subclasscheck) {
checker = _PyObject_LookupSpecial(cls, "__subclasscheck__", &name);
if (!checker && PyErr_Occurred())
return -1;
assert(checker);
}
if (checker != NULL) {
PyObject* res;
int ok = -1;
......
......@@ -1152,7 +1152,7 @@ static PyObject* slot_tp_del(PyObject* self) noexcept {
}
}
static int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept {
/* Pyston change: static */ int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept {
STAT_TIMER(t0, "us_timer_slot_tpinit", SLOT_AVOIDABILITY(self));
static PyObject* init_str;
......@@ -1645,6 +1645,7 @@ static slotdef slotdefs[]
TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""),
FLSLOT("__class__", has___class__, NULL, NULL, "", PyWrapperFlag_BOOL),
FLSLOT("__instancecheck__", has_instancecheck, NULL, NULL, "", PyWrapperFlag_BOOL),
FLSLOT("__subclasscheck__", has_subclasscheck, NULL, NULL, "", PyWrapperFlag_BOOL),
FLSLOT("__getattribute__", has_getattribute, NULL, NULL, "", PyWrapperFlag_BOOL),
TPPSLOT("__hasnext__", tpp_hasnext, slotTppHasnext, wrapInquirypred, "hasnext"),
......@@ -3262,6 +3263,18 @@ static Box* tppProxyToTpCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSp
return r;
}
extern "C" void PyType_RequestHcAttrs(PyTypeObject* cls, int offset) noexcept {
assert(cls->attrs_offset == 0);
assert(cls->tp_dictoffset == 0);
cls->attrs_offset = offset;
}
extern "C" void PyType_GiveHcAttrsDictDescr(PyTypeObject* cls) noexcept {
static BoxedString* dict_str = internStringImmortal("__dict__");
assert(!cls->hasattr(dict_str));
cls->giveAttr(dict_str, dict_descr);
}
extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
ASSERT(!cls->is_pyston_class, "should not call this on Pyston classes");
......@@ -3389,11 +3402,10 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
// e.g. CTypes does this.
bool is_metaclass = PyType_IsSubtype(cls, type_cls);
assert(!is_metaclass || cls->instancesHaveHCAttrs() || cls->instancesHaveDictAttrs());
} else {
// this should get automatically initialized to 0 on this path:
assert(cls->attrs_offset == 0);
}
assert(!(cls->instancesHaveHCAttrs() && cls->instancesHaveDictAttrs()));
if (Py_TPFLAGS_BASE_EXC_SUBCLASS & cls->tp_flags) {
exception_types.push_back(cls);
}
......
......@@ -51,6 +51,7 @@ int slot_sq_contains(PyObject* self, PyObject* value) noexcept;
Py_ssize_t slot_sq_length(PyObject* self) noexcept;
PyObject* slot_tp_getattr_hook(PyObject* self, PyObject* name) noexcept;
PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) noexcept;
int slot_tp_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept;
class GetattrRewriteArgs;
Box* slotTpGetattrHookInternal(Box* self, BoxedString* attr, GetattrRewriteArgs* rewrite_args);
......
......@@ -225,15 +225,18 @@ protected:
} else
++it;
}
ASSERT(this->map.size() == this->map_elts, "%ld %d", this->map.size(), this->map_elts);
}
public:
PerThreadSet(CtorArgs... ctor_args) : ctor_args(std::forward<CtorArgs>(ctor_args)...) {
int code = pthread_key_create(&pthread_key, &dtor);
ASSERT(this->map.size() == this->map_elts, "%ld %d", this->map.size(), this->map_elts);
}
void forEachValue(std::function<void(T*)> f) {
LOCK_REGION(&lock);
ASSERT(this->map.size() == this->map_elts, "%ld %d", this->map.size(), this->map_elts);
for (auto& p : map) {
f(&p.second->val);
......@@ -242,6 +245,7 @@ public:
template <typename... Arguments> void forEachValue(std::function<void(T*, Arguments...)> f, Arguments... args) {
LOCK_REGION(&lock);
ASSERT(this->map.size() == this->map_elts, "%ld %d", this->map.size(), this->map_elts);
for (auto& p : map) {
f(&p.second->val, std::forward<Arguments>(args)...);
......@@ -267,6 +271,7 @@ public:
assert(map.count(pthread_self()) == 0);
map[pthread_self()] = s;
ASSERT(this->map.size() == this->map_elts, "%ld %d", this->map.size(), this->map_elts);
}
return &s->val;
}
......
......@@ -505,6 +505,7 @@ public:
HCAttrs(HiddenClass* hcls = root_hcls) : hcls(hcls), attr_list(nullptr) {}
};
static_assert(sizeof(HCAttrs) == sizeof(struct _hcattrs), "");
class BoxedDict;
class BoxedString;
......
......@@ -984,6 +984,13 @@ extern "C" int PyErr_GivenExceptionMatches(PyObject* err, PyObject* exc) noexcep
err = PyExceptionInstance_Class(err);
if (PyExceptionClass_Check(err) && PyExceptionClass_Check(exc)) {
// Pyston addition: fast-path the check for if the exception exactly-matches the specifier.
// Note that we have to check that the exception specifier doesn't have a custom metaclass
// (ie it's cls is type_cls), since otherwise we would have to check for subclasscheck overloading.
// (TODO actually, that should be fast now)
if (exc->cls == type_cls && exc == err)
return 1;
int res = 0, reclimit;
PyObject* exception, *value, *tb;
PyErr_Fetch(&exception, &value, &tb);
......
......@@ -75,9 +75,9 @@ static Box* propertyInit(Box* _self, Box* fget, Box* fset, Box** args) {
Box* doc = args[1];
BoxedProperty* self = static_cast<BoxedProperty*>(_self);
self->prop_get = fget;
self->prop_set = fset;
self->prop_del = fdel;
self->prop_get = fget == None ? NULL : fget;
self->prop_set = fset == None ? NULL : fset;
self->prop_del = fdel == None ? NULL : fdel;
self->prop_doc = doc;
self->getter_doc = false;
......@@ -151,6 +151,12 @@ static Box* property_copy(BoxedProperty* old, Box* get, Box* set, Box* del) {
return prop;
} else {
if (!get)
get = None;
if (!set)
set = None;
if (!del)
del = None;
Box* doc;
if ((old->getter_doc && get != None) || !old->prop_doc)
doc = None;
......
......@@ -832,15 +832,16 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
RewriterVar* r_ccls = NULL;
RewriterVar* r_new = NULL;
RewriterVar* r_init = NULL;
Box* new_attr, *init_attr;
Box* new_attr, * init_attr = NULL;
if (rewrite_args) {
assert(!argspec.has_starargs);
assert(!argspec.has_kwargs);
assert(argspec.num_args > 0);
r_ccls = rewrite_args->arg1;
// This is probably a duplicate, but it's hard to really convince myself of that.
// Need to create a clear contract of who guards on what
// Guard on the requested class. We could potentially support multiple classes in a rewrite,
// but there are parts of this function's rewrite that currently take advantage of the fact
// that the requested class is fixed.
r_ccls->addGuard((intptr_t)arg1 /* = _cls */);
......@@ -930,7 +931,7 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
//
// For debugging, keep track of why we think we can rewrite this:
enum { NOT_ALLOWED, VERIFIED, NO_INIT, TYPE_NEW_SPECIAL_CASE, } why_rewrite_allowed = NOT_ALLOWED;
enum { NOT_ALLOWED, MAKES_CLS, NO_INIT, TYPE_NEW_SPECIAL_CASE, } why_rewrite_allowed = NOT_ALLOWED;
// These are __new__ functions that have the property that __new__(kls) always returns an instance of kls.
// These are ok to call regardless of what type was requested.
......@@ -939,32 +940,32 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
// type? then object.__new__ would not be able to be here:
//
// this array is ok with not using StlCompatAllocator since we will manually register these objects with the GC
static std::vector<Box*> allowable_news;
if (allowable_news.empty()) {
static std::vector<Box*> class_making_news;
if (class_making_news.empty()) {
for (BoxedClass* allowed_cls : { object_cls, enumerate_cls, xrange_cls, tuple_cls, list_cls, dict_cls }) {
auto new_obj = typeLookup(allowed_cls, new_str, NULL);
gc::registerPermanentRoot(new_obj, /* allow_duplicates= */ true);
allowable_news.push_back(new_obj);
class_making_news.push_back(new_obj);
}
}
if (rewrite_args) {
for (auto b : allowable_news) {
for (auto b : class_making_news) {
if (b == new_attr) {
why_rewrite_allowed = VERIFIED;
why_rewrite_allowed = MAKES_CLS;
break;
}
}
if (cls->tp_new == BaseException->tp_new)
why_rewrite_allowed = VERIFIED;
why_rewrite_allowed = MAKES_CLS;
bool know_first_arg = !argspec.has_starargs && !argspec.has_kwargs && argspec.num_keywords == 0;
if (know_first_arg) {
if (argspec.num_args == 1
&& (cls == int_cls || cls == float_cls || cls == long_cls || cls == str_cls || cls == unicode_cls))
why_rewrite_allowed = VERIFIED;
why_rewrite_allowed = MAKES_CLS;
if (argspec.num_args == 2 && (cls == int_cls || cls == float_cls || cls == long_cls)
&& (arg2->cls == int_cls || arg2->cls == str_cls || arg2->cls == float_cls
......@@ -975,7 +976,7 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
// str(obj) can return str-subtypes, but for builtin types it won't:
if (argspec.num_args == 2 && cls == str_cls && (arg2->cls == int_cls || arg2->cls == float_cls)) {
why_rewrite_allowed = VERIFIED;
why_rewrite_allowed = MAKES_CLS;
rewrite_args->arg2->addAttrGuard(offsetof(Box, cls), (intptr_t)arg2->cls);
}
......@@ -1011,22 +1012,25 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
}
static BoxedString* init_str = internStringImmortal("__init__");
if (rewrite_args) {
GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, r_ccls, rewrite_args->destination);
init_attr = typeLookup(cls, init_str, &grewrite_args);
if (cls->tp_init == slot_tp_init) {
// If there's a Python-level tp_init, try getting it, since calling it might be faster than calling
// tp_init if we can manage to rewrite it.
if (rewrite_args) {
GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, r_ccls, rewrite_args->destination);
init_attr = typeLookup(cls, init_str, &grewrite_args);
if (!grewrite_args.out_success)
rewrite_args = NULL;
else {
if (init_attr) {
r_init = grewrite_args.out_rtn;
r_init->addGuard((intptr_t)init_attr);
if (!grewrite_args.out_success)
rewrite_args = NULL;
else {
if (init_attr) {
r_init = grewrite_args.out_rtn;
r_init->addGuard((intptr_t)init_attr);
}
}
} else {
init_attr = typeLookup(cls, init_str, NULL);
}
} else {
init_attr = typeLookup(cls, init_str, NULL);
}
// The init_attr should always resolve as well, but doesn't yet
Box* made;
RewriterVar* r_made = NULL;
......@@ -1121,60 +1125,95 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
return made;
}
bool skip_init = false;
// If __new__ returns a subclass, supposed to call that subclass's __init__.
// If __new__ returns a non-subclass, not supposed to call __init__.
if (made->cls != cls) {
assert(why_rewrite_allowed != MAKES_CLS);
ASSERT(rewrite_args == NULL || (why_rewrite_allowed == NO_INIT && made->cls->tp_init == object_cls->tp_init
&& cls->tp_init == object_cls->tp_init),
"We should only have allowed the rewrite to continue if we were guaranteed that "
"made would have class cls!");
if (!isSubclass(made->cls, cls)) {
init_attr = NULL;
skip_init = true;
} else {
// We could have skipped the initial __init__ lookup
init_attr = typeLookup(made->cls, init_str, NULL);
if (init_attr) {
// Getting here means the init_attr is wrong; set it to NULL so that we don't use it.
init_attr = NULL;
}
}
}
if (init_attr && made->cls->tp_init != object_cls->tp_init) {
// TODO apply the same descriptor special-casing as in callattr?
if (!skip_init && made->cls->tp_init != object_cls->tp_init) {
Box* initrtn;
// Attempt to rewrite the basic case:
if (rewrite_args && init_attr->cls == function_cls) {
// Note: this code path includes the descriptor logic
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_init, rewrite_args->destination);
srewrite_args.arg1 = r_made;
if (npassed_args >= 2)
srewrite_args.arg2 = rewrite_args->arg2;
if (npassed_args >= 3)
srewrite_args.arg3 = rewrite_args->arg3;
if (npassed_args >= 4)
srewrite_args.args = rewrite_args->args;
srewrite_args.args_guarded = rewrite_args->args_guarded;
srewrite_args.func_guarded = true;
// If there's a Python-level __init__ function, try calling it.
if (init_attr && init_attr->cls == function_cls) {
if (rewrite_args) {
// We are going to rewrite as a call to cls.init:
assert(why_rewrite_allowed == MAKES_CLS);
assert(made->cls == cls);
}
// initrtn = callattrInternal<CXX>(cls, _init_str, INST_ONLY, &srewrite_args, argspec, made, arg2, arg3,
// args, keyword_names);
initrtn = runtimeCallInternal<S>(init_attr, &srewrite_args, argspec, made, arg2, arg3, args, keyword_names);
// Note: this code path includes the descriptor logic
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_init, rewrite_args->destination);
srewrite_args.arg1 = r_made;
if (npassed_args >= 2)
srewrite_args.arg2 = rewrite_args->arg2;
if (npassed_args >= 3)
srewrite_args.arg3 = rewrite_args->arg3;
if (npassed_args >= 4)
srewrite_args.args = rewrite_args->args;
srewrite_args.args_guarded = rewrite_args->args_guarded;
srewrite_args.func_guarded = true;
initrtn
= runtimeCallInternal<S>(init_attr, &srewrite_args, argspec, made, arg2, arg3, args, keyword_names);
if (!srewrite_args.out_success) {
rewrite_args = NULL;
} else {
assert(S == CXX && "this need to be converted");
rewrite_args->rewriter->call(true, (void*)assertInitNone, srewrite_args.out_rtn);
}
} else {
initrtn = runtimeCallInternal<S>(init_attr, NULL, argspec, made, arg2, arg3, args, keyword_names);
}
if (!initrtn) {
assert(S == CAPI);
return NULL;
}
if (!srewrite_args.out_success) {
rewrite_args = NULL;
} else {
assert(S == CXX && "this need to be converted");
rewrite_args->rewriter->call(true, (void*)assertInitNone, srewrite_args.out_rtn);
}
assert(initrtn);
if (S == CAPI) {
if (initrtn != None) {
PyErr_Format(TypeError, "__init__() should return None, not '%s'", getTypeName(initrtn));
return NULL;
}
} else
assertInitNone(initrtn);
} else {
rewrite_args = NULL;
// Otherwise, just call tp_init. This will work out well for extension classes, and no worse
// than failing the rewrite for Python non-extension non-functions (when does that happen?).
initproc tpinit = made->cls->tp_init;
if (rewrite_args) {
// This is the only case that should get here:
assert(why_rewrite_allowed == MAKES_CLS && made->cls == cls);
// We're going to emit a call to cls->tp_init, but really we should be calling made->cls->tp_init,
// but the MAKES_CLS condition tells us that made->cls is cls so the two tp_inits are the same.
assert(tpinit == cls->tp_init);
}
bool rewrite_success = false;
try {
init_attr = processDescriptor(init_attr, made, cls);
rearrangeArguments(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, rewrite_success,
argspec, made, arg2, arg3, args, NULL, keyword_names);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
......@@ -1183,30 +1222,28 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
throw e;
}
ArgPassSpec init_argspec = argspec;
init_argspec.num_args--;
int passed = init_argspec.totalPassed();
if (!rewrite_success)
rewrite_args = NULL;
// If we weren't passed the args array, it's not safe to index into it
if (passed <= 2)
initrtn = runtimeCallInternal<S>(init_attr, NULL, init_argspec, arg2, arg3, NULL, NULL, keyword_names);
else
initrtn = runtimeCallInternal<S>(init_attr, NULL, init_argspec, arg2, arg3, args[0], &args[1],
keyword_names);
assert(arg2->cls == tuple_cls);
assert(!arg3 || arg3->cls == dict_cls);
if (!initrtn) {
assert(S == CAPI);
return NULL;
int err = tpinit(made, arg2, arg3);
if (err == -1) {
if (S == CAPI)
return NULL;
else
throwCAPIException();
}
}
assert(initrtn);
if (S == CAPI && initrtn != None) {
PyErr_Format(TypeError, "__init__() should return None, not '%s'", getTypeName(initrtn));
return NULL;
if (rewrite_args) {
auto r_err
= rewrite_args->rewriter->call(true, (void*)tpinit, r_made, rewrite_args->arg2, rewrite_args->arg3);
assert(S == CXX && "this need to be converted");
rewrite_args->rewriter->checkAndThrowCAPIException(r_err, -1);
}
}
assertInitNone(initrtn);
} else {
if (new_attr == NULL && npassed_args != 1) {
// TODO not npassed args, since the starargs or kwargs could be null
......@@ -2480,6 +2517,10 @@ Box* Box::getAttrWrapper() {
return attrs->attr_list->attrs[offset];
}
extern "C" PyObject* PyObject_GetAttrWrapper(PyObject* obj) noexcept {
return obj->getAttrWrapper();
}
Box* unwrapAttrWrapper(Box* b) {
assert(b->cls == attrwrapper_cls);
return static_cast<AttrWrapper*>(b)->getUnderlying();
......@@ -3000,6 +3041,26 @@ inline void initUserAttrs(Box* obj, BoxedClass* cls) {
}
}
extern "C" void PyObject_InitHcAttrs(HCAttrs* attrs) noexcept {
new ((void*)attrs) HCAttrs();
}
extern "C" PyObject* PyObject_GetHcAttrString(PyObject* obj, const char* attr) PYSTON_NOEXCEPT {
return obj->getattr(internStringMortal(attr));
}
extern "C" int PyObject_SetHcAttrString(PyObject* obj, const char* attr, PyObject* val) PYSTON_NOEXCEPT {
obj->setattr(internStringMortal(attr), val, NULL);
return 0;
}
extern "C" int PyObject_DelHcAttrString(PyObject* obj, const char* attr) PYSTON_NOEXCEPT {
BoxedString* attr_str = internStringMortal(attr);
bool has = obj->hasattr(attr_str);
if (!has)
return -1;
obj->delattr(attr_str, NULL);
return 0;
}
extern "C" PyVarObject* PyObject_InitVar(PyVarObject* op, PyTypeObject* tp, Py_ssize_t size) noexcept {
assert(op);
assert(tp);
......
......@@ -232,6 +232,7 @@ public:
bool has___class__; // Has a custom __class__ attribute (ie different from object's __class__ descriptor)
bool has_instancecheck;
bool has_subclasscheck;
bool has_getattribute;
typedef bool (*pyston_inquiry)(Box*);
......
......@@ -104,7 +104,7 @@ f14()
def test_set_state():
exc = BaseException()
print exc.__dict__
print sorted(exc.__dict__.items())
attrs = {"x": 1, "y": 2}
exc.__setstate__(attrs)
print exc.__dict__ == attrs
......
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