summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c553
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;
}