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) {