Commit 60850a52 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #834 from kmod/perf3

improve getattr() speed
parents 86e550e5 91ecafb0
...@@ -30,9 +30,6 @@ pystontmp*/ ...@@ -30,9 +30,6 @@ pystontmp*/
/*_unittest /*_unittest
*.cache *.cache
tests/t.py
tests/t2.py
tests/t3.py
*.bc *.bc
stdlib.ll stdlib.ll
*.o *.o
......
...@@ -100,16 +100,16 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**) PY ...@@ -100,16 +100,16 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**) PY
/* */ /* */
// Pyston change: made these function calls for now
#if 0
#define PyExceptionClass_Check(x) \ #define PyExceptionClass_Check(x) \
(PyClass_Check((x)) || (PyType_Check((x)) && \ (PyClass_Check((x)) || (PyType_Check((x)) && \
PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS))) PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS)))
#define PyExceptionInstance_Check(x) \ #define PyExceptionInstance_Check(x) \
(PyInstance_Check((x)) || \ (PyInstance_Check((x)) || \
PyType_FastSubclass((x)->ob_type, Py_TPFLAGS_BASE_EXC_SUBCLASS)) PyType_FastSubclass(Py_TYPE(x), Py_TPFLAGS_BASE_EXC_SUBCLASS))
// Pyston change: made these function calls for now
#if 0
#define PyExceptionClass_Name(x) \ #define PyExceptionClass_Name(x) \
(PyClass_Check((x)) \ (PyClass_Check((x)) \
? PyString_AS_STRING(((PyClassObject*)(x))->cl_name) \ ? PyString_AS_STRING(((PyClassObject*)(x))->cl_name) \
...@@ -121,8 +121,6 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**) PY ...@@ -121,8 +121,6 @@ PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**) PY
: (PyObject*)((x)->ob_type))) : (PyObject*)((x)->ob_type)))
#endif #endif
// (We might have to make these wrapper macros that do appropriate casting to PyObject) // (We might have to make these wrapper macros that do appropriate casting to PyObject)
PyAPI_FUNC(int) PyExceptionClass_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(int) PyExceptionInstance_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(const char*) PyExceptionClass_Name(PyObject*) PYSTON_NOEXCEPT; PyAPI_FUNC(const char*) PyExceptionClass_Name(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(PyObject*) PyExceptionInstance_Class(PyObject*) PYSTON_NOEXCEPT; PyAPI_FUNC(PyObject*) PyExceptionInstance_Class(PyObject*) PYSTON_NOEXCEPT;
...@@ -244,6 +242,14 @@ PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc( ...@@ -244,6 +242,14 @@ PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc(
char *name, char *doc, PyObject *base, PyObject *dict) PYSTON_NOEXCEPT; char *name, char *doc, PyObject *base, PyObject *dict) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *) PYSTON_NOEXCEPT; PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *) PYSTON_NOEXCEPT;
// Pyston addition: allocate + initialize an instance of type `type`.
// arg represents the single value that will be passed to the constructor;
// a NULL value represents passing zero arguments, and a tuple value will
// not be expanded into multiple arguments.
// In the common cases this will be faster than creating the instance using
// PyObject_Call(type, PyTuple_Pack(1, arg), NULL)
PyAPI_FUNC(PyObject *) PyErr_CreateExceptionInstance(PyObject* type, PyObject* arg) PYSTON_NOEXCEPT;
/* In sigcheck.c or signalmodule.c */ /* In sigcheck.c or signalmodule.c */
PyAPI_FUNC(int) PyErr_CheckSignals(void) PYSTON_NOEXCEPT; PyAPI_FUNC(int) PyErr_CheckSignals(void) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) PyErr_SetInterrupt(void) PYSTON_NOEXCEPT; PyAPI_FUNC(void) PyErr_SetInterrupt(void) PYSTON_NOEXCEPT;
......
...@@ -72,6 +72,46 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds) ...@@ -72,6 +72,46 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
return 0; return 0;
} }
PyObject* PyErr_CreateExceptionInstance(PyObject* _type, PyObject* arg) {
if (PyType_Check(_type) && ((PyTypeObject*)_type)->tp_new == (newfunc)BaseException_new &&
((PyTypeObject*)_type)->tp_init == (initproc)BaseException_init) {
PyTypeObject* type = (PyTypeObject*)_type;
// Fast path: inline new and init
PyBaseExceptionObject *self;
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
if (!self)
return NULL;
self->dict = NULL;
if (arg) {
self->args = PyTuple_Pack(1, arg);
if (!self->args)
return NULL;
self->message = arg;
} else {
self->args = PyTuple_New(0);
self->message = PyString_FromString("");
if (!self->message)
return NULL;
}
return (PyObject*)self;
} else {
// Fallback
PyObject* args;
if (arg == NULL)
args = PyTuple_New(0);
else
args = PyTuple_Pack(1, arg);
if (args == NULL)
return NULL;
return PyObject_Call(_type, args, NULL);
}
}
static int static int
BaseException_clear(PyBaseExceptionObject *self) BaseException_clear(PyBaseExceptionObject *self)
{ {
......
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/testsuite/lib/django"))
BENCHMARK_SUITE_DIR = os.path.join(os.path.dirname(__file__), "../../pyston-perf/benchmarking/benchmark_suite")
sys.path.extend([os.path.join(BENCHMARK_SUITE_DIR, "django_template2_site")])
from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings
from django.apps import apps
import time
import shutil
# Copy the "base" db so we always start with a knownn state:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testsite.settings")
try:
import __pyston__
pyston_loaded = True
except:
pyston_loaded = False
template_source = """
{% load admin_static %}{% load firstof from future %}<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}" />
{% block extrastyle %}{% endblock %}
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="{% block stylesheet_ie %}{% static "admin/css/ie.css" %}{% endblock %}" /><![endif]-->
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}" />{% endif %}
<script type="text/javascript">window.__admin_media_prefix__ = "{% filter escapejs %}{% static "admin/" %}{% endfilter %}";</script>
<script type="text/javascript">window.__admin_utc_offset__ = "{% filter escapejs %}{% now "Z" %}{% endfilter %}";</script>
{% block extrahead %}{% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
</head>
{% load i18n %}
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}">
<!-- Container -->
<div id="container">
{% if not is_popup %}
<!-- Header -->
<div id="header">
<div id="branding">
{% block branding %}{% endblock %}
</div>
{% if user.is_active and user.is_staff %}
<div id="user-tools">
{% block welcome-msg %}
{% trans 'Welcome,' %}
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
{% endblock %}
{% block userlinks %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
{% endif %}
{% if user.has_usable_password %}
<a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
{% endif %}
<a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
{% endblock %}
</div>
{% endif %}
{% block nav-global %}{% endblock %}
</div>
<!-- END Header -->
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
{% if title %} &rsaquo; {{ title }}{% endif %}
</div>
{% endblock %}
{% endif %}
{% block messages %}
{% if messages %}
<ul class="messagelist">{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message|capfirst }}</li>
{% endfor %}</ul>
{% endif %}
{% endblock messages %}
<!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %}
<br class="clear" />
</div>
<!-- END Content -->
{% block footer %}<div id="footer"></div>{% endblock %}
</div>
<!-- END Container -->
</body>
</html>
"""
apps.populate((
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
))
settings.TEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
elapsed = 0
template = Template(template_source, None, "admin/index.html")
d = {}
from django.contrib.auth.models import User
d['user'] = User(2)
# This list was created by running an empty django instance and seeing what it passed for app_list:
d['app_list'] = [{'app_url': '/admin/auth/', 'models': [{'perms': {'add': True, 'change': True, 'delete': True}, 'admin_url': '/admin/auth/group/', 'object_name': 'Group', 'name': "<name>", 'add_url': '/admin/auth/group/add/'}, {'perms': {'add': True, 'change': True, 'delete': True}, 'admin_url': '/admin/auth/user/', 'object_name': 'User', 'name': "<name>", 'add_url': '/admin/auth/user/add/'}], 'has_module_perms': True, 'name': "<name>", 'app_label': 'auth'}]
context = Context(d)
def measure_iters():
for i in xrange(6000):
start = time.time()
template.render(context)
elapsed = time.time() - start
print elapsed
print "took %4.1fms for last iteration" % (elapsed * 1000.0,)
def measure_by_nodetype():
times = {}
for i in xrange(6000):
for n in template.nodelist:
start = time.time()
n.render(context)
elapsed = time.time() - start
times[type(n)] = times.get(type(n), 0) + elapsed
for k, v in sorted(times.items(), key=lambda (k,v):k.__name__):
print k.__name__, v
measure_by_nodetype()
...@@ -1474,7 +1474,7 @@ Value ASTInterpreter::visit_set(AST_Set* node) { ...@@ -1474,7 +1474,7 @@ Value ASTInterpreter::visit_set(AST_Set* node) {
Value ASTInterpreter::visit_str(AST_Str* node) { Value ASTInterpreter::visit_str(AST_Str* node) {
Box* o = NULL; Box* o = NULL;
if (node->str_type == AST_Str::STR) { if (node->str_type == AST_Str::STR) {
o = parent_module->getStringConstant(node->str_data); o = parent_module->getStringConstant(node->str_data, true);
} else if (node->str_type == AST_Str::UNICODE) { } else if (node->str_type == AST_Str::UNICODE) {
o = parent_module->getUnicodeConstant(node->str_data); o = parent_module->getUnicodeConstant(node->str_data);
} else { } else {
......
...@@ -1278,8 +1278,9 @@ private: ...@@ -1278,8 +1278,9 @@ private:
CompilerVariable* evalStr(AST_Str* node, const UnwindInfo& unw_info) { CompilerVariable* evalStr(AST_Str* node, const UnwindInfo& unw_info) {
if (node->str_type == AST_Str::STR) { if (node->str_type == AST_Str::STR) {
llvm::Value* rtn = embedRelocatablePtr( llvm::Value* rtn
irstate->getSourceInfo()->parent_module->getStringConstant(node->str_data), g.llvm_value_type_ptr); = embedRelocatablePtr(irstate->getSourceInfo()->parent_module->getStringConstant(node->str_data, true),
g.llvm_value_type_ptr);
return new ConcreteCompilerVariable(STR, rtn, true); return new ConcreteCompilerVariable(STR, rtn, true);
} else if (node->str_type == AST_Str::UNICODE) { } else if (node->str_type == AST_Str::UNICODE) {
......
...@@ -482,13 +482,8 @@ BoxedString* internStringImmortal(llvm::StringRef s); ...@@ -482,13 +482,8 @@ BoxedString* internStringImmortal(llvm::StringRef s);
// Callers should use this function if they can accept mortal string objects. // Callers should use this function if they can accept mortal string objects.
// FIXME For now it just returns immortal strings, but at least we can use it // FIXME For now it just returns immortal strings, but at least we can use it
// to start documenting the places that can take mortal strings. // to start documenting the places that can take mortal strings.
inline BoxedString* internStringMortal(const char* s) {
return internStringImmortal(s);
}
inline BoxedString* internStringMortal(llvm::StringRef s) { inline BoxedString* internStringMortal(llvm::StringRef s) {
assert(s.data()[s.size()] == '\0'); return internStringImmortal(s);
return internStringMortal(s.data());
} }
// TODO this is an immortal intern for now // TODO this is an immortal intern for now
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "runtime/list.h" #include "runtime/list.h"
#include "runtime/long.h" #include "runtime/long.h"
#include "runtime/objmodel.h" #include "runtime/objmodel.h"
#include "runtime/rewrite_args.h"
#include "runtime/set.h" #include "runtime/set.h"
#include "runtime/super.h" #include "runtime/super.h"
#include "runtime/types.h" #include "runtime/types.h"
...@@ -457,7 +458,47 @@ Box* delattrFunc(Box* obj, Box* _str) { ...@@ -457,7 +458,47 @@ Box* delattrFunc(Box* obj, Box* _str) {
return None; return None;
} }
template <ExceptionStyle S> Box* getattrFunc(Box* obj, Box* _str, Box* default_value) noexcept(S == CAPI) { static Box* getattrFuncHelper(Box* return_val, Box* obj, BoxedString* str, Box* default_val) noexcept {
if (return_val)
return return_val;
bool exc = PyErr_Occurred();
if (exc && !PyErr_ExceptionMatches(AttributeError))
return NULL;
if (default_val) {
if (exc)
PyErr_Clear();
return default_val;
}
if (!exc)
raiseAttributeErrorCapi(obj, str->s());
return NULL;
}
template <ExceptionStyle S>
Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1,
Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) {
if (argspec != ArgPassSpec(2, 0, false, false) || argspec != ArgPassSpec(3, 0, false, false)) {
static Box* defaults[] = { NULL };
bool rewrite_success = false;
rearrangeArguments(ParamReceiveSpec(3, 1, false, false), NULL, "getattr", defaults, rewrite_args,
rewrite_success, argspec, arg1, arg2, arg3, args, NULL, keyword_names);
if (!rewrite_success)
rewrite_args = NULL;
}
Box* obj = arg1;
Box* _str = arg2;
Box* default_value = arg3;
if (rewrite_args) {
if (!PyString_Check(_str))
rewrite_args = NULL;
else
rewrite_args->arg2->addGuard((intptr_t)arg2);
}
try { try {
_str = coerceUnicodeToStr(_str); _str = coerceUnicodeToStr(_str);
} catch (ExcInfo e) { } catch (ExcInfo e) {
...@@ -476,15 +517,43 @@ template <ExceptionStyle S> Box* getattrFunc(Box* obj, Box* _str, Box* default_v ...@@ -476,15 +517,43 @@ template <ExceptionStyle S> Box* getattrFunc(Box* obj, Box* _str, Box* default_v
raiseExcHelper(TypeError, "getattr(): attribute name must be string"); raiseExcHelper(TypeError, "getattr(): attribute name must be string");
} }
Box* rtn = PyObject_GetAttr(obj, _str); BoxedString* str = static_cast<BoxedString*>(_str);
if (rtn == NULL && default_value != NULL && PyErr_ExceptionMatches(AttributeError)) { if (!PyString_CHECK_INTERNED(str)) {
PyErr_Clear(); internStringMortalInplace(str);
return default_value; rewrite_args = NULL;
}
Box* rtn;
RewriterVar* r_rtn;
if (rewrite_args) {
GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination);
rtn = getattrInternal<CAPI>(obj, str, &grewrite_args);
if (!grewrite_args.out_success)
rewrite_args = NULL;
else {
if (!rtn && !PyErr_Occurred())
r_rtn = rewrite_args->rewriter->loadConst(0);
else
r_rtn = grewrite_args.out_rtn;
}
} else {
rtn = getattrInternal<CAPI>(obj, str, NULL);
} }
if (S == CXX && !rtn) if (rewrite_args) {
RewriterVar* final_rtn = rewrite_args->rewriter->call(
false, (void*)getattrFuncHelper, r_rtn, rewrite_args->arg1, rewrite_args->arg2, rewrite_args->arg3);
if (S == CXX)
rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn);
rewrite_args->out_success = true;
rewrite_args->out_rtn = final_rtn;
}
Box* r = getattrFuncHelper(rtn, obj, str, default_value);
if (S == CXX && !r)
throwCAPIException(); throwCAPIException();
return rtn; return r;
} }
Box* setattrFunc(Box* obj, Box* _str, Box* value) { Box* setattrFunc(Box* obj, Box* _str, Box* value) {
...@@ -1449,8 +1518,9 @@ void setupBuiltins() { ...@@ -1449,8 +1518,9 @@ void setupBuiltins() {
builtins_module->giveAttr("delattr", builtins_module->giveAttr("delattr",
new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)delattrFunc, NONE, 2), "delattr")); new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)delattrFunc, NONE, 2), "delattr"));
auto getattr_func = boxRTFunction((void*)getattrFunc<CXX>, UNKNOWN, 3, 1, false, false, ParamNames::empty(), CXX); auto getattr_func = createRTFunction(3, 1, 1, 1, ParamNames::empty());
addRTFunction(getattr_func, (void*)getattrFunc<CAPI>, UNKNOWN, CAPI); getattr_func->internal_callable.capi_val = &getattrFuncInternal<CAPI>;
getattr_func->internal_callable.cxx_val = &getattrFuncInternal<CXX>;
builtins_module->giveAttr("getattr", new BoxedBuiltinFunctionOrMethod(getattr_func, "getattr", { NULL })); builtins_module->giveAttr("getattr", new BoxedBuiltinFunctionOrMethod(getattr_func, "getattr", { NULL }));
builtins_module->giveAttr( builtins_module->giveAttr(
......
...@@ -703,23 +703,21 @@ extern "C" void PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObjec ...@@ -703,23 +703,21 @@ extern "C" void PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObjec
class. class.
*/ */
if (!inclass || !PyObject_IsSubclass(inclass, type)) { if (!inclass || !PyObject_IsSubclass(inclass, type)) {
PyObject* args, *res; // Pyston change: rewrote this section
if (value == Py_None) PyObject* res;
args = PyTuple_New(0); if (!PyTuple_Check(value)) {
else if (PyTuple_Check(value)) { res = PyErr_CreateExceptionInstance(type, value == Py_None ? NULL : value);
Py_INCREF(value); } else {
args = value; PyObject* args = value;
} else
args = PyTuple_Pack(1, value); // Pyston change:
// res = PyEval_CallObject(type, args);
res = PyObject_Call(type, args, NULL);
}
if (args == NULL)
goto finally;
res = PyEval_CallObject(type, args);
Py_DECREF(args);
if (res == NULL) if (res == NULL)
goto finally; goto finally;
Py_DECREF(value);
value = res; value = res;
} }
/* if the class of the instance doesn't exactly match the /* if the class of the instance doesn't exactly match the
...@@ -909,14 +907,6 @@ extern "C" PyObject* PyErr_NoMemory() noexcept { ...@@ -909,14 +907,6 @@ extern "C" PyObject* PyErr_NoMemory() noexcept {
return nullptr; return nullptr;
} }
extern "C" int PyExceptionClass_Check(PyObject* o) noexcept {
return PyClass_Check(o) || (PyType_Check(o) && isSubclass(static_cast<BoxedClass*>(o), BaseException));
}
extern "C" int PyExceptionInstance_Check(PyObject* o) noexcept {
return PyInstance_Check(o) || isSubclass(o->cls, BaseException);
}
extern "C" const char* PyExceptionClass_Name(PyObject* o) noexcept { extern "C" const char* PyExceptionClass_Name(PyObject* o) noexcept {
return PyClass_Check(o) ? PyString_AS_STRING(static_cast<BoxedClassobj*>(o)->name) return PyClass_Check(o) ? PyString_AS_STRING(static_cast<BoxedClassobj*>(o)->name)
: static_cast<BoxedClass*>(o)->tp_name; : static_cast<BoxedClass*>(o)->tp_name;
......
...@@ -3940,9 +3940,8 @@ static Box* runtimeCallEntry(Box* obj, ArgPassSpec argspec, Box* arg1, Box* arg2 ...@@ -3940,9 +3940,8 @@ static Box* runtimeCallEntry(Box* obj, ArgPassSpec argspec, Box* arg1, Box* arg2
if (!rewrite_args.out_success) { if (!rewrite_args.out_success) {
rewriter.reset(NULL); rewriter.reset(NULL);
} else if (rtn) { } else
rewriter->commitReturning(rewrite_args.out_rtn); rewriter->commitReturning(rewrite_args.out_rtn);
}
} else { } else {
rtn = runtimeCallInternal<S>(obj, NULL, argspec, arg1, arg2, arg3, args, keyword_names); rtn = runtimeCallInternal<S>(obj, NULL, argspec, arg1, arg2, arg3, args, keyword_names);
} }
......
...@@ -599,6 +599,35 @@ extern "C" CLFunction* unboxCLFunction(Box* b) { ...@@ -599,6 +599,35 @@ extern "C" CLFunction* unboxCLFunction(Box* b) {
return static_cast<BoxedFunction*>(b)->f; return static_cast<BoxedFunction*>(b)->f;
} }
static PyObject* cpython_type_call(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept {
PyObject* obj;
if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name);
return NULL;
}
obj = type->tp_new(type, args, kwds);
if (obj != NULL) {
/* Ugly exception: when the call was type(something),
* don't call tp_init on the result. */
if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1
&& (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
* it won't be initialized. */
if (!PyType_IsSubtype(obj->cls, type))
return obj;
type = obj->cls;
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL
&& type->tp_init(obj, args, kwds) < 0) {
Py_DECREF(obj);
obj = NULL;
}
}
return obj;
}
template <ExceptionStyle S> template <ExceptionStyle S>
static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI); Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI);
...@@ -608,6 +637,25 @@ static Box* typeTppCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSpec ar ...@@ -608,6 +637,25 @@ static Box* typeTppCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSpec ar
Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI) { Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI) {
int npassed_args = argspec.totalPassed(); int npassed_args = argspec.totalPassed();
// Common CAPI path call this function with *args, **kw.
if (argspec == ArgPassSpec(0, 0, true, false) || argspec == ArgPassSpec(0, 0, true, true)) {
// Wouldn't be able to rewrite anyway:
assert(!rewrite_args || !rewrite_args->out_success);
arg1 = PySequence_Tuple(arg1);
if (!arg1) {
if (S == CAPI)
return NULL;
else
throwCAPIException();
}
Box* r = cpython_type_call(static_cast<BoxedClass*>(self), arg1, argspec.has_kwargs ? arg2 : NULL);
if (S == CXX && !r)
throwCAPIException();
return r;
}
if (argspec.has_starargs || argspec.has_kwargs) { if (argspec.has_starargs || argspec.has_kwargs) {
// This would fail in typeCallInner // This would fail in typeCallInner
rewrite_args = NULL; rewrite_args = NULL;
...@@ -654,35 +702,6 @@ static void assertInitNone(Box* obj) { ...@@ -654,35 +702,6 @@ static void assertInitNone(Box* obj) {
} }
} }
static PyObject* cpython_type_call(PyTypeObject* type, PyObject* args, PyObject* kwds) noexcept {
PyObject* obj;
if (type->tp_new == NULL) {
PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name);
return NULL;
}
obj = type->tp_new(type, args, kwds);
if (obj != NULL) {
/* Ugly exception: when the call was type(something),
* don't call tp_init on the result. */
if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1
&& (kwds == NULL || (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
* it won't be initialized. */
if (!PyType_IsSubtype(obj->cls, type))
return obj;
type = obj->cls;
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && type->tp_init != NULL
&& type->tp_init(obj, args, kwds) < 0) {
Py_DECREF(obj);
obj = NULL;
}
}
return obj;
}
static PyObject* cpythonTypeCall(BoxedClass* type, PyObject* args, PyObject* kwds) { static PyObject* cpythonTypeCall(BoxedClass* type, PyObject* args, PyObject* kwds) {
Box* r = cpython_type_call(type, args, kwds); Box* r = cpython_type_call(type, args, kwds);
if (!r) if (!r)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment