summaryrefslogtreecommitdiff
path: root/internal/class.h
blob: 8a6c95623317b35109dc51ce419358508d32fa16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#ifndef INTERNAL_CLASS_H                                 /*-*-C-*-vi:se ft=c:*/
#define INTERNAL_CLASS_H
/**
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 * @brief      Internal header for Class.
 */
#include "id.h"
#include "id_table.h"           /* for struct rb_id_table */
#include "internal/serial.h"    /* for rb_serial_t */
#include "internal/static_assert.h"
#include "internal/variable.h"  /* for rb_class_ivar_set */
#include "ruby/internal/stdbool.h"     /* for bool */
#include "ruby/intern.h"        /* for rb_alloc_func_t */
#include "ruby/ruby.h"          /* for struct RBasic */
#include "shape.h"
#include "ruby_assert.h"
#include "vm_core.h"
#include "vm_sync.h"
#include "method.h"             /* for rb_cref_t */

#ifdef RCLASS_SUPER
# undef RCLASS_SUPER
#endif

struct rb_subclass_entry {
    VALUE klass;
    struct rb_subclass_entry *next;
    struct rb_subclass_entry *prev;
};
typedef struct rb_subclass_entry rb_subclass_entry_t;

struct rb_cvar_class_tbl_entry {
    uint32_t index;
    rb_serial_t global_cvar_state;
    const rb_cref_t * cref;
    VALUE class_value;
};

struct rb_classext_struct {
    VALUE *iv_ptr;
    struct rb_id_table *const_tbl;
    struct rb_id_table *callable_m_tbl;
    struct rb_id_table *cc_tbl; /* ID -> [[ci1, cc1], [ci2, cc2] ...] */
    struct rb_id_table *cvc_tbl;
    size_t superclass_depth;
    VALUE *superclasses;
    struct rb_subclass_entry *subclasses;
    struct rb_subclass_entry *subclass_entry;
    /**
     * In the case that this is an `ICLASS`, `module_subclasses` points to the link
     * in the module's `subclasses` list that indicates that the klass has been
     * included. Hopefully that makes sense.
     */
    struct rb_subclass_entry *module_subclass_entry;
    const VALUE origin_;
    const VALUE refined_class;
    union {
        struct {
            rb_alloc_func_t allocator;
        } class;
        struct {
            VALUE attached_object;
        } singleton_class;
    } as;
    const VALUE includer;
    attr_index_t max_iv_count;
    unsigned char variation_count;
    bool permanent_classpath : 1;
    bool cloned : 1;
    VALUE classpath;
};
typedef struct rb_classext_struct rb_classext_t;

STATIC_ASSERT(shape_max_variations, SHAPE_MAX_VARIATIONS < (1 << (sizeof(((rb_classext_t *)0)->variation_count) * CHAR_BIT)));

struct RClass {
    struct RBasic basic;
    VALUE super;
    struct rb_id_table *m_tbl;
};

// Assert that classes can be embedded in size_pools[2] (which has 160B slot size)
STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE);

struct RClass_and_rb_classext_t {
    struct RClass rclass;
    rb_classext_t classext;
};

#define RCLASS_EXT(c) (&((struct RClass_and_rb_classext_t*)(c))->classext)
#define RCLASS_CONST_TBL(c) (RCLASS_EXT(c)->const_tbl)
#define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
#define RCLASS_IVPTR(c) (RCLASS_EXT(c)->iv_ptr)
#define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT(c)->callable_m_tbl)
#define RCLASS_CC_TBL(c) (RCLASS_EXT(c)->cc_tbl)
#define RCLASS_CVC_TBL(c) (RCLASS_EXT(c)->cvc_tbl)
#define RCLASS_ORIGIN(c) (RCLASS_EXT(c)->origin_)
#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
#define RCLASS_INCLUDER(c) (RCLASS_EXT(c)->includer)
#define RCLASS_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->subclass_entry)
#define RCLASS_MODULE_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->module_subclass_entry)
#define RCLASS_SUBCLASSES(c) (RCLASS_EXT(c)->subclasses)
#define RCLASS_SUPERCLASS_DEPTH(c) (RCLASS_EXT(c)->superclass_depth)
#define RCLASS_SUPERCLASSES(c) (RCLASS_EXT(c)->superclasses)
#define RCLASS_ATTACHED_OBJECT(c) (RCLASS_EXT(c)->as.singleton_class.attached_object)

#define RCLASS_IS_ROOT FL_USER0
#define RICLASS_IS_ORIGIN FL_USER0
#define RCLASS_SUPERCLASSES_INCLUDE_SELF FL_USER2
#define RICLASS_ORIGIN_SHARED_MTBL FL_USER3

static inline st_table *
RCLASS_IV_HASH(VALUE obj)
{
    RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
    RUBY_ASSERT(rb_shape_obj_too_complex(obj));
    return (st_table *)RCLASS_IVPTR(obj);
}

static inline void
RCLASS_SET_IV_HASH(VALUE obj, const st_table *tbl)
{
    RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
    RUBY_ASSERT(rb_shape_obj_too_complex(obj));
    RCLASS_IVPTR(obj) = (VALUE *)tbl;
}

static inline uint32_t
RCLASS_IV_COUNT(VALUE obj)
{
    RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE));
    if (rb_shape_obj_too_complex(obj)) {
        uint32_t count;

        // "Too complex" classes could have their IV hash mutated in
        // parallel, so lets lock around getting the hash size.
        RB_VM_LOCK_ENTER();
        {
            count = (uint32_t)rb_st_table_size(RCLASS_IV_HASH(obj));
        }
        RB_VM_LOCK_LEAVE();

        return count;
    }
    else {
        return rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(obj))->next_iv_index;
    }
}

static inline void
RCLASS_SET_M_TBL(VALUE klass, struct rb_id_table *table)
{
    RUBY_ASSERT(!RB_OBJ_PROMOTED(klass));
    RCLASS_M_TBL(klass) = table;
}

/* class.c */
void rb_class_subclass_add(VALUE super, VALUE klass);
void rb_class_remove_from_super_subclasses(VALUE);
void rb_class_update_superclasses(VALUE);
size_t rb_class_superclasses_memsize(VALUE);
void rb_class_remove_subclass_head(VALUE);
int rb_singleton_class_internal_p(VALUE sklass);
VALUE rb_class_boot(VALUE);
VALUE rb_class_s_alloc(VALUE klass);
VALUE rb_module_s_alloc(VALUE klass);
void rb_module_set_initialized(VALUE module);
void rb_module_check_initializable(VALUE module);
VALUE rb_make_metaclass(VALUE, VALUE);
VALUE rb_include_class_new(VALUE, VALUE);
void rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE);
void rb_class_detach_subclasses(VALUE);
void rb_class_detach_module_subclasses(VALUE);
void rb_class_remove_from_module_subclasses(VALUE);
VALUE rb_define_class_id_under_no_pin(VALUE outer, ID id, VALUE super);
VALUE rb_obj_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_protected_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_private_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_obj_public_methods(int argc, const VALUE *argv, VALUE obj);
VALUE rb_class_undefined_instance_methods(VALUE mod);
VALUE rb_special_singleton_class(VALUE);
VALUE rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach);
VALUE rb_singleton_class_get(VALUE obj);
void rb_undef_methods_from(VALUE klass, VALUE super);

static inline void RCLASS_SET_ORIGIN(VALUE klass, VALUE origin);
static inline void RICLASS_SET_ORIGIN_SHARED_MTBL(VALUE iclass);
static inline VALUE RCLASS_SUPER(VALUE klass);
static inline VALUE RCLASS_SET_SUPER(VALUE klass, VALUE super);
static inline void RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass);

VALUE rb_class_inherited(VALUE, VALUE);
VALUE rb_keyword_error_new(const char *, VALUE);

static inline bool
RCLASS_SINGLETON_P(VALUE klass)
{
    return RB_TYPE_P(klass, T_CLASS) && FL_TEST_RAW(klass, FL_SINGLETON);
}

static inline rb_alloc_func_t
RCLASS_ALLOCATOR(VALUE klass)
{
    if (RCLASS_SINGLETON_P(klass)) {
        return 0;
    }
    return RCLASS_EXT(klass)->as.class.allocator;
}

static inline void
RCLASS_SET_ALLOCATOR(VALUE klass, rb_alloc_func_t allocator)
{
    assert(!RCLASS_SINGLETON_P(klass));
    RCLASS_EXT(klass)->as.class.allocator = allocator;
}

static inline void
RCLASS_SET_ORIGIN(VALUE klass, VALUE origin)
{
    RB_OBJ_WRITE(klass, &RCLASS_ORIGIN(klass), origin);
    if (klass != origin) FL_SET(origin, RICLASS_IS_ORIGIN);
}

static inline void
RICLASS_SET_ORIGIN_SHARED_MTBL(VALUE iclass)
{
    FL_SET(iclass, RICLASS_ORIGIN_SHARED_MTBL);
}

static inline bool
RICLASS_OWNS_M_TBL_P(VALUE iclass)
{
    return FL_TEST_RAW(iclass, RICLASS_IS_ORIGIN | RICLASS_ORIGIN_SHARED_MTBL) == RICLASS_IS_ORIGIN;
}

static inline void
RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass)
{
    RB_OBJ_WRITE(iclass, &RCLASS_INCLUDER(iclass), klass);
}

static inline VALUE
RCLASS_SUPER(VALUE klass)
{
    return RCLASS(klass)->super;
}

static inline VALUE
RCLASS_SET_SUPER(VALUE klass, VALUE super)
{
    if (super) {
        rb_class_remove_from_super_subclasses(klass);
        rb_class_subclass_add(super, klass);
    }
    RB_OBJ_WRITE(klass, &RCLASS(klass)->super, super);
    rb_class_update_superclasses(klass);
    return super;
}

static inline void
RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool permanent)
{
    assert(BUILTIN_TYPE(klass) == T_CLASS || BUILTIN_TYPE(klass) == T_MODULE);
    assert(classpath == 0 || BUILTIN_TYPE(classpath) == T_STRING);

    RB_OBJ_WRITE(klass, &(RCLASS_EXT(klass)->classpath), classpath);
    RCLASS_EXT(klass)->permanent_classpath = permanent;
}

static inline VALUE
RCLASS_SET_ATTACHED_OBJECT(VALUE klass, VALUE attached_object)
{
    assert(RCLASS_SINGLETON_P(klass));

    RB_OBJ_WRITE(klass, &RCLASS_EXT(klass)->as.singleton_class.attached_object, attached_object);
    return attached_object;
}

#endif /* INTERNAL_CLASS_H */