this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "object-builtins.h"
3
4#include "gtest/gtest.h"
5
6#include "builtins.h"
7#include "frame.h"
8#include "ic.h"
9#include "runtime.h"
10#include "str-builtins.h"
11#include "test-utils.h"
12#include "type-builtins.h"
13
14namespace py {
15namespace testing {
16
17using NoneBuiltinsTest = RuntimeFixture;
18using ObjectBuiltinsTest = RuntimeFixture;
19
20TEST_F(ObjectBuiltinsTest, DunderEqWithIdenticalObjectsReturnsTrue) {
21 ASSERT_FALSE(runFromCStr(runtime_, R"(
22result = object.__eq__(None, None)
23)")
24 .isError());
25 HandleScope scope(thread_);
26 Object result(&scope, mainModuleAt(runtime_, "result"));
27 EXPECT_EQ(*result, Bool::trueObj());
28}
29
30TEST_F(ObjectBuiltinsTest,
31 DunderEqWithNonIdenticalObjectsReturnsNotImplemented) {
32 ASSERT_FALSE(runFromCStr(runtime_, R"(
33result = object.__eq__(object(), object())
34)")
35 .isError());
36 HandleScope scope(thread_);
37 Object result(&scope, mainModuleAt(runtime_, "result"));
38 EXPECT_TRUE(result.isNotImplementedType());
39}
40
41TEST_F(ObjectBuiltinsTest, DunderGetattributeReturnsAttribute) {
42 HandleScope scope(thread_);
43 ASSERT_FALSE(runFromCStr(runtime_, R"(
44class C: pass
45i = C()
46i.foo = 79
47)")
48 .isError());
49 Object i(&scope, mainModuleAt(runtime_, "i"));
50 Object name(&scope, runtime_->newStrFromCStr("foo"));
51 EXPECT_TRUE(
52 isIntEqualsWord(runBuiltin(METH(object, __getattribute__), i, name), 79));
53}
54
55TEST_F(ObjectBuiltinsTest, DunderGetattributeWithNonStringNameRaisesTypeError) {
56 HandleScope scope(thread_);
57 Object object(&scope, NoneType::object());
58 Object name(&scope, runtime_->newInt(0));
59 EXPECT_TRUE(raisedWithStr(
60 runBuiltin(METH(object, __getattribute__), object, name),
61 LayoutId::kTypeError, "attribute name must be string, not 'int'"));
62}
63
64TEST_F(ObjectBuiltinsTest,
65 DunderGetattributeWithMissingAttributeRaisesAttributeError) {
66 HandleScope scope(thread_);
67 Object object(&scope, NoneType::object());
68 Object name(&scope, runtime_->newStrFromCStr("xxx"));
69 EXPECT_TRUE(raisedWithStr(
70 runBuiltin(METH(object, __getattribute__), object, name),
71 LayoutId::kAttributeError, "'NoneType' object has no attribute 'xxx'"));
72}
73
74TEST_F(ObjectBuiltinsTest, DunderSetattrSetsValue) {
75 HandleScope scope(thread_);
76 ASSERT_FALSE(runFromCStr(runtime_, R"(
77class C: pass
78i = C()
79)")
80 .isError());
81 Object i(&scope, mainModuleAt(runtime_, "i"));
82 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
83 Object value(&scope, runtime_->newInt(42));
84 EXPECT_TRUE(
85 runBuiltin(METH(object, __setattr__), i, name, value).isNoneType());
86 ASSERT_TRUE(i.isInstance());
87 Instance i_instance(&scope, *i);
88 EXPECT_TRUE(
89 isIntEqualsWord(instanceGetAttribute(thread_, i_instance, name), 42));
90}
91
92TEST_F(ObjectBuiltinsTest, DunderSetattrWithNonStringNameRaisesTypeError) {
93 HandleScope scope(thread_);
94 Object object(&scope, NoneType::object());
95 Object name(&scope, runtime_->newInt(0));
96 Object value(&scope, runtime_->newInt(1));
97 EXPECT_TRUE(raisedWithStr(
98 runBuiltin(METH(object, __setattr__), object, name, value),
99 LayoutId::kTypeError, "attribute name must be string, not 'int'"));
100}
101
102TEST_F(ObjectBuiltinsTest, DunderSetattrOnBuiltinTypeRaisesAttributeError) {
103 HandleScope scope(thread_);
104 Object object(&scope, NoneType::object());
105 Object name(&scope, runtime_->newStrFromCStr("foo"));
106 Object value(&scope, runtime_->newInt(1));
107 EXPECT_TRUE(raisedWithStr(
108 runBuiltin(METH(object, __setattr__), object, name, value),
109 LayoutId::kAttributeError, "'NoneType' object has no attribute 'foo'"));
110}
111
112TEST_F(ObjectBuiltinsTest,
113 DunderSizeofWithNonHeapObjectReturnsSizeofRawObject) {
114 HandleScope scope(thread_);
115 Object small_int(&scope, SmallInt::fromWord(6));
116 Object result(&scope, runBuiltin(METH(object, __sizeof__), small_int));
117 EXPECT_TRUE(isIntEqualsWord(*result, kPointerSize));
118}
119
120TEST_F(ObjectBuiltinsTest, DunderSizeofWithLargeStrReturnsSizeofHeapObject) {
121 HandleScope scope(thread_);
122 HeapObject large_str(&scope, runtime_->createLargeStr(40));
123 Object result(&scope, runBuiltin(METH(object, __sizeof__), large_str));
124 EXPECT_TRUE(isIntEqualsWord(*result, large_str.size()));
125}
126
127TEST_F(
128 ObjectBuiltinsTest,
129 DunderNeWithSelfImplementingDunderEqReturningNotImplementedReturnsNotImplemented) {
130 ASSERT_FALSE(runFromCStr(runtime_, R"(
131class Foo():
132 def __eq__(self, b): return NotImplemented
133
134result = object.__ne__(Foo(), None)
135)")
136 .isError());
137 EXPECT_TRUE(mainModuleAt(runtime_, "result").isNotImplementedType());
138}
139
140TEST_F(ObjectBuiltinsTest,
141 DunderNeWithSelfImplementingDunderEqReturningZeroReturnsTrue) {
142 ASSERT_FALSE(runFromCStr(runtime_, R"(
143class Foo():
144 def __eq__(self, b): return 0
145
146result = object.__ne__(Foo(), None)
147)")
148 .isError());
149 // 0 is converted to False, and flipped again for __ne__ from __eq__.
150 EXPECT_EQ(mainModuleAt(runtime_, "result"), Bool::trueObj());
151}
152
153TEST_F(ObjectBuiltinsTest,
154 DunderNeWithSelfImplementingDunderEqReturningOneReturnsFalse) {
155 ASSERT_FALSE(runFromCStr(runtime_, R"(
156class Foo():
157 def __eq__(self, b): return 1
158
159result = object.__ne__(Foo(), None)
160)")
161 .isError());
162 // 1 is converted to True, and flipped again for __ne__ from __eq__.
163 EXPECT_EQ(mainModuleAt(runtime_, "result"), Bool::falseObj());
164}
165
166TEST_F(ObjectBuiltinsTest,
167 DunderNeWithSelfImplementingDunderEqReturningFalseReturnsTrue) {
168 ASSERT_FALSE(runFromCStr(runtime_, R"(
169class Foo():
170 def __eq__(self, b): return False
171
172result = object.__ne__(Foo(), None)
173)")
174 .isError());
175 EXPECT_EQ(mainModuleAt(runtime_, "result"), Bool::trueObj());
176}
177
178TEST_F(ObjectBuiltinsTest,
179 DunderNeWithSelfImplementingDunderEqReturningTrueReturnsFalse) {
180 ASSERT_FALSE(runFromCStr(runtime_, R"(
181class Foo():
182 def __eq__(self, b): return True
183
184result = object.__ne__(Foo(), None)
185)")
186 .isError());
187 EXPECT_EQ(mainModuleAt(runtime_, "result"), Bool::falseObj());
188}
189
190TEST_F(ObjectBuiltinsTest, DunderStrReturnsDunderRepr) {
191 ASSERT_FALSE(runFromCStr(runtime_, R"(
192class Foo:
193 pass
194
195f = Foo()
196a = object.__str__(f)
197b = object.__repr__(f)
198)")
199 .isError());
200 HandleScope scope(thread_);
201 Object a(&scope, mainModuleAt(runtime_, "a"));
202 Object b(&scope, mainModuleAt(runtime_, "b"));
203 EXPECT_TRUE(isStrEquals(a, b));
204}
205
206TEST_F(ObjectBuiltinsTest, UserDefinedTypeInheritsDunderStr) {
207 ASSERT_FALSE(runFromCStr(runtime_, R"(
208class Foo:
209 pass
210
211f = Foo()
212a = object.__str__(f)
213b = f.__str__()
214)")
215 .isError());
216 HandleScope scope(thread_);
217 Object a(&scope, mainModuleAt(runtime_, "a"));
218 Object b(&scope, mainModuleAt(runtime_, "b"));
219 EXPECT_TRUE(isStrEquals(a, b));
220}
221
222TEST_F(ObjectBuiltinsTest,
223 DunderInitDoesNotRaiseIfNewIsDifferentButInitIsSame) {
224 ASSERT_FALSE(runFromCStr(runtime_, R"(
225class Foo:
226 def __new__(cls):
227 return object.__new__(cls)
228
229Foo.__init__(Foo(), 1)
230)")
231 .isError());
232 // It doesn't matter what the output is, just that it doesn't throw a
233 // TypeError.
234}
235
236TEST_F(ObjectBuiltinsTest, DunderInitWithNonInstanceIsOk) {
237 ASSERT_FALSE(runFromCStr(runtime_, R"(
238object.__init__(object)
239)")
240 .isError());
241 // It doesn't matter what the output is, just that it doesn't throw a
242 // TypeError.
243}
244
245TEST_F(ObjectBuiltinsTest, DunderInitWithNoArgsRaisesTypeError) {
246 // Passing no args to object.__init__ should throw a type error.
247 EXPECT_TRUE(raisedWithStr(
248 runFromCStr(runtime_, R"(
249object.__init__()
250)"),
251 LayoutId::kTypeError,
252 "'object.__init__' takes min 1 positional arguments but 0 given"));
253}
254
255TEST_F(ObjectBuiltinsTest, DunderInitWithArgsRaisesTypeError) {
256 // Passing extra args to object.__init__, without overwriting __new__,
257 // should throw a type error.
258 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
259class Foo:
260 pass
261
262Foo.__init__(Foo(), 1)
263)"),
264 LayoutId::kTypeError,
265 "object.__init__() takes no parameters"));
266}
267
268TEST_F(ObjectBuiltinsTest, DunderInitWithNewAndInitRaisesTypeError) {
269 // Passing extra args to object.__init__, and overwriting only __init__,
270 // should throw a type error.
271 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
272class Foo:
273 def __init__(self):
274 object.__init__(self, 1)
275
276Foo()
277)"),
278 LayoutId::kTypeError,
279 "object.__init__() takes no parameters"));
280}
281
282TEST_F(NoneBuiltinsTest, NewReturnsNone) {
283 HandleScope scope(thread_);
284 Type type(&scope, runtime_->typeAt(LayoutId::kNoneType));
285 EXPECT_TRUE(runBuiltin(METH(NoneType, __new__), type).isNoneType());
286}
287
288TEST_F(NoneBuiltinsTest, NewWithExtraArgsRaisesTypeError) {
289 EXPECT_TRUE(
290 raised(runFromCStr(runtime_, "NoneType.__new__(NoneType, 1, 2, 3, 4, 5)"),
291 LayoutId::kTypeError));
292}
293
294TEST_F(NoneBuiltinsTest, DunderReprIsBoundMethod) {
295 ASSERT_FALSE(runFromCStr(runtime_, "a = None.__repr__").isError());
296 HandleScope scope(thread_);
297 Object a(&scope, mainModuleAt(runtime_, "a"));
298 EXPECT_TRUE(a.isBoundMethod());
299}
300
301TEST_F(NoneBuiltinsTest, DunderReprReturnsNone) {
302 ASSERT_FALSE(runFromCStr(runtime_, "a = None.__repr__()").isError());
303 HandleScope scope(thread_);
304 Object a(&scope, mainModuleAt(runtime_, "a"));
305 EXPECT_TRUE(isStrEqualsCStr(*a, "None"));
306}
307
308TEST_F(NoneBuiltinsTest, BuiltinBaseIsNone) {
309 HandleScope scope(thread_);
310 Type none_type(&scope, runtime_->typeAt(LayoutId::kNoneType));
311 EXPECT_EQ(none_type.builtinBase(), LayoutId::kNoneType);
312}
313
314TEST_F(ObjectBuiltinsTest,
315 InstanceDelAttrWithHiddenAttributeReturnsErrorNotFound) {
316 HandleScope scope(thread_);
317 LayoutId layout_id = LayoutId::kUserWarning;
318 Object previous_layout(&scope, runtime_->layoutAt(layout_id));
319 BuiltinAttribute attrs[] = {
320 {ID(__globals__), 0, AttributeFlags::kHidden},
321 };
322 Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id,
323 LayoutId::kObject, attrs,
324 /*size=*/kPointerSize,
325 /*basetype=*/true));
326 Layout layout(&scope, type.instanceLayout());
327 runtime_->layoutAtPut(layout_id, *layout);
328 Instance instance(&scope, runtime_->newInstance(layout));
329 Str attribute_name(&scope,
330 Runtime::internStrFromCStr(thread_, "__globals__"));
331 EXPECT_TRUE(
332 instanceDelAttr(thread_, instance, attribute_name).isErrorNotFound());
333 EXPECT_EQ(instance.layoutId(), layout.id());
334 runtime_->layoutAtPut(layout_id, *previous_layout);
335}
336
337TEST_F(ObjectBuiltinsTest,
338 InstanceDelAttrWithInObjectAttributeDeletesAttribute) {
339 ASSERT_FALSE(runFromCStr(runtime_, R"(
340class C:
341 def __init__(self):
342 self.foo = 42
343instance = C()
344)")
345 .isError());
346 HandleScope scope(thread_);
347 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
348 Layout layout(&scope, runtime_->layoutOf(*instance));
349 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
350 AttributeInfo info;
351 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
352 ASSERT_TRUE(info.isInObject());
353
354 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isNoneType());
355 EXPECT_TRUE(instanceGetAttribute(thread_, instance, name).isErrorNotFound());
356 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isErrorNotFound());
357}
358
359TEST_F(ObjectBuiltinsTest,
360 InstanceDelAttrWithTupleOverflowAttributeDeletesAttribute) {
361 ASSERT_FALSE(runFromCStr(runtime_, R"(
362class C: pass
363instance = C()
364instance.foo = 42
365)")
366 .isError());
367 HandleScope scope(thread_);
368 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
369 Layout layout(&scope, runtime_->layoutOf(*instance));
370 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
371 AttributeInfo info;
372 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
373 ASSERT_TRUE(info.isOverflow());
374
375 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isNoneType());
376 EXPECT_TRUE(instanceGetAttribute(thread_, instance, name).isErrorNotFound());
377 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isErrorNotFound());
378}
379
380TEST_F(ObjectBuiltinsTest,
381 InstanceDelAttrWithNonexistentAttributeReturnsErrorNotFound) {
382 ASSERT_FALSE(runFromCStr(runtime_, R"(
383class C: pass
384instance = C()
385)")
386 .isError());
387 HandleScope scope(thread_);
388 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
389 Object name(&scope, Runtime::internStrFromCStr(thread_, "does_not_exist"));
390 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isErrorNotFound());
391}
392
393TEST_F(ObjectBuiltinsTest,
394 InstanceDelAttrWithTupleOverflowAttributeKeepsOtherAttributes) {
395 ASSERT_FALSE(runFromCStr(runtime_, R"(
396class C: pass
397instance = C()
398instance.y = 2
399instance.z = 3
400)")
401 .isError());
402 HandleScope scope(thread_);
403 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
404 Object y(&scope, Runtime::internStrFromCStr(thread_, "y"));
405 Object result(&scope, instanceDelAttr(thread_, instance, y));
406 EXPECT_TRUE(instanceGetAttribute(thread_, instance, y).isErrorNotFound());
407 EXPECT_TRUE(result.isNoneType());
408 Object z(&scope, Runtime::internStrFromCStr(thread_, "z"));
409 EXPECT_TRUE(isIntEqualsWord(instanceGetAttribute(thread_, instance, z), 3));
410}
411
412TEST_F(ObjectBuiltinsTest,
413 InstanceDelAttrWithReadonlyAttributeRaisesAttributeError) {
414 HandleScope scope(thread_);
415 LayoutId layout_id = LayoutId::kUserWarning;
416 Object previous_layout(&scope, runtime_->layoutAt(layout_id));
417 BuiltinAttribute attrs[] = {
418 {ID(__globals__), 0, AttributeFlags::kReadOnly},
419 };
420 Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id,
421 LayoutId::kObject, attrs,
422 /*size=*/kPointerSize,
423 /*basetype=*/true));
424 Layout layout(&scope, type.instanceLayout());
425 runtime_->layoutAtPut(layout_id, *layout);
426 Instance instance(&scope, runtime_->newInstance(layout));
427 Str attribute_name(&scope,
428 Runtime::internStrFromCStr(thread_, "__globals__"));
429 EXPECT_TRUE(raisedWithStr(instanceDelAttr(thread_, instance, attribute_name),
430 LayoutId::kAttributeError,
431 "'__globals__' attribute is read-only"));
432 EXPECT_EQ(instance.layoutId(), layout.id());
433 runtime_->layoutAtPut(layout_id, *previous_layout);
434}
435
436TEST_F(ObjectBuiltinsTest,
437 InstanceDelAttrWithDictOverflowAttributeDeletesAttribute) {
438 HandleScope scope(thread_);
439 ASSERT_FALSE(runFromCStr(runtime_, R"(
440def instance(): pass
441instance.foo = 42
442)")
443 .isError());
444 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
445 Layout layout(&scope, runtime_->layoutOf(*instance));
446 ASSERT_TRUE(layout.hasDictOverflow());
447 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
448 AttributeInfo info;
449 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout, name, &info));
450
451 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isNoneType());
452 EXPECT_TRUE(instanceGetAttribute(thread_, instance, name).isErrorNotFound());
453 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isErrorNotFound());
454}
455
456TEST_F(
457 ObjectBuiltinsTest,
458 InstanceDelAttrWithNonexistentAttributeDictOverflowReturnsErrorNotFound) {
459 HandleScope scope(thread_);
460 ASSERT_FALSE(runFromCStr(runtime_, R"(
461def instance(): pass
462)")
463 .isError());
464 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
465 Layout layout(&scope, runtime_->layoutOf(*instance));
466 ASSERT_TRUE(layout.hasDictOverflow());
467 Object name(&scope, Runtime::internStrFromCStr(thread_, "does_not_exist"));
468 EXPECT_TRUE(instanceDelAttr(thread_, instance, name).isErrorNotFound());
469}
470
471TEST_F(ObjectBuiltinsTest,
472 InstanceGetAttributeWithInObjectAttributeReturnsValue) {
473 HandleScope scope(thread_);
474 ASSERT_FALSE(runFromCStr(runtime_, R"(
475class C:
476 def __init__(self):
477 self.foo = 42
478instance = C()
479)")
480 .isError());
481 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
482 Layout layout(&scope, runtime_->layoutOf(*instance));
483 ASSERT_TRUE(layout.hasTupleOverflow());
484 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
485 AttributeInfo info;
486 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
487 ASSERT_TRUE(info.isInObject());
488
489 EXPECT_TRUE(
490 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), 42));
491}
492
493TEST_F(ObjectBuiltinsTest,
494 InstanceGetAttributeWithTupleOverflowAttributeReturnsValue) {
495 HandleScope scope(thread_);
496 ASSERT_FALSE(runFromCStr(runtime_, R"(
497class C: pass
498instance = C()
499instance.foo = 42
500)")
501 .isError());
502 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
503 Layout layout(&scope, runtime_->layoutOf(*instance));
504 ASSERT_TRUE(layout.hasTupleOverflow());
505 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
506 AttributeInfo info;
507 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
508 ASSERT_TRUE(info.isOverflow());
509
510 EXPECT_TRUE(
511 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), 42));
512}
513
514TEST_F(ObjectBuiltinsTest,
515 InstanceGetAttributeWithDictOverflowAttributeReturnsValue) {
516 HandleScope scope(thread_);
517 ASSERT_FALSE(runFromCStr(runtime_, R"(
518def instance(): pass
519instance.foo = 42
520)")
521 .isError());
522 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
523 Layout layout(&scope, runtime_->layoutOf(*instance));
524 ASSERT_TRUE(layout.hasDictOverflow());
525 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
526 AttributeInfo info;
527 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout, name, &info));
528
529 EXPECT_TRUE(
530 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), 42));
531}
532
533TEST_F(ObjectBuiltinsTest,
534 InstanceGetattributeWithHiddenAttributeReturnsErrorNotFound) {
535 HandleScope scope(thread_);
536 LayoutId layout_id = LayoutId::kUserWarning;
537 Object previous_layout(&scope, runtime_->layoutAt(layout_id));
538 BuiltinAttribute attrs[] = {
539 {ID(__globals__), 0, AttributeFlags::kHidden},
540 };
541 Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id,
542 LayoutId::kObject, attrs,
543 /*size=*/kPointerSize,
544 /*basetype=*/true));
545 Layout layout(&scope, type.instanceLayout());
546 runtime_->layoutAtPut(layout_id, *layout);
547 Instance instance(&scope, runtime_->newInstance(layout));
548 Str attribute_name(&scope,
549 Runtime::internStrFromCStr(thread_, "__globals__"));
550 EXPECT_TRUE(instanceGetAttribute(thread_, instance, attribute_name)
551 .isErrorNotFound());
552 EXPECT_EQ(instance.layoutId(), layout.id());
553 runtime_->layoutAtPut(layout_id, *previous_layout);
554}
555
556TEST_F(
557 ObjectBuiltinsTest,
558 InstanceGetAttributeWithNonExistentAttributeDictOverflowReturnsErrorNotFound) {
559 HandleScope scope(thread_);
560 ASSERT_FALSE(runFromCStr(runtime_, R"(
561def instance(): pass
562)")
563 .isError());
564 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
565 Layout layout(&scope, runtime_->layoutOf(*instance));
566 ASSERT_TRUE(layout.hasDictOverflow());
567 Object name(&scope, Runtime::internStrFromCStr(thread_, "does_not_exist"));
568 EXPECT_TRUE(instanceGetAttribute(thread_, instance, name).isErrorNotFound());
569}
570
571TEST_F(ObjectBuiltinsTest, InstanceSetAttrSetsInObjectAttribute) {
572 HandleScope scope(thread_);
573 ASSERT_FALSE(runFromCStr(runtime_, R"(
574class C:
575 def __init__(self, set_value):
576 if set_value:
577 self.foo = 42
578instance = C(False)
579)")
580 .isError());
581 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
582 Object name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
583 Object value(&scope, runtime_->newInt(-7));
584 EXPECT_TRUE(instanceSetAttr(thread_, instance, name, value).isNoneType());
585
586 Layout layout(&scope, runtime_->layoutOf(*instance));
587 AttributeInfo info;
588 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
589 ASSERT_TRUE(info.isInObject());
590
591 EXPECT_TRUE(
592 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), -7));
593}
594
595TEST_F(ObjectBuiltinsTest, InstanceSetAttrSetsNewTupleOverflowAttribute) {
596 HandleScope scope(thread_);
597 ASSERT_FALSE(runFromCStr(runtime_, R"(
598class C: pass
599instance = C()
600)")
601 .isError());
602 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
603 Object name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
604 Object value(&scope, runtime_->newInt(-14));
605 EXPECT_TRUE(instanceSetAttr(thread_, instance, name, value).isNoneType());
606
607 Layout layout(&scope, runtime_->layoutOf(*instance));
608 Tuple overflow(&scope, instance.instanceVariableAt(layout.overflowOffset()));
609 EXPECT_EQ(overflow.length(), 1);
610
611 AttributeInfo info;
612 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
613 ASSERT_TRUE(info.isOverflow());
614
615 EXPECT_TRUE(
616 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), -14));
617}
618
619TEST_F(ObjectBuiltinsTest, InstanceSetAttrSetsExistingTupleOverflowAttribute) {
620 HandleScope scope(thread_);
621 ASSERT_FALSE(runFromCStr(runtime_, R"(
622class C: pass
623instance = C()
624instance.bar = 5000
625)")
626 .isError());
627 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
628 Layout layout(&scope, runtime_->layoutOf(*instance));
629 Tuple overflow(&scope, instance.instanceVariableAt(layout.overflowOffset()));
630 ASSERT_EQ(overflow.length(), 1);
631
632 Object name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
633 Object value(&scope, runtime_->newInt(-14));
634 EXPECT_TRUE(instanceSetAttr(thread_, instance, name, value).isNoneType());
635 ASSERT_EQ(overflow.length(), 1);
636
637 AttributeInfo info;
638 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
639 ASSERT_TRUE(info.isOverflow());
640
641 EXPECT_TRUE(
642 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), -14));
643}
644
645TEST_F(ObjectBuiltinsTest, InstanceSetAttrSetsDictOverflowAttribute) {
646 HandleScope scope(thread_);
647 ASSERT_FALSE(runFromCStr(runtime_, R"(
648def instance(): pass
649)")
650 .isError());
651 Instance instance(&scope, mainModuleAt(runtime_, "instance"));
652 Object name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
653 Object value(&scope, runtime_->newInt(4711));
654 Layout layout(&scope, runtime_->layoutOf(*instance));
655 ASSERT_TRUE(layout.hasDictOverflow());
656
657 EXPECT_TRUE(instanceSetAttr(thread_, instance, name, value).isNoneType());
658 EXPECT_EQ(instance.layoutId(), layout.id());
659
660 AttributeInfo info;
661 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout, name, &info));
662 EXPECT_TRUE(
663 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), 4711));
664}
665
666TEST_F(ObjectBuiltinsTest,
667 InstanceSetAttrWithHiddenAttributeRaisesAttributeError) {
668 HandleScope scope(thread_);
669 LayoutId layout_id = LayoutId::kUserWarning;
670 Object previous_layout(&scope, runtime_->layoutAt(layout_id));
671 BuiltinAttribute attrs[] = {
672 {ID(__globals__), 0, AttributeFlags::kHidden},
673 };
674 Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id,
675 LayoutId::kObject, attrs,
676 /*size=*/kPointerSize,
677 /*basetype=*/true));
678 Layout layout(&scope, type.instanceLayout());
679 runtime_->layoutAtPut(layout_id, *layout);
680 Instance instance(&scope, runtime_->newInstance(layout));
681 Str attribute_name(&scope,
682 Runtime::internStrFromCStr(thread_, "__globals__"));
683 Object value(&scope, runtime_->newInt(4711));
684 EXPECT_TRUE(
685 raisedWithStr(instanceSetAttr(thread_, instance, attribute_name, value),
686 LayoutId::kAttributeError,
687 "'UserWarning.__globals__' attribute cannot be set"));
688 EXPECT_EQ(instance.layoutId(), layout.id());
689 runtime_->layoutAtPut(layout_id, *previous_layout);
690}
691
692TEST_F(ObjectBuiltinsTest, ObjectGetAttributeReturnsInstanceValue) {
693 HandleScope scope(thread_);
694 ASSERT_FALSE(runFromCStr(runtime_, R"(
695class C: pass
696c = C()
697c.__hash__ = 42
698)")
699 .isError());
700 Object c(&scope, mainModuleAt(runtime_, "c"));
701 Object name(&scope, Runtime::internStrFromCStr(thread_, "__hash__"));
702 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, c, name), 42));
703}
704
705TEST_F(ObjectBuiltinsTest, ObjectGetAttributeReturnsTypeValue) {
706 HandleScope scope(thread_);
707 ASSERT_FALSE(runFromCStr(runtime_, R"(
708class C:
709 x = -11
710c = C()
711)")
712 .isError());
713 Object c(&scope, mainModuleAt(runtime_, "c"));
714 Object name(&scope, Runtime::internStrFromCStr(thread_, "x"));
715 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, c, name), -11));
716}
717
718TEST_F(ObjectBuiltinsTest, ObjectGetAttributeWithNonExistentNameReturnsError) {
719 HandleScope scope(thread_);
720 ASSERT_FALSE(runFromCStr(runtime_, R"(
721class C: pass
722c = C()
723)")
724 .isError());
725 Object c(&scope, mainModuleAt(runtime_, "c"));
726 Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx"));
727 EXPECT_TRUE(objectGetAttribute(thread_, c, name).isError());
728 EXPECT_FALSE(thread_->hasPendingException());
729}
730
731TEST_F(ObjectBuiltinsTest, ObjectGetAttributeCallsDunderGetOnDataDescriptor) {
732 HandleScope scope(thread_);
733 ASSERT_FALSE(runFromCStr(runtime_, R"(
734class D:
735 def __set__(self, instance, value): pass
736 def __get__(self, instance, owner): return 42
737class A:
738 foo = D()
739a = A()
740)")
741 .isError());
742 Object a(&scope, mainModuleAt(runtime_, "a"));
743 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
744 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, a, name), 42));
745}
746
747TEST_F(ObjectBuiltinsTest,
748 ObjectGetAttributeCallsDunderGetOnNonDataDescriptor) {
749 HandleScope scope(thread_);
750 ASSERT_FALSE(runFromCStr(runtime_, R"(
751class D:
752 def __get__(self, instance, owner): return 42
753class A:
754 foo = D()
755a = A()
756)")
757 .isError());
758 Object a(&scope, mainModuleAt(runtime_, "a"));
759 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
760 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, a, name), 42));
761}
762
763TEST_F(ObjectBuiltinsTest,
764 ObjectGetAttributePrefersDataDescriptorOverInstanceAttr) {
765 HandleScope scope(thread_);
766 ASSERT_FALSE(runFromCStr(runtime_, R"(
767class D:
768 def __set__(self, instance, value): pass
769 def __get__(self, instance, owner): return 42
770class A:
771 pass
772a = A()
773a.foo = 12
774A.foo = D()
775)")
776 .isError());
777 Object a(&scope, mainModuleAt(runtime_, "a"));
778 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
779 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, a, name), 42));
780}
781
782TEST_F(ObjectBuiltinsTest,
783 ObjectGetAttributePrefersInstanceAttrOverNonDataDescriptor) {
784 HandleScope scope(thread_);
785 ASSERT_FALSE(runFromCStr(runtime_, R"(
786class D:
787 def __get__(self, instance, owner): return 42
788class A:
789 foo = D()
790a = A()
791a.foo = 12
792)")
793 .isError());
794 Object a(&scope, mainModuleAt(runtime_, "a"));
795 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
796 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, a, name), 12));
797}
798
799TEST_F(ObjectBuiltinsTest, ObjectGetAttributePropagatesDunderGetException) {
800 HandleScope scope(thread_);
801 ASSERT_FALSE(runFromCStr(runtime_, R"(
802class D:
803 def __set__(self, instance, value): pass
804 def __get__(self, instance, owner): raise UserWarning()
805class A:
806 foo = D()
807a = A()
808)")
809 .isError());
810 Object a(&scope, mainModuleAt(runtime_, "a"));
811 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
812 EXPECT_TRUE(
813 raised(objectGetAttribute(thread_, a, name), LayoutId::kUserWarning));
814}
815
816TEST_F(ObjectBuiltinsTest,
817 ObjectGetAttributeOnNoneNonDataDescriptorReturnsBoundMethod) {
818 HandleScope scope(thread_);
819 Object none(&scope, NoneType::object());
820 Object name(&scope, Runtime::internStrFromCStr(thread_, "__repr__"));
821 EXPECT_TRUE(objectGetAttribute(thread_, none, name).isBoundMethod());
822}
823
824TEST_F(ObjectBuiltinsTest,
825 ObjectGetAttributeSetLocationReturnsBoundMethodAndCachesFunction) {
826 HandleScope scope(thread_);
827 ASSERT_FALSE(runFromCStr(runtime_, R"(
828class C:
829 def foo():
830 pass
831foo = C.foo
832i = C()
833)")
834 .isError());
835 Object foo(&scope, mainModuleAt(runtime_, "foo"));
836 Object i(&scope, mainModuleAt(runtime_, "i"));
837
838 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
839 Object to_cache(&scope, NoneType::object());
840 LoadAttrKind kind = LoadAttrKind::kUnknown;
841 Object result_obj(&scope, objectGetAttributeSetLocation(thread_, i, name,
842 &to_cache, &kind));
843 ASSERT_TRUE(result_obj.isBoundMethod());
844 BoundMethod result(&scope, *result_obj);
845 EXPECT_EQ(result.function(), foo);
846 EXPECT_EQ(result.self(), i);
847 EXPECT_EQ(to_cache, foo);
848 EXPECT_EQ(kind, LoadAttrKind::kInstanceFunction);
849
850 Object load_cached_result_obj(
851 &scope, Interpreter::loadAttrWithLocation(thread_, *i, *to_cache));
852 ASSERT_TRUE(load_cached_result_obj.isBoundMethod());
853 BoundMethod load_cached_result(&scope, *load_cached_result_obj);
854 EXPECT_EQ(load_cached_result.function(), foo);
855 EXPECT_EQ(load_cached_result.self(), i);
856}
857
858TEST_F(ObjectBuiltinsTest,
859 ObjectGetAttributeSetLocationReturnsInstanceVariableAndCachesOffset) {
860 HandleScope scope(thread_);
861 ASSERT_FALSE(runFromCStr(runtime_, R"(
862class C:
863 def __init__(self):
864 self.foo = 42
865i = C()
866)")
867 .isError());
868 Object i(&scope, mainModuleAt(runtime_, "i"));
869
870 Layout layout(&scope, runtime_->layoutOf(*i));
871 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
872 AttributeInfo info;
873 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
874 ASSERT_TRUE(info.isInObject());
875
876 Object to_cache(&scope, NoneType::object());
877 LoadAttrKind kind = LoadAttrKind::kUnknown;
878 EXPECT_TRUE(isIntEqualsWord(
879 objectGetAttributeSetLocation(thread_, i, name, &to_cache, &kind), 42));
880 EXPECT_TRUE(isIntEqualsWord(*to_cache, info.offset()));
881 EXPECT_EQ(kind, LoadAttrKind::kInstanceOffset);
882
883 EXPECT_TRUE(isIntEqualsWord(
884 Interpreter::loadAttrWithLocation(thread_, *i, *to_cache), 42));
885}
886
887TEST_F(
888 ObjectBuiltinsTest,
889 ObjectGetAttributeSetLocationReturnsInstanceVariableAndCachesNegativeOffset) {
890 HandleScope scope(thread_);
891 ASSERT_FALSE(runFromCStr(runtime_, R"(
892class C:
893 pass
894i = C()
895i.foo = 17
896)")
897 .isError());
898 Object i(&scope, mainModuleAt(runtime_, "i"));
899
900 Layout layout(&scope, runtime_->layoutOf(*i));
901 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
902 AttributeInfo info;
903 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
904 ASSERT_TRUE(info.isOverflow());
905
906 Object to_cache(&scope, NoneType::object());
907 LoadAttrKind kind = LoadAttrKind::kUnknown;
908 EXPECT_TRUE(isIntEqualsWord(
909 objectGetAttributeSetLocation(thread_, i, name, &to_cache, &kind), 17));
910 EXPECT_TRUE(isIntEqualsWord(*to_cache, -info.offset() - 1));
911
912 EXPECT_TRUE(isIntEqualsWord(
913 Interpreter::loadAttrWithLocation(thread_, *i, *to_cache), 17));
914}
915
916TEST_F(ObjectBuiltinsTest,
917 ObjectGetAttributeSetLocationRaisesAttributeErrorAndDoesNotSetLocation) {
918 HandleScope scope(thread_);
919 ASSERT_FALSE(runFromCStr(runtime_, R"(
920class C:
921 pass
922i = C()
923)")
924 .isError());
925 Object i(&scope, mainModuleAt(runtime_, "i"));
926
927 Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx"));
928 Object to_cache(&scope, NoneType::object());
929 LoadAttrKind kind = LoadAttrKind::kUnknown;
930 EXPECT_TRUE(objectGetAttributeSetLocation(thread_, i, name, &to_cache, &kind)
931 .isError());
932 EXPECT_TRUE(to_cache.isNoneType());
933 EXPECT_EQ(kind, LoadAttrKind::kUnknown);
934}
935
936TEST_F(ObjectBuiltinsTest, ObjectGetItemCallsTypeObjectDunderClassItem) {
937 HandleScope scope(thread_);
938 ASSERT_FALSE(runFromCStr(runtime_, R"(
939class C:
940 def __class_getitem__(cls, item):
941 return f"C:{cls.__name__}[{item.__name__}]"
942)")
943 .isError());
944 Type type_c(&scope, mainModuleAt(runtime_, "C"));
945 Type key(&scope, runtime_->typeAt(LayoutId::kInt));
946 Object result(&scope, objectGetItem(thread_, type_c, key));
947 EXPECT_TRUE(isStrEqualsCStr(*result, "C:C[int]"));
948}
949
950TEST_F(ObjectBuiltinsTest, ObjectSetAttrSetsInstanceValue) {
951 HandleScope scope(thread_);
952 ASSERT_FALSE(runFromCStr(runtime_, R"(
953class C: pass
954i = C()
955)")
956 .isError());
957 Object i(&scope, mainModuleAt(runtime_, "i"));
958 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
959 Object value(&scope, runtime_->newInt(47));
960 EXPECT_TRUE(objectSetAttr(thread_, i, name, value).isNoneType());
961 EXPECT_TRUE(isIntEqualsWord(objectGetAttribute(thread_, i, name), 47));
962}
963
964TEST_F(ObjectBuiltinsTest, ObjectSetAttrOnDataDescriptorCallsDunderSet) {
965 HandleScope scope(thread_);
966 ASSERT_FALSE(runFromCStr(runtime_, R"(
967class D:
968 def __set__(self, instance, value):
969 global set_args
970 set_args = (self, instance, value)
971 return "ignored result"
972 def __get__(self, instance, owner): pass
973foo_descr = D()
974class C:
975 foo = foo_descr
976i = C()
977)")
978 .isError());
979 Object i(&scope, mainModuleAt(runtime_, "i"));
980 Object foo_descr(&scope, mainModuleAt(runtime_, "foo_descr"));
981 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
982 Object value(&scope, runtime_->newInt(47));
983 EXPECT_TRUE(objectSetAttr(thread_, i, name, value).isNoneType());
984 Object set_args_obj(&scope, mainModuleAt(runtime_, "set_args"));
985 ASSERT_TRUE(set_args_obj.isTuple());
986 Tuple dunder_set_args(&scope, *set_args_obj);
987 ASSERT_EQ(dunder_set_args.length(), 3);
988 EXPECT_EQ(dunder_set_args.at(0), foo_descr);
989 EXPECT_EQ(dunder_set_args.at(1), i);
990 EXPECT_TRUE(isIntEqualsWord(dunder_set_args.at(2), 47));
991}
992
993TEST_F(ObjectBuiltinsTest, ObjectSetAttrPropagatesErrorsInDunderSet) {
994 HandleScope scope(thread_);
995 ASSERT_FALSE(runFromCStr(runtime_, R"(
996class D:
997 def __set__(self, instance, value): raise UserWarning()
998 def __get__(self, instance, owner): pass
999class C:
1000 foo = D()
1001i = C()
1002)")
1003 .isError());
1004 Object i(&scope, mainModuleAt(runtime_, "i"));
1005 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1006 Object value(&scope, runtime_->newInt(1));
1007 EXPECT_TRUE(
1008 raised(objectSetAttr(thread_, i, name, value), LayoutId::kUserWarning));
1009}
1010
1011TEST_F(ObjectBuiltinsTest, ObjectSetAttrOnNonHeapObjectRaisesAttributeError) {
1012 HandleScope scope(thread_);
1013 Object object(&scope, runtime_->newInt(42));
1014 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1015 Object value(&scope, runtime_->newInt(1));
1016 EXPECT_TRUE(raisedWithStr(objectSetAttr(thread_, object, name, value),
1017 LayoutId::kAttributeError,
1018 "'int' object has no attribute 'foo'"));
1019}
1020
1021TEST_F(ObjectBuiltinsTest, ObjectSetAttrSetLocationSetsValueCachesOffset) {
1022 HandleScope scope(thread_);
1023 ASSERT_FALSE(runFromCStr(runtime_, R"(
1024class C:
1025 def __init__(self):
1026 self.foo = 0
1027i = C()
1028)")
1029 .isError());
1030 Object i(&scope, mainModuleAt(runtime_, "i"));
1031 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1032
1033 AttributeInfo info;
1034 Layout layout(&scope, runtime_->layoutOf(*i));
1035 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
1036 ASSERT_TRUE(info.isInObject());
1037
1038 Object value(&scope, runtime_->newInt(7));
1039 Object value2(&scope, runtime_->newInt(99));
1040 Object to_cache(&scope, NoneType::object());
1041 EXPECT_TRUE(objectSetAttrSetLocation(thread_, i, name, value, &to_cache)
1042 .isNoneType());
1043 EXPECT_TRUE(isIntEqualsWord(*to_cache, info.offset()));
1044 ASSERT_TRUE(i.isInstance());
1045 Instance instance(&scope, *i);
1046 EXPECT_TRUE(isIntEqualsWord(instance.instanceVariableAt(info.offset()), 7));
1047
1048 Interpreter::storeAttrWithLocation(thread_, *i, *to_cache, *value2);
1049 EXPECT_TRUE(isIntEqualsWord(instance.instanceVariableAt(info.offset()), 99));
1050}
1051
1052TEST_F(ObjectBuiltinsTest,
1053 ObjectSetAttrSetLocationSetsOverflowValueCachesOffset) {
1054 HandleScope scope(thread_);
1055 ASSERT_FALSE(runFromCStr(runtime_, R"(
1056class C: pass
1057i = C()
1058i.foo = 0
1059)")
1060 .isError());
1061 Object i(&scope, mainModuleAt(runtime_, "i"));
1062 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1063
1064 AttributeInfo info;
1065 Layout layout(&scope, runtime_->layoutOf(*i));
1066 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, name, &info));
1067 ASSERT_TRUE(info.isOverflow());
1068
1069 Object value(&scope, runtime_->newInt(-8));
1070 Object value2(&scope, runtime_->newInt(11));
1071 Object to_cache(&scope, NoneType::object());
1072 EXPECT_TRUE(objectSetAttrSetLocation(thread_, i, name, value, &to_cache)
1073 .isNoneType());
1074 EXPECT_TRUE(isIntEqualsWord(*to_cache, -info.offset() - 1));
1075 ASSERT_TRUE(i.isHeapObject());
1076 Instance instance(&scope, *i);
1077 EXPECT_TRUE(
1078 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), -8));
1079
1080 Interpreter::storeAttrWithLocation(thread_, *i, *to_cache, *value2);
1081 EXPECT_TRUE(
1082 isIntEqualsWord(instanceGetAttribute(thread_, instance, name), 11));
1083}
1084
1085} // namespace testing
1086} // namespace py