Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
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) {