free 0.1.2 → 0.2.0pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +8 -0
- data/README.md +25 -8
- data/ext/free/free.c +43 -8
- data/lib/free.rb +7 -2
- data/lib/free/version.rb +1 -1
- data/test/test.rb +29 -0
- metadata +9 -10
data/HISTORY
CHANGED
data/README.md
CHANGED
@@ -9,16 +9,17 @@ free provides the `Object#free` method enabling a user to garbage
|
|
9
9
|
collect an object on demand and free all its internal structures.
|
10
10
|
|
11
11
|
* Install the [gem](https://rubygems.org/gems/free)
|
12
|
-
* Read the [documentation](http://rdoc.info/github/banister/free/master/file/README.
|
12
|
+
* Read the [documentation](http://rdoc.info/github/banister/free/master/file/README.md)
|
13
13
|
* See the [source code](http://github.com/banister/free)
|
14
14
|
|
15
|
-
Example:
|
16
|
-
|
15
|
+
Example: Freeing a String
|
16
|
+
-------------------------
|
17
17
|
|
18
18
|
Let's free a String:
|
19
19
|
|
20
20
|
hello = "hello world"
|
21
21
|
id = hello.object_id
|
22
|
+
|
22
23
|
hello.free
|
23
24
|
|
24
25
|
# Note we go through the id as accessing the freed object directly
|
@@ -26,20 +27,36 @@ Let's free a String:
|
|
26
27
|
ObjectSpace._id2ref(id) #=> RangeError: _id2ref': 0x1c1a63c is recycled object
|
27
28
|
|
28
29
|
|
30
|
+
Example: Destructors
|
31
|
+
--------------------
|
32
|
+
|
33
|
+
Free also supports object destructors. If the `__destruct__` method is
|
34
|
+
implemented on the object being freed it will be called before freeing
|
35
|
+
takes place; and the return value of `free` will be the return value of `__destruct__`:
|
36
|
+
|
37
|
+
o = Object.new
|
38
|
+
def o.__destruct__
|
39
|
+
:killed
|
40
|
+
end
|
41
|
+
|
42
|
+
o.free #=> :killed
|
43
|
+
|
29
44
|
Features and limitations
|
30
45
|
-------------------------
|
31
46
|
|
32
47
|
### Features
|
33
48
|
|
34
|
-
* Can free any object (
|
35
|
-
|
36
|
-
*
|
49
|
+
* Can free any object (but be careful, see below)
|
50
|
+
* Works in both Ruby 1.8 and 1.9.
|
51
|
+
* Some protection from freeing critical objects and immediate values.
|
52
|
+
* Supports object destructors
|
37
53
|
|
38
54
|
### Limitations
|
39
55
|
|
40
56
|
* Beta software, beware.
|
41
|
-
*
|
42
|
-
|
57
|
+
* Supports MRI and YARV only.
|
58
|
+
* Not complete protection from freeing silly things, e.g core classes. Be sensible :)
|
59
|
+
* Can be dangerous - `free` will force garbage collection on an object even if references to it still exist. Trying to access an already freed object may result in unexpected behaviour or segfaults.
|
43
60
|
|
44
61
|
Contact
|
45
62
|
-------
|
data/ext/free/free.c
CHANGED
@@ -46,10 +46,6 @@ typedef struct RVALUE {
|
|
46
46
|
struct SCOPE scope;
|
47
47
|
#endif
|
48
48
|
} as;
|
49
|
-
#ifdef GC_DEBUG
|
50
|
-
const char *file;
|
51
|
-
int line;
|
52
|
-
#endif
|
53
49
|
} RVALUE;
|
54
50
|
|
55
51
|
#define RANY(o) ((RVALUE*)(o))
|
@@ -97,16 +93,55 @@ make_deferred(RVALUE *p)
|
|
97
93
|
|
98
94
|
VALUE object_free(VALUE obj)
|
99
95
|
{
|
100
|
-
|
101
|
-
|
96
|
+
ID id_destructor = rb_intern("__destruct__");
|
97
|
+
|
98
|
+
/* value returned by destructor */
|
99
|
+
VALUE destruct_value = Qnil;
|
100
|
+
|
101
|
+
/* prevent freeing of immediates */
|
102
|
+
switch (TYPE(obj)) {
|
102
103
|
case T_NIL:
|
103
104
|
case T_FIXNUM:
|
104
105
|
case T_TRUE:
|
105
106
|
case T_FALSE:
|
106
|
-
|
107
|
+
case T_SYMBOL:
|
108
|
+
rb_raise(rb_eTypeError, "obj_free() called for immediate value");
|
107
109
|
break;
|
108
110
|
}
|
109
111
|
|
112
|
+
/* prevent freeing of *some* critical objects */
|
113
|
+
if ((obj == rb_cObject) ||
|
114
|
+
(obj == rb_cClass) ||
|
115
|
+
(obj == rb_cModule) ||
|
116
|
+
(obj == rb_cSymbol) ||
|
117
|
+
(obj == rb_cFixnum) ||
|
118
|
+
(obj == rb_cFloat) ||
|
119
|
+
(obj == rb_cString) ||
|
120
|
+
(obj == rb_cRegexp) ||
|
121
|
+
(obj == rb_cInteger) ||
|
122
|
+
(obj == rb_cArray) ||
|
123
|
+
(obj == rb_cNilClass) ||
|
124
|
+
(obj == rb_cFalseClass) ||
|
125
|
+
(obj == rb_cTrueClass) ||
|
126
|
+
(obj == rb_cNumeric) ||
|
127
|
+
(obj == rb_cBignum) ||
|
128
|
+
(obj == rb_cStruct))
|
129
|
+
rb_raise(rb_eTypeError, "obj_free() called for critical object");
|
130
|
+
|
131
|
+
/* run destructor (if one is defined) */
|
132
|
+
if (rb_respond_to(obj, id_destructor))
|
133
|
+
destruct_value = rb_funcall(obj, id_destructor, 0);
|
134
|
+
|
135
|
+
#ifdef RUBY_19
|
136
|
+
switch (BUILTIN_TYPE(obj)) {
|
137
|
+
case T_NIL:
|
138
|
+
case T_FIXNUM:
|
139
|
+
case T_TRUE:
|
140
|
+
case T_FALSE:
|
141
|
+
rb_bug("obj_free() called for broken object");
|
142
|
+
break;
|
143
|
+
}
|
144
|
+
|
110
145
|
if (FL_TEST(obj, FL_EXIVAR)) {
|
111
146
|
rb_free_generic_ivar((VALUE)obj);
|
112
147
|
FL_UNSET(obj, FL_EXIVAR);
|
@@ -346,7 +381,7 @@ VALUE object_free(VALUE obj)
|
|
346
381
|
|
347
382
|
rb_gc_force_recycle(obj);
|
348
383
|
|
349
|
-
return
|
384
|
+
return destruct_value;
|
350
385
|
}
|
351
386
|
|
352
387
|
void
|
data/lib/free.rb
CHANGED
@@ -9,10 +9,15 @@ require "#{direc}/free/version"
|
|
9
9
|
module Free
|
10
10
|
|
11
11
|
# Force garbage collection on an object and free its internal structures.
|
12
|
-
# @return nil
|
12
|
+
# @return [nil, Object] Return value of \_\_destruct\_\_ method (if
|
13
|
+
# defined) or nil (if no \_\_destruct\_\_ method)
|
13
14
|
# @example
|
14
15
|
# h = "hello"
|
15
|
-
# h.
|
16
|
+
# def h.__destruct__
|
17
|
+
# :killed
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# h.free #=> :killed
|
16
21
|
def free() end
|
17
22
|
end
|
18
23
|
|
data/lib/free/version.rb
CHANGED
data/test/test.rb
CHANGED
@@ -32,4 +32,33 @@ describe Free do
|
|
32
32
|
(ObjectSpace._id2ref(id) != v || lambda { ObjectSpace._id2ref(id) } rescue true).should == true
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
it 'should run destructor before freeing object' do
|
37
|
+
o = Object.new
|
38
|
+
|
39
|
+
def o.__destruct__
|
40
|
+
:killed
|
41
|
+
end
|
42
|
+
|
43
|
+
o.free.should == :killed
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return nil if no destructor specified' do
|
47
|
+
o = Object.new
|
48
|
+
o.free.should == nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should raise when trying to free immediate values' do
|
52
|
+
[nil, 0, true, false, :symbol].each do |v|
|
53
|
+
lambda { v.free }.should.raise TypeError
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should raise when trying to free critical objects' do
|
58
|
+
[Object, Class, Module, Symbol, Fixnum, Float,
|
59
|
+
String, Regexp, Integer, Array, NilClass, FalseClass,
|
60
|
+
TrueClass, Numeric, Bignum, Struct].each do |v|
|
61
|
+
lambda { v.free }.should.raise TypeError
|
62
|
+
end
|
63
|
+
end
|
35
64
|
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: free
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
4
|
+
prerelease: true
|
6
5
|
segments:
|
7
6
|
- 0
|
8
|
-
- 1
|
9
7
|
- 2
|
10
|
-
|
8
|
+
- 0pre1
|
9
|
+
version: 0.2.0pre1
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- John Mair (banisterfiend)
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-04 00:00:00 +13:00
|
19
18
|
default_executable:
|
20
19
|
dependencies: []
|
21
20
|
|
@@ -72,19 +71,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
71
|
requirements:
|
73
72
|
- - ">="
|
74
73
|
- !ruby/object:Gem::Version
|
75
|
-
hash: 3
|
76
74
|
segments:
|
77
75
|
- 0
|
78
76
|
version: "0"
|
79
77
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
78
|
none: false
|
81
79
|
requirements:
|
82
|
-
- - "
|
80
|
+
- - ">"
|
83
81
|
- !ruby/object:Gem::Version
|
84
|
-
hash: 3
|
85
82
|
segments:
|
86
|
-
-
|
87
|
-
|
83
|
+
- 1
|
84
|
+
- 3
|
85
|
+
- 1
|
86
|
+
version: 1.3.1
|
88
87
|
requirements: []
|
89
88
|
|
90
89
|
rubyforge_project:
|