1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* method.h, vm_core.h: add rb_method_entry_t. Remove nodes around

method management.  This change affect some VM control stack structure.
* vm.c, vm_insnhelper.c, vm_method.c, vm_eval.c: ditto.  and make some
  refactoring.
* insns.def, class.c, eval.c, proc.c, vm_dump.c : ditto.
* vm_core.h, compile.c (iseq_specialized_instruction): remove
  VM_CALL_SEND_BIT.  use another optimization tech for Kernel#send.
* node.h: remove unused node types.
* ext/objspace/objspace.c (count_nodes): ditto.
* gc.c: add mark/free functions for method entry.
* include/ruby/intern.h: remove decl of
  rb_define_notimplement_method_id().  nobody can use it
  because noex is not opend.
* iseq.c (iseq_mark): fix to check ic_method is available.
* iseq.c (rb_iseq_disasm): fix to use rb_method_get_iseq().



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@24128 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2009-07-15 14:59:41 +00:00
parent d3cbda6e8d
commit c330876d7c
19 changed files with 886 additions and 789 deletions

View file

@ -1,3 +1,30 @@
Wed Jul 15 23:46:55 2009 Koichi Sasada <ko1@atdot.net>
* method.h, vm_core.h: add rb_method_entry_t. Remove nodes around
method management. This change affect some VM control stack structure.
* vm.c, vm_insnhelper.c, vm_method.c, vm_eval.c: ditto. and make some
refactoring.
* insns.def, class.c, eval.c, proc.c, vm_dump.c : ditto.
* vm_core.h, compile.c (iseq_specialized_instruction): remove
VM_CALL_SEND_BIT. use another optimization tech for Kernel#send.
* node.h: remove unused node types.
* ext/objspace/objspace.c (count_nodes): ditto.
* gc.c: add mark/free functions for method entry.
* include/ruby/intern.h: remove decl of
rb_define_notimplement_method_id(). nobody can use it
because noex is not opend.
* iseq.c (iseq_mark): fix to check ic_method is available.
* iseq.c (rb_iseq_disasm): fix to use rb_method_get_iseq().
Wed Jul 15 23:45:11 2009 Koichi Sasada <ko1@atdot.net>
* dir.c (push_glob): fix GC problem.

65
class.c
View file

@ -25,7 +25,8 @@
#include "ruby/ruby.h"
#include "ruby/st.h"
#include "node.h"
#include "method.h"
#include "vm_core.h"
#include <ctype.h>
extern st_table *rb_class_tbl;
@ -123,28 +124,19 @@ struct clone_method_data {
VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
static int
clone_method(ID mid, NODE *body, struct clone_method_data *data)
clone_method(ID mid, const rb_method_entry_t *me, struct clone_method_data *data)
{
if (body == 0) {
st_insert(data->tbl, mid, 0);
}
else {
NODE *fbody = body->nd_body->nd_body;
if (nd_type(fbody) == RUBY_VM_METHOD_NODE) {
fbody = NEW_NODE(RUBY_VM_METHOD_NODE, 0,
rb_iseq_clone((VALUE)fbody->nd_body, data->klass),
0);
}
st_insert(data->tbl, mid,
(st_data_t)
NEW_NODE_LONGLIFE(
NODE_FBODY,
0,
NEW_NODE_LONGLIFE(NODE_METHOD,
rb_gc_write_barrier(data->klass), /* TODO */
rb_gc_write_barrier((VALUE)fbody),
body->nd_body->nd_noex), 0));
switch (me->type) {
case VM_METHOD_TYPE_ISEQ: {
VALUE newiseqval = rb_iseq_clone(me->body.iseq->self, data->klass);
rb_iseq_t *iseq;
GetISeqPtr(newiseqval, iseq);
rb_add_method(data->klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag);
break;
}
default:
rb_add_method_me(data->klass, mid, me, me->flag);
break;
}
return ST_CONTINUE;
}
@ -674,7 +666,7 @@ ins_methods_pub_i(ID name, long type, VALUE ary)
}
static int
method_entry(ID key, NODE *body, st_table *list)
method_entry(ID key, const rb_method_entry_t *me, st_table *list)
{
long type;
@ -683,11 +675,11 @@ method_entry(ID key, NODE *body, st_table *list)
}
if (!st_lookup(list, key, 0)) {
if (body ==0 || !body->nd_body->nd_body) {
if (!me || me->type == VM_METHOD_TYPE_UNDEF) {
type = -1; /* none */
}
else {
type = VISI(body->nd_body->nd_noex);
type = VISI(me->flag);
}
st_add_direct(list, key, type);
}
@ -874,44 +866,33 @@ rb_obj_singleton_methods(int argc, VALUE *argv, VALUE obj)
}
void
rb_define_method_id(VALUE klass, ID name, VALUE (*func)(ANYARGS), int argc)
rb_define_method_id(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc)
{
if (func == rb_f_notimplement)
rb_define_notimplement_method_id(klass, name, NOEX_PUBLIC);
else
rb_add_method(klass, name, NEW_NODE_LONGLIFE(NODE_CFUNC, func, argc, 0), NOEX_PUBLIC);
rb_add_method_cfunc(klass, mid, func, argc, NOEX_PUBLIC);
}
void
rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
{
rb_define_method_id(klass, rb_intern(name), func, argc);
rb_add_method_cfunc(klass, rb_intern(name), func, argc, NOEX_PUBLIC);
}
void
rb_define_protected_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
{
ID id = rb_intern(name);
if (func == rb_f_notimplement)
rb_define_notimplement_method_id(klass, id, NOEX_PROTECTED);
else
rb_add_method(klass, id, NEW_NODE_LONGLIFE(NODE_CFUNC, func, argc, 0), NOEX_PROTECTED);
rb_add_method_cfunc(klass, rb_intern(name), func, argc, NOEX_PROTECTED);
}
void
rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
{
ID id = rb_intern(name);
if (func == rb_f_notimplement)
rb_define_notimplement_method_id(klass, id, NOEX_PRIVATE);
else
rb_add_method(klass, id, NEW_NODE_LONGLIFE(NODE_CFUNC, func, argc, 0), NOEX_PRIVATE);
rb_add_method_cfunc(klass, rb_intern(name), func, argc, NOEX_PRIVATE);
}
void
rb_undef_method(VALUE klass, const char *name)
{
rb_add_method(klass, rb_intern(name), 0, NOEX_UNDEF);
rb_add_method(klass, rb_intern(name), VM_METHOD_TYPE_UNDEF, 0, NOEX_UNDEF);
}
#define SPECIAL_SINGLETON(x,c) do {\

View file

@ -1855,12 +1855,6 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
}
}
}
if (argc > 0) {
if (mid == idSend || mid == id__send__ ) {
OPERAND_AT(iobj, 3) |= INT2FIX(VM_CALL_SEND_BIT);
}
}
}
return COMPILE_OK;
}

2
eval.c
View file

@ -684,7 +684,7 @@ frame_func_id(rb_control_frame_t *cfp)
{
rb_iseq_t *iseq = cfp->iseq;
if (!iseq) {
return cfp->method_id;
return cfp->me->original_id;
}
while (iseq) {
if (RUBY_VM_IFUNC_P(iseq)) {

View file

@ -350,9 +350,6 @@ count_nodes(int argc, VALUE *argv, VALUE os)
VALUE node;
switch (i) {
#define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); break;
COUNT_NODE(NODE_METHOD);
COUNT_NODE(NODE_FBODY);
COUNT_NODE(NODE_CFUNC);
COUNT_NODE(NODE_SCOPE);
COUNT_NODE(NODE_BLOCK);
COUNT_NODE(NODE_IF);
@ -441,7 +438,6 @@ count_nodes(int argc, VALUE *argv, VALUE os)
COUNT_NODE(NODE_DOT3);
COUNT_NODE(NODE_FLIP2);
COUNT_NODE(NODE_FLIP3);
COUNT_NODE(NODE_ATTRSET);
COUNT_NODE(NODE_SELF);
COUNT_NODE(NODE_NIL);
COUNT_NODE(NODE_TRUE);

72
gc.c
View file

@ -1435,12 +1435,6 @@ mark_tbl(rb_objspace_t *objspace, st_table *tbl, int lev)
st_foreach(tbl, mark_entry, (st_data_t)&arg);
}
void
rb_mark_tbl(st_table *tbl)
{
mark_tbl(&rb_objspace, tbl, 0);
}
static int
mark_key(VALUE key, VALUE value, st_data_t data)
{
@ -1490,6 +1484,64 @@ rb_mark_hash(st_table *tbl)
mark_hash(&rb_objspace, tbl, 0);
}
static void
mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me, int lev)
{
gc_mark(objspace, me->klass, lev);
switch (me->type) {
case VM_METHOD_TYPE_ISEQ:
gc_mark(objspace, me->body.iseq->self, lev);
break;
case VM_METHOD_TYPE_BMETHOD:
gc_mark(objspace, me->body.proc, lev);
break;
default:
break; /* ignore */
}
}
void
rb_gc_mark_method_entry(const rb_method_entry_t *me)
{
mark_method_entry(&rb_objspace, me, 0);
}
static int
mark_method_entry_i(ID key, const rb_method_entry_t *me, st_data_t data)
{
struct mark_tbl_arg *arg = (void*)data;
mark_method_entry(arg->objspace, me, arg->lev);
return ST_CONTINUE;
}
static void
mark_m_tbl(rb_objspace_t *objspace, st_table *tbl, int lev) {
struct mark_tbl_arg arg;
if (!tbl) return;
arg.objspace = objspace;
arg.lev = lev;
st_foreach(tbl, mark_method_entry_i, (st_data_t)&arg);
}
static int
free_method_entry_i(ID key, rb_method_entry_t *me, st_data_t data)
{
xfree(me);
return ST_CONTINUE;
}
static void
free_m_table(st_table *tbl)
{
st_foreach(tbl, free_method_entry_i, 0);
}
void
rb_mark_tbl(st_table *tbl)
{
mark_tbl(&rb_objspace, tbl, 0);
}
void
rb_gc_mark_maybe(VALUE obj)
{
@ -1591,7 +1643,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
ptr = (VALUE)obj->as.node.u3.node;
goto again;
case NODE_METHOD: /* 1,2 */
case NODE_WHILE:
case NODE_UNTIL:
case NODE_AND:
@ -1612,7 +1663,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
case NODE_ARGSCAT:
gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev);
/* fall through */
case NODE_FBODY: /* 2 */
case NODE_GASGN:
case NODE_LASGN:
case NODE_DASGN:
@ -1653,7 +1703,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
case NODE_ZARRAY: /* - */
case NODE_ZSUPER:
case NODE_CFUNC:
case NODE_VCALL:
case NODE_GVAR:
case NODE_LVAR:
@ -1669,7 +1718,6 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
case NODE_TRUE:
case NODE_FALSE:
case NODE_ERRINFO:
case NODE_ATTRSET:
case NODE_BLOCK_ARG:
break;
case NODE_ALLOCA:
@ -1698,7 +1746,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
case T_ICLASS:
case T_CLASS:
case T_MODULE:
mark_tbl(objspace, RCLASS_M_TBL(obj), lev);
mark_m_tbl(objspace, RCLASS_M_TBL(obj), lev);
mark_tbl(objspace, RCLASS_IV_TBL(obj), lev);
ptr = RCLASS_SUPER(obj);
goto again;
@ -2072,7 +2120,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
case T_MODULE:
case T_CLASS:
rb_clear_cache_by_class((VALUE)obj);
st_free_table(RCLASS_M_TBL(obj));
free_m_table(RCLASS_M_TBL(obj));
if (RCLASS_IV_TBL(obj)) {
st_free_table(RCLASS_IV_TBL(obj));
}

View file

@ -275,7 +275,6 @@ int rb_method_basic_definition_p(VALUE, ID);
VALUE rb_eval_cmd(VALUE, VALUE, int);
int rb_obj_respond_to(VALUE, ID, int);
int rb_respond_to(VALUE, ID);
void rb_define_notimplement_method_id(VALUE mod, ID id, int noex);
VALUE rb_f_notimplement(int argc, VALUE *argv, VALUE obj);
void rb_interrupt(void);
VALUE rb_apply(VALUE, ID, VALUE);

View file

@ -785,11 +785,11 @@ defined
break;
case DEFINED_METHOD:{
VALUE klass = CLASS_OF(v);
NODE *method = (NODE *) rb_method_node(klass, SYM2ID(obj));
const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj));
if (method) {
if (!(method->nd_noex & NOEX_PRIVATE)) {
if (!((method->nd_noex & NOEX_PROTECTED) &&
if (me) {
if (!(me->flag & NOEX_PRIVATE)) {
if (!((me->flag & NOEX_PROTECTED) &&
!rb_obj_is_kind_of(GET_SELF(),
rb_class_real(klass)))) {
expr_type = "method";
@ -979,7 +979,7 @@ send
(...)
(VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0));
{
NODE *mn;
const rb_method_entry_t *me;
VALUE recv, klass;
rb_block_t *blockptr = 0;
rb_num_t num = caller_setup_args(th, GET_CFP(), op_flag, (int)op_argc,
@ -990,14 +990,8 @@ send
/* get receiver */
recv = (flag & VM_CALL_FCALL_BIT) ? GET_SELF() : TOPN(num);
klass = CLASS_OF(recv);
mn = vm_method_search(id, klass, ic);
/* send/funcall optimization */
if (flag & VM_CALL_SEND_BIT) {
vm_send_optimize(GET_CFP(), &mn, &flag, &num, &id, klass);
}
CALL_METHOD(num, blockptr, flag, id, mn, recv);
me = vm_method_search(id, klass, ic);
CALL_METHOD(num, blockptr, flag, id, me, recv);
}
/**
@ -1017,15 +1011,15 @@ invokesuper
rb_num_t num = caller_setup_args(th, GET_CFP(), op_flag,
(int)op_argc, blockiseq, &blockptr);
VALUE recv, klass;
NODE *mn;
ID id;
const VALUE flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT;
VALUE flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT;
const rb_method_entry_t *me;
recv = GET_SELF();
vm_search_superclass(GET_CFP(), GET_ISEQ(), recv, TOPN(num), &id, &klass);
mn = rb_method_node(klass, id);
me = rb_method_entry(klass, id);
CALL_METHOD(num, blockptr, flag, id, mn, recv);
CALL_METHOD(num, blockptr, flag, id, me, recv);
}
/**
@ -1629,10 +1623,10 @@ opt_neq
(VALUE val)
{
extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
NODE *mn = vm_method_search(idNeq, CLASS_OF(recv), ic1);
const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ic1);
val = Qundef;
if (check_cfunc(mn, rb_obj_not_equal)) {
if (check_cfunc(me, rb_obj_not_equal)) {
val = opt_eq_func(recv, obj, ic2);
if (val != Qundef) {
@ -1997,9 +1991,9 @@ opt_not
(VALUE val)
{
extern VALUE rb_obj_not(VALUE obj);
NODE *mn = vm_method_search(idNot, CLASS_OF(recv), ic);
const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ic);
if (check_cfunc(mn, rb_obj_not)) {
if (check_cfunc(me, rb_obj_not)) {
val = RTEST(recv) ? Qfalse : Qtrue;
}
else {

14
iseq.c
View file

@ -107,6 +107,9 @@ iseq_mark(void *ptr)
for (i=0; i<iseq->ic_size; i++) {
RUBY_MARK_UNLESS_NULL(iseq->ic_entries[i].ic_class);
RUBY_MARK_UNLESS_NULL(iseq->ic_entries[i].ic_value);
if (iseq->ic_entries[i].ic_method) {
rb_gc_mark_method_entry(iseq->ic_entries[i].ic_method);
}
}
if (iseq->compile_data != 0) {
@ -1002,17 +1005,14 @@ rb_iseq_disasm(VALUE self)
static VALUE
iseq_s_disasm(VALUE klass, VALUE body)
{
extern NODE *rb_method_body(VALUE body);
NODE *node;
VALUE ret = Qnil;
rb_iseq_t *iseq;
extern rb_iseq_t *rb_method_get_iseq(VALUE body);
rb_secure(1);
if ((node = rb_method_body(body)) != 0) {
if (nd_type(node) == RUBY_VM_METHOD_NODE) {
VALUE iseqval = (VALUE)node->nd_body;
ret = rb_iseq_disasm(iseqval);
}
if ((iseq = rb_method_get_iseq(body)) != 0) {
ret = rb_iseq_disasm(iseq->self);
}
return ret;

78
method.h Normal file
View file

@ -0,0 +1,78 @@
/**********************************************************************
method.h -
$Author: ko1 $
created at: Wed Jul 15 20:02:33 2009
Copyright (C) 2009 Koichi Sasada
**********************************************************************/
#ifndef METHOD_H
#define METHOD_H
typedef enum {
NOEX_PUBLIC = 0x00,
NOEX_NOSUPER = 0x01,
NOEX_PRIVATE = 0x02,
NOEX_PROTECTED = 0x04,
NOEX_MASK = 0x06,
NOEX_BASIC = 0x08,
NOEX_UNDEF = NOEX_NOSUPER,
NOEX_MODFUNC = 0x12,
NOEX_SUPER = 0x20,
NOEX_VCALL = 0x40,
} rb_method_flag_t;
#define NOEX_SAFE(n) ((int)((n) >> 8) & 0x0F)
#define NOEX_WITH(n, s) ((s << 8) | (n) | (ruby_running ? 0 : NOEX_BASIC))
#define NOEX_WITH_SAFE(n) NOEX_WITH(n, rb_safe_level())
/* method data type */
typedef enum {
VM_METHOD_TYPE_ISEQ,
VM_METHOD_TYPE_CFUNC,
VM_METHOD_TYPE_ATTRSET,
VM_METHOD_TYPE_IVAR,
VM_METHOD_TYPE_BMETHOD,
VM_METHOD_TYPE_ZSUPER,
VM_METHOD_TYPE_UNDEF,
VM_METHOD_TYPE_NOTIMPLEMENTED,
VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
} rb_method_type_t;
typedef struct rb_method_cfunc_struct {
VALUE (*func)(ANYARGS);
int argc;
} rb_method_cfunc_t;
typedef struct rb_iseq_struct rb_iseq_t;
typedef struct rb_method_entry_struct {
rb_method_flag_t flag;
rb_method_type_t type; /* method type */
ID called_id;
ID original_id;
VALUE klass; /* should be mark */
union {
rb_iseq_t *iseq; /* should be mark */
rb_method_cfunc_t cfunc;
ID attr_id;
VALUE proc;
enum method_optimized_type {
OPTIMIZED_METHOD_TYPE_SEND,
OPTIMIZED_METHOD_TYPE_CALL,
} optimize_type;
} body;
int alias_count;
} rb_method_entry_t;
void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex);
rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
void rb_add_method_me(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);
rb_method_entry_t *rb_method_entry(VALUE klass, ID id);
int rb_method_entry_arity(const rb_method_entry_t *me);
void rb_gc_mark_method_entry(const rb_method_entry_t *me);
#endif /* METHOD_H */

47
node.h
View file

@ -20,12 +20,6 @@ extern "C" {
#endif
enum node_type {
NODE_METHOD,
#define NODE_METHOD NODE_METHOD
NODE_FBODY,
#define NODE_FBODY NODE_FBODY
NODE_CFUNC,
#define NODE_CFUNC NODE_CFUNC
NODE_SCOPE,
#define NODE_SCOPE NODE_SCOPE
NODE_BLOCK,
@ -202,8 +196,6 @@ enum node_type {
#define NODE_FLIP2 NODE_FLIP2
NODE_FLIP3,
#define NODE_FLIP3 NODE_FLIP3
NODE_ATTRSET,
#define NODE_ATTRSET NODE_ATTRSET
NODE_SELF,
#define NODE_SELF NODE_SELF
NODE_NIL,
@ -356,11 +348,8 @@ typedef struct RNode {
#define NEW_NODE(t,a0,a1,a2) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
#define NEW_NODE_LONGLIFE(t,a0,a1,a2) rb_node_newnode_longlife((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
#define NEW_METHOD(n,x,v) NEW_NODE(NODE_METHOD,x,n,v)
#define NEW_FBODY(n,i) NEW_NODE(NODE_FBODY,i,n,0)
#define NEW_DEFN(i,a,d,p) NEW_NODE(NODE_DEFN,0,i,NEW_SCOPE(a,d))
#define NEW_DEFS(r,i,a,d) NEW_NODE(NODE_DEFS,r,i,NEW_SCOPE(a,d))
#define NEW_CFUNC(f,c) NEW_NODE(NODE_CFUNC,f,c,0)
#define NEW_IFUNC(f,c) NEW_NODE(NODE_IFUNC,f,c,0)
#define NEW_SCOPE(a,b) NEW_NODE(NODE_SCOPE,local_tbl(),b,a)
#define NEW_BLOCK(a) NEW_NODE(NODE_BLOCK,a,0,0)
@ -446,7 +435,6 @@ typedef struct RNode {
#define NEW_COLON3(i) NEW_NODE(NODE_COLON3,0,i,0)
#define NEW_DOT2(b,e) NEW_NODE(NODE_DOT2,b,e,0)
#define NEW_DOT3(b,e) NEW_NODE(NODE_DOT3,b,e,0)
#define NEW_ATTRSET(a) NEW_NODE(NODE_ATTRSET,a,0,0)
#define NEW_SELF() NEW_NODE(NODE_SELF,0,0,0)
#define NEW_NIL() NEW_NODE(NODE_NIL,0,0,0)
#define NEW_TRUE() NEW_NODE(NODE_TRUE,0,0,0)
@ -460,30 +448,6 @@ typedef struct RNode {
#define NEW_PRELUDE(p,b) NEW_NODE(NODE_PRELUDE,p,b,0)
#define NEW_OPTBLOCK(a) NEW_NODE(NODE_OPTBLOCK,a,0,0)
#define NOEX_PUBLIC 0x00
#define NOEX_NOSUPER 0x01
#define NOEX_PRIVATE 0x02
#define NOEX_PROTECTED 0x04
#define NOEX_MASK 0x06 /* 0110 */
#define NOEX_BASIC 0x08
#define NOEX_UNDEF NOEX_NOSUPER
#define NOEX_MODFUNC 0x12
#define NOEX_SUPER 0x20
#define NOEX_VCALL 0x40
#define NOEX_SAFE(n) ((int)((n) >> 8) & 0x0F)
#define NOEX_WITH(n, s) ((s << 8) | (n) | (ruby_running ? 0 : NOEX_BASIC))
#define NOEX_WITH_SAFE(n) NOEX_WITH(n, rb_safe_level())
#define CALL_PUBLIC 0
#define CALL_FCALL 1
#define CALL_VCALL 2
#define CALL_SUPER 3
#define RUBY_VM_METHOD_NODE NODE_METHOD
VALUE rb_parser_new(void);
VALUE rb_parser_end_seen_p(VALUE);
VALUE rb_parser_encoding(VALUE);
@ -496,20 +460,9 @@ NODE *rb_compile_cstr(const char*, const char*, int, int);
NODE *rb_compile_string(const char*, VALUE, int);
NODE *rb_compile_file(const char*, VALUE, int);
void rb_add_method(VALUE, ID, NODE *, int);
NODE *rb_node_newnode(enum node_type,VALUE,VALUE,VALUE);
NODE *rb_node_newnode_longlife(enum node_type,VALUE,VALUE,VALUE);
NODE* rb_method_node(VALUE klass, ID id);
int rb_node_arity(NODE* node);
int rb_notimplement_body_p(NODE*);
struct global_entry *rb_global_entry(ID);
VALUE rb_gvar_get(struct global_entry *);
VALUE rb_gvar_set(struct global_entry *, VALUE);
VALUE rb_gvar_defined(struct global_entry *);
#if defined(__cplusplus)
#if 0
{ /* satisfy cc-mode */

210
proc.c
View file

@ -13,11 +13,10 @@
#include "gc.h"
struct METHOD {
VALUE oclass; /* class that holds the method */
VALUE rclass; /* class of the receiver */
VALUE recv;
ID id, oid;
NODE *body;
VALUE rclass;
ID id;
rb_method_entry_t *me;
};
VALUE rb_cUnboundMethod;
@ -30,7 +29,7 @@ VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc);
static VALUE bmcall(VALUE, VALUE);
static int method_arity(VALUE);
static int rb_obj_is_method(VALUE m);
static rb_iseq_t *get_method_iseq(VALUE method);
rb_iseq_t *rb_method_get_iseq(VALUE method);
/* Proc */
@ -657,7 +656,7 @@ get_proc_iseq(VALUE self, int *is_proc)
iseq = 0;
if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) {
/* method(:foo).to_proc */
iseq = get_method_iseq(node->nd_tval);
iseq = rb_method_get_iseq(node->nd_tval);
if (is_proc) *is_proc = 0;
}
}
@ -843,9 +842,8 @@ bm_mark(void *ptr)
{
struct METHOD *data = ptr;
rb_gc_mark(data->rclass);
rb_gc_mark(data->oclass);
rb_gc_mark(data->recv);
rb_gc_mark((VALUE)data->body);
rb_gc_mark_method_entry(data->me);
}
static size_t
@ -867,59 +865,47 @@ rb_obj_is_method(VALUE m)
return rb_typeddata_is_kind_of(m, &method_data_type);
}
NODE *
rb_method_body(VALUE method)
{
if (rb_obj_is_method(method)) {
struct METHOD *data = DATA_PTR(method);
return data->body;
}
else {
return 0;
}
}
NODE *rb_get_method_body(VALUE klass, ID id, ID *idp);
static VALUE
mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
{
VALUE method;
NODE *body;
struct METHOD *data;
VALUE rclass = klass;
ID oid = id;
ID rid = id;
struct METHOD *data;
rb_method_entry_t *me;
again:
if ((body = rb_get_method_body(klass, id, 0)) == 0) {
rb_print_undef(rclass, oid, 0);
me = rb_method_entry(klass, id);
if (!me) {
rb_print_undef(klass, id, 0);
}
if (scope && (body->nd_noex & NOEX_MASK) != NOEX_PUBLIC) {
rb_print_undef(rclass, oid, (int)(body->nd_noex & NOEX_MASK));
if (scope && (me->flag & NOEX_MASK) != NOEX_PUBLIC) {
rb_print_undef(rclass, me->original_id, (int)(me->flag & NOEX_MASK));
}
klass = body->nd_clss;
body = body->nd_body;
if (nd_type(body) == NODE_ZSUPER) {
klass = RCLASS_SUPER(klass);
if (me->type == VM_METHOD_TYPE_ZSUPER) {
klass = RCLASS_SUPER(me->klass);
id = me->original_id;
goto again;
}
klass = me->klass;
while (rclass != klass &&
(FL_TEST(rclass, FL_SINGLETON) || TYPE(rclass) == T_ICLASS)) {
rclass = RCLASS_SUPER(rclass);
}
if (TYPE(klass) == T_ICLASS)
klass = RBASIC(klass)->klass;
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
data->oclass = klass;
data->recv = obj;
data->id = id;
data->body = body;
if (TYPE(klass) == T_ICLASS) {
klass = RBASIC(klass)->klass;
}
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
data->recv = obj;
data->rclass = rclass;
data->oid = oid;
data->id = rid;
data->me = me;
OBJ_INFECT(method, klass);
return method;
@ -958,11 +944,11 @@ mnew(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
* object and contain the same body.
*/
static VALUE
method_eq(VALUE method, VALUE other)
{
struct METHOD *m1, *m2;
extern int rb_method_entry_eq(rb_method_entry_t *m1, rb_method_entry_t *m2);
if (!rb_obj_is_method(other))
return Qfalse;
@ -973,9 +959,11 @@ method_eq(VALUE method, VALUE other)
m1 = (struct METHOD *)DATA_PTR(method);
m2 = (struct METHOD *)DATA_PTR(other);
if (m1->oclass != m2->oclass || m1->rclass != m2->rclass ||
m1->recv != m2->recv || m1->body != m2->body)
if (!rb_method_entry_eq(m1->me, m2->me) ||
m1->rclass != m2->rclass ||
m1->recv != m2->recv) {
return Qfalse;
}
return Qtrue;
}
@ -994,10 +982,9 @@ method_hash(VALUE method)
long hash;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, m);
hash = (long)m->oclass;
hash ^= (long)m->rclass;
hash = (long)m->rclass;
hash ^= (long)m->recv;
hash ^= (long)m->body;
hash ^= (long)m->me;
return INT2FIX(hash);
}
@ -1018,14 +1005,12 @@ method_unbind(VALUE obj)
struct METHOD *orig, *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig);
method =
TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD, &method_data_type, data);
data->oclass = orig->oclass;
method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
&method_data_type, data);
data->recv = Qundef;
data->id = orig->id;
data->body = orig->body;
data->me = orig->me;
data->rclass = orig->rclass;
data->oid = orig->oid;
OBJ_INFECT(method, obj);
return method;
@ -1076,7 +1061,7 @@ method_owner(VALUE obj)
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return data->oclass;
return data->me->klass;
}
/*
@ -1205,7 +1190,6 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
{
ID id;
VALUE body;
NODE *node;
int noex = NOEX_PUBLIC;
if (argc == 1) {
@ -1239,7 +1223,7 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
rb_class2name(rclass));
}
}
node = method->body;
rb_add_method_me(mod, id, method->me, noex);
}
else if (rb_obj_is_proc(body)) {
rb_proc_t *proc;
@ -1251,16 +1235,13 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
proc->is_lambda = Qtrue;
proc->is_from_method = Qtrue;
}
node = NEW_BMETHOD(body);
rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)body, noex);
}
else {
/* type error */
rb_raise(rb_eTypeError, "wrong argument type (expected Proc/Method)");
}
/* TODO: visibility */
rb_add_method(mod, id, node, noex);
return body;
}
@ -1351,12 +1332,11 @@ rb_method_call(int argc, VALUE *argv, VALUE method)
}
if ((state = EXEC_TAG()) == 0) {
rb_thread_t *th = GET_THREAD();
VALUE rb_vm_call(rb_thread_t * th, VALUE klass, VALUE recv, VALUE id, ID oid,
int argc, const VALUE *argv, const NODE *body, int nosuper);
VALUE rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, const VALUE *argv,
const rb_method_entry_t *me);
PASS_PASSED_BLOCK_TH(th);
result = rb_vm_call(th, data->oclass, data->recv, data->id, data->oid,
argc, argv, data->body, 0);
result = rb_vm_call(th, data->recv, data->id, argc, argv, data->me);
}
POP_TAG();
if (safe >= 0)
@ -1483,34 +1463,32 @@ umethod_bind(VALUE method, VALUE recv)
}
int
rb_node_arity(NODE* body)
rb_method_entry_arity(const rb_method_entry_t *me)
{
switch (nd_type(body)) {
case NODE_CFUNC:
if (body->nd_argc < 0)
switch (me->type) {
case VM_METHOD_TYPE_CFUNC:
if (me->body.cfunc.argc < 0)
return -1;
return check_argc(body->nd_argc);
case NODE_ZSUPER:
return check_argc(me->body.cfunc.argc);
case VM_METHOD_TYPE_ZSUPER:
return -1;
case NODE_ATTRSET:
case VM_METHOD_TYPE_ATTRSET:
return 1;
case NODE_IVAR:
case VM_METHOD_TYPE_IVAR:
return 0;
case NODE_BMETHOD:
return rb_proc_arity(body->nd_cval);
case RUBY_VM_METHOD_NODE:
{
rb_iseq_t *iseq;
GetISeqPtr((VALUE)body->nd_body, iseq);
if (iseq->arg_rest == -1 && iseq->arg_opts == 0) {
return iseq->argc;
}
else {
return -(iseq->argc + 1 + iseq->arg_post_len);
}
}
case VM_METHOD_TYPE_BMETHOD:
return rb_proc_arity(me->body.proc);
case VM_METHOD_TYPE_ISEQ: {
rb_iseq_t *iseq = me->body.iseq;
if (iseq->arg_rest == -1 && iseq->arg_opts == 0) {
return iseq->argc;
}
else {
return -(iseq->argc + 1 + iseq->arg_post_len);
}
}
default:
rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body));
rb_bug("rb_method_entry_arity: invalid method entry type (%d)", me->type);
}
}
@ -1560,14 +1538,14 @@ method_arity(VALUE method)
struct METHOD *data;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
return rb_node_arity(data->body);
return rb_method_entry_arity(data->me);
}
int
rb_mod_method_arity(VALUE mod, ID id)
{
NODE *node = rb_method_node(mod, id);
return rb_node_arity(node);
rb_method_entry_t *me = rb_method_entry(mod, id);
return rb_method_entry_arity(me);
}
int
@ -1576,25 +1554,23 @@ rb_obj_method_arity(VALUE obj, ID id)
return rb_mod_method_arity(CLASS_OF(obj), id);
}
static rb_iseq_t *
get_method_iseq(VALUE method)
rb_iseq_t *
rb_method_get_iseq(VALUE method)
{
struct METHOD *data;
NODE *body;
rb_iseq_t *iseq;
rb_method_entry_t *me;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
body = data->body;
switch (nd_type(body)) {
case NODE_BMETHOD:
return get_proc_iseq(body->nd_cval, 0);
case RUBY_VM_METHOD_NODE:
GetISeqPtr((VALUE)body->nd_body, iseq);
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) break;
me = data->me;
switch (me->type) {
case VM_METHOD_TYPE_BMETHOD:
return get_proc_iseq(me->body.proc, 0);
case VM_METHOD_TYPE_ISEQ:
return me->body.iseq;
default:
return 0;
}
return iseq;
}
/*
@ -1608,7 +1584,7 @@ get_method_iseq(VALUE method)
VALUE
rb_method_location(VALUE method)
{
return iseq_location(get_method_iseq(method));
return iseq_location(rb_method_get_iseq(method));
}
/*
@ -1621,7 +1597,7 @@ rb_method_location(VALUE method)
static VALUE
rb_method_parameters(VALUE method)
{
rb_iseq_t *iseq = get_method_iseq(method);
rb_iseq_t *iseq = rb_method_get_iseq(method);
if (!iseq) {
return unnamed_parameters(method_arity(method));
}
@ -1652,11 +1628,11 @@ method_inspect(VALUE method)
rb_str_buf_cat2(str, s);
rb_str_buf_cat2(str, ": ");
if (FL_TEST(data->oclass, FL_SINGLETON)) {
VALUE v = rb_iv_get(data->oclass, "__attached__");
if (FL_TEST(data->me->klass, FL_SINGLETON)) {
VALUE v = rb_iv_get(data->me->klass, "__attached__");
if (data->recv == Qundef) {
rb_str_buf_append(str, rb_inspect(data->oclass));
rb_str_buf_append(str, rb_inspect(data->me->klass));
}
else if (data->recv == v) {
rb_str_buf_append(str, rb_inspect(v));
@ -1672,15 +1648,15 @@ method_inspect(VALUE method)
}
else {
rb_str_buf_cat2(str, rb_class2name(data->rclass));
if (data->rclass != data->oclass) {
if (data->rclass != data->me->klass) {
rb_str_buf_cat2(str, "(");
rb_str_buf_cat2(str, rb_class2name(data->oclass));
rb_str_buf_cat2(str, rb_class2name(data->me->klass));
rb_str_buf_cat2(str, ")");
}
}
rb_str_buf_cat2(str, sharp);
rb_str_append(str, rb_id2str(data->oid));
if (rb_notimplement_body_p(data->body)) {
rb_str_append(str, rb_id2str(data->me->original_id));
if (data->me->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
rb_str_buf_cat2(str, " (not-implemented)");
}
rb_str_buf_cat2(str, ">");
@ -1949,10 +1925,22 @@ Init_Proc(void)
rb_cProc = rb_define_class("Proc", rb_cObject);
rb_undef_alloc_func(rb_cProc);
rb_define_singleton_method(rb_cProc, "new", rb_proc_s_new, -1);
#if 0 /* incomplete. */
rb_add_method(rb_cProc, rb_intern("call"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
rb_add_method(rb_cProc, rb_intern("[]"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
rb_add_method(rb_cProc, rb_intern("==="), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
rb_add_method(rb_cProc, rb_intern("yield"), VM_METHOD_TYPE_OPTIMIZED,
(void *)OPTIMIZED_METHOD_TYPE_CALL, 0);
#else
rb_define_method(rb_cProc, "call", proc_call, -1);
rb_define_method(rb_cProc, "[]", proc_call, -1);
rb_define_method(rb_cProc, "===", proc_call, -1);
rb_define_method(rb_cProc, "yield", proc_call, -1);
#endif
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "clone", proc_clone, 0);

37
vm.c
View file

@ -575,7 +575,7 @@ vm_yield(rb_thread_t *th, int argc, const VALUE *argv)
VALUE
rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, rb_block_t * blockptr)
int argc, const VALUE *argv, const rb_block_t * blockptr)
{
VALUE val = Qundef;
int state;
@ -734,7 +734,7 @@ vm_backtrace_each(rb_thread_t *th, int lev, rb_backtrace_iter_func *iter, void *
}
}
else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
if ((*iter)(arg, file, line_no, rb_id2name(cfp->method_id))) break;
if ((*iter)(arg, file, line_no, rb_id2name(cfp->me->original_id))) break;
}
cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
}
@ -929,22 +929,23 @@ rb_iter_break(void)
static st_table *vm_opt_method_table = 0;
static void
rb_vm_check_redefinition_opt_method(const NODE *node)
rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me)
{
VALUE bop;
if (st_lookup(vm_opt_method_table, (st_data_t)node, &bop)) {
ruby_vm_redefined_flag[bop] = 1;
if (me->type == VM_METHOD_TYPE_CFUNC) {
if (st_lookup(vm_opt_method_table, (st_data_t)me, &bop)) {
ruby_vm_redefined_flag[bop] = 1;
}
}
}
static void
add_opt_method(VALUE klass, ID mid, VALUE bop)
{
NODE *node;
if (st_lookup(RCLASS_M_TBL(klass), mid, (void *)&node) &&
nd_type(node->nd_body->nd_body) == NODE_CFUNC) {
st_insert(vm_opt_method_table, (st_data_t)node, (st_data_t)bop);
rb_method_entry_t *me;
if (st_lookup(RCLASS_M_TBL(klass), mid, (void *)&me) &&
me->type == VM_METHOD_TYPE_CFUNC) {
st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop);
}
else {
rb_bug("undefined optimized method: %s", rb_id2name(mid));
@ -1323,8 +1324,8 @@ rb_thread_method_id_and_class(rb_thread_t *th,
rb_control_frame_t *cfp = th->cfp;
rb_iseq_t *iseq = cfp->iseq;
if (!iseq) {
if (idp) *idp = cfp->method_id;
if (klassp) *klassp = cfp->method_class;
if (idp) *idp = cfp->me->original_id;
if (klassp) *klassp = cfp->me->klass;
return 1;
}
while (iseq) {
@ -1367,10 +1368,10 @@ rb_thread_current_status(const rb_thread_t *th)
file, line_no, RSTRING_PTR(iseq->name));
}
}
else if (cfp->method_id) {
else if (cfp->me->original_id) {
str = rb_sprintf("`%s#%s' (cfunc)",
RSTRING_PTR(rb_class_name(cfp->method_class)),
rb_id2name(cfp->method_id));
RSTRING_PTR(rb_class_name(cfp->me->klass)),
rb_id2name(cfp->me->original_id));
}
return str;
@ -1739,7 +1740,6 @@ static void
vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
rb_num_t is_singleton, NODE *cref)
{
NODE *newbody;
VALUE klass = cref->nd_clss;
int noex = (int)cref->nd_visi;
rb_iseq_t *miseq;
@ -1768,11 +1768,10 @@ vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
COPY_CREF(miseq->cref_stack, cref);
miseq->klass = klass;
miseq->defined_method_id = id;
newbody = NEW_NODE_LONGLIFE(RUBY_VM_METHOD_NODE, 0, rb_gc_write_barrier(miseq->self), 0);
rb_add_method(klass, id, newbody, noex);
rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
if (!is_singleton && noex == NOEX_MODFUNC) {
rb_add_method(rb_singleton_class(klass), id, newbody, NOEX_PUBLIC);
rb_add_method(rb_singleton_class(klass), id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC);
}
INC_VM_STATE_VERSION();
}

View file

@ -21,6 +21,7 @@
#include "debug.h"
#include "vm_opts.h"
#include "id.h"
#include "method.h"
#if defined(_WIN32)
#include "thread_win32.h"
@ -92,6 +93,8 @@
typedef unsigned long rb_num_t;
/* iseq data type */
struct iseq_compile_data_ensure_node_stack;
typedef struct rb_compile_option_struct {
@ -109,12 +112,8 @@ typedef struct rb_compile_option_struct {
struct iseq_inline_cache_entry {
long ic_vmstat;
VALUE ic_class;
union {
NODE *method;
VALUE value;
} value;
#define ic_value value.value
#define ic_method value.method
VALUE ic_value;
rb_method_entry_t *ic_method;
#define ic_index ic_vmstat
};
@ -234,8 +233,6 @@ enum ruby_special_exceptions {
ruby_special_error_count
};
typedef struct rb_iseq_struct rb_iseq_t;
#define GetVMPtr(obj, ptr) \
GetCoreDataFromValue(obj, rb_vm_t, ptr)
@ -296,8 +293,7 @@ typedef struct {
VALUE *dfp; /* cfp[7] / block[2] */
rb_iseq_t *block_iseq; /* cfp[8] / block[3] */
VALUE proc; /* cfp[9] / block[4] */
ID method_id; /* cfp[10] saved in special case */
VALUE method_class; /* cfp[11] saved in special case */
const rb_method_entry_t *me;/* cfp[10] */
} rb_control_frame_t;
typedef struct rb_block_struct {
@ -359,7 +355,7 @@ typedef struct rb_thread_struct
int state;
/* for rb_iterate */
rb_block_t *passed_block;
const rb_block_t *passed_block;
/* for load(true) */
VALUE top_self;
@ -464,11 +460,6 @@ RUBY_EXTERN VALUE rb_mRubyVMFrozenCore;
/* each thread has this size stack : 128KB */
#define RUBY_VM_THREAD_STACK_SIZE (128 * 1024)
struct global_entry {
struct global_variable *var;
ID id;
};
#define GetProcPtr(obj, ptr) \
GetCoreDataFromValue(obj, rb_proc_t, ptr)
@ -500,6 +491,15 @@ typedef struct {
VALUE env;
} rb_binding_t;
struct global_entry {
struct global_variable *var;
ID id;
};
struct global_entry *rb_global_entry(ID);
VALUE rb_gvar_get(struct global_entry *);
VALUE rb_gvar_set(struct global_entry *, VALUE);
VALUE rb_gvar_defined(struct global_entry *);
/* used by compile time and send insn */
#define VM_CALL_ARGS_SPLAT_BIT (0x01 << 1)
@ -509,7 +509,6 @@ typedef struct {
#define VM_CALL_TAILCALL_BIT (0x01 << 5)
#define VM_CALL_TAILRECURSION_BIT (0x01 << 6)
#define VM_CALL_SUPER_BIT (0x01 << 7)
#define VM_CALL_SEND_BIT (0x01 << 8)
#define VM_SPECIAL_OBJECT_VMCORE 0x01
#define VM_SPECIAL_OBJECT_CBASE 0x02
@ -532,11 +531,9 @@ typedef struct {
/* other frame flag */
#define VM_FRAME_FLAG_PASSED 0x0100
#define RUBYVM_CFUNC_FRAME_P(cfp) \
(VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC)
/* inline cache */
typedef struct iseq_inline_cache_entry *IC;
@ -584,9 +581,7 @@ extern void rb_vmdebug_stack_dump_raw(rb_thread_t *, rb_control_frame_t *);
#define SDR2(cfp) rb_vmdebug_stack_dump_raw(GET_THREAD(), (cfp))
void rb_vm_bugreport(void);
/* functions about thread/vm execution */
VALUE rb_iseq_eval(VALUE iseqval);
VALUE rb_iseq_eval_main(VALUE iseqval);
void rb_enable_interrupt(void);
@ -594,7 +589,7 @@ void rb_disable_interrupt(void);
int rb_thread_method_id_and_class(rb_thread_t *th, ID *idp, VALUE *klassp);
VALUE rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, rb_block_t *blockptr);
int argc, const VALUE *argv, const rb_block_t *blockptr);
VALUE rb_vm_make_proc(rb_thread_t *th, const rb_block_t *block, VALUE klass);
VALUE rb_vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp);

View file

@ -112,9 +112,9 @@ control_frame_dump(rb_thread_t *th, rb_control_frame_t *cfp)
}
}
}
else if (cfp->method_id) {
iseq_name = rb_id2name(cfp->method_id);
snprintf(posbuf, MAX_POSBUF, ":%s", rb_id2name(cfp->method_id));
else if (cfp->me) {
iseq_name = rb_id2name(cfp->me->original_id);
snprintf(posbuf, MAX_POSBUF, ":%s", rb_id2name(cfp->me->original_id));
line = -1;
}
@ -253,7 +253,7 @@ vm_stack_dump_each(rb_thread_t *th, rb_control_frame_t *cfp)
if (iseq == 0) {
if (RUBYVM_CFUNC_FRAME_P(cfp)) {
name = rb_id2name(cfp->method_id);
name = rb_id2name(cfp->me->original_id);
}
else {
name = "?";

252
vm_eval.c
View file

@ -21,141 +21,153 @@ static NODE *vm_cref_push(rb_thread_t *th, VALUE klass, int noex);
static VALUE vm_exec(rb_thread_t *th);
static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref);
static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary);
static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, int scope);
typedef enum call_type {
CALL_PUBLIC,
CALL_FCALL,
CALL_VCALL,
} call_type;
static inline VALUE
vm_call0(rb_thread_t * th, VALUE klass, VALUE recv, VALUE id, ID oid,
int argc, const VALUE *argv, const NODE *body, int nosuper)
vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
const rb_method_entry_t *me)
{
VALUE val;
rb_block_t *blockptr = 0;
if (0) printf("id: %s, nd: %s, argc: %d, passed: %p\n",
rb_id2name(id), ruby_node_name(nd_type(body)),
argc, (void *)th->passed_block);
VALUE klass = me->klass;
const rb_block_t *blockptr = 0;
if (th->passed_block) {
blockptr = th->passed_block;
th->passed_block = 0;
}
again:
switch (nd_type(body)) {
case RUBY_VM_METHOD_NODE:{
rb_control_frame_t *reg_cfp;
VALUE iseqval = (VALUE)body->nd_body;
int i;
switch (me->type) {
case VM_METHOD_TYPE_ISEQ: {
rb_control_frame_t *reg_cfp;
int i;
rb_vm_set_finish_env(th);
reg_cfp = th->cfp;
rb_vm_set_finish_env(th);
reg_cfp = th->cfp;
CHECK_STACK_OVERFLOW(reg_cfp, argc + 1);
CHECK_STACK_OVERFLOW(reg_cfp, argc + 1);
*reg_cfp->sp++ = recv;
for (i = 0; i < argc; i++) {
*reg_cfp->sp++ = argv[i];
}
*reg_cfp->sp++ = recv;
for (i = 0; i < argc; i++) {
*reg_cfp->sp++ = argv[i];
}
vm_setup_method(th, reg_cfp, argc, blockptr, 0, iseqval, recv);
val = vm_exec(th);
break;
vm_setup_method(th, reg_cfp, recv, argc, blockptr, 0 /* flag */, me);
val = vm_exec(th);
break;
}
case NODE_CFUNC: {
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
{
rb_control_frame_t *reg_cfp = th->cfp;
rb_control_frame_t *cfp =
case VM_METHOD_TYPE_CFUNC: {
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
{
rb_control_frame_t *reg_cfp = th->cfp;
rb_control_frame_t *cfp =
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC,
recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1);
cfp->method_id = oid;
cfp->method_class = klass;
cfp->me = me;
val = call_cfunc(me->body.cfunc.func, recv, me->body.cfunc.argc, argc, argv);
val = call_cfunc(body->nd_cfnc, recv, (int)body->nd_argc, argc, argv);
if (reg_cfp != th->cfp + 1) {
SDR2(reg_cfp);
SDR2(th->cfp-5);
rb_bug("cfp consistency error - call0");
th->cfp = reg_cfp;
if (reg_cfp != th->cfp + 1) {
rb_bug("cfp consistency error - call0");
}
vm_pop_frame(th);
}
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
break;
}
case VM_METHOD_TYPE_ATTRSET: {
if (argc != 1) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
}
val = rb_ivar_set(recv, me->body.attr_id, argv[0]);
break;
}
case VM_METHOD_TYPE_IVAR: {
if (argc != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
val = rb_attr_get(recv, me->body.attr_id);
break;
}
case VM_METHOD_TYPE_BMETHOD: {
val = vm_call_bmethod(th, recv, argc, argv, blockptr, me);
break;
}
case VM_METHOD_TYPE_ZSUPER: {
klass = RCLASS_SUPER(klass);
if (!klass || !(me = rb_method_entry(klass, id))) {
return method_missing(recv, id, argc, argv, 0);
}
RUBY_VM_CHECK_INTS();
goto again;
}
case VM_METHOD_TYPE_OPTIMIZED: {
switch (me->body.optimize_type) {
case OPTIMIZED_METHOD_TYPE_SEND:
val = send_internal(argc, argv, recv, NOEX_NOSUPER | NOEX_PRIVATE);
break;
case OPTIMIZED_METHOD_TYPE_CALL: {
rb_proc_t *proc;
GetProcPtr(recv, proc);
val = rb_vm_invoke_proc(th, proc, proc->block.self, argc, argv, blockptr);
break;
}
vm_pop_frame(th);
}
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
break;
}
case NODE_ATTRSET:{
if (argc != 1) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
}
val = rb_ivar_set(recv, body->nd_vid, argv[0]);
break;
}
case NODE_IVAR: {
if (argc != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)",
argc);
}
val = rb_attr_get(recv, body->nd_vid);
break;
}
case NODE_BMETHOD:{
val = vm_call_bmethod(th, oid, body->nd_cval,
recv, klass, argc, (VALUE *)argv, blockptr);
break;
}
case NODE_ZSUPER:{
klass = RCLASS_SUPER(klass);
if (!klass || !(body = rb_method_node(klass, id))) {
return method_missing(recv, id, argc, argv, 0);
}
RUBY_VM_CHECK_INTS();
nosuper = CALL_SUPER;
body = body->nd_body;
goto again;
default:
rb_bug("vm_call0: unsupported optimized method type (%d)", me->body.optimize_type);
val = Qundef;
break;
}
break;
}
default:
rb_bug("unsupported: vm_call0(%s)", ruby_node_name(nd_type(body)));
rb_bug("vm_call0: unsupported method type (%d)", me->type);
val = Qundef;
}
RUBY_VM_CHECK_INTS();
return val;
}
VALUE
rb_vm_call(rb_thread_t * th, VALUE klass, VALUE recv, VALUE id, ID oid,
int argc, const VALUE *argv, const NODE *body, int nosuper)
rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, const VALUE *argv,
const rb_method_entry_t *me)
{
return vm_call0(th, klass, recv, id, oid, argc, argv, body, nosuper);
return vm_call0(th, recv, id, argc, argv, me);
}
static inline VALUE
vm_call_super(rb_thread_t * const th, const int argc, const VALUE * const argv)
vm_call_super(rb_thread_t *th, int argc, const VALUE *argv)
{
VALUE recv = th->cfp->self;
VALUE klass;
ID id;
NODE *body;
rb_method_entry_t *me;
rb_control_frame_t *cfp = th->cfp;
if (!cfp->iseq) {
klass = cfp->method_class;
klass = cfp->me->klass;
klass = RCLASS_SUPER(klass);
if (klass == 0) {
klass = vm_search_normal_superclass(cfp->method_class, recv);
klass = vm_search_normal_superclass(cfp->me->klass, recv);
}
id = cfp->method_id;
id = cfp->me->original_id;
}
else {
rb_bug("vm_call_super: should not be reached");
}
body = rb_method_node(klass, id); /* this returns NODE_METHOD */
if (!body) {
me = rb_method_entry(klass, id);
if (!me) {
return method_missing(recv, id, argc, argv, 0);
}
return vm_call0(th, klass, recv, id, (ID)body->nd_file,
argc, argv, body->nd_body, CALL_SUPER);
return vm_call0(th, recv, id, argc, argv, me);
}
VALUE
@ -177,14 +189,15 @@ stack_check(void)
}
static inline VALUE
rb_call0(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv,
int scope, VALUE self)
rb_call0(VALUE recv, ID mid, int argc, const VALUE *argv,
call_type scope, VALUE self)
{
NODE *body, *method;
int noex;
ID id = mid;
VALUE klass = CLASS_OF(recv);
rb_method_entry_t *me;
struct cache_entry *ent;
rb_thread_t *th = GET_THREAD();
ID oid;
int noex;
if (!klass) {
const char *adj = "terminated";
@ -194,40 +207,41 @@ rb_call0(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv,
"method `%s' called on %s object (%p)",
rb_id2name(mid), adj, (void *)recv);
}
/* is it in the method cache? */
ent = cache + EXPR1(klass, mid);
if (ent->mid == mid && ent->klass == klass) {
if (!ent->method)
return method_missing(recv, mid, argc, argv,
scope == 2 ? NOEX_VCALL : 0);
id = ent->mid0;
noex = (int)ent->method->nd_noex;
klass = ent->method->nd_clss;
body = ent->method->nd_body;
if (!ent->me) {
return method_missing(recv, mid, argc, argv,
scope == CALL_VCALL ? NOEX_VCALL : 0);
}
me = ent->me;
klass = me->klass;
}
else if ((method = rb_get_method_body(klass, id, &id)) != 0) {
noex = (int)method->nd_noex;
klass = method->nd_clss;
body = method->nd_body;
else if ((me = rb_method_entry(klass, mid)) != 0) {
klass = me->klass;
}
else {
if (scope == 3) {
return method_missing(recv, mid, argc, argv, NOEX_SUPER);
}
return method_missing(recv, mid, argc, argv,
scope == 2 ? NOEX_VCALL : 0);
scope == CALL_VCALL ? NOEX_VCALL : 0);
}
if (mid != idMethodMissing) {
oid = me->original_id;
noex = me->flag;
if (oid != idMethodMissing) {
/* receiver specified form for private method */
if (UNLIKELY(noex)) {
if (((noex & NOEX_MASK) & NOEX_PRIVATE) && scope == 0) {
if (((noex & NOEX_MASK) & NOEX_PRIVATE) && scope == CALL_PUBLIC) {
return method_missing(recv, mid, argc, argv, NOEX_PRIVATE);
}
/* self must be kind of a specified form for protected method */
if (((noex & NOEX_MASK) & NOEX_PROTECTED) && scope == 0) {
if (((noex & NOEX_MASK) & NOEX_PROTECTED) && scope == CALL_PUBLIC) {
VALUE defined_class = klass;
if (TYPE(defined_class) == T_ICLASS) {
@ -249,13 +263,13 @@ rb_call0(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv,
}
stack_check();
return vm_call0(th, klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER);
return vm_call0(th, recv, mid, argc, argv, me);
}
static inline VALUE
rb_call(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int scope)
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
{
return rb_call0(klass, recv, mid, argc, argv, scope, Qundef);
return rb_call0(recv, mid, argc, argv, scope, Qundef);
}
NORETURN(static void raise_method_missing(rb_thread_t *th, int argc, const VALUE *argv,
@ -404,7 +418,7 @@ rb_apply(VALUE recv, ID mid, VALUE args)
argc = RARRAY_LENINT(args);
argv = ALLOCA_N(VALUE, argc);
MEMCPY(argv, RARRAY_PTR(args), VALUE, argc);
return rb_call(CLASS_OF(recv), recv, mid, argc, argv, CALL_FCALL);
return rb_call(recv, mid, argc, argv, CALL_FCALL);
}
VALUE
@ -427,23 +441,23 @@ rb_funcall(VALUE recv, ID mid, int n, ...)
else {
argv = 0;
}
return rb_call(CLASS_OF(recv), recv, mid, n, argv, CALL_FCALL);
return rb_call(recv, mid, n, argv, CALL_FCALL);
}
VALUE
rb_funcall2(VALUE recv, ID mid, int argc, const VALUE *argv)
{
return rb_call(CLASS_OF(recv), recv, mid, argc, argv, CALL_FCALL);
return rb_call(recv, mid, argc, argv, CALL_FCALL);
}
VALUE
rb_funcall3(VALUE recv, ID mid, int argc, const VALUE *argv)
{
return rb_call(CLASS_OF(recv), recv, mid, argc, argv, CALL_PUBLIC);
return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
}
static VALUE
send_internal(int argc, VALUE *argv, VALUE recv, int scope)
send_internal(int argc, const VALUE *argv, VALUE recv, int scope)
{
VALUE vid;
VALUE self = RUBY_VM_PREVIOUS_CONTROL_FRAME(GET_THREAD()->cfp)->self;
@ -456,7 +470,7 @@ send_internal(int argc, VALUE *argv, VALUE recv, int scope)
vid = *argv++; argc--;
PASS_PASSED_BLOCK_TH(th);
return rb_call0(CLASS_OF(recv), recv, rb_to_id(vid), argc, argv, scope, self);
return rb_call0(recv, rb_to_id(vid), argc, argv, scope, self);
}
/*
@ -667,8 +681,7 @@ iterate_method(VALUE obj)
const struct iter_method_arg * arg =
(struct iter_method_arg *) obj;
return rb_call(CLASS_OF(arg->obj), arg->obj, arg->mid,
arg->argc, arg->argv, CALL_FCALL);
return rb_call(arg->obj, arg->mid, arg->argc, arg->argv, CALL_FCALL);
}
VALUE
@ -687,7 +700,7 @@ rb_block_call(VALUE obj, ID mid, int argc, VALUE * argv,
VALUE
rb_each(VALUE obj)
{
return rb_call(CLASS_OF(obj), obj, idEach, 0, 0, CALL_FCALL);
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
}
static VALUE
@ -1486,8 +1499,15 @@ Init_vm_eval(void)
rb_define_method(rb_cBasicObject, "instance_exec", rb_obj_instance_exec, -1);
rb_define_private_method(rb_cBasicObject, "method_missing", rb_method_missing, -1);
#if 1
rb_add_method(rb_cBasicObject, rb_intern("__send__"),
VM_METHOD_TYPE_OPTIMIZED, (void *)OPTIMIZED_METHOD_TYPE_SEND, 0);
rb_add_method(rb_mKernel, rb_intern("send"),
VM_METHOD_TYPE_OPTIMIZED, (void *)OPTIMIZED_METHOD_TYPE_SEND, 0);
#else
rb_define_method(rb_cBasicObject, "__send__", rb_f_send, -1);
rb_define_method(rb_mKernel, "send", rb_f_send, -1);
#endif
rb_define_method(rb_mKernel, "public_send", rb_f_public_send, -1);
rb_define_method(rb_cModule, "module_exec", rb_mod_module_exec, -1);

View file

@ -350,21 +350,24 @@ call_cfunc(VALUE (*func)(), VALUE recv,
break;
default:
rb_raise(rb_eArgError, "too many arguments(%d)", len);
break;
return Qundef; /* not reached */
}
return Qnil; /* not reached */
}
static inline VALUE
vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp,
int num, ID id, ID oid, VALUE recv, VALUE klass,
VALUE flag, const NODE *mn, const rb_block_t *blockptr)
int num, VALUE recv, const rb_block_t *blockptr, VALUE flag,
const rb_method_entry_t *me)
{
VALUE val = 0;
int state = 0;
VALUE klass = me->klass;
ID id = me->original_id;
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
TH_PUSH_TAG(th);
// TODO: fix me. separate event
if (th->event_flags & RUBY_EVENT_C_RETURN) {
state = TH_EXEC_TAG();
}
@ -376,12 +379,10 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp,
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC,
recv, (VALUE) blockptr, 0, reg_cfp->sp, 0, 1);
cfp->method_id = oid;
cfp->method_class = klass;
cfp->me = me;
reg_cfp->sp -= num + 1;
val = call_cfunc(mn->nd_cfnc, recv, (int)mn->nd_argc, num, reg_cfp->sp + 1);
val = call_cfunc(me->body.cfunc.func, recv, (int)me->body.cfunc.argc, num, reg_cfp->sp + 1);
if (reg_cfp != th->cfp + 1) {
rb_bug("cfp consistency error - send");
@ -397,25 +398,24 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp,
}
static inline VALUE
vm_call_bmethod(rb_thread_t *th, ID id, VALUE procval, VALUE recv,
VALUE klass, int argc, VALUE *argv, rb_block_t *blockptr)
vm_call_bmethod(rb_thread_t *th, VALUE recv, int argc, const VALUE *argv,
const rb_block_t *blockptr, const rb_method_entry_t *me)
{
rb_control_frame_t *cfp = th->cfp;
rb_proc_t *proc;
VALUE val;
/* control block frame */
(cfp-2)->method_id = id;
(cfp-2)->method_class = klass;
(cfp-2)->me = me;
GetProcPtr(procval, proc);
GetProcPtr(me->body.proc, proc);
val = rb_vm_invoke_proc(th, proc, recv, argc, argv, blockptr);
return val;
}
static inline void
vm_method_missing_args(rb_thread_t *th, VALUE *argv,
int num, rb_block_t *blockptr, int opt)
int num, const rb_block_t *blockptr, int opt)
{
rb_control_frame_t * const reg_cfp = th->cfp;
MEMCPY(argv, STACK_ADDR_FROM_TOP(num + 1), VALUE, num + 1);
@ -426,7 +426,7 @@ vm_method_missing_args(rb_thread_t *th, VALUE *argv,
static inline VALUE
vm_method_missing(rb_thread_t *th, ID id, VALUE recv,
int num, rb_block_t *blockptr, int opt)
int num, const rb_block_t *blockptr, int opt)
{
VALUE *argv = ALLOCA_N(VALUE, num + 1);
vm_method_missing_args(th, argv, num, blockptr, opt);
@ -435,16 +435,14 @@ vm_method_missing(rb_thread_t *th, ID id, VALUE recv,
}
static inline void
vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
const int argc, const rb_block_t *blockptr, const VALUE flag,
const VALUE iseqval, const VALUE recv)
vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
VALUE recv, int argc, const rb_block_t *blockptr, VALUE flag,
const rb_method_entry_t *me)
{
rb_iseq_t *iseq;
int opt_pc, i;
VALUE *sp, *rsp = cfp->sp - argc;
rb_iseq_t *iseq = me->body.iseq;
/* TODO: eliminate it */
GetISeqPtr(iseqval, iseq);
VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, rsp, &blockptr);
/* stack overflow check */
@ -491,69 +489,105 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
}
static inline VALUE
vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
const int num, rb_block_t * const blockptr, const VALUE flag,
const ID id, const NODE * mn, const VALUE recv)
vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp,
int num, const rb_block_t *blockptr, VALUE flag,
ID id, const rb_method_entry_t *me, VALUE recv)
{
VALUE val;
start_method_dispatch:
if (mn != 0) {
if ((mn->nd_noex == 0)) {
/* dispatch method */
NODE *node;
if (me != 0) {
if ((me->flag == 0)) {
normal_method_dispatch:
node = mn->nd_body;
switch (nd_type(node)) {
case RUBY_VM_METHOD_NODE:{
vm_setup_method(th, cfp, num, blockptr, flag, (VALUE)node->nd_body, recv);
switch (me->type) {
case VM_METHOD_TYPE_ISEQ:{
vm_setup_method(th, cfp, recv, num, blockptr, flag, me);
return Qundef;
}
case NODE_CFUNC:{
val = vm_call_cfunc(th, cfp, num, id, (ID)mn->nd_file, recv, mn->nd_clss, flag, node, blockptr);
case VM_METHOD_TYPE_NOTIMPLEMENTED:
case VM_METHOD_TYPE_CFUNC:{
val = vm_call_cfunc(th, cfp, num, recv, blockptr, flag, me);
break;
}
case NODE_ATTRSET:{
val = rb_ivar_set(recv, node->nd_vid, *(cfp->sp - 1));
case VM_METHOD_TYPE_ATTRSET:{
if (num != 1) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", num);
}
val = rb_ivar_set(recv, me->body.attr_id, *(cfp->sp - 1));
cfp->sp -= 2;
break;
}
case NODE_IVAR:{
case VM_METHOD_TYPE_IVAR:{
if (num != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)",
num);
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", num);
}
val = rb_attr_get(recv, node->nd_vid);
val = rb_attr_get(recv, me->body.attr_id);
cfp->sp -= 1;
break;
}
case NODE_BMETHOD:{
case VM_METHOD_TYPE_BMETHOD:{
VALUE *argv = ALLOCA_N(VALUE, num);
MEMCPY(argv, cfp->sp - num, VALUE, num);
cfp->sp += - num - 1;
val = vm_call_bmethod(th, (ID)mn->nd_file, node->nd_cval, recv, mn->nd_clss, num, argv, blockptr);
val = vm_call_bmethod(th, recv, num, argv, blockptr, me);
break;
}
case NODE_ZSUPER:{
VALUE klass;
klass = RCLASS_SUPER(mn->nd_clss);
mn = rb_method_node(klass, id);
case VM_METHOD_TYPE_ZSUPER:{
VALUE klass = RCLASS_SUPER(me->klass);
me = rb_method_entry(klass, id);
if (mn != 0) {
if (me != 0) {
goto normal_method_dispatch;
}
else {
goto start_method_dispatch;
}
}
case VM_METHOD_TYPE_OPTIMIZED:{
switch (me->body.optimize_type) {
case OPTIMIZED_METHOD_TYPE_SEND: {
rb_control_frame_t *reg_cfp = cfp;
rb_num_t i = num - 1;
VALUE sym;
if (num == 0) {
rb_raise(rb_eArgError, "no method name given");
}
sym = TOPN(i);
id = SYMBOL_P(sym) ? SYM2ID(sym) : rb_to_id(sym);
/* shift arguments */
if (i > 0) {
MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i);
}
me = rb_method_entry(CLASS_OF(recv), id);
num -= 1;
DEC_SP(1);
flag |= VM_CALL_FCALL_BIT;
goto start_method_dispatch;
}
break;
case OPTIMIZED_METHOD_TYPE_CALL: {
rb_proc_t *proc;
int argc = num;
VALUE *argv = ALLOCA_N(VALUE, num);
GetProcPtr(recv, proc);
MEMCPY(argv, cfp->sp - num, VALUE, num);
cfp->sp -= num + 1;
val = rb_vm_invoke_proc(th, proc, proc->block.self, argc, argv, blockptr);
}
break;
default:
rb_bug("eval_invoke_method: unsupported optimized method type (%d)",
me->body.optimize_type);
}
break;
}
default:{
printf("node: %s\n", ruby_node_name(nd_type(node)));
rb_bug("eval_invoke_method: unreachable");
/* unreachable */
rb_bug("eval_invoke_method: unsupported method type (%d)", me->type);
break;
}
}
@ -562,7 +596,7 @@ vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
int noex_safe;
if (!(flag & VM_CALL_FCALL_BIT) &&
(mn->nd_noex & NOEX_MASK) & NOEX_PRIVATE) {
(me->flag & NOEX_MASK) & NOEX_PRIVATE) {
int stat = NOEX_PRIVATE;
if (flag & VM_CALL_VCALL_BIT) {
@ -570,9 +604,8 @@ vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
}
val = vm_method_missing(th, id, recv, num, blockptr, stat);
}
else if (((mn->nd_noex & NOEX_MASK) & NOEX_PROTECTED) &&
!(flag & VM_CALL_SEND_BIT)) {
VALUE defined_class = mn->nd_clss;
else if ((me->flag & NOEX_MASK) & NOEX_PROTECTED) {
VALUE defined_class = me->klass;
if (TYPE(defined_class) == T_ICLASS) {
defined_class = RBASIC(defined_class)->klass;
@ -585,7 +618,7 @@ vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
goto normal_method_dispatch;
}
}
else if ((noex_safe = NOEX_SAFE(mn->nd_noex)) > th->safe_level &&
else if ((noex_safe = NOEX_SAFE(me->flag)) > th->safe_level &&
(noex_safe > 2)) {
rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(id));
}
@ -617,33 +650,6 @@ vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
return val;
}
static inline void
vm_send_optimize(rb_control_frame_t * const reg_cfp, NODE ** const mn,
rb_num_t * const flag, rb_num_t * const num,
ID * const id, const VALUE klass)
{
if (*mn && nd_type((*mn)->nd_body) == NODE_CFUNC) {
NODE *node = (*mn)->nd_body;
extern VALUE rb_f_send(int argc, VALUE *argv, VALUE recv);
if (node->nd_cfnc == rb_f_send) {
rb_num_t i = *num - 1;
VALUE sym = TOPN(i);
*id = SYMBOL_P(sym) ? SYM2ID(sym) : rb_to_id(sym);
/* shift arguments */
if (i > 0) {
MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i);
}
*mn = rb_method_node(klass, *id);
*num -= 1;
DEC_SP(1);
*flag |= VM_CALL_FCALL_BIT;
}
}
}
/* yield */
static inline int
@ -707,8 +713,8 @@ vm_yield_with_cfunc(rb_thread_t *th, const rb_block_t *block,
* @pre iseq is block style (not lambda style)
*/
static inline int
vm_yield_setup_block_args_complex(rb_thread_t *th, const rb_iseq_t * iseq,
int argc, VALUE * argv)
vm_yield_setup_block_args_complex(rb_thread_t *th, const rb_iseq_t *iseq,
int argc, VALUE *argv)
{
rb_num_t opt_pc = 0;
int i;
@ -765,8 +771,8 @@ vm_yield_setup_block_args_complex(rb_thread_t *th, const rb_iseq_t * iseq,
static inline int
vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq,
int orig_argc, VALUE * argv,
const rb_block_t *blockptr)
int orig_argc, VALUE *argv,
const rb_block_t *blockptr)
{
int i;
int argc = orig_argc;
@ -839,7 +845,12 @@ vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq,
VALUE procval = Qnil;
if (blockptr) {
procval = blockptr->proc;
if (blockptr->proc == 0) {
procval = rb_vm_make_proc(th, blockptr, rb_cProc);
}
else {
procval = blockptr->proc;
}
}
argv[iseq->arg_block] = procval;
@ -879,7 +890,7 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
static VALUE
vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_num_t flag)
{
rb_block_t * const block = GET_BLOCK_PTR();
const rb_block_t *block = GET_BLOCK_PTR();
rb_iseq_t *iseq;
int argc = (int)num;
@ -1218,28 +1229,25 @@ vm_getivar(VALUE obj, ID id, IC ic)
#endif
}
static inline NODE *
static inline const rb_method_entry_t *
vm_method_search(VALUE id, VALUE klass, IC ic)
{
NODE *mn;
rb_method_entry_t *me;
#if OPT_INLINE_METHOD_CACHE
{
if (LIKELY(klass == ic->ic_class) &&
LIKELY(GET_VM_STATE_VERSION() == ic->ic_vmstat)) {
mn = ic->ic_method;
}
else {
mn = rb_method_node(klass, id);
ic->ic_class = klass;
ic->ic_method = mn;
ic->ic_vmstat = GET_VM_STATE_VERSION();
}
if (LIKELY(klass == ic->ic_class) &&
LIKELY(GET_VM_STATE_VERSION() == ic->ic_vmstat)) {
me = ic->ic_method;
}
else {
me = rb_method_entry(klass, id);
ic->ic_class = klass;
ic->ic_method = me;
ic->ic_vmstat = GET_VM_STATE_VERSION();
}
#else
mn = rb_method_node(klass, id);
me = rb_method_entry(klass, id);
#endif
return mn;
return me;
}
static inline VALUE
@ -1300,8 +1308,8 @@ vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *ip,
}
}
id = lcfp->method_id;
klass = vm_search_normal_superclass(lcfp->method_class, recv);
id = lcfp->me->original_id;
klass = vm_search_normal_superclass(lcfp->me->klass, recv);
}
else {
klass = vm_search_normal_superclass(ip->klass, recv);
@ -1520,10 +1528,10 @@ vm_expandarray(rb_control_frame_t *cfp, VALUE ary, rb_num_t num, int flag)
}
static inline int
check_cfunc(const NODE *mn, VALUE (*func)())
check_cfunc(const rb_method_entry_t *me, VALUE (*func)())
{
if (mn && nd_type(mn->nd_body) == NODE_CFUNC &&
mn->nd_body->nd_cfnc == func) {
if (me && me->type == VM_METHOD_TYPE_CFUNC &&
me->body.cfunc.func == func) {
return 1;
}
else {
@ -1572,10 +1580,10 @@ opt_eq_func(VALUE recv, VALUE obj, IC ic)
val = rb_str_equal(recv, obj);
}
else {
NODE *mn = vm_method_search(idEq, CLASS_OF(recv), ic);
const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ic);
extern VALUE rb_obj_equal(VALUE obj1, VALUE obj2);
if (check_cfunc(mn, rb_obj_equal)) {
if (check_cfunc(me, rb_obj_equal)) {
return recv == obj ? Qtrue : Qfalse;
}
}

View file

@ -159,8 +159,8 @@ extern VALUE ruby_vm_const_missing_count;
c1->nd_next = (NODE *)rb_gc_write_barrier((VALUE)__tmp_c2->nd_next);\
} while (0)
#define CALL_METHOD(num, blockptr, flag, id, mn, recv) do { \
VALUE v = vm_call_method(th, GET_CFP(), num, blockptr, flag, id, mn, recv); \
#define CALL_METHOD(num, blockptr, flag, id, me, recv) do { \
VALUE v = vm_call_method(th, GET_CFP(), num, blockptr, flag, id, me, recv); \
if (v == Qundef) { \
RESTORE_REGS(); \
NEXT_INSN(); \
@ -189,7 +189,7 @@ extern VALUE ruby_vm_const_missing_count;
#define CALL_SIMPLE_METHOD(num, id, recv) do { \
VALUE klass = CLASS_OF(recv); \
CALL_METHOD(num, 0, 0, id, rb_method_node(klass, id), recv); \
CALL_METHOD(num, 0, 0, id, rb_method_entry(klass, id), recv); \
} while (0)
#endif /* RUBY_INSNHELPER_H */

View file

@ -6,7 +6,7 @@
#define CACHE_MASK 0x7ff
#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
static void rb_vm_check_redefinition_opt_method(const NODE *node);
static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me);
static ID object_id;
static ID removed, singleton_removed, undefined, singleton_undefined;
@ -14,18 +14,14 @@ static ID added, singleton_added;
struct cache_entry { /* method hash table. */
ID mid; /* method's id */
ID mid0; /* method's original id */
VALUE klass; /* receiver's class */
VALUE oklass; /* original's class */
NODE *method;
rb_method_entry_t *me;
};
static struct cache_entry cache[CACHE_SIZE];
#define ruby_running (GET_VM()->running)
/* int ruby_running = 0; */
static NODE *notimplement_body = 0;
void
rb_clear_cache(void)
{
@ -38,7 +34,7 @@ rb_clear_cache(void)
ent = cache;
end = ent + CACHE_SIZE;
while (ent < end) {
ent->mid = 0;
ent->me = ent->mid = 0;
ent++;
}
}
@ -55,8 +51,8 @@ rb_clear_cache_for_undef(VALUE klass, ID id)
ent = cache;
end = ent + CACHE_SIZE;
while (ent < end) {
if (ent->oklass == klass && ent->mid == id) {
ent->mid = 0;
if ((ent->me && ent->me->klass == klass) && ent->mid == id) {
ent->me = ent->mid = 0;
}
ent++;
}
@ -75,7 +71,7 @@ rb_clear_cache_by_id(ID id)
end = ent + CACHE_SIZE;
while (ent < end) {
if (ent->mid == id) {
ent->mid = 0;
ent->me = ent->mid = 0;
}
ent++;
}
@ -93,17 +89,43 @@ rb_clear_cache_by_class(VALUE klass)
ent = cache;
end = ent + CACHE_SIZE;
while (ent < end) {
if (ent->klass == klass || ent->oklass == klass) {
ent->mid = 0;
if (ent->klass == klass || (ent->me && ent->me->klass == klass)) {
ent->me = ent->mid = 0;
}
ent++;
}
}
void
rb_add_method(VALUE klass, ID mid, NODE * node, int noex)
VALUE rb_f_notimplement(int argc, VALUE *argv, VALUE obj)
{
NODE *body;
rb_notimplement();
}
static void rb_define_notimplement_method_id(VALUE mod, ID id, rb_method_flag_t noex)
{
rb_add_method(mod, id, VM_METHOD_TYPE_NOTIMPLEMENTED, 0, noex);
}
void
rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex)
{
if (func != rb_f_notimplement) {
rb_method_cfunc_t opt = {
func, argc,
};
rb_add_method(klass, mid, VM_METHOD_TYPE_CFUNC, &opt, noex);
}
else {
rb_define_notimplement_method_id(klass, mid, noex);
}
}
rb_method_entry_t *
rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_flag_t noex)
{
rb_method_entry_t *me;
st_table *mtbl;
st_data_t data;
if (NIL_P(klass)) {
klass = rb_cObject;
@ -113,15 +135,16 @@ rb_add_method(VALUE klass, ID mid, NODE * node, int noex)
rb_raise(rb_eSecurityError, "Insecure: can't define method");
}
if (!FL_TEST(klass, FL_SINGLETON) &&
node && nd_type(node) != NODE_ZSUPER &&
type != VM_METHOD_TYPE_NOTIMPLEMENTED &&
type != VM_METHOD_TYPE_ZSUPER &&
(mid == rb_intern("initialize") || mid == rb_intern("initialize_copy"))) {
noex = NOEX_PRIVATE | noex;
}
else if (FL_TEST(klass, FL_SINGLETON) && node
&& nd_type(node) == NODE_CFUNC && mid == rb_intern("allocate")) {
rb_warn
("defining %s.allocate is deprecated; use rb_define_alloc_func()",
rb_class2name(rb_iv_get(klass, "__attached__")));
else if (FL_TEST(klass, FL_SINGLETON) &&
type == VM_METHOD_TYPE_CFUNC &&
mid == rb_intern("allocate")) {
rb_warn("defining %s.allocate is deprecated; use rb_define_alloc_func()",
rb_class2name(rb_iv_get(klass, "__attached__")));
mid = ID_ALLOCATOR;
}
if (OBJ_FROZEN(klass)) {
@ -129,189 +152,178 @@ rb_add_method(VALUE klass, ID mid, NODE * node, int noex)
}
rb_clear_cache_by_id(mid);
/*
* NODE_METHOD (NEW_METHOD(body, klass, vis)):
* nd_file : original id // RBASIC()->klass (TODO: dirty hack)
* nd_body : method body // (2) // mark
* nd_clss : klass // (1) // mark
* nd_noex : visibility // (3)
*
* NODE_FBODY (NEW_FBODY(method, alias)):
* nd_body : method (NODE_METHOD) // (2) // mark
* nd_oid : original id // (1)
* nd_cnt : alias count // (3)
*/
if (node) {
NODE *method = NEW_NODE_LONGLIFE(NODE_METHOD,
rb_gc_write_barrier(klass),
rb_gc_write_barrier((VALUE)node),
NOEX_WITH_SAFE(noex));
method->nd_file = (void *)mid;
body = NEW_NODE_LONGLIFE(NODE_FBODY, mid, method, 0);
}
else {
body = 0;
me = ALLOC(rb_method_entry_t);
me->type = type;
me->original_id = me->called_id = mid;
me->klass = klass;
me->flag = NOEX_WITH_SAFE(noex);
me->alias_count = 0;
switch (type) {
case VM_METHOD_TYPE_ISEQ:
me->body.iseq = (rb_iseq_t *)opts;
break;
case VM_METHOD_TYPE_CFUNC:
me->body.cfunc = *(rb_method_cfunc_t *)opts;
break;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
me->body.attr_id = (ID)opts;
break;
case VM_METHOD_TYPE_BMETHOD:
me->body.proc = (VALUE)opts;
break;
case VM_METHOD_TYPE_NOTIMPLEMENTED:
me->body.cfunc.func = rb_f_notimplement;
me->body.cfunc.argc = -1;
break;
case VM_METHOD_TYPE_OPTIMIZED:
me->body.optimize_type = (enum method_optimized_type)opts;
break;
case VM_METHOD_TYPE_ZSUPER:
case VM_METHOD_TYPE_UNDEF:
break;
default:
rb_bug("rb_add_method: unsupported method type (%d)\n", type);
}
{
/* check re-definition */
st_data_t data;
NODE *old_node;
mtbl = RCLASS_M_TBL(klass);
if (st_lookup(RCLASS_M_TBL(klass), mid, &data)) {
old_node = (NODE *)data;
if (old_node) {
if (nd_type(old_node->nd_body->nd_body) == NODE_CFUNC) {
rb_vm_check_redefinition_opt_method(old_node);
}
if (RTEST(ruby_verbose) && node && old_node->nd_cnt == 0 && old_node->nd_body) {
rb_warning("method redefined; discarding old %s", rb_id2name(mid));
}
}
}
if (klass == rb_cObject && node && mid == idInitialize) {
rb_warn("redefining Object#initialize may cause infinite loop");
/* check re-definition */
if (st_lookup(mtbl, mid, &data)) {
rb_method_entry_t *old_me = (rb_method_entry_t *)data;
rb_vm_check_redefinition_opt_method(old_me);
if (RTEST(ruby_verbose) &&
old_me->alias_count == 0 &&
old_me->type != VM_METHOD_TYPE_UNDEF) {
rb_warning("method redefined; discarding old %s", rb_id2name(mid));
}
if (mid == object_id || mid == id__send__) {
if (node && nd_type(node) == RUBY_VM_METHOD_NODE) {
rb_warn("redefining `%s' may cause serious problems",
rb_id2name(mid));
}
// TODO: free old_me
}
/* check mid */
if (klass == rb_cObject && mid == idInitialize) {
rb_warn("redefining Object#initialize may cause infinite loop");
}
/* check mid */
if (mid == object_id || mid == id__send__) {
if (type == VM_METHOD_TYPE_ISEQ) {
rb_warn("redefining `%s' may cause serious problems", rb_id2name(mid));
}
}
st_insert(RCLASS_M_TBL(klass), mid, (st_data_t) body);
st_insert(mtbl, mid, (st_data_t) me);
if (node && mid != ID_ALLOCATOR && ruby_running) {
if (mid != ID_ALLOCATOR && ruby_running) {
if (FL_TEST(klass, FL_SINGLETON)) {
rb_funcall(rb_iv_get(klass, "__attached__"), singleton_added, 1,
ID2SYM(mid));
rb_funcall(rb_iv_get(klass, "__attached__"), singleton_added, 1, ID2SYM(mid));
}
else {
rb_funcall(klass, added, 1, ID2SYM(mid));
}
}
return me;
}
void
rb_define_alloc_func(VALUE klass, VALUE (*func)(VALUE))
{
Check_Type(klass, T_CLASS);
rb_add_method(rb_singleton_class(klass), ID_ALLOCATOR,
NEW_NODE_LONGLIFE(NODE_CFUNC, func, 0, 0),
NOEX_PRIVATE);
rb_add_method_cfunc(rb_singleton_class(klass), ID_ALLOCATOR,
func, 0, NOEX_PRIVATE);
}
void
rb_undef_alloc_func(VALUE klass)
{
Check_Type(klass, T_CLASS);
rb_add_method(rb_singleton_class(klass), ID_ALLOCATOR, 0, NOEX_UNDEF);
rb_add_method(rb_singleton_class(klass), ID_ALLOCATOR, VM_METHOD_TYPE_UNDEF, 0, NOEX_UNDEF);
}
rb_alloc_func_t
rb_get_alloc_func(VALUE klass)
{
NODE *n;
rb_method_entry_t *me;
Check_Type(klass, T_CLASS);
n = rb_method_node(CLASS_OF(klass), ID_ALLOCATOR);
if (!n) return 0;
if (nd_type(n) != NODE_METHOD) return 0;
n = n->nd_body;
if (nd_type(n) != NODE_CFUNC) return 0;
return (rb_alloc_func_t)n->nd_cfnc;
me = rb_method_entry(CLASS_OF(klass), ID_ALLOCATOR);
if (me && me->type == VM_METHOD_TYPE_CFUNC) {
return (rb_alloc_func_t)me->body.cfunc.func;
}
else {
return 0;
}
}
static NODE *
search_method(VALUE klass, ID id, VALUE *klassp)
static rb_method_entry_t*
search_method(VALUE klass, ID id)
{
st_data_t body;
if (!klass) {
return 0;
}
while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) {
klass = RCLASS_SUPER(klass);
if (!klass)
if (!klass) {
return 0;
}
}
if (klassp) {
*klassp = klass;
}
return (NODE *)body;
return (rb_method_entry_t *)body;
}
/*
* search method body (NODE_METHOD)
* with : klass and id
* without : method cache
* search method entry without method cache.
*
* if you need method node with method cache, use
* rb_method_node()
* if you need method entry with method cache, use
* rb_method_entry()
*/
NODE *
rb_get_method_body(VALUE klass, ID id, ID *idp)
rb_method_entry_t *
rb_get_method_entry(VALUE klass, ID id)
{
NODE *volatile fbody, *body;
NODE *method;
if ((fbody = search_method(klass, id, 0)) == 0 || !fbody->nd_body) {
/* store empty info in cache */
struct cache_entry *ent;
ent = cache + EXPR1(klass, id);
ent->klass = klass;
ent->mid = ent->mid0 = id;
ent->method = 0;
ent->oklass = 0;
return 0;
}
method = fbody->nd_body;
rb_method_entry_t *me = search_method(klass, id);
if (ruby_running) {
/* store in cache */
struct cache_entry *ent;
ent = cache + EXPR1(klass, id);
ent->klass = klass;
ent->mid = id;
ent->mid0 = fbody->nd_oid;
ent->method = body = method;
ent->oklass = method->nd_clss;
}
else {
body = method;
if (!me || me->type == VM_METHOD_TYPE_UNDEF) {
ent->mid = id;
ent->me = 0;
me = 0;
}
else {
ent->mid = id;
ent->me = me;
}
}
if (idp) {
*idp = fbody->nd_oid;
}
return body;
return me;
}
NODE *
rb_method_node(VALUE klass, ID id)
rb_method_entry_t *
rb_method_entry(VALUE klass, ID id)
{
struct cache_entry *ent;
ent = cache + EXPR1(klass, id);
if (ent->mid == id && ent->klass == klass) {
if (ent->method) return ent->method;
return 0;
return ent->me;
}
return rb_get_method_body(klass, id, 0);
return rb_get_method_entry(klass, id);
}
static void
remove_method(VALUE klass, ID mid)
{
st_data_t data;
NODE *body = 0;
rb_method_entry_t *me = 0;
if (klass == rb_cObject) {
rb_secure(4);
@ -324,26 +336,26 @@ remove_method(VALUE klass, ID mid)
if (mid == object_id || mid == id__send__ || mid == idInitialize) {
rb_warn("removing `%s' may cause serious problems", rb_id2name(mid));
}
if (st_lookup(RCLASS_M_TBL(klass), mid, &data)) {
body = (NODE *)data;
if (!body || !body->nd_body) body = 0;
me = (rb_method_entry_t *)data;
if (!me || me->type == VM_METHOD_TYPE_UNDEF) {
me = 0;
}
else {
st_delete(RCLASS_M_TBL(klass), &mid, &data);
}
}
if (!body) {
if (!me) {
rb_name_error(mid, "method `%s' not defined in %s",
rb_id2name(mid), rb_class2name(klass));
}
if (nd_type(body->nd_body->nd_body) == NODE_CFUNC) {
rb_vm_check_redefinition_opt_method(body);
}
rb_vm_check_redefinition_opt_method(me);
rb_clear_cache_for_undef(klass, mid);
if (FL_TEST(klass, FL_SINGLETON)) {
rb_funcall(rb_iv_get(klass, "__attached__"), singleton_removed, 1,
ID2SYM(mid));
rb_funcall(rb_iv_get(klass, "__attached__"), singleton_removed, 1, ID2SYM(mid));
}
else {
rb_funcall(klass, removed, 1, ID2SYM(mid));
@ -393,49 +405,45 @@ rb_enable_super(VALUE klass, const char *name)
static void
rb_export_method(VALUE klass, ID name, ID noex)
{
NODE *fbody;
VALUE origin;
rb_method_entry_t *me;
if (klass == rb_cObject) {
rb_secure(4);
}
fbody = search_method(klass, name, &origin);
if (!fbody && TYPE(klass) == T_MODULE) {
fbody = search_method(rb_cObject, name, &origin);
me = search_method(klass, name);
if (!me && TYPE(klass) == T_MODULE) {
me = search_method(rb_cObject, name);
}
if (!fbody || !fbody->nd_body) {
if (!me || me->type == VM_METHOD_TYPE_UNDEF) {
rb_print_undef(klass, name, 0);
}
if (fbody->nd_body->nd_noex != noex) {
if (nd_type(fbody->nd_body->nd_body) == NODE_CFUNC) {
rb_vm_check_redefinition_opt_method(fbody);
}
if (klass == origin) {
fbody->nd_body->nd_noex = noex;
if (me->flag != noex) {
rb_vm_check_redefinition_opt_method(me);
if (klass == me->klass) {
me->flag = noex;
}
else {
rb_add_method(klass, name, NEW_ZSUPER(), noex);
rb_add_method(klass, name, VM_METHOD_TYPE_ZSUPER, 0, noex);
}
}
}
int
rb_notimplement_body_p(NODE *method)
{
return method == notimplement_body ? Qtrue : Qfalse;
}
int
rb_method_boundp(VALUE klass, ID id, int ex)
{
NODE *method;
rb_method_entry_t *me = rb_method_entry(klass, id);
if ((method = rb_method_node(klass, id)) != 0) {
if (ex && (method->nd_noex & NOEX_PRIVATE)) {
if (me != 0) {
if (ex && (me->flag & NOEX_PRIVATE)) {
return Qfalse;
}
if (rb_notimplement_body_p(method->nd_body))
if (me->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
return Qfalse;
}
return Qtrue;
}
return Qfalse;
@ -447,7 +455,7 @@ rb_attr(VALUE klass, ID id, int read, int write, int ex)
const char *name;
ID attriv;
VALUE aname;
int noex;
rb_method_flag_t noex;
if (!ex) {
noex = NOEX_PUBLIC;
@ -478,32 +486,32 @@ rb_attr(VALUE klass, ID id, int read, int write, int ex)
rb_enc_copy(aname, rb_id2str(id));
attriv = rb_intern_str(aname);
if (read) {
rb_add_method(klass, id, NEW_IVAR(attriv), noex);
rb_add_method(klass, id, VM_METHOD_TYPE_IVAR, (void *)attriv, noex);
}
if (write) {
rb_add_method(klass, rb_id_attrset(id), NEW_ATTRSET(attriv), noex);
rb_add_method(klass, rb_id_attrset(id), VM_METHOD_TYPE_ATTRSET, (void *)attriv, noex);
}
}
void
rb_undef(VALUE klass, ID id)
{
VALUE origin;
NODE *body;
rb_method_entry_t *me;
if (rb_vm_cbase() == rb_cObject && klass == rb_cObject) {
rb_secure(4);
}
if (rb_safe_level() >= 4 && !OBJ_UNTRUSTED(klass)) {
rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'",
rb_id2name(id));
rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(id));
}
rb_frozen_class_p(klass);
if (id == object_id || id == id__send__ || id == idInitialize) {
rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
}
body = search_method(klass, id, &origin);
if (!body || !body->nd_body) {
me = search_method(klass, id);
if (!me || me->type == VM_METHOD_TYPE_UNDEF) {
const char *s0 = " class";
VALUE c = klass;
@ -524,11 +532,10 @@ rb_undef(VALUE klass, ID id)
rb_id2name(id), s0, rb_class2name(c));
}
rb_add_method(klass, id, 0, NOEX_PUBLIC);
rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC);
if (FL_TEST(klass, FL_SINGLETON)) {
rb_funcall(rb_iv_get(klass, "__attached__"),
singleton_undefined, 1, ID2SYM(id));
rb_funcall(rb_iv_get(klass, "__attached__"), singleton_undefined, 1, ID2SYM(id));
}
else {
rb_funcall(klass, undefined, 1, ID2SYM(id));
@ -622,6 +629,18 @@ rb_mod_method_defined(VALUE mod, VALUE mid)
#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f))
static VALUE
check_definition(VALUE mod, VALUE mid, rb_method_flag_t noex)
{
const rb_method_entry_t *me;
me = rb_method_entry(mod, mid);
if (me) {
if (VISI_CHECK(me->flag, noex))
return Qtrue;
}
return Qfalse;
}
/*
* call-seq:
* mod.public_method_defined?(symbol) => true or false
@ -651,15 +670,7 @@ rb_mod_method_defined(VALUE mod, VALUE mid)
static VALUE
rb_mod_public_method_defined(VALUE mod, VALUE mid)
{
ID id = rb_to_id(mid);
NODE *method;
method = rb_method_node(mod, id);
if (method) {
if (VISI_CHECK(method->nd_noex, NOEX_PUBLIC))
return Qtrue;
}
return Qfalse;
return check_definition(mod, rb_to_id(mid), NOEX_PUBLIC);
}
/*
@ -691,15 +702,7 @@ rb_mod_public_method_defined(VALUE mod, VALUE mid)
static VALUE
rb_mod_private_method_defined(VALUE mod, VALUE mid)
{
ID id = rb_to_id(mid);
NODE *method;
method = rb_method_node(mod, id);
if (method) {
if (VISI_CHECK(method->nd_noex, NOEX_PRIVATE))
return Qtrue;
}
return Qfalse;
return check_definition(mod, rb_to_id(mid), NOEX_PRIVATE);
}
/*
@ -731,70 +734,100 @@ rb_mod_private_method_defined(VALUE mod, VALUE mid)
static VALUE
rb_mod_protected_method_defined(VALUE mod, VALUE mid)
{
ID id = rb_to_id(mid);
NODE *method;
return check_definition(mod, rb_to_id(mid), NOEX_PROTECTED);
}
method = rb_method_node(mod, id);
if (method) {
if (VISI_CHECK(method->nd_noex, NOEX_PROTECTED))
return Qtrue;
static void *
me_opts(const rb_method_entry_t *me)
{
switch (me->type) {
case VM_METHOD_TYPE_ISEQ:
return me->body.iseq;
case VM_METHOD_TYPE_CFUNC:
return (void *)&me->body.cfunc;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
return (void *)me->body.attr_id;
case VM_METHOD_TYPE_BMETHOD:
return (void *)me->body.proc;
case VM_METHOD_TYPE_ZSUPER:
case VM_METHOD_TYPE_NOTIMPLEMENTED:
case VM_METHOD_TYPE_UNDEF:
return 0;
default:
rb_bug("rb_add_method: unsupported method type (%d)\n", me->type);
return 0;
}
}
void
rb_add_method_me(VALUE klass, ID mid, const rb_method_entry_t *me, rb_method_flag_t noex)
{
rb_add_method(klass, mid, me->type, me_opts(me), noex);
}
int
rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2)
{
if (m1->type != m2->type) {
return 0;
}
switch (m1->type) {
case VM_METHOD_TYPE_ISEQ:
return m1->body.iseq == m2->body.iseq;
case VM_METHOD_TYPE_CFUNC:
return
m1->body.cfunc.func == m2->body.cfunc.func &&
m1->body.cfunc.argc == m2->body.cfunc.argc;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
return m1->body.attr_id == m2->body.attr_id;
case VM_METHOD_TYPE_BMETHOD:
return m1->body.proc == m2->body.proc;
case VM_METHOD_TYPE_ZSUPER:
case VM_METHOD_TYPE_NOTIMPLEMENTED:
case VM_METHOD_TYPE_UNDEF:
return 1;
default:
rb_bug("rb_add_method: unsupported method type (%d)\n", m1->type);
return 0;
}
return Qfalse;
}
void
rb_alias(VALUE klass, ID name, ID def)
{
NODE *orig_fbody, *node, *method;
rb_method_entry_t *orig_me, *me;
VALUE singleton = 0;
st_data_t data;
rb_frozen_class_p(klass);
if (klass == rb_cObject) {
rb_secure(4);
}
orig_fbody = search_method(klass, def, 0);
if (!orig_fbody || !orig_fbody->nd_body) {
orig_me = search_method(klass, def);
if (!orig_me || orig_me->type == VM_METHOD_TYPE_UNDEF) {
if (TYPE(klass) == T_MODULE) {
orig_fbody = search_method(rb_cObject, def, 0);
orig_me = search_method(rb_cObject, def);
if (!orig_me || !orig_me->type == VM_METHOD_TYPE_UNDEF) {
rb_print_undef(klass, def, 0);
}
}
}
if (!orig_fbody || !orig_fbody->nd_body) {
rb_print_undef(klass, def, 0);
}
if (FL_TEST(klass, FL_SINGLETON)) {
singleton = rb_iv_get(klass, "__attached__");
}
orig_fbody->nd_cnt++;
if (st_lookup(RCLASS_M_TBL(klass), name, &data)) {
node = (NODE *)data;
if (node) {
if (RTEST(ruby_verbose) && node->nd_cnt == 0 && node->nd_body) {
rb_warning("discarding old %s", rb_id2name(name));
}
if (nd_type(node->nd_body->nd_body) == NODE_CFUNC) {
rb_vm_check_redefinition_opt_method(node);
}
}
}
st_insert(RCLASS_M_TBL(klass), name,
(st_data_t) NEW_NODE_LONGLIFE(
NODE_FBODY,
def,
method = NEW_NODE_LONGLIFE(NODE_METHOD,
rb_gc_write_barrier((VALUE)orig_fbody->nd_body->nd_clss),
rb_gc_write_barrier((VALUE)orig_fbody->nd_body->nd_body),
NOEX_WITH_SAFE(orig_fbody->nd_body->nd_noex)),
0));
method->nd_file = (void *)def;
rb_clear_cache_by_id(name);
orig_me->alias_count++;
me = rb_add_method(klass, name, orig_me->type, me_opts(orig_me), orig_me->flag);
me->original_id = def;
if (!ruby_running) return;
rb_clear_cache_by_id(name);
if (singleton) {
rb_funcall(singleton, singleton_added, 1, ID2SYM(name));
}
@ -832,18 +865,6 @@ rb_mod_alias_method(VALUE mod, VALUE newname, VALUE oldname)
return mod;
}
VALUE
rb_f_notimplement(int argc, VALUE *argv, VALUE obj)
{
rb_notimplement();
}
void
rb_define_notimplement_method_id(VALUE mod, ID id, int noex)
{
rb_add_method(mod, id, notimplement_body, noex);
}
static void
secure_visibility(VALUE self)
{
@ -1042,7 +1063,7 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
{
int i;
ID id;
NODE *fbody;
const rb_method_entry_t *me;
if (TYPE(module) != T_MODULE) {
rb_raise(rb_eTypeError, "module_function must be called for modules");
@ -1061,22 +1082,21 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
id = rb_to_id(argv[i]);
for (;;) {
fbody = search_method(m, id, &m);
if (fbody == 0) {
fbody = search_method(rb_cObject, id, &m);
me = search_method(m, id);
if (me == 0) {
me = search_method(rb_cObject, id);
}
if (fbody == 0 || fbody->nd_body == 0) {
if (me == 0 || me->type == VM_METHOD_TYPE_UNDEF) {
rb_print_undef(module, id, 0);
}
if (nd_type(fbody->nd_body->nd_body) != NODE_ZSUPER) {
break; /* normal case: need not to follow 'super' link */
if (me->type != VM_METHOD_TYPE_ZSUPER) {
break; /* normal case: need not to follow 'super' link */
}
m = RCLASS_SUPER(m);
if (!m)
break;
}
rb_add_method(rb_singleton_class(module), id, fbody->nd_body->nd_body,
NOEX_PUBLIC);
rb_add_method_me(rb_singleton_class(module), id, me, NOEX_PUBLIC);
}
return module;
}
@ -1084,8 +1104,8 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
int
rb_method_basic_definition_p(VALUE klass, ID id)
{
NODE *node = rb_method_node(klass, id);
if (node && (node->nd_noex & NOEX_BASIC))
const rb_method_entry_t *me = rb_method_entry(klass, id);
if (me && (me->flag & NOEX_BASIC))
return 1;
return 0;
}
@ -1174,8 +1194,5 @@ Init_eval_method(void)
singleton_removed = rb_intern("singleton_method_removed");
undefined = rb_intern("method_undefined");
singleton_undefined = rb_intern("singleton_method_undefined");
notimplement_body = NEW_CFUNC(rb_f_notimplement, -1);
rb_gc_register_mark_object((VALUE)notimplement_body);
}