Commit 47d02706 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #824 from kmod/perf4

More small optimizations
parents 109df648 ee5b6d49
......@@ -2558,11 +2558,11 @@ static int mro_internal(PyTypeObject* type) noexcept {
extern "C" int PyType_IsSubtype(PyTypeObject* a, PyTypeObject* b) noexcept {
PyObject* mro;
if (!(a->tp_flags & Py_TPFLAGS_HAVE_CLASS))
if (unlikely(!(a->tp_flags & Py_TPFLAGS_HAVE_CLASS)))
return b == a || b == &PyBaseObject_Type;
mro = a->tp_mro;
if (mro != NULL) {
if (likely(mro != NULL)) {
/* Deal with multiple inheritance without recursion
by walking the MRO tuple */
Py_ssize_t i, n;
......@@ -3217,10 +3217,10 @@ static Box* tppProxyToTpCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSp
}
bool rewrite_success = false;
Box* oarg1, * oarg2 = NULL, *oarg3, ** oargs = NULL;
Box** oargs = NULL;
try {
rearrangeArguments(paramspec, NULL, "", NULL, rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args,
keyword_names, oarg1, oarg2, oarg3, oargs);
oargs, keyword_names);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
......@@ -3251,7 +3251,7 @@ static Box* tppProxyToTpCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSp
rewrite_args->out_success = true;
}
Box* r = self->cls->tp_call(self, oarg1, oarg2);
Box* r = self->cls->tp_call(self, arg1, arg2);
if (!r && S == CXX)
throwCAPIException();
return r;
......
......@@ -468,7 +468,7 @@ template <ExceptionStyle S> Box* getattrFunc(Box* obj, Box* _str, Box* default_v
throw e;
}
if (!isSubclass(_str->cls, str_cls)) {
if (!PyString_Check(_str)) {
if (S == CAPI) {
PyErr_SetString(TypeError, "getattr(): attribute name must be string");
return NULL;
......
......@@ -1524,14 +1524,11 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
RELEASE_ASSERT(0, "0x%x", flags);
}
Box* oarg1 = NULL;
Box* oarg2 = NULL;
Box* oarg3 = NULL;
Box** oargs = NULL;
bool rewrite_success = false;
rearrangeArguments(paramspec, NULL, self->method_def->ml_name, NULL, rewrite_args, rewrite_success, argspec, arg1,
arg2, arg3, args, keyword_names, oarg1, oarg2, oarg3, oargs);
arg2, arg3, args, oargs, keyword_names);
if (!rewrite_success)
rewrite_args = NULL;
......@@ -1542,11 +1539,11 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
Box* rtn;
if (flags == METH_VARARGS) {
rtn = (Box*)func(self->passthrough, oarg1);
rtn = (Box*)func(self->passthrough, arg1);
if (rewrite_args)
rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1);
} else if (flags == (METH_VARARGS | METH_KEYWORDS)) {
rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, oarg1, oarg2);
rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, arg1, arg2);
if (rewrite_args)
rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1,
rewrite_args->arg2);
......@@ -1556,7 +1553,7 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
rewrite_args->out_rtn = rewrite_args->rewriter->call(
true, (void*)func, r_passthrough, rewrite_args->rewriter->loadConst(0, Location::forArg(1)));
} else if (flags == METH_O) {
rtn = (Box*)func(self->passthrough, oarg1);
rtn = (Box*)func(self->passthrough, arg1);
if (rewrite_args)
rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1);
} else if (flags == METH_OLDARGS) {
......@@ -1564,10 +1561,10 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
rewrite_args = NULL;
int size = PyTuple_GET_SIZE(oarg1);
Box* arg = oarg1;
int size = PyTuple_GET_SIZE(arg1);
Box* arg = arg1;
if (size == 1)
arg = PyTuple_GET_ITEM(oarg1, 0);
arg = PyTuple_GET_ITEM(arg1, 0);
else if (size == 0)
arg = NULL;
rtn = func(self->passthrough, arg);
......
......@@ -292,45 +292,41 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
RELEASE_ASSERT(0, "0x%x", call_flags);
}
Box* oarg1 = NULL;
Box* oarg2 = NULL;
Box* oarg3 = NULL;
Box** oargs = NULL;
Box* oargs_array[1];
if (paramspec.totalReceived() >= 3) {
if (paramspec.totalReceived() > 3) {
assert((paramspec.totalReceived() - 3) <= sizeof(oargs_array) / sizeof(oargs_array[0]));
oargs = oargs_array;
}
bool rewrite_success = false;
rearrangeArguments(paramspec, NULL, self->method->ml_name, defaults, rewrite_args, rewrite_success, argspec, arg1,
arg2, arg3, args, keyword_names, oarg1, oarg2, oarg3, oargs);
arg2, arg3, args, oargs, keyword_names);
if (!rewrite_success)
rewrite_args = NULL;
if (ml_flags & METH_CLASS) {
rewrite_args = NULL;
if (!isSubclass(oarg1->cls, type_cls))
if (!isSubclass(arg1->cls, type_cls))
raiseExcHelper(TypeError, "descriptor '%s' requires a type but received a '%s'", self->method->ml_name,
getFullTypeName(oarg1).c_str());
getFullTypeName(arg1).c_str());
} else {
if (!isSubclass(oarg1->cls, self->type))
raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' oarg1 but received a '%s'",
self->method->ml_name, getFullNameOfClass(self->type).c_str(),
getFullTypeName(oarg1).c_str());
if (!isSubclass(arg1->cls, self->type))
raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' arg1 but received a '%s'", self->method->ml_name,
getFullNameOfClass(self->type).c_str(), getFullTypeName(arg1).c_str());
}
if (rewrite_args) {
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)oarg1->cls);
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls);
}
Box* rtn;
if (call_flags == METH_NOARGS) {
{
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
rtn = (Box*)self->method->ml_meth(oarg1, NULL);
rtn = (Box*)self->method->ml_meth(arg1, NULL);
}
if (rewrite_args)
rewrite_args->out_rtn
......@@ -339,7 +335,7 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
} else if (call_flags == METH_VARARGS) {
{
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
rtn = (Box*)self->method->ml_meth(oarg1, oarg2);
rtn = (Box*)self->method->ml_meth(arg1, arg2);
}
if (rewrite_args)
rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
......@@ -347,7 +343,7 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
} else if (call_flags == (METH_VARARGS | METH_KEYWORDS)) {
{
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
rtn = (Box*)((PyCFunctionWithKeywords)self->method->ml_meth)(oarg1, oarg2, oarg3);
rtn = (Box*)((PyCFunctionWithKeywords)self->method->ml_meth)(arg1, arg2, arg3);
}
if (rewrite_args)
rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
......@@ -355,7 +351,7 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
} else if (call_flags == METH_O) {
{
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
rtn = (Box*)self->method->ml_meth(oarg1, oarg2);
rtn = (Box*)self->method->ml_meth(arg1, arg2);
}
if (rewrite_args)
rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
......@@ -363,7 +359,7 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
} else if ((call_flags & ~(METH_O3 | METH_D3)) == 0) {
{
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(oarg1, oarg2, oarg3, oargs);
rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(arg1, arg2, arg3, oargs);
}
if (rewrite_args) {
if (paramspec.totalReceived() == 2)
......@@ -536,20 +532,16 @@ Box* BoxedWrapperObject::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgP
RELEASE_ASSERT(0, "%d", flags);
}
Box* oarg1 = NULL;
Box* oarg2 = NULL;
Box* oarg3 = NULL;
Box** oargs = NULL;
bool rewrite_success = false;
rearrangeArguments(paramspec, NULL, self->descr->wrapper->name.data(), NULL, rewrite_args, rewrite_success, argspec,
arg1, arg2, arg3, args, keyword_names, oarg1, oarg2, oarg3, oargs);
arg1, arg2, arg3, args, oargs, keyword_names);
assert(oarg1 && oarg1->cls == tuple_cls);
assert(arg1 && arg1->cls == tuple_cls);
if (!paramspec.takes_kwargs)
assert(oarg2 == NULL);
assert(oarg3 == NULL);
assert(oargs == NULL);
assert(arg2 == NULL);
assert(arg3 == NULL);
if (!rewrite_success)
rewrite_args = NULL;
......@@ -557,7 +549,7 @@ Box* BoxedWrapperObject::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgP
Box* rtn;
if (flags == PyWrapperFlag_KEYWORDS) {
wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
rtn = (*wk)(self->obj, oarg1, self->descr->wrapped, oarg2);
rtn = (*wk)(self->obj, arg1, self->descr->wrapped, arg2);
if (rewrite_args) {
auto rewriter = rewrite_args->rewriter;
......@@ -569,7 +561,7 @@ Box* BoxedWrapperObject::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgP
rewrite_args->out_success = true;
}
} else if (flags == PyWrapperFlag_PYSTON || flags == 0) {
rtn = (*wrapper)(self->obj, oarg1, self->descr->wrapped);
rtn = (*wrapper)(self->obj, arg1, self->descr->wrapped);
if (rewrite_args) {
auto rewriter = rewrite_args->rewriter;
......
......@@ -1018,7 +1018,7 @@ template <ExceptionStyle S> Box* intNew(Box* _cls, Box* val, Box* base) noexcept
return n;
if (S == CAPI) {
PyErr_Format(OverflowError, "Python int too large to convert to C long");
PyErr_SetString(OverflowError, "Python int too large to convert to C long");
return NULL;
} else
raiseExcHelper(OverflowError, "Python int too large to convert to C long");
......
......@@ -244,11 +244,6 @@ extern "C" void my_assert(bool b) {
assert(b);
}
extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent) {
STAT_TIMER(t0, "us_timer_isSubclass", 10);
return PyType_IsSubtype(child, parent);
}
extern "C" void assertFail(Box* assertion_type, Box* msg) {
RELEASE_ASSERT(assertion_type->cls == type_cls, "%s", assertion_type->cls->tp_name);
if (msg) {
......@@ -3052,8 +3047,8 @@ ArgPassSpec bindObjIntoArgs(Box* bind_obj, RewriterVar* r_bind_obj, CallRewriteA
void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
Box** defaults, CallRewriteArgs* rewrite_args, bool& rewrite_success, ArgPassSpec argspec,
Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names,
Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** oargs) {
Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** args, Box** oargs,
const std::vector<BoxedString*>* keyword_names) {
/*
* Procedure:
* - First match up positional arguments; any extra go to varargs. error if too many.
......@@ -3066,21 +3061,30 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
int num_passed_args = argspec.totalPassed();
if (num_passed_args >= 1)
assert(gc::isValidGCObject(arg1) || !arg1);
assert(gc::isValidGCObject(oarg1) || !oarg1);
if (num_passed_args >= 2)
assert(gc::isValidGCObject(arg2) || !arg2);
assert(gc::isValidGCObject(oarg2) || !oarg2);
if (num_passed_args >= 3)
assert(gc::isValidGCObject(arg3) || !arg3);
assert(gc::isValidGCObject(oarg3) || !oarg3);
for (int i = 3; i < num_passed_args; i++) {
assert(gc::isValidGCObject(args[i - 3]) || args[i - 3] == NULL);
}
assert((oargs != NULL) == (num_output_args > 3));
assert((defaults != NULL) == (paramspec.num_defaults != 0));
if (rewrite_args) {
rewrite_success = false; // default case
}
// Super fast path:
if (argspec.num_keywords == 0 && !argspec.has_starargs && !paramspec.takes_varargs && !argspec.has_kwargs
&& argspec.num_args == paramspec.num_args && !paramspec.takes_kwargs) {
rewrite_success = true;
if (num_output_args > 3)
memcpy(oargs, args, sizeof(Box*) * (num_output_args - 3));
return;
}
// Fast path: if it's a simple-enough call, we don't have to do anything special. On a simple
// django-admin test this covers something like 93% of all calls to callFunc.
if (argspec.num_keywords == 0 && argspec.has_starargs == paramspec.takes_varargs && !argspec.has_kwargs
......@@ -3091,7 +3095,7 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
assert(num_output_args == num_passed_args + 1);
int idx = paramspec.kwargsIndex();
assert(idx < 3);
getArg(idx, arg1, arg2, arg3, NULL) = NULL; // pass NULL for kwargs
getArg(idx, oarg1, oarg2, oarg3, NULL) = NULL; // pass NULL for kwargs
if (rewrite_args) {
if (idx == 0)
rewrite_args->arg1 = rewrite_args->rewriter->loadConst(0);
......@@ -3111,31 +3115,31 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
// received by the caller are always tuples).
// This is why we can't pass kwargs here.
if (argspec.has_starargs) {
Box* given_varargs = getArg(argspec.num_args + argspec.num_keywords, arg1, arg2, arg3, args);
Box* given_varargs = getArg(argspec.num_args + argspec.num_keywords, oarg1, oarg2, oarg3, args);
if (given_varargs->cls == tuple_cls) {
if (rewrite_args) {
getArg(argspec.num_args + argspec.num_keywords, rewrite_args)
->addAttrGuard(offsetof(Box, cls), (intptr_t)tuple_cls);
}
rewrite_success = true;
oarg1 = arg1;
oarg2 = arg2;
oarg3 = arg3;
if (num_output_args > 3)
memcpy(oargs, args, sizeof(Box*) * (num_output_args - 3));
return;
}
} else {
rewrite_success = true;
oarg1 = arg1;
oarg2 = arg2;
oarg3 = arg3;
if (num_output_args > 3)
memcpy(oargs, args, sizeof(Box*) * (num_output_args - 3));
return;
}
}
// Save the original values:
Box* arg1 = oarg1;
Box* arg2 = oarg2;
Box* arg3 = oarg3;
oarg1 = oarg2 = oarg3 = NULL;
static StatCounter slowpath_rearrangeargs_slowpath("slowpath_rearrangeargs_slowpath");
slowpath_rearrangeargs_slowpath.log();
......@@ -3410,7 +3414,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
}
}
Box* oarg1, *oarg2, *oarg3, **oargs;
Box** oargs;
bool rewrite_success = false;
int num_output_args = paramspec.totalReceived();
......@@ -3431,7 +3435,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
try {
rearrangeArguments(paramspec, &f->param_names, getFunctionName(f).data(),
paramspec.num_defaults ? func->defaults->elts : NULL, rewrite_args, rewrite_success, argspec,
arg1, arg2, arg3, args, keyword_names, oarg1, oarg2, oarg3, oargs);
arg1, arg2, arg3, args, oargs, keyword_names);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(e);
......@@ -3442,7 +3446,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
#if 0
for (int i = 0; i < num_output_args; i++) {
auto arg = getArg(i, oarg1, oarg2, oarg3, oargs);
auto arg = getArg(i, arg1, arg2, arg3, oargs);
RELEASE_ASSERT(!arg || gc::isValidGCObject(arg), "%p", arg);
}
#endif
......@@ -3518,9 +3522,9 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
if (f->isGenerator()) {
// TODO: we might not have a lot to gain by rewriting into createGenerator, but we could at least
// rewrite up to the call to it:
res = createGenerator(func, oarg1, oarg2, oarg3, oargs);
res = createGenerator(func, arg1, arg2, arg3, oargs);
} else {
res = callCLFunc<S>(f, rewrite_args, num_output_args, closure, NULL, func->globals, oarg1, oarg2, oarg3, oargs);
res = callCLFunc<S>(f, rewrite_args, num_output_args, closure, NULL, func->globals, arg1, arg2, arg3, oargs);
}
return res;
......
......@@ -100,7 +100,11 @@ extern "C" Box** unpackIntoArray(Box* obj, int64_t expected_size);
extern "C" void assertNameDefined(bool b, const char* name, BoxedClass* exc_cls, bool local_var_msg);
extern "C" void assertFailDerefNameDefined(const char* name);
extern "C" void assertFail(Box* assertion_type, Box* msg);
extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent);
inline bool isSubclass(BoxedClass* child, BoxedClass* parent) {
return child == parent || PyType_IsSubtype(child, parent);
}
extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure, size_t size);
Box* getiter(Box* o);
......
......@@ -149,8 +149,8 @@ struct CompareRewriteArgs {
// match anyway. Or maybe it should call a callback function, which could save on the common case.
void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
Box** defaults, CallRewriteArgs* rewrite_args, bool& rewrite_success, ArgPassSpec argspec,
Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names,
Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** oargs);
Box*& arg1, Box*& arg2, Box*& arg3, Box** args, Box** oargs,
const std::vector<BoxedString*>* keyword_names);
// new_args should be allocated by the caller if at least three args get passed in.
// rewrite_args will get modified in place.
......
......@@ -328,7 +328,7 @@ Box* tupleCount(BoxedTuple* self, Box* elt) {
}
extern "C" Box* tupleNew(Box* _cls, BoxedTuple* args, BoxedDict* kwargs) {
if (!isSubclass(_cls->cls, type_cls))
if (!PyType_Check(_cls))
raiseExcHelper(TypeError, "tuple.__new__(X): X is not a type object (%s)", getTypeName(_cls));
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
......
......@@ -201,7 +201,7 @@ void* BoxVar::operator new(size_t size, BoxedClass* cls, size_t nitems) {
assert(cls);
// See definition of BoxedTuple for some notes on why we need this special case:
ASSERT(isSubclass(cls, tuple_cls) || cls->tp_basicsize >= size, "%s", cls->tp_name);
ASSERT(cls->tp_basicsize >= size || isSubclass(cls, tuple_cls), "%s", cls->tp_name);
assert(cls->tp_itemsize > 0);
assert(cls->tp_alloc);
......@@ -758,14 +758,13 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
// Special-case unicode for now, maybe there's something about this that can eventually be generalized:
ParamReceiveSpec paramspec(4, 3, false, false);
bool rewrite_success = false;
Box* oarg1, *oarg2, *oarg3;
static ParamNames param_names({ "string", "encoding", "errors" }, "", "");
static Box* defaults[3] = { NULL, NULL, NULL };
Box* oargs[1];
rearrangeArguments(paramspec, &param_names, "unicode", defaults, rewrite_args, rewrite_success, argspec, arg1,
arg2, arg3, args, keyword_names, oarg1, oarg2, oarg3, oargs);
assert(oarg1 == cls);
arg2, arg3, args, oargs, keyword_names);
assert(arg1 == cls);
if (!rewrite_success)
rewrite_args = NULL;
......@@ -778,7 +777,7 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
}
// TODO other encodings could return non-unicode?
return unicodeNewHelper(cls, oarg2, oarg3, oargs);
return unicodeNewHelper(cls, arg2, arg3, oargs);
}
if (cls->tp_new != object_cls->tp_new && cls->tp_new != slot_tp_new && S == CXX) {
......@@ -790,10 +789,10 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
ParamReceiveSpec paramspec(1, false, true, true);
bool rewrite_success = false;
Box* oarg1, *oarg2, *oarg3, ** oargs = NULL;
Box** oargs = NULL;
rearrangeArguments(paramspec, NULL, "", NULL, rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args,
keyword_names, oarg1, oarg2, oarg3, oargs);
assert(oarg1 == cls);
oargs, keyword_names);
assert(arg1 == cls);
if (!rewrite_success)
rewrite_args = NULL;
......@@ -804,7 +803,7 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
rewrite_args->out_success = true;
}
return cpythonTypeCall(cls, oarg2, oarg3);
return cpythonTypeCall(cls, arg2, arg3);
}
if (argspec.has_starargs || argspec.has_kwargs)
......@@ -877,7 +876,8 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
} else {
new_attr = typeLookup(cls, new_str, NULL);
try {
new_attr = processDescriptor(new_attr, None, cls);
if (new_attr->cls != function_cls) // optimization
new_attr = processDescriptor(new_attr, None, cls);
} catch (ExcInfo e) {
if (S == CAPI) {
setCAPIException(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