summaryrefslogtreecommitdiff
path: root/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c737
1 files changed, 499 insertions, 238 deletions
diff --git a/eval.c b/eval.c
index ae2ce62da9..12baccb2d7 100644
--- a/eval.c
+++ b/eval.c
@@ -6,7 +6,7 @@
$Date$
created at: Thu Jun 10 14:22:17 JST 1993
- Copyright (C) 1993-2000 Yukihiro Matsumoto
+ Copyright (C) 1993-2001 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -22,6 +22,25 @@
#include "st.h"
#include "dln.h"
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+# ifndef atarist
+# ifndef alloca
+# define alloca __builtin_alloca
+# endif
+# endif /* atarist */
+#else
+# if defined(HAVE_ALLOCA_H)
+# include <alloca.h>
+# elif !defined(alloca)
+char *alloca();
+# endif
+#endif /* __GNUC__ */
+
+#ifdef _AIX
+#pragma alloca
+#endif
+
#ifdef HAVE_STDARG_PROTOTYPES
#include <stdarg.h>
#define va_init_list(a,b) va_start(a,b)
@@ -109,33 +128,8 @@ int ruby_safe_level = 0;
4 - no global (non-tainted) variable modification/no direct output
*/
-void
-rb_set_safe_level(level)
- int level;
-{
- if (level > ruby_safe_level) {
- ruby_safe_level = level;
- }
-}
-
-static VALUE
-safe_getter()
-{
- return INT2FIX(ruby_safe_level);
-}
-
-static void
-safe_setter(val)
- VALUE val;
-{
- int level = NUM2INT(val);
-
- if (level < ruby_safe_level) {
- rb_raise(rb_eSecurityError, "tried to downgrade safe level from %d to %d",
- ruby_safe_level, level);
- }
- ruby_safe_level = level;
-}
+static VALUE safe_getter _((void));
+static void safe_setter _((VALUE val));
void
rb_secure(level)
@@ -483,9 +477,9 @@ rb_attr(klass, id, read, write, ex)
rb_clear_cache_by_id(id);
rb_funcall(klass, added, 1, ID2SYM(id));
}
- sprintf(buf, "%s=", name);
- id = rb_intern(buf);
if (write) {
+ sprintf(buf, "%s=", name);
+ id = rb_intern(buf);
rb_add_method(klass, id, NEW_ATTRSET(attriv), noex);
rb_clear_cache_by_id(id);
rb_funcall(klass, added, 1, ID2SYM(id));
@@ -527,14 +521,20 @@ static struct SCOPE *top_scope;
ruby_sourceline = _frame.line; \
ruby_frame = _frame.prev; }
+struct BLOCKTAG {
+ struct RBasic super;
+ long dst;
+ long flags;
+};
+
struct BLOCK {
NODE *var;
NODE *body;
VALUE self;
struct FRAME frame;
struct SCOPE *scope;
+ struct BLOCKTAG *tag;
VALUE klass;
- struct tag *tag;
int iter;
int vmode;
int flags;
@@ -545,12 +545,23 @@ struct BLOCK {
#define BLOCK_D_SCOPE 1
#define BLOCK_DYNAMIC 2
+#define BLOCK_ORPHAN 4
static struct BLOCK *ruby_block;
+static struct BLOCKTAG*
+new_blktag()
+{
+ NEWOBJ(blktag, struct BLOCKTAG);
+ OBJSETUP(blktag, 0, T_BLKTAG);
+ blktag->dst = 0;
+ blktag->flags = 0;
+ return blktag;
+}
+
#define PUSH_BLOCK(v,b) { \
struct BLOCK _block; \
- _block.tag = prot_tag; \
+ _block.tag = new_blktag(); \
_block.var = v; \
_block.body = b; \
_block.self = self; \
@@ -566,19 +577,18 @@ static struct BLOCK *ruby_block;
_block.dyna_vars = ruby_dyna_vars; \
ruby_block = &_block;
+#define POP_BLOCK_TAG(tag) do { \
+ if ((tag)->flags & BLOCK_DYNAMIC) \
+ (tag)->flags |= BLOCK_ORPHAN; \
+ else \
+ rb_gc_force_recycle((VALUE)tag); \
+} while (0)
+
#define POP_BLOCK() \
+ POP_BLOCK_TAG(_block.tag); \
ruby_block = _block.prev; \
}
-#define PUSH_BLOCK2(b) { \
- struct BLOCK * volatile _old; \
- _old = ruby_block; \
- ruby_block = b;
-
-#define POP_BLOCK2() \
- ruby_block = _old; \
-}
-
struct RVarmap *ruby_dyna_vars;
#define PUSH_VARS() { \
struct RVarmap * volatile _old; \
@@ -586,6 +596,8 @@ struct RVarmap *ruby_dyna_vars;
ruby_dyna_vars = 0;
#define POP_VARS() \
+ if (_old && (ruby_scope->flag & SCOPE_DONT_RECYCLE)) \
+ FL_SET(_old, DVAR_DONT_RECYCLE); \
ruby_dyna_vars = _old; \
}
@@ -739,7 +751,6 @@ static struct tag *prot_tag;
_tag.frame = ruby_frame; \
_tag.iter = ruby_iter; \
_tag.prev = prot_tag; \
- _tag.retval = Qnil; \
_tag.scope = ruby_scope; \
_tag.tag = ptag; \
_tag.dst = 0; \
@@ -785,6 +796,11 @@ static VALUE ruby_wrapper; /* security wrapper */
#define POP_CLASS() ruby_class = _class; }
+static NODE *ruby_cref = 0;
+static NODE *top_cref;
+#define PUSH_CREF(c) ruby_cref = rb_node_newnode(NODE_CREF,(c),0,ruby_cref)
+#define POP_CREF() ruby_cref = ruby_cref->nd_next
+
#define PUSH_SCOPE() { \
volatile int _vmode = scope_vmode; \
struct SCOPE * volatile _old; \
@@ -1011,7 +1027,9 @@ ruby_init()
rb_call_inits();
ruby_class = rb_cObject;
ruby_frame->self = ruby_top_self;
- ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,rb_cObject,0,0);
+ top_cref = rb_node_newnode(NODE_CREF,rb_cObject,0,0);
+ ruby_cref = top_cref;
+ ruby_frame->cbase = (VALUE)ruby_cref;
rb_define_global_const("TOPLEVEL_BINDING", rb_f_binding(ruby_top_self));
#ifdef __MACOS__
_macruby_init();
@@ -1051,7 +1069,7 @@ static int
error_handle(ex)
int ex;
{
- switch (ex & 0xf) {
+ switch (ex & TAG_MASK) {
case 0:
ex = 0;
break;
@@ -1122,9 +1140,13 @@ void rb_exec_end_proc _((void));
void
ruby_finalize()
{
- rb_trap_exit();
- rb_exec_end_proc();
- rb_gc_call_finalizer_at_exit();
+ PUSH_TAG(PROT_NONE);
+ if (EXEC_TAG() == 0) {
+ rb_trap_exit();
+ rb_exec_end_proc();
+ rb_gc_call_finalizer_at_exit();
+ }
+ POP_TAG();
}
void
@@ -1235,6 +1257,7 @@ rb_eval_string_wrap(str, state)
{
int status;
VALUE self = ruby_top_self;
+ VALUE wrapper = ruby_wrapper;
VALUE val;
PUSH_CLASS();
@@ -1246,6 +1269,7 @@ rb_eval_string_wrap(str, state)
ruby_top_self = self;
POP_CLASS();
+ ruby_wrapper = wrapper;
if (state) {
*state = status;
}
@@ -1255,6 +1279,34 @@ rb_eval_string_wrap(str, state)
return val;
}
+static void
+jump_tag_but_local_jump(state)
+ int state;
+{
+ switch (state) {
+ case 0:
+ break;
+ case TAG_RETURN:
+ rb_raise(rb_eLocalJumpError, "unexpected return");
+ break;
+ case TAG_NEXT:
+ rb_raise(rb_eLocalJumpError, "unexpected next");
+ break;
+ case TAG_BREAK:
+ rb_raise(rb_eLocalJumpError, "unexpected break");
+ break;
+ case TAG_REDO:
+ rb_raise(rb_eLocalJumpError, "unexpected redo");
+ break;
+ case TAG_RETRY:
+ rb_raise(rb_eLocalJumpError, "retry outside of rescue clause");
+ break;
+ default:
+ JUMP_TAG(state);
+ break;
+ }
+}
+
VALUE
rb_eval_cmd(cmd, arg)
VALUE cmd, arg;
@@ -1290,28 +1342,7 @@ rb_eval_cmd(cmd, arg)
POP_TAG();
POP_CLASS();
- switch (state) {
- case 0:
- break;
- case TAG_RETURN:
- rb_raise(rb_eLocalJumpError, "unexpected return");
- break;
- case TAG_NEXT:
- rb_raise(rb_eLocalJumpError, "unexpected next");
- break;
- case TAG_BREAK:
- rb_raise(rb_eLocalJumpError, "unexpected break");
- break;
- case TAG_REDO:
- rb_raise(rb_eLocalJumpError, "unexpected redo");
- break;
- case TAG_RETRY:
- rb_raise(rb_eLocalJumpError, "retry outside of rescue clause");
- break;
- default:
- JUMP_TAG(state);
- break;
- }
+ jump_tag_but_local_jump(state);
return val;
}
@@ -1373,17 +1404,18 @@ superclass(self, node)
#define ruby_cbase (RNODE(ruby_frame->cbase)->nd_clss)
static VALUE
-ev_const_defined(cref, id)
+ev_const_defined(cref, id, self)
NODE *cref;
ID id;
+ VALUE self;
{
NODE *cbase = cref;
- while (cbase && cbase->nd_clss != rb_cObject) {
+ while (cbase && cbase->nd_next) {
struct RClass *klass = RCLASS(cbase->nd_clss);
- if (klass->iv_tbl &&
- st_lookup(klass->iv_tbl, id, 0)) {
+ if (NIL_P(klass)) return rb_const_defined(CLASS_OF(self), id);
+ if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, 0)) {
return Qtrue;
}
cbase = cbase->nd_next;
@@ -1392,16 +1424,18 @@ ev_const_defined(cref, id)
}
static VALUE
-ev_const_get(cref, id)
+ev_const_get(cref, id, self)
NODE *cref;
ID id;
+ VALUE self;
{
NODE *cbase = cref;
VALUE result;
- while (cbase && cbase->nd_clss != rb_cObject) {
+ while (cbase && cbase->nd_next) {
struct RClass *klass = RCLASS(cbase->nd_clss);
+ if (NIL_P(klass)) return rb_const_get(CLASS_OF(self), id);
if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, &result)) {
return result;
}
@@ -1411,34 +1445,13 @@ ev_const_get(cref, id)
}
static VALUE
-ev_const_set(cref, id, val)
- NODE *cref;
- ID id;
- VALUE val;
-{
- NODE *cbase = cref;
-
- while (cbase && cbase->nd_clss != rb_cObject) {
- struct RClass *klass = RCLASS(cbase->nd_clss);
-
- if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, 0)) {
- st_insert(klass->iv_tbl, id, val);
- return val;
- }
- cbase = cbase->nd_next;
- }
- rb_const_assign(cbase->nd_clss, id, val);
- return val;
-}
-
-static VALUE
rb_mod_nesting()
{
NODE *cbase = RNODE(ruby_frame->cbase);
VALUE ary = rb_ary_new();
- while (cbase && cbase->nd_clss != rb_cObject) {
- rb_ary_push(ary, cbase->nd_clss);
+ while (cbase && cbase->nd_next) {
+ if (!NIL_P(cbase->nd_clss)) rb_ary_push(ary, cbase->nd_clss);
cbase = cbase->nd_next;
}
return ary;
@@ -1450,12 +1463,12 @@ rb_mod_s_constants()
NODE *cbase = RNODE(ruby_frame->cbase);
VALUE ary = rb_ary_new();
- while (cbase && cbase->nd_clss != rb_cObject) {
- rb_mod_const_at(cbase->nd_clss, ary);
+ while (cbase) {
+ if (!NIL_P(cbase->nd_clss)) rb_mod_const_at(cbase->nd_clss, ary);
cbase = cbase->nd_next;
}
- rb_mod_const_of(ruby_cbase, ary);
+ if (!NIL_P(ruby_cbase)) rb_mod_const_of(ruby_cbase, ary);
return ary;
}
@@ -1576,22 +1589,32 @@ rb_mod_alias_method(mod, newname, oldname)
return mod;
}
+static NODE*
+copy_node_scope(node, rval)
+ NODE *node;
+ VALUE rval;
+{
+ NODE *copy = rb_node_newnode(NODE_SCOPE,0,rval,node->nd_next);
+
+ if (node->nd_tbl) {
+ copy->nd_tbl = ALLOC_N(ID, node->nd_tbl[0]+1);
+ MEMCPY(copy->nd_tbl, node->nd_tbl, ID, node->nd_tbl[0]+1);
+ }
+ else {
+ copy->nd_tbl = 0;
+ }
+ return copy;
+}
+
#ifdef C_ALLOCA
# define TMP_PROTECT NODE * volatile tmp__protect_tmp=0
# define TMP_ALLOC(n) \
(tmp__protect_tmp = rb_node_newnode(NODE_ALLOCA, \
ALLOC_N(VALUE,n),tmp__protect_tmp,n), \
(void*)tmp__protect_tmp->nd_head)
-# define TMP_PROTECT_END do {\
- if (tmp__protect_tmp) {\
- rb_gc_force_recycle((VALUE)tmp__protect_tmp);\
- alloca(0);\
- }\
-} while (0)
#else
# define TMP_PROTECT typedef int foobazzz
# define TMP_ALLOC(n) ALLOCA_N(VALUE,n)
-# define TMP_PROTECT_END
#endif
#define SETUP_ARGS(anode) {\
@@ -1776,12 +1799,18 @@ is_defined(self, node, buf)
break;
case NODE_CONST:
- if (ev_const_defined(RNODE(ruby_frame->cbase), node->nd_vid)) {
+ if (ev_const_defined(RNODE(ruby_frame->cbase), node->nd_vid, self)) {
return "constant";
}
break;
case NODE_CVAR:
+ if (NIL_P(ruby_cbase)) {
+ if (rb_cvar_defined(CLASS_OF(self), node->nd_vid)) {
+ return "class variable";
+ }
+ break;
+ }
if (!FL_TEST(ruby_cbase, FL_SINGLETON)) {
if (rb_cvar_defined(ruby_cbase, node->nd_vid)) {
return "class variable";
@@ -1821,14 +1850,14 @@ is_defined(self, node, buf)
break;
case NODE_NTH_REF:
- if (rb_reg_nth_defined(node->nd_nth, MATCH_DATA)) {
+ if (RTEST(rb_reg_nth_defined(node->nd_nth, MATCH_DATA))) {
sprintf(buf, "$%d", node->nd_nth);
return buf;
}
break;
case NODE_BACK_REF:
- if (rb_reg_nth_defined(0, MATCH_DATA)) {
+ if (RTEST(rb_reg_nth_defined(0, MATCH_DATA))) {
sprintf(buf, "$%c", node->nd_nth);
return buf;
}
@@ -2207,15 +2236,14 @@ rb_eval(self, n)
case NODE_FOR:
{
iter_retry:
- PUSH_BLOCK(node->nd_var, node->nd_body);
PUSH_TAG(PROT_FUNC);
+ PUSH_BLOCK(node->nd_var, node->nd_body);
state = EXEC_TAG();
if (state == 0) {
+ PUSH_ITER(ITER_PRE);
if (nd_type(node) == NODE_ITER) {
- PUSH_ITER(ITER_PRE);
result = rb_eval(self, node->nd_iter);
- POP_ITER();
}
else {
VALUE recv;
@@ -2223,13 +2251,14 @@ rb_eval(self, n)
int line = ruby_sourceline;
_block.flags &= ~BLOCK_D_SCOPE;
+ BEGIN_CALLARGS;
recv = rb_eval(self, node->nd_iter);
- PUSH_ITER(ITER_PRE);
+ END_CALLARGS;
ruby_sourcefile = file;
ruby_sourceline = line;
result = rb_call(CLASS_OF(recv),recv,each,0,0,0);
- POP_ITER();
}
+ POP_ITER();
}
else if (_block.tag->dst == state) {
state &= TAG_MASK;
@@ -2237,8 +2266,8 @@ rb_eval(self, n)
result = prot_tag->retval;
}
}
- POP_TAG();
POP_BLOCK();
+ POP_TAG();
switch (state) {
case 0:
break;
@@ -2348,9 +2377,11 @@ rb_eval(self, n)
POP_TAG();
if (node->nd_ensr) {
VALUE retval = prot_tag->retval; /* save retval */
+ VALUE errinfo = ruby_errinfo;
rb_eval(self, node->nd_ensr);
return_value(retval);
+ ruby_errinfo = errinfo;
}
if (state) JUMP_TAG(state);
break;
@@ -2460,7 +2491,6 @@ rb_eval(self, n)
END_CALLARGS;
result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0);
- TMP_PROTECT_END;
}
break;
@@ -2474,7 +2504,6 @@ rb_eval(self, n)
END_CALLARGS;
result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1);
- TMP_PROTECT_END;
}
break;
@@ -2507,13 +2536,13 @@ rb_eval(self, n)
ruby_frame->self, ruby_frame->last_func,
argc, argv, 3);
POP_ITER();
- TMP_PROTECT_END;
}
break;
case NODE_SCOPE:
{
struct FRAME frame;
+ NODE *saved_cref = 0;
frame = *ruby_frame;
frame.tmp = ruby_frame;
@@ -2521,7 +2550,11 @@ rb_eval(self, n)
PUSH_SCOPE();
PUSH_TAG(PROT_NONE);
- if (node->nd_rval) ruby_frame->cbase = node->nd_rval;
+ if (node->nd_rval) {
+ saved_cref = ruby_cref;
+ ruby_cref = (NODE*)node->nd_rval;
+ ruby_frame->cbase = node->nd_rval;
+ }
if (node->nd_tbl) {
VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1);
*vars++ = (VALUE)node;
@@ -2539,6 +2572,8 @@ rb_eval(self, n)
POP_TAG();
POP_SCOPE();
ruby_frame = frame.tmp;
+ if (saved_cref)
+ ruby_cref = saved_cref;
if (state) JUMP_TAG(state);
}
break;
@@ -2569,7 +2604,6 @@ rb_eval(self, n)
argv[argc-1] = val;
val = rb_funcall2(recv, aset, argc, argv);
result = val;
- TMP_PROTECT_END;
}
break;
@@ -2606,10 +2640,12 @@ rb_eval(self, n)
goto again;
case NODE_OP_ASGN_OR:
- result = rb_eval(self, node->nd_head);
- if (RTEST(result)) break;
- node = node->nd_value;
- goto again;
+ if ((node->nd_aid && !rb_ivar_defined(self, node->nd_aid)) ||
+ !RTEST(result = rb_eval(self, node->nd_head))) {
+ node = node->nd_value;
+ goto again;
+ }
+ break;
case NODE_MASGN:
result = massign(self, node, rb_eval(self, node->nd_value),0);
@@ -2688,10 +2724,14 @@ rb_eval(self, n)
break;
case NODE_CONST:
- result = ev_const_get(RNODE(ruby_frame->cbase), node->nd_vid);
+ result = ev_const_get(RNODE(ruby_frame->cbase), node->nd_vid, self);
break;
case NODE_CVAR: /* normal method */
+ if (NIL_P(ruby_cbase)) {
+ result = rb_cvar_get(CLASS_OF(self), node->nd_vid);
+ break;
+ }
if (!FL_TEST(ruby_cbase, FL_SINGLETON)) {
result = rb_cvar_get(ruby_cbase, node->nd_vid);
break;
@@ -2878,7 +2918,7 @@ rb_eval(self, n)
case NODE_DEFN:
if (node->nd_defn) {
- NODE *body;
+ NODE *body, *defn;
VALUE origin;
int noex;
@@ -2920,11 +2960,13 @@ rb_eval(self, n)
if (body && origin == ruby_class && body->nd_noex & NOEX_UNDEF) {
noex |= NOEX_UNDEF;
}
- rb_add_method(ruby_class, node->nd_mid, node->nd_defn, noex);
+
+ defn = copy_node_scope(node->nd_defn, ruby_cref);
+ rb_add_method(ruby_class, node->nd_mid, defn, noex);
rb_clear_cache_by_id(node->nd_mid);
if (scope_vmode == SCOPE_MODFUNC) {
rb_add_method(rb_singleton_class(ruby_class),
- node->nd_mid, node->nd_defn, NOEX_PUBLIC);
+ node->nd_mid, defn, NOEX_PUBLIC);
rb_funcall(ruby_class, singleton_added, 1, ID2SYM(node->nd_mid));
}
if (FL_TEST(ruby_class, FL_SINGLETON)) {
@@ -2942,7 +2984,7 @@ rb_eval(self, n)
if (node->nd_defn) {
VALUE recv = rb_eval(self, node->nd_recv);
VALUE klass;
- NODE *body = 0;
+ NODE *body = 0, *defn;
if (rb_safe_level() >= 4 && !OBJ_TAINTED(recv)) {
rb_raise(rb_eSecurityError, "Insecure; can't define singleton method");
@@ -2964,7 +3006,9 @@ rb_eval(self, n)
rb_warning("redefine %s", rb_id2name(node->nd_mid));
}
}
- rb_add_method(klass, node->nd_mid, node->nd_defn,
+ defn = copy_node_scope(node->nd_defn, ruby_cref);
+ defn->nd_rval = (VALUE)ruby_cref;
+ rb_add_method(klass, node->nd_mid, defn,
NOEX_PUBLIC|(body?body->nd_noex&NOEX_UNDEF:0));
rb_clear_cache_by_id(node->nd_mid);
rb_funcall(recv, singleton_added, 1, ID2SYM(node->nd_mid));
@@ -3162,16 +3206,11 @@ module_setup(module, n)
frame.tmp = ruby_frame;
ruby_frame = &frame;
- /* fill c-ref */
- node->nd_clss = module;
- node = node->nd_body;
-
PUSH_CLASS();
ruby_class = module;
PUSH_SCOPE();
PUSH_VARS();
- if (node->nd_rval) ruby_frame->cbase = node->nd_rval;
if (node->nd_tbl) {
VALUE *vars = TMP_ALLOC(node->nd_tbl[0]+1);
*vars++ = (VALUE)node;
@@ -3184,6 +3223,8 @@ module_setup(module, n)
ruby_scope->local_tbl = 0;
}
+ PUSH_CREF(module);
+ ruby_frame->cbase = (VALUE)ruby_cref;
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
if (trace_func) {
@@ -3194,6 +3235,7 @@ module_setup(module, n)
result = rb_eval(ruby_class, node->nd_next);
}
POP_TAG();
+ POP_CREF();
POP_VARS();
POP_SCOPE();
POP_CLASS();
@@ -3203,7 +3245,6 @@ module_setup(module, n)
call_trace_func("end", file, line, 0,
ruby_frame->last_func, ruby_frame->last_class);
}
- TMP_PROTECT_END;
if (state) JUMP_TAG(state);
return result;
@@ -3467,7 +3508,7 @@ rb_yield_0(val, self, klass, acheck)
int state;
static unsigned serial = 1;
- if (!ruby_frame->iter || !ruby_block) {
+ if (!(rb_block_given_p() || rb_f_block_given_p()) || !ruby_block) {
rb_raise(rb_eLocalJumpError, "yield called out of block");
}
@@ -3489,7 +3530,7 @@ rb_yield_0(val, self, klass, acheck)
ruby_dyna_vars = block->dyna_vars;
}
ruby_class = klass?klass:block->klass;
- if (!self) self = block->self;
+ if (!klass) self = block->self;
node = block->body;
if (block->var) {
@@ -3518,6 +3559,13 @@ rb_yield_0(val, self, klass, acheck)
POP_TAG();
if (state) goto pop_state;
}
+ else {
+ /* argument adjust for proc_call etc. */
+ if (acheck && val != Qundef &&
+ TYPE(val) == T_ARRAY && RARRAY(val)->len == 1) {
+ val = RARRAY(val)->ptr[0];
+ }
+ }
PUSH_ITER(block->iter);
PUSH_TAG(PROT_NONE);
@@ -3526,7 +3574,7 @@ rb_yield_0(val, self, klass, acheck)
if (!node) {
result = Qnil;
}
- else if (nd_type(node) == NODE_CFUNC) {
+ else if (nd_type(node) == NODE_CFUNC || nd_type(node) == NODE_IFUNC) {
if (val == Qundef) val = rb_ary_new2(0);
result = (*node->nd_cfnc)(val, node->nd_tval, self);
}
@@ -3548,7 +3596,7 @@ rb_yield_0(val, self, klass, acheck)
case TAG_RETURN:
state |= (serial++ << 8);
state |= 0x10;
- block->tag->dst = state;
+ block->tag->dst = state;
break;
default:
break;
@@ -3558,26 +3606,56 @@ rb_yield_0(val, self, klass, acheck)
pop_state:
POP_ITER();
POP_CLASS();
- if ((block->flags & BLOCK_D_SCOPE) &&
+#if 0
+ if (ruby_dyna_vars && (block->flags & BLOCK_D_SCOPE) &&
+ (!(ruby_scope->flags & SCOPE_DONT_RECYCLE) ||
+ !(block->tag->flags & BLOCK_DYNAMIC) ||
+ !FL_TEST(ruby_dyna_vars, DVAR_DONT_RECYCLE))) {
+ struct RVarmap *vars, *tmp;
+
+ if (ruby_dyna_vars->id == 0) {
+ vars = ruby_dyna_vars->next;
+ rb_gc_force_recycle((VALUE)ruby_dyna_vars);
+ while (vars && vars->id != 0) {
+ tmp = vars->next;
+ rb_gc_force_recycle((VALUE)vars);
+ vars = tmp;
+ }
+ }
+ }
+#else
+ if (ruby_dyna_vars && (block->flags & BLOCK_D_SCOPE) &&
!FL_TEST(ruby_dyna_vars, DVAR_DONT_RECYCLE)) {
struct RVarmap *vars = ruby_dyna_vars;
- while (vars && vars->id != 0) {
- struct RVarmap *tmp = vars->next;
- rb_gc_force_recycle((VALUE)vars);
- vars = tmp;
- }
- if (ruby_dyna_vars && ruby_dyna_vars->id == 0) {
+ if (ruby_dyna_vars->id == 0) {
+ vars = ruby_dyna_vars->next;
rb_gc_force_recycle((VALUE)ruby_dyna_vars);
+ while (vars && vars->id != 0) {
+ struct RVarmap *tmp = vars->next;
+ rb_gc_force_recycle((VALUE)vars);
+ vars = tmp;
+ }
}
}
+#endif
POP_VARS();
ruby_block = block;
ruby_frame = ruby_frame->prev;
if (ruby_scope->flag & SCOPE_DONT_RECYCLE)
scope_dup(old_scope);
ruby_scope = old_scope;
- if (state) JUMP_TAG(state);
+ if (state) {
+ if (!block->tag) {
+ switch (state & TAG_MASK) {
+ case TAG_BREAK:
+ case TAG_RETURN:
+ jump_tag_but_local_jump(state & TAG_MASK);
+ break;
+ }
+ }
+ JUMP_TAG(state);
+ }
return result;
}
@@ -3744,7 +3822,7 @@ rb_iterate(it_proc, data1, bl_proc, data2)
{
int state;
volatile VALUE retval = Qnil;
- NODE *node = NEW_CFUNC(bl_proc, data2);
+ NODE *node = NEW_IFUNC(bl_proc, data2);
VALUE self = ruby_top_self;
iter_retry:
@@ -3809,7 +3887,6 @@ handle_rescue(self, node)
if (rb_obj_is_kind_of(ruby_errinfo, argv[0])) return 1;
argv++;
}
- TMP_PROTECT_END;
return 0;
}
@@ -3921,9 +3998,9 @@ rb_ensure(b_proc, data1, e_proc, data2)
result = (*b_proc)(data1);
}
POP_TAG();
- retval = prot_tag->retval; /* save retval */
+ retval = prot_tag ? prot_tag->retval : Qnil; /* save retval */
(*e_proc)(data2);
- return_value(retval);
+ if (prot_tag) return_value(retval);
if (state) JUMP_TAG(state);
return result;
@@ -4053,6 +4130,7 @@ static int STACK_LEVEL_MAX = 65535;
#ifdef __human68k__
extern int _stacksize;
# define STACK_LEVEL_MAX (_stacksize - 4096)
+#undef HAVE_GETRLIMIT
#else
#ifdef HAVE_GETRLIMIT
static int STACK_LEVEL_MAX = 655300;
@@ -4072,7 +4150,7 @@ stack_length(p)
alloca(0);
# define STACK_END (&stack_end)
#else
-# if defined(__GNUC__) && !defined(__alpha__) && !defined(__APPLE__)
+# if defined(__GNUC__) && (defined(__i386__) || defined(__m68k__))
VALUE *stack_end = __builtin_frame_address(0);
# else
VALUE *stack_end = alloca(1);
@@ -4081,7 +4159,7 @@ stack_length(p)
#endif
if (p) *p = STACK_END;
-#ifdef sparc
+#ifdef __sparc__
return rb_gc_stack_start - STACK_END + 0x80;
#else
return (STACK_END < rb_gc_stack_start) ? rb_gc_stack_start - STACK_END
@@ -4266,14 +4344,19 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper)
result = proc_call(body->nd_cval, rb_ary_new4(argc, argv));
break;
- default:
+ case NODE_SCOPE:
{
int state;
VALUE *local_vars; /* OK */
+ NODE *saved_cref = 0;
PUSH_SCOPE();
- if (body->nd_rval) ruby_frame->cbase = body->nd_rval;
+ if (body->nd_rval) {
+ saved_cref = ruby_cref;
+ ruby_cref = (NODE*)body->nd_rval;
+ ruby_frame->cbase = body->nd_rval;
+ }
if (body->nd_tbl) {
local_vars = TMP_ALLOC(body->nd_tbl[0]+1);
*local_vars++ = (VALUE)body;
@@ -4345,10 +4428,13 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper)
rb_eval(recv, opt);
}
if (node->nd_rest >= 0) {
+ VALUE v;
+
if (argc > 0)
- local_vars[node->nd_rest]=rb_ary_new4(argc,argv);
+ v = rb_ary_new4(argc,argv);
else
- local_vars[node->nd_rest]=rb_ary_new2(0);
+ v = rb_ary_new2(0);
+ local_vars[node->nd_rest] = v;
}
}
}
@@ -4366,6 +4452,7 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper)
POP_TAG();
POP_VARS();
POP_SCOPE();
+ ruby_cref = saved_cref;
if (trace_func) {
char *file = ruby_frame->prev->file;
int line = ruby_frame->prev->line;
@@ -4379,27 +4466,24 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper)
case 0:
break;
- case TAG_NEXT:
- rb_raise(rb_eLocalJumpError, "unexpected next");
- break;
- case TAG_BREAK:
- rb_raise(rb_eLocalJumpError, "unexpected break");
- break;
- case TAG_REDO:
- rb_raise(rb_eLocalJumpError, "unexpected redo");
- break;
case TAG_RETRY:
- if (!rb_block_given_p()) {
- rb_raise(rb_eLocalJumpError, "retry outside of rescue clause");
+ if (rb_block_given_p()) {
+ JUMP_TAG(state);
}
+ /* fall through */
default:
- JUMP_TAG(state);
+ jump_tag_but_local_jump(state);
+ break;
}
}
+ break;
+
+ default:
+ rb_bug("unknown node type %d", nd_type(body));
+ break;
}
POP_FRAME();
POP_ITER();
- TMP_PROTECT_END;
return result;
}
@@ -4416,6 +4500,9 @@ rb_call(klass, recv, mid, argc, argv, scope)
ID id = mid;
struct cache_entry *ent;
+ if (!klass) {
+ rb_raise(rb_eNotImpError, "method call on terminated object");
+ }
/* is it in the method cache? */
ent = cache + EXPR1(klass, mid);
if (ent->mid == mid && ent->klass == klass) {
@@ -4683,6 +4770,7 @@ eval(self, src, scope, file, line)
struct SCOPE * volatile old_scope;
struct BLOCK * volatile old_block;
struct RVarmap * volatile old_dyna_vars;
+ VALUE volatile old_cref;
int volatile old_vmode;
struct FRAME frame;
char *filesave = ruby_sourcefile;
@@ -4701,7 +4789,6 @@ eval(self, src, scope, file, line)
}
Data_Get_Struct(scope, struct BLOCK, data);
-
/* PUSH BLOCK from data */
frame = data->frame;
frame.tmp = ruby_frame; /* gc protection */
@@ -4714,6 +4801,8 @@ eval(self, src, scope, file, line)
ruby_dyna_vars = data->dyna_vars;
old_vmode = scope_vmode;
scope_vmode = data->vmode;
+ old_cref = (VALUE)ruby_cref;
+ ruby_cref = (NODE*)ruby_frame->cbase;
self = data->self;
ruby_frame->iter = data->iter;
@@ -4747,14 +4836,34 @@ eval(self, src, scope, file, line)
POP_CLASS();
ruby_in_eval--;
if (!NIL_P(scope)) {
+ int dont_recycle = ruby_scope->flag & SCOPE_DONT_RECYCLE;
+
+ ruby_cref = (NODE*)old_cref;
ruby_frame = frame.tmp;
- if (ruby_scope->flag & SCOPE_DONT_RECYCLE)
- scope_dup(old_scope);
ruby_scope = old_scope;
ruby_block = old_block;
ruby_dyna_vars = old_dyna_vars;
data->vmode = scope_vmode; /* write back visibility mode */
scope_vmode = old_vmode;
+ if (dont_recycle) {
+ struct tag *tag;
+ struct RVarmap *vars;
+
+ scope_dup(ruby_scope);
+ for (tag=prot_tag; tag; tag=tag->prev) {
+ scope_dup(tag->scope);
+ }
+ if (ruby_block) {
+ struct BLOCK *block = ruby_block;
+ while (block) {
+ block->tag->flags |= BLOCK_DYNAMIC;
+ block = block->prev;
+ }
+ }
+ for (vars = ruby_dyna_vars; vars; vars = vars->next) {
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
}
else {
ruby_frame->iter = iter;
@@ -4849,6 +4958,7 @@ exec_under(func, under, args)
if (ruby_cbase != under) {
ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,under,0,ruby_frame->cbase);
}
+ PUSH_CREF(under);
mode = scope_vmode;
SCOPE_SET(SCOPE_PUBLIC);
@@ -4857,6 +4967,7 @@ exec_under(func, under, args)
val = (*func)(args);
}
POP_TAG();
+ POP_CREF();
SCOPE_SET(mode);
POP_FRAME();
POP_CLASS();
@@ -4932,8 +5043,6 @@ static VALUE
yield_under(under, self)
VALUE under, self;
{
- if (rb_safe_level() >= 4 && !OBJ_TAINTED(self))
- rb_raise(rb_eSecurityError, "Insecure: can't eval");
return exec_under(yield_under_i, under, self);
}
@@ -5014,6 +5123,7 @@ rb_load(fname, wrap)
volatile ID last_func;
volatile VALUE wrapper = 0;
volatile VALUE self = ruby_top_self;
+ NODE *saved_cref = ruby_cref;
TMP_PROTECT;
if (wrap) {
@@ -5031,6 +5141,7 @@ rb_load(fname, wrap)
PUSH_VARS();
PUSH_CLASS();
wrapper = ruby_wrapper;
+ ruby_cref = top_cref;
if (!wrap) {
rb_secure(4); /* should alter global state */
ruby_class = rb_cObject;
@@ -5041,6 +5152,7 @@ rb_load(fname, wrap)
ruby_class = ruby_wrapper = rb_module_new();
self = rb_obj_clone(ruby_top_self);
rb_extend_object(self, ruby_class);
+ PUSH_CREF(ruby_wrapper);
}
PUSH_FRAME();
ruby_frame->last_func = 0;
@@ -5083,6 +5195,7 @@ rb_load(fname, wrap)
free(ruby_scope->local_tbl);
}
POP_TAG();
+ ruby_cref = saved_cref;
POP_SCOPE();
POP_FRAME();
POP_CLASS();
@@ -5092,8 +5205,7 @@ rb_load(fname, wrap)
ruby_nerrs = 0;
rb_exc_raise(ruby_errinfo);
}
- TMP_PROTECT_END;
- if (state) JUMP_TAG(state);
+ if (state) jump_tag_but_local_jump(state);
if (!NIL_P(ruby_errinfo)) /* exception during load */
rb_exc_raise(ruby_errinfo);
}
@@ -5126,6 +5238,7 @@ rb_f_load(argc, argv)
return Qtrue;
}
+VALUE ruby_dln_librefs;
static VALUE rb_features;
static st_table *loading_tbl;
@@ -5167,7 +5280,7 @@ rb_feature_p(feature, wait)
while (st_lookup(loading_tbl, f, &th)) {
if (th == curr_thread) {
- rb_raise(rb_eLoadError, "infinite load loop -- %s", f);
+ return Qtrue;
}
CHECK_INTS;
rb_thread_schedule();
@@ -5293,9 +5406,12 @@ rb_f_require(obj, fname)
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
+ void *handle;
+
load = rb_str_new2(file);
file = RSTRING(load)->ptr;
- dln_load(file);
+ handle = dln_load(file);
+ rb_ary_push(ruby_dln_librefs, INT2NUM((long)handle));
}
POP_TAG();
if (state) JUMP_TAG(state);
@@ -5333,6 +5449,15 @@ rb_require(fname)
}
static void
+secure_visibility(self)
+ VALUE self;
+{
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't change method visibility");
+ }
+}
+
+static void
set_method_visibility(self, argc, argv, ex)
VALUE self;
int argc;
@@ -5341,6 +5466,7 @@ set_method_visibility(self, argc, argv, ex)
{
int i;
+ secure_visibility(self);
for (i=0; i<argc; i++) {
rb_export_method(self, rb_to_id(argv[i]), ex);
}
@@ -5352,6 +5478,7 @@ rb_mod_public(argc, argv, module)
VALUE *argv;
VALUE module;
{
+ secure_visibility(module);
if (argc == 0) {
SCOPE_SET(SCOPE_PUBLIC);
}
@@ -5367,6 +5494,7 @@ rb_mod_protected(argc, argv, module)
VALUE *argv;
VALUE module;
{
+ secure_visibility(module);
if (argc == 0) {
SCOPE_SET(SCOPE_PROTECTED);
}
@@ -5382,6 +5510,7 @@ rb_mod_private(argc, argv, module)
VALUE *argv;
VALUE module;
{
+ secure_visibility(module);
if (argc == 0) {
SCOPE_SET(SCOPE_PRIVATE);
}
@@ -5441,6 +5570,7 @@ rb_mod_modfunc(argc, argv, module)
rb_raise(rb_eTypeError, "module_function must be called for modules");
}
+ secure_visibility(module);
if (argc == 0) {
SCOPE_SET(SCOPE_MODFUNC);
return module;
@@ -5849,6 +5979,9 @@ Init_load()
rb_define_global_function("require", rb_f_require, 1);
rb_define_global_function("autoload", rb_f_autoload, 2);
rb_global_variable(&ruby_wrapper);
+
+ ruby_dln_librefs = rb_ary_new();
+ rb_global_variable(&ruby_dln_librefs);
}
static void
@@ -5883,6 +6016,7 @@ blk_mark(data)
rb_gc_mark(data->self);
rb_gc_mark(data->dyna_vars);
rb_gc_mark(data->klass);
+ rb_gc_mark(data->tag);
data = data->prev;
}
}
@@ -5916,6 +6050,7 @@ blk_copy_prev(block)
struct BLOCK *block;
{
struct BLOCK *tmp;
+ struct RVarmap* vars;
while (block->prev) {
tmp = ALLOC_N(struct BLOCK, 1);
@@ -5925,6 +6060,13 @@ blk_copy_prev(block)
MEMCPY(tmp->frame.argv, block->prev->frame.argv, VALUE, tmp->frame.argc);
}
scope_dup(tmp->scope);
+ tmp->tag->flags |= BLOCK_DYNAMIC;
+
+ for (vars = tmp->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+
block->prev = tmp;
block = tmp;
}
@@ -5980,7 +6122,7 @@ static VALUE
rb_f_binding(self)
VALUE self;
{
- struct BLOCK *data;
+ struct BLOCK *data, *p;
struct RVarmap *vars;
VALUE bind;
@@ -6002,10 +6144,14 @@ rb_f_binding(self)
else {
data->prev = 0;
}
+ data->flags |= BLOCK_DYNAMIC;
+ data->tag->flags |= BLOCK_DYNAMIC;
- for (vars = data->dyna_vars; vars; vars = vars->next) {
- if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
- FL_SET(vars, DVAR_DONT_RECYCLE);
+ for (p = data; p; p = p->prev) {
+ for (vars = p->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
}
scope_dup(data->scope);
POP_BLOCK();
@@ -6063,11 +6209,11 @@ proc_new(klass)
VALUE klass;
{
volatile VALUE proc;
- struct BLOCK *data;
+ struct BLOCK *data, *p;
struct RVarmap *vars;
if (!rb_block_given_p() && !rb_f_block_given_p()) {
- rb_raise(rb_eArgError, "tried to create Procedure-Object without a block");
+ rb_raise(rb_eArgError, "tried to create Proc object without a block");
}
proc = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data);
@@ -6075,7 +6221,6 @@ proc_new(klass)
data->orig_thread = rb_thread_current();
data->iter = data->prev?Qtrue:Qfalse;
- data->tag = 0; /* should not point into stack */
frame_dup(&data->frame);
if (data->iter) {
blk_copy_prev(data);
@@ -6084,10 +6229,13 @@ proc_new(klass)
data->prev = 0;
}
data->flags |= BLOCK_DYNAMIC;
+ data->tag->flags |= BLOCK_DYNAMIC;
- for (vars = data->dyna_vars; vars; vars = vars->next) {
- if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
- FL_SET(vars, DVAR_DONT_RECYCLE);
+ for (p = data; p; p = p->prev) {
+ for (vars = p->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
}
scope_dup(data->scope);
proc_save_safe_level(proc);
@@ -6165,6 +6313,8 @@ proc_call(proc, args)
old_block = ruby_block;
_block = *data;
ruby_block = &_block;
+ ruby_block->frame.iter = ITER_NOT;
+
PUSH_ITER(ITER_CUR);
ruby_frame->iter = ITER_CUR;
@@ -6172,17 +6322,7 @@ proc_call(proc, args)
args = callargs(args);
}
- if (orphan) {/* orphan procedure */
- if (rb_block_given_p()) {
- ruby_block->frame.iter = ITER_CUR;
- }
- else {
- ruby_block->frame.iter = ITER_NOT;
- }
- }
-
PUSH_TAG(PROT_NONE);
- _block.tag = prot_tag;
state = EXEC_TAG();
if (state == 0) {
proc_set_safe_level(proc);
@@ -6243,6 +6383,39 @@ proc_arity(proc)
}
static VALUE
+proc_eq(self, other)
+ VALUE self, other;
+{
+ struct BLOCK *data, *data2;
+
+ if (self == other) return Qtrue;
+ if (TYPE(other) != T_DATA) return Qfalse;
+ if (RDATA(other)->dmark != (RUBY_DATA_FUNC)blk_mark) return Qfalse;
+ if (CLASS_OF(self) != CLASS_OF(other)) return Qfalse;
+ Data_Get_Struct(self, struct BLOCK, data);
+ Data_Get_Struct(other, struct BLOCK, data2);
+ if (data->tag == data2->tag) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+proc_to_s(self, other)
+ VALUE self, other;
+{
+ struct BLOCK *data;
+ char *cname = rb_class2name(CLASS_OF(self));
+ VALUE str;
+
+ Data_Get_Struct(self, struct BLOCK, data);
+ str = rb_str_new(0, strlen(cname)+6+16+1); /* 6:tags 16:addr 1:eos */
+ sprintf(RSTRING(str)->ptr, "#<%s:0x%lx>", cname, data->tag);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ if (OBJ_TAINTED(self)) OBJ_TAINT(str);
+
+ return str;
+}
+
+static VALUE
block_pass(self, node)
VALUE self;
NODE *node;
@@ -6278,10 +6451,11 @@ block_pass(self, node)
ruby_frame->iter = ITER_PRE;
PUSH_TAG(PROT_NONE);
- _block.tag = prot_tag;
state = EXEC_TAG();
if (state == 0) {
proc_set_safe_level(block);
+ if (safe > ruby_safe_level)
+ ruby_safe_level = safe;
result = rb_eval(self, node->nd_iter);
}
POP_TAG();
@@ -6300,25 +6474,33 @@ block_pass(self, node)
}
ptr = ptr->prev;
}
+ if (!ptr) {
+ state &= TAG_MASK;
+ }
}
}
ruby_block = old_block;
ruby_safe_level = safe;
- if (state) {
- switch (state) {/* escape from orphan procedure */
- case TAG_BREAK:
+ switch (state) {/* escape from orphan procedure */
+ case 0:
+ break;
+ case TAG_BREAK:
+ if (orphan) {
rb_raise(rb_eLocalJumpError, "break from proc-closure");
- break;
- case TAG_RETRY:
- rb_raise(rb_eLocalJumpError, "retry from proc-closure");
- break;
- case TAG_RETURN:
+ }
+ break;
+ case TAG_RETRY:
+ rb_raise(rb_eLocalJumpError, "retry from proc-closure");
+ break;
+ case TAG_RETURN:
+ if (orphan) {
rb_raise(rb_eLocalJumpError, "return from proc-closure");
- break;
}
+ default:
JUMP_TAG(state);
}
+
return result;
}
@@ -6445,8 +6627,8 @@ method_call(argc, argv, method)
Data_Get_Struct(method, struct METHOD, data);
PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
PUSH_TAG(PROT_NONE);
- if (OBJ_TAINTED(method)) {
- if (ruby_safe_level < 4) ruby_safe_level = 4;
+ if (OBJ_TAINTED(method) && ruby_safe_level < 4) {
+ ruby_safe_level = 4;
}
if ((state = EXEC_TAG()) == 0) {
result = rb_call0(data->klass,data->recv,data->id,argc,argv,data->body,0);
@@ -6611,7 +6793,7 @@ rb_mod_define_method(argc, argv, mod)
VALUE mod;
{
ID id;
- VALUE name, body;
+ VALUE body;
if (argc == 1) {
id = rb_to_id(argv[0]);
@@ -6626,17 +6808,20 @@ rb_mod_define_method(argc, argv, mod)
}
if (TYPE(body) != T_DATA) {
/* type error */
+ rb_raise(rb_eTypeError, "wrong argument type (expected Proc)");
}
if (RDATA(body)->dmark == (RUBY_DATA_FUNC)bm_mark) {
rb_add_method(mod, id, NEW_DMETHOD(method_unbind(body)), NOEX_PUBLIC);
}
- else if (RDATA(body)->dmark != (RUBY_DATA_FUNC)blk_mark) {
+ else if (RDATA(body)->dmark == (RUBY_DATA_FUNC)blk_mark) {
rb_add_method(mod, id, NEW_BMETHOD(body), NOEX_PUBLIC);
}
else {
/* type error */
+ rb_raise(rb_eTypeError, "wrong argument type (expected Proc)");
}
+ rb_clear_cache_by_id(id);
return body;
}
@@ -6652,6 +6837,8 @@ Init_Proc()
rb_define_method(rb_cProc, "call", proc_call, -2);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "[]", proc_call, -2);
+ rb_define_method(rb_cProc, "==", proc_eq, 1);
+ rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
rb_define_global_function("proc", rb_f_lambda, 0);
rb_define_global_function("lambda", rb_f_lambda, 0);
rb_define_global_function("binding", rb_f_binding, 0);
@@ -6725,6 +6912,7 @@ struct thread {
struct tag *tag;
VALUE klass;
VALUE wrapper;
+ NODE *cref;
int flags; /* misc. states (vmode/rb_trap_immediate/raised) */
@@ -6758,7 +6946,9 @@ struct thread {
VALUE thread;
};
-#define THREAD_RAISED 0x200
+#define THREAD_RAISED 0x200 /* temporary flag */
+#define THREAD_TERMINATING 0x400 /* persistent flag */
+#define THREAD_FLAGS_MASK 0x400 /* mask for persistent flags */
#define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next;
#define END_FOREACH_FROM(f,x) } while (x != f)
@@ -6766,6 +6956,37 @@ struct thread {
#define FOREACH_THREAD(x) FOREACH_THREAD_FROM(curr_thread,x)
#define END_FOREACH(x) END_FOREACH_FROM(curr_thread,x)
+/* $SAFE accessor */
+void
+rb_set_safe_level(level)
+ int level;
+{
+ if (level > ruby_safe_level) {
+ ruby_safe_level = level;
+ curr_thread->safe = level;
+ }
+}
+
+static VALUE
+safe_getter()
+{
+ return INT2NUM(ruby_safe_level);
+}
+
+static void
+safe_setter(val)
+ VALUE val;
+{
+ int level = NUM2INT(val);
+
+ if (level < ruby_safe_level) {
+ rb_raise(rb_eSecurityError, "tried to downgrade safe level from %d to %d",
+ ruby_safe_level, level);
+ }
+ ruby_safe_level = level;
+ curr_thread->safe = level;
+}
+
/* Return the current time as a floating-point number */
static double
timeofday()
@@ -6791,6 +7012,7 @@ thread_mark(th)
rb_gc_mark(th->klass);
rb_gc_mark(th->wrapper);
+ rb_gc_mark(th->cref);
rb_gc_mark(th->scope);
rb_gc_mark(th->dyna_vars);
@@ -6868,6 +7090,8 @@ rb_thread_check(data)
return (rb_thread_t)RDATA(data)->data;
}
+static VALUE rb_thread_raise _((int, VALUE*, rb_thread_t));
+
static int th_raise_argc;
static VALUE th_raise_argv[2];
static char *th_raise_file;
@@ -6906,9 +7130,11 @@ rb_thread_save_context(th)
th->scope = ruby_scope;
th->klass = ruby_class;
th->wrapper = ruby_wrapper;
+ th->cref = ruby_cref;
th->dyna_vars = ruby_dyna_vars;
th->block = ruby_block;
- th->flags = scope_vmode | (rb_trap_immediate<<8);
+ th->flags &= THREAD_FLAGS_MASK;
+ th->flags |= (rb_trap_immediate<<8) | scope_vmode;
th->iter = ruby_iter;
th->tag = prot_tag;
th->tracing = tracing;
@@ -6995,6 +7221,7 @@ rb_thread_restore_context(th, exit)
ruby_scope = th->scope;
ruby_class = th->klass;
ruby_wrapper = th->wrapper;
+ ruby_cref = th->cref;
ruby_dyna_vars = th->dyna_vars;
ruby_block = th->block;
scope_vmode = th->flags&SCOPE_MASK;
@@ -7055,13 +7282,8 @@ rb_thread_fd_close(fd)
FOREACH_THREAD(th) {
if ((th->wait_for & WAIT_FD) && fd == th->fd) {
- th_raise_argc = 1;
- th_raise_argv[0] = rb_exc_new2(rb_eIOError, "stream closed");
- th_raise_file = ruby_sourcefile;
- th_raise_line = ruby_sourceline;
- curr_thread = th;
- rb_thread_ready(th);
- rb_thread_restore_context(curr_thread, RESTORE_RAISE);
+ VALUE exc = rb_exc_new2(rb_eIOError, "stream closed");
+ rb_thread_raise(1, &exc, th);
}
}
END_FOREACH(th);
@@ -7220,6 +7442,9 @@ rb_thread_schedule()
delay = th_delay;
need_select = 1;
}
+ if (th->delay == DELAY_INFTY) {
+ need_select = 1;
+ }
}
}
END_FOREACH_FROM(curr, th);
@@ -7323,6 +7548,7 @@ rb_thread_schedule()
next->gid = 0;
rb_thread_ready(next);
next->status = THREAD_TO_KILL;
+ rb_thread_save_context(curr_thread);
rb_thread_deadlock();
}
next->wait_for = 0;
@@ -7339,8 +7565,11 @@ rb_thread_schedule()
curr_thread = next;
if (next->status == THREAD_TO_KILL) {
- /* execute ensure-clause if any */
- rb_thread_restore_context(next, RESTORE_FATAL);
+ if (!(next->flags & THREAD_TERMINATING)) {
+ next->flags |= THREAD_TERMINATING;
+ /* terminate; execute ensure-clause if any */
+ rb_thread_restore_context(next, RESTORE_FATAL);
+ }
}
rb_thread_restore_context(next, RESTORE_NORMAL);
}
@@ -7349,6 +7578,7 @@ void
rb_thread_wait_fd(fd)
int fd;
{
+ if (rb_thread_critical) return;
if (curr_thread == curr_thread->next) return;
if (curr_thread->status == THREAD_TO_KILL) return;
@@ -7362,9 +7592,9 @@ int
rb_thread_fd_writable(fd)
int fd;
{
+ if (rb_thread_critical) return Qtrue;
if (curr_thread == curr_thread->next) return Qtrue;
if (curr_thread->status == THREAD_TO_KILL) return Qtrue;
-
curr_thread->status = THREAD_STOPPED;
FD_ZERO(&curr_thread->readfds);
FD_ZERO(&curr_thread->writefds);
@@ -7382,7 +7612,8 @@ rb_thread_wait_for(time)
{
double date;
- if (curr_thread == curr_thread->next ||
+ if (rb_thread_critical ||
+ curr_thread == curr_thread->next ||
curr_thread->status == THREAD_TO_KILL) {
int n;
#ifndef linux
@@ -7394,7 +7625,14 @@ rb_thread_wait_for(time)
n = select(0, 0, 0, 0, &time);
TRAP_END;
if (n == 0) return;
-
+ if (n < 0) {
+ switch (errno) {
+ case EINTR:
+ return;
+ default:
+ rb_sys_fail("sleep");
+ }
+ }
#ifndef linux
d = limit - timeofday();
@@ -7447,7 +7685,8 @@ rb_thread_select(max, read, write, except, timeout)
(double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
}
- if (curr_thread == curr_thread->next ||
+ if (rb_thread_critical ||
+ curr_thread == curr_thread->next ||
curr_thread->status == THREAD_TO_KILL) {
#ifndef linux
struct timeval tv, *tvp = timeout;
@@ -7513,6 +7752,7 @@ rb_thread_join(thread)
rb_thread_t th = rb_thread_check(thread);
enum thread_status last_status = THREAD_RUNNABLE;
+ if (rb_thread_critical) rb_thread_deadlock();
if (!rb_thread_dead(th)) {
if (th == curr_thread)
rb_raise(rb_eThreadError, "thread tried to join itself");
@@ -7612,8 +7852,8 @@ rb_thread_kill(thread)
rb_thread_ready(th);
th->gid = 0;
th->status = THREAD_TO_KILL;
- rb_thread_schedule();
- return Qnil; /* not reached */
+ if (!rb_thread_critical) rb_thread_schedule();
+ return thread;
}
static VALUE
@@ -7780,6 +8020,7 @@ rb_thread_abort_exc_set(thread, val)
\
th->status = THREAD_RUNNABLE;\
th->result = 0;\
+ th->flags = 0;\
\
th->stk_ptr = 0;\
th->stk_len = 0;\
@@ -7795,6 +8036,7 @@ rb_thread_abort_exc_set(thread, val)
th->scope = 0;\
th->klass = 0;\
th->wrapper = 0;\
+ th->cref = ruby_cref;\
th->dyna_vars = ruby_dyna_vars;\
th->block = 0;\
th->iter = 0;\
@@ -7846,8 +8088,6 @@ catch_timer(sig)
int rb_thread_tick = THREAD_TICK;
#endif
-static VALUE rb_thread_raise _((int, VALUE*, rb_thread_t));
-
#define SCOPE_SHARED FL_USER1
#if defined(HAVE_SETITIMER)
@@ -7886,6 +8126,7 @@ rb_thread_start_0(fn, arg, th_arg)
{
volatile rb_thread_t th = th_arg;
volatile VALUE thread = th->thread;
+ struct BLOCK* saved_block = 0;
enum thread_status status;
int state;
@@ -7903,7 +8144,11 @@ rb_thread_start_0(fn, arg, th_arg)
#endif
if (ruby_block) { /* should nail down higher scopes */
- blk_copy_prev(ruby_block);
+ struct BLOCK dummy;
+
+ dummy.prev = ruby_block;
+ blk_copy_prev(&dummy);
+ saved_block = ruby_block = dummy.prev;
}
scope_dup(ruby_scope);
FL_SET(ruby_scope, SCOPE_SHARED);
@@ -7930,6 +8175,16 @@ rb_thread_start_0(fn, arg, th_arg)
}
POP_TAG();
status = th->status;
+
+ while (saved_block) {
+ struct BLOCK *tmp = saved_block;
+
+ if (tmp->frame.argc > 0)
+ free(tmp->frame.argv);
+ saved_block = tmp->prev;
+ free(tmp);
+ }
+
if (th == main_thread) ruby_stop(state);
rb_thread_remove(th);
if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) {
@@ -8018,8 +8273,6 @@ static VALUE
rb_thread_start(klass, args)
VALUE klass, args;
{
- rb_thread_t th;
-
if (!rb_block_given_p()) {
rb_raise(rb_eThreadError, "must be called with a block");
}
@@ -8366,6 +8619,14 @@ rb_callcc(self)
for (tag=prot_tag; tag; tag=tag->prev) {
scope_dup(tag->scope);
}
+ if (ruby_block) {
+ struct BLOCK *block = ruby_block;
+
+ while (block) {
+ block->tag->flags |= BLOCK_DYNAMIC;
+ block = block->prev;
+ }
+ }
th->thread = curr_thread->thread;
for (vars = th->dyna_vars; vars; vars = vars->next) {