Skip to content

Commit 2f40667

Browse files
committed
wip
1 parent 4977828 commit 2f40667

10 files changed

Lines changed: 221 additions & 16 deletions

File tree

hash.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "symbol.h"
4848
#include "ruby/thread_native.h"
4949
#include "ruby/ractor.h"
50+
#include "variable.h"
5051
#include "vm_sync.h"
5152
#include "builtin.h"
5253

internal/struct.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
*/
1111
#include "ruby/internal/stdbool.h" /* for bool */
1212
#include "ruby/ruby.h" /* for struct RBasic */
13+
#include "internal/imemo.h"
1314

1415
enum {
16+
RSTRUCT_FL_GENIVAR = RUBY_FL_USER8,
1517
RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 |
1618
RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1,
1719
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
@@ -23,6 +25,7 @@ struct RStruct {
2325
struct {
2426
long len;
2527
const VALUE *ptr;
28+
const VALUE fields_obj;
2629
} heap;
2730
/* This is a length 1 array because:
2831
* 1. GCC has a bug that does not optimize C flexible array members
@@ -116,4 +119,34 @@ RSTRUCT_GET(VALUE st, long k)
116119
return RSTRUCT_CONST_PTR(st)[k];
117120
}
118121

122+
static inline VALUE
123+
RSTRUCT_FIELDS_OBJ(VALUE st)
124+
{
125+
const long embed_len = RSTRUCT_EMBED_LEN(st);
126+
VALUE fields_obj;
127+
if (embed_len) {
128+
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_FL_GENIVAR));
129+
fields_obj = RSTRUCT_GET(st, embed_len);
130+
}
131+
else {
132+
fields_obj = RSTRUCT(st)->as.heap.fields_obj;
133+
}
134+
RUBY_ASSERT(!fields_obj || IMEMO_TYPE_P(fields_obj, imemo_fields));
135+
return fields_obj;
136+
}
137+
138+
static inline void
139+
RSTRUCT_SET_FIELDS_OBJ(VALUE st, VALUE fields_obj)
140+
{
141+
RUBY_ASSERT(!fields_obj || IMEMO_TYPE_P(fields_obj, imemo_fields));
142+
const long embed_len = RSTRUCT_EMBED_LEN(st);
143+
if (embed_len) {
144+
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_FL_GENIVAR));
145+
RSTRUCT_SET(st, embed_len, fields_obj);
146+
}
147+
else {
148+
RB_OBJ_WRITE(st, &RSTRUCT(st)->as.heap.fields_obj, fields_obj);
149+
}
150+
}
151+
119152
#endif /* INTERNAL_STRUCT_H */

object.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ init_copy(VALUE dest, VALUE obj)
385385
case T_OBJECT:
386386
rb_obj_copy_ivar(dest, obj);
387387
break;
388+
case T_STRUCT:
389+
rb_copy_struct_ivar(dest, obj);
390+
break;
388391
default:
389392
rb_copy_generic_ivar(dest, obj);
390393
break;

shape.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -413,22 +413,6 @@ rb_shape_obj_has_fields(VALUE obj)
413413
return rb_shape_has_fields(RBASIC_SHAPE_ID(obj));
414414
}
415415

416-
static inline bool
417-
rb_obj_exivar_p(VALUE obj)
418-
{
419-
switch (TYPE(obj)) {
420-
case T_NONE:
421-
case T_OBJECT:
422-
case T_CLASS:
423-
case T_MODULE:
424-
case T_IMEMO:
425-
return false;
426-
default:
427-
break;
428-
}
429-
return rb_shape_obj_has_fields(obj);
430-
}
431-
432416
// For ext/objspace
433417
RUBY_SYMBOL_EXPORT_BEGIN
434418
typedef void each_shape_callback(shape_id_t shape_id, void *data);

string.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "ruby/util.h"
4848
#include "ruby_assert.h"
4949
#include "shape.h"
50+
#include "variable.h"
5051
#include "vm_sync.h"
5152
#include "ruby/internal/attr/nonstring.h"
5253

struct.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,12 +811,18 @@ struct_alloc(VALUE klass)
811811
{
812812
long n = num_members(klass);
813813
size_t embedded_size = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * n);
814+
if (RCLASS_MAX_IV_COUNT(klass) > 0) {
815+
embedded_size += sizeof(VALUE);
816+
}
814817
VALUE flags = T_STRUCT | (RGENGC_WB_PROTECTED_STRUCT ? FL_WB_PROTECTED : 0);
815818

816819
if (n > 0 && rb_gc_size_allocatable_p(embedded_size)) {
817820
flags |= n << RSTRUCT_EMBED_LEN_SHIFT;
818821

819822
NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0);
823+
if (RCLASS_MAX_IV_COUNT(klass) == 0 && embedded_size == rb_gc_obj_slot_size((VALUE)st)) {
824+
FL_SET_RAW((VALUE)st, RSTRUCT_FL_GENIVAR);
825+
}
820826

821827
rb_mem_clear((VALUE *)st->as.ary, n);
822828

test/ruby/test_object_id.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,19 @@ def initialize
252252
end;
253253
end
254254
end
255+
256+
class TestObjectIdStruct < TestObjectId
257+
EmbeddedStruct = Struct.new(:embedded_field)
258+
259+
def setup
260+
@obj = EmbeddedStruct.new
261+
end
262+
end
263+
264+
class TestObjectIdStructGenIvar < TestObjectId
265+
GenIvarStruct = Struct.new(:a, :b, :c)
266+
267+
def setup
268+
@obj = GenIvarStruct.new
269+
end
270+
end

variable.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
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 *
11891190
generic_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+
20832135
static void
20842136
ivar_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+
23682485
void
23692486
rb_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;

variable.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,32 @@
1111
/* per-object */
1212

1313
#include "shape.h"
14+
#include "internal/struct.h"
15+
16+
static inline bool
17+
rb_obj_exivar_p(VALUE obj)
18+
{
19+
switch (TYPE(obj)) {
20+
case T_NONE:
21+
case T_OBJECT:
22+
case T_CLASS:
23+
case T_MODULE:
24+
case T_IMEMO:
25+
return false;
26+
case T_STRUCT:
27+
if (!FL_TEST_RAW(obj, RSTRUCT_FL_GENIVAR)) {
28+
return false;
29+
}
30+
break;
31+
default:
32+
break;
33+
}
34+
return rb_shape_obj_has_fields(obj);
35+
}
1436

1537
int rb_ivar_generic_fields_tbl_lookup(VALUE obj, VALUE *);
1638
void rb_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table);
39+
void rb_copy_struct_ivar(VALUE dest, VALUE obj);
1740

1841
void rb_free_rb_global_tbl(void);
1942
void rb_free_generic_fields_tbl_(void);

vm_insnhelper.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,18 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
12651265

12661266
break;
12671267
}
1268+
case T_STRUCT:
1269+
{
1270+
if (!FL_TEST_RAW(obj, RSTRUCT_FL_GENIVAR)) {
1271+
fields_obj = RSTRUCT_FIELDS_OBJ(obj);
1272+
if (!fields_obj) {
1273+
return default_value;
1274+
}
1275+
ivar_list = rb_imemo_fields_ptr(fields_obj);
1276+
goto general_path;
1277+
}
1278+
// fallthrough gen fields
1279+
}
12681280
default:
12691281
if (rb_obj_exivar_p(obj)) {
12701282
VALUE fields_obj = 0;

0 commit comments

Comments
 (0)