Ruby 3.5.0dev (2025-05-16 revision 097d742a1ed53afb91e83aef01365d68b763357b)
shape.h (097d742a1ed53afb91e83aef01365d68b763357b)
1#ifndef RUBY_SHAPE_H
2#define RUBY_SHAPE_H
3
4#include "internal/gc.h"
5
6#if (SIZEOF_UINT64_T <= SIZEOF_VALUE)
7
8#define SIZEOF_SHAPE_T 4
9#define SHAPE_IN_BASIC_FLAGS 1
10typedef uint32_t attr_index_t;
11typedef uint32_t shape_id_t;
12# define SHAPE_ID_NUM_BITS 32
13
14#else
15
16#define SIZEOF_SHAPE_T 2
17#define SHAPE_IN_BASIC_FLAGS 0
18typedef uint16_t attr_index_t;
19typedef uint16_t shape_id_t;
20# define SHAPE_ID_NUM_BITS 16
21
22#endif
23
24typedef uint32_t redblack_id_t;
25
26#define SHAPE_MAX_FIELDS (attr_index_t)(-1)
27
28# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
29
30# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_ID_NUM_BITS)
31
32# define SHAPE_MAX_VARIATIONS 8
33
34# define INVALID_SHAPE_ID (((uintptr_t)1 << SHAPE_ID_NUM_BITS) - 1)
35
36#define ROOT_SHAPE_ID 0x0
37#define SPECIAL_CONST_SHAPE_ID 0x1
38// ROOT_TOO_COMPLEX_SHAPE_ID 0x2
39#define FIRST_T_OBJECT_SHAPE_ID 0x3
40
41extern ID ruby_internal_object_id;
42
43typedef struct redblack_node redblack_node_t;
44
45struct rb_shape {
46 struct rb_id_table *edges; // id_table from ID (ivar) to next shape
47 ID edge_name; // ID (ivar) for transition from parent to rb_shape
48 attr_index_t next_field_index; // Fields are either ivars or internal properties like `object_id`
49 attr_index_t capacity; // Total capacity of the object with this shape
50 uint8_t type;
51 uint8_t heap_index;
52 uint8_t flags;
53 shape_id_t parent_id;
54 redblack_node_t *ancestor_index;
55};
56
57typedef struct rb_shape rb_shape_t;
58
60 ID key;
61 rb_shape_t *value;
62 redblack_id_t l;
63 redblack_id_t r;
64};
65
66enum shape_type {
67 SHAPE_ROOT,
68 SHAPE_IVAR,
69 SHAPE_OBJ_ID,
70 SHAPE_FROZEN,
71 SHAPE_T_OBJECT,
72 SHAPE_OBJ_TOO_COMPLEX,
73};
74
75typedef struct {
76 /* object shapes */
77 rb_shape_t *shape_list;
78 rb_shape_t *root_shape;
79 shape_id_t next_shape_id;
80
81 redblack_node_t *shape_cache;
82 unsigned int cache_size;
84RUBY_EXTERN rb_shape_tree_t *rb_shape_tree_ptr;
85
86static inline rb_shape_tree_t *
87rb_current_shape_tree(void)
88{
89 return rb_shape_tree_ptr;
90}
91#define GET_SHAPE_TREE() rb_current_shape_tree()
92
93static inline shape_id_t
94get_shape_id_from_flags(VALUE obj)
95{
98 return (shape_id_t)((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT);
99}
100
101static inline void
102set_shape_id_in_flags(VALUE obj, shape_id_t shape_id)
103{
106 // Ractors are occupying the upper 32 bits of flags, but only in debug mode
107 // Object shapes are occupying top bits
108 RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
109 RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
110}
111
112
113#if SHAPE_IN_BASIC_FLAGS
114static inline shape_id_t
115RBASIC_SHAPE_ID(VALUE obj)
116{
117 return get_shape_id_from_flags(obj);
118}
119
120static inline void
121RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
122{
123 set_shape_id_in_flags(obj, shape_id);
124}
125#endif
126
127static inline shape_id_t
128ROBJECT_SHAPE_ID(VALUE obj)
129{
130 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
131 return get_shape_id_from_flags(obj);
132}
133
134static inline void
135ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
136{
137 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
138 set_shape_id_in_flags(obj, shape_id);
139}
140
141static inline shape_id_t
142RCLASS_SHAPE_ID(VALUE obj)
143{
145 return get_shape_id_from_flags(obj);
146}
147
148static inline void
149RCLASS_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
150{
152 set_shape_id_in_flags(obj, shape_id);
153}
154
155#define RSHAPE rb_shape_lookup
156
157int32_t rb_shape_id_offset(void);
158
159RUBY_FUNC_EXPORTED rb_shape_t *rb_shape_lookup(shape_id_t shape_id);
160RUBY_FUNC_EXPORTED shape_id_t rb_obj_shape_id(VALUE obj);
161shape_id_t rb_shape_get_next_iv_shape(shape_id_t shape_id, ID id);
162bool rb_shape_get_iv_index(rb_shape_t *shape, ID id, attr_index_t *value);
163bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t *value, shape_id_t *shape_id_hint);
164RUBY_FUNC_EXPORTED bool rb_shape_obj_too_complex_p(VALUE obj);
165bool rb_shape_too_complex_p(rb_shape_t *shape);
166bool rb_shape_id_too_complex_p(shape_id_t shape_id);
167
168void rb_shape_set_shape(VALUE obj, rb_shape_t *shape);
169shape_id_t rb_shape_transition_frozen(VALUE obj);
170shape_id_t rb_shape_transition_complex(VALUE obj);
171bool rb_shape_transition_remove_ivar(VALUE obj, ID id, VALUE *removed);
172shape_id_t rb_shape_transition_add_ivar(VALUE obj, ID id);
173shape_id_t rb_shape_transition_add_ivar_no_warnings(VALUE obj, ID id);
174shape_id_t rb_shape_transition_object_id(VALUE obj);
175
176bool rb_shape_has_object_id(rb_shape_t *shape);
177void rb_shape_free_all(void);
178
179rb_shape_t *rb_shape_rebuild_shape(rb_shape_t *initial_shape, rb_shape_t *dest_shape);
180
181static inline rb_shape_t *
182rb_obj_shape(VALUE obj)
183{
184 return RSHAPE(rb_obj_shape_id(obj));
185}
186
187static inline bool
188rb_shape_canonical_p(rb_shape_t *shape)
189{
190 return !shape->flags;
191}
192
193static inline shape_id_t
194rb_shape_root(size_t heap_id)
195{
196 return (shape_id_t)(heap_id + FIRST_T_OBJECT_SHAPE_ID);
197}
198
199static inline uint32_t
200ROBJECT_FIELDS_CAPACITY(VALUE obj)
201{
202 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
203 // Asking for capacity doesn't make sense when the object is using
204 // a hash table for storing instance variables
205 RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
206 return RSHAPE(ROBJECT_SHAPE_ID(obj))->capacity;
207}
208
209static inline st_table *
210ROBJECT_FIELDS_HASH(VALUE obj)
211{
212 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
213 RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
214 return (st_table *)ROBJECT(obj)->as.heap.fields;
215}
216
217static inline void
218ROBJECT_SET_FIELDS_HASH(VALUE obj, const st_table *tbl)
219{
220 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
221 RUBY_ASSERT(rb_shape_obj_too_complex_p(obj));
222 ROBJECT(obj)->as.heap.fields = (VALUE *)tbl;
223}
224
225size_t rb_id_table_size(const struct rb_id_table *tbl);
226
227static inline uint32_t
228ROBJECT_FIELDS_COUNT(VALUE obj)
229{
230 if (rb_shape_obj_too_complex_p(obj)) {
231 return (uint32_t)rb_st_table_size(ROBJECT_FIELDS_HASH(obj));
232 }
233 else {
234 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
235 RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj));
236 return RSHAPE(ROBJECT_SHAPE_ID(obj))->next_field_index;
237 }
238}
239
240static inline uint32_t
241RBASIC_FIELDS_COUNT(VALUE obj)
242{
243 return RSHAPE(rb_obj_shape_id(obj))->next_field_index;
244}
245
246shape_id_t rb_shape_traverse_from_new_root(shape_id_t initial_shape_id, shape_id_t orig_shape_id);
247
248bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
249
250static inline bool
251rb_shape_obj_has_id(VALUE obj)
252{
253 return rb_shape_has_object_id(rb_obj_shape(obj));
254}
255
256// For ext/objspace
257RUBY_SYMBOL_EXPORT_BEGIN
258typedef void each_shape_callback(rb_shape_t *shape, void *data);
259void rb_shape_each_shape(each_shape_callback callback, void *data);
260size_t rb_shape_memsize(shape_id_t shape);
261size_t rb_shape_edges_count(shape_id_t shape_id);
262size_t rb_shape_depth(shape_id_t shape_id);
263shape_id_t rb_shape_id(rb_shape_t *shape);
264RUBY_SYMBOL_EXPORT_END
265
266#endif
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define RUBY_EXTERN
Declaration of externally visible global variables.
Definition dllexport.h:45
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
VALUE type(ANYARGS)
ANYARGS-ed function type.
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define ROBJECT(obj)
Convenient casting macro.
Definition robject.h:43
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376
@ RUBY_T_OBJECT
Definition value_type.h:116