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: 775d7e5c7311
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 310e2a5966c6
Choose a head ref
  • 3 commits
  • 6 files changed
  • 1 contributor

Commits on Dec 14, 2015

  1. Copy the full SHA
    72a6195 View commit details

Commits on Dec 15, 2015

  1. Copy the full SHA
    ec623a1 View commit details
  2. support throwing StopIteration with a result from Enumerator (fixes #…

    …3542)
    
    also did some internal cleanup e.g. private initialize now receive the runtime
    and added more asserts to go along with those from MRI's test_enumerator.rb
    kares committed Dec 15, 2015
    Copy the full SHA
    310e2a5 View commit details
26 changes: 20 additions & 6 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -1612,7 +1612,9 @@ private void initExceptions() {
typeError = defineClassIfAllowed("TypeError", standardError);
argumentError = defineClassIfAllowed("ArgumentError", standardError);
indexError = defineClassIfAllowed("IndexError", standardError);
stopIteration = defineClassIfAllowed("StopIteration", indexError);
if (profile.allowClass("StopIteration")) {
stopIteration = RubyStopIteration.createStopIterationClass(this, indexError);
}
syntaxError = defineClassIfAllowed("SyntaxError", scriptError);
loadError = defineClassIfAllowed("LoadError", scriptError);
notImplementedError = defineClassIfAllowed("NotImplementedError", scriptError);
@@ -4073,7 +4075,7 @@ private RaiseException newLightweightErrnoException(RubyClass exceptionClass, St
if (RubyInstanceConfig.ERRNO_BACKTRACE) {
return new RaiseException(this, exceptionClass, message, true);
} else {
return new RaiseException(this, exceptionClass, ERRNO_BACKTRACE_MESSAGE, RubyArray.newEmptyArray(this), true);
return new RaiseException(this, exceptionClass, ERRNO_BACKTRACE_MESSAGE, disabledBacktrace(), true);
}
}

@@ -4088,12 +4090,24 @@ private RaiseException newLightweightErrnoException(RubyClass exceptionClass, St
*
* @param message the message for the exception
*/
public RaiseException newLightweightStopIterationError(String message) {
public RaiseException newStopIteration(IRubyObject result, String message) {
final ThreadContext context = getCurrentContext();
if (RubyInstanceConfig.STOPITERATION_BACKTRACE) {
return new RaiseException(this, stopIteration, message, true);
} else {
return new RaiseException(this, stopIteration, STOPIERATION_BACKTRACE_MESSAGE, RubyArray.newEmptyArray(this), true);
RubyException ex = RubyStopIteration.newInstance(context, result, message);
return new RaiseException(ex);
}
if ( message == null ) message = STOPIERATION_BACKTRACE_MESSAGE;
RubyException ex = RubyStopIteration.newInstance(context, result, message);
return new RaiseException(ex, disabledBacktrace());
}

@Deprecated
public RaiseException newLightweightStopIterationError(String message) {
return newStopIteration(null, message);
}

private IRubyObject disabledBacktrace() {
return RubyArray.newEmptyArray(this);
}

// Equivalent of Data_Wrap_Struct
112 changes: 57 additions & 55 deletions core/src/main/java/org/jruby/RubyEnumerator.java
Original file line number Diff line number Diff line change
@@ -95,22 +95,22 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
private RubyEnumerator(Ruby runtime, RubyClass type) {
super(runtime, type);
object = runtime.getNil();
initialize(runtime.getNil(), RubyString.newEmptyString(runtime), IRubyObject.NULL_ARRAY);
initialize(runtime, runtime.getNil(), RubyString.newEmptyString(runtime), IRubyObject.NULL_ARRAY);
}

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

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

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

/**
@@ -186,13 +186,13 @@ public IRubyObject initialize20(ThreadContext context, IRubyObject[] args, Block
size = args[0];
args = Arrays.copyOfRange(args, 1, args.length);

if (!(size.isNil() || size.respondsTo("call")) &&
!(runtime.getFloat().isInstance(size) && ((RubyFloat)size).getDoubleValue() == Float.POSITIVE_INFINITY) &&
!(size instanceof RubyInteger)) {
if ( ! (size.isNil() || size.respondsTo("call")) &&
! (runtime.getFloat().isInstance(size) && ((RubyFloat) size).getDoubleValue() == Float.POSITIVE_INFINITY) &&
! (size instanceof RubyInteger) ) {
throw runtime.newTypeError(size, runtime.getInteger());
}
}
object = context.runtime.getGenerator().newInstance(context, IRubyObject.NULL_ARRAY, block);
object = runtime.getGenerator().newInstance(context, IRubyObject.NULL_ARRAY, block);

} else {
Arity.checkArgumentCount(runtime, args, 1, -1);
@@ -205,7 +205,7 @@ public IRubyObject initialize20(ThreadContext context, IRubyObject[] args, Block
}
}

return initialize20(object, method, args, size, null);
return initialize20(runtime, object, method, args, size, null);
}

public IRubyObject initialize(ThreadContext context, IRubyObject object, IRubyObject method) {
@@ -220,9 +220,8 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject object, IRuby
public IRubyObject initialize20(ThreadContext context, IRubyObject object, IRubyObject method, Block block) {
if (block.isGiven()) {
throw context.runtime.newArgumentError(2, 1);
} else {
return initialize(object, method, NULL_ARRAY);
}
return initialize(context.runtime, object, method, NULL_ARRAY);
}

public IRubyObject initialize(ThreadContext context, IRubyObject object, IRubyObject method, IRubyObject methodArg) {
@@ -237,9 +236,8 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject object, IRuby
public IRubyObject initialize20(ThreadContext context, IRubyObject object, IRubyObject method, IRubyObject methodArg, Block block) {
if (block.isGiven()) {
throw context.runtime.newArgumentError(3, 1);
} else {
return initialize(object, method, new IRubyObject[] { methodArg });
}
return initialize(context.runtime, object, method, new IRubyObject[] { methodArg });
}

public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
@@ -250,12 +248,11 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject[] args, Block
return initialize20(context, args, block);
}

private IRubyObject initialize(IRubyObject object, IRubyObject method, IRubyObject[] methodArgs) {
return initialize20(object, method, methodArgs, null, null);
private IRubyObject initialize(Ruby runtime, IRubyObject object, IRubyObject method, IRubyObject[] methodArgs) {
return initialize20(runtime, object, method, methodArgs, null, null);
}

private IRubyObject initialize20(IRubyObject object, IRubyObject method, IRubyObject[] methodArgs, IRubyObject size, SizeFn sizeFn) {
final Ruby runtime = getRuntime();
private IRubyObject initialize20(Ruby runtime, IRubyObject object, IRubyObject method, IRubyObject[] methodArgs, IRubyObject size, SizeFn sizeFn) {
this.object = object;
this.method = method.asJavaString();
this.methodArgs = methodArgs;
@@ -438,7 +435,7 @@ private IRubyObject with_index_common(ThreadContext context, final Block block,

@JRubyMethod
public IRubyObject each_with_index(ThreadContext context, final Block block) {
return with_index_common(context, block, "each_with_index", context.runtime.getNil());
return with_index_common(context, block, "each_with_index", context.nil);
}

public IRubyObject with_index(ThreadContext context, final Block block) {
@@ -447,7 +444,7 @@ public IRubyObject with_index(ThreadContext context, final Block block) {

@JRubyMethod(name = "with_index")
public IRubyObject with_index19(ThreadContext context, final Block block) {
return with_index_common(context, block, "with_index", context.runtime.getNil());
return with_index_common(context, block, "with_index", context.nil);
}

@JRubyMethod(name = "with_index")
@@ -615,15 +612,15 @@ protected IRubyObject get() {
}

private void checkIndex() throws RaiseException {
if (index >= array.size()) throw runtime.newLightweightStopIterationError("stop iteration");
if (index >= array.size()) throw runtime.newStopIteration(array, null);
}
}

private static class ThreadedNexter extends Nexter implements Runnable {
private static final boolean DEBUG = false;

/** sync queue to wait for values */
private SynchronousQueue<IRubyObject> out = new SynchronousQueue<IRubyObject>();
final SynchronousQueue<IRubyObject> out = new SynchronousQueue<IRubyObject>();

/** thread that's executing this Nexter */
private volatile Thread thread;
@@ -640,6 +637,9 @@ private static class ThreadedNexter extends Nexter implements Runnable {
/** the last value we got, used for peek */
private IRubyObject lastValue;

/** the block return value, to be fed as StopIteration#result */
private volatile IRubyObject stopValue;

/** Exception used for unrolling the iteration on terminate */
private static class TerminateEnumeration extends RuntimeException implements Unrescuable {}

@@ -729,7 +729,7 @@ private IRubyObject returnValue(IRubyObject value) {
// if it's the NEVER object, raise StopIteration
if (value == NEVER) {
doneObject = value;
throw runtime.newLightweightStopIterationError("stop iteration");
throw runtime.newStopIteration(stopValue, "iteration reached an end");
}

// if it's an exception, raise it
@@ -754,38 +754,41 @@ public void run() {
IRubyObject finalObject = NEVER;

try {
IRubyObject oldExc = runtime.getGlobalVariables().get("$!"); // Save $!
final IRubyObject oldExc = runtime.getGlobalVariables().get("$!"); // Save $!
final TerminateEnumeration terminateEnumeration = new TerminateEnumeration();
try {
object.callMethod(context, method, methodArgs, CallBlock.newCallClosure(object, object.getMetaClass(), Signature.OPTIONAL, new BlockCallback() {
@Override
public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
try {
if (DEBUG) System.out.println(Thread.currentThread().getName() + ": exchanging: " + Arrays.toString(args));
if (die) throw terminateEnumeration;
out.put(RubyEnumerable.packEnumValues(context, args));
if (die) throw terminateEnumeration;
} catch (InterruptedException ie) {
if (DEBUG) System.out.println(Thread.currentThread().getName() + ": interrupted");

throw terminateEnumeration;
}

IRubyObject feedValue = getFeedValue();
setFeedValue(context.nil);
return feedValue;
Block generatorClosure = CallBlock.newCallClosure(object, object.getMetaClass(), Signature.OPTIONAL, new BlockCallback() {

public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
try {
if (DEBUG) System.out.println(Thread.currentThread().getName() + ": exchanging: " + Arrays.toString(args));
if (die) throw terminateEnumeration;
out.put( RubyEnumerable.packEnumValues(context, args) );
if (die) throw terminateEnumeration;
}
}, context));
} catch (TerminateEnumeration te) {
if (te != terminateEnumeration) {
throw te;
catch (InterruptedException ie) {
if (DEBUG) System.out.println(Thread.currentThread().getName() + ": interrupted");

throw terminateEnumeration;
}

IRubyObject feedValue = getFeedValue();
setFeedValue(context.nil);
return feedValue;
}
}, context);
try {
this.stopValue = object.callMethod(context, method, methodArgs, generatorClosure);
}
catch (TerminateEnumeration te) {
if (te != terminateEnumeration) throw te;
// ignore, we're shutting down
} catch (RaiseException re) {
}
catch (RaiseException re) {
if (DEBUG) System.out.println(Thread.currentThread().getName() + ": exception at toplevel: " + re.getException());
finalObject = re.getException();
runtime.getGlobalVariables().set("$!", oldExc); // Restore $!
} catch (Throwable t) {
}
catch (Throwable t) {
if (DEBUG) {
System.out.println(Thread.currentThread().getName() + ": exception at toplevel: " + t);
t.printStackTrace();
@@ -795,12 +798,11 @@ public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block)

try {
if (!die) out.put(finalObject);
} catch (InterruptedException ie) {
// ignore
}
} finally {
// disassociate this Nexter with the thread running it
thread = null;
catch (InterruptedException ie) { /* ignore */ }
}
finally {
thread = null; // disassociate this Nexter with the thread running it
}
}
}
19 changes: 10 additions & 9 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -1279,27 +1279,28 @@ public static RubyProc proc_1_9(ThreadContext context, IRubyObject recv, Block b

@JRubyMethod(name = "loop", module = true, visibility = PRIVATE)
public static IRubyObject loop(ThreadContext context, IRubyObject recv, Block block) {
Ruby runtime = context.runtime;
if (!block.isGiven()) {
if ( ! block.isGiven() ) {
return RubyEnumerator.enumeratorizeWithSize(context, recv, "loop", loopSizeFn(context));
}
IRubyObject nil = runtime.getNil();
RubyClass stopIteration = runtime.getStopIteration();
final Ruby runtime = context.runtime;
IRubyObject oldExc = runtime.getGlobalVariables().get("$!"); // Save $!
try {
while (true) {
block.yieldSpecific(context);

context.pollThreadEvents();
}
} catch (RaiseException ex) {
if (!stopIteration.op_eqq(context, ex.getException()).isTrue()) {
throw ex;
} else {
}
catch (RaiseException ex) {
final RubyClass StopIteration = runtime.getStopIteration();
if ( StopIteration.isInstance(ex.getException()) ) {
runtime.getGlobalVariables().set("$!", oldExc); // Restore $!
return context.nil;
}
else {
throw ex;
}
}
return nil;
}

private static SizeFn loopSizeFn(final ThreadContext context) {
78 changes: 78 additions & 0 deletions core/src/main/java/org/jruby/RubyStopIteration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

package org.jruby;

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

/**
* Ruby's StopIteration exception.
* @see RubyEnumerator
* @author kares
*/
@JRubyClass(name="StopIteration", parent="IndexError")
public class RubyStopIteration extends RubyException {

private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyStopIteration(runtime, klass);
}
};

static RubyClass createStopIterationClass(Ruby runtime, RubyClass superClass) {
RubyClass StopIteration = runtime.defineClass("StopIteration", superClass, ALLOCATOR);
StopIteration.defineAnnotatedMethods(RubyStopIteration.class);
return StopIteration;
}

public static RubyStopIteration newInstance(ThreadContext context, IRubyObject result, String message) {
final Ruby runtime = context.runtime;
RubyClass StopIteration = runtime.getStopIteration();
final IRubyObject msg = message == null ? context.nil : runtime.newString(message);
RubyStopIteration instance = (RubyStopIteration)
StopIteration.newInstance(context, msg, Block.NULL_BLOCK);
instance.result = result;
return instance;
}

private IRubyObject result;

protected RubyStopIteration(Ruby runtime, RubyClass exceptionClass) {
super(runtime, exceptionClass);
}

@JRubyMethod
public IRubyObject result() {
return result == null ? getRuntime().getNil() : result;
}

}
Loading