@@ -2276,35 +2276,83 @@ class_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg,
22762276}
22772277
22782278void
2279- rb_copy_generic_ivar (VALUE clone , VALUE obj )
2279+ rb_copy_generic_ivar (VALUE dest , VALUE obj )
22802280{
22812281 struct gen_ivtbl * obj_ivtbl ;
22822282 struct gen_ivtbl * new_ivtbl ;
22832283
2284- rb_check_frozen (clone );
2284+ rb_check_frozen (dest );
22852285
22862286 if (!FL_TEST (obj , FL_EXIVAR )) {
22872287 goto clear ;
22882288 }
22892289
2290+ unsigned long src_num_ivs = rb_ivar_count (obj );
2291+ if (!src_num_ivs ) {
2292+ goto clear ;
2293+ }
2294+
2295+ rb_shape_t * src_shape = rb_shape_get_shape (obj );
2296+
22902297 if (rb_gen_ivtbl_get (obj , 0 , & obj_ivtbl )) {
22912298 if (gen_ivtbl_count (obj , obj_ivtbl ) == 0 )
22922299 goto clear ;
22932300
2294- FL_SET (clone , FL_EXIVAR );
2301+ FL_SET (dest , FL_EXIVAR );
22952302
2296- if (rb_shape_obj_too_complex (obj )) {
2297- new_ivtbl = xmalloc (sizeof (struct gen_ivtbl ));
2298- #if !SHAPE_IN_BASIC_FLAGS
2299- new_ivtbl -> shape_id = SHAPE_OBJ_TOO_COMPLEX ;
2300- #endif
2301- new_ivtbl -> as .complex .table = st_copy (obj_ivtbl -> as .complex .table );
2303+ if (rb_shape_too_complex_p (src_shape )) {
2304+ // obj is TOO_COMPLEX so we can copy its iv_hash
2305+ st_table * table = st_copy (ROBJECT_IV_HASH (obj ));
2306+ if (rb_shape_has_object_id (src_shape )) {
2307+ st_data_t id = (st_data_t )internal_object_id ;
2308+ st_delete (table , & id , NULL );
2309+ }
2310+ rb_obj_init_too_complex (dest , table );
2311+
2312+ return ;
2313+ }
2314+
2315+ rb_shape_t * shape_to_set_on_dest = src_shape ;
2316+ rb_shape_t * initial_shape = rb_shape_get_shape (dest );
2317+
2318+ if (initial_shape -> heap_index != src_shape -> heap_index || !rb_shape_canonical_p (src_shape )) {
2319+ RUBY_ASSERT (initial_shape -> type == SHAPE_ROOT );
2320+
2321+ shape_to_set_on_dest = rb_shape_rebuild_shape (initial_shape , src_shape );
2322+ if (UNLIKELY (rb_shape_too_complex_p (shape_to_set_on_dest ))) {
2323+ st_table * table = rb_st_init_numtable_with_size (src_num_ivs );
2324+ rb_obj_copy_ivs_to_hash_table (obj , table );
2325+ rb_obj_init_too_complex (dest , table );
2326+
2327+ return ;
2328+ }
2329+ }
2330+
2331+ new_ivtbl = gen_ivtbl_resize (0 , shape_to_set_on_dest -> next_field_index );
2332+
2333+ VALUE * src_buf = obj_ivtbl -> as .shape .ivptr ;
2334+ VALUE * dest_buf = new_ivtbl -> as .shape .ivptr ;
2335+
2336+ if (src_shape -> next_field_index == shape_to_set_on_dest -> next_field_index ) {
2337+ // Happy path, we can just memcpy the ivptr content
2338+ MEMCPY (dest_buf , src_buf , VALUE , src_num_ivs );
2339+
2340+ // Fire write barriers
2341+ for (uint32_t i = 0 ; i < src_num_ivs ; i ++ ) {
2342+ RB_OBJ_WRITTEN (dest , Qundef , dest_buf [i ]);
2343+ }
23022344 }
23032345 else {
2304- new_ivtbl = gen_ivtbl_resize (0 , obj_ivtbl -> as .shape .numiv );
2346+ rb_shape_t * dest_shape = shape_to_set_on_dest ;
2347+ while (src_shape -> parent_id != INVALID_SHAPE_ID ) {
2348+ if (src_shape -> type == SHAPE_IVAR ) {
2349+ while (dest_shape -> edge_name != src_shape -> edge_name ) {
2350+ dest_shape = rb_shape_get_shape_by_id (dest_shape -> parent_id );
2351+ }
23052352
2306- for (uint32_t i = 0 ; i < obj_ivtbl -> as .shape .numiv ; i ++ ) {
2307- RB_OBJ_WRITE (clone , & new_ivtbl -> as .shape .ivptr [i ], obj_ivtbl -> as .shape .ivptr [i ]);
2353+ RB_OBJ_WRITE (dest , & dest_buf [dest_shape -> next_field_index - 1 ], src_buf [src_shape -> next_field_index - 1 ]);
2354+ }
2355+ src_shape = rb_shape_get_shape_by_id (src_shape -> parent_id );
23082356 }
23092357 }
23102358
@@ -2314,25 +2362,18 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
23142362 */
23152363 RB_VM_LOCK_ENTER ();
23162364 {
2317- generic_ivtbl_no_ractor_check (clone );
2318- st_insert (generic_ivtbl_no_ractor_check (obj ), (st_data_t )clone , (st_data_t )new_ivtbl );
2365+ generic_ivtbl_no_ractor_check (dest );
2366+ st_insert (generic_ivtbl_no_ractor_check (obj ), (st_data_t )dest , (st_data_t )new_ivtbl );
23192367 }
23202368 RB_VM_LOCK_LEAVE ();
2321-
2322- rb_shape_t * obj_shape = rb_shape_get_shape (obj );
2323- if (rb_shape_frozen_shape_p (obj_shape )) {
2324- rb_shape_set_shape_id (clone , obj_shape -> parent_id );
2325- }
2326- else {
2327- rb_shape_set_shape (clone , obj_shape );
2328- }
2369+ rb_shape_set_shape (dest , shape_to_set_on_dest );
23292370 }
23302371 return ;
23312372
23322373 clear :
2333- if (FL_TEST (clone , FL_EXIVAR )) {
2334- rb_free_generic_ivar (clone );
2335- FL_UNSET (clone , FL_EXIVAR );
2374+ if (FL_TEST (dest , FL_EXIVAR )) {
2375+ rb_free_generic_ivar (dest );
2376+ FL_UNSET (dest , FL_EXIVAR );
23362377 }
23372378}
23382379
0 commit comments