Skip to content

Commit

Permalink
Showing 33 changed files with 457 additions and 204 deletions.
26 changes: 20 additions & 6 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -1614,7 +1614,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);
@@ -4075,7 +4077,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);
}
}

@@ -4090,12 +4092,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
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyArray.java
Original file line number Diff line number Diff line change
@@ -2321,7 +2321,7 @@ public RubyArray collectBang(ThreadContext context, Block block) {
*/
@JRubyMethod(name = "collect!")
public IRubyObject collect_bang(ThreadContext context, Block block) {
return block.isGiven() ? collectBang(context, block) : enumeratorize(context.runtime, this, "collect!");
return block.isGiven() ? collectBang(context, block) : enumeratorizeWithSize(context, this, "collect!", enumLengthFn());
}

/** rb_ary_collect_bang
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/RubyClass.java
Original file line number Diff line number Diff line change
@@ -1138,6 +1138,9 @@ public static void checkInheritable(IRubyObject superClass) {
if (((RubyClass)superClass).isSingleton()) {
throw superClass.getRuntime().newTypeError("can't make subclass of virtual class");
}
if (superClass == superClass.getRuntime().getClassClass()) {
throw superClass.getRuntime().newTypeError("can't make subclass of Class");
}
}

public final ObjectMarshal getMarshal() {
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/RubyEnumerable.java
Original file line number Diff line number Diff line change
@@ -1201,6 +1201,7 @@ public static IRubyObject max_by(ThreadContext context, IRubyObject self, final

@JRubyMethod
public static IRubyObject max_by(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
if (arg == context.nil) return singleExtentBy(context, self, "max", SORT_MAX, block);
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort_by(context, self, block);
return ((RubyArray) sorted.last(arg)).reverse();
@@ -1213,6 +1214,7 @@ public static IRubyObject min_by(ThreadContext context, IRubyObject self, final

@JRubyMethod
public static IRubyObject min_by(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
if (arg == context.nil) return singleExtentBy(context, self, "min", SORT_MIN, block);
// TODO: Replace with an implementation (quickselect, etc) which requires O(k) memory rather than O(n) memory
RubyArray sorted = (RubyArray)sort_by(context, self, block);
return sorted.first(arg);
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;
}

}
9 changes: 9 additions & 0 deletions core/src/main/java/org/jruby/ast/StrNode.java
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@
public class StrNode extends Node implements ILiteralNode, SideEffectFree {
private final ByteList value;
private final int codeRange;
private boolean frozen;

public StrNode(ISourcePosition position, ByteList value) {
this(position, value, StringSupport.codeRangeScan(value.getEncoding(), value));
@@ -103,4 +104,12 @@ public int getCodeRange() {
public List<Node> childNodes() {
return EMPTY_LIST;
}

public boolean isFrozen() {
return frozen;
}

public void setFrozen(boolean frozen) {
this.frozen = frozen;
}
}
21 changes: 9 additions & 12 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -2087,6 +2087,7 @@ private void handleBreakAndReturnsInLambdas() {
// --> IRRuntimeHelpers.handleBreakAndReturnsInLambdas(context, scope, bj, blockType)
Variable ret = createTemporaryVariable();
addInstr(new RuntimeHelperCall(ret, RuntimeHelperCall.Methods.HANDLE_BREAK_AND_RETURNS_IN_LAMBDA, new Operand[]{exc} ));
addInstr(new RethrowSavedExcInLambdaInstr());
addInstr(new ReturnInstr(ret));

// End
@@ -2531,14 +2532,7 @@ public Operand buildHash(HashNode hashNode) {
addInstr(new RuntimeHelperCall(hash, MERGE_KWARGS, new Operand[] { hash, splat}));
continue;
} else {
// TODO: This isn't super pretty. If AST were aware of literal hash string keys being "special"
// it could have an appropriate AST node for frozen string and this code would just go away.
if (key instanceof StrNode) {
StrNode strKey = (StrNode)key;
keyOperand = new FrozenString(strKey.getValue(), strKey.getCodeRange());
} else {
keyOperand = buildWithOrder(key, hasAssignments);
}
keyOperand = buildWithOrder(key, hasAssignments);
}

args.add(new KeyValuePair<>(keyOperand, buildWithOrder(pair.getValue(), hasAssignments)));
@@ -3314,10 +3308,13 @@ public Operand buildSplat(SplatNode splatNode) {
}

public Operand buildStr(StrNode strNode) {
if (strNode instanceof FileNode) {
return new Filename();
}
return copyAndReturnValue(new StringLiteral(strNode.getValue(), strNode.getCodeRange()));
if (strNode instanceof FileNode) return new Filename();

Operand literal = strNode.isFrozen() ?
new FrozenString(strNode.getValue(), strNode.getCodeRange()) :
new StringLiteral(strNode.getValue(), strNode.getCodeRange());

return copyAndReturnValue(literal);
}

private Operand buildSuperInstr(Operand block, Operand[] args) {
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/IRVisitor.java
Original file line number Diff line number Diff line change
@@ -117,6 +117,7 @@ private void error(Object object) {
public void RestArgMultipleAsgnInstr(RestArgMultipleAsgnInstr restargmultipleasgninstr) { error(restargmultipleasgninstr); }
public void RestoreBindingVisibilityInstr(RestoreBindingVisibilityInstr instr) { error(instr); }
public void ReturnInstr(ReturnInstr returninstr) { error(returninstr); }
public void RethrowSavedExcInLambdaInstr(RethrowSavedExcInLambdaInstr instr) { error(instr); }
public void RuntimeHelperCall(RuntimeHelperCall runtimehelpercall) { error(runtimehelpercall); }
public void SaveBindingVisibilityInstr(SaveBindingVisibilityInstr instr) { error(instr); }
public void SearchConstInstr(SearchConstInstr searchconstinstr) { error(searchconstinstr); }
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/Operation.java
Original file line number Diff line number Diff line change
@@ -213,6 +213,7 @@ public enum Operation {
POP_BINDING(OpFlags.f_is_book_keeping_op | OpFlags.f_has_side_effect),
SAVE_BINDING_VIZ(OpFlags.f_is_book_keeping_op | OpFlags.f_has_side_effect),
RESTORE_BINDING_VIZ(OpFlags.f_is_book_keeping_op | OpFlags.f_has_side_effect),
RETHROW_SAVED_EXC_IN_LAMBDA(OpFlags.f_is_book_keeping_op | OpFlags.f_has_side_effect),
TOGGLE_BACKTRACE(OpFlags.f_is_book_keeping_op | OpFlags.f_has_side_effect),
UPDATE_BLOCK_STATE(OpFlags.f_is_book_keeping_op | OpFlags.f_has_side_effect),

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.jruby.ir.instructions;

import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.transformations.inlining.CloneInfo;

public class RethrowSavedExcInLambdaInstr extends NoOperandInstr implements FixedArityInstr {
public RethrowSavedExcInLambdaInstr() {
super(Operation.RETHROW_SAVED_EXC_IN_LAMBDA);
}

@Override
public Instr clone(CloneInfo ii) {
return this; // FIXME: Needs update
}

public static RethrowSavedExcInLambdaInstr decode(IRReaderDecoder d) {
return new RethrowSavedExcInLambdaInstr();
}

@Override
public void visit(IRVisitor visitor) {
visitor.RethrowSavedExcInLambdaInstr(this);
}
}
Original file line number Diff line number Diff line change
@@ -114,12 +114,15 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
int n = instrs.length;
int ipc = 0;
Object exception = null;
boolean acceptsKeywordArgument = interpreterContext.receivesKeywordArguments();

if (interpreterContext.receivesKeywordArguments()) IRRuntimeHelpers.frobnicateKwargsArgument(context, interpreterContext.getRequiredArgsCount(), args);
// Blocks with explicit call protocol shouldn't do this before args are prepared
if (acceptsKeywordArgument && (block == null || !interpreterContext.hasExplicitCallProtocol())) {
IRRuntimeHelpers.frobnicateKwargsArgument(context, interpreterContext.getRequiredArgsCount(), args);
}

StaticScope currScope = interpreterContext.getStaticScope();
DynamicScope currDynScope = context.getCurrentScope();
boolean acceptsKeywordArgument = interpreterContext.receivesKeywordArguments();

// Init profiling this scope
boolean debug = IRRuntimeHelpers.isDebug();
@@ -185,7 +188,7 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
args = IRRuntimeHelpers.prepareFixedBlockArgs(context, block, args);
break;
case PREPARE_BLOCK_ARGS:
args = IRRuntimeHelpers.prepareBlockArgs(context, block, args);
args = IRRuntimeHelpers.prepareBlockArgs(context, block, args, acceptsKeywordArgument);
break;
default:
processBookKeepingOp(context, block, instr, operation, name, args, self, blockArg, implClass, currDynScope, temp, currScope);
@@ -375,6 +378,9 @@ protected static void processBookKeepingOp(ThreadContext context, Block block, I
case POP_BINDING:
context.popScope();
break;
case RETHROW_SAVED_EXC_IN_LAMBDA:
IRRuntimeHelpers.rethrowSavedExcInLambda(context);
break; // may not be reachable
case THREAD_POLL:
if (IRRuntimeHelpers.inProfileMode()) Profiler.clockTick();
context.callThreadPoll();
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@ public String getLabel() {

private boolean explicitCallProtocolSupported(IRScope scope) {
return scope instanceof IRMethod
// SSS: Turning this off till this is fully debugged
|| (scope instanceof IRClosure && !(scope instanceof IREvalScript))
|| (scope instanceof IRModuleBody && !(scope instanceof IRMetaClassBody));
}
@@ -46,7 +45,11 @@ private void fixReturn(IRScope scope, ReturnBase i, ListIterator<Instr> instrs)
}
}

private void popSavedState(IRScope scope, boolean requireBinding, boolean requireFrame, Variable savedViz, Variable savedFrame, ListIterator<Instr> instrs) {
private void popSavedState(IRScope scope, boolean isGEB, boolean requireBinding, boolean requireFrame, Variable savedViz, Variable savedFrame, ListIterator<Instr> instrs) {
if (scope instanceof IRClosure && isGEB) {
// Add before RethrowSavedExcInLambdaInstr
instrs.previous();
}
if (requireBinding) instrs.add(new PopBindingInstr());
if (scope instanceof IRClosure) {
instrs.add(new PopBlockFrameInstr(savedFrame));
@@ -137,47 +140,44 @@ public Object execute(IRScope scope, Object... data) {
ListIterator<Instr> instrs = bb.getInstrs().listIterator();
while (instrs.hasNext()) {
i = instrs.next();
// Right now, we only support explicit call protocol on methods.
// So, non-local returns and breaks don't get here.
// Non-local-returns and breaks are tricky since they almost always
// throw an exception and we don't multiple pops (once before the
// return/break, and once when the exception is caught).
if (!bb.isExitBB() && i instanceof ReturnBase) {
if (requireBinding || requireFrame) {
fixReturn(scope, (ReturnBase)i, instrs);
}
// Breaks & non-local returns in blocks will throw exceptions
// and pops for them will be handled in the GEB
if (!bb.isExitBB() && i instanceof ReturnInstr) {
if (requireBinding) fixReturn(scope, (ReturnInstr)i, instrs);
// Add before the break/return
instrs.previous();
popSavedState(scope, requireBinding, requireFrame, savedViz, savedFrame, instrs);
popSavedState(scope, bb == geb, requireBinding, requireFrame, savedViz, savedFrame, instrs);
if (bb == geb) gebProcessed = true;
break;
}
}

if (bb.isExitBB() && !bb.isEmpty()) {
// Last instr could be a return -- so, move iterator one position back
if (i != null && i instanceof ReturnBase) {
if (requireBinding || requireFrame) {
fixReturn(scope, (ReturnBase)i, instrs);
}
if (i != null && i instanceof ReturnInstr) {
if (requireBinding) fixReturn(scope, (ReturnInstr)i, instrs);
instrs.previous();
}
popSavedState(scope, requireBinding, requireFrame, savedViz, savedFrame, instrs);
popSavedState(scope, bb == geb, requireBinding, requireFrame, savedViz, savedFrame, instrs);
if (bb == geb) gebProcessed = true;
}

if (!gebProcessed && bb == geb) {
} else if (!gebProcessed && bb == geb) {
// Add before throw-exception-instr which would be the last instr
if (i != null) {
// Assumption: Last instr should always be a control-transfer instruction
assert i.getOperation().transfersControl(): "Last instruction of GEB in scope: " + scope + " is " + i + ", not a control-xfer instruction";
instrs.previous();
}
popSavedState(scope, requireBinding, requireFrame, savedViz, savedFrame, instrs);
popSavedState(scope, true, requireBinding, requireFrame, savedViz, savedFrame, instrs);
}
}
}

/*
if (scope instanceof IRClosure) {
System.out.println(scope + " after acp: " + cfg.toStringInstrs());
}
*/

// This scope has an explicit call protocol flag now
scope.setExplicitCallProtocolFlag();

47 changes: 36 additions & 11 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -151,7 +151,7 @@ public static IRubyObject initiateNonLocalReturn(ThreadContext context, DynamicS
public static IRubyObject handleNonlocalReturn(StaticScope scope, DynamicScope dynScope, Object rjExc, Block.Type blockType) throws RuntimeException {
if (!(rjExc instanceof IRReturnJump)) {
Helpers.throwException((Throwable)rjExc);
return null;
return null; // Unreachable
} else {
IRReturnJump rj = (IRReturnJump)rjExc;

@@ -195,24 +195,41 @@ public static IRubyObject initiateBreak(ThreadContext context, DynamicScope dynS
public static IRubyObject handleBreakAndReturnsInLambdas(ThreadContext context, StaticScope scope, DynamicScope dynScope, Object exc, Block.Type blockType) throws RuntimeException {
if ((exc instanceof IRBreakJump) && inNonMethodBodyLambda(scope, blockType)) {
// We just unwound all the way up because of a non-local break
throw IRException.BREAK_LocalJumpError.getException(context.getRuntime());
context.setSavedExceptionInLambda(IRException.BREAK_LocalJumpError.getException(context.getRuntime()));
return null;
} else if (exc instanceof IRReturnJump && (blockType == null || inLambda(blockType))) {
// Ignore non-local return processing in non-lambda blocks.
// Methods have a null blocktype
return handleNonlocalReturn(scope, dynScope, exc, blockType);
try {
// Ignore non-local return processing in non-lambda blocks.
// Methods have a null blocktype
return handleNonlocalReturn(scope, dynScope, exc, blockType);
} catch (Throwable e) {
context.setSavedExceptionInLambda(e);
return null;
}
} else {
// Propagate
Helpers.throwException((Throwable)exc);
// should not get here
// Propagate the exception
context.setSavedExceptionInLambda((Throwable)exc);
return null;
}
}

@JIT
public static void rethrowSavedExcInLambda(ThreadContext context) {
// This rethrows the exception saved in handleBreakAndReturnsInLambda
// after additional code to pop frames, bindings, etc. are done.
Throwable exc = context.getSavedExceptionInLambda();
if (exc != null) {
// IMPORTANT: always clear!
context.setSavedExceptionInLambda(null);
Helpers.throwException(exc);
}
}

@JIT
public static IRubyObject handlePropagatedBreak(ThreadContext context, DynamicScope dynScope, Object bjExc, Block.Type blockType) {
if (!(bjExc instanceof IRBreakJump)) {
Helpers.throwException((Throwable)bjExc);
return null;
return null; // Unreachable
}

IRBreakJump bj = (IRBreakJump)bjExc;
@@ -1491,8 +1508,7 @@ public static IRubyObject[] prepareProcArgs(ThreadContext context, Block b, IRub
}
}

@JIT
public static IRubyObject[] prepareBlockArgs(ThreadContext context, Block block, IRubyObject[] args) {
public static IRubyObject[] prepareBlockArgsInternal(ThreadContext context, Block block, IRubyObject[] args) {
// This is the placeholder for scenarios
// not handled by specialized instructions.
if (args == null) {
@@ -1557,6 +1573,15 @@ public static IRubyObject[] prepareBlockArgs(ThreadContext context, Block block,
return args;
}

@JIT
public static IRubyObject[] prepareBlockArgs(ThreadContext context, Block block, IRubyObject[] args, boolean usesKwArgs) {
args = prepareBlockArgsInternal(context, block, args);
if (usesKwArgs) {
frobnicateKwargsArgument(context, block.getBody().getSignature().required(), args);
}
return args;
}

public static IRubyObject[] prepareFixedBlockArgs(ThreadContext context, Block block, IRubyObject[] args) {
if (args == null) {
return IRubyObject.NULL_ARRAY;
9 changes: 8 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -1415,7 +1415,8 @@ public void PrepareBlockArgsInstr(PrepareBlockArgsInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmMethod().loadArgs();
jvmMethod().invokeIRHelper("prepareBlockArgs", sig(IRubyObject[].class, ThreadContext.class, Block.class, IRubyObject[].class));
jvmAdapter().ldc(((IRClosure)jvm.methodData().scope).receivesKeywordArgs());
jvmMethod().invokeIRHelper("prepareBlockArgs", sig(IRubyObject[].class, ThreadContext.class, Block.class, IRubyObject[].class, boolean.class));
jvmMethod().storeArgs();
}

@@ -1696,6 +1697,12 @@ public void RestoreBindingVisibilityInstr(RestoreBindingVisibilityInstr instr) {
jvmAdapter().invokevirtual(p(Binding.class), "setVisibility", sig(void.class, Visibility.class));
}

@Override
public void RethrowSavedExcInLambdaInstr(RethrowSavedExcInLambdaInstr instr) {
jvmMethod().loadContext();
jvmMethod().invokeIRHelper("rethrowSavedExcInLambda", sig(void.class, ThreadContext.class));
}

@Override
public void RuntimeHelperCall(RuntimeHelperCall runtimehelpercall) {
switch (runtimehelpercall.getHelperMethod()) {
7 changes: 7 additions & 0 deletions core/src/main/java/org/jruby/parser/ParserSupport.java
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Signature;
import org.jruby.util.ByteList;
import org.jruby.util.KeyValuePair;
import org.jruby.util.RegexpOptions;
import org.jruby.util.StringSupport;
import org.jruby.util.cli.Options;
@@ -881,6 +882,12 @@ public DStrNode createDStrNode(ISourcePosition position) {
return new DStrNode(position, lexer.getEncoding());
}

public KeyValuePair<Node, Node> createKeyValue(Node key, Node value) {
if (key != null && key instanceof StrNode) ((StrNode) key).setFrozen(true);

return new KeyValuePair<>(key, value);
}

public Node asSymbol(ISourcePosition position, String value) {
return new SymbolNode(position, value, lexer.getEncoding(), lexer.getTokenCR());
}
12 changes: 6 additions & 6 deletions core/src/main/java/org/jruby/parser/RubyParser.java
Original file line number Diff line number Diff line change
@@ -5236,14 +5236,14 @@ public Object yyparse (RubyLexer yyLex) throws java.io.IOException {
};
states[610] = new ParserState() {
@Override public Object execute(ParserSupport support, RubyLexer lexer, Object yyVal, Object[] yyVals, int yyTop) {
yyVal = new KeyValuePair<Node,Node>(((Node)yyVals[-2+yyTop]), ((Node)yyVals[0+yyTop]));
yyVal = support.createKeyValue(((Node)yyVals[-2+yyTop]), ((Node)yyVals[0+yyTop]));
return yyVal;
}
};
states[611] = new ParserState() {
@Override public Object execute(ParserSupport support, RubyLexer lexer, Object yyVal, Object[] yyVals, int yyTop) {
Node label = support.asSymbol(support.getPosition(((Node)yyVals[0+yyTop])), ((String)yyVals[-1+yyTop]));
yyVal = new KeyValuePair<Node,Node>(label, ((Node)yyVals[0+yyTop]));
yyVal = support.createKeyValue(label, ((Node)yyVals[0+yyTop]));
return yyVal;
}
};
@@ -5252,9 +5252,9 @@ public Object yyparse (RubyLexer yyLex) throws java.io.IOException {
if (((Node)yyVals[-2+yyTop]) instanceof StrNode) {
DStrNode dnode = new DStrNode(support.getPosition(((Node)yyVals[-2+yyTop])), lexer.getEncoding());
dnode.add(((Node)yyVals[-2+yyTop]));
yyVal = new KeyValuePair<Node,Node>(new DSymbolNode(support.getPosition(((Node)yyVals[-2+yyTop])), dnode), ((Node)yyVals[0+yyTop]));
yyVal = support.createKeyValue(new DSymbolNode(support.getPosition(((Node)yyVals[-2+yyTop])), dnode), ((Node)yyVals[0+yyTop]));
} else if (((Node)yyVals[-2+yyTop]) instanceof DStrNode) {
yyVal = new KeyValuePair<Node,Node>(new DSymbolNode(support.getPosition(((Node)yyVals[-2+yyTop])), ((DStrNode)yyVals[-2+yyTop])), ((Node)yyVals[0+yyTop]));
yyVal = support.createKeyValue(new DSymbolNode(support.getPosition(((Node)yyVals[-2+yyTop])), ((DStrNode)yyVals[-2+yyTop])), ((Node)yyVals[0+yyTop]));
} else {
support.compile_error("Uknown type for assoc in strings: " + ((Node)yyVals[-2+yyTop]));
}
@@ -5264,7 +5264,7 @@ public Object yyparse (RubyLexer yyLex) throws java.io.IOException {
};
states[613] = new ParserState() {
@Override public Object execute(ParserSupport support, RubyLexer lexer, Object yyVal, Object[] yyVals, int yyTop) {
yyVal = new KeyValuePair<Node,Node>(null, ((Node)yyVals[0+yyTop]));
yyVal = support.createKeyValue(null, ((Node)yyVals[0+yyTop]));
return yyVal;
}
};
@@ -5293,7 +5293,7 @@ public Object yyparse (RubyLexer yyLex) throws java.io.IOException {
}
};
}
// line 2525 "RubyParser.y"
// line 2524 "RubyParser.y"

/** The parse method use an lexer stream and parse it to an AST node
* structure
11 changes: 5 additions & 6 deletions core/src/main/java/org/jruby/parser/RubyParser.y
Original file line number Diff line number Diff line change
@@ -2470,27 +2470,26 @@ assocs : assoc {

// Cons: [!null]
assoc : arg_value tASSOC arg_value {
$$ = new KeyValuePair<Node,Node>($1, $3);
$$ = support.createKeyValue($1, $3);
}
| tLABEL arg_value {
Node label = support.asSymbol(support.getPosition($2), $1);
$$ = new KeyValuePair<Node,Node>(label, $2);
$$ = support.createKeyValue(label, $2);
}
| tSTRING_BEG string_contents tLABEL_END arg_value {
if ($2 instanceof StrNode) {
DStrNode dnode = new DStrNode(support.getPosition($2), lexer.getEncoding());
dnode.add($2);
$$ = new KeyValuePair<Node,Node>(new DSymbolNode(support.getPosition($2), dnode), $4);
$$ = support.createKeyValue(new DSymbolNode(support.getPosition($2), dnode), $4);
} else if ($2 instanceof DStrNode) {
$$ = new KeyValuePair<Node,Node>(new DSymbolNode(support.getPosition($2), $<DStrNode>2), $4);
$$ = support.createKeyValue(new DSymbolNode(support.getPosition($2), $<DStrNode>2), $4);
} else {
support.compile_error("Uknown type for assoc in strings: " + $2);
}

}

| tDSTAR arg_value {
$$ = new KeyValuePair<Node,Node>(null, $2);
$$ = support.createKeyValue(null, $2);
}

operation : tIDENTIFIER | tCONSTANT | tFID
14 changes: 13 additions & 1 deletion core/src/main/java/org/jruby/runtime/ThreadContext.java
Original file line number Diff line number Diff line change
@@ -128,7 +128,10 @@ public static ThreadContext newContext(Ruby runtime) {

IRubyObject lastExitStatus;

private Block.Type currentBlockType;
// These two fields are required to support explicit call protocol
// (via IR instructions) for blocks.
private Block.Type currentBlockType; // See prepareBlockArgs code in IRRuntimeHelpers
private Throwable savedExcInLambda; // See handleBreakAndReturnsInLambda in IRRuntimeHelpers

public final SecureRandom secureRandom = getSecureRandom();

@@ -154,6 +157,7 @@ private ThreadContext(Ruby runtime) {
this.runtime = runtime;
this.nil = runtime.getNil();
this.currentBlockType = Block.Type.NORMAL;
this.savedExcInLambda = null;

if (runtime.getInstanceConfig().isProfilingEntireRun()) {
startProfiling();
@@ -218,6 +222,14 @@ public void setCurrentBlockType(Block.Type type) {
currentBlockType = type;
}

public Throwable getSavedExceptionInLambda() {
return savedExcInLambda;
}

public void setSavedExceptionInLambda(Throwable e) {
savedExcInLambda = e;
}

public CallType getLastCallType() {
return lastCallType;
}
10 changes: 10 additions & 0 deletions spec/ruby/language/lambda_spec.rb
Original file line number Diff line number Diff line change
@@ -22,6 +22,16 @@ def create_lambda
-> () { }.lambda?.should be_true
end

it "has its own scope for local variables" do
l = -> arg {
var = arg
# this would override var if it was declared outside the lambda
l.call(arg-1) if arg > 0
var
}
l.call(1).should == 1
end

context "assigns no local variables" do
evaluate <<-ruby do
@a = -> { }
1 change: 0 additions & 1 deletion spec/tags/ruby/core/array/collect_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/tags/ruby/core/class/initialize_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/tags/ruby/core/enumerable/max_by_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Enumerable#max_by when called with an argument n without a block returns an enumerator
fails:Enumerable#max_by when called with an argument n when n is nil returns the maximum element
1 change: 0 additions & 1 deletion spec/tags/ruby/core/enumerable/min_by_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Enumerable#min_by when called with an argument n without a block returns an enumerator
fails:Enumerable#min_by when called with an argument n when n is nil returns the minimum element
1 change: 0 additions & 1 deletion spec/truffle/tags/language/return_tags.txt

This file was deleted.

77 changes: 77 additions & 0 deletions test/jruby/test_enumerator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'test/unit'

class TestEnumerator < Test::Unit::TestCase

def test_stop_result_array
a = [1, 2]
enum = a.each
assert_equal(1, enum.next)
assert_equal(2, enum.next)
exc = assert_raise(StopIteration) { enum.next }
assert_equal(a, exc.result)

exc = assert_raise(StopIteration) { enum.next }
assert_equal(a, exc.result)
#assert_equal('iteration reached an end', exc.message)

#enum = a.map
#enum.next; enum.next
#exc = assert_raise(StopIteration) { enum.next }
#assert_equal([nil, nil], exc.result)
#assert_equal('iteration reached an end', exc.message)
end

def test_stop_result_obj
o = Object.new
def o.each
yield
yield 1
yield 1, 2
end
enum = o.to_enum
assert_equal(nil, enum.next)
assert_equal(1, enum.next)
assert_equal([1,2], enum.next)
exc = assert_raise(StopIteration) { enum.next }
assert_equal(nil, exc.result)
assert_equal('iteration reached an end', exc.message)

o = Object.new
def o.each
yield 1
yield 2
100
end
enum = o.to_enum
assert_equal(1, enum.next)
assert_equal(2, enum.next)
exc = assert_raise(StopIteration) { enum.next }
assert_equal(100, exc.result)
assert_equal('iteration reached an end', exc.message)
end

def test_stop_result_explicit
fib = Enumerator.new do |y|
a = b = 1
loop do
if a < 10 # [1, 1, 2, 3, 5, 8, 13]
y << a
a, b = b, a + b
else
raise StopIteration
end
end
:done
end
assert_equal [1, 1, 2, 3, 5, 8], fib.take(8)
fib.next
fib.next
fib.next
fib.next
fib.next
fib.next
exc = assert_raise(StopIteration) { fib.next }
assert_equal(:done, exc.result)
end

end
1 change: 0 additions & 1 deletion test/mri/excludes/TestEnumerator.rb
Original file line number Diff line number Diff line change
@@ -11,5 +11,4 @@
exclude :test_peek_modify, "needs investigation"
exclude :test_peek_values, "needs investigation"
exclude :test_rewind_clear_feed, "needs investigation"
exclude :test_stop_result, "needs investigation"
exclude :test_with_index_large_offset, "needs investigation"
14 changes: 10 additions & 4 deletions test/truffle/integration-tests.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#!/bin/bash

set -u # no undefined vars
set -e # abort on first error
set -x # show expanded commands

PORT=8080

function wait_until_port_open {
@@ -11,10 +15,12 @@ function wait_until_port_open {
}

function test_server {
serverpid=$!
wait_until_port_open
response=`curl http://localhost:$PORT/`
kill -9 $!
wait
response=`curl -s http://localhost:$PORT/`
jobs -l
kill -9 $serverpid
wait $serverpid || true
if [[ $response != *"Hello"* ]]
then
echo Response not expected
@@ -23,7 +29,7 @@ function test_server {
}

echo "Array#pack with real usage..."
bin/jruby -X+T test/truffle/pack-real-usage.rb || exit 1
bin/jruby -X+T test/truffle/pack-real-usage.rb

echo "Simple web server..."
bin/jruby -X+T test/truffle/simple-server.rb &
Original file line number Diff line number Diff line change
@@ -140,6 +140,7 @@ public GCCountNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization
public int gcCount() {
return RubyGC.getCollectionCount();
Original file line number Diff line number Diff line change
@@ -1744,41 +1744,45 @@ public RubyNode visitInstVarNode(org.jruby.ast.InstVarNode node) {

@Override
public RubyNode visitIterNode(org.jruby.ast.IterNode node) {
return translateBlockLikeNode(node, false);
}

@Override
public RubyNode visitLambdaNode(org.jruby.ast.LambdaNode node) {
return translateBlockLikeNode(node, true);
}

private RubyNode translateBlockLikeNode(org.jruby.ast.IterNode node, boolean isLambda) {
final SourceSection sourceSection = translate(node.getPosition());
final org.jruby.ast.ArgsNode argsNode = node.getArgsNode();

/*
* In a block we do NOT allocate a new return ID - returns will return from the method, not
* the block (in the general case, see Proc and the difference between Proc and Lambda for
* specifics).
*/
// Unset this flag for any for any blocks within the for statement's body
final boolean hasOwnScope = isLambda || !translatingForStatement;

final boolean hasOwnScope = !translatingForStatement;
final String name = isLambda ? "(lambda)" : currentCallMethodName;
final boolean isProc = !isLambda;

org.jruby.ast.ArgsNode argsNode;
final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), MethodTranslator.getArity(argsNode), name, true,
Helpers.argsNodeToArgumentDescriptors(argsNode), false, false, false);

if (node.getVarNode() instanceof org.jruby.ast.ArgsNode) {
argsNode = (org.jruby.ast.ArgsNode) node.getVarNode();
} else if (node.getVarNode() instanceof org.jruby.ast.DAsgnNode) {
final org.jruby.ast.ArgumentNode arg = new org.jruby.ast.ArgumentNode(node.getPosition(), ((org.jruby.ast.DAsgnNode) node.getVarNode()).getName());
final org.jruby.ast.ListNode preArgs = new org.jruby.ast.ArrayNode(node.getPosition(), arg);
argsNode = new org.jruby.ast.ArgsNode(node.getPosition(), preArgs, null, null, null, null, null, null);
} else if (node.getVarNode() == null) {
argsNode = null;
} else {
throw new UnsupportedOperationException();
}
final String namedMethodName = isLambda ? sharedMethodInfo.getName(): environment.getNamedMethodName();

// Unset this flag for any for any blocks within the for statement's body
final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), MethodTranslator.getArity(argsNode), currentCallMethodName, true, Helpers.argsNodeToArgumentDescriptors(node.findFirstChild(org.jruby.ast.ArgsNode.class)), false, false, false);
final ParseEnvironment parseEnvironment = environment.getParseEnvironment();
final ReturnID returnID = isLambda ? parseEnvironment.allocateReturnID() : environment.getReturnID();

final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(
context, environment, environment.getParseEnvironment(), environment.getReturnID(), hasOwnScope, false,
sharedMethodInfo, environment.getNamedMethodName(), true, environment.getParseEnvironment().allocateBreakID());
context, environment, parseEnvironment, returnID, hasOwnScope, false,
sharedMethodInfo, namedMethodName, true, parseEnvironment.allocateBreakID());
final MethodTranslator methodCompiler = new MethodTranslator(currentNode, context, this, newEnvironment, true, source, argsNode);
methodCompiler.translatingForStatement = translatingForStatement;

final RubyNode ret = methodCompiler.compileBlockNode(translate(node.getPosition()), sharedMethodInfo.getName(), node.getBodyNode(), sharedMethodInfo, Type.PROC);
return addNewlineIfNeeded(node, ret);
if (isProc) {
methodCompiler.translatingForStatement = translatingForStatement;
}

final Type type = isLambda ? Type.LAMBDA : Type.PROC;
final RubyNode definitionNode = methodCompiler.compileBlockNode(sourceSection, sharedMethodInfo.getName(), node.getBodyNode(), sharedMethodInfo, type);

return addNewlineIfNeeded(node, definitionNode);
}

@Override
@@ -2770,36 +2774,6 @@ public RubyNode visitBackRefNode(org.jruby.ast.BackRefNode node) {
return addNewlineIfNeeded(node, ret);
}

public RubyNode visitLambdaNode(org.jruby.ast.LambdaNode node) {
final SourceSection sourceSection = translate(node.getPosition());

org.jruby.ast.ArgsNode argsNode;

if (node.getVarNode() instanceof org.jruby.ast.ArgsNode) {
argsNode = (org.jruby.ast.ArgsNode) node.getVarNode();
} else if (node.getVarNode() instanceof org.jruby.ast.DAsgnNode) {
final org.jruby.ast.ArgumentNode arg = new org.jruby.ast.ArgumentNode(node.getPosition(), ((org.jruby.ast.DAsgnNode) node.getVarNode()).getName());
final org.jruby.ast.ListNode preArgs = new org.jruby.ast.ArrayNode(node.getPosition(), arg);
argsNode = new org.jruby.ast.ArgsNode(node.getPosition(), preArgs, null, null, null, null, null, null);
} else if (node.getVarNode() == null) {
argsNode = null;
} else {
throw new UnsupportedOperationException();
}

// TODO(cs): code copied and modified from visitIterNode - extract common
final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), MethodTranslator.getArity(argsNode), "(lambda)", true, Helpers.argsNodeToArgumentDescriptors(node.findFirstChild(org.jruby.ast.ArgsNode.class)), false, false, false);

final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(
context, environment, environment.getParseEnvironment(), environment.getReturnID(), false, false,
sharedMethodInfo, sharedMethodInfo.getName(), true, environment.getParseEnvironment().allocateBreakID());
final MethodTranslator methodCompiler = new MethodTranslator(currentNode, context, this, newEnvironment, false, source, argsNode);

final RubyNode definitionNode = methodCompiler.compileBlockNode(translate(node.getPosition()), sharedMethodInfo.getName(), node.getBodyNode(), sharedMethodInfo, Type.LAMBDA);

return addNewlineIfNeeded(node, definitionNode);
}

protected RubyNode initFlipFlopStates(SourceSection sourceSection) {
final RubyNode[] initNodes = new RubyNode[environment.getFlipFlopStates().size()];

Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ public int getPreviousIndex() {
}
}

private final boolean isBlock;
private final boolean isProc;
private final BodyTranslator methodBodyTranslator;
private final Deque<ArraySlot> arraySlotStack = new ArrayDeque<>();

@@ -74,9 +74,9 @@ private enum State {

private org.jruby.ast.ArgsNode argsNode;

public LoadArgumentsTranslator(Node currentNode, RubyContext context, Source source, boolean isBlock, BodyTranslator methodBodyTranslator) {
public LoadArgumentsTranslator(Node currentNode, RubyContext context, Source source, boolean isProc, BodyTranslator methodBodyTranslator) {
super(currentNode, context, source);
this.isBlock = isBlock;
this.isProc = isProc;
this.methodBodyTranslator = methodBodyTranslator;
}

@@ -214,7 +214,7 @@ private RubyNode readArgument(SourceSection sourceSection) {
return PrimitiveArrayNodeFactory.read(context, sourceSection, loadArray(sourceSection), index);
} else {
if (state == State.PRE) {
return new ReadPreArgumentNode(context, sourceSection, index, isBlock ? MissingArgumentBehaviour.NIL : MissingArgumentBehaviour.RUNTIME_ERROR);
return new ReadPreArgumentNode(context, sourceSection, index, isProc ? MissingArgumentBehaviour.NIL : MissingArgumentBehaviour.RUNTIME_ERROR);
} else if (state == State.POST) {
return new ReadPostArgumentNode(context, sourceSection, index);
} else {
Original file line number Diff line number Diff line change
@@ -81,7 +81,8 @@ public BlockDefinitionNode compileBlockNode(SourceSection sourceSection, String
parentSourceSection.pop();
}

final LoadArgumentsTranslator loadArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isBlock, this);
final boolean isProc = type == Type.PROC;
final LoadArgumentsTranslator loadArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isProc, this);
final RubyNode loadArguments = argsNode.accept(loadArgumentsTranslator);

final RubyNode preludeProc;
@@ -91,7 +92,7 @@ public BlockDefinitionNode compileBlockNode(SourceSection sourceSection, String
final FrameSlot arraySlot = environment.declareVar(environment.allocateLocalTemp("destructure"));
final RubyNode writeArrayNode = new WriteLocalVariableNode(context, sourceSection, castArrayNode, arraySlot);

final LoadArgumentsTranslator destructureArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isBlock, this);
final LoadArgumentsTranslator destructureArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isProc, this);
destructureArgumentsTranslator.pushArraySlot(arraySlot);
final RubyNode newDestructureArguments = argsNode.accept(destructureArgumentsTranslator);

@@ -176,7 +177,7 @@ public RubyNode doCompileMethodBody(SourceSection sourceSection, String methodNa
parentSourceSection.pop();
}

final LoadArgumentsTranslator loadArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isBlock, this);
final LoadArgumentsTranslator loadArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, false, this);
final RubyNode loadArguments = argsNode.accept(loadArgumentsTranslator);

final RubyNode prelude;

0 comments on commit a6c404b

Please sign in to comment.