From 65a5162550f58047974793cdc8067a970b2435c0 Mon Sep 17 00:00:00 2001 From: matz Date: Fri, 13 Aug 1999 05:45:20 +0000 Subject: 1.4.0 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@520 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- eval.c | 1591 ++++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 942 insertions(+), 649 deletions(-) (limited to 'eval.c') diff --git a/eval.c b/eval.c index a7da313fbe..4f8f9f7c9e 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-1998 Yukihiro Matsumoto + Copyright (C) 1993-1999 Yukihiro Matsumoto ************************************************/ @@ -21,7 +21,7 @@ #include "dln.h" #ifndef HAVE_STRING_H -char *strrchr _((char*,char)); +char *strrchr _((const char*,const char)); #endif #ifdef HAVE_UNISTD_H @@ -38,6 +38,10 @@ char *strrchr _((char*,char)); #include #endif +#ifdef __MACOS__ +#include "macruby_private.h" +#endif + #ifndef setjmp #ifdef HAVE__SETJMP #define setjmp(env) _setjmp(env) @@ -64,6 +68,19 @@ static int scope_vmode; #define SCOPE_SET(f) do {scope_vmode=(f);} while(0) #define SCOPE_TEST(f) (scope_vmode&(f)) +static void print_undef _((VALUE, ID)) NORETURN; +static void +print_undef(klass, id) + VALUE klass; + ID id; +{ + rb_raise(rb_eNameError, "undefined method `%s' for %s `%s'", + rb_id2name(id), + (TYPE(klass) == T_MODULE)?"module":"class", + rb_class2name(klass)); +} + + #define CACHE_SIZE 0x800 #define CACHE_MASK 0x7ff #define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK) @@ -198,14 +215,13 @@ rb_alias(klass, name, def) } } if (!orig || !orig->nd_body) { - rb_raise(rb_eNameError, "undefined method `%s' for `%s'", - rb_id2name(def), rb_class2name(klass)); + print_undef(klass, def); } body = orig->nd_body; if (nd_type(body) == NODE_FBODY) { /* was alias */ - body = body->nd_head; def = body->nd_mid; origin = body->nd_orig; + body = body->nd_head; } st_insert(RCLASS(klass)->m_tbl, name, @@ -232,7 +248,7 @@ remove_method(klass, mid) void rb_remove_method(klass, name) VALUE klass; - char *name; + const char *name; { remove_method(klass, rb_intern(name)); } @@ -240,7 +256,7 @@ rb_remove_method(klass, name) void rb_disable_super(klass, name) VALUE klass; - char *name; + const char *name; { VALUE origin; NODE *body; @@ -248,8 +264,7 @@ rb_disable_super(klass, name) body = search_method(klass, mid, &origin); if (!body || !body->nd_body) { - rb_raise(rb_eNameError, "undefined method `%s' for `%s'", - rb_id2name(mid), rb_class2name(klass)); + print_undef(klass, mid); } if (origin == klass) { body->nd_noex |= NOEX_UNDEF; @@ -263,7 +278,7 @@ rb_disable_super(klass, name) void rb_enable_super(klass, name) VALUE klass; - char *name; + const char *name; { VALUE origin; NODE *body; @@ -271,8 +286,7 @@ rb_enable_super(klass, name) body = search_method(klass, mid, &origin); if (!body || !body->nd_body || origin != klass) { - rb_raise(rb_eNameError, "undefined method `%s' for `%s'", - rb_id2name(mid), rb_class2name(klass)); + print_undef(klass, mid); } body->nd_noex &= ~NOEX_UNDEF; } @@ -294,8 +308,7 @@ rb_export_method(klass, name, noex) body = search_method(rb_cObject, name, &origin); } if (!body) { - rb_raise(rb_eNameError, "undefined method `%s' for `%s'", - rb_id2name(name), rb_class2name(klass)); + print_undef(klass, name); } if (body->nd_noex != noex) { if (klass == origin) { @@ -317,7 +330,7 @@ rb_method_boundp(klass, id, ex) int noex; if (rb_get_method_body(&klass, &id, &noex)) { - if (ex && noex & NOEX_PRIVATE) + if (ex && (noex & NOEX_PRIVATE)) return Qfalse; return Qtrue; } @@ -330,7 +343,7 @@ rb_attr(klass, id, read, write, ex) ID id; int read, write, ex; { - char *name; + const char *name; char *buf; ID attriv; int noex; @@ -366,8 +379,8 @@ rb_attr(klass, id, read, write, ex) } } -static ID init, eqq, each, aref, aset, match; -VALUE rb_errinfo = Qnil; +static ID init, eqq, each, aref, aset, match, missing; +VALUE ruby_errinfo = Qnil; extern NODE *ruby_eval_tree_begin; extern NODE *ruby_eval_tree; extern int ruby_nerrs; @@ -385,11 +398,13 @@ static struct SCOPE *top_scope; #define PUSH_FRAME() { \ struct FRAME _frame; \ _frame.prev = ruby_frame; \ + _frame.tmp = 0; \ _frame.file = ruby_sourcefile; \ _frame.line = ruby_sourceline; \ _frame.iter = ruby_iter->iter; \ _frame.cbase = ruby_frame->cbase; \ _frame.argc = 0; \ + _frame.argv = 0; \ ruby_frame = &_frame; \ #define POP_FRAME() \ @@ -407,14 +422,16 @@ struct BLOCK { struct tag *tag; int iter; int vmode; + int flags; struct RVarmap *d_vars; -#ifdef USE_THREAD VALUE orig_thread; -#endif struct BLOCK *prev; }; + +#define BLOCK_D_SCOPE 1 +#define BLOCK_DYNAMIC 2 + static struct BLOCK *ruby_block; -static struct BLOCK *ruby_calling_block; #define PUSH_BLOCK(v,b) { \ struct BLOCK _block; \ @@ -427,10 +444,11 @@ static struct BLOCK *ruby_calling_block; _block.frame.file = ruby_sourcefile;\ _block.frame.line = ruby_sourceline;\ _block.scope = ruby_scope; \ - _block.d_vars = ruby_dyna_vars; \ _block.prev = ruby_block; \ _block.iter = ruby_iter->iter; \ _block.vmode = scope_vmode; \ + _block.flags = BLOCK_D_SCOPE; \ + _block.d_vars = ruby_dyna_vars; \ ruby_block = &_block; #define POP_BLOCK() \ @@ -439,37 +457,34 @@ static struct BLOCK *ruby_calling_block; #define PUSH_BLOCK2(b) { \ struct BLOCK * volatile _old; \ - struct BLOCK * volatile _old_call; \ _old = ruby_block; \ - _old_call = ruby_calling_block; \ - ruby_calling_block = b; \ ruby_block = b; #define POP_BLOCK2() \ - ruby_calling_block = _old_call; \ ruby_block = _old; \ } struct RVarmap *ruby_dyna_vars; #define PUSH_VARS() { \ - struct RVarmap * volatile _oldvmap; \ - _oldvmap = ruby_dyna_vars; \ + struct RVarmap * volatile _old; \ + _old = ruby_dyna_vars; \ ruby_dyna_vars = 0; #define POP_VARS() \ - ruby_dyna_vars = _oldvmap; \ + ruby_dyna_vars = _old; \ } static struct RVarmap* -new_dvar(id, value) +new_dvar(id, value, prev) ID id; VALUE value; + struct RVarmap *prev; { NEWOBJ(vars, struct RVarmap); OBJSETUP(vars, 0, T_VARMAP); vars->id = id; vars->val = value; - vars->next = ruby_dyna_vars; + vars->next = prev; return vars; } @@ -507,17 +522,19 @@ rb_dvar_push(id, value) ID id; VALUE value; { - ruby_dyna_vars = new_dvar(id, value); + ruby_dyna_vars = new_dvar(id, value, ruby_dyna_vars); } -void -rb_dvar_asgn(id, value) +static void +dvar_asgn(id, value, push) ID id; VALUE value; + int push; { struct RVarmap *vars = ruby_dyna_vars; while (vars) { + if (push && vars->id == 0) break; if (vars->id == id) { vars->val = value; return; @@ -525,7 +542,14 @@ rb_dvar_asgn(id, value) vars = vars->next; } rb_dvar_push(id, value); - return; +} + +void +rb_dvar_asgn(id, value) + ID id; + VALUE value; +{ + dvar_asgn(id, value, 0); } static void @@ -533,9 +557,16 @@ dvar_asgn_push(id, value) ID id; VALUE value; { - rb_dvar_asgn(id, value); - if (ruby_calling_block) { - ruby_calling_block->d_vars = ruby_dyna_vars; + struct RVarmap* vars = 0; + + if (ruby_dyna_vars && ruby_dyna_vars->id == 0) { + vars = ruby_dyna_vars; + ruby_dyna_vars = ruby_dyna_vars->next; + } + dvar_asgn(id, value, 1); + if (vars) { + vars->next = ruby_dyna_vars; + ruby_dyna_vars = vars; } } @@ -565,6 +596,7 @@ struct tag { struct iter *iter; ID tag; VALUE retval; + struct SCOPE *scope; int dst; struct tag *prev; }; @@ -577,6 +609,7 @@ static struct tag *prot_tag; _tag.iter = ruby_iter; \ _tag.prev = prot_tag; \ _tag.retval = Qnil; \ + _tag.scope = ruby_scope; \ _tag.tag = ptag; \ _tag.dst = 0; \ prot_tag = &_tag; @@ -612,8 +645,8 @@ static struct tag *prot_tag; VALUE ruby_class; static VALUE ruby_wrapper; /* security wrapper */ -#define PUSH_CLASS() { \ - VALUE _class = ruby_class; \ +#define PUSH_CLASS() { \ + VALUE _class = ruby_class; \ #define POP_CLASS() ruby_class = _class; } @@ -630,39 +663,35 @@ static VALUE ruby_wrapper; /* security wrapper */ scope_vmode = SCOPE_PUBLIC; #define SCOPE_DONT_RECYCLE FL_USER2 - -static void scope_dup(struct SCOPE *); - -#define POP_SCOPE() \ - if (ruby_scope->flag == SCOPE_ALLOCA) {\ - if (FL_TEST(ruby_scope, SCOPE_DONT_RECYCLE)) {\ - scope_dup(ruby_scope);\ - FL_SET(_old, SCOPE_DONT_RECYCLE);\ - }\ - else {\ - ruby_scope->local_vars = 0;\ - ruby_scope->local_tbl = 0;\ +#define POP_SCOPE() \ + if (FL_TEST(ruby_scope, SCOPE_DONT_RECYCLE)) {\ + FL_SET(_old, SCOPE_DONT_RECYCLE);\ + } \ + else { \ + if (ruby_scope->flag == SCOPE_ALLOCA) {\ + ruby_scope->local_vars = 0; \ + ruby_scope->local_tbl = 0; \ if (ruby_scope != top_scope)\ rb_gc_force_recycle((VALUE)ruby_scope);\ - }\ - }\ - else {\ - ruby_scope->flag |= SCOPE_NOSTACK;\ - }\ - ruby_scope = _old;\ - scope_vmode = _vmode;\ + } \ + else { \ + ruby_scope->flag |= SCOPE_NOSTACK;\ + } \ + } \ + ruby_scope = _old; \ + scope_vmode = _vmode; \ } static VALUE rb_eval _((VALUE,NODE*)); static VALUE eval _((VALUE,VALUE,VALUE,char*,int)); -static NODE *compile _((VALUE,char*)); -static VALUE rb_yield_0 _((VALUE, VALUE, VALUE)); +static NODE *compile _((VALUE, char*, int)); +static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int)); static VALUE rb_call _((VALUE,VALUE,ID,int,VALUE*,int)); static VALUE module_setup _((VALUE,NODE*)); -static VALUE massign _((VALUE,NODE*,VALUE)); -static void assign _((VALUE,NODE*,VALUE)); +static VALUE massign _((VALUE,NODE*,VALUE,int)); +static void assign _((VALUE,NODE*,VALUE,int)); static int safe_level = 0; /* safe-level: @@ -770,12 +799,19 @@ error_print() { VALUE errat; VALUE eclass; - VALUE einfo; - volatile int safe = safe_level; + char *einfo; + int elen; - if (NIL_P(rb_errinfo)) return; + if (NIL_P(ruby_errinfo)) return; - errat = get_backtrace(rb_errinfo); + PUSH_TAG(PROT_NONE); + if (EXEC_TAG() == 0) { + errat = get_backtrace(ruby_errinfo); + } + else { + errat = Qnil; + } + POP_TAG(); if (!NIL_P(errat)) { VALUE mesg = RARRAY(errat)->ptr[0]; @@ -785,38 +821,46 @@ error_print() } } - eclass = CLASS_OF(rb_errinfo); - einfo = rb_obj_as_string(rb_errinfo); - if (eclass == rb_eRuntimeError && RSTRING(einfo)->len == 0) { + eclass = CLASS_OF(ruby_errinfo); + PUSH_TAG(PROT_NONE); + if (EXEC_TAG() == 0) { + einfo = str2cstr(rb_obj_as_string(ruby_errinfo), &elen); + } + else { + einfo = ""; + elen = 0; + } + POP_TAG(); + if (eclass == rb_eRuntimeError && elen == 0) { fprintf(stderr, ": unhandled exception\n"); } else { VALUE epath; epath = rb_class_path(eclass); - if (RSTRING(einfo)->len == 0) { + if (elen == 0) { fprintf(stderr, ": "); fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr); putc('\n', stderr); } else { char *tail = 0; - int len = RSTRING(einfo)->len; + int len = elen; if (RSTRING(epath)->ptr[0] == '#') epath = 0; - if (tail = strchr(RSTRING(einfo)->ptr, '\n')) { - len = tail - RSTRING(einfo)->ptr; + if (tail = strchr(einfo, '\n')) { + len = tail - einfo; tail++; /* skip newline */ } fprintf(stderr, ": "); - fwrite(RSTRING(einfo)->ptr, 1, len, stderr); + fwrite(einfo, 1, len, stderr); if (epath) { fprintf(stderr, " ("); fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr); fprintf(stderr, ")\n"); } if (tail) { - fwrite(tail, 1, RSTRING(einfo)->len-len-1, stderr); + fwrite(tail, 1, elen-len-1, stderr); putc('\n', stderr); } } @@ -836,13 +880,12 @@ error_print() fprintf(stderr, "\tfrom %s\n", RSTRING(ep->ptr[i])->ptr); } if (i == TRACE_HEAD && ep->len > TRACE_MAX) { - fprintf(stderr, "\t ... %d levels...\n", + fprintf(stderr, "\t ... %ld levels...\n", ep->len - TRACE_HEAD - TRACE_TAIL); i = ep->len - TRACE_TAIL; } } } - safe_level = safe; } #if !defined(NT) && !defined(__MACOS__) @@ -851,17 +894,22 @@ extern char **environ; char **rb_origenviron; void rb_call_inits _((void)); -void Init_stack _((void)); +void Init_stack _((void*)); void Init_heap _((void)); void Init_ext _((void)); void ruby_init() { + static int initialized = 0; static struct FRAME frame; static struct iter iter; int state; + if (initialized) + return; + initialized = 1; + ruby_frame = top_frame = &frame; ruby_iter = &iter; @@ -871,6 +919,7 @@ ruby_init() rb_origenviron = environ; #endif + Init_stack(0); Init_heap(); PUSH_SCOPE(); ruby_scope->local_vars = 0; @@ -879,13 +928,16 @@ ruby_init() /* default visibility is private at toplevel */ SCOPE_SET(SCOPE_PRIVATE); - PUSH_TAG(PROT_NONE) + PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { 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); rb_define_global_const("TOPLEVEL_BINDING", rb_f_binding(ruby_top_self)); +#ifdef __MACOS__ + _macruby_init(); +#endif ruby_prog_init(); } POP_TAG(); @@ -905,13 +957,8 @@ ruby_options(argc, argv) PUSH_TAG(PROT_NONE) if ((state = EXEC_TAG()) == 0) { - NODE *save; - ruby_process_options(argc, argv); ext_init = 1; /* Init_ext() called in ruby_process_options */ - save = ruby_eval_tree; - ruby_require_modules(); - ruby_eval_tree = save; } POP_TAG(); if (state) { @@ -925,49 +972,54 @@ static VALUE eval_node(self) VALUE self; { - VALUE result = Qnil; - NODE *tree; + NODE *beg_tree, *tree; - if (ruby_eval_tree_begin) { - tree = ruby_eval_tree_begin; + beg_tree = ruby_eval_tree_begin; + tree = ruby_eval_tree; + if (beg_tree) { ruby_eval_tree_begin = 0; - rb_eval(self, tree); + rb_eval(self, beg_tree); } - if (!ruby_eval_tree) return Qnil; - - tree = ruby_eval_tree; + if (!tree) return Qnil; ruby_eval_tree = 0; - result = rb_eval(self, tree); - return result; + return rb_eval(self, tree); } -int rb_in_eval; +int ruby_in_eval; -#ifdef USE_THREAD static void rb_thread_cleanup _((void)); static void rb_thread_wait_other_threads _((void)); -#endif static int exit_status; -static void exec_end_proc _((void)); +static void +call_required_libraries() +{ + NODE *save; + + ruby_sourcefile = 0; + if (!ext_init) Init_ext(); + save = ruby_eval_tree; + ruby_require_libraries(); + ruby_eval_tree = save; +} void ruby_run() { int state; static int ex; + volatile NODE *tmp; if (ruby_nerrs > 0) exit(ruby_nerrs); - Init_stack(); - + Init_stack(&tmp); PUSH_TAG(PROT_NONE); PUSH_ITER(ITER_NOT); if ((state = EXEC_TAG()) == 0) { - if (!ext_init) Init_ext(); + call_required_libraries(); eval_node(ruby_top_self); } POP_ITER(); @@ -978,10 +1030,8 @@ ruby_run() PUSH_ITER(ITER_NOT); if ((state = EXEC_TAG()) == 0) { rb_trap_exit(); -#ifdef USE_THREAD rb_thread_cleanup(); rb_thread_wait_other_threads(); -#endif } else { ex = state; @@ -1021,30 +1071,32 @@ ruby_run() break; case TAG_RAISE: case TAG_FATAL: - if (rb_obj_is_kind_of(rb_errinfo, rb_eSystemExit)) { - exit(exit_status); + if (rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) { + ex = exit_status; + } + else { + error_print(); + ex = 1; } - error_print(); - ex = 1; break; default: rb_bug("Unknown longjmp status %d", ex); break; } - exec_end_proc(); + rb_exec_end_proc(); rb_gc_call_finalizer_at_exit(); exit(ex); } static void compile_error(at) - char *at; + const char *at; { VALUE str; char *mesg; int len; - mesg = str2cstr(rb_errinfo, &len); + mesg = str2cstr(ruby_errinfo, &len); ruby_nerrs = 0; str = rb_str_new2("compile error"); if (at) { @@ -1058,7 +1110,7 @@ compile_error(at) VALUE rb_eval_string(str) - char *str; + const char *str; { VALUE v; char *oldsrc = ruby_sourcefile; @@ -1072,7 +1124,7 @@ rb_eval_string(str) VALUE rb_eval_string_protect(str, state) - char *str; + const char *str; int *state; { VALUE result; /* OK */ @@ -1093,6 +1145,33 @@ rb_eval_string_protect(str, state) return result; } +VALUE +rb_eval_string_wrap(str, state) + const char *str; + int *state; +{ + int status; + VALUE self = ruby_top_self; + VALUE val; + + PUSH_CLASS(); + ruby_class = ruby_wrapper = rb_module_new(); + ruby_top_self = rb_obj_clone(ruby_top_self); + rb_extend_object(self, ruby_class); + + val = rb_eval_string_protect(str, &status); + ruby_top_self = self; + + POP_CLASS(); + if (state) { + *state = status; + if (status) { + JUMP_TAG(status); + } + } + return val; +} + VALUE rb_eval_cmd(cmd, arg) VALUE cmd, arg; @@ -1121,6 +1200,8 @@ rb_eval_cmd(cmd, arg) val = eval(ruby_top_self, cmd, Qnil, 0, 0); } + if (FL_TEST(ruby_scope, SCOPE_DONT_RECYCLE)) + FL_SET(saved_scope, SCOPE_DONT_RECYCLE); ruby_scope = saved_scope; safe_level = safe; POP_TAG(); @@ -1151,7 +1232,7 @@ rb_eval_cmd(cmd, arg) return val; } -VALUE +static VALUE rb_trap_eval(cmd, sig) VALUE cmd; int sig; @@ -1304,11 +1385,11 @@ rb_mod_alias_method(mod, newname, oldname) } #ifdef C_ALLOCA -# define TMP_PROTECT NODE * volatile __protect_tmp=0 -# define TMP_ALLOC(n) \ - (__protect_tmp = rb_node_newnode(NODE_ALLOCA, \ - ALLOC_N(VALUE,n),__protect_tmp,n), \ - (void*)__protect_tmp->nd_head) +# 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) #else # define TMP_PROTECT typedef int foobazzz # define TMP_ALLOC(n) ALLOCA_N(VALUE,n) @@ -1411,7 +1492,7 @@ is_defined(self, node, buf) case NODE_ZSUPER: if (ruby_frame->last_func == 0) return 0; else if (rb_method_boundp(RCLASS(ruby_frame->last_class)->super, - ruby_frame->last_func, 1)) { + ruby_frame->last_func, 0)) { if (nd_type(node) == NODE_SUPER) { return arg_defined(self, node->nd_args, buf, "super"); } @@ -1602,9 +1683,7 @@ call_trace_func(event, file, line, self, id, klass) trace = trace_func; trace_func = 0; -#ifdef USE_THREAD rb_thread_critical++; -#endif prev = ruby_frame; PUSH_FRAME(); @@ -1632,9 +1711,7 @@ call_trace_func(event, file, line, self, id, klass) POP_TAG(); POP_FRAME(); -#ifdef USE_THREAD rb_thread_critical--; -#endif if (!trace_func) trace_func = trace; ruby_sourceline = line_save; ruby_sourcefile = file_save; @@ -1651,10 +1728,6 @@ rb_eval(self, node) { int state; volatile VALUE result = Qnil; -#ifdef NOBLOCK_RECUR - NODE * volatile next = 0; - NODE * volatile nstack = 0; -#endif #define RETURN(v) { result = (v); goto finish; } @@ -1663,24 +1736,13 @@ rb_eval(self, node) switch (nd_type(node)) { case NODE_BLOCK: -#ifndef NOBLOCK_RECUR - if (!node->nd_next) { - node = node->nd_head; - goto again; - } - while (node) { - result = rb_eval(self, node->nd_head); + while (node->nd_next) { + rb_eval(self, node->nd_head); node = node->nd_next; } - break; -#else - if (next) { - nstack = rb_node_newnode(NODE_CREF,next,0,nstack); - } - next = node->nd_next; node = node->nd_head; goto again; -#endif + case NODE_POSTEXE: rb_f_END(); nd_set_type(node, NODE_NIL); /* exec just once */ @@ -1738,11 +1800,7 @@ rb_eval(self, node) case NODE_IF: ruby_sourceline = nd_line(node); -#ifdef NOBLOCK_RECUR - if (RTEST(result)){ -#else if (RTEST(rb_eval(self, node->nd_cond))) { -#endif node = node->nd_body; } else { @@ -1754,11 +1812,7 @@ rb_eval(self, node) { VALUE val; -#ifdef NOBLOCK_RECUR - val = result; -#else val = rb_eval(self, node->nd_head); -#endif node = node->nd_body; while (node) { NODE *tag; @@ -1883,6 +1937,7 @@ rb_eval(self, node) char *file = ruby_sourcefile; int line = ruby_sourceline; + _block.flags &= ~BLOCK_D_SCOPE; recv = rb_eval(self, node->nd_iter); PUSH_ITER(ITER_PRE); ruby_sourcefile = file; @@ -1953,13 +2008,13 @@ rb_eval(self, node) else { result = Qnil; } - result = rb_yield_0(result, 0, 0); + result = rb_yield_0(result, 0, 0, Qfalse); break; case NODE_RESCUE: retry_entry: { - volatile VALUE e_info = rb_errinfo; + volatile VALUE e_info = ruby_errinfo; PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { @@ -1978,7 +2033,7 @@ rb_eval(self, node) } POP_TAG(); if (state == 0) { - rb_errinfo = e_info; + ruby_errinfo = e_info; } else if (state == TAG_RETRY) { state = 0; @@ -2030,11 +2085,9 @@ rb_eval(self, node) case NODE_DOT2: case NODE_DOT3: - result = rb_range_new(rb_eval(self, node->nd_beg), rb_eval(self, node->nd_end)); -#if 0 - break; -#else - result = rb_range_new(rb_eval(self, node->nd_beg), rb_eval(self, node->nd_end)); + result = rb_range_new(rb_eval(self, node->nd_beg), + rb_eval(self, node->nd_end), + nd_type(node) == NODE_DOT3); if (node->nd_state) break; if (nd_type(node->nd_beg) == NODE_LIT && FIXNUM_P(node->nd_beg->nd_lit) && nd_type(node->nd_end) == NODE_LIT && FIXNUM_P(node->nd_end->nd_lit)) @@ -2045,7 +2098,6 @@ rb_eval(self, node) else { node->nd_state = 1; } -#endif break; case NODE_FLIP2: /* like AWK */ @@ -2106,13 +2158,7 @@ rb_eval(self, node) TMP_PROTECT; BEGIN_CALLARGS; -#ifdef NOBLOCK_RECUR_incomplete - printf("mid %s recv: ", rb_id2name(node->nd_mid)); - rb_p(result); - recv = result; -#else recv = rb_eval(self, node->nd_recv); -#endif SETUP_ARGS(node->nd_args); END_CALLARGS; @@ -2167,7 +2213,11 @@ rb_eval(self, node) case NODE_SCOPE: { - VALUE save = ruby_frame->cbase; + struct FRAME frame; + + frame = *ruby_frame; + frame.tmp = ruby_frame; + ruby_frame = &frame; PUSH_SCOPE(); PUSH_TAG(PROT_NONE); @@ -2188,7 +2238,7 @@ rb_eval(self, node) } POP_TAG(); POP_SCOPE(); - ruby_frame->cbase = save; + ruby_frame = frame.tmp; if (state) JUMP_TAG(state); } break; @@ -2204,15 +2254,16 @@ rb_eval(self, node) rval = node->nd_args->nd_head; SETUP_ARGS(node->nd_args->nd_next); val = rb_funcall2(recv, aref, argc-1, argv); - if (node->nd_mid == 0) { /* OR */ - if (RTEST(val)) break; + switch (node->nd_mid) { + case 0: /* OR */ + if (RTEST(val)) RETURN(val); val = rb_eval(self, rval); - } - else if (node->nd_mid == 1) { /* AND */ - if (!RTEST(val)) break; + break; + case 1: /* AND */ + if (!RTEST(val)) RETURN(val); val = rb_eval(self, rval); - } - else { + break; + default: val = rb_funcall(val, node->nd_mid, 1, rb_eval(self, rval)); } argv[argc-1] = val; @@ -2228,15 +2279,16 @@ rb_eval(self, node) recv = rb_eval(self, node->nd_recv); val = rb_funcall(recv, id, 0); - if (node->nd_next->nd_mid == 0) { /* OR */ - if (RTEST(val)) break; + switch (node->nd_next->nd_mid) { + case 0: /* OR */ + if (RTEST(val)) RETURN(val); val = rb_eval(self, node->nd_value); - } - else if (node->nd_next->nd_mid == 1) { /* AND */ - if (!RTEST(val)) break; + break; + case 1: /* AND */ + if (!RTEST(val)) RETURN(val); val = rb_eval(self, node->nd_value); - } - else { + break; + default: val = rb_funcall(val, node->nd_next->nd_mid, 1, rb_eval(self, node->nd_value)); } @@ -2261,7 +2313,7 @@ rb_eval(self, node) break; case NODE_MASGN: - result = massign(self, node, rb_eval(self, node->nd_value)); + result = massign(self, node, rb_eval(self, node->nd_value),0); break; case NODE_LASGN: @@ -2297,9 +2349,9 @@ rb_eval(self, node) } result = rb_eval(self, node->nd_value); /* check for static scope constants */ - if (RTEST(rb_verbose) && + if (RTEST(ruby_verbose) && ev_const_defined((NODE*)ruby_frame->cbase, node->nd_vid)) { - if (RTEST(rb_verbose)) { + if (RTEST(ruby_verbose)) { rb_warning("already initialized constant %s", rb_id2name(node->nd_vid)); } @@ -2445,10 +2497,13 @@ rb_eval(self, node) str2 = list->nd_head->nd_lit; break; case NODE_EVSTR: - rb_in_eval++; - list->nd_head = compile(list->nd_head->nd_lit,0); + ruby_sourceline = nd_line(node); + ruby_in_eval++; + list->nd_head = compile(list->nd_head->nd_lit, + ruby_sourcefile, + ruby_sourceline); ruby_eval_tree = 0; - rb_in_eval--; + ruby_in_eval--; if (ruby_nerrs > 0) { compile_error("string expansion"); } @@ -2494,7 +2549,7 @@ rb_eval(self, node) case NODE_ATTRSET: if (ruby_frame->argc != 1) - rb_raise(rb_eArgError, "Wrong # of arguments(%d for 1)", + rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", ruby_frame->argc); result = rb_ivar_set(self, node->nd_vid, ruby_frame->argv[0]); break; @@ -2514,10 +2569,10 @@ rb_eval(self, node) body = search_method(ruby_class, node->nd_mid, &origin); if (body) { if (origin == ruby_class) { - if (safe_level >= 3) { + if (safe_level >= 4) { rb_raise(rb_eSecurityError, "re-defining method prohibited"); } - if (RTEST(rb_verbose)) { + if (RTEST(ruby_verbose)) { rb_warning("discarding old %s", rb_id2name(node->nd_mid)); } } @@ -2562,18 +2617,11 @@ rb_eval(self, node) VALUE klass; NODE *body = 0; - if (FIXNUM_P(recv)) { - rb_raise(rb_eTypeError, "Can't define method \"%s\" for Fixnum", - rb_id2name(node->nd_mid)); - } - if (NIL_P(recv)) { - rb_raise(rb_eTypeError, "Can't define method \"%s\" for nil", - rb_id2name(node->nd_mid)); - } if (rb_special_const_p(recv)) { rb_raise(rb_eTypeError, - "Can't define method \"%s\" for special constants", - rb_id2name(node->nd_mid)); + "can't define method \"%s\" for %s", + rb_id2name(node->nd_mid), + rb_class2name(CLASS_OF(recv))); } if (rb_safe_level() >= 4 && !FL_TEST(recv, FL_TAINT)) { @@ -2581,10 +2629,10 @@ rb_eval(self, node) } klass = rb_singleton_class(recv); if (st_lookup(RCLASS(klass)->m_tbl, node->nd_mid, &body)) { - if (safe_level >= 3) { + if (safe_level >= 4) { rb_raise(rb_eSecurityError, "re-defining method prohibited"); } - if (RTEST(rb_verbose)) { + if (RTEST(ruby_verbose)) { rb_warning("redefine %s", rb_id2name(node->nd_mid)); } } @@ -2622,6 +2670,9 @@ rb_eval(self, node) s0 = ""; } } + else if (TYPE(klass) == T_MODULE) { + s0 = " module"; + } rb_raise(rb_eNameError, "undefined method `%s' for%s `%s'", rb_id2name(node->nd_mid),s0,rb_class2name(klass)); } @@ -2686,7 +2737,7 @@ rb_eval(self, node) rb_id2name(node->nd_cname)); } } - if (safe_level >= 3) { + if (safe_level >= 4) { rb_raise(rb_eSecurityError, "extending class prohibited"); } rb_clear_cache(); @@ -2696,7 +2747,6 @@ rb_eval(self, node) klass = rb_define_class_id(node->nd_cname, super); rb_const_set(ruby_class, node->nd_cname, klass); rb_set_class_path(klass,ruby_class,rb_id2name(node->nd_cname)); - rb_obj_call_init(klass); } if (ruby_wrapper) { rb_extend_object(klass, ruby_wrapper); @@ -2728,7 +2778,7 @@ rb_eval(self, node) rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(node->nd_cname)); } - if (safe_level >= 3) { + if (safe_level >= 4) { rb_raise(rb_eSecurityError, "extending module prohibited"); } } @@ -2736,7 +2786,6 @@ rb_eval(self, node) module = rb_define_module_id(node->nd_cname); rb_const_set(ruby_class, node->nd_cname, module); rb_set_class_path(module,ruby_class,rb_id2name(node->nd_cname)); - rb_obj_call_init(module); } if (ruby_wrapper) { rb_extend_object(module, ruby_wrapper); @@ -2752,14 +2801,9 @@ rb_eval(self, node) VALUE klass; klass = rb_eval(self, node->nd_recv); - if (FIXNUM_P(klass)) { - rb_raise(rb_eTypeError, "No virtual class for Fixnums"); - } - if (NIL_P(klass)) { - rb_raise(rb_eTypeError, "No virtual class for nil"); - } if (rb_special_const_p(klass)) { - rb_raise(rb_eTypeError, "No virtual class for special constants"); + rb_raise(rb_eTypeError, "no virtual class for %s", + rb_class2name(CLASS_OF(klass))); } if (FL_TEST(CLASS_OF(klass), FL_SINGLETON)) { rb_clear_cache(); @@ -2780,7 +2824,7 @@ rb_eval(self, node) char *desc = is_defined(self, node->nd_head, buf); if (desc) result = rb_str_new2(desc); - else result = Qfalse; + else result = Qnil; } break; @@ -2799,18 +2843,6 @@ rb_eval(self, node) } finish: CHECK_INTS; -#ifdef NOBLOCK_RECUR - if (next) { - node = next; - next = 0; - goto again; - } - if (nstack) { - node = nstack->nd_head; - nstack = nstack->nd_next; - goto again; - } -#endif return result; } @@ -2820,12 +2852,16 @@ module_setup(module, node) NODE * volatile node; { int state; - VALUE save = ruby_frame->cbase; + struct FRAME frame; VALUE result; /* OK */ char *file = ruby_sourcefile; int line = ruby_sourceline; TMP_PROTECT; + frame = *ruby_frame; + frame.tmp = ruby_frame; + ruby_frame = &frame; + /* fill c-ref */ node->nd_clss = module; node = node->nd_body; @@ -2833,6 +2869,7 @@ module_setup(module, node) PUSH_CLASS(); ruby_class = module; PUSH_SCOPE(); + PUSH_VARS(); if (node->nd_rval) ruby_frame->cbase = node->nd_rval; if (node->nd_tbl) { @@ -2856,10 +2893,11 @@ module_setup(module, node) result = rb_eval(ruby_class, node->nd_next); } POP_TAG(); + POP_VARS(); POP_SCOPE(); POP_CLASS(); - ruby_frame->cbase = save; + ruby_frame = frame.tmp; if (trace_func) { call_trace_func("end", file, line, 0, ruby_frame->last_func, 0); } @@ -2914,6 +2952,8 @@ rb_exit(status) exit_status = status; rb_exc_raise(rb_exc_new(rb_eSystemExit, 0, 0)); } + rb_exec_end_proc(); + rb_gc_call_finalizer_at_exit(); exit(status); } @@ -2924,22 +2964,23 @@ rb_f_exit(argc, argv, obj) VALUE obj; { VALUE status; + int istatus; rb_secure(4); if (rb_scan_args(argc, argv, "01", &status) == 1) { - status = NUM2INT(status); + istatus = NUM2INT(status); } else { - status = 0; + istatus = 0; } - rb_exit(status); + rb_exit(istatus); return Qnil; /* not reached */ } static void rb_abort() { - if (rb_errinfo) { + if (ruby_errinfo) { error_print(); } rb_exit(1); @@ -2969,7 +3010,7 @@ rb_longjmp(tag, mesg) { VALUE at; - if (NIL_P(mesg)) mesg = rb_errinfo; + if (NIL_P(mesg)) mesg = ruby_errinfo; if (NIL_P(mesg)) { mesg = rb_exc_new(rb_eRuntimeError, 0, 0); } @@ -2982,13 +3023,13 @@ rb_longjmp(tag, mesg) } } if (!NIL_P(mesg)) { - rb_errinfo = mesg; + ruby_errinfo = mesg; } - if (RTEST(rb_debug) && !NIL_P(rb_errinfo) - && !rb_obj_is_kind_of(rb_errinfo, rb_eSystemExit)) { + if (RTEST(ruby_debug) && !NIL_P(ruby_errinfo) + && !rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) { fprintf(stderr, "Exception `%s' at %s:%d\n", - rb_class2name(CLASS_OF(rb_errinfo)), + rb_class2name(CLASS_OF(ruby_errinfo)), ruby_sourcefile, ruby_sourceline); } @@ -3028,32 +3069,33 @@ rb_f_raise(argc, argv) int argc; VALUE *argv; { - VALUE arg1, arg2, arg3; VALUE mesg; - int n; mesg = Qnil; - switch (n = rb_scan_args(argc, argv, "03", &arg1, &arg2, &arg3)) { + switch (argc) { + case 0: + mesg = Qnil; + break; case 1: - mesg = arg1; + if (NIL_P(argv[0])) break; + if (TYPE(argv[0]) == T_STRING) { + mesg = rb_exc_new3(rb_eRuntimeError, argv[0]); + break; + } + mesg = rb_funcall(argv[0], rb_intern("exception"), 0, 0); break; case 3: case 2: - mesg = arg2; + mesg = rb_funcall(argv[0], rb_intern("exception"), 1, argv[1]); + break; + default: + rb_raise(rb_eArgError, "wrong # of arguments"); break; } - if (!NIL_P(mesg)) { - if (n == 1 && TYPE(mesg) == T_STRING) { - mesg = rb_exc_new3(rb_eRuntimeError, mesg); - } - else { - mesg = rb_funcall(arg1, rb_intern("new"), 1, mesg); - } - if (!rb_obj_is_kind_of(mesg, rb_eException)) { + if (!rb_obj_is_kind_of(mesg, rb_eException)) rb_raise(rb_eTypeError, "exception object expected"); - } - set_backtrace(mesg, arg3); + set_backtrace(mesg, (argc>2)?argv[2]:Qnil); } PUSH_FRAME(); /* fake frame */ @@ -3086,8 +3128,9 @@ rb_f_iterator_p() } static VALUE -rb_yield_0(val, self, klass) +rb_yield_0(val, self, klass, acheck) VALUE val, self, klass; /* OK */ + int acheck; { NODE *node; volatile VALUE result = Qnil; @@ -3110,15 +3153,27 @@ rb_yield_0(val, self, klass) old_scope = ruby_scope; ruby_scope = block->scope; ruby_block = block->prev; - ruby_dyna_vars = block->d_vars; + if (block->flags & BLOCK_D_SCOPE) { + /* put place holder for dynamic (in-block) local variables */ + ruby_dyna_vars = new_dvar(0, 0, block->d_vars); + } + else { + /* FOR does not introduce new scope */ + ruby_dyna_vars = block->d_vars; + } ruby_class = klass?klass:block->klass; if (!self) self = block->self; node = block->body; if (block->var) { - if (nd_type(block->var) == NODE_MASGN) - massign(self, block->var, val); - else - assign(self, block->var, val); + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + if (nd_type(block->var) == NODE_MASGN) + massign(self, block->var, val, acheck); + else + assign(self, block->var, val, acheck); + } + POP_TAG(); + if (state) goto pop_state; } PUSH_ITER(block->iter); PUSH_TAG(PROT_NONE); @@ -3154,6 +3209,7 @@ rb_yield_0(val, self, klass) } } POP_TAG(); + pop_state: POP_ITER(); POP_CLASS(); POP_VARS(); @@ -3170,23 +3226,24 @@ VALUE rb_yield(val) VALUE val; { - return rb_yield_0(val, 0, 0); + return rb_yield_0(val, 0, 0, Qfalse); } static VALUE rb_f_loop() { - for (;;) { rb_yield_0(Qnil, 0, 0); } + for (;;) { rb_yield_0(Qnil, 0, 0, Qfalse); } } static VALUE -massign(self, node, val) +massign(self, node, val, check) VALUE self; NODE *node; VALUE val; + int check; { NODE *list; - int i, len; + int i = 0, len; list = node->nd_head; @@ -3196,33 +3253,49 @@ massign(self, node, val) } len = RARRAY(val)->len; for (i=0; list && ind_head, RARRAY(val)->ptr[i]); + assign(self, list->nd_head, RARRAY(val)->ptr[i], check); list = list->nd_next; } + if (check && list) goto arg_error; if (node->nd_args) { - if (!list && ind_args, rb_ary_new4(len-i, RARRAY(val)->ptr+i)); + if (node->nd_args == (NODE*)-1) { + /* ignore rest args */ + } + else if (!list && ind_args, rb_ary_new4(len-i, RARRAY(val)->ptr+i), check); } else { - assign(self, node->nd_args, rb_ary_new2(0)); + assign(self, node->nd_args, rb_ary_new2(0), check); } } + else if (check && ind_args) { - assign(self, node->nd_args, Qnil); + else if (node->nd_args && node->nd_args != (NODE*)-1) { + assign(self, node->nd_args, Qnil, check); } + + if (check && list) goto arg_error; while (list) { - assign(self, list->nd_head, Qnil); + i++; + assign(self, list->nd_head, Qnil, check); list = list->nd_next; } return val; + + arg_error: + while (list) { + i++; + list = list->nd_next; + } + rb_raise(rb_eArgError, "wrong # of arguments (%d for %d)", len, i); } static void -assign(self, lhs, val) +assign(self, lhs, val, check) VALUE self; NODE *lhs; VALUE val; + int check; { switch (nd_type(lhs)) { case NODE_GASGN: @@ -3252,14 +3325,14 @@ assign(self, lhs, val) break; case NODE_MASGN: - massign(self, lhs, val); + massign(self, lhs, val, check); break; case NODE_CALL: { VALUE recv; recv = rb_eval(self, lhs->nd_recv); - if (!lhs->nd_args->nd_head) { + if (!lhs->nd_args) { /* attr set */ rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, 0); } @@ -3268,7 +3341,7 @@ assign(self, lhs, val) VALUE args; args = rb_eval(self, lhs->nd_args); - RARRAY(args)->ptr[RARRAY(args)->len-1] = val; + rb_ary_push(args, val); rb_call(CLASS_OF(recv), recv, lhs->nd_mid, RARRAY(args)->len, RARRAY(args)->ptr, 0); } @@ -3339,7 +3412,7 @@ handle_rescue(self, node) TMP_PROTECT; if (!node->nd_args) { - return rb_obj_is_kind_of(rb_errinfo, rb_eStandardError); + return rb_obj_is_kind_of(ruby_errinfo, rb_eStandardError); } BEGIN_CALLARGS; @@ -3350,7 +3423,7 @@ handle_rescue(self, node) if (!rb_obj_is_kind_of(argv[0], rb_cModule)) { rb_raise(rb_eTypeError, "class or module required for rescue clause"); } - if (rb_obj_is_kind_of(rb_errinfo, argv[0])) return 1; + if (rb_obj_is_kind_of(ruby_errinfo, argv[0])) return 1; argv++; } return 0; @@ -3363,18 +3436,18 @@ rb_rescue(b_proc, data1, r_proc, data2) { int state; volatile VALUE result; - volatile VALUE e_info = rb_errinfo; + volatile VALUE e_info = ruby_errinfo; PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { retry_entry: result = (*b_proc)(data1); } - else if (state == TAG_RAISE && rb_obj_is_kind_of(rb_errinfo, rb_eStandardError)) { + else if (state == TAG_RAISE && rb_obj_is_kind_of(ruby_errinfo, rb_eStandardError)) { if (r_proc) { PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { - result = (*r_proc)(data2, rb_errinfo); + result = (*r_proc)(data2, ruby_errinfo); } POP_TAG(); if (state == TAG_RETRY) { @@ -3387,7 +3460,7 @@ rb_rescue(b_proc, data1, r_proc, data2) state = 0; } if (state == 0) { - rb_errinfo = e_info; + ruby_errinfo = e_info; } } POP_TAG(); @@ -3443,6 +3516,26 @@ rb_ensure(b_proc, data1, e_proc, data2) return result; } +VALUE +rb_with_disable_interrupt(proc, data) + VALUE (*proc)(); + VALUE data; +{ + VALUE result; /* OK */ + int status; + + DEFER_INTS; + PUSH_TAG(PROT_NONE); + if ((status = EXEC_TAG()) == 0) { + result = (*proc)(data); + } + POP_TAG(); + ALLOW_INTS; + if (status) JUMP_TAG(status); + + return result; +} + static int last_call_status; #define CSTAT_PRIV 1 @@ -3456,8 +3549,9 @@ rb_f_missing(argc, argv, obj) VALUE obj; { ID id; - VALUE desc = 0; + volatile VALUE d = 0; char *format = 0; + char *desc = ""; char *file = ruby_sourcefile; int line = ruby_sourceline; @@ -3469,41 +3563,39 @@ rb_f_missing(argc, argv, obj) format = "undefined method `%s' for nil"; break; case T_TRUE: - format = "undefined method `%s' for Qtrue"; + format = "undefined method `%s' for true"; break; case T_FALSE: - format = "undefined method `%s' for Qfalse"; + format = "undefined method `%s' for false"; break; case T_OBJECT: - desc = rb_any_to_s(obj); + d = rb_any_to_s(obj); break; default: - desc = rb_inspect(obj); + d = rb_inspect(obj); break; } - if (desc) { + if (d) { if (last_call_status & CSTAT_PRIV) { - format = "private method `%s' called for %s"; + format = "private method `%s' called for %s%s%s"; } if (last_call_status & CSTAT_PROT) { - format = "protected method `%s' called for %s"; - } - else if (rb_iterator_p()) { - format = "undefined iterator `%s' for %s"; + format = "protected method `%s' called for %s%s%s"; } else if (last_call_status & CSTAT_VCALL) { - char *mname = rb_id2name(id); + const char *mname = rb_id2name(id); if (('a' <= mname[0] && mname[0] <= 'z') || mname[0] == '_') { - format = "undefined local variable or method `%s' for %s"; + format = "undefined local variable or method `%s' for %s%s%s"; } } if (!format) { - format = "undefined method `%s' for %s"; + format = "undefined method `%s' for %s%s%s"; } - if (RSTRING(desc)->len > 65) { - desc = rb_any_to_s(obj); + if (RSTRING(d)->len > 65) { + d = rb_any_to_s(obj); } + desc = RSTRING(d)->ptr; } ruby_sourcefile = file; @@ -3511,9 +3603,9 @@ rb_f_missing(argc, argv, obj) PUSH_FRAME(); /* fake frame */ *ruby_frame = *_frame.prev->prev; - rb_raise(rb_eNameError, format, - rb_id2name(id), - desc?(char*)RSTRING(desc)->ptr:""); + rb_raise(rb_eNameError, format, rb_id2name(id), + desc, desc[0]=='#'?"":":", + desc[0]=='#'?"":rb_class2name(CLASS_OF(obj))); POP_FRAME(); return Qnil; /* not reached */ @@ -3535,7 +3627,7 @@ rb_undefined(obj, id, argc, argv, call_status) last_call_status = call_status; - return rb_funcall2(obj, rb_intern("method_missing"), argc+1, nargv); + return rb_funcall2(obj, missing, argc+1, nargv); } #ifdef DJGPP @@ -3570,7 +3662,7 @@ call_cfunc(func, recv, len, argc, argv) VALUE *argv; { if (len >= 0 && argc != len) { - rb_raise(rb_eArgError, "Wrong # of arguments(%d for %d)", + rb_raise(rb_eArgError, "wrong # of arguments(%d for %d)", argc, len); } @@ -3681,7 +3773,6 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper) rb_raise(rb_eSysStackError, "stack level too deep"); } } - PUSH_ITER(itr); PUSH_FRAME(); @@ -3775,21 +3866,23 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper) i = node->nd_cnt; if (i > argc) { - rb_raise(rb_eArgError, "Wrong # of arguments(%d for %d)", + rb_raise(rb_eArgError, "wrong # of arguments(%d for %d)", argc, i); } if (node->nd_rest == -1) { - int opt = argc - i; + int opt = i; NODE *optnode = node->nd_opt; while (optnode) { - opt--; + opt++; optnode = optnode->nd_next; } - if (opt > 0) { - rb_raise(rb_eArgError, "Wrong # of arguments(%d for %d)", - argc, argc-opt); + if (opt < argc) { + rb_raise(rb_eArgError, "wrong # of arguments(%d for %d)", + argc, opt); } + ruby_frame->argc = opt; + ruby_frame->argv = local_vars+2; } if (local_vars) { @@ -3802,7 +3895,7 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper) NODE *opt = node->nd_opt; while (opt && argc) { - assign(recv, opt->nd_head, *argv); + assign(recv, opt->nd_head, *argv, 1); argv++; argc--; opt = opt->nd_next; } @@ -3896,13 +3989,20 @@ rb_call(klass, recv, mid, argc, argv, scope) return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0); } - /* receiver specified form for private method */ - if ((noex & NOEX_PRIVATE) && scope == 0) - return rb_undefined(recv, mid, argc, argv, CSTAT_PRIV); + if (mid != missing) { + /* receiver specified form for private method */ + if ((noex & NOEX_PRIVATE) && scope == 0) + return rb_undefined(recv, mid, argc, argv, CSTAT_PRIV); - /* self must be kind of a specified form for private method */ - if ((noex & NOEX_PROTECTED) && !rb_obj_is_kind_of(ruby_frame->self, klass)) - return rb_undefined(recv, mid, argc, argv, CSTAT_PROT); + /* self must be kind of a specified form for private method */ + if ((noex & NOEX_PROTECTED)) { + VALUE defined_class = klass; + while (TYPE(defined_class) == T_ICLASS) + defined_class = RBASIC(defined_class)->klass; + if (!rb_obj_is_kind_of(ruby_frame->self, defined_class)) + return rb_undefined(recv, mid, argc, argv, CSTAT_PROT); + } + } return rb_call0(klass, recv, id, argc, argv, body, noex & NOEX_UNDEF); } @@ -3991,6 +4091,16 @@ rb_funcall2(recv, mid, argc, argv) return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1); } +VALUE +rb_funcall3(recv, mid, argc, argv) + VALUE recv; + ID mid; + int argc; + VALUE *argv; +{ + return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 0); +} + static VALUE backtrace(lev) int lev; @@ -3998,9 +4108,7 @@ backtrace(lev) struct FRAME *frame = ruby_frame; char buf[BUFSIZ]; VALUE ary; - int slev = safe_level; - safe_level = 0; ary = rb_ary_new(); if (lev < 0) { if (frame->last_func) { @@ -4016,7 +4124,10 @@ backtrace(lev) else { while (lev-- > 0) { frame = frame->prev; - if (!frame) return Qnil; + if (!frame) { + ary = Qnil; + break; + } } } while (frame && frame->file) { @@ -4031,7 +4142,7 @@ backtrace(lev) rb_ary_push(ary, rb_str_new2(buf)); frame = frame->prev; } - safe_level = slev; + return ary; } @@ -4081,15 +4192,15 @@ rb_frame_last_func() } static NODE* -compile(src, place) +compile(src, file, line) VALUE src; - char *place; + char *file; + int line; { NODE *node; Check_Type(src, T_STRING); - if (place == 0) place = ruby_sourcefile; - node = rb_compile_string(place, src); + node = rb_compile_string(file, src, line); if (ruby_nerrs == 0) return node; return 0; @@ -4105,7 +4216,6 @@ eval(self, src, scope, file, line) volatile VALUE result = Qnil; struct SCOPE * volatile old_scope; struct BLOCK * volatile old_block; - struct BLOCK * volatile old_call_block; struct RVarmap * volatile old_d_vars; int volatile old_vmode; struct FRAME frame; @@ -4128,12 +4238,10 @@ eval(self, src, scope, file, line) /* PUSH BLOCK from data */ frame = data->frame; - frame.prev = ruby_frame; + frame.tmp = ruby_frame; /* gc protection */ ruby_frame = &(frame); old_scope = ruby_scope; ruby_scope = data->scope; - old_call_block = ruby_calling_block; - ruby_calling_block = data; old_block = ruby_block; ruby_block = data->prev; old_d_vars = ruby_dyna_vars; @@ -4152,15 +4260,13 @@ eval(self, src, scope, file, line) PUSH_CLASS(); ruby_class = ((NODE*)ruby_frame->cbase)->nd_clss; - rb_in_eval++; + ruby_in_eval++; if (TYPE(ruby_class) == T_ICLASS) { ruby_class = RBASIC(ruby_class)->klass; } PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { - ruby_sourcefile = file; - ruby_sourceline = line; - compile(src, file); + compile(src, file, line); if (ruby_nerrs > 0) { compile_error(0); } @@ -4168,15 +4274,13 @@ eval(self, src, scope, file, line) } POP_TAG(); POP_CLASS(); - rb_in_eval--; + ruby_in_eval--; if (!NIL_P(scope)) { - ruby_frame = ruby_frame->prev; + ruby_frame = frame.tmp; if (FL_TEST(ruby_scope, SCOPE_DONT_RECYCLE)) FL_SET(old_scope, SCOPE_DONT_RECYCLE); ruby_scope = old_scope; ruby_block = old_block; - ruby_calling_block = old_call_block; - data->d_vars = ruby_dyna_vars; ruby_dyna_vars = old_d_vars; data->vmode = scope_vmode; /* write back visibility mode */ scope_vmode = old_vmode; @@ -4191,20 +4295,20 @@ eval(self, src, scope, file, line) VALUE err; VALUE errat; - errat = get_backtrace(rb_errinfo); + errat = get_backtrace(ruby_errinfo); if (strcmp(file, "(eval)") == 0) { if (ruby_sourceline > 1) { err = RARRAY(errat)->ptr[0]; rb_str_cat(err, ": ", 2); - rb_str_concat(err, rb_errinfo); + rb_str_concat(err, ruby_errinfo); } else { - err = rb_str_dup(rb_errinfo); + err = rb_str_dup(ruby_errinfo); } errat = Qnil; - rb_exc_raise(rb_exc_new3(CLASS_OF(rb_errinfo), err)); + rb_exc_raise(rb_exc_new3(CLASS_OF(ruby_errinfo), err)); } - rb_exc_raise(rb_errinfo); + rb_exc_raise(ruby_errinfo); } JUMP_TAG(state); } @@ -4220,7 +4324,7 @@ rb_f_eval(argc, argv, self) { VALUE src, scope, vfile, vline; char *file = "(eval)"; - int line = 0; + int line = 1; rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline); if (argc >= 3) { @@ -4235,6 +4339,7 @@ rb_f_eval(argc, argv, self) return eval(self, src, scope, file, line); } +/* function to call func under the specified class/module context */ static VALUE exec_under(func, under, args) VALUE (*func)(); @@ -4276,10 +4381,11 @@ eval_under_i(args) return eval(args[0], args[1], Qnil, (char*)args[2], (int)args[3]); } +/* string eval under the class/module context */ static VALUE eval_under(under, self, src, file, line) VALUE under, self, src; - char *file; + const char *file; int line; { VALUE args[4]; @@ -4296,50 +4402,70 @@ static VALUE yield_under_i(self) VALUE self; { - return rb_yield_0(self, self, ruby_class); + if (ruby_block->flags & BLOCK_DYNAMIC) { + struct BLOCK * volatile old_block = ruby_block; + struct BLOCK block; + volatile VALUE cbase = ruby_block->frame.cbase; + /* cbase should be pointed from volatile local variable */ + /* to be protected from GC. */ + VALUE result; + int state; + + block = *ruby_block; + /* copy the block to avoid modifying global data. */ + block.frame.cbase = ruby_frame->cbase; + ruby_block = █ + + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + result = rb_yield_0(self, self, ruby_class, Qfalse); + } + POP_TAG(); + ruby_block = old_block; + if (state) JUMP_TAG(state); + + return result; + } + /* static block, no need to restore */ + ruby_block->frame.cbase = ruby_frame->cbase; + return rb_yield_0(self, self, ruby_class, Qfalse); } +/* block eval under the class/module context */ static VALUE yield_under(under, self) VALUE under, self; { - rb_secure(4); + if (rb_safe_level() >= 4 && !FL_TEST(self, FL_TAINT)) + rb_raise(rb_eSecurityError, "Insecure: can't eval"); return exec_under(yield_under_i, under, self); } -VALUE -rb_obj_instance_eval(argc, argv, self) +static VALUE +specific_eval(argc, argv, klass, self) int argc; VALUE *argv; - VALUE self; + VALUE klass, self; { char *file = 0; - int line = 0; - VALUE klass; + int line = 1; + int iter = rb_iterator_p(); - if (argc == 0) { - if (!rb_iterator_p()) { - rb_raise(rb_eArgError, "block not supplied"); - } - } - else if (argc < 4) { + if (argc > 0) { Check_SafeStr(argv[0]); + if (argc > 3) { + rb_raise(rb_eArgError, "wrong # of arguments: %s(src) or %s{..}", + rb_id2name(ruby_frame->last_func), + rb_id2name(ruby_frame->last_func)); + } if (argc > 1) file = STR2CSTR(argv[1]); if (argc > 2) line = NUM2INT(argv[2]); } - else { - rb_raise(rb_eArgError, "Wrong # of arguments: %s(src) or %s{..}", - rb_id2name(ruby_frame->last_func), - rb_id2name(ruby_frame->last_func)); + else if (!iter) { + rb_raise(rb_eArgError, "block not supplied"); } - if (rb_special_const_p(self)) { - klass = Qnil; - } - else { - klass = rb_singleton_class(self); - } - if (argc == 0) { + if (iter) { return yield_under(klass, self); } else { @@ -4347,53 +4473,57 @@ rb_obj_instance_eval(argc, argv, self) } } -static VALUE -rb_mod_module_eval(argc, argv, mod) +VALUE +rb_obj_instance_eval(argc, argv, self) int argc; VALUE *argv; - VALUE mod; + VALUE self; { - char *file = 0; - int line = 0; + VALUE klass; - if (argc == 0) { - if (!rb_iterator_p()) { - rb_raise(rb_eArgError, "block not supplied"); - } - } - else if (argc < 4) { - Check_SafeStr(argv[0]); - if (argc > 1) file = STR2CSTR(argv[1]); - if (argc > 2) line = NUM2INT(argv[2]); + if (rb_special_const_p(self)) { + klass = Qnil; } else { - rb_raise(rb_eArgError, "Wrong # of arguments: %s(src) or %s{..}", - rb_id2name(ruby_frame->last_func), - rb_id2name(ruby_frame->last_func)); + klass = rb_singleton_class(self); } - if (argc == 0) { - return yield_under(mod, mod); - } - else { - return eval_under(mod, mod, argv[0], file, line); - } + return specific_eval(argc, argv, klass, self); +} + +static VALUE +rb_mod_module_eval(argc, argv, mod) + int argc; + VALUE *argv; + VALUE mod; +{ + return specific_eval(argc, argv, mod, mod); } VALUE rb_load_path; static int is_absolute_path(path) - char *path; + const char *path; { if (path[0] == '/') return 1; -# if defined(MSDOS) || defined(NT) || defined(__human68k__) +# if defined(MSDOS) || defined(NT) || defined(__human68k__) || defined(__EMX__) if (path[0] == '\\') return 1; if (strlen(path) > 2 && path[1] == ':') return 1; # endif return 0; } +#ifdef __MACOS__ +static int +is_macos_native_path(path) + const char *path; +{ + if (strchr(path, ':')) return 1; + return 0; +} +#endif + static char* find_file(file) char *file; @@ -4402,6 +4532,16 @@ find_file(file) volatile VALUE vpath; char *path; +#ifdef __MACOS__ + if (is_macos_native_path(file)) { + FILE *f = fopen(file, "r"); + + if (f == NULL) return 0; + fclose(f); + return file; + } +#endif + if (is_absolute_path(file)) { FILE *f = fopen(file, "r"); @@ -4410,14 +4550,25 @@ find_file(file) return file; } + if (file[0] == '~') { + VALUE argv[1]; + argv[0] = rb_str_new2(file); + file = STR2CSTR(rb_file_s_expand_path(1, argv)); + } + if (rb_load_path) { int i; Check_Type(rb_load_path, T_ARRAY); + vpath = rb_ary_new(); for (i=0;ilen;i++) { - Check_SafeStr(RARRAY(rb_load_path)->ptr[i]); + VALUE str = RARRAY(rb_load_path)->ptr[i]; + Check_SafeStr(str); + if (RSTRING(str)->len > 0) { + rb_ary_push(vpath, str); + } } - vpath = rb_ary_join(rb_load_path, rb_str_new2(RUBY_PATH_SEP)); + vpath = rb_ary_join(vpath, rb_str_new2(PATH_SEP)); path = STR2CSTR(vpath); if (safe_level >= 2 && !rb_path_check(path)) { rb_raise(rb_eSecurityError, "loading from unsefe path %s", path); @@ -4447,18 +4598,12 @@ rb_load(fname, wrap) else { Check_SafeStr(fname); } -#ifndef __MACOS__ - if (RSTRING(fname)->ptr[0] == '~') { - fname = rb_file_s_expand_path(1, &fname); - } -#endif file = find_file(RSTRING(fname)->ptr); if (!file) { rb_raise(rb_eLoadError, "No such file to load -- %s", RSTRING(fname)->ptr); } PUSH_VARS(); - PUSH_TAG(PROT_NONE); PUSH_CLASS(); if (!wrap) { rb_secure(4); /* should alter global state */ @@ -4481,19 +4626,20 @@ rb_load(fname, wrap) VALUE *vars = TMP_ALLOC(len); *vars++ = 0; MEMCPY(tbl, top_scope->local_tbl, ID, len); - MEMCPY(vars, top_scope->local_vars, ID, len-1); + MEMCPY(vars, top_scope->local_vars, VALUE, len-1); ruby_scope->local_tbl = tbl; /* copy toplevel scope */ ruby_scope->local_vars = vars; /* will not alter toplevel variables */ } /* default visibility is private at loading toplevel */ SCOPE_SET(SCOPE_PRIVATE); + PUSH_TAG(PROT_NONE); state = EXEC_TAG(); last_func = ruby_frame->last_func; if (state == 0) { - rb_in_eval++; + ruby_in_eval++; rb_load_file(file); - rb_in_eval--; + ruby_in_eval--; if (ruby_nerrs == 0) { eval_node(self); } @@ -4503,14 +4649,15 @@ rb_load(fname, wrap) if (ruby_scope->local_tbl) /* toplevel was empty */ free(ruby_scope->local_tbl); } + POP_TAG(); POP_SCOPE(); POP_FRAME(); POP_CLASS(); - POP_TAG(); POP_VARS(); ruby_wrapper = 0; if (ruby_nerrs > 0) { - rb_exc_raise(rb_errinfo); + ruby_nerrs = 0; + rb_exc_raise(ruby_errinfo); } if (state) JUMP_TAG(state); } @@ -4547,7 +4694,7 @@ static VALUE rb_features; static int rb_provided(feature) - char *feature; + const char *feature; { VALUE *p, *pend; char *f; @@ -4568,14 +4715,12 @@ rb_provided(feature) return Qfalse; } -#ifdef USE_THREAD -static int rb_thread_loading _((char*)); -static void rb_thread_loading_done _((void)); -#endif +static int rb_thread_loading _((const char*)); +static void rb_thread_loading_done _((const char*)); void rb_provide(feature) - char *feature; + const char *feature; { char *buf, *ext; @@ -4598,6 +4743,7 @@ rb_f_require(obj, fname) { char *ext, *file, *feature, *buf; /* OK */ volatile VALUE load; + int state; rb_secure(4); Check_SafeStr(fname); @@ -4609,7 +4755,7 @@ rb_f_require(obj, fname) if (strcmp(".rb", ext) == 0) { feature = file = RSTRING(fname)->ptr; file = find_file(file); - if (file) goto rb_load; + if (file) goto load_rb; } else if (strcmp(".so", ext) == 0 || strcmp(".o", ext) == 0) { file = feature = RSTRING(fname)->ptr; @@ -4621,12 +4767,12 @@ rb_f_require(obj, fname) file = feature = buf; } file = find_file(file); - if (file) goto dyna_load; + if (file) goto load_dyna; } else if (strcmp(DLEXT, ext) == 0) { feature = RSTRING(fname)->ptr; file = find_file(feature); - if (file) goto dyna_load; + if (file) goto load_dyna; } } buf = ALLOCA_N(char, strlen(RSTRING(fname)->ptr) + 5); @@ -4636,59 +4782,56 @@ rb_f_require(obj, fname) if (file) { fname = rb_str_new2(file); feature = buf; - goto rb_load; + goto load_rb; } strcpy(buf, RSTRING(fname)->ptr); strcat(buf, DLEXT); file = find_file(buf); if (file) { feature = buf; - goto dyna_load; + goto load_dyna; } rb_raise(rb_eLoadError, "No such file to load -- %s", RSTRING(fname)->ptr); - dyna_load: -#ifdef USE_THREAD + load_dyna: if (rb_thread_loading(feature)) return Qfalse; - else { - int state; - PUSH_TAG(PROT_NONE); - if ((state = EXEC_TAG()) == 0) { -#endif - load = rb_str_new2(file); - file = RSTRING(load)->ptr; - dln_load(file); - rb_provide(feature); -#ifdef USE_THREAD - } - POP_TAG(); - rb_thread_loading_done(); - if (state) JUMP_TAG(state); + + rb_provide(feature); + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + load = rb_str_new2(file); + file = RSTRING(load)->ptr; + dln_load(file); } -#endif + POP_TAG(); + rb_thread_loading_done(feature); + if (state) JUMP_TAG(state); + return Qtrue; - rb_load: -#ifdef USE_THREAD + load_rb: if (rb_thread_loading(feature)) return Qfalse; - else { - int state; - PUSH_TAG(PROT_NONE); - if ((state = EXEC_TAG()) == 0) { -#endif - rb_load(fname, 0); - rb_provide(feature); -#ifdef USE_THREAD - } - POP_TAG(); - rb_thread_loading_done(); - if (state) JUMP_TAG(state); + rb_provide(feature); + + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + rb_load(fname, 0); } -#endif + POP_TAG(); + rb_thread_loading_done(feature); + if (state) JUMP_TAG(state); + return Qtrue; } +VALUE +rb_require(fname) + const char *fname; +{ + return rb_f_require(Qnil, rb_str_new2(fname)); +} + static void set_method_visibility(self, argc, argv, ex) VALUE self; @@ -4804,8 +4947,7 @@ rb_mod_modfunc(argc, argv, module) id = rb_to_id(argv[i]); body = search_method(module, id, 0); if (body == 0 || body->nd_body == 0) { - rb_raise(rb_eNameError, "undefined method `%s' for module `%s'", - rb_id2name(id), rb_class2name(module)); + rb_bug("undefined method `%s'; can't happen", rb_id2name(id)); } rb_clear_cache_by_id(id); rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC); @@ -4846,11 +4988,13 @@ rb_mod_include(argc, argv, module) } void -rb_obj_call_init(obj) +rb_obj_call_init(obj, argc, argv) VALUE obj; + int argc; + VALUE *argv; { PUSH_ITER(rb_iterator_p()?ITER_PRE:ITER_NOT); - rb_funcall2(obj, init, ruby_frame->argc, ruby_frame->argv); + rb_funcall2(obj, init, argc, argv); POP_ITER(); } @@ -4866,7 +5010,7 @@ rb_class_new_instance(argc, argv, klass) rb_raise(rb_eTypeError, "can't create instance of virtual class"); } obj = rb_obj_alloc(klass); - rb_obj_call_init(obj); + rb_obj_call_init(obj, argc, argv); return obj; } @@ -4919,7 +5063,7 @@ errinfo_setter(val, id, var) ID id; VALUE *var; { - if (!rb_obj_is_kind_of(val, rb_eException)) { + if (!NIL_P(val) && !rb_obj_is_kind_of(val, rb_eException)) { rb_raise(rb_eTypeError, "assigning non-exception to $!"); } *var = val; @@ -4929,7 +5073,7 @@ static VALUE errat_getter(id) ID id; { - return get_backtrace(rb_errinfo); + return get_backtrace(ruby_errinfo); } static void @@ -4938,10 +5082,10 @@ errat_setter(val, id, var) ID id; VALUE *var; { - if (NIL_P(rb_errinfo)) { + if (NIL_P(ruby_errinfo)) { rb_raise(rb_eArgError, "$! not set"); } - set_backtrace(rb_errinfo, val); + set_backtrace(ruby_errinfo, val); } VALUE rb_f_global_variables(); @@ -5026,15 +5170,20 @@ rb_f_at_exit() return proc; } -static void -exec_end_proc() +void +rb_exec_end_proc() { struct end_proc_data *link = end_proc_data; + struct end_proc_data *tmp; + int status; while (link) { - (*link->func)(link->data); - link = link->next; + rb_protect((VALUE(*)())link->func, link->data, &status); + tmp = link->next; + free(link); + link = tmp; } + end_proc_data = 0; } void @@ -5047,6 +5196,7 @@ Init_eval() aref = rb_intern("[]"); aset = rb_intern("[]="); match = rb_intern("=~"); + missing = rb_intern("method_missing"); rb_global_variable((VALUE*)&top_scope); rb_global_variable((VALUE*)&ruby_eval_tree_begin); @@ -5055,7 +5205,7 @@ Init_eval() rb_global_variable((VALUE*)&ruby_dyna_vars); rb_define_virtual_variable("$@", errat_getter, errat_setter); - rb_define_hooked_variable("$!", &rb_errinfo, 0, errinfo_setter); + rb_define_hooked_variable("$!", &ruby_errinfo, 0, errinfo_setter); rb_define_global_function("eval", rb_f_eval, -1); rb_define_global_function("iterator?", rb_f_iterator_p, 0); @@ -5178,10 +5328,20 @@ static void blk_free(data) struct BLOCK *data; { - struct BLOCK *tmp; + struct FRAME *frame; + void *tmp; + frame = data->frame.prev; + while (frame) { + if (frame->argc > 0) + free(frame->argv); + tmp = frame; + frame = frame->prev; + free(tmp); + } while (data) { - free(data->frame.argv); + if (data->frame.argc > 0) + free(data->frame.argv); tmp = data; data = data->prev; free(tmp); @@ -5197,13 +5357,37 @@ blk_copy_prev(block) while (block->prev) { tmp = ALLOC_N(struct BLOCK, 1); MEMCPY(tmp, block->prev, struct BLOCK, 1); - tmp->frame.argv = ALLOC_N(VALUE, tmp->frame.argc); - MEMCPY(tmp->frame.argv, block->frame.argv, VALUE, tmp->frame.argc); + if (tmp->frame.argc > 0) { + tmp->frame.argv = ALLOC_N(VALUE, tmp->frame.argc); + MEMCPY(tmp->frame.argv, block->frame.argv, VALUE, tmp->frame.argc); + } + scope_dup(tmp->scope); block->prev = tmp; block = tmp; } } +static void +frame_dup(frame) + struct FRAME *frame; +{ + VALUE *argv; + struct FRAME *tmp; + + for (;;) { + if (frame->argc > 0) { + argv = ALLOC_N(VALUE, frame->argc); + MEMCPY(argv, frame->argv, VALUE, frame->argc); + frame->argv = argv; + } + frame->tmp = 0; /* should not preserve tmp */ + if (!frame->prev) break; + tmp = ALLOC(struct FRAME); + *tmp = *frame->prev; + frame->prev = tmp; + frame = tmp; + } +} static VALUE bind_clone(self) @@ -5213,10 +5397,10 @@ bind_clone(self) VALUE bind; Data_Get_Struct(self, struct BLOCK, orig); - bind = Data_Make_Struct(self,struct BLOCK,blk_mark,blk_free,data); + bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data); + CLONESETUP(bind,self); MEMCPY(data, orig, struct BLOCK, 1); - data->frame.argv = ALLOC_N(VALUE, orig->frame.argc); - MEMCPY(data->frame.argv, orig->frame.argv, VALUE, orig->frame.argc); + frame_dup(&data->frame); if (data->iter) { blk_copy_prev(data); @@ -5239,15 +5423,12 @@ rb_f_binding(self) bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data); *data = *ruby_block; -#ifdef USE_THREAD data->orig_thread = rb_thread_current(); -#endif data->iter = rb_f_iterator_p(); + frame_dup(&data->frame); if (ruby_frame->prev) { data->frame.last_func = ruby_frame->prev->last_func; } - data->frame.argv = ALLOC_N(VALUE, data->frame.argc); - MEMCPY(data->frame.argv, ruby_block->frame.argv, VALUE, data->frame.argc); if (data->iter) { blk_copy_prev(data); @@ -5319,22 +5500,19 @@ proc_s_new(klass) proc = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data); *data = *ruby_block; -#ifdef USE_THREAD data->orig_thread = rb_thread_current(); -#endif data->iter = data->prev?Qtrue:Qfalse; - data->frame.argv = ALLOC_N(VALUE, data->frame.argc); - MEMCPY(data->frame.argv, ruby_block->frame.argv, VALUE, data->frame.argc); + frame_dup(&data->frame); if (data->iter) { blk_copy_prev(data); } else { data->prev = 0; } + data->flags |= BLOCK_DYNAMIC; scope_dup(data->scope); proc_save_safe_level(proc); - rb_obj_call_init(proc); return proc; } @@ -5353,11 +5531,9 @@ blk_orphan(data) (data->scope->flag & SCOPE_NOSTACK)) { return 1; } -#ifdef USE_THREAD if (data->orig_thread != rb_thread_current()) { return 1; } -#endif return 0; } @@ -5372,6 +5548,15 @@ proc_call(proc, args) volatile int orphan; volatile int safe = safe_level; + Data_Get_Struct(proc, struct BLOCK, data); + orphan = blk_orphan(data); + + /* PUSH BLOCK from data */ + old_block = ruby_block; + ruby_block = data; + PUSH_ITER(ITER_CUR); + ruby_frame->iter = ITER_CUR; + if (TYPE(args) == T_ARRAY) { switch (RARRAY(args)->len) { case 0: @@ -5383,15 +5568,6 @@ proc_call(proc, args) } } - Data_Get_Struct(proc, struct BLOCK, data); - orphan = blk_orphan(data); - - /* PUSH BLOCK from data */ - old_block = ruby_block; - ruby_block = data; - PUSH_ITER(ITER_CUR); - ruby_frame->iter = ITER_CUR; - if (orphan) {/* orphan procedure */ if (rb_iterator_p()) { ruby_block->frame.iter = ITER_CUR; @@ -5405,7 +5581,7 @@ proc_call(proc, args) state = EXEC_TAG(); if (state == 0) { proc_set_safe_level(proc); - result = rb_yield_0(args, 0, 0); + result = rb_yield_0(args, 0, 0, Qtrue); } POP_TAG(); @@ -5435,6 +5611,31 @@ proc_call(proc, args) return result; } +static VALUE +proc_arity(proc) + VALUE proc; +{ + struct BLOCK *data; + NODE *list; + int n; + + Data_Get_Struct(proc, struct BLOCK, data); + if (data->var == 0) return FIX2INT(-1); + switch (nd_type(data->var)) { + default: + return INT2FIX(-2); + case NODE_MASGN: + list = data->var->nd_head; + n = 0; + while (list) { + n++; + list = list->nd_next; + } + if (data->var->nd_args) return INT2FIX(-n-1); + return INT2FIX(n); + } +} + static VALUE block_pass(self, node) VALUE self; @@ -5586,13 +5787,45 @@ method_call(argc, argv, method) return result; } +static VALUE +method_arity(method) + VALUE method; +{ + struct METHOD *data; + NODE *body; + int n; + + Data_Get_Struct(method, struct METHOD, data); + + body = data->body; + switch (nd_type(body)) { + case NODE_CFUNC: + if (body->nd_argc < 0) return INT2FIX(-1); + return INT2FIX(body->nd_argc); + case NODE_ZSUPER: + return INT2FIX(-1); + case NODE_ATTRSET: + return INT2FIX(1); + case NODE_IVAR: + return INT2FIX(0); + default: + body = body->nd_next; /* skip NODE_SCOPE */ + if (nd_type(body) == NODE_BLOCK) + body = body->nd_head; + if (!body) return INT2FIX(0); + n = body->nd_cnt; + if (body->nd_rest) n = -n-1; + return INT2FIX(n); + } +} + static VALUE method_inspect(method) VALUE method; { struct METHOD *data; VALUE str; - char *s; + const char *s; Data_Get_Struct(method, struct METHOD, data); str = rb_str_new2("#<"); @@ -5651,6 +5884,7 @@ Init_Proc() rb_define_singleton_method(rb_cProc, "new", proc_s_new, 0); 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_global_function("proc", rb_f_lambda, 0); rb_define_global_function("lambda", rb_f_lambda, 0); @@ -5663,14 +5897,13 @@ Init_Proc() rb_undef_method(CLASS_OF(rb_cMethod), "new"); rb_define_method(rb_cMethod, "call", method_call, -1); rb_define_method(rb_cMethod, "[]", method_call, -1); + rb_define_method(rb_cMethod, "arity", method_arity, 0); rb_define_method(rb_cMethod, "inspect", method_inspect, 0); rb_define_method(rb_cMethod, "to_s", method_inspect, 0); rb_define_method(rb_cMethod, "to_proc", method_proc, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); } -#ifdef USE_THREAD - static VALUE rb_eThreadError; int rb_thread_pending = 0; @@ -5728,19 +5961,18 @@ struct thread { struct SCOPE *scope; struct RVarmap *dyna_vars; struct BLOCK *block; - struct BLOCK *cblock; struct iter *iter; struct tag *tag; VALUE klass; VALUE wrapper; VALUE trace; - int misc; /* misc. states (vmode/rb_trap_immediate) */ + int flags; /* misc. states (vmode/rb_trap_immediate/raised) */ char *file; int line; - VALUE rb_errinfo; + VALUE errinfo; VALUE last_status; VALUE last_line; VALUE last_match; @@ -5760,10 +5992,12 @@ struct thread { VALUE thread; }; -static thread_t curr_thread; -static int num_waiting_on_fd; -static int num_waiting_on_timer; -static int num_waiting_on_join; +#define THREAD_RAISED 0x200 + +static thread_t curr_thread = 0; +static int num_waiting_on_fd = 0; +static int num_waiting_on_timer = 0; +static int num_waiting_on_join = 0; #define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next; #define END_FOREACH_FROM(f,x) } while (x != f) @@ -5801,9 +6035,10 @@ thread_mark(th) rb_gc_mark(th->scope); rb_gc_mark(th->dyna_vars); - rb_gc_mark(th->rb_errinfo); + rb_gc_mark(th->errinfo); rb_gc_mark(th->last_line); rb_gc_mark(th->last_match); + rb_mark_tbl(th->locals); /* mark data in copied stack */ if (th->status == THREAD_KILLED) return; @@ -5817,20 +6052,24 @@ thread_mark(th) frame = th->frame; while (frame && frame != top_frame) { frame = ADJ(frame); - if (frame->argv && !STACK(frame->argv)) { - rb_gc_mark_frame(frame); + rb_gc_mark_frame(frame); + if (frame->tmp) { + struct FRAME *tmp = ADJ(frame->tmp); + + while (tmp && tmp != top_frame) { + tmp = ADJ(tmp); + rb_gc_mark_frame(tmp); + tmp = tmp->prev; + } } frame = frame->prev; } block = th->block; while (block) { block = ADJ(block); - if (block->frame.argv && !STACK(block->frame.argv)) { - rb_gc_mark_frame(&block->frame); - } + rb_gc_mark_frame(&block->frame); block = block->prev; } - rb_mark_tbl(th->locals); } void @@ -5889,11 +6128,10 @@ rb_thread_save_context(th) th->wrapper = ruby_wrapper; th->dyna_vars = ruby_dyna_vars; th->block = ruby_block; - th->cblock = ruby_calling_block; - th->misc = scope_vmode | (rb_trap_immediate<<8); + th->flags = scope_vmode | (rb_trap_immediate<<8); th->iter = ruby_iter; th->tag = prot_tag; - th->rb_errinfo = rb_errinfo; + th->errinfo = ruby_errinfo; th->last_status = rb_last_status; th->last_line = rb_lastline_get(); th->last_match = rb_backref_get(); @@ -5902,8 +6140,6 @@ rb_thread_save_context(th) th->trace = trace_func; th->file = ruby_sourcefile; th->line = ruby_sourceline; - - th->locals = 0; } static void rb_thread_restore_context _((thread_t,int)); @@ -5925,6 +6161,14 @@ static char *th_raise_file; static int th_raise_line; static VALUE th_cmd; static int th_sig; +static char *th_signm; + +#define RESTORE_NORMAL 0 +#define RESTORE_FATAL 1 +#define RESTORE_INTERRUPT 2 +#define RESTORE_TRAP 3 +#define RESTORE_RAISE 4 +#define RESTORE_SIGNAL 5 static void rb_thread_restore_context(th, exit) @@ -5952,12 +6196,11 @@ rb_thread_restore_context(th, exit) ruby_wrapper = th->wrapper; ruby_dyna_vars = th->dyna_vars; ruby_block = th->block; - ruby_calling_block = th->cblock; - scope_vmode = th->misc&SCOPE_MASK; - rb_trap_immediate = th->misc>>8; + scope_vmode = th->flags&SCOPE_MASK; + rb_trap_immediate = (th->flags&0x100)?1:0; ruby_iter = th->iter; prot_tag = th->tag; - rb_errinfo = th->rb_errinfo; + ruby_errinfo = th->errinfo; rb_last_status = th->last_status; safe_level = th->safe; @@ -5974,26 +6217,31 @@ rb_thread_restore_context(th, exit) rb_backref_set(tmp->last_match); switch (ex) { - case 1: + case RESTORE_FATAL: JUMP_TAG(TAG_FATAL); break; - case 2: + case RESTORE_INTERRUPT: rb_interrupt(); break; - case 3: + case RESTORE_TRAP: rb_trap_eval(th_cmd, th_sig); errno = EINTR; break; - case 4: + case RESTORE_SIGNAL: + rb_raise(rb_eSignal, "SIG%s", th_signm); + break; + + case RESTORE_RAISE: ruby_frame->last_func = 0; ruby_sourcefile = th_raise_file; ruby_sourceline = th_raise_line; rb_f_raise(th_raise_argc, th_raise_argv); break; + case RESTORE_NORMAL: default: longjmp(tmp->context, 1); } @@ -6033,15 +6281,46 @@ rb_thread_dead(th) return th->status == THREAD_KILLED; } +void +rb_thread_fd_close(fd) + int fd; +{ + thread_t th; + + FOREACH_THREAD(th) { + if ((th->wait_for & WAIT_FD) && th->fd == 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); + } + } + END_FOREACH(th); +} + static void rb_thread_deadlock() { +#if 1 curr_thread = main_thread; th_raise_argc = 1; th_raise_argv[0] = rb_exc_new2(rb_eFatal, "Thread: deadlock"); th_raise_file = ruby_sourcefile; th_raise_line = ruby_sourceline; + rb_thread_restore_context(main_thread, RESTORE_RAISE); +#else + static int invoked = 0; + + if (invoked) return; + invoked = 1; + rb_prohibit_interrupt = 1; + ruby_errinfo = rb_exc_new2(rb_eFatal, "Thread: deadlock"); + set_backtrace(ruby_errinfo, make_backtrace()); rb_abort(); +#endif } void @@ -6053,7 +6332,9 @@ rb_thread_schedule() select_err: rb_thread_pending = 0; - if (curr_thread == curr_thread->next) return; + if (curr_thread == curr_thread->next + && curr_thread->status == THREAD_RUNNABLE) + return; next = 0; curr = curr_thread; /* starting thread */ @@ -6143,7 +6424,14 @@ rb_thread_schedule() n = select(max+1, &readfds, 0, 0, delay_ptr); if (n < 0) { if (rb_trap_pending) rb_trap_exec(); - goto select_err; + switch (errno) { + case EBADF: + case ENOMEM: + n = 0; + break; + default: + goto select_err; + } } if (n > 0) { /* Some descriptors are ready. @@ -6172,13 +6460,18 @@ rb_thread_schedule() curr_thread->file = ruby_sourcefile; curr_thread->line = ruby_sourceline; FOREACH_THREAD_FROM(curr, th) { - fprintf(stderr, "%s:%d:deadlock 0x%x: %d:%d %s\n", + fprintf(stderr, "%s:%d:deadlock 0x%lx: %d:%d %s\n", th->file, th->line, th->thread, th->status, th->wait_for, th==main_thread?"(main)":""); + if (th->status == THREAD_STOPPED) { + next = th; + } } END_FOREACH_FROM(curr, th); /* raise fatal error to main thread */ rb_thread_deadlock(); + rb_thread_ready(next); + next->status = THREAD_TO_KILL; } if (next->status == THREAD_RUNNABLE && next == curr_thread) { return; @@ -6195,9 +6488,9 @@ rb_thread_schedule() curr_thread = next; if (next->status == THREAD_TO_KILL) { /* execute ensure-clause if any */ - rb_thread_restore_context(next, 1); + rb_thread_restore_context(next, RESTORE_FATAL); } - rb_thread_restore_context(next, 0); + rb_thread_restore_context(next, RESTORE_NORMAL); } void @@ -6213,20 +6506,20 @@ rb_thread_wait_fd(fd) rb_thread_schedule(); } -void +int rb_thread_fd_writable(fd) int fd; { struct timeval zero; fd_set fds; - if (curr_thread == curr_thread->next) return; + if (curr_thread == curr_thread->next) return 1; zero.tv_sec = zero.tv_usec = 0; for (;;) { FD_ZERO(&fds); FD_SET(fd, &fds); - if (select(fd+1, 0, &fds, 0, &zero) == 1) break; + if (select(fd+1, 0, &fds, 0, &zero) == 1) return 0; rb_thread_schedule(); } } @@ -6373,14 +6666,28 @@ rb_thread_join(thread) { thread_t th = rb_thread_check(thread); - if (rb_thread_dead(th)) return thread; - if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread) - rb_raise(rb_eThreadError, "Thread#join: deadlock"); - curr_thread->status = THREAD_STOPPED; - curr_thread->join = th; - num_waiting_on_join++; - curr_thread->wait_for |= WAIT_JOIN; - rb_thread_schedule(); + if (!rb_thread_dead(th)) { + if (th == curr_thread) + rb_raise(rb_eThreadError, "recursive join"); + if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread) + rb_raise(rb_eThreadError, "Thread#join: deadlock - mutual join"); + curr_thread->status = THREAD_STOPPED; + curr_thread->join = th; + num_waiting_on_join++; + curr_thread->wait_for |= WAIT_JOIN; + rb_thread_schedule(); + } + + if (!NIL_P(th->errinfo) && (th->flags & THREAD_RAISED)) { + VALUE oldbt = get_backtrace(th->errinfo); + VALUE errat = make_backtrace(); + + if (TYPE(oldbt) == T_ARRAY && RARRAY(oldbt)->len > 0) { + rb_ary_unshift(errat, rb_ary_entry(oldbt, 0)); + } + set_backtrace(th->errinfo, errat); + rb_exc_raise(th->errinfo); + } return thread; } @@ -6406,7 +6713,7 @@ rb_thread_main() return main_thread->thread; } -static VALUE +VALUE rb_thread_wakeup(thread) VALUE thread; { @@ -6419,7 +6726,7 @@ rb_thread_wakeup(thread) return thread; } -static VALUE +VALUE rb_thread_run(thread) VALUE thread; { @@ -6465,14 +6772,14 @@ rb_thread_pass() return Qnil; } -static VALUE +VALUE rb_thread_stop() { rb_thread_critical = 0; - curr_thread->status = THREAD_STOPPED; if (curr_thread == curr_thread->next) { rb_raise(rb_eThreadError, "stopping only thread"); } + curr_thread->status = THREAD_STOPPED; rb_thread_schedule(); return Qnil; @@ -6510,19 +6817,19 @@ rb_thread_sleep_forever() rb_thread_schedule(); } -static int rb_thread_abort; +static int thread_abort; static VALUE rb_thread_s_abort_exc() { - return rb_thread_abort?Qtrue:Qfalse; + return thread_abort?Qtrue:Qfalse; } static VALUE rb_thread_s_abort_exc_set(self, val) VALUE self, val; { - rb_thread_abort = RTEST(val); + thread_abort = RTEST(val); return val; } @@ -6545,41 +6852,44 @@ rb_thread_abort_exc_set(thread, val) return val; } +#define THREAD_ALLOC(th) do {\ + th = ALLOC(struct thread);\ +\ + th->status = 0;\ + th->result = 0;\ + th->errinfo = Qnil;\ +\ + th->stk_ptr = 0;\ + th->stk_len = 0;\ + th->stk_max = 0;\ + th->wait_for = 0;\ + th->fd = 0;\ + th->delay = 0.0;\ + th->join = 0;\ +\ + th->frame = 0;\ + th->scope = 0;\ + th->klass = 0;\ + th->wrapper = 0;\ + th->dyna_vars = 0;\ + th->block = 0;\ + th->iter = 0;\ + th->tag = 0;\ + th->errinfo = 0;\ + th->last_status = 0;\ + th->last_line = 0;\ + th->last_match = 0;\ + th->abort = 0;\ + th->locals = 0;\ +} while(0) + static thread_t rb_thread_alloc(klass) VALUE klass; { thread_t th; - th = ALLOC(struct thread); - th->status = THREAD_RUNNABLE; - - th->status = 0; - th->result = 0; - th->rb_errinfo = Qnil; - - th->stk_ptr = 0; - th->stk_len = 0; - th->stk_max = 0; - th->wait_for = 0; - th->fd = 0; - th->delay = 0.0; - th->join = 0; - - th->frame = 0; - th->scope = 0; - th->klass = 0; - th->wrapper = 0; - th->dyna_vars = 0; - th->block = 0; - th->iter = 0; - th->tag = 0; - th->rb_errinfo = 0; - th->last_status = 0; - th->last_line = 0; - th->last_match = 0; - th->abort = 0; - + THREAD_ALLOC(th); th->thread = Data_Wrap_Struct(klass, thread_mark, thread_free, th); if (curr_thread) { @@ -6654,6 +6964,7 @@ rb_thread_create_0(fn, arg, klass) VALUE klass; { thread_t th = rb_thread_alloc(klass); + volatile VALUE thread = th->thread; enum thread_status status; int state; @@ -6673,7 +6984,7 @@ rb_thread_create_0(fn, arg, klass) FL_SET(ruby_scope, SCOPE_SHARED); rb_thread_save_context(curr_thread); if (setjmp(curr_thread->context)) { - return th->thread; + return thread; } PUSH_TAG(PROT_THREAD); @@ -6687,24 +6998,25 @@ rb_thread_create_0(fn, arg, klass) POP_TAG(); status = th->status; rb_thread_remove(); - if (state && status != THREAD_TO_KILL && !NIL_P(rb_errinfo)) { + if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) { + th->flags |= THREAD_RAISED; if (state == TAG_FATAL) { /* fatal error within this thread, need to stop whole script */ - main_thread->rb_errinfo = rb_errinfo; + main_thread->errinfo = ruby_errinfo; rb_thread_cleanup(); } - else if (rb_obj_is_kind_of(rb_errinfo, rb_eSystemExit)) { + else if (rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) { /* delegate exception to main_thread */ - rb_thread_raise(1, &rb_errinfo, main_thread->thread); + rb_thread_raise(1, &ruby_errinfo, main_thread->thread); } - else if (rb_thread_abort || curr_thread->abort || RTEST(rb_debug)) { + else if (thread_abort || th->abort || RTEST(ruby_debug)) { VALUE err = rb_exc_new(rb_eSystemExit, 0, 0); error_print(); /* exit on main_thread */ rb_thread_raise(1, &err, main_thread->thread); } else { - curr_thread->rb_errinfo = rb_errinfo; + th->errinfo = ruby_errinfo; } } rb_thread_schedule(); @@ -6731,7 +7043,7 @@ rb_thread_yield(arg, th) thread_t th; { scope_dup(ruby_block->scope); - return rb_yield_0(th->thread, 0, 0); + return rb_yield_0(th->thread, 0, 0, Qfalse); } static VALUE @@ -6751,14 +7063,6 @@ rb_thread_value(thread) thread_t th = rb_thread_check(thread); rb_thread_join(thread); - if (!NIL_P(th->rb_errinfo)) { - VALUE oldbt = get_backtrace(th->rb_errinfo); - VALUE errat = make_backtrace(); - - rb_ary_unshift(errat, rb_ary_entry(oldbt, 0)); - set_backtrace(th->rb_errinfo, errat); - rb_exc_raise(th->rb_errinfo); - } return th->result; } @@ -6770,7 +7074,8 @@ rb_thread_status(thread) thread_t th = rb_thread_check(thread); if (rb_thread_dead(th)) { - if (NIL_P(th->rb_errinfo)) return Qfalse; + if (NIL_P(th->errinfo) && (th->flags & THREAD_RAISED)) + return Qfalse; return Qnil; } @@ -6808,8 +7113,8 @@ rb_thread_cleanup() FOREACH_THREAD(th) { if (th != curr_thread && th->status != THREAD_KILLED) { + rb_thread_ready(th); th->status = THREAD_TO_KILL; - th->wait_for = 0; } } END_FOREACH(th); @@ -6844,7 +7149,27 @@ rb_thread_interrupt() return; } curr_thread = main_thread; - rb_thread_restore_context(curr_thread, 2); + rb_thread_restore_context(curr_thread, RESTORE_INTERRUPT); +} + +void +rb_thread_signal_raise(sig) + char *sig; +{ + if (sig == 0) return; /* should not happen */ + rb_thread_critical = 0; + if (curr_thread == main_thread) { + rb_thread_ready(curr_thread); + rb_raise(rb_eSignal, "SIG%s", sig); + } + rb_thread_ready(main_thread); + rb_thread_save_context(curr_thread); + if (setjmp(curr_thread->context)) { + return; + } + th_signm = sig; + curr_thread = main_thread; + rb_thread_restore_context(curr_thread, RESTORE_SIGNAL); } void @@ -6866,7 +7191,7 @@ rb_thread_trap_eval(cmd, sig) th_cmd = cmd; th_sig = sig; curr_thread = main_thread; - rb_thread_restore_context(curr_thread, 3); + rb_thread_restore_context(curr_thread, RESTORE_TRAP); } static VALUE @@ -6895,36 +7220,33 @@ rb_thread_raise(argc, argv, thread) th_raise_argc = argc; th_raise_file = ruby_sourcefile; th_raise_line = ruby_sourceline; - rb_thread_restore_context(curr_thread, 4); + rb_thread_restore_context(curr_thread, RESTORE_RAISE); return Qnil; /* not reached */ } -static thread_t loading_thread; -static int loading_nest; +static st_table *loading_tbl; static int rb_thread_loading(feature) - char *feature; + const char *feature; { - if (curr_thread != curr_thread->next && loading_thread) { - while (loading_thread != curr_thread) { - rb_thread_schedule(); - CHECK_INTS; - } - if (rb_provided(feature)) return Qtrue; /* no need to load */ + if (!rb_provided(feature)) return Qfalse; /* need to load */ + if (!loading_tbl) { + loading_tbl = st_init_strtable(); } - - loading_thread = curr_thread; - loading_nest++; - - return Qfalse; + while (st_lookup(loading_tbl, feature, 0)) { + CHECK_INTS; + rb_thread_schedule(); + } + return Qtrue; } static void -rb_thread_loading_done() +rb_thread_loading_done(feature) + const char *feature; { - if (--loading_nest == 0) { - loading_thread = 0; + if (loading_tbl) { + st_delete(loading_tbl, feature, 0); } } @@ -6957,13 +7279,11 @@ rb_thread_local_aset(thread, id, val) ID id; VALUE val; { - thread_t th; - + thread_t th = rb_thread_check(thread); if (safe_level >= 4 && !FL_TEST(thread, FL_TAINT)) rb_raise(rb_eSecurityError, "Insecure: can't modify thread values"); - th = rb_thread_check(thread); if (!th->locals) { th->locals = st_init_numtable(); } @@ -7002,39 +7322,17 @@ rb_callcc(self) VALUE self; { volatile VALUE cont; - thread_t th = ALLOC(struct thread); - - th->status = THREAD_RUNNABLE; - - th->status = 0; - th->result = 0; - th->rb_errinfo = Qnil; - - th->stk_ptr = 0; - th->stk_len = 0; - th->stk_max = 0; - th->wait_for = 0; - th->fd = 0; - th->delay = 0.0; - th->join = 0; - - th->frame = 0; - th->scope = 0; - th->klass = 0; - th->dyna_vars = 0; - th->block = 0; - th->iter = 0; - th->tag = 0; - th->rb_errinfo = 0; - th->last_status = 0; - th->last_line = 0; - th->last_match = 0; - th->abort = 0; + thread_t th; + struct tag *tag; + THREAD_ALLOC(th); th->thread = cont = Data_Wrap_Struct(rb_cContinuation, thread_mark, thread_free, th); FL_SET(ruby_scope, SCOPE_DONT_RECYCLE); + for (tag=prot_tag; tag; tag=tag->prev) { + scope_dup(tag->scope); + } rb_thread_save_context(th); if (setjmp(th->context)) { return th->result; @@ -7063,7 +7361,7 @@ rb_continuation_call(argc, argv, cont) th->result = rb_ary_new4(argc, argv); break; } - rb_thread_restore_context(th, 0); + rb_thread_restore_context(th, RESTORE_NORMAL); return Qnil; } @@ -7114,9 +7412,8 @@ Init_Thread() rb_cContinuation = rb_define_class("Continuation", rb_cObject); rb_undef_method(CLASS_OF(rb_cContinuation), "new"); rb_define_method(rb_cContinuation, "call", rb_continuation_call, -1); - rb_define_method(rb_mKernel, "callcc", rb_callcc, 0); + rb_define_global_function("callcc", rb_callcc, 0); } -#endif static VALUE rb_f_catch(dmy, tag) @@ -7129,7 +7426,7 @@ rb_f_catch(dmy, tag) t = rb_to_id(tag); PUSH_TAG(t); if ((state = EXEC_TAG()) == 0) { - val = rb_yield_0(tag, 0, 0); + val = rb_yield_0(tag, 0, 0, Qfalse); } else if (state == TAG_THROW && t == prot_tag->dst) { val = prot_tag->retval; @@ -7150,7 +7447,7 @@ catch_i(tag) VALUE rb_catch(tag, proc, data) - char *tag; + const char *tag; VALUE (*proc)(); VALUE data; { @@ -7174,13 +7471,11 @@ rb_f_throw(argc, argv) tt->dst = t; break; } -#ifdef USE_THREAD if (tt->tag == PROT_THREAD) { rb_raise(rb_eThreadError, "uncaught throw `%s' in thread 0x%x", rb_id2name(t), curr_thread); } -#endif tt = tt->prev; } if (!tt) { @@ -7194,7 +7489,7 @@ rb_f_throw(argc, argv) void rb_throw(tag, val) - char *tag; + const char *tag; VALUE val; { VALUE argv[2]; @@ -7208,7 +7503,6 @@ rb_throw(tag, val) static void return_check() { -#ifdef USE_THREAD struct tag *tt = prot_tag; while (tt) { @@ -7221,6 +7515,5 @@ return_check() } tt = tt->prev; } -#endif } -- cgit v1.2.3