diff options
Diffstat (limited to 'variable.c')
-rw-r--r-- | variable.c | 553 |
1 files changed, 404 insertions, 149 deletions
diff --git a/variable.c b/variable.c index c6b180736d..9721c1c2d4 100644 --- a/variable.c +++ b/variable.c @@ -10,16 +10,14 @@ #include "ruby.h" #include "env.h" -#include "ident.h" #include "st.h" st_table *rb_global_tbl; st_table *rb_class_tbl; #define global_tbl rb_global_tbl #define class_tbl rb_class_tbl -#define instance_tbl (RBASIC(Qself)->iv_tbl) -VALUE rb_const_bound(); +VALUE rb_const_defined(); VALUE rb_const_get(); st_table * @@ -39,11 +37,31 @@ char * rb_class2path(class) VALUE class; { - VALUE path = rb_ivar_get_1(class, rb_intern("__classpath__")); + VALUE path; + + while (TYPE(class) == T_ICLASS) { + class = (VALUE)RCLASS(class)->super; + } + path = rb_ivar_get(class, rb_intern("__classpath__")); if (TYPE(path) != T_STRING) Bug("class path does not set properly"); return RSTRING(path)->ptr; } +VALUE +rb_class_path(class) + VALUE class; +{ + char *name = rb_class2path(class); + + if (strchr(name, ':')) { + VALUE ary = str_split(str_new2(name), ":"); + ary_pop(ary); + ary = ary_reverse(ary); + return ary_join(ary, str_new2("::")); + } + return str_new2(name); +} + void rb_set_class_path(class, under, name) VALUE class, under; @@ -58,7 +76,7 @@ rb_set_class_path(class, under, name) s = rb_class2path(under); str_cat(str, s, strlen(s)); } - rb_ivar_set_1(class, rb_intern("__classpath__"), str); + rb_ivar_set(class, rb_intern("__classpath__"), str); } VALUE @@ -88,7 +106,7 @@ rb_path2class(path) } *s = '\0'; id = rb_intern(name); - if (!rb_const_bound(class, id)) + if (!rb_const_defined(class, id)) Fail("%s not defined", name); class = rb_const_get(class, id); switch (TYPE(class)) { @@ -106,7 +124,39 @@ rb_name_class(class, id) VALUE class; ID id; { - rb_ivar_set_1(class, rb_intern("__classname__"), INT2FIX(id)); + rb_ivar_set(class, rb_intern("__classname__"), INT2FIX(id)); +} + +static st_table *autoload_tbl = 0; + +static void +rb_autoload_id(id, filename) + ID id; + char *filename; +{ + if (!autoload_tbl) { + autoload_tbl = new_idhash(); + } + st_insert(autoload_tbl, id, strdup(filename)); +} + +void +rb_autoload(class, filename) + char *class, *filename; +{ + rb_autoload_id(rb_intern(class), filename); +} + +VALUE +f_autoload(obj, class, file) + VALUE obj, class; + struct RString *file; +{ + ID id = rb_to_id(class); + + Check_Type(file, T_STRING); + rb_autoload_id(id, file->ptr); + return Qnil; } char * @@ -130,7 +180,7 @@ rb_class2name(class) class = (struct RClass*)class->super; } - name = rb_ivar_get_1(class, rb_intern("__classname__")); + name = rb_ivar_get(class, rb_intern("__classname__")); if (name) { name = FIX2INT(name); return rb_id2name((ID)name); @@ -138,45 +188,33 @@ rb_class2name(class) Bug("class 0x%x not named", class); } +struct trace_var { + void (*func)(); + void *data; + struct trace_var *next; +}; + struct global_entry { - enum { GLOBAL_VAL, GLOBAL_VAR, GLOBAL_UNDEF } mode; ID id; - union { - VALUE val; - VALUE *var; - } v; - VALUE (*get_hook)(); - VALUE (*set_hook)(); void *data; + VALUE (*getter)(); + void (*setter)(); + void (*marker)(); + int block_trace; + struct trace_var *trace; }; -static -mark_global_entry(key, entry) - ID key; - struct global_entry *entry; -{ - switch (entry->mode) { - case GLOBAL_VAL: - gc_mark(entry->v.val); /* normal global value */ - break; - case GLOBAL_VAR: - if (entry->v.var) - gc_mark(*entry->v.var); /* c variable pointer */ - break; - default: - break; - } - if (entry->data) { - gc_mark_maybe(entry->data); - } - return ST_CONTINUE; -} +static VALUE undef_getter(); +static void undef_setter(); +static void undef_marker(); -void -gc_mark_global_tbl() -{ - st_foreach(global_tbl, mark_global_entry, 0); -} +static VALUE val_getter(); +static void val_setter(); +static void val_marker(); + +static VALUE var_getter(); +static void var_setter(); +static void var_marker(); struct global_entry* rb_global_entry(id) @@ -188,22 +226,129 @@ rb_global_entry(id) entry = ALLOC(struct global_entry); st_insert(global_tbl, id, entry); entry->id = id; - entry->mode = GLOBAL_UNDEF; - entry->v.var = Qnil; - entry->get_hook = entry->set_hook = Qnil; + entry->data = 0; + entry->getter = undef_getter; + entry->setter = undef_setter; + entry->marker = undef_marker; + + entry->block_trace = 0; + entry->trace = 0; } return entry; } -void -rb_define_variable(name, var, get_hook, set_hook, data) - char *name; - VALUE *var; - VALUE (*get_hook)(); - VALUE (*set_hook)(); +static VALUE +undef_getter(id) + ID id; +{ + Warning("global var %s not initialized", rb_id2name(id)); + return Qnil; +} + +static void +undef_setter(val, id, data, entry) + VALUE val; + ID id; + void *data; + struct global_entry *entry; +{ + entry->getter = val_getter; + entry->setter = val_setter; + entry->marker = val_marker; + + entry->data = (void*)val; +} + +static void +undef_marker() +{ +} + +static VALUE +val_getter(id, val) + ID id; + VALUE val; +{ + return val; +} + +static void +val_setter(val, id, data, entry) + VALUE val; + ID id; + void *data; + struct global_entry *entry; +{ + entry->data = (void*)val; +} + +static void +val_marker(data) void *data; { + if (data) gc_mark_maybe(data); +} + +static VALUE +var_getter(id, var) + ID id; + VALUE *var; +{ + if (!var || !*var) return Qnil; + return *var; +} + +static void +var_setter(val, id, var) + VALUE val; + ID id; + VALUE *var; +{ + *var = val; +} + +static void +var_marker(var) + VALUE **var; +{ + if (var) gc_mark_maybe(*var); +} + +static void +readonly_setter(id, var, val) + ID id; + void *var; + VALUE val; +{ + Fail("Can't set variable %s", rb_id2name(id)); +} + +static int +mark_global_entry(key, entry) + ID key; struct global_entry *entry; +{ + struct trace_var *trace; + + (*entry->marker)(entry->data); + trace = entry->trace; + while (trace) { + if (trace->data) gc_mark_maybe(trace->data); + trace = trace->next; + } + return ST_CONTINUE; +} + +void +gc_mark_global_tbl() +{ + st_foreach(global_tbl, mark_global_entry, 0); +} + +static ID +global_id(name) + char *name; +{ ID id; if (name[0] == '$') id = rb_intern(name); @@ -213,80 +358,143 @@ rb_define_variable(name, var, get_hook, set_hook, data) strcpy(buf+1, name); id = rb_intern(buf); } + return id; +} + +void +rb_define_hooked_variable(name, var, getter, setter) + char *name; + VALUE *var; + VALUE (*getter)(); + void (*setter)(); +{ + struct global_entry *entry; + ID id = global_id(name); entry = rb_global_entry(id); - entry->mode = GLOBAL_VAR; - entry->v.var = var; - entry->get_hook = get_hook; - entry->set_hook = set_hook; - entry->data = data; + entry->data = (void*)var; + entry->getter = getter?getter:var_getter; + entry->setter = setter?setter:var_setter; + entry->marker = var_marker; } void -rb_define_varhook(name, get_hook, set_hook, data) +rb_define_variable(name, var) char *name; - VALUE (*get_hook)(); - VALUE (*set_hook)(); - void *data; + VALUE *var; { - struct global_entry *entry; + rb_define_hooked_variable(name, var, 0, 0); +} + +void +rb_define_readonly_variable(name, var) + char *name; + VALUE *var; +{ + rb_define_hooked_variable(name, var, 0, readonly_setter); +} + +void +rb_define_virtual_variable(name, getter, setter) + char *name; + VALUE (*getter)(); + void (*setter)(); +{ + if (!getter) getter = val_getter; + if (!setter) setter = readonly_setter; + rb_define_hooked_variable(name, 0, getter, setter); +} + +void rb_trace_eval(); + +void +rb_trace_eval(cmd, val) + VALUE cmd, val; +{ + rb_eval_cmd(cmd, ary_new3(1, val)); +} + +VALUE +f_trace_var(argc, argv) + int argc; + VALUE *argv; +{ + VALUE var, cmd; ID id; + struct global_entry *entry; + struct trace_var *trace; - if (name[0] == '$') id = rb_intern(name); - else { - char *buf = ALLOCA_N(char, strlen(name)+2); - buf[0] = '$'; - strcpy(buf+1, name); - id = rb_intern(buf); + if (rb_scan_args(argc, argv, "11", &var, &cmd) == 1) { + cmd = f_lambda(); } - + id = rb_to_id(var); if (!st_lookup(global_tbl, id, &entry)) { - entry = ALLOC(struct global_entry); - entry->id = id; - entry->mode = GLOBAL_VAL; - st_insert(global_tbl, id, entry); - } - else if (entry->mode == GLOBAL_UNDEF) { - entry->mode = GLOBAL_VAL; - } - entry->v.val = Qnil; - entry->get_hook = get_hook; - entry->set_hook = set_hook; - if (data) { - entry->data = data; + Fail("undefined global variable %s", rb_id2name(id)); } + trace = ALLOC(struct trace_var); + trace->next = entry->trace; + trace->func = rb_trace_eval; + trace->data = (void*)cmd; + entry->trace = trace; + + return Qnil; } VALUE -rb_readonly_hook(val, id) - VALUE val; - ID id; +f_untrace_var(obj, var) + VALUE obj, var; { - Fail("Can't set variable %s", rb_id2name(id)); - /* not reached */ + ID id; + struct global_entry *entry; + struct trace_var *trace; + VALUE ary; + + id = rb_to_id(var); + if (!st_lookup(global_tbl, id, &entry)) { + Fail("undefined global variable %s", rb_id2name(id)); + } + ary = ary_new(); + trace = entry->trace; + while (trace) { + struct trace_var *next = trace->next; + ary_push(ary, trace->data); + free(trace); + trace = next; + } + entry->trace = 0; + + return ary; } VALUE rb_gvar_get(entry) struct global_entry *entry; { - VALUE val; - - if (entry->get_hook) - val = (*entry->get_hook)(entry->id, entry->data); - switch (entry->mode) { - case GLOBAL_VAL: - return entry->v.val; + return (*entry->getter)(entry->id, entry->data, entry); +} - case GLOBAL_VAR: - if (entry->v.var == Qnil) return val; - return *entry->v.var; +struct trace_data { + struct trace_var *trace; + VALUE val; +}; + +static void +trace_ev(data) + struct trace_data *data; +{ + struct trace_var *trace = data->trace; - default: - break; + while (trace) { + (*trace->func)(trace->data, data->val); + trace = trace->next; } - Warning("global var %s not initialized", rb_id2name(entry->id)); - return Qnil; +} + +static void +trace_en(entry) + struct global_entry *entry; +{ + entry->block_trace = 0; } VALUE @@ -294,19 +502,15 @@ rb_gvar_set(entry, val) struct global_entry *entry; VALUE val; { - if (entry->set_hook) - (*entry->set_hook)(val, entry->id, entry->data); + struct trace_data trace; - if (entry->mode == GLOBAL_VAR) { - if (entry->v.var) { - *entry->v.var = val; - } - } - else { - if (entry->mode == GLOBAL_UNDEF) { - entry->mode = GLOBAL_VAL; - } - entry->v.val = val; + (*entry->setter)(val, entry->id, entry->data, entry); + + if (!entry->block_trace) { + entry->block_trace = 1; + trace.trace = entry->trace; + trace.val = val; + rb_ensure(trace_ev, &trace, trace_en, entry); } return val; } @@ -317,19 +521,21 @@ rb_gvar_set2(name, val) VALUE val; { struct global_entry *entry; - ID id; - id = rb_intern(name); - if (!st_lookup(global_tbl, id, &entry)) { - entry = rb_global_entry(id); - } - rb_gvar_set(entry, val); + entry = rb_global_entry(global_id(name)); + return rb_gvar_set(entry, val); +} - return val; +VALUE +rb_gvar_defined(entry) + struct global_entry *entry; +{ + if (entry->getter == undef_getter) return FALSE; + return TRUE; } VALUE -rb_ivar_get_1(obj, id) +rb_ivar_get(obj, id) struct RObject *obj; ID id; { @@ -352,14 +558,7 @@ rb_ivar_get_1(obj, id) } VALUE -rb_ivar_get(id) - ID id; -{ - return rb_ivar_get_1(Qself, id); -} - -VALUE -rb_ivar_set_1(obj, id, val) +rb_ivar_set(obj, id, val) struct RObject *obj; ID id; VALUE val; @@ -368,7 +567,7 @@ rb_ivar_set_1(obj, id, val) case T_OBJECT: case T_CLASS: case T_MODULE: - if (obj->iv_tbl == Qnil) obj->iv_tbl = new_idhash(); + if (!obj->iv_tbl) obj->iv_tbl = new_idhash(); st_insert(obj->iv_tbl, id, val); break; default: @@ -380,11 +579,19 @@ rb_ivar_set_1(obj, id, val) } VALUE -rb_ivar_set(id, val) +rb_ivar_defined(obj, id) + struct RObject *obj; ID id; - VALUE val; { - return rb_ivar_set_1(Qself, id, val); + switch (TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + if (obj->iv_tbl && st_lookup(obj->iv_tbl, id, 0)) + return TRUE; + break; + } + return FALSE; } VALUE @@ -393,40 +600,58 @@ rb_const_get(class, id) ID id; { VALUE value; + struct RClass *tmp; - while (class) { - if (class->iv_tbl && st_lookup(class->iv_tbl, id, &value)) { + tmp = class; + while (tmp) { + if (tmp->iv_tbl && st_lookup(tmp->iv_tbl, id, &value)) { return value; } - if (BUILTIN_TYPE(class) == T_MODULE) { - class = RCLASS(C_Object); - } - else { - class = class->super; - } + tmp = tmp->super; + } + if (BUILTIN_TYPE(class) == T_MODULE) { + return rb_const_get(cObject, id); } /* pre-defined class */ if (st_lookup(class_tbl, id, &value)) return value; - /* here comes autoload code in the future. */ + /* autoload */ + if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) { + char *modname; + VALUE module; - Fail("Uninitialized constant %s", rb_id2name(id)); + st_delete(autoload_tbl, &id, &modname); + module = str_new2(modname); + free(modname); + f_require(Qnil, module); + return rb_const_get(class, id); + } + + /* Uninitialized constant */ + if (class && (VALUE)class != cObject) + Fail("Uninitialized constant %s::%s", + RSTRING(rb_class_path(class))->ptr, + rb_id2name(id)); + else + Fail("Uninitialized constant %s",rb_id2name(id)); /* not reached */ } VALUE -rb_const_bound(class, id) +rb_const_defined(class, id) struct RClass *class; ID id; { while (class) { - if (class->iv_tbl && st_lookup(class->iv_tbl, id, Qnil)) { + if (class->iv_tbl && st_lookup(class->iv_tbl, id, 0)) { return TRUE; } class = class->super; } - if (st_lookup(class_tbl, id, Qnil)) + if (st_lookup(class_tbl, id, 0)) + return TRUE; + if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) return TRUE; return FALSE; } @@ -437,10 +662,10 @@ rb_const_set(class, id, val) ID id; VALUE val; { - if (rb_const_bound(class, id)) - Fail("already initialized constnant"); + if (rb_const_defined(class, id)) + Fail("already initialized constnant %s", rb_id2name(id)); - if (class->iv_tbl == Qnil) class->iv_tbl = new_idhash(); + if (!class->iv_tbl) class->iv_tbl = new_idhash(); st_insert(class->iv_tbl, id, val); } @@ -460,7 +685,7 @@ rb_iv_get(obj, name) { ID id = rb_intern(name); - return rb_ivar_get_1(obj, id); + return rb_ivar_get(obj, id); } VALUE @@ -471,5 +696,35 @@ rb_iv_set(obj, name, val) { ID id = rb_intern(name); - return rb_ivar_set_1(obj, id, val); + return rb_ivar_set(obj, id, val); +} + +VALUE +backref_get() +{ + int cnt, max; + + if (!the_scope->local_vars) return Qnil; + for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) { + if (the_scope->local_tbl[cnt] == '~') { + cnt--; + if (the_scope->local_vars[cnt]) + return the_scope->local_vars[cnt]; + else + return 1; + } + } + return Qnil; +} + +void +backref_set(val) + VALUE val; +{ + int cnt, max; + + for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) { + if (the_scope->local_tbl[cnt] == '~') break; + } + the_scope->local_vars[cnt-1] = val; } |