summaryrefslogtreecommitdiff
path: root/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'proc.c')
-rw-r--r--proc.c1266
1 files changed, 831 insertions, 435 deletions
diff --git a/proc.c b/proc.c
index 200a592701..739de9d8ff 100644
--- a/proc.c
+++ b/proc.c
@@ -10,34 +10,31 @@
**********************************************************************/
#include "eval_intern.h"
-#include "gc.h"
#include "internal.h"
#include "internal/class.h"
#include "internal/error.h"
#include "internal/eval.h"
+#include "internal/gc.h"
+#include "internal/hash.h"
#include "internal/object.h"
#include "internal/proc.h"
#include "internal/symbol.h"
#include "method.h"
#include "iseq.h"
#include "vm_core.h"
+#include "ractor_core.h"
#include "yjit.h"
-#if !defined(__GNUC__) || __GNUC__ < 5 || defined(__MINGW32__)
-# define NO_CLOBBERED(v) (*(volatile VALUE *)&(v))
-#else
-# define NO_CLOBBERED(v) (v)
-#endif
-
-#define UPDATE_TYPED_REFERENCE(_type, _ref) *(_type*)&_ref = (_type)rb_gc_location((VALUE)_ref)
-#define UPDATE_REFERENCE(_ref) UPDATE_TYPED_REFERENCE(VALUE, _ref)
-
const rb_cref_t *rb_vm_cref_in_context(VALUE self, VALUE cbase);
struct METHOD {
const VALUE recv;
const VALUE klass;
+ /* needed for #super_method */
const VALUE iclass;
+ /* Different than me->owner only for ZSUPER methods.
+ This is error-prone but unavoidable unless ZSUPER methods are removed. */
+ const VALUE owner;
const rb_method_entry_t * const me;
/* for bound methods, `me' should be rb_callable_method_entry_t * */
};
@@ -52,87 +49,39 @@ static int method_arity(VALUE);
static int method_min_max_arity(VALUE, int *max);
static VALUE proc_binding(VALUE self);
-#define attached id__attached__
-
/* Proc */
#define IS_METHOD_PROC_IFUNC(ifunc) ((ifunc)->func == bmcall)
-/* :FIXME: The way procs are cloned has been historically different from the
- * way everything else are. @shyouhei is not sure for the intention though.
- */
-#undef CLONESETUP
-static inline void
-CLONESETUP(VALUE clone, VALUE obj)
-{
- RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(obj));
- RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(clone));
-
- const VALUE flags = RUBY_FL_PROMOTED0 | RUBY_FL_PROMOTED1 | RUBY_FL_FINALIZE;
- rb_obj_setup(clone, rb_singleton_class_clone(obj),
- RB_FL_TEST_RAW(obj, ~flags));
- rb_singleton_class_attached(RBASIC_CLASS(clone), clone);
- if (RB_FL_TEST(obj, RUBY_FL_EXIVAR)) rb_copy_generic_ivar(clone, obj);
-}
-
-static void
-block_mark(const struct rb_block *block)
-{
- switch (vm_block_type(block)) {
- case block_type_iseq:
- case block_type_ifunc:
- {
- const struct rb_captured_block *captured = &block->as.captured;
- RUBY_MARK_MOVABLE_UNLESS_NULL(captured->self);
- RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)captured->code.val);
- if (captured->ep && captured->ep[VM_ENV_DATA_INDEX_ENV] != Qundef /* cfunc_proc_t */) {
- rb_gc_mark(VM_ENV_ENVVAL(captured->ep));
- }
- }
- break;
- case block_type_symbol:
- RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.symbol);
- break;
- case block_type_proc:
- RUBY_MARK_MOVABLE_UNLESS_NULL(block->as.proc);
- break;
- }
-}
-
static void
-block_compact(struct rb_block *block)
+block_mark_and_move(struct rb_block *block)
{
switch (block->type) {
case block_type_iseq:
case block_type_ifunc:
{
struct rb_captured_block *captured = &block->as.captured;
- captured->self = rb_gc_location(captured->self);
- captured->code.val = rb_gc_location(captured->code.val);
+ rb_gc_mark_and_move(&captured->self);
+ rb_gc_mark_and_move(&captured->code.val);
+ if (captured->ep) {
+ rb_gc_mark_and_move((VALUE *)&captured->ep[VM_ENV_DATA_INDEX_ENV]);
+ }
}
break;
case block_type_symbol:
- block->as.symbol = rb_gc_location(block->as.symbol);
+ rb_gc_mark_and_move(&block->as.symbol);
break;
case block_type_proc:
- block->as.proc = rb_gc_location(block->as.proc);
+ rb_gc_mark_and_move(&block->as.proc);
break;
}
}
static void
-proc_compact(void *ptr)
-{
- rb_proc_t *proc = ptr;
- block_compact((struct rb_block *)&proc->block);
-}
-
-static void
-proc_mark(void *ptr)
+proc_mark_and_move(void *ptr)
{
rb_proc_t *proc = ptr;
- block_mark(&proc->block);
- RUBY_MARK_LEAVE("proc");
+ block_mark_and_move((struct rb_block *)&proc->block);
}
typedef struct {
@@ -149,17 +98,19 @@ proc_memsize(const void *ptr)
return sizeof(rb_proc_t);
}
-static const rb_data_type_t proc_data_type = {
+const rb_data_type_t ruby_proc_data_type = {
"proc",
{
- proc_mark,
+ proc_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
proc_memsize,
- proc_compact,
+ proc_mark_and_move,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};
+#define proc_data_type ruby_proc_data_type
+
VALUE
rb_proc_alloc(VALUE klass)
{
@@ -178,8 +129,15 @@ static VALUE
proc_clone(VALUE self)
{
VALUE procval = rb_proc_dup(self);
- CLONESETUP(procval, self);
- return procval;
+ return rb_obj_clone_setup(self, procval, Qnil);
+}
+
+/* :nodoc: */
+static VALUE
+proc_dup(VALUE self)
+{
+ VALUE procval = rb_proc_dup(self);
+ return rb_obj_dup_setup(self, procval);
}
/*
@@ -300,28 +258,17 @@ static void
binding_free(void *ptr)
{
RUBY_FREE_ENTER("binding");
- ruby_xfree(ptr);
+ SIZED_FREE((rb_binding_t *)ptr);
RUBY_FREE_LEAVE("binding");
}
static void
-binding_mark(void *ptr)
-{
- rb_binding_t *bind = ptr;
-
- RUBY_MARK_ENTER("binding");
- block_mark(&bind->block);
- rb_gc_mark_movable(bind->pathobj);
- RUBY_MARK_LEAVE("binding");
-}
-
-static void
-binding_compact(void *ptr)
+binding_mark_and_move(void *ptr)
{
rb_binding_t *bind = ptr;
- block_compact((struct rb_block *)&bind->block);
- UPDATE_REFERENCE(bind->pathobj);
+ block_mark_and_move((struct rb_block *)&bind->block);
+ rb_gc_mark_and_move((VALUE *)&bind->pathobj);
}
static size_t
@@ -333,10 +280,10 @@ binding_memsize(const void *ptr)
const rb_data_type_t ruby_binding_data_type = {
"binding",
{
- binding_mark,
+ binding_mark_and_move,
binding_free,
binding_memsize,
- binding_compact,
+ binding_mark_and_move,
},
0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -353,10 +300,8 @@ rb_binding_alloc(VALUE klass)
return obj;
}
-
-/* :nodoc: */
static VALUE
-binding_dup(VALUE self)
+binding_copy(VALUE self)
{
VALUE bindval = rb_binding_alloc(rb_cBinding);
rb_binding_t *src, *dst;
@@ -370,11 +315,16 @@ binding_dup(VALUE self)
/* :nodoc: */
static VALUE
+binding_dup(VALUE self)
+{
+ return rb_obj_dup_setup(self, binding_copy(self));
+}
+
+/* :nodoc: */
+static VALUE
binding_clone(VALUE self)
{
- VALUE bindval = binding_dup(self);
- CLONESETUP(bindval, self);
- return bindval;
+ return rb_obj_clone_setup(self, binding_copy(self), Qnil);
}
VALUE
@@ -388,16 +338,44 @@ rb_binding_new(void)
* call-seq:
* binding -> a_binding
*
- * Returns a +Binding+ object, describing the variable and
+ * Returns a Binding object, describing the variable and
* method bindings at the point of call. This object can be used when
- * calling +eval+ to execute the evaluated command in this
- * environment. See also the description of class +Binding+.
+ * calling Binding#eval to execute the evaluated command in this
+ * environment, or extracting its local variables.
*
- * def get_binding(param)
- * binding
+ * class User
+ * def initialize(name, position)
+ * @name = name
+ * @position = position
+ * end
+ *
+ * def get_binding
+ * binding
+ * end
* end
- * b = get_binding("hello")
- * eval("param", b) #=> "hello"
+ *
+ * user = User.new('Joan', 'manager')
+ * template = '{name: @name, position: @position}'
+ *
+ * # evaluate template in context of the object
+ * eval(template, user.get_binding)
+ * #=> {name: "Joan", position: "manager"}
+ *
+ * Binding#local_variable_get can be used to access the variables
+ * whose names are reserved Ruby keywords:
+ *
+ * # This is valid parameter declaration, but `if` parameter can't
+ * # be accessed by name, because it is a reserved word.
+ * def validate(field, validation, if: nil)
+ * condition = binding.local_variable_get('if')
+ * return unless condition
+ *
+ * # ...Some implementation ...
+ * end
+ *
+ * validate(:name, :empty?, if: false) # skips validation
+ * validate(:name, :empty?, if: true) # performs validation
+ *
*/
static VALUE
@@ -407,19 +385,31 @@ rb_f_binding(VALUE self)
}
/*
- * call-seq:
- * binding.eval(string [, filename [,lineno]]) -> obj
+ * call-seq:
+ * binding.eval(string, filename = default_filename, lineno = 1) -> obj
*
- * Evaluates the Ruby expression(s) in <em>string</em>, in the
- * <em>binding</em>'s context. If the optional <em>filename</em> and
- * <em>lineno</em> parameters are present, they will be used when
- * reporting syntax errors.
+ * Evaluates the Ruby expression(s) in +string+ in the context of
+ * +self+. Returns the result of the last expression:
*
- * def get_binding(param)
- * binding
- * end
+ * def get_binding(param) = binding
* b = get_binding("hello")
* b.eval("param") #=> "hello"
+ *
+ * If the optional +filename+ is given, it will be used as the
+ * filename of the evaluation (for <tt>__FILE__</tt> and errors).
+ * Otherwise, it will default to <tt>(eval at __FILE__:__LINE__)</tt>
+ * where <tt>__FILE__</tt> and <tt>__LINE__</tt> are the filename and
+ * line number of the caller, respectively:
+ *
+ * b.eval("puts __FILE__") # => "(eval at test.rb:4)"
+ * b.eval("puts __FILE__", "foobar.rb") # => "foobar.rb"
+ *
+ * If the optional +lineno+ is given, it will be used as the
+ * line number of the evaluation (for <tt>__LINE__</tt> and errors).
+ * Otherwise, it will default to 1:
+ *
+ * b.eval("puts __LINE__") # => 1
+ * b.eval("puts __LINE__", "foobar.rb", 10) # => 10
*/
static VALUE
@@ -433,7 +423,7 @@ bind_eval(int argc, VALUE *argv, VALUE bindval)
}
static const VALUE *
-get_local_variable_ptr(const rb_env_t **envp, ID lid)
+get_local_variable_ptr(const rb_env_t **envp, ID lid, bool search_outer)
{
const rb_env_t *env = *envp;
do {
@@ -443,11 +433,11 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid)
}
const rb_iseq_t *iseq = env->iseq;
- unsigned int i;
VM_ASSERT(rb_obj_is_iseq((VALUE)iseq));
- for (i=0; i<ISEQ_BODY(iseq)->local_table_size; i++) {
+ const unsigned int local_table_size = ISEQ_BODY(iseq)->local_table_size;
+ for (unsigned int i=0; i<local_table_size; i++) {
if (ISEQ_BODY(iseq)->local_table[i] == lid) {
if (ISEQ_BODY(iseq)->local_iseq == iseq &&
ISEQ_BODY(iseq)->param.flags.has_block &&
@@ -460,7 +450,9 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid)
}
*envp = env;
- return &env->env[i];
+ unsigned int last_lvar = env->env_size+VM_ENV_INDEX_LAST_LVAR
+ - 1 /* errinfo */;
+ return &env->env[last_lvar - (local_table_size - i)];
}
}
}
@@ -468,7 +460,7 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid)
*envp = NULL;
return NULL;
}
- } while ((env = rb_vm_env_prev_env(env)) != NULL);
+ } while (search_outer && (env = rb_vm_env_prev_env(env)) != NULL);
*envp = NULL;
return NULL;
@@ -487,13 +479,13 @@ check_local_id(VALUE bindval, volatile VALUE *pname)
if (lid) {
if (!rb_is_local_id(lid)) {
- rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
+ rb_name_err_raise("wrong local variable name '%1$s' for %2$s",
bindval, ID2SYM(lid));
}
}
else {
if (!rb_is_local_name(name)) {
- rb_name_err_raise("wrong local variable name `%1$s' for %2$s",
+ rb_name_err_raise("wrong local variable name '%1$s' for %2$s",
bindval, name);
}
return 0;
@@ -530,6 +522,18 @@ bind_local_variables(VALUE bindval)
return rb_vm_env_local_variables(env);
}
+int
+rb_numparam_id_p(ID id)
+{
+ return (tNUMPARAM_1 << ID_SCOPE_SHIFT) <= id && id < ((tNUMPARAM_1 + 9) << ID_SCOPE_SHIFT);
+}
+
+int
+rb_implicit_param_p(ID id)
+{
+ return id == idItImplicit || rb_numparam_id_p(id);
+}
+
/*
* call-seq:
* binding.local_variable_get(symbol) -> obj
@@ -556,17 +560,21 @@ bind_local_variable_get(VALUE bindval, VALUE sym)
const rb_env_t *env;
if (!lid) goto undefined;
+ if (rb_numparam_id_p(lid)) {
+ rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
+ bindval, ID2SYM(lid));
+ }
GetBindingPtr(bindval, bind);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
- if ((ptr = get_local_variable_ptr(&env, lid)) != NULL) {
+ if ((ptr = get_local_variable_ptr(&env, lid, TRUE)) != NULL) {
return *ptr;
}
sym = ID2SYM(lid);
undefined:
- rb_name_err_raise("local variable `%1$s' is not defined for %2$s",
+ rb_name_err_raise("local variable '%1$s' is not defined for %2$s",
bindval, sym);
UNREACHABLE_RETURN(Qundef);
}
@@ -605,10 +613,14 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
const rb_env_t *env;
if (!lid) lid = rb_intern_str(sym);
+ if (rb_numparam_id_p(lid)) {
+ rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
+ bindval, ID2SYM(lid));
+ }
GetBindingPtr(bindval, bind);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
- if ((ptr = get_local_variable_ptr(&env, lid)) == NULL) {
+ if ((ptr = get_local_variable_ptr(&env, lid, TRUE)) == NULL) {
/* not found. create new env */
ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
@@ -648,10 +660,140 @@ bind_local_variable_defined_p(VALUE bindval, VALUE sym)
const rb_env_t *env;
if (!lid) return Qfalse;
+ if (rb_numparam_id_p(lid)) {
+ rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
+ bindval, ID2SYM(lid));
+ }
+
+ GetBindingPtr(bindval, bind);
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+ return RBOOL(get_local_variable_ptr(&env, lid, TRUE));
+}
+
+/*
+ * call-seq:
+ * binding.implicit_parameters -> Array
+ *
+ * Returns the names of numbered parameters and "it" parameter
+ * that are defined in the binding.
+ *
+ * def foo
+ * [42].each do
+ * it
+ * binding.implicit_parameters #=> [:it]
+ * end
+ *
+ * { k: 42 }.each do
+ * _2
+ * binding.implicit_parameters #=> [:_1, :_2]
+ * end
+ * end
+ *
+ */
+static VALUE
+bind_implicit_parameters(VALUE bindval)
+{
+ const rb_binding_t *bind;
+ const rb_env_t *env;
+
+ GetBindingPtr(bindval, bind);
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+
+ if (get_local_variable_ptr(&env, idItImplicit, FALSE)) {
+ return rb_ary_new_from_args(1, ID2SYM(idIt));
+ }
+
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+ return rb_vm_env_numbered_parameters(env);
+}
+
+/*
+ * call-seq:
+ * binding.implicit_parameter_get(symbol) -> obj
+ *
+ * Returns the value of the numbered parameter or "it" parameter.
+ *
+ * def foo
+ * [42].each do
+ * it
+ * binding.implicit_parameter_get(:it) #=> 42
+ * end
+ *
+ * { k: 42 }.each do
+ * _2
+ * binding.implicit_parameter_get(:_1) #=> :k
+ * binding.implicit_parameter_get(:_2) #=> 42
+ * end
+ * end
+ *
+ */
+static VALUE
+bind_implicit_parameter_get(VALUE bindval, VALUE sym)
+{
+ ID lid = check_local_id(bindval, &sym);
+ const rb_binding_t *bind;
+ const VALUE *ptr;
+ const rb_env_t *env;
+
+ if (lid == idIt) lid = idItImplicit;
+
+ if (!lid || !rb_implicit_param_p(lid)) {
+ rb_name_err_raise("'%1$s' is not an implicit parameter",
+ bindval, sym);
+ }
+
+ GetBindingPtr(bindval, bind);
+
+ env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
+ if ((ptr = get_local_variable_ptr(&env, lid, FALSE)) != NULL) {
+ return *ptr;
+ }
+
+ if (lid == idItImplicit) lid = idIt;
+ rb_name_err_raise("implicit parameter '%1$s' is not defined for %2$s", bindval, ID2SYM(lid));
+ UNREACHABLE_RETURN(Qundef);
+}
+
+/*
+ * call-seq:
+ * binding.implicit_parameter_defined?(symbol) -> obj
+ *
+ * Returns +true+ if the numbered parameter or "it" parameter exists.
+ *
+ * def foo
+ * [42].each do
+ * it
+ * binding.implicit_parameter_defined?(:it) #=> true
+ * binding.implicit_parameter_defined?(:_1) #=> false
+ * end
+ *
+ * { k: 42 }.each do
+ * _2
+ * binding.implicit_parameter_defined?(:_1) #=> true
+ * binding.implicit_parameter_defined?(:_2) #=> true
+ * binding.implicit_parameter_defined?(:_3) #=> false
+ * binding.implicit_parameter_defined?(:it) #=> false
+ * end
+ * end
+ *
+ */
+static VALUE
+bind_implicit_parameter_defined_p(VALUE bindval, VALUE sym)
+{
+ ID lid = check_local_id(bindval, &sym);
+ const rb_binding_t *bind;
+ const rb_env_t *env;
+
+ if (lid == idIt) lid = idItImplicit;
+
+ if (!lid || !rb_implicit_param_p(lid)) {
+ rb_name_err_raise("'%1$s' is not an implicit parameter",
+ bindval, sym);
+ }
GetBindingPtr(bindval, bind);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
- return RBOOL(get_local_variable_ptr(&env, lid));
+ return RBOOL(get_local_variable_ptr(&env, lid, FALSE));
}
/*
@@ -709,6 +851,32 @@ cfunc_proc_new(VALUE klass, VALUE ifunc)
return procval;
}
+VALUE
+rb_func_proc_dup(VALUE src_obj)
+{
+ RUBY_ASSERT(rb_typeddata_is_instance_of(src_obj, &proc_data_type));
+
+ rb_proc_t *src_proc;
+ GetProcPtr(src_obj, src_proc);
+ RUBY_ASSERT(vm_block_type(&src_proc->block) == block_type_ifunc);
+
+ cfunc_proc_t *proc;
+ VALUE proc_obj = TypedData_Make_Struct(rb_obj_class(src_obj), cfunc_proc_t, &proc_data_type, proc);
+
+ memcpy(&proc->basic, src_proc, sizeof(rb_proc_t));
+ RB_OBJ_WRITTEN(proc_obj, Qundef, proc->basic.block.as.captured.self);
+ RB_OBJ_WRITTEN(proc_obj, Qundef, proc->basic.block.as.captured.code.val);
+
+ const VALUE *src_ep = src_proc->block.as.captured.ep;
+ VALUE *ep = *(VALUE **)&proc->basic.block.as.captured.ep = proc->env + VM_ENV_DATA_SIZE - 1;
+ ep[VM_ENV_DATA_INDEX_FLAGS] = src_ep[VM_ENV_DATA_INDEX_FLAGS];
+ ep[VM_ENV_DATA_INDEX_ME_CREF] = src_ep[VM_ENV_DATA_INDEX_ME_CREF];
+ ep[VM_ENV_DATA_INDEX_SPECVAL] = src_ep[VM_ENV_DATA_INDEX_SPECVAL];
+ RB_OBJ_WRITE(proc_obj, &ep[VM_ENV_DATA_INDEX_ENV], src_ep[VM_ENV_DATA_INDEX_ENV]);
+
+ return proc_obj;
+}
+
static VALUE
sym_proc_new(VALUE klass, VALUE sym)
{
@@ -725,11 +893,6 @@ sym_proc_new(VALUE klass, VALUE sym)
struct vm_ifunc *
rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int max_argc)
{
- union {
- struct vm_ifunc_argc argc;
- VALUE packed;
- } arity;
-
if (min_argc < UNLIMITED_ARGUMENTS ||
#if SIZEOF_INT * 2 > SIZEOF_VALUE
min_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) ||
@@ -746,20 +909,21 @@ rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int m
rb_raise(rb_eRangeError, "maximum argument number out of range: %d",
max_argc);
}
- arity.argc.min = min_argc;
- arity.argc.max = max_argc;
- VALUE ret = rb_imemo_new(imemo_ifunc, (VALUE)func, (VALUE)data, arity.packed, 0);
- return (struct vm_ifunc *)ret;
-}
+ rb_execution_context_t *ec = GET_EC();
-MJIT_FUNC_EXPORTED VALUE
-rb_func_proc_new(rb_block_call_func_t func, VALUE val)
-{
- struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(func, (void *)val);
- return cfunc_proc_new(rb_cProc, (VALUE)ifunc);
+ struct vm_ifunc *ifunc = IMEMO_NEW(struct vm_ifunc, imemo_ifunc, (VALUE)rb_vm_svar_lep(ec, ec->cfp));
+
+ rb_gc_register_pinning_obj((VALUE)ifunc);
+
+ ifunc->func = func;
+ ifunc->data = data;
+ ifunc->argc.min = min_argc;
+ ifunc->argc.max = max_argc;
+
+ return ifunc;
}
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc)
{
struct vm_ifunc *ifunc = rb_vm_ifunc_new(func, (void *)val, min_argc, max_argc);
@@ -769,7 +933,7 @@ rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_a
static const char proc_without_block[] = "tried to create Proc object without a block";
static VALUE
-proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
+proc_new(VALUE klass, int8_t is_lambda)
{
VALUE procval;
const rb_execution_context_t *ec = GET_EC();
@@ -802,16 +966,8 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
break;
case block_handler_type_ifunc:
- return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda);
case block_handler_type_iseq:
- {
- const struct rb_captured_block *captured = VM_BH_TO_CAPT_BLOCK(block_handler);
- rb_control_frame_t *last_ruby_cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
- if (is_lambda && last_ruby_cfp && vm_cfp_forwarded_bh_p(last_ruby_cfp, block_handler)) {
- is_lambda = false;
- }
- return rb_vm_make_proc_lambda(ec, captured, klass, is_lambda);
- }
+ return rb_vm_make_proc_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda);
}
VM_UNREACHABLE(proc_new);
return Qnil;
@@ -834,7 +990,7 @@ proc_new(VALUE klass, int8_t is_lambda, int8_t kernel)
static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
- VALUE block = proc_new(klass, FALSE, FALSE);
+ VALUE block = proc_new(klass, FALSE);
rb_obj_call_init_kw(block, argc, argv, RB_PASS_CALLED_KEYWORDS);
return block;
@@ -843,7 +999,7 @@ rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
VALUE
rb_block_proc(void)
{
- return proc_new(rb_cProc, FALSE, FALSE);
+ return proc_new(rb_cProc, FALSE);
}
/*
@@ -856,41 +1012,44 @@ rb_block_proc(void)
static VALUE
f_proc(VALUE _)
{
- return proc_new(rb_cProc, FALSE, TRUE);
+ return proc_new(rb_cProc, FALSE);
}
VALUE
rb_block_lambda(void)
{
- return proc_new(rb_cProc, TRUE, FALSE);
+ return proc_new(rb_cProc, TRUE);
}
static void
-f_lambda_warn(void)
+f_lambda_filter_non_literal(void)
{
rb_control_frame_t *cfp = GET_EC()->cfp;
VALUE block_handler = rb_vm_frame_block_handler(cfp);
- if (block_handler != VM_BLOCK_HANDLER_NONE) {
- switch (vm_block_handler_type(block_handler)) {
- case block_handler_type_iseq:
- if (RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)->ep == VM_BH_TO_ISEQ_BLOCK(block_handler)->ep) {
- return;
- }
- break;
- case block_handler_type_symbol:
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ // no block error raised else where
+ return;
+ }
+
+ switch (vm_block_handler_type(block_handler)) {
+ case block_handler_type_iseq:
+ if (RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)->ep == VM_BH_TO_ISEQ_BLOCK(block_handler)->ep) {
+ return;
+ }
+ break;
+ case block_handler_type_symbol:
+ return;
+ case block_handler_type_proc:
+ if (rb_proc_lambda_p(VM_BH_TO_PROC(block_handler))) {
return;
- case block_handler_type_proc:
- if (rb_proc_lambda_p(VM_BH_TO_PROC(block_handler))) {
- return;
- }
- break;
- case block_handler_type_ifunc:
- break;
}
+ break;
+ case block_handler_type_ifunc:
+ break;
}
- rb_warn_deprecated("lambda without a literal block", "the proc without lambda");
+ rb_raise(rb_eArgError, "the lambda method requires a literal block");
}
/*
@@ -904,7 +1063,7 @@ f_lambda_warn(void)
static VALUE
f_lambda(VALUE _)
{
- f_lambda_warn();
+ f_lambda_filter_non_literal();
return rb_block_lambda();
}
@@ -926,13 +1085,12 @@ f_lambda(VALUE _)
* Document-method: Proc#yield
*
* call-seq:
- * prc.call(params,...) -> obj
- * prc[params,...] -> obj
- * prc.(params,...) -> obj
- * prc.yield(params,...) -> obj
+ * call(...) -> obj
+ * self[...] -> obj
+ * yield(...) -> obj
*
- * Invokes the block, setting the block's parameters to the values in
- * <i>params</i> using something close to method calling semantics.
+ * Invokes the block, setting the block's parameters to the arguments
+ * using something close to method calling semantics.
* Returns the value of the last expression evaluated in the block.
*
* a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
@@ -986,7 +1144,12 @@ rb_proc_call_kw(VALUE self, VALUE args, int kw_splat)
VALUE vret;
rb_proc_t *proc;
int argc = check_argc(RARRAY_LEN(args));
- const VALUE *argv = RARRAY_CONST_PTR(args);
+
+ // rb_vm_invoke_proc may end up modifying argv as part of calling and so we
+ // must use RARRAY_PTR, which marks the array as WB_UNPROTECTED instead of
+ // RARRAY_CONST_PTR. Unfortunately this is worse for GC.
+ // See invoke_block_from_c_proc
+ VALUE *argv = RARRAY_PTR(args);
GetProcPtr(self, proc);
vret = rb_vm_invoke_proc(GET_EC(), proc, argc, argv,
kw_splat, VM_BLOCK_HANDLER_NONE);
@@ -1079,7 +1242,7 @@ rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max)
{
*max = ISEQ_BODY(iseq)->param.flags.has_rest == FALSE ?
ISEQ_BODY(iseq)->param.lead_num + ISEQ_BODY(iseq)->param.opt_num + ISEQ_BODY(iseq)->param.post_num +
- (ISEQ_BODY(iseq)->param.flags.has_kw == TRUE || ISEQ_BODY(iseq)->param.flags.has_kwrest == TRUE)
+ (ISEQ_BODY(iseq)->param.flags.has_kw == TRUE || ISEQ_BODY(iseq)->param.flags.has_kwrest == TRUE || ISEQ_BODY(iseq)->param.flags.forwardable == TRUE)
: UNLIMITED_ARGUMENTS;
return ISEQ_BODY(iseq)->param.lead_num + ISEQ_BODY(iseq)->param.post_num + (ISEQ_BODY(iseq)->param.flags.has_kw && ISEQ_BODY(iseq)->param.keyword->required_num > 0);
}
@@ -1175,10 +1338,10 @@ rb_block_pair_yield_optimizable(void)
min = rb_vm_block_min_max_arity(&block, &max);
switch (vm_block_type(&block)) {
- case block_handler_type_symbol:
+ case block_type_symbol:
return 0;
- case block_handler_type_proc:
+ case block_type_proc:
{
VALUE procval = block_handler;
rb_proc_t *proc;
@@ -1188,6 +1351,12 @@ rb_block_pair_yield_optimizable(void)
return min > 1;
}
+ case block_type_ifunc:
+ {
+ const struct vm_ifunc *ifunc = block.as.captured.code.ifunc;
+ if (ifunc->flags & IFUNC_YIELD_OPTIMIZABLE) return 1;
+ }
+
default:
return min > 1;
}
@@ -1209,10 +1378,10 @@ rb_block_arity(void)
block_setup(&block, block_handler);
switch (vm_block_type(&block)) {
- case block_handler_type_symbol:
+ case block_type_symbol:
return -1;
- case block_handler_type_proc:
+ case block_type_proc:
return rb_proc_arity(block_handler);
default:
@@ -1273,10 +1442,10 @@ rb_proc_get_iseq(VALUE self, int *is_proc)
}
/* call-seq:
- * prc == other -> true or false
- * prc.eql?(other) -> true or false
+ * self == other -> true or false
+ * eql?(other) -> true or false
*
- * Two procs are the same if, and only if, they were created from the same code block.
+ * Returns whether +self+ and +other+ were created from the same code block:
*
* def return_block(&block)
* block
@@ -1333,12 +1502,17 @@ proc_eq(VALUE self, VALUE other)
}
break;
case block_type_ifunc:
- if (self_block->as.captured.ep != \
- other_block->as.captured.ep ||
- self_block->as.captured.code.ifunc != \
+ if (self_block->as.captured.code.ifunc != \
other_block->as.captured.code.ifunc) {
return Qfalse;
}
+
+ if (memcmp(
+ ((cfunc_proc_t *)self_proc)->env,
+ ((cfunc_proc_t *)other_proc)->env,
+ sizeof(((cfunc_proc_t *)self_proc)->env))) {
+ return Qfalse;
+ }
break;
case block_type_proc:
if (self_block->as.proc != other_block->as.proc) {
@@ -1363,12 +1537,12 @@ iseq_location(const rb_iseq_t *iseq)
if (!iseq) return Qnil;
rb_iseq_check(iseq);
loc[0] = rb_iseq_path(iseq);
- loc[1] = ISEQ_BODY(iseq)->location.first_lineno;
+ loc[1] = RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
return rb_ary_new4(2, loc);
}
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_iseq_location(const rb_iseq_t *iseq)
{
return iseq_location(iseq);
@@ -1460,11 +1634,36 @@ rb_hash_proc(st_index_t hash, VALUE prc)
{
rb_proc_t *proc;
GetProcPtr(prc, proc);
- hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val);
- hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self);
- return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep);
+
+ switch (vm_block_type(&proc->block)) {
+ case block_type_iseq:
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.iseq->body);
+ break;
+ case block_type_ifunc:
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->func);
+ hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->data);
+ break;
+ case block_type_symbol:
+ hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.symbol));
+ break;
+ case block_type_proc:
+ hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.proc));
+ break;
+ default:
+ rb_bug("rb_hash_proc: unknown block type %d", vm_block_type(&proc->block));
+ }
+
+ /* ifunc procs have their own allocated ep. If an ifunc is duplicated, they
+ * will point to different ep but they should return the same hash code, so
+ * we cannot include the ep in the hash. */
+ if (vm_block_type(&proc->block) != block_type_ifunc) {
+ hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep);
+ }
+
+ return hash;
}
+static VALUE sym_proc_cache = Qfalse;
/*
* call-seq:
@@ -1480,32 +1679,36 @@ rb_hash_proc(st_index_t hash, VALUE prc)
*
*/
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_sym_to_proc(VALUE sym)
{
- static VALUE sym_proc_cache = Qfalse;
enum {SYM_PROC_CACHE_SIZE = 67};
- VALUE proc;
- long index;
- ID id;
- if (!sym_proc_cache) {
- sym_proc_cache = rb_ary_hidden_new(SYM_PROC_CACHE_SIZE * 2);
- rb_gc_register_mark_object(sym_proc_cache);
- rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil);
- }
+ if (rb_ractor_main_p()) {
+ if (!sym_proc_cache) {
+ sym_proc_cache = rb_ary_hidden_new(SYM_PROC_CACHE_SIZE);
+ rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE - 1, Qnil);
+ }
+
+ ID id = SYM2ID(sym);
+ long index = (id % SYM_PROC_CACHE_SIZE);
+ VALUE procval = RARRAY_AREF(sym_proc_cache, index);
+ if (RTEST(procval)) {
+ rb_proc_t *proc;
+ GetProcPtr(procval, proc);
- id = SYM2ID(sym);
- index = (id % SYM_PROC_CACHE_SIZE) << 1;
+ if (proc->block.as.symbol == sym) {
+ return procval;
+ }
+ }
- if (RARRAY_AREF(sym_proc_cache, index) == sym) {
- return RARRAY_AREF(sym_proc_cache, index + 1);
+ procval = sym_proc_new(rb_cProc, sym);
+ RARRAY_ASET(sym_proc_cache, index, procval);
+
+ return RB_GC_GUARD(procval);
}
else {
- proc = sym_proc_new(rb_cProc, ID2SYM(id));
- RARRAY_ASET(sym_proc_cache, index, sym);
- RARRAY_ASET(sym_proc_cache, index + 1, proc);
- return proc;
+ return sym_proc_new(rb_cProc, sym);
}
}
@@ -1544,7 +1747,7 @@ rb_block_to_s(VALUE self, const struct rb_block *block, const char *additional_i
const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq);
rb_str_catf(str, "%p %"PRIsVALUE":%d", (void *)self,
rb_iseq_path(iseq),
- FIX2INT(ISEQ_BODY(iseq)->location.first_lineno));
+ ISEQ_BODY(iseq)->location.first_lineno);
}
break;
case block_type_symbol:
@@ -1591,40 +1794,25 @@ proc_to_proc(VALUE self)
}
static void
-bm_mark(void *ptr)
-{
- struct METHOD *data = ptr;
- rb_gc_mark_movable(data->recv);
- rb_gc_mark_movable(data->klass);
- rb_gc_mark_movable(data->iclass);
- rb_gc_mark_movable((VALUE)data->me);
-}
-
-static void
-bm_compact(void *ptr)
+bm_mark_and_move(void *ptr)
{
struct METHOD *data = ptr;
- UPDATE_REFERENCE(data->recv);
- UPDATE_REFERENCE(data->klass);
- UPDATE_REFERENCE(data->iclass);
- UPDATE_TYPED_REFERENCE(rb_method_entry_t *, data->me);
-}
-
-static size_t
-bm_memsize(const void *ptr)
-{
- return sizeof(struct METHOD);
+ rb_gc_mark_and_move((VALUE *)&data->recv);
+ rb_gc_mark_and_move((VALUE *)&data->klass);
+ rb_gc_mark_and_move((VALUE *)&data->iclass);
+ rb_gc_mark_and_move((VALUE *)&data->owner);
+ rb_gc_mark_and_move_ptr((rb_method_entry_t **)&data->me);
}
static const rb_data_type_t method_data_type = {
"method",
{
- bm_mark,
+ bm_mark_and_move,
RUBY_TYPED_DEFAULT_FREE,
- bm_memsize,
- bm_compact,
+ NULL, // No external memory to report,
+ bm_mark_and_move,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE | RUBY_TYPED_FROZEN_SHAREABLE_NO_REC
};
VALUE
@@ -1639,7 +1827,7 @@ respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope)
/* TODO: merge with obj_respond_to() */
ID rmiss = idRespond_to_missing;
- if (obj == Qundef) return 0;
+ if (UNDEF_P(obj)) return 0;
if (rb_method_basic_definition_p(klass, rmiss)) return 0;
return RTEST(rb_funcall(obj, rmiss, 2, sym, RBOOL(!scope)));
}
@@ -1655,6 +1843,7 @@ mnew_missing(VALUE klass, VALUE obj, ID id, VALUE mclass)
RB_OBJ_WRITE(method, &data->recv, obj);
RB_OBJ_WRITE(method, &data->klass, klass);
+ RB_OBJ_WRITE(method, &data->owner, klass);
def = ZALLOC(rb_method_definition_t);
def->type = VM_METHOD_TYPE_MISSING;
@@ -1676,14 +1865,18 @@ mnew_missing_by_name(VALUE klass, VALUE obj, VALUE *name, int scope, VALUE mclas
return mnew_missing(klass, obj, SYM2ID(vid), mclass);
}
+VALUE rb_zsuper_to_super(int argc, VALUE *argv, VALUE self);
+
static VALUE
mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
VALUE obj, ID id, VALUE mclass, int scope, int error)
{
struct METHOD *data;
VALUE method;
+ const rb_method_entry_t *original_me = me;
rb_method_visibility_t visi = METHOD_VISI_UNDEF;
+ again:
if (UNDEFINED_METHOD_ENTRY_P(me)) {
if (respond_to_missing_p(klass, obj, ID2SYM(id), scope)) {
return mnew_missing(klass, obj, id, mclass);
@@ -1699,12 +1892,33 @@ mnew_internal(const rb_method_entry_t *me, VALUE klass, VALUE iclass,
rb_print_inaccessible(klass, id, visi);
}
}
+ if (me->def->type == VM_METHOD_TYPE_ZSUPER ||
+ (me->def->type == VM_METHOD_TYPE_CFUNC && me->def->body.cfunc.func == (rb_cfunc_t)rb_zsuper_to_super)) {
+ if (me->def->type == VM_METHOD_TYPE_ZSUPER && me->defined_class) {
+ VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->defined_class));
+ id = me->def->original_id;
+ me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
+ }
+ else {
+ VALUE klass = RCLASS_SUPER(RCLASS_ORIGIN(me->owner));
+ id = me->def->original_id;
+ me = rb_method_entry_without_refinements(klass, id, &iclass);
+ }
+ goto again;
+ }
method = TypedData_Make_Struct(mclass, struct METHOD, &method_data_type, data);
- RB_OBJ_WRITE(method, &data->recv, obj);
- RB_OBJ_WRITE(method, &data->klass, klass);
+ if (UNDEF_P(obj)) {
+ RB_OBJ_WRITE(method, &data->recv, Qundef);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
+ }
+ else {
+ RB_OBJ_WRITE(method, &data->recv, obj);
+ RB_OBJ_WRITE(method, &data->klass, klass);
+ }
RB_OBJ_WRITE(method, &data->iclass, iclass);
+ RB_OBJ_WRITE(method, &data->owner, original_me->owner);
RB_OBJ_WRITE(method, &data->me, me);
return method;
@@ -1723,7 +1937,7 @@ mnew_callable(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope)
const rb_method_entry_t *me;
VALUE iclass = Qnil;
- ASSUME(obj != Qundef);
+ ASSUME(!UNDEF_P(obj));
me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass);
return mnew_from_me(me, klass, iclass, obj, id, mclass, scope);
}
@@ -1738,27 +1952,6 @@ mnew_unbound(VALUE klass, ID id, VALUE mclass, int scope)
return mnew_from_me(me, klass, iclass, Qundef, id, mclass, scope);
}
-static const rb_method_entry_t*
-zsuper_resolve(const rb_method_entry_t *me)
-{
- const rb_method_entry_t *super_me;
- while (me->def->type == VM_METHOD_TYPE_ZSUPER) {
- VALUE defined_class = me->defined_class ? me->defined_class : me->owner;
- VALUE super_class = RCLASS_SUPER(RCLASS_ORIGIN(defined_class));
- if (!super_class) {
- break;
- }
- ID id = me->def->original_id;
- VALUE iclass;
- super_me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, id, &iclass);
- if (!super_me) {
- break;
- }
- me = super_me;
- }
- return me;
-}
-
static inline VALUE
method_entry_defined_class(const rb_method_entry_t *me)
{
@@ -1770,7 +1963,7 @@ method_entry_defined_class(const rb_method_entry_t *me)
*
* Document-class: Method
*
- * Method objects are created by Object#method, and are associated
+ * +Method+ objects are created by Object#method, and are associated
* with a particular object (not just with a class). They may be
* used to invoke the method within the object, and as a block
* associated with an iterator. They may also be unbound from one
@@ -1796,10 +1989,9 @@ method_entry_defined_class(const rb_method_entry_t *me)
/*
* call-seq:
- * meth.eql?(other_meth) -> true or false
- * meth == other_meth -> true or false
+ * self == other -> true or false
*
- * Two method objects are equal if they are bound to the same
+ * Returns whether +self+ and +other+ are bound to the same
* object and refer to the same method definition and the classes
* defining the methods are the same class or module.
*/
@@ -1816,16 +2008,15 @@ method_eq(VALUE method, VALUE other)
return Qfalse;
Check_TypedStruct(method, &method_data_type);
- m1 = (struct METHOD *)DATA_PTR(method);
- m2 = (struct METHOD *)DATA_PTR(other);
+ m1 = (struct METHOD *)RTYPEDDATA_GET_DATA(method);
+ m2 = (struct METHOD *)RTYPEDDATA_GET_DATA(other);
- const rb_method_entry_t *m1_me = zsuper_resolve(m1->me);
- const rb_method_entry_t *m2_me = zsuper_resolve(m2->me);
+ klass1 = method_entry_defined_class(m1->me);
+ klass2 = method_entry_defined_class(m2->me);
+ if (RB_TYPE_P(klass1, T_ICLASS)) klass1 = RBASIC_CLASS(klass1);
+ if (RB_TYPE_P(klass2, T_ICLASS)) klass2 = RBASIC_CLASS(klass2);
- klass1 = method_entry_defined_class(m1_me);
- klass2 = method_entry_defined_class(m2_me);
-
- if (!rb_method_entry_eq(m1_me, m2_me) ||
+ if (!rb_method_entry_eq(m1->me, m2->me) ||
klass1 != klass2 ||
m1->klass != m2->klass ||
m1->recv != m2->recv) {
@@ -1837,6 +2028,22 @@ method_eq(VALUE method, VALUE other)
/*
* call-seq:
+ * meth.eql?(other_meth) -> true or false
+ * meth == other_meth -> true or false
+ *
+ * Two unbound method objects are equal if they refer to the same
+ * method definition.
+ *
+ * Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice)
+ * #=> true
+ *
+ * Array.instance_method(:sum) == Enumerable.instance_method(:sum)
+ * #=> false, Array redefines the method for efficiency
+ */
+#define unbound_method_eq method_eq
+
+/*
+ * call-seq:
* meth.hash -> integer
*
* Returns a hash value corresponding to the method object.
@@ -1877,8 +2084,9 @@ method_unbind(VALUE obj)
method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
&method_data_type, data);
RB_OBJ_WRITE(method, &data->recv, Qundef);
- RB_OBJ_WRITE(method, &data->klass, orig->klass);
+ RB_OBJ_WRITE(method, &data->klass, Qundef);
RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(method, &data->owner, orig->me->owner);
RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));
return method;
@@ -1963,18 +2171,38 @@ method_owner(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
- return data->me->owner;
+ return data->owner;
+}
+
+/*
+ * call-seq:
+ * meth.box -> box or nil
+ *
+ * Returns the Ruby::Box where +meth+ is defined in.
+ */
+static VALUE
+method_box(VALUE obj)
+{
+ struct METHOD *data;
+ const rb_box_t *box;
+
+ TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
+ box = data->me->def->box;
+ if (!box) return Qnil;
+ if (box->box_object) return box->box_object;
+ rb_bug("Unexpected box on the method definition: %p", (void*) box);
+ UNREACHABLE_RETURN(Qnil);
}
void
rb_method_name_error(VALUE klass, VALUE str)
{
-#define MSG(s) rb_fstring_lit("undefined method `%1$s' for"s" `%2$s'")
+#define MSG(s) rb_fstring_lit("undefined method '%1$s' for"s" '%2$s'")
VALUE c = klass;
VALUE s = Qundef;
- if (FL_TEST(c, FL_SINGLETON)) {
- VALUE obj = rb_ivar_get(klass, attached);
+ if (RCLASS_SINGLETON_P(c)) {
+ VALUE obj = RCLASS_ATTACHED_OBJECT(klass);
switch (BUILTIN_TYPE(obj)) {
case T_MODULE:
@@ -1988,7 +2216,7 @@ rb_method_name_error(VALUE klass, VALUE str)
else if (RB_TYPE_P(c, T_MODULE)) {
s = MSG(" module");
}
- if (s == Qundef) {
+ if (UNDEF_P(s)) {
s = MSG(" class");
}
rb_name_err_raise_str(s, c, str);
@@ -2015,7 +2243,7 @@ obj_method(VALUE obj, VALUE vid, int scope)
* obj.method(sym) -> method
*
* Looks up the named method as a receiver in <i>obj</i>, returning a
- * Method object (or raising NameError). The Method object acts as a
+ * +Method+ object (or raising NameError). The +Method+ object acts as a
* closure in <i>obj</i>'s object instance, so instance variables and
* the value of <code>self</code> remain available.
*
@@ -2036,7 +2264,7 @@ obj_method(VALUE obj, VALUE vid, int scope)
* m = l.method("hello")
* m.call #=> "Hello, @iv = Fred"
*
- * Note that Method implements <code>to_proc</code> method, which
+ * Note that +Method+ implements <code>to_proc</code> method, which
* means it can be used with iterators.
*
* [ 1, 2, 3 ].each(&method(:puts)) # => prints 3 lines to stdout
@@ -2068,6 +2296,19 @@ rb_obj_public_method(VALUE obj, VALUE vid)
return obj_method(obj, vid, TRUE);
}
+static VALUE
+rb_obj_singleton_method_lookup(VALUE arg)
+{
+ VALUE *args = (VALUE *)arg;
+ return rb_obj_method(args[0], args[1]);
+}
+
+static VALUE
+rb_obj_singleton_method_lookup_fail(VALUE arg1, VALUE arg2)
+{
+ return Qfalse;
+}
+
/*
* call-seq:
* obj.singleton_method(sym) -> method
@@ -2095,13 +2336,13 @@ rb_obj_public_method(VALUE obj, VALUE vid)
VALUE
rb_obj_singleton_method(VALUE obj, VALUE vid)
{
- VALUE klass = rb_singleton_class_get(obj);
+ VALUE sc = rb_singleton_class_get(obj);
+ VALUE klass;
ID id = rb_check_id(&vid);
- if (NIL_P(klass)) {
- /* goto undef; */
- }
- else if (NIL_P(klass = RCLASS_ORIGIN(klass))) {
+ if (NIL_P(sc) ||
+ NIL_P(klass = RCLASS_ORIGIN(sc)) ||
+ !NIL_P(rb_special_singleton_class(obj))) {
/* goto undef; */
}
else if (! id) {
@@ -2110,22 +2351,27 @@ rb_obj_singleton_method(VALUE obj, VALUE vid)
/* else goto undef; */
}
else {
- const rb_method_entry_t *me = rb_method_entry_at(klass, id);
- vid = ID2SYM(id);
-
- if (UNDEFINED_METHOD_ENTRY_P(me)) {
- /* goto undef; */
- }
- else if (UNDEFINED_REFINED_METHOD_P(me->def)) {
- /* goto undef; */
- }
- else {
- return mnew_from_me(me, klass, klass, obj, id, rb_cMethod, FALSE);
+ VALUE args[2] = {obj, vid};
+ VALUE ruby_method = rb_rescue(rb_obj_singleton_method_lookup, (VALUE)args, rb_obj_singleton_method_lookup_fail, Qfalse);
+ if (ruby_method) {
+ struct METHOD *method = (struct METHOD *)RTYPEDDATA_GET_DATA(ruby_method);
+ VALUE lookup_class = RBASIC_CLASS(obj);
+ VALUE stop_class = rb_class_superclass(sc);
+ VALUE method_class = method->iclass;
+
+ /* Determine if method is in singleton class, or module included in or prepended to it */
+ do {
+ if (lookup_class == method_class) {
+ return ruby_method;
+ }
+ lookup_class = RCLASS_SUPER(lookup_class);
+ } while (lookup_class && lookup_class != stop_class);
}
}
/* undef: */
- rb_name_err_raise("undefined singleton method `%1$s' for `%2$s'",
+ vid = ID2SYM(id);
+ rb_name_err_raise("undefined singleton method '%1$s' for '%2$s'",
obj, vid);
UNREACHABLE_RETURN(Qundef);
}
@@ -2136,29 +2382,29 @@ rb_obj_singleton_method(VALUE obj, VALUE vid)
*
* Returns an +UnboundMethod+ representing the given
* instance method in _mod_.
+ * See +UnboundMethod+ about how to utilize it
*
- * class Interpreter
- * def do_a() print "there, "; end
- * def do_d() print "Hello "; end
- * def do_e() print "!\n"; end
- * def do_v() print "Dave"; end
- * Dispatcher = {
- * "a" => instance_method(:do_a),
- * "d" => instance_method(:do_d),
- * "e" => instance_method(:do_e),
- * "v" => instance_method(:do_v)
- * }
- * def interpret(string)
- * string.each_char {|b| Dispatcher[b].bind(self).call }
- * end
- * end
+ * class Person
+ * def initialize(name)
+ * @name = name
+ * end
+ *
+ * def hi
+ * puts "Hi, I'm #{@name}!"
+ * end
+ * end
*
- * interpreter = Interpreter.new
- * interpreter.interpret('dave')
+ * dave = Person.new('Dave')
+ * thomas = Person.new('Thomas')
+ *
+ * hi = Person.instance_method(:hi)
+ * hi.bind_call(dave)
+ * hi.bind_call(thomas)
*
* <em>produces:</em>
*
- * Hello there, Dave!
+ * Hi, I'm Dave!
+ * Hi, I'm Thomas!
*/
static VALUE
@@ -2220,10 +2466,10 @@ rb_mod_define_method_with_visibility(int argc, VALUE *argv, VALUE mod, const str
if (!id) id = rb_to_id(name);
if (is_method) {
- struct METHOD *method = (struct METHOD *)DATA_PTR(body);
+ struct METHOD *method = (struct METHOD *)RTYPEDDATA_GET_DATA(body);
if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) &&
!RTEST(rb_class_inherited_p(mod, method->me->owner))) {
- if (FL_TEST(method->me->owner, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(method->me->owner)) {
rb_raise(rb_eTypeError,
"can't bind singleton method to a different class");
}
@@ -2360,17 +2606,7 @@ rb_obj_define_method(int argc, VALUE *argv, VALUE obj)
static VALUE
top_define_method(int argc, VALUE *argv, VALUE obj)
{
- rb_thread_t *th = GET_THREAD();
- VALUE klass;
-
- klass = th->top_wrapper;
- if (klass) {
- rb_warning("main.define_method in the wrapped load is effective only in wrapper module");
- }
- else {
- klass = rb_cObject;
- }
- return rb_mod_define_method(argc, argv, klass);
+ return rb_mod_define_method(argc, argv, rb_top_main_class("define_method"));
}
/*
@@ -2397,63 +2633,62 @@ method_clone(VALUE self)
struct METHOD *orig, *data;
TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
- clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
- CLONESETUP(clone, self);
+ clone = TypedData_Make_Struct(rb_obj_class(self), struct METHOD, &method_data_type, data);
+ rb_obj_clone_setup(self, clone, Qnil);
RB_OBJ_WRITE(clone, &data->recv, orig->recv);
RB_OBJ_WRITE(clone, &data->klass, orig->klass);
RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(clone, &data->owner, orig->owner);
RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
return clone;
}
-/* Document-method: Method#===
- *
- * call-seq:
- * method === obj -> result_of_method
- *
- * Invokes the method with +obj+ as the parameter like #call.
- * This allows a method object to be the target of a +when+ clause
- * in a case statement.
- *
- * require 'prime'
- *
- * case 1373
- * when Prime.method(:prime?)
- * # ...
- * end
- */
-
+/* :nodoc: */
+static VALUE
+method_dup(VALUE self)
+{
+ VALUE clone;
+ struct METHOD *orig, *data;
-/* Document-method: Method#[]
- *
- * call-seq:
- * meth[args, ...] -> obj
- *
- * Invokes the <i>meth</i> with the specified arguments, returning the
- * method's return value, like #call.
- *
- * m = 12.method("+")
- * m[3] #=> 15
- * m[20] #=> 32
- */
+ TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
+ clone = TypedData_Make_Struct(rb_obj_class(self), struct METHOD, &method_data_type, data);
+ rb_obj_dup_setup(self, clone);
+ RB_OBJ_WRITE(clone, &data->recv, orig->recv);
+ RB_OBJ_WRITE(clone, &data->klass, orig->klass);
+ RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
+ RB_OBJ_WRITE(clone, &data->owner, orig->owner);
+ RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
+ return clone;
+}
/*
* call-seq:
- * meth.call(args, ...) -> obj
+ * call(...) -> obj
+ * self[...] -> obj
+ * self === obj -> result_of_method
*
- * Invokes the <i>meth</i> with the specified arguments, returning the
+ * Invokes +self+ with the specified arguments, returning the
* method's return value.
*
* m = 12.method("+")
* m.call(3) #=> 15
* m.call(20) #=> 32
+ *
+ * Using Method#=== allows a method object to be the target of a +when+ clause
+ * in a case statement.
+ *
+ * require 'prime'
+ *
+ * case 1373
+ * when Prime.method(:prime?)
+ * # ...
+ * end
*/
static VALUE
rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method)
{
- VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
- return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS);
+ return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS);
}
VALUE
@@ -2493,7 +2728,7 @@ rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE pa
rb_execution_context_t *ec = GET_EC();
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- if (data->recv == Qundef) {
+ if (UNDEF_P(data->recv)) {
rb_raise(rb_eTypeError, "can't call unbound method; bind first");
}
return call_method_data(ec, data, argc, argv, passed_procval, kw_splat);
@@ -2509,7 +2744,7 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
*
* Document-class: UnboundMethod
*
- * Ruby supports two forms of objectified methods. Class Method is
+ * Ruby supports two forms of objectified methods. Class +Method+ is
* used to represent methods that are associated with a particular
* object: these method objects are bound to that object. Bound
* method objects for an object can be created using Object#method.
@@ -2563,7 +2798,7 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passe
static void
convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALUE *methclass_out, VALUE *klass_out, VALUE *iclass_out, const rb_method_entry_t **me_out, const bool clone)
{
- VALUE methclass = data->me->owner;
+ VALUE methclass = data->owner;
VALUE iclass = data->me->defined_class;
VALUE klass = CLASS_OF(recv);
@@ -2571,9 +2806,8 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU
VALUE refined_class = rb_refinement_module_get_refined_class(methclass);
if (!NIL_P(refined_class)) methclass = refined_class;
}
- if (!RB_TYPE_P(methclass, T_MODULE) &&
- methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) {
- if (FL_TEST(methclass, FL_SINGLETON)) {
+ if (!RB_TYPE_P(methclass, T_MODULE) && !RTEST(rb_obj_is_kind_of(recv, methclass))) {
+ if (RCLASS_SINGLETON_P(methclass)) {
rb_raise(rb_eTypeError,
"singleton method called for a different object");
}
@@ -2594,7 +2828,7 @@ convert_umethod_to_method_components(const struct METHOD *data, VALUE recv, VALU
if (RB_TYPE_P(me->owner, T_MODULE)) {
if (!clone) {
// if we didn't previously clone the method entry, then we need to clone it now
- // because this branch manipualtes it in rb_method_entry_complement_defined_class
+ // because this branch manipulates it in rb_method_entry_complement_defined_class
me = rb_method_entry_clone(me);
}
VALUE ic = rb_class_search_ancestor(klass, me->owner);
@@ -2663,6 +2897,7 @@ umethod_bind(VALUE method, VALUE recv)
RB_OBJ_WRITE(method, &bound->recv, recv);
RB_OBJ_WRITE(method, &bound->klass, klass);
RB_OBJ_WRITE(method, &bound->iclass, iclass);
+ RB_OBJ_WRITE(method, &bound->owner, methclass);
RB_OBJ_WRITE(method, &bound->me, me);
return method;
@@ -2699,7 +2934,7 @@ umethod_bind_call(int argc, VALUE *argv, VALUE method)
VALUE methclass, klass, iclass;
const rb_method_entry_t *me;
convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false);
- struct METHOD bound = { recv, klass, 0, me };
+ struct METHOD bound = { recv, klass, 0, methclass, me };
return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS);
}
@@ -2851,7 +3086,11 @@ original_method_entry(VALUE mod, ID id)
while ((me = rb_method_entry(mod, id)) != 0) {
const rb_method_definition_t *def = me->def;
- if (def->type != VM_METHOD_TYPE_ZSUPER) break;
+
+ if (def->type != VM_METHOD_TYPE_ZSUPER &&
+ (def->type != VM_METHOD_TYPE_CFUNC ||
+ def->body.cfunc.func != (rb_cfunc_t)rb_zsuper_to_super)) break;
+
mod = RCLASS_SUPER(me->owner);
id = def->original_id;
}
@@ -2970,14 +3209,6 @@ rb_method_entry_location(const rb_method_entry_t *me)
return method_def_location(me->def);
}
-static const rb_method_definition_t *
-zsuper_ref_method_def(VALUE method)
-{
- const struct METHOD *data;
- TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
- return zsuper_resolve(data->me)->def;
-}
-
/*
* call-seq:
* meth.source_location -> [String, Integer]
@@ -2989,7 +3220,7 @@ zsuper_ref_method_def(VALUE method)
VALUE
rb_method_location(VALUE method)
{
- return method_def_location(zsuper_ref_method_def(method));
+ return method_def_location(rb_method_def(method));
}
static const rb_method_definition_t *
@@ -3077,7 +3308,7 @@ method_def_parameters(const rb_method_definition_t *def)
static VALUE
rb_method_parameters(VALUE method)
{
- return method_def_parameters(zsuper_ref_method_def(method));
+ return method_def_parameters(rb_method_def(method));
}
/*
@@ -3140,17 +3371,21 @@ method_inspect(VALUE method)
defined_class = data->me->def->body.alias.original_me->owner;
}
else {
- defined_class = method_entry_defined_class(zsuper_resolve(data->me));
+ defined_class = method_entry_defined_class(data->me);
}
if (RB_TYPE_P(defined_class, T_ICLASS)) {
defined_class = RBASIC_CLASS(defined_class);
}
- if (FL_TEST(mklass, FL_SINGLETON)) {
- VALUE v = rb_ivar_get(mklass, attached);
+ if (UNDEF_P(data->recv)) {
+ // UnboundMethod
+ rb_str_buf_append(str, rb_inspect(defined_class));
+ }
+ else if (RCLASS_SINGLETON_P(mklass)) {
+ VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
- if (data->recv == Qundef) {
+ if (UNDEF_P(data->recv)) {
rb_str_buf_append(str, rb_inspect(mklass));
}
else if (data->recv == v) {
@@ -3167,8 +3402,8 @@ method_inspect(VALUE method)
}
else {
mklass = data->klass;
- if (FL_TEST(mklass, FL_SINGLETON)) {
- VALUE v = rb_ivar_get(mklass, attached);
+ if (RCLASS_SINGLETON_P(mklass)) {
+ VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) {
do {
mklass = RCLASS_SUPER(mklass);
@@ -3202,6 +3437,7 @@ method_inspect(VALUE method)
const VALUE keyrest = ID2SYM(rb_intern("keyrest"));
const VALUE block = ID2SYM(rb_intern("block"));
const VALUE nokey = ID2SYM(rb_intern("nokey"));
+ const VALUE noblock = ID2SYM(rb_intern("noblock"));
int forwarding = 0;
rb_str_buf_cat2(str, "(");
@@ -3219,9 +3455,10 @@ method_inspect(VALUE method)
for (int i = 0; i < RARRAY_LEN(params); i++) {
pair = RARRAY_AREF(params, i);
kind = RARRAY_AREF(pair, 0);
- name = RARRAY_AREF(pair, 1);
- // FIXME: in tests it turns out that kind, name = [:req] produces name to be false. Why?..
- if (NIL_P(name) || name == Qfalse) {
+ if (RARRAY_LEN(pair) > 1) {
+ name = RARRAY_AREF(pair, 1);
+ }
+ else {
// FIXME: can it be reduced to switch/case?
if (kind == req || kind == opt) {
name = rb_str_new2("_");
@@ -3235,6 +3472,12 @@ method_inspect(VALUE method)
else if (kind == nokey) {
name = rb_str_new2("nil");
}
+ else if (kind == noblock) {
+ name = rb_str_new2("nil");
+ }
+ else {
+ name = Qnil;
+ }
}
if (kind == req) {
@@ -3284,6 +3527,9 @@ method_inspect(VALUE method)
else if (kind == nokey) {
rb_str_buf_cat2(str, "**nil");
}
+ else if (kind == noblock) {
+ rb_str_buf_cat2(str, "&nil");
+ }
if (i < RARRAY_LEN(params) - 1) {
rb_str_buf_cat2(str, ", ");
@@ -3349,12 +3595,14 @@ method_to_proc(VALUE method)
}
extern VALUE rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner);
+extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
+rb_cref_t * rb_vm_get_cref(const VALUE *ep);
/*
* call-seq:
* meth.super_method -> method
*
- * Returns a Method of superclass which would be called when super is used
+ * Returns a +Method+ of superclass which would be called when super is used
* or nil if there is no method on superclass.
*/
@@ -3375,11 +3623,76 @@ method_super_method(VALUE method)
mid = data->me->def->body.alias.original_me->def->original_id;
}
else {
- super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
+ VALUE klass = iclass;
+ if (RICLASS_FOR_REFINEMENT_P(klass)) {
+ // Refined methods need this check before superclass determination
+ klass = RBASIC(klass)->klass;
+ }
+ super_class = RCLASS_SUPER(RCLASS_ORIGIN(klass));
mid = data->me->def->original_id;
}
if (!super_class) return Qnil;
- me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
+
+ // For refined methods, skip refinements for the same definition, but consider
+ // refinements for superclass methods
+ const rb_method_definition_t *skip_def = RICLASS_FOR_REFINEMENT_P(iclass) ? data->me->def : NULL;
+
+ // Use the CREF of the Method/UnboundMethod, not the CREF of the caller of super_method.
+ // We must avoid the use of rb_callable_method_entry_with_refinements, as that will
+ // implicitly use the refinements activated in of the caller of super_method.
+ const rb_cref_t *cref = NULL;
+ switch (data->me->def->type) {
+ case VM_METHOD_TYPE_ISEQ:
+ cref = data->me->def->body.iseq.cref;
+ break;
+ case VM_METHOD_TYPE_BMETHOD: {
+ const rb_proc_t *proc;
+ GetProcPtr(data->me->def->body.bmethod.proc, proc);
+ const struct rb_block *block = &proc->block;
+ if (vm_block_type(block) == block_type_iseq)
+ cref = rb_vm_get_cref(block->as.captured.ep);
+ break;
+ }
+ default:
+ break;
+ }
+ VALUE klass = super_class;
+ me = NULL;
+ while (klass) {
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
+ if (!cme) break;
+ if (cme->def->type != VM_METHOD_TYPE_REFINED) {
+ me = (rb_method_entry_t *)cme;
+ iclass = cme->defined_class;
+ break;
+ }
+ // Look through all CREF scopes for a refinement for cme->owner, mirroring
+ // the loop in search_refined_method.
+ const rb_cref_t *c;
+ for (c = cref; c; c = CREF_NEXT(c)) {
+ VALUE refs = CREF_REFINEMENTS(c);
+ if (NIL_P(refs)) continue;
+ VALUE r = rb_hash_lookup(refs, cme->owner);
+ if (NIL_P(r)) continue;
+ const rb_callable_method_entry_t *ref_cme = rb_callable_method_entry(r, mid);
+ if (!ref_cme) break;
+ if (ref_cme->def->type == VM_METHOD_TYPE_REFINED) continue;
+ if (skip_def && rb_method_definition_eq(ref_cme->def, skip_def)) continue;
+ me = (rb_method_entry_t *)ref_cme;
+ iclass = ref_cme->defined_class;
+ break;
+ }
+ if (me) break;
+ // No refined method found. Use orig_me if available, or normal method lookup
+ // in superclass otherwise.
+ const rb_method_entry_t *orig_me = cme->def->body.refined.orig_me;
+ if (orig_me) {
+ me = (rb_method_entry_t *)orig_me;
+ iclass = orig_me->defined_class ? orig_me->defined_class : cme->defined_class;
+ break;
+ }
+ klass = RCLASS_SUPER(cme->defined_class);
+ }
if (!me) return Qnil;
return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
}
@@ -3427,9 +3740,15 @@ env_clone(const rb_env_t *env, const rb_cref_t *cref)
}
new_body = ALLOC_N(VALUE, env->env_size);
- MEMCPY(new_body, env->env, VALUE, env->env_size);
new_ep = &new_body[env->ep - env->env];
new_env = vm_env_new(new_ep, new_body, env->env_size, env->iseq);
+
+ /* The memcpy has to happen after the vm_env_new because it can trigger a
+ * GC compaction which can move the objects in the env. */
+ MEMCPY(new_body, env->env, VALUE, env->env_size);
+ /* VM_ENV_DATA_INDEX_ENV is set in vm_env_new but will get overwritten
+ * by the memcpy above. */
+ new_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)new_env;
RB_OBJ_WRITE(new_env, &new_ep[VM_ENV_DATA_INDEX_ME_CREF], (VALUE)cref);
VM_ASSERT(VM_ENV_ESCAPED_P(new_ep));
return new_env;
@@ -3486,7 +3805,7 @@ proc_binding(VALUE self)
env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
env = env_clone(env, method_cref(method));
/* set empty iseq */
- empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
+ empty = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP);
RB_OBJ_WRITE(env, &env->iseq, empty);
break;
}
@@ -3507,7 +3826,7 @@ proc_binding(VALUE self)
if (iseq) {
rb_iseq_check(iseq);
RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj);
- bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
+ bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno;
}
else {
RB_OBJ_WRITE(bindval, &bind->pathobj,
@@ -3799,19 +4118,18 @@ rb_proc_compose_to_right(VALUE self, VALUE g)
/*
* call-seq:
- * meth << g -> a_proc
+ * self << g -> a_proc
*
- * Returns a proc that is the composition of this method and the given <i>g</i>.
- * The returned proc takes a variable number of arguments, calls <i>g</i> with them
- * then calls this method with the result.
+ * Returns a proc that is the composition of the given +g+ and this method.
*
- * def f(x)
- * x * x
- * end
+ * The returned proc takes a variable number of arguments. It first calls +g+
+ * with the arguments, then calls +self+ with the return value of +g+.
+ *
+ * def f(ary) = ary << 'in f'
*
* f = self.method(:f)
- * g = proc {|x| x + x }
- * p (f << g).call(2) #=> 16
+ * g = proc { |ary| ary << 'in proc' }
+ * (f << g).call([]) # => ["in proc", "in f"]
*/
static VALUE
rb_method_compose_to_left(VALUE self, VALUE g)
@@ -3823,19 +4141,18 @@ rb_method_compose_to_left(VALUE self, VALUE g)
/*
* call-seq:
- * meth >> g -> a_proc
+ * self >> g -> a_proc
*
- * Returns a proc that is the composition of this method and the given <i>g</i>.
- * The returned proc takes a variable number of arguments, calls this method
- * with them then calls <i>g</i> with the result.
+ * Returns a proc that is the composition of this method and the given +g+.
*
- * def f(x)
- * x * x
- * end
+ * The returned proc takes a variable number of arguments. It first calls +self+
+ * with the arguments, then calls +g+ with the return value of +self+.
+ *
+ * def f(ary) = ary << 'in f'
*
* f = self.method(:f)
- * g = proc {|x| x + x }
- * p (f >> g).call(2) #=> 8
+ * g = proc { |ary| ary << 'in proc' }
+ * (f >> g).call([]) # => ["in f", "in proc"]
*/
static VALUE
rb_method_compose_to_right(VALUE self, VALUE g)
@@ -3893,12 +4210,13 @@ proc_ruby2_keywords(VALUE procval)
switch (proc->block.type) {
case block_type_iseq:
if (ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_rest &&
+ !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_post &&
!ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kw &&
!ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kwrest) {
ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.ruby2_keywords = 1;
}
else {
- rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or proc does not accept argument splat)");
+ rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or post arguments or proc does not accept argument splat)");
}
break;
default:
@@ -4142,7 +4460,6 @@ proc_ruby2_keywords(VALUE procval)
* a proc by the <code>&</code> operator, and therefore can be
* consumed by iterators.
*
-
* class Greeter
* def initialize(greeting)
* @greeting = greeting
@@ -4158,8 +4475,8 @@ proc_ruby2_keywords(VALUE procval)
* ["Bob", "Jane"].map(&hi) #=> ["Hi, Bob!", "Hi, Jane!"]
* ["Bob", "Jane"].map(&hey) #=> ["Hey, Bob!", "Hey, Jane!"]
*
- * Of the Ruby core classes, this method is implemented by Symbol,
- * Method, and Hash.
+ * Of the Ruby core classes, this method is implemented by +Symbol+,
+ * +Method+, and +Hash+.
*
* :to_s.to_proc.call(1) #=> "1"
* [1, 2].map(&:to_s) #=> ["1", "2"]
@@ -4193,19 +4510,86 @@ proc_ruby2_keywords(VALUE procval)
* Since +return+ and +break+ exits the block itself in lambdas,
* lambdas cannot be orphaned.
*
- * == Numbered parameters
+ * == Anonymous block parameters
*
- * Numbered parameters are implicitly defined block parameters intended to
- * simplify writing short blocks:
+ * To simplify writing short blocks, Ruby provides two different types of
+ * anonymous parameters: +it+ (single parameter) and numbered ones: <tt>_1</tt>,
+ * <tt>_2</tt> and so on.
*
* # Explicit parameter:
* %w[test me please].each { |str| puts str.upcase } # prints TEST, ME, PLEASE
* (1..5).map { |i| i**2 } # => [1, 4, 9, 16, 25]
*
- * # Implicit parameter:
+ * # it:
+ * %w[test me please].each { puts it.upcase } # prints TEST, ME, PLEASE
+ * (1..5).map { it**2 } # => [1, 4, 9, 16, 25]
+ *
+ * # Numbered parameter:
* %w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE
* (1..5).map { _1**2 } # => [1, 4, 9, 16, 25]
*
+ * === +it+
+ *
+ * +it+ is a name that is available inside a block when no explicit parameters
+ * defined, as shown above.
+ *
+ * %w[test me please].each { puts it.upcase } # prints TEST, ME, PLEASE
+ * (1..5).map { it**2 } # => [1, 4, 9, 16, 25]
+ *
+ * +it+ is a "soft keyword": it is not a reserved name, and can be used as
+ * a name for methods and local variables:
+ *
+ * it = 5 # no warnings
+ * def it(&block) # RSpec-like API, no warnings
+ * # ...
+ * end
+ *
+ * +it+ can be used as a local variable even in blocks that use it as an
+ * implicit parameter (though this style is obviously confusing):
+ *
+ * [1, 2, 3].each {
+ * # takes a value of implicit parameter "it" and uses it to
+ * # define a local variable with the same name
+ * it = it**2
+ * p it
+ * }
+ *
+ * In a block with explicit parameters defined +it+ usage raises an exception:
+ *
+ * [1, 2, 3].each { |x| p it }
+ * # syntax error found (SyntaxError)
+ * # [1, 2, 3].each { |x| p it }
+ * # ^~ 'it' is not allowed when an ordinary parameter is defined
+ *
+ * But if a local name (variable or method) is available, it would be used:
+ *
+ * it = 5
+ * [1, 2, 3].each { |x| p it }
+ * # Prints 5, 5, 5
+ *
+ * Blocks using +it+ can be nested:
+ *
+ * %w[test me].each { it.each_char { p it } }
+ * # Prints "t", "e", "s", "t", "m", "e"
+ *
+ * Blocks using +it+ are considered to have one parameter:
+ *
+ * p = proc { it**2 }
+ * l = lambda { it**2 }
+ * p.parameters # => [[:opt]]
+ * p.arity # => 1
+ * l.parameters # => [[:req]]
+ * l.arity # => 1
+ *
+ * === Numbered parameters
+ *
+ * Numbered parameters are another way to name block parameters implicitly.
+ * Unlike +it+, numbered parameters allow to refer to several parameters
+ * in one block.
+ *
+ * %w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE
+ * {a: 100, b: 200}.map { "#{_1} = #{_2}" } # => "a = 100", "b = 200"
+ *
* Parameter names from +_1+ to +_9+ are supported:
*
* [10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1 + _2 + _3 }
@@ -4220,11 +4604,16 @@ proc_ruby2_keywords(VALUE procval)
* [10, 20, 30].map { |x| _1**2 }
* # SyntaxError (ordinary parameter is defined)
*
+ * Numbered parameters can't be mixed with +it+ either:
+ *
+ * [10, 20, 30].map { _1 + it }
+ * # SyntaxError: 'it' is not allowed when a numbered parameter is already used
+ *
* To avoid conflicts, naming local variables or method
- * arguments +_1+, +_2+ and so on, causes a warning.
+ * arguments +_1+, +_2+ and so on, causes an error.
*
- * _1 = 'test'
- * # warning: `_1' is reserved as numbered parameter
+ * _1 = 'test'
+ * # ^~ _1 is reserved for numbered parameters (SyntaxError)
*
* Using implicit numbered parameters affects block's arity:
*
@@ -4238,14 +4627,12 @@ proc_ruby2_keywords(VALUE procval)
* Blocks with numbered parameters can't be nested:
*
* %w[test me].each { _1.each_char { p _1 } }
- * # SyntaxError (numbered parameter is already used in outer block here)
+ * # numbered parameter is already used in outer block (SyntaxError)
* # %w[test me].each { _1.each_char { p _1 } }
* # ^~
*
- * Numbered parameters were introduced in Ruby 2.7.
*/
-
void
Init_Proc(void)
{
@@ -4270,7 +4657,7 @@ Init_Proc(void)
rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "clone", proc_clone, 0);
- rb_define_method(rb_cProc, "dup", rb_proc_dup, 0);
+ rb_define_method(rb_cProc, "dup", proc_dup, 0);
rb_define_method(rb_cProc, "hash", proc_hash, 0);
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
rb_define_alias(rb_cProc, "inspect", "to_s");
@@ -4306,6 +4693,7 @@ Init_Proc(void)
rb_define_method(rb_cMethod, "eql?", method_eq, 1);
rb_define_method(rb_cMethod, "hash", method_hash, 0);
rb_define_method(rb_cMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cMethod, "dup", method_dup, 0);
rb_define_method(rb_cMethod, "call", rb_method_call_pass_called_kw, -1);
rb_define_method(rb_cMethod, "===", rb_method_call_pass_called_kw, -1);
rb_define_method(rb_cMethod, "curry", rb_method_curry, -1);
@@ -4328,14 +4716,17 @@ Init_Proc(void)
rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1);
rb_define_method(rb_mKernel, "singleton_method", rb_obj_singleton_method, 1);
+ rb_define_method(rb_cMethod, "box", method_box, 0);
+
/* UnboundMethod */
rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
rb_undef_alloc_func(rb_cUnboundMethod);
rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
- rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
- rb_define_method(rb_cUnboundMethod, "eql?", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "==", unbound_method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "eql?", unbound_method_eq, 1);
rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cUnboundMethod, "dup", method_dup, 0);
rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
@@ -4398,6 +4789,8 @@ Init_Proc(void)
void
Init_Binding(void)
{
+ rb_gc_register_address(&sym_proc_cache);
+
rb_cBinding = rb_define_class("Binding", rb_cObject);
rb_undef_alloc_func(rb_cBinding);
rb_undef_method(CLASS_OF(rb_cBinding), "new");
@@ -4408,6 +4801,9 @@ Init_Binding(void)
rb_define_method(rb_cBinding, "local_variable_get", bind_local_variable_get, 1);
rb_define_method(rb_cBinding, "local_variable_set", bind_local_variable_set, 2);
rb_define_method(rb_cBinding, "local_variable_defined?", bind_local_variable_defined_p, 1);
+ rb_define_method(rb_cBinding, "implicit_parameters", bind_implicit_parameters, 0);
+ rb_define_method(rb_cBinding, "implicit_parameter_get", bind_implicit_parameter_get, 1);
+ rb_define_method(rb_cBinding, "implicit_parameter_defined?", bind_implicit_parameter_defined_p, 1);
rb_define_method(rb_cBinding, "receiver", bind_receiver, 0);
rb_define_method(rb_cBinding, "source_location", bind_location, 0);
rb_define_global_function("binding", rb_f_binding, 0);