Commit 0e10126d authored by Kevin Modzelewski's avatar Kevin Modzelewski

Fix an arg-handling bug in typeCallInternal

In typeCallInternal, we used to expand out any starargs in order to take a look
at the first arg (and change it when passing it).

We had a bug in this code, and rather than make that code more complicated
to fix it, just call back into callFunc to resolve it.  This is kind of tricky
since callFunc will call typeCall, and we don't want typeCall to duplicate
the typeCallInternal behavior (that's not any better than duplicating the
arg behavior), so we want typeCall to call into typeCallInternal.  But
typeCall receives varargs! which typeCallInternal doesn't support.  So typeCall
has to do some (simpler) arg handling to expand out the varargs.

In the end, it simplifies the code a little bit but causes a bunch of extra calls
in the varargs case, so it's less of a win than I thought, but at least it
fixes the bug.
parent bac77762
...@@ -102,14 +102,6 @@ static Box* (*runtimeCallInternal2)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, B ...@@ -102,14 +102,6 @@ static Box* (*runtimeCallInternal2)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, B
static Box* (*runtimeCallInternal3)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*) static Box* (*runtimeCallInternal3)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))runtimeCallInternal; = (Box * (*)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))runtimeCallInternal;
static Box* (*typeCallInternal1)(BoxedFunctionBase*, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box*)
= (Box * (*)(BoxedFunctionBase*, CallRewriteArgs*, ArgPassSpec, Box*))typeCallInternal;
static Box* (*typeCallInternal2)(BoxedFunctionBase*, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box*, Box*)
= (Box * (*)(BoxedFunctionBase*, CallRewriteArgs*, ArgPassSpec, Box*, Box*))typeCallInternal;
static Box* (*typeCallInternal3)(BoxedFunctionBase*, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box*, Box*,
Box*)
= (Box * (*)(BoxedFunctionBase*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))typeCallInternal;
bool checkClass(LookupScope scope) { bool checkClass(LookupScope scope) {
return (scope & CLASS_ONLY) != 0; return (scope & CLASS_ONLY) != 0;
} }
...@@ -3555,47 +3547,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa ...@@ -3555,47 +3547,10 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
static StatCounter slowpath_typecall("slowpath_typecall"); static StatCounter slowpath_typecall("slowpath_typecall");
slowpath_typecall.log(); slowpath_typecall.log();
// TODO shouldn't have to redo this argument handling here... if (argspec.has_starargs)
if (argspec.has_starargs) { return callFunc(f, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
rewrite_args = NULL;
REWRITE_ABORTED("");
Box* starargs;
if (argspec.num_args == 0)
starargs = arg1;
else if (argspec.num_args == 1)
starargs = arg2;
else
abort();
assert(starargs->cls == tuple_cls);
BoxedTuple* targs = static_cast<BoxedTuple*>(starargs);
int n = targs->elts.size();
if (argspec.num_args == 0) {
if (n >= 1)
arg1 = targs->elts[0];
if (n >= 2)
arg2 = targs->elts[1];
if (n >= 3)
arg3 = targs->elts[2];
if (n >= 4)
args = &targs->elts[3];
} else if (argspec.num_args == 1) {
if (n >= 1)
arg2 = targs->elts[0];
if (n >= 2)
arg3 = targs->elts[1];
if (n >= 3)
args = &targs->elts[2];
} else {
abort(); // unhandled
}
argspec = ArgPassSpec(n + argspec.num_args);
}
assert(argspec.num_args >= 1);
Box* _cls = arg1; Box* _cls = arg1;
if (!isSubclass(_cls->cls, type_cls)) { if (!isSubclass(_cls->cls, type_cls)) {
...@@ -3867,16 +3822,24 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa ...@@ -3867,16 +3822,24 @@ Box* typeCallInternal(BoxedFunctionBase* f, CallRewriteArgs* rewrite_args, ArgPa
return made; return made;
} }
Box* typeCall(Box* obj, BoxedList* vararg) { Box* typeCall(Box* obj, BoxedTuple* vararg, BoxedDict* kwargs) {
assert(vararg->cls == list_cls); assert(vararg->cls == tuple_cls);
if (vararg->size == 0)
return typeCallInternal1(NULL, NULL, ArgPassSpec(1), obj); int n = vararg->elts.size();
else if (vararg->size == 1) int args_to_pass = n + 2; // 1 for obj, 1 for kwargs
return typeCallInternal2(NULL, NULL, ArgPassSpec(2), obj, vararg->elts->elts[0]);
else if (vararg->size == 2) Box** args = NULL;
return typeCallInternal3(NULL, NULL, ArgPassSpec(3), obj, vararg->elts->elts[0], vararg->elts->elts[1]); if (args_to_pass > 3)
else args = (Box**)alloca(sizeof(Box*) * (args_to_pass - 3));
abort();
Box* arg1, *arg2, *arg3;
arg1 = obj;
for (int i = 0; i < n; i++) {
getArg(i + 1, arg1, arg2, arg3, args) = vararg->elts[i];
}
getArg(n + 1, arg1, arg2, arg3, args) = kwargs;
return typeCallInternal(NULL, NULL, ArgPassSpec(n + 1, 0, false, true), arg1, arg2, arg3, args, NULL);
} }
extern "C" void delGlobal(BoxedModule* m, const std::string* name) { extern "C" void delGlobal(BoxedModule* m, const std::string* name) {
......
...@@ -29,6 +29,7 @@ class BoxedInt; ...@@ -29,6 +29,7 @@ class BoxedInt;
class BoxedList; class BoxedList;
class BoxedString; class BoxedString;
class BoxedGenerator; class BoxedGenerator;
class BoxedTuple;
// user-level raise functions that implement python-level semantics // user-level raise functions that implement python-level semantics
extern "C" void raise0() __attribute__((__noreturn__)); extern "C" void raise0() __attribute__((__noreturn__));
...@@ -130,7 +131,7 @@ extern "C" void raiseAttributeErrorStr(const char* typeName, const char* attr) _ ...@@ -130,7 +131,7 @@ extern "C" void raiseAttributeErrorStr(const char* typeName, const char* attr) _
extern "C" void raiseAttributeError(Box* obj, const char* attr) __attribute__((__noreturn__)); extern "C" void raiseAttributeError(Box* obj, const char* attr) __attribute__((__noreturn__));
extern "C" void raiseNotIterableError(const char* typeName) __attribute__((__noreturn__)); extern "C" void raiseNotIterableError(const char* typeName) __attribute__((__noreturn__));
Box* typeCall(Box*, BoxedList*); Box* typeCall(Box*, BoxedTuple*, BoxedDict*);
Box* typeNew(Box* cls, Box* arg1, Box* arg2, Box** _args); Box* typeNew(Box* cls, Box* arg1, Box* arg2, Box** _args);
bool isUserDefined(BoxedClass* cls); bool isUserDefined(BoxedClass* cls);
......
...@@ -1226,7 +1226,7 @@ void setupRuntime() { ...@@ -1226,7 +1226,7 @@ void setupRuntime() {
object_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)objectStr, UNKNOWN, 1, 0, false, false))); object_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)objectStr, UNKNOWN, 1, 0, false, false)));
object_cls->freeze(); object_cls->freeze();
auto typeCallObj = boxRTFunction((void*)typeCall, UNKNOWN, 1, 0, true, false); auto typeCallObj = boxRTFunction((void*)typeCall, UNKNOWN, 1, 0, true, true);
typeCallObj->internal_callable = &typeCallInternal; typeCallObj->internal_callable = &typeCallInternal;
type_cls->giveAttr("__name__", new BoxedGetsetDescriptor(type_name, type_set_name, NULL)); type_cls->giveAttr("__name__", new BoxedGetsetDescriptor(type_name, type_set_name, NULL));
......
...@@ -6,3 +6,15 @@ C(1) ...@@ -6,3 +6,15 @@ C(1)
C(a=2) C(a=2)
C(*(3,)) C(*(3,))
C(**{'a':4}) C(**{'a':4})
# Regression test: make sure we pass these through correctly:
class C(object):
def __init__(self, k=None):
print k
def f(*args, **kw):
print args, kw
return C(*args, **kw)
f(k=1)
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