RawObject FUNC()

in runtime/under-builtins-module.cpp [5862:5974]


RawObject FUNC(_builtins, _type_dunder_call)(Thread* thread, Arguments args) {
  HandleScope scope(thread);
  Runtime* runtime = thread->runtime();
  Object self_obj(&scope, args.get(0));
  Tuple pargs(&scope, args.get(1));
  Dict kwargs(&scope, args.get(2));
  word pargs_length = pargs.length();
  bool is_kwargs_empty = kwargs.numItems() == 0;
  // Shortcut for type(x) calls.
  if (pargs_length == 1 && is_kwargs_empty &&
      self_obj == runtime->typeAt(LayoutId::kType)) {
    return runtime->typeOf(pargs.at(0));
  }

  if (!runtime->isInstanceOfType(*self_obj)) {
    return thread->raiseWithFmt(
        LayoutId::kTypeError,
        "'__call__' for '%Y' objects doesn't apply to a '%T' object", ID(type),
        &self_obj);
  }
  Type self(&scope, *self_obj);

  // `instance = self.__new__(...)`
  Object instance(&scope, NoneType::object());
  Object call_args_obj(&scope, NoneType::object());
  bool use_object_dunder_new =
      self.isType() && self.hasFlag(Type::Flag::kHasObjectDunderNew);
  if (use_object_dunder_new) {
    // Most common case `__new__` was not overridden and is just
    // `object.__new__`.
    instance = objectNew(thread, self);
    if (instance.isErrorException()) return *instance;
  } else {
    Object dunder_new(&scope, Unbound::object());
    Object dunder_new_name(&scope, runtime->symbols()->at(ID(__new__)));
    if (self.isType()) {
      // Metaclass is "type" so we do not need to check for __new__ being a
      // datadescriptor and we can look it up directly on the type.
      dunder_new = typeLookupInMro(thread, *self, *dunder_new_name);
    }
    if (dunder_new.isStaticMethod()) {
      // Next most common case `__new__` is overridden with a normal function
      dunder_new = StaticMethod::cast(*dunder_new).function();
    } else {
      // Finally fallback to complete lookup for corner cases
      dunder_new = typeGetAttribute(thread, self, dunder_new_name);
    }

    CHECK(!dunder_new.isError(), "self must have __new__");
    thread->stackPush(*dunder_new);
    if (is_kwargs_empty) {
      thread->stackPush(*self);
      for (word i = 0; i < pargs_length; ++i) {
        thread->stackPush(pargs.at(i));
      }
      instance = Interpreter::call(thread, pargs_length + 1);
    } else {
      MutableTuple call_args(&scope,
                             runtime->newMutableTuple(pargs_length + 1));
      call_args.atPut(0, *self);
      call_args.replaceFromWith(1, *pargs, pargs_length);
      thread->stackPush(call_args.becomeImmutable());
      thread->stackPush(*kwargs);
      instance = Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS);
      call_args_obj = *call_args;
    }
    if (instance.isErrorException()) return *instance;
    if (!typeIsSubclass(runtime->typeOf(*instance), *self)) {
      return *instance;
    }
  }

  // instance.__init__(...)
  Object dunder_init_name(&scope, runtime->symbols()->at(ID(__init__)));
  Object dunder_init(&scope, typeGetAttribute(thread, self, dunder_init_name));
  // `object.__init__` does nothing, we may be able to just skip things.
  // The exception to the rule being `object.__init__` raising errors when
  // arguments are provided and nothing is overridden.
  if (dunder_init != runtime->objectDunderInit() ||
      (use_object_dunder_new &&
       (pargs.length() != 0 || kwargs.numItems() != 0))) {
    CHECK(!dunder_init.isError(), "self must have __init__");
    Object result(&scope, NoneType::object());
    thread->stackPush(*dunder_init);
    if (is_kwargs_empty) {
      thread->stackPush(*instance);
      for (word i = 0; i < pargs_length; ++i) {
        thread->stackPush(pargs.at(i));
      }
      result = Interpreter::call(thread, pargs_length + 1);
    } else {
      if (!call_args_obj.isMutableTuple()) {
        MutableTuple call_args(&scope,
                               runtime->newMutableTuple(pargs_length + 1));
        call_args.atPut(0, *instance);
        call_args.replaceFromWith(1, *pargs, pargs_length);
        call_args_obj = *call_args;
      } else {
        MutableTuple::cast(*call_args_obj).atPut(0, *instance);
      }
      thread->stackPush(*call_args_obj);
      thread->stackPush(*kwargs);
      result = Interpreter::callEx(thread, CallFunctionExFlag::VAR_KEYWORDS);
    }
    if (result.isErrorException()) return *result;
    if (!result.isNoneType()) {
      Object type_name(&scope, self.name());
      return thread->raiseWithFmt(LayoutId::kTypeError,
                                  "%S.__init__ returned non None", &type_name);
    }
  }
  return *instance;
}