2929#include "internal/object.h"
3030#include "internal/gc.h"
3131#include "internal/re.h"
32+ #include "internal/struct.h"
3233#include "internal/symbol.h"
3334#include "internal/thread.h"
3435#include "internal/variable.h"
@@ -1189,6 +1190,7 @@ static inline struct st_table *
11891190generic_fields_tbl (VALUE obj , ID id , bool force_check_ractor )
11901191{
11911192 ASSERT_vm_locking ();
1193+ RUBY_ASSERT (!RB_TYPE_P (obj , T_STRUCT ) || FL_TEST_RAW (obj , RSTRUCT_FL_GENIVAR ));
11921194
11931195 if ((force_check_ractor || LIKELY (rb_is_instance_id (id )) /* not internal ID */ ) &&
11941196 !RB_OBJ_FROZEN_RAW (obj ) &&
@@ -1421,6 +1423,19 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
14211423 ivar_list = ROBJECT_FIELDS (obj );
14221424 break ;
14231425 }
1426+ case T_STRUCT :
1427+ {
1428+ if (!FL_TEST_RAW (obj , RSTRUCT_FL_GENIVAR )) {
1429+ VALUE fields_obj = RSTRUCT_FIELDS_OBJ (obj );
1430+ if (fields_obj ) {
1431+ return rb_ivar_lookup (fields_obj , id , undef );
1432+ }
1433+ else {
1434+ return undef ;
1435+ }
1436+ }
1437+ // fallthrough gen fields
1438+ }
14241439 default :
14251440 shape_id = RBASIC_SHAPE_ID (obj );
14261441 if (rb_obj_exivar_p (obj )) {
@@ -2080,6 +2095,43 @@ void rb_obj_freeze_inline(VALUE x)
20802095 }
20812096}
20822097
2098+ static void
2099+ struct_field_set (VALUE obj , shape_id_t target_shape_id , ID field_name , VALUE val )
2100+ {
2101+ if (FL_TEST_RAW (obj , RSTRUCT_FL_GENIVAR )) {
2102+ generic_field_set (obj , target_shape_id , field_name , val );
2103+ return ;
2104+ }
2105+
2106+ const VALUE original_fields_obj = RSTRUCT_FIELDS_OBJ (obj );
2107+ VALUE fields_obj = imemo_fields_set (rb_obj_class (obj ), original_fields_obj , target_shape_id , field_name , val , false);
2108+ if (fields_obj != original_fields_obj ) {
2109+ RSTRUCT_SET_FIELDS_OBJ (obj , fields_obj );
2110+ if (original_fields_obj ) {
2111+ // Clear root shape to avoid triggering cleanup such as free_object_id.
2112+ rb_imemo_fields_clear (original_fields_obj );
2113+ }
2114+ }
2115+
2116+ if (RBASIC_SHAPE_ID (fields_obj ) == target_shape_id ) {
2117+ RBASIC_SET_SHAPE_ID (obj , target_shape_id );
2118+ }
2119+
2120+ RUBY_ASSERT (RBASIC_SHAPE_ID (obj ) == RBASIC_SHAPE_ID (fields_obj ));
2121+ }
2122+
2123+ static void
2124+ struct_ivar_set (VALUE obj , ID id , VALUE val )
2125+ {
2126+ if (FL_TEST_RAW (obj , RSTRUCT_FL_GENIVAR )) {
2127+ generic_ivar_set (obj , id , val );
2128+ return ;
2129+ }
2130+ bool dontcare ;
2131+ shape_id_t target_shape_id = generic_shape_ivar (obj , id , & dontcare );
2132+ struct_field_set (obj , target_shape_id , id , val );
2133+ }
2134+
20832135static void
20842136ivar_set (VALUE obj , ID id , VALUE val )
20852137{
@@ -2096,6 +2148,9 @@ ivar_set(VALUE obj, ID id, VALUE val)
20962148 IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR (id );
20972149 rb_class_ivar_set (obj , id , val );
20982150
2151+ break ;
2152+ case T_STRUCT :
2153+ struct_ivar_set (obj , id , val );
20992154 break ;
21002155 default :
21012156 generic_ivar_set (obj , id , val );
@@ -2132,6 +2187,9 @@ rb_obj_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE val
21322187 // The only field is object_id and T_CLASS handle it differently.
21332188 rb_bug ("Unreachable" );
21342189 break ;
2190+ case T_STRUCT :
2191+ struct_field_set (obj , target_shape_id , field_name , val );
2192+ break ;
21352193 default :
21362194 generic_field_set (obj , target_shape_id , field_name , val );
21372195 break ;
@@ -2365,6 +2423,65 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
23652423 rb_free_generic_ivar (dest );
23662424}
23672425
2426+ void
2427+ rb_copy_struct_ivar (VALUE dest , VALUE obj )
2428+ {
2429+ if (FL_TEST_RAW (obj , RSTRUCT_FL_GENIVAR )) {
2430+ rb_copy_generic_ivar (dest , obj );
2431+ return ;
2432+ }
2433+
2434+ VALUE fields_obj = RSTRUCT_FIELDS_OBJ (obj );
2435+ VALUE new_fields_obj ;
2436+
2437+ rb_check_frozen (dest );
2438+
2439+ shape_id_t src_shape_id = rb_obj_shape_id (obj );
2440+
2441+ if (fields_obj ) {
2442+ unsigned long src_num_ivs = rb_ivar_count (fields_obj );
2443+ if (!src_num_ivs ) {
2444+ return ;
2445+ }
2446+
2447+ if (rb_shape_too_complex_p (src_shape_id )) {
2448+ rb_shape_copy_complex_ivars (dest , obj , src_shape_id , rb_imemo_fields_complex_tbl (fields_obj ));
2449+ return ;
2450+ }
2451+
2452+ shape_id_t dest_shape_id = src_shape_id ;
2453+ shape_id_t initial_shape_id = rb_obj_shape_id (dest );
2454+
2455+ if (!rb_shape_canonical_p (src_shape_id )) {
2456+ RUBY_ASSERT (RSHAPE_TYPE_P (initial_shape_id , SHAPE_ROOT ));
2457+
2458+ dest_shape_id = rb_shape_rebuild (initial_shape_id , src_shape_id );
2459+ if (UNLIKELY (rb_shape_too_complex_p (dest_shape_id ))) {
2460+ st_table * table = rb_st_init_numtable_with_size (src_num_ivs );
2461+ rb_obj_copy_ivs_to_hash_table (obj , table );
2462+ rb_obj_init_too_complex (dest , table );
2463+ return ;
2464+ }
2465+ }
2466+
2467+ if (!RSHAPE_LEN (dest_shape_id )) {
2468+ rb_obj_set_shape_id (dest , dest_shape_id );
2469+ return ;
2470+ }
2471+
2472+ new_fields_obj = rb_imemo_fields_new (rb_obj_class (dest ), RSHAPE_CAPACITY (dest_shape_id ));
2473+ VALUE * src_buf = rb_imemo_fields_ptr (fields_obj );
2474+ VALUE * dest_buf = rb_imemo_fields_ptr (new_fields_obj );
2475+ rb_shape_copy_fields (new_fields_obj , dest_buf , dest_shape_id , src_buf , src_shape_id );
2476+ RBASIC_SET_SHAPE_ID (new_fields_obj , dest_shape_id );
2477+
2478+ RSTRUCT_SET_FIELDS_OBJ (dest , new_fields_obj );
2479+
2480+ RBASIC_SET_SHAPE_ID (dest , dest_shape_id );
2481+ }
2482+ return ;
2483+ }
2484+
23682485void
23692486rb_replace_generic_ivar (VALUE clone , VALUE obj )
23702487{
@@ -2403,6 +2520,15 @@ rb_field_foreach(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
24032520 }
24042521 }
24052522 break ;
2523+ case T_STRUCT :
2524+ if (!FL_TEST_RAW (obj , RSTRUCT_FL_GENIVAR )) {
2525+ VALUE fields_obj = RSTRUCT_FIELDS_OBJ (obj );
2526+ if (fields_obj ) {
2527+ imemo_fields_each (fields_obj , func , arg , ivar_only );
2528+ }
2529+ break ;
2530+ }
2531+ // fallthrough gen fields
24062532 default :
24072533 if (rb_obj_exivar_p (obj )) {
24082534 VALUE fields_obj = 0 ;
0 commit comments