зеркало из https://github.com/github/ruby.git
This reverts commits:10d6a3aca7
8ba48c1b85
fba8627dc1
dd883de5ba
6c6a25feca
167e6b48f1
7cb96d41a5
3207979278
595b3c4fdd
1521f7cf89
c11c5e69ac
cf33608203
3632a812c0
f56506be0d
86427a3219
. The reason for the revert is that we observe ABA problem around inline method cache. When a cache misshits, we search for a method entry. And if the entry is identical to what was cached before, we reuse the cache. But the commits we are reverting here introduced situations where a method entry is freed, then the identical memory region is used for another method entry. An inline method cache cannot detect that ABA. Here is a code that reproduce such situation: ```ruby require 'prime' class << Integer alias org_sqrt sqrt def sqrt(n) raise end GC.stress = true Prime.each(7*37){} rescue nil # <- Here we populate CC class << Object.new; end # These adjacent remove-then-alias maneuver # frees a method entry, then immediately # reuses it for another. remove_method :sqrt alias sqrt org_sqrt end Prime.each(7*37).to_a # <- SEGV ```
This commit is contained in:
Родитель
ef697388be
Коммит
eb92159d72
45
class.c
45
class.c
|
@ -956,41 +956,25 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
|
||||||
return method_changed;
|
return method_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct tuple {
|
|
||||||
struct RClass *klass;
|
|
||||||
struct RClass *origin;
|
|
||||||
} tuple;
|
|
||||||
|
|
||||||
static enum rb_id_table_iterator_result
|
|
||||||
inject_refined_method(ID *key, VALUE *value, void *data, int _)
|
|
||||||
{
|
|
||||||
const tuple *ptr = data;
|
|
||||||
const rb_method_entry_t *me = *(const rb_method_entry_t **) value;
|
|
||||||
const rb_method_entry_t *orig_me = me->def->body.refined.orig_me;
|
|
||||||
const rb_method_entry_t *new_me =
|
|
||||||
rb_method_entry_from_template(
|
|
||||||
me, &(rb_method_refined_t) {
|
|
||||||
.orig_me = NULL,
|
|
||||||
.owner = me->def->body.refined.owner, });
|
|
||||||
rb_id_table_insert(RCLASS_M_TBL(ptr->klass), *key, (VALUE)new_me);
|
|
||||||
RB_OBJ_WRITTEN(ptr->klass, Qundef, new_me);
|
|
||||||
*value = (VALUE)rb_method_entry_clone(orig_me);
|
|
||||||
RB_OBJ_WRITTEN(ptr->origin, Qundef, orig_me);
|
|
||||||
return ID_TABLE_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum rb_id_table_iterator_result
|
static enum rb_id_table_iterator_result
|
||||||
move_refined_method(ID key, VALUE value, void *data)
|
move_refined_method(ID key, VALUE value, void *data)
|
||||||
{
|
{
|
||||||
const tuple *ptr = data;
|
rb_method_entry_t *me = (rb_method_entry_t *) value;
|
||||||
const rb_method_entry_t *me = (const rb_method_entry_t *) value;
|
VALUE klass = (VALUE)data;
|
||||||
|
struct rb_id_table *tbl = RCLASS_M_TBL(klass);
|
||||||
|
|
||||||
if (me->def->type == VM_METHOD_TYPE_REFINED) {
|
if (me->def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
if (me->def->body.refined.orig_me) {
|
if (me->def->body.refined.orig_me) {
|
||||||
return ID_TABLE_REPLACE;
|
const rb_method_entry_t *orig_me = me->def->body.refined.orig_me, *new_me;
|
||||||
|
RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, NULL);
|
||||||
|
new_me = rb_method_entry_clone(me);
|
||||||
|
rb_id_table_insert(tbl, key, (VALUE)new_me);
|
||||||
|
RB_OBJ_WRITTEN(klass, Qundef, new_me);
|
||||||
|
rb_method_entry_copy(me, orig_me);
|
||||||
|
return ID_TABLE_CONTINUE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_id_table_insert(RCLASS_M_TBL(ptr->klass), key, (VALUE)me);
|
rb_id_table_insert(tbl, key, (VALUE)me);
|
||||||
return ID_TABLE_DELETE;
|
return ID_TABLE_DELETE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1016,12 +1000,7 @@ rb_prepend_module(VALUE klass, VALUE module)
|
||||||
RCLASS_SET_ORIGIN(klass, origin);
|
RCLASS_SET_ORIGIN(klass, origin);
|
||||||
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
|
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
|
||||||
RCLASS_M_TBL_INIT(klass);
|
RCLASS_M_TBL_INIT(klass);
|
||||||
rb_id_table_foreach_with_replace_with_key(
|
rb_id_table_foreach(RCLASS_M_TBL(origin), move_refined_method, (void *)klass);
|
||||||
RCLASS_M_TBL(origin),
|
|
||||||
move_refined_method,
|
|
||||||
inject_refined_method,
|
|
||||||
&(tuple) { RCLASS(klass), RCLASS(origin), },
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
changed = include_modules_at(klass, klass, module, FALSE);
|
changed = include_modules_at(klass, klass, module, FALSE);
|
||||||
if (changed < 0)
|
if (changed < 0)
|
||||||
|
|
|
@ -123,7 +123,7 @@ method_coverage_i(void *vstart, void *vend, size_t stride, void *data)
|
||||||
|
|
||||||
for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
|
for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
|
||||||
if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) {
|
if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) {
|
||||||
const rb_method_entry_t *me = (const rb_method_entry_t *) v;
|
const rb_method_entry_t *me = (rb_method_entry_t *) v;
|
||||||
VALUE path, first_lineno, first_column, last_lineno, last_column;
|
VALUE path, first_lineno, first_column, last_lineno, last_column;
|
||||||
VALUE data[5], ncoverage, methods;
|
VALUE data[5], ncoverage, methods;
|
||||||
VALUE methods_id = ID2SYM(rb_intern("methods"));
|
VALUE methods_id = ID2SYM(rb_intern("methods"));
|
||||||
|
|
4
gc.c
4
gc.c
|
@ -7829,9 +7829,9 @@ void rb_update_st_references(struct st_table *ht)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gc_ref_update_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
|
gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me)
|
||||||
{
|
{
|
||||||
const rb_method_definition_t *def = me->def;
|
rb_method_definition_t *def = me->def;
|
||||||
|
|
||||||
UPDATE_IF_MOVED(objspace, me->owner);
|
UPDATE_IF_MOVED(objspace, me->owner);
|
||||||
UPDATE_IF_MOVED(objspace, me->defined_class);
|
UPDATE_IF_MOVED(objspace, me->defined_class);
|
||||||
|
|
83
id_table.c
83
id_table.c
|
@ -269,62 +269,57 @@ rb_id_table_delete(struct rb_id_table *tbl, ID id)
|
||||||
void
|
void
|
||||||
rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data)
|
rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data)
|
||||||
{
|
{
|
||||||
rb_id_table_foreach_with_replace_with_key(tbl, func, replace, data, false);
|
int i, capa = tbl->capa;
|
||||||
|
|
||||||
|
for (i=0; i<capa; i++) {
|
||||||
|
if (ITEM_KEY_ISSET(tbl, i)) {
|
||||||
|
const id_key_t key = ITEM_GET_KEY(tbl, i);
|
||||||
|
enum rb_id_table_iterator_result ret = (*func)(Qundef, tbl->items[i].val, data);
|
||||||
|
assert(key != 0);
|
||||||
|
|
||||||
|
if (ret == ID_TABLE_REPLACE) {
|
||||||
|
VALUE val = tbl->items[i].val;
|
||||||
|
ret = (*replace)(NULL, &val, data, TRUE);
|
||||||
|
tbl->items[i].val = val;
|
||||||
|
}
|
||||||
|
else if (ret == ID_TABLE_STOP)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
|
rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
|
||||||
{
|
{
|
||||||
rb_id_table_foreach_with_replace_with_key(tbl, func, 0, data, true);
|
int i, capa = tbl->capa;
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct tuple {
|
for (i=0; i<capa; i++) {
|
||||||
rb_id_table_foreach_values_func_t *const func;
|
if (ITEM_KEY_ISSET(tbl, i)) {
|
||||||
void *const data;
|
const id_key_t key = ITEM_GET_KEY(tbl, i);
|
||||||
} tuple;
|
enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
|
||||||
|
assert(key != 0);
|
||||||
|
|
||||||
static enum rb_id_table_iterator_result
|
if (ret == ID_TABLE_DELETE)
|
||||||
cdr(ID car, VALUE cdr, void *data)
|
hash_delete_index(tbl, i);
|
||||||
{
|
else if (ret == ID_TABLE_STOP)
|
||||||
const tuple *ptr = data;
|
return;
|
||||||
return ptr->func(cdr, ptr->data);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
|
rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
|
||||||
{
|
{
|
||||||
rb_id_table_foreach_with_replace(
|
int i, capa = tbl->capa;
|
||||||
tbl, cdr, 0, &(tuple) { func, data, });
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
for (i=0; i<capa; i++) {
|
||||||
rb_id_table_foreach_with_replace_with_key(
|
if (ITEM_KEY_ISSET(tbl, i)) {
|
||||||
struct rb_id_table *tbl,
|
enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data);
|
||||||
rb_id_table_foreach_func_t *func,
|
|
||||||
rb_id_table_update_callback_func_t *replace,
|
if (ret == ID_TABLE_DELETE)
|
||||||
void *data,
|
hash_delete_index(tbl, i);
|
||||||
bool needkey)
|
else if (ret == ID_TABLE_STOP)
|
||||||
{
|
return;
|
||||||
for (int i = 0; i < tbl->capa; i++) {
|
}
|
||||||
if (ITEM_KEY_ISSET(tbl, i)) {
|
|
||||||
const id_key_t key = ITEM_GET_KEY(tbl, i);
|
|
||||||
assert(key != 0);
|
|
||||||
ID k = needkey ? key2id(key) : 0;
|
|
||||||
VALUE v = tbl->items[i].val;
|
|
||||||
switch (func(k, v, data)) {
|
|
||||||
case ID_TABLE_DELETE:
|
|
||||||
hash_delete_index(tbl, i);
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case ID_TABLE_CONTINUE:
|
|
||||||
continue;
|
|
||||||
case ID_TABLE_STOP:
|
|
||||||
return;
|
|
||||||
case ID_TABLE_REPLACE:
|
|
||||||
if (replace) {
|
|
||||||
replace(&k, &v, data, true);
|
|
||||||
tbl->items[i].val = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ enum rb_id_table_iterator_result {
|
||||||
ID_TABLE_STOP = ST_STOP,
|
ID_TABLE_STOP = ST_STOP,
|
||||||
ID_TABLE_DELETE = ST_DELETE,
|
ID_TABLE_DELETE = ST_DELETE,
|
||||||
ID_TABLE_REPLACE = ST_REPLACE,
|
ID_TABLE_REPLACE = ST_REPLACE,
|
||||||
|
ID_TABLE_ITERATOR_RESULT_END
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rb_id_table *rb_id_table_create(size_t size);
|
struct rb_id_table *rb_id_table_create(size_t size);
|
||||||
|
@ -28,7 +29,6 @@ typedef enum rb_id_table_iterator_result rb_id_table_foreach_func_t(ID id, VALUE
|
||||||
typedef enum rb_id_table_iterator_result rb_id_table_foreach_values_func_t(VALUE val, void *data);
|
typedef enum rb_id_table_iterator_result rb_id_table_foreach_values_func_t(VALUE val, void *data);
|
||||||
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data);
|
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data);
|
||||||
void rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data);
|
void rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data);
|
||||||
void rb_id_table_foreach_with_replace_with_key(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data, bool needkey);
|
|
||||||
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data);
|
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data);
|
||||||
|
|
||||||
#endif /* RUBY_ID_TABLE_H */
|
#endif /* RUBY_ID_TABLE_H */
|
||||||
|
|
|
@ -911,7 +911,7 @@ invokeblock
|
||||||
// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci);
|
// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci);
|
||||||
{
|
{
|
||||||
static struct rb_call_cache cc = {
|
static struct rb_call_cache cc = {
|
||||||
0, 0, NULL, vm_invokeblock_i,
|
0, 0, NULL, NULL, vm_invokeblock_i,
|
||||||
};
|
};
|
||||||
|
|
||||||
VALUE bh = VM_BLOCK_HANDLER_NONE;
|
VALUE bh = VM_BLOCK_HANDLER_NONE;
|
||||||
|
|
|
@ -2330,6 +2330,7 @@ enum method_missing_reason {
|
||||||
MISSING_NONE = 0x40
|
MISSING_NONE = 0x40
|
||||||
};
|
};
|
||||||
struct rb_callable_method_entry_struct;
|
struct rb_callable_method_entry_struct;
|
||||||
|
struct rb_method_definition_struct;
|
||||||
struct rb_execution_context_struct;
|
struct rb_execution_context_struct;
|
||||||
struct rb_control_frame_struct;
|
struct rb_control_frame_struct;
|
||||||
struct rb_calling_info;
|
struct rb_calling_info;
|
||||||
|
@ -2341,6 +2342,7 @@ struct rb_call_cache {
|
||||||
|
|
||||||
/* inline cache: values */
|
/* inline cache: values */
|
||||||
const struct rb_callable_method_entry_struct *me;
|
const struct rb_callable_method_entry_struct *me;
|
||||||
|
const struct rb_method_definition_struct *def;
|
||||||
|
|
||||||
VALUE (*call)(struct rb_execution_context_struct *ec,
|
VALUE (*call)(struct rb_execution_context_struct *ec,
|
||||||
struct rb_control_frame_struct *cfp,
|
struct rb_control_frame_struct *cfp,
|
||||||
|
|
89
method.h
89
method.h
|
@ -49,23 +49,54 @@ typedef struct rb_cref_struct {
|
||||||
/* method data type */
|
/* method data type */
|
||||||
|
|
||||||
typedef struct rb_method_entry_struct {
|
typedef struct rb_method_entry_struct {
|
||||||
const VALUE flags;
|
VALUE flags;
|
||||||
const VALUE defined_class;
|
VALUE defined_class;
|
||||||
struct rb_method_definition_struct * const def;
|
struct rb_method_definition_struct * const def;
|
||||||
const ID called_id;
|
ID called_id;
|
||||||
const VALUE owner;
|
VALUE owner;
|
||||||
} rb_method_entry_t;
|
} rb_method_entry_t;
|
||||||
|
|
||||||
typedef struct rb_callable_method_entry_struct { /* same fields with rb_method_entry_t */
|
typedef struct rb_callable_method_entry_struct { /* same fields with rb_method_entry_t */
|
||||||
const VALUE flags;
|
VALUE flags;
|
||||||
const VALUE defined_class;
|
const VALUE defined_class;
|
||||||
struct rb_method_definition_struct * const def;
|
struct rb_method_definition_struct * const def;
|
||||||
const ID called_id;
|
ID called_id;
|
||||||
const VALUE owner;
|
const VALUE owner;
|
||||||
} rb_callable_method_entry_t;
|
} rb_callable_method_entry_t;
|
||||||
|
|
||||||
#define METHOD_ENTRY_VISI(me) (rb_method_visibility_t)(((me)->flags & (IMEMO_FL_USER0 | IMEMO_FL_USER1)) >> (IMEMO_FL_USHIFT+0))
|
#define METHOD_ENTRY_VISI(me) (rb_method_visibility_t)(((me)->flags & (IMEMO_FL_USER0 | IMEMO_FL_USER1)) >> (IMEMO_FL_USHIFT+0))
|
||||||
#define METHOD_ENTRY_BASIC(me) (int) (((me)->flags & (IMEMO_FL_USER2 )) >> (IMEMO_FL_USHIFT+2))
|
#define METHOD_ENTRY_BASIC(me) (int) (((me)->flags & (IMEMO_FL_USER2 )) >> (IMEMO_FL_USHIFT+2))
|
||||||
|
#define METHOD_ENTRY_COMPLEMENTED(me) ((me)->flags & IMEMO_FL_USER3)
|
||||||
|
#define METHOD_ENTRY_COMPLEMENTED_SET(me) ((me)->flags = (me)->flags | IMEMO_FL_USER3)
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
METHOD_ENTRY_VISI_SET(rb_method_entry_t *me, rb_method_visibility_t visi)
|
||||||
|
{
|
||||||
|
VM_ASSERT((int)visi >= 0 && visi <= 3);
|
||||||
|
me->flags = (me->flags & ~(IMEMO_FL_USER0 | IMEMO_FL_USER1)) | (visi << (IMEMO_FL_USHIFT+0));
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
METHOD_ENTRY_BASIC_SET(rb_method_entry_t *me, unsigned int basic)
|
||||||
|
{
|
||||||
|
VM_ASSERT(basic <= 1);
|
||||||
|
me->flags = (me->flags & ~(IMEMO_FL_USER2 )) | (basic << (IMEMO_FL_USHIFT+2));
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
METHOD_ENTRY_FLAGS_SET(rb_method_entry_t *me, rb_method_visibility_t visi, unsigned int basic)
|
||||||
|
{
|
||||||
|
VM_ASSERT((int)visi >= 0 && visi <= 3);
|
||||||
|
VM_ASSERT(basic <= 1);
|
||||||
|
me->flags =
|
||||||
|
(me->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
|
||||||
|
((visi << (IMEMO_FL_USHIFT+0)) | (basic << (IMEMO_FL_USHIFT+2)));
|
||||||
|
}
|
||||||
|
static inline void
|
||||||
|
METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src)
|
||||||
|
{
|
||||||
|
dst->flags =
|
||||||
|
(dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
|
||||||
|
(src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2));
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VM_METHOD_TYPE_ISEQ, /*!< Ruby method */
|
VM_METHOD_TYPE_ISEQ, /*!< Ruby method */
|
||||||
|
@ -93,32 +124,32 @@ typedef struct rb_iseq_struct rb_iseq_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct rb_method_iseq_struct {
|
typedef struct rb_method_iseq_struct {
|
||||||
const rb_iseq_t *const iseqptr; /*!< iseq pointer, should be separated from iseqval */
|
rb_iseq_t * iseqptr; /*!< iseq pointer, should be separated from iseqval */
|
||||||
rb_cref_t *const cref; /*!< class reference, should be marked */
|
rb_cref_t * cref; /*!< class reference, should be marked */
|
||||||
} rb_method_iseq_t;
|
} rb_method_iseq_t; /* check rb_add_method_iseq() when modify the fields */
|
||||||
|
|
||||||
typedef struct rb_method_cfunc_struct {
|
typedef struct rb_method_cfunc_struct {
|
||||||
VALUE (*const func)(ANYARGS);
|
VALUE (*func)(ANYARGS);
|
||||||
VALUE (*const invoker)(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS));
|
VALUE (*invoker)(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS));
|
||||||
const int argc;
|
int argc;
|
||||||
} rb_method_cfunc_t;
|
} rb_method_cfunc_t;
|
||||||
|
|
||||||
typedef struct rb_method_attr_struct {
|
typedef struct rb_method_attr_struct {
|
||||||
const ID id;
|
ID id;
|
||||||
const VALUE location; /* should be marked */
|
VALUE location; /* should be marked */
|
||||||
} rb_method_attr_t;
|
} rb_method_attr_t;
|
||||||
|
|
||||||
typedef struct rb_method_alias_struct {
|
typedef struct rb_method_alias_struct {
|
||||||
const struct rb_method_entry_struct *const original_me; /* original_me->klass is original owner */
|
struct rb_method_entry_struct * original_me; /* original_me->klass is original owner */
|
||||||
} rb_method_alias_t;
|
} rb_method_alias_t;
|
||||||
|
|
||||||
typedef struct rb_method_refined_struct {
|
typedef struct rb_method_refined_struct {
|
||||||
const struct rb_method_entry_struct *const orig_me;
|
struct rb_method_entry_struct * orig_me;
|
||||||
const VALUE owner;
|
VALUE owner;
|
||||||
} rb_method_refined_t;
|
} rb_method_refined_t;
|
||||||
|
|
||||||
typedef struct rb_method_bmethod_struct {
|
typedef struct rb_method_bmethod_struct {
|
||||||
const VALUE proc; /* should be marked */
|
VALUE proc; /* should be marked */
|
||||||
struct rb_hook_list_struct *hooks;
|
struct rb_hook_list_struct *hooks;
|
||||||
} rb_method_bmethod_t;
|
} rb_method_bmethod_t;
|
||||||
|
|
||||||
|
@ -130,22 +161,22 @@ enum method_optimized_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rb_method_definition_struct {
|
struct rb_method_definition_struct {
|
||||||
BITFIELD(rb_method_type_t, const type, VM_METHOD_TYPE_MINIMUM_BITS);
|
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
|
||||||
int alias_count : 28;
|
int alias_count : 28;
|
||||||
int complemented_count : 28;
|
int complemented_count : 28;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
const rb_method_iseq_t iseq;
|
rb_method_iseq_t iseq;
|
||||||
const rb_method_cfunc_t cfunc;
|
rb_method_cfunc_t cfunc;
|
||||||
const rb_method_attr_t attr;
|
rb_method_attr_t attr;
|
||||||
const rb_method_alias_t alias;
|
rb_method_alias_t alias;
|
||||||
const rb_method_refined_t refined;
|
rb_method_refined_t refined;
|
||||||
rb_method_bmethod_t bmethod;
|
rb_method_bmethod_t bmethod;
|
||||||
|
|
||||||
const enum method_optimized_type optimize_type;
|
enum method_optimized_type optimize_type;
|
||||||
} body;
|
} body;
|
||||||
|
|
||||||
const ID original_id;
|
ID original_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct rb_method_definition_struct rb_method_definition_t;
|
typedef struct rb_method_definition_struct rb_method_definition_t;
|
||||||
|
@ -161,9 +192,8 @@ void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *c
|
||||||
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
|
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
|
||||||
void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi);
|
void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi);
|
||||||
|
|
||||||
const rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
|
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
|
||||||
const rb_method_entry_t *rb_method_entry_from_template(const rb_method_entry_t *template, const void *opts);
|
rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def);
|
||||||
const rb_method_entry_t *rb_method_entry_for_missing(ID mid, VALUE klass);
|
|
||||||
|
|
||||||
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
|
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
|
||||||
|
|
||||||
|
@ -193,6 +223,7 @@ void rb_sweep_method_entry(void *vm);
|
||||||
|
|
||||||
const rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me);
|
const rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me);
|
||||||
const rb_callable_method_entry_t *rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class);
|
const rb_callable_method_entry_t *rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class);
|
||||||
|
void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src);
|
||||||
|
|
||||||
void rb_scope_visibility_set(rb_method_visibility_t);
|
void rb_scope_visibility_set(rb_method_visibility_t);
|
||||||
|
|
||||||
|
|
20
proc.c
20
proc.c
|
@ -1426,7 +1426,7 @@ bm_compact(void *ptr)
|
||||||
UPDATE_REFERENCE(data->recv);
|
UPDATE_REFERENCE(data->recv);
|
||||||
UPDATE_REFERENCE(data->klass);
|
UPDATE_REFERENCE(data->klass);
|
||||||
UPDATE_REFERENCE(data->iclass);
|
UPDATE_REFERENCE(data->iclass);
|
||||||
UPDATE_TYPED_REFERENCE(const rb_method_entry_t *, data->me);
|
UPDATE_TYPED_REFERENCE(rb_method_entry_t *, data->me);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
|
@ -1474,9 +1474,19 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass)
|
||||||
{
|
{
|
||||||
struct METHOD *data;
|
struct METHOD *data;
|
||||||
VALUE method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
|
VALUE method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
|
||||||
|
rb_method_entry_t *me;
|
||||||
|
rb_method_definition_t *def;
|
||||||
|
|
||||||
RB_OBJ_WRITE(method, &data->recv, obj);
|
RB_OBJ_WRITE(method, &data->recv, obj);
|
||||||
RB_OBJ_WRITE(method, &data->klass, klass);
|
RB_OBJ_WRITE(method, &data->klass, klass);
|
||||||
RB_OBJ_WRITE(method, &data->me, rb_method_entry_for_missing(id, klass));
|
|
||||||
|
def = ZALLOC(rb_method_definition_t);
|
||||||
|
def->type = VM_METHOD_TYPE_MISSING;
|
||||||
|
def->original_id = id;
|
||||||
|
|
||||||
|
me = rb_method_entry_create(id, klass, METHOD_VISI_UNDEF, def);
|
||||||
|
|
||||||
|
RB_OBJ_WRITE(method, &data->me, me);
|
||||||
|
|
||||||
OBJ_INFECT(method, klass);
|
OBJ_INFECT(method, klass);
|
||||||
|
|
||||||
|
@ -1519,7 +1529,7 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
|
||||||
if (me->defined_class) {
|
if (me->defined_class) {
|
||||||
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
|
VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
|
||||||
id = me->def->original_id;
|
id = me->def->original_id;
|
||||||
me = (const rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
|
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VALUE klass = RCLASS_SUPER(me->owner);
|
VALUE klass = RCLASS_SUPER(me->owner);
|
||||||
|
@ -1557,7 +1567,7 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
|
||||||
me = rb_method_entry_with_refinements(klass, id, &iclass);
|
me = rb_method_entry_with_refinements(klass, id, &iclass);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
me = (const rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
|
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
|
||||||
}
|
}
|
||||||
return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
|
return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
|
||||||
}
|
}
|
||||||
|
@ -2947,7 +2957,7 @@ method_super_method(VALUE method)
|
||||||
super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
|
super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
|
||||||
mid = data->me->called_id;
|
mid = data->me->called_id;
|
||||||
if (!super_class) return Qnil;
|
if (!super_class) return Qnil;
|
||||||
me = (const rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
|
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
|
||||||
if (!me) return Qnil;
|
if (!me) return Qnil;
|
||||||
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
|
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
|
||||||
}
|
}
|
||||||
|
|
13
vm.c
13
vm.c
|
@ -298,6 +298,17 @@ rb_vm_cref_new_toplevel(void)
|
||||||
return vm_cref_new_toplevel(GET_EC());
|
return vm_cref_new_toplevel(GET_EC());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_cref_dump(const char *mesg, const rb_cref_t *cref)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "vm_cref_dump: %s (%p)\n", mesg, (void *)cref);
|
||||||
|
|
||||||
|
while (cref) {
|
||||||
|
fprintf(stderr, "= cref| klass: %s\n", RSTRING_PTR(rb_class_path(CREF_CLASS(cref))));
|
||||||
|
cref = CREF_NEXT(cref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_vm_block_ep_update(VALUE obj, const struct rb_block *dst, const VALUE *ep)
|
rb_vm_block_ep_update(VALUE obj, const struct rb_block *dst, const VALUE *ep)
|
||||||
{
|
{
|
||||||
|
@ -1595,7 +1606,7 @@ static enum rb_id_table_iterator_result
|
||||||
check_redefined_method(ID mid, VALUE value, void *data)
|
check_redefined_method(ID mid, VALUE value, void *data)
|
||||||
{
|
{
|
||||||
VALUE klass = (VALUE)data;
|
VALUE klass = (VALUE)data;
|
||||||
const rb_method_entry_t *me = (const rb_method_entry_t *)value;
|
const rb_method_entry_t *me = (rb_method_entry_t *)value;
|
||||||
const rb_method_entry_t *newme = rb_method_entry(klass, mid);
|
const rb_method_entry_t *newme = rb_method_entry(klass, mid);
|
||||||
|
|
||||||
if (newme != me) rb_vm_check_redefinition_opt_method(me, me->owner);
|
if (newme != me) rb_vm_check_redefinition_opt_method(me, me->owner);
|
||||||
|
|
|
@ -1349,7 +1349,7 @@ frame2iseq(VALUE frame)
|
||||||
return (const rb_iseq_t *)frame;
|
return (const rb_iseq_t *)frame;
|
||||||
case imemo_ment:
|
case imemo_ment:
|
||||||
{
|
{
|
||||||
const rb_callable_method_entry_t *cme = (const rb_callable_method_entry_t *)frame;
|
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
||||||
switch (cme->def->type) {
|
switch (cme->def->type) {
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
return cme->def->body.iseq.iseqptr;
|
return cme->def->body.iseq.iseqptr;
|
||||||
|
@ -1405,7 +1405,7 @@ frame2klass(VALUE frame)
|
||||||
if (frame == Qnil) return Qnil;
|
if (frame == Qnil) return Qnil;
|
||||||
|
|
||||||
if (RB_TYPE_P(frame, T_IMEMO)) {
|
if (RB_TYPE_P(frame, T_IMEMO)) {
|
||||||
const rb_callable_method_entry_t *cme = (const rb_callable_method_entry_t *)frame;
|
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
||||||
|
|
||||||
if (imemo_type(frame) == imemo_ment) {
|
if (imemo_type(frame) == imemo_ment) {
|
||||||
return cme->defined_class;
|
return cme->defined_class;
|
||||||
|
|
|
@ -491,7 +491,7 @@ rb_iseq_check(const rb_iseq_t *iseq)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const rb_iseq_t *
|
static inline const rb_iseq_t *
|
||||||
def_iseq_ptr(const rb_method_definition_t *def)
|
def_iseq_ptr(rb_method_definition_t *def)
|
||||||
{
|
{
|
||||||
#if VM_CHECK_MODE > 0
|
#if VM_CHECK_MODE > 0
|
||||||
if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
|
if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
#include "ruby/config.h"
|
#include "ruby/config.h"
|
||||||
#include "debug_counter.h"
|
#include "debug_counter.h"
|
||||||
|
|
||||||
extern void rb_method_entry_spoof(const rb_method_entry_t *me);
|
extern rb_method_definition_t *rb_method_definition_create(rb_method_type_t type, ID mid);
|
||||||
|
extern void rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts);
|
||||||
extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
||||||
extern VALUE rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
|
extern VALUE rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
|
||||||
int argc, const VALUE *argv, int priv);
|
int argc, const VALUE *argv, int priv);
|
||||||
|
@ -578,8 +579,8 @@ vm_getspecial(const rb_execution_context_t *ec, const VALUE *lep, rb_num_t key,
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
PUREFUNC(static const rb_callable_method_entry_t *check_method_entry(VALUE obj, int can_be_svar));
|
PUREFUNC(static rb_callable_method_entry_t *check_method_entry(VALUE obj, int can_be_svar));
|
||||||
static const rb_callable_method_entry_t *
|
static rb_callable_method_entry_t *
|
||||||
check_method_entry(VALUE obj, int can_be_svar)
|
check_method_entry(VALUE obj, int can_be_svar)
|
||||||
{
|
{
|
||||||
if (obj == Qfalse) return NULL;
|
if (obj == Qfalse) return NULL;
|
||||||
|
@ -590,7 +591,7 @@ check_method_entry(VALUE obj, int can_be_svar)
|
||||||
|
|
||||||
switch (imemo_type(obj)) {
|
switch (imemo_type(obj)) {
|
||||||
case imemo_ment:
|
case imemo_ment:
|
||||||
return (const rb_callable_method_entry_t *)obj;
|
return (rb_callable_method_entry_t *)obj;
|
||||||
case imemo_cref:
|
case imemo_cref:
|
||||||
return NULL;
|
return NULL;
|
||||||
case imemo_svar:
|
case imemo_svar:
|
||||||
|
@ -609,7 +610,7 @@ MJIT_STATIC const rb_callable_method_entry_t *
|
||||||
rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
|
rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
|
||||||
{
|
{
|
||||||
const VALUE *ep = cfp->ep;
|
const VALUE *ep = cfp->ep;
|
||||||
const rb_callable_method_entry_t *me;
|
rb_callable_method_entry_t *me;
|
||||||
|
|
||||||
while (!VM_ENV_LOCAL_P(ep)) {
|
while (!VM_ENV_LOCAL_P(ep)) {
|
||||||
if ((me = check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me;
|
if ((me = check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me;
|
||||||
|
@ -620,7 +621,7 @@ rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
|
||||||
}
|
}
|
||||||
|
|
||||||
static rb_cref_t *
|
static rb_cref_t *
|
||||||
method_entry_cref(const rb_callable_method_entry_t *me)
|
method_entry_cref(rb_callable_method_entry_t *me)
|
||||||
{
|
{
|
||||||
switch (me->def->type) {
|
switch (me->def->type) {
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
|
@ -644,7 +645,7 @@ check_cref(VALUE obj, int can_be_svar)
|
||||||
|
|
||||||
switch (imemo_type(obj)) {
|
switch (imemo_type(obj)) {
|
||||||
case imemo_ment:
|
case imemo_ment:
|
||||||
return method_entry_cref((const rb_callable_method_entry_t *)obj);
|
return method_entry_cref((rb_callable_method_entry_t *)obj);
|
||||||
case imemo_cref:
|
case imemo_cref:
|
||||||
return (rb_cref_t *)obj;
|
return (rb_cref_t *)obj;
|
||||||
case imemo_svar:
|
case imemo_svar:
|
||||||
|
@ -1390,6 +1391,9 @@ calccall(const struct rb_call_info *ci, const struct rb_call_cache *cc, const rb
|
||||||
else if (LIKELY(cc->me != me)) {
|
else if (LIKELY(cc->me != me)) {
|
||||||
return vm_call_general; /* normal cases */
|
return vm_call_general; /* normal cases */
|
||||||
}
|
}
|
||||||
|
else if (UNLIKELY(cc->def != me->def)) {
|
||||||
|
return vm_call_general; /* cc->me was refined elsewhere */
|
||||||
|
}
|
||||||
/* "Calling a formerly-public method, which is now privatised, with an
|
/* "Calling a formerly-public method, which is now privatised, with an
|
||||||
* explicit receiver" is the only situation we have to check here. A
|
* explicit receiver" is the only situation we have to check here. A
|
||||||
* formerly-private method now publicised is an absolutely safe thing.
|
* formerly-private method now publicised is an absolutely safe thing.
|
||||||
|
@ -1412,6 +1416,7 @@ rb_vm_search_method_slowpath(const struct rb_call_info *ci, struct rb_call_cache
|
||||||
GET_GLOBAL_METHOD_STATE(),
|
GET_GLOBAL_METHOD_STATE(),
|
||||||
RCLASS_SERIAL(klass),
|
RCLASS_SERIAL(klass),
|
||||||
me,
|
me,
|
||||||
|
me ? me->def : NULL,
|
||||||
calccall(ci, cc, me),
|
calccall(ci, cc, me),
|
||||||
};
|
};
|
||||||
VM_ASSERT(callable_method_entry_p(cc->me));
|
VM_ASSERT(callable_method_entry_p(cc->me));
|
||||||
|
@ -2573,31 +2578,32 @@ find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
|
||||||
return current_class; /* maybe module function */
|
return current_class; /* maybe module function */
|
||||||
}
|
}
|
||||||
|
|
||||||
static const void*
|
static const rb_callable_method_entry_t *
|
||||||
aliased_callable_method_entry0(const rb_method_entry_t *me)
|
aliased_callable_method_entry(const rb_callable_method_entry_t *me)
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *orig_me = me->def->body.alias.original_me;
|
const rb_method_entry_t *orig_me = me->def->body.alias.original_me;
|
||||||
const rb_callable_method_entry_t *cme;
|
const rb_callable_method_entry_t *cme;
|
||||||
|
|
||||||
if (orig_me->defined_class != 0) {
|
if (orig_me->defined_class == 0) {
|
||||||
VM_ASSERT(callable_class_p(orig_me->defined_class));
|
|
||||||
return orig_me;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
VALUE defined_class = find_defined_class_by_owner(me->defined_class, orig_me->owner);
|
VALUE defined_class = find_defined_class_by_owner(me->defined_class, orig_me->owner);
|
||||||
VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE));
|
VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE));
|
||||||
cme = rb_method_entry_complement_defined_class(orig_me, me->called_id, defined_class);
|
cme = rb_method_entry_complement_defined_class(orig_me, me->called_id, defined_class);
|
||||||
const rb_method_entry_t *ret =
|
|
||||||
rb_method_entry_from_template((const rb_method_entry_t*)me, cme);
|
|
||||||
rb_method_entry_spoof(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const rb_callable_method_entry_t*
|
if (me->def->alias_count + me->def->complemented_count == 0) {
|
||||||
aliased_callable_method_entry(const rb_callable_method_entry_t *me)
|
RB_OBJ_WRITE(me, &me->def->body.alias.original_me, cme);
|
||||||
{
|
}
|
||||||
return aliased_callable_method_entry0((const void*)me);
|
else {
|
||||||
|
rb_method_definition_t *def =
|
||||||
|
rb_method_definition_create(VM_METHOD_TYPE_ALIAS, me->def->original_id);
|
||||||
|
rb_method_definition_set((rb_method_entry_t *)me, def, (void *)cme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cme = (const rb_callable_method_entry_t *)orig_me;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_ASSERT(callable_method_entry_p(cme));
|
||||||
|
return cme;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const rb_callable_method_entry_t *
|
static const rb_callable_method_entry_t *
|
||||||
|
|
568
vm_method.c
568
vm_method.c
|
@ -41,7 +41,7 @@ struct cache_entry {
|
||||||
rb_serial_t method_state;
|
rb_serial_t method_state;
|
||||||
rb_serial_t class_serial;
|
rb_serial_t class_serial;
|
||||||
ID mid;
|
ID mid;
|
||||||
const rb_method_entry_t* me;
|
rb_method_entry_t* me;
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,7 +132,9 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me
|
||||||
{
|
{
|
||||||
if (argc < -2 || 15 < argc) rb_raise(rb_eArgError, "arity out of range: %d for -2..15", argc);
|
if (argc < -2 || 15 < argc) rb_raise(rb_eArgError, "arity out of range: %d for -2..15", argc);
|
||||||
if (func != rb_f_notimplement) {
|
if (func != rb_f_notimplement) {
|
||||||
rb_method_cfunc_t opt = { func, 0, argc, };
|
rb_method_cfunc_t opt;
|
||||||
|
opt.func = func;
|
||||||
|
opt.argc = argc;
|
||||||
rb_add_method(klass, mid, VM_METHOD_TYPE_CFUNC, &opt, visi);
|
rb_add_method(klass, mid, VM_METHOD_TYPE_CFUNC, &opt, visi);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -140,11 +142,9 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
rb_free_method_entry(const rb_method_entry_t *me)
|
rb_method_definition_release(rb_method_definition_t *def, int complemented)
|
||||||
{
|
{
|
||||||
rb_method_definition_t *def = me->def;
|
|
||||||
|
|
||||||
if (def != NULL) {
|
if (def != NULL) {
|
||||||
const int alias_count = def->alias_count;
|
const int alias_count = def->alias_count;
|
||||||
const int complemented_count = def->complemented_count;
|
const int complemented_count = def->complemented_count;
|
||||||
|
@ -158,7 +158,7 @@ rb_free_method_entry(const rb_method_entry_t *me)
|
||||||
xfree(def);
|
xfree(def);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (def->complemented_count > 0) def->complemented_count--;
|
if (complemented) def->complemented_count--;
|
||||||
else if (def->alias_count > 0) def->alias_count--;
|
else if (def->alias_count > 0) def->alias_count--;
|
||||||
|
|
||||||
if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d,%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id),
|
if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d,%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id),
|
||||||
|
@ -167,17 +167,23 @@ rb_free_method_entry(const rb_method_entry_t *me)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const rb_method_entry_t *search_method(VALUE klass, ID id, VALUE *defined_class_ptr);
|
void
|
||||||
|
rb_free_method_entry(const rb_method_entry_t *me)
|
||||||
|
{
|
||||||
|
rb_method_definition_release(me->def, METHOD_ENTRY_COMPLEMENTED(me));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline rb_method_entry_t *search_method(VALUE klass, ID id, VALUE *defined_class_ptr);
|
||||||
extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
||||||
|
|
||||||
static inline const rb_method_entry_t *
|
static inline rb_method_entry_t *
|
||||||
lookup_method_table(VALUE klass, ID id)
|
lookup_method_table(VALUE klass, ID id)
|
||||||
{
|
{
|
||||||
st_data_t body;
|
st_data_t body;
|
||||||
struct rb_id_table *m_tbl = RCLASS_M_TBL(klass);
|
struct rb_id_table *m_tbl = RCLASS_M_TBL(klass);
|
||||||
|
|
||||||
if (rb_id_table_lookup(m_tbl, id, &body)) {
|
if (rb_id_table_lookup(m_tbl, id, &body)) {
|
||||||
return (const rb_method_entry_t *) body;
|
return (rb_method_entry_t *) body;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -211,10 +217,98 @@ static VALUE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc)
|
||||||
|
{
|
||||||
|
cfunc->func = func;
|
||||||
|
cfunc->argc = argc;
|
||||||
|
cfunc->invoker = call_cfunc_invoker_func(argc);
|
||||||
|
}
|
||||||
|
|
||||||
|
MJIT_FUNC_EXPORTED void
|
||||||
|
rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts)
|
||||||
|
{
|
||||||
|
*(rb_method_definition_t **)&me->def = def;
|
||||||
|
|
||||||
|
if (opts != NULL) {
|
||||||
|
switch (def->type) {
|
||||||
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
|
{
|
||||||
|
rb_method_iseq_t *iseq_body = (rb_method_iseq_t *)opts;
|
||||||
|
rb_cref_t *method_cref, *cref = iseq_body->cref;
|
||||||
|
|
||||||
|
/* setup iseq first (before invoking GC) */
|
||||||
|
RB_OBJ_WRITE(me, &def->body.iseq.iseqptr, iseq_body->iseqptr);
|
||||||
|
|
||||||
|
if (0) vm_cref_dump("rb_method_definition_create", cref);
|
||||||
|
|
||||||
|
if (cref) {
|
||||||
|
method_cref = cref;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
method_cref = vm_cref_new_toplevel(GET_EC()); /* TODO: can we reuse? */
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_OBJ_WRITE(me, &def->body.iseq.cref, method_cref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_CFUNC:
|
||||||
|
{
|
||||||
|
rb_method_cfunc_t *cfunc = (rb_method_cfunc_t *)opts;
|
||||||
|
setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), cfunc->func, cfunc->argc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_ATTRSET:
|
||||||
|
case VM_METHOD_TYPE_IVAR:
|
||||||
|
{
|
||||||
|
const rb_execution_context_t *ec = GET_EC();
|
||||||
|
rb_control_frame_t *cfp;
|
||||||
|
int line;
|
||||||
|
|
||||||
|
def->body.attr.id = (ID)(VALUE)opts;
|
||||||
|
|
||||||
|
cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
|
||||||
|
|
||||||
|
if (cfp && (line = rb_vm_get_sourceline(cfp))) {
|
||||||
|
VALUE location = rb_ary_new3(2, rb_iseq_path(cfp->iseq), INT2FIX(line));
|
||||||
|
RB_OBJ_WRITE(me, &def->body.attr.location, rb_ary_freeze(location));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VM_ASSERT(def->body.attr.location == 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_BMETHOD:
|
||||||
|
RB_OBJ_WRITE(me, &def->body.bmethod.proc, (VALUE)opts);
|
||||||
|
return;
|
||||||
|
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
||||||
|
setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), rb_f_notimplement, -1);
|
||||||
|
return;
|
||||||
|
case VM_METHOD_TYPE_OPTIMIZED:
|
||||||
|
def->body.optimize_type = (enum method_optimized_type)opts;
|
||||||
|
return;
|
||||||
|
case VM_METHOD_TYPE_REFINED:
|
||||||
|
{
|
||||||
|
const rb_method_refined_t *refined = (rb_method_refined_t *)opts;
|
||||||
|
RB_OBJ_WRITE(me, &def->body.refined.orig_me, refined->orig_me);
|
||||||
|
RB_OBJ_WRITE(me, &def->body.refined.owner, refined->owner);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_ALIAS:
|
||||||
|
RB_OBJ_WRITE(me, &def->body.alias.original_me, (rb_method_entry_t *)opts);
|
||||||
|
return;
|
||||||
|
case VM_METHOD_TYPE_ZSUPER:
|
||||||
|
case VM_METHOD_TYPE_UNDEF:
|
||||||
|
case VM_METHOD_TYPE_MISSING:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
method_definition_reset(const rb_method_entry_t *me)
|
method_definition_reset(const rb_method_entry_t *me)
|
||||||
{
|
{
|
||||||
const rb_method_definition_t *def = me->def;
|
rb_method_definition_t *def = me->def;
|
||||||
|
|
||||||
switch(def->type) {
|
switch(def->type) {
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
case VM_METHOD_TYPE_ISEQ:
|
||||||
|
@ -247,218 +341,14 @@ method_definition_reset(const rb_method_entry_t *me)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static rb_cref_t*
|
MJIT_FUNC_EXPORTED rb_method_definition_t *
|
||||||
the_top_cref(void)
|
rb_method_definition_create(rb_method_type_t type, ID mid)
|
||||||
{
|
{
|
||||||
static rb_cref_t *top = NULL;
|
rb_method_definition_t *def;
|
||||||
if (!top) {
|
def = ZALLOC(rb_method_definition_t);
|
||||||
top = vm_cref_new_toplevel(GET_EC());
|
def->type = type;
|
||||||
rb_gc_register_mark_object((VALUE)top); // cref is an IMEMO.
|
def->original_id = mid;
|
||||||
}
|
return def;
|
||||||
return top;
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_iseq_t
|
|
||||||
the_method_iseq(const rb_method_iseq_t *p)
|
|
||||||
{
|
|
||||||
return (rb_method_iseq_t) {
|
|
||||||
.iseqptr = p->iseqptr,
|
|
||||||
.cref = p->cref ? p->cref : the_top_cref(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_cfunc_t
|
|
||||||
the_method_cfunc(const rb_method_cfunc_t *p)
|
|
||||||
{
|
|
||||||
return (rb_method_cfunc_t) {
|
|
||||||
.func = p->func,
|
|
||||||
.invoker = call_cfunc_invoker_func(p->argc),
|
|
||||||
.argc = p->argc,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static VALUE
|
|
||||||
the_location(void)
|
|
||||||
{
|
|
||||||
const rb_execution_context_t *ec = GET_EC();
|
|
||||||
const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
|
|
||||||
int line;
|
|
||||||
|
|
||||||
if (!cfp) {
|
|
||||||
return Qfalse;
|
|
||||||
}
|
|
||||||
else if (! (line = rb_vm_get_sourceline(cfp))) {
|
|
||||||
return Qfalse;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
VALUE loc = rb_ary_new3(2, rb_iseq_path(cfp->iseq), INT2FIX(line));
|
|
||||||
rb_ary_freeze(loc);
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_attr_t
|
|
||||||
the_method_attr(const void *p)
|
|
||||||
{
|
|
||||||
return (rb_method_attr_t) {
|
|
||||||
.id = (ID)(VALUE)p,
|
|
||||||
.location = the_location(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_bmethod_t
|
|
||||||
the_method_bmethod(const void *p)
|
|
||||||
{
|
|
||||||
return (rb_method_bmethod_t) {
|
|
||||||
.proc = (VALUE)p,
|
|
||||||
.hooks = NULL,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_cfunc_t
|
|
||||||
the_method_notimplemented(void)
|
|
||||||
{
|
|
||||||
return (rb_method_cfunc_t) {
|
|
||||||
.func = rb_f_notimplement,
|
|
||||||
.invoker = call_cfunc_m1,
|
|
||||||
.argc = -1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum method_optimized_type
|
|
||||||
the_method_optimized(const void *p)
|
|
||||||
{
|
|
||||||
return (enum method_optimized_type)p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_refined_t
|
|
||||||
the_method_refined(const rb_method_refined_t *p)
|
|
||||||
{
|
|
||||||
if (!p) {
|
|
||||||
return (rb_method_refined_t) { 0, };
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (rb_method_refined_t) {
|
|
||||||
.orig_me = p->orig_me,
|
|
||||||
.owner = p->owner,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_alias_t
|
|
||||||
the_method_alias(const rb_method_entry_t *p)
|
|
||||||
{
|
|
||||||
return (rb_method_alias_t) {
|
|
||||||
.original_me = p,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static rb_method_definition_t
|
|
||||||
rb_method_definition_new(rb_method_type_t type, ID mid, const void *opts)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case VM_METHOD_TYPE_ISEQ:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.iseq = the_method_iseq(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_CFUNC:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.cfunc = the_method_cfunc(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_ATTRSET:
|
|
||||||
case VM_METHOD_TYPE_IVAR:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.attr = the_method_attr(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_BMETHOD:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.bmethod = the_method_bmethod(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.cfunc = the_method_notimplemented(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_OPTIMIZED:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.optimize_type = the_method_optimized(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_REFINED:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.refined = the_method_refined(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_ALIAS:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
.body = {
|
|
||||||
.alias = the_method_alias(opts),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case VM_METHOD_TYPE_ZSUPER:
|
|
||||||
case VM_METHOD_TYPE_UNDEF:
|
|
||||||
case VM_METHOD_TYPE_MISSING:
|
|
||||||
return (rb_method_definition_t) {
|
|
||||||
.type = type,
|
|
||||||
.original_id = mid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
UNREACHABLE_RETURN((rb_method_definition_t){0});
|
|
||||||
}
|
|
||||||
|
|
||||||
MJIT_FUNC_EXPORTED void
|
|
||||||
rb_method_entry_spoof(const rb_method_entry_t *me)
|
|
||||||
{
|
|
||||||
VALUE v = (VALUE)me;
|
|
||||||
VALUE o = me->owner;
|
|
||||||
rb_id_table_insert(RCLASS_M_TBL(o), me->called_id, v);
|
|
||||||
RB_OBJ_WRITTEN(o, Qundef, v);
|
|
||||||
rb_clear_method_cache_by_class(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const rb_method_definition_t *
|
|
||||||
rb_method_definition_create(rb_method_type_t type, ID mid, const void *opts)
|
|
||||||
{
|
|
||||||
rb_method_definition_t template = rb_method_definition_new(type, mid, opts);
|
|
||||||
void *ptr = ALLOC(rb_method_definition_t);
|
|
||||||
memcpy(ptr, &template, sizeof template);
|
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static rb_method_definition_t *
|
static rb_method_definition_t *
|
||||||
|
@ -477,15 +367,10 @@ method_definition_addref_complement(rb_method_definition_t *def)
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const rb_method_entry_t *
|
static rb_method_entry_t *
|
||||||
rb_method_entry_alloc(VALUE flags, ID called_id, VALUE owner, VALUE defined_class, const rb_method_definition_t *def)
|
rb_method_entry_alloc(ID called_id, VALUE owner, VALUE defined_class, const rb_method_definition_t *def)
|
||||||
{
|
{
|
||||||
VALUE v = rb_imemo_new(imemo_ment, (VALUE)def, (VALUE)called_id, owner, defined_class);
|
rb_method_entry_t *me = (rb_method_entry_t *)rb_imemo_new(imemo_ment, (VALUE)def, (VALUE)called_id, owner, defined_class);
|
||||||
RBASIC(v)->flags =
|
|
||||||
(RBASIC(v)->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
|
|
||||||
(flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2));
|
|
||||||
const rb_method_entry_t *me = (void *)v;
|
|
||||||
method_definition_reset(me);
|
|
||||||
return me;
|
return me;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,58 +388,33 @@ filter_defined_class(VALUE klass)
|
||||||
rb_bug("filter_defined_class: %s", rb_obj_info(klass));
|
rb_bug("filter_defined_class: %s", rb_obj_info(klass));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
rb_method_entry_t *
|
||||||
method_entry_flags(rb_method_visibility_t visi)
|
rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def)
|
||||||
{
|
{
|
||||||
return (visi << (IMEMO_FL_USHIFT+0)) |
|
rb_method_entry_t *me = rb_method_entry_alloc(called_id, klass, filter_defined_class(klass), def);
|
||||||
((!ruby_running) << (IMEMO_FL_USHIFT+2));
|
METHOD_ENTRY_FLAGS_SET(me, visi, ruby_running ? FALSE : TRUE);
|
||||||
}
|
if (def != NULL) method_definition_reset(me);
|
||||||
|
return me;
|
||||||
MJIT_FUNC_EXPORTED const rb_method_entry_t *
|
|
||||||
rb_method_entry_from_template(
|
|
||||||
const rb_method_entry_t *me,
|
|
||||||
const void *body)
|
|
||||||
{
|
|
||||||
return rb_method_entry_alloc(
|
|
||||||
me->flags,
|
|
||||||
me->called_id,
|
|
||||||
me->owner,
|
|
||||||
me->defined_class,
|
|
||||||
rb_method_definition_create(
|
|
||||||
me->def->type,
|
|
||||||
me->def->original_id,
|
|
||||||
body));
|
|
||||||
}
|
|
||||||
|
|
||||||
const rb_method_entry_t *
|
|
||||||
rb_method_entry_for_missing(ID mid, VALUE klass)
|
|
||||||
{
|
|
||||||
return rb_method_entry_alloc(
|
|
||||||
method_entry_flags(METHOD_VISI_UNDEF),
|
|
||||||
mid,
|
|
||||||
klass,
|
|
||||||
filter_defined_class(klass),
|
|
||||||
rb_method_definition_create(
|
|
||||||
VM_METHOD_TYPE_MISSING,
|
|
||||||
mid,
|
|
||||||
NULL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rb_method_entry_t *
|
const rb_method_entry_t *
|
||||||
rb_method_entry_clone(const rb_method_entry_t *src_me)
|
rb_method_entry_clone(const rb_method_entry_t *src_me)
|
||||||
{
|
{
|
||||||
return rb_method_entry_alloc(
|
rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class,
|
||||||
src_me->flags,
|
method_definition_addref(src_me->def));
|
||||||
src_me->called_id,
|
METHOD_ENTRY_FLAGS_COPY(me, src_me);
|
||||||
src_me->owner,
|
return me;
|
||||||
src_me->defined_class,
|
|
||||||
method_definition_addref(src_me->def));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
|
MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
|
||||||
rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class)
|
rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class)
|
||||||
{
|
{
|
||||||
const rb_method_definition_t *def = src_me->def;
|
rb_method_definition_t *def = src_me->def;
|
||||||
|
rb_method_entry_t *me;
|
||||||
|
struct {
|
||||||
|
const struct rb_method_entry_struct *orig_me;
|
||||||
|
VALUE owner;
|
||||||
|
} refined = {0};
|
||||||
|
|
||||||
if (!src_me->defined_class &&
|
if (!src_me->defined_class &&
|
||||||
def->type == VM_METHOD_TYPE_REFINED &&
|
def->type == VM_METHOD_TYPE_REFINED &&
|
||||||
|
@ -562,69 +422,73 @@ rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID cal
|
||||||
const rb_method_entry_t *orig_me =
|
const rb_method_entry_t *orig_me =
|
||||||
rb_method_entry_clone(def->body.refined.orig_me);
|
rb_method_entry_clone(def->body.refined.orig_me);
|
||||||
RB_OBJ_WRITE((VALUE)orig_me, &orig_me->defined_class, defined_class);
|
RB_OBJ_WRITE((VALUE)orig_me, &orig_me->defined_class, defined_class);
|
||||||
def = rb_method_definition_create(
|
refined.orig_me = orig_me;
|
||||||
VM_METHOD_TYPE_REFINED,
|
refined.owner = orig_me->owner;
|
||||||
called_id,
|
def = NULL;
|
||||||
&(rb_method_refined_t) {
|
|
||||||
.orig_me = orig_me,
|
|
||||||
.owner = orig_me->owner});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
def = method_definition_addref_complement((rb_method_definition_t *)def);
|
def = method_definition_addref_complement(def);
|
||||||
|
}
|
||||||
|
me = rb_method_entry_alloc(called_id, src_me->owner, defined_class, def);
|
||||||
|
METHOD_ENTRY_FLAGS_COPY(me, src_me);
|
||||||
|
METHOD_ENTRY_COMPLEMENTED_SET(me);
|
||||||
|
if (!def) {
|
||||||
|
def = rb_method_definition_create(VM_METHOD_TYPE_REFINED, called_id);
|
||||||
|
rb_method_definition_set(me, def, &refined);
|
||||||
}
|
}
|
||||||
const rb_method_entry_t *me =
|
|
||||||
rb_method_entry_alloc(
|
|
||||||
src_me->flags,
|
|
||||||
called_id,
|
|
||||||
src_me->owner,
|
|
||||||
defined_class,
|
|
||||||
def);
|
|
||||||
|
|
||||||
VM_ASSERT(RB_TYPE_P(me->owner, T_MODULE));
|
VM_ASSERT(RB_TYPE_P(me->owner, T_MODULE));
|
||||||
|
|
||||||
return (const rb_callable_method_entry_t *)me;
|
return (rb_callable_method_entry_t *)me;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const rb_method_entry_t*
|
void
|
||||||
make_method_entry_refined(VALUE owner, const rb_method_entry_t *me)
|
rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src)
|
||||||
|
{
|
||||||
|
*(rb_method_definition_t **)&dst->def = method_definition_addref(src->def);
|
||||||
|
method_definition_reset(dst);
|
||||||
|
dst->called_id = src->called_id;
|
||||||
|
RB_OBJ_WRITE((VALUE)dst, &dst->owner, src->owner);
|
||||||
|
RB_OBJ_WRITE((VALUE)dst, &dst->defined_class, src->defined_class);
|
||||||
|
METHOD_ENTRY_FLAGS_COPY(dst, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
make_method_entry_refined(VALUE owner, rb_method_entry_t *me)
|
||||||
{
|
{
|
||||||
if (me->def->type == VM_METHOD_TYPE_REFINED) {
|
if (me->def->type == VM_METHOD_TYPE_REFINED) {
|
||||||
return me;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
struct {
|
||||||
|
struct rb_method_entry_struct *orig_me;
|
||||||
|
VALUE owner;
|
||||||
|
} refined;
|
||||||
|
rb_method_definition_t *def;
|
||||||
|
|
||||||
rb_vm_check_redefinition_opt_method(me, me->owner);
|
rb_vm_check_redefinition_opt_method(me, me->owner);
|
||||||
/* :FIXME: can rb_method_entry_from_template be tweaked to also be
|
|
||||||
* applicable here? */
|
refined.orig_me =
|
||||||
return rb_method_entry_alloc(
|
rb_method_entry_alloc(me->called_id, me->owner,
|
||||||
method_entry_flags(METHOD_VISI_PUBLIC),
|
me->defined_class ?
|
||||||
me->called_id,
|
me->defined_class : owner,
|
||||||
me->owner,
|
method_definition_addref(me->def));
|
||||||
me->defined_class,
|
METHOD_ENTRY_FLAGS_COPY(refined.orig_me, me);
|
||||||
rb_method_definition_create(
|
refined.owner = owner;
|
||||||
VM_METHOD_TYPE_REFINED,
|
|
||||||
me->called_id,
|
def = rb_method_definition_create(VM_METHOD_TYPE_REFINED, me->called_id);
|
||||||
&(rb_method_refined_t) {
|
rb_method_definition_set(me, def, (void *)&refined);
|
||||||
.owner = owner,
|
METHOD_ENTRY_VISI_SET(me, METHOD_VISI_PUBLIC);
|
||||||
.orig_me =
|
|
||||||
rb_method_entry_alloc(
|
|
||||||
me->flags,
|
|
||||||
me->called_id,
|
|
||||||
me->owner,
|
|
||||||
me->defined_class ?
|
|
||||||
me->defined_class : owner,
|
|
||||||
method_definition_addref(me->def))}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_add_refined_method_entry(VALUE refined_class, ID mid)
|
rb_add_refined_method_entry(VALUE refined_class, ID mid)
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *me = lookup_method_table(refined_class, mid);
|
rb_method_entry_t *me = lookup_method_table(refined_class, mid);
|
||||||
|
|
||||||
if (me) {
|
if (me) {
|
||||||
me = make_method_entry_refined(refined_class, me);
|
make_method_entry_refined(refined_class, me);
|
||||||
rb_method_entry_spoof(me);
|
|
||||||
rb_clear_method_cache_by_class(refined_class);
|
rb_clear_method_cache_by_class(refined_class);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -654,10 +518,11 @@ check_override_opt_method(VALUE klass, VALUE arg)
|
||||||
* If def is given (!= NULL), then just use it and ignore original_id and otps.
|
* If def is given (!= NULL), then just use it and ignore original_id and otps.
|
||||||
* If not given, then make a new def with original_id and opts.
|
* If not given, then make a new def with original_id and opts.
|
||||||
*/
|
*/
|
||||||
static const rb_method_entry_t *
|
static rb_method_entry_t *
|
||||||
rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibility_t visi,
|
rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibility_t visi,
|
||||||
rb_method_type_t type, const rb_method_definition_t *def, ID original_id, void *opts)
|
rb_method_type_t type, rb_method_definition_t *def, ID original_id, void *opts)
|
||||||
{
|
{
|
||||||
|
rb_method_entry_t *me;
|
||||||
struct rb_id_table *mtbl;
|
struct rb_id_table *mtbl;
|
||||||
st_data_t data;
|
st_data_t data;
|
||||||
int make_refined = 0;
|
int make_refined = 0;
|
||||||
|
@ -685,7 +550,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
|
||||||
rb_add_refined_method_entry(refined_class, mid);
|
rb_add_refined_method_entry(refined_class, mid);
|
||||||
}
|
}
|
||||||
if (type == VM_METHOD_TYPE_REFINED) {
|
if (type == VM_METHOD_TYPE_REFINED) {
|
||||||
const rb_method_entry_t *old_me = lookup_method_table(RCLASS_ORIGIN(klass), mid);
|
rb_method_entry_t *old_me = lookup_method_table(RCLASS_ORIGIN(klass), mid);
|
||||||
if (old_me) rb_vm_check_redefinition_opt_method(old_me, klass);
|
if (old_me) rb_vm_check_redefinition_opt_method(old_me, klass);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -695,8 +560,8 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
|
||||||
|
|
||||||
/* check re-definition */
|
/* check re-definition */
|
||||||
if (rb_id_table_lookup(mtbl, mid, &data)) {
|
if (rb_id_table_lookup(mtbl, mid, &data)) {
|
||||||
const rb_method_entry_t *old_me = (const rb_method_entry_t *)data;
|
rb_method_entry_t *old_me = (rb_method_entry_t *)data;
|
||||||
const rb_method_definition_t *old_def = old_me->def;
|
rb_method_definition_t *old_def = old_me->def;
|
||||||
|
|
||||||
if (rb_method_definition_eq(old_def, def)) return old_me;
|
if (rb_method_definition_eq(old_def, def)) return old_me;
|
||||||
rb_vm_check_redefinition_opt_method(old_me, klass);
|
rb_vm_check_redefinition_opt_method(old_me, klass);
|
||||||
|
@ -733,16 +598,9 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create method entry */
|
/* create method entry */
|
||||||
if (!def) {
|
me = rb_method_entry_create(mid, defined_class, visi, NULL);
|
||||||
def = rb_method_definition_create(type, original_id, opts);
|
if (def == NULL) def = rb_method_definition_create(type, original_id);
|
||||||
}
|
rb_method_definition_set(me, def, opts);
|
||||||
const rb_method_entry_t *me =
|
|
||||||
rb_method_entry_alloc(
|
|
||||||
method_entry_flags(visi),
|
|
||||||
mid,
|
|
||||||
defined_class,
|
|
||||||
filter_defined_class(defined_class),
|
|
||||||
def);
|
|
||||||
|
|
||||||
rb_clear_method_cache_by_class(klass);
|
rb_clear_method_cache_by_class(klass);
|
||||||
|
|
||||||
|
@ -764,7 +622,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
|
||||||
}
|
}
|
||||||
|
|
||||||
if (make_refined) {
|
if (make_refined) {
|
||||||
me = make_method_entry_refined(klass, me);
|
make_method_entry_refined(klass, me);
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_id_table_insert(mtbl, mid, (VALUE)me);
|
rb_id_table_insert(mtbl, mid, (VALUE)me);
|
||||||
|
@ -812,21 +670,27 @@ rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_
|
||||||
MJIT_FUNC_EXPORTED void
|
MJIT_FUNC_EXPORTED void
|
||||||
rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi)
|
rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi)
|
||||||
{
|
{
|
||||||
rb_method_iseq_t iseq_body = { iseq, cref };
|
struct { /* should be same fields with rb_method_iseq_struct */
|
||||||
|
const rb_iseq_t *iseqptr;
|
||||||
|
rb_cref_t *cref;
|
||||||
|
} iseq_body;
|
||||||
|
|
||||||
|
iseq_body.iseqptr = iseq;
|
||||||
|
iseq_body.cref = cref;
|
||||||
rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, &iseq_body, visi);
|
rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, &iseq_body, visi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const rb_method_entry_t *
|
static rb_method_entry_t *
|
||||||
method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me,
|
method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me,
|
||||||
rb_method_visibility_t visi, VALUE defined_class)
|
rb_method_visibility_t visi, VALUE defined_class)
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *newme = rb_method_entry_make(klass, mid, defined_class, visi,
|
rb_method_entry_t *newme = rb_method_entry_make(klass, mid, defined_class, visi,
|
||||||
me->def->type, method_definition_addref(me->def), 0, NULL);
|
me->def->type, method_definition_addref(me->def), 0, NULL);
|
||||||
method_added(klass, mid);
|
method_added(klass, mid);
|
||||||
return newme;
|
return newme;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rb_method_entry_t *
|
rb_method_entry_t *
|
||||||
rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me, rb_method_visibility_t visi)
|
rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me, rb_method_visibility_t visi)
|
||||||
{
|
{
|
||||||
return method_entry_set(klass, mid, me, visi, klass);
|
return method_entry_set(klass, mid, me, visi, klass);
|
||||||
|
@ -860,10 +724,10 @@ rb_get_alloc_func(VALUE klass)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const rb_method_entry_t*
|
static inline rb_method_entry_t*
|
||||||
search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
|
search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
|
|
||||||
for (; klass; klass = RCLASS_SUPER(klass)) {
|
for (; klass; klass = RCLASS_SUPER(klass)) {
|
||||||
RB_DEBUG_COUNTER_INC(mc_search_super);
|
RB_DEBUG_COUNTER_INC(mc_search_super);
|
||||||
|
@ -887,12 +751,12 @@ rb_method_entry_at(VALUE klass, ID id)
|
||||||
* if you need method entry with method cache (normal case), use
|
* if you need method entry with method cache (normal case), use
|
||||||
* rb_method_entry() simply.
|
* rb_method_entry() simply.
|
||||||
*/
|
*/
|
||||||
static const rb_method_entry_t *
|
static rb_method_entry_t *
|
||||||
method_entry_get_without_cache(VALUE klass, ID id,
|
method_entry_get_without_cache(VALUE klass, ID id,
|
||||||
VALUE *defined_class_ptr)
|
VALUE *defined_class_ptr)
|
||||||
{
|
{
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
const rb_method_entry_t *me = search_method(klass, id, &defined_class);
|
rb_method_entry_t *me = search_method(klass, id, &defined_class);
|
||||||
|
|
||||||
if (ruby_running) {
|
if (ruby_running) {
|
||||||
if (OPT_GLOBAL_METHOD_CACHE) {
|
if (OPT_GLOBAL_METHOD_CACHE) {
|
||||||
|
@ -924,11 +788,11 @@ method_entry_get_without_cache(VALUE klass, ID id,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
verify_method_cache(VALUE klass, ID id, VALUE defined_class, const rb_method_entry_t *me)
|
verify_method_cache(VALUE klass, ID id, VALUE defined_class, rb_method_entry_t *me)
|
||||||
{
|
{
|
||||||
if (!VM_DEBUG_VERIFY_METHOD_CACHE) return;
|
if (!VM_DEBUG_VERIFY_METHOD_CACHE) return;
|
||||||
VALUE actual_defined_class;
|
VALUE actual_defined_class;
|
||||||
const rb_method_entry_t *actual_me =
|
rb_method_entry_t *actual_me =
|
||||||
method_entry_get_without_cache(klass, id, &actual_defined_class);
|
method_entry_get_without_cache(klass, id, &actual_defined_class);
|
||||||
|
|
||||||
if (me != actual_me || defined_class != actual_defined_class) {
|
if (me != actual_me || defined_class != actual_defined_class) {
|
||||||
|
@ -936,7 +800,7 @@ verify_method_cache(VALUE klass, ID id, VALUE defined_class, const rb_method_ent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const rb_method_entry_t *
|
static rb_method_entry_t *
|
||||||
method_entry_get(VALUE klass, ID id, VALUE *defined_class_ptr)
|
method_entry_get(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
{
|
{
|
||||||
struct cache_entry *ent;
|
struct cache_entry *ent;
|
||||||
|
@ -977,7 +841,7 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_
|
||||||
|
|
||||||
if (mtbl && rb_id_table_lookup(mtbl, id, (VALUE *)&me)) {
|
if (mtbl && rb_id_table_lookup(mtbl, id, (VALUE *)&me)) {
|
||||||
RB_DEBUG_COUNTER_INC(mc_cme_complement_hit);
|
RB_DEBUG_COUNTER_INC(mc_cme_complement_hit);
|
||||||
cme = (const rb_callable_method_entry_t *)me;
|
cme = (rb_callable_method_entry_t *)me;
|
||||||
VM_ASSERT(callable_method_entry_p(cme));
|
VM_ASSERT(callable_method_entry_p(cme));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1001,7 +865,7 @@ MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
|
||||||
rb_callable_method_entry(VALUE klass, ID id)
|
rb_callable_method_entry(VALUE klass, ID id)
|
||||||
{
|
{
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
const rb_method_entry_t *me = method_entry_get(klass, id, &defined_class);
|
rb_method_entry_t *me = method_entry_get(klass, id, &defined_class);
|
||||||
return prepare_callable_method_entry(defined_class, id, me);
|
return prepare_callable_method_entry(defined_class, id, me);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,7 +979,7 @@ static void
|
||||||
remove_method(VALUE klass, ID mid)
|
remove_method(VALUE klass, ID mid)
|
||||||
{
|
{
|
||||||
VALUE data;
|
VALUE data;
|
||||||
const rb_method_entry_t *me = NULL;
|
rb_method_entry_t *me = 0;
|
||||||
VALUE self = klass;
|
VALUE self = klass;
|
||||||
|
|
||||||
klass = RCLASS_ORIGIN(klass);
|
klass = RCLASS_ORIGIN(klass);
|
||||||
|
@ -1125,7 +989,7 @@ remove_method(VALUE klass, ID mid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rb_id_table_lookup(RCLASS_M_TBL(klass), mid, &data) ||
|
if (!rb_id_table_lookup(RCLASS_M_TBL(klass), mid, &data) ||
|
||||||
!(me = (const rb_method_entry_t *)data) ||
|
!(me = (rb_method_entry_t *)data) ||
|
||||||
(!me->def || me->def->type == VM_METHOD_TYPE_UNDEF) ||
|
(!me->def || me->def->type == VM_METHOD_TYPE_UNDEF) ||
|
||||||
UNDEFINED_REFINED_METHOD_P(me->def)) {
|
UNDEFINED_REFINED_METHOD_P(me->def)) {
|
||||||
rb_name_err_raise("method `%1$s' not defined in %2$s",
|
rb_name_err_raise("method `%1$s' not defined in %2$s",
|
||||||
|
@ -1183,18 +1047,10 @@ rb_mod_remove_method(int argc, VALUE *argv, VALUE mod)
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
METHOD_ENTRY_VISI_SET(rb_method_entry_t *me, rb_method_visibility_t visi)
|
|
||||||
{
|
|
||||||
VM_ASSERT((int)visi >= 0 && visi <= 3);
|
|
||||||
VALUE flags = (me->flags & ~(IMEMO_FL_USER0 | IMEMO_FL_USER1)) | (visi << (IMEMO_FL_USHIFT+0));
|
|
||||||
memcpy((void *)&me->flags, &flags, sizeof flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
|
rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
VALUE origin_class = RCLASS_ORIGIN(klass);
|
VALUE origin_class = RCLASS_ORIGIN(klass);
|
||||||
|
|
||||||
|
@ -1212,7 +1068,7 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
|
||||||
rb_vm_check_redefinition_opt_method(me, klass);
|
rb_vm_check_redefinition_opt_method(me, klass);
|
||||||
|
|
||||||
if (klass == defined_class || origin_class == defined_class) {
|
if (klass == defined_class || origin_class == defined_class) {
|
||||||
METHOD_ENTRY_VISI_SET((rb_method_entry_t *)me, visi);
|
METHOD_ENTRY_VISI_SET(me, visi);
|
||||||
|
|
||||||
if (me->def->type == VM_METHOD_TYPE_REFINED && me->def->body.refined.orig_me) {
|
if (me->def->type == VM_METHOD_TYPE_REFINED && me->def->body.refined.orig_me) {
|
||||||
METHOD_ENTRY_VISI_SET((rb_method_entry_t *)me->def->body.refined.orig_me, visi);
|
METHOD_ENTRY_VISI_SET((rb_method_entry_t *)me->def->body.refined.orig_me, visi);
|
||||||
|
@ -1740,7 +1596,7 @@ rb_alias(VALUE klass, ID alias_name, ID original_name)
|
||||||
method_added(target_klass, alias_name);
|
method_added(target_klass, alias_name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const rb_method_entry_t *alias_me;
|
rb_method_entry_t *alias_me;
|
||||||
|
|
||||||
alias_me = method_entry_set(target_klass, alias_name, orig_me, visi, orig_me->owner);
|
alias_me = method_entry_set(target_klass, alias_name, orig_me, visi, orig_me->owner);
|
||||||
RB_OBJ_WRITE(alias_me, &alias_me->owner, target_klass);
|
RB_OBJ_WRITE(alias_me, &alias_me->owner, target_klass);
|
||||||
|
@ -1931,7 +1787,7 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module)
|
||||||
for (i = 0; i < argc; i++) {
|
for (i = 0; i < argc; i++) {
|
||||||
VALUE v = argv[i];
|
VALUE v = argv[i];
|
||||||
ID name = rb_check_id(&v);
|
ID name = rb_check_id(&v);
|
||||||
const rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче