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.
- data/README.txt +20 -0
- data/Rakefile +36 -0
- data/lib/rubinius/core-api.rb +16 -0
- data/lib/rubinius/kernel/bootstrap/channel.rb +106 -0
- data/lib/rubinius/kernel/common/bytearray.rb +26 -0
- data/lib/rubinius/kernel/common/channel.rb +21 -0
- data/lib/rubinius/kernel/common/env.rb +239 -0
- data/lib/rubinius/kernel/common/thread.rb +118 -0
- data/lib/rubinius/kernel/common/tuple.rb +148 -0
- data/lib/rubinius/kernel/common/type.rb +114 -0
- data/rubinius-core-api.gemspec +28 -0
- data/src/org/jruby/ext/rubinius/RubiniusByteArray.java +290 -0
- data/src/org/jruby/ext/rubinius/RubiniusChannel.java +115 -0
- data/src/org/jruby/ext/rubinius/RubiniusEnvironmentAccess.java +57 -0
- data/src/org/jruby/ext/rubinius/RubiniusKernel.java +42 -0
- data/src/org/jruby/ext/rubinius/RubiniusLibrary.java +111 -0
- data/src/org/jruby/ext/rubinius/RubiniusTuple.java +302 -0
- data/src/org/jruby/ext/rubinius/RubiniusType.java +43 -0
- metadata +85 -0
data/README.txt
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|