Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.137.2.85 diff -u -r1.137.2.85 eval.c --- eval.c 2001/12/20 18:18:19 1.137.2.85 +++ eval.c 2002/02/24 05:39:22 @@ -2,8 +2,8 @@ eval.c - - $Author: nobu $ - $Date: 2001/12/20 18:18:19 $ + $Author: matz $ + $Date: 2002/02/21 07:14:11 $ created at: Thu Jun 10 14:22:17 JST 1993 Copyright (C) 1993-2001 Yukihiro Matsumoto @@ -190,6 +190,79 @@ static struct cache_entry cache[CACHE_SIZE]; +#define CACHE_DAMAGE_TYPE 2 +#if CACHE_DAMAGE_TYPE == 1 +static int cache_damage[CACHE_SIZE]; +#define Init_cache() ((void)0) +#define CACHE_DAMAGE(id) (cache_damage[(id)&CACHE_MASK]++) +#define CACHE_UNDAMAGE(id) ((cache_damage[(id)&CACHE_MASK] == 1) ? (cache_damage +[(id)&CACHE_MASK] = 0) : 0) +#define CACHE_DAMAGED_P(id) (cache_damage[(id)&CACHE_MASK]) +#define CACHE_CLEAR_DAMAGE() memset(cache_damage, 0, sizeof cache_damage) +#elif CACHE_DAMAGE_TYPE == 2 +static st_table *cache_damage; +#define Init_cache() (cache_damage = st_init_numtable_with_size(CACHE_SIZE)) +#define CACHE_DAMAGE(id) st_insert(cache_damage, (char *)(id), NULL) +#define CACHE_UNDAMAGE(id) ((void)(id)) +#define CACHE_DAMAGED_P(id) st_delete(cache_damage, (char **)&(id), NULL) +#define CACHE_CLEAR_DAMAGE() st_cleanup_safe(cache_damage, 0) +#elif CACHE_DAMAGE_TYPE == 3 +struct damaged_entry { + ID mid; + struct damaged_entry *next; +}; +static struct damaged_entry *cache_damage[CACHE_SIZE]; +#define Init_cache() ((void)0) +#define CACHE_DAMAGE(id) damage(id) +#define CACHE_UNDAMAGE(id) ((void)(id)) +#define CACHE_DAMAGED_P(id) damaged_p(id) +#define CACHE_CLEAR_DAMAGE() clear_damage() +static void damage(id) + ID id; +{ + struct damaged_entry *ent, **entp = &cache_damage[id & CACHE_MASK]; + for (ent = *entp; ent; ent = ent->next) { + if (ent->mid == id) return; + } + ent = ALLOC(struct damaged_entry); + ent->mid = id; + ent->next = *entp; + *entp = ent; +} +static int damaged_p(id) + ID id; +{ + struct damaged_entry *ent = cache_damage[id & CACHE_MASK]; + for (; ent; ent = ent->next) { + if (ent->mid == id) return 1; + } + return 0; +} +static void clear_damage() +{ + struct damaged_entry **ent = cache_damage, **end = cache_damage + CACHE_SIZE +; + for (; ent < end; ent++) { + struct damaged_entry *ptr = *ent; + if (ptr) { + do { + struct damaged_entry *next = ptr->next; + free(ptr); + ptr = next; + } while (ptr); + *ent = NULL; + } + } +} +#else +#define Init_cache() ((void)0) +#define CACHE_DAMAGE(id) ((void)(id)) +#define CACHE_UNDAMAGE(id) ((void)(id)) +#define CACHE_DAMAGED_P(id) (1) +#define CACHE_CLEAR_DAMAGE() ((void)0) +#endif + + void rb_clear_cache() { @@ -200,6 +273,7 @@ ent->mid = 0; ent++; } + CACHE_CLEAR_DAMAGE(); } static void @@ -208,6 +282,8 @@ { struct cache_entry *ent, *end; + if( !CACHE_DAMAGED_P(id) ) return; + CACHE_UNDAMAGE(id); ent = cache; end = ent + CACHE_SIZE; while (ent < end) { if (ent->mid == id) { @@ -231,6 +307,7 @@ rb_raise(rb_eSecurityError, "Insecure: can't define method"); } if (OBJ_FROZEN(klass)) rb_error_frozen("class/module"); + rb_clear_cache_by_id(mid); body = NEW_METHOD(node, noex); st_insert(RCLASS(klass)->m_tbl, mid, body); } @@ -264,6 +341,7 @@ NODE * volatile body; struct cache_entry *ent; + CACHE_DAMAGE(id); if ((body = search_method(klass, id, &origin)) == 0 || !body->nd_body) { /* store empty info in cache */ ent = cache + EXPR1(klass, id); @@ -313,7 +391,7 @@ rb_raise(rb_eSecurityError, "Insecure: can't remove method"); } if (OBJ_FROZEN(klass)) rb_error_frozen("class/module"); - if (!st_delete(RCLASS(klass)->m_tbl, &mid, &body)) { + if (!st_delete(RCLASS(klass)->m_tbl, &mid, &body) || !body->nd_body) { rb_raise(rb_eNameError, "method `%s' not defined in %s", rb_id2name(mid), rb_class2name(klass)); } @@ -353,7 +431,6 @@ body->nd_noex |= NOEX_UNDEF; } else { - rb_clear_cache_by_id(mid); rb_add_method(klass, mid, 0, NOEX_UNDEF); } } @@ -403,7 +480,6 @@ body->nd_noex = noex; } else { - rb_clear_cache_by_id(name); rb_add_method(klass, name, NEW_ZSUPER(), noex); } } @@ -473,14 +549,12 @@ sprintf(buf, "@%s", name); attriv = rb_intern(buf); if (read) { - rb_clear_cache_by_id(id); rb_add_method(klass, id, NEW_IVAR(attriv), noex); rb_funcall(klass, added, 1, ID2SYM(id)); } if (write) { sprintf(buf, "%s=", name); id = rb_intern(buf); - rb_clear_cache_by_id(id); rb_add_method(klass, id, NEW_ATTRSET(attriv), noex); rb_funcall(klass, added, 1, ID2SYM(id)); } @@ -1017,6 +1091,7 @@ Init_stack(0); Init_heap(); + Init_cache(); PUSH_SCOPE(); ruby_scope->local_vars = 0; ruby_scope->local_tbl = 0; @@ -1169,6 +1244,8 @@ POP_ITER(); POP_TAG(); + trace_func = 0; + tracing = 0; ex = error_handle(ex); ruby_finalize(); exit(ex); @@ -1271,10 +1348,12 @@ ruby_frame->last_class = 0; ruby_frame->self = self; ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,ruby_wrapper,0,0); + PUSH_SCOPE(); val = rb_eval_string_protect(str, &status); ruby_top_self = self; + POP_SCOPE(); POP_FRAME(); POP_CLASS(); ruby_wrapper = wrapper; @@ -1549,7 +1628,6 @@ rb_raise(rb_eNameError, "undefined method `%s' for%s `%s'", rb_id2name(id),s0,rb_class2name(c)); } - rb_clear_cache_by_id(id); rb_add_method(klass, id, 0, NOEX_PUBLIC); } @@ -1757,7 +1835,23 @@ return 0; } check_bound: - if (rb_method_boundp(val, node->nd_mid, nd_type(node)== NODE_CALL)) { + { + int call = nd_type(node)== NODE_CALL; + if (call) { + int noex; + ID id = node->nd_mid; + + if (!rb_get_method_body(&val, &id, &noex)) + break; + if ((noex & NOEX_PRIVATE)) + break; + if ((noex & NOEX_PROTECTED)) { + if (!rb_obj_is_kind_of(self, rb_class_real(val))) + break; + } + } + else if (!rb_method_boundp(val, node->nd_mid, call)) + break; return arg_defined(self, node->nd_args, buf, "method"); } break; @@ -1833,10 +1927,7 @@ } break; } - self = rb_iv_get(ruby_cbase, "__attached__"); - /* fall through */ - case NODE_CVAR2: - if (rb_cvar_defined(rb_cvar_singleton(self), node->nd_vid)) { + if (rb_cvar_defined(rb_iv_get(ruby_cbase, "__attached__"), node->nd_vid)) { return "class variable"; } break; @@ -1857,8 +1948,9 @@ case T_MODULE: if (rb_const_defined_at(val, node->nd_mid)) return "constant"; + break; default: - if (rb_method_boundp(val, node->nd_mid, 1)) { + if (rb_method_boundp(CLASS_OF(val), node->nd_mid, 1)) { return "method"; } } @@ -2710,17 +2802,15 @@ rb_raise(rb_eTypeError, "no class/module to define class variable"); } result = rb_eval(self, node->nd_value); - if (FL_TEST(ruby_cbase, FL_SINGLETON)) { - rb_cvar_declare(rb_cvar_singleton(rb_iv_get(ruby_cbase, "__attached__")), - node->nd_vid, result); - break; + if (ruby_verbose && FL_TEST(ruby_cbase, FL_SINGLETON)) { + rb_warn("declaring singleton class variable"); } rb_cvar_declare(ruby_cbase, node->nd_vid, result); break; case NODE_CVASGN: result = rb_eval(self, node->nd_value); - rb_cvar_set(rb_cvar_singleton(self), node->nd_vid, result); + rb_cvar_set(ruby_cbase, node->nd_vid, result); break; case NODE_LVAR: @@ -2746,7 +2836,7 @@ result = ev_const_get(RNODE(ruby_frame->cbase), node->nd_vid, self); break; - case NODE_CVAR: /* normal method */ + case NODE_CVAR: if (NIL_P(ruby_cbase)) { result = rb_cvar_get(CLASS_OF(self), node->nd_vid); break; @@ -2755,10 +2845,7 @@ result = rb_cvar_get(ruby_cbase, node->nd_vid); break; } - self = rb_iv_get(ruby_cbase, "__attached__"); - /* fall through */ - case NODE_CVAR2: /* singleton method */ - result = rb_cvar_get(rb_cvar_singleton(self), node->nd_vid); + result = rb_cvar_get(rb_iv_get(ruby_cbase, "__attached__"), node->nd_vid); break; case NODE_BLOCK_ARG: @@ -2981,7 +3068,6 @@ } defn = copy_node_scope(node->nd_defn, ruby_cref); - rb_clear_cache_by_id(node->nd_mid); rb_add_method(ruby_class, node->nd_mid, defn, noex); if (scope_vmode == SCOPE_MODFUNC) { rb_add_method(rb_singleton_class(ruby_class), @@ -3027,7 +3113,6 @@ } defn = copy_node_scope(node->nd_defn, ruby_cref); defn->nd_rval = (VALUE)ruby_cref; - rb_clear_cache_by_id(node->nd_mid); rb_add_method(klass, node->nd_mid, defn, NOEX_PUBLIC|(body?body->nd_noex&NOEX_UNDEF:0)); rb_funcall(recv, singleton_added, 1, ID2SYM(node->nd_mid)); @@ -3098,8 +3183,9 @@ override_class: if (!super) super = rb_cObject; 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_class_inherited(super, klass); + rb_const_set(ruby_class, node->nd_cname, klass); } if (ruby_wrapper) { rb_extend_object(klass, ruby_wrapper); @@ -3151,17 +3237,28 @@ { VALUE klass; - klass = rb_eval(self, node->nd_recv); - if (rb_special_const_p(klass)) { - rb_raise(rb_eTypeError, "no virtual class for %s", - rb_class2name(CLASS_OF(klass))); - } - if (rb_safe_level() >= 4 && !OBJ_TAINTED(klass)) - rb_raise(rb_eSecurityError, "Insecure: can't extend object"); - if (FL_TEST(CLASS_OF(klass), FL_SINGLETON)) { - rb_clear_cache(); + result = rb_eval(self, node->nd_recv); + if (result == Qtrue) { + klass = rb_cTrueClass; + } + else if (result == Qfalse) { + klass = rb_cTrueClass; + } + else if (result == Qnil) { + klass = rb_cNilClass; } - klass = rb_singleton_class(klass); + else { + if (rb_special_const_p(result)) { + rb_raise(rb_eTypeError, "no virtual class for %s", + rb_class2name(CLASS_OF(result))); + } + if (rb_safe_level() >= 4 && !OBJ_TAINTED(result)) + rb_raise(rb_eSecurityError, "Insecure: can't extend object"); + if (FL_TEST(CLASS_OF(result), FL_SINGLETON)) { + rb_clear_cache(); + } + klass = rb_singleton_class(result); + } if (ruby_wrapper) { rb_extend_object(klass, ruby_wrapper); @@ -3520,6 +3617,8 @@ struct BLOCK * volatile block; struct SCOPE * volatile old_scope; struct FRAME frame; + char *const file = ruby_sourcefile; + int line = ruby_sourceline; int state; static unsigned serial = 1; @@ -3666,6 +3765,8 @@ if (ruby_scope->flag & SCOPE_DONT_RECYCLE) scope_dup(old_scope); ruby_scope = old_scope; + ruby_sourcefile = file; + ruby_sourceline = line; if (state) { if (!block->tag) { switch (state & TAG_MASK) { @@ -3796,14 +3897,14 @@ break; case NODE_CVDECL: - if (!FL_TEST(ruby_cbase, FL_SINGLETON)) { - rb_cvar_declare(ruby_cbase, lhs->nd_vid, val); - break; + if (ruby_verbose && FL_TEST(ruby_cbase, FL_SINGLETON)) { + rb_warn("declaring singleton class variable"); } - self = rb_iv_get(ruby_cbase, "__attached__"); - /* fall through */ + rb_cvar_declare(ruby_cbase, lhs->nd_vid, val); + break; + case NODE_CVASGN: - rb_cvar_set(rb_cvar_singleton(self), lhs->nd_vid, val); + rb_cvar_set(ruby_cbase, lhs->nd_vid, val); break; case NODE_MASGN: @@ -4199,8 +4300,17 @@ void rb_stack_check() { - if (stack_length(0) > STACK_LEVEL_MAX) { - rb_raise(rb_eSysStackError, "stack level too deep"); + static int overflowing = 0; + if (!overflowing && stack_length(0) > STACK_LEVEL_MAX) { + int state; + overflowing = 1; + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + rb_raise(rb_eSysStackError, "stack level too deep"); + } + POP_TAG(); + overflowing = 0; + JUMP_TAG(state); } } @@ -4571,7 +4681,7 @@ /* 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) + if (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); @@ -5636,7 +5746,6 @@ if (body == 0 || body->nd_body == 0) { 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); rb_funcall(module, singleton_added, 1, ID2SYM(id)); } @@ -6250,22 +6359,30 @@ } } -static void -proc_set_safe_level(data) +static int +proc_get_safe_level(data) VALUE data; { if (OBJ_TAINTED(data)) { switch (RBASIC(data)->flags & PROC_TMASK) { case PROC_T3: - ruby_safe_level = 3; - break; + return 3; case PROC_T4: - ruby_safe_level = 4; - break; + return 4; case PROC_TMAX: - ruby_safe_level = 5; - break; + return 5; } + return 3; + } + return 0; +} + +static void +proc_set_safe_level(data) + VALUE data; +{ + if (OBJ_TAINTED(data)) { + ruby_safe_level = proc_get_safe_level(data); } } @@ -6498,7 +6615,10 @@ volatile int safe = ruby_safe_level; if (NIL_P(block)) { - return rb_eval(self, node->nd_iter); + PUSH_ITER(ITER_NOT); + result = rb_eval(self, node->nd_iter); + POP_ITER(); + return result; } if (rb_obj_is_kind_of(block, rb_cMethod)) { block = method_proc(block); @@ -6508,6 +6628,12 @@ rb_class2name(CLASS_OF(block))); } + if (rb_safe_level() >= 1 && OBJ_TAINTED(block)) { + if (rb_safe_level() > proc_get_safe_level(block)) { + rb_raise(rb_eSecurityError, "Insecure: tainted block value"); + } + } + Data_Get_Struct(block, struct BLOCK, data); orphan = blk_orphan(data); @@ -6744,6 +6870,7 @@ method = Data_Make_Struct(rb_cMethod,struct METHOD,bm_mark,free,bound); *bound = *data; bound->recv = recv; + bound->oklass = CLASS_OF(recv); return method; } @@ -6902,7 +7029,6 @@ else { noex = NOEX_PUBLIC; } - rb_clear_cache_by_id(id); rb_add_method(mod, id, node, noex); if (scope_vmode == SCOPE_MODFUNC) { rb_add_method(rb_singleton_class(mod), id, node, NOEX_PUBLIC); @@ -7616,7 +7742,8 @@ if (select_timeout && n == 0) { if (now < 0.0) now = timeofday(); FOREACH_THREAD_FROM(curr, th) { - if ((th->wait_for & (WAIT_SELECT|WAIT_TIME)) && th->delay <= now) { + if (((th->wait_for&(WAIT_SELECT|WAIT_TIME)) == (WAIT_SELECT|WAIT_TIME)) && + th->delay <= now) { th->status = THREAD_RUNNABLE; th->wait_for = 0; th->select_value = 0;