Report abuse

diff --git a/kernel/alpha.rb b/kernel/alpha.rb
index 55d4eb4..e19fe9b 100644
--- a/kernel/alpha.rb
+++ b/kernel/alpha.rb
@@ -101,6 +101,39 @@ module Kernel
   def method_missing(meth, *args)
     raise NoMethodError, "Unable to send '#{meth}' on '#{self}' (#{self.class})"
   end
+
+  # reimplemented in kernel/common/kernel.rb
+  def initialize_copy(other)
+  end
+
+  def copy_header(other)
+    Ruby.primitive :object_copy_header
+    raise PrimitiveFailure, "Kernel#copy_header primitive failed"
+  end
+
+  def copy_object(other)
+    Ruby.primitive :object_copy_object
+    raise PrimitiveFailure, "Kernel#copy_object primitive failed"
+  end
+
+  def copy_metaclass(other)
+    Ruby.primitive :object_copy_metaclass
+    raise PrimitiveFailure, "Kernel#copy_metaclass primitive failed"
+  end
+
+  def dup
+    copy = self.class.allocate
+    copy.copy_object self
+    copy.send :initialize_copy, self
+    copy
+  end
+
+  def clone
+    copy = dup
+    copy.copy_metaclass self
+    copy.freeze if frozen?
+    copy
+  end
 end

 class CompiledMethod < Executable
diff --git a/kernel/bootstrap/bytearray.rb b/kernel/bootstrap/bytearray.rb
index 3be2594..d5dee09 100644
--- a/kernel/bootstrap/bytearray.rb
+++ b/kernel/bootstrap/bytearray.rb
@@ -1,11 +1,15 @@
 class ByteArray
-  def self.allocate(cnt)
+  def self.allocate
+    raise TypeError, "ByteArray cannot be created via allocate()"
+  end
+
+  def self.allocate_sized(cnt)
     Ruby.primitive :bytearray_allocate
     raise PrimitiveFailure, "ByteArray#allocate primitive failed"
   end

   def self.new(cnt)
-    obj = allocate(cnt)
+    obj = allocate_sized cnt
     Rubinius.asm(obj) do |obj|
       push_block
       run obj
@@ -53,7 +57,8 @@ class ByteArray
   def dup(cls=nil)
     cls ||= self.class
     obj = cls.new(self.size)
-    dup_into obj
+    obj.copy_object self
+    obj.send :initialize_copy, self
     return obj
   end

diff --git a/kernel/bootstrap/data.rb b/kernel/bootstrap/data.rb
new file mode 100644
index 0000000..741ea90
--- /dev/null
+++ b/kernel/bootstrap/data.rb
@@ -0,0 +1,8 @@
+class Data
+  def dup
+    copy = self.class.allocate
+    copy.copy_header self
+    copy.send :initialize_copy, self
+    copy
+  end
+end
diff --git a/kernel/bootstrap/kernel.rb b/kernel/bootstrap/kernel.rb
index eeaf07e..16efb97 100644
--- a/kernel/bootstrap/kernel.rb
+++ b/kernel/bootstrap/kernel.rb
@@ -1,15 +1,5 @@
 module Kernel

-  def clone
-    Ruby.primitive :object_clone
-    raise TypeError, "Kernel#clone primitive failed"
-  end
-
-  def dup
-    Ruby.primitive :object_dup
-    raise TypeError, "Kernel#dup primitive failed"
-  end
-
   def equal?(other)
     Ruby.primitive :object_equal
     raise PrimitiveFailure, "Kernel#equal? primitive failed"
diff --git a/kernel/bootstrap/lookuptable.rb b/kernel/bootstrap/lookuptable.rb
index 0459d2f..1376b00 100644
--- a/kernel/bootstrap/lookuptable.rb
+++ b/kernel/bootstrap/lookuptable.rb
@@ -76,12 +76,16 @@ class LookupTable
     end
   end

-  def dup
-    Ruby.primitive :lookuptable_dup
-    raise PrimitiveFailure, "LookupTable#dup primitive failed"
+  def duplicate
+    Ruby.primitive :lookuptable_duplicate
+    raise PrimitiveFailure, "LookupTable#duplicate primitive failed"
   end

-  alias_method :clone, :dup
+  def dup
+    copy = duplicate
+    copy.send :initialize_copy, self
+    copy
+  end

   def fetch(key, return_on_failure)
     Ruby.primitive :lookuptable_fetch
diff --git a/kernel/bootstrap/tuple.rb b/kernel/bootstrap/tuple.rb
index 62ced70..506ea19 100644
--- a/kernel/bootstrap/tuple.rb
+++ b/kernel/bootstrap/tuple.rb
@@ -1,5 +1,9 @@
 class Tuple

+  def self.allocate
+    raise TypeError, "Tuple cannot be created via allocate()"
+  end
+
   def self.new(cnt)
     Ruby.primitive :tuple_allocate
     raise PrimitiveFailure, "Tuple.new primitive failed"
@@ -40,6 +44,13 @@ class Tuple
     raise PrimitiveFailure, "Tuple#copy_from primitive failed"
   end

+  def dup
+    obj = self.class.new(self.size)
+    obj.copy_object self
+    obj.send :initialize_copy, self
+    obj
+  end
+
   def delete(start,length,object)
     Ruby.primitive :tuple_delete_inplace
     raise PrimitiveFailure, "Tuple#delete primitive failed"
diff --git a/kernel/common/kernel.rb b/kernel/common/kernel.rb
index 5a4e4d0..c4de1b7 100644
--- a/kernel/common/kernel.rb
+++ b/kernel/common/kernel.rb
@@ -77,23 +77,6 @@ module Kernel
   end
   private :FloatValue

-  alias_method :primitive_clone, :clone
-
-  def clone
-    copy = primitive_clone
-    copy.send :initialize_copy, self
-    copy.freeze if frozen?
-    copy
-  end
-
-  alias_method :primitive_dup, :dup
-
-  def dup
-    copy = primitive_dup
-    copy.send :initialize_copy, self
-    copy
-  end
-
   def initialize_copy(source)
     unless source.class == self.class then
       raise TypeError, "initialize_copy should take same class object"
diff --git a/kernel/platform/memorypointer.rb b/kernel/platform/memorypointer.rb
index ff43ef1..5efb0c4 100644
--- a/kernel/platform/memorypointer.rb
+++ b/kernel/platform/memorypointer.rb
@@ -82,25 +82,18 @@ module FFI
       end
     end

-    def clone
-      other = Platform::POSIX.malloc total
-      other.total = total
-      other.type_size = type_size
-      Platform::POSIX.memcpy other, self, total
-      other
-    end
-
     def dup
       other = Platform::POSIX.malloc total
       other.total = total
       other.type_size = type_size
       Platform::POSIX.memcpy other, self, total
+      other.send :initialize_copy, self
       other
     end

-    def address= thingy
+    def address=(address)
       Ruby.primitive :memorypointer_set_address
-      raise PrimitiveFailure, "Unable to fuck over your address well enough"
+      raise PrimitiveFailure, "MemoryPointer#address= primitive failed"
     end

     # Indicates how many bytes the chunk of memory that is pointed to takes up.
diff --git a/kernel/platform/struct.rb b/kernel/platform/struct.rb
index 44b7ce7..71ebd48 100644
--- a/kernel/platform/struct.rb
+++ b/kernel/platform/struct.rb
@@ -77,8 +77,8 @@ module FFI
       @pointer.free
     end

-    def initialize_copy ptr
-      @pointer = @pointer.dup
+    def initialize_copy(ptr)
+      @pointer = ptr.pointer.dup
     end

     def [](field)
diff --git a/vm/builtin/lookuptable.cpp b/vm/builtin/lookuptable.cpp
index d256c00..b8dd6e5 100644
--- a/vm/builtin/lookuptable.cpp
+++ b/vm/builtin/lookuptable.cpp
@@ -49,7 +49,7 @@ namespace rubinius {
     return tbl;
   }

-  LookupTable* LookupTable::dup(STATE) {
+  LookupTable* LookupTable::duplicate(STATE) {
     size_t size, i;
     LookupTable *dup;

diff --git a/vm/builtin/lookuptable.hpp b/vm/builtin/lookuptable.hpp
index 9fc12cc..8103c86 100644
--- a/vm/builtin/lookuptable.hpp
+++ b/vm/builtin/lookuptable.hpp
@@ -96,8 +96,8 @@ namespace rubinius {

     Object* fetch(STATE, Object* key, bool* found);

-    // Ruby.primitive :lookuptable_dup
-    LookupTable* dup(STATE);
+    // Ruby.primitive :lookuptable_duplicate
+    LookupTable* duplicate(STATE);
     void   redistribute(STATE, size_t size);
     LookupTableBucket* find_entry(STATE, Object* key);
     Object* find(STATE, Object* key);
diff --git a/vm/builtin/object.cpp b/vm/builtin/object.cpp
index 931a00f..aa380e6 100644
--- a/vm/builtin/object.cpp
+++ b/vm/builtin/object.cpp
@@ -51,41 +51,39 @@ namespace rubinius {
     type_info(state)->cleanup(this);
   }

-  Object* Object::clone(STATE) {
-    Object* other = dup(state);
+  Object* Object::duplicate(STATE) {
+    Object* other = state->om->allocate_object(this->total_size(state));
+
+    return other->copy_object(state, this);
+  }

-    other->copy_internal_state_from(state, this);
+  Object* Object::copy_object(STATE, Object* other) {
+    copy_header(state, other);
+    copy_body(state, other);

-    return other;
+    return this;
   }

-  void Object::copy_internal_state_from(STATE, Object* original) {
-    if(MetaClass* mc = try_as<MetaClass>(original->klass())) {
-      LookupTable* source_methods = mc->method_table()->dup(state);
-      LookupTable* source_constants = mc->constants()->dup(state);
+  Object* Object::copy_metaclass(STATE, Object* other) {
+    if(MetaClass* mc = try_as<MetaClass>(other->klass())) {
+      LookupTable* source_methods = mc->method_table()->duplicate(state);
+      LookupTable* source_constants = mc->constants()->duplicate(state);

-      this->metaclass(state)->method_table(state, source_methods);
-      this->metaclass(state)->constants(state, source_constants);
+      metaclass(state)->method_table(state, source_methods);
+      metaclass(state)->constants(state, source_constants);

       // This allows us to preserve included modules
-      this->metaclass(state)->superclass(state, mc->superclass());
+      metaclass(state)->superclass(state, mc->superclass());
     }
-  }

-  Object* Object::dup(STATE) {
-    Object* other = state->om->allocate_object(this->total_size(state));
-
-#ifdef RBX_GC_STATS
-    // This counter is only valid if the line above allocates in the
-    // young object space.
-    stats::GCStats::get()->young_object_types[this->type_id()]++;
-#endif
+    return this;
+  }

-    other->initialize_copy(this, age);
-    other->copy_body(state, this);
+  Object* Object::copy_header(STATE, Object* other) {
+    initialize_copy(other, age);

-    // Set the dup's class this's class
-    other->klass(state, class_object(state));
+    // Ensure that the metaclass is not shared
+    klass(state, other->class_object(state));

     // HACK: If other is mature, remember it.
     // We could inspect inspect the references we just copied to see
@@ -93,19 +91,19 @@ namespace rubinius {
     // then remember other. The up side to just remembering it like
     // this is that other is rarely mature, and the remember_set is
     // flushed on each collection anyway.
-    if(other->zone == MatureObjectZone) {
-      state->om->remember_object(other);
+    if(zone == MatureObjectZone) {
+      state->om->remember_object(this);
     }

     // Copy ivars.
-    if(ivars_->reference_p()) {
+    if(other->ivars_->reference_p()) {
       // NOTE Don't combine these 2 branches even though they both just call
-      // ::dup. There is a special LookupTable::dup that can only be seen
+      // ::copy. There is a special LookupTable::copy that can only be seen
       // when the receiver is of LookupTable* type. Without the explicit cast
       // and call, the wrong one will be called.
-      if(LookupTable* lt = try_as<LookupTable>(ivars_)) {
-        other->ivars_ = lt->dup(state);
-        LookupTable* ld = as<LookupTable>(other->ivars_);
+      if(LookupTable* lt = try_as<LookupTable>(other->ivars_)) {
+        ivars_ = lt->duplicate(state);
+        LookupTable* ld = as<LookupTable>(ivars_);

         // We store the object_id in the ivar table, so nuke it.
         ld->remove(state, G(sym_object_id));
@@ -113,9 +111,9 @@ namespace rubinius {
       } else {
         // Use as<> so that we throw a TypeError if there is something else
         // here.
-        CompactLookupTable* clt = as<CompactLookupTable>(ivars_);
-        other->ivars_ = clt->dup(state);
-        CompactLookupTable* ld = as<CompactLookupTable>(other->ivars_);
+        CompactLookupTable* clt = as<CompactLookupTable>(other->ivars_);
+        ivars_ = clt->duplicate(state);
+        CompactLookupTable* ld = as<CompactLookupTable>(ivars_);

         // We store the object_id in the ivar table, so nuke it.
         ld->remove(state, G(sym_object_id));
@@ -123,7 +121,7 @@ namespace rubinius {
       };
     }

-    return other;
+    return this;
   }

   Object* Object::equal(STATE, Object* other) {
diff --git a/vm/builtin/object.hpp b/vm/builtin/object.hpp
index 8a1cc72..e01c08b 100644
--- a/vm/builtin/object.hpp
+++ b/vm/builtin/object.hpp
@@ -108,17 +108,38 @@ namespace rubinius {

   public:   /* Type information, field access, copy support &c. */

-    /** Sets the other Object's flags the same as this. @see vm/oop.hpp. */
-    void        copy_flags(STATE, Object* other);
-    /** Copies metaclass from original to this one. @see clone(). */
-    void        copy_internal_state_from(STATE, Object* original);
-    /** NOT IMPLEMENTED. Copies instance variables to the other Object. */
-    void        copy_ivars(STATE, Object* other);
-    /** NOT IMPLEMENTED. Copies this Object's MetaClass to the other Object. */
-    void        copy_metaclass(STATE, Object* other);
+    /**
+     * Returns a copy of this object. This is NOT the same as Ruby
+     * Object#dup. Code that needs Object#dup semantics MUST call
+     * #dup from Ruby. This method is used in the VM to duplicate
+     * data structures. It will not call the Ruby allocate() or
+     * initialize_copy() methods.
+     */
+    Object* duplicate(STATE);
+
+    /**
+     * Copies the header and instance variables from other. Called
+     * by Kernel#dup.
+     */
+    // Ruby.primitive :object_copy_header
+    Object* copy_header(STATE, Object* other);
+
+    /**
+     *  Copies the entire object by first calling copy_header and
+     *  then copying the body. Called by ByteArray#dup and Tuple#dup.
+     */
+    // Ruby.primitive :object_copy_object
+    Object* copy_object(STATE, Object* other);
+
+    /**
+     * Copies this Object's MetaClass to the other Object. Called
+     * by Kernel#clone.
+     */
+    // Ruby.primitive :object_copy_metaclass
+    Object* copy_metaclass(STATE, Object* other);

     /** True if this Object* is actually a Fixnum, false otherwise. */
-    bool        fixnum_p() const;
+    bool fixnum_p() const;

     /**
      *  Retrieve the Object stored in given field index of this Object.
@@ -175,33 +196,11 @@ namespace rubinius {
     // Ruby.primitive :object_change_class_to
     Object*   change_class_to(STATE, Class* other_klass);

-    /**
-     *  Ruby #clone.
-     *
-     *  Creates and returns a new Object that is a copy of this one
-     *  including internal state.
-     *
-     *  @see  dup()
-     */
-    // Ruby.primitive :object_clone
-    Object*   clone(STATE);
-
     /** Returns the Class object of which this Object is an instance. */
     // Ruby.primitive :object_class
     Class*    class_object(STATE) const;

     /**
-     *  Ruby #dup.
-     *
-     *  Creates and returns a new Object that is a copy of this one
-     *  except for internal state.
-     *
-     *  @see  clone()
-     */
-    // Ruby.primitive :object_dup
-    Object*   dup(STATE);
-
-    /**
      *  Ruby #equal?.
      *
      *  Returns true if and only if this and the other object are
diff --git a/vm/builtin/string.cpp b/vm/builtin/string.cpp
index 18ec906..c725eaf 100644
--- a/vm/builtin/string.cpp
+++ b/vm/builtin/string.cpp
@@ -185,7 +185,7 @@ namespace rubinius {
   String* String::string_dup(STATE) {
     String* ns;

-    ns = as<String>(dup(state));
+    ns = as<String>(duplicate(state));
     ns->shared(state, Qtrue);
     shared(state, Qtrue);

@@ -195,7 +195,7 @@ namespace rubinius {
   }

   void String::unshare(STATE) {
-    data(state, as<ByteArray>(data_->dup(state)));
+    data(state, as<ByteArray>(data_->duplicate(state)));
     shared(state, Qfalse);
   }

diff --git a/vm/capi/object.cpp b/vm/capi/object.cpp
index 29fefb5..e5d8676 100644
--- a/vm/capi/object.cpp
+++ b/vm/capi/object.cpp
@@ -130,9 +130,8 @@ extern "C" {
   VALUE rb_obj_clone(VALUE obj_handle) {
     NativeMethodEnvironment* env = NativeMethodEnvironment::get();

-    Object* object = env->get_object(obj_handle);
-
-    return env->get_handle(object->clone(env->state()));
+    env->flush_cached_data(false);
+    return rb_funcall(obj_handle, rb_intern("clone"), 0);
   }

   VALUE rb_inspect(VALUE obj_handle) {