|
diff --git a/common.mk b/common.mk |
|
index 349aa9a..d044d76 100644 |
|
--- a/common.mk |
|
+++ b/common.mk |
|
@@ -73,6 +73,7 @@ COMMONOBJS = array.$(OBJEXT) \ |
|
regexec.$(OBJEXT) \ |
|
regparse.$(OBJEXT) \ |
|
regsyntax.$(OBJEXT) \ |
|
+ ref.$(OBJEXT) \ |
|
ruby.$(OBJEXT) \ |
|
safe.$(OBJEXT) \ |
|
signal.$(OBJEXT) \ |
|
@@ -712,6 +713,8 @@ regparse.$(OBJEXT): {$(VPATH)}regparse.c {$(VPATH)}regparse.h \ |
|
$(RUBY_H_INCLUDES) |
|
regsyntax.$(OBJEXT): {$(VPATH)}regsyntax.c {$(VPATH)}regint.h \ |
|
{$(VPATH)}regenc.h {$(VPATH)}oniguruma.h $(RUBY_H_INCLUDES) |
|
+ref.$(OBJEXT): {$(VPATH)}ref.c {$(VPATH)}ruby.h {$(VPATH)}vm_core.h \ |
|
+ {$(VPATH)}node.h {$(VPATH)}gc.h |
|
ruby.$(OBJEXT): {$(VPATH)}ruby.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \ |
|
$(ENCODING_H_INCLUDES) {$(VPATH)}eval_intern.h $(VM_CORE_H_INCLUDES) \ |
|
{$(VPATH)}dln.h {$(VPATH)}internal.h |
|
diff --git a/compile.c b/compile.c |
|
index 3daa607..aebbab7 100644 |
|
--- a/compile.c |
|
+++ b/compile.c |
|
@@ -4517,6 +4517,14 @@ enum compile_array_type_t { |
|
} |
|
break; |
|
} |
|
+ case NODE_LVAR_REF:{ |
|
+ if (!poped) { |
|
+ ID id = node->nd_vid; |
|
+ int idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id); |
|
+ ADD_INSN2(ret, nd_line(node), reflocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq))); |
|
+ } |
|
+ break; |
|
+ } |
|
case NODE_LVAR:{ |
|
if (!poped) { |
|
ID id = node->nd_vid; |
|
@@ -4527,6 +4535,17 @@ enum compile_array_type_t { |
|
} |
|
break; |
|
} |
|
+ case NODE_DVAR_REF:{ |
|
+ int lv, idx, ls; |
|
+ if (!poped) { |
|
+ idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls); |
|
+ if (idx < 0) { |
|
+ rb_bug("unknown dvar (%s)", rb_id2name(node->nd_vid)); |
|
+ } |
|
+ ADD_INSN2(ret, nd_line(node), reflocal, INT2FIX(ls - idx), INT2FIX(lv)); |
|
+ } |
|
+ break; |
|
+ } |
|
case NODE_DVAR:{ |
|
int lv, idx, ls; |
|
debugi("nd_vid", node->nd_vid); |
|
@@ -4555,6 +4574,12 @@ enum compile_array_type_t { |
|
} |
|
break; |
|
} |
|
+ case NODE_IVAR_REF:{ |
|
+ if(!poped) { |
|
+ ADD_INSN1(ret, nd_line(node), refivar, ID2SYM(node->nd_vid)); |
|
+ } |
|
+ break; |
|
+ } |
|
case NODE_CONST:{ |
|
debugi("nd_vid", node->nd_vid); |
|
|
|
@@ -4584,6 +4609,12 @@ enum compile_array_type_t { |
|
} |
|
break; |
|
} |
|
+ case NODE_CVAR_REF:{ |
|
+ if (!poped) { |
|
+ ADD_INSN1(ret, nd_line(node), refcvar, ID2SYM(node->nd_vid)); |
|
+ } |
|
+ break; |
|
+ } |
|
case NODE_NTH_REF:{ |
|
if (!poped) { |
|
ADD_INSN2(ret, nd_line(node), getspecial, INT2FIX(1) /* '~' */, |
|
diff --git a/inits.c b/inits.c |
|
index fe0aade..c39a039 100644 |
|
--- a/inits.c |
|
+++ b/inits.c |
|
@@ -61,5 +61,6 @@ |
|
CALL(Complex); |
|
CALL(version); |
|
CALL(vm_trace); |
|
+ CALL(ref); |
|
} |
|
#undef CALL |
|
diff --git a/insns.def b/insns.def |
|
index c339180..1772607 100644 |
|
--- a/insns.def |
|
+++ b/insns.def |
|
@@ -249,6 +249,41 @@ setglobal |
|
SET_GLOBAL((VALUE)entry, val); |
|
} |
|
|
|
+DEFINE_INSN |
|
+reflocal |
|
+(lindex_t idx, rb_num_t level) |
|
+() |
|
+(VALUE ref) |
|
+{ |
|
+ int i, lev = (int)level; |
|
+ VALUE env, *ep = GET_EP(); |
|
+ |
|
+ for(i = 0; i < lev; i++) { |
|
+ ep = GET_PREV_EP(ep); |
|
+ } |
|
+ |
|
+ env = ep[1]; /* ep[1] holds the env val referencing the ep */ |
|
+ ref = rb_ref_local(env, idx); |
|
+} |
|
+ |
|
+DEFINE_INSN |
|
+refivar |
|
+(ID var) |
|
+() |
|
+(VALUE ref) |
|
+{ |
|
+ ref = rb_ref_ivar(GET_SELF(), var); |
|
+} |
|
+ |
|
+DEFINE_INSN |
|
+refcvar |
|
+(ID var) |
|
+() |
|
+(VALUE ref) |
|
+{ |
|
+ NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); |
|
+ ref = rb_ref_cvar(vm_get_cvar_base(cref, GET_CFP()), var); |
|
+} |
|
|
|
/**********************************************************/ |
|
/* deal with values */ |
|
diff --git a/internal.h b/internal.h |
|
index 2f05f48..f3a0a56 100644 |
|
--- a/internal.h |
|
+++ b/internal.h |
|
@@ -250,6 +250,13 @@ struct rb_execarg { |
|
VALUE rb_reg_compile(VALUE str, int options, const char *sourcefile, int sourceline); |
|
VALUE rb_reg_check_preprocess(VALUE); |
|
|
|
+/* ref.c */ |
|
+VALUE rb_ref_local(VALUE env, size_t index); |
|
+VALUE rb_ref_ivar(VALUE self, ID var); |
|
+VALUE rb_ref_cvar(VALUE self, ID var); |
|
+struct rb_global_entry; |
|
+VALUE rb_ref_global(struct rb_global_entry* ge); |
|
+ |
|
/* signal.c */ |
|
int rb_get_next_signal(void); |
|
int rb_sigaltstack_size(void); |
|
diff --git a/node.h b/node.h |
|
index 0918609..6e6b41f 100644 |
|
--- a/node.h |
|
+++ b/node.h |
|
@@ -112,16 +112,26 @@ enum node_type { |
|
#define NODE_YIELD NODE_YIELD |
|
NODE_LVAR, |
|
#define NODE_LVAR NODE_LVAR |
|
+ NODE_LVAR_REF, |
|
+#define NODE_LVAR_REF NODE_LVAR_REF |
|
NODE_DVAR, |
|
#define NODE_DVAR NODE_DVAR |
|
+ NODE_DVAR_REF, |
|
+#define NODE_DVAR_REF NODE_DVAR_REF |
|
NODE_GVAR, |
|
#define NODE_GVAR NODE_GVAR |
|
+ NODE_GVAR_REF, |
|
+#define NODE_GVAR_REF NODE_GVAR_REF |
|
NODE_IVAR, |
|
#define NODE_IVAR NODE_IVAR |
|
+ NODE_IVAR_REF, |
|
+#define NODE_IVAR_REF NODE_IVAR_REF |
|
NODE_CONST, |
|
#define NODE_CONST NODE_CONST |
|
NODE_CVAR, |
|
#define NODE_CVAR NODE_CVAR |
|
+ NODE_CVAR_REF, |
|
+#define NODE_CVAR_REF NODE_CVAR_REF |
|
NODE_NTH_REF, |
|
#define NODE_NTH_REF NODE_NTH_REF |
|
NODE_BACK_REF, |
|
@@ -400,11 +410,16 @@ enum node_type { |
|
#define NEW_OP_ASGN_OR(i,val) NEW_NODE(NODE_OP_ASGN_OR,i,val,0) |
|
#define NEW_OP_ASGN_AND(i,val) NEW_NODE(NODE_OP_ASGN_AND,i,val,0) |
|
#define NEW_GVAR(v) NEW_NODE(NODE_GVAR,v,0,rb_global_entry(v)) |
|
+#define NEW_GVAR_REF(v) NEW_NODE(NODE_LIT,rb_ref_global(rb_global_entry(v)),0,0) |
|
#define NEW_LVAR(v) NEW_NODE(NODE_LVAR,v,0,0) |
|
+#define NEW_LVAR_REF(v) NEW_NODE(NODE_LVAR_REF,v,0,0) |
|
#define NEW_DVAR(v) NEW_NODE(NODE_DVAR,v,0,0) |
|
+#define NEW_DVAR_REF(v) NEW_NODE(NODE_DVAR_REF,v,0,0) |
|
#define NEW_IVAR(v) NEW_NODE(NODE_IVAR,v,0,0) |
|
+#define NEW_IVAR_REF(v) NEW_NODE(NODE_IVAR_REF,v,0,0) |
|
#define NEW_CONST(v) NEW_NODE(NODE_CONST,v,0,0) |
|
#define NEW_CVAR(v) NEW_NODE(NODE_CVAR,v,0,0) |
|
+#define NEW_CVAR_REF(v) NEW_NODE(NODE_CVAR_REF,v,0,0) |
|
#define NEW_NTH_REF(n) NEW_NODE(NODE_NTH_REF,0,n,0) |
|
#define NEW_BACK_REF(n) NEW_NODE(NODE_BACK_REF,0,n,0) |
|
#define NEW_MATCH(c) NEW_NODE(NODE_MATCH,c,0,0) |
|
diff --git a/parse.y b/parse.y |
|
index e42cd9e..3db1e57 100644 |
|
--- a/parse.y |
|
+++ b/parse.y |
|
@@ -811,6 +811,7 @@ static void token_info_pop(struct parser_params*, const char *token); |
|
%token tDSTAR "**arg" |
|
%token tAMPER "&" |
|
%token tLAMBDA "->" |
|
+%token tREF "\\" |
|
%token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG |
|
%token tSTRING_DBEG tSTRING_DEND tSTRING_DVAR tSTRING_END tLAMBEG |
|
|
|
@@ -2573,6 +2574,32 @@ primary : literal |
|
| qsymbols |
|
| var_ref |
|
| backref |
|
+ | '\\' tIVAR |
|
+ { |
|
+ $$ = NEW_IVAR_REF($2); |
|
+ } |
|
+ | '\\' tCVAR |
|
+ { |
|
+ $$ = NEW_CVAR_REF($2); |
|
+ } |
|
+ | '\\' tGVAR |
|
+ { |
|
+ $$ = NEW_GVAR_REF($2); |
|
+ } |
|
+ | '\\' tIDENTIFIER |
|
+ { |
|
+ if(dyna_in_block() && dvar_defined($2)) { |
|
+ $$ = NEW_DVAR_REF($2); |
|
+ } else { |
|
+ /* if not a local variable, use this as an implicit definition */ |
|
+ if(!local_id($2)) { |
|
+ dyna_var($2); |
|
+ $$ = NEW_LVAR_REF($2); |
|
+ } else { |
|
+ $$ = NEW_LVAR_REF($2); |
|
+ } |
|
+ } |
|
+ } |
|
| tFID |
|
{ |
|
/*%%%*/ |
|
diff --git a/ref.c b/ref.c |
|
new file mode 100644 |
|
index 0000000..006c2e1 |
|
--- /dev/null |
|
+++ b/ref.c |
|
@@ -0,0 +1,198 @@ |
|
+#include "ruby/ruby.h" |
|
+#include "vm_core.h" |
|
+#include "node.h" |
|
+#include "gc.h" |
|
+ |
|
+VALUE rb_cRef; |
|
+ |
|
+typedef enum { |
|
+ REF_NONE, |
|
+ REF_LOCAL, |
|
+ REF_IVAR, |
|
+ REF_CVAR, |
|
+ REF_GVAR |
|
+} |
|
+rb_ref_type_t; |
|
+ |
|
+typedef struct { |
|
+ rb_ref_type_t type; |
|
+ union { |
|
+ struct { |
|
+ VALUE env; |
|
+ size_t index; |
|
+ } local; |
|
+ struct { |
|
+ VALUE self; |
|
+ ID var; |
|
+ } ivar, cvar; |
|
+ struct rb_global_entry *global; |
|
+ } as; |
|
+} |
|
+rb_ref_t; |
|
+ |
|
+static void |
|
+ref_mark(void *ptr) |
|
+{ |
|
+ rb_ref_t *ref = ptr; |
|
+ RUBY_MARK_ENTER("ref"); |
|
+ if(ref) { |
|
+ switch(ref->type) { |
|
+ case REF_NONE: |
|
+ break; |
|
+ case REF_LOCAL: |
|
+ RUBY_MARK_UNLESS_NULL(ref->as.local.env); |
|
+ break; |
|
+ case REF_IVAR: |
|
+ case REF_CVAR: |
|
+ RUBY_MARK_UNLESS_NULL(ref->as.ivar.self); |
|
+ break; |
|
+ case REF_GVAR: |
|
+ break; |
|
+ } |
|
+ } |
|
+ RUBY_MARK_LEAVE("ref"); |
|
+} |
|
+ |
|
+static void |
|
+ref_free(void* ptr) |
|
+{ |
|
+ if(ptr) { |
|
+ ruby_xfree(ptr); |
|
+ } |
|
+} |
|
+ |
|
+static size_t |
|
+ref_memsize(const void* ptr) |
|
+{ |
|
+ return ptr ? sizeof(rb_ref_t) : 0; |
|
+} |
|
+ |
|
+static const rb_data_type_t |
|
+ref_data_type = { |
|
+ "ref", |
|
+ { |
|
+ ref_mark, |
|
+ ref_free, |
|
+ ref_memsize |
|
+ } |
|
+}; |
|
+ |
|
+static VALUE |
|
+ref_alloc(VALUE klass) |
|
+{ |
|
+ VALUE obj; |
|
+ rb_ref_t* ref = NULL; |
|
+ obj = TypedData_Make_Struct(klass, rb_ref_t, &ref_data_type, ref); |
|
+ ref->type = REF_NONE; |
|
+ return obj; |
|
+} |
|
+ |
|
+VALUE |
|
+rb_ref_local(VALUE env, size_t idx) |
|
+{ |
|
+ VALUE refv = ref_alloc(rb_cRef); |
|
+ rb_ref_t* ref; |
|
+ TypedData_Get_Struct(refv, rb_ref_t, &ref_data_type, ref); |
|
+ ref->type = REF_LOCAL; |
|
+ ref->as.local.env = env; |
|
+ ref->as.local.index = idx; |
|
+ return refv; |
|
+} |
|
+ |
|
+VALUE |
|
+rb_ref_ivar(VALUE self, ID var) |
|
+{ |
|
+ VALUE refv = ref_alloc(rb_cRef); |
|
+ rb_ref_t* ref; |
|
+ TypedData_Get_Struct(refv, rb_ref_t, &ref_data_type, ref); |
|
+ ref->type = REF_IVAR; |
|
+ ref->as.ivar.self = self; |
|
+ ref->as.ivar.var = var; |
|
+ return refv; |
|
+} |
|
+ |
|
+VALUE |
|
+rb_ref_cvar(VALUE self, ID var) |
|
+{ |
|
+ VALUE refv = ref_alloc(rb_cRef); |
|
+ rb_ref_t* ref; |
|
+ TypedData_Get_Struct(refv, rb_ref_t, &ref_data_type, ref); |
|
+ ref->type = REF_CVAR; |
|
+ ref->as.ivar.self = self; |
|
+ ref->as.ivar.var = var; |
|
+ return refv; |
|
+} |
|
+ |
|
+VALUE |
|
+rb_ref_global(struct rb_global_entry* ge) |
|
+{ |
|
+ VALUE refv = ref_alloc(rb_cRef); |
|
+ rb_ref_t* ref; |
|
+ TypedData_Get_Struct(refv, rb_ref_t, &ref_data_type, ref); |
|
+ ref->type = REF_GVAR; |
|
+ ref->as.global = ge; |
|
+ return refv; |
|
+} |
|
+ |
|
+static VALUE* |
|
+local_ref(rb_ref_t* ref) |
|
+{ |
|
+ rb_env_t* env; |
|
+ GetEnvPtr(ref->as.local.env, env); |
|
+ return env->env + env->local_size - ref->as.local.index; |
|
+} |
|
+ |
|
+static VALUE |
|
+ref_value(VALUE self) |
|
+{ |
|
+ rb_ref_t* ref; |
|
+ TypedData_Get_Struct(self, rb_ref_t, &ref_data_type, ref); |
|
+ switch(ref->type) { |
|
+ case REF_NONE: |
|
+ rb_raise(rb_eTypeError, "Can't get value of uninitialized reference"); |
|
+ case REF_LOCAL: |
|
+ return *local_ref(ref); |
|
+ case REF_IVAR: |
|
+ return rb_ivar_get(ref->as.ivar.self, ref->as.ivar.var); |
|
+ case REF_CVAR: |
|
+ return rb_cvar_get(ref->as.cvar.self, ref->as.cvar.var); |
|
+ case REF_GVAR: |
|
+ return rb_gvar_get(ref->as.global); |
|
+ } |
|
+ return Qundef; |
|
+} |
|
+ |
|
+static VALUE |
|
+ref_value_set(VALUE self, VALUE val) |
|
+{ |
|
+ rb_ref_t* ref; |
|
+ TypedData_Get_Struct(self, rb_ref_t, &ref_data_type, ref); |
|
+ switch(ref->type) { |
|
+ case REF_NONE: |
|
+ rb_raise(rb_eTypeError, "Can't get value of uninitialized reference"); |
|
+ break; |
|
+ case REF_LOCAL: |
|
+ *local_ref(ref) = val; |
|
+ break; |
|
+ case REF_IVAR: |
|
+ rb_ivar_set(ref->as.ivar.self, ref->as.ivar.var, val); |
|
+ break; |
|
+ case REF_CVAR: |
|
+ rb_cvar_set(ref->as.cvar.self, ref->as.cvar.var, val); |
|
+ break; |
|
+ case REF_GVAR: |
|
+ rb_gvar_set(ref->as.global, val); |
|
+ break; |
|
+ } |
|
+ return val; |
|
+} |
|
+ |
|
+void |
|
+Init_ref() |
|
+{ |
|
+ rb_cRef = rb_define_class("Ref", rb_cObject); |
|
+ rb_define_alloc_func(rb_cRef, ref_alloc); |
|
+ rb_define_method(rb_cRef, "value", ref_value, 0); |
|
+ rb_define_method(rb_cRef, "value=", ref_value_set, 1); |
|
+} |
|
+ |