this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "type-builtins.h"
3
4#include "attributedict.h"
5#include "builtins.h"
6#include "bytecode.h"
7#include "capi.h"
8#include "compile-utils.h"
9#include "dict-builtins.h"
10#include "frame.h"
11#include "globals.h"
12#include "ic.h"
13#include "list-builtins.h"
14#include "module-builtins.h"
15#include "mro.h"
16#include "object-builtins.h"
17#include "objects.h"
18#include "runtime.h"
19#include "str-builtins.h"
20#include "thread.h"
21#include "typeslots.h"
22
23namespace py {
24
25static RawObject addBuiltinTypeWithLayout(Thread* thread, const Layout& layout,
26 SymbolId name, LayoutId builtin_base,
27 LayoutId superclass_id, word flags) {
28 HandleScope scope(thread);
29 Runtime* runtime = thread->runtime();
30 Type type(&scope, runtime->newType());
31 type.setName(runtime->symbols()->at(name));
32 Type superclass(&scope, runtime->typeAt(superclass_id));
33 type.setInstanceLayout(*layout);
34 type.setInstanceLayoutId(layout.id());
35 flags |= superclass.flags() & Type::kInheritableFlags;
36 if (builtin_base == layout.id()) {
37 flags |= Type::Flag::kIsFixedAttributeBase;
38 }
39 type.setFlagsAndBuiltinBase(static_cast<Type::Flag>(flags), builtin_base);
40 type.setBases(runtime->newTupleWith1(superclass));
41 layout.setDescribedType(*type);
42 return *type;
43}
44
45RawObject addBuiltinType(Thread* thread, SymbolId name, LayoutId layout_id,
46 LayoutId superclass_id, View<BuiltinAttribute> attrs,
47 word size, bool basetype) {
48 HandleScope scope(thread);
49 Runtime* runtime = thread->runtime();
50 Layout layout(&scope, runtime->layoutCreateSubclassWithBuiltins(
51 thread, layout_id, superclass_id, attrs, size));
52 runtime->layoutAtPut(layout_id, *layout);
53 LayoutId builtin_base = attrs.length() == 0 ? superclass_id : layout_id;
54 word flags = basetype ? Type::Flag::kIsBasetype : Type::Flag::kNone;
55 return addBuiltinTypeWithLayout(thread, layout, name, builtin_base,
56 superclass_id, flags);
57}
58
59RawObject addImmediateBuiltinType(Thread* thread, SymbolId name,
60 LayoutId layout_id, LayoutId builtin_base,
61 LayoutId superclass_id, bool basetype) {
62 HandleScope scope(thread);
63 Runtime* runtime = thread->runtime();
64 Layout layout(&scope, runtime->newLayout(layout_id));
65 runtime->layoutAtPut(layout_id, *layout);
66 word flags = basetype ? Type::Flag::kIsBasetype : Type::Flag::kNone;
67 return addBuiltinTypeWithLayout(thread, layout, name, builtin_base,
68 superclass_id, flags);
69}
70
71void builtinTypeEnableTupleOverflow(Thread* thread, const Type& type) {
72 DCHECK(type.mro().isNoneType(),
73 "Enabling overflow is unsafe after initialization");
74 thread->runtime()->layoutSetTupleOverflow(
75 Layout::cast(type.instanceLayout()));
76}
77
78RawObject findBuiltinTypeWithName(Thread* thread, const Object& name) {
79 DCHECK(Runtime::isInternedStr(thread, name), "must be interned str");
80 HandleScope scope(thread);
81 Runtime* runtime = thread->runtime();
82 Object layout(&scope, NoneType::object());
83 Object type_obj(&scope, NoneType::object());
84 for (int i = 0; i <= static_cast<int>(LayoutId::kLastBuiltinId); i++) {
85 layout = runtime->layoutAtSafe(static_cast<LayoutId>(i));
86 if (layout.isErrorNotFound()) continue;
87 type_obj = Layout::cast(*layout).describedType();
88 if (!type_obj.isType()) continue;
89 if (Type::cast(*type_obj).name() == name) {
90 return *type_obj;
91 }
92 }
93 return Error::notFound();
94}
95
96RawObject raiseTypeErrorCannotSetImmutable(Thread* thread, const Type& type) {
97 HandleScope scope(thread);
98 Object type_name(&scope, type.name());
99 return thread->raiseWithFmt(
100 LayoutId::kTypeError,
101 "can't set attributes of built-in/extension type '%S'", &type_name);
102}
103
104RawObject typeAt(const Type& type, const Object& name) {
105 RawObject result = NoneType::object();
106 if (!attributeAt(*type, *name, &result)) return Error::notFound();
107 return result;
108}
109
110RawObject typeAtById(Thread* thread, const Type& type, SymbolId id) {
111 RawObject name = thread->runtime()->symbols()->at(id);
112 RawObject result = NoneType::object();
113 if (!attributeAt(*type, name, &result)) return Error::notFound();
114 return result;
115}
116
117// Returns type flags updated with the given attribute.
118static word computeAttributeTypeFlags(Thread* thread, const Type& type,
119 SymbolId name, word flags) {
120 Runtime* runtime = thread->runtime();
121 // Custom MROs can contain types that are not part of the subclass hierarchy
122 // which does not fit our cache invalidation strategy for the flags. Be safe
123 // and do not set any!
124 if (flags & Type::Flag::kHasCustomMro) {
125 return flags & ~Type::kAttributeFlags;
126 }
127 if (name == ID(__getattribute__)) {
128 RawObject value = typeLookupInMroById(thread, *type, name);
129 if (value == runtime->objectDunderGetattribute()) {
130 flags |= Type::Flag::kHasObjectDunderGetattribute;
131 } else {
132 flags &= ~Type::Flag::kHasObjectDunderGetattribute;
133 }
134 if (value == runtime->typeDunderGetattribute()) {
135 flags |= Type::Flag::kHasTypeDunderGetattribute;
136 } else {
137 flags &= ~Type::Flag::kHasTypeDunderGetattribute;
138 }
139 if (value == runtime->moduleDunderGetattribute()) {
140 flags |= Type::Flag::kHasModuleDunderGetattribute;
141 } else {
142 flags &= ~Type::Flag::kHasModuleDunderGetattribute;
143 }
144 return flags;
145 }
146 if (name == ID(__new__)) {
147 RawObject value = typeLookupInMroById(thread, *type, name);
148 if (value == runtime->objectDunderNew()) {
149 flags |= Type::Flag::kHasObjectDunderNew;
150 } else {
151 flags &= ~Type::Flag::kHasObjectDunderNew;
152 }
153 return flags;
154 }
155 if (name == ID(__hash__)) {
156 RawObject value = typeLookupInMroById(thread, *type, name);
157 if (value == runtime->objectDunderHash()) {
158 flags |= Type::Flag::kHasObjectDunderHash;
159 } else if (value == runtime->strDunderHash()) {
160 flags |= Type::Flag::kHasStrDunderHash;
161 } else {
162 flags &=
163 ~(Type::Flag::kHasObjectDunderHash | Type::Flag::kHasStrDunderHash);
164 }
165 return flags;
166 }
167 if (name == ID(__bool__)) {
168 RawObject value = typeLookupInMroById(thread, *type, name);
169 if (!value.isErrorNotFound()) {
170 flags |= Type::Flag::kHasDunderBool;
171 } else {
172 flags &= ~Type::Flag::kHasDunderBool;
173 }
174 return flags;
175 }
176 if (name == ID(__len__)) {
177 RawObject value = typeLookupInMroById(thread, *type, name);
178 if (!value.isErrorNotFound()) {
179 flags |= Type::Flag::kHasDunderLen;
180 } else {
181 flags &= ~Type::Flag::kHasDunderLen;
182 }
183 return flags;
184 }
185 if (name == ID(__class__)) {
186 RawObject value = typeLookupInMroById(thread, *type, name);
187 if (value == runtime->objectDunderClass()) {
188 flags |= Type::Flag::kHasObjectDunderClass;
189 } else {
190 flags &= ~Type::Flag::kHasObjectDunderClass;
191 }
192 return flags;
193 }
194 if (name == ID(__eq__)) {
195 RawObject value = typeLookupInMroById(thread, *type, name);
196 if (value == runtime->objectDunderEq()) {
197 flags |= Type::Flag::kHasObjectDunderEq;
198 } else {
199 flags &= ~Type::Flag::kHasObjectDunderEq;
200 }
201 return flags;
202 }
203 if (name == ID(__get__)) {
204 RawObject value = typeLookupInMroById(thread, *type, name);
205 if (!value.isErrorNotFound()) {
206 flags |= Type::Flag::kHasDunderGet;
207 } else {
208 flags &= ~Type::Flag::kHasDunderGet;
209 }
210 return flags;
211 }
212 if (name == ID(__set__)) {
213 RawObject value = typeLookupInMroById(thread, *type, name);
214 if (!value.isErrorNotFound()) {
215 flags |= Type::Flag::kHasDunderSet;
216 } else {
217 flags &= ~Type::Flag::kHasDunderSet;
218 }
219 return flags;
220 }
221 if (name == ID(__delete__)) {
222 RawObject value = typeLookupInMroById(thread, *type, name);
223 if (!value.isErrorNotFound()) {
224 flags |= Type::Flag::kHasDunderDelete;
225 } else {
226 flags &= ~Type::Flag::kHasDunderDelete;
227 }
228 return flags;
229 }
230 return flags;
231}
232
233// Propagate attribute type flags through `type`'s descendents.
234static void typePropagateAttributeTypeFlag(Thread* thread, const Type& type,
235 SymbolId attr_name) {
236 Type::Flag new_flags = Type::Flag::kNone;
237 new_flags = static_cast<Type::Flag>(
238 computeAttributeTypeFlags(thread, type, attr_name, type.flags()));
239 if (new_flags == type.flags()) {
240 // Stop tree traversal since flags doesn't change for this type and its
241 // subclases.
242 return;
243 }
244 type.setFlags(new_flags);
245 if (type.subclasses().isNoneType()) {
246 return;
247 }
248 HandleScope scope(thread);
249 List subclasses(&scope, type.subclasses());
250 Type subclass(&scope, thread->runtime()->typeAt(LayoutId::kObject));
251 for (word i = 0, length = subclasses.numItems(); i < length; ++i) {
252 RawObject referent = WeakRef::cast(subclasses.at(i)).referent();
253 if (referent.isNoneType()) continue;
254 subclass = referent.rawCast<RawType>();
255 if (typeAtById(thread, subclass, attr_name).isErrorNotFound()) {
256 // subclass inherits the attribute for `attr_name`.
257 typePropagateAttributeTypeFlag(thread, subclass, attr_name);
258 }
259 }
260}
261
262static const SymbolId kAttributesForTypeFlags[] = {
263 ID(__getattribute__), ID(__new__), ID(__hash__), ID(__bool__),
264 ID(__len__), ID(__class__), ID(__get__), ID(__set__),
265 ID(__delete__), ID(__eq__),
266};
267
268// Returns `SymbolId` for `attr_name` if given `attr_name` is marked in
269// Type::Flags. Returns `SymbolId::kInvalid` otherwise. See Type::Flags.
270static SymbolId attributeForTypeFlag(Thread* thread, const Object& attr_name) {
271 Symbols* symbols = thread->runtime()->symbols();
272 for (SymbolId id : kAttributesForTypeFlags) {
273 if (attr_name == symbols->at(id)) return id;
274 }
275 return SymbolId::kInvalid;
276}
277
278RawObject typeAtPut(Thread* thread, const Type& type, const Object& name,
279 const Object& value) {
280 DCHECK(thread->runtime()->isInternedStr(thread, name),
281 "name should be an interned str");
282 RawValueCell value_cell =
283 ValueCell::cast(attributeValueCellAtPut(thread, type, name));
284 value_cell.setValue(*value);
285 if (!value_cell.dependencyLink().isNoneType()) {
286 HandleScope scope(thread);
287 ValueCell value_cell_obj(&scope, value_cell);
288 icInvalidateAttr(thread, type, name, value_cell_obj);
289 return *value_cell_obj;
290 }
291 SymbolId attr_name_for_type_flag = attributeForTypeFlag(thread, name);
292 if (attributeForTypeFlag(thread, name) != SymbolId::kInvalid) {
293 typePropagateAttributeTypeFlag(thread, type, attr_name_for_type_flag);
294 }
295 return value_cell;
296}
297
298RawObject typeAtPutById(Thread* thread, const Type& type, SymbolId id,
299 const Object& value) {
300 HandleScope scope(thread);
301 Object name(&scope, thread->runtime()->symbols()->at(id));
302 return typeAtPut(thread, type, name, value);
303}
304
305RawObject typeAtSetLocation(RawType type, RawObject name, word hash,
306 Object* location) {
307 RawObject result = NoneType::object();
308 if (!attributeValueCellAtWithHash(type, name, hash, &result) ||
309 ValueCell::cast(result).isPlaceholder()) {
310 return Error::notFound();
311 }
312 if (location != nullptr) {
313 *location = result;
314 }
315 return ValueCell::cast(result).value();
316}
317
318bool typeIsDataDescriptor(RawType type) {
319 word flags = type.flags();
320 return (flags & Type::Flag::kHasDunderSet) ||
321 (flags & Type::Flag::kHasDunderDelete);
322}
323
324bool typeIsNonDataDescriptor(RawType type) {
325 word flags = type.flags();
326 return flags & Type::Flag::kHasDunderGet;
327}
328
329RawObject resolveDescriptorGet(Thread* thread, const Object& descr,
330 const Object& instance,
331 const Object& instance_type) {
332 if (!typeIsNonDataDescriptor(
333 thread->runtime()->typeOf(*descr).rawCast<RawType>())) {
334 return *descr;
335 }
336 return Interpreter::callDescriptorGet(thread, descr, instance, instance_type);
337}
338
339RawObject typeAssignFromDict(Thread* thread, const Type& type,
340 const Dict& dict) {
341 HandleScope scope(thread);
342 Object key(&scope, NoneType::object());
343 Object value(&scope, NoneType::object());
344 for (word i = 0; dictNextItem(dict, &i, &key, &value);) {
345 DCHECK(!(value.isValueCell() && ValueCell::cast(*value).isPlaceholder()),
346 "value should not be a placeholder value cell");
347 key = attributeName(thread, key);
348 if (key.isErrorException()) return *key;
349 typeAtPut(thread, type, key, value);
350 }
351 return NoneType::object();
352}
353
354RawObject typeLookupInMroSetLocation(Thread* thread, RawType type,
355 RawObject name, Object* location) {
356 RawTuple mro = Tuple::cast(type.mro());
357 word hash = internedStrHash(name);
358 for (word i = 0, length = mro.length(); i < length; i++) {
359 DCHECK(thread->runtime()->isInstanceOfType(mro.at(i)), "non-type in MRO");
360 RawType mro_type = mro.at(i).rawCast<RawType>();
361 RawObject result = typeAtSetLocation(mro_type, name, hash, location);
362 if (!result.isErrorNotFound()) {
363 return result;
364 }
365 }
366 return Error::notFound();
367}
368
369RawObject typeDeleteAttribute(Thread* thread, const Type& type,
370 const Object& name) {
371 HandleScope scope(thread);
372 Runtime* runtime = thread->runtime();
373 terminateIfUnimplementedTypeAttrCacheInvalidation(thread, type, name);
374 if (!type.hasMutableDict()) {
375 return raiseTypeErrorCannotSetImmutable(thread, type);
376 }
377
378 // Check for a delete descriptor
379 Type metatype(&scope, runtime->typeOf(*type));
380 Object meta_attr(&scope, typeLookupInMro(thread, *metatype, *name));
381 if (!meta_attr.isError() && runtime->isDeleteDescriptor(thread, meta_attr)) {
382 return Interpreter::callDescriptorDelete(thread, meta_attr, type);
383 }
384
385 // No delete descriptor found, attempt to delete from the type dict
386 if (typeRemove(thread, type, name).isErrorNotFound()) {
387 Str type_name(&scope, type.name());
388 return thread->raiseWithFmt(LayoutId::kAttributeError,
389 "type object '%S' has no attribute '%S'",
390 &type_name, &name);
391 }
392 return NoneType::object();
393}
394
395RawObject typeLookupInMroById(Thread* thread, RawType type, SymbolId id) {
396 return typeLookupInMro(thread, type, thread->runtime()->symbols()->at(id));
397}
398
399RawObject typeRemove(Thread* thread, const Type& type, const Object& name) {
400 DCHECK(Runtime::isInternedStr(thread, name), "expected interned str");
401 HandleScope scope(thread);
402 word index;
403 Object value_cell_obj(&scope, NoneType::object());
404 if (!attributeFindForRemoval(type, name, &value_cell_obj, &index)) {
405 return Error::notFound();
406 }
407 ValueCell value_cell(&scope, *value_cell_obj);
408 icInvalidateAttr(thread, type, name, value_cell);
409 attributeRemove(type, index);
410 if (value_cell.isPlaceholder()) return Error::notFound();
411 SymbolId attr_name_for_type_flag = attributeForTypeFlag(thread, name);
412 if (attributeForTypeFlag(thread, name) != SymbolId::kInvalid) {
413 typePropagateAttributeTypeFlag(thread, type, attr_name_for_type_flag);
414 }
415 return value_cell.value();
416}
417
418RawObject typeRemoveById(Thread* thread, const Type& type, SymbolId id) {
419 HandleScope scope(thread);
420 Object name(&scope, thread->runtime()->symbols()->at(id));
421 return typeRemove(thread, type, name);
422}
423
424RawObject typeKeys(Thread* thread, const Type& type) {
425 return attributeKeys(thread, type);
426}
427
428word typeLen(Thread* thread, const Type& type) {
429 return attributeLen(thread, type);
430}
431
432RawObject typeValues(Thread* thread, const Type& type) {
433 return attributeValues(thread, type);
434}
435
436RawObject typeGetAttribute(Thread* thread, const Type& type,
437 const Object& name) {
438 return typeGetAttributeSetLocation(thread, type, name, nullptr);
439}
440
441RawObject typeGetAttributeSetLocation(Thread* thread, const Type& type,
442 const Object& name,
443 Object* location_out) {
444 // Look for the attribute in the meta class
445 HandleScope scope(thread);
446 Runtime* runtime = thread->runtime();
447 Type meta_type(&scope, runtime->typeOf(*type));
448 Object meta_attr(&scope, typeLookupInMro(thread, *meta_type, *name));
449 if (!meta_attr.isError()) {
450 // TODO(T56002494): Remove this once type.__getattribute__ gets cached.
451 if (meta_attr.isProperty()) {
452 Object getter(&scope, Property::cast(*meta_attr).getter());
453 if (!getter.isNoneType()) {
454 return Interpreter::call1(thread, getter, type);
455 }
456 }
457 Type meta_attr_type(&scope, runtime->typeOf(*meta_attr));
458 if (typeIsDataDescriptor(*meta_attr_type)) {
459 return Interpreter::callDescriptorGet(thread, meta_attr, type, meta_type);
460 }
461 }
462
463 // No data descriptor found on the meta class, look in the mro of the type
464 Object attr(&scope,
465 typeLookupInMroSetLocation(thread, *type, *name, location_out));
466 if (!attr.isError()) {
467 // TODO(T56002494): Remove this once type.__getattribute__ gets cached.
468 if (attr.isFunction()) {
469 // We always return the function object itself instead of a BoundMethod
470 // due to the exception made below and another exception for NoneType in
471 // function.__get__.
472 return *attr;
473 }
474 Type attr_type(&scope, runtime->typeOf(*attr));
475 if (typeIsNonDataDescriptor(*attr_type)) {
476 // Unfortunately calling `__get__` for a lookup on `type(None)` will look
477 // exactly the same as calling it for a lookup on the `None` object.
478 // To solve the ambiguity we add a special case for `type(None)` here.
479 // Luckily it is impossible for the user to change the type so we can
480 // special case the desired lookup behavior here.
481 // Also see `METH(function, __get__)` for the related special casing
482 // of lookups on the `None` object.
483 if (type.builtinBase() == LayoutId::kNoneType) {
484 return *attr;
485 }
486 if (location_out != nullptr) {
487 *location_out = NoneType::object();
488 }
489 Object none(&scope, NoneType::object());
490 return Interpreter::callDescriptorGet(thread, attr, none, type);
491 }
492 return *attr;
493 }
494
495 // No data descriptor found on the meta class, look on the type
496 Object result(&scope, instanceGetAttribute(thread, type, name));
497 if (!result.isError()) {
498 return *result;
499 }
500
501 // No attr found in type or its mro, use the non-data descriptor found in
502 // the metaclass (if any).
503 if (!meta_attr.isError()) {
504 return resolveDescriptorGet(thread, meta_attr, type, meta_type);
505 }
506
507 return Error::notFound();
508}
509
510static void addSubclass(Thread* thread, const Type& base, const Type& type) {
511 HandleScope scope(thread);
512 Runtime* runtime = thread->runtime();
513 if (base.subclasses().isNoneType()) {
514 base.setSubclasses(runtime->newList());
515 }
516 List subclasses(&scope, base.subclasses());
517 Object value(&scope, runtime->newWeakRef(thread, type));
518 runtime->listAdd(thread, subclasses, value);
519}
520
521void typeAddDocstring(Thread* thread, const Type& type) {
522 // If the type dictionary doesn't contain a __doc__, set it from the doc
523 // slot
524 if (typeAtById(thread, type, ID(__doc__)).isErrorNotFound()) {
525 HandleScope scope(thread);
526 Object doc(&scope, type.doc());
527 typeAtPutById(thread, type, ID(__doc__), doc);
528 }
529}
530
531void typeAddInstanceDict(Thread* thread, const Type& type) {
532 HandleScope scope(thread);
533 Runtime* runtime = thread->runtime();
534 Object instance_proxy(&scope, runtime->typeAt(LayoutId::kInstanceProxy));
535 CHECK(instance_proxy.isType(), "instance_proxy not found");
536 Module under_builtins(&scope, runtime->findModuleById(ID(_builtins)));
537 Function under_instance_dunder_dict_set(
538 &scope,
539 moduleAtById(thread, under_builtins, ID(_instance_dunder_dict_set)));
540 Object none(&scope, NoneType::object());
541 Object property(&scope,
542 runtime->newProperty(instance_proxy,
543 under_instance_dunder_dict_set, none));
544 typeAtPutById(thread, type, ID(__dict__), property);
545}
546
547static RawObject fixedAttributeBaseOfType(Thread* thread, const Type& type);
548
549// This searches recursively through `bases` for classes with the
550// `kIsFixedAttributeBase` flag set. The algorithm picks the entry in bases
551// which leads to a fixed attribute base class that is equal or a superclass of
552// the fixed attribute bases found by the other bases entries.
553// If `get_fixed_attr_base` is false, then the fixed attribute base is returned.
554// If it is true, then the first entry in `bases` that is superclass of the
555// fixed attribute base is returned.
556static RawObject computeFixedAttributeBaseImpl(Thread* thread,
557 const Tuple& bases,
558 bool get_fixed_attr_base) {
559 HandleScope scope(thread);
560 Type result(&scope, bases.at(0));
561 if (!result.isBasetype()) {
562 Object type_name(&scope, result.name());
563 return thread->raiseWithFmt(LayoutId::kTypeError,
564 "type '%S' is not an acceptable base type",
565 &type_name);
566 }
567 word bases_length = bases.length();
568 if (!get_fixed_attr_base && bases_length == 1) {
569 return *result;
570 }
571
572 Type result_fixed_attr_base(&scope, fixedAttributeBaseOfType(thread, result));
573 Type base(&scope, *result);
574 Type fixed_attr_base(&scope, *result);
575 for (word i = 1, length = bases.length(); i < length; i++) {
576 base = bases.at(i);
577 if (!base.isBasetype()) {
578 Object type_name(&scope, base.name());
579 return thread->raiseWithFmt(LayoutId::kTypeError,
580 "type '%S' is not an acceptable base type",
581 &type_name);
582 }
583
584 fixed_attr_base = fixedAttributeBaseOfType(thread, base);
585 if (typeIsSubclass(*result_fixed_attr_base, *fixed_attr_base)) {
586 continue;
587 }
588 if (typeIsSubclass(*fixed_attr_base, *result_fixed_attr_base)) {
589 result = *base;
590 result_fixed_attr_base = *fixed_attr_base;
591 } else {
592 return thread->raiseWithFmt(
593 LayoutId::kTypeError,
594 "multiple bases have instance lay-out conflict");
595 }
596 }
597 return get_fixed_attr_base ? *result_fixed_attr_base : *result;
598}
599
600// Returns the most generic base of `type` on `type's type hierarchy that
601// contains all in-object attributes of `type`. Note that this is designed to
602// simulate `solid_base` from CPython's typeobject.c.
603static RawObject fixedAttributeBaseOfType(Thread* thread, const Type& type) {
604 if (type.hasFlag(Type::Flag::kIsFixedAttributeBase)) {
605 return *type;
606 }
607 HandleScope scope(thread);
608 Tuple bases(&scope, type.bases());
609 return computeFixedAttributeBaseImpl(thread, bases, true);
610}
611
612RawObject computeFixedAttributeBase(Thread* thread, const Tuple& bases) {
613 return computeFixedAttributeBaseImpl(thread, bases, false);
614}
615
616static RawObject validateSlots(Thread* thread, const Type& type,
617 const Tuple& slots, bool base_has_instance_dict,
618 bool* add_instance_dict) {
619 HandleScope scope(thread);
620 word slots_len = slots.length();
621 Runtime* runtime = thread->runtime();
622 Str dunder_dict(&scope, runtime->symbols()->at(ID(__dict__)));
623 *add_instance_dict = false;
624 List result(&scope, runtime->newList());
625 Object slot_obj(&scope, NoneType::object());
626 Str slot_str(&scope, Str::empty());
627 Object type_name(&scope, type.name());
628 for (word i = 0; i < slots_len; i++) {
629 slot_obj = slots.at(i);
630 if (!runtime->isInstanceOfStr(*slot_obj)) {
631 return thread->raiseWithFmt(LayoutId::kTypeError,
632 "__slots__ items must be strings, not '%T'",
633 &slot_obj);
634 }
635 slot_str = strUnderlying(*slot_obj);
636 if (!strIsIdentifier(slot_str)) {
637 return thread->raiseWithFmt(LayoutId::kTypeError,
638 "__slots__ must be identifiers");
639 }
640 slot_str = mangle(thread, type_name, slot_str);
641 slot_str = Runtime::internStr(thread, slot_str);
642 if (slot_str == dunder_dict) {
643 if (base_has_instance_dict || *add_instance_dict) {
644 return thread->raiseWithFmt(
645 LayoutId::kTypeError,
646 "__dict__ slot disallowed: we already got one");
647 }
648 *add_instance_dict = true;
649 continue;
650 }
651 if (!typeAt(type, slot_str).isErrorNotFound()) {
652 return thread->raiseWithFmt(
653 LayoutId::kValueError,
654 "'%S' in __slots__ conflicts with class variable", &slot_str);
655 }
656 runtime->listAdd(thread, result, slot_str);
657 }
658 if (result.numItems() == 0) return NoneType::object();
659 listSort(thread, result);
660 return *result;
661}
662
663static word estimateNumAttributes(Thread* thread, const Type& type) {
664 HandleScope scope(thread);
665 Runtime* runtime = thread->runtime();
666 // Collect set of in-object attributes by scanning the __init__ method of
667 // each class in the MRO. This is used to determine the number of slots
668 // allocated for in-object attributes when instances are created.
669 Tuple mro(&scope, type.mro());
670 Dict attr_names(&scope, runtime->newDict());
671 for (word i = 0; i < mro.length(); i++) {
672 Type mro_type(&scope, mro.at(i));
673 Object maybe_init(&scope, runtime->classConstructor(mro_type));
674 if (!maybe_init.isFunction()) {
675 continue;
676 }
677 Function init(&scope, *maybe_init);
678 RawObject maybe_code = init.code();
679 if (!maybe_code.isCode()) {
680 continue; // native trampoline
681 }
682 Code code(&scope, maybe_code);
683 if (code.code().isSmallInt()) {
684 continue; // builtin trampoline
685 }
686 runtime->collectAttributes(code, attr_names);
687 }
688 return attr_names.numItems();
689}
690
691static void setSlotAttributes(Thread* thread, const MutableTuple& dst,
692 word start_index, const List& slots) {
693 HandleScope scope(thread);
694 Object descriptor(&scope, NoneType::object());
695 Object name(&scope, NoneType::object());
696 Runtime* runtime = thread->runtime();
697 for (word i = 0, offset = start_index * kPointerSize; i < slots.numItems();
698 i++, offset += kPointerSize) {
699 descriptor = slots.at(i);
700 AttributeInfo info(offset, AttributeFlags::kInObject |
701 AttributeFlags::kFixedOffset |
702 AttributeFlags::kInitWithUnbound);
703 name = SlotDescriptor::cast(*descriptor).name();
704 dst.atPut(start_index + i, runtime->layoutNewAttribute(name, info));
705 SlotDescriptor::cast(*descriptor).setOffset(offset);
706 }
707}
708
709static RawObject typeComputeLayout(Thread* thread, const Type& type,
710 const Type& fixed_attr_base,
711 bool enable_overflow, const Object& slots) {
712 HandleScope scope(thread);
713 Runtime* runtime = thread->runtime();
714 Tuple bases(&scope, type.bases());
715
716 // Create new layout.
717 DCHECK(type.instanceLayout().isNoneType(),
718 "must not set layout multiple times");
719 LayoutId layout_id = runtime->reserveLayoutId(thread);
720 Layout layout(&scope, runtime->newLayout(layout_id));
721
722 // Compute in-object attributes.
723 Layout base_layout(&scope, fixed_attr_base.instanceLayout());
724 Tuple base_attributes(&scope, base_layout.inObjectAttributes());
725 word num_base_attributes = base_attributes.length();
726 word num_slots = slots.isList() ? List::cast(*slots).numItems() : 0;
727 word num_initial_attributes = num_base_attributes + num_slots;
728 if (num_initial_attributes == 0) {
729 layout.setInObjectAttributes(runtime->emptyTuple());
730 } else {
731 MutableTuple attributes(&scope,
732 runtime->newMutableTuple(num_initial_attributes));
733 attributes.replaceFromWith(0, *base_attributes, num_base_attributes);
734 if (num_slots > 0) {
735 List slots_list(&scope, *slots);
736 setSlotAttributes(thread, attributes, /*start_index=*/num_base_attributes,
737 slots_list);
738 }
739 layout.setInObjectAttributes(attributes.becomeImmutable());
740 }
741
742 word num_extra_attributes = estimateNumAttributes(thread, type);
743 layout.setNumInObjectAttributes(num_initial_attributes +
744 num_extra_attributes);
745
746 // Determine overflow mode.
747 Type base(&scope, *type);
748 Object overflow(&scope, base_layout.overflowAttributes());
749 Object base_overflow(&scope, NoneType::object());
750 bool bases_have_custom_dict = false;
751 for (word i = 0, length = bases.length(); i < length; i++) {
752 base = bases.at(i);
753 if (base.hasCustomDict()) bases_have_custom_dict = true;
754 base_overflow = Layout::cast(base.instanceLayout()).overflowAttributes();
755 if (overflow == base_overflow || base_overflow.isNoneType()) continue;
756 if (overflow.isNoneType() && base_overflow.isTuple()) {
757 overflow = *base_overflow;
758 continue;
759 }
760 UNIMPLEMENTED("Mixing dict and tuple overflow");
761 }
762 DCHECK(!overflow.isTuple() || Tuple::cast(*overflow).length() == 0,
763 "base layout must not have tuple overflow attributes assigned");
764 if (enable_overflow && !bases_have_custom_dict && overflow.isNoneType()) {
765 overflow = runtime->emptyTuple();
766 }
767 layout.setOverflowAttributes(*overflow);
768
769 runtime->layoutAtPut(layout_id, *layout);
770 layout.setDescribedType(*type);
771 type.setInstanceLayout(*layout);
772 type.setInstanceLayoutId(layout.id());
773 type.setBuiltinBase(fixed_attr_base.builtinBase());
774 return NoneType::object();
775}
776
777static RawObject getModuleNameAtFrame(Thread* thread, int depth) {
778 HandleScope scope(thread);
779 Frame* frame = thread->currentFrame();
780 for (int i = 0; i < depth && !frame->isSentinel(); i++) {
781 frame = frame->previousFrame();
782 }
783 if (frame->isSentinel()) return Error::notFound();
784 Object module_obj(&scope, frame->function().moduleObject());
785 // Some functions (e.g C-API extension functions) have no associated module.
786 if (module_obj.isNoneType()) return Error::notFound();
787 Module module(&scope, *module_obj);
788 return moduleAtById(thread, module, ID(__name__));
789}
790
791static RawObject typeSetNames(Thread* thread, const Type& type,
792 const Dict& dict) {
793 HandleScope scope(thread);
794 Runtime* runtime = thread->runtime();
795 Object key(&scope, NoneType::object());
796 Object value(&scope, NoneType::object());
797 Object result(&scope, NoneType::object());
798 Object set_name(&scope, NoneType::object());
799
800 for (word i = 0; dictNextItem(dict, &i, &key, &value);) {
801 // If a method is looked up during bootstrapping (which we do during
802 // typeNew(), there is a chance that the MRO won't be present yet. This
803 // check allows dealing with that case gracefully.
804 if (runtime->typeOf(*value).mro().isNoneType()) {
805 continue;
806 }
807
808 set_name = Interpreter::lookupMethod(thread, value, ID(__set_name__));
809 if (set_name.isError()) {
810 if (set_name.isErrorException()) {
811 return *set_name;
812 }
813 DCHECK(set_name.isErrorNotFound(), "expected not found");
814 continue;
815 }
816
817 result = Interpreter::callMethod3(thread, set_name, value, type, key);
818 if (result.isErrorException()) {
819 Str type_name(&scope, type.name());
820 return thread->raiseWithFmtChainingPendingAsCause(
821 LayoutId::kRuntimeError,
822 "Error calling __set_name__ on '%T' instance in %S", &value,
823 &type_name);
824 }
825 }
826 return NoneType::object();
827}
828
829RawObject typeNew(Thread* thread, const Type& metaclass, const Str& name,
830 const Tuple& bases, const Dict& dict, word flags,
831 bool inherit_slots, bool add_instance_dict) {
832 HandleScope scope(thread);
833 Runtime* runtime = thread->runtime();
834
835 LayoutId metaclass_id = Layout::cast(metaclass.instanceLayout()).id();
836 Type type(&scope, runtime->newTypeWithMetaclass(metaclass_id));
837 type.setName(*name);
838 type.setBases(*bases);
839
840 Object fixed_attr_base_obj(&scope, computeFixedAttributeBase(thread, bases));
841 if (fixed_attr_base_obj.isErrorException()) return *fixed_attr_base_obj;
842 Type fixed_attr_base(&scope, *fixed_attr_base_obj);
843
844 // Determine metaclass.mro method. Set `mro_method` to `None` if it is the
845 // default `type.mro`.
846 Object mro_method(&scope, Unbound::object());
847 if (metaclass_id != LayoutId::kType) {
848 mro_method = Interpreter::lookupMethod(thread, type, ID(mro));
849 if (mro_method.isErrorException()) return *mro_method;
850 if (mro_method.isErrorNotFound()) {
851 Object mro_name(&scope, runtime->symbols()->at(ID(mro)));
852 return objectRaiseAttributeError(thread, metaclass, mro_name);
853 }
854 Type builtin_type(&scope, runtime->typeAt(LayoutId::kType));
855 if (mro_method == typeAtById(thread, builtin_type, ID(mro))) {
856 mro_method = Unbound::object();
857 }
858 }
859 Object mro_obj(&scope, NoneType::object());
860 if (mro_method.isUnbound()) {
861 mro_obj = computeMro(thread, type);
862 if (mro_obj.isErrorException()) return *mro_obj;
863 } else {
864 flags |= Type::Flag::kHasCustomMro;
865 mro_obj = Interpreter::callMethod1(thread, mro_method, type);
866 if (mro_obj.isErrorException()) return *mro_obj;
867 if (!mro_obj.isTuple()) {
868 mro_obj = thread->invokeFunction1(ID(builtins), ID(tuple), mro_obj);
869 if (mro_obj.isErrorException()) return *mro_obj;
870 CHECK(mro_obj.isTuple(), "Result of builtins.tuple should be tuple");
871 }
872 }
873
874 Tuple mro(&scope, *mro_obj);
875 type.setMro(*mro);
876
877 Object result(&scope, typeAssignFromDict(thread, type, dict));
878 if (result.isErrorException()) return *result;
879
880 if (flags & Type::Flag::kIsCPythonHeaptype) {
881 if (typeAtById(thread, type, ID(__module__)).isErrorNotFound()) {
882 // Use depth=3 to skip over frame of `_type_init`, `type.__new__`
883 // and `type.__call__`.
884 Object module_name(&scope, getModuleNameAtFrame(thread, /*depth=*/3));
885 if (!module_name.isErrorNotFound()) {
886 typeAtPutById(thread, type, ID(__module__), module_name);
887 }
888 }
889 } else {
890 // Non-heap-types in CPython have no `__module__` unless there is a
891 // "." in `tp_name`. Remove the attribute when it equals "builtins".
892 Object module_name(&scope, typeAtById(thread, type, ID(__module__)));
893 if (module_name.isStr() &&
894 Str::cast(*module_name).equals(runtime->symbols()->at(ID(builtins)))) {
895 typeRemoveById(thread, type, ID(__module__));
896 }
897 }
898
899 Object qualname(&scope, typeRemoveById(thread, type, ID(__qualname__)));
900 if (qualname.isErrorNotFound()) {
901 qualname = *name;
902 } else if (!runtime->isInstanceOfStr(*qualname)) {
903 return thread->raiseWithFmt(LayoutId::kTypeError,
904 "type __qualname__ must be a str, not %T",
905 &qualname);
906 }
907 type.setQualname(*qualname);
908
909 // TODO(T53997177): Centralize type initialization
910 typeAddDocstring(thread, type);
911
912 // Special-case __new__ to be a staticmethod
913 Object dunder_new(&scope, typeAtById(thread, type, ID(__new__)));
914 if (dunder_new.isFunction()) {
915 StaticMethod dunder_new_method(&scope, runtime->newStaticMethod());
916 dunder_new_method.setFunction(*dunder_new);
917 typeAtPutById(thread, type, ID(__new__), dunder_new_method);
918 }
919
920 // Special-case __init_subclass__ to be a classmethod
921 Object init_subclass(&scope, typeAtById(thread, type, ID(__init_subclass__)));
922 if (init_subclass.isFunction()) {
923 ClassMethod init_subclass_method(&scope, runtime->newClassMethod());
924 init_subclass_method.setFunction(*init_subclass);
925 typeAtPutById(thread, type, ID(__init_subclass__), init_subclass_method);
926 }
927
928 // Special-case __class_getitem__ to be a classmethod
929 Object class_getitem(&scope, typeAtById(thread, type, ID(__class_getitem__)));
930 if (class_getitem.isFunction()) {
931 ClassMethod class_getitem_method(&scope, runtime->newClassMethod());
932 class_getitem_method.setFunction(*class_getitem);
933 typeAtPutById(thread, type, ID(__class_getitem__), class_getitem_method);
934 }
935
936 Object class_cell(&scope, typeAtById(thread, type, ID(__classcell__)));
937 if (!class_cell.isErrorNotFound()) {
938 DCHECK(class_cell.isCell(), "class cell must be a cell");
939 Cell::cast(*class_cell).setValue(*type);
940 Object class_cell_name(&scope, runtime->symbols()->at(ID(__classcell__)));
941 typeRemove(thread, type, class_cell_name);
942 }
943
944 // Analyze bases: Merge flags; add to subclasses lists; check for attribute
945 // dictionaries.
946 Type base_type(&scope, *type);
947 bool bases_have_instance_dict = false;
948 bool bases_have_type_slots = false;
949 word bases_flags = 0;
950 for (word i = 0; i < bases.length(); i++) {
951 base_type = bases.at(i);
952 bases_flags |= base_type.flags();
953 addSubclass(thread, base_type, type);
954 bases_have_type_slots |= typeHasSlots(base_type);
955 if (base_type.hasCustomDict()) bases_have_instance_dict = true;
956 if (!Layout::cast(base_type.instanceLayout()).isSealed()) {
957 bases_have_instance_dict = true;
958 }
959 }
960 if (bases_have_instance_dict) add_instance_dict = false;
961 flags |= (bases_flags & Type::kInheritableFlags);
962 // Attribute flags are set explicitly here since `typeAssignFromDict` cannot
963 // compute it properly with a partially created type object:
964 // Computing this properly depends if Type::Flag::kHasCustomMro is set or not.
965 for (SymbolId id : kAttributesForTypeFlags) {
966 flags = computeAttributeTypeFlags(thread, type, id, flags);
967 }
968 // TODO(T66646764): This is a hack to make `type` look finalized. Remove this.
969 type.setFlags(static_cast<Type::Flag>(flags));
970
971 if (bases_have_type_slots) {
972 if (inherit_slots) {
973 result = typeInheritSlots(thread, type, fixed_attr_base);
974 if (result.isErrorException()) return *result;
975 }
976 }
977
978 Object dunder_slots_obj(&scope, typeAtById(thread, type, ID(__slots__)));
979 Object slots_obj(&scope, NoneType::object());
980 if (!dunder_slots_obj.isErrorNotFound()) {
981 // NOTE: CPython raises an exception when slots are given to a subtype of a
982 // type with type.tp_itemsize != 0, which means having a variable length.
983 // For example, __slots__ in int's subtype or str's type is disallowed.
984 // This behavior is ignored in Pyro since all objects' size in RawObject is
985 // fixed in Pyro.
986 if (runtime->isInstanceOfStr(*dunder_slots_obj)) {
987 dunder_slots_obj = runtime->newTupleWith1(dunder_slots_obj);
988 } else if (!runtime->isInstanceOfTuple(*dunder_slots_obj)) {
989 Type tuple_type(&scope, runtime->typeAt(LayoutId::kTuple));
990 dunder_slots_obj =
991 Interpreter::call1(thread, tuple_type, dunder_slots_obj);
992 if (dunder_slots_obj.isErrorException()) {
993 return *dunder_slots_obj;
994 }
995 CHECK(dunder_slots_obj.isTuple(), "Converting __slots__ to tuple failed");
996 }
997 Tuple slots_tuple(&scope, *dunder_slots_obj);
998 slots_obj = validateSlots(thread, type, slots_tuple,
999 bases_have_instance_dict, &add_instance_dict);
1000 if (slots_obj.isErrorException()) {
1001 return *slots_obj;
1002 }
1003 if (!slots_obj.isNoneType()) {
1004 List slots(&scope, *slots_obj);
1005 // Add descriptors that mediate access to __slots__ attributes.
1006 Object slot_descriptor(&scope, NoneType::object());
1007 Object slot_name(&scope, NoneType::object());
1008 for (word i = 0, num_items = slots.numItems(); i < num_items; i++) {
1009 slot_name = slots.at(i);
1010 slot_descriptor = runtime->newSlotDescriptor(type, slot_name);
1011 typeAtPut(thread, type, slot_name, slot_descriptor);
1012 slots.atPut(i, *slot_descriptor);
1013 }
1014 }
1015 }
1016
1017 if (!slots_obj.isNoneType()) {
1018 flags |= Type::Flag::kHasSlots;
1019 flags |= Type::Flag::kIsFixedAttributeBase;
1020 }
1021
1022 type.setFlags(static_cast<Type::Flag>(flags));
1023
1024 // Add a `__dict__` descriptor when we added an instance dict.
1025 if (add_instance_dict &&
1026 typeAtById(thread, type, ID(__dict__)).isErrorNotFound()) {
1027 typeAddInstanceDict(thread, type);
1028 }
1029
1030 Function type_dunder_call(&scope,
1031 runtime->lookupNameInModule(thread, ID(_builtins),
1032 ID(_type_dunder_call)));
1033 type.setCtor(*type_dunder_call);
1034
1035 result = typeComputeLayout(thread, type, fixed_attr_base, add_instance_dict,
1036 slots_obj);
1037 if (result.isErrorException()) return *result;
1038
1039 result = typeSetNames(thread, type, dict);
1040 if (result.isErrorException()) return *result;
1041
1042 return *type;
1043}
1044
1045// NOTE: Keep the order of these type attributes same as the one from
1046// rewriteOperation.
1047static const SymbolId kUnimplementedTypeAttrUpdates[] = {
1048 // LOAD_ATTR, LOAD_METHOD
1049 ID(__getattribute__),
1050 // STORE_ATTR
1051 ID(__setattr__)};
1052
1053void terminateIfUnimplementedTypeAttrCacheInvalidation(
1054 Thread* thread, const Type& type, const Object& attr_name) {
1055 RawObject unused = NoneType::object();
1056 if (!attributeValueCellAt(*type, *attr_name, &unused)) {
1057 // No need for cache invalidation due to the absence of the attribute.
1058 return;
1059 }
1060 Runtime* runtime = thread->runtime();
1061 DCHECK(Runtime::isInternedStr(thread, attr_name), "expected interned str");
1062 for (uword i = 0; i < ARRAYSIZE(kUnimplementedTypeAttrUpdates); ++i) {
1063 if (attr_name == runtime->symbols()->at(kUnimplementedTypeAttrUpdates[i])) {
1064 UNIMPLEMENTED("unimplemented cache invalidation for type.%s update",
1065 Str::cast(*attr_name).toCStr());
1066 }
1067 }
1068}
1069
1070RawObject typeSetAttr(Thread* thread, const Type& type, const Object& name,
1071 const Object& value) {
1072 Runtime* runtime = thread->runtime();
1073 DCHECK(runtime->isInternedStr(thread, name),
1074 "name must be an interned string");
1075 // Make sure cache invalidation is correctly done for this.
1076 terminateIfUnimplementedTypeAttrCacheInvalidation(thread, type, name);
1077 HandleScope scope(thread);
1078 if (!type.hasMutableDict()) {
1079 return raiseTypeErrorCannotSetImmutable(thread, type);
1080 }
1081
1082 // Check for a data descriptor
1083 Type metatype(&scope, runtime->typeOf(*type));
1084 Object meta_attr(&scope, typeLookupInMro(thread, *metatype, *name));
1085 if (!meta_attr.isError()) {
1086 Type meta_attr_type(&scope, runtime->typeOf(*meta_attr));
1087 if (typeIsDataDescriptor(*meta_attr_type)) {
1088 Object set_result(&scope, Interpreter::callDescriptorSet(
1089 thread, meta_attr, type, value));
1090 if (set_result.isError()) return *set_result;
1091 return NoneType::object();
1092 }
1093 }
1094
1095 // No data descriptor found, store the attribute in the type dict
1096 typeAtPut(thread, type, name, value);
1097 return NoneType::object();
1098}
1099
1100RawObject typeSetDunderClass(Thread* thread, const Object& self,
1101 const Type& new_type) {
1102 Runtime* runtime = thread->runtime();
1103 // TODO(T60761420): A module can't change its type since its attributes are
1104 // cached based on object identity (and not layout id). This needs extra
1105 // cache invalidation code here to support it.
1106 if (runtime->isInstanceOfModule(*self)) {
1107 UNIMPLEMENTED("Cannot change type of modules");
1108 }
1109
1110 HandleScope scope(thread);
1111 Type instance_type(&scope, runtime->typeOf(*self));
1112 // Builtin base type must match
1113 if (instance_type.builtinBase() != new_type.builtinBase()) {
1114 Str type_name(&scope, new_type.name());
1115 return thread->raiseWithFmt(
1116 LayoutId::kTypeError,
1117 "__class__ assignment '%T' object layout differs from '%S'", &self,
1118 &type_name);
1119 }
1120
1121 // Handle C Extension types
1122 if (instance_type.hasFlag(RawType::Flag::kHasNativeData) &&
1123 new_type.hasFlag(RawType::Flag::kHasNativeData)) {
1124 // TODO(T60752528): Handle __class__ setter for C Extension Types
1125 UNIMPLEMENTED("Check if native memory is compatible");
1126 } else if (instance_type.hasFlag(RawType::Flag::kHasNativeData) !=
1127 new_type.hasFlag(RawType::Flag::kHasNativeData)) {
1128 Str type_name(&scope, new_type.name());
1129 return thread->raiseWithFmt(
1130 LayoutId::kTypeError,
1131 "__class__ assignment '%T' object layout differs from '%S'", &self,
1132 &type_name);
1133 }
1134
1135 // LOAD_ATTR_TYPE caches attributes based on the instance layout id of the
1136 // type. Create a copy of the layout with a new id, to avoid incorrectly
1137 // hitting the cache.
1138 if (runtime->isInstanceOfType(*self)) {
1139 Type type(&scope, *self);
1140 Layout instance_layout(&scope, type.instanceLayout());
1141 Layout new_layout(&scope,
1142 runtime->layoutCreateCopy(thread, instance_layout));
1143 type.setInstanceLayout(*new_layout);
1144 type.setInstanceLayoutId(new_layout.id());
1145 }
1146
1147 // Transition the layout
1148 Instance instance(&scope, *self);
1149 Layout from_layout(&scope, runtime->layoutOf(*instance));
1150 Layout new_layout(
1151 &scope, runtime->layoutSetDescribedType(thread, from_layout, new_type));
1152 instance.setLayoutId(new_layout.id());
1153 return NoneType::object();
1154}
1155
1156static const BuiltinAttribute kTypeAttributes[] = {
1157 {ID(_type__attributes), RawType::kAttributesOffset,
1158 AttributeFlags::kHidden},
1159 {ID(_type__attributes_remaining), RawType::kAttributesRemainingOffset,
1160 AttributeFlags::kHidden},
1161 {ID(__mro__), RawType::kMroOffset, AttributeFlags::kReadOnly},
1162 {ID(_type__bases), RawType::kBasesOffset, AttributeFlags::kHidden},
1163 {ID(_type__instance_layout), RawType::kInstanceLayoutOffset,
1164 AttributeFlags::kHidden},
1165 {ID(_type__instance_layout_id), RawType::kInstanceLayoutIdOffset,
1166 AttributeFlags::kHidden},
1167 {ID(_type__name), RawType::kNameOffset, AttributeFlags::kHidden},
1168 {ID(__doc__), RawType::kDocOffset},
1169 {ID(_type__flags), RawType::kFlagsOffset, AttributeFlags::kHidden},
1170 {ID(_type__slots), RawType::kSlotsOffset, AttributeFlags::kHidden},
1171 {ID(_type__abstract_methods), RawType::kAbstractMethodsOffset,
1172 AttributeFlags::kHidden},
1173 {ID(_type__subclasses), RawType::kSubclassesOffset,
1174 AttributeFlags::kHidden},
1175 {ID(_type__proxy), RawType::kProxyOffset, AttributeFlags::kHidden},
1176 {ID(_type__ctor), RawType::kCtorOffset, AttributeFlags::kHidden},
1177 {ID(_type__qualname), RawType::kQualnameOffset, AttributeFlags::kHidden},
1178};
1179
1180static const BuiltinAttribute kTypeProxyAttributes[] = {
1181 {ID(_type_proxy__type), TypeProxy::kTypeOffset, AttributeFlags::kHidden},
1182};
1183
1184void initializeTypeTypes(Thread* thread) {
1185 HandleScope scope(thread);
1186 Type type(&scope, addBuiltinType(thread, ID(type), LayoutId::kType,
1187 /*superclass_id=*/LayoutId::kObject,
1188 kTypeAttributes, Type::kSize,
1189 /*basetype=*/true));
1190 word flags = static_cast<word>(type.flags());
1191 flags |= RawType::Flag::kHasCustomDict;
1192 type.setFlags(static_cast<Type::Flag>(flags));
1193
1194 addBuiltinType(thread, ID(type_proxy), LayoutId::kTypeProxy,
1195 /*superclass_id=*/LayoutId::kObject, kTypeProxyAttributes,
1196 TypeProxy::kSize, /*basetype=*/false);
1197}
1198
1199RawObject METH(type, __base__)(Thread* thread, Arguments args) {
1200 HandleScope scope(thread);
1201 Object self_obj(&scope, args.get(0));
1202 if (!thread->runtime()->isInstanceOfType(*self_obj)) {
1203 return thread->raiseRequiresType(self_obj, ID(type));
1204 }
1205 Type self(&scope, *self_obj);
1206 Tuple bases(&scope, self.bases());
1207 if (bases.length() == 0) {
1208 return NoneType::object();
1209 }
1210 return computeFixedAttributeBase(thread, bases);
1211}
1212
1213RawObject METH(type, __basicsize__)(Thread* thread, Arguments args) {
1214 HandleScope scope(thread);
1215 Runtime* runtime = thread->runtime();
1216 Object self_obj(&scope, args.get(0));
1217 if (!runtime->isInstanceOfType(*self_obj)) {
1218 return thread->raiseRequiresType(self_obj, ID(type));
1219 }
1220 Type self(&scope, *self_obj);
1221 uword basicsize = typeGetBasicSize(self);
1222 return runtime->newIntFromUnsigned(basicsize);
1223}
1224
1225RawObject METH(type, __delattr__)(Thread* thread, Arguments args) {
1226 HandleScope scope(thread);
1227 Object self_obj(&scope, args.get(0));
1228 if (!thread->runtime()->isInstanceOfType(*self_obj)) {
1229 return thread->raiseRequiresType(self_obj, ID(type));
1230 }
1231 Type self(&scope, *self_obj);
1232 Object name(&scope, args.get(1));
1233 name = attributeName(thread, name);
1234 if (name.isErrorException()) return *name;
1235 return typeDeleteAttribute(thread, self, name);
1236}
1237
1238RawObject METH(type, __flags__)(Thread* thread, Arguments args) {
1239 HandleScope scope(thread);
1240 Object self_obj(&scope, args.get(0));
1241 Runtime* runtime = thread->runtime();
1242 if (!runtime->isInstanceOfType(*self_obj)) {
1243 return thread->raiseRequiresType(self_obj, ID(type));
1244 }
1245 Type self(&scope, *self_obj);
1246 uword cpython_flags = typeGetFlags(self);
1247 return runtime->newIntFromUnsigned(cpython_flags);
1248}
1249
1250RawObject METH(type, __getattribute__)(Thread* thread, Arguments args) {
1251 HandleScope scope(thread);
1252 Object self_obj(&scope, args.get(0));
1253 Runtime* runtime = thread->runtime();
1254 if (!runtime->isInstanceOfType(*self_obj)) {
1255 return thread->raiseRequiresType(self_obj, ID(type));
1256 }
1257 Type self(&scope, *self_obj);
1258 Object name(&scope, args.get(1));
1259 name = attributeName(thread, name);
1260 if (name.isErrorException()) return *name;
1261 Object result(&scope, typeGetAttribute(thread, self, name));
1262 if (result.isErrorNotFound()) {
1263 Object type_name(&scope, self.name());
1264 return thread->raiseWithFmt(LayoutId::kAttributeError,
1265 "type object '%S' has no attribute '%S'",
1266 &type_name, &name);
1267 }
1268 return *result;
1269}
1270
1271RawObject METH(type, __setattr__)(Thread* thread, Arguments args) {
1272 HandleScope scope(thread);
1273 Object self_obj(&scope, args.get(0));
1274 Runtime* runtime = thread->runtime();
1275 if (!runtime->isInstanceOfType(*self_obj)) {
1276 return thread->raiseRequiresType(self_obj, ID(type));
1277 }
1278 Type self(&scope, *self_obj);
1279 Object name(&scope, args.get(1));
1280 name = attributeName(thread, name);
1281 if (name.isErrorException()) return *name;
1282 Object value(&scope, args.get(2));
1283 return typeSetAttr(thread, self, name, value);
1284}
1285
1286RawObject METH(type, __subclasses__)(Thread* thread, Arguments args) {
1287 HandleScope scope(thread);
1288 Object self_obj(&scope, args.get(0));
1289 Runtime* runtime = thread->runtime();
1290 if (!runtime->isInstanceOfType(*self_obj)) {
1291 return thread->raiseRequiresType(self_obj, ID(type));
1292 }
1293 Type self(&scope, *self_obj);
1294 Object subclasses_obj(&scope, self.subclasses());
1295 if (subclasses_obj.isNoneType()) {
1296 return runtime->newList();
1297 }
1298
1299 // Check list for `None` and compact it.
1300 List subclasses(&scope, *subclasses_obj);
1301 word num_items = subclasses.numItems();
1302 Object ref(&scope, NoneType::object());
1303 Object value(&scope, NoneType::object());
1304 word compact_shift = 0;
1305 for (word i = 0; i < num_items; i++) {
1306 ref = subclasses.at(i);
1307 value = WeakRef::cast(*ref).referent();
1308 if (value.isNoneType()) {
1309 compact_shift++;
1310 continue;
1311 }
1312 if (compact_shift > 0) {
1313 subclasses.atPut(i - compact_shift, *ref);
1314 }
1315 }
1316 if (compact_shift > 0) {
1317 num_items -= compact_shift;
1318 subclasses.setNumItems(num_items);
1319 }
1320
1321 List result(&scope, runtime->newList());
1322 runtime->listEnsureCapacity(thread, result, num_items);
1323 for (word i = 0; i < num_items; i++) {
1324 ref = subclasses.at(i);
1325 value = WeakRef::cast(*ref).referent();
1326 runtime->listAdd(thread, result, value);
1327 }
1328 return *result;
1329}
1330
1331} // namespace py