6#include "internal/class.h"
7#include "internal/error.h"
8#include "internal/gc.h"
9#include "internal/object.h"
10#include "internal/symbol.h"
11#include "internal/variable.h"
20#define SHAPE_DEBUG (VM_CHECK_MODE > 0)
23#define REDBLACK_CACHE_SIZE (SHAPE_BUFFER_SIZE * 32)
27#define SINGLE_CHILD_TAG 0x1
28#define TAG_SINGLE_CHILD(x) (VALUE)((uintptr_t)(x) | SINGLE_CHILD_TAG)
29#define SINGLE_CHILD_MASK (~((uintptr_t)SINGLE_CHILD_TAG))
30#define SINGLE_CHILD_P(x) ((uintptr_t)(x) & SINGLE_CHILD_TAG)
31#define SINGLE_CHILD(x) (rb_shape_t *)((uintptr_t)(x) & SINGLE_CHILD_MASK)
32#define ANCESTOR_CACHE_THRESHOLD 10
33#define MAX_SHAPE_ID (INVALID_SHAPE_ID - 1)
34#define ANCESTOR_SEARCH_MAX_DEPTH 2
45typedef struct redblack_node redblack_node_t;
47static redblack_node_t *redblack_cache;
53static inline redblack_node_t *
54redblack_node(redblack_id_t
id)
56 return id ? &redblack_cache[
id - 1] : LEAF;
59static redblack_node_t *
60redblack_left(redblack_node_t *node)
62 if (node->l == LEAF) {
67 redblack_node_t *left = redblack_node(node->l);
72static redblack_node_t *
73redblack_right(redblack_node_t *node)
75 if (node->r == LEAF) {
80 redblack_node_t *right = redblack_node(node->r);
85static redblack_node_t *
86redblack_find0(redblack_node_t *tree,
ID key)
92 RUBY_ASSERT(redblack_left(tree) == LEAF || redblack_left(tree)->key < tree->key);
93 RUBY_ASSERT(redblack_right(tree) == LEAF || redblack_right(tree)->key > tree->key);
95 if (tree->key == key) {
99 if (key < tree->key) {
100 return redblack_find0(redblack_left(tree), key);
103 return redblack_find0(redblack_right(tree), key);
109static redblack_node_t *
110redblack_find(redblack_id_t tree_id,
ID key)
112 return redblack_find0(redblack_node(tree_id), key);
116redblack_value(redblack_node_t *node)
120 return (
rb_shape_t *)((uintptr_t)node->value & ~(uintptr_t)1);
125redblack_color(redblack_node_t *node)
127 return node && ((uintptr_t)node->value & RED);
131redblack_red_p(redblack_node_t *node)
133 return redblack_color(node) == RED;
137redblack_id_for(redblack_node_t *node)
144 redblack_node_t *redblack_nodes = redblack_cache;
145 redblack_id_t
id = (redblack_id_t)(node - redblack_nodes);
150static redblack_node_t *
151redblack_new(
char color,
ID key,
rb_shape_t *value, redblack_node_t *left, redblack_node_t *right)
153 if (redblack_cache_size + 1 >= REDBLACK_CACHE_SIZE) {
161 redblack_node_t *redblack_nodes = redblack_cache;
164 node->value = (
rb_shape_t *)((uintptr_t)value | color);
165 node->l = redblack_id_for(left);
166 node->r = redblack_id_for(right);
170static redblack_node_t *
171redblack_balance(
char color,
ID key,
rb_shape_t *value, redblack_node_t *left, redblack_node_t *right)
173 if (color == BLACK) {
174 ID new_key, new_left_key, new_right_key;
175 rb_shape_t *new_value, *new_left_value, *new_right_value;
176 redblack_node_t *new_left_left, *new_left_right, *new_right_left, *new_right_right;
178 if (redblack_red_p(left) && redblack_red_p(redblack_left(left))) {
180 new_right_value = value;
181 new_right_right = right;
184 new_value = redblack_value(left);
185 new_right_left = redblack_right(left);
187 new_left_key = redblack_left(left)->key;
188 new_left_value = redblack_value(redblack_left(left));
190 new_left_left = redblack_left(redblack_left(left));
191 new_left_right = redblack_right(redblack_left(left));
193 else if (redblack_red_p(left) && redblack_red_p(redblack_right(left))) {
195 new_right_value = value;
196 new_right_right = right;
198 new_left_key = left->key;
199 new_left_value = redblack_value(left);
200 new_left_left = redblack_left(left);
202 new_key = redblack_right(left)->key;
203 new_value = redblack_value(redblack_right(left));
204 new_left_right = redblack_left(redblack_right(left));
205 new_right_left = redblack_right(redblack_right(left));
207 else if (redblack_red_p(right) && redblack_red_p(redblack_left(right))) {
209 new_left_value = value;
210 new_left_left = left;
212 new_right_key = right->key;
213 new_right_value = redblack_value(right);
214 new_right_right = redblack_right(right);
216 new_key = redblack_left(right)->key;
217 new_value = redblack_value(redblack_left(right));
218 new_left_right = redblack_left(redblack_left(right));
219 new_right_left = redblack_right(redblack_left(right));
221 else if (redblack_red_p(right) && redblack_red_p(redblack_right(right))) {
223 new_left_value = value;
224 new_left_left = left;
226 new_key = right->key;
227 new_value = redblack_value(right);
228 new_left_right = redblack_left(right);
230 new_right_key = redblack_right(right)->key;
231 new_right_value = redblack_value(redblack_right(right));
232 new_right_left = redblack_left(redblack_right(right));
233 new_right_right = redblack_right(redblack_right(right));
236 return redblack_new(color, key, value, left, right);
241 RUBY_ASSERT(new_left_left == LEAF || new_left_left->key < new_left_key);
242 RUBY_ASSERT(new_left_right == LEAF || new_left_right->key > new_left_key);
243 RUBY_ASSERT(new_left_right == LEAF || new_left_right->key < new_key);
244 RUBY_ASSERT(new_right_left == LEAF || new_right_left->key < new_right_key);
245 RUBY_ASSERT(new_right_left == LEAF || new_right_left->key > new_key);
246 RUBY_ASSERT(new_right_right == LEAF || new_right_right->key > new_right_key);
249 RED, new_key, new_value,
250 redblack_new(BLACK, new_left_key, new_left_value, new_left_left, new_left_right),
251 redblack_new(BLACK, new_right_key, new_right_value, new_right_left, new_right_right));
254 return redblack_new(color, key, value, left, right);
257static redblack_node_t *
258redblack_insert_aux(redblack_node_t *tree,
ID key,
rb_shape_t *value)
261 return redblack_new(RED, key, value, LEAF, LEAF);
264 redblack_node_t *left, *right;
265 if (key < tree->key) {
266 left = redblack_insert_aux(redblack_left(tree), key, value);
268 right = redblack_right(tree);
269 RUBY_ASSERT(right == LEAF || right->key > tree->key);
271 else if (key > tree->key) {
272 left = redblack_left(tree);
273 RUBY_ASSERT(left == LEAF || left->key < tree->key);
274 right = redblack_insert_aux(redblack_right(tree), key, value);
281 return redblack_balance(
282 redblack_color(tree),
284 redblack_value(tree),
291static redblack_node_t *
292redblack_force_black(redblack_node_t *node)
294 node->value = redblack_value(node);
299redblack_insert(redblack_node_t *tree,
ID key,
rb_shape_t *value)
301 redblack_node_t *root = redblack_insert_aux(tree, key, value);
303 if (redblack_red_p(root)) {
304 return redblack_id_for(redblack_force_black(root));
307 return redblack_id_for(root);
319rb_shape_get_root_shape(
void)
321 return rb_shape_tree.shape_list;
325shape_tree_mark_and_move(
void *data)
327 rb_shape_t *cursor = rb_shape_get_root_shape();
329 while (cursor <= end) {
330 if (cursor->edges && !SINGLE_CHILD_P(cursor->edges)) {
331 rb_gc_mark_and_move(&cursor->edges);
338rb_shapes_cache_size(
void)
340 return redblack_cache ? redblack_cache_size : 0;
350shape_tree_memsize(
const void *data)
352 if (redblack_cache) {
353 return redblack_cache_size *
sizeof(redblack_node_t);
361 .dmark = shape_tree_mark_and_move,
363 .dsize = shape_tree_memsize,
364 .dcompact = shape_tree_mark_and_move,
366 .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED,
374static inline shape_id_t
378 return (shape_id_t)(shape - rb_shape_tree.shape_list);
381static inline shape_id_t
382SHAPE_ID(
rb_shape_t *shape, shape_id_t previous_shape_id)
385 shape_id_t offset = (shape_id_t)(shape - rb_shape_tree.shape_list);
386 return offset | RSHAPE_FLAGS(previous_shape_id);
390rb_shape_each_shape_id(each_shape_callback callback,
void *data)
392 rb_shape_t *start = rb_shape_get_root_shape();
395 while (cursor < end) {
396 callback((shape_id_t)(cursor - start), data);
401RUBY_FUNC_EXPORTED shape_id_t
402rb_obj_shape_id(
VALUE obj)
405 rb_bug(
"rb_obj_shape_id: called on a special constant");
409 VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
410 shape_id_t base = ROOT_SHAPE_ID;
415 base = RBASIC_SHAPE_ID(fields_obj) & ~(SHAPE_ID_LAYOUT_MASK | SHAPE_ID_CAPACITY_MASK);
417 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
418 return rb_shape_layout(shape_id) | (shape_id & SHAPE_ID_CAPACITY_MASK) | base;
420 return RBASIC_SHAPE_ID(obj);
424rb_shape_depth(shape_id_t shape_id)
429 while (shape->parent_offset != INVALID_SHAPE_ID) {
431 shape = RSHAPE(shape->parent_offset);
440 shape_id_t current, new_id;
444 if (current > MAX_SHAPE_ID) {
447 new_id = current + 1;
450 return &rb_shape_tree.shape_list[current];
454rb_shape_alloc_with_parent_offset(
ID edge_name, shape_id_t parent_offset)
457 if (!shape)
return NULL;
459 shape->edge_name = edge_name;
460 shape->next_field_index = 0;
461 shape->parent_offset = parent_offset;
470 rb_shape_t *shape = rb_shape_alloc_with_parent_offset(edge_name, SHAPE_OFFSET(parent));
471 if (!shape)
return NULL;
473 shape->type = (uint8_t)
type;
474 shape->capacity = parent->capacity;
480static redblack_node_t *
483 if (!(shape->ancestor_index || shape->parent_offset == INVALID_SHAPE_ID)) {
484 redblack_node_t *parent_index_node = redblack_cache_ancestors(RSHAPE(shape->parent_offset));
486 if (shape->type == SHAPE_IVAR) {
487 shape->ancestor_index = redblack_insert(parent_index_node, shape->edge_name, shape);
490 if (shape->ancestor_index) {
491 redblack_node_t *inserted_node = redblack_find(shape->ancestor_index, shape->edge_name);
493 RUBY_ASSERT(redblack_value(inserted_node) == shape);
498 shape->ancestor_index = redblack_id_for(parent_index_node);
502 return redblack_node(shape->ancestor_index);
505static redblack_node_t *
513shape_grow_capa(attr_index_t current_capa)
515 size_t next_size = rb_obj_embedded_size(current_capa + 1);
516 if (UNLIKELY(!rb_gc_size_allocatable_p(next_size))) {
517 return rb_shape_max_capacity();
520 attr_index_t next_capa = rb_shape_capacity_for_slot_size(rb_gc_size_slot_size(next_size));
526rb_shape_alloc_new_child(
ID id,
rb_shape_t *shape,
enum shape_type shape_type)
528 rb_shape_t *new_shape = rb_shape_alloc(
id, shape, shape_type);
529 if (!new_shape)
return NULL;
531 switch (shape_type) {
534 if (UNLIKELY(shape->next_field_index >= shape->capacity)) {
535 RUBY_ASSERT(shape->next_field_index == shape->capacity);
536 new_shape->capacity = shape_grow_capa(shape->capacity);
539 RUBY_ASSERT(new_shape->capacity > shape->next_field_index);
540 new_shape->next_field_index = shape->next_field_index + 1;
541 if (new_shape->next_field_index > ANCESTOR_CACHE_THRESHOLD) {
543 redblack_cache_ancestors(new_shape);
548 rb_bug(
"Unreachable");
556get_next_shape_internal_atomic(
rb_shape_t *shape,
ID id,
enum shape_type shape_type,
bool *variation_created,
bool new_variations_allowed)
560 *variation_created =
false;
564 edges_table = RUBY_ATOMIC_VALUE_LOAD(shape->edges);
569 if (SINGLE_CHILD_P(edges_table)) {
570 rb_shape_t *child = SINGLE_CHILD(edges_table);
573 if (child->edge_name ==
id) {
580 if (rb_managed_id_table_lookup(edges_table,
id, &lookup_result)) {
587 if (!res && new_variations_allowed) {
590 rb_shape_t *new_shape = rb_shape_alloc_new_child(
id, shape, shape_type);
596 new_edges = TAG_SINGLE_CHILD(new_shape);
600 if (SINGLE_CHILD_P(edges_table)) {
601 rb_shape_t *old_child = SINGLE_CHILD(edges_table);
602 new_edges = rb_managed_id_table_new(2);
603 rb_managed_id_table_insert(new_edges, old_child->edge_name, (
VALUE)old_child);
606 new_edges = rb_managed_id_table_dup(edges_table);
609 rb_managed_id_table_insert(new_edges, new_shape->edge_name, (
VALUE)new_shape);
610 *variation_created =
true;
627get_next_shape_internal(
rb_shape_t *shape,
ID id,
enum shape_type shape_type,
bool *variation_created,
bool new_variations_allowed)
629 if (rb_multi_ractor_p()) {
630 return get_next_shape_internal_atomic(shape,
id, shape_type, variation_created, new_variations_allowed);
634 *variation_created =
false;
636 VALUE edges_table = shape->edges;
641 if (SINGLE_CHILD_P(edges_table)) {
642 rb_shape_t *child = SINGLE_CHILD(edges_table);
645 if (child->edge_name ==
id) {
652 if (rb_managed_id_table_lookup(edges_table,
id, &lookup_result)) {
662 if (!new_variations_allowed || rb_shapes_count() > MAX_SHAPE_ID) {
666 rb_shape_t *new_shape = rb_shape_alloc_new_child(
id, shape, shape_type);
670 shape->edges = TAG_SINGLE_CHILD(new_shape);
674 if (SINGLE_CHILD_P(edges_table)) {
675 rb_shape_t *old_child = SINGLE_CHILD(edges_table);
676 VALUE new_edges = rb_managed_id_table_new(2);
677 rb_managed_id_table_insert(new_edges, old_child->edge_name, (
VALUE)old_child);
681 rb_managed_id_table_insert(shape->edges, new_shape->edge_name, (
VALUE)new_shape);
682 *variation_created =
true;
693rb_shape_transition_object_id(shape_id_t original_shape_id)
695 RUBY_ASSERT(!rb_shape_has_object_id(original_shape_id));
697 rb_shape_t *original_shape = RSHAPE(original_shape_id);
701 if (LIKELY(original_shape->next_field_index < rb_shape_max_capacity())) {
702 shape = get_next_shape_internal(original_shape, rb_shape_tree.id_object_id, SHAPE_OBJ_ID, &dont_care,
true);
705 return rb_shape_layout(original_shape_id) | ROOT_COMPLEX_WITH_OBJ_ID | RSHAPE_FLAGS(original_shape_id);
709 return SHAPE_ID(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
713rb_shape_object_id(shape_id_t original_shape_id)
715 RUBY_ASSERT(rb_shape_has_object_id(original_shape_id));
717 rb_shape_t *shape = RSHAPE(original_shape_id);
718 while (shape->type != SHAPE_OBJ_ID) {
719 if (UNLIKELY(shape->parent_offset == INVALID_SHAPE_ID)) {
720 rb_bug(
"Missing object_id in shape tree");
722 shape = RSHAPE(shape->parent_offset);
725 return SHAPE_ID(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
729shape_get_iv_index(
rb_shape_t *shape,
ID id, attr_index_t *value)
731 while (shape->parent_offset != INVALID_SHAPE_ID) {
732 if (shape->edge_name ==
id) {
733 enum shape_type shape_type;
734 shape_type = (
enum shape_type)shape->type;
736 switch (shape_type) {
739 *value = shape->next_field_index - 1;
744 rb_bug(
"Ivar should not exist on transition");
748 shape = RSHAPE(shape->parent_offset);
755shape_get_next(
rb_shape_t *shape,
enum shape_type shape_type,
VALUE klass,
ID id,
bool emit_warnings)
761 if (shape_get_iv_index(shape,
id, &index)) {
762 rb_bug(
"rb_shape_get_next: trying to create ivar that already exists at index %u", index);
768 if (UNLIKELY(shape->next_field_index >= rb_shape_max_capacity())) {
772 bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS;
773 bool variation_created =
false;
774 rb_shape_t *new_shape = get_next_shape_internal(shape,
id, shape_type, &variation_created, allow_new_shape);
782 if (new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass) && !RCLASS_EXPECT_NO_IVAR(klass)) {
783 RCLASS_SET_MAX_IV_COUNT(klass, new_shape->next_field_index);
786 if (variation_created) {
787 RCLASS_VARIATION_COUNT(klass)++;
790 if (RCLASS_VARIATION_COUNT(klass) >= SHAPE_MAX_VARIATIONS) {
793 "The class %"PRIsVALUE
" reached %d shape variations, instance variables accesses will be slower and memory usage increased.\n"
794 "It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method.",
806obj_get_owner_class(
VALUE obj)
809 if (IMEMO_TYPE_P(obj, imemo_fields)) {
810 VALUE owner = rb_imemo_fields_owner(obj);
830 if (shape->parent_offset == INVALID_SHAPE_ID) {
833 *removed_shape = NULL;
837 if (shape->type == SHAPE_IVAR && shape->edge_name ==
id) {
838 *removed_shape = shape;
840 return RSHAPE(shape->parent_offset);
844 rb_shape_t *new_parent = remove_shape_recursive(obj, RSHAPE(shape->parent_offset),
id, removed_shape);
849 VALUE klass = obj_get_owner_class(obj);
850 rb_shape_t *new_child = shape_get_next(new_parent, shape->type, klass, shape->edge_name,
true);
851 RUBY_ASSERT(!new_child || new_child->capacity <= shape->capacity);
864rb_obj_shape_transition_remove_ivar(
VALUE obj,
ID id, shape_id_t *removed_shape_id)
866 shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
867 RUBY_ASSERT(!rb_shape_frozen_p(original_shape_id));
869 if (rb_shape_complex_p(original_shape_id)) {
870 return original_shape_id;
874 rb_shape_t *new_shape = remove_shape_recursive(obj, RSHAPE(original_shape_id),
id, &removed_shape);
877 *removed_shape_id = SHAPE_OFFSET(removed_shape);
881 return SHAPE_ID(new_shape, original_shape_id);
883 else if (removed_shape) {
886 shape_id_t next_shape_id = rb_shape_transition_complex(original_shape_id);
887 RUBY_ASSERT(rb_shape_has_object_id(next_shape_id) == rb_shape_has_object_id(original_shape_id));
888 return next_shape_id;
890 return original_shape_id;
894rb_obj_shape_transition_add_ivar(
VALUE obj,
ID id)
896 shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
897 RUBY_ASSERT(!rb_shape_frozen_p(original_shape_id));
899 VALUE klass = obj_get_owner_class(obj);
900 rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass,
id,
true);
902 return SHAPE_ID(next_shape, original_shape_id);
905 return rb_shape_transition_complex(original_shape_id);
910rb_shape_transition_add_ivar_no_warnings(shape_id_t original_shape_id,
ID id,
VALUE klass)
912 RUBY_ASSERT(!rb_shape_frozen_p(original_shape_id));
914 rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass,
id,
false);
916 return SHAPE_ID(next_shape, original_shape_id);
919 return rb_shape_transition_complex(original_shape_id);
926rb_shape_get_iv_index_with_hint(shape_id_t shape_id,
ID id, attr_index_t *value, shape_id_t *shape_id_hint)
928 attr_index_t index_hint = *value;
930 if (*shape_id_hint == INVALID_SHAPE_ID) {
931 *shape_id_hint = shape_id;
932 return rb_shape_get_iv_index(shape_id,
id, value);
937 rb_shape_t *shape_hint = RSHAPE(*shape_id_hint);
944 if (shape->ancestor_index && shape->next_field_index >= ANCESTOR_CACHE_THRESHOLD) {
945 depth = ANCESTOR_SEARCH_MAX_DEPTH;
948 while (depth > 0 && shape->next_field_index > index_hint) {
949 while (shape_hint->next_field_index > shape->next_field_index) {
950 shape_hint = RSHAPE(shape_hint->parent_offset);
953 if (shape_hint == shape) {
956 *shape_id_hint = SHAPE_OFFSET(shape);
959 if (shape->edge_name ==
id) {
961 *value = shape->next_field_index - 1;
962 *shape_id_hint = SHAPE_OFFSET(shape);
966 shape = RSHAPE(shape->parent_offset);
972 if (!shape->ancestor_index && initial_shape->ancestor_index) {
973 shape = initial_shape;
975 *shape_id_hint = shape_id;
976 return shape_get_iv_index(shape,
id, value);
982 if (shape->ancestor_index && shape->next_field_index >= ANCESTOR_CACHE_THRESHOLD) {
983 redblack_node_t *node = redblack_find(shape->ancestor_index,
id);
985 *ivar_shape = redblack_value(node);
997 while (shape->parent_offset != INVALID_SHAPE_ID) {
998 if (shape->edge_name ==
id) {
1000 *ivar_shape = shape;
1004 shape = RSHAPE(shape->parent_offset);
1011rb_shape_find_ivar(shape_id_t current_shape_id,
ID id, shape_id_t *ivar_shape_id)
1013 RUBY_ASSERT(!rb_shape_complex_p(current_shape_id));
1015 rb_shape_t *shape = RSHAPE(current_shape_id);
1018 if (!shape_cache_find_ivar(shape,
id, &ivar_shape)) {
1020 if (shape->ancestor_index && shape->next_field_index >= ANCESTOR_CACHE_THRESHOLD) {
1024 if (!shape_find_ivar(shape,
id, &ivar_shape)) {
1030 *ivar_shape_id = SHAPE_ID(ivar_shape, current_shape_id);
1036rb_shape_get_iv_index(shape_id_t shape_id,
ID id, attr_index_t *value)
1042 shape_id_t ivar_shape_id;
1043 if (rb_shape_find_ivar(shape_id,
id, &ivar_shape_id)) {
1044 *value = RSHAPE_INDEX(ivar_shape_id);
1051rb_shape_id_offset(
void)
1053 return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS /
sizeof(uintptr_t);
1065 if (dest_shape->type != initial_shape->type) {
1066 midway_shape = shape_rebuild(initial_shape, RSHAPE(dest_shape->parent_offset));
1067 if (UNLIKELY(!midway_shape)) {
1072 midway_shape = initial_shape;
1075 switch ((
enum shape_type)dest_shape->type) {
1078 midway_shape = get_next_shape_internal(midway_shape, dest_shape->edge_name, SHAPE_IVAR, &dont_care,
true);
1086 return midway_shape;
1092rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
1094 RUBY_ASSERT(RSHAPE_TYPE_P(initial_shape_id, SHAPE_ROOT));
1096 if (RB_UNLIKELY(rb_shape_complex_p(initial_shape_id))) {
1098 return initial_shape_id;
1101 if (RB_UNLIKELY(rb_shape_complex_p(dest_shape_id))) {
1102 return rb_shape_transition_complex(initial_shape_id);
1105 shape_id_t next_shape_id;
1107 if (dest_shape_id & SHAPE_ID_FL_HAS_OBJECT_ID) {
1108 rb_shape_t *next_shape = shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id));
1110 next_shape_id = SHAPE_ID(next_shape, initial_shape_id & ~SHAPE_ID_FL_NON_CANONICAL_MASK);
1113 return rb_shape_transition_complex(initial_shape_id | (dest_shape_id & ~SHAPE_ID_FL_NON_CANONICAL_MASK));
1118 next_shape_id = RSHAPE_OFFSET(dest_shape_id) | RSHAPE_FLAGS(initial_shape_id);
1120 return next_shape_id;
1124rb_shape_copy_fields(
VALUE dest,
VALUE *dest_buf, shape_id_t dest_shape_id,
VALUE *src_buf, shape_id_t src_shape_id)
1126 rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
1127 rb_shape_t *src_shape = RSHAPE(src_shape_id);
1129 if (src_shape->next_field_index == dest_shape->next_field_index) {
1131 MEMCPY(dest_buf, src_buf,
VALUE, dest_shape->next_field_index);
1134 for (uint32_t i = 0; i < dest_shape->next_field_index; i++) {
1139 while (src_shape->parent_offset != INVALID_SHAPE_ID) {
1140 if (src_shape->type == SHAPE_IVAR) {
1141 while (dest_shape->edge_name != src_shape->edge_name) {
1142 if (UNLIKELY(dest_shape->parent_offset == INVALID_SHAPE_ID)) {
1143 rb_bug(
"Lost field %s", rb_id2name(src_shape->edge_name));
1145 dest_shape = RSHAPE(dest_shape->parent_offset);
1148 RB_OBJ_WRITE(dest, &dest_buf[dest_shape->next_field_index - 1], src_buf[src_shape->next_field_index - 1]);
1150 src_shape = RSHAPE(src_shape->parent_offset);
1156rb_shape_edges_count(shape_id_t shape_id)
1160 if (SINGLE_CHILD_P(shape->edges)) {
1164 return rb_managed_id_table_size(shape->edges);
1171rb_shape_memsize(shape_id_t shape_id)
1176 if (shape->edges && !SINGLE_CHILD_P(shape->edges)) {
1177 memsize += rb_managed_id_table_size(shape->edges);
1183rb_shape_foreach_field(shape_id_t initial_shape_id, rb_shape_foreach_transition_callback func,
void *data)
1185 RUBY_ASSERT(!rb_shape_complex_p(initial_shape_id));
1187 rb_shape_t *shape = RSHAPE(initial_shape_id);
1188 if (shape->type == SHAPE_ROOT) {
1192 shape_id_t parent_offset = SHAPE_ID(RSHAPE(shape->parent_offset), initial_shape_id);
1193 if (rb_shape_foreach_field(parent_offset, func, data)) {
1194 switch (func(SHAPE_ID(shape, initial_shape_id), data)) {
1201 rb_bug(
"unreachable");
1216rb_shape_expected_layout(
VALUE obj)
1220 return SHAPE_ID_LAYOUT_ROBJECT;
1226 return SHAPE_ID_LAYOUT_OTHER;
1228 return SHAPE_ID_LAYOUT_RCLASS;
1232 return SHAPE_ID_LAYOUT_EXTENDED;
1235 if (IMEMO_TYPE_P(obj, imemo_fields)) {
1236 return SHAPE_ID_LAYOUT_ROBJECT;
1238 return SHAPE_ID_LAYOUT_OTHER;
1241 return SHAPE_ID_LAYOUT_OTHER;
1246shape_layout_name(shape_id_t shape_id)
1248 switch (rb_shape_layout(shape_id)) {
1249 case SHAPE_ID_LAYOUT_ROBJECT:
1251 case SHAPE_ID_LAYOUT_RCLASS:
1253 case SHAPE_ID_LAYOUT_EXTENDED:
1254 return "extended (or RData)";
1255 case SHAPE_ID_LAYOUT_OTHER:
1263rb_shape_verify_capacity_consistency_p(
VALUE obj)
1267 return IMEMO_TYPE_P(obj, imemo_fields);
1278rb_shape_verify_consistency(
VALUE obj, shape_id_t shape_id)
1280 if (shape_id == INVALID_SHAPE_ID) {
1281 rb_bug(
"Can't set INVALID_SHAPE_ID on an object");
1284 shape_id_t actual_layout = rb_shape_layout(rb_obj_shape_id(obj));
1285 shape_id_t expected_layout = rb_shape_expected_layout(obj);
1286 if (actual_layout != expected_layout) {
1288 rb_bug(
"shape_id layout mismatch: expected=%s actual=%s shape_id=%u obj=%s",
1289 shape_layout_name(expected_layout), shape_layout_name(actual_layout), shape_id, rb_obj_info(obj));
1293 if (shape_id == ROOT_SHAPE_ID) {
1300 if (rb_shape_complex_p(shape_id)) {
1307 else if (IMEMO_TYPE_P(obj, imemo_fields)) {
1312 bool has_object_id =
false;
1313 while (shape->parent_offset != INVALID_SHAPE_ID) {
1314 if (shape->type == SHAPE_OBJ_ID) {
1315 has_object_id =
true;
1318 shape = RSHAPE(shape->parent_offset);
1321 if (rb_shape_has_object_id(shape_id)) {
1322 if (!has_object_id) {
1323 rb_bug(
"shape_id claim having obj_id but doesn't shape_id=%u, obj=%s", shape_id, rb_obj_info(obj));
1327 if (has_object_id) {
1328 rb_bug(
"shape_id claim not having obj_id but it does shape_id=%u, obj=%s", shape_id, rb_obj_info(obj));
1332 attr_index_t ivar_count = RSHAPE_LEN(shape_id);
1333 if (has_object_id) {
1340 RUBY_ASSERT(!(shape_id & SHAPE_ID_HAS_IVAR_MASK));
1344 if (rb_shape_verify_capacity_consistency_p(obj)) {
1345 attr_index_t shape_id_capacity = rb_shape_embedded_capacity(shape_id);
1347 size_t shape_id_slot_size = shape_id_capacity *
sizeof(
VALUE) +
sizeof(
struct RBasic);
1348 size_t actual_slot_size = rb_gc_obj_slot_size(obj);
1350 if (shape_id_capacity == SHAPE_ID_CAPACITY_MAX) {
1351 if (actual_slot_size < SHAPE_ID_CAPACITY_MAX) {
1352 rb_bug(
"shape_id_capacity is SHAPE_ID_CAPACITY_MAX, but actual slot size is only %zu", actual_slot_size);
1356 if (shape_id_slot_size != actual_slot_size) {
1357 rb_bug(
"shape_id capacity flags mismatch: shape_id_slot_size=%zu, gc_slot_size=%zu\n", shape_id_slot_size, actual_slot_size);
1373shape_complex(
VALUE self)
1376 return RBOOL(rb_shape_complex_p(shape_id));
1380shape_frozen(
VALUE self)
1383 return RBOOL(shape_id & SHAPE_ID_FL_FROZEN);
1387shape_has_object_id_p(
VALUE self)
1390 return RBOOL(rb_shape_has_object_id(shape_id));
1394shape_layout(
VALUE self)
1398 switch (rb_shape_layout(shape_id)) {
1399 case SHAPE_ID_LAYOUT_ROBJECT:
1400 return ID2SYM(rb_intern(
"robject"));
1401 case SHAPE_ID_LAYOUT_RCLASS:
1402 return ID2SYM(rb_intern(
"rclass"));
1403 case SHAPE_ID_LAYOUT_EXTENDED:
1404 return ID2SYM(rb_intern(
"extended_or_rdata"));
1405 case SHAPE_ID_LAYOUT_OTHER:
1406 return ID2SYM(rb_intern(
"other"));
1408 rb_bug(
"unknown shape layout: %u", rb_shape_layout(shape_id));
1415 if (is_instance_id(key)) {
1424shape_id_t_to_rb_cShape(shape_id_t shape_id)
1431 INT2NUM(RSHAPE_OFFSET(shape_id)),
1432 INT2NUM(shape->parent_offset),
1433 rb_shape_edge_name(shape),
1434 INT2NUM(shape->next_field_index),
1435 INT2NUM(rb_shape_embedded_capacity(shape_id)),
1437 INT2NUM(RSHAPE_CAPACITY(shape_id)));
1442static enum rb_id_table_iterator_result
1443rb_edges_to_hash(
ID key,
VALUE value,
void *ref)
1445 rb_hash_aset(*(
VALUE *)ref, parse_key(key), shape_id_t_to_rb_cShape(SHAPE_OFFSET((
rb_shape_t *)value)));
1446 return ID_TABLE_CONTINUE;
1450rb_shape_edges(
VALUE self)
1454 VALUE hash = rb_hash_new();
1457 if (SINGLE_CHILD_P(shape->edges)) {
1458 rb_shape_t *child = SINGLE_CHILD(shape->edges);
1459 rb_edges_to_hash(child->edge_name, (
VALUE)child, &hash);
1462 VALUE edges = shape->edges;
1463 rb_managed_id_table_foreach(edges, rb_edges_to_hash, &hash);
1474 if (shape->edge_name) {
1475 if (is_instance_id(shape->edge_name)) {
1476 return ID2SYM(shape->edge_name);
1478 return INT2NUM(shape->capacity);
1484rb_shape_export_depth(
VALUE self)
1487 return SIZET2NUM(rb_shape_depth(shape_id));
1491rb_shape_parent(
VALUE self)
1495 if (shape->parent_offset != INVALID_SHAPE_ID) {
1496 return shape_id_t_to_rb_cShape(shape->parent_offset);
1507 rb_raise(rb_eArgError,
"Can't get shape of special constant");
1509 return shape_id_t_to_rb_cShape(rb_obj_shape_id(obj));
1513rb_shape_root_shape(
VALUE self)
1515 return shape_id_t_to_rb_cShape(ROOT_SHAPE_ID);
1519rb_shape_shapes_available(
VALUE self)
1521 return ULL2NUM(MAX_SHAPE_ID - (rb_shapes_count() - 1));
1525rb_shape_exhaust(
int argc,
VALUE *argv,
VALUE self)
1528 int offset = argc == 1 ?
NUM2INT(argv[0]) : 0;
1534rb_shape_class_max_iv_count(
VALUE self,
VALUE klass)
1536 return INT2NUM(RCLASS_MAX_IV_COUNT(klass));
1541static enum rb_id_table_iterator_result collect_keys_and_values(
ID key,
VALUE value,
void *ref)
1543 rb_hash_aset(*(
VALUE *)ref, parse_key(key), shape_to_h((
rb_shape_t *)value));
1544 return ID_TABLE_CONTINUE;
1549 VALUE hash = rb_hash_new();
1551 if (SINGLE_CHILD_P(edges)) {
1553 collect_keys_and_values(child->edge_name, (
VALUE)child, &hash);
1556 rb_managed_id_table_foreach(edges, collect_keys_and_values, &hash);
1568 rb_hash_aset(
rb_shape,
ID2SYM(rb_intern(
"edges")), edges(shape->edges));
1570 if (shape == rb_shape_get_root_shape()) {
1577 rb_hash_aset(
rb_shape,
ID2SYM(rb_intern(
"edge_name")), rb_id2str(shape->edge_name));
1582shape_transition_tree(
VALUE self)
1584 return shape_to_h(rb_shape_get_root_shape());
1590 shape_id_t shape_id =
NUM2UINT(
id);
1591 if (shape_id >= rb_shapes_count()) {
1592 rb_raise(rb_eArgError,
"Shape ID %d is out of bounds\n", shape_id);
1594 return shape_id_t_to_rb_cShape(shape_id);
1599#include <sys/mman.h>
1603Init_default_shapes(
void)
1605 attr_index_t max_capacity = (attr_index_t)((rb_gc_max_allocation_size() -
sizeof(
struct RBasic)) /
sizeof(
VALUE));
1606 if (max_capacity > SHAPE_ID_CAPACITY_MAX) max_capacity = SHAPE_ID_CAPACITY_MAX;
1607 rb_shape_tree.max_capacity = max_capacity;
1611 rb_shape_tree.shape_list = (
rb_shape_t *)mmap(NULL, shape_list_mmap_size,
1612 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1613 if (rb_shape_tree.shape_list == MAP_FAILED) {
1614 rb_shape_tree.shape_list = 0;
1617 ruby_annotate_mmap(rb_shape_tree.shape_list, shape_list_mmap_size,
"Ruby:Init_default_shapes:shape_list");
1623 if (!rb_shape_tree.shape_list) {
1627 rb_shape_tree.id_object_id = rb_make_internal_id();
1630 size_t shape_cache_mmap_size = rb_size_mul_or_raise(REDBLACK_CACHE_SIZE,
sizeof(redblack_node_t),
rb_eRuntimeError);
1631 redblack_cache = (redblack_node_t *)mmap(NULL, shape_cache_mmap_size,
1632 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1633 redblack_cache_size = 0;
1638 if (redblack_cache == MAP_FAILED) {
1639 redblack_cache = NULL;
1640 redblack_cache_size = REDBLACK_CACHE_SIZE;
1643 ruby_annotate_mmap(redblack_cache, shape_cache_mmap_size,
"Ruby:Init_default_shapes:shape_cache");
1647 rb_gc_register_address(&shape_tree_obj);
1655 rb_shape_t *root = rb_shape_alloc_with_parent_offset(0, INVALID_SHAPE_ID);
1657 root->type = SHAPE_ROOT;
1659 RUBY_ASSERT(!(SHAPE_OFFSET(root) & SHAPE_ID_HAS_IVAR_MASK));
1662 rb_shape_t *root_with_obj_id = get_next_shape_internal(root, rb_shape_tree.id_object_id, SHAPE_OBJ_ID, &dontcare,
true);
1664 RUBY_ASSERT(SHAPE_OFFSET(root_with_obj_id) == ROOT_SHAPE_WITH_OBJ_ID);
1665 RUBY_ASSERT(root_with_obj_id->type == SHAPE_OBJ_ID);
1666 RUBY_ASSERT(root_with_obj_id->edge_name == rb_shape_tree.id_object_id);
1667 RUBY_ASSERT(root_with_obj_id->next_field_index == 1);
1668 RUBY_ASSERT(!(SHAPE_OFFSET(root_with_obj_id) & SHAPE_ID_HAS_IVAR_MASK));
1669 (void)root_with_obj_id;
1684 "embedded_capacity",
1697 rb_define_const(rb_cShape,
"SHAPE_ROOT",
INT2NUM(SHAPE_ROOT));
1698 rb_define_const(rb_cShape,
"SHAPE_IVAR",
INT2NUM(SHAPE_IVAR));
1699 rb_define_const(rb_cShape,
"SHAPE_ID_NUM_BITS",
INT2NUM(SHAPE_ID_NUM_BITS));
1700 rb_define_const(rb_cShape,
"SHAPE_FLAG_SHIFT",
INT2NUM(SHAPE_FLAG_SHIFT));
1701 rb_define_const(rb_cShape,
"SHAPE_MAX_VARIATIONS",
INT2NUM(SHAPE_MAX_VARIATIONS));
1702 rb_define_const(rb_cShape,
"SHAPE_MAX_FIELDS",
INT2NUM(rb_shape_max_capacity()));
1704 rb_define_const(rb_cShape,
"SIZEOF_REDBLACK_NODE_T",
INT2NUM(
sizeof(redblack_node_t)));
1705 rb_define_const(rb_cShape,
"SHAPE_BUFFER_SIZE",
INT2NUM(
sizeof(
rb_shape_t) * SHAPE_BUFFER_SIZE));
1706 rb_define_const(rb_cShape,
"REDBLACK_CACHE_SIZE",
INT2NUM(
sizeof(redblack_node_t) * REDBLACK_CACHE_SIZE));
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define RUBY_ATOMIC_VALUE_CAS(var, oldval, newval)
Identical to RUBY_ATOMIC_CAS, except it expects its arguments are VALUE.
#define RUBY_ATOMIC_CAS(var, oldval, newval)
Atomic compare-and-swap.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
#define RUBY_ATOMIC_LOAD(var)
Atomic load.
#define RUBY_ATOMIC_SET(var, val)
Identical to RUBY_ATOMIC_EXCHANGE, except for the return type.
#define RUBY_ALIGNAS
Wraps (or simulates) alignas.
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
VALUE rb_singleton_class(VALUE obj)
Finds or creates the singleton class of the passed object.
#define T_STRING
Old name of RUBY_T_STRING.
#define Qundef
Old name of RUBY_Qundef.
#define T_IMEMO
Old name of RUBY_T_IMEMO.
#define ID2SYM
Old name of RB_ID2SYM.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
#define FL_USER19
Old name of RUBY_FL_USER19.
#define T_DATA
Old name of RUBY_T_DATA.
#define SIZET2NUM
Old name of RB_SIZE2NUM.
#define T_MODULE
Old name of RUBY_T_MODULE.
#define NUM2UINT
Old name of RB_NUM2UINT.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define LONG2NUM
Old name of RB_LONG2NUM.
#define ULL2NUM
Old name of RB_ULL2NUM.
#define NUM2INT
Old name of RB_NUM2INT.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
#define T_CLASS
Old name of RUBY_T_CLASS.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
#define xcalloc
Old name of ruby_xcalloc.
void rb_category_warn(rb_warning_category_t category, const char *fmt,...)
Identical to rb_category_warning(), except it reports unless $VERBOSE is nil.
VALUE rb_eRuntimeError
RuntimeError exception.
@ RB_WARN_CATEGORY_PERFORMANCE
Warning is for performance issues (not enabled by -w).
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
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...
VALUE rb_struct_new(VALUE klass,...)
Creates an instance of the given struct.
VALUE rb_struct_getmember(VALUE self, ID key)
Identical to rb_struct_aref(), except it takes ID instead of VALUE.
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
VALUE type(ANYARGS)
ANYARGS-ed function type.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
This is the struct that holds necessary info for a struct.
const char * wrap_struct_name
Name of structs of this kind.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.