mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using parameters such as `op_id', 'op_argc', `blockiseq' and flag. These information are stored in rb_call_info_t at the compile time. This technique simplifies parameter passings at related function calls (~10% speedups for simple mehtod invocation at my machine). `rb_call_info_t' also has new function pointer variable `call'. This `call' variable enables to customize method (block) invocation process for each place. However, it always call `vm_call_general()' at this changes. `rb_call_info_t' also has temporary variables for method (block) invocation. * vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP VM_CALL macro. This flag indicates that this call can skip caller_setup (block arg and splat arg). * compile.c: catch up above changes. * iseq.c: catch up above changes (especially for TS_CALLINFO). * tool/instruction.rb: catch up above chagnes. * vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions parameters are changed. * vm_eval.c (vm_call0): ditto (it will be rewriten soon). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
e8c234577f
commit
cbd597e9bc
9 changed files with 671 additions and 550 deletions
32
ChangeLog
32
ChangeLog
|
@ -1,3 +1,35 @@
|
||||||
|
Mon Oct 15 01:38:06 2012 Koichi Sasada <ko1@atdot.net>
|
||||||
|
|
||||||
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
|
||||||
|
use only a `ci' (rb_call_info_t) parameter instead of using
|
||||||
|
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
|
||||||
|
These information are stored in rb_call_info_t at the compile
|
||||||
|
time.
|
||||||
|
This technique simplifies parameter passings at related
|
||||||
|
function calls (~10% speedups for simple mehtod invocation at
|
||||||
|
my machine).
|
||||||
|
`rb_call_info_t' also has new function pointer variable `call'.
|
||||||
|
This `call' variable enables to customize method (block)
|
||||||
|
invocation process for each place. However, it always call
|
||||||
|
`vm_call_general()' at this changes.
|
||||||
|
`rb_call_info_t' also has temporary variables for method
|
||||||
|
(block) invocation.
|
||||||
|
|
||||||
|
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
|
||||||
|
VM_CALL macro. This flag indicates that this call can skip
|
||||||
|
caller_setup (block arg and splat arg).
|
||||||
|
|
||||||
|
* compile.c: catch up above changes.
|
||||||
|
|
||||||
|
* iseq.c: catch up above changes (especially for TS_CALLINFO).
|
||||||
|
|
||||||
|
* tool/instruction.rb: catch up above chagnes.
|
||||||
|
|
||||||
|
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
|
||||||
|
parameters are changed.
|
||||||
|
|
||||||
|
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
|
||||||
|
|
||||||
Sun Oct 14 12:30:44 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Sun Oct 14 12:30:44 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* ruby.c (rb_f_sub, rb_f_gsub): pass the given block.
|
* ruby.c (rb_f_sub, rb_f_gsub): pass the given block.
|
||||||
|
|
181
compile.c
181
compile.c
|
@ -935,20 +935,36 @@ new_insn_body(rb_iseq_t *iseq, int line_no, int insn_id, int argc, ...)
|
||||||
return new_insn_core(iseq, line_no, insn_id, argc, operands);
|
return new_insn_core(iseq, line_no, insn_id, argc, operands);
|
||||||
}
|
}
|
||||||
|
|
||||||
static INSN *
|
static rb_call_info_t *
|
||||||
new_insn_send(rb_iseq_t *iseq, int line_no,
|
new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned long flag)
|
||||||
VALUE id, VALUE argc, VALUE block, VALUE flag)
|
|
||||||
{
|
{
|
||||||
INSN *iobj = 0;
|
rb_call_info_t *ci = (rb_call_info_t *)compile_data_alloc(iseq, sizeof(rb_call_info_t));
|
||||||
VALUE *operands =
|
ci->mid = mid;
|
||||||
(VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 5);
|
ci->flag = flag;
|
||||||
operands[0] = id;
|
ci->orig_argc = argc;
|
||||||
operands[1] = argc;
|
|
||||||
operands[2] = block;
|
if (block) {
|
||||||
operands[3] = flag;
|
GetISeqPtr(block, ci->blockiseq);
|
||||||
operands[4] = INT2FIX(iseq->callinfo_size++);
|
}
|
||||||
iobj = new_insn_core(iseq, line_no, BIN(send), 5, operands);
|
else {
|
||||||
return iobj;
|
ci->blockiseq = 0;
|
||||||
|
if (!(ci->flag & (VM_CALL_ARGS_BLOCKARG_BIT | VM_CALL_ARGS_SPLAT_BIT))) {
|
||||||
|
ci->flag |= VM_CALL_ARGS_SKIP_SETUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ci->vmstat = 0;
|
||||||
|
ci->argc = iseq->callinfo_size++; /* index of callinfo in this iseq */
|
||||||
|
|
||||||
|
return ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
static INSN *
|
||||||
|
new_insn_send(rb_iseq_t *iseq, int line_no, VALUE id, VALUE argc, VALUE block, VALUE flag)
|
||||||
|
{
|
||||||
|
|
||||||
|
VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 1);
|
||||||
|
operands[0] = (VALUE)new_callinfo(iseq, SYM2ID(id), FIX2INT(argc), block, FIX2INT(flag));
|
||||||
|
return new_insn_core(iseq, line_no, BIN(send), 1, operands);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -1399,7 +1415,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
|
||||||
iseq->ic_entries = ALLOC_N(struct iseq_inline_cache_entry, iseq->ic_size);
|
iseq->ic_entries = ALLOC_N(struct iseq_inline_cache_entry, iseq->ic_size);
|
||||||
MEMZERO(iseq->ic_entries, struct iseq_inline_cache_entry, iseq->ic_size);
|
MEMZERO(iseq->ic_entries, struct iseq_inline_cache_entry, iseq->ic_size);
|
||||||
iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size);
|
iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size);
|
||||||
MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size);
|
/* MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); */
|
||||||
|
|
||||||
list = FIRST_ELEMENT(anchor);
|
list = FIRST_ELEMENT(anchor);
|
||||||
k = pos = sp = 0;
|
k = pos = sp = 0;
|
||||||
|
@ -1504,10 +1520,12 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
|
||||||
}
|
}
|
||||||
case TS_CALLINFO: /* call info */
|
case TS_CALLINFO: /* call info */
|
||||||
{
|
{
|
||||||
int ci_index = FIX2INT(operands[j]);
|
rb_call_info_t *base_ci = (rb_call_info_t *)operands[j];
|
||||||
CALL_INFO ci = &iseq->callinfo_entries[ci_index];
|
rb_call_info_t *ci = &iseq->callinfo_entries[base_ci->argc];
|
||||||
if (UNLIKELY(ci_index >= iseq->callinfo_size)) {
|
*ci = *base_ci;
|
||||||
rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", ci_index, iseq->callinfo_size);
|
|
||||||
|
if (UNLIKELY(base_ci->argc >= iseq->callinfo_size)) {
|
||||||
|
rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", base_ci->argc, iseq->callinfo_size);
|
||||||
}
|
}
|
||||||
generated_iseq[pos + 1 + j] = (VALUE)ci;
|
generated_iseq[pos + 1 + j] = (VALUE)ci;
|
||||||
break;
|
break;
|
||||||
|
@ -1847,10 +1865,11 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
|
||||||
*/
|
*/
|
||||||
INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
|
INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
|
||||||
|
|
||||||
if (piobj->insn_id == BIN(send) &&
|
if (piobj->insn_id == BIN(send)) {
|
||||||
piobj->operands[2] == 0 /* block */
|
rb_call_info_t *ci = (rb_call_info_t *)piobj->operands[0];
|
||||||
) {
|
if (ci->blockiseq == 0) {
|
||||||
piobj->operands[3] = FIXNUM_OR(piobj->operands[3], VM_CALL_TAILCALL_BIT);
|
ci->flag |= VM_CALL_TAILCALL_BIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return COMPILE_OK;
|
return COMPILE_OK;
|
||||||
|
@ -1859,18 +1878,18 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
|
||||||
static int
|
static int
|
||||||
insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
|
insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
|
||||||
{
|
{
|
||||||
int i, old_opsize = iobj->operand_size;
|
int old_opsize = iobj->operand_size;
|
||||||
|
|
||||||
iobj->insn_id = insn_id;
|
iobj->insn_id = insn_id;
|
||||||
iobj->operand_size = insn_len(insn_id) - 1;
|
iobj->operand_size = insn_len(insn_id) - 1;
|
||||||
/* printf("iobj->operand_size: %d\n", iobj->operand_size); */
|
|
||||||
|
|
||||||
if (iobj->operand_size > old_opsize) {
|
if (iobj->operand_size > old_opsize) {
|
||||||
iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size);
|
VALUE *old_operands = iobj->operands;
|
||||||
|
if (insn_id != BIN(opt_neq)) {
|
||||||
|
rb_bug("insn_set_specialized_instruction: unknown insn: %d", insn_id);
|
||||||
}
|
}
|
||||||
|
iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE));
|
||||||
for (i=0; i<iobj->operand_size; i++) {
|
iobj->operands[0] = old_operands[0];
|
||||||
iobj->operands[i] = INT2FIX(iseq->callinfo_size++);
|
iobj->operands[1] = (VALUE)new_callinfo(iseq, idEq, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return COMPILE_OK;
|
return COMPILE_OK;
|
||||||
|
@ -1880,17 +1899,13 @@ static int
|
||||||
iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
||||||
{
|
{
|
||||||
if (iobj->insn_id == BIN(send)) {
|
if (iobj->insn_id == BIN(send)) {
|
||||||
ID mid = SYM2ID(OPERAND_AT(iobj, 0));
|
rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, 0);
|
||||||
int argc = FIX2INT(OPERAND_AT(iobj, 1));
|
|
||||||
VALUE block = OPERAND_AT(iobj, 2);
|
|
||||||
VALUE flag = OPERAND_AT(iobj, 3);
|
|
||||||
|
|
||||||
#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
|
#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
|
||||||
|
if (ci->blockiseq == 0 && (ci->flag & ~VM_CALL_ARGS_SKIP_SETUP) == 0) {
|
||||||
if (block == 0 && flag == INT2FIX(0)) {
|
switch (ci->orig_argc) {
|
||||||
switch (argc) {
|
|
||||||
case 0:
|
case 0:
|
||||||
switch (mid) {
|
switch (ci->mid) {
|
||||||
case idLength: SP_INSN(length); break;
|
case idLength: SP_INSN(length); break;
|
||||||
case idSize: SP_INSN(size); break;
|
case idSize: SP_INSN(size); break;
|
||||||
case idEmptyP: SP_INSN(empty_p);break;
|
case idEmptyP: SP_INSN(empty_p);break;
|
||||||
|
@ -1899,7 +1914,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
switch (mid) {
|
switch (ci->mid) {
|
||||||
case idPLUS: SP_INSN(plus); break;
|
case idPLUS: SP_INSN(plus); break;
|
||||||
case idMINUS: SP_INSN(minus); break;
|
case idMINUS: SP_INSN(minus); break;
|
||||||
case idMULT: SP_INSN(mult); break;
|
case idMULT: SP_INSN(mult); break;
|
||||||
|
@ -1918,8 +1933,9 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return COMPILE_OK;
|
|
||||||
#undef SP_INSN
|
#undef SP_INSN
|
||||||
|
|
||||||
|
return COMPILE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -2502,15 +2518,16 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
|
||||||
switch (nd_type(node)) {
|
switch (nd_type(node)) {
|
||||||
case NODE_ATTRASGN: {
|
case NODE_ATTRASGN: {
|
||||||
INSN *iobj;
|
INSN *iobj;
|
||||||
|
rb_call_info_t *ci;
|
||||||
VALUE dupidx;
|
VALUE dupidx;
|
||||||
|
|
||||||
COMPILE_POPED(ret, "masgn lhs (NODE_ATTRASGN)", node);
|
COMPILE_POPED(ret, "masgn lhs (NODE_ATTRASGN)", node);
|
||||||
|
|
||||||
POP_ELEMENT(ret); /* pop pop insn */
|
POP_ELEMENT(ret); /* pop pop insn */
|
||||||
iobj = (INSN *)POP_ELEMENT(ret); /* pop send insn */
|
iobj = (INSN *)POP_ELEMENT(ret); /* pop send insn */
|
||||||
|
ci = (rb_call_info_t *)iobj->operands[0];
|
||||||
dupidx = iobj->operands[1];
|
ci->orig_argc += 1;
|
||||||
dupidx = FIXNUM_INC(dupidx, 1);
|
dupidx = INT2FIX(ci->orig_argc);
|
||||||
iobj->operands[1] = dupidx;
|
|
||||||
|
|
||||||
ADD_INSN1(ret, nd_line(node), topn, dupidx);
|
ADD_INSN1(ret, nd_line(node), topn, dupidx);
|
||||||
ADD_ELEM(ret, (LINK_ELEMENT *)iobj);
|
ADD_ELEM(ret, (LINK_ELEMENT *)iobj);
|
||||||
|
@ -4355,11 +4372,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dummy receiver */
|
/* dummy receiver */
|
||||||
ADD_INSN1(ret, nd_line(node), putobject,
|
ADD_INSN1(ret, nd_line(node), putobject, nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
|
||||||
nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
|
|
||||||
ADD_SEQ(ret, args);
|
ADD_SEQ(ret, args);
|
||||||
ADD_INSN3(ret, nd_line(node), invokesuper,
|
ADD_INSN1(ret, nd_line(node), invokesuper, new_callinfo(iseq, 0, FIX2INT(argc), parent_block,
|
||||||
argc, parent_block, LONG2FIX(flag));
|
flag | VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT));
|
||||||
|
|
||||||
if (poped) {
|
if (poped) {
|
||||||
ADD_INSN(ret, nd_line(node), pop);
|
ADD_INSN(ret, nd_line(node), pop);
|
||||||
|
@ -4468,7 +4484,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
||||||
}
|
}
|
||||||
|
|
||||||
ADD_SEQ(ret, args);
|
ADD_SEQ(ret, args);
|
||||||
ADD_INSN2(ret, nd_line(node), invokeblock, argc, LONG2FIX(flag));
|
ADD_INSN1(ret, nd_line(node), invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), 0, flag));
|
||||||
|
|
||||||
if (poped) {
|
if (poped) {
|
||||||
ADD_INSN(ret, nd_line(node), pop);
|
ADD_INSN(ret, nd_line(node), pop);
|
||||||
|
@ -5225,7 +5241,7 @@ insn_data_to_s_detail(INSN *iobj)
|
||||||
{
|
{
|
||||||
rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
|
rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
|
||||||
VALUE val = Qnil;
|
VALUE val = Qnil;
|
||||||
if (iseq) {
|
if (0 && iseq) { /* TODO: invalidate now */
|
||||||
val = iseq->self;
|
val = iseq->self;
|
||||||
}
|
}
|
||||||
rb_str_concat(str, rb_inspect(val));
|
rb_str_concat(str, rb_inspect(val));
|
||||||
|
@ -5234,8 +5250,11 @@ insn_data_to_s_detail(INSN *iobj)
|
||||||
case TS_LINDEX:
|
case TS_LINDEX:
|
||||||
case TS_NUM: /* ulong */
|
case TS_NUM: /* ulong */
|
||||||
case TS_VALUE: /* VALUE */
|
case TS_VALUE: /* VALUE */
|
||||||
rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j)));
|
{
|
||||||
|
VALUE v = OPERAND_AT(iobj, j);
|
||||||
|
rb_str_concat(str, rb_inspect(v));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TS_ID: /* ID */
|
case TS_ID: /* ID */
|
||||||
rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j)));
|
rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j)));
|
||||||
break;
|
break;
|
||||||
|
@ -5244,13 +5263,20 @@ insn_data_to_s_detail(INSN *iobj)
|
||||||
struct rb_global_entry *entry = (struct rb_global_entry *)
|
struct rb_global_entry *entry = (struct rb_global_entry *)
|
||||||
(OPERAND_AT(iobj, j) & (~1));
|
(OPERAND_AT(iobj, j) & (~1));
|
||||||
rb_str_cat2(str, rb_id2name(entry->id));
|
rb_str_cat2(str, rb_id2name(entry->id));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case TS_IC: /* inline cache */
|
case TS_IC: /* inline cache */
|
||||||
rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
|
rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
|
||||||
break;
|
break;
|
||||||
case TS_CALLINFO: /* call info */
|
case TS_CALLINFO: /* call info */
|
||||||
rb_str_catf(str, "<callinfo:%d>", FIX2INT(OPERAND_AT(iobj, j)));
|
{
|
||||||
|
rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, j);
|
||||||
|
rb_str_catf(str, "<callinfo:%s, %d>", ci->mid ? rb_id2name(ci->mid) : "", ci->orig_argc);
|
||||||
|
if (ci->orig_argc > 60) {
|
||||||
|
rb_bug("xyzzy");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TS_CDHASH: /* case/when condition cache */
|
case TS_CDHASH: /* case/when condition cache */
|
||||||
rb_str_cat2(str, "<ch>");
|
rb_str_cat2(str, "<ch>");
|
||||||
break;
|
break;
|
||||||
|
@ -5282,8 +5308,7 @@ dump_disasm_list(struct iseq_link_element *link)
|
||||||
{
|
{
|
||||||
iobj = (INSN *)link;
|
iobj = (INSN *)link;
|
||||||
str = insn_data_to_s_detail(iobj);
|
str = insn_data_to_s_detail(iobj);
|
||||||
printf("%04d %-65s(%4d)\n", pos, StringValueCStr(str),
|
printf("%04d %-65s(%4d)\n", pos, StringValueCStr(str), insn_data_line_no(iobj));
|
||||||
insn_data_line_no(iobj));
|
|
||||||
pos += insn_data_length(iobj);
|
pos += insn_data_length(iobj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -5422,6 +5447,23 @@ insn_make_insn_table(void)
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
iseq_build_load_iseq(rb_iseq_t *iseq, VALUE op)
|
||||||
|
{
|
||||||
|
VALUE iseqval;
|
||||||
|
if (RB_TYPE_P(op, T_ARRAY)) {
|
||||||
|
iseqval = rb_iseq_load(op, iseq->self, Qnil);
|
||||||
|
}
|
||||||
|
else if (CLASS_OF(op) == rb_cISeq) {
|
||||||
|
iseqval = op;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_raise(rb_eSyntaxError, "ISEQ is required");
|
||||||
|
}
|
||||||
|
iseq_add_mark_object(iseq, iseqval);
|
||||||
|
return iseqval;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
|
iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
|
||||||
VALUE body, struct st_table *labels_table)
|
VALUE body, struct st_table *labels_table)
|
||||||
|
@ -5492,16 +5534,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
|
||||||
case TS_ISEQ:
|
case TS_ISEQ:
|
||||||
{
|
{
|
||||||
if (op != Qnil) {
|
if (op != Qnil) {
|
||||||
if (RB_TYPE_P(op, T_ARRAY)) {
|
argv[j] = iseq_build_load_iseq(iseq, op);
|
||||||
argv[j] = rb_iseq_load(op, iseq->self, Qnil);
|
|
||||||
}
|
|
||||||
else if (CLASS_OF(op) == rb_cISeq) {
|
|
||||||
argv[j] = op;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_raise(rb_eSyntaxError, "ISEQ is required");
|
|
||||||
}
|
|
||||||
iseq_add_mark_object(iseq, argv[j]);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
argv[j] = 0;
|
argv[j] = 0;
|
||||||
|
@ -5519,10 +5552,26 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TS_CALLINFO:
|
case TS_CALLINFO:
|
||||||
argv[j] = op;
|
{
|
||||||
if (NUM2INT(op) >= iseq->callinfo_size) {
|
ID mid = 0;
|
||||||
iseq->callinfo_size = NUM2INT(op) + 1;
|
int orig_argc = 0;
|
||||||
|
VALUE block = 0;
|
||||||
|
unsigned long flag = 0;
|
||||||
|
|
||||||
|
if (!NIL_P(op)) {
|
||||||
|
VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern("mid")));
|
||||||
|
VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern("flag")));
|
||||||
|
VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern("orig_argc")));
|
||||||
|
VALUE vblock = rb_hash_aref(op, ID2SYM(rb_intern("block")));
|
||||||
|
|
||||||
|
if (!NIL_P(vmid)) mid = SYM2ID(vmid);
|
||||||
|
if (!NIL_P(vflag)) flag = NUM2ULONG(vflag);
|
||||||
|
if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
|
||||||
|
if (!NIL_P(vblock)) block = iseq_build_load_iseq(iseq, vblock);
|
||||||
}
|
}
|
||||||
|
argv[j] = (VALUE)new_callinfo(iseq, mid, orig_argc, block, flag);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TS_ID:
|
case TS_ID:
|
||||||
argv[j] = rb_convert_type(op, T_SYMBOL,
|
argv[j] = rb_convert_type(op, T_SYMBOL,
|
||||||
"Symbol", "to_sym");
|
"Symbol", "to_sym");
|
||||||
|
|
140
insns.def
140
insns.def
|
@ -952,100 +952,62 @@ defineclass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@c method/iterator
|
@c method/iterator
|
||||||
@e obj.send(id, args..) # args.size => num
|
@e invoke method.
|
||||||
@j メソッド呼び出しを行う。
|
@j メソッド呼び出しを行う。ci に必要な情報が格納されている。
|
||||||
obj.send(id, args..) # args.size => num
|
|
||||||
flag & VM_CALL_ARGS_SPLAT_BIT != 0 -> splat last arg
|
|
||||||
flag & VM_CALL_ARGS_BLOCKARG_BIT != 0 -> Proc as Block
|
|
||||||
flag & VM_CALL_FCALL_BIT != 0 -> FCALL ( func() )
|
|
||||||
flag & VM_CALL_VCALL_BIT != 0 -> VCALL ( func )
|
|
||||||
...
|
|
||||||
*/
|
*/
|
||||||
DEFINE_INSN
|
DEFINE_INSN
|
||||||
send
|
send
|
||||||
(ID op_id, rb_num_t op_argc, ISEQ blockiseq, rb_num_t op_flag, CALL_INFO ci)
|
(CALL_INFO ci)
|
||||||
(...)
|
(...)
|
||||||
(VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0));
|
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0));
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *me;
|
ci->argc = ci->orig_argc;
|
||||||
VALUE recv, klass, defined_class;
|
ci->blockptr = 0;
|
||||||
rb_block_t *blockptr = 0;
|
|
||||||
VALUE flag = op_flag;
|
|
||||||
int num = caller_setup_args(th, GET_CFP(), flag, (int)op_argc,
|
|
||||||
(rb_iseq_t *)blockiseq, &blockptr);
|
|
||||||
ID id = op_id;
|
|
||||||
|
|
||||||
/* get receiver */
|
if (!LIKELY(ci->flag & VM_CALL_ARGS_SKIP_SETUP)) {
|
||||||
recv = TOPN(num);
|
vm_caller_setup_args(th, reg_cfp, ci);
|
||||||
klass = CLASS_OF(recv);
|
}
|
||||||
me = vm_method_search(id, klass, ci, &defined_class);
|
vm_search_method(ci, ci->recv = TOPN(ci->argc));
|
||||||
CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class);
|
CALL_METHOD(ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@c method/iterator
|
@c method/iterator
|
||||||
@e super(args) # args.size => num
|
@e super(args) # args.size => num
|
||||||
@j super を実行する。
|
@j super を実行する。ci に必要な情報が格納されている。
|
||||||
super(args) # args.size => num
|
|
||||||
flag 等オペランドの意味は send と同じ。
|
|
||||||
*/
|
*/
|
||||||
DEFINE_INSN
|
DEFINE_INSN
|
||||||
invokesuper
|
invokesuper
|
||||||
(rb_num_t op_argc, ISEQ blockiseq, rb_num_t op_flag)
|
(CALL_INFO ci)
|
||||||
(...)
|
(...)
|
||||||
(VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0));
|
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0));
|
||||||
{
|
{
|
||||||
rb_block_t *blockptr = !(op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? GET_BLOCK_PTR() : 0;
|
ci->argc = ci->orig_argc;
|
||||||
VALUE flag = op_flag;
|
ci->blockptr = !(ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) ? GET_BLOCK_PTR() : 0;
|
||||||
int num = caller_setup_args(th, GET_CFP(), flag,
|
|
||||||
(int)op_argc, blockiseq, &blockptr);
|
|
||||||
VALUE recv, klass;
|
|
||||||
ID id;
|
|
||||||
const rb_method_entry_t *me;
|
|
||||||
rb_iseq_t *ip;
|
|
||||||
|
|
||||||
flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT;
|
if (!LIKELY(ci->flag & VM_CALL_ARGS_SKIP_SETUP)) {
|
||||||
|
vm_caller_setup_args(th, reg_cfp, ci);
|
||||||
klass = GET_CFP()->klass;
|
|
||||||
if (NIL_P(klass)) {
|
|
||||||
vm_super_outside();
|
|
||||||
}
|
}
|
||||||
if (!NIL_P(RCLASS_REFINED_CLASS(klass))) {
|
ci->recv = GET_SELF();
|
||||||
klass = RCLASS_REFINED_CLASS(klass);
|
vm_search_super_method(th, GET_CFP(), ci);
|
||||||
}
|
CALL_METHOD(ci);
|
||||||
recv = GET_SELF();
|
|
||||||
if (!rb_obj_is_kind_of(recv, klass)) {
|
|
||||||
rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later");
|
|
||||||
}
|
|
||||||
vm_search_superclass(GET_CFP(), GET_ISEQ(), TOPN(num), &id, &klass);
|
|
||||||
|
|
||||||
ip = GET_ISEQ();
|
|
||||||
while (ip && !ip->klass) {
|
|
||||||
ip = ip->parent_iseq;
|
|
||||||
}
|
|
||||||
me = rb_method_entry(klass, id, &klass);
|
|
||||||
if (me && me->def->type == VM_METHOD_TYPE_ISEQ &&
|
|
||||||
me->def->body.iseq == ip) {
|
|
||||||
klass = RCLASS_SUPER(klass);
|
|
||||||
me = rb_method_entry_get_with_refinements(Qnil, klass, id, &klass);
|
|
||||||
}
|
|
||||||
|
|
||||||
CALL_METHOD(num, blockptr, flag, id, me, recv, klass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@c method/iterator
|
@c method/iterator
|
||||||
@e yield(args) # args.size => num, flag shows expand argument or not
|
@e yield(args)
|
||||||
@j yield を実行する。
|
@j yield を実行する。
|
||||||
yield(args) # args.size => num
|
|
||||||
*/
|
*/
|
||||||
DEFINE_INSN
|
DEFINE_INSN
|
||||||
invokeblock
|
invokeblock
|
||||||
(rb_num_t num, rb_num_t flag)
|
(CALL_INFO ci)
|
||||||
(...)
|
(...)
|
||||||
(VALUE val) // inc += 1 - num;
|
(VALUE val) // inc += 1 - ci->orig_argc;
|
||||||
{
|
{
|
||||||
val = vm_invoke_block(th, GET_CFP(), num, flag);
|
ci->argc = ci->orig_argc;
|
||||||
|
ci->blockptr = 0;
|
||||||
|
ci->recv = GET_SELF();
|
||||||
|
val = vm_invoke_block(th, GET_CFP(), ci);
|
||||||
if (val == Qundef) {
|
if (val == Qundef) {
|
||||||
RESTORE_REGS();
|
RESTORE_REGS();
|
||||||
NEXT_INSN();
|
NEXT_INSN();
|
||||||
|
@ -1338,7 +1300,7 @@ opt_plus
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idPLUS, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1386,7 +1348,7 @@ opt_minus
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idMINUS, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1439,7 +1401,7 @@ opt_mult
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idMULT, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1502,7 +1464,7 @@ opt_div
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idDIV, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1566,7 +1528,7 @@ opt_mod
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idMOD, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1587,7 +1549,7 @@ opt_eq
|
||||||
/* other */
|
/* other */
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idEq, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1603,10 +1565,10 @@ opt_neq
|
||||||
(VALUE val)
|
(VALUE val)
|
||||||
{
|
{
|
||||||
extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
|
extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
|
||||||
const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ci, 0);
|
vm_search_method(ci, recv);
|
||||||
val = Qundef;
|
val = Qundef;
|
||||||
|
|
||||||
if (check_cfunc(me, rb_obj_not_equal)) {
|
if (check_cfunc(ci->me, rb_obj_not_equal)) {
|
||||||
val = opt_eq_func(recv, obj, ci_eq);
|
val = opt_eq_func(recv, obj, ci_eq);
|
||||||
|
|
||||||
if (val != Qundef) {
|
if (val != Qundef) {
|
||||||
|
@ -1618,7 +1580,7 @@ opt_neq
|
||||||
/* other */
|
/* other */
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idNeq, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,7 +1624,7 @@ opt_lt
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idLT, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1697,7 +1659,7 @@ opt_le
|
||||||
/* other */
|
/* other */
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idLE, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1741,7 +1703,7 @@ opt_gt
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idGT, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1775,7 +1737,7 @@ opt_ge
|
||||||
else {
|
else {
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idGE, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1807,7 +1769,7 @@ opt_ltlt
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idLTLT, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1837,7 +1799,7 @@ opt_aref
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
CALL_SIMPLE_METHOD(1, idAREF, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1870,7 +1832,7 @@ opt_aset
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
PUSH(set);
|
PUSH(set);
|
||||||
CALL_SIMPLE_METHOD(2, idASET, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1905,7 +1867,7 @@ opt_length
|
||||||
else {
|
else {
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
CALL_SIMPLE_METHOD(0, idLength, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1940,7 +1902,7 @@ opt_size
|
||||||
else {
|
else {
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
CALL_SIMPLE_METHOD(0, idSize, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1978,7 +1940,7 @@ opt_empty_p
|
||||||
else {
|
else {
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
CALL_SIMPLE_METHOD(0, idEmptyP, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2025,7 +1987,7 @@ opt_succ
|
||||||
if (0) {
|
if (0) {
|
||||||
INSN_LABEL(normal_dispatch):
|
INSN_LABEL(normal_dispatch):
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
CALL_SIMPLE_METHOD(0, idSucc, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2041,14 +2003,14 @@ opt_not
|
||||||
(VALUE val)
|
(VALUE val)
|
||||||
{
|
{
|
||||||
extern VALUE rb_obj_not(VALUE obj);
|
extern VALUE rb_obj_not(VALUE obj);
|
||||||
const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ci, 0);
|
vm_search_method(ci, recv);
|
||||||
|
|
||||||
if (check_cfunc(me, rb_obj_not)) {
|
if (check_cfunc(ci->me, rb_obj_not)) {
|
||||||
val = RTEST(recv) ? Qfalse : Qtrue;
|
val = RTEST(recv) ? Qfalse : Qtrue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PUSH(recv);
|
PUSH(recv);
|
||||||
CALL_SIMPLE_METHOD(0, idNot, recv);
|
CALL_SIMPLE_METHOD(recv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
iseq.c
44
iseq.c
|
@ -149,6 +149,7 @@ iseq_memsize(const void *ptr)
|
||||||
size += iseq->catch_table_size * sizeof(struct iseq_catch_table_entry);
|
size += iseq->catch_table_size * sizeof(struct iseq_catch_table_entry);
|
||||||
size += iseq->arg_opts * sizeof(VALUE);
|
size += iseq->arg_opts * sizeof(VALUE);
|
||||||
size += iseq->ic_size * sizeof(struct iseq_inline_cache_entry);
|
size += iseq->ic_size * sizeof(struct iseq_inline_cache_entry);
|
||||||
|
size += iseq->callinfo_size * sizeof(rb_call_info_t);
|
||||||
|
|
||||||
if (iseq->compile_data) {
|
if (iseq->compile_data) {
|
||||||
struct iseq_compile_data_storage *cur;
|
struct iseq_compile_data_storage *cur;
|
||||||
|
@ -1041,7 +1042,37 @@ insn_operand_intern(rb_iseq_t *iseq,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TS_CALLINFO:
|
case TS_CALLINFO:
|
||||||
ret = rb_sprintf("<ci:%"PRIdPTRDIFF">", (rb_call_info_t *)op - iseq->callinfo_entries);
|
{
|
||||||
|
rb_call_info_t *ci = (rb_call_info_t *)op;
|
||||||
|
VALUE ary = rb_ary_new();
|
||||||
|
|
||||||
|
if (ci->mid) {
|
||||||
|
rb_ary_push(ary, rb_sprintf("mid:%s", rb_id2name(ci->mid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_ary_push(ary, rb_sprintf("argc:%d", ci->orig_argc));
|
||||||
|
|
||||||
|
if (ci->blockiseq) {
|
||||||
|
if (child) {
|
||||||
|
rb_ary_push(child, ci->blockiseq->self);
|
||||||
|
}
|
||||||
|
rb_ary_push(ary, rb_sprintf("block:%"PRIsVALUE, ci->blockiseq->location.label));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ci->flag) {
|
||||||
|
VALUE flags = rb_ary_new();
|
||||||
|
if (ci->flag & VM_CALL_ARGS_SPLAT_BIT) rb_ary_push(flags, rb_str_new2("ARGS_SPLAT"));
|
||||||
|
if (ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) rb_ary_push(flags, rb_str_new2("ARGS_BLOCKARG"));
|
||||||
|
if (ci->flag & VM_CALL_FCALL_BIT) rb_ary_push(flags, rb_str_new2("FCALL"));
|
||||||
|
if (ci->flag & VM_CALL_VCALL_BIT) rb_ary_push(flags, rb_str_new2("VCALL"));
|
||||||
|
if (ci->flag & VM_CALL_TAILCALL_BIT) rb_ary_push(flags, rb_str_new2("TAILCALL"));
|
||||||
|
if (ci->flag & VM_CALL_SUPER_BIT) rb_ary_push(flags, rb_str_new2("SUPER"));
|
||||||
|
if (ci->flag & VM_CALL_OPT_SEND_BIT) rb_ary_push(flags, rb_str_new2("SNED")); /* maybe not reachable */
|
||||||
|
if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) rb_ary_push(flags, rb_str_new2("ARGS_SKIP"));
|
||||||
|
rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
|
||||||
|
}
|
||||||
|
ret = rb_sprintf("<callinfo!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TS_CDHASH:
|
case TS_CDHASH:
|
||||||
|
@ -1542,6 +1573,17 @@ iseq_data_to_ary(rb_iseq_t *iseq)
|
||||||
rb_ary_push(ary, INT2FIX(ic - iseq->ic_entries));
|
rb_ary_push(ary, INT2FIX(ic - iseq->ic_entries));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TS_CALLINFO:
|
||||||
|
{
|
||||||
|
rb_call_info_t *ci = (rb_call_info_t *)*seq;
|
||||||
|
VALUE e = rb_hash_new();
|
||||||
|
rb_hash_aset(e, ID2SYM(rb_intern("mid")), ci->mid ? ID2SYM(ci->mid) : Qnil);
|
||||||
|
rb_hash_aset(e, ID2SYM(rb_intern("flag")), ULONG2NUM(ci->flag));
|
||||||
|
rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")), INT2FIX(ci->orig_argc));
|
||||||
|
rb_hash_aset(e, ID2SYM(rb_intern("blockptr")), ci->blockiseq ? iseq_data_to_ary(ci->blockiseq) : Qnil);
|
||||||
|
rb_ary_push(ary, e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TS_ID:
|
case TS_ID:
|
||||||
rb_ary_push(ary, ID2SYM(*seq));
|
rb_ary_push(ary, ID2SYM(*seq));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -66,11 +66,15 @@ class RubyVM
|
||||||
ret = "int inc = 0;\n"
|
ret = "int inc = 0;\n"
|
||||||
|
|
||||||
@opes.each_with_index{|(t, v), i|
|
@opes.each_with_index{|(t, v), i|
|
||||||
if t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc ||
|
if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) ||
|
||||||
@defopes.any?{|t, val| re =~ val})
|
(@defopes.any?{|t, val| re =~ val})
|
||||||
ret << " int #{v} = FIX2INT(opes[#{i}]);\n"
|
ret << " int #{v} = FIX2INT(opes[#{i}]);\n"
|
||||||
|
elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc))
|
||||||
|
ret << " CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@defopes.each_with_index{|((t, var), val), i|
|
@defopes.each_with_index{|((t, var), val), i|
|
||||||
if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc
|
if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc
|
||||||
ret << " #{t} #{var} = #{val};\n"
|
ret << " #{t} #{var} = #{val};\n"
|
||||||
|
|
25
vm_core.h
25
vm_core.h
|
@ -138,15 +138,31 @@ struct iseq_inline_cache_entry {
|
||||||
} ic_value;
|
} ic_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* to avoid warning */
|
||||||
|
struct rb_thread_struct;
|
||||||
|
struct rb_control_frame_struct;
|
||||||
|
|
||||||
/* rb_call_info_t contains calling information including inline cache */
|
/* rb_call_info_t contains calling information including inline cache */
|
||||||
typedef struct rb_call_info_struct {
|
typedef struct rb_call_info_struct {
|
||||||
|
/* fixed at compile time */
|
||||||
|
ID mid;
|
||||||
|
VALUE flag;
|
||||||
|
int orig_argc;
|
||||||
|
rb_iseq_t *blockiseq;
|
||||||
|
|
||||||
/* inline cache: keys */
|
/* inline cache: keys */
|
||||||
VALUE ic_vmstat;
|
VALUE vmstat;
|
||||||
VALUE ic_class;
|
VALUE klass;
|
||||||
|
|
||||||
/* inline cache: values */
|
/* inline cache: values */
|
||||||
rb_method_entry_t *method;
|
const rb_method_entry_t *me;
|
||||||
VALUE defined_class;
|
VALUE defined_class;
|
||||||
|
|
||||||
|
/* temporary values for method calling */
|
||||||
|
int argc;
|
||||||
|
struct rb_block_struct *blockptr;
|
||||||
|
VALUE recv;
|
||||||
|
VALUE (*call)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_call_info_struct *ci);
|
||||||
} rb_call_info_t;
|
} rb_call_info_t;
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
|
@ -367,7 +383,7 @@ typedef struct rb_vm_struct {
|
||||||
#define VM_DEBUG_BP_CHECK 1
|
#define VM_DEBUG_BP_CHECK 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct rb_control_frame_struct {
|
||||||
VALUE *pc; /* cfp[0] */
|
VALUE *pc; /* cfp[0] */
|
||||||
VALUE *sp; /* cfp[1] */
|
VALUE *sp; /* cfp[1] */
|
||||||
rb_iseq_t *iseq; /* cfp[2] */
|
rb_iseq_t *iseq; /* cfp[2] */
|
||||||
|
@ -640,6 +656,7 @@ enum vm_check_match_type {
|
||||||
#define VM_CALL_TAILRECURSION_BIT (0x01 << 6)
|
#define VM_CALL_TAILRECURSION_BIT (0x01 << 6)
|
||||||
#define VM_CALL_SUPER_BIT (0x01 << 7)
|
#define VM_CALL_SUPER_BIT (0x01 << 7)
|
||||||
#define VM_CALL_OPT_SEND_BIT (0x01 << 8)
|
#define VM_CALL_OPT_SEND_BIT (0x01 << 8)
|
||||||
|
#define VM_CALL_ARGS_SKIP_SETUP (0x01 << 9)
|
||||||
|
|
||||||
enum vm_special_object_type {
|
enum vm_special_object_type {
|
||||||
VM_SPECIAL_OBJECT_VMCORE = 1,
|
VM_SPECIAL_OBJECT_VMCORE = 1,
|
||||||
|
|
41
vm_eval.c
41
vm_eval.c
|
@ -37,14 +37,23 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
|
||||||
{
|
{
|
||||||
const rb_method_definition_t *def = me->def;
|
const rb_method_definition_t *def = me->def;
|
||||||
VALUE val;
|
VALUE val;
|
||||||
VALUE klass = defined_class;
|
rb_call_info_t ci_entry, *ci = &ci_entry;
|
||||||
const rb_block_t *blockptr = 0;
|
|
||||||
|
ci->flag = 0;
|
||||||
|
ci->mid = id;
|
||||||
|
ci->recv = recv;
|
||||||
|
ci->defined_class = defined_class;
|
||||||
|
ci->argc = argc;
|
||||||
|
ci->me = me;
|
||||||
|
|
||||||
if (!def) return Qnil;
|
if (!def) return Qnil;
|
||||||
if (th->passed_block) {
|
if (th->passed_block) {
|
||||||
blockptr = th->passed_block;
|
ci->blockptr = (rb_block_t *)th->passed_block;
|
||||||
th->passed_block = 0;
|
th->passed_block = 0;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
ci->blockptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
again:
|
again:
|
||||||
switch (def->type) {
|
switch (def->type) {
|
||||||
|
@ -59,20 +68,19 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
|
||||||
*reg_cfp->sp++ = argv[i];
|
*reg_cfp->sp++ = argv[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_setup_method(th, reg_cfp, recv, argc, blockptr, 0 /* flag */,
|
vm_setup_method(th, reg_cfp, ci);
|
||||||
me, klass);
|
|
||||||
th->cfp->flag |= VM_FRAME_FLAG_FINISH;
|
th->cfp->flag |= VM_FRAME_FLAG_FINISH;
|
||||||
val = vm_exec(th);
|
val = vm_exec(th);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
||||||
case VM_METHOD_TYPE_CFUNC: {
|
case VM_METHOD_TYPE_CFUNC: {
|
||||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class);
|
||||||
{
|
{
|
||||||
rb_control_frame_t *reg_cfp = th->cfp;
|
rb_control_frame_t *reg_cfp = th->cfp;
|
||||||
rb_control_frame_t *cfp =
|
rb_control_frame_t *cfp =
|
||||||
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC,
|
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC,
|
||||||
recv, klass, VM_ENVVAL_BLOCK_PTR(blockptr),
|
ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr),
|
||||||
0, reg_cfp->sp, 1, me);
|
0, reg_cfp->sp, 1, me);
|
||||||
|
|
||||||
cfp->me = me;
|
cfp->me = me;
|
||||||
|
@ -83,7 +91,7 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
|
||||||
}
|
}
|
||||||
vm_pop_frame(th);
|
vm_pop_frame(th);
|
||||||
}
|
}
|
||||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VM_METHOD_TYPE_ATTRSET: {
|
case VM_METHOD_TYPE_ATTRSET: {
|
||||||
|
@ -97,13 +105,13 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VM_METHOD_TYPE_BMETHOD: {
|
case VM_METHOD_TYPE_BMETHOD: {
|
||||||
val = vm_call_bmethod(th, recv, argc, argv, blockptr, me, klass);
|
val = vm_call_bmethod(th, recv, argc, argv, ci->blockptr, me, ci->defined_class);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VM_METHOD_TYPE_ZSUPER: {
|
case VM_METHOD_TYPE_ZSUPER: {
|
||||||
klass = RCLASS_SUPER(klass);
|
ci->defined_class = RCLASS_SUPER(ci->defined_class);
|
||||||
if (!klass || !(me = rb_method_entry(klass, id, &klass))) {
|
if (!ci->defined_class || !(ci->me = rb_method_entry(ci->defined_class, id, &ci->defined_class))) {
|
||||||
return method_missing(recv, id, argc, argv, NOEX_SUPER);
|
return method_missing(recv, ci->mid, ci->argc, argv, NOEX_SUPER);
|
||||||
}
|
}
|
||||||
RUBY_VM_CHECK_INTS(th);
|
RUBY_VM_CHECK_INTS(th);
|
||||||
if (!(def = me->def)) return Qnil;
|
if (!(def = me->def)) return Qnil;
|
||||||
|
@ -113,10 +121,9 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
|
||||||
VALUE new_args = rb_ary_new4(argc, argv);
|
VALUE new_args = rb_ary_new4(argc, argv);
|
||||||
|
|
||||||
RB_GC_GUARD(new_args);
|
RB_GC_GUARD(new_args);
|
||||||
rb_ary_unshift(new_args, ID2SYM(id));
|
rb_ary_unshift(new_args, ID2SYM(ci->mid));
|
||||||
th->passed_block = blockptr;
|
th->passed_block = ci->blockptr;
|
||||||
return rb_funcall2(recv, idMethodMissing,
|
return rb_funcall2(ci->recv, idMethodMissing, argc+1, RARRAY_PTR(new_args));
|
||||||
argc+1, RARRAY_PTR(new_args));
|
|
||||||
}
|
}
|
||||||
case VM_METHOD_TYPE_OPTIMIZED: {
|
case VM_METHOD_TYPE_OPTIMIZED: {
|
||||||
switch (def->body.optimize_type) {
|
switch (def->body.optimize_type) {
|
||||||
|
@ -126,7 +133,7 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv,
|
||||||
case OPTIMIZED_METHOD_TYPE_CALL: {
|
case OPTIMIZED_METHOD_TYPE_CALL: {
|
||||||
rb_proc_t *proc;
|
rb_proc_t *proc;
|
||||||
GetProcPtr(recv, proc);
|
GetProcPtr(recv, proc);
|
||||||
val = rb_vm_invoke_proc(th, proc, argc, argv, blockptr);
|
val = rb_vm_invoke_proc(th, proc, argc, argv, ci->blockptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
693
vm_insnhelper.c
693
vm_insnhelper.c
|
@ -158,27 +158,26 @@ rb_error_arity(int argc, int min, int max)
|
||||||
rb_exc_raise(rb_arg_error_new(argc, min, max));
|
rb_exc_raise(rb_arg_error_new(argc, min, max));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VM_CALLEE_SETUP_ARG(ret, th, iseq, orig_argc, orig_argv, block) \
|
#define VM_CALLEE_SETUP_ARG(ret, th, ci, iseq, argv) \
|
||||||
if (LIKELY((iseq)->arg_simple & 0x01)) { \
|
if (LIKELY((iseq)->arg_simple & 0x01)) { \
|
||||||
/* simple check */ \
|
/* simple check */ \
|
||||||
if ((orig_argc) != (iseq)->argc) { \
|
if ((ci)->argc != (iseq)->argc) { \
|
||||||
argument_error((iseq), (orig_argc), (iseq)->argc, (iseq)->argc); \
|
argument_error((iseq), ((ci)->argc), (iseq)->argc, (iseq)->argc); \
|
||||||
} \
|
} \
|
||||||
(ret) = 0; \
|
(ret) = 0; \
|
||||||
} \
|
} \
|
||||||
else { \
|
else { \
|
||||||
(ret) = vm_callee_setup_arg_complex((th), (iseq), (orig_argc), (orig_argv), (block)); \
|
(ret) = vm_callee_setup_arg_complex((th), (ci), (iseq), (argv)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq,
|
vm_callee_setup_arg_complex(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t * iseq, VALUE *orig_argv)
|
||||||
int orig_argc, VALUE * orig_argv,
|
|
||||||
const rb_block_t **block)
|
|
||||||
{
|
{
|
||||||
const int m = iseq->argc;
|
const int m = iseq->argc;
|
||||||
const int opts = iseq->arg_opts - (iseq->arg_opts > 0);
|
const int opts = iseq->arg_opts - (iseq->arg_opts > 0);
|
||||||
const int min = m + iseq->arg_post_len;
|
const int min = m + iseq->arg_post_len;
|
||||||
const int max = (iseq->arg_rest == -1) ? m + opts + iseq->arg_post_len : UNLIMITED_ARGUMENTS;
|
const int max = (iseq->arg_rest == -1) ? m + opts + iseq->arg_post_len : UNLIMITED_ARGUMENTS;
|
||||||
|
const int orig_argc = ci->argc;
|
||||||
int argc = orig_argc;
|
int argc = orig_argc;
|
||||||
VALUE *argv = orig_argv;
|
VALUE *argv = orig_argv;
|
||||||
rb_num_t opt_pc = 0;
|
rb_num_t opt_pc = 0;
|
||||||
|
@ -255,9 +254,9 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* block arguments */
|
/* block arguments */
|
||||||
if (block && iseq->arg_block != -1) {
|
if (iseq->arg_block != -1) {
|
||||||
VALUE blockval = Qnil;
|
VALUE blockval = Qnil;
|
||||||
const rb_block_t *blockptr = *block;
|
const rb_block_t *blockptr = ci->blockptr;
|
||||||
|
|
||||||
if (blockptr) {
|
if (blockptr) {
|
||||||
/* make Proc object */
|
/* make Proc object */
|
||||||
|
@ -265,7 +264,7 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq,
|
||||||
rb_proc_t *proc;
|
rb_proc_t *proc;
|
||||||
blockval = rb_vm_make_proc(th, blockptr, rb_cProc);
|
blockval = rb_vm_make_proc(th, blockptr, rb_cProc);
|
||||||
GetProcPtr(blockval, proc);
|
GetProcPtr(blockval, proc);
|
||||||
*block = &proc->block;
|
ci->blockptr = &proc->block;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
blockval = blockptr->proc;
|
blockval = blockptr->proc;
|
||||||
|
@ -279,14 +278,10 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq,
|
||||||
return (int)opt_pc;
|
return (int)opt_pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static void
|
||||||
caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag,
|
vm_caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
||||||
int argc, rb_iseq_t *blockiseq, rb_block_t **block)
|
|
||||||
{
|
{
|
||||||
rb_block_t *blockptr = 0;
|
if (UNLIKELY(ci->flag & VM_CALL_ARGS_BLOCKARG_BIT)) {
|
||||||
|
|
||||||
if (block) {
|
|
||||||
if (flag & VM_CALL_ARGS_BLOCKARG_BIT) {
|
|
||||||
rb_proc_t *po;
|
rb_proc_t *po;
|
||||||
VALUE proc;
|
VALUE proc;
|
||||||
|
|
||||||
|
@ -303,21 +298,19 @@ caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag,
|
||||||
proc = b;
|
proc = b;
|
||||||
}
|
}
|
||||||
GetProcPtr(proc, po);
|
GetProcPtr(proc, po);
|
||||||
blockptr = &po->block;
|
ci->blockptr = &po->block;
|
||||||
RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc;
|
RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc;
|
||||||
*block = blockptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (blockiseq) {
|
else if (UNLIKELY(ci->blockiseq != 0)) {
|
||||||
blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
|
ci->blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
|
||||||
blockptr->iseq = blockiseq;
|
ci->blockptr->iseq = ci->blockiseq;
|
||||||
blockptr->proc = 0;
|
ci->blockptr->proc = 0;
|
||||||
*block = blockptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* expand top of stack? */
|
/* expand top of stack? */
|
||||||
if (flag & VM_CALL_ARGS_SPLAT_BIT) {
|
|
||||||
|
if (UNLIKELY(ci->flag & VM_CALL_ARGS_SPLAT_BIT)) {
|
||||||
VALUE ary = *(cfp->sp - 1);
|
VALUE ary = *(cfp->sp - 1);
|
||||||
VALUE *ptr;
|
VALUE *ptr;
|
||||||
int i;
|
int i;
|
||||||
|
@ -336,11 +329,9 @@ caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag,
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
*cfp->sp++ = ptr[i];
|
*cfp->sp++ = ptr[i];
|
||||||
}
|
}
|
||||||
argc += i-1;
|
ci->argc += i-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return argc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
|
@ -428,21 +419,20 @@ call_cfunc(VALUE (*func)(), VALUE recv,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp,
|
vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
|
||||||
int num, volatile VALUE recv, const rb_block_t *blockptr,
|
|
||||||
const rb_method_entry_t *me, VALUE defined_class)
|
|
||||||
{
|
{
|
||||||
volatile VALUE val = 0;
|
volatile VALUE val = 0;
|
||||||
|
const rb_method_entry_t *me = ci->me;
|
||||||
const rb_method_definition_t *def = me->def;
|
const rb_method_definition_t *def = me->def;
|
||||||
|
|
||||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, me->called_id, me->klass);
|
||||||
|
|
||||||
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class,
|
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class,
|
||||||
VM_ENVVAL_BLOCK_PTR(blockptr), 0, th->cfp->sp, 1, me);
|
VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, th->cfp->sp, 1, me);
|
||||||
|
|
||||||
reg_cfp->sp -= num + 1;
|
reg_cfp->sp -= ci->argc + 1;
|
||||||
|
|
||||||
val = call_cfunc(def->body.cfunc.func, recv, (int)def->body.cfunc.argc, num, reg_cfp->sp + 1);
|
val = call_cfunc(def->body.cfunc.func, ci->recv, (int)def->body.cfunc.argc, ci->argc, reg_cfp->sp + 1);
|
||||||
|
|
||||||
if (reg_cfp != th->cfp + 1) {
|
if (reg_cfp != th->cfp + 1) {
|
||||||
rb_bug("cfp consistency error - send");
|
rb_bug("cfp consistency error - send");
|
||||||
|
@ -450,7 +440,7 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp,
|
||||||
|
|
||||||
vm_pop_frame(th);
|
vm_pop_frame(th);
|
||||||
|
|
||||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, me->called_id, me->klass);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -491,20 +481,18 @@ vm_method_missing(rb_thread_t *th, rb_control_frame_t *const reg_cfp,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
|
vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
||||||
VALUE recv, int argc, const rb_block_t *blockptr, VALUE flag,
|
|
||||||
const rb_method_entry_t *me, VALUE defined_class)
|
|
||||||
{
|
{
|
||||||
int opt_pc, i;
|
int opt_pc, i;
|
||||||
VALUE *argv = cfp->sp - argc;
|
VALUE *argv = cfp->sp - ci->argc;
|
||||||
rb_iseq_t *iseq = me->def->body.iseq;
|
rb_iseq_t *iseq = ci->me->def->body.iseq;
|
||||||
|
|
||||||
VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr);
|
VM_CALLEE_SETUP_ARG(opt_pc, th, ci, iseq, argv);
|
||||||
|
|
||||||
/* stack overflow check */
|
/* stack overflow check */
|
||||||
CHECK_STACK_OVERFLOW(cfp, iseq->stack_max);
|
CHECK_STACK_OVERFLOW(cfp, iseq->stack_max);
|
||||||
|
|
||||||
if (LIKELY(!(flag & VM_CALL_TAILCALL_BIT))) {
|
if (LIKELY(!(ci->flag & VM_CALL_TAILCALL_BIT))) {
|
||||||
VALUE *sp = argv + iseq->arg_size;
|
VALUE *sp = argv + iseq->arg_size;
|
||||||
|
|
||||||
/* clear local variables */
|
/* clear local variables */
|
||||||
|
@ -512,9 +500,9 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
|
||||||
*sp++ = Qnil;
|
*sp++ = Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, defined_class,
|
vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, ci->recv, ci->defined_class,
|
||||||
VM_ENVVAL_BLOCK_PTR(blockptr),
|
VM_ENVVAL_BLOCK_PTR(ci->blockptr),
|
||||||
iseq->iseq_encoded + opt_pc, sp, 0, me);
|
iseq->iseq_encoded + opt_pc, sp, 0, ci->me);
|
||||||
|
|
||||||
cfp->sp = argv - 1 /* recv */;
|
cfp->sp = argv - 1 /* recv */;
|
||||||
}
|
}
|
||||||
|
@ -527,7 +515,7 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
|
||||||
sp_orig = sp = cfp->sp;
|
sp_orig = sp = cfp->sp;
|
||||||
|
|
||||||
/* push self */
|
/* push self */
|
||||||
sp[0] = recv;
|
sp[0] = ci->recv;
|
||||||
sp++;
|
sp++;
|
||||||
|
|
||||||
/* copy arguments */
|
/* copy arguments */
|
||||||
|
@ -541,187 +529,13 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp,
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | finish_flag,
|
vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | finish_flag,
|
||||||
recv, defined_class, VM_ENVVAL_BLOCK_PTR(blockptr),
|
ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr),
|
||||||
iseq->iseq_encoded + opt_pc, sp, 0, me);
|
iseq->iseq_encoded + opt_pc, sp, 0, ci->me);
|
||||||
|
|
||||||
cfp->sp = sp_orig;
|
cfp->sp = sp_orig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
|
||||||
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 defined_class)
|
|
||||||
{
|
|
||||||
VALUE val;
|
|
||||||
|
|
||||||
start_method_dispatch:
|
|
||||||
|
|
||||||
if (me != 0) {
|
|
||||||
if ((me->flag == 0)) {
|
|
||||||
normal_method_dispatch:
|
|
||||||
switch (me->def->type) {
|
|
||||||
case VM_METHOD_TYPE_ISEQ:{
|
|
||||||
vm_setup_method(th, cfp, recv, num, blockptr, flag, me,
|
|
||||||
defined_class);
|
|
||||||
return Qundef;
|
|
||||||
}
|
|
||||||
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
|
||||||
case VM_METHOD_TYPE_CFUNC:{
|
|
||||||
val = vm_call_cfunc(th, cfp, num, recv, blockptr, me,
|
|
||||||
defined_class);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VM_METHOD_TYPE_ATTRSET:{
|
|
||||||
rb_check_arity(num, 1, 1);
|
|
||||||
val = rb_ivar_set(recv, me->def->body.attr.id, *(cfp->sp - 1));
|
|
||||||
cfp->sp -= 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VM_METHOD_TYPE_IVAR:{
|
|
||||||
rb_check_arity(num, 0, 0);
|
|
||||||
val = rb_attr_get(recv, me->def->body.attr.id);
|
|
||||||
cfp->sp -= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VM_METHOD_TYPE_MISSING:{
|
|
||||||
VALUE *argv = ALLOCA_N(VALUE, num+1);
|
|
||||||
argv[0] = ID2SYM(me->def->original_id);
|
|
||||||
MEMCPY(argv+1, cfp->sp - num, VALUE, num);
|
|
||||||
cfp->sp += - num - 1;
|
|
||||||
th->passed_block = blockptr;
|
|
||||||
val = rb_funcall2(recv, rb_intern("method_missing"), num+1, argv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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, recv, num, argv, blockptr, me,
|
|
||||||
defined_class);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VM_METHOD_TYPE_ZSUPER:{
|
|
||||||
VALUE klass = RCLASS_SUPER(me->klass);
|
|
||||||
me = rb_method_entry(klass, id, &defined_class);
|
|
||||||
|
|
||||||
if (me != 0) {
|
|
||||||
goto normal_method_dispatch;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto start_method_dispatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case VM_METHOD_TYPE_OPTIMIZED:{
|
|
||||||
switch (me->def->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);
|
|
||||||
if (SYMBOL_P(sym)) {
|
|
||||||
id = SYM2ID(sym);
|
|
||||||
}
|
|
||||||
else if (!(id = rb_check_id(&sym))) {
|
|
||||||
if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) {
|
|
||||||
VALUE exc = make_no_method_exception(rb_eNoMethodError, NULL, recv,
|
|
||||||
rb_long2int(num), &TOPN(i));
|
|
||||||
rb_exc_raise(exc);
|
|
||||||
}
|
|
||||||
id = 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, &defined_class);
|
|
||||||
num -= 1;
|
|
||||||
DEC_SP(1);
|
|
||||||
flag |= VM_CALL_FCALL_BIT | VM_CALL_OPT_SEND_BIT;
|
|
||||||
|
|
||||||
goto start_method_dispatch;
|
|
||||||
}
|
|
||||||
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, argc, argv, blockptr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
rb_bug("eval_invoke_method: unsupported optimized method type (%d)",
|
|
||||||
me->def->body.optimize_type);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:{
|
|
||||||
rb_bug("eval_invoke_method: unsupported method type (%d)", me->def->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int noex_safe;
|
|
||||||
|
|
||||||
if (!(flag & VM_CALL_FCALL_BIT) &&
|
|
||||||
(me->flag & NOEX_MASK) & NOEX_PRIVATE) {
|
|
||||||
int stat = NOEX_PRIVATE;
|
|
||||||
|
|
||||||
if (flag & VM_CALL_VCALL_BIT) {
|
|
||||||
stat |= NOEX_VCALL;
|
|
||||||
}
|
|
||||||
val = vm_method_missing(th, cfp, id, recv, num, blockptr, stat);
|
|
||||||
}
|
|
||||||
else if (!(flag & VM_CALL_OPT_SEND_BIT) && (me->flag & NOEX_MASK) & NOEX_PROTECTED) {
|
|
||||||
if (!rb_obj_is_kind_of(cfp->self, defined_class)) {
|
|
||||||
val = vm_method_missing(th, cfp, id, recv, num, blockptr, NOEX_PROTECTED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto normal_method_dispatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto normal_method_dispatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* method missing */
|
|
||||||
int stat = 0;
|
|
||||||
if (flag & VM_CALL_VCALL_BIT) {
|
|
||||||
stat |= NOEX_VCALL;
|
|
||||||
}
|
|
||||||
if (flag & VM_CALL_SUPER_BIT) {
|
|
||||||
stat |= NOEX_SUPER;
|
|
||||||
}
|
|
||||||
if (id == idMethodMissing) {
|
|
||||||
rb_control_frame_t *reg_cfp = cfp;
|
|
||||||
VALUE *argv = STACK_ADDR_FROM_TOP(num);
|
|
||||||
rb_raise_method_missing(th, num, argv, recv, stat);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val = vm_method_missing(th, cfp, id, recv, num, blockptr, stat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RUBY_VM_CHECK_INTS(th);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* yield */
|
/* yield */
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -938,8 +752,7 @@ vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq,
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
|
vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
|
||||||
int argc, VALUE *argv,
|
int argc, VALUE *argv, const rb_block_t *blockptr, int lambda)
|
||||||
const rb_block_t *blockptr, int lambda)
|
|
||||||
{
|
{
|
||||||
if (0) { /* for debug */
|
if (0) { /* for debug */
|
||||||
printf(" argc: %d\n", argc);
|
printf(" argc: %d\n", argc);
|
||||||
|
@ -955,7 +768,11 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
|
||||||
if (lambda) {
|
if (lambda) {
|
||||||
/* call as method */
|
/* call as method */
|
||||||
int opt_pc;
|
int opt_pc;
|
||||||
VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr);
|
rb_call_info_t ci_entry;
|
||||||
|
ci_entry.flag = 0;
|
||||||
|
ci_entry.argc = argc;
|
||||||
|
ci_entry.blockptr = (rb_block_t *)blockptr;
|
||||||
|
VM_CALLEE_SETUP_ARG(opt_pc, th, &ci_entry, iseq, argv);
|
||||||
return opt_pc;
|
return opt_pc;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -964,11 +781,10 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq,
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_num_t flag)
|
vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
|
||||||
{
|
{
|
||||||
const rb_block_t *block = VM_CF_BLOCK_PTR(reg_cfp);
|
const rb_block_t *block = VM_CF_BLOCK_PTR(reg_cfp);
|
||||||
rb_iseq_t *iseq;
|
rb_iseq_t *iseq;
|
||||||
int argc = (int)num;
|
|
||||||
VALUE type = GET_ISEQ()->local_iseq->type;
|
VALUE type = GET_ISEQ()->local_iseq->type;
|
||||||
|
|
||||||
if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) {
|
if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) {
|
||||||
|
@ -976,17 +792,16 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n
|
||||||
}
|
}
|
||||||
iseq = block->iseq;
|
iseq = block->iseq;
|
||||||
|
|
||||||
argc = caller_setup_args(th, GET_CFP(), flag, argc, 0, 0);
|
vm_caller_setup_args(th, GET_CFP(), ci);
|
||||||
|
|
||||||
if (BUILTIN_TYPE(iseq) != T_NODE) {
|
if (BUILTIN_TYPE(iseq) != T_NODE) {
|
||||||
int opt_pc;
|
int opt_pc;
|
||||||
const int arg_size = iseq->arg_size;
|
const int arg_size = iseq->arg_size;
|
||||||
VALUE * const rsp = GET_SP() - argc;
|
VALUE * const rsp = GET_SP() - ci->argc;
|
||||||
SET_SP(rsp);
|
SET_SP(rsp);
|
||||||
|
|
||||||
CHECK_STACK_OVERFLOW(GET_CFP(), iseq->stack_max);
|
CHECK_STACK_OVERFLOW(GET_CFP(), iseq->stack_max);
|
||||||
opt_pc = vm_yield_setup_args(th, iseq, argc, rsp, 0,
|
opt_pc = vm_yield_setup_args(th, iseq, ci->argc, rsp, 0, block_proc_is_lambda(block->proc));
|
||||||
block_proc_is_lambda(block->proc));
|
|
||||||
|
|
||||||
vm_push_frame(th, iseq, VM_FRAME_MAGIC_BLOCK, block->self,
|
vm_push_frame(th, iseq, VM_FRAME_MAGIC_BLOCK, block->self,
|
||||||
block->klass,
|
block->klass,
|
||||||
|
@ -998,8 +813,8 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n
|
||||||
return Qundef;
|
return Qundef;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VALUE val = vm_yield_with_cfunc(th, block, block->self, argc, STACK_ADDR_FROM_TOP(argc), 0);
|
VALUE val = vm_yield_with_cfunc(th, block, block->self, ci->argc, STACK_ADDR_FROM_TOP(ci->argc), 0);
|
||||||
POPN(argc); /* TODO: should put before C/yield? */
|
POPN(ci->argc); /* TODO: should put before C/yield? */
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1429,105 +1244,6 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const rb_method_entry_t *
|
|
||||||
vm_method_search(VALUE id, VALUE klass, CALL_INFO ci, VALUE *defined_class_ptr)
|
|
||||||
{
|
|
||||||
rb_method_entry_t *me;
|
|
||||||
#if OPT_INLINE_METHOD_CACHE
|
|
||||||
if (LIKELY(klass == ci->ic_class &&
|
|
||||||
GET_VM_STATE_VERSION() == ci->ic_vmstat)) {
|
|
||||||
me = ci->method;
|
|
||||||
if (defined_class_ptr) {
|
|
||||||
*defined_class_ptr = ci->defined_class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
VALUE defined_class;
|
|
||||||
me = rb_method_entry(klass, id, &defined_class);
|
|
||||||
if (defined_class_ptr) {
|
|
||||||
*defined_class_ptr = defined_class;
|
|
||||||
}
|
|
||||||
ci->ic_class = klass;
|
|
||||||
ci->method = me;
|
|
||||||
ci->defined_class = defined_class;
|
|
||||||
ci->ic_vmstat = GET_VM_STATE_VERSION();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
me = rb_method_entry(klass, id, defined_class_ptr);
|
|
||||||
#endif
|
|
||||||
return me;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline VALUE
|
|
||||||
vm_search_normal_superclass(VALUE klass)
|
|
||||||
{
|
|
||||||
klass = RCLASS_ORIGIN(klass);
|
|
||||||
return RCLASS_SUPER(klass);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vm_super_outside(void)
|
|
||||||
{
|
|
||||||
rb_raise(rb_eNoMethodError, "super called outside of method");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq,
|
|
||||||
VALUE sigval,
|
|
||||||
ID *idp, VALUE *klassp)
|
|
||||||
{
|
|
||||||
ID id;
|
|
||||||
VALUE klass;
|
|
||||||
|
|
||||||
while (iseq && !iseq->klass) {
|
|
||||||
iseq = iseq->parent_iseq;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iseq == 0) {
|
|
||||||
vm_super_outside();
|
|
||||||
}
|
|
||||||
|
|
||||||
id = iseq->defined_method_id;
|
|
||||||
|
|
||||||
if (iseq != iseq->local_iseq) {
|
|
||||||
/* defined by Module#define_method() */
|
|
||||||
rb_control_frame_t *lcfp = GET_CFP();
|
|
||||||
|
|
||||||
if (!sigval) {
|
|
||||||
/* zsuper */
|
|
||||||
rb_raise(rb_eRuntimeError, "implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly.");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (lcfp->iseq != iseq) {
|
|
||||||
rb_thread_t *th = GET_THREAD();
|
|
||||||
VALUE *tep = VM_EP_PREV_EP(lcfp->ep);
|
|
||||||
while (1) {
|
|
||||||
lcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(lcfp);
|
|
||||||
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, lcfp)) {
|
|
||||||
vm_super_outside();
|
|
||||||
}
|
|
||||||
if (lcfp->ep == tep) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* temporary measure for [Bug #2420] [Bug #3136] */
|
|
||||||
if (!lcfp->me) {
|
|
||||||
vm_super_outside();
|
|
||||||
}
|
|
||||||
|
|
||||||
id = lcfp->me->def->original_id;
|
|
||||||
klass = vm_search_normal_superclass(lcfp->klass);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
klass = vm_search_normal_superclass(reg_cfp->klass);
|
|
||||||
}
|
|
||||||
|
|
||||||
*idp = id;
|
|
||||||
*klassp = klass;
|
|
||||||
}
|
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,
|
vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,
|
||||||
rb_num_t throw_state, VALUE throwobj)
|
rb_num_t throw_state, VALUE throwobj)
|
||||||
|
@ -1758,6 +1474,32 @@ vm_expandarray(rb_control_frame_t *cfp, VALUE ary, rb_num_t num, int flag)
|
||||||
RB_GC_GUARD(ary);
|
RB_GC_GUARD(ary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci);
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_search_method(rb_call_info_t *ci, VALUE recv)
|
||||||
|
{
|
||||||
|
VALUE klass = CLASS_OF(recv);
|
||||||
|
rb_method_entry_t *me;
|
||||||
|
|
||||||
|
#if OPT_INLINE_METHOD_CACHE
|
||||||
|
if (LIKELY(GET_VM_STATE_VERSION() == ci->vmstat && klass == ci->klass)) {
|
||||||
|
/* cache hit! */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
me = rb_method_entry(klass, ci->mid, &ci->defined_class);
|
||||||
|
ci->me = me;
|
||||||
|
ci->klass = klass;
|
||||||
|
ci->vmstat = GET_VM_STATE_VERSION();
|
||||||
|
ci->call = vm_call_general;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ci->method = rb_method_entry(klass, id, &ci->defined_class);
|
||||||
|
ci->call = vm_call_general;
|
||||||
|
ci->klass = klass;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
check_cfunc(const rb_method_entry_t *me, VALUE (*func)())
|
check_cfunc(const rb_method_entry_t *me, VALUE (*func)())
|
||||||
{
|
{
|
||||||
|
@ -1805,9 +1547,9 @@ opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ci, 0);
|
vm_search_method(ci, recv);
|
||||||
|
|
||||||
if (check_cfunc(me, rb_obj_equal)) {
|
if (check_cfunc(ci->me, rb_obj_equal)) {
|
||||||
return recv == obj ? Qtrue : Qfalse;
|
return recv == obj ? Qtrue : Qfalse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1927,3 +1669,280 @@ vm_base_ptr(rb_control_frame_t *cfp)
|
||||||
return bp;
|
return bp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline VALUE
|
||||||
|
vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci)
|
||||||
|
{
|
||||||
|
VALUE val;
|
||||||
|
|
||||||
|
start_method_dispatch:
|
||||||
|
if (ci->me != 0) {
|
||||||
|
if ((ci->me->flag == 0)) {
|
||||||
|
normal_method_dispatch:
|
||||||
|
switch (ci->me->def->type) {
|
||||||
|
case VM_METHOD_TYPE_ISEQ:{
|
||||||
|
vm_setup_method(th, cfp, ci);
|
||||||
|
return Qundef;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
||||||
|
case VM_METHOD_TYPE_CFUNC:{
|
||||||
|
val = vm_call_cfunc(th, cfp, ci);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_ATTRSET:{
|
||||||
|
rb_check_arity(ci->argc, 1, 1);
|
||||||
|
val = rb_ivar_set(ci->recv, ci->me->def->body.attr.id, *(cfp->sp - 1));
|
||||||
|
cfp->sp -= 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_IVAR:{
|
||||||
|
rb_check_arity(ci->argc, 0, 0);
|
||||||
|
val = rb_attr_get(ci->recv, ci->me->def->body.attr.id);
|
||||||
|
cfp->sp -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_MISSING:{
|
||||||
|
VALUE *argv = ALLOCA_N(VALUE, ci->argc+1);
|
||||||
|
argv[0] = ID2SYM(ci->me->def->original_id);
|
||||||
|
MEMCPY(argv+1, cfp->sp - ci->argc, VALUE, ci->argc);
|
||||||
|
cfp->sp += - ci->argc - 1;
|
||||||
|
th->passed_block = ci->blockptr;
|
||||||
|
val = rb_funcall2(ci->recv, rb_intern("method_missing"), ci->argc+1, argv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_BMETHOD:{
|
||||||
|
VALUE *argv = ALLOCA_N(VALUE, ci->argc);
|
||||||
|
MEMCPY(argv, cfp->sp - ci->argc, VALUE, ci->argc);
|
||||||
|
cfp->sp += - ci->argc - 1;
|
||||||
|
val = vm_call_bmethod(th, ci->recv, ci->argc, argv,
|
||||||
|
ci->blockptr, ci->me, ci->defined_class);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_ZSUPER:{
|
||||||
|
VALUE klass = RCLASS_SUPER(ci->me->klass);
|
||||||
|
rb_call_info_t cie = *ci;
|
||||||
|
ci = &cie;
|
||||||
|
|
||||||
|
ci->me = rb_method_entry(klass, ci->mid, &ci->defined_class);
|
||||||
|
|
||||||
|
if (ci->me != 0) {
|
||||||
|
goto normal_method_dispatch;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto start_method_dispatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case VM_METHOD_TYPE_OPTIMIZED:{
|
||||||
|
switch (ci->me->def->body.optimize_type) {
|
||||||
|
case OPTIMIZED_METHOD_TYPE_SEND: {
|
||||||
|
rb_control_frame_t *reg_cfp = cfp;
|
||||||
|
int i = ci->argc - 1;
|
||||||
|
VALUE sym;
|
||||||
|
rb_call_info_t ci_entry;
|
||||||
|
|
||||||
|
if (ci->argc == 0) {
|
||||||
|
rb_raise(rb_eArgError, "no method name given");
|
||||||
|
}
|
||||||
|
|
||||||
|
ci_entry = *ci; /* copy ci entry */
|
||||||
|
ci = &ci_entry;
|
||||||
|
|
||||||
|
sym = TOPN(i);
|
||||||
|
|
||||||
|
if (SYMBOL_P(sym)) {
|
||||||
|
ci->mid = SYM2ID(sym);
|
||||||
|
}
|
||||||
|
else if (!(ci->mid = rb_check_id(&sym))) {
|
||||||
|
if (rb_method_basic_definition_p(CLASS_OF(ci->recv), idMethodMissing)) {
|
||||||
|
VALUE exc = make_no_method_exception(rb_eNoMethodError, NULL, ci->recv, rb_long2int(ci->argc), &TOPN(i));
|
||||||
|
rb_exc_raise(exc);
|
||||||
|
}
|
||||||
|
ci->mid = rb_to_id(sym);
|
||||||
|
}
|
||||||
|
/* shift arguments */
|
||||||
|
if (i > 0) {
|
||||||
|
MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i);
|
||||||
|
}
|
||||||
|
ci->me = rb_method_entry(CLASS_OF(ci->recv), ci->mid, &ci->defined_class);
|
||||||
|
ci->argc -= 1;
|
||||||
|
DEC_SP(1);
|
||||||
|
/* TODO: fixme */
|
||||||
|
ci->flag |= VM_CALL_FCALL_BIT | VM_CALL_OPT_SEND_BIT;
|
||||||
|
|
||||||
|
goto start_method_dispatch;
|
||||||
|
}
|
||||||
|
case OPTIMIZED_METHOD_TYPE_CALL: {
|
||||||
|
rb_proc_t *proc;
|
||||||
|
int argc = ci->argc;
|
||||||
|
VALUE *argv = ALLOCA_N(VALUE, argc);
|
||||||
|
GetProcPtr(ci->recv, proc);
|
||||||
|
MEMCPY(argv, cfp->sp - argc, VALUE, argc);
|
||||||
|
cfp->sp -= argc + 1;
|
||||||
|
|
||||||
|
val = rb_vm_invoke_proc(th, proc, argc, argv, ci->blockptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
rb_bug("eval_invoke_method: unsupported optimized method type (%d)",
|
||||||
|
ci->me->def->body.optimize_type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:{
|
||||||
|
rb_bug("eval_invoke_method: unsupported method type (%d)", ci->me->def->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int noex_safe;
|
||||||
|
if (!(ci->flag & VM_CALL_FCALL_BIT) && (ci->me->flag & NOEX_MASK) & NOEX_PRIVATE) {
|
||||||
|
int stat = NOEX_PRIVATE;
|
||||||
|
|
||||||
|
if (ci->flag & VM_CALL_VCALL_BIT) {
|
||||||
|
stat |= NOEX_VCALL;
|
||||||
|
}
|
||||||
|
val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, stat);
|
||||||
|
}
|
||||||
|
else if (!(ci->flag & VM_CALL_OPT_SEND_BIT) && (ci->me->flag & NOEX_MASK) & NOEX_PROTECTED) {
|
||||||
|
if (!rb_obj_is_kind_of(cfp->self, ci->defined_class)) {
|
||||||
|
val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, NOEX_PROTECTED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto normal_method_dispatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((noex_safe = NOEX_SAFE(ci->me->flag)) > th->safe_level && (noex_safe > 2)) {
|
||||||
|
rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(ci->mid));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto normal_method_dispatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* method missing */
|
||||||
|
int stat = 0;
|
||||||
|
if (ci->flag & VM_CALL_VCALL_BIT) {
|
||||||
|
stat |= NOEX_VCALL;
|
||||||
|
}
|
||||||
|
if (ci->flag & VM_CALL_SUPER_BIT) {
|
||||||
|
stat |= NOEX_SUPER;
|
||||||
|
}
|
||||||
|
if (ci->mid == idMethodMissing) {
|
||||||
|
rb_control_frame_t *reg_cfp = cfp;
|
||||||
|
VALUE *argv = STACK_ADDR_FROM_TOP(ci->argc);
|
||||||
|
rb_raise_method_missing(th, ci->argc, argv, ci->recv, stat);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, stat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RUBY_VM_CHECK_INTS(th);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline VALUE
|
||||||
|
vm_search_normal_superclass(VALUE klass)
|
||||||
|
{
|
||||||
|
klass = RCLASS_ORIGIN(klass);
|
||||||
|
return RCLASS_SUPER(klass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_super_outside(void)
|
||||||
|
{
|
||||||
|
rb_raise(rb_eNoMethodError, "super called outside of method");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, VALUE sigval, rb_call_info_t *ci)
|
||||||
|
{
|
||||||
|
while (iseq && !iseq->klass) {
|
||||||
|
iseq = iseq->parent_iseq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iseq == 0) {
|
||||||
|
vm_super_outside();
|
||||||
|
}
|
||||||
|
|
||||||
|
ci->mid = iseq->defined_method_id;
|
||||||
|
|
||||||
|
if (iseq != iseq->local_iseq) {
|
||||||
|
/* defined by Module#define_method() */
|
||||||
|
rb_control_frame_t *lcfp = GET_CFP();
|
||||||
|
|
||||||
|
if (!sigval) {
|
||||||
|
/* zsuper */
|
||||||
|
rb_raise(rb_eRuntimeError, "implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (lcfp->iseq != iseq) {
|
||||||
|
rb_thread_t *th = GET_THREAD();
|
||||||
|
VALUE *tep = VM_EP_PREV_EP(lcfp->ep);
|
||||||
|
while (1) {
|
||||||
|
lcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(lcfp);
|
||||||
|
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, lcfp)) {
|
||||||
|
vm_super_outside();
|
||||||
|
}
|
||||||
|
if (lcfp->ep == tep) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* temporary measure for [Bug #2420] [Bug #3136] */
|
||||||
|
if (!lcfp->me) {
|
||||||
|
vm_super_outside();
|
||||||
|
}
|
||||||
|
|
||||||
|
ci->mid = lcfp->me->def->original_id;
|
||||||
|
ci->klass = vm_search_normal_superclass(lcfp->klass);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ci->klass = vm_search_normal_superclass(reg_cfp->klass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
|
||||||
|
{
|
||||||
|
VALUE current_defind_class;
|
||||||
|
rb_iseq_t *iseq = GET_ISEQ();
|
||||||
|
VALUE sigval = TOPN(ci->orig_argc);
|
||||||
|
|
||||||
|
current_defind_class = GET_CFP()->klass;
|
||||||
|
if (NIL_P(current_defind_class)) {
|
||||||
|
vm_super_outside();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NIL_P(RCLASS_REFINED_CLASS(current_defind_class))) {
|
||||||
|
current_defind_class = RCLASS_REFINED_CLASS(current_defind_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rb_obj_is_kind_of(ci->recv, current_defind_class)) {
|
||||||
|
rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later");
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_search_superclass(GET_CFP(), iseq, sigval, ci);
|
||||||
|
|
||||||
|
/* TODO: use inline cache */
|
||||||
|
ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class);
|
||||||
|
ci->call = vm_call_general;
|
||||||
|
|
||||||
|
while (iseq && !iseq->klass) {
|
||||||
|
iseq = iseq->parent_iseq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq == iseq) {
|
||||||
|
ci->klass = RCLASS_SUPER(ci->defined_class);
|
||||||
|
ci->me = rb_method_entry_get_with_refinements(Qnil, ci->klass, ci->mid, &ci->defined_class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
|
||||||
|
{
|
||||||
|
return vm_call_method(th, reg_cfp, ci);
|
||||||
|
}
|
||||||
|
|
|
@ -189,8 +189,8 @@ enum vm_regan_acttype {
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class) do { \
|
#define CALL_METHOD(ci) do { \
|
||||||
VALUE v = vm_call_method(th, GET_CFP(), (num), (blockptr), (flag), (id), (me), (recv), (defined_class)); \
|
VALUE v = (*(ci)->call)(th, GET_CFP(), (ci)); \
|
||||||
if (v == Qundef) { \
|
if (v == Qundef) { \
|
||||||
RESTORE_REGS(); \
|
RESTORE_REGS(); \
|
||||||
NEXT_INSN(); \
|
NEXT_INSN(); \
|
||||||
|
@ -235,23 +235,12 @@ enum vm_regan_acttype {
|
||||||
#define USE_IC_FOR_SPECIALIZED_METHOD 1
|
#define USE_IC_FOR_SPECIALIZED_METHOD 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_IC_FOR_SPECIALIZED_METHOD
|
#define CALL_SIMPLE_METHOD(recv) do { \
|
||||||
|
ci->blockptr = 0; ci->argc = ci->orig_argc; \
|
||||||
#define CALL_SIMPLE_METHOD(num, id, recv) do { \
|
vm_search_method(ci, ci->recv = (recv)); \
|
||||||
VALUE klass = CLASS_OF(recv), defined_class; \
|
CALL_METHOD(ci); \
|
||||||
const rb_method_entry_t *me = vm_method_search((id), klass, ci, &defined_class); \
|
|
||||||
CALL_METHOD((num), 0, 0, (id), me, (recv), defined_class); \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define CALL_SIMPLE_METHOD(num, id, recv) do { \
|
|
||||||
VALUE klass = CLASS_OF(recv); \
|
|
||||||
CALL_METHOD((num), 0, 0, (id), rb_method_entry(klass, (id)), (recv)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static VALUE ruby_vm_global_state_version = 1;
|
static VALUE ruby_vm_global_state_version = 1;
|
||||||
|
|
||||||
#define GET_VM_STATE_VERSION() (ruby_vm_global_state_version)
|
#define GET_VM_STATE_VERSION() (ruby_vm_global_state_version)
|
||||||
|
|
Loading…
Reference in a new issue