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 <cinttypes>
5
6#include "attributedict.h"
7#include "builtins.h"
8#include "descriptor-builtins.h"
9#include "dict-builtins.h"
10#include "frame.h"
11#include "globals.h"
12#include "ic.h"
13#include "module-builtins.h"
14#include "objects.h"
15#include "runtime.h"
16#include "thread.h"
17#include "type-builtins.h"
18
19namespace py {
20
21RawObject objectRaiseAttributeError(Thread* thread, const Object& object,
22 const Object& name) {
23 return thread->raiseWithFmt(LayoutId::kAttributeError,
24 "'%T' object has no attribute '%S'", &object,
25 &name);
26}
27
28RawObject objectDeleteAttribute(Thread* thread, const Object& object,
29 const Object& name) {
30 HandleScope scope(thread);
31 Runtime* runtime = thread->runtime();
32
33 // Check for a descriptor with __delete__
34 Type type(&scope, runtime->typeOf(*object));
35 Object type_attr(&scope, typeLookupInMro(thread, *type, *name));
36 if (!type_attr.isError() && runtime->isDeleteDescriptor(thread, type_attr)) {
37 return Interpreter::callDescriptorDelete(thread, type_attr, object);
38 }
39
40 // No delete descriptor found, delete from the instance
41 if (object.isInstance()) {
42 Instance instance(&scope, *object);
43 Object result(&scope, instanceDelAttr(thread, instance, name));
44 if (!result.isErrorNotFound()) return *result;
45 }
46 return thread->raiseWithFmt(LayoutId::kAttributeError,
47 "'%T' object has no attribute '%S'", &object,
48 &name);
49}
50
51RawObject instanceDelAttr(Thread* thread, const Instance& instance,
52 const Object& name) {
53 HandleScope scope(thread);
54
55 // Remove the reference to the attribute value from the instance
56 Runtime* runtime = thread->runtime();
57 Layout layout(&scope, runtime->layoutOf(*instance));
58 AttributeInfo info;
59 if (!Runtime::layoutFindAttribute(*layout, name, &info)) {
60 if (layout.hasDictOverflow()) {
61 word offset = layout.dictOverflowOffset();
62 Object overflow_dict_obj(&scope, instance.instanceVariableAt(offset));
63 if (!overflow_dict_obj.isNoneType()) {
64 Dict overflow_dict(&scope, *overflow_dict_obj);
65 Object result(&scope, dictRemoveByStr(thread, overflow_dict, name));
66 if (result.isError()) return *result;
67 return NoneType::object();
68 }
69 }
70 return Error::notFound();
71 }
72
73 if (info.isHidden()) {
74 return Error::notFound();
75 }
76
77 if (info.isReadOnly()) {
78 return thread->raiseWithFmt(LayoutId::kAttributeError,
79 "'%S' attribute is read-only", &name);
80 }
81
82 // Make the attribute invisible
83 Layout new_layout(&scope,
84 runtime->layoutDeleteAttribute(thread, layout, name, info));
85 LayoutId new_layout_id = new_layout.id();
86 instance.setHeader(instance.header().withLayoutId(new_layout_id));
87
88 if (info.isInObject()) {
89 instance.instanceVariableAtPut(info.offset(), NoneType::object());
90 } else {
91 MutableTuple overflow(
92 &scope, instance.instanceVariableAt(new_layout.overflowOffset()));
93 overflow.atPut(info.offset(), NoneType::object());
94 }
95
96 return NoneType::object();
97}
98
99RawObject instanceGetAttributeSetLocation(Thread* thread,
100 const Instance& instance,
101 const Object& name,
102 Object* location_out) {
103 HandleScope scope(thread);
104 Runtime* runtime = thread->runtime();
105 Layout layout(&scope, runtime->layoutOf(*instance));
106 AttributeInfo info;
107 if (Runtime::layoutFindAttribute(*layout, name, &info)) {
108 if (info.isHidden()) {
109 return Error::notFound();
110 }
111 if (info.isInObject()) {
112 if (location_out != nullptr) {
113 *location_out = SmallInt::fromWord(info.offset());
114 }
115 return instance.instanceVariableAt(info.offset());
116 }
117 word offset = info.offset();
118 if (location_out != nullptr) {
119 *location_out = SmallInt::fromWord(-offset - 1);
120 }
121 word tuple_offset = layout.overflowOffset();
122 return Tuple::cast(instance.instanceVariableAt(tuple_offset)).at(offset);
123 }
124 if (layout.hasDictOverflow()) {
125 word offset = layout.dictOverflowOffset();
126 Object overflow_dict_obj(&scope, instance.instanceVariableAt(offset));
127 if (!overflow_dict_obj.isNoneType()) {
128 Dict overflow_dict(&scope, *overflow_dict_obj);
129 return dictAtByStr(thread, overflow_dict, name);
130 }
131 }
132 return Error::notFound();
133}
134
135RawObject instanceGetAttribute(Thread* thread, const Instance& instance,
136 const Object& name) {
137 return instanceGetAttributeSetLocation(thread, instance, name, nullptr);
138}
139
140void instanceGrowOverflow(Thread* thread, const Instance& instance,
141 word length) {
142 HandleScope scope(thread);
143 Layout layout(&scope, thread->runtime()->layoutOf(*instance));
144 Tuple overflow(&scope, instance.instanceVariableAt(layout.overflowOffset()));
145 DCHECK(overflow.length() < length, "unexpected overflow");
146 MutableTuple new_overflow(&scope, thread->runtime()->newMutableTuple(length));
147 new_overflow.replaceFromWith(0, *overflow, overflow.length());
148 instance.instanceVariableAtPut(layout.overflowOffset(), *new_overflow);
149}
150
151static RawObject instanceSetAttrSetLocation(Thread* thread,
152 const Instance& instance,
153 const Object& name,
154 const Object& value,
155 Object* location_out) {
156 HandleScope scope(thread);
157
158 // If the attribute doesn't exist we'll need to transition the layout
159 Runtime* runtime = thread->runtime();
160 Layout layout(&scope, runtime->layoutOf(*instance));
161 AttributeInfo info;
162 if (!Runtime::layoutFindAttribute(*layout, name, &info)) {
163 if (!layout.hasTupleOverflow()) {
164 if (layout.hasDictOverflow()) {
165 word offset = layout.dictOverflowOffset();
166 Object overflow_dict_obj(&scope, instance.instanceVariableAt(offset));
167 if (overflow_dict_obj.isNoneType()) {
168 overflow_dict_obj = runtime->newDict();
169 instance.instanceVariableAtPut(offset, *overflow_dict_obj);
170 }
171 Dict overflow_dict(&scope, *overflow_dict_obj);
172 dictAtPutByStr(thread, overflow_dict, name, value);
173 return NoneType::object();
174 }
175 if (layout.isSealed()) {
176 return thread->raiseWithFmt(
177 LayoutId::kAttributeError,
178 "Cannot set attribute '%S' on sealed class '%T'", &name, &instance);
179 }
180 }
181 // Transition the layout.
182
183 Layout new_layout(
184 &scope, runtime->layoutAddAttribute(thread, layout, name, 0, &info));
185 if (info.isOverflow() &&
186 info.offset() >=
187 Tuple::cast(instance.instanceVariableAt(layout.overflowOffset()))
188 .length()) {
189 instanceGrowOverflow(thread, instance, info.offset() + 1);
190 }
191 instance.setLayoutId(new_layout.id());
192 layout = *new_layout;
193 } else if (info.isReadOnly()) {
194 return thread->raiseWithFmt(LayoutId::kAttributeError,
195 "'%T.%S' attribute is read-only", &instance,
196 &name);
197 } else if (info.isHidden()) {
198 return thread->raiseWithFmt(LayoutId::kAttributeError,
199 "'%T.%S' attribute cannot be set", &instance,
200 &name);
201 }
202 DCHECK(!thread->runtime()->isInstanceOfType(*instance),
203 "must not cache type attributes");
204 // Store the attribute
205 if (info.isInObject()) {
206 instance.instanceVariableAtPut(info.offset(), *value);
207 if (location_out != nullptr) {
208 *location_out = SmallInt::fromWord(info.offset());
209 }
210 } else {
211 MutableTuple::cast(instance.instanceVariableAt(layout.overflowOffset()))
212 .atPut(info.offset(), *value);
213 if (location_out != nullptr) {
214 *location_out = SmallInt::fromWord(-info.offset() - 1);
215 }
216 }
217 return NoneType::object();
218}
219
220RawObject instanceSetAttr(Thread* thread, const Instance& instance,
221 const Object& name, const Object& value) {
222 return instanceSetAttrSetLocation(thread, instance, name, value, nullptr);
223}
224
225RawObject objectGetAttributeSetLocation(Thread* thread, const Object& object,
226 const Object& name,
227 Object* location_out,
228 LoadAttrKind* kind) {
229 // Look for the attribute in the class
230 HandleScope scope(thread);
231 Runtime* runtime = thread->runtime();
232 Type type(&scope, runtime->typeOf(*object));
233 Object type_attr_location(&scope, NoneType::object());
234 Object type_attr(&scope, typeLookupInMroSetLocation(thread, *type, *name,
235 &type_attr_location));
236 if (!type_attr.isError()) {
237 // TODO(T56252621): Remove this once property gets cached.
238 if (type_attr.isProperty()) {
239 Object getter(&scope, Property::cast(*type_attr).getter());
240 DCHECK(!object.isNoneType(), "object cannot be NoneType");
241 if (getter.isFunction()) {
242 // We cache only function objects as a getter to simplify its usage.
243 if (location_out != nullptr) {
244 *location_out = *getter;
245 if (type.hasFlag(Type::Flag::kHasObjectDunderClass) &&
246 *name == runtime->symbols()->at(ID(__class__))) {
247 *kind = LoadAttrKind::kDunderClass;
248 } else {
249 *kind = LoadAttrKind::kInstanceProperty;
250 }
251 }
252 return Interpreter::call1(thread, getter, object);
253 }
254 }
255 if (type_attr.isSlotDescriptor()) {
256 SlotDescriptor slot_descriptor(&scope, *type_attr);
257 Object owner(&scope, NoneType::object());
258 Object result(&scope,
259 slotDescriptorGet(thread, slot_descriptor, object, owner));
260 if (!result.isErrorException() && location_out != nullptr) {
261 // Cache slot_descriptor at success only to avoid type checking
262 // afterwards. However, unbound check should be performed when
263 // cache is hit.
264 *location_out = SmallInt::fromWord(slot_descriptor.offset());
265 *kind = LoadAttrKind::kInstanceSlotDescr;
266 }
267 return *result;
268 }
269 Type type_attr_type(&scope, runtime->typeOf(*type_attr));
270 if (typeIsDataDescriptor(*type_attr_type)) {
271 if (location_out != nullptr) {
272 *location_out = *type_attr;
273 *kind = LoadAttrKind::kInstanceTypeDescr;
274 }
275 return Interpreter::callDescriptorGet(thread, type_attr, object, type);
276 }
277 }
278
279 // No data descriptor found on the class, look at the instance.
280 if (object.isInstance()) {
281 Instance instance(&scope, *object);
282 Object result(&scope, instanceGetAttributeSetLocation(thread, instance,
283 name, location_out));
284 if (!result.isError()) {
285 if (location_out != nullptr) *kind = LoadAttrKind::kInstanceOffset;
286 return *result;
287 }
288 }
289
290 // Nothing found in the instance, if we found a non-data descriptor via the
291 // class search, use it.
292 if (!type_attr.isError()) {
293 if (type_attr.isFunction()) {
294 if (location_out != nullptr) {
295 *location_out = *type_attr;
296 *kind = LoadAttrKind::kInstanceFunction;
297 }
298 return runtime->newBoundMethod(type_attr, object);
299 }
300
301 Type type_attr_type(&scope, thread->runtime()->typeOf(*type_attr));
302 if (!typeIsNonDataDescriptor(*type_attr_type)) {
303 if (location_out != nullptr) {
304 *location_out = *type_attr;
305 *kind = LoadAttrKind::kInstanceType;
306 }
307 return *type_attr;
308 }
309 if (location_out != nullptr) {
310 *location_out = *type_attr;
311 *kind = LoadAttrKind::kInstanceTypeDescr;
312 }
313 return Interpreter::callDescriptorGet(thread, type_attr, object, type);
314 }
315 return Error::notFound();
316}
317
318RawObject objectGetAttribute(Thread* thread, const Object& object,
319 const Object& name) {
320 return objectGetAttributeSetLocation(thread, object, name, nullptr, nullptr);
321}
322
323RawObject objectNew(Thread* thread, const Type& type) {
324 HandleScope scope(thread);
325 if (!type.hasFlag(Type::Flag::kIsAbstract)) {
326 Layout layout(&scope, type.instanceLayout());
327 LayoutId id = layout.id();
328 Runtime* runtime = thread->runtime();
329 if (!isInstanceLayout(id)) {
330 Object type_name(&scope, type.name());
331 return thread->raiseWithFmt(
332 LayoutId::kTypeError,
333 "object.__new__(%S) is not safe. Use %S.__new__()", &type_name,
334 &type_name);
335 }
336 Instance result(&scope, runtime->newInstance(layout));
337 if (type.hasFlag(Type::Flag::kHasSlots)) {
338 Tuple attributes(&scope, layout.inObjectAttributes());
339 for (word i = 0, length = attributes.length(); i < length; i++) {
340 AttributeInfo info(Tuple::cast(attributes.at(i)).at(1));
341 if (info.isInitWithUnbound()) {
342 DCHECK(info.isInObject(), "in-object is expected");
343 result.instanceVariableAtPut(info.offset(), Unbound::object());
344 }
345 }
346 }
347 return *result;
348 }
349 // `type` is an abstract class and cannot be instantiated.
350 Object name(&scope, type.name());
351 Object comma(&scope, SmallStr::fromCStr(", "));
352 Object methods(&scope, type.abstractMethods());
353 Object sorted(&scope,
354 thread->invokeFunction1(ID(builtins), ID(sorted), methods));
355 if (sorted.isError()) return *sorted;
356 Object joined(&scope, thread->invokeMethod2(comma, ID(join), sorted));
357 if (joined.isError()) return *joined;
358 return thread->raiseWithFmt(
359 LayoutId::kTypeError,
360 "Can't instantiate abstract class %S with abstract methods %S", &name,
361 &joined);
362}
363
364RawObject objectSetAttrSetLocation(Thread* thread, const Object& object,
365 const Object& name, const Object& value,
366 Object* location_out) {
367 Runtime* runtime = thread->runtime();
368 // Check for a data descriptor
369 HandleScope scope(thread);
370 Type type(&scope, runtime->typeOf(*object));
371 Object type_attr(&scope, typeLookupInMro(thread, *type, *name));
372 if (!type_attr.isError()) {
373 if (type_attr.isSlotDescriptor()) {
374 SlotDescriptor slot_descriptor(&scope, *type_attr);
375 Object result(&scope,
376 slotDescriptorSet(thread, slot_descriptor, object, value));
377 if (!result.isErrorException() && location_out != nullptr) {
378 // Cache slot_descriptor at success only to avoid type checking
379 // afterwards. Note that writes via slot_descriptor are treated equally
380 // as ones to in-object instance attributes since the same cache
381 // invalidation rule applies to them.
382 *location_out = SmallInt::fromWord(slot_descriptor.offset());
383 }
384 return *result;
385 }
386 Type type_attr_type(&scope, runtime->typeOf(*type_attr));
387 if (typeIsDataDescriptor(*type_attr_type)) {
388 // Do not cache data descriptors.
389 Object set_result(&scope, Interpreter::callDescriptorSet(
390 thread, type_attr, object, value));
391 if (set_result.isError()) return *set_result;
392 return NoneType::object();
393 }
394 }
395
396 // No data descriptor found, store on the instance.
397 if (object.isInstance()) {
398 Instance instance(&scope, *object);
399 return instanceSetAttrSetLocation(thread, instance, name, value,
400 location_out);
401 }
402 return objectRaiseAttributeError(thread, object, name);
403}
404
405RawObject objectSetAttr(Thread* thread, const Object& object,
406 const Object& name, const Object& value) {
407 return objectSetAttrSetLocation(thread, object, name, value, nullptr);
408}
409
410RawObject objectDelItem(Thread* thread, const Object& object,
411 const Object& key) {
412 HandleScope scope(thread);
413 Object result(&scope, thread->invokeMethod2(object, ID(__delitem__), key));
414 if (result.isErrorNotFound()) {
415 return thread->raiseWithFmt(LayoutId::kTypeError,
416 "'%T' object does not support item deletion",
417 &object);
418 }
419 return *result;
420}
421
422RawObject objectGetItem(Thread* thread, const Object& object,
423 const Object& key) {
424 HandleScope scope(thread);
425 // This logic is replicated in Interpreter::binarySubscrUpdateCache for
426 // optimization.
427 Object result(&scope, thread->invokeMethod2(object, ID(__getitem__), key));
428 if (result.isErrorNotFound()) {
429 Runtime* runtime = thread->runtime();
430 if (runtime->isInstanceOfType(*object)) {
431 Type object_as_type(&scope, *object);
432 Str dunder_class_getitem_name(
433 &scope, runtime->symbols()->at(ID(__class_getitem__)));
434 Object class_getitem(&scope, typeGetAttribute(thread, object_as_type,
435 dunder_class_getitem_name));
436 if (!class_getitem.isErrorNotFound()) {
437 return Interpreter::call1(thread, class_getitem, key);
438 }
439 }
440 return thread->raiseWithFmt(LayoutId::kTypeError,
441 "'%T' object is not subscriptable", &object);
442 }
443 return *result;
444}
445
446RawObject objectSetItem(Thread* thread, const Object& object, const Object& key,
447 const Object& value) {
448 HandleScope scope(thread);
449 // Short-cut for the common case of dict. This also helps during bootstrapping
450 // as it allows us to use `objectSetItem` before `dict.__setitem__` is added.
451 if (object.isDict()) {
452 Dict object_dict(&scope, *object);
453 RawObject hash = Interpreter::hash(thread, key);
454 if (hash.isErrorException()) return hash;
455 dictAtPut(thread, object_dict, key, SmallInt::cast(hash).value(), value);
456 return NoneType::object();
457 }
458 Object result(&scope,
459 thread->invokeMethod3(object, ID(__setitem__), key, value));
460 if (result.isErrorNotFound()) {
461 return thread->raiseWithFmt(LayoutId::kTypeError,
462 "'%T' object does not support item assignment",
463 &object);
464 }
465 return *result;
466}
467
468RawObject METH(object, __delattr__)(Thread* thread, Arguments args) {
469 HandleScope scope(thread);
470 Runtime* runtime = thread->runtime();
471 Object self(&scope, args.get(0));
472 Object name(&scope, args.get(1));
473 name = attributeName(thread, name);
474 if (name.isErrorException()) return *name;
475 Object result(&scope, objectDeleteAttribute(thread, self, name));
476 if (!result.isErrorException()) return *result;
477 if (runtime->isInstanceOfType(*self) || runtime->isInstanceOfModule(*self)) {
478 thread->clearPendingException();
479 return thread->raiseWithFmt(LayoutId::kTypeError,
480 "can't apply this __delattr__ to type '%T'",
481 &self);
482 }
483 return *result;
484}
485
486bool METH(object, __eq___intrinsic)(Thread* thread) {
487 RawObject result = Bool::trueObj();
488 if (thread->stackTop() != thread->stackPeek(1)) {
489 result = NotImplementedType::object();
490 }
491 thread->stackDrop(2);
492 thread->stackSetTop(result);
493 return true;
494}
495
496RawObject METH(object, __getattribute__)(Thread* thread, Arguments args) {
497 HandleScope scope(thread);
498 Object self(&scope, args.get(0));
499 Object name(&scope, args.get(1));
500 name = attributeName(thread, name);
501 if (name.isErrorException()) return *name;
502 Object result(&scope, objectGetAttribute(thread, self, name));
503 if (result.isErrorNotFound()) {
504 return objectRaiseAttributeError(thread, self, name);
505 }
506 return *result;
507}
508
509RawObject METH(object, __hash__)(Thread* thread, Arguments args) {
510 return SmallInt::fromWord(thread->runtime()->hash(args.get(0)));
511}
512
513RawObject METH(object, __init__)(Thread* thread, Arguments args) {
514 // Too many arguments were given. Determine if the __new__ was not overwritten
515 // or the __init__ was to throw a TypeError.
516 HandleScope scope(thread);
517 Runtime* runtime = thread->runtime();
518 Object self(&scope, args.get(0));
519 Tuple starargs(&scope, args.get(1));
520 Dict kwargs(&scope, args.get(2));
521 if (starargs.length() == 0 && kwargs.numItems() == 0) {
522 // object.__init__ doesn't do anything except throw a TypeError if the
523 // wrong number of arguments are given. It only throws if __new__ is not
524 // overloaded or __init__ was overloaded, else it allows the excess
525 // arguments.
526 return NoneType::object();
527 }
528 Type type(&scope, runtime->typeOf(*self));
529 if ((typeLookupInMroById(thread, *type, ID(__new__)) ==
530 runtime->objectDunderNew()) ||
531 (typeLookupInMroById(thread, *type, ID(__init__)) !=
532 runtime->objectDunderInit())) {
533 // Throw a TypeError if extra arguments were passed, and __new__ was not
534 // overwritten by self, or __init__ was overloaded by self.
535 return thread->raiseWithFmt(LayoutId::kTypeError,
536 "object.__init__() takes no parameters");
537 }
538 // Else it's alright to have extra arguments.
539 return NoneType::object();
540}
541
542RawObject METH(object, __new__)(Thread* thread, Arguments args) {
543 HandleScope scope(thread);
544 Object type_obj(&scope, args.get(0));
545 if (!thread->runtime()->isInstanceOfType(*type_obj)) {
546 return thread->raiseRequiresType(type_obj, ID(type));
547 }
548 Type type(&scope, args.get(0));
549 return objectNew(thread, type);
550}
551
552RawObject METH(object, __setattr__)(Thread* thread, Arguments args) {
553 HandleScope scope(thread);
554 Object self(&scope, args.get(0));
555 Object name(&scope, args.get(1));
556 name = attributeName(thread, name);
557 if (name.isErrorException()) return *name;
558 Object value(&scope, args.get(2));
559 return objectSetAttr(thread, self, name, value);
560}
561
562RawObject METH(object, __sizeof__)(Thread* thread, Arguments args) {
563 HandleScope scope(thread);
564 Object obj(&scope, args.get(0));
565 if (obj.isHeapObject()) {
566 HeapObject heap_obj(&scope, *obj);
567 return SmallInt::fromWord(heap_obj.size());
568 }
569 return SmallInt::fromWord(kPointerSize);
570}
571
572RawObject METH(NoneType, __new__)(Thread*, Arguments) {
573 return NoneType::object();
574}
575
576RawObject METH(NoneType, __repr__)(Thread* thread, Arguments args) {
577 if (!args.get(0).isNoneType()) {
578 return thread->raiseWithFmt(LayoutId::kTypeError,
579 "__repr__ expects None as first argument");
580 }
581 return thread->runtime()->symbols()->at(ID(None));
582}
583
584static const BuiltinAttribute kInstanceProxyAttributes[] = {
585 {ID(_instance), RawInstanceProxy::kInstanceOffset},
586};
587
588static const BuiltinAttribute kEnumerateAttributes[] = {
589 {ID(iterator), RawEnumerate::kIteratorOffset},
590 {ID(index), RawEnumerate::kIndexOffset}};
591
592static void addObjectType(Thread* thread) {
593 HandleScope scope(thread);
594 Runtime* runtime = thread->runtime();
595 Layout layout(&scope, runtime->newLayout(LayoutId::kObject));
596 runtime->layoutAtPut(LayoutId::kObject, *layout);
597 Type type(&scope, runtime->newType());
598 layout.setDescribedType(*type);
599 type.setName(runtime->symbols()->at(ID(object)));
600 Tuple mro(&scope, runtime->newTupleWith1(type));
601 type.setMro(*mro);
602 type.setInstanceLayout(*layout);
603 type.setInstanceLayoutId(layout.id());
604 type.setBases(runtime->emptyTuple());
605 word flags = Type::Flag::kIsBasetype | Type::Flag::kIsFixedAttributeBase;
606 type.setFlagsAndBuiltinBase(static_cast<Type::Flag>(flags),
607 LayoutId::kObject);
608
609 // Manually create `__getattribute__` method to avoid bootstrap problems.
610 MutableTuple parameter_names(&scope, runtime->newMutableTuple(2));
611 parameter_names.atPut(0, runtime->symbols()->at(ID(self)));
612 parameter_names.atPut(1, runtime->symbols()->at(ID(name)));
613 Object name(&scope, runtime->symbols()->at(ID(__getattribute__)));
614 Object parameter_names_tuple(&scope, parameter_names.becomeImmutable());
615 Code code(&scope,
616 runtime->newBuiltinCode(
617 /*argcount=*/2, /*posonlyargcount=*/2, /*kwonlyargcount=*/0,
618 /*flags=*/0, METH(object, __getattribute__),
619 parameter_names_tuple, name));
620 Object qualname(
621 &scope, Runtime::internStrFromCStr(thread, "object.__getattribute__"));
622 Object module_obj(&scope, NoneType::object());
623 Function dunder_getattribute(
624 &scope, runtime->newFunctionWithCode(thread, qualname, code, module_obj));
625 typeAtPutById(thread, type, ID(__getattribute__), dunder_getattribute);
626}
627
628void initializeObjectTypes(Thread* thread) {
629 addObjectType(thread);
630
631 addImmediateBuiltinType(thread, ID(NoneType), LayoutId::kNoneType,
632 /*builtin_base=*/LayoutId::kNoneType,
633 /*superclass_id=*/LayoutId::kObject,
634 /*basetype=*/false);
635
636 addImmediateBuiltinType(thread, ID(NotImplementedType),
637 LayoutId::kNotImplementedType,
638 /*builtin_base=*/LayoutId::kNotImplementedType,
639 /*superclass_id=*/LayoutId::kObject,
640 /*basetype=*/false);
641
642 addImmediateBuiltinType(thread, ID(_UnboundType), LayoutId::kUnbound,
643 /*builtin_base=*/LayoutId::kUnbound,
644 /*superclass_id=*/LayoutId::kObject,
645 /*basetype=*/false);
646
647 addBuiltinType(thread, ID(instance_proxy), LayoutId::kInstanceProxy,
648 /*superclass_id=*/LayoutId::kObject, kInstanceProxyAttributes,
649 RawInstanceProxy::kSize, /*basetype=*/false);
650
651 addBuiltinType(thread, ID(enumerate), LayoutId::kEnumerate,
652 /*superclass_id=*/LayoutId::kObject, kEnumerateAttributes,
653 RawEnumerate::kSize, /*basetype=*/true);
654}
655
656} // namespace py