diff --git a/lib/ruby/site_ruby/shared/generator.rb b/lib/ruby/site_ruby/shared/generator.rb
index 1678c49..66230e8 100644
--- a/lib/ruby/site_ruby/shared/generator.rb
+++ b/lib/ruby/site_ruby/shared/generator.rb
@@ -59,121 +59,165 @@ require 'thread'
class Generator
include Enumerable
- # marks the end of enumeration
- END_MARKER = Object.new
- def END_MARKER.inspect ; "END_MARKER" ; end
-
- # a queue which emulates the producer-side interface of a generator
- class ProducerQueue < SizedQueue
- def initialize
- super(1)
+ # Returns true if the generator has not reached the end yet.
+ def next?()
+ !end?
+ end
+
+ # Returns the current index (position) counting from zero.
+ def index()
+ @index
+ end
+ alias pos index
+
+ # Construct a new generator; defaults to the threaded impl
+ def self.new(*args)
+ generator = (self == Generator) ? ThreadedGenerator : self
+
+ gen = generator.allocate
+ gen.send :initialize, *args
+ gen
+ end
+
+ class IndexedEachGenerator < Generator
+ SIMPLE_INDEXER = proc {|ary, i| ary[i]}
+ def initialize(ary, indexer = nil)
+ @ary = ary
+ @index = 0
+ @indexer = indexer || SIMPLE_INDEXER
end
- alias yield push
+ def end?
+ @index >= @ary.size
+ end
- def _run_enum(enum)
- _run { enum.each { |x| self.yield(x) } }
+ def next
+ obj, @index = current, @index + 1
+ obj
end
- def _run
- # caller manages thread, to avoid circularity
- Thread.new do
- begin
- yield self
- ensure
- self.yield(END_MARKER)
- end
- end
+ def current
+ raise EOFError, "no more elements available" if end?
+ @indexer.call(@ary, @index)
end
- end
- # Creates a new generator either from an Enumerable object or from a
- # block.
- #
- # In the former, block is ignored even if given.
- #
- # In the latter, the given block is called with the generator
- # itself, and expected to call the +yield+ method for each element.
- def initialize(enum = nil, &block)
- @thread.kill if @thread
-
- @queue = ProducerQueue.new
- @got_next_element = false
- @next_element = nil
- @index = 0
-
- @enum = enum
- @block = block
- if enum
- @thread = @queue._run_enum(enum)
- else
- @thread = @queue._run(&block)
+ def rewind
+ @index = 0
end
- self
+ def each
+ for i in 0...@ary.size
+ yield @indexer.call(i)
+ end
+ end
end
- # Yields an element to the generator.
- def yield(value)
- @queue.yield(value)
- self
- end
+ class ThreadedGenerator < Generator
+ # marks the end of enumeration
+ END_MARKER = Object.new
+ def END_MARKER.inspect ; "END_MARKER" ; end
+
+ # a queue which emulates the producer-side interface of a generator
+ class ProducerQueue < SizedQueue
+ def initialize
+ super(1)
+ end
+
+ alias yield push
- # gets the next element; may block
- def _next_element
- unless @got_next_element
- @next_element = @queue.pop
- @got_next_element = true
+ def _run_enum(enum)
+ _run { enum.each { |x| self.yield(x) } }
+ end
+
+ def _run
+ # caller manages thread, to avoid circularity
+ Thread.new do
+ begin
+ yield self
+ ensure
+ self.yield(END_MARKER)
+ end
+ end
+ end
end
- @next_element
- end
- # Returns true if the generator has reached the end.
- def end?()
- END_MARKER.equal? _next_element
- end
+ # Creates a new generator either from an Enumerable object or from a
+ # block.
+ #
+ # In the former, block is ignored even if given.
+ #
+ # In the latter, the given block is called with the generator
+ # itself, and expected to call the +yield+ method for each element.
+ def initialize(enum = nil, &block)
+ @thread.kill if @thread
+
+ @queue = ProducerQueue.new
+ @got_next_element = false
+ @next_element = nil
+ @index = 0
+
+ @enum = enum
+ @block = block
+ if enum
+ @thread = @queue._run_enum(enum)
+ else
+ @thread = @queue._run(&block)
+ end
- # Returns true if the generator has not reached the end yet.
- def next?()
- !end?
- end
+ self
+ end
- # Returns the current index (position) counting from zero.
- def index()
- @index
- end
- alias pos index
+ # Yields an element to the generator.
+ def yield(value)
+ @queue.yield(value)
+ self
+ end
- # Returns the element at the current position and moves forward.
- def next()
- result = current
- @index += 1
- @got_next_element = false
- @next_element = nil
- result
- end
+ # gets the next element; may block
+ def _next_element
+ unless @got_next_element
+ @next_element = @queue.pop
+ @got_next_element = true
+ end
+ @next_element
+ end
- # Returns the element at the current position.
- def current()
- raise EOFError, "no more elements available" if end?
- _next_element
- end
+ # Returns true if the generator has reached the end.
+ def end?()
+ END_MARKER.equal? _next_element
+ end
- # Rewinds the generator.
- def rewind()
- initialize(@enum, &@block) if @index.nonzero?
- self
- end
+ # Returns the element at the current position and moves forward.
+ def next()
+ result = current
+ @index += 1
+ @got_next_element = false
+ @next_element = nil
+ result
+ end
- # Rewinds the generator and enumerates the elements.
- def each
- rewind
+ # Returns the element at the current position.
+ def current()
+ raise EOFError, "no more elements available" if end?
+ _next_element
+ end
- until end?
- yield self.next
+ # Rewinds the generator.
+ def rewind()
+ initialize(@enum, &@block) if @index.nonzero?
+ self
end
- self
+ # Rewinds the generator and enumerates the elements.
+ def each
+ rewind
+
+ until end?
+ yield self.next
+ end
+
+ self
+ end
end
if RUBY_VERSION =~ /^1\.9/
@@ -184,9 +228,29 @@ class Generator
class Enumerator
def __generator
- @generator ||= Generator.new(self)
+ @generator ||= __choose_generator
+ end
+
+ def __choose_generator
+ case @__object__
+ when Array
+ case @__method__
+ when :each
+ return Generator::IndexedEachGenerator.new(@__object__)
+ end
+ when Hash
+ case @__method__
+ when :each
+ keys = @__object__.keys
+ hash_indexer = lambda do |hash, i|
+ return keys[i], hash[keys[i]]
+ end
+ return Generator::IndexedEachGenerator.new(@__object__, hash_indexer)
+ end
+ end
+ return Generator.new(self)
end
- private :__generator
+ private :__generator, :__choose_generator
# call-seq:
# e.next => object
diff --git a/src/org/jruby/RubyEnumerator.java b/src/org/jruby/RubyEnumerator.java
index bbd05fa..ce3fe45 100644
--- a/src/org/jruby/RubyEnumerator.java
+++ b/src/org/jruby/RubyEnumerator.java
@@ -84,13 +84,12 @@ public class RubyEnumerator extends RubyObject {
private RubyEnumerator(Ruby runtime, RubyClass type) {
super(runtime, type);
object = runtime.getNil();
+ initialize(runtime.getNil(), RubyString.newEmptyString(runtime), IRubyObject.NULL_ARRAY);
}
private RubyEnumerator(Ruby runtime, IRubyObject object, IRubyObject method, IRubyObject[]args) {
super(runtime, runtime.getEnumerator());
- this.object = object;
- this.method = method.asJavaString();
- this.methodArgs = args;
+ initialize(object, method, args);
}
static IRubyObject enumeratorize(Ruby runtime, IRubyObject object, String method) {
@@ -124,6 +123,9 @@ public class RubyEnumerator extends RubyObject {
this.object = object;
this.method = method.asJavaString();
this.methodArgs = methodArgs;
+ setInstanceVariable("@__object__", object);
+ setInstanceVariable("@__method__", method);
+ setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(getRuntime(), methodArgs));
return this;
}