Ruby: shape.c Source File

Ruby 3.2.5p208 (2024-07-26 revision 31d0f1a2e7dbfb60731d1f05b868e1d578cda493)

shape.c
1#include "vm_core.h"
2#include "vm_sync.h"
3#include "shape.h"
4#include "gc.h"
5#include "symbol.h"
6#include "id_table.h"
7#include "internal/class.h"
8#include "internal/symbol.h"
9#include "internal/variable.h"
10#include "variable.h"
11#include <stdbool.h>
12
13#ifndef SHAPE_DEBUG
14#define SHAPE_DEBUG (VM_CHECK_MODE > 0)
15#endif
16
17static ID id_frozen;
18static ID id_t_object;
19static ID size_pool_edge_names[SIZE_POOL_COUNT];
20
21/*
22 * Shape getters
23 */
24 rb_shape_t *
25rb_shape_get_root_shape(void)
26{
27 return GET_VM()->root_shape;
28}
29
30shape_id_t
31rb_shape_id(rb_shape_t * shape)
32{
33 return (shape_id_t)(shape - GET_VM()->shape_list);
34}
35
36bool
37rb_shape_root_shape_p(rb_shape_t* shape)
38{
39 return shape == rb_shape_get_root_shape();
40}
41
42void
43rb_shape_each_shape(each_shape_callback callback, void *data)
44{
45 rb_shape_t *cursor = rb_shape_get_root_shape();
46 rb_shape_t *end = rb_shape_get_shape_by_id(GET_VM()->next_shape_id);
47 while (cursor < end) {
48 callback(cursor, data);
49 cursor += 1;
50 }
51}
52
53 rb_shape_t*
54rb_shape_get_shape_by_id(shape_id_t shape_id)
55{
56 RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
57
58 rb_vm_t *vm = GET_VM();
59 rb_shape_t *shape = &vm->shape_list[shape_id];
60 return shape;
61}
62
63 rb_shape_t*
64rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id)
65{
66 RUBY_ASSERT(shape_id != INVALID_SHAPE_ID);
67
68 rb_vm_t *vm = GET_VM();
69 rb_shape_t *shape = &vm->shape_list[shape_id];
70 return shape;
71}
72
73 rb_shape_t *
74rb_shape_get_parent(rb_shape_t * shape)
75{
76 return rb_shape_get_shape_by_id(shape->parent_id);
77}
78
79#if !SHAPE_IN_BASIC_FLAGS
80shape_id_t
81rb_rclass_shape_id(VALUE obj)
82{
83 RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
84 return RCLASS_EXT(obj)->shape_id;
85}
86
87shape_id_t rb_generic_shape_id(VALUE obj);
88#endif
89
90shape_id_t
91rb_shape_get_shape_id(VALUE obj)
92{
93 if (RB_SPECIAL_CONST_P(obj)) {
94 return SPECIAL_CONST_SHAPE_ID;
95 }
96
97#if SHAPE_IN_BASIC_FLAGS
98 return RBASIC_SHAPE_ID(obj);
99#else
100 switch (BUILTIN_TYPE(obj)) {
101 case T_OBJECT:
102 return ROBJECT_SHAPE_ID(obj);
103 break;
104 case T_CLASS:
105 case T_MODULE:
106 return RCLASS_SHAPE_ID(obj);
107 default:
108 return rb_generic_shape_id(obj);
109 }
110#endif
111}
112
113size_t
114rb_shape_depth(rb_shape_t * shape)
115{
116 size_t depth = 1;
117
118 while (shape->parent_id != INVALID_SHAPE_ID) {
119 depth++;
120 shape = rb_shape_get_parent(shape);
121 }
122
123 return depth;
124}
125
126 rb_shape_t*
127rb_shape_get_shape(VALUE obj)
128{
129 return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj));
130}
131
132static rb_shape_t*
133get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type, bool * variation_created, bool new_shapes_allowed)
134{
135 rb_shape_t *res = NULL;
136
137 // There should never be outgoing edges from "too complex"
138 RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
139
140 *variation_created = false;
141
142 if (new_shapes_allowed) {
143 RB_VM_LOCK_ENTER();
144 {
145 bool had_edges = !!shape->edges;
146
147 if (!shape->edges) {
148 shape->edges = rb_id_table_create(0);
149 }
150
151 // Lookup the shape in edges - if there's already an edge and a corresponding shape for it,
152 // we can return that. Otherwise, we'll need to get a new shape
153 VALUE lookup_result;
154 if (rb_id_table_lookup(shape->edges, id, &lookup_result)) {
155 res = (rb_shape_t *)lookup_result;
156 }
157 else {
158 *variation_created = had_edges;
159
160 rb_shape_t * new_shape = rb_shape_alloc(id, shape);
161
162 new_shape->type = (uint8_t)shape_type;
163 new_shape->capacity = shape->capacity;
164
165 switch (shape_type) {
166 case SHAPE_IVAR:
167 new_shape->next_iv_index = shape->next_iv_index + 1;
168 break;
169 case SHAPE_CAPACITY_CHANGE:
170 case SHAPE_FROZEN:
171 case SHAPE_T_OBJECT:
172 new_shape->next_iv_index = shape->next_iv_index;
173 break;
174 case SHAPE_OBJ_TOO_COMPLEX:
175 case SHAPE_INITIAL_CAPACITY:
176 case SHAPE_ROOT:
177 rb_bug("Unreachable");
178 break;
179 }
180
181 rb_id_table_insert(shape->edges, id, (VALUE)new_shape);
182
183 res = new_shape;
184 }
185 }
186 RB_VM_LOCK_LEAVE();
187 }
188 return res;
189}
190
191MJIT_FUNC_EXPORTED int
192rb_shape_frozen_shape_p(rb_shape_t* shape)
193{
194 return SHAPE_FROZEN == (enum shape_type)shape->type;
195}
196
197static void
198move_iv(VALUE obj, ID id, attr_index_t from, attr_index_t to)
199{
200 switch(BUILTIN_TYPE(obj)) {
201 case T_CLASS:
202 case T_MODULE:
203 RCLASS_IVPTR(obj)[to] = RCLASS_IVPTR(obj)[from];
204 break;
205 case T_OBJECT:
206 RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
207 ROBJECT_IVPTR(obj)[to] = ROBJECT_IVPTR(obj)[from];
208 break;
209 default: {
210 struct gen_ivtbl *ivtbl;
211 rb_gen_ivtbl_get(obj, id, &ivtbl);
212 ivtbl->ivptr[to] = ivtbl->ivptr[from];
213 break;
214 }
215 }
216}
217
218static rb_shape_t *
219remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
220{
221 if (shape->parent_id == INVALID_SHAPE_ID) {
222 // We've hit the top of the shape tree and couldn't find the
223 // IV we wanted to remove, so return NULL
224 return NULL;
225 }
226 else {
227 if (shape->type == SHAPE_IVAR && shape->edge_name == id) {
228 // We've hit the edge we wanted to remove, return it's _parent_
229 // as the new parent while we go back down the stack.
230 attr_index_t index = shape->next_iv_index - 1;
231
232 switch(BUILTIN_TYPE(obj)) {
233 case T_CLASS:
234 case T_MODULE:
235 *removed = RCLASS_IVPTR(obj)[index];
236 break;
237 case T_OBJECT:
238 *removed = ROBJECT_IVPTR(obj)[index];
239 break;
240 default: {
241 struct gen_ivtbl *ivtbl;
242 rb_gen_ivtbl_get(obj, id, &ivtbl);
243 *removed = ivtbl->ivptr[index];
244 break;
245 }
246 }
247 return rb_shape_get_parent(shape);
248 }
249 else {
250 // This isn't the IV we want to remove, keep walking up.
251 rb_shape_t * new_parent = remove_shape_recursive(obj, id, rb_shape_get_parent(shape), removed);
252
253 // We found a new parent. Create a child of the new parent that
254 // has the same attributes as this shape.
255 if (new_parent) {
256 bool dont_care;
257 rb_shape_t * new_child = get_next_shape_internal(new_parent, shape->edge_name, shape->type, &dont_care, true);
258 new_child->capacity = shape->capacity;
259 if (new_child->type == SHAPE_IVAR) {
260 move_iv(obj, id, shape->next_iv_index - 1, new_child->next_iv_index - 1);
261 }
262
263 return new_child;
264 }
265 else {
266 // We went all the way to the top of the shape tree and couldn't
267 // find an IV to remove, so return NULL
268 return NULL;
269 }
270 }
271 }
272}
273
274void
275rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape, VALUE * removed)
276{
277 rb_shape_t * new_shape = remove_shape_recursive(obj, id, shape, removed);
278 if (new_shape) {
279 rb_shape_set_shape(obj, new_shape);
280 }
281}
282
283void
284rb_shape_transition_shape_frozen(VALUE obj)
285{
286 rb_shape_t* shape = rb_shape_get_shape(obj);
287 RUBY_ASSERT(shape);
288 RUBY_ASSERT(RB_OBJ_FROZEN(obj));
289
290 if (rb_shape_frozen_shape_p(shape) || rb_shape_obj_too_complex(obj)) {
291 return;
292 }
293
294 rb_shape_t* next_shape;
295
296 if (shape == rb_shape_get_root_shape()) {
297 rb_shape_set_shape_id(obj, SPECIAL_CONST_SHAPE_ID);
298 return;
299 }
300
301 bool dont_care;
302 next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
303
304 RUBY_ASSERT(next_shape);
305 rb_shape_set_shape(obj, next_shape);
306}
307
308/*
309 * This function is used for assertions where we don't want to increment
310 * max_iv_count
311 */
312 rb_shape_t *
313rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id)
314{
315 RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
316 bool dont_care;
317 return get_next_shape_internal(shape, id, SHAPE_IVAR, &dont_care, true);
318}
319
320 rb_shape_t *
321rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
322{
323 RUBY_ASSERT(!is_instance_id(id) || RTEST(rb_sym2str(ID2SYM(id))));
324
325 bool allow_new_shape = true;
326
327 if (BUILTIN_TYPE(obj) == T_OBJECT) {
328 VALUE klass = rb_obj_class(obj);
329 allow_new_shape = RCLASS_EXT(klass)->variation_count < SHAPE_MAX_VARIATIONS;
330 }
331
332 bool variation_created = false;
333 rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
334
335 if (!new_shape) {
336 RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
337 new_shape = rb_shape_get_shape_by_id(OBJ_TOO_COMPLEX_SHAPE_ID);
338 }
339
340 // Check if we should update max_iv_count on the object's class
341 if (BUILTIN_TYPE(obj) == T_OBJECT) {
342 VALUE klass = rb_obj_class(obj);
343 if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) {
344 RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index;
345 }
346
347 if (variation_created) {
348 RCLASS_EXT(klass)->variation_count++;
349 }
350 }
351
352 return new_shape;
353}
354
355 rb_shape_t *
356rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity)
357{
358 ID edge_name = rb_make_temporary_id(new_capacity);
359 bool dont_care;
360 rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care, true);
361 new_shape->capacity = new_capacity;
362 return new_shape;
363}
364
365bool
366rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
367{
368 // It doesn't make sense to ask for the index of an IV that's stored
369 // on an object that is "too complex" as it uses a hash for storing IVs
370 RUBY_ASSERT(rb_shape_id(shape) != OBJ_TOO_COMPLEX_SHAPE_ID);
371
372 while (shape->parent_id != INVALID_SHAPE_ID) {
373 if (shape->edge_name == id) {
374 enum shape_type shape_type;
375 shape_type = (enum shape_type)shape->type;
376
377 switch (shape_type) {
378 case SHAPE_IVAR:
379 RUBY_ASSERT(shape->next_iv_index > 0);
380 *value = shape->next_iv_index - 1;
381 return true;
382 case SHAPE_CAPACITY_CHANGE:
383 case SHAPE_ROOT:
384 case SHAPE_INITIAL_CAPACITY:
385 case SHAPE_T_OBJECT:
386 return false;
387 case SHAPE_OBJ_TOO_COMPLEX:
388 case SHAPE_FROZEN:
389 rb_bug("Ivar should not exist on transition\n");
390 }
391 }
392 shape = rb_shape_get_parent(shape);
393 }
394 return false;
395}
396
397static rb_shape_t *
398shape_alloc(void)
399{
400 rb_vm_t *vm = GET_VM();
401 shape_id_t shape_id = vm->next_shape_id;
402 vm->next_shape_id++;
403
404 if (shape_id == MAX_SHAPE_ID) {
405 // TODO: Make an OutOfShapesError ??
406 rb_bug("Out of shapes\n");
407 }
408
409 return &GET_VM()->shape_list[shape_id];
410}
411
412 rb_shape_t *
413rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id)
414{
415 rb_shape_t * shape = shape_alloc();
416
417 shape->edge_name = edge_name;
418 shape->next_iv_index = 0;
419 shape->parent_id = parent_id;
420
421 return shape;
422}
423
424 rb_shape_t *
425rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index)
426{
427 rb_shape_t * shape = rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent));
428 shape->size_pool_index = size_pool_index;
429 return shape;
430}
431
432
433 rb_shape_t *
434rb_shape_alloc(ID edge_name, rb_shape_t * parent)
435{
436 return rb_shape_alloc_with_size_pool_index(edge_name, parent, parent->size_pool_index);
437}
438
439MJIT_FUNC_EXPORTED void
440rb_shape_set_shape(VALUE obj, rb_shape_t* shape)
441{
442 rb_shape_set_shape_id(obj, rb_shape_id(shape));
443}
444
445int32_t
446rb_shape_id_offset(void)
447{
448 return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS / sizeof(uintptr_t);
449}
450
451 rb_shape_t *
452rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shape)
453{
454 RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
455 rb_shape_t *next_shape = initial_shape;
456
457 if (dest_shape->type != initial_shape->type) {
458 next_shape = rb_shape_traverse_from_new_root(initial_shape, rb_shape_get_parent(dest_shape));
459 if (!next_shape) {
460 return NULL;
461 }
462 }
463
464 switch ((enum shape_type)dest_shape->type) {
465 case SHAPE_IVAR:
466 case SHAPE_FROZEN:
467 if (!next_shape->edges) {
468 return NULL;
469 }
470
471 VALUE lookup_result;
472 if (rb_id_table_lookup(next_shape->edges, dest_shape->edge_name, &lookup_result)) {
473 next_shape = (rb_shape_t *)lookup_result;
474 }
475 else {
476 return NULL;
477 }
478 break;
479 case SHAPE_ROOT:
480 case SHAPE_CAPACITY_CHANGE:
481 case SHAPE_INITIAL_CAPACITY:
482 case SHAPE_T_OBJECT:
483 break;
484 case SHAPE_OBJ_TOO_COMPLEX:
485 rb_bug("Unreachable\n");
486 break;
487 }
488
489 return next_shape;
490}
491
492 rb_shape_t *
493rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
494{
495 rb_shape_t * midway_shape;
496
497 RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT);
498
499 if (dest_shape->type != initial_shape->type) {
500 midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape));
501 }
502 else {
503 midway_shape = initial_shape;
504 }
505
506 switch ((enum shape_type)dest_shape->type) {
507 case SHAPE_IVAR:
508 if (midway_shape->capacity <= midway_shape->next_iv_index) {
509 // There isn't enough room to write this IV, so we need to increase the capacity
510 midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2);
511 }
512
513 midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
514 break;
515 case SHAPE_ROOT:
516 case SHAPE_FROZEN:
517 case SHAPE_CAPACITY_CHANGE:
518 case SHAPE_INITIAL_CAPACITY:
519 case SHAPE_T_OBJECT:
520 break;
521 case SHAPE_OBJ_TOO_COMPLEX:
522 rb_bug("Unreachable\n");
523 break;
524 }
525
526 return midway_shape;
527}
528
529bool
530rb_shape_obj_too_complex(VALUE obj)
531{
532 return rb_shape_get_shape_id(obj) == OBJ_TOO_COMPLEX_SHAPE_ID;
533}
534
535void
536rb_shape_set_too_complex(VALUE obj)
537{
538 RUBY_ASSERT(BUILTIN_TYPE(obj) == T_OBJECT);
539 RUBY_ASSERT(!rb_shape_obj_too_complex(obj));
540 rb_shape_set_shape_id(obj, OBJ_TOO_COMPLEX_SHAPE_ID);
541}
542
543size_t
544rb_shape_edges_count(rb_shape_t *shape)
545{
546 if (shape->edges) {
547 return rb_id_table_size(shape->edges);
548 }
549 return 0;
550}
551
552size_t
553rb_shape_memsize(rb_shape_t *shape)
554{
555 size_t memsize = sizeof(rb_shape_t);
556 if (shape->edges) {
557 memsize += rb_id_table_memsize(shape->edges);
558 }
559 return memsize;
560}
561
562#if SHAPE_DEBUG
563/*
564 * Exposing Shape to Ruby via RubyVM.debug_shape
565 */
566
567/* :nodoc: */
568static VALUE
569rb_shape_too_complex(VALUE self)
570{
571 rb_shape_t * shape;
572 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
573 if (rb_shape_id(shape) == OBJ_TOO_COMPLEX_SHAPE_ID) {
574 return Qtrue;
575 }
576 else {
577 return Qfalse;
578 }
579}
580
581static VALUE
582parse_key(ID key)
583{
584 if (is_instance_id(key)) {
585 return ID2SYM(key);
586 }
587 return LONG2NUM(key);
588}
589
590static VALUE rb_shape_edge_name(rb_shape_t * shape);
591
592static VALUE
593rb_shape_t_to_rb_cShape(rb_shape_t *shape)
594{
595 VALUE rb_cShape = rb_const_get(rb_cRubyVM, rb_intern("Shape"));
596
597 VALUE obj = rb_struct_new(rb_cShape,
598 INT2NUM(rb_shape_id(shape)),
599 INT2NUM(shape->parent_id),
600 rb_shape_edge_name(shape),
601 INT2NUM(shape->next_iv_index),
602 INT2NUM(shape->size_pool_index),
603 INT2NUM(shape->type),
604 INT2NUM(shape->capacity));
605 rb_obj_freeze(obj);
606 return obj;
607}
608
609static enum rb_id_table_iterator_result
610rb_edges_to_hash(ID key, VALUE value, void *ref)
611{
612 rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_shape_t_to_rb_cShape((rb_shape_t*)value));
613 return ID_TABLE_CONTINUE;
614}
615
616/* :nodoc: */
617static VALUE
618rb_shape_edges(VALUE self)
619{
620 rb_shape_t* shape;
621
622 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
623
624 VALUE hash = rb_hash_new();
625
626 if (shape->edges) {
627 rb_id_table_foreach(shape->edges, rb_edges_to_hash, &hash);
628 }
629
630 return hash;
631}
632
633static VALUE
634rb_shape_edge_name(rb_shape_t * shape)
635{
636 if (shape->edge_name) {
637 if (is_instance_id(shape->edge_name)) {
638 return ID2SYM(shape->edge_name);
639 }
640 return INT2NUM(shape->capacity);
641 }
642 return Qnil;
643}
644
645/* :nodoc: */
646static VALUE
647rb_shape_export_depth(VALUE self)
648{
649 rb_shape_t* shape;
650 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
651 return SIZET2NUM(rb_shape_depth(shape));
652}
653
654/* :nodoc: */
655static VALUE
656rb_shape_parent(VALUE self)
657{
658 rb_shape_t * shape;
659 shape = rb_shape_get_shape_by_id(NUM2INT(rb_struct_getmember(self, rb_intern("id"))));
660 if (shape->parent_id != INVALID_SHAPE_ID) {
661 return rb_shape_t_to_rb_cShape(rb_shape_get_parent(shape));
662 }
663 else {
664 return Qnil;
665 }
666}
667
668/* :nodoc: */
669static VALUE
670rb_shape_debug_shape(VALUE self, VALUE obj)
671{
672 return rb_shape_t_to_rb_cShape(rb_shape_get_shape(obj));
673}
674
675/* :nodoc: */
676static VALUE
677rb_shape_root_shape(VALUE self)
678{
679 return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape());
680}
681
682 VALUE rb_obj_shape(rb_shape_t* shape);
683
684static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref)
685{
686 rb_hash_aset(*(VALUE *)ref, parse_key(key), rb_obj_shape((rb_shape_t*)value));
687 return ID_TABLE_CONTINUE;
688}
689
690static VALUE edges(struct rb_id_table* edges)
691{
692 VALUE hash = rb_hash_new();
693 if (edges)
694 rb_id_table_foreach(edges, collect_keys_and_values, &hash);
695 return hash;
696}
697
698/* :nodoc: */
699 VALUE
700rb_obj_shape(rb_shape_t* shape)
701{
702 VALUE rb_shape = rb_hash_new();
703
704 rb_hash_aset(rb_shape, ID2SYM(rb_intern("id")), INT2NUM(rb_shape_id(shape)));
705 rb_hash_aset(rb_shape, ID2SYM(rb_intern("edges")), edges(shape->edges));
706
707 if (shape == rb_shape_get_root_shape()) {
708 rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(ROOT_SHAPE_ID));
709 }
710 else {
711 rb_hash_aset(rb_shape, ID2SYM(rb_intern("parent_id")), INT2NUM(shape->parent_id));
712 }
713
714 rb_hash_aset(rb_shape, ID2SYM(rb_intern("edge_name")), rb_id2str(shape->edge_name));
715 return rb_shape;
716}
717
718/* :nodoc: */
719static VALUE
720shape_transition_tree(VALUE self)
721{
722 return rb_obj_shape(rb_shape_get_root_shape());
723}
724
725/* :nodoc: */
726static VALUE
727rb_shape_find_by_id(VALUE mod, VALUE id)
728{
729 shape_id_t shape_id = NUM2UINT(id);
730 if (shape_id >= GET_VM()->next_shape_id) {
731 rb_raise(rb_eArgError, "Shape ID %d is out of bounds\n", shape_id);
732 }
733 return rb_shape_t_to_rb_cShape(rb_shape_get_shape_by_id(shape_id));
734}
735#endif
736
737void
738Init_default_shapes(void)
739{
740 id_frozen = rb_make_internal_id();
741 id_t_object = rb_make_internal_id();
742
743 // Shapes by size pool
744 for (int i = 0; i < SIZE_POOL_COUNT; i++) {
745 size_pool_edge_names[i] = rb_make_internal_id();
746 }
747
748 // Root shape
749 rb_shape_t * root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
750 root->capacity = (uint32_t)((rb_size_pool_slot_size(0) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
751 root->type = SHAPE_ROOT;
752 root->size_pool_index = 0;
753 GET_VM()->root_shape = root;
754 RUBY_ASSERT(rb_shape_id(GET_VM()->root_shape) == ROOT_SHAPE_ID);
755
756 // Shapes by size pool
757 for (int i = 1; i < SIZE_POOL_COUNT; i++) {
758 uint32_t capa = (uint32_t)((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
759 rb_shape_t * new_shape = rb_shape_transition_shape_capa(root, capa);
760 new_shape->type = SHAPE_INITIAL_CAPACITY;
761 new_shape->size_pool_index = i;
762 RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i);
763 }
764
765 // Make shapes for T_OBJECT
766 for (int i = 0; i < SIZE_POOL_COUNT; i++) {
767 rb_shape_t * shape = rb_shape_get_shape_by_id(i);
768 bool dont_care;
769 rb_shape_t * t_object_shape =
770 get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT, &dont_care, true);
771 t_object_shape->edges = rb_id_table_create(0);
772 RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT));
773 }
774
775 bool dont_care;
776 // Special const shape
777#if RUBY_DEBUG
778 rb_shape_t * special_const_shape =
779#endif
780 get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN, &dont_care, true);
781 RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID);
782 RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_VM()->next_shape_id - 1));
783 RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape));
784
785 rb_shape_t * hash_fallback_shape = rb_shape_alloc_with_parent_id(0, ROOT_SHAPE_ID);
786 hash_fallback_shape->type = SHAPE_OBJ_TOO_COMPLEX;
787 hash_fallback_shape->size_pool_index = 0;
788 RUBY_ASSERT(OBJ_TOO_COMPLEX_SHAPE_ID == (GET_VM()->next_shape_id - 1));
789 RUBY_ASSERT(rb_shape_id(hash_fallback_shape) == OBJ_TOO_COMPLEX_SHAPE_ID);
790}
791
792void
793Init_shape(void)
794{
795#if SHAPE_DEBUG
796 VALUE rb_cShape = rb_struct_define_under(rb_cRubyVM, "Shape",
797 "id",
798 "parent_id",
799 "edge_name",
800 "next_iv_index",
801 "size_pool_index",
802 "type",
803 "capacity",
804 NULL);
805
806 rb_define_method(rb_cShape, "parent", rb_shape_parent, 0);
807 rb_define_method(rb_cShape, "edges", rb_shape_edges, 0);
808 rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0);
809 rb_define_method(rb_cShape, "too_complex?", rb_shape_too_complex, 0);
810 rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT));
811 rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR));
812 rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT));
813 rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN));
814 rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS));
815 rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT));
816 rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID));
817 rb_define_const(rb_cShape, "OBJ_TOO_COMPLEX_SHAPE_ID", INT2NUM(OBJ_TOO_COMPLEX_SHAPE_ID));
818 rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS));
819
820 rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0);
821 rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1);
822 rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1);
823 rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0);
824#endif
825}
RUBY_ASSERT
#define RUBY_ASSERT(expr)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:177
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition cxxanyargs.hpp:670
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition cxxanyargs.hpp:685
RB_OBJ_FROZEN
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition fl_type.h:921
ID2SYM
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
SIZET2NUM
#define SIZET2NUM
Old name of RB_SIZE2NUM.
Definition size_t.h:62
T_MODULE
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
NUM2UINT
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition int.h:45
LONG2NUM
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
Qtrue
#define Qtrue
Old name of RUBY_Qtrue.
Definition special_consts.h:61
NUM2INT
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
INT2NUM
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
Qfalse
#define Qfalse
Old name of RUBY_Qfalse.
Definition special_consts.h:59
T_OBJECT
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
T_CLASS
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
BUILTIN_TYPE
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition error.c:3150
rb_bug
void rb_bug(const char *fmt,...)
Interpreter panic switch.
Definition error.c:794
rb_eArgError
VALUE rb_eArgError
ArgumentError exception.
Definition error.c:1092
rb_obj_class
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:191
rb_obj_freeze
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1183
rb_struct_define_under
VALUE rb_struct_define_under(VALUE space, const char *name,...)
Identical to rb_struct_define(), except it defines the class under the specified namespace instead of...
Definition struct.c:509
rb_struct_new
VALUE rb_struct_new(VALUE klass,...)
Creates an instance of the given struct.
Definition struct.c:880
rb_struct_getmember
VALUE rb_struct_getmember(VALUE self, ID key)
Identical to rb_struct_aref(), except it takes ID instead of VALUE.
Definition struct.c:233
rb_const_get
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:2896
rb_intern
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
Definition symbol.c:796
rb_sym2str
VALUE rb_sym2str(VALUE id)
Identical to rb_id2str(), except it takes an instance of rb_cSymbol rather than an ID.
Definition symbol.c:943
rb_define_const
void rb_define_const(VALUE klass, const char *name, VALUE val)
Defines a Ruby level constant under a namespace.
Definition variable.c:3440
ROBJECT_IVPTR
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
Definition robject.h:162
RB_SPECIAL_CONST_P
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Definition special_consts.h:327
RTEST
#define RTEST
This is an old name of RB_TEST.
Definition special_consts.h:51
stdbool.h
C99 shim for <stdbool.h>
RObject
Ruby's ordinal objects.
Definition robject.h:94
RObject::ary
VALUE ary[ROBJECT_EMBED_LEN_MAX]
Embedded instance variables.
Definition robject.h:136
gen_ivtbl
Definition variable.h:13
rb_id_table
Definition id_table.c:41
rb_shape
Definition shape.h:42
rb_vm_struct
Definition vm_core.h:615
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
RB_TYPE_P
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:375