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: b82e3c483dec
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 77bb1ca55b42
Choose a head ref
  • 5 commits
  • 9 files changed
  • 1 contributor

Commits on Jul 31, 2015

  1. [Truffle] Handle argumentsAsArray as a rest argument in @coremethod.

    * Much easier to express most efficient specializations.
    * A bit cumbersome for specializations using all arguments when some are optional.
    eregon committed Jul 31, 2015
    Copy the full SHA
    5f14842 View commit details
  2. Copy the full SHA
    b170b9e View commit details
  3. Copy the full SHA
    80a445d View commit details
  4. Copy the full SHA
    0ee3251 View commit details
  5. Copy the full SHA
    77bb1ca View commit details
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.arguments;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;

public class ReadRemainingArgumentsNode extends RubyNode {

private final int start;
private final ConditionProfile remainingArguments = ConditionProfile.createBinaryProfile();

public ReadRemainingArgumentsNode(RubyContext context, SourceSection sourceSection, int start) {
super(context, sourceSection);
this.start = start;
}

@Override
public Object[] executeObjectArray(VirtualFrame frame) {
final int count = RubyArguments.getUserArgumentsCount(frame.getArguments());

if (remainingArguments.profile(start < count)) {
return RubyArguments.extractUserArgumentsFrom(frame.getArguments(), start);
} else {
return new Object[] {};
}
}

@Override
public Object execute(VirtualFrame frame) {
return executeObjectArray(frame);
}

}
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
@@ -28,6 +29,7 @@
import org.jruby.truffle.runtime.array.ArrayUtils;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyException;

@CoreClass(name = "BasicObject")
public abstract class BasicObjectNodes {
@@ -170,27 +172,27 @@ public Object instanceExec(Object receiver, Object[] arguments, NotProvided bloc

}

@CoreMethod(names = "method_missing", needsBlock = true, argumentsAsArray = true, visibility = Visibility.PRIVATE)
@CoreMethod(names = "method_missing", needsBlock = true, argumentsAsArray = true, optional = 1, visibility = Visibility.PRIVATE)
public abstract static class MethodMissingNode extends CoreMethodArrayArgumentsNode {

public MethodMissingNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization
public Object methodMissing(Object self, Object[] args, NotProvided block) {
public Object methodMissingNoName(Object self, NotProvided name, Object[] args, NotProvided block) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().argumentError("no id given", this));
}

return methodMissing(self, args, (RubyBasicObject) null);
@Specialization
public Object methodMissingNoBlock(Object self, RubyBasicObject name, Object[] args, NotProvided block) {
return methodMissingBlock(self, name, args, (RubyBasicObject) null);
}

@Specialization(guards = "isRubyProc(block)")
public Object methodMissing(Object self, Object[] args, RubyBasicObject block) {
CompilerDirectives.transferToInterpreter();

final RubyBasicObject name = (RubyBasicObject) args[0];
final Object[] sentArgs = ArrayUtils.extractRange(args, 1, args.length);
return methodMissing(self, name, sentArgs, block);
public Object methodMissingBlock(Object self, RubyBasicObject name, Object[] args, RubyBasicObject block) {
return methodMissing(self, name, args, block);
}

private Object methodMissing(Object self, RubyBasicObject name, Object[] args, RubyBasicObject block) {
@@ -223,7 +225,7 @@ private boolean lastCallWasVCall() {

}

@CoreMethod(names = "__send__", needsBlock = true, required = 1, argumentsAsArray = true)
@CoreMethod(names = "__send__", needsBlock = true, argumentsAsArray = true, required = 1)
public abstract static class SendNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode dispatchNode;
@@ -241,15 +243,13 @@ public SendNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public Object send(VirtualFrame frame, Object self, Object[] args, NotProvided block) {
return send(frame, self, args, (RubyBasicObject) null);
public Object send(VirtualFrame frame, Object self, Object name, Object[] args, NotProvided block) {
return send(frame, self, name, args, (RubyBasicObject) null);
}

@Specialization(guards = "isRubyProc(block)")
public Object send(VirtualFrame frame, Object self, Object[] args, RubyBasicObject block) {
final Object name = args[0];
final Object[] sendArgs = ArrayUtils.extractRange(args, 1, args.length);
return dispatchNode.call(frame, self, name, block, sendArgs);
public Object send(VirtualFrame frame, Object self, Object name, Object[] args, RubyBasicObject block) {
return dispatchNode.call(frame, self, name, block, args);
}

}
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeUtil;

import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
@@ -151,13 +152,7 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails
final CoreSourceSection sourceSection = new CoreSourceSection(methodDetails.getClassAnnotation().name(), method.names()[0]);

final int required = method.required();
final int optional;

if (method.argumentsAsArray()) {
optional = 0;
} else {
optional = method.optional();
}
final int optional = method.optional();

final Arity arity = new Arity(required, optional, method.argumentsAsArray());

@@ -183,22 +178,21 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails
argumentsNodes.add(readSelfNode);
}

if (method.argumentsAsArray()) {
argumentsNodes.add(new ReadAllArgumentsNode(context, sourceSection));
} else {
for (int n = 0; n < arity.getPreRequired() + arity.getOptional(); n++) {
RubyNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, MissingArgumentBehaviour.UNDEFINED);

if (ArrayUtils.contains(method.lowerFixnumParameters(), n)) {
readArgumentNode = FixnumLowerNodeGen.create(context, sourceSection, readArgumentNode);
}
for (int n = 0; n < arity.getPreRequired() + arity.getOptional(); n++) {
RubyNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, MissingArgumentBehaviour.UNDEFINED);

if (ArrayUtils.contains(method.raiseIfFrozenParameters(), n)) {
readArgumentNode = new RaiseIfFrozenNode(readArgumentNode);
}
if (ArrayUtils.contains(method.lowerFixnumParameters(), n)) {
readArgumentNode = FixnumLowerNodeGen.create(context, sourceSection, readArgumentNode);
}

argumentsNodes.add(readArgumentNode);
if (ArrayUtils.contains(method.raiseIfFrozenParameters(), n)) {
readArgumentNode = new RaiseIfFrozenNode(readArgumentNode);
}

argumentsNodes.add(readArgumentNode);
}
if (method.argumentsAsArray()) {
argumentsNodes.add(new ReadRemainingArgumentsNode(context, sourceSection, arity.getPreRequired() + arity.getOptional()));
}

if (method.needsBlock()) {
@@ -246,6 +240,12 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails
return new RubyRootNode(context, sourceSection, null, sharedMethodInfo, exceptionTranslatingNode);
}

public void allMethodInstalled() {
if (!AmbiguousOptionalArgumentChecker.SUCCESS) {
System.exit(1);
}
}

public static class MethodDetails {

private final CoreClass classAnnotation;
@@ -281,36 +281,33 @@ public String getIndicativeName() {
private static class AmbiguousOptionalArgumentChecker {

private static final Method GET_PARAMETERS = checkParametersNamesAvailable();
private static boolean SUCCESS = true;

private static Method checkParametersNamesAvailable() {
try {
return Method.class.getMethod("getParameters");
} catch (NoSuchMethodException | SecurityException e) {
// Java 7 or could not find how to get names of method parameters
System.err.println("Could not find method Method.getParameters()");
System.exit(1);
return null;
}
}

private static void verifyNoAmbiguousOptionalArguments(MethodDetails methodDetails) {
if (GET_PARAMETERS == null) {
System.exit(1);
}

try {
verifyNoAmbiguousOptionalArgumentsWithReflection(methodDetails);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
SUCCESS = false;
}
}

private static void verifyNoAmbiguousOptionalArgumentsWithReflection(MethodDetails methodDetails) throws ReflectiveOperationException {
boolean success = true;

if (methodDetails.getMethodAnnotation().optional() > 0) {
int opt = methodDetails.getMethodAnnotation().optional();
if (methodDetails.getMethodAnnotation().needsBlock()) {
final CoreMethod methodAnnotation = methodDetails.getMethodAnnotation();
if (methodAnnotation.optional() > 0 || methodAnnotation.needsBlock()) {
int opt = methodAnnotation.optional();
if (methodAnnotation.needsBlock()) {
opt++;
}

@@ -324,6 +321,9 @@ private static void verifyNoAmbiguousOptionalArgumentsWithReflection(MethodDetai
// count from the end to ignore optional VirtualFrame in front.
Class<?>[] parameterTypes = method.getParameterTypes();
int n = parameterTypes.length - i;
if (methodAnnotation.argumentsAsArray()) {
n--; // ignore final Object[] argument
}
Class<?> parameterType = parameterTypes[n];
Object[] parameters = (Object[]) GET_PARAMETERS.invoke(method);

@@ -335,7 +335,7 @@ private static void verifyNoAmbiguousOptionalArgumentsWithReflection(MethodDetai
}
String name = (String) parameter.getClass().getMethod("getName").invoke(parameter);

if (parameterType == Object.class && !name.startsWith("unused")) {
if (parameterType == Object.class && !name.startsWith("unused") && !name.equals("maybeBlock")) {
String[] guards = method.getAnnotation(Specialization.class).guards();
if (!isGuarded(name, guards)) {
unguardedObjectArgument = true;
@@ -346,16 +346,12 @@ private static void verifyNoAmbiguousOptionalArgumentsWithReflection(MethodDetai
}

if (unguardedObjectArgument) {
success = false;
SUCCESS = false;
System.err.println("Ambiguous optional argument in " + node.getCanonicalName() + ":");
System.err.println(errors);
}
}
}

if (!success) {
System.exit(1);
}
}

private static boolean isGuarded(String name, String[] guards) {
45 changes: 16 additions & 29 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -645,18 +645,18 @@ public ExecNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public Object exec(VirtualFrame frame, Object[] args) {
public Object exec(VirtualFrame frame, Object command, Object[] args) {
if (toHashNode == null) {
CompilerDirectives.transferToInterpreter();
toHashNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

CompilerDirectives.transferToInterpreter();

final String[] commandLine = new String[args.length];

final String[] commandLine = new String[1 + args.length];
commandLine[0] = command.toString();
for (int n = 0; n < args.length; n++) {
commandLine[n] = args[n].toString();
commandLine[1 + n] = args[n].toString();
}

final RubyBasicObject env = getContext().getCoreLibrary().getENV();
@@ -1442,15 +1442,13 @@ public PublicSendNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public Object send(VirtualFrame frame, Object self, Object[] args, NotProvided block) {
return send(frame, self, args, (RubyBasicObject) null);
public Object send(VirtualFrame frame, Object self, Object name, Object[] args, NotProvided block) {
return send(frame, self, name, args, (RubyBasicObject) null);
}

@Specialization(guards = "isRubyProc(block)")
public Object send(VirtualFrame frame, Object self, Object[] args, RubyBasicObject block) {
final Object name = args[0];
final Object[] sendArgs = ArrayUtils.extractRange(args, 1, args.length);
return dispatchNode.call(frame, self, name, block, sendArgs);
public Object send(VirtualFrame frame, Object self, Object name, Object[] args, RubyBasicObject block) {
return dispatchNode.call(frame, self, name, block, args);
}

}
@@ -1883,18 +1881,17 @@ public FormatNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = {"isRubyString(firstArgument(arguments))", "byteListsEqual(asRubyBasicObject(firstArgument(arguments)), cachedFormat)"})
@Specialization(guards = { "isRubyString(format)", "byteListsEqual(format, cachedFormat)" })
public RubyBasicObject formatCached(
VirtualFrame frame,
RubyBasicObject format,
Object[] arguments,
@Cached("privatizeByteList(asRubyBasicObject(firstArgument(arguments)))") ByteList cachedFormat,
@Cached("create(compileFormat(asRubyBasicObject(firstArgument(arguments))))") DirectCallNode callPackNode) {
final Object[] store = ArrayUtils.extractRange(arguments, 1, arguments.length);

@Cached("privatizeByteList(format)") ByteList cachedFormat,
@Cached("create(compileFormat(format))") DirectCallNode callPackNode) {
final PackResult result;

try {
result = (PackResult) callPackNode.call(frame, new Object[]{store, store.length});
result = (PackResult) callPackNode.call(frame, new Object[] { arguments, arguments.length });
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
@@ -1903,18 +1900,16 @@ public RubyBasicObject formatCached(
return finishFormat(cachedFormat, result);
}

@Specialization(guards = "isRubyString(firstArgument(arguments))", contains = "formatCached")
@Specialization(guards = "isRubyString(format)", contains = "formatCached")
public RubyBasicObject formatUncached(
VirtualFrame frame,
RubyBasicObject format,
Object[] arguments,
@Cached("create()") IndirectCallNode callPackNode) {
final RubyBasicObject format = (RubyBasicObject) arguments[0];
final Object[] store = ArrayUtils.extractRange(arguments, 1, arguments.length);

final PackResult result;

try {
result = (PackResult) callPackNode.call(frame, compileFormat((RubyBasicObject) arguments[0]), new Object[]{store, store.length});
result = (PackResult) callPackNode.call(frame, compileFormat(format), new Object[] { arguments, arguments.length });
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
@@ -1985,14 +1980,6 @@ protected CallTarget compileFormat(RubyBasicObject format) {
}
}

protected Object firstArgument(Object[] args) {
return args[0];
}

protected RubyBasicObject asRubyBasicObject(Object arg) {
return (RubyBasicObject) arg;
}

}

@CoreMethod(names = "system", isModuleFunction = true, needsSelf = false, required = 1)
Original file line number Diff line number Diff line change
@@ -246,7 +246,7 @@ public Object executeForeign(VirtualFrame frame, TruffleObject receiver) {
}

}
// TODO: remove maxArgs - hits an assertion if maxArgs is removed - trying argumentsAsArray = true (CS)

@CoreMethod(names = "execute", isModuleFunction = true, needsSelf = false, required = 1, argumentsAsArray = true)
public abstract static class ExecuteNode extends CoreMethodArrayArgumentsNode {

Loading