summaryrefslogtreecommitdiff
path: root/vm_eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm_eval.c')
-rw-r--r--vm_eval.c760
1 files changed, 556 insertions, 204 deletions
diff --git a/vm_eval.c b/vm_eval.c
index 1caa76000b..707344718b 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1,6 +1,6 @@
/**********************************************************************
- vm_eval.c -
+ vm_eval.c - Included into vm.c.
$Author$
created at: Sat May 24 16:02:32 JST 2008
@@ -21,7 +21,7 @@ static inline VALUE vm_yield_with_cref(rb_execution_context_t *ec, int argc, con
static inline VALUE vm_yield(rb_execution_context_t *ec, int argc, const VALUE *argv, int kw_splat);
static inline VALUE vm_yield_with_block(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE block_handler, int kw_splat);
static inline VALUE vm_yield_force_blockarg(rb_execution_context_t *ec, VALUE args);
-VALUE vm_exec(rb_execution_context_t *ec, bool jit_enable_p);
+VALUE vm_exec(rb_execution_context_t *ec);
static void vm_set_eval_stack(rb_execution_context_t * th, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block);
static int vm_collect_local_variables_in_heap(const VALUE *dfp, const struct local_var_list *vars);
@@ -29,36 +29,39 @@ static VALUE rb_eUncaughtThrow;
static ID id_result, id_tag, id_value;
#define id_mesg idMesg
-typedef enum call_type {
- CALL_PUBLIC,
- CALL_FCALL,
- CALL_VCALL,
- CALL_PUBLIC_KW,
- CALL_FCALL_KW,
- CALL_TYPE_MAX
-} call_type;
-
static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope);
static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv);
-#ifndef MJIT_HEADER
+static VALUE *
+vm_argv_ruby_array(VALUE *av, const VALUE *argv, int *flags, int *argc, int kw_splat)
+{
+ *flags |= VM_CALL_ARGS_SPLAT;
+ VALUE argv_ary = rb_ary_hidden_new(*argc);
+ rb_ary_cat(argv_ary, argv, *argc);
+ *argc = 2;
+ av[0] = argv_ary;
+ if (kw_splat) {
+ av[1] = rb_ary_pop(argv_ary);
+ }
+ else {
+ // Make sure flagged keyword hash passed as regular argument
+ // isn't treated as keywords
+ *flags |= VM_CALL_KW_SPLAT;
+ av[1] = rb_hash_new();
+ }
+ return av;
+}
-MJIT_FUNC_EXPORTED VALUE
+static inline VALUE vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callcache *cc, int kw_splat);
+
+VALUE
rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *cme, int kw_splat)
{
- struct rb_calling_info calling = {
- .ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL),
- .cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme),
- .block_handler = vm_passed_block_handler(ec),
- .recv = recv,
- .argc = argc,
- .kw_splat = kw_splat,
- };
-
- return vm_call0_body(ec, &calling, argv);
+ const struct rb_callcache cc = VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme);
+ return vm_call0_cc(ec, recv, id, argc, argv, &cc, kw_splat);
}
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_vm_call_with_refinements(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, int kw_splat)
{
const rb_callable_method_entry_t *me =
@@ -75,8 +78,19 @@ rb_vm_call_with_refinements(rb_execution_context_t *ec, VALUE recv, ID id, int a
static inline VALUE
vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callcache *cc, int kw_splat)
{
+ int flags = kw_splat ? VM_CALL_KW_SPLAT : 0;
+ VALUE *use_argv = (VALUE *)argv;
+ VALUE av[2];
+
+ if (UNLIKELY(vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_ISEQ && argc > VM_ARGC_STACK_MAX)) {
+ use_argv = vm_argv_ruby_array(av, argv, &flags, &argc, kw_splat);
+ }
+
struct rb_calling_info calling = {
- .ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL),
+ .cd = &(struct rb_call_data) {
+ .ci = &VM_CI_ON_STACK(id, flags, argc, NULL),
+ .cc = NULL,
+ },
.cc = cc,
.block_handler = vm_passed_block_handler(ec),
.recv = recv,
@@ -84,20 +98,20 @@ vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
.kw_splat = kw_splat,
};
- return vm_call0_body(ec, &calling, argv);
+ return vm_call0_body(ec, &calling, use_argv);
}
static VALUE
vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme)
{
- calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme);
+ calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme);
return vm_call0_body(ec, calling, argv);
}
static VALUE
vm_call0_super(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, VALUE klass, enum method_missing_reason ex)
{
- ID mid = vm_ci_mid(calling->ci);
+ ID mid = vm_ci_mid(calling->cd->ci);
klass = RCLASS_SUPER(klass);
if (klass) {
@@ -116,7 +130,7 @@ vm_call0_super(rb_execution_context_t *ec, struct rb_calling_info *calling, cons
static VALUE
vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
VALUE val;
const rb_callable_method_entry_t *me = vm_cc_cme(calling->cc);
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
@@ -181,7 +195,7 @@ vm_call_check_arity(struct rb_calling_info *calling, int argc, const VALUE *argv
static VALUE
vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv)
{
- const struct rb_callinfo *ci = calling->ci;
+ const struct rb_callinfo *ci = calling->cd->ci;
const struct rb_callcache *cc = calling->cc;
VALUE ret;
@@ -201,9 +215,14 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
*reg_cfp->sp++ = argv[i];
}
- vm_call_iseq_setup(ec, reg_cfp, calling);
+ if (ISEQ_BODY(def_iseq_ptr(vm_cc_cme(cc)->def))->param.flags.forwardable) {
+ vm_call_iseq_fwd_setup(ec, reg_cfp, calling);
+ }
+ else {
+ vm_call_iseq_setup(ec, reg_cfp, calling);
+ }
VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
- return vm_exec(ec, true); /* CHECK_INTS in this function */
+ return vm_exec(ec); // CHECK_INTS in this function
}
case VM_METHOD_TYPE_NOTIMPLEMENTED:
case VM_METHOD_TYPE_CFUNC:
@@ -276,11 +295,15 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
}
case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
vm_call_check_arity(calling, 0, argv);
- ret = vm_call_opt_struct_aref0(ec, calling);
+ VM_CALL_METHOD_ATTR(ret,
+ vm_call_opt_struct_aref0(ec, calling),
+ (void)0);
goto success;
case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
vm_call_check_arity(calling, 1, argv);
- ret = vm_call_opt_struct_aset0(ec, calling, argv[0]);
+ VM_CALL_METHOD_ATTR(ret,
+ vm_call_opt_struct_aset0(ec, calling, argv[0]),
+ (void)0);
goto success;
default:
rb_bug("vm_call0: unsupported optimized method type (%d)", vm_cc_cme(cc)->def->body.optimized.type);
@@ -297,7 +320,7 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
return ret;
}
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_vm_call_kw(rb_execution_context_t *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me, int kw_splat)
{
return rb_vm_call0(ec, recv, id, argc, argv, me, kw_splat);
@@ -352,20 +375,16 @@ rb_current_receiver(void)
return cfp->self;
}
-#endif /* #ifndef MJIT_HEADER */
-
static inline void
stack_check(rb_execution_context_t *ec)
{
if (!rb_ec_raised_p(ec, RAISED_STACKOVERFLOW) &&
rb_ec_stack_check(ec)) {
rb_ec_raised_set(ec, RAISED_STACKOVERFLOW);
- rb_ec_stack_overflow(ec, FALSE);
+ rb_ec_stack_overflow(ec, 0);
}
}
-#ifndef MJIT_HEADER
-
void
rb_check_stack_overflow(void)
{
@@ -380,73 +399,56 @@ NORETURN(static void uncallable_object(VALUE recv, ID mid));
static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid);
static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self);
-static const struct rb_callcache *
-cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme)
+static VALUE
+gccct_hash(VALUE klass, VALUE box_value, ID mid)
{
- const struct rb_callcache *cc = NULL;
-
- RB_VM_LOCK_ENTER();
- {
- struct rb_class_cc_entries *ccs;
- struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
- VALUE ccs_data;
-
- if (rb_id_table_lookup(cc_tbl, mid, &ccs_data)) {
- // ok
- ccs = (struct rb_class_cc_entries *)ccs_data;
- }
- else {
- ccs = vm_ccs_create(klass, cc_tbl, mid, cme);
- }
+ return ((klass ^ box_value) >> 3) ^ (VALUE)mid;
+}
- for (int i=0; i<ccs->len; i++) {
- cc = ccs->entries[i].cc;
- if (vm_cc_cme(cc) == cme) {
- break;
- }
- cc = NULL;
- }
+NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo * ci));
- if (cc == NULL) {
- const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, NULL); // TODO: proper ci
- cc = vm_cc_new(klass, cme, vm_call_general);
- METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme);
- vm_ccs_push(klass, ccs, ci, cc);
- }
- }
- RB_VM_LOCK_LEAVE();
+static const struct rb_callcache *
+gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo *ci)
+{
+ struct rb_call_data cd = {
+ .ci = ci,
+ .cc = NULL
+ };
- return cc;
-}
+ vm_search_method_slowpath0(vm->self, &cd, klass);
-static VALUE
-gccct_hash(VALUE klass, ID mid)
-{
- return (klass >> 3) ^ (VALUE)mid;
+ return vm->global_cc_cache_table[index] = cd.cc;
}
-NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index));
-
-static const struct rb_callcache *
-gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index)
+static void
+scope_to_ci(call_type scope, ID mid, int argc, struct rb_callinfo *ci)
{
- const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
- const struct rb_callcache *cc;
+ int flags = 0;
- if (cme != NULL) {
- cc = cc_new(klass, mid, argc, cme);
- }
- else {
- cc = NULL;
+ switch(scope) {
+ case CALL_PUBLIC:
+ break;
+ case CALL_FCALL:
+ flags |= VM_CALL_FCALL;
+ break;
+ case CALL_VCALL:
+ flags |= VM_CALL_VCALL;
+ break;
+ case CALL_PUBLIC_KW:
+ flags |= VM_CALL_KWARG;
+ break;
+ case CALL_FCALL_KW:
+ flags |= (VM_CALL_KWARG | VM_CALL_FCALL);
+ break;
}
-
- return vm->global_cc_cache_table[index] = cc;
+ *ci = VM_CI_ON_STACK(mid, flags, argc, NULL);
}
static inline const struct rb_callcache *
-gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
+gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci)
{
- VALUE klass;
+ VALUE klass, box_value;
+ const rb_box_t *box = rb_current_box();
if (!SPECIAL_CONST_P(recv)) {
klass = RBASIC_CLASS(recv);
@@ -456,8 +458,14 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
klass = CLASS_OF(recv);
}
+ if (BOX_USER_P(box)) {
+ box_value = box->box_object;
+ }
+ else {
+ box_value = 0;
+ }
// search global method cache
- unsigned int index = (unsigned int)(gccct_hash(klass, mid) % VM_GLOBAL_CC_CACHE_TABLE_SIZE);
+ unsigned int index = (unsigned int)(gccct_hash(klass, box_value, mid) % VM_GLOBAL_CC_CACHE_TABLE_SIZE);
rb_vm_t *vm = rb_ec_vm_ptr(ec);
const struct rb_callcache *cc = vm->global_cc_cache_table[index];
@@ -479,24 +487,35 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
}
RB_DEBUG_COUNTER_INC(gccct_miss);
- return gccct_method_search_slowpath(vm, klass, mid, argc, index);
+ return gccct_method_search_slowpath(vm, klass, index, ci);
+}
+
+VALUE
+rb_gccct_clear_table(VALUE _self)
+{
+ int i;
+ rb_vm_t *vm = GET_VM();
+ for (i=0; i<VM_GLOBAL_CC_CACHE_TABLE_SIZE; i++) {
+ vm->global_cc_cache_table[i] = NULL;
+ }
+ return Qnil;
}
-/*!
- * \internal
+/**
+ * @internal
* calls the specified method.
*
* This function is called by functions in rb_call* family.
- * \param ec current execution context
- * \param recv receiver of the method
- * \param mid an ID that represents the name of the method
- * \param argc the number of method arguments
- * \param argv a pointer to an array of method arguments
- * \param scope
- * \param self self in the caller. Qundef means no self is considered and
+ * @param ec current execution context
+ * @param recv receiver of the method
+ * @param mid an ID that represents the name of the method
+ * @param argc the number of method arguments
+ * @param argv a pointer to an array of method arguments
+ * @param scope
+ * @param self self in the caller. Qundef means no self is considered and
* protected methods cannot be called
*
- * \note \a self is used in order to controlling access to protected methods.
+ * @note `self` is used in order to controlling access to protected methods.
*/
static inline VALUE
rb_call0(rb_execution_context_t *ec,
@@ -520,13 +539,16 @@ rb_call0(rb_execution_context_t *ec,
break;
}
- const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+ struct rb_callinfo ci;
+ scope_to_ci(scope, mid, argc, &ci);
+
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci);
if (scope == CALL_PUBLIC) {
RB_DEBUG_COUNTER_INC(call0_public);
const rb_callable_method_entry_t *cc_cme = cc ? vm_cc_cme(cc) : NULL;
- const rb_callable_method_entry_t *cme = callable_method_entry_refeinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
+ const rb_callable_method_entry_t *cme = callable_method_entry_refinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
call_status = rb_method_call_status(ec, cme, scope, self);
if (UNLIKELY(call_status != MISSING_NONE)) {
@@ -722,13 +744,6 @@ rb_check_funcall_with_hook_kw(VALUE recv, ID mid, int argc, const VALUE *argv,
return rb_vm_call_kw(ec, recv, mid, argc, argv, me, kw_splat);
}
-VALUE
-rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv,
- rb_check_funcall_hook *hook, VALUE arg)
-{
- return rb_check_funcall_with_hook_kw(recv, mid, argc, argv, hook, arg, RB_NO_KEYWORDS);
-}
-
const char *
rb_type_str(enum ruby_value_type type)
{
@@ -777,29 +792,29 @@ uncallable_object(VALUE recv, ID mid)
if (SPECIAL_CONST_P(recv)) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on unexpected immediate object (%p)",
+ "method '%"PRIsVALUE"' called on unexpected immediate object (%p)",
mname, (void *)recv);
}
else if ((flags = RBASIC(recv)->flags) == 0) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on terminated object (%p)",
+ "method '%"PRIsVALUE"' called on terminated object (%p)",
mname, (void *)recv);
}
else if (!(typestr = rb_type_str(type = BUILTIN_TYPE(recv)))) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on broken T_?""?""?(0x%02x) object"
+ "method '%"PRIsVALUE"' called on broken T_?""?""?(0x%02x) object"
" (%p flags=0x%"PRIxVALUE")",
mname, type, (void *)recv, flags);
}
else if (T_OBJECT <= type && type < T_NIL) {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on hidden %s object"
+ "method '%"PRIsVALUE"' called on hidden %s object"
" (%p flags=0x%"PRIxVALUE")",
mname, typestr, (void *)recv, flags);
}
else {
rb_raise(rb_eNotImpError,
- "method `%"PRIsVALUE"' called on unexpected %s object"
+ "method '%"PRIsVALUE"' called on unexpected %s object"
" (%p flags=0x%"PRIxVALUE")",
mname, typestr, (void *)recv, flags);
}
@@ -858,16 +873,16 @@ rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry
}
-/*!
- * \internal
+/**
+ * @internal
* calls the specified method.
*
* This function is called by functions in rb_call* family.
- * \param recv receiver
- * \param mid an ID that represents the name of the method
- * \param argc the number of method arguments
- * \param argv a pointer to an array of method arguments
- * \param scope
+ * @param recv receiver
+ * @param mid an ID that represents the name of the method
+ * @param argc the number of method arguments
+ * @param argv a pointer to an array of method arguments
+ * @param scope
*/
static inline VALUE
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
@@ -926,14 +941,14 @@ rb_method_missing(int argc, const VALUE *argv, VALUE obj)
UNREACHABLE_RETURN(Qnil);
}
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
int argc, const VALUE *argv, int priv)
{
VALUE name = argv[0];
if (!format) {
- format = rb_fstring_lit("undefined method `%s' for %s%s%s");
+ format = rb_fstring_lit("undefined method '%1$s' for %3$s%4$s");
}
if (exc == rb_eNoMethodError) {
VALUE args = rb_ary_new4(argc - 1, argv + 1);
@@ -944,8 +959,6 @@ rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
}
}
-#endif /* #ifndef MJIT_HEADER */
-
static void
raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE obj,
enum method_missing_reason last_call_status)
@@ -965,17 +978,17 @@ raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VA
stack_check(ec);
if (last_call_status & MISSING_PRIVATE) {
- format = rb_fstring_lit("private method `%s' called for %s%s%s");
+ format = rb_fstring_lit("private method '%1$s' called for %3$s%4$s");
}
else if (last_call_status & MISSING_PROTECTED) {
- format = rb_fstring_lit("protected method `%s' called for %s%s%s");
+ format = rb_fstring_lit("protected method '%1$s' called for %3$s%4$s");
}
else if (last_call_status & MISSING_VCALL) {
- format = rb_fstring_lit("undefined local variable or method `%s' for %s%s%s");
+ format = rb_fstring_lit("undefined local variable or method '%1$s' for %3$s%4$s");
exc = rb_eNameError;
}
else if (last_call_status & MISSING_SUPER) {
- format = rb_fstring_lit("super: no superclass method `%s' for %s%s%s");
+ format = rb_fstring_lit("super: no superclass method '%1$s' for %3$s%4$s");
}
{
@@ -1035,13 +1048,15 @@ method_missing(rb_execution_context_t *ec, VALUE obj, ID id, int argc, const VAL
UNREACHABLE_RETURN(Qundef);
}
-#ifndef MJIT_HEADER
-
static inline VALUE
rb_funcallv_scope(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
{
rb_execution_context_t *ec = GET_EC();
- const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+
+ struct rb_callinfo ci;
+ scope_to_ci(scope, mid, argc, &ci);
+
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci);
VALUE self = ec->cfp->self;
if (LIKELY(cc) &&
@@ -1089,7 +1104,7 @@ rb_apply(VALUE recv, ID mid, VALUE args)
return ret;
}
argv = ALLOCA_N(VALUE, argc);
- MEMCPY(argv, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, argc);
+ MEMCPY(argv, RARRAY_CONST_PTR(args), VALUE, argc);
return rb_funcallv(recv, mid, argc, argv);
}
@@ -1122,15 +1137,15 @@ rb_funcall(VALUE recv, ID mid, int n, ...)
return rb_funcallv(recv, mid, n, argv);
}
-/*!
+/**
* Calls a method only if it is the basic method of `ancestor`
* otherwise returns Qundef;
- * \param recv receiver of the method
- * \param mid an ID that represents the name of the method
- * \param ancestor the Class that defined the basic method
- * \param argc the number of arguments
- * \param argv pointer to an array of method arguments
- * \param kw_splat bool
+ * @param recv receiver of the method
+ * @param mid an ID that represents the name of the method
+ * @param ancestor the Class that defined the basic method
+ * @param argc the number of arguments
+ * @param argv pointer to an array of method arguments
+ * @param kw_splat bool
*/
VALUE
rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VALUE *argv, int kw_splat)
@@ -1364,6 +1379,17 @@ rb_yield(VALUE val)
}
}
+VALUE
+rb_ec_yield(rb_execution_context_t *ec, VALUE val)
+{
+ if (UNDEF_P(val)) {
+ return vm_yield(ec, 0, NULL, RB_NO_KEYWORDS);
+ }
+ else {
+ return vm_yield(ec, 1, &val, RB_NO_KEYWORDS);
+ }
+}
+
#undef rb_yield_values
VALUE
rb_yield_values(int n, ...)
@@ -1507,13 +1533,6 @@ rb_iterate_internal(VALUE (* it_proc)(VALUE), VALUE data1,
GET_EC());
}
-VALUE
-rb_iterate(VALUE (* it_proc)(VALUE), VALUE data1,
- rb_block_call_func_t bl_proc, VALUE data2)
-{
- return rb_iterate_internal(it_proc, data1, bl_proc, data2);
-}
-
struct iter_method_arg {
VALUE obj;
ID mid;
@@ -1554,6 +1573,37 @@ rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv,
return rb_iterate_internal(iterate_method, (VALUE)&arg, bl_proc, data2);
}
+/*
+ * A flexible variant of rb_block_call and rb_block_call_kw.
+ * This function accepts flags:
+ *
+ * RB_NO_KEYWORDS, RB_PASS_KEYWORDS, RB_PASS_CALLED_KEYWORDS:
+ * Works as the same as rb_block_call_kw.
+ *
+ * RB_BLOCK_NO_USE_PACKED_ARGS:
+ * The given block ("bl_proc") does not use "yielded_arg" of rb_block_call_func_t.
+ * Instead, the block accesses the yielded arguments via "argc" and "argv".
+ * This flag allows the called method to yield arguments without allocating an Array.
+ */
+VALUE
+rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv,
+ rb_block_call_func_t bl_proc, VALUE data2, long flags)
+{
+ struct iter_method_arg arg;
+
+ arg.obj = obj;
+ arg.mid = mid;
+ arg.argc = argc;
+ arg.argv = argv;
+ arg.kw_splat = flags & 1;
+
+ struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(bl_proc, (void *)data2);
+ if (flags & RB_BLOCK_NO_USE_PACKED_ARGS)
+ ifunc->flags |= IFUNC_YIELD_OPTIMIZABLE;
+
+ return rb_iterate0(iterate_method, (VALUE)&arg, ifunc, GET_EC());
+}
+
VALUE
rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
rb_block_call_func_t bl_proc, int min_argc, int max_argc,
@@ -1601,40 +1651,289 @@ rb_each(VALUE obj)
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
}
-void rb_parser_warn_location(VALUE, int);
+static VALUE eval_default_path = Qfalse;
+
+#define EVAL_LOCATION_MARK "eval at "
+#define EVAL_LOCATION_MARK_LEN (int)rb_strlen_lit(EVAL_LOCATION_MARK)
+
+static VALUE
+get_eval_default_path(void)
+{
+ int location_lineno;
+ VALUE location_path = rb_source_location(&location_lineno);
+ if (!NIL_P(location_path)) {
+ return rb_fstring(rb_sprintf("("EVAL_LOCATION_MARK"%"PRIsVALUE":%d)",
+ location_path, location_lineno));
+ }
+
+ if (!eval_default_path) {
+ eval_default_path = rb_fstring_lit("(eval)");
+ rb_vm_register_global_object(eval_default_path);
+ }
+ return eval_default_path;
+}
+
+static inline int
+compute_isolated_depth_from_ep(const VALUE *ep)
+{
+ int depth = 1;
+ while (1) {
+ if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) return depth;
+ if (VM_ENV_LOCAL_P(ep)) return 0;
+ ep = VM_ENV_PREV_EP(ep);
+ depth++;
+ }
+}
-static VALUE eval_default_path;
+static inline int
+compute_isolated_depth_from_block(const struct rb_block *blk)
+{
+ return compute_isolated_depth_from_ep(vm_block_ep(blk));
+}
static const rb_iseq_t *
-eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
- const struct rb_block *base_block)
+pm_eval_make_iseq(VALUE src, VALUE fname, int line,
+ const struct rb_block *base_block)
{
- const VALUE parser = rb_parser_new();
const rb_iseq_t *const parent = vm_block_iseq(base_block);
- rb_iseq_t *iseq = NULL;
- rb_ast_t *ast;
- int isolated_depth = 0;
+ const rb_iseq_t *iseq = parent;
+ VALUE name = rb_fstring_lit("<compiled>");
- // Conditionally enable coverage depending on the current mode:
- VALUE coverage_enabled = RBOOL(rb_get_coverage_mode() & COVERAGE_TARGET_EVAL);
+ int coverage_enabled = ((rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0) ? 1 : 0;
+ int isolated_depth = compute_isolated_depth_from_block(base_block);
- {
- int depth = 1;
- const VALUE *ep = vm_block_ep(base_block);
+ if (!fname) {
+ fname = rb_source_location(&line);
+ }
- while (1) {
- if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) {
- isolated_depth = depth;
- break;
+ if (!UNDEF_P(fname)) {
+ if (!NIL_P(fname)) fname = rb_fstring(fname);
+ }
+ else {
+ fname = get_eval_default_path();
+ coverage_enabled = 0;
+ }
+
+ pm_parse_result_t result = { 0 };
+ pm_options_line_set(&result.options, line);
+ result.node.coverage_enabled = coverage_enabled;
+
+ // Cout scopes, one for each parent iseq, plus one for our local scope
+ int scopes_count = 0;
+ do {
+ scopes_count++;
+ } while ((iseq = ISEQ_BODY(iseq)->parent_iseq));
+ pm_options_scopes_init(&result.options, scopes_count + 1);
+
+ // Walk over the scope tree, adding known locals at the correct depths. The
+ // scope array should be deepest -> shallowest. so lower indexes in the
+ // scopes array refer to root nodes on the tree, and higher indexes are the
+ // leaf nodes.
+ iseq = parent;
+ rb_encoding *encoding = rb_enc_get(src);
+
+#define FORWARDING_POSITIONALS_CHR '*'
+#define FORWARDING_POSITIONALS_STR "*"
+#define FORWARDING_KEYWORDS_CHR ':'
+#define FORWARDING_KEYWORDS_STR ":"
+#define FORWARDING_BLOCK_CHR '&'
+#define FORWARDING_BLOCK_STR "&"
+#define FORWARDING_ALL_CHR '.'
+#define FORWARDING_ALL_STR "."
+
+ for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
+ VALUE iseq_value = (VALUE)iseq;
+ int locals_count = ISEQ_BODY(iseq)->local_table_size;
+
+ pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
+ pm_options_scope_init(options_scope, locals_count);
+
+ uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
+
+ for (int local_index = 0; local_index < locals_count; local_index++) {
+ pm_string_t *scope_local = &options_scope->locals[local_index];
+ ID local = ISEQ_BODY(iseq)->local_table[local_index];
+
+ if (rb_is_local_id(local)) {
+ VALUE name_obj = rb_id2str(local);
+ const char *name = RSTRING_PTR(name_obj);
+ size_t length = strlen(name);
+
+ // Explicitly skip numbered parameters. These should not be sent
+ // into the eval.
+ if (length == 2 && name[0] == '_' && name[1] >= '1' && name[1] <= '9') {
+ continue;
+ }
+
+ // Check here if this local can be represented validly in the
+ // encoding of the source string. If it _cannot_, then it should
+ // not be added to the constant pool as it would not be able to
+ // be referenced anyway.
+ if (rb_enc_str_coderange_scan(name_obj, encoding) == ENC_CODERANGE_BROKEN) {
+ continue;
+ }
+
+ /* We need to duplicate the string because the Ruby string may
+ * be embedded so compaction could move the string and the pointer
+ * will change. */
+ char *name_dup = xmalloc(length + 1);
+ strlcpy(name_dup, name, length + 1);
+
+ RB_GC_GUARD(name_obj);
+
+ pm_string_owned_init(scope_local, (uint8_t *) name_dup, length);
}
- else if (VM_ENV_LOCAL_P(ep)) {
- break;
+ else if (local == idMULT) {
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS;
+ pm_string_constant_init(scope_local, FORWARDING_POSITIONALS_STR, 1);
}
- ep = VM_ENV_PREV_EP(ep);
- depth++;
+ else if (local == idPow) {
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS;
+ pm_string_constant_init(scope_local, FORWARDING_KEYWORDS_STR, 1);
+ }
+ else if (local == idAnd) {
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK;
+ pm_string_constant_init(scope_local, FORWARDING_BLOCK_STR, 1);
+ }
+ else if (local == idDot3) {
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL;
+ pm_string_constant_init(scope_local, FORWARDING_ALL_STR, 1);
+ }
+ }
+
+ pm_options_scope_forwarding_set(options_scope, forwarding);
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
+
+ /* We need to GC guard the iseq because the code above malloc memory
+ * which could trigger a GC. Since we only use ISEQ_BODY, the compiler
+ * may optimize out the iseq local variable so we need to GC guard it. */
+ RB_GC_GUARD(iseq_value);
+ }
+
+ // Add our empty local scope at the very end of the array for our eval
+ // scope's locals.
+ pm_options_scope_init(&result.options.scopes[scopes_count], 0);
+
+ VALUE script_lines;
+ VALUE error = pm_parse_string(&result, src, fname, ruby_vm_keep_script_lines ? &script_lines : NULL);
+
+ // If the parse failed, clean up and raise.
+ if (error != Qnil) {
+ pm_parse_result_free(&result);
+ rb_exc_raise(error);
+ }
+
+ // Create one scope node for each scope passed in, initialize the local
+ // lookup table with all the local variable information attached to the
+ // scope used by the parser.
+ pm_scope_node_t *node = &result.node;
+ iseq = parent;
+
+ for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
+ pm_scope_node_t *parent_scope = ruby_xcalloc(1, sizeof(pm_scope_node_t));
+ RUBY_ASSERT(parent_scope != NULL);
+
+ pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
+ parent_scope->coverage_enabled = coverage_enabled;
+ parent_scope->parser = &result.parser;
+ parent_scope->index_lookup_table = st_init_numtable();
+
+ int locals_count = ISEQ_BODY(iseq)->local_table_size;
+ parent_scope->local_table_for_iseq_size = locals_count;
+ pm_constant_id_list_init(&parent_scope->locals);
+
+ for (int local_index = 0; local_index < locals_count; local_index++) {
+ const pm_string_t *scope_local = &options_scope->locals[local_index];
+ pm_constant_id_t constant_id = 0;
+
+ const uint8_t *source = pm_string_source(scope_local);
+ size_t length = pm_string_length(scope_local);
+
+ if (length > 0) {
+ if (length == 1) {
+ switch (*source) {
+ case FORWARDING_POSITIONALS_CHR:
+ constant_id = PM_CONSTANT_MULT;
+ break;
+ case FORWARDING_KEYWORDS_CHR:
+ constant_id = PM_CONSTANT_POW;
+ break;
+ case FORWARDING_BLOCK_CHR:
+ constant_id = PM_CONSTANT_AND;
+ break;
+ case FORWARDING_ALL_CHR:
+ constant_id = PM_CONSTANT_DOT3;
+ break;
+ default:
+ constant_id = pm_constant_pool_insert_constant(&result.parser.constant_pool, source, length);
+ break;
+ }
+ }
+ else {
+ constant_id = pm_constant_pool_insert_constant(&result.parser.constant_pool, source, length);
+ }
+
+ st_insert(parent_scope->index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
+ }
+
+ pm_constant_id_list_append(&parent_scope->locals, constant_id);
}
+
+ node->previous = parent_scope;
+ node = parent_scope;
+ iseq = ISEQ_BODY(iseq)->parent_iseq;
}
+#undef FORWARDING_POSITIONALS_CHR
+#undef FORWARDING_POSITIONALS_STR
+#undef FORWARDING_KEYWORDS_CHR
+#undef FORWARDING_KEYWORDS_STR
+#undef FORWARDING_BLOCK_CHR
+#undef FORWARDING_BLOCK_STR
+#undef FORWARDING_ALL_CHR
+#undef FORWARDING_ALL_STR
+
+ int error_state;
+ iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, isolated_depth, &error_state);
+
+ pm_scope_node_t *prev = result.node.previous;
+ while (prev) {
+ pm_scope_node_t *next = prev->previous;
+ pm_constant_id_list_free(&prev->locals);
+ pm_scope_node_destroy(prev);
+ ruby_xfree(prev);
+ prev = next;
+ }
+
+ pm_parse_result_free(&result);
+
+ // If there was an error, raise it after memory has been cleaned up
+ if (error_state) {
+ RUBY_ASSERT(iseq == NULL);
+ rb_jump_tag(error_state);
+ }
+
+ rb_exec_event_hook_script_compiled(GET_EC(), iseq, src);
+
+ return iseq;
+}
+
+static const rb_iseq_t *
+eval_make_iseq(VALUE src, VALUE fname, int line,
+ const struct rb_block *base_block)
+{
+ if (rb_ruby_prism_p()) {
+ return pm_eval_make_iseq(src, fname, line, base_block);
+ }
+ const VALUE parser = rb_parser_new();
+ const rb_iseq_t *const parent = vm_block_iseq(base_block);
+ rb_iseq_t *iseq = NULL;
+ VALUE ast_value;
+ rb_ast_t *ast;
+
+ int coverage_enabled = (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0;
+ int isolated_depth = compute_isolated_depth_from_block(base_block);
+
if (!fname) {
fname = rb_source_location(&line);
}
@@ -1643,24 +1942,19 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
if (!NIL_P(fname)) fname = rb_fstring(fname);
}
else {
- fname = rb_fstring_lit("(eval)");
- if (!eval_default_path) {
- eval_default_path = rb_fstring_lit("(eval)");
- rb_gc_register_mark_object(eval_default_path);
- }
- fname = eval_default_path;
- coverage_enabled = Qfalse;
+ fname = get_eval_default_path();
+ coverage_enabled = FALSE;
}
rb_parser_set_context(parser, parent, FALSE);
- ast = rb_parser_compile_string_path(parser, fname, src, line);
- if (ast->body.root) {
- if (ast->body.compile_option == Qnil) {
- ast->body.compile_option = rb_obj_hide(rb_ident_hash_new());
- }
- rb_hash_aset(ast->body.compile_option, rb_sym_intern_ascii_cstr("coverage_enabled"), coverage_enabled);
+ if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser);
+ ast_value = rb_parser_compile_string_path(parser, fname, src, line);
+
+ ast = rb_ruby_ast_data_get(ast_value);
- iseq = rb_iseq_new_eval(&ast->body,
+ if (ast->body.root) {
+ ast->body.coverage_enabled = coverage_enabled;
+ iseq = rb_iseq_new_eval(ast_value,
ISEQ_BODY(parent)->location.label,
fname, Qnil, line,
parent, isolated_depth);
@@ -1695,7 +1989,11 @@ eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int li
block.as.captured.code.iseq = cfp->iseq;
block.type = block_type_iseq;
- iseq = eval_make_iseq(src, file, line, NULL, &block);
+ // EP is not escaped to the heap here, but captured and reused by another frame.
+ // ZJIT's locals are incompatible with it unlike YJIT's, so invalidate the ISEQ for ZJIT.
+ rb_zjit_invalidate_no_ep_escape(cfp->iseq);
+
+ iseq = eval_make_iseq(src, file, line, &block);
if (!iseq) {
rb_exc_raise(ec->errinfo);
}
@@ -1708,7 +2006,7 @@ eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int li
vm_set_eval_stack(ec, iseq, cref, &block);
/* kick */
- return vm_exec(ec, true);
+ return vm_exec(ec);
}
static VALUE
@@ -1716,7 +2014,7 @@ eval_string_with_scope(VALUE scope, VALUE src, VALUE file, int line)
{
rb_execution_context_t *ec = GET_EC();
rb_binding_t *bind = Check_TypedStruct(scope, &ruby_binding_data_type);
- const rb_iseq_t *iseq = eval_make_iseq(src, file, line, bind, &bind->block);
+ const rb_iseq_t *iseq = eval_make_iseq(src, file, line, &bind->block);
if (!iseq) {
rb_exc_raise(ec->errinfo);
}
@@ -1729,7 +2027,7 @@ eval_string_with_scope(VALUE scope, VALUE src, VALUE file, int line)
}
/* kick */
- return vm_exec(ec, true);
+ return vm_exec(ec);
}
/*
@@ -1758,7 +2056,7 @@ rb_f_eval(int argc, const VALUE *argv, VALUE self)
int line = 1;
rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline);
- SafeStringValue(src);
+ StringValue(src);
if (argc >= 3) {
StringValue(vfile);
}
@@ -1854,6 +2152,17 @@ rb_eval_string_wrap(const char *str, int *pstate)
VALUE
rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat)
{
+ Check_Type(arg, T_ARRAY);
+ int argc = RARRAY_LENINT(arg);
+ const VALUE *argv = RARRAY_CONST_PTR(arg);
+ VALUE val = rb_eval_cmd_call_kw(cmd, argc, argv, kw_splat);
+ RB_GC_GUARD(arg);
+ return val;
+}
+
+VALUE
+rb_eval_cmd_call_kw(VALUE cmd, int argc, const VALUE *argv, int kw_splat)
+{
enum ruby_tag_type state;
volatile VALUE val = Qnil; /* OK */
rb_execution_context_t * volatile ec = GET_EC();
@@ -1861,8 +2170,7 @@ rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
if (!RB_TYPE_P(cmd, T_STRING)) {
- val = rb_funcallv_kw(cmd, idCall, RARRAY_LENINT(arg),
- RARRAY_CONST_PTR(arg), kw_splat);
+ val = rb_funcallv_kw(cmd, idCall, argc, argv, kw_splat);
}
else {
val = eval_string_with_cref(rb_vm_top_self(), cmd, NULL, 0, 0);
@@ -1936,13 +2244,14 @@ rb_yield_refine_block(VALUE refinement, VALUE refinements)
else {
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
struct rb_captured_block new_captured = *captured;
+ const VALUE *const argv = &new_captured.self; /* dummy to suppress nonnull warning from gcc */
VALUE new_block_handler = VM_BH_FROM_ISEQ_BLOCK(&new_captured);
const VALUE *ep = captured->ep;
rb_cref_t *cref = vm_cref_push(ec, refinement, ep, TRUE, FALSE);
CREF_REFINEMENTS_SET(cref, refinements);
VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
new_captured.self = refinement;
- return vm_yield_with_cref(ec, 0, NULL, RB_NO_KEYWORDS, cref, FALSE);
+ return vm_yield_with_cref(ec, 0, argv, RB_NO_KEYWORDS, cref, FALSE);
}
}
@@ -1951,7 +2260,7 @@ static VALUE
eval_under(VALUE self, int singleton, VALUE src, VALUE file, int line)
{
rb_cref_t *cref = vm_cref_push(GET_EC(), self, NULL, FALSE, singleton);
- SafeStringValue(src);
+ StringValue(src);
return eval_string_with_cref(self, src, cref, file, line);
}
@@ -1964,19 +2273,24 @@ specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_spl
return yield_under(self, singleton, 1, &self, kw_splat);
}
else {
- VALUE file = Qundef;
+ VALUE file = Qnil;
int line = 1;
VALUE code;
rb_check_arity(argc, 1, 3);
code = argv[0];
- SafeStringValue(code);
+ StringValue(code);
if (argc > 2)
line = NUM2INT(argv[2]);
if (argc > 1) {
file = argv[1];
if (!NIL_P(file)) StringValue(file);
}
+
+ if (NIL_P(file)) {
+ file = get_eval_default_path();
+ }
+
return eval_under(self, singleton, code, file, line);
}
}
@@ -2387,11 +2701,31 @@ local_var_list_update(st_data_t *key, st_data_t *value, st_data_t arg, int exist
return ST_CONTINUE;
}
+extern int rb_numparam_id_p(ID id);
+
static void
local_var_list_add(const struct local_var_list *vars, ID lid)
{
- if (lid && rb_is_local_id(lid)) {
- /* should skip temporary variable */
+ /* should skip temporary variable */
+ if (!lid) return;
+ if (!rb_is_local_id(lid)) return;
+
+ /* should skip numbered parameters as well */
+ if (rb_numparam_id_p(lid)) return;
+
+ st_data_t idx = 0; /* tbl->num_entries */
+ rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
+}
+
+static void
+numparam_list_add(const struct local_var_list *vars, ID lid)
+{
+ /* should skip temporary variable */
+ if (!lid) return;
+ if (!rb_is_local_id(lid)) return;
+
+ /* should skip anything but numbered parameters */
+ if (rb_numparam_id_p(lid)) {
st_data_t idx = 0; /* tbl->num_entries */
rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
}
@@ -2503,13 +2837,33 @@ rb_current_realfilepath(void)
if (path == eval_default_path) {
return Qnil;
}
- else {
- return path;
+
+ // [Feature #19755] implicit eval location is "(eval at #{__FILE__}:#{__LINE__})"
+ const long len = RSTRING_LEN(path);
+ if (len > EVAL_LOCATION_MARK_LEN+1) {
+ const char *const ptr = RSTRING_PTR(path);
+ if (ptr[len - 1] == ')' &&
+ memcmp(ptr, "("EVAL_LOCATION_MARK, EVAL_LOCATION_MARK_LEN+1) == 0) {
+ return Qnil;
+ }
}
+
+ return path;
}
return Qnil;
}
+// Assert that an internal function is running and return
+// the imemo object that represents it.
+struct vm_ifunc *
+rb_current_ifunc(void)
+{
+ // Search VM_FRAME_MAGIC_IFUNC to see ifunc imemos put on the iseq field.
+ VALUE ifunc = (VALUE)GET_EC()->cfp->iseq;
+ RUBY_ASSERT_ALWAYS(imemo_type_p(ifunc, imemo_ifunc));
+ return (struct vm_ifunc *)ifunc;
+}
+
void
Init_vm_eval(void)
{
@@ -2551,5 +2905,3 @@ Init_vm_eval(void)
id_tag = rb_intern_const("tag");
id_value = rb_intern_const("value");
}
-
-#endif /* #ifndef MJIT_HEADER */