RawObject Runtime::attributeAtSetLocation()

in runtime/runtime.cpp [2910:3018]


RawObject Runtime::attributeAtSetLocation(Thread* thread,
                                          const Object& receiver,
                                          const Object& name,
                                          LoadAttrKind* kind,
                                          Object* location_out) {
  DCHECK(isInternedStr(thread, name), "name must be an interned str");
  HandleScope scope(thread);
  Runtime* runtime = thread->runtime();
  Type type(&scope, runtime->typeOf(*receiver));
  if (kind != nullptr) *kind = LoadAttrKind::kUnknown;
  if (type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)) {
    DCHECK(objectDunderGetattribute().isNoneType() ||
               typeLookupInMroById(thread, *type, ID(__getattribute__)) ==
                   objectDunderGetattribute(),
           "object.__getattribute__ is expected");
    Object result(&scope, objectGetAttributeSetLocation(thread, receiver, name,
                                                        location_out, kind));
    if (!result.isErrorNotFound()) {
      return *result;
    }
    result = thread->invokeMethod2(receiver, ID(__getattr__), name);
    if (!result.isErrorNotFound()) {
      return *result;
    }
    return objectRaiseAttributeError(thread, receiver, name);
  }
  if (type.hasFlag(Type::Flag::kHasModuleDunderGetattribute) &&
      runtime->isInstanceOfModule(*receiver)) {
    DCHECK(runtime->moduleDunderGetattribute().isNoneType() ||
               typeLookupInMroById(thread, *type, ID(__getattribute__)) ==
                   runtime->moduleDunderGetattribute(),
           "module.__getattribute__ is expected");
    Module module(&scope, *receiver);
    Object result(&scope, moduleGetAttributeSetLocation(thread, module, name,
                                                        location_out));
    if (!result.isErrorNotFound()) {
      // We have a result that can be cached.
      if (kind != nullptr) *kind = LoadAttrKind::kModule;
      return *result;
    }
    // Try again
    result = thread->invokeMethod2(receiver, ID(__getattr__), name);
    if (!result.isErrorNotFound()) {
      return *result;
    }
    return moduleRaiseAttributeError(thread, module, name);
  }
  if (type.hasFlag(Type::Flag::kHasTypeDunderGetattribute) &&
      runtime->isInstanceOfType(*receiver)) {
    DCHECK(runtime->typeDunderGetattribute().isNoneType() ||
               typeLookupInMroById(thread, *type, ID(__getattribute__)) ==
                   runtime->typeDunderGetattribute(),
           "type.__getattribute__ is expected");
    Type object_as_type(&scope, *receiver);
    Object result(&scope, typeGetAttributeSetLocation(thread, object_as_type,
                                                      name, location_out));
    if (!result.isErrorNotFound()) {
      // We have a result that can be cached.
      if (kind != nullptr && location_out->isValueCell()) {
        *kind = LoadAttrKind::kType;
      }
      return *result;
    }
    // Try again
    result = thread->invokeMethod2(receiver, ID(__getattr__), name);
    if (!result.isErrorNotFound()) {
      return *result;
    }
    Object type_name(&scope, object_as_type.name());
    return thread->raiseWithFmt(LayoutId::kAttributeError,
                                "type object '%S' has no attribute '%S'",
                                &type_name, &name);
  }
  if (receiver.isSuper()) {
    Super object_as_super(&scope, *receiver);
    Object result(&scope, superGetAttribute(thread, object_as_super, name));
    if (!result.isErrorNotFound()) {
      return *result;
    }
    return thread->raiseWithFmt(LayoutId::kAttributeError,
                                "super object has no attribute '%S'", &name);
  }
  Object dunder_getattribute(
      &scope,
      Interpreter::lookupMethod(thread, receiver, ID(__getattribute__)));
  DCHECK(!dunder_getattribute.isErrorNotFound(),
         "__getattribute__ is expected to be found");
  if (UNLIKELY(dunder_getattribute.isError())) return *dunder_getattribute;
  Object result(&scope, Interpreter::callMethod2(thread, dunder_getattribute,
                                                 receiver, name));
  if (!result.isErrorException() ||
      !thread->pendingExceptionMatches(LayoutId::kAttributeError)) {
    return *result;
  }

  // Save the attribute error and clear it then attempt to call `__getattr__`.
  Object saved_type(&scope, thread->pendingExceptionType());
  Object saved_value(&scope, thread->pendingExceptionValue());
  Object saved_traceback(&scope, thread->pendingExceptionTraceback());
  thread->clearPendingException();
  result = thread->invokeMethod2(receiver, ID(__getattr__), name);
  if (result.isErrorNotFound()) {
    thread->setPendingExceptionType(*saved_type);
    thread->setPendingExceptionValue(*saved_value);
    thread->setPendingExceptionTraceback(*saved_traceback);
    return Error::exception();
  }
  return *result;
}