Ruby 3.5.0dev (2025-02-20 revision 34098b669c0cbc024cd08e686891f1dfe0a10aaf)
shape.h (34098b669c0cbc024cd08e686891f1dfe0a10aaf)
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 MAX_IVARS (attr_index_t)(-1)
27
28# define SHAPE_MASK (((uintptr_t)1 << SHAPE_ID_NUM_BITS) - 1)
29# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS)
30
31# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_ID_NUM_BITS)
32
33# define SHAPE_MAX_VARIATIONS 8
34
35# define INVALID_SHAPE_ID SHAPE_MASK
36# define ROOT_SHAPE_ID 0x0
37
38# define SPECIAL_CONST_SHAPE_ID (ROOT_SHAPE_ID + 1)
39# define OBJ_TOO_COMPLEX_SHAPE_ID (SPECIAL_CONST_SHAPE_ID + 1)
40# define FIRST_T_OBJECT_SHAPE_ID (OBJ_TOO_COMPLEX_SHAPE_ID + 1)
41
42typedef struct redblack_node redblack_node_t;
43
44struct rb_shape {
45 struct rb_id_table * edges; // id_table from ID (ivar) to next shape
46 ID edge_name; // ID (ivar) for transition from parent to rb_shape
47 attr_index_t next_iv_index;
48 uint32_t capacity; // Total capacity of the object with this shape
49 uint8_t type;
50 uint8_t heap_index;
51 shape_id_t parent_id;
52 redblack_node_t * ancestor_index;
53};
54
55typedef struct rb_shape rb_shape_t;
56
58 ID key;
59 rb_shape_t * value;
60 redblack_id_t l;
61 redblack_id_t r;
62};
63
64enum shape_type {
65 SHAPE_ROOT,
66 SHAPE_IVAR,
67 SHAPE_FROZEN,
68 SHAPE_T_OBJECT,
69 SHAPE_OBJ_TOO_COMPLEX,
70};
71
72typedef struct {
73 /* object shapes */
74 rb_shape_t *shape_list;
75 rb_shape_t *root_shape;
76 shape_id_t next_shape_id;
77
78 redblack_node_t *shape_cache;
79 unsigned int cache_size;
81RUBY_EXTERN rb_shape_tree_t *rb_shape_tree_ptr;
82
83static inline rb_shape_tree_t *
84rb_current_shape_tree(void)
85{
86 return rb_shape_tree_ptr;
87}
88#define GET_SHAPE_TREE() rb_current_shape_tree()
89
90static inline shape_id_t
91get_shape_id_from_flags(VALUE obj)
92{
94 return (shape_id_t)(SHAPE_MASK & ((RBASIC(obj)->flags) >> SHAPE_FLAG_SHIFT));
95}
96
97static inline void
98set_shape_id_in_flags(VALUE obj, shape_id_t shape_id)
99{
100 // Ractors are occupying the upper 32 bits of flags, but only in debug mode
101 // Object shapes are occupying top bits
102 RBASIC(obj)->flags &= SHAPE_FLAG_MASK;
103 RBASIC(obj)->flags |= ((VALUE)(shape_id) << SHAPE_FLAG_SHIFT);
104}
105
106
107#if SHAPE_IN_BASIC_FLAGS
108static inline shape_id_t
109RBASIC_SHAPE_ID(VALUE obj)
110{
111 return get_shape_id_from_flags(obj);
112}
113
114static inline void
115RBASIC_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
116{
117 set_shape_id_in_flags(obj, shape_id);
118}
119#endif
120
121static inline shape_id_t
122ROBJECT_SHAPE_ID(VALUE obj)
123{
124 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
125 return get_shape_id_from_flags(obj);
126}
127
128static inline void
129ROBJECT_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
130{
131 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
132 set_shape_id_in_flags(obj, shape_id);
133}
134
135static inline shape_id_t
136RCLASS_SHAPE_ID(VALUE obj)
137{
139 return get_shape_id_from_flags(obj);
140}
141
142static inline void
143RCLASS_SET_SHAPE_ID(VALUE obj, shape_id_t shape_id)
144{
146 set_shape_id_in_flags(obj, shape_id);
147}
148
149rb_shape_t * rb_shape_get_root_shape(void);
150int32_t rb_shape_id_offset(void);
151
152rb_shape_t * rb_shape_get_parent(rb_shape_t * shape);
153
154RUBY_FUNC_EXPORTED rb_shape_t *rb_shape_get_shape_by_id(shape_id_t shape_id);
155RUBY_FUNC_EXPORTED shape_id_t rb_shape_get_shape_id(VALUE obj);
156rb_shape_t * rb_shape_get_next_iv_shape(rb_shape_t * shape, ID id);
157bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value);
158bool rb_shape_get_iv_index_with_hint(shape_id_t shape_id, ID id, attr_index_t * value, shape_id_t *shape_id_hint);
159RUBY_FUNC_EXPORTED bool rb_shape_obj_too_complex(VALUE obj);
160
161void rb_shape_set_shape(VALUE obj, rb_shape_t* shape);
162rb_shape_t* rb_shape_get_shape(VALUE obj);
163int rb_shape_frozen_shape_p(rb_shape_t* shape);
164rb_shape_t* rb_shape_transition_shape_frozen(VALUE obj);
165bool rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed);
166rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id);
167rb_shape_t* rb_shape_get_next_no_warnings(rb_shape_t* shape, VALUE obj, ID id);
168
169rb_shape_t * rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape);
170
171static inline uint32_t
172ROBJECT_IV_CAPACITY(VALUE obj)
173{
174 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
175 // Asking for capacity doesn't make sense when the object is using
176 // a hash table for storing instance variables
177 RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
178 return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity;
179}
180
181static inline st_table *
182ROBJECT_IV_HASH(VALUE obj)
183{
184 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
185 RUBY_ASSERT(rb_shape_obj_too_complex(obj));
186 return (st_table *)ROBJECT(obj)->as.heap.ivptr;
187}
188
189static inline void
190ROBJECT_SET_IV_HASH(VALUE obj, const st_table *tbl)
191{
192 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
193 RUBY_ASSERT(rb_shape_obj_too_complex(obj));
194 ROBJECT(obj)->as.heap.ivptr = (VALUE *)tbl;
195}
196
197size_t rb_id_table_size(const struct rb_id_table *tbl);
198
199static inline uint32_t
200ROBJECT_IV_COUNT(VALUE obj)
201{
202 if (rb_shape_obj_too_complex(obj)) {
203 return (uint32_t)rb_st_table_size(ROBJECT_IV_HASH(obj));
204 }
205 else {
206 RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
207 RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
208 return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->next_iv_index;
209 }
210}
211
212static inline uint32_t
213RBASIC_IV_COUNT(VALUE obj)
214{
215 return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj))->next_iv_index;
216}
217
218rb_shape_t *rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *orig_shape);
219
220bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
221
222VALUE rb_obj_debug_shape(VALUE self, VALUE obj);
223
224// For ext/objspace
225RUBY_SYMBOL_EXPORT_BEGIN
226typedef void each_shape_callback(rb_shape_t * shape, void *data);
227void rb_shape_each_shape(each_shape_callback callback, void *data);
228size_t rb_shape_memsize(rb_shape_t *shape);
229size_t rb_shape_edges_count(rb_shape_t *shape);
230size_t rb_shape_depth(rb_shape_t *shape);
231shape_id_t rb_shape_id(rb_shape_t * shape);
232RUBY_SYMBOL_EXPORT_END
233
234#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_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