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 (SHAPE_BUFFER_SIZE - 1)
34#define ANCESTOR_SEARCH_MAX_DEPTH 2
36static ID id_object_id;
47typedef struct redblack_node redblack_node_t;
49static redblack_node_t *redblack_cache;
55static inline redblack_node_t *
56redblack_node(redblack_id_t
id)
58 return id ? &redblack_cache[
id - 1] : LEAF;
61static redblack_node_t *
62redblack_left(redblack_node_t *node)
64 if (node->l == LEAF) {
69 redblack_node_t *left = redblack_node(node->l);
74static redblack_node_t *
75redblack_right(redblack_node_t *node)
77 if (node->r == LEAF) {
82 redblack_node_t *right = redblack_node(node->r);
87static redblack_node_t *
88redblack_find0(redblack_node_t *tree,
ID key)
94 RUBY_ASSERT(redblack_left(tree) == LEAF || redblack_left(tree)->key < tree->key);
95 RUBY_ASSERT(redblack_right(tree) == LEAF || redblack_right(tree)->key > tree->key);
97 if (tree->key == key) {
101 if (key < tree->key) {
102 return redblack_find0(redblack_left(tree), key);
105 return redblack_find0(redblack_right(tree), key);
111static redblack_node_t *
112redblack_find(redblack_id_t tree_id,
ID key)
114 return redblack_find0(redblack_node(tree_id), key);
118redblack_value(redblack_node_t *node)
122 return (
rb_shape_t *)((uintptr_t)node->value & ~(uintptr_t)1);
127redblack_color(redblack_node_t *node)
129 return node && ((uintptr_t)node->value & RED);
133redblack_red_p(redblack_node_t *node)
135 return redblack_color(node) == RED;
139redblack_id_for(redblack_node_t *node)
146 redblack_node_t *redblack_nodes = redblack_cache;
147 redblack_id_t
id = (redblack_id_t)(node - redblack_nodes);
152static redblack_node_t *
153redblack_new(
char color,
ID key,
rb_shape_t *value, redblack_node_t *left, redblack_node_t *right)
155 if (redblack_cache_size + 1 >= REDBLACK_CACHE_SIZE) {
163 redblack_node_t *redblack_nodes = redblack_cache;
166 node->value = (
rb_shape_t *)((uintptr_t)value | color);
167 node->l = redblack_id_for(left);
168 node->r = redblack_id_for(right);
172static redblack_node_t *
173redblack_balance(
char color,
ID key,
rb_shape_t *value, redblack_node_t *left, redblack_node_t *right)
175 if (color == BLACK) {
176 ID new_key, new_left_key, new_right_key;
177 rb_shape_t *new_value, *new_left_value, *new_right_value;
178 redblack_node_t *new_left_left, *new_left_right, *new_right_left, *new_right_right;
180 if (redblack_red_p(left) && redblack_red_p(redblack_left(left))) {
182 new_right_value = value;
183 new_right_right = right;
186 new_value = redblack_value(left);
187 new_right_left = redblack_right(left);
189 new_left_key = redblack_left(left)->key;
190 new_left_value = redblack_value(redblack_left(left));
192 new_left_left = redblack_left(redblack_left(left));
193 new_left_right = redblack_right(redblack_left(left));
195 else if (redblack_red_p(left) && redblack_red_p(redblack_right(left))) {
197 new_right_value = value;
198 new_right_right = right;
200 new_left_key = left->key;
201 new_left_value = redblack_value(left);
202 new_left_left = redblack_left(left);
204 new_key = redblack_right(left)->key;
205 new_value = redblack_value(redblack_right(left));
206 new_left_right = redblack_left(redblack_right(left));
207 new_right_left = redblack_right(redblack_right(left));
209 else if (redblack_red_p(right) && redblack_red_p(redblack_left(right))) {
211 new_left_value = value;
212 new_left_left = left;
214 new_right_key = right->key;
215 new_right_value = redblack_value(right);
216 new_right_right = redblack_right(right);
218 new_key = redblack_left(right)->key;
219 new_value = redblack_value(redblack_left(right));
220 new_left_right = redblack_left(redblack_left(right));
221 new_right_left = redblack_right(redblack_left(right));
223 else if (redblack_red_p(right) && redblack_red_p(redblack_right(right))) {
225 new_left_value = value;
226 new_left_left = left;
228 new_key = right->key;
229 new_value = redblack_value(right);
230 new_left_right = redblack_left(right);
232 new_right_key = redblack_right(right)->key;
233 new_right_value = redblack_value(redblack_right(right));
234 new_right_left = redblack_left(redblack_right(right));
235 new_right_right = redblack_right(redblack_right(right));
238 return redblack_new(color, key, value, left, right);
243 RUBY_ASSERT(new_left_left == LEAF || new_left_left->key < new_left_key);
244 RUBY_ASSERT(new_left_right == LEAF || new_left_right->key > new_left_key);
245 RUBY_ASSERT(new_left_right == LEAF || new_left_right->key < new_key);
246 RUBY_ASSERT(new_right_left == LEAF || new_right_left->key < new_right_key);
247 RUBY_ASSERT(new_right_left == LEAF || new_right_left->key > new_key);
248 RUBY_ASSERT(new_right_right == LEAF || new_right_right->key > new_right_key);
251 RED, new_key, new_value,
252 redblack_new(BLACK, new_left_key, new_left_value, new_left_left, new_left_right),
253 redblack_new(BLACK, new_right_key, new_right_value, new_right_left, new_right_right));
256 return redblack_new(color, key, value, left, right);
259static redblack_node_t *
260redblack_insert_aux(redblack_node_t *tree,
ID key,
rb_shape_t *value)
263 return redblack_new(RED, key, value, LEAF, LEAF);
266 redblack_node_t *left, *right;
267 if (key < tree->key) {
268 left = redblack_insert_aux(redblack_left(tree), key, value);
270 right = redblack_right(tree);
271 RUBY_ASSERT(right == LEAF || right->key > tree->key);
273 else if (key > tree->key) {
274 left = redblack_left(tree);
275 RUBY_ASSERT(left == LEAF || left->key < tree->key);
276 right = redblack_insert_aux(redblack_right(tree), key, value);
283 return redblack_balance(
284 redblack_color(tree),
286 redblack_value(tree),
293static redblack_node_t *
294redblack_force_black(redblack_node_t *node)
296 node->value = redblack_value(node);
301redblack_insert(redblack_node_t *tree,
ID key,
rb_shape_t *value)
303 redblack_node_t *root = redblack_insert_aux(tree, key, value);
305 if (redblack_red_p(root)) {
306 return redblack_id_for(redblack_force_black(root));
309 return redblack_id_for(root);
321rb_shape_get_root_shape(
void)
323 return rb_shape_tree.shape_list;
327shape_tree_mark_and_move(
void *data)
329 rb_shape_t *cursor = rb_shape_get_root_shape();
331 while (cursor <= end) {
332 if (cursor->edges && !SINGLE_CHILD_P(cursor->edges)) {
333 rb_gc_mark_and_move(&cursor->edges);
340rb_shapes_cache_size(
void)
342 return redblack_cache ? redblack_cache_size : 0;
352shape_tree_memsize(
const void *data)
354 if (redblack_cache) {
355 return redblack_cache_size *
sizeof(redblack_node_t);
363 .dmark = shape_tree_mark_and_move,
365 .dsize = shape_tree_memsize,
366 .dcompact = shape_tree_mark_and_move,
376static inline shape_id_t
380 return (shape_id_t)(shape - rb_shape_tree.shape_list);
383static inline shape_id_t
384shape_id(
rb_shape_t *shape, shape_id_t previous_shape_id)
387 shape_id_t raw_id = (shape_id_t)(shape - rb_shape_tree.shape_list);
388 return raw_id | RSHAPE_FLAGS(previous_shape_id);
393shape_frozen_p(shape_id_t shape_id)
395 return shape_id & SHAPE_ID_FL_FROZEN;
400rb_shape_each_shape_id(each_shape_callback callback,
void *data)
402 rb_shape_t *start = rb_shape_get_root_shape();
405 while (cursor < end) {
406 callback((shape_id_t)(cursor - start), data);
411RUBY_FUNC_EXPORTED shape_id_t
412rb_obj_shape_id(
VALUE obj)
415 rb_bug(
"rb_obj_shape_id: called on a special constant");
419 VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj);
421 return RBASIC_SHAPE_ID(fields_obj);
423 return ROOT_SHAPE_ID;
425 return RBASIC_SHAPE_ID(obj);
429rb_shape_depth(shape_id_t shape_id)
434 while (shape->parent_id != INVALID_SHAPE_ID) {
436 shape = RSHAPE(shape->parent_id);
445 shape_id_t current, new_id;
449 if (current > MAX_SHAPE_ID) {
452 new_id = current + 1;
455 return &rb_shape_tree.shape_list[current];
459rb_shape_alloc_with_parent_id(
ID edge_name, shape_id_t parent_id)
462 if (!shape)
return NULL;
464 shape->edge_name = edge_name;
465 shape->next_field_index = 0;
466 shape->parent_id = parent_id;
475 rb_shape_t *shape = rb_shape_alloc_with_parent_id(edge_name, shape_offset(parent));
476 if (!shape)
return NULL;
478 shape->type = (uint8_t)
type;
479 shape->capacity = parent->capacity;
485static redblack_node_t *
488 if (!(shape->ancestor_index || shape->parent_id == INVALID_SHAPE_ID)) {
489 redblack_node_t *parent_index_node = redblack_cache_ancestors(RSHAPE(shape->parent_id));
491 if (shape->type == SHAPE_IVAR) {
492 shape->ancestor_index = redblack_insert(parent_index_node, shape->edge_name, shape);
495 if (shape->ancestor_index) {
496 redblack_node_t *inserted_node = redblack_find(shape->ancestor_index, shape->edge_name);
498 RUBY_ASSERT(redblack_value(inserted_node) == shape);
503 shape->ancestor_index = redblack_id_for(parent_index_node);
507 return redblack_node(shape->ancestor_index);
510static redblack_node_t *
518shape_grow_capa(attr_index_t current_capa)
520 const attr_index_t *capacities = rb_shape_tree.capacities;
521 size_t heaps_count = rb_shape_tree.heaps_count;
524 for (
size_t i = 0; i < heaps_count; i++) {
525 attr_index_t
capa = capacities[i];
526 if (
capa > current_capa) {
531 return (attr_index_t)rb_malloc_grow_capa(current_capa,
sizeof(
VALUE));
535rb_shape_alloc_new_child(
ID id,
rb_shape_t *shape,
enum shape_type shape_type)
537 rb_shape_t *new_shape = rb_shape_alloc(
id, shape, shape_type);
538 if (!new_shape)
return NULL;
540 switch (shape_type) {
543 if (UNLIKELY(shape->next_field_index >= shape->capacity)) {
544 RUBY_ASSERT(shape->next_field_index == shape->capacity);
545 new_shape->capacity = shape_grow_capa(shape->capacity);
547 RUBY_ASSERT(new_shape->capacity > shape->next_field_index);
548 new_shape->next_field_index = shape->next_field_index + 1;
549 if (new_shape->next_field_index > ANCESTOR_CACHE_THRESHOLD) {
551 redblack_cache_ancestors(new_shape);
556 rb_bug(
"Unreachable");
564get_next_shape_internal_atomic(
rb_shape_t *shape,
ID id,
enum shape_type shape_type,
bool *variation_created,
bool new_variations_allowed)
568 *variation_created =
false;
572 edges_table = RUBY_ATOMIC_VALUE_LOAD(shape->edges);
577 if (SINGLE_CHILD_P(edges_table)) {
578 rb_shape_t *child = SINGLE_CHILD(edges_table);
581 if (child->edge_name ==
id) {
588 if (rb_managed_id_table_lookup(edges_table,
id, &lookup_result)) {
595 if (!res && new_variations_allowed) {
598 rb_shape_t *new_shape = rb_shape_alloc_new_child(
id, shape, shape_type);
604 new_edges = TAG_SINGLE_CHILD(new_shape);
608 if (SINGLE_CHILD_P(edges_table)) {
609 rb_shape_t *old_child = SINGLE_CHILD(edges_table);
610 new_edges = rb_managed_id_table_new(2);
611 rb_managed_id_table_insert(new_edges, old_child->edge_name, (
VALUE)old_child);
614 new_edges = rb_managed_id_table_dup(edges_table);
617 rb_managed_id_table_insert(new_edges, new_shape->edge_name, (
VALUE)new_shape);
618 *variation_created =
true;
635get_next_shape_internal(
rb_shape_t *shape,
ID id,
enum shape_type shape_type,
bool *variation_created,
bool new_variations_allowed)
637 if (rb_multi_ractor_p()) {
638 return get_next_shape_internal_atomic(shape,
id, shape_type, variation_created, new_variations_allowed);
642 *variation_created =
false;
644 VALUE edges_table = shape->edges;
649 if (SINGLE_CHILD_P(edges_table)) {
650 rb_shape_t *child = SINGLE_CHILD(edges_table);
653 if (child->edge_name ==
id) {
660 if (rb_managed_id_table_lookup(edges_table,
id, &lookup_result)) {
670 if (!new_variations_allowed || rb_shapes_count() > MAX_SHAPE_ID) {
674 rb_shape_t *new_shape = rb_shape_alloc_new_child(
id, shape, shape_type);
678 shape->edges = TAG_SINGLE_CHILD(new_shape);
682 if (SINGLE_CHILD_P(edges_table)) {
683 rb_shape_t *old_child = SINGLE_CHILD(edges_table);
684 VALUE new_edges = rb_managed_id_table_new(2);
685 rb_managed_id_table_insert(new_edges, old_child->edge_name, (
VALUE)old_child);
689 rb_managed_id_table_insert(shape->edges, new_shape->edge_name, (
VALUE)new_shape);
690 *variation_created =
true;
701rb_shape_transition_object_id(shape_id_t original_shape_id)
703 RUBY_ASSERT(!rb_shape_has_object_id(original_shape_id));
706 rb_shape_t *shape = get_next_shape_internal(RSHAPE(original_shape_id), id_object_id, SHAPE_OBJ_ID, &dont_care,
true);
708 return ROOT_TOO_COMPLEX_WITH_OBJ_ID | RSHAPE_FLAGS(original_shape_id);
712 return shape_id(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
716rb_shape_object_id(shape_id_t original_shape_id)
718 RUBY_ASSERT(rb_shape_has_object_id(original_shape_id));
720 rb_shape_t *shape = RSHAPE(original_shape_id);
721 while (shape->type != SHAPE_OBJ_ID) {
722 if (UNLIKELY(shape->parent_id == INVALID_SHAPE_ID)) {
723 rb_bug(
"Missing object_id in shape tree");
725 shape = RSHAPE(shape->parent_id);
728 return shape_id(shape, original_shape_id) | SHAPE_ID_FL_HAS_OBJECT_ID;
740 return get_next_shape_internal(shape,
id, SHAPE_IVAR, &dont_care,
true);
744rb_shape_get_next_iv_shape(shape_id_t shape_id,
ID id)
747 rb_shape_t *next_shape = shape_get_next_iv_shape(shape,
id);
749 return INVALID_SHAPE_ID;
751 return shape_offset(next_shape);
755shape_get_iv_index(
rb_shape_t *shape,
ID id, attr_index_t *value)
757 while (shape->parent_id != INVALID_SHAPE_ID) {
758 if (shape->edge_name ==
id) {
759 enum shape_type shape_type;
760 shape_type = (
enum shape_type)shape->type;
762 switch (shape_type) {
765 *value = shape->next_field_index - 1;
770 rb_bug(
"Ivar should not exist on transition");
774 shape = RSHAPE(shape->parent_id);
781shape_get_next(
rb_shape_t *shape,
enum shape_type shape_type,
VALUE klass,
ID id,
bool emit_warnings)
787 if (shape_get_iv_index(shape,
id, &index)) {
788 rb_bug(
"rb_shape_get_next: trying to create ivar that already exists at index %u", index);
792 bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS;
793 bool variation_created =
false;
794 rb_shape_t *new_shape = get_next_shape_internal(shape,
id, shape_type, &variation_created, allow_new_shape);
802 if (new_shape->next_field_index > RCLASS_MAX_IV_COUNT(klass) && !RCLASS_EXPECT_NO_IVAR(klass)) {
803 RCLASS_SET_MAX_IV_COUNT(klass, new_shape->next_field_index);
806 if (variation_created) {
807 RCLASS_VARIATION_COUNT(klass)++;
810 if (RCLASS_VARIATION_COUNT(klass) >= SHAPE_MAX_VARIATIONS) {
813 "The class %"PRIsVALUE
" reached %d shape variations, instance variables accesses will be slower and memory usage increased.\n"
814 "It is recommended to define instance variables in a consistent order, for instance by eagerly defining them all in the #initialize method.",
826obj_get_owner_class(
VALUE obj)
829 if (IMEMO_TYPE_P(obj, imemo_fields)) {
830 VALUE owner = rb_imemo_fields_owner(obj);
850 if (shape->parent_id == INVALID_SHAPE_ID) {
853 *removed_shape = NULL;
857 if (shape->type == SHAPE_IVAR && shape->edge_name ==
id) {
858 *removed_shape = shape;
860 return RSHAPE(shape->parent_id);
864 rb_shape_t *new_parent = remove_shape_recursive(obj, RSHAPE(shape->parent_id),
id, removed_shape);
869 VALUE klass = obj_get_owner_class(obj);
870 rb_shape_t *new_child = shape_get_next(new_parent, shape->type, klass, shape->edge_name,
true);
871 RUBY_ASSERT(!new_child || new_child->capacity <= shape->capacity);
884rb_obj_shape_transition_remove_ivar(
VALUE obj,
ID id, shape_id_t *removed_shape_id)
886 shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
889 if (rb_shape_too_complex_p(original_shape_id)) {
890 return original_shape_id;
894 rb_shape_t *new_shape = remove_shape_recursive(obj, RSHAPE(original_shape_id),
id, &removed_shape);
897 *removed_shape_id = shape_offset(removed_shape);
901 return shape_id(new_shape, original_shape_id);
903 else if (removed_shape) {
906 shape_id_t next_shape_id = rb_shape_transition_complex(original_shape_id);
907 RUBY_ASSERT(rb_shape_has_object_id(next_shape_id) == rb_shape_has_object_id(original_shape_id));
908 return next_shape_id;
910 return original_shape_id;
914rb_obj_shape_transition_add_ivar(
VALUE obj,
ID id)
916 shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj);
919 VALUE klass = obj_get_owner_class(obj);
920 rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass,
id,
true);
922 return shape_id(next_shape, original_shape_id);
925 return rb_shape_transition_complex(original_shape_id);
930rb_shape_transition_add_ivar_no_warnings(shape_id_t original_shape_id,
ID id,
VALUE klass)
934 rb_shape_t *next_shape = shape_get_next(RSHAPE(original_shape_id), SHAPE_IVAR, klass,
id,
false);
936 return shape_id(next_shape, original_shape_id);
939 return rb_shape_transition_complex(original_shape_id);
946rb_shape_get_iv_index_with_hint(shape_id_t shape_id,
ID id, attr_index_t *value, shape_id_t *shape_id_hint)
948 attr_index_t index_hint = *value;
950 if (*shape_id_hint == INVALID_SHAPE_ID) {
951 *shape_id_hint = shape_id;
952 return rb_shape_get_iv_index(shape_id,
id, value);
957 rb_shape_t *shape_hint = RSHAPE(*shape_id_hint);
964 if (shape->ancestor_index && shape->next_field_index >= ANCESTOR_CACHE_THRESHOLD) {
965 depth = ANCESTOR_SEARCH_MAX_DEPTH;
968 while (depth > 0 && shape->next_field_index > index_hint) {
969 while (shape_hint->next_field_index > shape->next_field_index) {
970 shape_hint = RSHAPE(shape_hint->parent_id);
973 if (shape_hint == shape) {
976 *shape_id_hint = shape_offset(shape);
979 if (shape->edge_name ==
id) {
981 *value = shape->next_field_index - 1;
982 *shape_id_hint = shape_offset(shape);
986 shape = RSHAPE(shape->parent_id);
992 if (!shape->ancestor_index && initial_shape->ancestor_index) {
993 shape = initial_shape;
995 *shape_id_hint = shape_id;
996 return shape_get_iv_index(shape,
id, value);
1002 if (shape->ancestor_index && shape->next_field_index >= ANCESTOR_CACHE_THRESHOLD) {
1003 redblack_node_t *node = redblack_find(shape->ancestor_index,
id);
1005 *ivar_shape = redblack_value(node);
1017 while (shape->parent_id != INVALID_SHAPE_ID) {
1018 if (shape->edge_name ==
id) {
1020 *ivar_shape = shape;
1024 shape = RSHAPE(shape->parent_id);
1031rb_shape_find_ivar(shape_id_t current_shape_id,
ID id, shape_id_t *ivar_shape_id)
1033 RUBY_ASSERT(!rb_shape_too_complex_p(current_shape_id));
1035 rb_shape_t *shape = RSHAPE(current_shape_id);
1038 if (!shape_cache_find_ivar(shape,
id, &ivar_shape)) {
1040 if (shape->ancestor_index && shape->next_field_index >= ANCESTOR_CACHE_THRESHOLD) {
1044 if (!shape_find_ivar(shape,
id, &ivar_shape)) {
1050 *ivar_shape_id = shape_id(ivar_shape, current_shape_id);
1056rb_shape_get_iv_index(shape_id_t shape_id,
ID id, attr_index_t *value)
1062 shape_id_t ivar_shape_id;
1063 if (rb_shape_find_ivar(shape_id,
id, &ivar_shape_id)) {
1064 *value = RSHAPE_INDEX(ivar_shape_id);
1071rb_shape_id_offset(
void)
1073 return sizeof(uintptr_t) - SHAPE_ID_NUM_BITS /
sizeof(uintptr_t);
1085 if (dest_shape->type != initial_shape->type) {
1086 midway_shape = shape_rebuild(initial_shape, RSHAPE(dest_shape->parent_id));
1087 if (UNLIKELY(!midway_shape)) {
1092 midway_shape = initial_shape;
1095 switch ((
enum shape_type)dest_shape->type) {
1097 midway_shape = shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
1104 return midway_shape;
1110rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id)
1112 RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
1113 RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id));
1115 shape_id_t next_shape_id;
1117 if (dest_shape_id & SHAPE_ID_FL_HAS_OBJECT_ID) {
1118 rb_shape_t *next_shape = shape_rebuild(RSHAPE(initial_shape_id), RSHAPE(dest_shape_id));
1120 next_shape_id = shape_id(next_shape, initial_shape_id & ~SHAPE_ID_FL_NON_CANONICAL_MASK);
1123 return rb_shape_transition_complex(initial_shape_id | (dest_shape_id & ~SHAPE_ID_FL_NON_CANONICAL_MASK));
1128 next_shape_id = RSHAPE_OFFSET(dest_shape_id) | RSHAPE_FLAGS(initial_shape_id);
1130 return next_shape_id;
1134rb_shape_copy_fields(
VALUE dest,
VALUE *dest_buf, shape_id_t dest_shape_id,
VALUE *src_buf, shape_id_t src_shape_id)
1136 rb_shape_t *dest_shape = RSHAPE(dest_shape_id);
1137 rb_shape_t *src_shape = RSHAPE(src_shape_id);
1139 if (src_shape->next_field_index == dest_shape->next_field_index) {
1141 MEMCPY(dest_buf, src_buf,
VALUE, dest_shape->next_field_index);
1144 for (uint32_t i = 0; i < dest_shape->next_field_index; i++) {
1149 while (src_shape->parent_id != INVALID_SHAPE_ID) {
1150 if (src_shape->type == SHAPE_IVAR) {
1151 while (dest_shape->edge_name != src_shape->edge_name) {
1152 if (UNLIKELY(dest_shape->parent_id == INVALID_SHAPE_ID)) {
1153 rb_bug(
"Lost field %s", rb_id2name(src_shape->edge_name));
1155 dest_shape = RSHAPE(dest_shape->parent_id);
1158 RB_OBJ_WRITE(dest, &dest_buf[dest_shape->next_field_index - 1], src_buf[src_shape->next_field_index - 1]);
1160 src_shape = RSHAPE(src_shape->parent_id);
1166rb_shape_copy_complex_ivars(
VALUE dest,
VALUE obj, shape_id_t src_shape_id,
st_table *fields_table)
1169 st_table *table = st_copy(fields_table);
1170 if (rb_shape_has_object_id(src_shape_id)) {
1171 st_data_t
id = (st_data_t)id_object_id;
1172 st_delete(table, &
id, NULL);
1174 rb_obj_init_too_complex(dest, table);
1175 rb_gc_writebarrier_remember(dest);
1179rb_shape_edges_count(shape_id_t shape_id)
1183 if (SINGLE_CHILD_P(shape->edges)) {
1187 return rb_managed_id_table_size(shape->edges);
1194rb_shape_memsize(shape_id_t shape_id)
1199 if (shape->edges && !SINGLE_CHILD_P(shape->edges)) {
1200 memsize += rb_managed_id_table_size(shape->edges);
1206rb_shape_foreach_field(shape_id_t initial_shape_id, rb_shape_foreach_transition_callback func,
void *data)
1208 RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id));
1210 rb_shape_t *shape = RSHAPE(initial_shape_id);
1211 if (shape->type == SHAPE_ROOT) {
1215 shape_id_t parent_id = shape_id(RSHAPE(shape->parent_id), initial_shape_id);
1216 if (rb_shape_foreach_field(parent_id, func, data)) {
1217 switch (func(shape_id(shape, initial_shape_id), data)) {
1224 rb_bug(
"unreachable");
1232rb_shape_verify_consistency(
VALUE obj, shape_id_t shape_id)
1234 if (shape_id == ROOT_SHAPE_ID) {
1238 if (shape_id == INVALID_SHAPE_ID) {
1239 rb_bug(
"Can't set INVALID_SHAPE_ID on an object");
1244 bool has_object_id =
false;
1245 while (shape->parent_id != INVALID_SHAPE_ID) {
1246 if (shape->type == SHAPE_OBJ_ID) {
1247 has_object_id =
true;
1250 shape = RSHAPE(shape->parent_id);
1253 if (rb_shape_has_object_id(shape_id)) {
1254 if (!has_object_id) {
1256 rb_bug(
"shape_id claim having obj_id but doesn't shape_id=%u, obj=%s", shape_id, rb_obj_info(obj));
1260 if (has_object_id) {
1262 rb_bug(
"shape_id claim not having obj_id but it does shape_id=%u, obj=%s", shape_id, rb_obj_info(obj));
1267 if (rb_shape_too_complex_p(shape_id)) {
1276 attr_index_t ivar_count = RSHAPE_LEN(shape_id);
1277 if (has_object_id) {
1284 RUBY_ASSERT(!(shape_id & SHAPE_ID_HAS_IVAR_MASK));
1288 uint8_t flags_heap_index = rb_shape_heap_index(shape_id);
1291 size_t shape_id_slot_size = rb_shape_tree.capacities[flags_heap_index - 1] *
sizeof(
VALUE) +
sizeof(
struct RBasic);
1292 size_t actual_slot_size = rb_gc_obj_slot_size(obj);
1294 if (shape_id_slot_size != actual_slot_size) {
1295 rb_bug(
"shape_id heap_index flags mismatch: shape_id_slot_size=%zu, gc_slot_size=%zu\n", shape_id_slot_size, actual_slot_size);
1299 if (flags_heap_index) {
1300 rb_bug(
"shape_id indicate heap_index > 0 but object is not T_OBJECT: %s", rb_obj_info(obj));
1315shape_too_complex(
VALUE self)
1318 return RBOOL(rb_shape_too_complex_p(shape_id));
1322shape_frozen(
VALUE self)
1325 return RBOOL(shape_id & SHAPE_ID_FL_FROZEN);
1329shape_has_object_id_p(
VALUE self)
1332 return RBOOL(rb_shape_has_object_id(shape_id));
1338 if (is_instance_id(key)) {
1347shape_id_t_to_rb_cShape(shape_id_t shape_id)
1354 INT2NUM(RSHAPE_OFFSET(shape_id)),
1356 rb_shape_edge_name(shape),
1357 INT2NUM(shape->next_field_index),
1358 INT2NUM(rb_shape_heap_index(shape_id)),
1360 INT2NUM(RSHAPE_CAPACITY(shape_id)));
1365static enum rb_id_table_iterator_result
1366rb_edges_to_hash(
ID key,
VALUE value,
void *ref)
1368 rb_hash_aset(*(
VALUE *)ref, parse_key(key), shape_id_t_to_rb_cShape(shape_offset((
rb_shape_t *)value)));
1369 return ID_TABLE_CONTINUE;
1373rb_shape_edges(
VALUE self)
1377 VALUE hash = rb_hash_new();
1380 if (SINGLE_CHILD_P(shape->edges)) {
1381 rb_shape_t *child = SINGLE_CHILD(shape->edges);
1382 rb_edges_to_hash(child->edge_name, (
VALUE)child, &hash);
1385 VALUE edges = shape->edges;
1386 rb_managed_id_table_foreach(edges, rb_edges_to_hash, &hash);
1397 if (shape->edge_name) {
1398 if (is_instance_id(shape->edge_name)) {
1399 return ID2SYM(shape->edge_name);
1401 return INT2NUM(shape->capacity);
1407rb_shape_export_depth(
VALUE self)
1410 return SIZET2NUM(rb_shape_depth(shape_id));
1414rb_shape_parent(
VALUE self)
1418 if (shape->parent_id != INVALID_SHAPE_ID) {
1419 return shape_id_t_to_rb_cShape(shape->parent_id);
1430 rb_raise(rb_eArgError,
"Can't get shape of special constant");
1432 return shape_id_t_to_rb_cShape(rb_obj_shape_id(obj));
1436rb_shape_root_shape(
VALUE self)
1438 return shape_id_t_to_rb_cShape(ROOT_SHAPE_ID);
1442rb_shape_shapes_available(
VALUE self)
1444 return ULL2NUM(MAX_SHAPE_ID - (rb_shapes_count() - 1));
1448rb_shape_exhaust(
int argc,
VALUE *argv,
VALUE self)
1451 int offset = argc == 1 ?
NUM2INT(argv[0]) : 0;
1457rb_shape_class_max_iv_count(
VALUE self,
VALUE klass)
1459 return INT2NUM(RCLASS_MAX_IV_COUNT(klass));
1464static enum rb_id_table_iterator_result collect_keys_and_values(
ID key,
VALUE value,
void *ref)
1466 rb_hash_aset(*(
VALUE *)ref, parse_key(key), shape_to_h((
rb_shape_t *)value));
1467 return ID_TABLE_CONTINUE;
1472 VALUE hash = rb_hash_new();
1474 if (SINGLE_CHILD_P(edges)) {
1476 collect_keys_and_values(child->edge_name, (
VALUE)child, &hash);
1479 rb_managed_id_table_foreach(edges, collect_keys_and_values, &hash);
1491 rb_hash_aset(
rb_shape,
ID2SYM(rb_intern(
"edges")), edges(shape->edges));
1493 if (shape == rb_shape_get_root_shape()) {
1500 rb_hash_aset(
rb_shape,
ID2SYM(rb_intern(
"edge_name")), rb_id2str(shape->edge_name));
1505shape_transition_tree(
VALUE self)
1507 return shape_to_h(rb_shape_get_root_shape());
1513 shape_id_t shape_id =
NUM2UINT(
id);
1514 if (shape_id >= rb_shapes_count()) {
1515 rb_raise(rb_eArgError,
"Shape ID %d is out of bounds\n", shape_id);
1517 return shape_id_t_to_rb_cShape(shape_id);
1522#include <sys/mman.h>
1526Init_default_shapes(
void)
1528 size_t *heap_sizes = rb_gc_heap_sizes();
1529 size_t heaps_count = 0;
1530 while (heap_sizes[heaps_count]) {
1534 if (heaps_count > SHAPE_ID_HEAP_INDEX_MAX) {
1535 rb_bug(
"Init_default_shapes initialized with %lu heaps, only up to %u are supported", heaps_count, SHAPE_ID_HEAP_INDEX_MAX);
1539 for (index = 0; index < heaps_count; index++) {
1540 if (heap_sizes[index] >
sizeof(
struct RBasic)) {
1541 rb_shape_tree.capacities[index] = (heap_sizes[index] -
sizeof(
struct RBasic)) / sizeof(
VALUE);
1544 rb_shape_tree.capacities[index] = 0;
1547 rb_shape_tree.heaps_count = heaps_count;
1551 rb_shape_tree.shape_list = (
rb_shape_t *)mmap(NULL, shape_list_mmap_size,
1552 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1553 if (rb_shape_tree.shape_list == MAP_FAILED) {
1554 rb_shape_tree.shape_list = 0;
1557 ruby_annotate_mmap(rb_shape_tree.shape_list, shape_list_mmap_size,
"Ruby:Init_default_shapes:shape_list");
1563 if (!rb_shape_tree.shape_list) {
1567 id_object_id = rb_make_internal_id();
1570 size_t shape_cache_mmap_size = rb_size_mul_or_raise(REDBLACK_CACHE_SIZE,
sizeof(redblack_node_t),
rb_eRuntimeError);
1571 redblack_cache = (redblack_node_t *)mmap(NULL, shape_cache_mmap_size,
1572 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1573 redblack_cache_size = 0;
1578 if (redblack_cache == MAP_FAILED) {
1579 redblack_cache = NULL;
1580 redblack_cache_size = REDBLACK_CACHE_SIZE;
1583 ruby_annotate_mmap(redblack_cache, shape_cache_mmap_size,
"Ruby:Init_default_shapes:shape_cache");
1587 rb_gc_register_address(&shape_tree_obj);
1591 rb_shape_t *root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID);
1593 root->type = SHAPE_ROOT;
1595 RUBY_ASSERT(!(shape_offset(root) & SHAPE_ID_HAS_IVAR_MASK));
1598 rb_shape_t *root_with_obj_id = get_next_shape_internal(root, id_object_id, SHAPE_OBJ_ID, &dontcare,
true);
1600 RUBY_ASSERT(shape_offset(root_with_obj_id) == ROOT_SHAPE_WITH_OBJ_ID);
1601 RUBY_ASSERT(root_with_obj_id->type == SHAPE_OBJ_ID);
1602 RUBY_ASSERT(root_with_obj_id->edge_name == id_object_id);
1603 RUBY_ASSERT(root_with_obj_id->next_field_index == 1);
1604 RUBY_ASSERT(!(shape_offset(root_with_obj_id) & SHAPE_ID_HAS_IVAR_MASK));
1605 (void)root_with_obj_id;
1632 rb_define_const(rb_cShape,
"SHAPE_ROOT",
INT2NUM(SHAPE_ROOT));
1633 rb_define_const(rb_cShape,
"SHAPE_IVAR",
INT2NUM(SHAPE_IVAR));
1634 rb_define_const(rb_cShape,
"SHAPE_ID_NUM_BITS",
INT2NUM(SHAPE_ID_NUM_BITS));
1635 rb_define_const(rb_cShape,
"SHAPE_FLAG_SHIFT",
INT2NUM(SHAPE_FLAG_SHIFT));
1636 rb_define_const(rb_cShape,
"SHAPE_MAX_VARIATIONS",
INT2NUM(SHAPE_MAX_VARIATIONS));
1637 rb_define_const(rb_cShape,
"SHAPE_MAX_EMBEDDED_CAPACITY",
INT2NUM(rb_shape_tree.capacities[rb_shape_tree.heaps_count - 1]));
1639 rb_define_const(rb_cShape,
"SIZEOF_REDBLACK_NODE_T",
INT2NUM(
sizeof(redblack_node_t)));
1640 rb_define_const(rb_cShape,
"SHAPE_BUFFER_SIZE",
INT2NUM(
sizeof(
rb_shape_t) * SHAPE_BUFFER_SIZE));
1641 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 Qundef
Old name of RUBY_Qundef.
#define ID2SYM
Old name of RB_ID2SYM.
#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_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).
int capa
Designed capacity of the buffer.
#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 RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
void rb_p(VALUE obj)
Inspects an 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.