2008-05-25 05:12:12 +04:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
vm_eval.c -
|
|
|
|
|
|
|
|
$Author$
|
|
|
|
created at: Sat May 24 16:02:32 JST 2008
|
|
|
|
|
|
|
|
Copyright (C) 1993-2007 Yukihiro Matsumoto
|
|
|
|
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
|
|
|
|
Copyright (C) 2000 Information-technology Promotion Agency, Japan
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
2022-10-07 14:33:40 +03:00
|
|
|
#include "internal/thread.h"
|
2014-05-07 08:26:49 +04:00
|
|
|
struct local_var_list {
|
|
|
|
VALUE tbl;
|
|
|
|
};
|
|
|
|
|
2020-07-06 04:46:57 +03:00
|
|
|
static inline VALUE method_missing(rb_execution_context_t *ec, VALUE obj, ID id, int argc, const VALUE *argv, enum method_missing_reason call_status, int kw_splat);
|
2019-09-27 03:25:54 +03:00
|
|
|
static inline VALUE vm_yield_with_cref(rb_execution_context_t *ec, int argc, const VALUE *argv, int kw_splat, const rb_cref_t *cref, int is_lambda);
|
|
|
|
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);
|
2017-10-27 09:06:31 +03:00
|
|
|
static inline VALUE vm_yield_force_blockarg(rb_execution_context_t *ec, VALUE args);
|
2023-03-15 00:00:19 +03:00
|
|
|
VALUE vm_exec(rb_execution_context_t *ec);
|
2017-10-28 13:23:58 +03:00
|
|
|
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);
|
2017-10-27 03:46:11 +03:00
|
|
|
static int vm_collect_local_variables_in_heap(const VALUE *dfp, const struct local_var_list *vars);
|
2009-07-15 18:59:41 +04:00
|
|
|
|
2014-11-15 10:28:08 +03:00
|
|
|
static VALUE rb_eUncaughtThrow;
|
2015-10-22 12:58:01 +03:00
|
|
|
static ID id_result, id_tag, id_value;
|
2014-11-16 11:33:35 +03:00
|
|
|
#define id_mesg idMesg
|
2014-11-15 10:28:08 +03:00
|
|
|
|
2009-10-30 10:57:21 +03:00
|
|
|
static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope);
|
2020-12-14 23:40:38 +03:00
|
|
|
static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv);
|
2012-10-14 21:54:21 +04:00
|
|
|
|
Generalize cfunc large array splat fix to fix many additional cases raising SystemStackError
Originally, when 2e7bceb34ea858649e1f975a934ce1894d1f06a6 fixed cfuncs to no
longer use the VM stack for large array splats, it was thought to have fully
fixed Bug #4040, since the issue was fixed for methods defined in Ruby (iseqs)
back in Ruby 2.2.
After additional research, I determined that same issue affects almost all
types of method calls, not just iseq and cfunc calls. There were two main
types of remaining issues, important cases (where large array splat should
work) and pedantic cases (where large array splat raised SystemStackError
instead of ArgumentError).
Important cases:
```ruby
define_method(:a){|*a|}
a(*1380888.times)
def b(*a); end
send(:b, *1380888.times)
:b.to_proc.call(self, *1380888.times)
def d; yield(*1380888.times) end
d(&method(:b))
def self.method_missing(*a); end
not_a_method(*1380888.times)
```
Pedantic cases:
```ruby
def a; end
a(*1380888.times)
def b(_); end
b(*1380888.times)
def c(_=nil); end
c(*1380888.times)
c = Class.new do
attr_accessor :a
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
c = Struct.new(:a) do
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
```
This patch fixes all usage of CALLER_SETUP_ARG with splatting a large
number of arguments, and required similar fixes to use a temporary
hidden array in three other cases where the VM would use the VM stack
for handling a large number of arguments. However, it is possible
there may be additional cases where splatting a large number
of arguments still causes a SystemStackError.
This has a measurable performance impact, as it requires additional
checks for a large number of arguments in many additional cases.
This change is fairly invasive, as there were many different VM
functions that needed to be modified to support this. To avoid
too much API change, I modified struct rb_calling_info to add a
heap_argv member for storing the array, so I would not have to
thread it through many functions. This struct is always stack
allocated, which helps ensure sure GC doesn't collect it early.
Because of how invasive the changes are, and how rarely large
arrays are actually splatted in Ruby code, the existing test/spec
suites are not great at testing for correct behavior. To try to
find and fix all issues, I tested this in CI with
VM_ARGC_STACK_MAX to -1, ensuring that a temporary array is used
for all array splat method calls. This was very helpful in
finding breaking cases, especially ones involving flagged keyword
hashes.
Fixes [Bug #4040]
Co-authored-by: Jimmy Miller <jimmy.miller@shopify.com>
2023-03-07 02:58:58 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2023-03-07 08:34:31 +03:00
|
|
|
VALUE
|
2020-12-14 23:40:38 +03:00
|
|
|
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)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
Generalize cfunc large array splat fix to fix many additional cases raising SystemStackError
Originally, when 2e7bceb34ea858649e1f975a934ce1894d1f06a6 fixed cfuncs to no
longer use the VM stack for large array splats, it was thought to have fully
fixed Bug #4040, since the issue was fixed for methods defined in Ruby (iseqs)
back in Ruby 2.2.
After additional research, I determined that same issue affects almost all
types of method calls, not just iseq and cfunc calls. There were two main
types of remaining issues, important cases (where large array splat should
work) and pedantic cases (where large array splat raised SystemStackError
instead of ArgumentError).
Important cases:
```ruby
define_method(:a){|*a|}
a(*1380888.times)
def b(*a); end
send(:b, *1380888.times)
:b.to_proc.call(self, *1380888.times)
def d; yield(*1380888.times) end
d(&method(:b))
def self.method_missing(*a); end
not_a_method(*1380888.times)
```
Pedantic cases:
```ruby
def a; end
a(*1380888.times)
def b(_); end
b(*1380888.times)
def c(_=nil); end
c(*1380888.times)
c = Class.new do
attr_accessor :a
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
c = Struct.new(:a) do
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
```
This patch fixes all usage of CALLER_SETUP_ARG with splatting a large
number of arguments, and required similar fixes to use a temporary
hidden array in three other cases where the VM would use the VM stack
for handling a large number of arguments. However, it is possible
there may be additional cases where splatting a large number
of arguments still causes a SystemStackError.
This has a measurable performance impact, as it requires additional
checks for a large number of arguments in many additional cases.
This change is fairly invasive, as there were many different VM
functions that needed to be modified to support this. To avoid
too much API change, I modified struct rb_calling_info to add a
heap_argv member for storing the array, so I would not have to
thread it through many functions. This struct is always stack
allocated, which helps ensure sure GC doesn't collect it early.
Because of how invasive the changes are, and how rarely large
arrays are actually splatted in Ruby code, the existing test/spec
suites are not great at testing for correct behavior. To try to
find and fix all issues, I tested this in CI with
VM_ARGC_STACK_MAX to -1, ensuring that a temporary array is used
for all array splat method calls. This was very helpful in
finding breaking cases, especially ones involving flagged keyword
hashes.
Fixes [Bug #4040]
Co-authored-by: Jimmy Miller <jimmy.miller@shopify.com>
2023-03-07 02:58:58 +03:00
|
|
|
const struct rb_callcache cc = VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme);
|
|
|
|
return vm_call0_cc(ec, recv, id, argc, argv, &cc, kw_splat);
|
2021-01-20 21:33:59 +03:00
|
|
|
}
|
|
|
|
|
2023-03-07 08:34:31 +03:00
|
|
|
VALUE
|
2021-09-30 22:35:27 +03:00
|
|
|
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 =
|
|
|
|
rb_callable_method_entry_with_refinements(CLASS_OF(recv), id, NULL);
|
|
|
|
if (me) {
|
|
|
|
return rb_vm_call0(ec, recv, id, argc, argv, me, kw_splat);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* fallback to funcall (e.g. method_missing) */
|
|
|
|
return rb_funcallv(recv, id, argc, argv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
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)
|
|
|
|
{
|
Generalize cfunc large array splat fix to fix many additional cases raising SystemStackError
Originally, when 2e7bceb34ea858649e1f975a934ce1894d1f06a6 fixed cfuncs to no
longer use the VM stack for large array splats, it was thought to have fully
fixed Bug #4040, since the issue was fixed for methods defined in Ruby (iseqs)
back in Ruby 2.2.
After additional research, I determined that same issue affects almost all
types of method calls, not just iseq and cfunc calls. There were two main
types of remaining issues, important cases (where large array splat should
work) and pedantic cases (where large array splat raised SystemStackError
instead of ArgumentError).
Important cases:
```ruby
define_method(:a){|*a|}
a(*1380888.times)
def b(*a); end
send(:b, *1380888.times)
:b.to_proc.call(self, *1380888.times)
def d; yield(*1380888.times) end
d(&method(:b))
def self.method_missing(*a); end
not_a_method(*1380888.times)
```
Pedantic cases:
```ruby
def a; end
a(*1380888.times)
def b(_); end
b(*1380888.times)
def c(_=nil); end
c(*1380888.times)
c = Class.new do
attr_accessor :a
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
c = Struct.new(:a) do
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
```
This patch fixes all usage of CALLER_SETUP_ARG with splatting a large
number of arguments, and required similar fixes to use a temporary
hidden array in three other cases where the VM would use the VM stack
for handling a large number of arguments. However, it is possible
there may be additional cases where splatting a large number
of arguments still causes a SystemStackError.
This has a measurable performance impact, as it requires additional
checks for a large number of arguments in many additional cases.
This change is fairly invasive, as there were many different VM
functions that needed to be modified to support this. To avoid
too much API change, I modified struct rb_calling_info to add a
heap_argv member for storing the array, so I would not have to
thread it through many functions. This struct is always stack
allocated, which helps ensure sure GC doesn't collect it early.
Because of how invasive the changes are, and how rarely large
arrays are actually splatted in Ruby code, the existing test/spec
suites are not great at testing for correct behavior. To try to
find and fix all issues, I tested this in CI with
VM_ARGC_STACK_MAX to -1, ensuring that a temporary array is used
for all array splat method calls. This was very helpful in
finding breaking cases, especially ones involving flagged keyword
hashes.
Fixes [Bug #4040]
Co-authored-by: Jimmy Miller <jimmy.miller@shopify.com>
2023-03-07 02:58:58 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
struct rb_calling_info calling = {
|
2023-07-31 10:04:16 +03:00
|
|
|
.cd = &(struct rb_call_data) {
|
|
|
|
.ci = &VM_CI_ON_STACK(id, flags, argc, NULL),
|
|
|
|
.cc = NULL,
|
|
|
|
},
|
2021-01-20 21:33:59 +03:00
|
|
|
.cc = cc,
|
|
|
|
.block_handler = vm_passed_block_handler(ec),
|
2020-06-04 07:03:13 +03:00
|
|
|
.recv = recv,
|
|
|
|
.argc = argc,
|
|
|
|
.kw_splat = kw_splat,
|
|
|
|
};
|
|
|
|
|
Generalize cfunc large array splat fix to fix many additional cases raising SystemStackError
Originally, when 2e7bceb34ea858649e1f975a934ce1894d1f06a6 fixed cfuncs to no
longer use the VM stack for large array splats, it was thought to have fully
fixed Bug #4040, since the issue was fixed for methods defined in Ruby (iseqs)
back in Ruby 2.2.
After additional research, I determined that same issue affects almost all
types of method calls, not just iseq and cfunc calls. There were two main
types of remaining issues, important cases (where large array splat should
work) and pedantic cases (where large array splat raised SystemStackError
instead of ArgumentError).
Important cases:
```ruby
define_method(:a){|*a|}
a(*1380888.times)
def b(*a); end
send(:b, *1380888.times)
:b.to_proc.call(self, *1380888.times)
def d; yield(*1380888.times) end
d(&method(:b))
def self.method_missing(*a); end
not_a_method(*1380888.times)
```
Pedantic cases:
```ruby
def a; end
a(*1380888.times)
def b(_); end
b(*1380888.times)
def c(_=nil); end
c(*1380888.times)
c = Class.new do
attr_accessor :a
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
c = Struct.new(:a) do
alias b a=
end.new
c.a(*1380888.times)
c.b(*1380888.times)
```
This patch fixes all usage of CALLER_SETUP_ARG with splatting a large
number of arguments, and required similar fixes to use a temporary
hidden array in three other cases where the VM would use the VM stack
for handling a large number of arguments. However, it is possible
there may be additional cases where splatting a large number
of arguments still causes a SystemStackError.
This has a measurable performance impact, as it requires additional
checks for a large number of arguments in many additional cases.
This change is fairly invasive, as there were many different VM
functions that needed to be modified to support this. To avoid
too much API change, I modified struct rb_calling_info to add a
heap_argv member for storing the array, so I would not have to
thread it through many functions. This struct is always stack
allocated, which helps ensure sure GC doesn't collect it early.
Because of how invasive the changes are, and how rarely large
arrays are actually splatted in Ruby code, the existing test/spec
suites are not great at testing for correct behavior. To try to
find and fix all issues, I tested this in CI with
VM_ARGC_STACK_MAX to -1, ensuring that a temporary array is used
for all array splat method calls. This was very helpful in
finding breaking cases, especially ones involving flagged keyword
hashes.
Fixes [Bug #4040]
Co-authored-by: Jimmy Miller <jimmy.miller@shopify.com>
2023-03-07 02:58:58 +03:00
|
|
|
return vm_call0_body(ec, &calling, use_argv);
|
2012-10-14 21:54:21 +04:00
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
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)
|
|
|
|
{
|
2022-10-03 18:14:32 +03:00
|
|
|
calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme);
|
2021-01-20 21:33:59 +03:00
|
|
|
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)
|
|
|
|
{
|
2023-07-31 10:04:16 +03:00
|
|
|
ID mid = vm_ci_mid(calling->cd->ci);
|
2021-01-20 21:33:59 +03:00
|
|
|
klass = RCLASS_SUPER(klass);
|
|
|
|
|
|
|
|
if (klass) {
|
|
|
|
const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
|
|
|
|
|
|
|
|
if (cme) {
|
|
|
|
RUBY_VM_CHECK_INTS(ec);
|
|
|
|
return vm_call0_cme(ec, calling, argv, cme);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vm_passed_block_handler_set(ec, calling->block_handler);
|
|
|
|
return method_missing(ec, calling->recv, mid, calling->argc, argv, ex, calling->kw_splat);
|
|
|
|
}
|
|
|
|
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
static VALUE
|
2020-12-14 23:40:38 +03:00
|
|
|
vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv)
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
{
|
2023-07-31 10:04:16 +03:00
|
|
|
const struct rb_callinfo *ci = calling->cd->ci;
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
VALUE val;
|
2021-01-20 21:33:59 +03:00
|
|
|
const rb_callable_method_entry_t *me = vm_cc_cme(calling->cc);
|
2019-05-31 09:58:50 +03:00
|
|
|
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
|
2012-11-13 13:48:08 +04:00
|
|
|
int len = cfunc->argc;
|
2015-09-19 20:59:58 +03:00
|
|
|
VALUE recv = calling->recv;
|
|
|
|
int argc = calling->argc;
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
ID mid = vm_ci_mid(ci);
|
2016-07-28 14:02:30 +03:00
|
|
|
VALUE block_handler = calling->block_handler;
|
2019-09-13 19:31:13 +03:00
|
|
|
int frame_flags = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL;
|
|
|
|
|
|
|
|
if (calling->kw_splat) {
|
|
|
|
if (argc > 0 && RB_TYPE_P(argv[argc-1], T_HASH) && RHASH_EMPTY_P(argv[argc-1])) {
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
frame_flags |= VM_FRAME_FLAG_CFRAME_KW;
|
|
|
|
}
|
|
|
|
}
|
2012-11-13 13:48:08 +04:00
|
|
|
|
2017-11-07 11:19:25 +03:00
|
|
|
RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
|
2017-10-29 16:19:14 +03:00
|
|
|
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, mid, me->owner, Qnil);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
{
|
2017-10-27 05:49:30 +03:00
|
|
|
rb_control_frame_t *reg_cfp = ec->cfp;
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
|
2019-09-13 19:31:13 +03:00
|
|
|
vm_push_frame(ec, 0, frame_flags, recv,
|
2016-07-28 14:02:30 +03:00
|
|
|
block_handler, (VALUE)me,
|
|
|
|
0, reg_cfp->sp, 0, 0);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
|
2012-11-13 13:48:08 +04:00
|
|
|
if (len >= 0) rb_check_arity(argc, len, len);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
|
2019-02-22 10:25:54 +03:00
|
|
|
val = (*cfunc->invoker)(recv, argc, argv, cfunc->func);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
|
2017-04-11 07:17:45 +03:00
|
|
|
CHECK_CFP_CONSISTENCY("vm_call0_cfunc_with_frame");
|
2017-10-27 05:49:30 +03:00
|
|
|
rb_vm_pop_frame(ec);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
}
|
2017-10-29 16:19:14 +03:00
|
|
|
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, mid, me->owner, val);
|
2017-11-07 11:19:25 +03:00
|
|
|
RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2020-12-14 23:40:38 +03:00
|
|
|
vm_call0_cfunc(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv)
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
{
|
2020-12-14 23:40:38 +03:00
|
|
|
return vm_call0_cfunc_with_frame(ec, calling, argv);
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
}
|
|
|
|
|
2021-11-18 05:01:31 +03:00
|
|
|
static void
|
|
|
|
vm_call_check_arity(struct rb_calling_info *calling, int argc, const VALUE *argv)
|
|
|
|
{
|
|
|
|
if (calling->kw_splat &&
|
|
|
|
calling->argc > 0 &&
|
|
|
|
RB_TYPE_P(argv[calling->argc-1], T_HASH) &&
|
|
|
|
RHASH_EMPTY_P(argv[calling->argc-1])) {
|
|
|
|
calling->argc--;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_check_arity(calling->argc, argc, argc);
|
|
|
|
}
|
|
|
|
|
2012-10-15 16:29:42 +04:00
|
|
|
/* `ci' should point temporal value (on stack value) */
|
2012-10-14 21:54:21 +04:00
|
|
|
static VALUE
|
2020-12-14 23:40:38 +03:00
|
|
|
vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv)
|
2012-10-14 21:54:21 +04:00
|
|
|
{
|
2023-07-31 10:04:16 +03:00
|
|
|
const struct rb_callinfo *ci = calling->cd->ci;
|
2020-12-14 23:40:38 +03:00
|
|
|
const struct rb_callcache *cc = calling->cc;
|
2013-02-19 03:53:41 +04:00
|
|
|
VALUE ret;
|
|
|
|
|
2021-02-03 09:29:26 +03:00
|
|
|
retry:
|
|
|
|
|
2020-01-08 10:14:01 +03:00
|
|
|
switch (vm_cc_cme(cc)->def->type) {
|
2012-10-24 00:34:25 +04:00
|
|
|
case VM_METHOD_TYPE_ISEQ:
|
|
|
|
{
|
2017-10-27 05:49:30 +03:00
|
|
|
rb_control_frame_t *reg_cfp = ec->cfp;
|
2012-10-24 00:34:25 +04:00
|
|
|
int i;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2015-09-19 20:59:58 +03:00
|
|
|
CHECK_VM_STACK_OVERFLOW(reg_cfp, calling->argc + 1);
|
2019-02-01 10:26:39 +03:00
|
|
|
vm_check_canary(ec, reg_cfp->sp);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2015-09-19 20:59:58 +03:00
|
|
|
*reg_cfp->sp++ = calling->recv;
|
|
|
|
for (i = 0; i < calling->argc; i++) {
|
2012-10-24 00:34:25 +04:00
|
|
|
*reg_cfp->sp++ = argv[i];
|
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2024-05-25 00:33:03 +03:00
|
|
|
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);
|
|
|
|
}
|
2017-10-27 05:49:30 +03:00
|
|
|
VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
|
2023-03-15 00:00:19 +03:00
|
|
|
return vm_exec(ec); // CHECK_INTS in this function
|
2012-10-24 00:34:25 +04:00
|
|
|
}
|
2010-08-07 08:33:33 +04:00
|
|
|
case VM_METHOD_TYPE_NOTIMPLEMENTED:
|
* vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME):
add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'.
This optimization makes all cfunc method calls `frameless', which
is fster than ordinal cfunc method call.
If `frame' is needed (for example, it calls another method with
`rb_funcall()'), then build a frame. In other words, this
optimization delays frame building.
However, to delay the frame building, we need additional overheads:
(1) Store the last call information.
(2) Check the delayed frame buidling before the frame is needed.
(3) Overhead to build a delayed frame.
rb_thread_t::passed_ci is storage of delayed cfunc call information.
(1) is lightweight because it is only 1 assignment to `passed_ci'.
To achieve (2), we modify GET_THREAD() to check `passed_ci' every
time. It causes 10% overhead on my envrionment.
This optimization only works for cfunc methods which do not need
their `frame'.
After evaluation on my environment, this optimization does not
effective every time. Because of this evaluation results, this
optimization is disabled at default.
* vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour
of VM internals. I will extend this feature.
* vm_method.c, method.h: change parameters of the `invoker' function.
Receive `func' pointer as the first parameter.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-23 08:22:31 +04:00
|
|
|
case VM_METHOD_TYPE_CFUNC:
|
2020-12-14 23:40:38 +03:00
|
|
|
ret = vm_call0_cfunc(ec, calling, argv);
|
2013-02-19 03:53:41 +04:00
|
|
|
goto success;
|
2012-10-24 00:34:25 +04:00
|
|
|
case VM_METHOD_TYPE_ATTRSET:
|
2021-11-18 05:01:31 +03:00
|
|
|
vm_call_check_arity(calling, 1, argv);
|
2021-09-18 10:15:24 +03:00
|
|
|
VM_CALL_METHOD_ATTR(ret,
|
|
|
|
rb_ivar_set(calling->recv, vm_cc_cme(cc)->def->body.attr.id, argv[0]),
|
|
|
|
(void)0);
|
2013-02-19 03:53:41 +04:00
|
|
|
goto success;
|
2012-10-24 00:34:25 +04:00
|
|
|
case VM_METHOD_TYPE_IVAR:
|
2021-11-18 05:01:31 +03:00
|
|
|
vm_call_check_arity(calling, 0, argv);
|
2021-09-18 10:15:24 +03:00
|
|
|
VM_CALL_METHOD_ATTR(ret,
|
|
|
|
rb_attr_get(calling->recv, vm_cc_cme(cc)->def->body.attr.id),
|
|
|
|
(void)0);
|
2013-02-19 03:53:41 +04:00
|
|
|
goto success;
|
2012-10-24 00:34:25 +04:00
|
|
|
case VM_METHOD_TYPE_BMETHOD:
|
2020-12-14 23:40:38 +03:00
|
|
|
ret = vm_call_bmethod_body(ec, calling, argv);
|
2013-02-19 03:53:41 +04:00
|
|
|
goto success;
|
2012-10-14 21:54:21 +04:00
|
|
|
case VM_METHOD_TYPE_ZSUPER:
|
2021-01-20 21:33:59 +03:00
|
|
|
{
|
|
|
|
VALUE klass = RCLASS_ORIGIN(vm_cc_cme(cc)->defined_class);
|
|
|
|
return vm_call0_super(ec, calling, argv, klass, MISSING_SUPER);
|
|
|
|
}
|
* revised r37993 to avoid SEGV/ILL in tests. In r37993, a method
entry with VM_METHOD_TYPE_REFINED holds only the original method
definition, so ci->me is set to a method entry allocated in the
stack, and it causes SEGV/ILL. In this commit, a method entry
with VM_METHOD_TYPE_REFINED holds the whole original method entry.
Furthermore, rb_thread_mark() is changed to mark cfp->klass to
avoid GC for iclasses created by copy_refinement_iclass().
* vm_method.c (rb_method_entry_make): add a method entry with
VM_METHOD_TYPE_REFINED to the class refined by the refinement if
the target module is a refinement. When a method entry with
VM_METHOD_TYPE_UNDEF is invoked by vm_call_method(), a method with
the same name is searched in refinements. If such a method is
found, the method is invoked. Otherwise, the original method in
the refined class (rb_method_definition_t::body.orig_me) is
invoked. This change is made to simplify the normal method lookup
and to improve the performance of normal method calls.
* vm_method.c (EXPR1, search_method, rb_method_entry),
vm_eval.c (rb_call0, rb_search_method_entry): do not use
refinements for method lookup.
* vm_insnhelper.c (vm_call_method): search methods in refinements if
ci->me is VM_METHOD_TYPE_REFINED. If the method is called by
super (i.e., ci->call == vm_call_super_method), skip the same
method entry as the current method to avoid infinite call of the
same method.
* class.c (include_modules_at): add a refined method entry for each
method defined in a module included in a refinement.
* class.c (rb_prepend_module): set an empty table to
RCLASS_M_TBL(klass) to add refined method entries, because
refinements should have priority over prepended modules.
* proc.c (mnew): use rb_method_entry_with_refinements() to get
a refined method.
* vm.c (rb_thread_mark): mark cfp->klass for iclasses created by
copy_refinement_iclass().
* vm.c (Init_VM), cont.c (fiber_init): initialize th->cfp->klass.
* test/ruby/test_refinement.rb (test_inline_method_cache): do not skip
the test because it should pass successfully.
* test/ruby/test_refinement.rb (test_redefine_refined_method): new
test for the case a refined method is redefined.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-06 17:08:41 +04:00
|
|
|
case VM_METHOD_TYPE_REFINED:
|
2012-10-14 21:54:21 +04:00
|
|
|
{
|
2021-01-20 21:33:59 +03:00
|
|
|
const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
|
* revised r37993 to avoid SEGV/ILL in tests. In r37993, a method
entry with VM_METHOD_TYPE_REFINED holds only the original method
definition, so ci->me is set to a method entry allocated in the
stack, and it causes SEGV/ILL. In this commit, a method entry
with VM_METHOD_TYPE_REFINED holds the whole original method entry.
Furthermore, rb_thread_mark() is changed to mark cfp->klass to
avoid GC for iclasses created by copy_refinement_iclass().
* vm_method.c (rb_method_entry_make): add a method entry with
VM_METHOD_TYPE_REFINED to the class refined by the refinement if
the target module is a refinement. When a method entry with
VM_METHOD_TYPE_UNDEF is invoked by vm_call_method(), a method with
the same name is searched in refinements. If such a method is
found, the method is invoked. Otherwise, the original method in
the refined class (rb_method_definition_t::body.orig_me) is
invoked. This change is made to simplify the normal method lookup
and to improve the performance of normal method calls.
* vm_method.c (EXPR1, search_method, rb_method_entry),
vm_eval.c (rb_call0, rb_search_method_entry): do not use
refinements for method lookup.
* vm_insnhelper.c (vm_call_method): search methods in refinements if
ci->me is VM_METHOD_TYPE_REFINED. If the method is called by
super (i.e., ci->call == vm_call_super_method), skip the same
method entry as the current method to avoid infinite call of the
same method.
* class.c (include_modules_at): add a refined method entry for each
method defined in a module included in a refinement.
* class.c (rb_prepend_module): set an empty table to
RCLASS_M_TBL(klass) to add refined method entries, because
refinements should have priority over prepended modules.
* proc.c (mnew): use rb_method_entry_with_refinements() to get
a refined method.
* vm.c (rb_thread_mark): mark cfp->klass for iclasses created by
copy_refinement_iclass().
* vm.c (Init_VM), cont.c (fiber_init): initialize th->cfp->klass.
* test/ruby/test_refinement.rb (test_inline_method_cache): do not skip
the test because it should pass successfully.
* test/ruby/test_refinement.rb (test_redefine_refined_method): new
test for the case a refined method is redefined.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-06 17:08:41 +04:00
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
if (cme->def->body.refined.orig_me) {
|
|
|
|
const rb_callable_method_entry_t *orig_cme = refined_method_callable_without_refinement(cme);
|
|
|
|
return vm_call0_cme(ec, calling, argv, orig_cme);
|
2019-12-16 11:38:41 +03:00
|
|
|
}
|
2012-10-14 21:54:21 +04:00
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
VALUE klass = cme->defined_class;
|
|
|
|
return vm_call0_super(ec, calling, argv, klass, 0);
|
2009-07-16 04:38:07 +04:00
|
|
|
}
|
2015-05-30 21:45:28 +03:00
|
|
|
case VM_METHOD_TYPE_ALIAS:
|
2021-02-03 09:29:26 +03:00
|
|
|
{
|
|
|
|
const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
|
|
|
|
const rb_callable_method_entry_t *orig_cme = aliased_callable_method_entry(cme);
|
|
|
|
|
|
|
|
if (cme == orig_cme) rb_bug("same!!");
|
|
|
|
|
|
|
|
if (vm_cc_markable(cc)) {
|
|
|
|
return vm_call0_cme(ec, calling, argv, orig_cme);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*((const rb_callable_method_entry_t **)&cc->cme_) = orig_cme;
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
}
|
2012-10-24 00:34:25 +04:00
|
|
|
case VM_METHOD_TYPE_MISSING:
|
|
|
|
{
|
2021-01-20 21:33:59 +03:00
|
|
|
vm_passed_block_handler_set(ec, calling->block_handler);
|
2020-07-06 04:46:57 +03:00
|
|
|
return method_missing(ec, calling->recv, vm_ci_mid(ci), calling->argc,
|
2019-09-16 23:19:06 +03:00
|
|
|
argv, MISSING_NOENTRY, calling->kw_splat);
|
2012-10-24 00:34:25 +04:00
|
|
|
}
|
|
|
|
case VM_METHOD_TYPE_OPTIMIZED:
|
2021-11-17 18:43:40 +03:00
|
|
|
switch (vm_cc_cme(cc)->def->body.optimized.type) {
|
2009-07-16 04:38:07 +04:00
|
|
|
case OPTIMIZED_METHOD_TYPE_SEND:
|
2019-09-13 19:31:13 +03:00
|
|
|
ret = send_internal(calling->argc, argv, calling->recv, calling->kw_splat ? CALL_FCALL_KW : CALL_FCALL);
|
2013-02-19 03:53:41 +04:00
|
|
|
goto success;
|
2012-10-24 00:34:25 +04:00
|
|
|
case OPTIMIZED_METHOD_TYPE_CALL:
|
|
|
|
{
|
|
|
|
rb_proc_t *proc;
|
2015-09-19 20:59:58 +03:00
|
|
|
GetProcPtr(calling->recv, proc);
|
2019-09-03 19:32:42 +03:00
|
|
|
ret = rb_vm_invoke_proc(ec, proc, calling->argc, argv, calling->kw_splat, calling->block_handler);
|
2013-02-19 03:53:41 +04:00
|
|
|
goto success;
|
2012-10-24 00:34:25 +04:00
|
|
|
}
|
2021-11-18 05:01:31 +03:00
|
|
|
case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
|
|
|
|
vm_call_check_arity(calling, 0, argv);
|
2023-10-27 03:03:17 +03:00
|
|
|
VM_CALL_METHOD_ATTR(ret,
|
|
|
|
vm_call_opt_struct_aref0(ec, calling),
|
|
|
|
(void)0);
|
2021-11-18 05:01:31 +03:00
|
|
|
goto success;
|
|
|
|
case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
|
|
|
|
vm_call_check_arity(calling, 1, argv);
|
2023-10-27 03:03:17 +03:00
|
|
|
VM_CALL_METHOD_ATTR(ret,
|
|
|
|
vm_call_opt_struct_aset0(ec, calling, argv[0]),
|
|
|
|
(void)0);
|
2021-11-18 05:01:31 +03:00
|
|
|
goto success;
|
2009-07-16 04:38:07 +04:00
|
|
|
default:
|
2021-11-17 18:43:40 +03:00
|
|
|
rb_bug("vm_call0: unsupported optimized method type (%d)", vm_cc_cme(cc)->def->body.optimized.type);
|
2009-07-16 04:38:07 +04:00
|
|
|
}
|
|
|
|
break;
|
2012-10-24 00:34:25 +04:00
|
|
|
case VM_METHOD_TYPE_UNDEF:
|
|
|
|
break;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2020-01-08 10:14:01 +03:00
|
|
|
rb_bug("vm_call0: unsupported method type (%d)", vm_cc_cme(cc)->def->type);
|
2012-10-24 00:34:25 +04:00
|
|
|
return Qundef;
|
2013-02-19 03:53:41 +04:00
|
|
|
|
|
|
|
success:
|
2017-11-06 10:44:28 +03:00
|
|
|
RUBY_VM_CHECK_INTS(ec);
|
2013-02-19 03:53:41 +04:00
|
|
|
return ret;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2023-03-07 08:34:31 +03:00
|
|
|
VALUE
|
2019-09-06 05:25:34 +03:00
|
|
|
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)
|
|
|
|
{
|
2019-10-04 22:51:57 +03:00
|
|
|
return rb_vm_call0(ec, recv, id, argc, argv, me, kw_splat);
|
2019-09-06 05:25:34 +03:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
static inline VALUE
|
2019-09-14 11:49:33 +03:00
|
|
|
vm_call_super(rb_execution_context_t *ec, int argc, const VALUE *argv, int kw_splat)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-10-29 17:38:25 +03:00
|
|
|
VALUE recv = ec->cfp->self;
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE klass;
|
|
|
|
ID id;
|
2017-10-29 17:38:25 +03:00
|
|
|
rb_control_frame_t *cfp = ec->cfp;
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2016-08-03 04:50:50 +03:00
|
|
|
if (VM_FRAME_RUBYFRAME_P(cfp)) {
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_bug("vm_call_super: should not be reached");
|
|
|
|
}
|
|
|
|
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
klass = RCLASS_ORIGIN(me->defined_class);
|
2015-03-06 04:31:03 +03:00
|
|
|
klass = RCLASS_SUPER(klass);
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
id = me->def->original_id;
|
|
|
|
me = rb_callable_method_entry(klass, id);
|
|
|
|
|
2009-07-15 18:59:41 +04:00
|
|
|
if (!me) {
|
2020-07-06 04:46:57 +03:00
|
|
|
return method_missing(ec, recv, id, argc, argv, MISSING_SUPER, kw_splat);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2019-09-19 01:30:59 +03:00
|
|
|
return rb_vm_call_kw(ec, recv, id, argc, argv, me, kw_splat);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2019-09-14 11:49:33 +03:00
|
|
|
VALUE
|
|
|
|
rb_call_super_kw(int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
PASS_PASSED_BLOCK_HANDLER_EC(ec);
|
|
|
|
return vm_call_super(ec, argc, argv, kw_splat);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_call_super(int argc, const VALUE *argv)
|
|
|
|
{
|
2021-09-21 16:59:35 +03:00
|
|
|
return rb_call_super_kw(argc, argv, RB_NO_KEYWORDS);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2014-11-16 13:38:15 +03:00
|
|
|
VALUE
|
|
|
|
rb_current_receiver(void)
|
|
|
|
{
|
2017-10-29 17:39:51 +03:00
|
|
|
const rb_execution_context_t *ec = GET_EC();
|
2014-11-16 13:38:15 +03:00
|
|
|
rb_control_frame_t *cfp;
|
2017-10-29 17:39:51 +03:00
|
|
|
if (!ec || !(cfp = ec->cfp)) {
|
2014-11-16 13:38:15 +03:00
|
|
|
rb_raise(rb_eRuntimeError, "no self, no life");
|
2017-10-29 17:39:51 +03:00
|
|
|
}
|
2014-11-16 13:38:15 +03:00
|
|
|
return cfp->self;
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
static inline void
|
2017-10-28 15:23:51 +03:00
|
|
|
stack_check(rb_execution_context_t *ec)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-11-07 08:22:09 +03:00
|
|
|
if (!rb_ec_raised_p(ec, RAISED_STACKOVERFLOW) &&
|
2017-10-29 16:49:45 +03:00
|
|
|
rb_ec_stack_check(ec)) {
|
2017-11-07 08:22:09 +03:00
|
|
|
rb_ec_raised_set(ec, RAISED_STACKOVERFLOW);
|
|
|
|
rb_ec_stack_overflow(ec, FALSE);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 16:00:11 +03:00
|
|
|
void
|
|
|
|
rb_check_stack_overflow(void)
|
|
|
|
{
|
|
|
|
#ifndef RB_THREAD_LOCAL_SPECIFIER
|
|
|
|
if (!ruby_current_ec_key) return;
|
|
|
|
#endif
|
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
if (ec) stack_check(ec);
|
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
NORETURN(static void uncallable_object(VALUE recv, ID mid));
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid);
|
2017-10-28 15:23:51 +03:00
|
|
|
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);
|
2009-10-30 10:42:04 +03:00
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
static VALUE
|
|
|
|
gccct_hash(VALUE klass, ID mid)
|
|
|
|
{
|
|
|
|
return (klass >> 3) ^ (VALUE)mid;
|
|
|
|
}
|
|
|
|
|
2024-04-24 20:02:06 +03:00
|
|
|
NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo * ci));
|
2021-01-20 21:33:59 +03:00
|
|
|
|
|
|
|
static const struct rb_callcache *
|
2024-04-24 20:02:06 +03:00
|
|
|
gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo *ci)
|
2021-01-20 21:33:59 +03:00
|
|
|
{
|
2024-04-20 03:02:21 +03:00
|
|
|
struct rb_call_data cd = {
|
2024-04-24 20:02:06 +03:00
|
|
|
.ci = ci,
|
2024-04-20 03:02:21 +03:00
|
|
|
.cc = NULL
|
|
|
|
};
|
2021-01-20 21:33:59 +03:00
|
|
|
|
2024-04-24 20:02:06 +03:00
|
|
|
vm_search_method_slowpath0(vm->self, &cd, klass);
|
2021-01-20 21:33:59 +03:00
|
|
|
|
2024-04-24 20:02:06 +03:00
|
|
|
return vm->global_cc_cache_table[index] = cd.cc;
|
2021-01-20 21:33:59 +03:00
|
|
|
}
|
|
|
|
|
2024-04-24 20:20:07 +03:00
|
|
|
static void
|
|
|
|
scope_to_ci(call_type scope, ID mid, int argc, struct rb_callinfo *ci)
|
|
|
|
{
|
|
|
|
int flags = 0;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
*ci = VM_CI_ON_STACK(mid, flags, argc, NULL);
|
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
static inline const struct rb_callcache *
|
2024-04-24 20:20:07 +03:00
|
|
|
gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci)
|
2021-01-20 21:33:59 +03:00
|
|
|
{
|
|
|
|
VALUE klass;
|
|
|
|
|
|
|
|
if (!SPECIAL_CONST_P(recv)) {
|
|
|
|
klass = RBASIC_CLASS(recv);
|
|
|
|
if (UNLIKELY(!klass)) uncallable_object(recv, mid);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
klass = CLASS_OF(recv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// search global method cache
|
|
|
|
unsigned int index = (unsigned int)(gccct_hash(klass, 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];
|
|
|
|
|
|
|
|
if (LIKELY(cc)) {
|
|
|
|
if (LIKELY(vm_cc_class_check(cc, klass))) {
|
|
|
|
const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
|
|
|
|
if (LIKELY(!METHOD_ENTRY_INVALIDATED(cme) &&
|
|
|
|
cme->called_id == mid)) {
|
|
|
|
|
`Primitive.mandatory_only?` for fast path
Compare with the C methods, A built-in methods written in Ruby is
slower if only mandatory parameters are given because it needs to
check the argumens and fill default values for optional and keyword
parameters (C methods can check the number of parameters with `argc`,
so there are no overhead). Passing mandatory arguments are common
(optional arguments are exceptional, in many cases) so it is important
to provide the fast path for such common cases.
`Primitive.mandatory_only?` is a special builtin function used with
`if` expression like that:
```ruby
def self.at(time, subsec = false, unit = :microsecond, in: nil)
if Primitive.mandatory_only?
Primitive.time_s_at1(time)
else
Primitive.time_s_at(time, subsec, unit, Primitive.arg!(:in))
end
end
```
and it makes two ISeq,
```
def self.at(time, subsec = false, unit = :microsecond, in: nil)
Primitive.time_s_at(time, subsec, unit, Primitive.arg!(:in))
end
def self.at(time)
Primitive.time_s_at1(time)
end
```
and (2) is pointed by (1). Note that `Primitive.mandatory_only?`
should be used only in a condition of an `if` statement and the
`if` statement should be equal to the methdo body (you can not
put any expression before and after the `if` statement).
A method entry with `mandatory_only?` (`Time.at` on the above case)
is marked as `iseq_overload`. When the method will be dispatch only
with mandatory arguments (`Time.at(0)` for example), make another
method entry with ISeq (2) as mandatory only method entry and it
will be cached in an inline method cache.
The idea is similar discussed in https://bugs.ruby-lang.org/issues/16254
but it only checks mandatory parameters or more, because many cases
only mandatory parameters are given. If we find other cases (optional
or keyword parameters are used frequently and it hurts performance),
we can extend the feature.
2021-11-12 20:12:20 +03:00
|
|
|
VM_ASSERT(vm_cc_check_cme(cc, rb_callable_method_entry(klass, mid)));
|
2021-01-20 21:33:59 +03:00
|
|
|
RB_DEBUG_COUNTER_INC(gccct_hit);
|
|
|
|
|
|
|
|
return cc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
RB_DEBUG_COUNTER_INC(gccct_null);
|
|
|
|
}
|
|
|
|
|
|
|
|
RB_DEBUG_COUNTER_INC(gccct_miss);
|
2024-04-24 20:20:07 +03:00
|
|
|
return gccct_method_search_slowpath(vm, klass, index, ci);
|
2021-01-20 21:33:59 +03:00
|
|
|
}
|
|
|
|
|
2024-03-19 20:59:25 +03:00
|
|
|
/**
|
|
|
|
* @internal
|
2009-08-29 17:39:44 +04:00
|
|
|
* calls the specified method.
|
|
|
|
*
|
|
|
|
* This function is called by functions in rb_call* family.
|
2024-03-19 20:59:25 +03:00
|
|
|
* @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
|
2012-12-05 20:10:41 +04:00
|
|
|
* protected methods cannot be called
|
2009-08-29 17:39:44 +04:00
|
|
|
*
|
2024-03-19 20:59:25 +03:00
|
|
|
* @note `self` is used in order to controlling access to protected methods.
|
2009-08-29 17:39:44 +04:00
|
|
|
*/
|
2008-05-25 05:12:12 +04:00
|
|
|
static inline VALUE
|
2017-10-29 17:45:16 +03:00
|
|
|
rb_call0(rb_execution_context_t *ec,
|
|
|
|
VALUE recv, ID mid, int argc, const VALUE *argv,
|
2019-09-04 00:49:03 +03:00
|
|
|
call_type call_scope, VALUE self)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2018-11-22 10:53:07 +03:00
|
|
|
enum method_missing_reason call_status;
|
2019-09-04 00:49:03 +03:00
|
|
|
call_type scope = call_scope;
|
2019-09-30 02:41:00 +03:00
|
|
|
int kw_splat = RB_NO_KEYWORDS;
|
2019-09-04 00:49:03 +03:00
|
|
|
|
2021-02-08 04:35:51 +03:00
|
|
|
switch (scope) {
|
|
|
|
case CALL_PUBLIC_KW:
|
2019-09-04 00:49:03 +03:00
|
|
|
scope = CALL_PUBLIC;
|
|
|
|
kw_splat = 1;
|
2019-09-06 05:25:34 +03:00
|
|
|
break;
|
2021-02-08 04:35:51 +03:00
|
|
|
case CALL_FCALL_KW:
|
2019-09-06 05:25:34 +03:00
|
|
|
scope = CALL_FCALL;
|
|
|
|
kw_splat = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2019-09-04 00:49:03 +03:00
|
|
|
}
|
2018-11-22 10:53:07 +03:00
|
|
|
|
2024-04-24 20:20:07 +03:00
|
|
|
struct rb_callinfo ci;
|
|
|
|
scope_to_ci(scope, mid, argc, &ci);
|
|
|
|
|
|
|
|
const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci);
|
2021-01-20 21:33:59 +03:00
|
|
|
|
2018-11-22 10:53:07 +03:00
|
|
|
if (scope == CALL_PUBLIC) {
|
2020-12-15 06:10:35 +03:00
|
|
|
RB_DEBUG_COUNTER_INC(call0_public);
|
2021-01-20 21:33:59 +03:00
|
|
|
|
|
|
|
const rb_callable_method_entry_t *cc_cme = cc ? vm_cc_cme(cc) : NULL;
|
2023-09-20 02:34:50 +03:00
|
|
|
const rb_callable_method_entry_t *cme = callable_method_entry_refinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
|
2021-01-20 21:33:59 +03:00
|
|
|
call_status = rb_method_call_status(ec, cme, scope, self);
|
|
|
|
|
|
|
|
if (UNLIKELY(call_status != MISSING_NONE)) {
|
|
|
|
return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
|
|
|
|
}
|
|
|
|
else if (UNLIKELY(cc_cme != cme)) { // refinement is solved
|
|
|
|
stack_check(ec);
|
|
|
|
return rb_vm_call_kw(ec, recv, mid, argc, argv, cme, kw_splat);
|
|
|
|
}
|
2018-11-22 10:53:07 +03:00
|
|
|
}
|
|
|
|
else {
|
2020-12-15 06:10:35 +03:00
|
|
|
RB_DEBUG_COUNTER_INC(call0_other);
|
2021-01-20 21:33:59 +03:00
|
|
|
call_status = rb_method_call_status(ec, cc ? vm_cc_cme(cc) : NULL, scope, self);
|
2009-10-30 10:42:04 +03:00
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
if (UNLIKELY(call_status != MISSING_NONE)) {
|
|
|
|
return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
|
|
|
|
}
|
2009-10-30 10:42:04 +03:00
|
|
|
}
|
2021-01-20 21:33:59 +03:00
|
|
|
|
2017-10-28 15:23:51 +03:00
|
|
|
stack_check(ec);
|
2021-01-20 21:33:59 +03:00
|
|
|
return vm_call0_cc(ec, recv, mid, argc, argv, cc, kw_splat);
|
2009-10-30 10:42:04 +03:00
|
|
|
}
|
|
|
|
|
2009-10-31 18:32:22 +03:00
|
|
|
struct rescue_funcall_args {
|
2015-08-22 05:05:57 +03:00
|
|
|
VALUE defined_class;
|
2009-10-31 18:32:22 +03:00
|
|
|
VALUE recv;
|
2015-02-06 05:33:38 +03:00
|
|
|
ID mid;
|
2017-11-07 08:39:02 +03:00
|
|
|
rb_execution_context_t *ec;
|
2020-01-08 10:14:01 +03:00
|
|
|
const rb_callable_method_entry_t *cme;
|
2015-08-22 09:43:14 +03:00
|
|
|
unsigned int respond: 1;
|
|
|
|
unsigned int respond_to_missing: 1;
|
2009-10-31 18:32:22 +03:00
|
|
|
int argc;
|
2013-08-27 11:08:32 +04:00
|
|
|
const VALUE *argv;
|
2019-09-18 22:08:14 +03:00
|
|
|
int kw_splat;
|
2009-10-31 18:32:22 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
2019-08-26 08:51:00 +03:00
|
|
|
check_funcall_exec(VALUE v)
|
2009-10-31 18:32:22 +03:00
|
|
|
{
|
2019-08-26 08:51:00 +03:00
|
|
|
struct rescue_funcall_args *args = (void *)v;
|
2017-11-07 08:39:02 +03:00
|
|
|
return call_method_entry(args->ec, args->defined_class,
|
2015-08-22 05:05:57 +03:00
|
|
|
args->recv, idMethodMissing,
|
2020-01-08 10:14:01 +03:00
|
|
|
args->cme, args->argc, args->argv, args->kw_splat);
|
2009-10-31 18:32:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2019-08-26 08:51:00 +03:00
|
|
|
check_funcall_failed(VALUE v, VALUE e)
|
2009-10-31 18:32:22 +03:00
|
|
|
{
|
2019-08-26 08:51:00 +03:00
|
|
|
struct rescue_funcall_args *args = (void *)v;
|
2015-08-22 09:43:14 +03:00
|
|
|
int ret = args->respond;
|
|
|
|
if (!ret) {
|
2020-12-14 08:51:39 +03:00
|
|
|
switch (method_boundp(args->defined_class, args->mid,
|
|
|
|
BOUND_PRIVATE|BOUND_RESPONDS)) {
|
2015-08-22 09:43:14 +03:00
|
|
|
case 2:
|
|
|
|
ret = TRUE;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
ret = args->respond_to_missing;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret) {
|
2009-10-31 18:32:22 +03:00
|
|
|
rb_exc_raise(e);
|
2009-11-28 03:26:27 +03:00
|
|
|
}
|
2009-10-31 18:32:22 +03:00
|
|
|
return Qundef;
|
|
|
|
}
|
|
|
|
|
2012-12-23 10:05:50 +04:00
|
|
|
static int
|
2017-10-28 15:23:51 +03:00
|
|
|
check_funcall_respond_to(rb_execution_context_t *ec, VALUE klass, VALUE recv, ID mid)
|
2009-10-30 10:42:04 +03:00
|
|
|
{
|
2017-10-28 15:23:51 +03:00
|
|
|
return vm_respond_to(ec, klass, recv, mid, TRUE);
|
2012-12-23 10:05:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-10-28 15:23:51 +03:00
|
|
|
check_funcall_callable(rb_execution_context_t *ec, const rb_callable_method_entry_t *me)
|
2012-12-23 10:05:50 +04:00
|
|
|
{
|
2017-10-28 15:23:51 +03:00
|
|
|
return rb_method_call_status(ec, me, CALL_FCALL, ec->cfp->self) == MISSING_NONE;
|
2012-12-23 10:05:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2019-09-18 22:08:14 +03:00
|
|
|
check_funcall_missing(rb_execution_context_t *ec, VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int respond, VALUE def, int kw_splat)
|
2012-12-23 10:05:50 +04:00
|
|
|
{
|
2015-08-22 05:05:57 +03:00
|
|
|
struct rescue_funcall_args args;
|
2020-01-08 10:14:01 +03:00
|
|
|
const rb_callable_method_entry_t *cme;
|
2015-08-22 05:05:57 +03:00
|
|
|
VALUE ret = Qundef;
|
2015-08-22 09:43:14 +03:00
|
|
|
|
2017-10-28 15:23:51 +03:00
|
|
|
ret = basic_obj_respond_to_missing(ec, klass, recv,
|
2017-09-21 01:02:10 +03:00
|
|
|
ID2SYM(mid), Qtrue);
|
2017-06-01 11:02:41 +03:00
|
|
|
if (!RTEST(ret)) return def;
|
2015-08-22 09:43:14 +03:00
|
|
|
args.respond = respond > 0;
|
2022-11-15 07:24:08 +03:00
|
|
|
args.respond_to_missing = !UNDEF_P(ret);
|
2015-11-09 15:48:20 +03:00
|
|
|
ret = def;
|
2020-01-08 10:14:01 +03:00
|
|
|
cme = callable_method_entry(klass, idMethodMissing, &args.defined_class);
|
|
|
|
|
|
|
|
if (cme && !METHOD_ENTRY_BASIC(cme)) {
|
2015-08-22 05:05:57 +03:00
|
|
|
VALUE argbuf, *new_args = ALLOCV_N(VALUE, argbuf, argc+1);
|
|
|
|
|
|
|
|
new_args[0] = ID2SYM(mid);
|
2019-04-26 12:01:24 +03:00
|
|
|
#ifdef __GLIBC__
|
|
|
|
if (!argv) {
|
|
|
|
static const VALUE buf = Qfalse;
|
|
|
|
VM_ASSERT(argc == 0);
|
|
|
|
argv = &buf;
|
|
|
|
}
|
|
|
|
#endif
|
2015-08-22 05:05:57 +03:00
|
|
|
MEMCPY(new_args+1, argv, VALUE, argc);
|
2017-11-07 08:01:51 +03:00
|
|
|
ec->method_missing_reason = MISSING_NOENTRY;
|
2017-11-07 08:39:02 +03:00
|
|
|
args.ec = ec;
|
2012-12-23 10:05:50 +04:00
|
|
|
args.recv = recv;
|
2020-01-08 10:14:01 +03:00
|
|
|
args.cme = cme;
|
2015-02-06 05:33:38 +03:00
|
|
|
args.mid = mid;
|
2015-08-22 05:05:57 +03:00
|
|
|
args.argc = argc + 1;
|
|
|
|
args.argv = new_args;
|
2019-09-18 22:08:14 +03:00
|
|
|
args.kw_splat = kw_splat;
|
2015-08-22 05:05:57 +03:00
|
|
|
ret = rb_rescue2(check_funcall_exec, (VALUE)&args,
|
|
|
|
check_funcall_failed, (VALUE)&args,
|
|
|
|
rb_eNoMethodError, (VALUE)0);
|
|
|
|
ALLOCV_END(argbuf);
|
2012-12-23 10:05:50 +04:00
|
|
|
}
|
2015-08-22 05:05:57 +03:00
|
|
|
return ret;
|
2012-12-23 10:05:50 +04:00
|
|
|
}
|
|
|
|
|
2019-11-18 06:13:08 +03:00
|
|
|
static VALUE rb_check_funcall_default_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE def, int kw_splat);
|
|
|
|
|
2019-09-18 22:59:01 +03:00
|
|
|
VALUE
|
|
|
|
rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
|
|
|
return rb_check_funcall_default_kw(recv, mid, argc, argv, Qundef, kw_splat);
|
|
|
|
}
|
|
|
|
|
2012-12-23 10:05:50 +04:00
|
|
|
VALUE
|
2013-08-27 11:08:32 +04:00
|
|
|
rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv)
|
2015-11-09 15:48:20 +03:00
|
|
|
{
|
2019-09-18 22:59:01 +03:00
|
|
|
return rb_check_funcall_default_kw(recv, mid, argc, argv, Qundef, RB_NO_KEYWORDS);
|
2015-11-09 15:48:20 +03:00
|
|
|
}
|
|
|
|
|
2019-11-18 06:13:08 +03:00
|
|
|
static VALUE
|
2019-09-18 22:59:01 +03:00
|
|
|
rb_check_funcall_default_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE def, int kw_splat)
|
2012-12-23 10:05:50 +04:00
|
|
|
{
|
2021-07-16 06:22:17 +03:00
|
|
|
VM_ASSERT(ruby_thread_has_gvl_p());
|
|
|
|
|
2012-12-23 10:05:50 +04:00
|
|
|
VALUE klass = CLASS_OF(recv);
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
const rb_callable_method_entry_t *me;
|
2017-10-28 15:23:51 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
int respond = check_funcall_respond_to(ec, klass, recv, mid);
|
2012-12-23 10:05:50 +04:00
|
|
|
|
2015-08-22 09:43:14 +03:00
|
|
|
if (!respond)
|
2015-11-09 15:48:20 +03:00
|
|
|
return def;
|
2009-10-30 10:42:04 +03:00
|
|
|
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
me = rb_search_method_entry(recv, mid);
|
2017-10-28 15:23:51 +03:00
|
|
|
if (!check_funcall_callable(ec, me)) {
|
2019-09-19 01:30:59 +03:00
|
|
|
VALUE ret = check_funcall_missing(ec, klass, recv, mid, argc, argv,
|
|
|
|
respond, def, kw_splat);
|
2022-11-15 07:24:08 +03:00
|
|
|
if (UNDEF_P(ret)) ret = def;
|
2017-06-01 11:02:41 +03:00
|
|
|
return ret;
|
2009-10-30 10:42:04 +03:00
|
|
|
}
|
2017-10-28 15:23:51 +03:00
|
|
|
stack_check(ec);
|
2019-09-19 01:30:59 +03:00
|
|
|
return rb_vm_call_kw(ec, recv, mid, argc, argv, me, kw_splat);
|
2019-09-18 22:59:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_check_funcall_default(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE def)
|
|
|
|
{
|
|
|
|
return rb_check_funcall_default_kw(recv, mid, argc, argv, def, RB_NO_KEYWORDS);
|
2009-10-30 10:42:04 +03:00
|
|
|
}
|
|
|
|
|
2009-10-31 18:32:22 +03:00
|
|
|
VALUE
|
2019-09-18 22:08:14 +03:00
|
|
|
rb_check_funcall_with_hook_kw(VALUE recv, ID mid, int argc, const VALUE *argv,
|
|
|
|
rb_check_funcall_hook *hook, VALUE arg, int kw_splat)
|
2009-10-31 18:32:22 +03:00
|
|
|
{
|
2012-12-23 10:05:50 +04:00
|
|
|
VALUE klass = CLASS_OF(recv);
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
const rb_callable_method_entry_t *me;
|
2017-10-28 15:23:51 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
int respond = check_funcall_respond_to(ec, klass, recv, mid);
|
2012-12-23 10:05:50 +04:00
|
|
|
|
2016-01-30 09:19:13 +03:00
|
|
|
if (!respond) {
|
|
|
|
(*hook)(FALSE, recv, mid, argc, argv, arg);
|
2012-12-23 10:05:50 +04:00
|
|
|
return Qundef;
|
2016-01-30 09:19:13 +03:00
|
|
|
}
|
2012-12-23 10:05:50 +04:00
|
|
|
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
me = rb_search_method_entry(recv, mid);
|
2017-10-28 15:23:51 +03:00
|
|
|
if (!check_funcall_callable(ec, me)) {
|
2019-09-19 01:30:59 +03:00
|
|
|
VALUE ret = check_funcall_missing(ec, klass, recv, mid, argc, argv,
|
|
|
|
respond, Qundef, kw_splat);
|
2022-11-15 07:24:08 +03:00
|
|
|
(*hook)(!UNDEF_P(ret), recv, mid, argc, argv, arg);
|
2015-12-12 12:51:30 +03:00
|
|
|
return ret;
|
2012-12-23 10:05:50 +04:00
|
|
|
}
|
2017-10-28 15:23:51 +03:00
|
|
|
stack_check(ec);
|
2012-12-23 10:05:50 +04:00
|
|
|
(*hook)(TRUE, recv, mid, argc, argv, arg);
|
2019-09-19 01:30:59 +03:00
|
|
|
return rb_vm_call_kw(ec, recv, mid, argc, argv, me, kw_splat);
|
2019-09-18 22:08:14 +03:00
|
|
|
}
|
|
|
|
|
iseq.c: dump type of branchiftype on disasm
This makes easier to debug scripts related to r59950.
* before
$ ./ruby --dump=insns -e '"#{a}"'
== disasm: #<ISeq:<main>@-e>============================================
0000 putobject "" ( 1)[Li]
0002 putself
0003 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0006 dup
0007 branchiftype 5, 15
0010 dup
0011 opt_send_without_block <callinfo!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0014 tostring
0015 concatstrings 2
0017 leave
* after
$ ./ruby --dump=insns -e '"#{a}"'
== disasm: #<ISeq:<main>@-e>============================================
0000 putobject "" ( 1)[Li]
0002 putself
0003 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0006 dup
0007 branchiftype T_STRING, 15
0010 dup
0011 opt_send_without_block <callinfo!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0014 tostring
0015 concatstrings 2
0017 leave
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61217 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-12-13 19:07:52 +03:00
|
|
|
const char *
|
2009-11-18 20:52:12 +03:00
|
|
|
rb_type_str(enum ruby_value_type type)
|
|
|
|
{
|
2018-01-18 10:59:03 +03:00
|
|
|
#define type_case(t) t: return #t
|
2009-11-18 20:52:12 +03:00
|
|
|
switch (type) {
|
2018-01-18 10:59:03 +03:00
|
|
|
case type_case(T_NONE);
|
|
|
|
case type_case(T_OBJECT);
|
|
|
|
case type_case(T_CLASS);
|
|
|
|
case type_case(T_MODULE);
|
|
|
|
case type_case(T_FLOAT);
|
|
|
|
case type_case(T_STRING);
|
|
|
|
case type_case(T_REGEXP);
|
|
|
|
case type_case(T_ARRAY);
|
|
|
|
case type_case(T_HASH);
|
|
|
|
case type_case(T_STRUCT);
|
|
|
|
case type_case(T_BIGNUM);
|
|
|
|
case type_case(T_FILE);
|
|
|
|
case type_case(T_DATA);
|
|
|
|
case type_case(T_MATCH);
|
|
|
|
case type_case(T_COMPLEX);
|
|
|
|
case type_case(T_RATIONAL);
|
|
|
|
case type_case(T_NIL);
|
|
|
|
case type_case(T_TRUE);
|
|
|
|
case type_case(T_FALSE);
|
|
|
|
case type_case(T_SYMBOL);
|
|
|
|
case type_case(T_FIXNUM);
|
|
|
|
case type_case(T_IMEMO);
|
|
|
|
case type_case(T_UNDEF);
|
|
|
|
case type_case(T_NODE);
|
|
|
|
case type_case(T_ICLASS);
|
|
|
|
case type_case(T_ZOMBIE);
|
2019-04-20 04:19:47 +03:00
|
|
|
case type_case(T_MOVED);
|
2017-10-24 11:13:13 +03:00
|
|
|
case T_MASK: break;
|
2009-11-18 20:52:12 +03:00
|
|
|
}
|
|
|
|
#undef type_case
|
2017-10-24 11:13:13 +03:00
|
|
|
return NULL;
|
2009-11-18 20:52:12 +03:00
|
|
|
}
|
|
|
|
|
2017-10-24 10:17:36 +03:00
|
|
|
static void
|
|
|
|
uncallable_object(VALUE recv, ID mid)
|
|
|
|
{
|
|
|
|
VALUE flags;
|
|
|
|
int type;
|
|
|
|
const char *typestr;
|
|
|
|
VALUE mname = rb_id2str(mid);
|
|
|
|
|
|
|
|
if (SPECIAL_CONST_P(recv)) {
|
|
|
|
rb_raise(rb_eNotImpError,
|
2024-01-19 10:03:38 +03:00
|
|
|
"method '%"PRIsVALUE"' called on unexpected immediate object (%p)",
|
2017-10-24 10:17:36 +03:00
|
|
|
mname, (void *)recv);
|
|
|
|
}
|
|
|
|
else if ((flags = RBASIC(recv)->flags) == 0) {
|
|
|
|
rb_raise(rb_eNotImpError,
|
2024-01-19 10:03:38 +03:00
|
|
|
"method '%"PRIsVALUE"' called on terminated object (%p)",
|
2017-10-24 10:17:36 +03:00
|
|
|
mname, (void *)recv);
|
|
|
|
}
|
|
|
|
else if (!(typestr = rb_type_str(type = BUILTIN_TYPE(recv)))) {
|
|
|
|
rb_raise(rb_eNotImpError,
|
2024-01-19 10:03:38 +03:00
|
|
|
"method '%"PRIsVALUE"' called on broken T_?""?""?(0x%02x) object"
|
2017-10-24 10:17:36 +03:00
|
|
|
" (%p flags=0x%"PRIxVALUE")",
|
|
|
|
mname, type, (void *)recv, flags);
|
|
|
|
}
|
|
|
|
else if (T_OBJECT <= type && type < T_NIL) {
|
|
|
|
rb_raise(rb_eNotImpError,
|
2024-01-19 10:03:38 +03:00
|
|
|
"method '%"PRIsVALUE"' called on hidden %s object"
|
2017-10-24 10:17:36 +03:00
|
|
|
" (%p flags=0x%"PRIxVALUE")",
|
|
|
|
mname, typestr, (void *)recv, flags);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eNotImpError,
|
2024-01-19 10:03:38 +03:00
|
|
|
"method '%"PRIsVALUE"' called on unexpected %s object"
|
2017-10-24 10:17:36 +03:00
|
|
|
" (%p flags=0x%"PRIxVALUE")",
|
|
|
|
mname, typestr, (void *)recv, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
static inline const rb_callable_method_entry_t *
|
|
|
|
rb_search_method_entry(VALUE recv, ID mid)
|
2009-10-30 10:42:04 +03:00
|
|
|
{
|
|
|
|
VALUE klass = CLASS_OF(recv);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-10-24 10:17:36 +03:00
|
|
|
if (!klass) uncallable_object(recv, mid);
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
return rb_callable_method_entry(klass, mid);
|
2009-10-30 10:42:04 +03:00
|
|
|
}
|
2009-07-15 18:59:41 +04:00
|
|
|
|
2015-06-03 13:42:18 +03:00
|
|
|
static inline enum method_missing_reason
|
2017-10-28 15:23:51 +03:00
|
|
|
rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self)
|
2009-10-30 10:42:04 +03:00
|
|
|
{
|
2021-01-29 06:54:43 +03:00
|
|
|
if (UNLIKELY(UNDEFINED_METHOD_ENTRY_P(me))) {
|
2020-06-22 04:59:19 +03:00
|
|
|
goto undefined;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2021-01-29 06:54:43 +03:00
|
|
|
else if (UNLIKELY(me->def->type == VM_METHOD_TYPE_REFINED)) {
|
|
|
|
me = rb_resolve_refined_method_callable(Qnil, me);
|
|
|
|
if (UNDEFINED_METHOD_ENTRY_P(me)) goto undefined;
|
2015-05-08 06:11:35 +03:00
|
|
|
}
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 14:24:50 +03:00
|
|
|
|
2021-01-29 06:54:43 +03:00
|
|
|
rb_method_visibility_t visi = METHOD_ENTRY_VISI(me);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2021-01-29 06:54:43 +03:00
|
|
|
/* receiver specified form for private method */
|
|
|
|
if (UNLIKELY(visi != METHOD_VISI_PUBLIC)) {
|
|
|
|
if (me->def->original_id == idMethodMissing) {
|
|
|
|
return MISSING_NONE;
|
|
|
|
}
|
|
|
|
else if (visi == METHOD_VISI_PRIVATE &&
|
|
|
|
scope == CALL_PUBLIC) {
|
|
|
|
return MISSING_PRIVATE;
|
|
|
|
}
|
|
|
|
/* self must be kind of a specified form for protected method */
|
|
|
|
else if (visi == METHOD_VISI_PROTECTED &&
|
|
|
|
scope == CALL_PUBLIC) {
|
2009-02-22 17:23:33 +03:00
|
|
|
|
2021-01-29 06:54:43 +03:00
|
|
|
VALUE defined_class = me->owner;
|
|
|
|
if (RB_TYPE_P(defined_class, T_ICLASS)) {
|
|
|
|
defined_class = RBASIC(defined_class)->klass;
|
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2022-11-15 07:24:08 +03:00
|
|
|
if (UNDEF_P(self) || !rb_obj_is_kind_of(self, defined_class)) {
|
2021-01-29 06:54:43 +03:00
|
|
|
return MISSING_PROTECTED;
|
|
|
|
}
|
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2015-06-03 04:39:16 +03:00
|
|
|
|
|
|
|
return MISSING_NONE;
|
2021-01-29 06:54:43 +03:00
|
|
|
|
2020-06-22 04:59:19 +03:00
|
|
|
undefined:
|
|
|
|
return scope == CALL_VCALL ? MISSING_VCALL : MISSING_NOENTRY;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2009-08-29 17:39:44 +04:00
|
|
|
|
2024-03-19 20:59:25 +03:00
|
|
|
/**
|
|
|
|
* @internal
|
2009-08-29 17:39:44 +04:00
|
|
|
* calls the specified method.
|
|
|
|
*
|
|
|
|
* This function is called by functions in rb_call* family.
|
2024-03-19 20:59:25 +03:00
|
|
|
* @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
|
2009-08-29 17:39:44 +04:00
|
|
|
*/
|
2008-05-25 05:12:12 +04:00
|
|
|
static inline VALUE
|
2009-07-15 18:59:41 +04:00
|
|
|
rb_call(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-10-29 17:45:16 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
return rb_call0(ec, recv, mid, argc, argv, scope, ec->cfp->self);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2017-10-29 17:52:05 +03:00
|
|
|
NORETURN(static void raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv,
|
2015-06-03 13:42:18 +03:00
|
|
|
VALUE obj, enum method_missing_reason call_status));
|
2009-02-22 04:43:59 +03:00
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* obj.method_missing(symbol [, *args] ) -> result
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
|
|
|
|
* <i>symbol</i> is the symbol for the method called, and <i>args</i>
|
|
|
|
* are any arguments that were passed to it. By default, the interpreter
|
|
|
|
* raises an error when this method is called. However, it is possible
|
|
|
|
* to override the method to provide more dynamic behavior.
|
|
|
|
* If it is decided that a particular method should not be handled, then
|
|
|
|
* <i>super</i> should be called, so that ancestors can pick up the
|
|
|
|
* missing method.
|
|
|
|
* The example below creates
|
|
|
|
* a class <code>Roman</code>, which responds to methods with names
|
|
|
|
* consisting of roman numerals, returning the corresponding integer
|
|
|
|
* values.
|
|
|
|
*
|
|
|
|
* class Roman
|
2011-03-07 11:44:45 +03:00
|
|
|
* def roman_to_int(str)
|
2008-05-25 05:12:12 +04:00
|
|
|
* # ...
|
|
|
|
* end
|
2019-08-16 16:56:30 +03:00
|
|
|
*
|
|
|
|
* def method_missing(symbol, *args)
|
|
|
|
* str = symbol.id2name
|
|
|
|
* begin
|
|
|
|
* roman_to_int(str)
|
|
|
|
* rescue
|
|
|
|
* super(symbol, *args)
|
|
|
|
* end
|
2008-05-25 05:12:12 +04:00
|
|
|
* end
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* r = Roman.new
|
|
|
|
* r.iv #=> 4
|
|
|
|
* r.xxiii #=> 23
|
|
|
|
* r.mm #=> 2000
|
2019-08-16 16:56:30 +03:00
|
|
|
* r.foo #=> NoMethodError
|
2008-05-25 05:12:12 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_method_missing(int argc, const VALUE *argv, VALUE obj)
|
2009-02-22 04:43:59 +03:00
|
|
|
{
|
2017-11-07 08:01:51 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
raise_method_missing(ec, argc, argv, obj, ec->method_missing_reason);
|
2018-07-24 08:38:07 +03:00
|
|
|
UNREACHABLE_RETURN(Qnil);
|
2009-02-22 04:43:59 +03:00
|
|
|
}
|
|
|
|
|
2023-03-07 08:34:31 +03:00
|
|
|
VALUE
|
2018-02-17 04:30:05 +03:00
|
|
|
rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
|
|
|
|
int argc, const VALUE *argv, int priv)
|
2011-10-06 11:29:33 +04:00
|
|
|
{
|
2018-04-12 06:48:48 +03:00
|
|
|
VALUE name = argv[0];
|
2011-10-06 11:29:33 +04:00
|
|
|
|
|
|
|
if (!format) {
|
2024-01-19 10:03:38 +03:00
|
|
|
format = rb_fstring_lit("undefined method '%1$s' for %3$s%4$s");
|
2011-10-06 11:29:33 +04:00
|
|
|
}
|
|
|
|
if (exc == rb_eNoMethodError) {
|
2018-04-12 06:48:48 +03:00
|
|
|
VALUE args = rb_ary_new4(argc - 1, argv + 1);
|
|
|
|
return rb_nomethod_err_new(format, obj, name, args, priv);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return rb_name_err_new(format, obj, name);
|
2011-10-06 11:29:33 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-22 04:43:59 +03:00
|
|
|
static void
|
2017-10-29 17:52:05 +03:00
|
|
|
raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE obj,
|
2015-06-03 13:42:18 +03:00
|
|
|
enum method_missing_reason last_call_status)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
|
|
|
VALUE exc = rb_eNoMethodError;
|
2016-01-13 11:05:07 +03:00
|
|
|
VALUE format = 0;
|
2009-02-22 04:43:59 +03:00
|
|
|
|
2015-09-03 04:07:52 +03:00
|
|
|
if (UNLIKELY(argc == 0)) {
|
2015-09-03 11:57:47 +03:00
|
|
|
rb_raise(rb_eArgError, "no method name given");
|
2015-09-03 04:07:52 +03:00
|
|
|
}
|
|
|
|
else if (UNLIKELY(!SYMBOL_P(argv[0]))) {
|
|
|
|
const VALUE e = rb_eArgError; /* TODO: TypeError? */
|
|
|
|
rb_raise(e, "method name must be a Symbol but %"PRIsVALUE" is given",
|
|
|
|
rb_obj_class(argv[0]));
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2017-10-29 17:52:05 +03:00
|
|
|
stack_check(ec);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2015-06-03 04:39:16 +03:00
|
|
|
if (last_call_status & MISSING_PRIVATE) {
|
2024-01-19 10:03:38 +03:00
|
|
|
format = rb_fstring_lit("private method '%1$s' called for %3$s%4$s");
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2015-06-03 04:39:16 +03:00
|
|
|
else if (last_call_status & MISSING_PROTECTED) {
|
2024-01-19 10:03:38 +03:00
|
|
|
format = rb_fstring_lit("protected method '%1$s' called for %3$s%4$s");
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2015-06-03 04:39:16 +03:00
|
|
|
else if (last_call_status & MISSING_VCALL) {
|
2024-01-19 10:03:38 +03:00
|
|
|
format = rb_fstring_lit("undefined local variable or method '%1$s' for %3$s%4$s");
|
2008-05-25 05:12:12 +04:00
|
|
|
exc = rb_eNameError;
|
|
|
|
}
|
2015-06-03 04:39:16 +03:00
|
|
|
else if (last_call_status & MISSING_SUPER) {
|
2024-01-19 10:03:38 +03:00
|
|
|
format = rb_fstring_lit("super: no superclass method '%1$s' for %3$s%4$s");
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-02-17 04:30:05 +03:00
|
|
|
exc = rb_make_no_method_exception(exc, format, obj, argc, argv,
|
|
|
|
last_call_status & (MISSING_FCALL|MISSING_VCALL));
|
2015-06-03 04:39:16 +03:00
|
|
|
if (!(last_call_status & MISSING_MISSING)) {
|
2014-01-09 14:12:59 +04:00
|
|
|
rb_vm_pop_cfunc_frame();
|
2009-02-22 04:43:59 +03:00
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_exc_raise(exc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-16 10:38:41 +03:00
|
|
|
static void
|
|
|
|
vm_raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv,
|
|
|
|
VALUE obj, int call_status)
|
|
|
|
{
|
|
|
|
vm_passed_block_handler_set(ec, VM_BLOCK_HANDLER_NONE);
|
|
|
|
raise_method_missing(ec, argc, argv, obj, call_status | MISSING_MISSING);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
static inline VALUE
|
2020-07-06 04:46:57 +03:00
|
|
|
method_missing(rb_execution_context_t *ec, VALUE obj, ID id, int argc, const VALUE *argv, enum method_missing_reason call_status, int kw_splat)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2016-03-02 10:28:50 +03:00
|
|
|
VALUE *nargv, result, work, klass;
|
2017-10-28 13:01:54 +03:00
|
|
|
VALUE block_handler = vm_passed_block_handler(ec);
|
2016-03-02 10:28:50 +03:00
|
|
|
const rb_callable_method_entry_t *me;
|
2008-12-15 08:15:26 +03:00
|
|
|
|
2017-11-07 08:01:51 +03:00
|
|
|
ec->method_missing_reason = call_status;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2008-12-15 08:15:26 +03:00
|
|
|
if (id == idMethodMissing) {
|
2020-06-22 05:00:31 +03:00
|
|
|
goto missing;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2014-01-01 20:40:11 +04:00
|
|
|
nargv = ALLOCV_N(VALUE, work, argc + 1);
|
2008-05-25 05:12:12 +04:00
|
|
|
nargv[0] = ID2SYM(id);
|
2019-04-26 12:01:24 +03:00
|
|
|
#ifdef __GLIBC__
|
|
|
|
if (!argv) {
|
|
|
|
static const VALUE buf = Qfalse;
|
|
|
|
VM_ASSERT(argc == 0);
|
|
|
|
argv = &buf;
|
|
|
|
}
|
|
|
|
#endif
|
2008-05-25 05:12:12 +04:00
|
|
|
MEMCPY(nargv + 1, argv, VALUE, argc);
|
2016-03-02 10:28:50 +03:00
|
|
|
++argc;
|
|
|
|
argv = nargv;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2016-03-02 10:28:50 +03:00
|
|
|
klass = CLASS_OF(obj);
|
|
|
|
if (!klass) goto missing;
|
|
|
|
me = rb_callable_method_entry(klass, idMethodMissing);
|
|
|
|
if (!me || METHOD_ENTRY_BASIC(me)) goto missing;
|
2017-10-28 13:01:54 +03:00
|
|
|
vm_passed_block_handler_set(ec, block_handler);
|
2019-09-19 01:30:59 +03:00
|
|
|
result = rb_vm_call_kw(ec, obj, idMethodMissing, argc, argv, me, kw_splat);
|
2014-01-01 20:40:11 +04:00
|
|
|
if (work) ALLOCV_END(work);
|
2008-12-15 08:15:26 +03:00
|
|
|
return result;
|
2020-06-22 05:00:31 +03:00
|
|
|
missing:
|
|
|
|
raise_method_missing(ec, argc, argv, obj, call_status | MISSING_MISSING);
|
2020-06-24 10:23:59 +03:00
|
|
|
UNREACHABLE_RETURN(Qundef);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
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();
|
2024-04-24 20:20:07 +03:00
|
|
|
|
|
|
|
struct rb_callinfo ci;
|
|
|
|
scope_to_ci(scope, mid, argc, &ci);
|
|
|
|
|
|
|
|
const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci);
|
2021-01-20 21:33:59 +03:00
|
|
|
VALUE self = ec->cfp->self;
|
|
|
|
|
|
|
|
if (LIKELY(cc) &&
|
|
|
|
LIKELY(rb_method_call_status(ec, vm_cc_cme(cc), scope, self) == MISSING_NONE)) {
|
|
|
|
// fastpath
|
|
|
|
return vm_call0_cc(ec, recv, mid, argc, argv, cc, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return rb_call0(ec, recv, mid, argc, argv, scope, self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef rb_funcallv
|
|
|
|
#undef rb_funcallv
|
|
|
|
#endif
|
|
|
|
VALUE
|
|
|
|
rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
|
|
|
|
{
|
2021-07-16 06:43:57 +03:00
|
|
|
VM_ASSERT(ruby_thread_has_gvl_p());
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_funcallv_scope(recv, mid, argc, argv, CALL_FCALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
2021-07-16 06:43:57 +03:00
|
|
|
VM_ASSERT(ruby_thread_has_gvl_p());
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_apply(VALUE recv, ID mid, VALUE args)
|
|
|
|
{
|
|
|
|
int argc;
|
2011-07-29 18:53:47 +04:00
|
|
|
VALUE *argv, ret;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2009-05-20 20:43:41 +04:00
|
|
|
argc = RARRAY_LENINT(args);
|
2011-07-29 18:53:47 +04:00
|
|
|
if (argc >= 0x100) {
|
|
|
|
args = rb_ary_subseq(args, 0, argc);
|
* include/ruby/ruby.h: constify RBasic::klass and add
RBASIC_CLASS(obj) macro which returns a class of `obj'.
This change is a part of RGENGC branch [ruby-trunk - Feature #8339].
* object.c: add new function rb_obj_reveal().
This function reveal interal (hidden) object by rb_obj_hide().
Note that do not change class before and after hiding.
Only permitted example is:
klass = RBASIC_CLASS(obj);
rb_obj_hide(obj);
....
rb_obj_reveal(obj, klass);
TODO: API design. rb_obj_reveal() should be replaced with others.
TODO: modify constified variables using cast may be harmful for
compiler's analysis and optimizaton.
Any idea to prohibt inserting RBasic::klass directly?
If rename RBasic::klass and force to use RBASIC_CLASS(obj),
then all codes such as `RBASIC(obj)->klass' will be
compilation error. Is it acceptable? (We have similar
experience at Ruby 1.9,
for example "RARRAY(ary)->ptr" to "RARRAY_PTR(ary)".
* internal.h: add some macros.
* RBASIC_CLEAR_CLASS(obj) clear RBasic::klass to make it internal
object.
* RBASIC_SET_CLASS(obj, cls) set RBasic::klass.
* RBASIC_SET_CLASS_RAW(obj, cls) same as RBASIC_SET_CLASS
without write barrier (planned).
* RCLASS_SET_SUPER(a, b) set super class of a.
* array.c, class.c, compile.c, encoding.c, enum.c, error.c, eval.c,
file.c, gc.c, hash.c, io.c, iseq.c, marshal.c, object.c,
parse.y, proc.c, process.c, random.c, ruby.c, sprintf.c,
string.c, thread.c, transcode.c, vm.c, vm_eval.c, win32/file.c:
Use above macros and functions to access RBasic::klass.
* ext/coverage/coverage.c, ext/readline/readline.c,
ext/socket/ancdata.c, ext/socket/init.c,
* ext/zlib/zlib.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40691 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2013-05-13 14:49:11 +04:00
|
|
|
RBASIC_CLEAR_CLASS(args);
|
2011-07-29 18:53:47 +04:00
|
|
|
OBJ_FREEZE(args);
|
2014-03-17 08:20:16 +04:00
|
|
|
ret = rb_call(recv, mid, argc, RARRAY_CONST_PTR(args), CALL_FCALL);
|
2011-07-29 18:53:47 +04:00
|
|
|
RB_GC_GUARD(args);
|
|
|
|
return ret;
|
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
argv = ALLOCA_N(VALUE, argc);
|
2023-07-13 16:45:35 +03:00
|
|
|
MEMCPY(argv, RARRAY_CONST_PTR(args), VALUE, argc);
|
2021-01-20 21:33:59 +03:00
|
|
|
|
|
|
|
return rb_funcallv(recv, mid, argc, argv);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2018-10-11 05:47:49 +03:00
|
|
|
#ifdef rb_funcall
|
2017-04-15 05:09:27 +03:00
|
|
|
#undef rb_funcall
|
2018-10-11 05:47:49 +03:00
|
|
|
#endif
|
2021-02-01 08:10:07 +03:00
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_funcall(VALUE recv, ID mid, int n, ...)
|
|
|
|
{
|
|
|
|
VALUE *argv;
|
|
|
|
va_list ar;
|
|
|
|
|
|
|
|
if (n > 0) {
|
|
|
|
long i;
|
|
|
|
|
2020-05-11 08:27:25 +03:00
|
|
|
va_start(ar, n);
|
2010-11-15 16:48:37 +03:00
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
argv = ALLOCA_N(VALUE, n);
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
argv[i] = va_arg(ar, VALUE);
|
|
|
|
}
|
|
|
|
va_end(ar);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
argv = 0;
|
|
|
|
}
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_funcallv(recv, mid, n, argv);
|
2019-09-06 05:25:34 +03:00
|
|
|
}
|
|
|
|
|
2024-03-19 20:59:25 +03:00
|
|
|
/**
|
2020-12-24 20:08:12 +03:00
|
|
|
* Calls a method only if it is the basic method of `ancestor`
|
|
|
|
* otherwise returns Qundef;
|
2024-03-19 20:59:25 +03:00
|
|
|
* @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
|
2020-12-24 20:08:12 +03:00
|
|
|
*/
|
|
|
|
VALUE
|
|
|
|
rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
|
|
|
const rb_callable_method_entry_t *cme;
|
|
|
|
rb_execution_context_t *ec;
|
|
|
|
VALUE klass = CLASS_OF(recv);
|
|
|
|
if (!klass) return Qundef; /* hidden object */
|
|
|
|
|
|
|
|
cme = rb_callable_method_entry(klass, mid);
|
|
|
|
if (cme && METHOD_ENTRY_BASIC(cme) && RBASIC_CLASS(cme->defined_class) == ancestor) {
|
|
|
|
ec = GET_EC();
|
|
|
|
return rb_vm_call0(ec, recv, mid, argc, argv, cme, kw_splat);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Qundef;
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2013-05-31 12:27:06 +04:00
|
|
|
rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_funcallv_scope(recv, mid, argc, argv, CALL_PUBLIC);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2019-09-30 03:47:17 +03:00
|
|
|
VALUE
|
|
|
|
rb_funcallv_public_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
2019-10-04 22:51:57 +03:00
|
|
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
2019-09-30 03:47:17 +03:00
|
|
|
}
|
|
|
|
|
2010-09-24 18:45:19 +04:00
|
|
|
VALUE
|
|
|
|
rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
|
|
|
|
{
|
2016-07-28 14:02:30 +03:00
|
|
|
PASS_PASSED_BLOCK_HANDLER();
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_funcallv_public(recv, mid, argc, argv);
|
2010-09-24 18:45:19 +04:00
|
|
|
}
|
|
|
|
|
2019-09-30 03:47:17 +03:00
|
|
|
VALUE
|
|
|
|
rb_funcall_passing_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
|
|
|
PASS_PASSED_BLOCK_HANDLER();
|
2019-10-04 22:51:57 +03:00
|
|
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
2019-09-30 03:47:17 +03:00
|
|
|
}
|
|
|
|
|
2013-06-17 16:47:26 +04:00
|
|
|
VALUE
|
2016-07-28 14:02:30 +03:00
|
|
|
rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval)
|
2013-06-17 16:47:26 +04:00
|
|
|
{
|
2016-07-28 14:02:30 +03:00
|
|
|
if (!NIL_P(passed_procval)) {
|
2017-10-28 13:01:54 +03:00
|
|
|
vm_passed_block_handler_set(GET_EC(), passed_procval);
|
2013-06-17 16:47:26 +04:00
|
|
|
}
|
|
|
|
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_funcallv_public(recv, mid, argc, argv);
|
2013-06-17 16:47:26 +04:00
|
|
|
}
|
|
|
|
|
2019-09-04 00:49:03 +03:00
|
|
|
VALUE
|
|
|
|
rb_funcall_with_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval, int kw_splat)
|
|
|
|
{
|
|
|
|
if (!NIL_P(passed_procval)) {
|
|
|
|
vm_passed_block_handler_set(GET_EC(), passed_procval);
|
|
|
|
}
|
|
|
|
|
2019-10-04 22:51:57 +03:00
|
|
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
2019-09-04 00:49:03 +03:00
|
|
|
}
|
|
|
|
|
2015-02-05 07:41:05 +03:00
|
|
|
static VALUE *
|
2017-10-29 18:31:47 +03:00
|
|
|
current_vm_stack_arg(const rb_execution_context_t *ec, const VALUE *argv)
|
2015-02-05 07:41:05 +03:00
|
|
|
{
|
2017-10-29 18:31:47 +03:00
|
|
|
rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
|
|
|
|
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, prev_cfp)) return NULL;
|
2015-02-05 07:41:05 +03:00
|
|
|
if (prev_cfp->sp + 1 != argv) return NULL;
|
|
|
|
return prev_cfp->sp + 1;
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
static VALUE
|
2009-10-30 10:57:21 +03:00
|
|
|
send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2011-10-06 11:29:33 +04:00
|
|
|
ID id;
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE vid;
|
2012-12-05 20:10:41 +04:00
|
|
|
VALUE self;
|
2015-02-05 07:41:05 +03:00
|
|
|
VALUE ret, vargv = 0;
|
2017-10-29 17:45:16 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
2019-09-13 19:31:13 +03:00
|
|
|
int public = scope == CALL_PUBLIC || scope == CALL_PUBLIC_KW;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2019-09-13 19:31:13 +03:00
|
|
|
if (public) {
|
2012-12-05 20:10:41 +04:00
|
|
|
self = Qundef;
|
|
|
|
}
|
|
|
|
else {
|
2017-10-29 17:45:16 +03:00
|
|
|
self = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp)->self;
|
2012-12-05 20:10:41 +04:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
if (argc == 0) {
|
|
|
|
rb_raise(rb_eArgError, "no method name given");
|
|
|
|
}
|
|
|
|
|
2015-02-04 22:10:03 +03:00
|
|
|
vid = *argv;
|
* vm.c, eval_intern.h (PASS_PASSED_BLOCK):
set a VM_FRAME_FLAG_PASSED flag to skip this frame when
searching ruby-level-cfp.
* eval.c, eval_intern.h, proc.c: fix to check cfp. if there is
no valid ruby-level-cfp, cause RuntimeError exception.
[ruby-dev:34128]
* vm_core.h, vm_evalbody.c, vm.c, vm_dump.c, vm_insnhelper.c,
insns.def: rename FRAME_MAGIC_* to VM_FRAME_MAGIC_*.
* KNOWNBUGS.rb, bootstraptest/test*.rb: move solved bugs.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17084 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-06-11 01:46:43 +04:00
|
|
|
|
2011-10-06 11:29:33 +04:00
|
|
|
id = rb_check_id(&vid);
|
|
|
|
if (!id) {
|
|
|
|
if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) {
|
2018-02-17 04:30:05 +03:00
|
|
|
VALUE exc = rb_make_no_method_exception(rb_eNoMethodError, 0,
|
|
|
|
recv, argc, argv,
|
2019-09-13 19:31:13 +03:00
|
|
|
!public);
|
2011-10-06 11:29:33 +04:00
|
|
|
rb_exc_raise(exc);
|
|
|
|
}
|
2015-02-05 07:41:05 +03:00
|
|
|
if (!SYMBOL_P(*argv)) {
|
2017-10-29 18:31:47 +03:00
|
|
|
VALUE *tmp_argv = current_vm_stack_arg(ec, argv);
|
2015-02-05 07:41:05 +03:00
|
|
|
vid = rb_str_intern(vid);
|
|
|
|
if (tmp_argv) {
|
|
|
|
tmp_argv[0] = vid;
|
|
|
|
}
|
|
|
|
else if (argc > 1) {
|
|
|
|
tmp_argv = ALLOCV_N(VALUE, vargv, argc);
|
|
|
|
tmp_argv[0] = vid;
|
|
|
|
MEMCPY(tmp_argv+1, argv+1, VALUE, argc-1);
|
|
|
|
argv = tmp_argv;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
argv = &vid;
|
|
|
|
}
|
|
|
|
}
|
2015-02-04 22:10:03 +03:00
|
|
|
id = idMethodMissing;
|
2017-11-07 08:01:51 +03:00
|
|
|
ec->method_missing_reason = MISSING_NOENTRY;
|
2015-02-05 07:41:05 +03:00
|
|
|
}
|
|
|
|
else {
|
2015-02-04 22:10:03 +03:00
|
|
|
argv++; argc--;
|
2011-10-06 11:29:33 +04:00
|
|
|
}
|
2017-10-29 17:45:16 +03:00
|
|
|
PASS_PASSED_BLOCK_HANDLER_EC(ec);
|
|
|
|
ret = rb_call0(ec, recv, id, argc, argv, scope, self);
|
2015-02-05 07:41:05 +03:00
|
|
|
ALLOCV_END(vargv);
|
|
|
|
return ret;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2019-09-23 18:44:38 +03:00
|
|
|
static VALUE
|
|
|
|
send_internal_kw(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
|
|
|
{
|
2019-10-04 22:51:57 +03:00
|
|
|
if (rb_keyword_given_p()) {
|
2019-09-23 18:44:38 +03:00
|
|
|
switch (scope) {
|
|
|
|
case CALL_PUBLIC:
|
|
|
|
scope = CALL_PUBLIC_KW;
|
|
|
|
break;
|
|
|
|
case CALL_FCALL:
|
|
|
|
scope = CALL_FCALL_KW;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-10-04 22:51:57 +03:00
|
|
|
return send_internal(argc, argv, recv, scope);
|
2019-09-23 18:44:38 +03:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
/*
|
2013-02-24 08:24:52 +04:00
|
|
|
* call-seq:
|
|
|
|
* foo.send(symbol [, args...]) -> obj
|
|
|
|
* foo.__send__(symbol [, args...]) -> obj
|
|
|
|
* foo.send(string [, args...]) -> obj
|
|
|
|
* foo.__send__(string [, args...]) -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Invokes the method identified by _symbol_, passing it any
|
2020-11-05 14:51:17 +03:00
|
|
|
* arguments specified.
|
2013-02-24 08:24:52 +04:00
|
|
|
* When the method is identified by a string, the string is converted
|
|
|
|
* to a symbol.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2019-07-23 01:07:22 +03:00
|
|
|
* BasicObject implements +__send__+, Kernel implements +send+.
|
2020-11-05 14:51:17 +03:00
|
|
|
* <code>__send__</code> is safer than +send+
|
|
|
|
* when _obj_ has the same method name like <code>Socket</code>.
|
|
|
|
* See also <code>public_send</code>.
|
2019-07-23 01:07:22 +03:00
|
|
|
*
|
2008-05-25 05:12:12 +04:00
|
|
|
* class Klass
|
|
|
|
* def hello(*args)
|
|
|
|
* "Hello " + args.join(' ')
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
* k = Klass.new
|
|
|
|
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
|
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_f_send(int argc, VALUE *argv, VALUE recv)
|
|
|
|
{
|
2019-09-23 18:44:38 +03:00
|
|
|
return send_internal_kw(argc, argv, recv, CALL_FCALL);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* obj.public_send(symbol [, args...]) -> obj
|
2013-02-24 08:24:52 +04:00
|
|
|
* obj.public_send(string [, args...]) -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Invokes the method identified by _symbol_, passing it any
|
|
|
|
* arguments specified. Unlike send, public_send calls public
|
|
|
|
* methods only.
|
2013-02-24 08:24:52 +04:00
|
|
|
* When the method is identified by a string, the string is converted
|
|
|
|
* to a symbol.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* 1.public_send(:puts, "hello") # causes NoMethodError
|
|
|
|
*/
|
|
|
|
|
2017-11-16 10:47:33 +03:00
|
|
|
static VALUE
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_f_public_send(int argc, VALUE *argv, VALUE recv)
|
|
|
|
{
|
2019-09-23 18:44:38 +03:00
|
|
|
return send_internal_kw(argc, argv, recv, CALL_PUBLIC);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* yield */
|
|
|
|
|
2019-09-27 03:25:54 +03:00
|
|
|
static inline VALUE
|
|
|
|
rb_yield_0_kw(int argc, const VALUE * argv, int kw_splat)
|
|
|
|
{
|
2019-10-04 22:51:57 +03:00
|
|
|
return vm_yield(GET_EC(), argc, argv, kw_splat);
|
2019-09-27 03:25:54 +03:00
|
|
|
}
|
|
|
|
|
2017-08-16 00:39:38 +03:00
|
|
|
static inline VALUE
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_yield_0(int argc, const VALUE * argv)
|
|
|
|
{
|
2019-09-27 03:25:54 +03:00
|
|
|
return vm_yield(GET_EC(), argc, argv, RB_NO_KEYWORDS);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2015-10-11 00:22:54 +03:00
|
|
|
VALUE
|
|
|
|
rb_yield_1(VALUE val)
|
|
|
|
{
|
|
|
|
return rb_yield_0(1, &val);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_yield(VALUE val)
|
|
|
|
{
|
2022-11-15 07:24:08 +03:00
|
|
|
if (UNDEF_P(val)) {
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_yield_0(0, NULL);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
else {
|
2021-01-20 21:33:59 +03:00
|
|
|
return rb_yield_0(1, &val);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-14 11:33:08 +03:00
|
|
|
#undef rb_yield_values
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_yield_values(int n, ...)
|
|
|
|
{
|
|
|
|
if (n == 0) {
|
|
|
|
return rb_yield_0(0, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int i;
|
|
|
|
VALUE *argv;
|
|
|
|
va_list args;
|
|
|
|
argv = ALLOCA_N(VALUE, n);
|
|
|
|
|
2020-05-11 08:27:25 +03:00
|
|
|
va_start(args, n);
|
2008-05-25 05:12:12 +04:00
|
|
|
for (i=0; i<n; i++) {
|
|
|
|
argv[i] = va_arg(args, VALUE);
|
|
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return rb_yield_0(n, argv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_yield_values2(int argc, const VALUE *argv)
|
|
|
|
{
|
|
|
|
return rb_yield_0(argc, argv);
|
|
|
|
}
|
|
|
|
|
2019-09-27 03:25:54 +03:00
|
|
|
VALUE
|
|
|
|
rb_yield_values_kw(int argc, const VALUE *argv, int kw_splat)
|
|
|
|
{
|
|
|
|
return rb_yield_0_kw(argc, argv, kw_splat);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_yield_splat(VALUE values)
|
|
|
|
{
|
|
|
|
VALUE tmp = rb_check_array_type(values);
|
2016-06-18 04:50:46 +03:00
|
|
|
VALUE v;
|
2008-05-25 05:12:12 +04:00
|
|
|
if (NIL_P(tmp)) {
|
|
|
|
rb_raise(rb_eArgError, "not an array");
|
|
|
|
}
|
2013-11-08 06:37:47 +04:00
|
|
|
v = rb_yield_0(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp));
|
2014-11-14 03:26:26 +03:00
|
|
|
RB_GC_GUARD(tmp);
|
2008-05-25 05:12:12 +04:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2019-09-30 03:47:17 +03:00
|
|
|
VALUE
|
|
|
|
rb_yield_splat_kw(VALUE values, int kw_splat)
|
|
|
|
{
|
|
|
|
VALUE tmp = rb_check_array_type(values);
|
|
|
|
VALUE v;
|
|
|
|
if (NIL_P(tmp)) {
|
|
|
|
rb_raise(rb_eArgError, "not an array");
|
|
|
|
}
|
|
|
|
v = rb_yield_0_kw(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), kw_splat);
|
|
|
|
RB_GC_GUARD(tmp);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2017-03-19 04:11:12 +03:00
|
|
|
VALUE
|
2017-06-05 09:15:28 +03:00
|
|
|
rb_yield_force_blockarg(VALUE values)
|
2017-03-19 04:11:12 +03:00
|
|
|
{
|
2017-10-27 09:06:31 +03:00
|
|
|
return vm_yield_force_blockarg(GET_EC(), values);
|
2017-03-19 04:11:12 +03:00
|
|
|
}
|
|
|
|
|
2013-11-29 12:06:19 +04:00
|
|
|
VALUE
|
2019-09-27 03:25:54 +03:00
|
|
|
rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(val, arg))
|
2013-11-29 12:06:19 +04:00
|
|
|
{
|
2019-10-04 22:51:57 +03:00
|
|
|
return vm_yield_with_block(GET_EC(), argc, argv,
|
|
|
|
NIL_P(blockarg) ? VM_BLOCK_HANDLER_NONE : blockarg,
|
|
|
|
rb_keyword_given_p());
|
2013-11-29 12:06:19 +04:00
|
|
|
}
|
|
|
|
|
2010-02-17 11:05:42 +03:00
|
|
|
#if VMDEBUG
|
2010-01-24 16:52:32 +03:00
|
|
|
static const char *
|
|
|
|
vm_frametype_name(const rb_control_frame_t *cfp);
|
2010-02-17 11:05:42 +03:00
|
|
|
#endif
|
2010-01-24 16:52:32 +03:00
|
|
|
|
2015-03-28 08:22:12 +03:00
|
|
|
static VALUE
|
|
|
|
rb_iterate0(VALUE (* it_proc) (VALUE), VALUE data1,
|
|
|
|
const struct vm_ifunc *const ifunc,
|
2017-10-29 17:55:19 +03:00
|
|
|
rb_execution_context_t *ec)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2008-05-25 05:12:12 +04:00
|
|
|
volatile VALUE retval = Qnil;
|
2017-10-29 17:55:19 +03:00
|
|
|
rb_control_frame_t *const cfp = ec->cfp;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-10-29 17:55:19 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-10-26 14:02:13 +03:00
|
|
|
state = EC_EXEC_TAG();
|
2008-05-25 05:12:12 +04:00
|
|
|
if (state == 0) {
|
|
|
|
iter_retry:
|
2009-12-20 17:20:46 +03:00
|
|
|
{
|
2016-07-28 14:02:30 +03:00
|
|
|
VALUE block_handler;
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2015-03-28 08:22:12 +03:00
|
|
|
if (ifunc) {
|
2016-07-28 14:02:30 +03:00
|
|
|
struct rb_captured_block *captured = VM_CFP_TO_CAPTURED_BLOCK(cfp);
|
|
|
|
captured->code.ifunc = ifunc;
|
|
|
|
block_handler = VM_BH_FROM_IFUNC_BLOCK(captured);
|
2009-12-20 17:20:46 +03:00
|
|
|
}
|
|
|
|
else {
|
2016-07-28 14:02:30 +03:00
|
|
|
block_handler = VM_CF_BLOCK_HANDLER(cfp);
|
2009-12-20 17:20:46 +03:00
|
|
|
}
|
2017-10-29 17:55:19 +03:00
|
|
|
vm_passed_block_handler_set(ec, block_handler);
|
2009-12-20 17:20:46 +03:00
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
retval = (*it_proc) (data1);
|
|
|
|
}
|
2015-03-28 08:22:04 +03:00
|
|
|
else if (state == TAG_BREAK || state == TAG_RETRY) {
|
2017-10-29 17:55:19 +03:00
|
|
|
const struct vm_throw_data *const err = (struct vm_throw_data *)ec->errinfo;
|
2015-03-28 08:22:04 +03:00
|
|
|
const rb_control_frame_t *const escape_cfp = THROW_DATA_CATCH_FRAME(err);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2015-03-28 08:22:04 +03:00
|
|
|
if (cfp == escape_cfp) {
|
2017-10-29 17:55:19 +03:00
|
|
|
rb_vm_rewind_cfp(ec, cfp);
|
2010-01-24 16:52:32 +03:00
|
|
|
|
2015-03-28 08:22:04 +03:00
|
|
|
state = 0;
|
2017-10-29 17:55:19 +03:00
|
|
|
ec->tag->state = TAG_NONE;
|
|
|
|
ec->errinfo = Qnil;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2015-03-28 08:22:04 +03:00
|
|
|
if (state == TAG_RETRY) goto iter_retry;
|
|
|
|
retval = THROW_DATA_VAL(err);
|
|
|
|
}
|
|
|
|
else if (0) {
|
2018-01-02 09:41:40 +03:00
|
|
|
SDR(); fprintf(stderr, "%p, %p\n", (void *)cfp, (void *)escape_cfp);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2015-03-28 02:51:18 +03:00
|
|
|
if (state) {
|
2017-10-29 17:55:19 +03:00
|
|
|
EC_JUMP_TAG(ec, state);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2021-07-06 19:52:29 +03:00
|
|
|
static VALUE
|
|
|
|
rb_iterate_internal(VALUE (* it_proc)(VALUE), VALUE data1,
|
|
|
|
rb_block_call_func_t bl_proc, VALUE data2)
|
2015-03-28 08:22:12 +03:00
|
|
|
{
|
|
|
|
return rb_iterate0(it_proc, data1,
|
2017-07-18 11:31:02 +03:00
|
|
|
bl_proc ? rb_vm_ifunc_proc_new(bl_proc, (void *)data2) : 0,
|
2017-10-29 17:55:19 +03:00
|
|
|
GET_EC());
|
2015-03-28 08:22:12 +03:00
|
|
|
}
|
|
|
|
|
2021-07-06 19:52:29 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
struct iter_method_arg {
|
|
|
|
VALUE obj;
|
|
|
|
ID mid;
|
|
|
|
int argc;
|
2013-11-29 12:02:51 +04:00
|
|
|
const VALUE *argv;
|
2019-09-06 23:33:19 +03:00
|
|
|
int kw_splat;
|
2008-05-25 05:12:12 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
iterate_method(VALUE obj)
|
|
|
|
{
|
|
|
|
const struct iter_method_arg * arg =
|
|
|
|
(struct iter_method_arg *) obj;
|
|
|
|
|
2019-09-06 23:33:19 +03:00
|
|
|
return rb_call(arg->obj, arg->mid, arg->argc, arg->argv, arg->kw_splat ? CALL_FCALL_KW : CALL_FCALL);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2021-06-13 07:44:46 +03:00
|
|
|
VALUE rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv, rb_block_call_func_t bl_proc, VALUE data2, int kw_splat);
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2013-11-29 12:02:51 +04:00
|
|
|
rb_block_call(VALUE obj, ID mid, int argc, const VALUE * argv,
|
2019-08-27 08:11:02 +03:00
|
|
|
rb_block_call_func_t bl_proc, VALUE data2)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-06-13 07:44:46 +03:00
|
|
|
return rb_block_call_kw(obj, mid, argc, argv, bl_proc, data2, RB_NO_KEYWORDS);
|
2019-09-06 23:33:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv,
|
|
|
|
rb_block_call_func_t bl_proc, VALUE data2, int kw_splat)
|
|
|
|
{
|
|
|
|
struct iter_method_arg arg;
|
|
|
|
|
|
|
|
arg.obj = obj;
|
|
|
|
arg.mid = mid;
|
|
|
|
arg.argc = argc;
|
|
|
|
arg.argv = argv;
|
|
|
|
arg.kw_splat = kw_splat;
|
2021-07-06 19:52:29 +03:00
|
|
|
return rb_iterate_internal(iterate_method, (VALUE)&arg, bl_proc, data2);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2024-07-09 12:27:02 +03:00
|
|
|
/*
|
|
|
|
* 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());
|
|
|
|
}
|
|
|
|
|
2017-07-18 11:31:02 +03:00
|
|
|
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,
|
|
|
|
VALUE data2)
|
|
|
|
{
|
|
|
|
struct iter_method_arg arg;
|
|
|
|
struct vm_ifunc *block;
|
|
|
|
|
|
|
|
if (!bl_proc) rb_raise(rb_eArgError, "NULL lambda function");
|
|
|
|
arg.obj = obj;
|
|
|
|
arg.mid = mid;
|
|
|
|
arg.argc = argc;
|
|
|
|
arg.argv = argv;
|
2019-09-06 23:33:19 +03:00
|
|
|
arg.kw_splat = 0;
|
2017-07-18 11:31:02 +03:00
|
|
|
block = rb_vm_ifunc_new(bl_proc, (void *)data2, min_argc, max_argc);
|
2017-10-29 17:55:19 +03:00
|
|
|
return rb_iterate0(iterate_method, (VALUE)&arg, block, GET_EC());
|
2017-07-18 11:31:02 +03:00
|
|
|
}
|
|
|
|
|
2012-09-19 19:42:26 +04:00
|
|
|
static VALUE
|
|
|
|
iterate_check_method(VALUE obj)
|
|
|
|
{
|
|
|
|
const struct iter_method_arg * arg =
|
|
|
|
(struct iter_method_arg *) obj;
|
|
|
|
|
|
|
|
return rb_check_funcall(arg->obj, arg->mid, arg->argc, arg->argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2013-11-29 12:02:51 +04:00
|
|
|
rb_check_block_call(VALUE obj, ID mid, int argc, const VALUE *argv,
|
2019-08-27 08:11:02 +03:00
|
|
|
rb_block_call_func_t bl_proc, VALUE data2)
|
2012-09-19 19:42:26 +04:00
|
|
|
{
|
|
|
|
struct iter_method_arg arg;
|
|
|
|
|
|
|
|
arg.obj = obj;
|
|
|
|
arg.mid = mid;
|
|
|
|
arg.argc = argc;
|
|
|
|
arg.argv = argv;
|
2019-09-06 23:33:19 +03:00
|
|
|
arg.kw_splat = 0;
|
2021-07-06 19:52:29 +03:00
|
|
|
return rb_iterate_internal(iterate_check_method, (VALUE)&arg, bl_proc, data2);
|
2012-09-19 19:42:26 +04:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_each(VALUE obj)
|
|
|
|
{
|
2009-07-15 18:59:41 +04:00
|
|
|
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2023-07-13 12:49:28 +03:00
|
|
|
static VALUE eval_default_path = Qfalse;
|
|
|
|
|
2023-07-24 17:41:01 +03:00
|
|
|
#define EVAL_LOCATION_MARK "eval at "
|
|
|
|
#define EVAL_LOCATION_MARK_LEN (int)rb_strlen_lit(EVAL_LOCATION_MARK)
|
|
|
|
|
2023-07-13 12:49:28 +03:00
|
|
|
static VALUE
|
|
|
|
get_eval_default_path(void)
|
|
|
|
{
|
|
|
|
int location_lineno;
|
|
|
|
VALUE location_path = rb_source_location(&location_lineno);
|
|
|
|
if (!NIL_P(location_path)) {
|
2023-07-24 17:41:01 +03:00
|
|
|
return rb_fstring(rb_sprintf("("EVAL_LOCATION_MARK"%"PRIsVALUE":%d)",
|
|
|
|
location_path, location_lineno));
|
2023-07-13 12:49:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!eval_default_path) {
|
|
|
|
eval_default_path = rb_fstring_lit("(eval)");
|
2024-03-03 12:46:46 +03:00
|
|
|
rb_vm_register_global_object(eval_default_path);
|
2023-07-13 12:49:28 +03:00
|
|
|
}
|
|
|
|
return eval_default_path;
|
|
|
|
}
|
2021-05-21 21:01:06 +03:00
|
|
|
|
2024-01-31 23:56:08 +03:00
|
|
|
static const rb_iseq_t *
|
|
|
|
pm_eval_make_iseq(VALUE src, VALUE fname, int line,
|
|
|
|
const struct rb_block *base_block)
|
|
|
|
{
|
|
|
|
const rb_iseq_t *const parent = vm_block_iseq(base_block);
|
|
|
|
const rb_iseq_t *iseq = parent;
|
|
|
|
VALUE name = rb_fstring_lit("<compiled>");
|
2024-05-20 18:29:50 +03:00
|
|
|
|
|
|
|
// Conditionally enable coverage depending on the current mode:
|
|
|
|
int coverage_enabled = ((rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0) ? 1 : 0;
|
|
|
|
|
2024-02-08 19:09:07 +03:00
|
|
|
if (!fname) {
|
|
|
|
fname = rb_source_location(&line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!UNDEF_P(fname)) {
|
|
|
|
if (!NIL_P(fname)) fname = rb_fstring(fname);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fname = get_eval_default_path();
|
2024-05-20 18:29:50 +03:00
|
|
|
coverage_enabled = 0;
|
2024-02-08 19:09:07 +03:00
|
|
|
}
|
2024-01-31 23:56:08 +03:00
|
|
|
|
|
|
|
pm_parse_result_t result = { 0 };
|
2024-02-14 22:17:32 +03:00
|
|
|
pm_options_line_set(&result.options, line);
|
2024-05-20 18:29:50 +03:00
|
|
|
result.node.coverage_enabled = coverage_enabled;
|
2024-01-31 23:56:08 +03:00
|
|
|
|
2024-02-01 18:00:50 +03:00
|
|
|
// 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) && (ISEQ_BODY(iseq)->type != ISEQ_TYPE_TOP));
|
|
|
|
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;
|
|
|
|
for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) {
|
|
|
|
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);
|
|
|
|
|
|
|
|
for (int local_index = 0; local_index < locals_count; local_index++) {
|
|
|
|
pm_string_t *scope_local = &options_scope->locals[local_index];
|
2024-03-13 22:47:40 +03:00
|
|
|
ID local = ISEQ_BODY(iseq)->local_table[local_index];
|
|
|
|
|
|
|
|
if (rb_is_local_id(local)) {
|
|
|
|
const char *name = rb_id2name(local);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
pm_string_constant_init(scope_local, name, strlen(name));
|
|
|
|
}
|
2024-02-01 18:00:50 +03:00
|
|
|
}
|
2024-01-31 23:56:08 +03:00
|
|
|
|
2024-02-01 18:00:50 +03:00
|
|
|
iseq = ISEQ_BODY(iseq)->parent_iseq;
|
2024-01-31 23:56:08 +03:00
|
|
|
}
|
2024-02-01 18:00:50 +03:00
|
|
|
|
|
|
|
// 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);
|
2024-09-03 18:22:08 +03:00
|
|
|
|
|
|
|
VALUE script_lines;
|
|
|
|
VALUE error = pm_parse_string(&result, src, fname, ruby_vm_keep_script_lines ? &script_lines : NULL);
|
2024-02-01 18:00:50 +03:00
|
|
|
|
|
|
|
// If the parse failed, clean up and raise.
|
|
|
|
if (error != Qnil) {
|
2024-01-31 23:56:08 +03:00
|
|
|
pm_parse_result_free(&result);
|
|
|
|
rb_exc_raise(error);
|
|
|
|
}
|
2024-02-14 22:17:32 +03:00
|
|
|
|
2024-02-01 18:00:50 +03:00
|
|
|
// 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));
|
2024-02-14 22:17:32 +03:00
|
|
|
RUBY_ASSERT(parent_scope != NULL);
|
2024-02-01 18:00:50 +03:00
|
|
|
|
|
|
|
pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1];
|
2024-05-20 18:29:50 +03:00
|
|
|
parent_scope->coverage_enabled = coverage_enabled;
|
2024-02-01 18:00:50 +03:00
|
|
|
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;
|
|
|
|
if (pm_string_length(scope_local) > 0) {
|
|
|
|
constant_id = pm_constant_pool_insert_constant(
|
|
|
|
&result.parser.constant_pool, pm_string_source(scope_local),
|
|
|
|
pm_string_length(scope_local));
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-21 23:27:11 +03:00
|
|
|
iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, 0);
|
2024-02-01 18:00:50 +03:00
|
|
|
|
|
|
|
pm_scope_node_t *prev = result.node.previous;
|
|
|
|
while (prev) {
|
|
|
|
pm_scope_node_t *next = prev->previous;
|
2024-09-23 20:58:47 +03:00
|
|
|
pm_constant_id_list_free(&prev->locals);
|
2024-09-23 20:59:42 +03:00
|
|
|
pm_scope_node_destroy(prev);
|
2024-02-01 18:00:50 +03:00
|
|
|
ruby_xfree(prev);
|
|
|
|
prev = next;
|
|
|
|
}
|
2024-04-04 21:33:10 +03:00
|
|
|
|
2024-02-01 18:00:50 +03:00
|
|
|
pm_parse_result_free(&result);
|
2024-04-04 21:33:10 +03:00
|
|
|
rb_exec_event_hook_script_compiled(GET_EC(), iseq, src);
|
2024-01-31 23:56:08 +03:00
|
|
|
|
|
|
|
return iseq;
|
|
|
|
}
|
|
|
|
|
2017-12-26 13:10:41 +03:00
|
|
|
static const rb_iseq_t *
|
2024-02-09 00:56:26 +03:00
|
|
|
eval_make_iseq(VALUE src, VALUE fname, int line,
|
2017-12-27 05:55:15 +03:00
|
|
|
const struct rb_block *base_block)
|
2017-12-26 13:10:41 +03:00
|
|
|
{
|
2024-01-31 23:56:08 +03:00
|
|
|
if (*rb_ruby_prism_ptr()) {
|
|
|
|
return pm_eval_make_iseq(src, fname, line, base_block);
|
|
|
|
}
|
2017-12-26 13:10:41 +03:00
|
|
|
const VALUE parser = rb_parser_new();
|
|
|
|
const rb_iseq_t *const parent = vm_block_iseq(base_block);
|
2020-01-06 05:36:51 +03:00
|
|
|
rb_iseq_t *iseq = NULL;
|
2024-05-03 02:57:55 +03:00
|
|
|
VALUE ast_value;
|
2017-12-26 13:10:41 +03:00
|
|
|
rb_ast_t *ast;
|
2020-10-23 07:27:21 +03:00
|
|
|
int isolated_depth = 0;
|
2022-10-07 14:33:40 +03:00
|
|
|
|
|
|
|
// Conditionally enable coverage depending on the current mode:
|
2023-06-17 04:21:37 +03:00
|
|
|
int coverage_enabled = (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0;
|
2022-09-17 11:19:57 +03:00
|
|
|
|
2020-10-23 07:27:21 +03:00
|
|
|
{
|
|
|
|
int depth = 1;
|
|
|
|
const VALUE *ep = vm_block_ep(base_block);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) {
|
|
|
|
isolated_depth = depth;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (VM_ENV_LOCAL_P(ep)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ep = VM_ENV_PREV_EP(ep);
|
|
|
|
depth++;
|
|
|
|
}
|
|
|
|
}
|
2017-12-26 13:10:41 +03:00
|
|
|
|
2017-12-27 05:55:15 +03:00
|
|
|
if (!fname) {
|
|
|
|
fname = rb_source_location(&line);
|
|
|
|
}
|
|
|
|
|
2022-11-15 07:24:08 +03:00
|
|
|
if (!UNDEF_P(fname)) {
|
2019-08-04 04:08:17 +03:00
|
|
|
if (!NIL_P(fname)) fname = rb_fstring(fname);
|
2017-12-27 05:55:15 +03:00
|
|
|
}
|
|
|
|
else {
|
2023-07-13 12:49:28 +03:00
|
|
|
fname = get_eval_default_path();
|
2023-06-17 04:21:37 +03:00
|
|
|
coverage_enabled = FALSE;
|
2017-12-27 05:55:15 +03:00
|
|
|
}
|
|
|
|
|
2019-10-03 19:48:31 +03:00
|
|
|
rb_parser_set_context(parser, parent, FALSE);
|
2024-03-28 04:26:42 +03:00
|
|
|
if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser);
|
2024-05-03 02:57:55 +03:00
|
|
|
ast_value = rb_parser_compile_string_path(parser, fname, src, line);
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
|
2024-05-03 02:57:55 +03:00
|
|
|
ast = rb_ruby_ast_data_get(ast_value);
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
|
2018-01-05 11:59:20 +03:00
|
|
|
if (ast->body.root) {
|
2023-06-17 04:21:37 +03:00
|
|
|
ast->body.coverage_enabled = coverage_enabled;
|
2024-05-03 02:57:55 +03:00
|
|
|
iseq = rb_iseq_new_eval(ast_value,
|
2022-03-23 22:19:48 +03:00
|
|
|
ISEQ_BODY(parent)->location.label,
|
2022-09-25 10:45:28 +03:00
|
|
|
fname, Qnil, line,
|
2020-10-23 07:27:21 +03:00
|
|
|
parent, isolated_depth);
|
2017-12-26 13:10:41 +03:00
|
|
|
}
|
|
|
|
rb_ast_dispose(ast);
|
2017-12-27 05:55:15 +03:00
|
|
|
|
2020-01-06 05:36:51 +03:00
|
|
|
if (iseq != NULL) {
|
|
|
|
if (0 && iseq) { /* for debug */
|
|
|
|
VALUE disasm = rb_iseq_disasm(iseq);
|
|
|
|
printf("%s\n", StringValuePtr(disasm));
|
|
|
|
}
|
2017-12-27 05:55:15 +03:00
|
|
|
|
2020-01-06 05:36:51 +03:00
|
|
|
rb_exec_event_hook_script_compiled(GET_EC(), iseq, src);
|
|
|
|
}
|
2018-12-27 20:39:17 +03:00
|
|
|
|
2017-12-26 13:10:41 +03:00
|
|
|
return iseq;
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
static VALUE
|
2017-12-27 05:55:16 +03:00
|
|
|
eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int line)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-10-28 13:23:58 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
2016-07-28 14:02:30 +03:00
|
|
|
struct rb_block block;
|
2017-12-27 05:55:16 +03:00
|
|
|
const rb_iseq_t *iseq;
|
|
|
|
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
|
|
|
|
if (!cfp) {
|
|
|
|
rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread");
|
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
block.as.captured = *VM_CFP_TO_CAPTURED_BLOCK(cfp);
|
|
|
|
block.as.captured.self = self;
|
|
|
|
block.as.captured.code.iseq = cfp->iseq;
|
|
|
|
block.type = block_type_iseq;
|
2014-03-18 18:06:34 +04:00
|
|
|
|
2024-02-09 00:56:26 +03:00
|
|
|
iseq = eval_make_iseq(src, file, line, &block);
|
2017-12-27 05:55:16 +03:00
|
|
|
if (!iseq) {
|
|
|
|
rb_exc_raise(ec->errinfo);
|
|
|
|
}
|
2016-07-28 14:02:30 +03:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
/* TODO: what the code checking? */
|
|
|
|
if (!cref && block.as.captured.code.val) {
|
2019-04-05 11:15:21 +03:00
|
|
|
rb_cref_t *orig_cref = vm_get_cref(vm_block_ep(&block));
|
2017-12-27 05:55:16 +03:00
|
|
|
cref = vm_cref_dup(orig_cref);
|
|
|
|
}
|
|
|
|
vm_set_eval_stack(ec, iseq, cref, &block);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
/* kick */
|
2023-03-15 00:00:19 +03:00
|
|
|
return vm_exec(ec);
|
2017-12-27 05:55:16 +03:00
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
static VALUE
|
|
|
|
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);
|
2024-02-09 00:56:26 +03:00
|
|
|
const rb_iseq_t *iseq = eval_make_iseq(src, file, line, &bind->block);
|
2017-12-27 05:55:16 +03:00
|
|
|
if (!iseq) {
|
|
|
|
rb_exc_raise(ec->errinfo);
|
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
vm_set_eval_stack(ec, iseq, NULL, &bind->block);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
/* save new env */
|
2022-03-23 22:19:48 +03:00
|
|
|
if (ISEQ_BODY(iseq)->local_table_size > 0) {
|
2017-12-27 05:55:16 +03:00
|
|
|
vm_bind_update_env(scope, bind, vm_make_env_object(ec, ec->cfp));
|
2016-03-29 00:39:24 +03:00
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-12-26 12:05:28 +03:00
|
|
|
/* kick */
|
2023-03-15 00:00:19 +03:00
|
|
|
return vm_exec(ec);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* eval(string [, binding [, filename [,lineno]]]) -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Evaluates the Ruby expression(s) in <em>string</em>. If
|
2019-03-28 06:33:35 +03:00
|
|
|
* <em>binding</em> is given, which must be a Binding object, the
|
|
|
|
* evaluation is performed in its context. If the optional
|
|
|
|
* <em>filename</em> and <em>lineno</em> parameters are present, they
|
|
|
|
* will be used when reporting syntax errors.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2011-03-07 11:44:45 +03:00
|
|
|
* def get_binding(str)
|
2008-05-25 05:12:12 +04:00
|
|
|
* return binding
|
|
|
|
* end
|
|
|
|
* str = "hello"
|
|
|
|
* eval "str + ' Fred'" #=> "hello Fred"
|
2011-03-07 11:44:45 +03:00
|
|
|
* eval "str + ' Fred'", get_binding("bye") #=> "bye Fred"
|
2008-05-25 05:12:12 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
2014-06-18 10:16:39 +04:00
|
|
|
rb_f_eval(int argc, const VALUE *argv, VALUE self)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
|
|
|
VALUE src, scope, vfile, vline;
|
2013-07-29 12:00:34 +04:00
|
|
|
VALUE file = Qundef;
|
2008-05-25 05:12:12 +04:00
|
|
|
int line = 1;
|
|
|
|
|
|
|
|
rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline);
|
2024-01-09 22:04:20 +03:00
|
|
|
StringValue(src);
|
2008-05-25 05:12:12 +04:00
|
|
|
if (argc >= 3) {
|
|
|
|
StringValue(vfile);
|
|
|
|
}
|
|
|
|
if (argc >= 4) {
|
|
|
|
line = NUM2INT(vline);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!NIL_P(vfile))
|
2013-07-29 12:00:34 +04:00
|
|
|
file = vfile;
|
2017-12-27 06:00:30 +03:00
|
|
|
|
|
|
|
if (NIL_P(scope))
|
|
|
|
return eval_string_with_cref(self, src, NULL, file, line);
|
|
|
|
else
|
|
|
|
return eval_string_with_scope(scope, src, file, line);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2012-07-11 07:25:26 +04:00
|
|
|
/** @note This function name is not stable. */
|
|
|
|
VALUE
|
2012-09-04 06:52:44 +04:00
|
|
|
ruby_eval_string_from_file(const char *str, const char *filename)
|
|
|
|
{
|
2013-07-29 12:00:34 +04:00
|
|
|
VALUE file = filename ? rb_str_new_cstr(filename) : 0;
|
2022-07-29 10:02:10 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_control_frame_t *cfp = ec ? rb_vm_get_ruby_level_next_cfp(ec, ec->cfp) : NULL;
|
|
|
|
VALUE self = cfp ? cfp->self : rb_vm_top_self();
|
|
|
|
return eval_string_with_cref(self, rb_str_new2(str), NULL, file, 1);
|
2012-07-11 07:25:26 +04:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
|
|
|
rb_eval_string(const char *str)
|
|
|
|
{
|
2012-07-11 07:25:26 +04:00
|
|
|
return ruby_eval_string_from_file(str, "eval");
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2018-05-08 13:58:56 +03:00
|
|
|
static VALUE
|
|
|
|
eval_string_protect(VALUE str)
|
|
|
|
{
|
|
|
|
return rb_eval_string((char *)str);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2017-06-23 10:25:52 +03:00
|
|
|
rb_eval_string_protect(const char *str, int *pstate)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2018-05-08 13:58:56 +03:00
|
|
|
return rb_protect(eval_string_protect, (VALUE)str, pstate);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2019-07-23 05:56:02 +03:00
|
|
|
struct eval_string_wrap_arg {
|
|
|
|
VALUE top_self;
|
|
|
|
VALUE klass;
|
|
|
|
const char *str;
|
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
eval_string_wrap_protect(VALUE data)
|
|
|
|
{
|
|
|
|
const struct eval_string_wrap_arg *const arg = (struct eval_string_wrap_arg*)data;
|
|
|
|
rb_cref_t *cref = rb_vm_cref_new_toplevel();
|
2021-12-03 02:53:39 +03:00
|
|
|
cref->klass_or_self = arg->klass;
|
2019-07-23 05:56:02 +03:00
|
|
|
return eval_string_with_cref(arg->top_self, rb_str_new_cstr(arg->str), cref, rb_str_new_cstr("eval"), 1);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2017-06-23 10:25:52 +03:00
|
|
|
rb_eval_string_wrap(const char *str, int *pstate)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-06-23 10:25:52 +03:00
|
|
|
int state;
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_thread_t *th = GET_THREAD();
|
|
|
|
VALUE self = th->top_self;
|
|
|
|
VALUE wrapper = th->top_wrapper;
|
|
|
|
VALUE val;
|
2019-07-23 05:56:02 +03:00
|
|
|
struct eval_string_wrap_arg data;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
|
|
|
th->top_wrapper = rb_module_new();
|
|
|
|
th->top_self = rb_obj_clone(rb_vm_top_self());
|
|
|
|
rb_extend_object(th->top_self, th->top_wrapper);
|
|
|
|
|
2019-07-23 05:56:02 +03:00
|
|
|
data.top_self = th->top_self;
|
|
|
|
data.klass = th->top_wrapper;
|
|
|
|
data.str = str;
|
|
|
|
|
|
|
|
val = rb_protect(eval_string_wrap_protect, (VALUE)&data, &state);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
|
|
|
th->top_self = self;
|
|
|
|
th->top_wrapper = wrapper;
|
|
|
|
|
2017-06-23 10:25:52 +03:00
|
|
|
if (pstate) {
|
|
|
|
*pstate = state;
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2017-06-23 10:25:52 +03:00
|
|
|
else if (state != TAG_NONE) {
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_JUMP_TAG(th->ec, state);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2019-09-26 01:49:21 +03:00
|
|
|
rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2013-05-15 12:06:10 +04:00
|
|
|
volatile VALUE val = Qnil; /* OK */
|
2017-10-29 17:59:04 +03:00
|
|
|
rb_execution_context_t * volatile ec = GET_EC();
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-10-29 17:59:04 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-10-26 14:02:13 +03:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2016-06-18 04:50:49 +03:00
|
|
|
if (!RB_TYPE_P(cmd, T_STRING)) {
|
2019-09-26 01:49:21 +03:00
|
|
|
val = rb_funcallv_kw(cmd, idCall, RARRAY_LENINT(arg),
|
|
|
|
RARRAY_CONST_PTR(arg), kw_splat);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2016-06-18 04:50:49 +03:00
|
|
|
else {
|
2017-12-27 06:00:30 +03:00
|
|
|
val = eval_string_with_cref(rb_vm_top_self(), cmd, NULL, 0, 0);
|
2016-06-18 04:50:49 +03:00
|
|
|
}
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-10-29 17:59:04 +03:00
|
|
|
if (state) EC_JUMP_TAG(ec, state);
|
2008-05-25 05:12:12 +04:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* block eval under the class/module context */
|
|
|
|
|
|
|
|
static VALUE
|
2021-12-03 02:53:39 +03:00
|
|
|
yield_under(VALUE self, int singleton, int argc, const VALUE *argv, int kw_splat)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2017-10-27 09:06:31 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_control_frame_t *cfp = ec->cfp;
|
2016-07-28 14:02:30 +03:00
|
|
|
VALUE block_handler = VM_CF_BLOCK_HANDLER(cfp);
|
|
|
|
VALUE new_block_handler = 0;
|
|
|
|
const struct rb_captured_block *captured = NULL;
|
|
|
|
struct rb_captured_block new_captured;
|
|
|
|
const VALUE *ep = NULL;
|
2015-03-09 00:22:43 +03:00
|
|
|
rb_cref_t *cref;
|
2017-06-05 10:03:27 +03:00
|
|
|
int is_lambda = FALSE;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2016-07-28 14:02:30 +03:00
|
|
|
if (block_handler != VM_BLOCK_HANDLER_NONE) {
|
|
|
|
again:
|
|
|
|
switch (vm_block_handler_type(block_handler)) {
|
|
|
|
case block_handler_type_iseq:
|
|
|
|
captured = VM_BH_TO_CAPT_BLOCK(block_handler);
|
|
|
|
new_captured = *captured;
|
|
|
|
new_block_handler = VM_BH_FROM_ISEQ_BLOCK(&new_captured);
|
|
|
|
break;
|
|
|
|
case block_handler_type_ifunc:
|
|
|
|
captured = VM_BH_TO_CAPT_BLOCK(block_handler);
|
|
|
|
new_captured = *captured;
|
|
|
|
new_block_handler = VM_BH_FROM_IFUNC_BLOCK(&new_captured);
|
|
|
|
break;
|
|
|
|
case block_handler_type_proc:
|
2017-06-05 10:03:27 +03:00
|
|
|
is_lambda = rb_proc_lambda_p(block_handler) != Qfalse;
|
2016-07-28 14:02:30 +03:00
|
|
|
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
|
|
|
|
goto again;
|
|
|
|
case block_handler_type_symbol:
|
2019-10-04 22:51:57 +03:00
|
|
|
return rb_sym_proc_call(SYM2ID(VM_BH_TO_SYMBOL(block_handler)),
|
|
|
|
argc, argv, kw_splat,
|
|
|
|
VM_BLOCK_HANDLER_NONE);
|
2016-07-28 14:02:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
new_captured.self = self;
|
|
|
|
ep = captured->ep;
|
|
|
|
|
2017-10-27 09:06:31 +03:00
|
|
|
VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2021-12-03 02:53:39 +03:00
|
|
|
VM_ASSERT(singleton || RB_TYPE_P(self, T_MODULE) || RB_TYPE_P(self, T_CLASS));
|
|
|
|
cref = vm_cref_push(ec, self, ep, TRUE, singleton);
|
|
|
|
|
2019-10-04 22:51:57 +03:00
|
|
|
return vm_yield_with_cref(ec, argc, argv, kw_splat, cref, is_lambda);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2012-12-08 06:37:16 +04:00
|
|
|
VALUE
|
|
|
|
rb_yield_refine_block(VALUE refinement, VALUE refinements)
|
|
|
|
{
|
2017-10-27 09:06:31 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
VALUE block_handler = VM_CF_BLOCK_HANDLER(ec->cfp);
|
2012-12-08 06:37:16 +04:00
|
|
|
|
2016-07-28 14:02:30 +03:00
|
|
|
if (vm_block_handler_type(block_handler) != block_handler_type_iseq) {
|
|
|
|
rb_bug("rb_yield_refine_block: an iseq block is required");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
|
|
|
|
struct rb_captured_block new_captured = *captured;
|
2023-11-05 14:08:58 +03:00
|
|
|
const VALUE *const argv = &new_captured.self; /* dummy to suppress nonnull warning from gcc */
|
2016-07-28 14:02:30 +03:00
|
|
|
VALUE new_block_handler = VM_BH_FROM_ISEQ_BLOCK(&new_captured);
|
|
|
|
const VALUE *ep = captured->ep;
|
2021-12-03 02:53:39 +03:00
|
|
|
rb_cref_t *cref = vm_cref_push(ec, refinement, ep, TRUE, FALSE);
|
2016-07-28 14:02:30 +03:00
|
|
|
CREF_REFINEMENTS_SET(cref, refinements);
|
2017-10-27 09:06:31 +03:00
|
|
|
VM_FORCE_WRITE_SPECIAL_CONST(&VM_CF_LEP(ec->cfp)[VM_ENV_DATA_INDEX_SPECVAL], new_block_handler);
|
2016-07-28 14:02:30 +03:00
|
|
|
new_captured.self = refinement;
|
2023-11-05 14:08:58 +03:00
|
|
|
return vm_yield_with_cref(ec, 0, argv, RB_NO_KEYWORDS, cref, FALSE);
|
2012-12-08 06:37:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
/* string eval under the class/module context */
|
|
|
|
static VALUE
|
2021-12-03 02:53:39 +03:00
|
|
|
eval_under(VALUE self, int singleton, VALUE src, VALUE file, int line)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
rb_cref_t *cref = vm_cref_push(GET_EC(), self, NULL, FALSE, singleton);
|
2024-01-09 22:04:20 +03:00
|
|
|
StringValue(src);
|
2021-12-03 02:53:39 +03:00
|
|
|
|
2017-12-27 05:55:16 +03:00
|
|
|
return eval_string_with_cref(self, src, cref, file, line);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2021-12-03 02:53:39 +03:00
|
|
|
specific_eval(int argc, const VALUE *argv, VALUE self, int singleton, int kw_splat)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
|
|
|
if (rb_block_given_p()) {
|
2012-03-15 01:10:34 +04:00
|
|
|
rb_check_arity(argc, 0, 0);
|
2021-12-03 02:53:39 +03:00
|
|
|
return yield_under(self, singleton, 1, &self, kw_splat);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
else {
|
2023-07-13 12:49:28 +03:00
|
|
|
VALUE file = Qnil;
|
2008-05-25 05:12:12 +04:00
|
|
|
int line = 1;
|
2014-06-18 10:16:39 +04:00
|
|
|
VALUE code;
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2012-03-15 01:10:34 +04:00
|
|
|
rb_check_arity(argc, 1, 3);
|
2014-06-18 10:16:39 +04:00
|
|
|
code = argv[0];
|
2024-01-09 22:04:20 +03:00
|
|
|
StringValue(code);
|
2012-03-15 01:10:34 +04:00
|
|
|
if (argc > 2)
|
|
|
|
line = NUM2INT(argv[2]);
|
|
|
|
if (argc > 1) {
|
2013-07-29 12:00:34 +04:00
|
|
|
file = argv[1];
|
|
|
|
if (!NIL_P(file)) StringValue(file);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2023-07-13 12:49:28 +03:00
|
|
|
|
|
|
|
if (NIL_P(file)) {
|
|
|
|
file = get_eval_default_path();
|
|
|
|
}
|
|
|
|
|
2021-12-03 02:53:39 +03:00
|
|
|
return eval_under(self, singleton, code, file, line);
|
2015-04-23 05:35:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* obj.instance_eval(string [, filename [, lineno]] ) -> obj
|
2014-05-25 01:52:31 +04:00
|
|
|
* obj.instance_eval {|obj| block } -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Evaluates a string containing Ruby source code, or the given block,
|
|
|
|
* within the context of the receiver (_obj_). In order to set the
|
|
|
|
* context, the variable +self+ is set to _obj_ while
|
|
|
|
* the code is executing, giving the code access to _obj_'s
|
2014-05-25 01:52:31 +04:00
|
|
|
* instance variables and private methods.
|
|
|
|
*
|
|
|
|
* When <code>instance_eval</code> is given a block, _obj_ is also
|
|
|
|
* passed in as the block's only argument.
|
|
|
|
*
|
|
|
|
* When <code>instance_eval</code> is given a +String+, the optional
|
|
|
|
* second and third parameters supply a filename and starting line number
|
|
|
|
* that are used when reporting compilation errors.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* class KlassWithSecret
|
|
|
|
* def initialize
|
|
|
|
* @secret = 99
|
|
|
|
* end
|
2014-05-25 01:52:31 +04:00
|
|
|
* private
|
|
|
|
* def the_secret
|
|
|
|
* "Ssssh! The secret is #{@secret}."
|
|
|
|
* end
|
2008-05-25 05:12:12 +04:00
|
|
|
* end
|
|
|
|
* k = KlassWithSecret.new
|
2014-05-25 01:52:31 +04:00
|
|
|
* k.instance_eval { @secret } #=> 99
|
|
|
|
* k.instance_eval { the_secret } #=> "Ssssh! The secret is 99."
|
|
|
|
* k.instance_eval {|obj| obj == self } #=> true
|
2008-05-25 05:12:12 +04:00
|
|
|
*/
|
|
|
|
|
2019-09-29 23:15:04 +03:00
|
|
|
static VALUE
|
|
|
|
rb_obj_instance_eval_internal(int argc, const VALUE *argv, VALUE self)
|
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return specific_eval(argc, argv, self, TRUE, RB_PASS_CALLED_KEYWORDS);
|
2019-09-29 23:15:04 +03:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2014-06-18 10:16:39 +04:00
|
|
|
rb_obj_instance_eval(int argc, const VALUE *argv, VALUE self)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return specific_eval(argc, argv, self, TRUE, RB_NO_KEYWORDS);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* obj.instance_exec(arg...) {|var...| block } -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Executes the given block within the context of the receiver
|
|
|
|
* (_obj_). In order to set the context, the variable +self+ is set
|
|
|
|
* to _obj_ while the code is executing, giving the code access to
|
|
|
|
* _obj_'s instance variables. Arguments are passed as block parameters.
|
|
|
|
*
|
|
|
|
* class KlassWithSecret
|
|
|
|
* def initialize
|
|
|
|
* @secret = 99
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
* k = KlassWithSecret.new
|
|
|
|
* k.instance_exec(5) {|x| @secret+x } #=> 104
|
|
|
|
*/
|
|
|
|
|
2019-09-29 23:15:04 +03:00
|
|
|
static VALUE
|
|
|
|
rb_obj_instance_exec_internal(int argc, const VALUE *argv, VALUE self)
|
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return yield_under(self, TRUE, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
2019-09-29 23:15:04 +03:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2014-06-18 10:16:39 +04:00
|
|
|
rb_obj_instance_exec(int argc, const VALUE *argv, VALUE self)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return yield_under(self, TRUE, argc, argv, RB_NO_KEYWORDS);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* mod.class_eval(string [, filename [, lineno]]) -> obj
|
2016-02-20 08:44:52 +03:00
|
|
|
* mod.class_eval {|mod| block } -> obj
|
|
|
|
* mod.module_eval(string [, filename [, lineno]]) -> obj
|
|
|
|
* mod.module_eval {|mod| block } -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2012-03-16 09:28:55 +04:00
|
|
|
* Evaluates the string or block in the context of _mod_, except that when
|
2012-03-16 09:38:52 +04:00
|
|
|
* a block is given, constant/class variable lookup is not affected. This
|
|
|
|
* can be used to add methods to a class. <code>module_eval</code> returns
|
|
|
|
* the result of evaluating its argument. The optional _filename_ and
|
|
|
|
* _lineno_ parameters set the text for error messages.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* class Thing
|
|
|
|
* end
|
|
|
|
* a = %q{def hello() "Hello there!" end}
|
|
|
|
* Thing.module_eval(a)
|
|
|
|
* puts Thing.new.hello()
|
|
|
|
* Thing.module_eval("invalid code", "dummy", 123)
|
|
|
|
*
|
|
|
|
* <em>produces:</em>
|
|
|
|
*
|
|
|
|
* Hello there!
|
|
|
|
* dummy:123:in `module_eval': undefined local variable
|
|
|
|
* or method `code' for Thing:Class
|
|
|
|
*/
|
|
|
|
|
2019-09-29 23:15:04 +03:00
|
|
|
static VALUE
|
|
|
|
rb_mod_module_eval_internal(int argc, const VALUE *argv, VALUE mod)
|
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return specific_eval(argc, argv, mod, FALSE, RB_PASS_CALLED_KEYWORDS);
|
2019-09-29 23:15:04 +03:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2014-06-18 10:16:39 +04:00
|
|
|
rb_mod_module_eval(int argc, const VALUE *argv, VALUE mod)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return specific_eval(argc, argv, mod, FALSE, RB_NO_KEYWORDS);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* mod.module_exec(arg...) {|var...| block } -> obj
|
|
|
|
* mod.class_exec(arg...) {|var...| block } -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Evaluates the given block in the context of the class/module.
|
|
|
|
* The method defined in the block will belong to the receiver.
|
2013-06-01 18:17:14 +04:00
|
|
|
* Any arguments passed to the method will be passed to the block.
|
|
|
|
* This can be used if the block needs to access instance variables.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* class Thing
|
|
|
|
* end
|
|
|
|
* Thing.class_exec{
|
|
|
|
* def hello() "Hello there!" end
|
|
|
|
* }
|
|
|
|
* puts Thing.new.hello()
|
|
|
|
*
|
|
|
|
* <em>produces:</em>
|
|
|
|
*
|
|
|
|
* Hello there!
|
|
|
|
*/
|
|
|
|
|
2019-09-29 23:15:04 +03:00
|
|
|
static VALUE
|
|
|
|
rb_mod_module_exec_internal(int argc, const VALUE *argv, VALUE mod)
|
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return yield_under(mod, FALSE, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
2019-09-29 23:15:04 +03:00
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
VALUE
|
2014-06-18 10:16:39 +04:00
|
|
|
rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2021-12-03 02:53:39 +03:00
|
|
|
return yield_under(mod, FALSE, argc, argv, RB_NO_KEYWORDS);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2014-11-15 10:28:08 +03:00
|
|
|
/*
|
|
|
|
* Document-class: UncaughtThrowError
|
|
|
|
*
|
|
|
|
* Raised when +throw+ is called with a _tag_ which does not have
|
|
|
|
* corresponding +catch+ block.
|
|
|
|
*
|
|
|
|
* throw "foo", "bar"
|
|
|
|
*
|
|
|
|
* <em>raises the exception:</em>
|
|
|
|
*
|
|
|
|
* UncaughtThrowError: uncaught throw "foo"
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
uncaught_throw_init(int argc, const VALUE *argv, VALUE exc)
|
|
|
|
{
|
|
|
|
rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
|
|
|
|
rb_call_super(argc - 2, argv + 2);
|
2014-11-16 11:33:49 +03:00
|
|
|
rb_ivar_set(exc, id_tag, argv[0]);
|
|
|
|
rb_ivar_set(exc, id_value, argv[1]);
|
2014-11-15 10:28:08 +03:00
|
|
|
return exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* uncaught_throw.tag -> obj
|
|
|
|
*
|
|
|
|
* Return the tag object which was called for.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
uncaught_throw_tag(VALUE exc)
|
|
|
|
{
|
2014-11-16 11:33:49 +03:00
|
|
|
return rb_ivar_get(exc, id_tag);
|
2014-11-15 10:28:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* uncaught_throw.value -> obj
|
|
|
|
*
|
|
|
|
* Return the return value which was called for.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
uncaught_throw_value(VALUE exc)
|
|
|
|
{
|
2014-11-16 11:33:49 +03:00
|
|
|
return rb_ivar_get(exc, id_value);
|
2014-11-15 10:28:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* uncaught_throw.to_s -> string
|
|
|
|
*
|
|
|
|
* Returns formatted message with the inspected tag.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
uncaught_throw_to_s(VALUE exc)
|
|
|
|
{
|
2014-11-16 11:33:35 +03:00
|
|
|
VALUE mesg = rb_attr_get(exc, id_mesg);
|
2014-11-15 10:28:08 +03:00
|
|
|
VALUE tag = uncaught_throw_tag(exc);
|
|
|
|
return rb_str_format(1, &tag, mesg);
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2009-06-13 02:06:59 +04:00
|
|
|
* throw(tag [, obj])
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
|
|
|
* Transfers control to the end of the active +catch+ block
|
2014-11-15 10:28:08 +03:00
|
|
|
* waiting for _tag_. Raises +UncaughtThrowError+ if there
|
2009-06-13 02:06:59 +04:00
|
|
|
* is no +catch+ block for the _tag_. The optional second
|
2008-05-25 05:12:12 +04:00
|
|
|
* parameter supplies a return value for the +catch+ block,
|
|
|
|
* which otherwise defaults to +nil+. For examples, see
|
2019-03-28 06:33:35 +03:00
|
|
|
* Kernel::catch.
|
2008-05-25 05:12:12 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2019-08-28 12:19:11 +03:00
|
|
|
rb_f_throw(int argc, VALUE *argv, VALUE _)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
|
|
|
VALUE tag, value;
|
2009-03-13 12:10:07 +03:00
|
|
|
|
|
|
|
rb_scan_args(argc, argv, "11", &tag, &value);
|
|
|
|
rb_throw_obj(tag, value);
|
2018-07-24 08:38:07 +03:00
|
|
|
UNREACHABLE_RETURN(Qnil);
|
2009-03-13 12:10:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_throw_obj(VALUE tag, VALUE value)
|
|
|
|
{
|
2017-10-29 18:00:23 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
struct rb_vm_tag *tt = ec->tag;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
|
|
|
while (tt) {
|
|
|
|
if (tt->tag == tag) {
|
|
|
|
tt->retval = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tt = tt->prev;
|
|
|
|
}
|
|
|
|
if (!tt) {
|
2014-11-15 10:28:08 +03:00
|
|
|
VALUE desc[3];
|
|
|
|
desc[0] = tag;
|
|
|
|
desc[1] = value;
|
|
|
|
desc[2] = rb_str_new_cstr("uncaught throw %p");
|
|
|
|
rb_exc_raise(rb_class_new_instance(numberof(desc), desc, rb_eUncaughtThrow));
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2017-10-29 18:00:23 +03:00
|
|
|
ec->errinfo = (VALUE)THROW_DATA_NEW(tag, NULL, TAG_THROW);
|
|
|
|
EC_JUMP_TAG(ec, TAG_THROW);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_throw(const char *tag, VALUE val)
|
|
|
|
{
|
2015-11-30 07:44:41 +03:00
|
|
|
rb_throw_obj(rb_sym_intern_ascii_cstr(tag), val);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
|
|
|
|
2009-09-16 01:18:04 +04:00
|
|
|
static VALUE
|
2019-08-26 09:27:48 +03:00
|
|
|
catch_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, _))
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
{
|
2009-09-16 01:18:04 +04:00
|
|
|
return rb_yield_0(1, &tag);
|
|
|
|
}
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2014-07-28 01:35:47 +04:00
|
|
|
* catch([tag]) {|tag| block } -> obj
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2014-07-28 01:38:23 +04:00
|
|
|
* +catch+ executes its block. If +throw+ is not called, the block executes
|
|
|
|
* normally, and +catch+ returns the value of the last expression evaluated.
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2014-07-28 01:35:47 +04:00
|
|
|
* catch(1) { 123 } # => 123
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2016-03-12 03:50:29 +03:00
|
|
|
* If <code>throw(tag2, val)</code> is called, Ruby searches up its stack for
|
|
|
|
* a +catch+ block whose +tag+ has the same +object_id+ as _tag2_. When found,
|
|
|
|
* the block stops executing and returns _val_ (or +nil+ if no second argument
|
|
|
|
* was given to +throw+).
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2014-07-28 01:35:47 +04:00
|
|
|
* catch(1) { throw(1, 456) } # => 456
|
|
|
|
* catch(1) { throw(1) } # => nil
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2014-07-28 01:38:23 +04:00
|
|
|
* When +tag+ is passed as the first argument, +catch+ yields it as the
|
|
|
|
* parameter of the block.
|
2014-07-28 01:35:47 +04:00
|
|
|
*
|
|
|
|
* catch(1) {|x| x + 2 } # => 3
|
2008-05-25 05:12:12 +04:00
|
|
|
*
|
2014-07-28 01:38:23 +04:00
|
|
|
* When no +tag+ is given, +catch+ yields a new unique object (as from
|
|
|
|
* +Object.new+) as the block parameter. This object can then be used as the
|
|
|
|
* argument to +throw+, and will match the correct +catch+ block.
|
2009-06-13 02:06:59 +04:00
|
|
|
*
|
2014-07-28 01:35:47 +04:00
|
|
|
* catch do |obj_A|
|
|
|
|
* catch do |obj_B|
|
|
|
|
* throw(obj_B, 123)
|
|
|
|
* puts "This puts is not reached"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* puts "This puts is displayed"
|
|
|
|
* 456
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* # => 456
|
|
|
|
*
|
|
|
|
* catch do |obj_A|
|
|
|
|
* catch do |obj_B|
|
|
|
|
* throw(obj_A, 123)
|
|
|
|
* puts "This puts is still not reached"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* puts "Now this puts is also not reached"
|
|
|
|
* 456
|
|
|
|
* end
|
2009-06-13 02:06:59 +04:00
|
|
|
*
|
2014-07-28 01:35:47 +04:00
|
|
|
* # => 123
|
2008-05-25 05:12:12 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2018-12-06 10:49:24 +03:00
|
|
|
rb_f_catch(int argc, VALUE *argv, VALUE self)
|
2008-05-25 05:12:12 +04:00
|
|
|
{
|
2018-12-06 10:49:24 +03:00
|
|
|
VALUE tag = rb_check_arity(argc, 0, 1) ? argv[0] : rb_obj_alloc(rb_cObject);
|
2009-09-16 01:18:04 +04:00
|
|
|
return rb_catch_obj(tag, catch_i, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2019-08-26 09:27:48 +03:00
|
|
|
rb_catch(const char *tag, rb_block_call_func_t func, VALUE data)
|
2009-09-16 01:18:04 +04:00
|
|
|
{
|
2015-11-30 07:44:41 +03:00
|
|
|
VALUE vtag = tag ? rb_sym_intern_ascii_cstr(tag) : rb_obj_alloc(rb_cObject);
|
2009-09-16 01:18:04 +04:00
|
|
|
return rb_catch_obj(vtag, func, data);
|
|
|
|
}
|
|
|
|
|
2016-05-05 06:22:20 +03:00
|
|
|
static VALUE
|
|
|
|
vm_catch_protect(VALUE tag, rb_block_call_func *func, VALUE data,
|
2017-10-29 18:03:33 +03:00
|
|
|
enum ruby_tag_type *stateptr, rb_execution_context_t *volatile ec)
|
2009-09-16 01:18:04 +04:00
|
|
|
{
|
2017-06-23 10:25:52 +03:00
|
|
|
enum ruby_tag_type state;
|
2016-05-05 06:22:20 +03:00
|
|
|
VALUE val = Qnil; /* OK */
|
2017-10-29 18:03:33 +03:00
|
|
|
rb_control_frame_t *volatile saved_cfp = ec->cfp;
|
2009-09-16 01:18:04 +04:00
|
|
|
|
2017-10-29 18:03:33 +03:00
|
|
|
EC_PUSH_TAG(ec);
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2013-11-18 17:47:56 +04:00
|
|
|
_tag.tag = tag;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
2017-10-26 14:02:13 +03:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2009-09-16 01:18:04 +04:00
|
|
|
/* call with argc=1, argv = [tag], block = Qnil to insure compatibility */
|
2013-12-03 16:53:18 +04:00
|
|
|
val = (*func)(tag, data, 1, (const VALUE *)&tag, Qnil);
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|
2017-10-29 18:03:33 +03:00
|
|
|
else if (state == TAG_THROW && THROW_DATA_VAL((struct vm_throw_data *)ec->errinfo) == tag) {
|
|
|
|
rb_vm_rewind_cfp(ec, saved_cfp);
|
|
|
|
val = ec->tag->retval;
|
|
|
|
ec->errinfo = Qnil;
|
2008-05-25 05:12:12 +04:00
|
|
|
state = 0;
|
|
|
|
}
|
2017-10-26 14:02:13 +03:00
|
|
|
EC_POP_TAG();
|
2013-12-03 16:53:18 +04:00
|
|
|
if (stateptr)
|
|
|
|
*stateptr = state;
|
2008-05-25 05:12:12 +04:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2017-06-23 11:24:54 +03:00
|
|
|
VALUE
|
|
|
|
rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, enum ruby_tag_type *stateptr)
|
|
|
|
{
|
2017-10-29 18:03:33 +03:00
|
|
|
return vm_catch_protect(t, func, data, stateptr, GET_EC());
|
2017-06-23 11:24:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2019-08-26 09:27:48 +03:00
|
|
|
rb_catch_obj(VALUE t, rb_block_call_func_t func, VALUE data)
|
2017-06-23 11:24:54 +03:00
|
|
|
{
|
|
|
|
enum ruby_tag_type state;
|
2017-10-29 18:03:33 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
VALUE val = vm_catch_protect(t, (rb_block_call_func *)func, data, &state, ec);
|
|
|
|
if (state) EC_JUMP_TAG(ec, state);
|
2017-06-23 11:24:54 +03:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2014-07-01 21:57:37 +04:00
|
|
|
static void
|
|
|
|
local_var_list_init(struct local_var_list *vars)
|
|
|
|
{
|
2019-07-02 20:09:01 +03:00
|
|
|
vars->tbl = rb_ident_hash_new();
|
2014-07-01 21:57:37 +04:00
|
|
|
RBASIC_CLEAR_CLASS(vars->tbl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
local_var_list_finish(struct local_var_list *vars)
|
|
|
|
{
|
|
|
|
/* TODO: not to depend on the order of st_table */
|
|
|
|
VALUE ary = rb_hash_keys(vars->tbl);
|
|
|
|
rb_hash_clear(vars->tbl);
|
|
|
|
vars->tbl = 0;
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
2014-05-07 08:26:51 +04:00
|
|
|
static int
|
|
|
|
local_var_list_update(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
|
|
|
|
{
|
|
|
|
if (existing) return ST_STOP;
|
|
|
|
*value = (st_data_t)Qtrue; /* INT2FIX(arg) */
|
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2014-05-07 08:26:49 +04:00
|
|
|
static void
|
|
|
|
local_var_list_add(const struct local_var_list *vars, ID lid)
|
|
|
|
{
|
2015-11-11 11:38:27 +03:00
|
|
|
if (lid && rb_is_local_id(lid)) {
|
2018-10-31 01:11:51 +03:00
|
|
|
/* should skip temporary variable */
|
|
|
|
st_data_t idx = 0; /* tbl->num_entries */
|
|
|
|
rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
|
2014-05-07 08:26:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:13:44 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* local_variables -> array
|
2009-01-19 03:13:44 +03:00
|
|
|
*
|
|
|
|
* Returns the names of the current local variables.
|
|
|
|
*
|
|
|
|
* fred = 1
|
|
|
|
* for i in 1..10
|
|
|
|
* # ...
|
|
|
|
* end
|
2009-06-13 02:08:46 +04:00
|
|
|
* local_variables #=> [:fred, :i]
|
2009-01-19 03:13:44 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2019-08-28 12:19:11 +03:00
|
|
|
rb_f_local_variables(VALUE _)
|
2009-01-19 03:13:44 +03:00
|
|
|
{
|
2014-05-07 08:26:49 +04:00
|
|
|
struct local_var_list vars;
|
2017-10-27 03:46:11 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp));
|
2015-07-25 00:44:14 +03:00
|
|
|
unsigned int i;
|
2009-01-19 03:13:44 +03:00
|
|
|
|
2014-07-01 21:57:37 +04:00
|
|
|
local_var_list_init(&vars);
|
2009-01-19 03:13:44 +03:00
|
|
|
while (cfp) {
|
|
|
|
if (cfp->iseq) {
|
2022-03-23 22:19:48 +03:00
|
|
|
for (i = 0; i < ISEQ_BODY(cfp->iseq)->local_table_size; i++) {
|
|
|
|
local_var_list_add(&vars, ISEQ_BODY(cfp->iseq)->local_table[i]);
|
2009-01-19 03:13:44 +03:00
|
|
|
}
|
|
|
|
}
|
2016-07-28 14:02:30 +03:00
|
|
|
if (!VM_ENV_LOCAL_P(cfp->ep)) {
|
2009-01-19 03:13:44 +03:00
|
|
|
/* block */
|
2016-07-28 14:02:30 +03:00
|
|
|
const VALUE *ep = VM_CF_PREV_EP(cfp);
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2017-10-27 03:46:11 +03:00
|
|
|
if (vm_collect_local_variables_in_heap(ep, &vars)) {
|
2009-01-19 03:13:44 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
2012-06-11 07:14:59 +04:00
|
|
|
while (cfp->ep != ep) {
|
2009-01-19 03:13:44 +03:00
|
|
|
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-07-01 21:57:37 +04:00
|
|
|
return local_var_list_finish(&vars);
|
2009-01-19 03:13:44 +03:00
|
|
|
}
|
|
|
|
|
2009-01-19 06:03:09 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2010-05-18 01:07:33 +04:00
|
|
|
* block_given? -> true or false
|
2009-01-19 06:03:09 +03:00
|
|
|
*
|
|
|
|
* Returns <code>true</code> if <code>yield</code> would execute a
|
|
|
|
* block in the current context. The <code>iterator?</code> form
|
|
|
|
* is mildly deprecated.
|
|
|
|
*
|
|
|
|
* def try
|
|
|
|
* if block_given?
|
|
|
|
* yield
|
|
|
|
* else
|
|
|
|
* "no block"
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
* try #=> "no block"
|
|
|
|
* try { "hello" } #=> "hello"
|
|
|
|
* try do "hello" end #=> "hello"
|
|
|
|
*/
|
|
|
|
|
2017-11-16 10:49:43 +03:00
|
|
|
static VALUE
|
2019-08-28 12:19:11 +03:00
|
|
|
rb_f_block_given_p(VALUE _)
|
2009-01-19 06:03:09 +03:00
|
|
|
{
|
2017-10-27 03:46:11 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_control_frame_t *cfp = ec->cfp;
|
|
|
|
cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
|
2009-01-19 06:03:09 +03:00
|
|
|
|
2021-08-02 06:06:44 +03:00
|
|
|
return RBOOL(cfp != NULL && VM_CF_BLOCK_HANDLER(cfp) != VM_BLOCK_HANDLER_NONE);
|
2009-01-19 06:03:09 +03:00
|
|
|
}
|
|
|
|
|
2019-01-19 05:34:26 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iterator? -> true or false
|
|
|
|
*
|
|
|
|
* Deprecated. Use block_given? instead.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
rb_f_iterator_p(VALUE self)
|
|
|
|
{
|
|
|
|
rb_warn_deprecated("iterator?", "block_given?");
|
|
|
|
return rb_f_block_given_p(self);
|
|
|
|
}
|
|
|
|
|
2010-03-16 20:40:00 +03:00
|
|
|
VALUE
|
|
|
|
rb_current_realfilepath(void)
|
|
|
|
{
|
2017-10-27 03:46:11 +03:00
|
|
|
const rb_execution_context_t *ec = GET_EC();
|
|
|
|
rb_control_frame_t *cfp = ec->cfp;
|
|
|
|
cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
|
2021-05-21 21:01:06 +03:00
|
|
|
if (cfp != NULL) {
|
|
|
|
VALUE path = rb_iseq_realpath(cfp->iseq);
|
|
|
|
if (RTEST(path)) return path;
|
|
|
|
// eval context
|
|
|
|
path = rb_iseq_path(cfp->iseq);
|
|
|
|
if (path == eval_default_path) {
|
|
|
|
return Qnil;
|
|
|
|
}
|
2023-07-13 12:49:28 +03:00
|
|
|
|
|
|
|
// [Feature #19755] implicit eval location is "(eval at #{__FILE__}:#{__LINE__})"
|
2023-07-24 17:41:01 +03:00
|
|
|
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) {
|
2023-07-13 12:49:28 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
2021-05-21 21:01:06 +03:00
|
|
|
}
|
2023-07-13 12:49:28 +03:00
|
|
|
|
|
|
|
return path;
|
2021-05-21 21:01:06 +03:00
|
|
|
}
|
2010-03-16 20:40:00 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
void
|
|
|
|
Init_vm_eval(void)
|
|
|
|
{
|
2008-09-25 22:04:34 +04:00
|
|
|
rb_define_global_function("eval", rb_f_eval, -1);
|
2009-01-19 03:13:44 +03:00
|
|
|
rb_define_global_function("local_variables", rb_f_local_variables, 0);
|
2019-01-19 05:34:26 +03:00
|
|
|
rb_define_global_function("iterator?", rb_f_iterator_p, 0);
|
2009-01-19 06:03:09 +03:00
|
|
|
rb_define_global_function("block_given?", rb_f_block_given_p, 0);
|
2009-01-19 03:13:44 +03:00
|
|
|
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_define_global_function("catch", rb_f_catch, -1);
|
|
|
|
rb_define_global_function("throw", rb_f_throw, -1);
|
|
|
|
|
2019-09-29 23:15:04 +03:00
|
|
|
rb_define_method(rb_cBasicObject, "instance_eval", rb_obj_instance_eval_internal, -1);
|
|
|
|
rb_define_method(rb_cBasicObject, "instance_exec", rb_obj_instance_exec_internal, -1);
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_define_private_method(rb_cBasicObject, "method_missing", rb_method_missing, -1);
|
|
|
|
|
2009-07-15 18:59:41 +04:00
|
|
|
#if 1
|
2018-01-22 16:09:37 +03:00
|
|
|
rb_add_method(rb_cBasicObject, id__send__,
|
2015-06-03 04:39:16 +03:00
|
|
|
VM_METHOD_TYPE_OPTIMIZED, (void *)OPTIMIZED_METHOD_TYPE_SEND, METHOD_VISI_PUBLIC);
|
2018-01-22 16:09:37 +03:00
|
|
|
rb_add_method(rb_mKernel, idSend,
|
2015-06-03 04:39:16 +03:00
|
|
|
VM_METHOD_TYPE_OPTIMIZED, (void *)OPTIMIZED_METHOD_TYPE_SEND, METHOD_VISI_PUBLIC);
|
2009-07-15 18:59:41 +04:00
|
|
|
#else
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_define_method(rb_cBasicObject, "__send__", rb_f_send, -1);
|
|
|
|
rb_define_method(rb_mKernel, "send", rb_f_send, -1);
|
2009-07-15 18:59:41 +04:00
|
|
|
#endif
|
2008-05-25 05:12:12 +04:00
|
|
|
rb_define_method(rb_mKernel, "public_send", rb_f_public_send, -1);
|
|
|
|
|
2019-09-29 23:15:04 +03:00
|
|
|
rb_define_method(rb_cModule, "module_exec", rb_mod_module_exec_internal, -1);
|
|
|
|
rb_define_method(rb_cModule, "class_exec", rb_mod_module_exec_internal, -1);
|
|
|
|
rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval_internal, -1);
|
|
|
|
rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval_internal, -1);
|
2014-11-15 10:28:08 +03:00
|
|
|
|
|
|
|
rb_eUncaughtThrow = rb_define_class("UncaughtThrowError", rb_eArgError);
|
|
|
|
rb_define_method(rb_eUncaughtThrow, "initialize", uncaught_throw_init, -1);
|
|
|
|
rb_define_method(rb_eUncaughtThrow, "tag", uncaught_throw_tag, 0);
|
|
|
|
rb_define_method(rb_eUncaughtThrow, "value", uncaught_throw_value, 0);
|
|
|
|
rb_define_method(rb_eUncaughtThrow, "to_s", uncaught_throw_to_s, 0);
|
2014-11-16 11:33:49 +03:00
|
|
|
|
2015-10-22 12:58:01 +03:00
|
|
|
id_result = rb_intern_const("result");
|
2014-11-16 11:33:49 +03:00
|
|
|
id_tag = rb_intern_const("tag");
|
|
|
|
id_value = rb_intern_const("value");
|
2008-05-25 05:12:12 +04:00
|
|
|
}
|