in runtime/trampolines.cpp [298:448]
RawObject prepareKeywordCall(Thread* thread, word nargs,
RawFunction function_raw) {
HandleScope scope(thread);
Function function(&scope, function_raw);
// Pop the tuple of kwarg names
Tuple keywords(&scope, thread->stackPop());
Code code(&scope, function.code());
word expected_args = function.argcount() + code.kwonlyargcount();
word num_keyword_args = keywords.length();
word num_positional_args = nargs - num_keyword_args;
Tuple varnames(&scope, code.varnames());
Object tmp_varargs(&scope, NoneType::object());
Object tmp_dict(&scope, NoneType::object());
// We expect use of keyword argument calls to be uncommon, but when used
// we anticipate mostly use of simple forms. General scheme here is to
// normalize the odd forms into standard form and then handle them all
// in the same place.
if (function.hasVarargsOrVarkeyargs()) {
Runtime* runtime = thread->runtime();
if (function.hasVarargs()) {
// If we have more positional than expected, add the remainder to a tuple,
// remove from the stack and close up the hole.
word excess = num_positional_args - function.argcount();
if (excess > 0) {
MutableTuple varargs(&scope, runtime->newMutableTuple(excess));
// Point to the leftmost excess argument
RawObject* p = (thread->stackPointer() + num_keyword_args + excess) - 1;
// Copy the excess to the * tuple
for (word i = 0; i < excess; i++) {
varargs.atPut(i, *(p - i));
}
// Fill in the hole
for (word i = 0; i < num_keyword_args; i++) {
*p = *(p - excess);
p--;
}
// Adjust the counts
thread->stackDrop(excess);
nargs -= excess;
num_positional_args -= excess;
tmp_varargs = varargs.becomeImmutable();
} else {
tmp_varargs = runtime->emptyTuple();
}
}
if (function.hasVarkeyargs()) {
// Too many positional args passed?
if (num_positional_args > function.argcount()) {
thread->stackDrop(nargs + 1);
return thread->raiseWithFmt(LayoutId::kTypeError,
"Too many positional arguments");
}
// If we have keyword arguments that don't appear in the formal parameter
// list, add them to a keyword dict.
Dict dict(&scope, runtime->newDict());
List saved_keyword_list(&scope, runtime->newList());
List saved_values(&scope, runtime->newList());
DCHECK(varnames.length() >= expected_args,
"varnames must be greater than or equal to positional args");
RawObject* p = thread->stackPointer() + (num_keyword_args - 1);
word posonlyargcount = code.posonlyargcount();
for (word i = 0; i < num_keyword_args; i++) {
Object key(&scope, keywords.at(i));
Object value(&scope, *(p - i));
word result = findName(thread, posonlyargcount, key, varnames);
if (result < 0) {
thread->stackDrop(nargs + 1);
return Error::exception();
}
if (result < expected_args) {
// Got a match, stash pair for future restoration on the stack
runtime->listAdd(thread, saved_keyword_list, key);
runtime->listAdd(thread, saved_values, value);
} else {
// New, add it and associated value to the varkeyargs dict
Object hash_obj(&scope, Interpreter::hash(thread, key));
if (hash_obj.isErrorException()) {
thread->stackDrop(nargs + 1);
return *hash_obj;
}
word hash = SmallInt::cast(*hash_obj).value();
Object dict_result(&scope, dictAtPut(thread, dict, key, hash, value));
if (dict_result.isErrorException()) {
thread->stackDrop(nargs + 1);
return *dict_result;
}
nargs--;
}
}
// Now, restore the stashed values to the stack and build a new
// keywords name list.
thread->stackDrop(num_keyword_args); // Pop all of the old keyword values
num_keyword_args = saved_keyword_list.numItems();
// Replace the old keywords list with a new one.
if (num_keyword_args > 0) {
MutableTuple new_keywords(&scope,
runtime->newMutableTuple(num_keyword_args));
for (word i = 0; i < num_keyword_args; i++) {
thread->stackPush(saved_values.at(i));
new_keywords.atPut(i, saved_keyword_list.at(i));
}
keywords = new_keywords.becomeImmutable();
} else {
keywords = runtime->emptyTuple();
}
tmp_dict = *dict;
}
}
// At this point, all vararg forms have been normalized
RawObject* kw_arg_base = (thread->stackPointer() + num_keyword_args) -
1; // pointer to first non-positional arg
if (UNLIKELY(nargs > expected_args)) {
thread->stackDrop(nargs + 1);
return thread->raiseWithFmt(LayoutId::kTypeError, "Too many arguments");
}
if (UNLIKELY(nargs < expected_args)) {
// Too few args passed. Can we supply default args to make it work?
// First, normalize & pad keywords and stack arguments
word name_tuple_size = expected_args - num_positional_args;
MutableTuple padded_keywords(
&scope, thread->runtime()->newMutableTuple(name_tuple_size));
padded_keywords.replaceFromWith(0, *keywords, num_keyword_args);
// Fill in missing spots w/ Error code
for (word i = num_keyword_args; i < name_tuple_size; i++) {
thread->stackPush(Error::error());
nargs++;
padded_keywords.atPut(i, Error::error());
}
keywords = padded_keywords.becomeImmutable();
}
// Now we've got the right number. Do they match up?
RawObject res = checkArgs(thread, function, kw_arg_base, keywords, varnames,
num_positional_args);
if (res.isErrorException()) {
thread->stackDrop(nargs + 1);
return res; // TypeError created by checkArgs.
}
CHECK(res.isNoneType(), "checkArgs should return an Error or None");
// If we're a vararg form, need to push the tuple/dict.
if (function.hasVarargs()) {
thread->stackPush(*tmp_varargs);
nargs++;
}
if (function.hasVarkeyargs()) {
thread->stackPush(*tmp_dict);
nargs++;
}
DCHECK(function.totalArgs() == nargs, "argument count mismatch");
return *function;
}