>>>>> "Y" == Yukihiro Matsumoto <matz / ruby-lang.org> writes:

Y> Do you mean you've implemented private instance variable, even if it's
Y> incomplete?  If so, show me the code please.

 Well, here a quick diff (*WARNING* old 1.6.7 : you can see it with
 NODE_CVASGN)

 To understand what it do

pigeon% cat b.rb
#!./ruby
class A
   def initialize
      @a = 12
      @_m = [1,2]
   end
end

class B < A
   def initialize
      @_m = [2,4]
      @_n = 6
      super
   end
end

Marshal.dump(B.new, File.new("aa", "w"))
pigeon% 

pigeon% b.rb
pigeon% 

pigeon% cat c.rb
#!/usr/bin/ruby
class A
end
class B < A
   def b
      puts "a = #{@a.inspect} - _m = #{@_m.inspect} - _n = #{@_n.inspect}"
   end
end

Marshal.load(IO.readlines("aa", nil)[0]).b
pigeon%

pigeon% c.rb
a = 12 - _m = {B=>[2, 4], A=>[1, 2]} - _n = {B=>6}
pigeon% 


 Now the problems :

  1) you need to have 2 hash access for a private variable, rather than one
     for a public variable

  2) I use

#define ID_PRIVATE 0x07

  which is used for ID_JUNK in 1.7.*

 For 1.7 I see only a possibility

#define ID_JUNK 0x00
#define ID_PRIVATE 0x07

 but this means that *all* bits are used and it will not possible to add
 new types after this


Guy Decoux


diff -u ruby-1.6.7/env.h ruby-1.6.m/env.h
--- ruby-1.6.7/env.h	2001-03-21 09:04:11.000000000 +0100
+++ ruby-1.6.m/env.h	2002-09-07 15:55:19.000000000 +0200
@@ -20,6 +20,7 @@
     ID last_func;
     VALUE last_class;
     VALUE cbase;
+    VALUE curr_class;
     struct FRAME *prev;
     struct FRAME *tmp;
     char *file;
diff -u ruby-1.6.7/eval.c ruby-1.6.m/eval.c
--- ruby-1.6.7/eval.c	2002-02-27 05:50:29.000000000 +0100
+++ ruby-1.6.m/eval.c	2002-09-11 14:17:29.000000000 +0200
@@ -110,6 +110,8 @@
 static VALUE rb_cUnboundMethod;
 static VALUE umethod_bind _((VALUE, VALUE));
 static VALUE rb_mod_define_method _((int, VALUE*, VALUE));
+static VALUE remove_private_variable __((VALUE, VALUE));
+static VALUE rb_current_class __((void));
 
 static int scope_vmode;
 #define SCOPE_PUBLIC    0
@@ -500,18 +502,19 @@
 static struct FRAME *top_frame;
 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;			\
-    _frame.flags = FRAME_ALLOCA;	\
-    ruby_frame = &_frame;		\
+#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.curr_class = ruby_frame->curr_class;	\
+    _frame.argc = 0;				\
+    _frame.argv = 0;				\
+    _frame.flags = FRAME_ALLOCA;		\
+    ruby_frame = &_frame;
 
 #define POP_FRAME()  			\
     ruby_sourcefile = _frame.file;	\
@@ -1026,6 +1029,7 @@
 	rb_call_inits();
 	ruby_class = rb_cObject;
 	ruby_frame->self = ruby_top_self;
+	ruby_frame->curr_class = rb_cObject;
 	top_cref = rb_node_newnode(NODE_CREF,rb_cObject,0,0);
 	ruby_cref = top_cref;
 	ruby_frame->cbase = (VALUE)ruby_cref;
@@ -1266,6 +1270,7 @@
     ruby_top_self = rb_obj_clone(ruby_top_self);
     rb_extend_object(ruby_top_self, ruby_wrapper);
     PUSH_FRAME();
+    ruby_frame->curr_class = ruby_class;
     ruby_frame->last_func = 0;
     ruby_frame->last_class = 0;
     ruby_frame->self = self;
@@ -2708,7 +2713,12 @@
 
       case NODE_IASGN:
 	result = rb_eval(self, node->nd_value);
-	rb_ivar_set(self, node->nd_vid, result);
+	if (rb_is_instance_id(node->nd_vid)) {
+	    rb_ivar_set(self, node->nd_vid, result);
+	}
+	else {
+	    rb_pvar_set(self, rb_current_class(), node->nd_vid, result);
+	}
 	break;
 
       case NODE_CDECL:
@@ -2732,7 +2742,11 @@
 
       case NODE_CVASGN:
 	result = rb_eval(self, node->nd_value);
-	rb_cvar_set(ruby_cbase, node->nd_vid, result);
+	if (!FL_TEST(ruby_cbase, FL_SINGLETON)) {
+	    rb_cvar_set(ruby_cbase, node->nd_vid, result);
+	    break;
+	}
+	rb_cvar_set(rb_iv_get(ruby_cbase, "__attached__"), node->nd_vid, result);
 	break;
 
       case NODE_LVAR:
@@ -2751,7 +2765,12 @@
 	break;
 
       case NODE_IVAR:
-	result = rb_ivar_get(self, node->nd_vid);
+        if (rb_is_instance_id(node->nd_vid)) {
+	    result = rb_ivar_get(self, node->nd_vid);
+	}
+	else {
+	    result = rb_pvar_get(self, rb_current_class(), node->nd_vid);
+	}
 	break;
 
       case NODE_CONST:
@@ -2941,7 +2960,13 @@
 	if (ruby_frame->argc != 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]);
+        if (rb_is_instance_id(node->nd_vid)) {
+	    result = rb_ivar_set(self, node->nd_vid, ruby_frame->argv[0]);
+	}
+	else {
+	    result = rb_pvar_set(self, rb_current_class(), node->nd_vid,
+				 ruby_frame->argv[0]);
+	}
 	break;
 
       case NODE_DEFN:
@@ -3242,6 +3267,8 @@
     PUSH_SCOPE();
     PUSH_VARS();
 
+    ruby_frame->curr_class = ruby_class;
+
     if (node->nd_tbl) {
 	VALUE *vars = TMP_ALLOC(node->nd_tbl[0]+1);
 	*vars++ = (VALUE)node;
@@ -3570,6 +3597,7 @@
 	ruby_dyna_vars = block->dyna_vars;
     }
     ruby_class = klass?klass:block->klass;
+    ruby_frame->curr_class = ruby_class;
     if (!klass) self = block->self;
     node = block->body;
 
@@ -3826,7 +3854,11 @@
 	break;
 
       case NODE_CVASGN:
-	rb_cvar_set(ruby_cbase, lhs->nd_vid, val);
+	if (!FL_TEST(ruby_cbase, FL_SINGLETON)) {
+	    rb_cvar_set(ruby_cbase, lhs->nd_vid, val);
+	    break;
+	}
+	rb_cvar_set(rb_iv_get(ruby_cbase, "__attached__"), lhs->nd_vid, val);
 	break;
 
       case NODE_MASGN:
@@ -4362,6 +4394,9 @@
     ruby_frame->self = recv;
     ruby_frame->argc = argc;
     ruby_frame->argv = argv;
+    if (nd_type(body) != NODE_CFUNC) {
+	ruby_frame->curr_class = klass;
+    }
 
     switch (nd_type(body)) {
       case NODE_CFUNC:
@@ -4902,6 +4937,7 @@
     if (TYPE(ruby_class) == T_ICLASS) {
 	ruby_class = RBASIC(ruby_class)->klass;
     }
+    ruby_frame->curr_class = ruby_class;
     PUSH_TAG(PROT_NONE);
     if ((state = EXEC_TAG()) == 0) {
 	NODE *node;
@@ -5043,6 +5079,7 @@
     ruby_frame->last_class = _frame.prev->last_class;
     ruby_frame->argc = _frame.prev->argc;
     ruby_frame->argv = _frame.prev->argv;
+    ruby_frame->curr_class = ruby_class;
     if (ruby_cbase != under) {
 	ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,under,0,ruby_frame->cbase);
     }
@@ -5248,6 +5285,7 @@
     ruby_frame->last_class = 0;
     ruby_frame->self = self;
     ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,ruby_class,0,0);
+    ruby_frame->curr_class = ruby_class;
     PUSH_SCOPE();
     /* default visibility is private at loading toplevel */
     SCOPE_SET(SCOPE_PRIVATE);
@@ -6005,6 +6043,8 @@
     rb_define_method(rb_mKernel, "send", rb_f_send, -1);
     rb_define_method(rb_mKernel, "__send__", rb_f_send, -1);
     rb_define_method(rb_mKernel, "instance_eval", rb_obj_instance_eval, -1);
+    rb_define_private_method(rb_mKernel, "remove_private_variable",
+			     remove_private_variable, 1);
 
     rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
     rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
@@ -9091,3 +9131,16 @@
 	tt = tt->prev;
     }
 }
+
+static VALUE
+rb_current_class()
+{
+    return ruby_frame->curr_class;
+}
+
+static VALUE
+remove_private_variable(obj, name)
+    VALUE obj, name;
+{
+    return rb_obj_remove_private_variable(obj, rb_current_class(), name);
+}
diff -u ruby-1.6.7/gc.c ruby-1.6.m/gc.c
--- ruby-1.6.7/gc.c	2002-02-13 10:02:15.000000000 +0100
+++ ruby-1.6.m/gc.c	2002-09-07 16:11:11.000000000 +0200
@@ -911,6 +911,7 @@
 {
     mark_locations_array(frame->argv, frame->argc);
     rb_gc_mark(frame->cbase);
+    rb_gc_mark(frame->curr_class);
 }
 
 #ifdef __GNUC__
diff -u ruby-1.6.7/intern.h ruby-1.6.m/intern.h
--- ruby-1.6.7/intern.h	2002-02-27 05:50:30.000000000 +0100
+++ ruby-1.6.m/intern.h	2002-09-11 12:12:50.000000000 +0200
@@ -267,6 +267,7 @@
 void rb_parser_while_loop _((int, int));
 int rb_is_const_id _((ID));
 int rb_is_instance_id _((ID));
+int rb_is_private_id _((ID));
 int rb_is_class_id _((ID));
 VALUE rb_backref_get _((void));
 void rb_backref_set _((VALUE));
@@ -370,6 +371,11 @@
 void rb_mark_generic_ivar _((VALUE));
 void rb_mark_generic_ivar_tbl _((void));
 void rb_free_generic_ivar _((VALUE));
+VALUE rb_pvar_get _((VALUE, VALUE, ID));
+VALUE rb_pvar_set _((VALUE, VALUE, ID, VALUE));
+VALUE rb_pvar_defined _((VALUE, VALUE, ID));
+VALUE rb_obj_private_variables _((VALUE, VALUE));
+VALUE rb_obj_remove_private_variable _((VALUE, VALUE, VALUE));
 VALUE rb_ivar_get _((VALUE, ID));
 VALUE rb_ivar_set _((VALUE, ID, VALUE));
 VALUE rb_ivar_defined _((VALUE, ID));
diff -u ruby-1.6.7/marshal.c ruby-1.6.m/marshal.c
--- ruby-1.6.7/marshal.c	2002-02-28 07:52:47.000000000 +0100
+++ ruby-1.6.m/marshal.c	2002-09-08 13:50:33.000000000 +0200
@@ -755,19 +755,41 @@
     return v;
 }
 
+struct obj_id {
+    VALUE obj;
+    ID id;
+};
+
+static VALUE
+r_i_pvar(ary, st)
+    VALUE ary;
+    struct obj_id *st;
+{
+    rb_pvar_set(st->obj, RARRAY(ary)->ptr[0], st->id, RARRAY(ary)->ptr[1]);
+    return Qnil;
+}
+
 static void
 r_ivar(obj, arg)
     VALUE obj;
     struct load_arg *arg;
 {
     long len;
+    struct obj_id st;
 
     len = r_long(arg);
     if (len > 0) {
 	while (len--) {
 	    ID id = r_symbol(arg);
 	    VALUE val = r_object(arg);
-	    rb_ivar_set(obj, id, val);
+	    if (rb_is_private_id(id)) {
+		st.obj = obj;
+		st.id = id;
+		rb_iterate(rb_each, val, r_i_pvar, (VALUE)&st);
+	    }
+	    else {
+		rb_ivar_set(obj, id, val);
+	    }
 	}
     }
 }
diff -u ruby-1.6.7/object.c ruby-1.6.m/object.c
--- ruby-1.6.7/object.c	2002-02-04 09:09:10.000000000 +0100
+++ ruby-1.6.m/object.c	2002-09-11 12:14:15.000000000 +0200
@@ -1165,7 +1165,7 @@
     rb_define_method(rb_mKernel, "instance_variables", rb_obj_instance_variables, 0);
     rb_define_private_method(rb_mKernel, "remove_instance_variable",
 			     rb_obj_remove_instance_variable, 1);
-
+    rb_define_method(rb_mKernel, "private_variables", rb_obj_private_variables, 1);
     rb_define_method(rb_mKernel, "instance_of?", rb_obj_is_instance_of, 1);
     rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
     rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1);
diff -u ruby-1.6.7/parse.y ruby-1.6.m/parse.y
--- ruby-1.6.7/parse.y	2002-02-20 05:28:51.000000000 +0100
+++ ruby-1.6.m/parse.y	2002-09-08 12:25:58.000000000 +0200
@@ -22,12 +22,14 @@
 
 #define ID_SCOPE_SHIFT 3
 #define ID_SCOPE_MASK 0x07
+
 #define ID_LOCAL    0x01
 #define ID_INSTANCE 0x02
 #define ID_GLOBAL   0x03
 #define ID_ATTRSET  0x04
 #define ID_CONST    0x05
 #define ID_CLASS    0x06
+#define ID_PRIVATE  0x07
 
 #define is_notop_id(id) ((id)>LAST_TOKEN)
 #define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
@@ -36,6 +38,7 @@
 #define is_attrset_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET)
 #define is_const_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CONST)
 #define is_class_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CLASS)
+#define is_private_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_PRIVATE)
 
 NODE *ruby_eval_tree_begin = 0;
 NODE *ruby_eval_tree = 0;
@@ -673,7 +676,7 @@
 			    if ($2 == tOROP) {
 				$<node>3->nd_value = $4;
 				$$ = NEW_OP_ASGN_OR(gettable($1), $<node>3);
-				if (is_instance_id($1)) {
+				if (is_instance_id($1) || is_private_id($1)) {
 				    $$->nd_aid = $1;
 				}
 			    }
@@ -4182,7 +4185,7 @@
     else if (is_global_id(id)) {
 	return NEW_GVAR(id);
     }
-    else if (is_instance_id(id)) {
+    else if (is_instance_id(id) || is_private_id(id)) {
 	return NEW_IVAR(id);
     }
     else if (is_const_id(id)) {
@@ -4237,7 +4240,7 @@
     else if (is_global_id(id)) {
 	return NEW_GASGN(id, val);
     }
-    else if (is_instance_id(id)) {
+    else if (is_instance_id(id) || is_private_id(id)) {
 	return NEW_IASGN(id, val);
     }
     else if (is_const_id(id)) {
@@ -4955,6 +4958,8 @@
       case '@':
 	if (name[1] == '@')
 	    id |= ID_CLASS;
+	else if (name[1] == '_')
+	    id |= ID_PRIVATE;
 	else
 	    id |= ID_INSTANCE;
 	break;
@@ -5065,6 +5070,14 @@
     return Qfalse;
 }
 
+int
+rb_is_private_id(id)
+    ID id;
+{
+    if (is_private_id(id)) return Qtrue;
+    return Qfalse;
+}
+
 static void
 special_local_set(c, val)
     char c;
diff -u ruby-1.6.7/ruby.h ruby-1.6.m/ruby.h
--- ruby-1.6.7/ruby.h	2002-02-26 14:08:17.000000000 +0100
+++ ruby-1.6.m/ruby.h	2002-09-08 14:00:04.000000000 +0200
@@ -466,6 +466,8 @@
 VALUE rb_gv_get _((const char*));
 VALUE rb_iv_get _((VALUE, const char*));
 VALUE rb_iv_set _((VALUE, const char*, VALUE));
+VALUE rb_pv_get _((VALUE, VALUE, const char*));
+VALUE rb_pv_set _((VALUE, VALUE, const char*, VALUE));
 
 VALUE rb_equal _((VALUE,VALUE));
 
diff -u ruby-1.6.7/variable.c ruby-1.6.m/variable.c
--- ruby-1.6.7/variable.c	2002-02-19 05:48:04.000000000 +0100
+++ ruby-1.6.m/variable.c	2002-09-11 12:12:17.000000000 +0200
@@ -885,6 +885,7 @@
     VALUE clone, obj;
 {
     st_table *tbl;
+    VALUE val;
 
     if (!generic_iv_tbl) return;
     if (st_lookup(generic_iv_tbl, obj, &tbl)) {
@@ -892,6 +893,295 @@
     }
 }
 
+static void
+check_valid_class(klass)
+    VALUE klass;
+{
+    switch (TYPE(klass)) {
+    case T_CLASS:
+    case T_ICLASS:
+    case T_MODULE:
+    case T_TRUE:
+    case T_FALSE:
+    case T_NIL:
+	break;
+    default:
+	rb_raise(rb_eArgError, "expected a class object");
+    }
+}
+
+static VALUE
+private_ivar_get(obj, klass, id)
+    VALUE obj, klass;
+    ID id;
+{
+    st_table *tbl;
+    VALUE val;
+
+    if (!generic_iv_tbl) return Qnil;
+    if (!st_lookup(generic_iv_tbl, obj, &tbl)) return Qnil;
+    if (st_lookup(tbl, id, &val) && TYPE(val) == T_HASH &&
+	st_lookup(RHASH(val)->tbl, klass, &val)) {
+	return val;
+    }
+    return Qnil;
+}
+
+VALUE
+rb_pvar_get(obj, klass, id)
+    VALUE klass, obj;
+    ID id;
+{
+    VALUE val;
+
+    check_valid_class(klass);
+    if (!rb_is_private_id(id)) {
+	rb_raise(rb_eNameError, "`%s' is not a private variable",
+		 rb_id2name(id));
+    }
+    switch (TYPE(obj)) {
+      case T_OBJECT:
+      case T_CLASS:
+      case T_MODULE:
+	if (ROBJECT(obj)->iv_tbl && 
+	    st_lookup(ROBJECT(obj)->iv_tbl, id, &val) &&
+	    TYPE(val) == T_HASH && st_lookup(RHASH(val)->tbl, klass, &val)) {
+	    return val;
+	}
+	break;
+      default:
+	if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+	    return private_ivar_get(obj, klass, id);
+	}
+	break;
+    }
+    if (ruby_verbose) {
+	rb_warning("private variable %s not initialized", rb_id2name(id));
+    }
+    return Qnil;
+}
+
+static void
+private_ivar_set(obj, klass, id, val)
+    VALUE obj, klass;
+    ID id;
+    VALUE val;
+{
+    st_table *tbl;
+    VALUE stbl;
+
+    if (rb_special_const_p(obj)) {
+	special_generic_ivar = 1;
+    }
+    if (!generic_iv_tbl) {
+	generic_iv_tbl = st_init_numtable();
+    }
+
+    if (!st_lookup(generic_iv_tbl, obj, &tbl)) {
+	FL_SET(obj, FL_EXIVAR);
+	tbl = st_init_numtable();
+	st_add_direct(generic_iv_tbl, obj, tbl);
+    }
+    if (!st_lookup(tbl, id, &stbl)) {
+	stbl = rb_hash_new();
+	st_add_direct(tbl, id, stbl);
+    }
+    if (TYPE(stbl) != T_HASH) {
+	rb_raise(rb_eNameError, "`%s' is not a private variable",
+		 rb_id2name(id));
+    }
+    rb_hash_aset(stbl, klass, val);
+}
+ 
+VALUE
+rb_pvar_set(obj, klass, id, val)
+    VALUE klass, obj;
+    ID id;
+    VALUE val;
+{
+    VALUE stbl;
+
+    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+	rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
+    if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+
+    check_valid_class(klass);
+    if (!rb_is_private_id(id)) {
+	rb_raise(rb_eNameError, "`%s' is not a private variable",
+		 rb_id2name(id));
+    }
+    switch (TYPE(obj)) {
+      case T_OBJECT:
+      case T_CLASS:
+      case T_MODULE:
+	if (!ROBJECT(obj)->iv_tbl) ROBJECT(obj)->iv_tbl = st_init_numtable();
+	if (!st_lookup(ROBJECT(obj)->iv_tbl, id, &stbl)) {
+	    stbl = rb_hash_new();
+	    st_add_direct(ROBJECT(obj)->iv_tbl, id, stbl);
+	}
+	if (TYPE(stbl) != T_HASH) {
+	    rb_raise(rb_eNameError, "`%s' is not a private variable",
+		     rb_id2name(id));
+	}
+	rb_hash_aset(stbl, klass, val);
+	break;
+      default:
+	private_ivar_set(obj, id, val);
+	break;
+    }
+    return val;
+}
+
+static VALUE
+private_ivar_defined(obj, klass, id)
+    VALUE obj, klass;
+    ID id;
+{
+    st_table *tbl;
+    VALUE val;
+    
+    if (!generic_iv_tbl) return Qfalse;
+    if (st_lookup(generic_iv_tbl, obj, &tbl) &&	st_lookup(tbl, id, &val) &&
+	TYPE(val) == T_HASH && st_lookup(RHASH(val)->tbl, klass, 0)) {
+	return Qtrue;
+    }
+    return Qfalse;
+}
+
+VALUE
+rb_pvar_defined(obj, klass, id)
+    VALUE obj, klass;
+    ID id;
+{
+    VALUE stbl;
+
+    check_valid_class(klass);
+    if (!rb_is_private_id(id)) {
+	rb_raise(rb_eNameError, "`%s' is not a private variable",
+		 rb_id2name(id));
+    }
+    switch (TYPE(obj)) {
+      case T_OBJECT:
+      case T_CLASS:
+      case T_MODULE:
+	if (ROBJECT(obj)->iv_tbl && 
+	    st_lookup(ROBJECT(obj)->iv_tbl, id, &stbl) &&
+	    TYPE(stbl) == T_HASH && st_lookup(RHASH(stbl)->tbl, klass, 0))
+	    return Qtrue;
+	break;
+      default:
+	  if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+	    return private_ivar_defined(obj, klass, id);
+	  }
+	break;
+    }
+    return Qfalse;
+}
+
+struct ary_class {
+    VALUE ary;
+    VALUE klass;
+};
+
+static int
+pvar_i(key, val, st)
+    ID key;
+    VALUE val;
+    struct ary_class *st;
+{
+    if (rb_is_private_id(key) && TYPE(val) == T_HASH &&
+	st_lookup(RHASH(val)->tbl, st->klass, 0)) {
+	rb_ary_push(st->ary, rb_str_new2(rb_id2name(key)));
+    }
+    return ST_CONTINUE;
+}
+
+VALUE
+rb_obj_private_variables(obj, klass)
+    VALUE obj, klass;
+{
+    VALUE ary;
+    struct ary_class st;
+
+    check_valid_class(klass);
+    ary = rb_ary_new();
+    switch (TYPE(obj)) {
+      case T_OBJECT:
+      case T_CLASS:
+      case T_MODULE:
+	if (ROBJECT(obj)->iv_tbl) {
+	    st.ary = ary;
+	    st.klass = klass;
+	    st_foreach(ROBJECT(obj)->iv_tbl, pvar_i, &st);
+	}
+	break;
+      default:
+	if (!generic_iv_tbl) break;
+	if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+	    st_table *tbl;
+
+	    if (st_lookup(generic_iv_tbl, obj, &tbl)) {
+		st.ary = ary;
+		st.klass = klass;
+		st_foreach(tbl, pvar_i, &st);
+	    }
+	}
+	break;
+    }
+    return ary;
+}
+
+static VALUE
+generic_pvar_remove(obj, klass, id)
+    VALUE obj, klass;
+    ID id;
+{
+    st_table *tbl;
+    VALUE val, stbl;
+
+    if (!generic_iv_tbl) return Qnil;
+    if (!st_lookup(generic_iv_tbl, obj, &tbl)) return Qnil;
+    if (!st_lookup(tbl, id, &stbl)) return Qnil;
+    if (TYPE(stbl) != T_HASH) return Qnil;
+    val = RHASH(stbl)->ifnone;
+    st_delete(RHASH(stbl)->tbl, &klass, &val);
+    return val;
+}
+
+VALUE
+rb_obj_remove_private_variable(obj, klass, name)
+    VALUE obj, klass, name;
+{
+    VALUE val = Qnil;
+    ID id = rb_to_id(name);
+
+    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+	rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
+    if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+    if (!rb_is_private_id(id)) {
+	rb_raise(rb_eNameError, "`%s' is not a private variable",
+		 rb_id2name(id));
+    }
+    check_valid_class(klass);
+
+    switch (TYPE(obj)) {
+      case T_OBJECT:
+      case T_CLASS:
+      case T_MODULE:
+	if (ROBJECT(obj)->iv_tbl &&
+	    st_lookup(ROBJECT(obj)->iv_tbl, id, &val) &&
+	    TYPE(val) == T_HASH) {
+	    st_delete(RHASH(val)->tbl, &klass, &val);
+	}
+	break;
+      default:
+	if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
+	    return generic_pvar_remove(obj, klass, id);
+	break;
+    }
+    return val;
+}
+
 VALUE
 rb_ivar_get(obj, id)
     VALUE obj;
@@ -899,6 +1189,9 @@
 {
     VALUE val;
 
+    if (rb_is_private_id(id)) {
+	return rb_pvar_get(obj, 1, id);
+    }
     switch (TYPE(obj)) {
       case T_OBJECT:
       case T_CLASS:
@@ -926,6 +1219,9 @@
     if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
 	rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
     if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+    if (rb_is_private_id(id)) {
+	return rb_pvar_set(obj, 1, id, val);
+    }
     switch (TYPE(obj)) {
       case T_OBJECT:
       case T_CLASS:
@@ -945,6 +1241,9 @@
     VALUE obj;
     ID id;
 {
+    if (rb_is_private_id(id)) {
+	return rb_pvar_defined(obj, 1, id);
+    }
     switch (TYPE(obj)) {
       case T_OBJECT:
       case T_CLASS:
@@ -1549,3 +1848,24 @@
 
     return rb_ivar_set(obj, id, val);
 }
+
+VALUE
+rb_pv_get(obj, klass, name)
+    VALUE obj, klass;
+    const char *name;
+{
+    ID id = rb_intern(name);
+
+    return rb_pvar_get(obj, klass, id);
+}
+
+VALUE
+rb_pv_set(obj, klass, name, val)
+    VALUE obj, klass;
+    const char *name;
+    VALUE val;
+{
+    ID id = rb_intern(name);
+
+    return rb_pvar_set(obj, klass, id, val);
+}