ref 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/README.rdoc +35 -0
  2. data/VERSION +1 -0
  3. data/ext/java/org/jruby/ext/ref/ReferencesService.java +28 -0
  4. data/ext/java/org/jruby/ext/ref/RubySoftReference.java +43 -0
  5. data/ext/java/org/jruby/ext/ref/RubyWeakReference.java +43 -0
  6. data/lib/org/jruby/ext/ref/references.jar +0 -0
  7. data/lib/ref.rb +39 -0
  8. data/lib/ref/abstract_reference_key_map.rb +117 -0
  9. data/lib/ref/abstract_reference_value_map.rb +127 -0
  10. data/lib/ref/mock.rb +145 -0
  11. data/lib/ref/reference.rb +24 -0
  12. data/lib/ref/reference_queue.rb +89 -0
  13. data/lib/ref/safe_monitor.rb +50 -0
  14. data/lib/ref/soft_key_map.rb +26 -0
  15. data/lib/ref/soft_reference.rb +67 -0
  16. data/lib/ref/soft_value_map.rb +28 -0
  17. data/lib/ref/strong_reference.rb +17 -0
  18. data/lib/ref/weak_key_map.rb +26 -0
  19. data/lib/ref/weak_reference.rb +26 -0
  20. data/lib/ref/weak_reference/iron_ruby.rb +14 -0
  21. data/lib/ref/weak_reference/pure_ruby.rb +91 -0
  22. data/lib/ref/weak_reference/weak_ref.rb +23 -0
  23. data/lib/ref/weak_value_map.rb +27 -0
  24. data/test/reference_key_map_behavior.rb +157 -0
  25. data/test/reference_key_map_behavior.rbc +4296 -0
  26. data/test/reference_queue_test.rb +60 -0
  27. data/test/reference_queue_test.rbc +1954 -0
  28. data/test/reference_value_map_behavior.rb +137 -0
  29. data/test/reference_value_map_behavior.rbc +3615 -0
  30. data/test/soft_key_map_test.rb +13 -0
  31. data/test/soft_key_map_test.rbc +374 -0
  32. data/test/soft_reference_test.rb +49 -0
  33. data/test/soft_reference_test.rbc +1481 -0
  34. data/test/soft_value_map_test.rb +13 -0
  35. data/test/soft_value_map_test.rbc +374 -0
  36. data/test/strong_reference_test.rb +15 -0
  37. data/test/strong_reference_test.rbc +562 -0
  38. data/test/test_helper.rb +4 -0
  39. data/test/weak_key_map_test.rb +13 -0
  40. data/test/weak_key_map_test.rbc +374 -0
  41. data/test/weak_reference_test.rb +46 -0
  42. data/test/weak_reference_test.rbc +1254 -0
  43. data/test/weak_value_map_test.rb +13 -0
  44. data/test/weak_value_map_test.rbc +374 -0
  45. metadata +113 -0
@@ -0,0 +1,35 @@
1
+ This library provides object references for Ruby as well as some common utilities for working with references. Object references are used to point to other objects and come in three distinct flavors that interact differently with the garbage collector.
2
+
3
+ * Ref::StrongReference - This is a plain old pointer to another object.
4
+ * Ref::WeakReference - This is a pointer to another object, but it is not seen by the garbage collector and the memory used by the object can be reclaimed at any time.
5
+ * Ref::SoftReference - This is similar to a weak reference, but the garbage collector is not as eager to reclaim the referenced object.
6
+
7
+ All of these classes extend from a common Ref::Reference class and have a common interface.
8
+
9
+ Weak and soft references are useful when you have instantiated objects that you may want to use again but can recreate if necessary. Since the garbage collector determines when to reclaim the memory used by the objects, you don't need to worry about bloating the Ruby heap.
10
+
11
+ = Example Usage
12
+
13
+ ref = Ref::WeakReference.new("hello")
14
+ ref.object # should be "hello"
15
+ ObjectSpace.garbage_collect
16
+ ref.object # should be nil (assuming the garbage collector reclaimed the reference)
17
+
18
+ = Goodies
19
+
20
+ This library also includes tools for some common uses of weak and soft references.
21
+
22
+ * Ref::WeakKeyMap - A map of keys to values where the keys are weak references
23
+ * Ref::WeakValueMap - A map of keys to values where the values are weak references
24
+ * Ref::SoftKeyMap - A map of keys to values where the keys are soft references
25
+ * Ref::SoftValueMap - A map of keys to values where the values are soft references
26
+ * Ref::ReferenceQueue - A thread safe implementation of a queue that will add references to itself as their objects are garbage collected.
27
+
28
+ = Problems with WeakRef
29
+
30
+ Ruby does come with the WeakRef class in the standard library. However, there are issues with this class across several different Ruby runtimes. This gem provides a common interface to weak references that works across MRI, Ruby Enterprise Edition, YARV, Jruby, Rubinius, and IronRuby.
31
+
32
+ 1. MRI and REE 1.8 - WeakRef extends from Delegator which is a very heavy weight class under Ruby 1.8. Creating a WeakRef object will allocate thousands of other objects and use up hundreds of kilobytes of memory. This makes WeakRef all but unusable even if you only need several hundred of them.
33
+ 2. YARV 1.9 - WeakRef is unsafe to use because the garbage collector can run in a different system thread than a thread allocating memory. This exposes a bug where a WeakRef may end up pointing to a completely different object than it originally referenced.
34
+ 3. Jruby and IronRuby - Jruby and IronRuby using the Ruby 1.8 libraries suffers from the same performance issue with the Delegator class. Furthermore, these VM's don't implement the method used to load an object from the heap using an object id and so cannot use a pure Ruby method to implement weak references.
35
+ 4. Rubinius - Rubinius implements WeakRef with a lighter weight version of delegation and works very well.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,28 @@
1
+ package org.jruby.ext.ref;
2
+
3
+ import java.io.IOException;
4
+ import org.jruby.Ruby;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyModule;
7
+ import org.jruby.runtime.builtin.IRubyObject;
8
+ import org.jruby.runtime.load.BasicLibraryService;
9
+
10
+ /**
11
+ * This library adds native Java support for weak and soft references.
12
+ *
13
+ * @author Brian Durand
14
+ */
15
+ public class ReferencesService implements BasicLibraryService {
16
+ public boolean basicLoad(Ruby runtime) throws IOException {
17
+ RubyModule refModule = runtime.getModule("Ref");
18
+ RubyClass referenceClass = refModule.getClass("Reference");
19
+
20
+ RubyClass rubyWeakReferenceClass = runtime.defineClassUnder("WeakReference", referenceClass, RubyWeakReference.ALLOCATOR, refModule);
21
+ rubyWeakReferenceClass.defineAnnotatedMethods(RubyWeakReference.class);
22
+
23
+ RubyClass rubySoftReferenceClass = runtime.defineClassUnder("SoftReference", referenceClass, RubySoftReference.ALLOCATOR, refModule);
24
+ rubySoftReferenceClass.defineAnnotatedMethods(RubySoftReference.class);
25
+
26
+ return true;
27
+ }
28
+ }
@@ -0,0 +1,43 @@
1
+ package org.jruby.ext.ref;
2
+
3
+ import java.lang.ref.SoftReference;
4
+ import org.jruby.Ruby;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyObject;
7
+ import org.jruby.anno.JRubyMethod;
8
+ import org.jruby.runtime.builtin.IRubyObject;
9
+ import org.jruby.runtime.ObjectAllocator;
10
+ import org.jruby.runtime.ThreadContext;
11
+ import org.jruby.runtime.Visibility;
12
+
13
+ public class RubySoftReference extends RubyObject {
14
+ private SoftReference _ref;
15
+ private static final String REFERENCED_OBJECT_ID_VARIABLE = "@referenced_object_id".intern();
16
+
17
+ public RubySoftReference(Ruby runtime, RubyClass klass) {
18
+ super(runtime, klass);
19
+ }
20
+
21
+ public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
22
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
23
+ return new RubySoftReference(runtime, klass);
24
+ }
25
+ };
26
+
27
+ @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
28
+ public IRubyObject initialize(ThreadContext context, IRubyObject obj) {
29
+ _ref = new SoftReference<IRubyObject>(obj);
30
+ fastSetInstanceVariable(REFERENCED_OBJECT_ID_VARIABLE, obj.id());
31
+ return context.getRuntime().getNil();
32
+ }
33
+
34
+ @JRubyMethod(name = "object")
35
+ public IRubyObject object() {
36
+ IRubyObject obj = (IRubyObject)_ref.get();
37
+ if (obj != null) {
38
+ return obj;
39
+ } else {
40
+ return getRuntime().getNil();
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,43 @@
1
+ package org.jruby.ext.ref;
2
+
3
+ import java.lang.ref.WeakReference;
4
+ import org.jruby.Ruby;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyObject;
7
+ import org.jruby.anno.JRubyMethod;
8
+ import org.jruby.runtime.builtin.IRubyObject;
9
+ import org.jruby.runtime.ObjectAllocator;
10
+ import org.jruby.runtime.ThreadContext;
11
+ import org.jruby.runtime.Visibility;
12
+
13
+ public class RubyWeakReference extends RubyObject {
14
+ private WeakReference _ref;
15
+ private static final String REFERENCED_OBJECT_ID_VARIABLE = "@referenced_object_id".intern();
16
+
17
+ public RubyWeakReference(Ruby runtime, RubyClass klass) {
18
+ super(runtime, klass);
19
+ }
20
+
21
+ public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
22
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
23
+ return new RubyWeakReference(runtime, klass);
24
+ }
25
+ };
26
+
27
+ @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
28
+ public IRubyObject initialize(ThreadContext context, IRubyObject obj) {
29
+ _ref = new WeakReference<IRubyObject>(obj);
30
+ fastSetInstanceVariable(REFERENCED_OBJECT_ID_VARIABLE, obj.id());
31
+ return context.getRuntime().getNil();
32
+ }
33
+
34
+ @JRubyMethod(name = "object")
35
+ public IRubyObject object() {
36
+ IRubyObject obj = (IRubyObject)_ref.get();
37
+ if (obj != null) {
38
+ return obj;
39
+ } else {
40
+ return getRuntime().getNil();
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,39 @@
1
+ module Ref
2
+ autoload :AbstractReferenceValueMap, File.join(File.dirname(__FILE__), "ref", "abstract_reference_value_map.rb")
3
+ autoload :AbstractReferenceKeyMap, File.join(File.dirname(__FILE__), "ref", "abstract_reference_key_map.rb")
4
+ autoload :Mock, File.join(File.dirname(__FILE__), "ref", "mock.rb")
5
+ autoload :Reference, File.join(File.dirname(__FILE__), "ref", "reference.rb")
6
+ autoload :ReferenceQueue, File.join(File.dirname(__FILE__), "ref", "reference_queue.rb")
7
+ autoload :SafeMonitor, File.join(File.dirname(__FILE__), "ref", "safe_monitor.rb")
8
+ autoload :SoftKeyMap, File.join(File.dirname(__FILE__), "ref", "soft_key_map.rb")
9
+ autoload :SoftValueMap, File.join(File.dirname(__FILE__), "ref", "soft_value_map.rb")
10
+ autoload :StrongReference, File.join(File.dirname(__FILE__), "ref", "strong_reference.rb")
11
+ autoload :WeakKeyMap, File.join(File.dirname(__FILE__), "ref", "weak_key_map.rb")
12
+ autoload :WeakValueMap, File.join(File.dirname(__FILE__), "ref", "weak_value_map.rb")
13
+
14
+ # Set the best implementation for weak references based on the runtime.
15
+ if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
16
+ # Use native Java references
17
+ begin
18
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
19
+ require 'org/jruby/ext/ref/references'
20
+ ensure
21
+ $LOAD_PATH.shift if $LOAD_PATH.first == File.dirname(__FILE__)
22
+ end
23
+ else
24
+ autoload :SoftReference, File.join(File.dirname(__FILE__), "ref", "soft_reference.rb")
25
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
26
+ # IronRuby has it's own implementation of weak references.
27
+ autoload :WeakReference, File.join(File.dirname(__FILE__), "ref", "weak_reference", "iron_ruby.rb")
28
+ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
29
+ # If using Rubinius set the implementation to use WeakRef since it is very efficient and using finalizers is not.
30
+ autoload :WeakReference, File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
31
+ elsif defined?(ObjectSpace._id2ref)
32
+ # If ObjectSpace can lookup objects from their object_id, then use the pure ruby implementation.
33
+ autoload :WeakReference, File.join(File.dirname(__FILE__), "ref", "weak_reference", "pure_ruby.rb")
34
+ else
35
+ # Otherwise, wrap the standard library WeakRef class
36
+ autoload :WeakReference, File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,117 @@
1
+ module Ref
2
+ # Abstract base class for WeakKeyMap and SoftKeyMap.
3
+ #
4
+ # The classes behave similar to Hashes, but the keys in the map are not strong references
5
+ # and can be reclaimed by the garbage collector at any time. When a key is reclaimed, the
6
+ # map entry will be removed.
7
+ class AbstractReferenceKeyMap
8
+ class << self
9
+ def reference_class=(klass) #:nodoc:
10
+ @reference_class = klass
11
+ end
12
+
13
+ def reference_class #:nodoc:
14
+ raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class
15
+ @reference_class
16
+ end
17
+ end
18
+
19
+ # Create a new map. Values added to the hash will be cleaned up by the garbage
20
+ # collector if there are no other reference except in the map.
21
+ def initialize
22
+ @values = {}
23
+ @references_to_keys_map = {}
24
+ @lock = SafeMonitor.new
25
+ @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
26
+ end
27
+
28
+ # Get a value from the map by key. If the value has been reclaimed by the garbage
29
+ # collector, this will return nil.
30
+ def [](key)
31
+ rkey = ref_key(key)
32
+ @values[rkey] if rkey
33
+ end
34
+
35
+ # Add a key/value to the map.
36
+ def []=(key, value)
37
+ ObjectSpace.define_finalizer(key, @reference_cleanup)
38
+ @lock.synchronize do
39
+ @references_to_keys_map[key.__id__] = self.class.reference_class.new(key)
40
+ @values[key.__id__] = value
41
+ end
42
+ end
43
+
44
+ # Remove the value associated with the key from the map.
45
+ def delete(key)
46
+ rkey = ref_key(key)
47
+ if rkey
48
+ @references_to_keys_map.delete(rkey)
49
+ @values.delete(rkey)
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
55
+ # Get an array of keys that have not yet been garbage collected.
56
+ def keys
57
+ @values.keys.collect{|rkey| @references_to_keys_map[rkey].object}.compact
58
+ end
59
+
60
+ # Turn the map into an arry of [key, value] entries.
61
+ def to_a
62
+ array = []
63
+ each{|k,v| array << [k, v]}
64
+ array
65
+ end
66
+
67
+ # Iterate through all the key/value pairs in the map that have not been reclaimed
68
+ # by the garbage collector.
69
+ def each
70
+ @references_to_keys_map.each do |rkey, ref|
71
+ key = ref.object
72
+ yield(key, @values[rkey]) if key
73
+ end
74
+ end
75
+
76
+ # Clear the map of all key/value pairs.
77
+ def clear
78
+ @lock.synchronize do
79
+ @values.clear
80
+ @references_to_keys_map.clear
81
+ end
82
+ end
83
+
84
+ # Merge the values from another hash into this map.
85
+ def merge!(other_hash)
86
+ other_hash.each do |key, value|
87
+ self[key] = value
88
+ end
89
+ end
90
+
91
+ def inspect
92
+ live_entries = {}
93
+ each do |key, value|
94
+ live_entries[key] = value
95
+ end
96
+ live_entries.inspect
97
+ end
98
+
99
+ private
100
+
101
+ def ref_key (key)
102
+ ref = @references_to_keys_map[key.__id__]
103
+ if ref && ref.object
104
+ ref.referenced_object_id
105
+ else
106
+ nil
107
+ end
108
+ end
109
+
110
+ def remove_reference_to(object_id)
111
+ @lock.synchronize do
112
+ @references_to_keys_map.delete(object_id)
113
+ @values.delete(object_id)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,127 @@
1
+ module Ref
2
+ # Abstract base class for WeakValueMap and SoftValueMap.
3
+ #
4
+ # The classes behave similar to Hashes, but the values in the map are not strong references
5
+ # and can be reclaimed by the garbage collector at any time. When a value is reclaimed, the
6
+ # map entry will be removed.
7
+ class AbstractReferenceValueMap
8
+ class << self
9
+ def reference_class=(klass) #:nodoc:
10
+ @reference_class = klass
11
+ end
12
+
13
+ def reference_class #:nodoc:
14
+ raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class
15
+ @reference_class
16
+ end
17
+ end
18
+
19
+ # Create a new map. Values added to the map will be cleaned up by the garbage
20
+ # collector if there are no other reference except in the map.
21
+ def initialize
22
+ @references = {}
23
+ @references_to_keys_map = {}
24
+ @lock = SafeMonitor.new
25
+ @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
26
+ end
27
+
28
+ # Get a value from the map by key. If the value has been reclaimed by the garbage
29
+ # collector, this will return nil.
30
+ def [](key)
31
+ ref = @references[key]
32
+ value = ref.object if ref
33
+ value
34
+ end
35
+
36
+ # Add a key/value to the map.
37
+ def []=(key, value)
38
+ ObjectSpace.define_finalizer(value, @reference_cleanup)
39
+ key = key.dup if key.is_a?(String)
40
+ @lock.synchronize do
41
+ @references[key] = self.class.reference_class.new(value)
42
+ keys_for_id = @references_to_keys_map[value.__id__]
43
+ unless keys_for_id
44
+ keys_for_id = []
45
+ @references_to_keys_map[value.__id__] = keys_for_id
46
+ end
47
+ keys_for_id << key
48
+ end
49
+ value
50
+ end
51
+
52
+ # Remove the entry associated with the key from the map.
53
+ def delete(key)
54
+ ref = @references.delete(key)
55
+ if ref
56
+ keys_to_id = @references_to_keys_map[ref.referenced_object_id]
57
+ if keys_to_id
58
+ keys_to_id.delete(key)
59
+ @references_to_keys_map.delete(ref.referenced_object_id) if keys_to_id.empty?
60
+ end
61
+ ref.object
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
67
+ # Get the list of all values that have not yet been garbage collected.
68
+ def values
69
+ vals = []
70
+ each{|k,v| vals << v}
71
+ vals
72
+ end
73
+
74
+ # Turn the map into an arry of [key, value] entries
75
+ def to_a
76
+ array = []
77
+ each{|k,v| array << [k, v]}
78
+ array
79
+ end
80
+
81
+ # Iterate through all the key/value pairs in the map that have not been reclaimed
82
+ # by the garbage collector.
83
+ def each
84
+ @references.each do |key, ref|
85
+ value = ref.object
86
+ yield(key, value) if value
87
+ end
88
+ end
89
+
90
+ # Clear the map of all key/value pairs.
91
+ def clear
92
+ @lock.synchronize do
93
+ @references.clear
94
+ @references_to_keys_map.clear
95
+ end
96
+ end
97
+
98
+ # Merge the values from another hash into this map.
99
+ def merge!(other_hash)
100
+ other_hash.each do |key, value|
101
+ self[key] = value
102
+ end
103
+ end
104
+
105
+ def inspect
106
+ live_entries = {}
107
+ each do |key, value|
108
+ live_entries[key] = value
109
+ end
110
+ live_entries.inspect
111
+ end
112
+
113
+ private
114
+
115
+ def remove_reference_to(object_id)
116
+ @lock.synchronize do
117
+ keys = @references_to_keys_map[object_id]
118
+ if keys
119
+ keys.each do |key|
120
+ @references.delete(key)
121
+ end
122
+ @references_to_keys_map.delete(object_id)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,145 @@
1
+ module Ref
2
+ # This module provides mock weak and strong references that are designed to be
3
+ # used in tests. You can define a block where all weak and soft references created
4
+ # will be mock references. You can then mimic running the garbage collector on
5
+ # the objects pointed to by the references.
6
+ #
7
+ # Example usage:
8
+ #
9
+ # References::Mock.use do
10
+ # obj = Object.new
11
+ # ref = References::WeakReference.new(obj)
12
+ # ref.object # obj
13
+ # References::Mock.gc(obj) # mimics the garbage collector reclaiming the referenced object
14
+ # ref.object # nil
15
+ # end
16
+ module Mock
17
+ class << self
18
+ # Use the mock implementation inside a block and then restore the original implementation.
19
+ def use
20
+ if object_space
21
+ yield
22
+ else
23
+ setup
24
+ begin
25
+ yield
26
+ ensure
27
+ cleanup
28
+ end
29
+ end
30
+ end
31
+
32
+ # Start using mock references.
33
+ def setup
34
+ raise "Ref::Mock already setup" if object_space
35
+
36
+ @object_space = {}
37
+
38
+ class << ObjectSpace
39
+ unless method_defined?(:define_finalizer_with_mock_reference)
40
+ def define_finalizer_with_mock_reference(obj, finalizer)
41
+ if ::Ref::Mock.object_space.include?(obj.__id__)
42
+ ::Ref::Mock.object_space[obj.__id__] << finalizer
43
+ else
44
+ define_finalizer_without_mock_reference(obj, finalizer)
45
+ end
46
+ end
47
+ end
48
+
49
+ alias_method :define_finalizer_without_mock_reference, :define_finalizer
50
+ alias_method :define_finalizer, :define_finalizer_with_mock_reference
51
+ end
52
+
53
+ class << WeakReference
54
+ unless method_defined?(:new_with_mock_reference)
55
+ def new_with_mock_reference(obj)
56
+ if self == Mock::MockWeakReference
57
+ new_without_mock_reference(obj)
58
+ else
59
+ Mock::MockWeakReference.new(obj)
60
+ end
61
+ end
62
+ end
63
+
64
+ alias_method :new_without_mock_reference, :new
65
+ alias_method :new, :new_with_mock_reference
66
+ end
67
+
68
+ class << SoftReference
69
+ unless method_defined?(:new_with_mock_reference)
70
+ def new_with_mock_reference(obj)
71
+ if self == Mock::MockSoftReference
72
+ new_without_mock_reference(obj)
73
+ else
74
+ Mock::MockSoftReference.new(obj)
75
+ end
76
+ end
77
+ end
78
+
79
+ alias_method :new_without_mock_reference, :new
80
+ alias_method :new, :new_with_mock_reference
81
+ end
82
+ end
83
+
84
+ # Stop using mock references.
85
+ def cleanup
86
+ @object_space = nil
87
+ class << ObjectSpace
88
+ alias_method :define_finalizer_with_mock_reference, :define_finalizer
89
+ alias_method :define_finalizer, :define_finalizer_without_mock_reference
90
+ end
91
+
92
+ class << WeakReference
93
+ alias_method :new_with_mock_reference, :new
94
+ alias_method :new, :new_without_mock_reference
95
+ end
96
+
97
+ class << SoftReference
98
+ alias_method :new_with_mock_reference, :new
99
+ alias_method :new, :new_without_mock_reference
100
+ end
101
+ end
102
+
103
+ def object_space # :nodoc:
104
+ @object_space if instance_variable_defined?(:@object_space)
105
+ end
106
+
107
+ # Simulate garbage collection of the objects passed in as arguments. If no objects
108
+ # are specified, all objects will be reclaimed.
109
+ def gc(*objects)
110
+ objects = object_space.keys if objects.empty?
111
+ objects.each do |obj|
112
+ finalizers = object_space.delete(obj.__id__)
113
+ if finalizers
114
+ finalizers.each{|finalizer| finalizer.call(obj.__id__)}
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ module MockReference #:nodoc:
121
+ def initialize(obj)
122
+ @object = obj
123
+ @referenced_object_id = obj.__id__
124
+ raise "Reference::Mock not setup yet" unless Mock.object_space
125
+ Mock.object_space[obj.__id__] ||= []
126
+ end
127
+
128
+ def object
129
+ if @object && Mock.object_space.include?(@object.__id__)
130
+ @object
131
+ else
132
+ @object = nil
133
+ end
134
+ end
135
+ end
136
+
137
+ class MockWeakReference < WeakReference #:nodoc:
138
+ include MockReference
139
+ end
140
+
141
+ class MockSoftReference < SoftReference #:nodoc:
142
+ include MockReference
143
+ end
144
+ end
145
+ end