Skip to content

Commit

Permalink
(peazy) enumerator backports for 1.7 (#4563)
Browse files Browse the repository at this point in the history
* Implemented Enumerator#peek_values

* Implemented Enumerator#next_values

* Implemented Enumerator#feed method

* Set feedValue to nil for Enumerator#next_values method as well

* Reduce getRuntime/context.runtime calls for better performance

* Fix the bug with size with each_slice without block

* Return Enumerator self calling #each with no block. Fixes #3009.

+ back-port-fix: make sure feedValue is always initialized
kares authored Apr 20, 2017
1 parent 2f67fdb commit c4198ee
Showing 2 changed files with 68 additions and 5 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyArray.java
Original file line number Diff line number Diff line change
@@ -1650,7 +1650,7 @@ public IRubyObject each_slice(ThreadContext context, IRubyObject arg, Block bloc
final int size = RubyNumeric.num2int(arg);
final Ruby runtime = context.runtime;
if (size <= 0) throw runtime.newArgumentError("invalid slice size");
return block.isGiven() ? eachSlice(context, size, block) : enumeratorize(context.runtime, this, "each_slice", arg);
return block.isGiven() ? eachSlice(context, size, block) : RubyEnumerator.enumeratorizeWithSize(context, this, "each_slice", arg, arg);
}

/** rb_ary_each_index
71 changes: 67 additions & 4 deletions core/src/main/java/org/jruby/RubyEnumerator.java
Original file line number Diff line number Diff line change
@@ -66,6 +66,8 @@ public class RubyEnumerator extends RubyObject {

/** A value or proc to provide the size of the Enumerator contents*/
private IRubyObject size;

private IRubyObject feedValue;

public static void defineEnumerator(Ruby runtime) {
RubyModule enm = runtime.getClassFromPath("Enumerable");
@@ -96,11 +98,24 @@ private RubyEnumerator(Ruby runtime, RubyClass type) {
initialize(runtime.getNil(), RubyString.newEmptyString(runtime), IRubyObject.NULL_ARRAY);
}

private RubyEnumerator(Ruby runtime, RubyClass type, IRubyObject object, IRubyObject method, IRubyObject[]args, IRubyObject size) {
super(runtime, type);
initialize20(object, method, args, size);
}

private RubyEnumerator(Ruby runtime, RubyClass type, IRubyObject object, IRubyObject method, IRubyObject[]args) {
super(runtime, type);
initialize(object, method, args);
}

/**
* Transform object into an Enumerator with the given size
*/
static IRubyObject enumeratorizeWithSize(ThreadContext context, IRubyObject object, String method,IRubyObject arg, IRubyObject size) {
Ruby runtime = context.runtime;
return new RubyEnumerator(runtime, runtime.getEnumerator(), object, runtime.fastNewSymbol(method), new IRubyObject[] { arg }, size);
}

public static IRubyObject enumeratorize(Ruby runtime, IRubyObject object, String method) {
return new RubyEnumerator(runtime, runtime.getEnumerator(), object, runtime.fastNewSymbol(method), IRubyObject.NULL_ARRAY);
}
@@ -233,23 +248,27 @@ public IRubyObject initialize20(ThreadContext context, IRubyObject[] args, Block
}

private IRubyObject initialize(IRubyObject object, IRubyObject method, IRubyObject[] methodArgs) {
final Ruby runtime = getRuntime();
this.object = object;
this.method = method.asJavaString();
this.methodArgs = methodArgs;
this.feedValue = runtime.getNil();
setInstanceVariable("@__object__", object);
setInstanceVariable("@__method__", method);
setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(getRuntime(), methodArgs));
setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(runtime, methodArgs));
return this;
}

private IRubyObject initialize20(IRubyObject object, IRubyObject method, IRubyObject[] methodArgs, IRubyObject size) {
final Ruby runtime = getRuntime();
this.object = object;
this.method = method.asJavaString();
this.methodArgs = methodArgs;
this.size = size;
this.feedValue = runtime.getNil();
setInstanceVariable("@__object__", object);
setInstanceVariable("@__method__", method);
setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(getRuntime(), methodArgs));
setInstanceVariable("@__args__", RubyArray.newArrayNoCopyLight(runtime, methodArgs));
return this;
}

@@ -261,6 +280,8 @@ public IRubyObject dup() {
copy.object = this.object;
copy.method = this.method;
copy.methodArgs = this.methodArgs;
copy.size = this.size;
copy.feedValue = getRuntime().getNil();
return copy;
}

@@ -271,6 +292,10 @@ public IRubyObject dup() {
*/
@JRubyMethod
public IRubyObject each(ThreadContext context, Block block) {
if (!block.isGiven()) {
return this;
}

return object.callMethod(context, method, methodArgs, block);
}

@@ -432,7 +457,7 @@ public static IRubyObject with_index19(ThreadContext context, IRubyObject self,
@JRubyMethod
public synchronized IRubyObject next(ThreadContext context) {
ensureNexter(context);

if (!feedValue.isNil()) feedValue = context.nil;
return nexter.next();
}

@@ -455,6 +480,31 @@ public synchronized IRubyObject peek(ThreadContext context) {
return nexter.peek();
}

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

return RubyArray.newArray(context.runtime, nexter.peek());
}

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

@JRubyMethod(compat = RUBY1_9)
public IRubyObject feed(ThreadContext context, IRubyObject val) {
ensureNexter(context);
if (!feedValue.isNil()) {
throw context.runtime.newTypeError("feed value already set");
}
feedValue = val;
nexter.setFeedValue(val);
return context.nil;
}

private void ensureNexter(ThreadContext context) {
if (nexter == null) {
if (Options.ENUMERATOR_LIGHTWEIGHT.load()) {
@@ -494,13 +544,23 @@ private static abstract class Nexter {
/** args to each method */
protected final IRubyObject[] methodArgs;

private IRubyObject feedValue;

public Nexter(Ruby runtime, IRubyObject object, String method, IRubyObject[] methodArgs) {
this.object = object;
this.method = method;
this.methodArgs = methodArgs;
this.runtime = runtime;
}

public void setFeedValue(IRubyObject feedValue) {
this.feedValue = feedValue;
}

public IRubyObject getFeedValue() {
return feedValue;
}

public abstract IRubyObject next();

public abstract void shutdown();
@@ -569,6 +629,7 @@ private static class ThreadedNexter extends Nexter implements Runnable {

public ThreadedNexter(Ruby runtime, IRubyObject object, String method, IRubyObject[] methodArgs) {
super(runtime, object, method, methodArgs);
setFeedValue(runtime.getNil());
}

public synchronized IRubyObject next() {
@@ -688,7 +749,9 @@ public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block)
throw new JumpException.BreakJump(-1, NEVER);
}

return context.nil;
IRubyObject feedValue = getFeedValue();
setFeedValue(context.nil);
return feedValue;
}
}, context));
} catch (JumpException.BreakJump bj) {

0 comments on commit c4198ee

Please sign in to comment.