mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
52db92de53
mrb_proc_new_cfunc_with_env() allocates RProc with RProc::env as NULL
then allocates REnv and sets it to RProc::env of the allocated RProc. If
incremental GC is ran before "allocates REnv and sets it to RProc::env
of the allocated RProc", the allocated RProc's GC status is
"marked" (Black) and the allocated REnv's GC status is
"unmarked" (White). The next incremental GC sweeps the allocated REnv
without re-marking the allocated RProc. Because the RProc is Black and
the REnv is White.
We need to implement write barrier for the case.
We can force to cause the above situation by the following patch:
diff --git a/src/proc.c b/src/proc.c
index f98998f..4f4e25c 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -92,6 +92,7 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const
struct REnv *e;
int i;
+ mrb_incremental_gc(mrb);
p->env = e = env_new(mrb, argc);
MRB_ENV_UNSHARE_STACK(e);
e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
With this patch, "rake test" causes segmentation fault.
281 lines
6.6 KiB
C
281 lines
6.6 KiB
C
/*
|
|
** proc.c - Proc class
|
|
**
|
|
** See Copyright Notice in mruby.h
|
|
*/
|
|
|
|
#include "mruby.h"
|
|
#include "mruby/class.h"
|
|
#include "mruby/proc.h"
|
|
#include "mruby/opcode.h"
|
|
|
|
static mrb_code call_iseq[] = {
|
|
MKOP_A(OP_CALL, 0),
|
|
};
|
|
|
|
struct RProc *
|
|
mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
|
|
{
|
|
struct RProc *p;
|
|
mrb_callinfo *ci = mrb->c->ci;
|
|
|
|
p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
|
|
p->target_class = 0;
|
|
if (ci) {
|
|
if (ci->proc)
|
|
p->target_class = ci->proc->target_class;
|
|
if (!p->target_class)
|
|
p->target_class = ci->target_class;
|
|
}
|
|
p->body.irep = irep;
|
|
p->env = 0;
|
|
mrb_irep_incref(mrb, irep);
|
|
|
|
return p;
|
|
}
|
|
|
|
static struct REnv*
|
|
env_new(mrb_state *mrb, int nlocals)
|
|
{
|
|
struct REnv *e;
|
|
|
|
e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
|
|
MRB_SET_ENV_STACK_LEN(e, nlocals);
|
|
e->mid = mrb->c->ci->mid;
|
|
e->cioff = mrb->c->ci - mrb->c->cibase;
|
|
e->stack = mrb->c->stack;
|
|
|
|
return e;
|
|
}
|
|
|
|
static void
|
|
closure_setup(mrb_state *mrb, struct RProc *p, int nlocals)
|
|
{
|
|
struct REnv *e;
|
|
|
|
if (!mrb->c->ci->env) {
|
|
e = env_new(mrb, nlocals);
|
|
mrb->c->ci->env = e;
|
|
}
|
|
else {
|
|
e = mrb->c->ci->env;
|
|
}
|
|
p->env = e;
|
|
}
|
|
|
|
struct RProc *
|
|
mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
|
|
{
|
|
struct RProc *p = mrb_proc_new(mrb, irep);
|
|
|
|
closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals);
|
|
return p;
|
|
}
|
|
|
|
MRB_API struct RProc *
|
|
mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
|
|
{
|
|
struct RProc *p;
|
|
|
|
p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
|
|
p->body.func = func;
|
|
p->flags |= MRB_PROC_CFUNC;
|
|
p->env = 0;
|
|
|
|
return p;
|
|
}
|
|
|
|
MRB_API struct RProc *
|
|
mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
|
|
{
|
|
struct RProc *p = mrb_proc_new_cfunc(mrb, func);
|
|
struct REnv *e;
|
|
int i;
|
|
|
|
p->env = e = env_new(mrb, argc);
|
|
mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
|
|
MRB_ENV_UNSHARE_STACK(e);
|
|
e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
|
|
if (argv) {
|
|
for (i = 0; i < argc; ++i) {
|
|
e->stack[i] = argv[i];
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < argc; ++i) {
|
|
SET_NIL_VALUE(e->stack[i]);
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
MRB_API struct RProc *
|
|
mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
|
|
{
|
|
return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
|
|
}
|
|
|
|
MRB_API mrb_value
|
|
mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
|
|
{
|
|
struct RProc *p = mrb->c->ci->proc;
|
|
struct REnv *e = p->env;
|
|
|
|
if (!MRB_PROC_CFUNC_P(p)) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
|
|
}
|
|
if (!e) {
|
|
mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
|
|
}
|
|
if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
|
|
mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
|
|
mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
|
|
}
|
|
|
|
return e->stack[idx];
|
|
}
|
|
|
|
MRB_API void
|
|
mrb_proc_copy(struct RProc *a, struct RProc *b)
|
|
{
|
|
a->flags = b->flags;
|
|
a->body = b->body;
|
|
if (!MRB_PROC_CFUNC_P(a)) {
|
|
a->body.irep->refcnt++;
|
|
}
|
|
a->target_class = b->target_class;
|
|
a->env = b->env;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_proc_initialize(mrb_state *mrb, mrb_value self)
|
|
{
|
|
mrb_value blk;
|
|
|
|
mrb_get_args(mrb, "&", &blk);
|
|
if (mrb_nil_p(blk)) {
|
|
/* Calling Proc.new without a block is not implemented yet */
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
|
|
}
|
|
else {
|
|
mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(blk));
|
|
}
|
|
return self;
|
|
}
|
|
|
|
static mrb_value
|
|
mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
|
|
{
|
|
mrb_value proc;
|
|
|
|
mrb_get_args(mrb, "o", &proc);
|
|
if (mrb_type(proc) != MRB_TT_PROC) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
|
|
}
|
|
mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc));
|
|
return self;
|
|
}
|
|
|
|
int
|
|
mrb_proc_cfunc_p(struct RProc *p)
|
|
{
|
|
return MRB_PROC_CFUNC_P(p);
|
|
}
|
|
|
|
mrb_value
|
|
mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self)
|
|
{
|
|
return (p->body.func)(mrb, self);
|
|
}
|
|
|
|
mrb_code*
|
|
mrb_proc_iseq(mrb_state *mrb, struct RProc *p)
|
|
{
|
|
return p->body.irep->iseq;
|
|
}
|
|
|
|
/* 15.2.17.4.2 */
|
|
static mrb_value
|
|
mrb_proc_arity(mrb_state *mrb, mrb_value self)
|
|
{
|
|
struct RProc *p = mrb_proc_ptr(self);
|
|
mrb_code *iseq = mrb_proc_iseq(mrb, p);
|
|
mrb_aspec aspec;
|
|
int ma, op, ra, pa, arity;
|
|
|
|
if (MRB_PROC_CFUNC_P(p)) {
|
|
/* TODO cfunc aspec not implemented yet */
|
|
return mrb_fixnum_value(-1);
|
|
}
|
|
|
|
/* arity is depend on OP_ENTER */
|
|
if (GET_OPCODE(*iseq) != OP_ENTER) {
|
|
return mrb_fixnum_value(0);
|
|
}
|
|
|
|
aspec = GETARG_Ax(*iseq);
|
|
ma = MRB_ASPEC_REQ(aspec);
|
|
op = MRB_ASPEC_OPT(aspec);
|
|
ra = MRB_ASPEC_REST(aspec);
|
|
pa = MRB_ASPEC_POST(aspec);
|
|
arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
|
|
|
|
return mrb_fixnum_value(arity);
|
|
}
|
|
|
|
/* 15.3.1.2.6 */
|
|
/* 15.3.1.3.27 */
|
|
/*
|
|
* call-seq:
|
|
* lambda { |...| block } -> a_proc
|
|
*
|
|
* Equivalent to <code>Proc.new</code>, except the resulting Proc objects
|
|
* check the number of parameters passed when called.
|
|
*/
|
|
static mrb_value
|
|
proc_lambda(mrb_state *mrb, mrb_value self)
|
|
{
|
|
mrb_value blk;
|
|
struct RProc *p;
|
|
|
|
mrb_get_args(mrb, "&", &blk);
|
|
if (mrb_nil_p(blk)) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
|
|
}
|
|
if (mrb_type(blk) != MRB_TT_PROC) {
|
|
mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
|
|
}
|
|
p = mrb_proc_ptr(blk);
|
|
if (!MRB_PROC_STRICT_P(p)) {
|
|
struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c);
|
|
mrb_proc_copy(p2, p);
|
|
p2->flags |= MRB_PROC_STRICT;
|
|
return mrb_obj_value(p2);
|
|
}
|
|
return blk;
|
|
}
|
|
|
|
void
|
|
mrb_init_proc(mrb_state *mrb)
|
|
{
|
|
struct RProc *m;
|
|
mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
|
|
static const mrb_irep mrb_irep_zero = { 0 };
|
|
|
|
*call_irep = mrb_irep_zero;
|
|
call_irep->flags = MRB_ISEQ_NO_FREE;
|
|
call_irep->iseq = call_iseq;
|
|
call_irep->ilen = 1;
|
|
|
|
mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE());
|
|
mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
|
|
mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
|
|
|
|
m = mrb_proc_new(mrb, call_irep);
|
|
mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
|
|
mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
|
|
|
|
mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6 */
|
|
mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */
|
|
}
|