Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5717e3511910
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a0ea58c0fdbd
Choose a head ref
  • 6 commits
  • 4 files changed
  • 2 contributors

Commits on Aug 5, 2016

  1. Copy the full SHA
    cda3cc8 View commit details
  2. Copy the full SHA
    c76b21d View commit details
  3. [ji] Ruby Enumerators now convertible to Java iterator

    .. useful for getting Ruby enum structures into Java stream land
    kares committed Aug 5, 2016
    Copy the full SHA
    2752cbd View commit details

Commits on Aug 8, 2016

  1. make Enumerator's Java Iterator iface behave look-ahead with hasNext

    ... test confirm that Enumerator is now usable with Java 8 stream APIs
    kares committed Aug 8, 2016
    Copy the full SHA
    52cea3d View commit details

Commits on Aug 24, 2016

  1. [ji] add .rb bits for Enumerator Java 8 extensions enum.to_java.stream

    ... this would be desirable/possible to do in RubyEnumerator.java
    but we can not since we're not compliling under Java 8
    
    it is still useful to be able to do `enum.to_java.stream` from Ruby
    kares committed Aug 24, 2016
    Copy the full SHA
    57dc657 View commit details
  2. Merge pull request #4054 from kares/test-ji-enumerator

    RubyEnumerator implements java.util.Iterator
    kares authored Aug 24, 2016
    Copy the full SHA
    a0ea58c View commit details
Showing with 221 additions and 41 deletions.
  1. +80 −37 core/src/main/java/org/jruby/RubyEnumerator.java
  2. +1 −0 core/src/main/ruby/jruby/java.rb
  3. +19 −0 core/src/main/ruby/jruby/java/java_8.rb
  4. +121 −4 test/jruby/test_enumerator.rb
117 changes: 80 additions & 37 deletions core/src/main/java/org/jruby/RubyEnumerator.java
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@
* Implementation of Ruby's Enumerator module.
*/
@JRubyModule(name="Enumerator", include="Enumerable")
public class RubyEnumerator extends RubyObject {
public class RubyEnumerator extends RubyObject implements java.util.Iterator<Object> {
/** target for each operation */
private IRubyObject object;

@@ -434,20 +434,30 @@ public IRubyObject each_cons(ThreadContext context, IRubyObject arg, final Block
}

@JRubyMethod
public IRubyObject size(ThreadContext context) {
public final IRubyObject size(ThreadContext context) {
if (sizeFn != null) {
return sizeFn.size(methodArgs);
}

IRubyObject size = this.size;
if (size != null) {
if (size.respondsTo("call")) {
if (context == null) context = getRuntime().getCurrentContext();
return size.callMethod(context, "call");
}

return size;
}

return context.nil;
return context == null ? null : context.nil;
}

public long size() {
final IRubyObject size = size(null);
if ( size instanceof RubyNumeric ) {
return ((RubyNumeric) size).getLongValue();
}
return -1;
}

private SizeFn enumSizeFn(final ThreadContext context) {
@@ -501,7 +511,8 @@ public IRubyObject with_index19(ThreadContext context, IRubyObject arg, final Bl

@JRubyMethod
public synchronized IRubyObject next(ThreadContext context) {
ensureNexter(context);
final Nexter nexter = ensureNexter(context.runtime);

if (!feedValue.isNil()) feedValue = context.nil;
return nexter.next();
}
@@ -520,28 +531,24 @@ public synchronized IRubyObject rewind(ThreadContext context) {

@JRubyMethod
public synchronized IRubyObject peek(ThreadContext context) {
ensureNexter(context);
final Nexter nexter = ensureNexter(context.runtime);

return nexter.peek();
}

@JRubyMethod(name = "peek_values")
public synchronized IRubyObject peekValues(ThreadContext context) {
ensureNexter(context);

return RubyArray.newArray(context.runtime, nexter.peek());
public IRubyObject peekValues(ThreadContext context) {
return RubyArray.newArray(context.runtime, peek(context));
}

@JRubyMethod(name = "next_values")
public synchronized IRubyObject nextValues(ThreadContext context) {
ensureNexter(context);
if (!feedValue.isNil()) feedValue = context.nil;
return RubyArray.newArray(context.runtime, nexter.next());
public IRubyObject nextValues(ThreadContext context) {
return RubyArray.newArray(context.runtime, next(context));
}

@JRubyMethod
public IRubyObject feed(ThreadContext context, IRubyObject val) {
ensureNexter(context);
public synchronized IRubyObject feed(ThreadContext context, IRubyObject val) {
final Nexter nexter = ensureNexter(context.runtime);
if (!feedValue.isNil()) {
throw context.runtime.newTypeError("feed value already set");
}
@@ -550,18 +557,16 @@ public IRubyObject feed(ThreadContext context, IRubyObject val) {
return context.nil;
}

private void ensureNexter(ThreadContext context) {
if (nexter == null) {
if (Options.ENUMERATOR_LIGHTWEIGHT.load()) {
if (object instanceof RubyArray && method.equals("each") && methodArgs.length == 0) {
nexter = new ArrayNexter(context.runtime, object, method, methodArgs);
} else {
nexter = new ThreadedNexter(context.runtime, object, method, methodArgs);
}
} else {
nexter = new ThreadedNexter(context.runtime, object, method, methodArgs);
private Nexter ensureNexter(final Ruby runtime) {
Nexter nexter = this.nexter;
if (nexter != null) return nexter;

if (Options.ENUMERATOR_LIGHTWEIGHT.load()) {
if (object instanceof RubyArray && method.equals("each") && methodArgs.length == 0) {
return this.nexter = new ArrayNexter(runtime, object, method, methodArgs);
}
}
return this.nexter = new ThreadedNexter(runtime, object, method, methodArgs);
}

@Override
@@ -577,6 +582,23 @@ protected void finalize() throws Throwable {
}
}

// java.util.Iterator :

@Override
public synchronized boolean hasNext() {
return ensureNexter(getRuntime()).hasNext();
}

@Override
public Object next() {
return next( getRuntime().getCurrentContext() ).toJava( java.lang.Object.class );
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}

/**
* "Function" type for java-created enumerators with size. Should be implemented so that calls to
* SizeFn#size are kept in sync with the size of the created enum (i.e. if the object underlying an enumerator
@@ -623,6 +645,8 @@ public IRubyObject getFeedValue() {
public abstract void shutdown();

public abstract IRubyObject peek();

abstract boolean hasNext() ;
}

private static class ArrayNexter extends Nexter {
@@ -659,7 +683,12 @@ protected IRubyObject get() {
}

private void checkIndex() throws RaiseException {
if (index >= array.size()) throw runtime.newStopIteration(array, null);
if ( ! hasNext() ) throw runtime.newStopIteration(array, null);
}

@Override
final boolean hasNext() {
return index < array.size();
}
}

@@ -697,13 +726,7 @@ public ThreadedNexter(Ruby runtime, IRubyObject object, String method, IRubyObje

@Override
public synchronized IRubyObject next() {
if (doneObject != null) {
return returnValue(doneObject);
}

ensureStarted();

return returnValue(take());
return nextImpl(false);
}

@Override
@@ -732,7 +755,7 @@ public synchronized void shutdown() {
@Override
public synchronized IRubyObject peek() {
if (doneObject != null) {
return returnValue(doneObject);
return returnValue(doneObject, false);
}

ensureStarted();
@@ -743,7 +766,7 @@ public synchronized IRubyObject peek() {

peekTake();

return returnValue(lastValue);
return returnValue(lastValue, false);
}

private void ensureStarted() {
@@ -783,23 +806,43 @@ private IRubyObject take() {
}
}

private IRubyObject returnValue(IRubyObject value) {
private IRubyObject returnValue(IRubyObject value, final boolean silent) {
// if it's the NEVER object, raise StopIteration
if (value == NEVER) {
doneObject = value;
if ( silent ) return null;
throw runtime.newStopIteration(stopValue, "iteration reached an end");
}

// if it's an exception, raise it
if (value instanceof RubyException) {
doneObject = value;
throw new RaiseException((RubyException)value);
if ( silent ) return null;
throw new RaiseException((RubyException) value);
}

// otherwise, just return it
return value;
}

private IRubyObject nextImpl(boolean hasNext) {
if (doneObject != null) {
return returnValue(doneObject, hasNext);
}

ensureStarted();

return returnValue(take(), hasNext);
}

@Override
final synchronized boolean hasNext() {
if ( doneObject == NEVER ) return false; // already done
// we're doing read-ahead so Iterator#hasNext() might do enum.next
// value 'buffering' - to be returned on following Iterator#next
return ( lastValue = nextImpl(true) ) != null;
}

@Override
public void run() {
if (die) return;
1 change: 1 addition & 0 deletions core/src/main/ruby/jruby/java.rb
Original file line number Diff line number Diff line change
@@ -38,3 +38,4 @@

load 'jruby/java/core_ext.rb'
load 'jruby/java/java_ext.rb'
load 'jruby/java/java_8.rb' if java.util.Spliterator rescue nil
19 changes: 19 additions & 0 deletions core/src/main/ruby/jruby/java/java_8.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Extensions for Java 8

# @private shall be moved to Java when compiling against Java 8
org.jruby.RubyEnumerator.class_eval do

def stream(parallel = false)
java.util.stream.StreamSupport.stream spliterator, parallel
end

def spliterator(mod = nil)
size = self.size
# mod = java.util.Spliterator::NONNULL
# we do not have ArrayNexter detection - assume immutable
mod ||= java.util.Spliterator::IMMUTABLE
mod ||= java.util.Spliterator::SIZED if size >= 0
java.util.Spliterators.spliterator(self, size, mod)
end

end
Loading