runtime/type-builtins-test.cpp (1,404 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "type-builtins.h" #include "cpython-types.h" #include "gtest/gtest.h" #include "attributedict.h" #include "builtins.h" #include "dict-builtins.h" #include "handles.h" #include "ic.h" #include "module-builtins.h" #include "objects.h" #include "runtime.h" #include "str-builtins.h" #include "test-utils.h" namespace py { namespace testing { using TypeBuiltinsDeathTest = RuntimeFixture; using TypeBuiltinsTest = RuntimeFixture; TEST_F(TypeBuiltinsTest, TypeAtReturnsNoPlaceholderValue) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); Object name(&scope, Runtime::internStrFromCStr(thread_, "__ne__")); Object value(&scope, Runtime::internStrFromCStr(thread_, "__ne__'s value")); typeAtPut(thread_, type, name, value); EXPECT_EQ(typeAt(type, name), *value); EXPECT_EQ(typeAtById(thread_, type, ID(__ne__)), *value); } TEST_F(TypeBuiltinsTest, TypeAtReturnsErrorNotFoundForPlaceholder) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); Object name(&scope, Runtime::internStrFromCStr(thread_, "__ne__")); Object value(&scope, Runtime::internStrFromCStr(thread_, "__ne__'s value")); ValueCell value_cell(&scope, typeAtPut(thread_, type, name, value)); value_cell.makePlaceholder(); EXPECT_TRUE(typeAt(type, name).isErrorNotFound()); EXPECT_TRUE(typeAtById(thread_, type, ID(__ne__)).isErrorNotFound()); } TEST_F(TypeBuiltinsTest, TypeAtPutPutsValueInValueCell) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); Object name(&scope, Runtime::internStrFromCStr(thread_, "__ne__")); Object value(&scope, Runtime::internStrFromCStr(thread_, "__ne__'s value")); ValueCell result(&scope, typeAtPut(thread_, type, name, value)); ASSERT_EQ(result.value(), *value); EXPECT_EQ(typeAt(type, name), *value); result.setValue(NoneType::object()); result = typeAtPutById(thread_, type, ID(__ne__), value); ASSERT_EQ(result.value(), *value); EXPECT_EQ(typeAtById(thread_, type, ID(__ne__)), *value); } TEST_F(TypeBuiltinsTest, TypeAtPutByStrInvalidatesCache) { ASSERT_FALSE(runFromCStr(runtime_, R"( class A: def foo(self): return 4 def cache_a_foo(a): return a.foo a = A() cache_a_foo(a) )") .isError()); HandleScope scope(thread_); Function cache_a_foo(&scope, mainModuleAt(runtime_, "cache_a_foo")); MutableTuple caches(&scope, cache_a_foo.caches()); Object a(&scope, mainModuleAt(runtime_, "a")); ASSERT_FALSE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound()); Type type_a(&scope, mainModuleAt(runtime_, "A")); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object none(&scope, NoneType::object()); typeAtPut(thread_, type_a, foo, none); EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound()); } TEST_F(TypeBuiltinsTest, TypeAtPutByIdInvalidatesCache) { ASSERT_FALSE(runFromCStr(runtime_, R"( class A: def __eq__(self, other): return True def cache_a_eq(a): return a.__eq__ a = A() cache_a_eq(a) )") .isError()); HandleScope scope(thread_); Function cache_a_eq(&scope, mainModuleAt(runtime_, "cache_a_eq")); MutableTuple caches(&scope, cache_a_eq.caches()); Object a(&scope, mainModuleAt(runtime_, "a")); ASSERT_FALSE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound()); Type type_a(&scope, mainModuleAt(runtime_, "A")); Object none(&scope, NoneType::object()); typeAtPutById(thread_, type_a, ID(__eq__), none); EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound()); } TEST_F(TypeBuiltinsTest, TypeAtPutDoesNotGrowOnTombstones) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass )") .isError()); HandleScope scope(thread_); Type type(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(type.attributes().isTuple()); word initial_capacity = Tuple::cast(type.attributes()).length(); // Insert and remove symbols to fill the dictionary with tombstones. Object name(&scope, NoneType::object()); for (word i = 0; i < static_cast<word>(SymbolId::kMaxId); i++) { name = runtime_->symbols()->at(static_cast<SymbolId>(i)); attributeValueCellAtPut(thread_, type, name); typeRemove(thread_, type, name); } EXPECT_EQ(Tuple::cast(type.attributes()).length(), initial_capacity); } TEST_F(TypeBuiltinsTest, TypeRemoveForNonExistingEntryReturnsErrorNotFound) { ASSERT_FALSE(runFromCStr(runtime_, R"( class A: def __eq__(self, other): return True )") .isError()); HandleScope scope(thread_); Type type(&scope, mainModuleAt(runtime_, "A")); Str dunder_gt(&scope, runtime_->newStrFromCStr("__gt__")); EXPECT_TRUE(typeRemove(thread_, type, dunder_gt).isErrorNotFound()); } TEST_F(TypeBuiltinsTest, TypeRemoveRemovesAssociatedEntry) { ASSERT_FALSE(runFromCStr(runtime_, R"( class A: def __eq__(self, other): return True )") .isError()); HandleScope scope(thread_); Type type(&scope, mainModuleAt(runtime_, "A")); Object dunder_eq(&scope, Runtime::internStrFromCStr(thread_, "__eq__")); ASSERT_FALSE(typeAt(type, dunder_eq).isErrorNotFound()); ASSERT_FALSE(typeRemove(thread_, type, dunder_eq).isErrorNotFound()); EXPECT_TRUE(typeAt(type, dunder_eq).isErrorNotFound()); } TEST_F(TypeBuiltinsTest, TypeRemoveInvalidatesCache) { ASSERT_FALSE(runFromCStr(runtime_, R"( class A: def __eq__(self, other): return True def cache_a_eq(a): return a.__eq__ a = A() cache_a_eq(a) )") .isError()); HandleScope scope(thread_); Function cache_a_eq(&scope, mainModuleAt(runtime_, "cache_a_eq")); MutableTuple caches(&scope, cache_a_eq.caches()); Object a(&scope, mainModuleAt(runtime_, "a")); ASSERT_FALSE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound()); Type type_a(&scope, mainModuleAt(runtime_, "A")); Str dunder_eq(&scope, runtime_->newStrFromCStr("__eq__")); ASSERT_FALSE(typeRemove(thread_, type_a, dunder_eq).isErrorNotFound()); EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound()); } TEST_F(TypeBuiltinsTest, TypeKeysFiltersOutPlaceholders) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz")); Str value(&scope, runtime_->newStrFromCStr("value")); typeAtPut(thread_, type, foo, value); typeAtPut(thread_, type, bar, value); typeAtPut(thread_, type, baz, value); ValueCell::cast(typeValueCellAt(*type, *bar)).makePlaceholder(); List keys(&scope, typeKeys(thread_, type)); EXPECT_EQ(keys.numItems(), 2); EXPECT_EQ(keys.at(0), *foo); EXPECT_EQ(keys.at(1), *baz); } TEST_F(TypeBuiltinsTest, TypeLenReturnsItemCountExcludingPlaceholders) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz")); Str value(&scope, runtime_->newStrFromCStr("value")); typeAtPut(thread_, type, foo, value); typeAtPut(thread_, type, bar, value); typeAtPut(thread_, type, baz, value); word previous_len = typeLen(thread_, type); ValueCell::cast(typeValueCellAt(*type, *bar)).makePlaceholder(); word after_len = typeLen(thread_, type); EXPECT_EQ(previous_len, after_len + 1); } TEST_F(TypeBuiltinsTest, TypeValuesFiltersOutPlaceholders) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object foo_value(&scope, runtime_->newStrFromCStr("foo_value")); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object bar_value(&scope, runtime_->newStrFromCStr("bar_value")); Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz")); Object baz_value(&scope, runtime_->newStrFromCStr("baz_value")); typeAtPut(thread_, type, foo, foo_value); typeAtPut(thread_, type, bar, bar_value); typeAtPut(thread_, type, baz, baz_value); ValueCell::cast(typeValueCellAt(*type, *bar)).makePlaceholder(); List values(&scope, typeValues(thread_, type)); EXPECT_TRUE(listContains(values, foo_value)); EXPECT_FALSE(listContains(values, bar_value)); EXPECT_TRUE(listContains(values, baz_value)); } TEST_F(TypeBuiltinsTest, DunderBasesReturnsTuple) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: pass class B: pass class C(A, B): pass )") .isError()); Object a(&scope, mainModuleAt(runtime_, "A")); Object b(&scope, mainModuleAt(runtime_, "B")); Object c(&scope, mainModuleAt(runtime_, "C")); Object result_obj(&scope, runtime_->attributeAtById(thread_, c, ID(__bases__))); ASSERT_TRUE(result_obj.isTuple()); Tuple result(&scope, *result_obj); ASSERT_EQ(result.length(), 2); EXPECT_EQ(result.at(0), a); EXPECT_EQ(result.at(1), b); } TEST_F(TypeBuiltinsTest, DunderBasesOnObjectReturnsEmptyTuple) { HandleScope scope(thread_); Object type(&scope, runtime_->typeAt(LayoutId::kObject)); Object result_obj(&scope, runtime_->attributeAtById(thread_, type, ID(__bases__))); ASSERT_TRUE(result_obj.isTuple()); EXPECT_EQ(Tuple::cast(*result_obj).length(), 0); } TEST_F(TypeBuiltinsTest, DunderBasesOnBuiltinTypeReturnsTuple) { HandleScope scope(thread_); Object type(&scope, runtime_->typeAt(LayoutId::kInt)); Object result_obj(&scope, runtime_->attributeAtById(thread_, type, ID(__bases__))); ASSERT_TRUE(result_obj.isTuple()); Tuple result(&scope, *result_obj); ASSERT_EQ(result.length(), 1); EXPECT_EQ(result.at(0), runtime_->typeAt(LayoutId::kObject)); } TEST_F(TypeBuiltinsTest, DunderCallType) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass c = C() )") .isError()); Type type(&scope, mainModuleAt(runtime_, "C")); ASSERT_FALSE(type.isError()); Object instance(&scope, mainModuleAt(runtime_, "c")); ASSERT_FALSE(instance.isError()); Object instance_type(&scope, runtime_->typeOf(*instance)); ASSERT_FALSE(instance_type.isError()); EXPECT_EQ(*type, *instance_type); } TEST_F(TypeBuiltinsTest, DunderCallTypeWithInit) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __init__(self): global g g = 2 g = 1 C() )") .isError()); Object global(&scope, mainModuleAt(runtime_, "g")); ASSERT_FALSE(global.isError()); EXPECT_TRUE(isIntEqualsWord(*global, 2)); } TEST_F(TypeBuiltinsTest, DunderCallTypeWithInitAndArgs) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __init__(self, x): global g g = x g = 1 C(9) )") .isError()); Object global(&scope, mainModuleAt(runtime_, "g")); ASSERT_FALSE(global.isError()); EXPECT_TRUE(isIntEqualsWord(*global, 9)); } TEST_F(TypeBuiltinsTest, DunderCallWithNonTypeDudnerNewResultReturnsWithoutCallingDunderInit) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __new__(self, *args): return 17 def __init__(self, *args): raise Exception("should not happen") result = type.__call__(C, "C", (), {}) )") .isError()); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(isIntEqualsWord(*result, 17)); } TEST_F(TypeBuiltinsTest, DunderDirReturnsList) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: x = 42 def foo(): pass class B(A): def bar(): pass dir = type.__dir__(B) )") .isError()); Object dir(&scope, mainModuleAt(runtime_, "dir")); Object x(&scope, Runtime::internStrFromCStr(thread_, "x")); EXPECT_TRUE(listContains(dir, x)); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(listContains(dir, foo)); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); EXPECT_TRUE(listContains(dir, bar)); } TEST_F(TypeBuiltinsTest, DunderDocOnEmptyTypeReturnsNone) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError()); Object c(&scope, mainModuleAt(runtime_, "C")); Object doc(&scope, runtime_->attributeAtById(thread_, c, ID(__doc__))); EXPECT_EQ(doc, NoneType::object()); } TEST_F(TypeBuiltinsTest, DunderDocReturnsDocumentationString) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: """hello documentation""" pass )") .isError()); Object c(&scope, mainModuleAt(runtime_, "C")); Object doc(&scope, runtime_->attributeAtById(thread_, c, ID(__doc__))); EXPECT_TRUE(isStrEqualsCStr(*doc, "hello documentation")); } TEST_F(TypeBuiltinsTest, DunderGetattributeReturnsAttribute) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: foo = -13 )") .isError()); Object c(&scope, mainModuleAt(runtime_, "C")); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE( isIntEqualsWord(runBuiltin(METH(type, __getattribute__), c, name), -13)); } TEST_F(TypeBuiltinsTest, DunderGetattributeWithNonStringNameRaisesTypeError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass )") .isError()); Object c(&scope, mainModuleAt(runtime_, "C")); Object name(&scope, runtime_->newInt(0)); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(type, __getattribute__), c, name), LayoutId::kTypeError, "attribute name must be string, not 'int'")); } TEST_F(TypeBuiltinsTest, DunderGetattributeWithMissingAttributeRaisesAttributeError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass )") .isError()); Object c(&scope, mainModuleAt(runtime_, "C")); Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx")); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(type, __getattribute__), c, name), LayoutId::kAttributeError, "type object 'C' has no attribute 'xxx'")); } TEST_F(TypeBuiltinsTest, DunderReprForBuiltinReturnsStr) { ASSERT_FALSE( runFromCStr(runtime_, "result = type.__repr__(object)").isError()); EXPECT_TRUE( isStrEqualsCStr(mainModuleAt(runtime_, "result"), "<class 'object'>")); } TEST_F(TypeBuiltinsTest, DunderReprForUserDefinedTypeReturnsStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: pass result = type.__repr__(Foo) )") .isError()); EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "<class '__main__.Foo'>")); } TEST_F(TypeBuiltinsTest, DunderNewWithOneArgReturnsTypeOfArg) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( a = type.__new__(type, 1); b = type.__new__(type, "hello"); )") .isError()); Type a(&scope, mainModuleAt(runtime_, "a")); Type b(&scope, mainModuleAt(runtime_, "b")); EXPECT_EQ(a.instanceLayoutId(), LayoutId::kInt); EXPECT_EQ(b.instanceLayoutId(), LayoutId::kStr); } TEST_F(TypeBuiltinsTest, DunderNewWithOneMetaclassArgReturnsType) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo(type): pass a = type.__new__(type, Foo); )") .isError()); Type a(&scope, mainModuleAt(runtime_, "a")); EXPECT_EQ(a.instanceLayoutId(), LayoutId::kType); } TEST_F(TypeBuiltinsTest, DunderSetattrSetsAttribute) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError()); Object c_obj(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(c_obj.isType()); Type c(&scope, *c_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object value(&scope, runtime_->newInt(-7331)); EXPECT_TRUE(runBuiltin(METH(type, __setattr__), c, name, value).isNoneType()); EXPECT_TRUE(isIntEqualsWord(typeAt(c, name), -7331)); } TEST_F(TypeBuiltinsTest, DunderSetattrWithNonStrNameRaisesTypeError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError()); Object c_obj(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(c_obj.isType()); Type c(&scope, *c_obj); Object name(&scope, NoneType::object()); Object value(&scope, runtime_->newInt(1)); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(type, __setattr__), c, name, value), LayoutId::kTypeError, "attribute name must be string, not 'NoneType'")); } TEST_F(TypeBuiltinsTest, DunderSlotsCreatesLayoutWithInObjectAttributes) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = "x", "y", "z" )") .isError()); HandleScope scope(thread_); Type c(&scope, mainModuleAt(runtime_, "C")); EXPECT_TRUE(c.hasFlag(Type::Flag::kHasSlots)); Layout layout(&scope, c.instanceLayout()); EXPECT_TRUE(layout.isSealed()); Tuple attributes(&scope, layout.inObjectAttributes()); ASSERT_EQ(attributes.length(), 3); Tuple elt0(&scope, attributes.at(0)); EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x")); AttributeInfo info0(elt0.at(1)); EXPECT_TRUE(info0.isInObject()); EXPECT_TRUE(info0.isFixedOffset()); Tuple elt1(&scope, attributes.at(1)); EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y")); AttributeInfo info1(elt1.at(1)); EXPECT_TRUE(info1.isInObject()); EXPECT_TRUE(info1.isFixedOffset()); Tuple elt2(&scope, attributes.at(2)); EXPECT_TRUE(isStrEqualsCStr(elt2.at(0), "z")); AttributeInfo info2(elt2.at(1)); EXPECT_TRUE(info2.isInObject()); EXPECT_TRUE(info2.isFixedOffset()); } TEST_F(TypeBuiltinsTest, DunderSlotsWithEmptyTupleCreatesEmptyLayout) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = () )") .isError()); HandleScope scope(thread_); Type c(&scope, mainModuleAt(runtime_, "C")); EXPECT_FALSE(c.hasFlag(Type::Flag::kHasSlots)); Layout layout(&scope, c.instanceLayout()); EXPECT_TRUE(layout.isSealed()); Tuple attributes(&scope, layout.inObjectAttributes()); ASSERT_EQ(attributes.length(), 0); } TEST_F(TypeBuiltinsTest, DunderSlotsAreInheritedFromLayoutBase) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass class D(C): __slots__ = "x", "y" class E(D, C): __slots__ = "z" )") .isError()); HandleScope scope(thread_); Type e(&scope, mainModuleAt(runtime_, "E")); Layout layout(&scope, e.instanceLayout()); // The layout of E is not sealed due to C. EXPECT_FALSE(layout.isSealed()); Tuple attributes(&scope, layout.inObjectAttributes()); // D is chosen as the layout base for E since D is a subtype of C. ASSERT_EQ(attributes.length(), 3); Tuple elt0(&scope, attributes.at(0)); EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x")); AttributeInfo info0(elt0.at(1)); EXPECT_TRUE(info0.isInObject()); EXPECT_TRUE(info0.isFixedOffset()); Tuple elt1(&scope, attributes.at(1)); EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y")); AttributeInfo info1(elt1.at(1)); EXPECT_TRUE(info1.isInObject()); EXPECT_TRUE(info1.isFixedOffset()); Tuple elt2(&scope, attributes.at(2)); EXPECT_TRUE(isStrEqualsCStr(elt2.at(0), "z")); AttributeInfo info2(elt2.at(1)); EXPECT_TRUE(info2.isInObject()); EXPECT_TRUE(info2.isFixedOffset()); } TEST_F(TypeBuiltinsTest, DunderSlotsSealsTypeWhenAllBasesAreSealed) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = () class D: __slots__ = () class E(C, D): __slots__ = "x" )") .isError()); HandleScope scope(thread_); Type e(&scope, mainModuleAt(runtime_, "E")); Layout layout(&scope, e.instanceLayout()); EXPECT_TRUE(layout.isSealed()); } TEST_F(TypeBuiltinsTest, DunderSlotsDoesNotSealTypeWhenBaseHasDunderDict) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass class D: __slots__ = () class E(C, D): __slots__ = "x" )") .isError()); HandleScope scope(thread_); Type c(&scope, mainModuleAt(runtime_, "C")); ASSERT_FALSE(Layout::cast(c.instanceLayout()).isSealed()); Type e(&scope, mainModuleAt(runtime_, "E")); Layout layout(&scope, e.instanceLayout()); EXPECT_FALSE(layout.isSealed()); } TEST_F(TypeBuiltinsTest, DunderSlotsWithNonObjectBuiltinBaseAddsInObjectAttributes) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(dict): __slots__ = "x" )") .isError()); HandleScope scope(thread_); Layout dict_layout(&scope, runtime_->layoutAt(LayoutId::kDict)); ASSERT_TRUE(dict_layout.isSealed()); Tuple dict_attributes(&scope, dict_layout.inObjectAttributes()); ASSERT_EQ(dict_attributes.length(), 4); Tuple dict_elt3(&scope, dict_attributes.at(3)); AttributeInfo dict_elt3_info(dict_elt3.at(1)); EXPECT_TRUE(dict_elt3_info.isInObject()); EXPECT_TRUE(dict_elt3_info.isFixedOffset()); Type c(&scope, mainModuleAt(runtime_, "C")); Layout layout(&scope, c.instanceLayout()); // C's layout is sealed since it has only a sealed base. EXPECT_TRUE(layout.isSealed()); Tuple attributes(&scope, layout.inObjectAttributes()); ASSERT_EQ(attributes.length(), 5); Tuple elt0(&scope, attributes.at(4)); EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x")); AttributeInfo info0(elt0.at(1)); EXPECT_TRUE(info0.isInObject()); EXPECT_TRUE(info0.isFixedOffset()); EXPECT_EQ(info0.offset(), dict_elt3_info.offset() + kPointerSize); } TEST_F(TypeBuiltinsTest, DunderSlotsWithConflictingLayoutBasesOfUserTypeRaisesTypeError) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = "x" class D: __slots__ = "y" )") .isError()); ASSERT_TRUE(raisedWithStr(runFromCStr(runtime_, "class E(C, D): pass"), LayoutId::kTypeError, "multiple bases have instance lay-out conflict")); } TEST_F(TypeBuiltinsTest, DunderSlotsWithConflictingLayoutBasesOfBuiltinTypeRaisesTypeError) { // Confliction between a builtin type and a user-defined type. ASSERT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( class C: __slots__ = "x" # This is conflicting with dict's in-object attributes. class D(C, dict): pass )"), LayoutId::kTypeError, "multiple bases have instance lay-out conflict")); } TEST_F(TypeBuiltinsTest, DunderSlotsWithEmptyTupleDoesNotConflictWithOtherDunderSlots) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = () class D: __slots__ = "x" class E(C, D): pass )") .isError()); HandleScope scope(thread_); Type e(&scope, mainModuleAt(runtime_, "E")); Layout layout(&scope, e.instanceLayout()); Tuple attributes(&scope, layout.inObjectAttributes()); ASSERT_EQ(attributes.length(), 1); Tuple elt0(&scope, attributes.at(0)); EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x")); AttributeInfo info0(elt0.at(1)); EXPECT_TRUE(info0.isInObject()); EXPECT_TRUE(info0.isFixedOffset()); } TEST_F(TypeBuiltinsTest, DunderSlotsSharingSameLayoutBaseCanServceAsBases) { // Although F's bases, D, E do not appear in the same type hierarchy // (neither D is a subtype of E nor E is a subtype of D), but // D's layout base (C) is the supertype of E, which makes the type checking // succeed. ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = "x" class D(C): pass class E(C): __slots__ = "y" class F(D, E): pass )") .isError()); HandleScope scope(thread_); Type f(&scope, mainModuleAt(runtime_, "F")); Layout layout(&scope, f.instanceLayout()); Tuple attributes(&scope, layout.inObjectAttributes()); ASSERT_EQ(attributes.length(), 2); Tuple elt0(&scope, attributes.at(0)); EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x")); AttributeInfo info0(elt0.at(1)); EXPECT_TRUE(info0.isInObject()); EXPECT_TRUE(info0.isFixedOffset()); Tuple elt1(&scope, attributes.at(1)); EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y")); AttributeInfo info1(elt1.at(1)); EXPECT_TRUE(info1.isInObject()); EXPECT_TRUE(info1.isFixedOffset()); } TEST_F(TypeBuiltinsTest, DunderSlotsPopulatesSlotDescriptorsWithCorrectValues) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: __slots__ = "x" class D(C): __slots__ = "y" )") .isError()); HandleScope scope(thread_); Type c(&scope, mainModuleAt(runtime_, "C")); Layout c_layout(&scope, c.instanceLayout()); // Checking descriptor for "x" in C. { Str x(&scope, runtime_->newStrFromCStr("x")); SlotDescriptor descriptor_x(&scope, typeAt(c, x)); EXPECT_EQ(descriptor_x.type(), *c); EXPECT_TRUE(isStrEqualsCStr(descriptor_x.name(), "x")); AttributeInfo info; ASSERT_TRUE(Runtime::layoutFindAttribute(*c_layout, x, &info)); EXPECT_EQ(descriptor_x.offset(), info.offset()); } Type d(&scope, mainModuleAt(runtime_, "D")); Layout d_layout(&scope, d.instanceLayout()); // Checking descriptors for "x" and "y" in D. { // "x" is inherited from C to D. Str x(&scope, runtime_->newStrFromCStr("x")); ASSERT_TRUE(typeAt(d, x).isErrorNotFound()); SlotDescriptor descriptor_x(&scope, typeLookupInMro(thread_, *d, *x)); EXPECT_EQ(descriptor_x.type(), *c); EXPECT_TRUE(isStrEqualsCStr(descriptor_x.name(), "x")); AttributeInfo info; ASSERT_TRUE(Runtime::layoutFindAttribute(*d_layout, x, &info)); EXPECT_EQ(descriptor_x.offset(), info.offset()); // "y" is populated in D itself. Str y(&scope, runtime_->newStrFromCStr("y")); SlotDescriptor descriptor_y(&scope, typeAt(d, y)); EXPECT_EQ(descriptor_y.type(), *d); EXPECT_TRUE(isStrEqualsCStr(descriptor_y.name(), "y")); ASSERT_TRUE(Runtime::layoutFindAttribute(*d_layout, y, &info)); EXPECT_EQ(descriptor_y.offset(), info.offset()); } } static RawObject newExtensionType(PyObject* extension_type) { Thread* thread = Thread::current(); Runtime* runtime = thread->runtime(); HandleScope scope(thread); Str name(&scope, runtime->newStrFromCStr("ExtType")); Object object_type(&scope, runtime->typeAt(LayoutId::kObject)); Tuple bases(&scope, runtime->newTupleWith1(object_type)); Dict dict(&scope, runtime->newDict()); Type metaclass(&scope, runtime->typeAt(LayoutId::kType)); Type type(&scope, typeNew(thread, metaclass, name, bases, dict, Type::Flag::kHasNativeData | Type::Flag::kIsBasetype, /*inherit_slots=*/false, /*add_instance_dict=*/false)); extension_type->reference_ = type.raw(); return *type; } TEST_F(TypeBuiltinsTest, DunderSlotsWithExtensionTypeAsBaseAllocatesExtraSpace) { // Create a main module. ASSERT_FALSE(runFromCStr(runtime_, "").isError()); HandleScope scope(thread_); PyObject extension_type; Type type(&scope, newExtensionType(&extension_type)); ASSERT_TRUE(type.hasFlag(Type::Flag::kHasNativeData)); Module main_module(&scope, findMainModule(runtime_)); Str type_name(&scope, runtime_->newStrFromCStr("ExtType")); moduleAtPut(thread_, main_module, type_name, type); ASSERT_FALSE(runFromCStr(runtime_, R"( class C(ExtType): __slots__ = "x", "y" )") .isError()); Type c(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(c.hasFlag(Type::Flag::kHasNativeData)); Layout layout(&scope, c.instanceLayout()); Tuple attributes(&scope, layout.inObjectAttributes()); // "x" and "y" are added as a regular attribute. ASSERT_EQ(attributes.length(), 2); // However, more speace was allocated to be a native proxy. EXPECT_EQ(layout.instanceSize(), 2 * kPointerSize + NativeProxy::kSizeFromEnd); Tuple elt0(&scope, attributes.at(0)); EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x")); AttributeInfo info0(elt0.at(1)); EXPECT_TRUE(info0.isInObject()); EXPECT_TRUE(info0.isFixedOffset()); Tuple elt1(&scope, attributes.at(1)); EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y")); AttributeInfo info1(elt1.at(1)); EXPECT_TRUE(info1.isInObject()); EXPECT_TRUE(info1.isFixedOffset()); } TEST_F(TypeBuiltinsTest, TypeHasDunderMroAttribute) { HandleScope scope(thread_); ASSERT_FALSE( runFromCStr(runtime_, "result = str.__class__.__mro__").isError()); Object result(&scope, mainModuleAt(runtime_, "result")); ASSERT_TRUE(result.isTuple()); } TEST_F(TypeBuiltinsTest, TypeHasDunderNameAttribute) { HandleScope scope(thread_); ASSERT_FALSE( runFromCStr(runtime_, "result = str.__class__.__name__").isError()); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(isStrEqualsCStr(*result, "type")); } TEST_F(TypeBuiltinsTest, TypeHasDunderDictAttribute) { HandleScope scope(thread_); ASSERT_FALSE( runFromCStr(runtime_, "result = str.__class__.__dict__").isError()); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(result.isMappingProxy()); } TEST_F(TypeBuiltinsTest, TypeLookupNameInMroReturnsValue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: foo = 2 )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(typeLookupInMro(thread_, *a, *foo), 2)); } TEST_F(TypeBuiltinsTest, TypeLookupNameInMroReturnsParentValue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: foo = 2 class B(A): bar = 4 )") .isError()); Object b_obj(&scope, mainModuleAt(runtime_, "B")); ASSERT_TRUE(b_obj.isType()); Type b(&scope, *b_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(typeLookupInMro(thread_, *b, *name), 2)); } TEST_F(TypeBuiltinsTest, TypeLookupNameInMroReturnsOverriddenValue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: foo = 2 class B(A): foo = 4 )") .isError()); Object b_obj(&scope, mainModuleAt(runtime_, "B")); ASSERT_TRUE(b_obj.isType()); Type b(&scope, *b_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(typeLookupInMro(thread_, *b, *name), 4)); } TEST_F(TypeBuiltinsTest, TypeLookupNameInMroWithNonExistentNameReturnsError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: bar = 2 )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(typeLookupInMro(thread_, *a, *name).isError()); EXPECT_FALSE(thread_->hasPendingException()); } TEST_F(TypeBuiltinsTest, TypeLookupSymbolInMroReturnsValue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: __add__ = 3 )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); EXPECT_TRUE( isIntEqualsWord(typeLookupInMroById(thread_, *a, ID(__add__)), 3)); } TEST_F(TypeBuiltinsTest, DunderCallReceivesExArgs) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __init__(self, *args): self.args = args def num_args(self): return len(self.args) result = C(*(1,2,3)).num_args() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(*result, RawSmallInt::fromWord(3)); } TEST_F(TypeBuiltinsTest, TypeNewWithNonStrKeyInDictRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( ty = type.__new__(type, *("foo", (object,), {4: 1})) name = ty.__name__N )"), LayoutId::kTypeError, "attribute name must be string, not 'int'")); } TEST_F(TypeBuiltinsTest, ClassMethodDunderCallReceivesExArgs) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: @classmethod def foo(cls, *args): return len(args) result = Foo.foo(*(1,2,3)) )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_EQ(*result, RawSmallInt::fromWord(3)); } TEST_F(TypeBuiltinsTest, TypeNewReceivesExArgs) { ASSERT_FALSE(runFromCStr(runtime_, R"( ty = type.__new__(type, *("foo", (object,), {'a': 1})) name = ty.__name__ )") .isError()); HandleScope scope(thread_); Object name(&scope, mainModuleAt(runtime_, "name")); EXPECT_TRUE(isStrEqualsCStr(*name, "foo")); } TEST_F(TypeBuiltinsTest, TypeCallWithInitReturningNonNoneRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( class C: def __init__(self, *args, **kwargs): return 5 C() )"), LayoutId::kTypeError, "C.__init__ returned non None")); } TEST_F(TypeBuiltinsTest, TypeGetAttributeReturnsAttributeValue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: x = 42 )") .isError()); Object c_obj(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj)); Type c(&scope, *c_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "x")); EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, c, name), 42)); } TEST_F(TypeBuiltinsTest, TypeGetAttributeReturnsMetaclassAttributeValue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class M(type): x = 77 class C(metaclass=M): pass )") .isError()); Object c_obj(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj)); Type c(&scope, *c_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "x")); EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, c, name), 77)); } TEST_F(TypeBuiltinsTest, TypeGetAttributeWithMissingAttributeReturnsError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError()); Object c_obj(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj)); Type c(&scope, *c_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx")); EXPECT_TRUE(typeGetAttribute(thread_, c, name).isError()); EXPECT_FALSE(thread_->hasPendingException()); } TEST_F(TypeBuiltinsTest, TypeGetAttributeCallsDunderGetOnDataDescriptor) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __set__(self, instance, value): pass def __get__(self, instance, owner): return (self, instance, owner) class M(type): foo = D() class A(metaclass=M): pass )") .isError()); Object d_obj(&scope, mainModuleAt(runtime_, "D")); ASSERT_TRUE(d_obj.isType()); Type d(&scope, *d_obj); Object m(&scope, mainModuleAt(runtime_, "M")); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object result_obj(&scope, typeGetAttribute(thread_, a, name)); ASSERT_TRUE(result_obj.isTuple()); Tuple result(&scope, *result_obj); ASSERT_EQ(result.length(), 3); Type result_0_type(&scope, runtime_->typeOf(result.at(0))); EXPECT_TRUE(typeIsSubclass(*result_0_type, *d)); EXPECT_EQ(result.at(1), a); EXPECT_EQ(result.at(2), m); } TEST_F(TypeBuiltinsTest, TypeGetAttributeCallsDunderGetOnNonDataDescriptor) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __get__(self, instance, owner): return 42 class M(type): foo = D() class A(metaclass=M): pass )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, a, name), 42)); } TEST_F(TypeBuiltinsTest, TypeGetAttributePrefersDataDescriptorOverTypeAttr) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __set__(self, instance, value): pass def __get__(self, instance, owner): return 42 class M(type): foo = D() class A(metaclass=M): foo = 12 )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, a, name), 42)); } TEST_F(TypeBuiltinsTest, TypeGetAttributePrefersFieldOverNonDataDescriptor) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __get__(self, instance, owner): return 42 class M(type): foo = D() class A(metaclass=M): foo = 12 )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, a, name), 12)); } TEST_F(TypeBuiltinsTest, TypeGetAttributePropagatesDunderGetException) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __set__(self, instance, value): pass def __get__(self, instance, owner): raise UserWarning() class M(type): foo = D() class A(metaclass=M): pass )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE( raised(typeGetAttribute(thread_, a, name), LayoutId::kUserWarning)); } TEST_F(TypeBuiltinsTest, TypeGetAttributeOnNoneTypeReturnsFunction) { HandleScope scope(thread_); Type none_type(&scope, runtime_->typeAt(LayoutId::kNoneType)); Object name(&scope, Runtime::internStrFromCStr(thread_, "__repr__")); EXPECT_TRUE(typeGetAttribute(thread_, none_type, name).isFunction()); } TEST_F(TypeBuiltinsTest, TypeSetAttrSetsAttribute) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError()); Object c_obj(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj)); Type c(&scope, *c_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foobarbaz")); Object value(&scope, runtime_->newInt(-444)); EXPECT_TRUE(typeSetAttr(thread_, c, name, value).isNoneType()); EXPECT_TRUE(isIntEqualsWord(typeAt(c, name), -444)); } TEST_F(TypeBuiltinsTest, TypeSetAttrCallsDunderSetOnDataDescriptor) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __get__(self, instance, owner): pass def __set__(self, instance, value): global set_args set_args = (self, instance, value) return "ignored result" foo = D() class M(type): foo = foo class A(metaclass=M): foo = "hidden by data descriptor" )") .isError()); Object foo(&scope, mainModuleAt(runtime_, "foo")); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object value(&scope, runtime_->newInt(77)); EXPECT_TRUE(typeSetAttr(thread_, a, name, value).isNoneType()); Object set_args_obj(&scope, mainModuleAt(runtime_, "set_args")); ASSERT_TRUE(set_args_obj.isTuple()); Tuple set_args(&scope, *set_args_obj); ASSERT_EQ(set_args.length(), 3); EXPECT_EQ(set_args.at(0), foo); EXPECT_EQ(set_args.at(1), a); EXPECT_TRUE(isIntEqualsWord(set_args.at(2), 77)); } TEST_F(TypeBuiltinsTest, TypeSetAttrPropagatesDunderSetException) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class D: def __get__(self, instance, owner): pass def __set__(self, instance, value): raise UserWarning() class M(type): foo = D() class A(metaclass=M): pass )") .isError()); Object a_obj(&scope, mainModuleAt(runtime_, "A")); ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj)); Type a(&scope, *a_obj); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object value(&scope, runtime_->newInt(1)); EXPECT_TRUE( raised(typeSetAttr(thread_, a, name, value), LayoutId::kUserWarning)); } TEST_F(TypeBuiltinsTest, TypeSetAttrOnBuiltinTypeRaisesTypeError) { HandleScope scope(thread_); Type type(&scope, runtime_->typeAt(LayoutId::kInt)); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object value(&scope, NoneType::object()); EXPECT_TRUE(raisedWithStr( typeSetAttr(thread_, type, name, value), LayoutId::kTypeError, "can't set attributes of built-in/extension type 'int'")); } TEST_F(TypeBuiltinsTest, TypeofSmallStrReturnsStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = type('a') )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kStr)); } TEST_F(TypeBuiltinsTest, TypeofLargeStrReturnsStr) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = type('aaaaaaaaaaaaaaaaaaaaaaa') )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kStr)); } TEST_F(TypeBuiltinsTest, TypeofSmallIntReturnsInt) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = type(5) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kInt)); } TEST_F(TypeBuiltinsTest, TypeofLargeIntReturnsInt) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = type(99999999999999999999999999999999999999999) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kInt)); } TEST_F(TypeBuiltinsTest, ResolveDescriptorGetReturnsNonDescriptor) { HandleScope scope(thread_); Object instance(&scope, runtime_->newInt(123)); Object owner(&scope, NoneType::object()); Object descr(&scope, runtime_->newInt(456)); EXPECT_EQ(resolveDescriptorGet(thread_, descr, instance, owner), *descr); } TEST_F(TypeBuiltinsTest, ResolveDescriptorGetCallsDescriptorDunderGet) { HandleScope scope(thread_); Object instance(&scope, runtime_->newInt(123)); Type owner(&scope, runtime_->typeOf(*instance)); Object descr(&scope, typeLookupInMroById(thread_, *owner, ID(__add__))); ASSERT_TRUE(descr.isFunction()); EXPECT_TRUE( resolveDescriptorGet(thread_, descr, instance, owner).isBoundMethod()); } TEST_F( TypeBuiltinsTest, TerminateIfUnimplementedTypeAttrCacheInvalidationDoesNotTerminateRuntimeForSupportedCacheInvalidation) { // __len__ supports cache invalidation. EXPECT_FALSE(runFromCStr(runtime_, R"( class C: def __len__(self): return 0 C.__len__ = lambda self: 4 )") .isError()); // __setattr__ does not support cache invalidation, but it is not populated in // C so we do not terminate the runtime. EXPECT_FALSE(runFromCStr(runtime_, R"( class C: pass C.__setattr__ = lambda self, key: 5 )") .isError()); } TEST_F( TypeBuiltinsDeathTest, TerminateIfUnimplementedTypeAttrCacheInvalidationTerminatesRuntimeForUnsupportedCacheInvalidation) { // Redefining the existing __setattr__ terminates the runtime due to the // unsupported cache invalidation for it. ASSERT_DEATH(static_cast<void>(runFromCStr(runtime_, R"( class C: def __setattr__(self, key): pass C.__setattr__ = lambda self, key: 5 )")), "unimplemented cache invalidation for type.__setattr__ update"); } TEST_F(TypeBuiltinsTest, TypeIsMarkedAsCustomDict) { HandleScope scope(thread_); Type type(&scope, runtime_->typeAt(LayoutId::kType)); EXPECT_TRUE(type.hasFlag(Type::Flag::kHasCustomDict)); EXPECT_TRUE(Layout::cast(type.instanceLayout()).isSealed()); } TEST_F(TypeBuiltinsTest, TypeSubclassLayoutIsSealed) { HandleScope scope(thread_); EXPECT_FALSE(runFromCStr(runtime_, R"( class Meta(type): pass )") .isError()); Type meta(&scope, mainModuleAt(runtime_, "Meta")); EXPECT_TRUE(meta.hasFlag(Type::Flag::kHasCustomDict)); EXPECT_TRUE(Layout::cast(meta.instanceLayout()).isSealed()); } TEST_F(TypeBuiltinsTest, BuiltinTypesHaveAppropriateAttributeTypeFlags) { HandleScope scope(thread_); Type object_type(&scope, runtime_->typeAt(LayoutId::kObject)); EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasStrDunderHash)); EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderBool)); EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderLen)); EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderClass)); EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderGet)); EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderSet)); EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderDelete)); EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderEq)); Type type_type(&scope, runtime_->typeAt(LayoutId::kType)); EXPECT_TRUE(type_type.hasFlag(Type::Flag::kHasTypeDunderGetattribute)); EXPECT_FALSE(type_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(type_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(type_type.hasFlag(Type::Flag::kHasObjectDunderEq)); Type module_type(&scope, runtime_->typeAt(LayoutId::kModule)); EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasModuleDunderGetattribute)); EXPECT_FALSE(module_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasObjectDunderEq)); Type property_type(&scope, runtime_->typeAt(LayoutId::kProperty)); EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(property_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasDunderGet)); EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasDunderSet)); EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasDunderDelete)); EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasObjectDunderEq)); Type function_type(&scope, runtime_->typeAt(LayoutId::kFunction)); EXPECT_TRUE(function_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(function_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(function_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(function_type.hasFlag(Type::Flag::kHasObjectDunderEq)); Type int_type(&scope, runtime_->typeAt(LayoutId::kInt)); EXPECT_TRUE(int_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasStrDunderHash)); EXPECT_TRUE(int_type.hasFlag(Type::Flag::kHasDunderBool)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderLen)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderGet)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderSet)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderDelete)); EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasObjectDunderEq)); Type str_type(&scope, runtime_->typeAt(LayoutId::kStr)); EXPECT_TRUE(str_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(str_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_FALSE(str_type.hasFlag(Type::Flag::kHasObjectDunderHash)); // TODO(T83275120): Flip the condition. EXPECT_TRUE(str_type.hasFlag(Type::Flag::kHasDunderBool)); EXPECT_TRUE(str_type.hasFlag(Type::Flag::kHasDunderLen)); EXPECT_FALSE(str_type.hasFlag(Type::Flag::kHasObjectDunderEq)); // super.__getattribute__ is not same as object.__getattribute. Type super_type(&scope, runtime_->typeAt(LayoutId::kSuper)); EXPECT_FALSE(super_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(super_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(super_type.hasFlag(Type::Flag::kHasObjectDunderHash)); // BaseException inherits object.__new__. Type base_exception_type(&scope, runtime_->typeAt(LayoutId::kBaseException)); EXPECT_TRUE( base_exception_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_TRUE(base_exception_type.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(base_exception_type.hasFlag(Type::Flag::kHasObjectDunderHash)); // NoneType.__class__'s behavior is same as object.__class__ although // they point to different objects. Type none_type(&scope, runtime_->typeAt(LayoutId::kNoneType)); EXPECT_TRUE(none_type.hasFlag(Type::Flag::kHasObjectDunderClass)); } TEST_F(TypeBuiltinsTest, UserTypesHaveAttributeTypeFlags) { HandleScope scope(thread_); EXPECT_FALSE(runFromCStr(runtime_, R"( class C: pass class D(type): pass class E(module): pass class F: def __new__(cls): return None class G: def __hash__(self): return 10 class H: def __bool__(self): return False class I: def __len__(self): return 10 class J: __class__ = None class K: def __get__(self, owner, type): return None class L: def __set__(self, owner, value): return None class M: def __delete__(self, obj): return None class N: def __eq__(self, other): return None class SubN(N): pass class Str(str): pass class Str2(Str): def __hash__(self): return 10 )") .isError()); Type c(&scope, mainModuleAt(runtime_, "C")); EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderClass)); EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderEq)); Type d(&scope, mainModuleAt(runtime_, "D")); EXPECT_TRUE(d.hasFlag(Type::Flag::kHasTypeDunderGetattribute)); EXPECT_FALSE(d.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(d.hasFlag(Type::Flag::kHasObjectDunderHash)); Type e(&scope, mainModuleAt(runtime_, "E")); EXPECT_TRUE(e.hasFlag(Type::Flag::kHasModuleDunderGetattribute)); EXPECT_FALSE(e.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(e.hasFlag(Type::Flag::kHasObjectDunderHash)); Type f(&scope, mainModuleAt(runtime_, "F")); EXPECT_TRUE(f.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(f.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_TRUE(f.hasFlag(Type::Flag::kHasObjectDunderHash)); Type g(&scope, mainModuleAt(runtime_, "G")); EXPECT_TRUE(g.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_TRUE(g.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_FALSE(g.hasFlag(Type::Flag::kHasObjectDunderHash)); Type h(&scope, mainModuleAt(runtime_, "H")); EXPECT_TRUE(h.hasFlag(Type::Flag::kHasDunderBool)); EXPECT_FALSE(h.hasFlag(Type::Flag::kHasDunderLen)); Type i(&scope, mainModuleAt(runtime_, "I")); EXPECT_FALSE(i.hasFlag(Type::Flag::kHasDunderBool)); EXPECT_TRUE(i.hasFlag(Type::Flag::kHasDunderLen)); Type j(&scope, mainModuleAt(runtime_, "J")); EXPECT_FALSE(j.hasFlag(Type::Flag::kHasObjectDunderClass)); Type k(&scope, mainModuleAt(runtime_, "K")); EXPECT_TRUE(k.hasFlag(Type::Flag::kHasDunderGet)); EXPECT_FALSE(k.hasFlag(Type::Flag::kHasDunderSet)); EXPECT_FALSE(k.hasFlag(Type::Flag::kHasDunderDelete)); EXPECT_FALSE(typeIsDataDescriptor(*k)); EXPECT_TRUE(typeIsNonDataDescriptor(*k)); Type l(&scope, mainModuleAt(runtime_, "L")); EXPECT_FALSE(l.hasFlag(Type::Flag::kHasDunderGet)); EXPECT_TRUE(l.hasFlag(Type::Flag::kHasDunderSet)); EXPECT_FALSE(l.hasFlag(Type::Flag::kHasDunderDelete)); EXPECT_TRUE(typeIsDataDescriptor(*l)); EXPECT_FALSE(typeIsNonDataDescriptor(*l)); Type m(&scope, mainModuleAt(runtime_, "M")); EXPECT_FALSE(m.hasFlag(Type::Flag::kHasDunderGet)); EXPECT_FALSE(m.hasFlag(Type::Flag::kHasDunderSet)); EXPECT_TRUE(m.hasFlag(Type::Flag::kHasDunderDelete)); EXPECT_TRUE(typeIsDataDescriptor(*m)); EXPECT_FALSE(typeIsNonDataDescriptor(*m)); Type n(&scope, mainModuleAt(runtime_, "N")); EXPECT_FALSE(n.hasFlag(Type::Flag::kHasObjectDunderEq)); Type sub_n(&scope, mainModuleAt(runtime_, "SubN")); EXPECT_FALSE(sub_n.hasFlag(Type::Flag::kHasObjectDunderEq)); Type str(&scope, mainModuleAt(runtime_, "Str")); EXPECT_TRUE(str.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(str.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_FALSE(str.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_TRUE(str.hasFlag(Type::Flag::kHasStrDunderHash)); Type str2(&scope, mainModuleAt(runtime_, "Str2")); EXPECT_TRUE(str2.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); EXPECT_FALSE(str2.hasFlag(Type::Flag::kHasObjectDunderNew)); EXPECT_FALSE(str2.hasFlag(Type::Flag::kHasObjectDunderHash)); EXPECT_FALSE(str2.hasFlag(Type::Flag::kHasStrDunderHash)); } TEST_F(TypeBuiltinsTest, AttributeTypeFlagsPropagateThroughTypeHierarchy) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class A: pass class B(A): def __getattribute__(self, name): return name class C(B): pass class X: pass class D(X, C): pass )") .isError()); Type a(&scope, mainModuleAt(runtime_, "A")); EXPECT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); Type b(&scope, mainModuleAt(runtime_, "B")); EXPECT_FALSE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); Type c(&scope, mainModuleAt(runtime_, "C")); EXPECT_FALSE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); Type d(&scope, mainModuleAt(runtime_, "D")); EXPECT_FALSE(d.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); } TEST_F(TypeBuiltinsTest, AttributeTypeFlagsForTypesWithMetaclassWithDefaultMro) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class MetaWithDefaultMro(type): def foo(self): return 500 class A(metaclass=MetaWithDefaultMro): pass class B(A): pass )") .isError()); Type a(&scope, mainModuleAt(runtime_, "A")); EXPECT_FALSE(a.hasFlag(Type::Flag::kHasCustomMro)); EXPECT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); Type b(&scope, mainModuleAt(runtime_, "B")); EXPECT_TRUE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); } TEST_F(TypeBuiltinsTest, AttributeTypeFlagsForTypesWithMetaclassWithCustomMro) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class MetaWithCustomMro(type): def mro(self): return (self,) class A(metaclass=MetaWithCustomMro): pass class B(A): pass )") .isError()); Type a(&scope, mainModuleAt(runtime_, "A")); EXPECT_TRUE(a.hasFlag(Type::Flag::kHasCustomMro)); EXPECT_FALSE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); Type b(&scope, mainModuleAt(runtime_, "B")); EXPECT_FALSE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute)); } } // namespace testing } // namespace py