diff --git a/array.c b/array.c index 8c7fa583e9..b2ebf3c0e9 100644 --- a/array.c +++ b/array.c @@ -1027,7 +1027,7 @@ rb_ary_memsize(VALUE ary) static VALUE ary_make_shared(VALUE ary) { - assert(!ARY_EMBED_P(ary)); + assert(USE_RVARGC || !ARY_EMBED_P(ary)); ary_verify(ary); if (ARY_SHARED_P(ary)) { @@ -1037,21 +1037,38 @@ ary_make_shared(VALUE ary) return ary; } else if (OBJ_FROZEN(ary)) { - rb_ary_transient_heap_evacuate(ary, TRUE); - ary_shrink_capa(ary); + if (!ARY_EMBED_P(ary)) { + rb_ary_transient_heap_evacuate(ary, TRUE); + ary_shrink_capa(ary); + } return ary; } else { - long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary); - const VALUE *ptr; + rb_ary_transient_heap_evacuate(ary, TRUE); + + long capa = ARY_CAPA(ary); + long len = RARRAY_LEN(ary); + + /* Shared roots cannot be embedded because the reference count + * (refcnt) is stored in as.heap.aux.capa. */ VALUE shared = ary_alloc_heap(0); - rb_ary_transient_heap_evacuate(ary, TRUE); - ptr = ARY_HEAP_PTR(ary); + if (ARY_EMBED_P(ary)) { + /* Cannot use ary_heap_alloc because we don't want to allocate + * on the transient heap. */ + VALUE *ptr = ALLOC_N(VALUE, capa); + ARY_SET_PTR(shared, ptr); + ary_memcpy(shared, 0, len, RARRAY_PTR(ary)); + + FL_UNSET_EMBED(ary); + ARY_SET_HEAP_LEN(ary, len); + ARY_SET_PTR(ary, ptr); + } + else { + ARY_SET_PTR(shared, RARRAY_PTR(ary)); + } - FL_UNSET_EMBED(shared); ARY_SET_LEN(shared, capa); - ARY_SET_PTR(shared, ptr); ary_mem_clear(shared, len, capa - len); FL_SET_SHARED_ROOT(shared); ARY_SET_SHARED_ROOT_REFCNT(shared, 1); @@ -1318,7 +1335,9 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len) assert(len >= 0); assert(offset+len <= RARRAY_LEN(ary)); - if (ary_embeddable_p(len)) { + const size_t rarray_embed_capa_max = (sizeof(struct RArray) - offsetof(struct RArray, as.ary)) / sizeof(VALUE); + + if ((size_t)len <= rarray_embed_capa_max && ary_embeddable_p(len)) { VALUE result = ary_alloc_embed(klass, len); ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset); ARY_SET_EMBED_LEN(result, len); diff --git a/gc.c b/gc.c index 84f9aa30f6..cd48c47a94 100644 --- a/gc.c +++ b/gc.c @@ -9958,7 +9958,21 @@ static void gc_ref_update_array(rb_objspace_t * objspace, VALUE v) { if (ARY_SHARED_P(v)) { +#if USE_RVARGC + VALUE old_root = RARRAY(v)->as.heap.aux.shared_root; +#endif + UPDATE_IF_MOVED(objspace, RARRAY(v)->as.heap.aux.shared_root); + +#if USE_RVARGC + VALUE new_root = RARRAY(v)->as.heap.aux.shared_root; + // If the root is embedded and its location has changed + if (ARY_EMBED_P(new_root) && new_root != old_root) { + size_t offset = (size_t)(RARRAY(v)->as.heap.ptr - RARRAY(old_root)->as.ary); + GC_ASSERT(RARRAY(v)->as.heap.ptr >= RARRAY(old_root)->as.ary); + RARRAY(v)->as.heap.ptr = RARRAY(new_root)->as.ary + offset; + } +#endif } else { long len = RARRAY_LEN(v);