ext/Objects/typeobject.cpp (1,773 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) // typeobject.c implementation #include <cinttypes> #include "cpython-data.h" #include "cpython-func.h" #include "cpython-types.h" #include "structmember.h" #include "api-handle.h" #include "attributedict.h" #include "builtins-module.h" #include "capi-typeslots.h" #include "capi.h" #include "dict-builtins.h" #include "extension-object.h" #include "function-builtins.h" #include "function-utils.h" #include "handles.h" #include "int-builtins.h" #include "mro.h" #include "object-utils.h" #include "objects.h" #include "runtime.h" #include "str-builtins.h" #include "trampolines.h" #include "type-builtins.h" #include "type-utils.h" #include "utils.h" namespace py { PY_EXPORT PyTypeObject* PySuper_Type_Ptr() { Runtime* runtime = Thread::current()->runtime(); return reinterpret_cast<PyTypeObject*>( ApiHandle::borrowedReference(runtime, runtime->typeAt(LayoutId::kSuper))); } PY_EXPORT int PyType_CheckExact_Func(PyObject* obj) { return ApiHandle::fromPyObject(obj)->asObject().isType(); } PY_EXPORT int PyType_Check_Func(PyObject* obj) { return Thread::current()->runtime()->isInstanceOfType( ApiHandle::fromPyObject(obj)->asObject()); } PY_EXPORT unsigned long PyType_GetFlags(PyTypeObject* type_obj) { ApiHandle* handle = ApiHandle::fromPyTypeObject(type_obj); HandleScope scope(Thread::current()); Type type(&scope, handle->asObjectNoImmediate()); if (type.isBuiltin()) return Py_TPFLAGS_DEFAULT; if (!typeHasSlots(type)) { UNIMPLEMENTED("GetFlags from types initialized through Python code"); } return typeSlotUWordAt(type, kSlotFlags); } // PyType_FromSpec() operator support // // The functions and data in this namespace, culminating in addOperators(), are // used to add Python-visible wrappers for type slot C functions (e.g., passing // a Py_nb_add slot will result in a __add__() method being added to the type). // The wrapper functions (wrapUnaryfunc(), wrapBinaryfunc(), etc...) handle // translating between incoming/outgoing RawObject/PyObject* values, along with // various bits of slot-specific logic. // // As other builtins the function's Code object has a pointer to the // appropriate wrapper function as its code field, and its consts field // is a 1-element tuple containing a pointer to the slot function provided by // the user. If this multi-step lookup ever becomes a performance problem, we // can easily template the trampolines and/or the wrapper functions, but this // keeps the code compact for now. static ALIGN_16 RawObject wrapUnaryfunc(Thread* thread, Arguments args) { unaryfunc func = reinterpret_cast<unaryfunc>(getNativeFunc(thread)); PyObject* o = ApiHandle::newReference(thread->runtime(), args.get(0)); PyObject* result = (*func)(o); Py_DECREF(o); return ApiHandle::checkFunctionResult(thread, result); } // Common work for hashfunc, lenfunc, and inquiry, all of which take a single // PyObject* and return an integral value. template <typename cfunc, typename RetFunc> static RawObject wrapIntegralfunc(Thread* thread, Arguments args, RetFunc ret) { cfunc func = reinterpret_cast<cfunc>(getNativeFunc(thread)); PyObject* o = ApiHandle::newReference(thread->runtime(), args.get(0)); auto result = func(o); Py_DECREF(o); if (result == -1 && thread->hasPendingException()) return Error::exception(); return ret(result); } static ALIGN_16 RawObject wrapHashfunc(Thread* thread, Arguments args) { return wrapIntegralfunc<hashfunc>(thread, args, [thread](Py_hash_t hash) { return thread->runtime()->newInt(hash); }); } static ALIGN_16 RawObject wrapLenfunc(Thread* thread, Arguments args) { return wrapIntegralfunc<lenfunc>(thread, args, [thread](Py_ssize_t len) { return thread->runtime()->newInt(len); }); } static ALIGN_16 RawObject wrapInquirypred(Thread* thread, Arguments args) { return wrapIntegralfunc<inquiry>( thread, args, [](int result) { return Bool::fromBool(result); }); } static ALIGN_16 RawObject wrapBinaryfunc(Thread* thread, Arguments args) { binaryfunc func = reinterpret_cast<binaryfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* o1 = ApiHandle::newReference(runtime, args.get(0)); PyObject* o2 = ApiHandle::newReference(runtime, args.get(1)); PyObject* result = (*func)(o1, o2); Py_DECREF(o2); Py_DECREF(o1); return ApiHandle::checkFunctionResult(thread, result); } static ALIGN_16 RawObject wrapBinaryfuncSwapped(Thread* thread, Arguments args) { binaryfunc func = reinterpret_cast<binaryfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* o1 = ApiHandle::newReference(runtime, args.get(0)); PyObject* o2 = ApiHandle::newReference(runtime, args.get(1)); PyObject* result = (*func)(o2, o1); Py_DECREF(o2); Py_DECREF(o1); return ApiHandle::checkFunctionResult(thread, result); } static RawObject wrapTernaryfuncImpl(Thread* thread, Arguments args, bool swap) { ternaryfunc func = reinterpret_cast<ternaryfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(swap ? 1 : 0)); PyObject* value = ApiHandle::newReference(runtime, args.get(swap ? 0 : 1)); PyObject* mod = ApiHandle::newReference(runtime, args.get(2)); PyObject* result = (*func)(self, value, mod); Py_DECREF(mod); Py_DECREF(value); Py_DECREF(self); return ApiHandle::checkFunctionResult(thread, result); } // wrapTernaryfunc() vs. wrapVarkwTernaryfunc(): // - wrapTernaryfunc(Swapped)(): Wraps a C function expecting exactly 3 // normal arguments, with the 3rd argument defaulting to None. // - wrapVarkwTernaryfunc(): Wraps a C function expecting a self argument, a // tuple of positional arguments and an optional dict of keyword arguments. static ALIGN_16 RawObject wrapTernaryfunc(Thread* thread, Arguments args) { return wrapTernaryfuncImpl(thread, args, false); } static ALIGN_16 RawObject wrapTernaryfuncSwapped(Thread* thread, Arguments args) { return wrapTernaryfuncImpl(thread, args, true); } static ALIGN_16 RawObject wrapTpNew(Thread* thread, Arguments args) { ternaryfunc func = reinterpret_cast<ternaryfunc>(getNativeFunc(thread)); HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfType(*self_obj)) { return thread->raiseWithFmt(LayoutId::kTypeError, "'__new__' requires 'type' but got '%T'", &self_obj); } Function function(&scope, thread->currentFrame()->function()); Type expected_type(&scope, slotWrapperFunctionType(function)); if (!typeIsSubclass(*self_obj, *expected_type)) { Str expected_type_name(&scope, strUnderlying(expected_type.name())); return thread->raiseWithFmt(LayoutId::kTypeError, "'__new__' requires '%S' but got '%T'", &expected_type_name, &self_obj); } PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* varargs = ApiHandle::newReference(runtime, args.get(1)); PyObject* kwargs = Dict::cast(args.get(2)).numItems() == 0 ? nullptr : ApiHandle::newReference(runtime, args.get(2)); PyObject* result = (*func)(self, varargs, kwargs); Py_XDECREF(kwargs); Py_DECREF(varargs); Py_DECREF(self); return ApiHandle::checkFunctionResult(thread, result); } static bool checkSelfWithSlotType(Thread* thread, const Object& self) { HandleScope scope(thread); Function function(&scope, thread->currentFrame()->function()); Type expected_type(&scope, slotWrapperFunctionType(function)); if (!typeIsSubclass(thread->runtime()->typeOf(*self), *expected_type)) { Str slot_name(&scope, function.name()); Str expected_type_name(&scope, strUnderlying(expected_type.name())); thread->raiseWithFmt(LayoutId::kTypeError, "'%S' requires '%S' but got '%T'", &slot_name, &expected_type_name, &self); return false; } return true; } static ALIGN_16 RawObject wrapVarkwTernaryfunc(Thread* thread, Arguments args) { ternaryfunc func = reinterpret_cast<ternaryfunc>(getNativeFunc(thread)); HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!checkSelfWithSlotType(thread, self_obj)) { return Error::exception(); } Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, *self_obj); PyObject* varargs = ApiHandle::newReference(runtime, args.get(1)); PyObject* kwargs = Dict::cast(args.get(2)).numItems() == 0 ? nullptr : ApiHandle::newReference(runtime, args.get(2)); PyObject* result = (*func)(self, varargs, kwargs); Py_XDECREF(kwargs); Py_DECREF(varargs); Py_DECREF(self); return ApiHandle::checkFunctionResult(thread, result); } static ALIGN_16 RawObject wrapSetattr(Thread* thread, Arguments args) { setattrofunc func = reinterpret_cast<setattrofunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* name = ApiHandle::newReference(runtime, args.get(1)); PyObject* value = ApiHandle::newReference(runtime, args.get(2)); int result = func(self, name, value); Py_DECREF(value); Py_DECREF(name); Py_DECREF(self); if (result < 0) return Error::exception(); return NoneType::object(); } static ALIGN_16 RawObject wrapDelattr(Thread* thread, Arguments args) { setattrofunc func = reinterpret_cast<setattrofunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* name = ApiHandle::newReference(runtime, args.get(1)); int result = func(self, name, nullptr); Py_DECREF(name); Py_DECREF(self); if (result < 0) return Error::exception(); return NoneType::object(); } template <CompareOp op> static ALIGN_16 RawObject wrapRichcompare(Thread* thread, Arguments args) { richcmpfunc func = reinterpret_cast<richcmpfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* other = ApiHandle::newReference(runtime, args.get(1)); PyObject* result = (*func)(self, other, op); Py_DECREF(other); Py_DECREF(self); return ApiHandle::checkFunctionResult(thread, result); } static ALIGN_16 RawObject wrapNext(Thread* thread, Arguments args) { unaryfunc func = reinterpret_cast<unaryfunc>(getNativeFunc(thread)); PyObject* self = ApiHandle::newReference(thread->runtime(), args.get(0)); PyObject* result = (*func)(self); Py_DECREF(self); if (result == nullptr && !thread->hasPendingException()) { return thread->raise(LayoutId::kStopIteration, NoneType::object()); } return ApiHandle::checkFunctionResult(thread, result); } static ALIGN_16 RawObject wrapDescrGet(Thread* thread, Arguments args) { descrgetfunc func = reinterpret_cast<descrgetfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* obj = nullptr; if (!args.get(1).isNoneType()) { obj = ApiHandle::newReference(runtime, args.get(1)); } PyObject* type = nullptr; if (!args.get(2).isNoneType()) { type = ApiHandle::newReference(runtime, args.get(2)); } if (obj == nullptr && type == nullptr) { return thread->raiseWithFmt(LayoutId::kTypeError, "__get__(None, None), is invalid"); } PyObject* result = (*func)(self, obj, type); Py_DECREF(self); Py_XDECREF(obj); Py_XDECREF(type); return ApiHandle::checkFunctionResult(thread, result); } static ALIGN_16 RawObject wrapDescrSet(Thread* thread, Arguments args) { descrsetfunc func = reinterpret_cast<descrsetfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* obj = ApiHandle::newReference(runtime, args.get(1)); PyObject* value = ApiHandle::newReference(runtime, args.get(2)); int result = func(self, obj, value); Py_DECREF(value); Py_DECREF(obj); Py_DECREF(self); if (result < 0) return Error::exception(); return NoneType::object(); } static ALIGN_16 RawObject wrapDescrDelete(Thread* thread, Arguments args) { descrsetfunc func = reinterpret_cast<descrsetfunc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* obj = ApiHandle::newReference(runtime, args.get(1)); int result = func(self, obj, nullptr); Py_DECREF(obj); Py_DECREF(self); if (result < 0) return Error::exception(); return NoneType::object(); } static ALIGN_16 RawObject wrapInit(Thread* thread, Arguments args) { initproc func = reinterpret_cast<initproc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* varargs = ApiHandle::newReference(runtime, args.get(1)); PyObject* kwargs = Dict::cast(args.get(2)).numItems() == 0 ? nullptr : ApiHandle::newReference(runtime, args.get(2)); int result = func(self, varargs, kwargs); Py_XDECREF(kwargs); Py_DECREF(varargs); Py_DECREF(self); if (result < 0) return Error::exception(); return NoneType::object(); } static ALIGN_16 RawObject wrapDel(Thread* thread, Arguments args) { destructor func = reinterpret_cast<destructor>(getNativeFunc(thread)); PyObject* self = ApiHandle::newReference(thread->runtime(), args.get(0)); func(self); Py_DECREF(self); return NoneType::object(); } static ALIGN_16 RawObject wrapObjobjargproc(Thread* thread, Arguments args) { objobjargproc func = reinterpret_cast<objobjargproc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* key = ApiHandle::newReference(runtime, args.get(1)); PyObject* value = ApiHandle::newReference(runtime, args.get(2)); int res = func(self, key, value); Py_DECREF(value); Py_DECREF(key); Py_DECREF(self); if (res == -1 && thread->hasPendingException()) return Error::exception(); return NoneType::object(); } static ALIGN_16 RawObject wrapObjobjproc(Thread* thread, Arguments args) { objobjproc func = reinterpret_cast<objobjproc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* value = ApiHandle::newReference(runtime, args.get(1)); int res = func(self, value); Py_DECREF(value); Py_DECREF(self); if (res == -1 && thread->hasPendingException()) return Error::exception(); return Bool::fromBool(res); } static ALIGN_16 RawObject wrapDelitem(Thread* thread, Arguments args) { objobjargproc func = reinterpret_cast<objobjargproc>(getNativeFunc(thread)); Runtime* runtime = thread->runtime(); PyObject* self = ApiHandle::newReference(runtime, args.get(0)); PyObject* key = ApiHandle::newReference(runtime, args.get(1)); int res = func(self, key, nullptr); Py_DECREF(key); Py_DECREF(self); if (res == -1 && thread->hasPendingException()) return Error::exception(); return NoneType::object(); } // Convert obj into a word-sized int or raise an OverflowError, in the style of // PyNumber_AsSsize_t(). static RawObject makeIndex(Thread* thread, const Object& obj) { HandleScope scope(thread); Object converted(&scope, intFromIndex(thread, obj)); if (converted.isError()) return *converted; Int i(&scope, intUnderlying(*converted)); if (i.numDigits() != 1) { return thread->raiseWithFmt(LayoutId::kOverflowError, "cannot fit '%T' into an index-sized integer", &obj); } return *i; } static ALIGN_16 RawObject wrapIndexargfunc(Thread* thread, Arguments args) { ssizeargfunc func = reinterpret_cast<ssizeargfunc>(getNativeFunc(thread)); HandleScope scope(thread); PyObject* self = ApiHandle::newReference(thread->runtime(), args.get(0)); Object arg(&scope, args.get(1)); arg = makeIndex(thread, arg); if (arg.isError()) { Py_DECREF(self); return *arg; } PyObject* result = (*func)(self, Int::cast(*arg).asWord()); Py_DECREF(self); return ApiHandle::checkFunctionResult(thread, result); } // First, convert arg to a word-sized int using makeIndex(). Then, if the result // is negative, add len(self) to normalize it. static RawObject normalizeIndex(Thread* thread, const Object& self, const Object& arg) { HandleScope scope(thread); Object index(&scope, makeIndex(thread, arg)); if (index.isError()) return *index; word i = Int::cast(*index).asWord(); if (i >= 0) { return *index; } Object len(&scope, thread->invokeFunction1(ID(builtins), ID(len), self)); if (len.isError()) return *len; len = makeIndex(thread, len); if (len.isError()) return *len; i += Int::cast(*len).asWord(); return thread->runtime()->newInt(i); } static ALIGN_16 RawObject wrapSqItem(Thread* thread, Arguments args) { ssizeargfunc func = reinterpret_cast<ssizeargfunc>(getNativeFunc(thread)); HandleScope scope(thread); Object self(&scope, args.get(0)); Object arg(&scope, args.get(1)); arg = normalizeIndex(thread, self, arg); if (arg.isError()) return *arg; PyObject* py_self = ApiHandle::newReference(thread->runtime(), *self); PyObject* result = (*func)(py_self, Int::cast(*arg).asWord()); Py_DECREF(py_self); return ApiHandle::checkFunctionResult(thread, result); } static ALIGN_16 RawObject wrapSqSetitem(Thread* thread, Arguments args) { ssizeobjargproc func = reinterpret_cast<ssizeobjargproc>(getNativeFunc(thread)); HandleScope scope(thread); Object self(&scope, args.get(0)); Object arg(&scope, args.get(1)); arg = normalizeIndex(thread, self, arg); if (arg.isError()) return *arg; Runtime* runtime = thread->runtime(); PyObject* py_self = ApiHandle::newReference(runtime, *self); PyObject* py_value = ApiHandle::newReference(runtime, args.get(2)); int result = func(py_self, Int::cast(*arg).asWord(), py_value); Py_DECREF(py_value); Py_DECREF(py_self); if (result == -1 && thread->hasPendingException()) return Error::exception(); return NoneType::object(); } static ALIGN_16 RawObject wrapSqDelitem(Thread* thread, Arguments args) { ssizeobjargproc func = reinterpret_cast<ssizeobjargproc>(getNativeFunc(thread)); HandleScope scope(thread); Object self(&scope, args.get(0)); Object arg(&scope, args.get(1)); arg = normalizeIndex(thread, self, arg); if (arg.isError()) return *arg; PyObject* py_self = ApiHandle::newReference(thread->runtime(), *self); int result = func(py_self, Int::cast(*arg).asWord(), nullptr); Py_DECREF(py_self); if (result == -1 && thread->hasPendingException()) return Error::exception(); return NoneType::object(); } // Information about a single type slot. struct SlotDef { // The name of the method in managed code. SymbolId name; // type slot it. int id; // List of parameter names/symbols. View<SymbolId> parameters; // The wrapper function for this slot. BuiltinFunction wrapper; // RawCode::Flags to be set on slot function. word flags; // Doc string for the function. const char* doc; }; static const SymbolId kParamsSelfArgsKwargs[] = {ID(self), ID(args), ID(kwargs)}; static const SymbolId kParamsSelfInstanceOwner[] = {ID(self), ID(instance), ID(owner)}; static const SymbolId kParamsSelfInstanceValue[] = {ID(self), ID(instance), ID(value)}; static const SymbolId kParamsSelfInstance[] = {ID(self), ID(instance)}; static const SymbolId kParamsSelfKeyValue[] = {ID(self), ID(key), ID(value)}; static const SymbolId kParamsSelfKey[] = {ID(self), ID(key)}; static const SymbolId kParamsSelfNameValue[] = {ID(self), ID(name), ID(value)}; static const SymbolId kParamsSelfName[] = {ID(self), ID(name)}; static const SymbolId kParamsSelfValueMod[] = {ID(self), ID(value), ID(mod)}; static const SymbolId kParamsSelfValue[] = {ID(self), ID(value)}; static const SymbolId kParamsSelf[] = {ID(self)}; static const SymbolId kParamsTypeArgsKwargs[] = {ID(type), ID(args), ID(kwargs)}; // These macros currently ignore the FUNCTION argument, which is still the // function name inherited from CPython. This will be cleaned up when we add // default slot implementations that delegate to the corresponding Python // method, along with logic to update slots as needed when a user assigns to a // type dict. #define TPSLOT(NAME, SLOT, PARAMETERS, FUNCTION, WRAPPER, DOC) \ { NAME, SLOT, PARAMETERS, WRAPPER, 0, DOC } #define KWSLOT(NAME, SLOT, PARAMETERS, FUNCTION, WRAPPER, DOC) \ { \ NAME, SLOT, PARAMETERS, WRAPPER, \ Code::Flags::kVarargs | Code::Flags::kVarkeyargs, DOC, \ } #define UNSLOT(NAME, C_NAME, SLOT, FUNCTION, DOC) \ TPSLOT(NAME, SLOT, kParamsSelf, FUNCTION, wrapUnaryfunc, \ C_NAME "($self, /)\n--\n\n" DOC) #define IBSLOT(NAME, C_NAME, SLOT, FUNCTION, WRAPPER, DOC) \ TPSLOT(NAME, SLOT, kParamsSelfValue, FUNCTION, WRAPPER, \ C_NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") #define BINSLOT(NAME, C_NAME, SLOT, FUNCTION, DOC) \ TPSLOT(NAME, SLOT, kParamsSelfValue, FUNCTION, wrapBinaryfunc, \ C_NAME "($self, value, /)\n--\n\nReturn self" DOC "value.") #define RBINSLOT(NAME, C_NAME, SLOT, FUNCTION, DOC) \ TPSLOT(NAME, SLOT, kParamsSelfValue, FUNCTION, wrapBinaryfuncSwapped, \ C_NAME "($self, value, /)\n--\n\nReturn value" DOC "self.") #define BINSLOTNOTINFIX(NAME, C_NAME, SLOT, FUNCTION, DOC) \ TPSLOT(NAME, SLOT, kParamsSelfValue, FUNCTION, wrapBinaryfunc, \ C_NAME "($self, value, /)\n--\n\n" DOC) #define RBINSLOTNOTINFIX(NAME, C_NAME, SLOT, FUNCTION, DOC) \ TPSLOT(NAME, SLOT, kParamsSelfValue, FUNCTION, wrapBinaryfuncSwapped, \ C_NAME "($self, value, /)\n--\n\n" DOC) static const SlotDef kSlotdefs[] = { TPSLOT(ID(__getattribute__), Py_tp_getattr, kParamsSelfName, nullptr, nullptr, ""), TPSLOT(ID(__getattr__), Py_tp_getattr, kParamsSelfName, nullptr, nullptr, ""), TPSLOT(ID(__setattr__), Py_tp_setattr, kParamsSelfNameValue, nullptr, nullptr, ""), TPSLOT(ID(__delattr__), Py_tp_setattr, kParamsSelfName, nullptr, nullptr, ""), UNSLOT(ID(__repr__), "__repr__", Py_tp_repr, slot_tp_repr, "Return repr(self)."), TPSLOT(ID(__hash__), Py_tp_hash, kParamsSelf, slot_tp_hash, wrapHashfunc, "__hash__($self, /)\n--\n\nReturn hash(self)."), KWSLOT( ID(__call__), Py_tp_call, kParamsSelfArgsKwargs, slot_tp_call, wrapVarkwTernaryfunc, "__call__($self, /, *args, **kwargs)\n--\n\nCall self as a function."), UNSLOT(ID(__str__), "__str__", Py_tp_str, slot_tp_str, "Return str(self)."), TPSLOT(ID(__getattribute__), Py_tp_getattro, kParamsSelfName, slot_tp_getattr_hook, wrapBinaryfunc, "__getattribute__($self, name, /)\n--\n\nReturn getattr(self, " "name)."), TPSLOT(ID(__getattr__), Py_tp_getattro, kParamsSelfName, slot_tp_getattr_hook, nullptr, ""), TPSLOT(ID(__setattr__), Py_tp_setattro, kParamsSelfNameValue, slot_tp_setattro, wrapSetattr, "__setattr__($self, name, value, /)\n--\n\nImplement setattr(self, " "name, value)."), TPSLOT(ID(__delattr__), Py_tp_setattro, kParamsSelfName, slot_tp_setattro, wrapDelattr, "__delattr__($self, name, /)\n--\n\nImplement delattr(self, name)."), TPSLOT(ID(__lt__), Py_tp_richcompare, kParamsSelfValue, slot_tp_richcompare, wrapRichcompare<LT>, "__lt__($self, value, /)\n--\n\nReturn self<value."), TPSLOT(ID(__le__), Py_tp_richcompare, kParamsSelfValue, slot_tp_richcompare, wrapRichcompare<LE>, "__le__($self, value, /)\n--\n\nReturn self<=value."), TPSLOT(ID(__eq__), Py_tp_richcompare, kParamsSelfValue, slot_tp_richcompare, wrapRichcompare<EQ>, "__eq__($self, value, /)\n--\n\nReturn self==value."), TPSLOT(ID(__ne__), Py_tp_richcompare, kParamsSelfValue, slot_tp_richcompare, wrapRichcompare<NE>, "__ne__($self, value, /)\n--\n\nReturn self!=value."), TPSLOT(ID(__gt__), Py_tp_richcompare, kParamsSelfValue, slot_tp_richcompare, wrapRichcompare<GT>, "__gt__($self, value, /)\n--\n\nReturn self>value."), TPSLOT(ID(__ge__), Py_tp_richcompare, kParamsSelfValue, slot_tp_richcompare, wrapRichcompare<GE>, "__ge__($self, value, /)\n--\n\nReturn self>=value."), UNSLOT(ID(__iter__), "__iter__", Py_tp_iter, slot_tp_iter, "Implement iter(self)."), TPSLOT(ID(__next__), Py_tp_iternext, kParamsSelf, slot_tp_iternext, wrapNext, "__next__($self, /)\n--\n\nImplement next(self)."), TPSLOT(ID(__get__), Py_tp_descr_get, kParamsSelfInstanceOwner, slot_tp_descr_get, wrapDescrGet, "__get__($self, instance, owner, /)\n--\n\nReturn an attribute of " "instance, which is of type owner."), TPSLOT(ID(__set__), Py_tp_descr_set, kParamsSelfInstanceValue, slot_tp_descr_set, wrapDescrSet, "__set__($self, instance, value, /)\n--\n\nSet an attribute of " "instance to value."), TPSLOT(ID(__delete__), Py_tp_descr_set, kParamsSelfInstance, slot_tp_descr_set, wrapDescrDelete, "__delete__($self, instance, /)\n--\n\nDelete an attribute of " "instance."), KWSLOT(ID(__init__), Py_tp_init, kParamsSelfArgsKwargs, slot_tp_init, wrapInit, "__init__($self, /, *args, **kwargs)\n--\n\nInitialize self. See " "help(type(self)) for accurate signature."), KWSLOT(ID(__new__), Py_tp_new, kParamsTypeArgsKwargs, slot_tp_new, wrapTpNew, "__new__(type, /, *args, **kwargs)\n--\n\n" "Create and return new object. See help(type) for accurate " "signature."), TPSLOT(ID(__del__), Py_tp_finalize, kParamsSelf, slot_tp_finalize, wrapDel, ""), UNSLOT(ID(__await__), "__await__", Py_am_await, slot_am_await, "Return an iterator to be used in await expression."), UNSLOT(ID(__aiter__), "__aiter__", Py_am_aiter, slot_am_aiter, "Return an awaitable, that resolves in asynchronous iterator."), UNSLOT(ID(__anext__), "__anext__", Py_am_anext, slot_am_anext, "Return a value or raise StopAsyncIteration."), BINSLOT(ID(__add__), "__add__", Py_nb_add, slot_nb_add, "+"), RBINSLOT(ID(__radd__), "__radd__", Py_nb_add, slot_nb_add, "+"), BINSLOT(ID(__sub__), "__sub__", Py_nb_subtract, slot_nb_subtract, "-"), RBINSLOT(ID(__rsub__), "__rsub__", Py_nb_subtract, slot_nb_subtract, "-"), BINSLOT(ID(__mul__), "__mul__", Py_nb_multiply, slot_nb_multiply, "*"), RBINSLOT(ID(__rmul__), "__rmul__", Py_nb_multiply, slot_nb_multiply, "*"), BINSLOT(ID(__mod__), "__mod__", Py_nb_remainder, slot_nb_remainder, "%"), RBINSLOT(ID(__rmod__), "__rmod__", Py_nb_remainder, slot_nb_remainder, "%"), BINSLOTNOTINFIX(ID(__divmod__), "__divmod__", Py_nb_divmod, slot_nb_divmod, "Return divmod(self, value)."), RBINSLOTNOTINFIX(ID(__rdivmod__), "__rdivmod__", Py_nb_divmod, slot_nb_divmod, "Return divmod(value, self)."), TPSLOT(ID(__pow__), Py_nb_power, kParamsSelfValueMod, slot_nb_power, wrapTernaryfunc, "__pow__($self, value, mod=None, /)\n--\n\nReturn pow(self, value, " "mod)."), TPSLOT(ID(__rpow__), Py_nb_power, kParamsSelfValueMod, slot_nb_power, wrapTernaryfuncSwapped, "__rpow__($self, value, mod=None, /)\n--\n\nReturn pow(value, self, " "mod)."), UNSLOT(ID(__neg__), "__neg__", Py_nb_negative, slot_nb_negative, "-self"), UNSLOT(ID(__pos__), "__pos__", Py_nb_positive, slot_nb_positive, "+self"), UNSLOT(ID(__abs__), "__abs__", Py_nb_absolute, slot_nb_absolute, "abs(self)"), TPSLOT(ID(__bool__), Py_nb_bool, kParamsSelf, slot_nb_bool, wrapInquirypred, "__bool__($self, /)\n--\n\nself != 0"), UNSLOT(ID(__invert__), "__invert__", Py_nb_invert, slot_nb_invert, "~self"), BINSLOT(ID(__lshift__), "__lshift__", Py_nb_lshift, slot_nb_lshift, "<<"), RBINSLOT(ID(__rlshift__), "__rlshift__", Py_nb_lshift, slot_nb_lshift, "<<"), BINSLOT(ID(__rshift__), "__rshift__", Py_nb_rshift, slot_nb_rshift, ">>"), RBINSLOT(ID(__rrshift__), "__rrshift__", Py_nb_rshift, slot_nb_rshift, ">>"), BINSLOT(ID(__and__), "__and__", Py_nb_and, slot_nb_and, "&"), RBINSLOT(ID(__rand__), "__rand__", Py_nb_and, slot_nb_and, "&"), BINSLOT(ID(__xor__), "__xor__", Py_nb_xor, slot_nb_xor, "^"), RBINSLOT(ID(__rxor__), "__rxor__", Py_nb_xor, slot_nb_xor, "^"), BINSLOT(ID(__or__), "__or__", Py_nb_or, slot_nb_or, "|"), RBINSLOT(ID(__ror__), "__ror__", Py_nb_or, slot_nb_or, "|"), UNSLOT(ID(__int__), "__int__", Py_nb_int, slot_nb_int, "int(self)"), UNSLOT(ID(__float__), "__float__", Py_nb_float, slot_nb_float, "float(self)"), IBSLOT(ID(__iadd__), "__iadd__", Py_nb_inplace_add, slot_nb_inplace_add, wrapBinaryfunc, "+="), IBSLOT(ID(__isub__), "__isub__", Py_nb_inplace_subtract, slot_nb_inplace_subtract, wrapBinaryfunc, "-="), IBSLOT(ID(__imul__), "__imul__", Py_nb_inplace_multiply, slot_nb_inplace_multiply, wrapBinaryfunc, "*="), IBSLOT(ID(__imod__), "__imod__", Py_nb_inplace_remainder, slot_nb_inplace_remainder, wrapBinaryfunc, "%="), IBSLOT(ID(__ipow__), "__ipow__", Py_nb_inplace_power, slot_nb_inplace_power, wrapBinaryfunc, "**="), IBSLOT(ID(__ilshift__), "__ilshift__", Py_nb_inplace_lshift, slot_nb_inplace_lshift, wrapBinaryfunc, "<<="), IBSLOT(ID(__irshift__), "__irshift__", Py_nb_inplace_rshift, slot_nb_inplace_rshift, wrapBinaryfunc, ">>="), IBSLOT(ID(__iand__), "__iand__", Py_nb_inplace_and, slot_nb_inplace_and, wrapBinaryfunc, "&="), IBSLOT(ID(__ixor__), "__ixor__", Py_nb_inplace_xor, slot_nb_inplace_xor, wrapBinaryfunc, "^="), IBSLOT(ID(__ior__), "__ior__", Py_nb_inplace_or, slot_nb_inplace_or, wrapBinaryfunc, "|="), BINSLOT(ID(__floordiv__), "__floordiv__", Py_nb_floor_divide, slot_nb_floor_divide, "//"), RBINSLOT(ID(__rfloordiv__), "__rfloordiv__", Py_nb_floor_divide, slot_nb_floor_divide, "//"), BINSLOT(ID(__truediv__), "__truediv__", Py_nb_true_divide, slot_nb_true_divide, "/"), RBINSLOT(ID(__rtruediv__), "__rtruediv__", Py_nb_true_divide, slot_nb_true_divide, "/"), IBSLOT(ID(__ifloordiv__), "__ifloordiv__", Py_nb_inplace_floor_divide, slot_nb_inplace_floor_divide, wrapBinaryfunc, "//="), IBSLOT(ID(__itruediv__), "__itruediv__", Py_nb_inplace_true_divide, slot_nb_inplace_true_divide, wrapBinaryfunc, "/="), UNSLOT(ID(__index__), "__index__", Py_nb_index, slot_nb_index, "Return self converted to an integer, if self is suitable " "for use as an index into a list."), BINSLOT(ID(__matmul__), "__matmul__", Py_nb_matrix_multiply, slot_nb_matrix_multiply, "@"), RBINSLOT(ID(__rmatmul__), "__rmatmul__", Py_nb_matrix_multiply, slot_nb_matrix_multiply, "@"), IBSLOT(ID(__imatmul__), "__imatmul__", Py_nb_inplace_matrix_multiply, slot_nb_inplace_matrix_multiply, wrapBinaryfunc, "@="), TPSLOT(ID(__len__), Py_mp_length, kParamsSelf, slot_mp_length, wrapLenfunc, "__len__($self, /)\n--\n\nReturn len(self)."), TPSLOT(ID(__getitem__), Py_mp_subscript, kParamsSelfKey, slot_mp_subscript, wrapBinaryfunc, "__getitem__($self, key, /)\n--\n\nReturn self[key]."), TPSLOT(ID(__setitem__), Py_mp_ass_subscript, kParamsSelfKeyValue, slot_mp_ass_subscript, wrapObjobjargproc, "__setitem__($self, key, value, /)\n--\n\nSet self[key] to value."), TPSLOT(ID(__delitem__), Py_mp_ass_subscript, kParamsSelfKey, slot_mp_ass_subscript, wrapDelitem, "__delitem__($self, key, /)\n--\n\nDelete self[key]."), TPSLOT(ID(__len__), Py_sq_length, kParamsSelf, slot_sq_length, wrapLenfunc, "__len__($self, /)\n--\n\nReturn len(self)."), TPSLOT(ID(__add__), Py_sq_concat, kParamsSelfValue, nullptr, wrapBinaryfunc, "__add__($self, value, /)\n--\n\nReturn self+value."), TPSLOT(ID(__mul__), Py_sq_repeat, kParamsSelfValue, nullptr, wrapIndexargfunc, "__mul__($self, value, /)\n--\n\nReturn self*value."), TPSLOT(ID(__rmul__), Py_sq_repeat, kParamsSelfValue, nullptr, wrapIndexargfunc, "__rmul__($self, value, /)\n--\n\nReturn value*self."), TPSLOT(ID(__getitem__), Py_sq_item, kParamsSelfKey, slot_sq_item, wrapSqItem, "__getitem__($self, key, /)\n--\n\nReturn self[key]."), TPSLOT(ID(__setitem__), Py_sq_ass_item, kParamsSelfKeyValue, slot_sq_ass_item, wrapSqSetitem, "__setitem__($self, key, value, /)\n--\n\nSet self[key] to value."), TPSLOT(ID(__delitem__), Py_sq_ass_item, kParamsSelfKey, slot_sq_ass_item, wrapSqDelitem, "__delitem__($self, key, /)\n--\n\nDelete self[key]."), TPSLOT(ID(__contains__), Py_sq_contains, kParamsSelfKey, slot_sq_contains, wrapObjobjproc, "__contains__($self, key, /)\n--\n\nReturn key in self."), TPSLOT(ID(__iadd__), Py_sq_inplace_concat, kParamsSelfValue, nullptr, wrapBinaryfunc, "__iadd__($self, value, /)\n--\n\nImplement self+=value."), TPSLOT(ID(__imul__), Py_sq_inplace_repeat, kParamsSelfValue, nullptr, wrapIndexargfunc, "__imul__($self, value, /)\n--\n\nImplement self*=value."), }; // For every entry in kSlotdefs with a non-null wrapper function, a slot id // that was provided by the user, and no preexisting entry in the type dict, add // a wrapper function to call the slot from Python. // // Returns Error if an exception was raised at any point, None otherwise. static RawObject addOperators(Thread* thread, const Type& type) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); Str type_name(&scope, type.name()); for (const SlotDef& slot : kSlotdefs) { if (slot.wrapper == nullptr) continue; void* slot_value = typeSlotAt(type, slot.id); if (slot_value == nullptr) continue; // Unlike most slots, we always allow __new__ to be overwritten by a subtype if (slot.id != Py_tp_new && !typeAtById(thread, type, slot.name).isErrorNotFound()) { continue; } // When given PyObject_HashNotImplemented, put None in the type dict // rather than a wrapper. CPython does this regardless of which slot it // was given for, so we do too. if (slot_value == reinterpret_cast<void*>(&PyObject_HashNotImplemented)) { Object none(&scope, NoneType::object()); typeAtPutById(thread, type, slot.name, none); return NoneType::object(); } Object func_obj(&scope, NoneType::object()); if (slot_value == reinterpret_cast<void*>(&PyObject_GenericGetAttr)) { func_obj = runtime->objectDunderGetattribute(); } else if (slot_value == reinterpret_cast<void*>(&PyObject_GenericSetAttr)) { func_obj = runtime->objectDunderSetattr(); } else { // Create the wrapper function. Str slot_name(&scope, runtime->symbols()->at(slot.name)); Str qualname(&scope, runtime->newStrFromFmt("%S.%S", &type_name, &slot_name)); Code code(&scope, newExtCode(thread, slot_name, slot.parameters, slot.flags, slot.wrapper, slot_value)); Object globals(&scope, NoneType::object()); Function func(&scope, runtime->newFunctionWithCode(thread, qualname, code, globals)); slotWrapperFunctionSetType(func, type); if (slot.id == Py_nb_power) { Object none(&scope, NoneType::object()); func.setDefaults(runtime->newTupleWith1(none)); } // __new__ is the one special-case static method, so wrap it // appropriately. func_obj = *func; if (slot.id == Py_tp_new) { func_obj = thread->invokeFunction1(ID(builtins), ID(staticmethod), func); if (func_obj.isError()) return *func_obj; } } // Finally, put the wrapper in the type dict. typeAtPutById(thread, type, slot.name, func_obj); } return NoneType::object(); } static PyObject* allocatePyObject(const Type& type, Py_ssize_t nitems) { uword basic_size = typeSlotUWordAt(type, kSlotBasicSize); uword item_size = typeSlotUWordAt(type, kSlotItemSize); Py_ssize_t size = Utils::roundUp(nitems * item_size + basic_size, kWordSize); PyObject* result = nullptr; if (type.hasFlag(Type::Flag::kHasCycleGC)) { result = static_cast<PyObject*>(_PyObject_GC_Calloc(size)); } else { result = static_cast<PyObject*>(PyObject_Calloc(1, size)); } // Track object in native GC queue if (type.hasFlag(Type::Flag::kHasCycleGC)) { PyObject_GC_Track(result); } return result; } static PyObject* superclassTpNew(PyTypeObject* typeobj, PyObject* args, PyObject* kwargs) { Thread* thread = Thread::current(); HandleScope scope(thread); Runtime* runtime = thread->runtime(); Type type(&scope, ApiHandle::fromPyTypeObject(typeobj)->asObject()); Object args_obj(&scope, args == nullptr ? runtime->emptyTuple() : ApiHandle::fromPyObject(args)->asObject()); DCHECK(runtime->isInstanceOfTuple(*args_obj), "Slot __new__ expected tuple args"); Object kwargs_obj(&scope, kwargs == nullptr ? NoneType::object() : ApiHandle::fromPyObject(kwargs)->asObject()); DCHECK(kwargs == nullptr || runtime->isInstanceOfDict(*kwargs_obj), "Slot __new__ expected nullptr or dict kwargs"); Tuple type_mro(&scope, type.mro()); word i = 0; Type superclass(&scope, type_mro.at(i++)); while (typeHasSlots(superclass)) { superclass = type_mro.at(i++); } Object dunder_new(&scope, typeLookupInMroById(thread, *superclass, ID(__new__))); Object none(&scope, NoneType::object()); dunder_new = resolveDescriptorGet(thread, dunder_new, none, type); dunder_new = runtime->newBoundMethod(dunder_new, type); thread->stackPush(*dunder_new); thread->stackPush(*args_obj); word flags = 0; if (kwargs != nullptr) { thread->stackPush(*kwargs_obj); flags = CallFunctionExFlag::VAR_KEYWORDS; } Object instance(&scope, Interpreter::callEx(thread, flags)); if (instance.isErrorException()) return nullptr; // If the type doesn't require NativeData, we don't need to allocate or // create a NativeProxy for the instance if (!type.hasNativeData()) { return ApiHandle::newReference(runtime, *instance); } PyObject* result = allocatePyObject(type, 0); if (result == nullptr) { thread->raiseMemoryError(); return nullptr; } return initializeExtensionObject(thread, result, typeobj, instance); } // tp_new slot implementation that delegates to a Type's __new__ attribute. static PyObject* slotTpNew(PyObject* type, PyObject* args, PyObject* kwargs) { Thread* thread = Thread::current(); Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object type_obj(&scope, ApiHandle::fromPyObject(type)->asObject()); Object args_obj(&scope, ApiHandle::fromPyObject(args)->asObject()); DCHECK(runtime->isInstanceOfTuple(*args_obj), "Slot __new__ expected tuple args"); Object kwargs_obj(&scope, kwargs ? ApiHandle::fromPyObject(kwargs)->asObject() : NoneType::object()); DCHECK(kwargs == nullptr || runtime->isInstanceOfDict(*kwargs_obj), "Slot __new__ expected nullptr or dict kwargs"); // Construct a new args tuple with type at the front. Tuple args_tuple(&scope, tupleUnderlying(*args_obj)); MutableTuple new_args(&scope, runtime->newMutableTuple(args_tuple.length() + 1)); new_args.atPut(0, *type_obj); new_args.replaceFromWith(1, *args_tuple, args_tuple.length()); // Call type.__new__(type, *args, **kwargs) Object dunder_new(&scope, runtime->attributeAtById(thread, type_obj, ID(__new__))); if (dunder_new.isError()) return nullptr; thread->stackPush(*dunder_new); thread->stackPush(new_args.becomeImmutable()); word flags = 0; if (kwargs != nullptr) { thread->stackPush(*kwargs_obj); flags = CallFunctionExFlag::VAR_KEYWORDS; } Object result(&scope, Interpreter::callEx(thread, flags)); if (result.isError()) return nullptr; return ApiHandle::newReference(runtime, *result); } // Return a default slot wrapper for the given slot, or abort if it's not yet // supported. // // This performs similar duties to update_one_slot() in CPython, but it's // drastically simpler. This is intentional, and will only change if a real C // extension exercises slots in a way that exposes the differences. static void* defaultSlot(int slot_id) { switch (slot_id) { case Py_tp_new: return reinterpret_cast<void*>(&slotTpNew); default: UNIMPLEMENTED("Unsupported default slot %d", slot_id); } } static int emptyClear(PyObject*) { return 0; } static int emptyTraverse(PyObject*, visitproc, void*) { return 0; } static void builtinDealloc(PyObject* self) { // This function is called for instances of non-heaptypes (most builtin types) // or subclasses of them. There is nothing to deallocate/free for // instances of builtin types. However subclasses may have extra data // allocated that needs to be freed with `PyObject_Del`. Thread* thread = Thread::current(); Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object self_obj(&scope, ApiHandle::fromPyObject(self)->asObject()); Type type(&scope, runtime->typeOf(*self_obj)); if (type.hasNativeData()) { PyObject_Del(self); } } PY_EXPORT void* PyType_GetSlot(PyTypeObject* type_obj, int slot_id) { Thread* thread = Thread::current(); if (slot_id < 0) { thread->raiseBadInternalCall(); return nullptr; } HandleScope scope(thread); ApiHandle* handle = ApiHandle::fromPyTypeObject(type_obj); Type type(&scope, handle->asObjectNoImmediate()); if (!type.isCPythonHeaptype()) { if (slot_id == Py_tp_new) { return reinterpret_cast<void*>(&superclassTpNew); } if (slot_id == Py_tp_clear) { return reinterpret_cast<void*>(&emptyClear); } if (slot_id == Py_tp_traverse) { return reinterpret_cast<void*>(&emptyTraverse); } if (slot_id == Py_tp_dealloc) { return reinterpret_cast<void*>(&builtinDealloc); } thread->raiseBadInternalCall(); return nullptr; } // Extension module requesting slot from a future version if (!isValidSlotId(slot_id)) { return nullptr; } if (!typeHasSlots(type)) { // The Type was not created by PyType_FromSpec(), so return a default slot // implementation that delegates to managed methods. return defaultSlot(slot_id); } if (isObjectSlotId(slot_id)) { return ApiHandle::borrowedReference(thread->runtime(), typeSlotObjectAt(type, slot_id)); } return typeSlotAt(type, slot_id); } PY_EXPORT int PyType_Ready(PyTypeObject*) { UNIMPLEMENTED("This function will never be implemented"); } PY_EXPORT PyObject* PyType_FromSpec(PyType_Spec* spec) { return PyType_FromSpecWithBases(spec, nullptr); } static RawObject memberGetter(Thread* thread, PyMemberDef* member) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object name(&scope, runtime->newStrFromCStr(member->name)); Int offset(&scope, runtime->newInt(member->offset)); switch (member->type) { case T_BOOL: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_bool), offset); case T_BYTE: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_byte), offset); case T_UBYTE: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_ubyte), offset); case T_SHORT: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_short), offset); case T_USHORT: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_ushort), offset); case T_INT: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_int), offset); case T_UINT: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_uint), offset); case T_LONG: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_long), offset); case T_ULONG: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_ulong), offset); case T_PYSSIZET: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_ulong), offset); case T_FLOAT: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_float), offset); case T_DOUBLE: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_double), offset); case T_LONGLONG: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_long), offset); case T_ULONGLONG: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_ulong), offset); case T_STRING: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_string), offset); case T_STRING_INPLACE: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_string), offset); case T_CHAR: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_char), offset); case T_OBJECT: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_pyobject), offset); case T_OBJECT_EX: return thread->invokeFunction2(ID(builtins), ID(_new_member_get_pyobject), offset, name); case T_NONE: return thread->invokeFunction1(ID(builtins), ID(_new_member_get_pyobject), offset); default: return thread->raiseWithFmt(LayoutId::kSystemError, "bad member name type"); } } static RawObject memberSetter(Thread* thread, PyMemberDef* member) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); if (member->flags & READONLY) { Object name(&scope, runtime->newStrFromCStr(member->name)); Function setter( &scope, thread->invokeFunction1(ID(builtins), ID(_new_member_set_readonly), name)); return *setter; } Int offset(&scope, runtime->newInt(member->offset)); switch (member->type) { case T_BOOL: return thread->invokeFunction1(ID(builtins), ID(_new_member_set_bool), offset); case T_BYTE: { Int num_bytes(&scope, runtime->newInt(sizeof(char))); Int min_value(&scope, runtime->newInt(std::numeric_limits<char>::min())); Int max_value(&scope, runtime->newInt(std::numeric_limits<char>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("char")); Function setter(&scope, thread->invokeFunction5( ID(builtins), ID(_new_member_set_integral), offset, num_bytes, min_value, max_value, primitive_type)); return *setter; } case T_UBYTE: { Int num_bytes(&scope, runtime->newInt(sizeof(unsigned char))); Int max_value(&scope, runtime->newIntFromUnsigned( std::numeric_limits<unsigned char>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("unsigned char")); Function setter(&scope, thread->invokeFunction4( ID(builtins), ID(_new_member_set_integral_unsigned), offset, num_bytes, max_value, primitive_type)); return *setter; } case T_SHORT: { Int num_bytes(&scope, runtime->newInt(sizeof(short))); Int min_value(&scope, runtime->newInt(std::numeric_limits<short>::min())); Int max_value(&scope, runtime->newInt(std::numeric_limits<short>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("short")); Function setter(&scope, thread->invokeFunction5( ID(builtins), ID(_new_member_set_integral), offset, num_bytes, min_value, max_value, primitive_type)); return *setter; } case T_USHORT: { Int num_bytes(&scope, runtime->newInt(sizeof(unsigned short))); Int max_value(&scope, runtime->newIntFromUnsigned( std::numeric_limits<unsigned short>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("unsigned short")); Function setter(&scope, thread->invokeFunction4( ID(builtins), ID(_new_member_set_integral_unsigned), offset, num_bytes, max_value, primitive_type)); return *setter; } case T_INT: { Int num_bytes(&scope, runtime->newInt(sizeof(int))); Int min_value(&scope, runtime->newInt(std::numeric_limits<int>::min())); Int max_value(&scope, runtime->newInt(std::numeric_limits<int>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("int")); Function setter(&scope, thread->invokeFunction5( ID(builtins), ID(_new_member_set_integral), offset, num_bytes, min_value, max_value, primitive_type)); return *setter; } case T_UINT: { Int num_bytes(&scope, runtime->newInt(sizeof(unsigned int))); Int max_value(&scope, runtime->newIntFromUnsigned( std::numeric_limits<unsigned int>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("unsigned int")); Function setter(&scope, thread->invokeFunction4( ID(builtins), ID(_new_member_set_integral_unsigned), offset, num_bytes, max_value, primitive_type)); return *setter; } case T_LONG: { Int num_bytes(&scope, runtime->newInt(sizeof(long))); Int min_value(&scope, runtime->newInt(std::numeric_limits<long>::min())); Int max_value(&scope, runtime->newInt(std::numeric_limits<long>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("long")); Function setter(&scope, thread->invokeFunction5( ID(builtins), ID(_new_member_set_integral), offset, num_bytes, min_value, max_value, primitive_type)); return *setter; } case T_ULONG: { Int num_bytes(&scope, runtime->newInt(sizeof(unsigned long))); Int max_value(&scope, runtime->newIntFromUnsigned( std::numeric_limits<unsigned long>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("unsigned long")); Function setter(&scope, thread->invokeFunction4( ID(builtins), ID(_new_member_set_integral_unsigned), offset, num_bytes, max_value, primitive_type)); return *setter; } case T_PYSSIZET: { Int num_bytes(&scope, runtime->newInt(sizeof(Py_ssize_t))); Int min_value(&scope, SmallInt::fromWord(0)); Int max_value(&scope, runtime->newInt(std::numeric_limits<Py_ssize_t>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("Py_ssize_t")); Function setter(&scope, thread->invokeFunction5( ID(builtins), ID(_new_member_set_integral), offset, num_bytes, min_value, max_value, primitive_type)); return *setter; } case T_FLOAT: { return thread->invokeFunction1(ID(builtins), ID(_new_member_set_float), offset); } case T_DOUBLE: { return thread->invokeFunction1(ID(builtins), ID(_new_member_set_double), offset); } case T_STRING: { Object name(&scope, runtime->newStrFromCStr(member->name)); Function setter(&scope, thread->invokeFunction1( ID(builtins), ID(_new_member_set_readonly_strings), name)); return *setter; } case T_STRING_INPLACE: { Object name(&scope, runtime->newStrFromCStr(member->name)); Function setter(&scope, thread->invokeFunction1( ID(builtins), ID(_new_member_set_readonly_strings), name)); return *setter; } case T_CHAR: { Function setter( &scope, thread->invokeFunction1(ID(builtins), ID(_new_member_set_char), offset)); return *setter; } case T_OBJECT: { Function setter(&scope, thread->invokeFunction1( ID(builtins), ID(_new_member_set_pyobject), offset)); return *setter; } case T_OBJECT_EX: { Function setter(&scope, thread->invokeFunction1( ID(builtins), ID(_new_member_set_pyobject), offset)); return *setter; } case T_LONGLONG: { Int num_bytes(&scope, runtime->newInt(sizeof(long long))); Int min_value(&scope, runtime->newInt(std::numeric_limits<long long>::min())); Int max_value(&scope, runtime->newInt(std::numeric_limits<long long>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("long long")); Function setter(&scope, thread->invokeFunction5( ID(builtins), ID(_new_member_set_integral), offset, num_bytes, min_value, max_value, primitive_type)); return *setter; } case T_ULONGLONG: { Int num_bytes(&scope, runtime->newInt(sizeof(unsigned long long))); Int max_value(&scope, runtime->newIntFromUnsigned( std::numeric_limits<unsigned long long>::max())); Str primitive_type(&scope, runtime->newStrFromCStr("unsigned long long")); Function setter(&scope, thread->invokeFunction4( ID(builtins), ID(_new_member_set_integral_unsigned), offset, num_bytes, max_value, primitive_type)); return *setter; } default: return thread->raiseWithFmt(LayoutId::kSystemError, "bad member name type"); } } static RawObject addMethods(Thread* thread, const Type& type) { HandleScope scope(thread); PyMethodDef* methods = static_cast<PyMethodDef*>(typeSlotAt(type, Py_tp_methods)); if (methods == nullptr) return NoneType::object(); Object none(&scope, NoneType::object()); Object unbound(&scope, Unbound::object()); Object name(&scope, NoneType::object()); Object member(&scope, NoneType::object()); for (PyMethodDef* method = methods; method->ml_name != nullptr; method++) { name = Runtime::internStrFromCStr(thread, method->ml_name); int flags = method->ml_flags; if (!(flags & METH_COEXIST) && !typeAt(type, name).isErrorNotFound()) { continue; } if (flags & METH_CLASS) { if (flags & METH_STATIC) { return thread->raiseWithFmt(LayoutId::kValueError, "method cannot be both class and static"); } member = newClassMethod(thread, method, name, type); } else if (flags & METH_STATIC) { member = newCFunction(thread, method, name, unbound, none); } else { member = newMethod(thread, method, name, type); } typeAtPut(thread, type, name, member); } return NoneType::object(); } static RawObject addMembers(Thread* thread, const Type& type, PyMemberDef* members) { if (members == nullptr) return NoneType::object(); HandleScope scope(thread); Object none(&scope, NoneType::object()); Runtime* runtime = thread->runtime(); Object getter(&scope, NoneType::object()); Object setter(&scope, NoneType::object()); Object property(&scope, NoneType::object()); Object name_obj(&scope, NoneType::object()); for (PyMemberDef* member = members; member->name != nullptr; member++) { const char* name = member->name; if (std::strcmp(name, "__dictoffset__") == 0 || std::strcmp(name, "__weaklistoffset__") == 0) { continue; } getter = memberGetter(thread, member); if (getter.isErrorException()) return *getter; setter = memberSetter(thread, member); if (setter.isErrorException()) return *setter; property = runtime->newProperty(getter, setter, none); name_obj = Runtime::internStrFromCStr(thread, name); typeAtPut(thread, type, name_obj, property); } return NoneType::object(); } static RawObject addGetSet(Thread* thread, const Type& type) { HandleScope scope(thread); PyGetSetDef* getsets = static_cast<PyGetSetDef*>(typeSlotAt(type, Py_tp_getset)); if (getsets == nullptr) return NoneType::object(); for (word i = 0; getsets[i].name != nullptr; i++) { Object name(&scope, Runtime::internStrFromCStr(thread, getsets[i].name)); Object property(&scope, newGetSet(thread, name, &getsets[i])); typeAtPut(thread, type, name, property); } return NoneType::object(); } // The default tp_dealloc slot for all C Types. This loosely follows CPython's // subtype_dealloc. static void subtypeDealloc(PyObject* self) { Thread* thread = Thread::current(); Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object self_obj(&scope, ApiHandle::fromPyObject(self)->asObject()); Type type(&scope, runtime->typeOf(*self_obj)); DCHECK(type.isCPythonHeaptype(), "must be called with heaptype"); void* finalize_func = typeSlotAt(type, Py_tp_finalize); if (finalize_func != nullptr) { if (PyObject_CallFinalizerFromDealloc(self) < 0) { // Resurrected return; } } destructor del_func = reinterpret_cast<destructor>(typeSlotAt(type, Py_tp_del)); if (del_func != nullptr) { (*del_func)(self); if (Py_REFCNT(self) > 1) { // Resurrected return; } } destructor base_dealloc = subtypeDealloc; Type base_type(&scope, *type); while (base_dealloc == subtypeDealloc) { base_type = typeSlotObjectAt(base_type, Py_tp_base); if (typeHasSlots(base_type)) { base_dealloc = reinterpret_cast<destructor>(typeSlotAt(base_type, Py_tp_dealloc)); } else { base_dealloc = reinterpret_cast<destructor>(PyObject_Del); } } type = runtime->typeOf(*self_obj); (*base_dealloc)(self); if (type.isCPythonHeaptype() && !base_type.isCPythonHeaptype()) { ApiHandle::borrowedReference(runtime, *type)->decref(); } } // Copy the slot from the base type if it defined. static void copySlot(Thread* thread, const Type& type, const Type& base, int slot_id) { if (typeSlotAt(type, slot_id) != nullptr) return; void* base_slot = typeSlotAt(base, slot_id); typeSlotAtPut(thread, type, slot_id, base_slot); } static void* baseBaseSlot(Thread* thread, const Type& base, int slot_id) { if (typeSlotObjectAt(base, Py_tp_base) != SmallInt::fromWord(0)) { return nullptr; } HandleScope scope(thread); Type basebase(&scope, typeSlotObjectAt(base, Py_tp_base)); if (!typeHasSlots(basebase)) return nullptr; return typeSlotAt(basebase, slot_id); } // Copy the slot from the base type if defined and it is the first type that // defines it. If base's base type defines the same slot, then base inherited // it. Thus, it is not the first type to define it. static void copySlotIfImplementedInBase(Thread* thread, const Type& type, const Type& base, int slot_id) { if (typeSlotAt(type, slot_id) != nullptr) return; void* base_slot = typeSlotAt(base, slot_id); if (base_slot != nullptr) { void* basebase_slot = baseBaseSlot(thread, base, slot_id); if (basebase_slot == nullptr || base_slot != basebase_slot) { typeSlotAtPut(thread, type, slot_id, base_slot); } } } static void inheritFree(Thread* thread, const Type& type, unsigned long type_flags, const Type& base, unsigned long base_flags) { // Both child and base are GC or non GC if ((type_flags & Py_TPFLAGS_HAVE_GC) == (base_flags & Py_TPFLAGS_HAVE_GC)) { copySlotIfImplementedInBase(thread, type, base, Py_tp_free); return; } DCHECK(!(base_flags & Py_TPFLAGS_HAVE_GC), "The child should not remove GC"); // Only the child is GC // Set the free function if the base has a default free if ((type_flags & Py_TPFLAGS_HAVE_GC) && typeSlotAt(type, Py_tp_free) == nullptr) { if (typeSlotAt(base, Py_tp_free) == reinterpret_cast<void*>(&PyObject_Free)) { typeSlotAtPut(thread, type, Py_tp_free, reinterpret_cast<void*>(&PyObject_GC_Del)); } } } static void inheritGCFlagsAndSlots(Thread* thread, const Type& type, const Type& base) { unsigned long type_flags = typeSlotUWordAt(type, kSlotFlags); unsigned long base_flags = typeSlotUWordAt(base, kSlotFlags); if (!(type_flags & Py_TPFLAGS_HAVE_GC) && (base_flags & Py_TPFLAGS_HAVE_GC) && typeSlotAt(type, Py_tp_traverse) == nullptr && typeSlotAt(type, Py_tp_clear) == nullptr) { typeSlotUWordAtPut(thread, type, kSlotFlags, type_flags | Py_TPFLAGS_HAVE_GC); copySlot(thread, type, base, Py_tp_traverse); copySlot(thread, type, base, Py_tp_clear); } } static void inheritNonFunctionSlots(Thread* thread, const Type& type, const Type& base) { if (typeSlotUWordAt(type, kSlotBasicSize) == 0) { typeSlotUWordAtPut(thread, type, kSlotBasicSize, typeSlotUWordAt(base, kSlotBasicSize)); } typeSlotUWordAtPut(thread, type, kSlotItemSize, typeSlotUWordAt(base, kSlotItemSize)); } // clang-format off static const int kInheritableSlots[] = { // Number slots Py_nb_add, Py_nb_subtract, Py_nb_multiply, Py_nb_remainder, Py_nb_divmod, Py_nb_power, Py_nb_negative, Py_nb_positive, Py_nb_absolute, Py_nb_bool, Py_nb_invert, Py_nb_lshift, Py_nb_rshift, Py_nb_and, Py_nb_xor, Py_nb_or, Py_nb_int, Py_nb_float, Py_nb_inplace_add, Py_nb_inplace_subtract, Py_nb_inplace_multiply, Py_nb_inplace_remainder, Py_nb_inplace_power, Py_nb_inplace_lshift, Py_nb_inplace_rshift, Py_nb_inplace_and, Py_nb_inplace_xor, Py_nb_inplace_or, Py_nb_true_divide, Py_nb_floor_divide, Py_nb_inplace_true_divide, Py_nb_inplace_floor_divide, Py_nb_index, Py_nb_matrix_multiply, Py_nb_inplace_matrix_multiply, // Await slots Py_am_await, Py_am_aiter, Py_am_anext, // Sequence slots Py_sq_length, Py_sq_concat, Py_sq_repeat, Py_sq_item, Py_sq_ass_item, Py_sq_contains, Py_sq_inplace_concat, Py_sq_inplace_repeat, // Mapping slots Py_mp_length, Py_mp_subscript, Py_mp_ass_subscript, // Buffer protocol is not part of PEP-384 // Type slots Py_tp_dealloc, Py_tp_repr, Py_tp_call, Py_tp_str, Py_tp_iter, Py_tp_iternext, Py_tp_descr_get, Py_tp_descr_set, Py_tp_init, Py_tp_alloc, Py_tp_is_gc, Py_tp_finalize, // Instance dictionary is not part of PEP-384 // Weak reference support is not part of PEP-384 }; // clang-format on static int typeSetattro(PyTypeObject* type, PyObject* name, PyObject* value) { DCHECK(PyType_GetFlags(type) & Py_TPFLAGS_HEAPTYPE, "typeSetattro requires an instance from a heap allocated C " "extension type"); Thread* thread = Thread::current(); HandleScope scope(thread); Type type_obj(&scope, ApiHandle::fromPyTypeObject(type)->asObject()); Object name_obj(&scope, ApiHandle::fromPyObject(name)->asObject()); Object value_obj(&scope, ApiHandle::fromPyObject(value)->asObject()); if (thread ->invokeMethodStatic3(LayoutId::kType, ID(__setattr__), type_obj, name_obj, value_obj) .isError()) { return -1; } return 0; } static void inheritSlots(Thread* thread, const Type& type, const Type& base) { // Heap allocated types are guaranteed to have slot space, no check is needed // i.e. CPython does: `if (type->tp_as_number != NULL)` // Only static types need to do this type of check. for (int slot_id : kInheritableSlots) { copySlotIfImplementedInBase(thread, type, base, slot_id); } // Inherit conditional type slots if (typeSlotAt(type, Py_tp_getattr) == nullptr && typeSlotAt(type, Py_tp_getattro) == nullptr) { copySlot(thread, type, base, Py_tp_getattr); copySlot(thread, type, base, Py_tp_getattro); } if (typeSlotAt(type, Py_tp_setattr) == nullptr && typeSlotAt(type, Py_tp_setattro) == nullptr) { copySlot(thread, type, base, Py_tp_setattr); copySlot(thread, type, base, Py_tp_setattro); } if (typeSlotAt(type, Py_tp_richcompare) == nullptr && typeSlotAt(type, Py_tp_hash) == nullptr) { copySlot(thread, type, base, Py_tp_richcompare); copySlot(thread, type, base, Py_tp_hash); } unsigned long type_flags = typeSlotUWordAt(type, kSlotFlags); unsigned long base_flags = typeSlotUWordAt(base, kSlotFlags); inheritFree(thread, type, type_flags, base, base_flags); } static int objectInit(PyObject*, PyObject*, PyObject*) { UNIMPLEMENTED("should not directly call tp_init"); } static RawObject addDefaultsForRequiredSlots(Thread* thread, const Type& type, const Type& base) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); Str type_name(&scope, type.name()); // tp_basicsize -> sizeof(PyObject) uword basic_size = typeSlotUWordAt(type, kSlotBasicSize); if (basic_size == 0) { basic_size = sizeof(PyObject); typeSlotUWordAtPut(thread, type, kSlotBasicSize, basic_size); } DCHECK(basic_size >= sizeof(PyObject), "sizeof(PyObject) is the minimum size required for an extension " "instance"); // tp_repr -> PyObject_Repr if (typeSlotAt(type, Py_tp_repr) == nullptr) { typeSlotAtPut(thread, type, Py_tp_repr, reinterpret_cast<void*>(&PyObject_Repr)); // PyObject_Repr delegates its job to type.__repr__(). DCHECK(!typeLookupInMroById(thread, *type, ID(__repr__)).isErrorNotFound(), "__repr__ is expected"); } // tp_str -> object_str if (typeSlotAt(type, Py_tp_str) == nullptr) { typeSlotAtPut(thread, type, Py_tp_str, reinterpret_cast<void*>(&PyObject_Str)); // PyObject_Str delegates its job to type.__str__(). DCHECK(!typeLookupInMroById(thread, *type, ID(__str__)).isErrorNotFound(), "__str__ is expected"); } // tp_init -> object_init if (typeSlotAt(type, Py_tp_init) == nullptr) { typeSlotAtPut(thread, type, Py_tp_init, reinterpret_cast<void*>(&objectInit)); } // tp_alloc -> PyType_GenericAlloc if (typeSlotAt(type, Py_tp_alloc) == nullptr) { typeSlotAtPut(thread, type, Py_tp_alloc, reinterpret_cast<void*>(&PyType_GenericAlloc)); } // tp_new -> PyType_GenericNew if (typeSlotAt(type, Py_tp_new) == nullptr) { void* new_func = reinterpret_cast<void*>(&superclassTpNew); typeSlotAtPut(thread, type, Py_tp_new, new_func); Str dunder_new_name(&scope, runtime->symbols()->at(ID(__new__))); Str qualname(&scope, runtime->newStrFromFmt("%S.%S", &type_name, &dunder_new_name)); Code code(&scope, newExtCode(thread, dunder_new_name, kParamsTypeArgsKwargs, Code::Flags::kVarargs | Code::Flags::kVarkeyargs, wrapTpNew, new_func)); Object globals(&scope, NoneType::object()); Function func( &scope, runtime->newFunctionWithCode(thread, qualname, code, globals)); slotWrapperFunctionSetType(func, type); Object func_obj( &scope, thread->invokeFunction1(ID(builtins), ID(staticmethod), func)); if (func_obj.isError()) return *func; typeAtPutById(thread, type, ID(__new__), func_obj); } // tp_free. if (typeSlotAt(type, Py_tp_free) == nullptr) { unsigned long type_flags = typeSlotUWordAt(type, kSlotFlags); freefunc func = (type_flags & Py_TPFLAGS_HAVE_GC) ? &PyObject_GC_Del : &PyObject_Del; typeSlotAtPut(thread, type, Py_tp_free, reinterpret_cast<void*>(func)); } // tp_setattro if (typeSlotAt(type, Py_tp_setattr) == nullptr && typeSlotAt(type, Py_tp_setattro) == nullptr) { if (typeIsSubclass(*base, runtime->typeAt(LayoutId::kType))) { typeSlotAtPut(thread, type, Py_tp_setattro, reinterpret_cast<void*>(typeSetattro)); } } return NoneType::object(); } RawObject typeInheritSlots(Thread* thread, const Type& type, const Type& base) { HandleScope scope(thread); if (!typeHasSlots(type)) { typeSlotsAllocate(thread, type); unsigned long flags = typeGetFlags(base); if (type.hasFlag(Type::Flag::kHasCycleGC)) flags |= Py_TPFLAGS_HAVE_GC; if (type.hasFlag(Type::Flag::kIsBasetype)) flags |= Py_TPFLAGS_BASETYPE; if (type.hasFlag(Type::Flag::kIsCPythonHeaptype)) { flags |= Py_TPFLAGS_HEAPTYPE; } typeSlotUWordAtPut(thread, type, kSlotFlags, flags); uword basicsize = typeGetBasicSize(base); typeSlotUWordAtPut(thread, type, kSlotBasicSize, basicsize); typeSlotUWordAtPut(thread, type, kSlotItemSize, 0); typeSlotObjectAtPut(type, Py_tp_base, *base); } // Inherit special slots from dominant base if (typeHasSlots(base)) { inheritGCFlagsAndSlots(thread, type, base); if (typeSlotAt(type, Py_tp_new) == nullptr) { copySlot(thread, type, base, Py_tp_new); } inheritNonFunctionSlots(thread, type, base); } // Inherit slots from the MRO Tuple mro(&scope, type.mro()); for (word i = 1; i < mro.length(); i++) { Type mro_entry(&scope, mro.at(i)); // Skip inheritance if mro_entry does not define slots if (!typeHasSlots(mro_entry)) continue; inheritSlots(thread, type, mro_entry); } // Inherit all the default slots that would have been inherited // through the base object type in CPython Object result(&scope, addDefaultsForRequiredSlots(thread, type, base)); if (result.isError()) return *result; return NoneType::object(); } PY_EXPORT PyObject* PyType_FromSpecWithBases(PyType_Spec* spec, PyObject* bases) { Thread* thread = Thread::current(); Runtime* runtime = thread->runtime(); HandleScope scope(thread); // Define the type name Object module_name(&scope, NoneType::object()); const char* class_name = strrchr(spec->name, '.'); if (class_name == nullptr) { class_name = spec->name; } else { module_name = Runtime::internStrFromAll( thread, View<byte>(reinterpret_cast<const byte*>(spec->name), class_name - spec->name)); class_name++; } Str type_name(&scope, Runtime::internStrFromCStr(thread, class_name)); // Create a new type for the PyTypeObject with an instance layout // matching the layout of RawNativeProxy // TODO(T54277314): Fill the dictionary before creating the type Tuple bases_obj(&scope, runtime->implicitBases()); if (bases != nullptr) bases_obj = ApiHandle::fromPyObject(bases)->asObject(); Dict dict(&scope, runtime->newDict()); if (!module_name.isNoneType()) { dictAtPutById(thread, dict, ID(__module__), module_name); } dictAtPutById(thread, dict, ID(__qualname__), type_name); bool has_default_dealloc = true; for (PyType_Slot* slot = spec->slots; slot->slot; slot++) { int slot_id = slot->slot; if (!isValidSlotId(slot_id)) { thread->raiseWithFmt(LayoutId::kRuntimeError, "invalid slot offset"); return nullptr; } switch (slot_id) { case Py_tp_dealloc: case Py_tp_del: case Py_tp_finalize: has_default_dealloc = false; break; case Py_tp_setattr: UNIMPLEMENTED("Py_tp_setattr not supported"); } } word flags = Type::Flag::kIsCPythonHeaptype; // We allocate a heap object in the C heap space only when 1) we need to // execute a custom finalizer with it or 2) the type explicitly uses non-zero // sizes to store a user-specified structure. if (!has_default_dealloc || static_cast<size_t>(spec->basicsize) > sizeof(PyObject) || spec->itemsize > 0) { flags |= Type::Flag::kHasNativeData; } if (spec->flags & Py_TPFLAGS_HAVE_GC) { flags |= Type::Flag::kHasCycleGC; } if (spec->flags & Py_TPFLAGS_BASETYPE) { flags |= Type::Flag::kIsBasetype; } Object fixed_attr_base_obj(&scope, computeFixedAttributeBase(thread, bases_obj)); if (fixed_attr_base_obj.isErrorException()) return nullptr; Type fixed_attr_base(&scope, *fixed_attr_base_obj); word base_size = typeGetBasicSize(fixed_attr_base); if (spec->basicsize > base_size) { flags |= Type::Flag::kIsFixedAttributeBase; } PyMemberDef* members = nullptr; word dictoffset = 0; for (PyType_Slot* slot = spec->slots; slot->slot != 0; slot++) { if (slot->slot != Py_tp_members) continue; members = static_cast<PyMemberDef*>(slot->pfunc); for (PyMemberDef* member = members; member->name != nullptr; member++) { const char* name = member->name; if (std::strcmp(name, "__weaklistoffset__") == 0) { DCHECK(member->type == T_PYSSIZET, "must be T_PYSSIZET"); DCHECK(member->flags == READONLY, "must be readonly"); // weaklistoffset not used yet. } else if (std::strcmp(name, "__dictoffset__") == 0) { DCHECK(member->type == T_PYSSIZET, "must be T_PYSSIZET"); DCHECK(member->flags == READONLY, "must be readonly"); dictoffset = member->offset; } } } bool add_instance_dict = (dictoffset != 0); Type metaclass(&scope, runtime->typeAt(LayoutId::kType)); Object type_obj(&scope, typeNew(thread, metaclass, type_name, bases_obj, dict, static_cast<Type::Flag>(flags), /*inherit_slots=*/false, /*add_instance_dict=*/add_instance_dict)); if (type_obj.isError()) return nullptr; Type type(&scope, *type_obj); // Initialize the extension slots tuple typeSlotsAllocate(thread, type); typeSlotObjectAtPut(type, Py_tp_bases, *bases_obj); typeSlotObjectAtPut(type, Py_tp_base, *fixed_attr_base); unsigned long extension_flags = spec->flags | Py_TPFLAGS_READY | Py_TPFLAGS_HEAPTYPE; typeSlotUWordAtPut(thread, type, kSlotFlags, extension_flags); typeSlotUWordAtPut(thread, type, kSlotBasicSize, spec->basicsize); typeSlotUWordAtPut(thread, type, kSlotItemSize, spec->itemsize); // Set the type slots for (PyType_Slot* slot = spec->slots; slot->slot; slot++) { typeSlotAtPut(thread, type, slot->slot, slot->pfunc); } if (has_default_dealloc) { type.setFlags( static_cast<Type::Flag>(type.flags() | Type::Flag::kHasDefaultDealloc)); } // If no custom tp_dealloc is defined set subtypeDealloc if (typeSlotAt(type, Py_tp_dealloc) == nullptr) { typeSlotAtPut(thread, type, Py_tp_dealloc, reinterpret_cast<void*>(&subtypeDealloc)); } if (addOperators(thread, type).isError()) return nullptr; if (addMethods(thread, type).isError()) return nullptr; if (addMembers(thread, type, members).isErrorException()) return nullptr; if (addGetSet(thread, type).isError()) return nullptr; if (typeInheritSlots(thread, type, fixed_attr_base).isError()) return nullptr; return ApiHandle::newReferenceWithManaged(runtime, *type); } PY_EXPORT PyObject* PyType_GenericAlloc(PyTypeObject* type_obj, Py_ssize_t nitems) { Thread* thread = Thread::current(); HandleScope scope(thread); Type type(&scope, ApiHandle::fromPyTypeObject(type_obj)->asObjectNoImmediate()); DCHECK(!type.isBuiltin(), "Type is unmanaged. Please initialize using PyType_FromSpec"); DCHECK(typeHasSlots(type), "GenericAlloc from types initialized through Python code"); if (!type.hasNativeData()) { // Since the type will be pointed to by the layout as long as there are any // objects of its type, we don't need to INCREF the type object if it // doesn't have NativeData. Layout layout(&scope, type.instanceLayout()); Runtime* runtime = thread->runtime(); return ApiHandle::newReference(runtime, runtime->newInstance(layout)); } PyObject* result = allocatePyObject(type, nitems); if (result == nullptr) { thread->raiseMemoryError(); return nullptr; } // Initialize the newly-allocated instance if (typeSlotUWordAt(type, kSlotItemSize) == 0) { PyObject_Init(result, type_obj); } else { PyObject_InitVar(reinterpret_cast<PyVarObject*>(result), type_obj, nitems); } return result; } PY_EXPORT Py_ssize_t _PyObject_SIZE_Func(PyObject* obj) { HandleScope scope(Thread::current()); Type type(&scope, ApiHandle::fromPyObject(obj)->asObject()); return typeSlotUWordAt(type, kSlotBasicSize); } PY_EXPORT Py_ssize_t _PyObject_VAR_SIZE_Func(PyObject* obj, Py_ssize_t nitems) { HandleScope scope(Thread::current()); Type type(&scope, ApiHandle::fromPyObject(obj)->asObject()); uword basic_size = typeSlotUWordAt(type, kSlotBasicSize); uword item_size = typeSlotUWordAt(type, kSlotItemSize); return Utils::roundUp(nitems * item_size + basic_size, kWordSize); } PY_EXPORT unsigned int PyType_ClearCache() { UNIMPLEMENTED("PyType_ClearCache"); } PY_EXPORT PyObject* PyType_GenericNew(PyTypeObject* type, PyObject*, PyObject*) { auto alloc_func = reinterpret_cast<allocfunc>(PyType_GetSlot(type, Py_tp_alloc)); return alloc_func(type, 0); } PY_EXPORT int PyObject_TypeCheck_Func(PyObject* obj, PyTypeObject* type) { PyTypeObject* obj_type = reinterpret_cast<PyTypeObject*>(PyObject_Type(obj)); int res = PyType_IsSubtype(obj_type, type); Py_DECREF(obj_type); return res; } PY_EXPORT int PyType_IsSubtype(PyTypeObject* a, PyTypeObject* b) { return a == b || typeIsSubclass(ApiHandle::fromPyTypeObject(a)->asObject(), ApiHandle::fromPyTypeObject(b)->asObject()) ? 1 : 0; } PY_EXPORT void PyType_Modified(PyTypeObject* /* e */) { // We invalidate caches incrementally, so do nothing. } PY_EXPORT PyTypeObject* PyType_Type_Ptr() { Runtime* runtime = Thread::current()->runtime(); return reinterpret_cast<PyTypeObject*>( ApiHandle::borrowedReference(runtime, runtime->typeAt(LayoutId::kType))); } PY_EXPORT PyObject* _PyObject_LookupSpecial(PyObject* /* f */, _Py_Identifier* /* d */) { UNIMPLEMENTED("_Py_Identifiers are not supported"); } PY_EXPORT const char* _PyType_Name(PyTypeObject* type) { Thread* thread = Thread::current(); if (type == nullptr) { thread->raiseBadInternalCall(); return nullptr; } HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object obj(&scope, ApiHandle::fromPyTypeObject(type)->asObject()); if (!runtime->isInstanceOfType(*obj)) { thread->raiseBadInternalCall(); return nullptr; } Type type_obj(&scope, *obj); return PyUnicode_AsUTF8( ApiHandle::borrowedReference(runtime, type_obj.name())); } PY_EXPORT PyObject* _PyType_Lookup(PyTypeObject* type, PyObject* name) { Thread* thread = Thread::current(); HandleScope scope(thread); Type type_obj( &scope, ApiHandle::fromPyObject(reinterpret_cast<PyObject*>(type))->asObject()); Object name_obj(&scope, ApiHandle::fromPyObject(name)->asObject()); name_obj = attributeNameNoException(thread, name_obj); if (name_obj.isErrorError()) return nullptr; Object res(&scope, typeLookupInMro(thread, *type_obj, *name_obj)); if (res.isErrorNotFound()) { return nullptr; } return ApiHandle::borrowedReference(thread->runtime(), *res); } uword typeGetBasicSize(const Type& type) { if (typeHasSlots(type)) { return typeSlotUWordAt(type, kSlotBasicSize); } DCHECK(!type.hasNativeData(), "Types with native data should have slots"); // Return just the size needed for a pure `PyObject`/`PyObject_HEAD`. return sizeof(PyObject); } uword typeGetFlags(const Type& type) { if (typeHasSlots(type)) { return typeSlotUWordAt(type, kSlotFlags); } uword result = Py_TPFLAGS_READY | Py_TPFLAGS_DEFAULT; Type::Flag internal_flags = type.flags(); if (internal_flags & Type::Flag::kHasCycleGC) { result |= Py_TPFLAGS_HAVE_GC; } if (internal_flags & Type::Flag::kIsAbstract) { result |= Py_TPFLAGS_IS_ABSTRACT; } if (internal_flags & Type::Flag::kIsCPythonHeaptype) { result |= Py_TPFLAGS_HEAPTYPE; } if (internal_flags & Type::Flag::kIsBasetype) { result |= Py_TPFLAGS_BASETYPE; } switch (type.builtinBase()) { case LayoutId::kInt: result |= Py_TPFLAGS_LONG_SUBCLASS; break; case LayoutId::kList: result |= Py_TPFLAGS_LIST_SUBCLASS; break; case LayoutId::kTuple: result |= Py_TPFLAGS_TUPLE_SUBCLASS; break; case LayoutId::kBytes: result |= Py_TPFLAGS_BYTES_SUBCLASS; break; case LayoutId::kStr: result |= Py_TPFLAGS_UNICODE_SUBCLASS; break; case LayoutId::kDict: result |= Py_TPFLAGS_DICT_SUBCLASS; break; // BaseException is handled separately down below case LayoutId::kType: result |= Py_TPFLAGS_TYPE_SUBCLASS; break; default: if (type.isBaseExceptionSubclass()) { result |= Py_TPFLAGS_BASE_EXC_SUBCLASS; } break; } return result; } } // namespace py