rubinius-core-api 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ This is an attempt to provide some of Rubinius's additional core classes on
2
+ other implementations.
3
+
4
+ Rubinius has all the normal Ruby classes, but to support implementing them
5
+ mostly in Ruby, they have added a number of other support classes. This
6
+ library hopes to implement those additional support classes for other Ruby
7
+ implementations, so they their utility can be shared across implementations.
8
+
9
+ Currently, only the following classes are implemented, and only on JRuby:
10
+
11
+ Rubinius::ByteArray - a fixed-size array of bytes
12
+ Rubinius::Channel - a low-level synchronization mechanism
13
+ Rubinius::EnvironmentAccess - env variable support
14
+ Rubinius::Tuple - a fixed-size array of object references
15
+ Rubinius::Type - utilities for type conversions
16
+
17
+ In addition, some utility methods added to Thread for recursive guards and
18
+ Kernel::StringValue are also implemented.
19
+
20
+ More APIs will be added over time.
@@ -0,0 +1,36 @@
1
+ task :default => :spec
2
+
3
+ if defined?(JRUBY_VERSION)
4
+ require 'ant'
5
+
6
+ directory "pkg/classes"
7
+
8
+ desc "Clean up build artifacts"
9
+ task :clean do
10
+ rm_rf "pkg/classes"
11
+ rm_rf "lib/rubinius-core-api.jar"
12
+ end
13
+
14
+ desc "Compile the extension"
15
+ task :compile => "pkg/classes" do |t|
16
+ ant.javac :srcdir => "src", :destdir => t.prerequisites.first,
17
+ :source => "1.5", :target => "1.5", :debug => true,
18
+ :classpath => "${java.class.path}:${sun.boot.class.path}"
19
+ end
20
+
21
+ desc "Build the jar"
22
+ task :jar => :compile do
23
+ ant.jar :basedir => "pkg/classes", :destfile => "lib/rubinius-core-api.jar", :includes => "**/*.class"
24
+ end
25
+
26
+ task :package => :jar
27
+ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
28
+ task :package do
29
+ # nop
30
+ end
31
+ end
32
+
33
+ desc "Run the specs"
34
+ task :spec => :package do
35
+ ruby "-S", "rspec", "spec"
36
+ end
@@ -0,0 +1,16 @@
1
+ if defined?(JRUBY_VERSION)
2
+ begin
3
+ require 'rubinius-core-api.jar'
4
+ rescue LoadError
5
+ # keep going; might be bundled into classpath already
6
+ end
7
+
8
+ # for access to JRuby runtime
9
+ require 'jruby'
10
+
11
+ org.jruby.ext.rubinius.RubiniusLibrary.new.load(JRuby.runtime, false)
12
+ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
13
+ # do nothing for Rubinius
14
+ else
15
+ raise "Only supported on JRuby and Rubinius"
16
+ end
@@ -0,0 +1,106 @@
1
+ ##
2
+ # Channel is a FIFO, thread-aware value passing class that can hold any number
3
+ # of objects similar to Queue. Use #send to add objects to the channel and
4
+ # #receive to remove objects from the channel.
5
+ #
6
+ # If Channel#receive is called on an empty channel, the VM puts the current
7
+ # Thread (t1) to sleep. At some future time, when Channel#send is called on
8
+ # that same thread, the VM wakes up t1 and the value passed to #send is
9
+ # returned. This allows us to implement all manner of Thread semantics, such
10
+ # as Mutex.
11
+ #
12
+ # Channel is used heavily by Scheduler, to allow ruby code to interact with
13
+ # the outside world in a thread aware manner.
14
+
15
+ module Rubinius
16
+ class Channel
17
+
18
+ ##
19
+ # Returns nil if nothing is waiting, or a List object which contains all
20
+ # Thread objects waiting on this Channel.
21
+
22
+ attr_reader :waiting
23
+
24
+ ##
25
+ # Returns nil if there are no values, otherwise a List object containing all
26
+ # values the Channel contains.
27
+
28
+ attr_reader :value
29
+
30
+ ##
31
+ # Creates a new Channel and registers it with the VM.
32
+
33
+ def self.new
34
+ Ruby.primitive :channel_new
35
+ raise PrimitiveFailure, "Channel.new primitive failed"
36
+ end
37
+
38
+ # We must be sure a Channel is always created properly, so handle
39
+ # this the same as new.
40
+ def self.allocate
41
+ Ruby.primitive :channel_new
42
+ raise PrimitiveFailure, "Channel.new primitive failed"
43
+ end
44
+
45
+ ##
46
+ # Puts +obj+ in the Channel. If there are waiting threads the first thread
47
+ # will be woken up and handed +obj+.
48
+
49
+ def send(obj)
50
+ Ruby.primitive :channel_send
51
+ raise PrimitiveFailure, "Channel#send primitive failed"
52
+ end
53
+
54
+ alias_method :<<, :send
55
+
56
+ ##
57
+ # Removes and returns the first value from the Channel. If the channel
58
+ # is empty, Thread.current is put to sleep until #send is called.
59
+
60
+ def receive
61
+ Ruby.primitive :channel_receive
62
+ raise PrimitiveFailure, "Channel#receive primitive failed"
63
+ end
64
+
65
+ def receive_timeout(duration)
66
+ Ruby.primitive :channel_receive_timeout
67
+ raise PrimitiveFailure, "Channel#receive_timeout primitive failed"
68
+ end
69
+
70
+ def try_receive
71
+ Ruby.primitive :channel_try_receive
72
+ raise PrimitiveFailure, "Channel#try_receive primitive failed"
73
+ end
74
+
75
+ ##
76
+ # Converts +obj+ into a Channel using #to_channel.
77
+
78
+ def self.convert_to_channel(obj)
79
+ return obj if Channel === obj
80
+ begin
81
+ o2 = obj.to_channel
82
+ unless Channel === o2
83
+ raise ArgumentError, "to_channel on #{obj.inspect} did not return a Channel"
84
+ end
85
+ return o2
86
+ rescue NoMethodError
87
+ raise ArgumentError, "Unable to convert #{obj.inspect} into a channel"
88
+ end
89
+ end
90
+
91
+ ##
92
+ # Legacy API. To be removed.
93
+
94
+ def self.receive(obj) # :nodoc:
95
+ return convert_to_channel(obj).receive
96
+ end
97
+
98
+ ##
99
+ # API compliance, returns self.
100
+
101
+ def to_channel
102
+ self
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,26 @@
1
+ ##
2
+ # An array of bytes, used as a low-level data store for implementing various
3
+ # other classes.
4
+
5
+ class Rubinius::ByteArray
6
+ alias_method :[], :get_byte
7
+ alias_method :[]=, :set_byte
8
+
9
+ def each
10
+ i = 0
11
+ max = size()
12
+
13
+ while i < max
14
+ yield get_byte(i)
15
+ i += 1
16
+ end
17
+ end
18
+
19
+ def inspect
20
+ "#<#{self.class}:0x#{object_id.to_s(16)} #{size} bytes>"
21
+ end
22
+
23
+ def <=>(other)
24
+ compare_bytes other, size, other.size
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ ##
2
+ # A communication mechanism based on pi-calculus channels used primarily to
3
+ # communicate between ruby and the VM about events.
4
+
5
+ module Rubinius
6
+ class Channel
7
+ def inspect
8
+ "#<Rubinius::Channel>"
9
+ end
10
+
11
+ def as_lock(val=nil)
12
+ receive
13
+
14
+ begin
15
+ yield
16
+ ensure
17
+ self << val
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,239 @@
1
+ ##
2
+ # Interface to process environment variables.
3
+
4
+ module Rubinius
5
+ class EnvironmentVariables
6
+ include Enumerable
7
+ include Rubinius::EnvironmentAccess
8
+
9
+ def [](key)
10
+ getenv(StringValue(key)).freeze
11
+ end
12
+
13
+ def []=(key, value)
14
+ key = StringValue(key)
15
+ if value.nil?
16
+ unsetenv(key)
17
+ else
18
+ setenv key, StringValue(value), 1
19
+ end
20
+ value
21
+ end
22
+
23
+ alias_method :store, :[]=
24
+
25
+ def each_key
26
+ return to_enum(:each_key) unless block_given?
27
+
28
+ each { |k, v| yield k }
29
+ end
30
+
31
+ def each_value
32
+ return to_enum(:each_value) unless block_given?
33
+
34
+ each { |k, v| yield v }
35
+ end
36
+
37
+ def each
38
+ return to_enum(:each) unless block_given?
39
+
40
+ env = environ()
41
+ ptr_size = FFI.type_size FFI.find_type(:pointer)
42
+
43
+ i = 0
44
+
45
+ offset = 0
46
+ cur = env + offset
47
+
48
+ until cur.read_pointer.null?
49
+ entry = cur.read_pointer.read_string
50
+ key, value = entry.split '=', 2
51
+ value.taint if value
52
+ key.taint if key
53
+
54
+ yield key, value
55
+
56
+ offset += ptr_size
57
+ cur = env + offset
58
+ end
59
+
60
+ self
61
+ end
62
+
63
+ alias_method :each_pair, :each
64
+
65
+ def delete(key)
66
+ existing_value = self[key]
67
+ self[key] = nil if existing_value
68
+ existing_value
69
+ end
70
+
71
+ def delete_if(&block)
72
+ return to_enum(:delete_it) unless block_given?
73
+ reject!(&block)
74
+ self
75
+ end
76
+
77
+ def fetch(key, absent=undefined)
78
+ if block_given? and !absent.equal?(undefined)
79
+ warn "block supersedes default value argument"
80
+ end
81
+
82
+ if value = self[key]
83
+ return value
84
+ end
85
+
86
+ if block_given?
87
+ return yield(key)
88
+ elsif absent.equal?(undefined)
89
+ raise IndexError, "key not found"
90
+ end
91
+
92
+ return absent
93
+ end
94
+
95
+ def include?(key)
96
+ !self[key].nil?
97
+ end
98
+
99
+ alias_method :has_key?, :include?
100
+ alias_method :key?, :include?
101
+ # More efficient than using the one from Enumerable
102
+ alias_method :member?, :include?
103
+
104
+ def to_s
105
+ "ENV"
106
+ end
107
+
108
+ def inspect
109
+ to_hash.inspect
110
+ end
111
+
112
+ def reject(&block)
113
+ to_hash.reject(&block)
114
+ end
115
+
116
+ def reject!
117
+ return to_enum(:reject!) unless block_given?
118
+
119
+ rejected = false
120
+ each do |k, v|
121
+ if yield(k, v)
122
+ delete k
123
+ rejected = true
124
+ end
125
+ end
126
+
127
+ rejected ? self : nil
128
+ end
129
+
130
+ def clear
131
+ # Avoid deleting from environ while iterating because the
132
+ # OS can handle that in a million different bad ways.
133
+
134
+ keys = []
135
+ each { |k,v| keys << k }
136
+ keys.each { |k| delete k }
137
+
138
+ self
139
+ end
140
+
141
+ def has_value?(value)
142
+ each { |k,v| return true if v == value }
143
+ return false
144
+ end
145
+
146
+ alias_method :value?, :has_value?
147
+
148
+ def values_at(*params)
149
+ params.map{ |k| self[k] }
150
+ end
151
+
152
+ def index(value)
153
+ each do |k, v|
154
+ return k if v == value
155
+ end
156
+ nil
157
+ end
158
+
159
+ def invert
160
+ to_hash.invert
161
+ end
162
+
163
+ def keys
164
+ keys = []
165
+ each { |k,v| keys << k }
166
+ keys
167
+ end
168
+
169
+ def values
170
+ vals = []
171
+ each { |k,v| vals << v }
172
+ vals
173
+ end
174
+
175
+ def empty?
176
+ each { return false }
177
+ return true
178
+ end
179
+
180
+ def length
181
+ sz = 0
182
+ each { |k,v| sz += 1 }
183
+ sz
184
+ end
185
+
186
+ alias_method :size, :length
187
+
188
+ def rehash
189
+ # No need to do anything, our keys are always strings
190
+ end
191
+
192
+ def replace(other)
193
+ clear
194
+ other.each { |k, v| self[k] = v }
195
+ end
196
+
197
+ def shift
198
+ env = environ()
199
+ ptr_size = FFI.type_size FFI.find_type(:pointer)
200
+
201
+ offset = 0
202
+ cur = env + offset
203
+
204
+ ptr = cur.read_pointer
205
+ return nil unless ptr
206
+
207
+ key, value = ptr.read_string.split "=", 2
208
+
209
+ return nil unless key
210
+
211
+ key.taint if key
212
+ value.taint if value
213
+
214
+ delete key
215
+
216
+ return [key, value]
217
+ end
218
+
219
+ def to_a
220
+ ary = []
221
+ each { |k,v| ary << [k,v] }
222
+ ary
223
+ end
224
+
225
+ def to_hash
226
+ return environ_as_hash()
227
+ end
228
+
229
+ def update(other, &block)
230
+ if block_given?
231
+ other.each { |k, v| self[k] = yield(k, self[k], v) }
232
+ else
233
+ other.each { |k, v| self[k] = v }
234
+ end
235
+ end
236
+
237
+ # Missing and deprecated: indexes, indices
238
+ end
239
+ end