in runtime/builtins-module.cpp [488:660]
RawObject FUNC(builtins, __build_class__)(Thread* thread, Arguments args) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
Object body_obj(&scope, args.get(0));
if (!body_obj.isFunction()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"__build_class__: func must be a function");
}
Function body(&scope, *body_obj);
Object name(&scope, args.get(1));
if (!runtime->isInstanceOfStr(*name)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"__build_class__: name is not a string");
}
Object metaclass(&scope, args.get(2));
Object bootstrap(&scope, args.get(3));
Tuple orig_bases(&scope, args.get(4));
Tuple bases(&scope, *orig_bases);
Dict kwargs(&scope, args.get(5));
if (bootstrap == Bool::trueObj()) {
CHECK(name.isStr(), "bootstrap class names must not be str subclass");
name = Runtime::internStr(thread, name);
Object type_obj(&scope, findBuiltinTypeWithName(thread, name));
CHECK(!type_obj.isErrorNotFound(), "Unknown builtin type");
Type type(&scope, *type_obj);
if (bases.length() == 0 && name != runtime->symbols()->at(ID(object))) {
bases = runtime->implicitBases();
}
Tuple builtin_bases(&scope, type.bases());
word bases_length = bases.length();
CHECK(builtin_bases.length() == bases_length, "mismatching bases for '%s'",
Str::cast(*name).toCStr());
for (word i = 0; i < bases_length; i++) {
CHECK(builtin_bases.at(i) == bases.at(i), "mismatching bases for '%s'",
Str::cast(*name).toCStr());
}
if (type.mro().isNoneType()) {
Type superclass(&scope, bases.at(0));
DCHECK(!superclass.mro().isNoneType(), "superclass not initialized yet");
Tuple superclass_mro(&scope, superclass.mro());
word mro_length = superclass_mro.length() + 1;
MutableTuple mro(&scope, runtime->newMutableTuple(mro_length));
mro.atPut(0, *type);
mro.replaceFromWith(1, *superclass_mro, mro_length - 1);
type.setMro(mro.becomeImmutable());
}
Dict type_dict(&scope, runtime->newDict());
Object result(&scope,
thread->callFunctionWithImplicitGlobals(body, type_dict));
if (result.isError()) return *result;
CHECK(!typeAssignFromDict(thread, type, type_dict).isErrorException(),
"error while assigning bootstrap type dict");
// TODO(T53997177): Centralize type initialization
Object module_name(&scope, typeAtById(thread, type, ID(__module__)));
// non-heap-types in CPython have no `__module__` unless there is a
// "." in `tp_name`. Remove the attribute when it equals "builtins".
if (module_name.isStr() &&
Str::cast(*module_name).equals(runtime->symbols()->at(ID(builtins)))) {
typeRemoveById(thread, type, ID(__module__));
}
Object qualname(&scope, NoneType::object());
if (type.instanceLayoutId() == LayoutId::kType) {
qualname = *name;
// Note: `type` is the only type allowed to have a descriptor instead of
// a string for `__qualname__`.
} else {
qualname = typeRemoveById(thread, type, ID(__qualname__));
DCHECK(qualname.isStr() && Str::cast(*qualname).equals(Str::cast(*name)),
"unexpected __qualname__ attribute");
}
type.setQualname(*qualname);
typeAddDocstring(thread, type);
if (Layout::cast(type.instanceLayout()).hasTupleOverflow() &&
typeAtById(thread, type, ID(__dict__)).isErrorNotFound()) {
typeAddInstanceDict(thread, type);
}
if (DCHECK_IS_ON()) {
Object dunder_new(&scope, typeAtById(thread, type, ID(__new__)));
if (!dunder_new.isStaticMethod()) {
if (!(dunder_new.isNoneType() || dunder_new.isErrorNotFound())) {
DCHECK(false, "__new__ for %s should be a staticmethod",
Str::cast(*name).toCStr());
}
}
}
pickBuiltinTypeCtorFunction(thread, type);
runtime->builtinTypeCreated(thread, type);
return *type;
}
Object updated_bases(&scope, replaceNonTypeBases(thread, bases));
if (updated_bases.isErrorException()) {
return *updated_bases;
}
bases = *updated_bases;
bool metaclass_is_class;
if (metaclass.isUnbound()) {
metaclass_is_class = true;
if (bases.length() == 0) {
metaclass = runtime->typeAt(LayoutId::kType);
} else {
metaclass = runtime->typeOf(bases.at(0));
}
} else {
metaclass_is_class = runtime->isInstanceOfType(*metaclass);
}
if (metaclass_is_class) {
Type metaclass_type(&scope, *metaclass);
metaclass = calculateMetaclass(thread, metaclass_type, bases);
if (metaclass.isError()) return *metaclass;
}
Object dict_obj(&scope, NoneType::object());
Object prepare_method(
&scope, runtime->attributeAtById(thread, metaclass, ID(__prepare__)));
if (prepare_method.isError()) {
Object given(&scope, thread->pendingExceptionType());
Object exc(&scope, runtime->typeAt(LayoutId::kAttributeError));
if (!givenExceptionMatches(thread, given, exc)) {
return *prepare_method;
}
thread->clearPendingException();
dict_obj = runtime->newDict();
} else {
thread->stackPush(*prepare_method);
Tuple pargs(&scope, runtime->newTupleWith2(name, bases));
thread->stackPush(*pargs);
thread->stackPush(*kwargs);
dict_obj = Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS);
if (dict_obj.isError()) return *dict_obj;
}
if (!runtime->isMapping(thread, dict_obj)) {
if (metaclass_is_class) {
Type metaclass_type(&scope, *metaclass);
Str metaclass_type_name(&scope, metaclass_type.name());
return thread->raiseWithFmt(
LayoutId::kTypeError,
"%S.__prepare__() must return a mapping, not %T",
&metaclass_type_name, &dict_obj);
}
return thread->raiseWithFmt(
LayoutId::kTypeError,
"<metaclass>.__prepare__() must return a mapping, not %T", &dict_obj);
}
Dict type_dict(&scope, *dict_obj);
// TODO(cshapiro): might need to do some kind of callback here and we want
// backtraces to work correctly. The key to doing that would be to put some
// state on the stack in between the the incoming arguments from the builtin
// caller and the on-stack state for the class body function call.
Object body_result(&scope,
thread->callFunctionWithImplicitGlobals(body, type_dict));
if (body_result.isError()) return *body_result;
if (bases != orig_bases) {
dictAtPutById(thread, type_dict, ID(__orig_bases__), orig_bases);
}
thread->stackPush(*metaclass);
Tuple pargs(&scope, runtime->newTupleWith3(name, bases, type_dict));
thread->stackPush(*pargs);
thread->stackPush(*kwargs);
return Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS);
}