From 0378138a1f2b0409a683b9f4209dbd54a0d28513 Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Wed, 11 Feb 2015 18:20:38 +0100 Subject: [PATCH 1/7] Treat keyword arguments like regular named arguments. --- .../src/main/java/org/jruby/ast/ArgsNode.java | 12 ++ .../jruby/truffle/nodes/ReadConstantNode.java | 2 +- .../org/jruby/truffle/nodes/RubyCallNode.java | 84 +------------- .../nodes/core/CoreMethodNodeManager.java | 2 +- .../jruby/truffle/nodes/core/KernelNodes.java | 12 +- .../jruby/truffle/nodes/core/ModuleNodes.java | 7 +- .../dispatch/CachedBooleanDispatchNode.java | 17 +-- .../dispatch/CachedBoxedDispatchNode.java | 109 ++++++++++++++++-- .../CachedBoxedMethodMissingDispatchNode.java | 25 ++-- .../CachedBoxedReturnMissingDispatchNode.java | 19 +-- .../CachedBoxedSymbolDispatchNode.java | 23 ++-- .../nodes/dispatch/CachedDispatchNode.java | 14 ++- .../dispatch/CachedUnboxedDispatchNode.java | 24 ++-- .../nodes/dispatch/CallDispatchHeadNode.java | 10 +- .../nodes/dispatch/DispatchHeadNode.java | 27 +++-- .../dispatch/DispatchHeadNodeFactory.java | 37 ++++-- .../truffle/nodes/dispatch/DispatchNode.java | 97 ++++++++++++++-- .../dispatch/DoesRespondDispatchHeadNode.java | 8 +- .../nodes/dispatch/UncachedDispatchNode.java | 21 ++-- .../dispatch/UnresolvedDispatchNode.java | 41 ++++--- .../nodes/literal/HashLiteralNode.java | 16 ++- .../truffle/nodes/methods/MarkerNode.java | 24 ++++ .../methods/arguments/CheckArityNode.java | 10 +- .../OptionalKeywordArgMissingNode.java | 25 ++++ .../arguments/ReadKeywordArgumentNode.java | 26 ++++- .../ReadKeywordRestArgumentNode.java | 27 ++++- .../arguments/ReadOptionalArgumentNode.java | 2 +- .../nodes/respondto/RespondToNode.java | 4 +- .../nodes/rubinius/FixnumPrimitiveNodes.java | 2 +- .../jruby/truffle/runtime/RubyArguments.java | 28 ++++- .../jruby/truffle/runtime/methods/Arity.java | 14 ++- .../runtime/methods/SharedMethodInfo.java | 60 ++++++++++ .../truffle/translator/BodyTranslator.java | 2 +- .../translator/LoadArgumentsTranslator.java | 13 ++- .../truffle/translator/MethodTranslator.java | 4 +- 35 files changed, 614 insertions(+), 234 deletions(-) create mode 100644 truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java create mode 100644 truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java diff --git a/core/src/main/java/org/jruby/ast/ArgsNode.java b/core/src/main/java/org/jruby/ast/ArgsNode.java index f9a3c8495a4..a050f22d929 100644 --- a/core/src/main/java/org/jruby/ast/ArgsNode.java +++ b/core/src/main/java/org/jruby/ast/ArgsNode.java @@ -148,6 +148,18 @@ protected Arity calculateArity() { public boolean hasKwargs() { return hasKwargs; } + + public int countKeywords() { + if (hasKwargs) { + if (keywords == null) { + // Rest keyword argument + return 0; + } + return keywords.size(); + } else { + return 0; + } + } protected boolean hasMasgnArgs() { if (preCount > 0) for (Node node : pre.childNodes()) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java index d4a125e60ad..a0b7f5958e4 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java @@ -32,7 +32,7 @@ public ReadConstantNode(RubyContext context, SourceSection sourceSection, String super(context, sourceSection); this.name = name; this.receiver = receiver; - dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, lexicalScope, DispatchAction.READ_CONSTANT); + dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, lexicalScope, DispatchAction.READ_CONSTANT, null, false); } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java index dfee7c25135..5b3a24c6f5e 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java @@ -10,11 +10,8 @@ package org.jruby.truffle.nodes; import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.utilities.BranchProfile; import org.jruby.truffle.nodes.cast.BooleanCastNode; import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory; import org.jruby.truffle.nodes.cast.ProcOrNullNode; @@ -23,10 +20,8 @@ import org.jruby.truffle.runtime.ModuleOperations; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; -import org.jruby.truffle.runtime.core.RubyArray; import org.jruby.truffle.runtime.core.RubyProc; import org.jruby.truffle.runtime.methods.InternalMethod; -import org.jruby.truffle.runtime.util.ArrayUtils; public class RubyCallNode extends RubyNode { @@ -34,21 +29,12 @@ public class RubyCallNode extends RubyNode { @Child private RubyNode receiver; @Child private ProcOrNullNode block; - @Children private final RubyNode[] arguments; + private final RubyNode[] arguments; - private final boolean isSplatted; private final boolean isVCall; @Child private CallDispatchHeadNode dispatchHead; - private final BranchProfile splatNotArrayProfile = BranchProfile.create(); - - @CompilerDirectives.CompilationFinal private boolean seenNullInUnsplat = false; - @CompilerDirectives.CompilationFinal private boolean seenIntegerFixnumInUnsplat = false; - @CompilerDirectives.CompilationFinal private boolean seenLongFixnumInUnsplat = false; - @CompilerDirectives.CompilationFinal private boolean seenFloatInUnsplat = false; - @CompilerDirectives.CompilationFinal private boolean seenObjectInUnsplat = false; - @Child private CallDispatchHeadNode respondToMissing; @Child private BooleanCastNode respondToMissingCast; @@ -76,10 +62,9 @@ public RubyCallNode(RubyContext context, SourceSection section, String methodNam } this.arguments = arguments; - this.isSplatted = isSplatted; this.isVCall = isVCall; - dispatchHead = DispatchHeadNodeFactory.createMethodCall(context, ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING); + dispatchHead = DispatchHeadNodeFactory.createMethodCall(context, ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING, arguments, isSplatted); respondToMissing = DispatchHeadNodeFactory.createMethodCall(context, true, MissingBehavior.RETURN_MISSING); respondToMissingCast = BooleanCastNodeFactory.create(context, section, null); @@ -89,10 +74,9 @@ public RubyCallNode(RubyContext context, SourceSection section, String methodNam @Override public Object execute(VirtualFrame frame) { final Object receiverObject = receiver.execute(frame); - final Object[] argumentsObjects = executeArguments(frame); final RubyProc blockObject = executeBlock(frame); - return dispatchHead.call(frame, receiverObject, methodName, blockObject, argumentsObjects); + return dispatchHead.call(frame, receiverObject, methodName, blockObject, (Object[]) null); } private RubyProc executeBlock(VirtualFrame frame) { @@ -103,68 +87,6 @@ private RubyProc executeBlock(VirtualFrame frame) { } } - @ExplodeLoop - private Object[] executeArguments(VirtualFrame frame) { - final Object[] argumentsObjects = new Object[arguments.length]; - - for (int i = 0; i < arguments.length; i++) { - argumentsObjects[i] = arguments[i].execute(frame); - } - - if (isSplatted) { - return splat(argumentsObjects[0]); - } else { - return argumentsObjects; - } - } - - private Object[] splat(Object argument) { - // TODO(CS): what happens if isn't just one argument, or it isn't an Array? - - if (!(argument instanceof RubyArray)) { - splatNotArrayProfile.enter(); - notDesignedForCompilation(); - throw new UnsupportedOperationException(); - } - - final RubyArray array = (RubyArray) argument; - final int size = array.getSize(); - final Object store = array.getStore(); - - if (seenNullInUnsplat && store == null) { - return new Object[]{}; - } else if (seenIntegerFixnumInUnsplat && store instanceof int[]) { - return ArrayUtils.boxUntil((int[]) store, size); - } else if (seenLongFixnumInUnsplat && store instanceof long[]) { - return ArrayUtils.boxUntil((long[]) store, size); - } else if (seenFloatInUnsplat && store instanceof double[]) { - return ArrayUtils.boxUntil((double[]) store, size); - } else if (seenObjectInUnsplat && store instanceof Object[]) { - return ArrayUtils.extractRange((Object[]) store, 0, size); - } - - CompilerDirectives.transferToInterpreterAndInvalidate(); - - if (store == null) { - seenNullInUnsplat = true; - return new Object[]{}; - } else if (store instanceof int[]) { - seenIntegerFixnumInUnsplat = true; - return ArrayUtils.boxUntil((int[]) store, size); - } else if (store instanceof long[]) { - seenLongFixnumInUnsplat = true; - return ArrayUtils.boxUntil((long[]) store, size); - } else if (store instanceof double[]) { - seenFloatInUnsplat = true; - return ArrayUtils.boxUntil((double[]) store, size); - } else if (store instanceof Object[]) { - seenObjectInUnsplat = true; - return ArrayUtils.extractRange((Object[]) store, 0, size); - } - - throw new UnsupportedOperationException(); - } - @Override public Object isDefined(VirtualFrame frame) { notDesignedForCompilation(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java index a3855d20215..48f4f79e936 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java @@ -142,7 +142,7 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails optional = methodDetails.getMethodAnnotation().optional(); } - final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false); + final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false, 0); final List argumentsNodes = new ArrayList<>(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java index 36662ff0138..f0538205151 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java @@ -24,11 +24,8 @@ import org.jruby.common.IRubyWarnings; import org.jruby.runtime.Visibility; import org.jruby.truffle.nodes.RubyNode; -import org.jruby.truffle.nodes.cast.BooleanCastNode; -import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory; import org.jruby.truffle.nodes.cast.NumericToFloatNode; import org.jruby.truffle.nodes.cast.NumericToFloatNodeFactory; -import org.jruby.truffle.nodes.coerce.ToStrNode; import org.jruby.truffle.nodes.coerce.ToStrNodeFactory; import org.jruby.truffle.nodes.control.WhileNode; import org.jruby.truffle.nodes.core.KernelNodesFactory.SameOrEqualNodeFactory; @@ -49,7 +46,6 @@ import org.jruby.truffle.runtime.backtrace.Backtrace; import org.jruby.truffle.runtime.backtrace.MRIBacktraceFormatter; import org.jruby.truffle.runtime.control.RaiseException; -import org.jruby.truffle.runtime.control.ThrowException; import org.jruby.truffle.runtime.core.*; import org.jruby.truffle.runtime.hash.HashOperations; import org.jruby.truffle.runtime.hash.KeyValue; @@ -1064,8 +1060,8 @@ public abstract static class IntegerNode extends CoreMethodNode { public IntegerNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - toIntRespondTo = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null); - toInt = new CallDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null); + toIntRespondTo = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, false); + toInt = new CallDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, false); } public IntegerNode(IntegerNode prev) { @@ -1684,8 +1680,8 @@ public abstract static class RespondToNode extends CoreMethodNode { public RespondToNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null); - dispatchIgnoreVisibility = new DoesRespondDispatchHeadNode(context, true, false, MissingBehavior.RETURN_MISSING, null); + dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, false); + dispatchIgnoreVisibility = new DoesRespondDispatchHeadNode(context, true, false, MissingBehavior.RETURN_MISSING, null, null, false); if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) { dispatch.forceUncached(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java index 24b1c9ffca2..f8ded9636ce 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java @@ -14,7 +14,6 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; @@ -271,7 +270,7 @@ public RubyNilClass attrReader(RubyModule module, Object[] args) { public static void attrReader(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) { CompilerDirectives.transferToInterpreter(); - final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false)); + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false, 0)); final SelfNode self = new SelfNode(context, sourceSection); final ReadInstanceVariableNode readInstanceVariable = new ReadInstanceVariableNode(context, sourceSection, "@" + name, self, false); @@ -323,7 +322,7 @@ public RubyNilClass attrWriter(RubyModule module, Object[] args) { public static void attrWriter(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) { CompilerDirectives.transferToInterpreter(); - final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false)); + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false, 0)); final SelfNode self = new SelfNode(context, sourceSection); final ReadPreArgumentNode readArgument = new ReadPreArgumentNode(context, sourceSection, 0, MissingArgumentBehaviour.RUNTIME_ERROR); @@ -636,7 +635,7 @@ public abstract static class ConstGetNode extends CoreMethodNode { public ConstGetNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, null, DispatchAction.READ_CONSTANT); + dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, null, DispatchAction.READ_CONSTANT, null, false); } public ConstGetNode(ConstGetNode prev) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java index 6a01ba15af0..e4846f0c43a 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java @@ -12,12 +12,13 @@ import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.InvalidAssumptionException; import com.oracle.truffle.api.utilities.BranchProfile; + +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyProc; @@ -52,8 +53,10 @@ public CachedBooleanDispatchNode( Object trueValue, InternalMethod trueMethod, boolean indirect, - DispatchAction dispatchAction) { - super(context, cachedName, next, indirect, dispatchAction); + DispatchAction dispatchAction, RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, + argumentNodes, isSplatted); this.falseUnmodifiedAssumption = falseUnmodifiedAssumption; this.falseMethod = falseMethod; @@ -132,7 +135,7 @@ public Object executeDispatch( trueMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else { return trueCallDirect.call( frame, @@ -141,7 +144,7 @@ public Object executeDispatch( trueMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } } @@ -180,7 +183,7 @@ public Object executeDispatch( falseMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else { return falseCallDirect.call( frame, @@ -189,7 +192,7 @@ public Object executeDispatch( falseMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java index e52fcee35aa..aa1bc8a0ce8 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java @@ -9,14 +9,22 @@ */ package org.jruby.truffle.nodes.dispatch; +import java.util.ArrayList; +import java.util.List; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.InvalidAssumptionException; + +import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.literal.HashLiteralNode; +import org.jruby.truffle.nodes.literal.ObjectLiteralNode; +import org.jruby.truffle.nodes.methods.MarkerNode; +import org.jruby.truffle.nodes.methods.arguments.OptionalKeywordArgMissingNode; import org.jruby.truffle.runtime.DebugOperations; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; @@ -44,7 +52,9 @@ public CachedBoxedDispatchNode( Object value, InternalMethod method, boolean indirect, - DispatchAction dispatchAction) { + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { this( context, cachedName, @@ -54,7 +64,87 @@ public CachedBoxedDispatchNode( value, method, indirect, - dispatchAction); + dispatchAction, + argumentNodes, + isSplatted); + } + + public static RubyNode[] expandedArgumentNodes(RubyContext context, InternalMethod method, RubyNode[] argumentNodes) { + final RubyNode[] result; + + if (method != null && method.getSharedMethodInfo().getKeywordArguments() != null && ( + argumentNodes.length == 0 || argumentNodes[argumentNodes.length - 1] instanceof HashLiteralNode)) { + List kwargs = method.getSharedMethodInfo().getKeywordArguments(); + + int countArgNodes = argumentNodes.length + kwargs.size() + 1; + if (argumentNodes.length == 0) { + countArgNodes++; + } + + result = new RubyNode[countArgNodes]; + int i; + + for (i = 0; i < argumentNodes.length - 1; ++i) { + result[i] = argumentNodes[i]; + } + + int firstMarker = i++; + result[firstMarker] = new MarkerNode(context, null); + + HashLiteralNode hashNode; + if (argumentNodes.length > 0) { + hashNode = (HashLiteralNode) argumentNodes[argumentNodes.length - 1]; + } else { + hashNode = HashLiteralNode.create(context, null, + new RubyNode[0]); + } + + List restKeywordLabels = new ArrayList(); + for (int j = 0; j < hashNode.size(); j++) { + final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + restKeywordLabels.add(label); + } + + for (String kwarg : kwargs) { + result[i] = new OptionalKeywordArgMissingNode(context, null); + for (int j = 0; j < hashNode.size(); j++) { + final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + + if (label.equals(kwarg)) { + result[i] = hashNode.getValue(j); + restKeywordLabels.remove(label); + break; + } + } + i++; + } + result[i++] = new MarkerNode(context, null); + + if (restKeywordLabels.size() > 0) { + i = 0; + RubyNode[] keyValues = new RubyNode[2 * restKeywordLabels.size()]; + + for (String label : restKeywordLabels) { + for (int j = 0; j < hashNode.size(); j++) { + final String argLabel = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + + if (argLabel.equals(label)) { + keyValues[i++] = hashNode.getKey(j); + keyValues[i++] = hashNode.getValue(j); + } + } + } + + HashLiteralNode restHash = HashLiteralNode.create(context, null, keyValues); + result[firstMarker] = restHash; + } + + } + else { + result = argumentNodes; + } + + return result; } /** @@ -69,8 +159,10 @@ public CachedBoxedDispatchNode( Object value, InternalMethod method, boolean indirect, - DispatchAction dispatchAction) { - super(context, cachedName, next, indirect, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, expandedArgumentNodes(context, method, argumentNodes), isSplatted); this.expectedClass = expectedClass; this.unmodifiedAssumption = unmodifiedAssumption; @@ -133,16 +225,17 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else { - return callNode.call( + Object args = executeArguments(frame, argumentsObjects); + return callNode.call( frame, RubyArguments.pack( method, method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(args, Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java index aa7319f85a3..5e8c9be1288 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java @@ -9,14 +9,7 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.DirectCallNode; -import com.oracle.truffle.api.nodes.IndirectCallNode; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyBasicObject; @@ -25,6 +18,14 @@ import org.jruby.truffle.runtime.methods.InternalMethod; import org.jruby.util.cli.Options; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.IndirectCallNode; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; + public class CachedBoxedMethodMissingDispatchNode extends CachedDispatchNode { private final RubyClass expectedClass; @@ -41,8 +42,10 @@ public CachedBoxedMethodMissingDispatchNode( RubyClass expectedClass, InternalMethod method, boolean indirect, - DispatchAction dispatchAction) { - super(context, cachedName, next, indirect, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); this.expectedClass = expectedClass; unmodifiedAssumption = expectedClass.getUnmodifiedAssumption(); @@ -107,7 +110,7 @@ public Object executeDispatch( case CALL_METHOD: { // When calling #method_missing we need to prepend the symbol - final Object[] argumentsObjectsArray = CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true); + final Object[] argumentsObjectsArray = CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true); final Object[] modifiedArgumentsObjects = new Object[1 + argumentsObjectsArray.length]; modifiedArgumentsObjects[0] = getCachedNameAsSymbol(); RubyArguments.arraycopy(argumentsObjectsArray, 0, modifiedArgumentsObjects, 1, argumentsObjectsArray.length); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java index c0516583c37..914928f1db2 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java @@ -9,17 +9,17 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyBasicObject; import org.jruby.truffle.runtime.core.RubyClass; import org.jruby.truffle.runtime.core.RubyProc; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; + public class CachedBoxedReturnMissingDispatchNode extends CachedDispatchNode { private final RubyClass expectedClass; @@ -31,8 +31,11 @@ public CachedBoxedReturnMissingDispatchNode( DispatchNode next, RubyClass expectedClass, boolean indirect, - DispatchAction dispatchAction) { - super(context, cachedName, next, indirect, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); + assert expectedClass != null; this.expectedClass = expectedClass; unmodifiedAssumption = expectedClass.getUnmodifiedAssumption(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java index 61fe16c3640..c147c6d6c3f 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java @@ -9,19 +9,20 @@ */ package org.jruby.truffle.nodes.dispatch; +import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.runtime.RubyArguments; +import org.jruby.truffle.runtime.RubyContext; +import org.jruby.truffle.runtime.core.RubyProc; +import org.jruby.truffle.runtime.core.RubySymbol; +import org.jruby.truffle.runtime.methods.InternalMethod; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.InvalidAssumptionException; -import org.jruby.truffle.runtime.RubyArguments; -import org.jruby.truffle.runtime.RubyContext; -import org.jruby.truffle.runtime.core.RubyProc; -import org.jruby.truffle.runtime.core.RubySymbol; -import org.jruby.truffle.runtime.methods.InternalMethod; public class CachedBoxedSymbolDispatchNode extends CachedDispatchNode { @@ -40,8 +41,10 @@ public CachedBoxedSymbolDispatchNode( Object value, InternalMethod method, boolean indirect, - DispatchAction dispatchAction) { - super(context, cachedName, next, indirect, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); unmodifiedAssumption = context.getCoreLibrary().getSymbolClass().getUnmodifiedAssumption(); this.value = value; @@ -102,7 +105,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else { return callNode.call( frame, @@ -111,7 +114,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java index 55165b64ac5..e38fc67940c 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java @@ -10,9 +10,12 @@ package org.jruby.truffle.nodes.dispatch; import com.oracle.truffle.api.utilities.BranchProfile; + +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyString; import org.jruby.truffle.runtime.core.RubySymbol; +import org.jruby.truffle.runtime.methods.InternalMethod; public abstract class CachedDispatchNode extends DispatchNode { @@ -29,8 +32,10 @@ public CachedDispatchNode( Object cachedName, DispatchNode next, boolean indirect, - DispatchAction dispatchAction) { - super(context, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, dispatchAction, argumentNodes, isSplatted); assert (cachedName instanceof String) || (cachedName instanceof RubySymbol) || (cachedName instanceof RubyString); this.cachedName = cachedName; @@ -84,5 +89,10 @@ protected RubySymbol getCachedNameAsSymbol() { public boolean isIndirect() { return indirect; } + + protected RubyNode[] argumentsWithDecodedKwargsHash(RubyNode[] arguments, + InternalMethod method) { + return null; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java index ceb78c9024d..bf69ab2e385 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java @@ -9,19 +9,20 @@ */ package org.jruby.truffle.nodes.dispatch; +import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.runtime.RubyArguments; +import org.jruby.truffle.runtime.RubyContext; +import org.jruby.truffle.runtime.core.RubyBasicObject; +import org.jruby.truffle.runtime.core.RubyProc; +import org.jruby.truffle.runtime.methods.InternalMethod; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.InvalidAssumptionException; -import org.jruby.truffle.runtime.RubyArguments; -import org.jruby.truffle.runtime.RubyContext; -import org.jruby.truffle.runtime.core.RubyBasicObject; -import org.jruby.truffle.runtime.core.RubyProc; -import org.jruby.truffle.runtime.methods.InternalMethod; public class CachedUnboxedDispatchNode extends CachedDispatchNode { @@ -43,8 +44,11 @@ public CachedUnboxedDispatchNode( Object value, InternalMethod method, boolean indirect, - DispatchAction dispatchAction) { - super(context, cachedName, next, indirect, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); + this.expectedClass = expectedClass; this.unmodifiedAssumption = unmodifiedAssumption; this.value = value; @@ -99,7 +103,7 @@ public Object executeDispatch( method, method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else { return callNode.call( frame, @@ -107,7 +111,7 @@ public Object executeDispatch( method, method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java index 715eea8b048..0d45f581948 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java @@ -9,8 +9,7 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.frame.VirtualFrame; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.nodes.cast.BooleanCastNode; import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory; import org.jruby.truffle.runtime.LexicalScope; @@ -18,12 +17,15 @@ import org.jruby.truffle.runtime.control.RaiseException; import org.jruby.truffle.runtime.core.RubyProc; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; + public class CallDispatchHeadNode extends DispatchHeadNode { @Child private BooleanCastNode booleanCastNode; - public CallDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope) { - super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.CALL_METHOD); + public CallDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, RubyNode[] argumentNodes, boolean isSplatted) { + super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.CALL_METHOD, argumentNodes, isSplatted); } public Object call( diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java index 6b2dbd1030a..1b7847db3d1 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java @@ -9,13 +9,12 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.RubyContext; -import org.jruby.truffle.runtime.control.RaiseException; -import org.jruby.truffle.runtime.core.RubyProc; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; public class DispatchHeadNode extends Node { @@ -25,6 +24,8 @@ public class DispatchHeadNode extends Node { protected final MissingBehavior missingBehavior; protected final LexicalScope lexicalScope; protected final DispatchAction dispatchAction; + protected final boolean isSplatted; + protected final RubyNode[] argumentNodes; @Child private DispatchNode first; @@ -34,14 +35,18 @@ public DispatchHeadNode( boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, - DispatchAction dispatchAction) { + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { this.context = context; this.ignoreVisibility = ignoreVisibility; this.indirect = indirect; this.missingBehavior = missingBehavior; this.lexicalScope = lexicalScope; this.dispatchAction = dispatchAction; - first = new UnresolvedDispatchNode(context, ignoreVisibility, indirect, missingBehavior, dispatchAction); + this.argumentNodes = argumentNodes; + this.isSplatted = isSplatted; + first = new UnresolvedDispatchNode(context, ignoreVisibility, indirect, missingBehavior, dispatchAction, argumentNodes, isSplatted); } public Object dispatch( @@ -60,7 +65,7 @@ public Object dispatch( public void reset(String reason) { first.replace(new UnresolvedDispatchNode( - context, ignoreVisibility, indirect, missingBehavior, dispatchAction), reason); + context, ignoreVisibility, indirect, missingBehavior, dispatchAction, argumentNodes, isSplatted), reason); } public DispatchNode getFirstDispatchNode() { @@ -77,7 +82,11 @@ public DispatchAction getDispatchAction() { public void forceUncached() { adoptChildren(); - first.replace(new UncachedDispatchNode(context, ignoreVisibility, dispatchAction)); + first.replace(new UncachedDispatchNode(context, ignoreVisibility, dispatchAction, argumentNodes, isSplatted)); } + + public RubyNode[] getArgumentNodes() { + return argumentNodes; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java index 1d9c799589e..54946dbe4c8 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java @@ -9,7 +9,7 @@ */ package org.jruby.truffle.nodes.dispatch; -import org.jruby.truffle.runtime.LexicalScope; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyContext; public class DispatchHeadNodeFactory { @@ -20,7 +20,9 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context) { false, false, MissingBehavior.CALL_METHOD_MISSING, - null); + null, + null, + false); } public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility) { @@ -29,7 +31,9 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING, - null); + null, + null, + false); } public static CallDispatchHeadNode createMethodCall(RubyContext context, MissingBehavior missingBehavior) { @@ -38,7 +42,9 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, Missing false, false, missingBehavior, - null); + null, + null, + false); } public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, MissingBehavior missingBehavior) { @@ -47,7 +53,9 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, false, missingBehavior, - null); + null, + null, + false); } public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior) { @@ -56,7 +64,20 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, indirect, missingBehavior, - null); + null, + null, + false); + } + + public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, RubyNode[] arguments, boolean isSplatted) { + return new CallDispatchHeadNode( + context, + ignoreVisibility, + indirect, + missingBehavior, + null, + arguments, + isSplatted); } public static CallDispatchHeadNode createMethodCallOnSelf(RubyContext context) { @@ -65,7 +86,9 @@ public static CallDispatchHeadNode createMethodCallOnSelf(RubyContext context) { true, false, MissingBehavior.CALL_METHOD_MISSING, - null); + null, + null, + false); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java index 6ba9ea65a50..ea6f3fd1eee 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java @@ -9,37 +9,55 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.NodeChild; -import com.oracle.truffle.api.dsl.NodeChildren; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeUtil; import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.ModuleOperations; import org.jruby.truffle.runtime.RubyConstant; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.control.RaiseException; +import org.jruby.truffle.runtime.core.RubyArray; import org.jruby.truffle.runtime.core.RubyClass; import org.jruby.truffle.runtime.core.RubyModule; import org.jruby.truffle.runtime.core.RubyProc; import org.jruby.truffle.runtime.methods.InternalMethod; +import org.jruby.truffle.runtime.util.ArrayUtils; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.NodeUtil; +import com.oracle.truffle.api.utilities.BranchProfile; public abstract class DispatchNode extends RubyNode { private final DispatchAction dispatchAction; + + protected final boolean isSplatted; + private final BranchProfile splatNotArrayProfile = BranchProfile.create(); + + @CompilerDirectives.CompilationFinal private boolean seenNullInUnsplat = false; + @CompilerDirectives.CompilationFinal private boolean seenIntegerFixnumInUnsplat = false; + @CompilerDirectives.CompilationFinal private boolean seenLongFixnumInUnsplat = false; + @CompilerDirectives.CompilationFinal private boolean seenFloatInUnsplat = false; + @CompilerDirectives.CompilationFinal private boolean seenObjectInUnsplat = false; + public static final Object MISSING = new Object(); + + @Children protected final RubyNode[] argumentNodes; - public DispatchNode(RubyContext context, DispatchAction dispatchAction) { + public DispatchNode(RubyContext context, DispatchAction dispatchAction, RubyNode[] argumentNodes, boolean isSplatted) { super(context, null); this.dispatchAction = dispatchAction; + this.argumentNodes = argumentNodes; + this.isSplatted = isSplatted; assert dispatchAction != null; } public DispatchNode(DispatchNode prev) { super(prev); + argumentNodes = prev.getHeadNode().getArgumentNodes(); + isSplatted = prev.isSplatted; dispatchAction = prev.dispatchAction; } @@ -136,5 +154,70 @@ public final Object execute(VirtualFrame frame) { public DispatchAction getDispatchAction() { return dispatchAction; } + + private Object[] splat(Object argument) { + // TODO(CS): what happens if isn't just one argument, or it isn't an Array? + + if (!(argument instanceof RubyArray)) { + splatNotArrayProfile.enter(); + notDesignedForCompilation(); + throw new UnsupportedOperationException(); + } + + final RubyArray array = (RubyArray) argument; + final int size = array.getSize(); + final Object store = array.getStore(); + + if (seenNullInUnsplat && store == null) { + return new Object[]{}; + } else if (seenIntegerFixnumInUnsplat && store instanceof int[]) { + return ArrayUtils.boxUntil((int[]) store, size); + } else if (seenLongFixnumInUnsplat && store instanceof long[]) { + return ArrayUtils.boxUntil((long[]) store, size); + } else if (seenObjectInUnsplat && store instanceof Object[]) { + return ArrayUtils.extractRange((Object[]) store, 0, size); + } + + CompilerDirectives.transferToInterpreterAndInvalidate(); + + if (store == null) { + seenNullInUnsplat = true; + return new Object[]{}; + } else if (store instanceof int[]) { + seenIntegerFixnumInUnsplat = true; + return ArrayUtils.boxUntil((int[]) store, size); + } else if (store instanceof long[]) { + seenLongFixnumInUnsplat = true; + return ArrayUtils.boxUntil((long[]) store, size); + } else if (store instanceof Object[]) { + seenObjectInUnsplat = true; + return ArrayUtils.extractRange((Object[]) store, 0, size); + } + + throw new UnsupportedOperationException(); + } + + @ExplodeLoop + protected Object executeArguments(VirtualFrame frame, Object argumentOverride) { + if (argumentOverride != null) { + return argumentOverride; + } + + final Object[] argumentsObjects = new Object[argumentNodes.length]; + + for (int i = 0; i < argumentNodes.length; i++) { + argumentsObjects[i] = argumentNodes[i].execute(frame); + } + + if (isSplatted) { + return splat(argumentsObjects[0]); + } else { + return argumentsObjects; + } + } + + public boolean isSplatted() { + return isSplatted(); + } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java index a98d1279c76..6c9352ef248 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java @@ -9,14 +9,16 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.frame.VirtualFrame; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.RubyContext; +import com.oracle.truffle.api.frame.VirtualFrame; + public class DoesRespondDispatchHeadNode extends DispatchHeadNode { - public DoesRespondDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope) { - super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.RESPOND_TO_METHOD); + public DoesRespondDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, RubyNode[] argumentNodes, boolean isSplatted) { + super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.RESPOND_TO_METHOD, argumentNodes, isSplatted); } /** diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java index cd19032765d..caeaa032258 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java @@ -9,12 +9,7 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.IndirectCallNode; -import com.oracle.truffle.api.utilities.BranchProfile; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.nodes.conversion.ToJavaStringNode; import org.jruby.truffle.nodes.conversion.ToJavaStringNodeFactory; import org.jruby.truffle.nodes.conversion.ToSymbolNode; @@ -28,6 +23,12 @@ import org.jruby.truffle.runtime.core.RubyProc; import org.jruby.truffle.runtime.methods.InternalMethod; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.IndirectCallNode; +import com.oracle.truffle.api.utilities.BranchProfile; + public class UncachedDispatchNode extends DispatchNode { private final boolean ignoreVisibility; @@ -39,8 +40,8 @@ public class UncachedDispatchNode extends DispatchNode { private final BranchProfile constantMissingProfile = BranchProfile.create(); private final BranchProfile methodMissingProfile = BranchProfile.create(); - public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, DispatchAction dispatchAction) { - super(context, dispatchAction); + public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, DispatchAction dispatchAction, RubyNode[] argumentNodes, boolean isSplatted) { + super(context, dispatchAction, argumentNodes, isSplatted); this.ignoreVisibility = ignoreVisibility; callNode = Truffle.getRuntime().createIndirectCallNode(); toSymbolNode = ToSymbolNodeFactory.create(context, null, null); @@ -101,7 +102,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, (RubyProc) blockObject, - CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); + CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else if (dispatchAction == DispatchAction.RESPOND_TO_METHOD) { return true; } else { @@ -124,7 +125,7 @@ public Object executeDispatch( } if (dispatchAction == DispatchAction.CALL_METHOD) { - final Object[] argumentsObjectsArray = CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true); + final Object[] argumentsObjectsArray = CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true); final Object[] modifiedArgumentsObjects = new Object[1 + argumentsObjectsArray.length]; diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java index b8af9ef1ee2..b017f1a6845 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java @@ -9,9 +9,7 @@ */ package org.jruby.truffle.nodes.dispatch; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.frame.VirtualFrame; +import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyConstant; import org.jruby.truffle.runtime.RubyContext; @@ -23,6 +21,10 @@ import org.jruby.truffle.runtime.methods.InternalMethod; import org.jruby.util.cli.Options; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; + public final class UnresolvedDispatchNode extends DispatchNode { private int depth = 0; @@ -36,8 +38,11 @@ public UnresolvedDispatchNode( boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, - DispatchAction dispatchAction) { - super(context, dispatchAction); + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, dispatchAction, argumentNodes, isSplatted); + this.ignoreVisibility = ignoreVisibility; this.indirect = indirect; this.missingBehavior = missingBehavior; @@ -54,7 +59,7 @@ public Object executeDispatch( if (depth == Options.TRUFFLE_DISPATCH_POLYMORPHIC_MAX.load()) { return getHeadNode().getFirstDispatchNode() - .replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction())) + .replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, isSplatted)) .executeDispatch(frame, receiverObject, methodName, blockObject, argumentsObjects); } @@ -130,7 +135,8 @@ private Object doUnboxedObject( final CachedBooleanDispatchNode newDispatch = new CachedBooleanDispatchNode(getContext(), methodName, first, falseUnmodifiedAssumption, null, falseMethod, - trueUnmodifiedAssumption, null, trueMethod, indirect, getDispatchAction()); + trueUnmodifiedAssumption, null, trueMethod, indirect, getDispatchAction(), + argumentNodes, isSplatted); first.replace(newDispatch); @@ -139,7 +145,8 @@ private Object doUnboxedObject( } else { final CachedUnboxedDispatchNode newDispatch = new CachedUnboxedDispatchNode(getContext(), methodName, first, receiverObject.getClass(), - getContext().getCoreLibrary().getLogicalClass(receiverObject).getUnmodifiedAssumption(), null, method, indirect, getDispatchAction()); + getContext().getCoreLibrary().getLogicalClass(receiverObject).getUnmodifiedAssumption(), null, method, indirect, getDispatchAction(), + argumentNodes, isSplatted); first.replace(newDispatch); @@ -174,10 +181,10 @@ private Object doRubyBasicObject( final DispatchNode newDispatch; if (receiverObject instanceof RubySymbol) { - newDispatch = new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, null, method, indirect, getDispatchAction()); + newDispatch = new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, null, method, indirect, getDispatchAction(), argumentNodes, isSplatted); } else { newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first, - getContext().getCoreLibrary().getMetaClass(receiverObject), null, method, indirect, getDispatchAction()); + getContext().getCoreLibrary().getMetaClass(receiverObject), null, method, indirect, getDispatchAction(), argumentNodes, isSplatted); } first.replace(newDispatch); @@ -199,7 +206,7 @@ private Object doRubyBasicObject( // But we want to check the module assumption, not its singleton class assumption. final DispatchNode newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first, module.getSingletonClass(null), module.getUnmodifiedAssumption(), constant.getValue(), - null, indirect, getDispatchAction()); + null, indirect, getDispatchAction(), argumentNodes, isSplatted); first.replace(newDispatch); return newDispatch.executeDispatch(frame, receiverObject, @@ -218,7 +225,7 @@ private DispatchNode createConstantMissingNode( switch (missingBehavior) { case RETURN_MISSING: { return first.replace(new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first, - receiverObject.getMetaClass(), indirect, getDispatchAction())); + receiverObject.getMetaClass(), indirect, getDispatchAction(), argumentNodes, isSplatted)); } case CALL_CONST_MISSING: { @@ -230,11 +237,11 @@ private DispatchNode createConstantMissingNode( } if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) { - return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction())); + return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, isSplatted)); } return first.replace(new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first, - receiverObject.getMetaClass(), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction())); + receiverObject.getMetaClass(), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction(), argumentNodes, isSplatted)); } default: { @@ -251,7 +258,7 @@ private DispatchNode createMethodMissingNode( switch (missingBehavior) { case RETURN_MISSING: { return first.replace(new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first, - getContext().getCoreLibrary().getMetaClass(receiverObject), indirect, getDispatchAction())); + getContext().getCoreLibrary().getMetaClass(receiverObject), indirect, getDispatchAction(), argumentNodes, isSplatted)); } case CALL_METHOD_MISSING: { @@ -263,11 +270,11 @@ private DispatchNode createMethodMissingNode( } if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) { - return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction())); + return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, isSplatted)); } return first.replace(new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first, - getContext().getCoreLibrary().getMetaClass(receiverObject), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction())); + getContext().getCoreLibrary().getMetaClass(receiverObject), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction(), argumentNodes, isSplatted)); } default: { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java index 0c0c62b1b64..c240ba21386 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java @@ -34,8 +34,20 @@ protected HashLiteralNode(RubyContext context, SourceSection sourceSection, Ruby assert keyValues.length % 2 == 0; this.keyValues = keyValues; dupNode = DispatchHeadNodeFactory.createMethodCall(context); - freezeNode = DispatchHeadNodeFactory.createMethodCall(context); - } + freezeNode = DispatchHeadNodeFactory.createMethodCall(context); + } + + public int size() { + return keyValues.length / 2; + } + + public RubyNode getKey(int index) { + return keyValues[2 * index]; + } + + public RubyNode getValue(int index) { + return keyValues[2 * index + 1]; + } public static HashLiteralNode create(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) { if (keyValues.length == 0) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java new file mode 100644 index 00000000000..eb36db410b5 --- /dev/null +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java @@ -0,0 +1,24 @@ +package org.jruby.truffle.nodes.methods; + +import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.runtime.RubyContext; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; + +public class MarkerNode extends RubyNode { + + public MarkerNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object execute(VirtualFrame frame) { + return new Marker(); + } + + public static class Marker { + + } + +} \ No newline at end of file diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java index bdbc333ecd9..9bcb2ea0dcb 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java @@ -44,8 +44,16 @@ public CheckArityNode(RubyContext context, SourceSection sourceSection, Arity ar @Override public void executeVoid(VirtualFrame frame) { final Object[] frameArguments = frame.getArguments(); - final int given = RubyArguments.getUserArgumentsCount(frameArguments); + final int given; final RubyHash keywordArguments; + + //TODO (MS): Check merge + if (RubyArguments.isKwOptimized(frame.getArguments())) { + given = RubyArguments.getUserArgumentsCount(frame.getArguments()) + - arity.getCountKeywords() - 2; + } else { + given = RubyArguments.getUserArgumentsCount(frame.getArguments()); + } if (arity.hasKeywords()) { keywordArguments = RubyArguments.getUserKeywordsHash(frameArguments, arity.getRequired()); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java new file mode 100644 index 00000000000..289924f8307 --- /dev/null +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java @@ -0,0 +1,25 @@ +package org.jruby.truffle.nodes.methods.arguments; + +import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.runtime.RubyContext; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; + +public class OptionalKeywordArgMissingNode extends RubyNode { + + public OptionalKeywordArgMissingNode(RubyContext context, + SourceSection sourceSection) { + super(context, sourceSection); + } + + public static class OptionalKeywordArgMissing { + + } + + @Override + public Object execute(VirtualFrame frame) { + return new OptionalKeywordArgMissing(); + } + +} \ No newline at end of file diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java index 93d6d2f9715..11b88c20f40 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java @@ -11,6 +11,8 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.utilities.ValueProfile; + import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; @@ -22,17 +24,37 @@ public class ReadKeywordArgumentNode extends RubyNode { private final int minimum; private final String name; + private final int kwIndex; + private final ValueProfile argumentValueProfile = ValueProfile.createPrimitiveProfile(); + @Child private RubyNode defaultValue; - public ReadKeywordArgumentNode(RubyContext context, SourceSection sourceSection, int minimum, String name, RubyNode defaultValue) { + public ReadKeywordArgumentNode(RubyContext context, SourceSection sourceSection, int minimum, String name, RubyNode defaultValue, int kwIndex) { super(context, sourceSection); this.minimum = minimum; this.name = name; this.defaultValue = defaultValue; + this.kwIndex = kwIndex; } @Override - public Object execute(VirtualFrame frame) { + public Object execute(VirtualFrame frame) { + if (RubyArguments.isKwOptimized(frame.getArguments())) { + Object kwarg = argumentValueProfile + .profile(RubyArguments.getOptimizedKeywordArgument( + frame.getArguments(), kwIndex)); + + if (kwarg instanceof OptionalKeywordArgMissingNode.OptionalKeywordArgMissing) { + return defaultValue.execute(frame); + } else { + return kwarg; + } + } else { + return lookupKeywordInHash(frame); + } + } + + public Object lookupKeywordInHash(VirtualFrame frame) { notDesignedForCompilation(); final RubyHash hash = RubyArguments.getUserKeywordsHash(frame.getArguments(), minimum); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java index 5a650daeea0..2e83b454c4b 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java @@ -12,6 +12,8 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.literal.HashLiteralNode; +import org.jruby.truffle.nodes.methods.MarkerNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyHash; @@ -25,16 +27,35 @@ public class ReadKeywordRestArgumentNode extends RubyNode { private final int minimum; private final String[] excludedKeywords; + private final int kwIndex; - public ReadKeywordRestArgumentNode(RubyContext context, SourceSection sourceSection, int minimum, String[] excludedKeywords) { + public ReadKeywordRestArgumentNode(RubyContext context, SourceSection sourceSection, int minimum, String[] excludedKeywords, int kwIndex) { super(context, sourceSection); this.minimum = minimum; this.excludedKeywords = excludedKeywords; + this.kwIndex = kwIndex; } @Override - public Object execute(VirtualFrame frame) { - notDesignedForCompilation(); + public Object execute(VirtualFrame frame) { + if (RubyArguments.isKwOptimized(frame.getArguments())) { + Object restHash = RubyArguments.getOptimizedKeywordArgument( + frame.getArguments(), kwIndex); + + if (restHash instanceof MarkerNode.Marker) { + // no rest keyword args hash passed + return HashLiteralNode.create(getContext(), null, + new RubyNode[0]).execute(frame); + } else { + return restHash; + } + } else { + return lookupRestKeywordArgumentHash(frame); + } + } + + private Object lookupRestKeywordArgumentHash(VirtualFrame frame) { + notDesignedForCompilation(); final RubyHash hash = RubyArguments.getUserKeywordsHash(frame.getArguments(), minimum); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadOptionalArgumentNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadOptionalArgumentNode.java index de5c93f7344..b1512c57dca 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadOptionalArgumentNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadOptionalArgumentNode.java @@ -36,7 +36,7 @@ public ReadOptionalArgumentNode(RubyContext context, SourceSection sourceSection @Override public Object execute(VirtualFrame frame) { - if (RubyArguments.getUserArgumentsCount(frame.getArguments()) < minimum) { + if (RubyArguments.getNamedUserArgumentsCount(frame.getArguments()) < minimum) { defaultValueProfile.enter(); return defaultValue.execute(frame); } else { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java index e3eb60177a7..4ef4fcb9a9b 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java @@ -12,8 +12,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.jruby.truffle.nodes.RubyNode; -import org.jruby.truffle.nodes.dispatch.DispatchAction; -import org.jruby.truffle.nodes.dispatch.DispatchHeadNode; import org.jruby.truffle.nodes.dispatch.DoesRespondDispatchHeadNode; import org.jruby.truffle.nodes.dispatch.MissingBehavior; import org.jruby.truffle.runtime.RubyContext; @@ -29,7 +27,7 @@ public RespondToNode(RubyContext context, SourceSection sourceSection, RubyNode super(context, sourceSection); this.methodName = methodName; this.child = child; - dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null); + dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, false); } public boolean executeBoolean(VirtualFrame frame) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java index 75fb74141d5..f15f8012570 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java @@ -31,7 +31,7 @@ public static abstract class FixnumCoercePrimitiveNode extends RubiniusPrimitive public FixnumCoercePrimitiveNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - toFRespond = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null); + toFRespond = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, false); toF = DispatchHeadNodeFactory.createMethodCall(context); } diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java b/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java index 9f119cc0366..556f6fb051a 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java @@ -9,15 +9,16 @@ */ package org.jruby.truffle.runtime; -import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.ExplodeLoop; - +import org.jruby.truffle.nodes.methods.MarkerNode; import org.jruby.truffle.runtime.core.RubyHash; import org.jruby.truffle.runtime.core.RubyProc; import org.jruby.truffle.runtime.methods.InternalMethod; import org.jruby.truffle.runtime.util.ArrayUtils; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.ExplodeLoop; + /** * Pack and unpack Ruby method arguments to and from an array of objects. */ @@ -40,6 +41,15 @@ public static Object[] pack(InternalMethod method, MaterializedFrame declaration return packed; } + + public static Object getOptimizedKeywordArgument(Object[] arguments, + int index) { + return arguments[arguments.length - 1 + index]; + } + + public static boolean isKwOptimized(Object[] arguments) { + return arguments[arguments.length - 1] instanceof MarkerNode.Marker; + } public static InternalMethod getMethod(Object[] arguments) { return (InternalMethod) arguments[METHOD_INDEX]; @@ -80,6 +90,16 @@ public static int getUserArgumentsCount(Object[] internalArguments) { return internalArguments.length - RUNTIME_ARGUMENT_COUNT; } + public static int getNamedUserArgumentsCount(Object[] internalArguments) { + if (isKwOptimized(internalArguments)) { + return getUserArgumentsCount(internalArguments) + - getMethod(internalArguments).getSharedMethodInfo() + .getKeywordArguments().size() - 1; + } else { + return getUserArgumentsCount(internalArguments); + } + } + public static Object getUserArgument(Object[] internalArguments, int index) { return internalArguments[RUNTIME_ARGUMENT_COUNT + index]; } diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java b/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java index a4f4c00c406..7b016c7923b 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java @@ -14,12 +14,14 @@ public class Arity { private final int required; private final int optional; private final boolean allowsMore; + private final int definedKeywords; private final boolean hasKeywords; - public Arity(int required, int optional, boolean allowsMore, boolean hasKeywords) { + public Arity(int required, int optional, boolean allowsMore, boolean hasKeywords, int definedKeywords) { this.required = required; this.optional = optional; this.allowsMore = allowsMore; + this.definedKeywords = definedKeywords; this.hasKeywords = hasKeywords; } @@ -35,8 +37,12 @@ public boolean allowsMore() { return allowsMore; } - public boolean hasKeywords() { - return hasKeywords; - } + public boolean hasKeywords() { + return hasKeywords; + } + + public int getCountKeywords() { + return definedKeywords; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java b/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java index e3cfa24ae2c..5ec5ce6d43b 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java @@ -9,6 +9,16 @@ */ package org.jruby.truffle.runtime.methods; +import java.util.ArrayList; +import java.util.List; + +import org.jruby.ast.ArgsNode; +import org.jruby.ast.AssignableNode; +import org.jruby.ast.DAsgnNode; +import org.jruby.ast.KeywordArgNode; +import org.jruby.ast.LocalAsgnNode; +import org.jruby.ast.Node; + import com.oracle.truffle.api.source.SourceSection; import org.jruby.truffle.runtime.LexicalScope; @@ -24,6 +34,9 @@ public class SharedMethodInfo { private final boolean isBlock; private final org.jruby.ast.Node parseTree; private final boolean alwaysSplit; + + private final List keywordArguments; + private final int countKwArgs; public SharedMethodInfo(SourceSection sourceSection, LexicalScope lexicalScope, String name, boolean isBlock, org.jruby.ast.Node parseTree, boolean alwaysSplit) { assert sourceSection != null; @@ -35,7 +48,50 @@ public SharedMethodInfo(SourceSection sourceSection, LexicalScope lexicalScope, this.isBlock = isBlock; this.parseTree = parseTree; this.alwaysSplit = alwaysSplit; + this.keywordArguments = null; + this.countKwArgs = 0; } + + public SharedMethodInfo(SourceSection sourceSection, + LexicalScope lexicalScope, String name, boolean isBlock, + org.jruby.ast.Node parseTree, boolean alwaysSplit, ArgsNode argsNode) { + assert sourceSection != null; + assert name != null; + + this.sourceSection = sourceSection; + this.lexicalScope = lexicalScope; + this.name = name; + this.isBlock = isBlock; + this.parseTree = parseTree; + this.alwaysSplit = alwaysSplit; + + if (argsNode.hasKwargs()) { + keywordArguments = new ArrayList(); + if (argsNode.getKeywords() != null) { + for (Node node : argsNode.getKeywords().childNodes()) { + final KeywordArgNode kwarg = (KeywordArgNode) node; + final AssignableNode assignableNode = kwarg.getAssignable(); + + if (assignableNode instanceof LocalAsgnNode) { + keywordArguments.add(((LocalAsgnNode) assignableNode) + .getName()); + } else if (assignableNode instanceof DAsgnNode) { + keywordArguments.add(((DAsgnNode) assignableNode) + .getName()); + } else { + throw new UnsupportedOperationException( + "unsupported keyword arg " + node); + } + } + countKwArgs = keywordArguments.size(); + } else { + countKwArgs = 0; + } + } else { + keywordArguments = null; + countKwArgs = 0; + } + } public SourceSection getSourceSection() { return sourceSection; @@ -75,5 +131,9 @@ public String toString() { return builder.toString(); } + + public List getKeywordArguments() { + return keywordArguments; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java index 46d9624617c..9bb7785485b 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java @@ -1057,7 +1057,7 @@ public RubyNode visitDefsNode(org.jruby.ast.DefsNode node) { } protected RubyNode translateMethodDefinition(SourceSection sourceSection, RubyNode classNode, String methodName, org.jruby.ast.Node parseTree, org.jruby.ast.ArgsNode argsNode, org.jruby.ast.Node bodyNode) { - final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), methodName, false, parseTree, false); + final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), methodName, false, parseTree, false, argsNode); final TranslatorEnvironment newEnvironment = new TranslatorEnvironment( context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, sharedMethodInfo, methodName, false); diff --git a/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java index 81c4a2e4d6b..3a4ec35a852 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java @@ -65,6 +65,8 @@ private enum State { private int required; private int index; + private int kwIndex; + private int countKwArgs; private int indexFromEnd = 1; private State state; private boolean hasKeywordArguments; @@ -126,8 +128,15 @@ public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) { } if (hasKeywordArguments) { + kwIndex = 0; + countKwArgs = 0; + for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) { + countKwArgs++; + } + for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) { sequence.add(arg.accept(this)); + kwIndex++; } } @@ -146,7 +155,7 @@ public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) { public RubyNode visitKeywordRestArgNode(org.jruby.ast.KeywordRestArgNode node) { final SourceSection sourceSection = translate(node.getPosition()); - final RubyNode readNode = new ReadKeywordRestArgumentNode(context, sourceSection, required, excludedKeywords.toArray(new String[excludedKeywords.size()])); + final RubyNode readNode = new ReadKeywordRestArgumentNode(context, sourceSection, required, excludedKeywords.toArray(new String[excludedKeywords.size()]), -countKwArgs - 1); final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findOrAddFrameSlot(node.getName()); return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode); @@ -191,7 +200,7 @@ public RubyNode visitKeywordArgNode(org.jruby.ast.KeywordArgNode node) { excludedKeywords.add(name); - final RubyNode readNode = new ReadKeywordArgumentNode(context, sourceSection, required, name, defaultValue); + final RubyNode readNode = new ReadKeywordArgumentNode(context, sourceSection, required, name, defaultValue, kwIndex - countKwArgs); final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findOrAddFrameSlot(name); return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode); diff --git a/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java index 0586fb2c8b6..1252221b9f0 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java @@ -68,7 +68,7 @@ public RubyNode compileFunctionNode(SourceSection sourceSection, String methodNa */ if (isBlock && argsNode.childNodes().size() == 2 && argsNode.getRestArgNode() instanceof org.jruby.ast.UnnamedRestArgNode) { - arityForCheck = new Arity(arity.getRequired(), 0, false, false); + arityForCheck = new Arity(arity.getRequired(), 0, false, false, 0); } else { arityForCheck = arity; } @@ -236,7 +236,7 @@ public RubyNode compileFunctionNode(SourceSection sourceSection, String methodNa private static Arity getArity(org.jruby.ast.ArgsNode argsNode) { final int minimum = argsNode.getRequiredArgsCount(); final int maximum = argsNode.getMaxArgumentsCount(); - return new Arity(minimum, argsNode.getOptionalArgsCount(), maximum == -1, argsNode.hasKwargs()); + return new Arity(minimum, argsNode.getOptionalArgsCount(), maximum == -1, argsNode.hasKwargs(), argsNode.countKeywords()); } @Override From 27636a1b2069d4e1d02170755830b80745249cc7 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 12 Feb 2015 11:48:29 +0100 Subject: [PATCH 2/7] [Truffle] Fix failing Travis-CI tests --- .../src/main/java/org/jruby/ast/ArgsNode.java | 6 ++- .../nodes/core/CoreMethodNodeManager.java | 2 +- .../jruby/truffle/nodes/core/ModuleNodes.java | 4 +- .../dispatch/CachedBoxedDispatchNode.java | 47 ++++++++++++++----- .../arguments/UnknownArgumentErrorNode.java | 26 ++++++++++ .../jruby/truffle/runtime/methods/Arity.java | 8 +++- .../runtime/methods/SharedMethodInfo.java | 24 +++++++--- .../truffle/translator/BodyTranslator.java | 2 +- .../truffle/translator/MethodTranslator.java | 4 +- 9 files changed, 97 insertions(+), 26 deletions(-) create mode 100644 truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java diff --git a/core/src/main/java/org/jruby/ast/ArgsNode.java b/core/src/main/java/org/jruby/ast/ArgsNode.java index 65012ec3c6b..1c8c1201903 100644 --- a/core/src/main/java/org/jruby/ast/ArgsNode.java +++ b/core/src/main/java/org/jruby/ast/ArgsNode.java @@ -264,7 +264,11 @@ public ListNode getKeywords() { public KeywordRestArgNode getKeyRest() { return keyRest; - } + } + + public boolean hasKeyRest() { + return keyRest != null; + } public void checkArgCount(Ruby runtime, int argsLength) { Arity.checkArgumentCount(runtime, argsLength, requiredArgsCount, maxArgsCount, hasKwargs); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java index 48f4f79e936..8549cf8aae8 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/CoreMethodNodeManager.java @@ -142,7 +142,7 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails optional = methodDetails.getMethodAnnotation().optional(); } - final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false, 0); + final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false, false, 0); final List argumentsNodes = new ArrayList<>(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java index 465931e247c..803cc9d3242 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java @@ -270,7 +270,7 @@ public RubyNilClass attrReader(RubyModule module, Object[] args) { public static void attrReader(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) { CompilerDirectives.transferToInterpreter(); - final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false, 0)); + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false, false, 0)); final SelfNode self = new SelfNode(context, sourceSection); final ReadInstanceVariableNode readInstanceVariable = new ReadInstanceVariableNode(context, sourceSection, "@" + name, self, false); @@ -322,7 +322,7 @@ public RubyNilClass attrWriter(RubyModule module, Object[] args) { public static void attrWriter(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) { CompilerDirectives.transferToInterpreter(); - final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false, 0)); + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false, false, 0)); final SelfNode self = new SelfNode(context, sourceSection); final ReadPreArgumentNode readArgument = new ReadPreArgumentNode(context, sourceSection, 0, MissingArgumentBehaviour.RUNTIME_ERROR); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java index aa1bc8a0ce8..c077cb7c86f 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java @@ -25,6 +25,7 @@ import org.jruby.truffle.nodes.literal.ObjectLiteralNode; import org.jruby.truffle.nodes.methods.MarkerNode; import org.jruby.truffle.nodes.methods.arguments.OptionalKeywordArgMissingNode; +import org.jruby.truffle.nodes.methods.arguments.UnknownArgumentErrorNode; import org.jruby.truffle.runtime.DebugOperations; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; @@ -69,12 +70,32 @@ public CachedBoxedDispatchNode( isSplatted); } - public static RubyNode[] expandedArgumentNodes(RubyContext context, InternalMethod method, RubyNode[] argumentNodes) { - final RubyNode[] result; - - if (method != null && method.getSharedMethodInfo().getKeywordArguments() != null && ( - argumentNodes.length == 0 || argumentNodes[argumentNodes.length - 1] instanceof HashLiteralNode)) { - List kwargs = method.getSharedMethodInfo().getKeywordArguments(); + public static RubyNode[] expandedArgumentNodes(RubyContext context, + InternalMethod method, RubyNode[] argumentNodes, boolean isSplatted) { + final RubyNode[] result; + + boolean shouldExpand = true; + if (method == null + || method.getSharedMethodInfo().getKeywordArguments() == null) { + // no keyword arguments in method definition + shouldExpand = false; + } else if (argumentNodes.length != 0 + && !(argumentNodes[argumentNodes.length - 1] instanceof HashLiteralNode)) { + // last argument is not a Hash that could be expanded + shouldExpand = false; + } else if (method.getSharedMethodInfo().getArity() == null + || method.getSharedMethodInfo().getArity().getRequired() >= argumentNodes.length) { + shouldExpand = false; + } else if (isSplatted + || method.getSharedMethodInfo().getArity().allowsMore()) { + // TODO: make optimization work if splat arguments are involed + // the problem is that Markers and keyword args are used when + // reading splatted args + shouldExpand = false; + } + + if (shouldExpand) { + List kwargs = method.getSharedMethodInfo().getKeywordArguments(); int countArgNodes = argumentNodes.length + kwargs.size() + 1; if (argumentNodes.length == 0) { @@ -120,10 +141,14 @@ public static RubyNode[] expandedArgumentNodes(RubyContext context, InternalMeth } result[i++] = new MarkerNode(context, null); - if (restKeywordLabels.size() > 0) { - i = 0; - RubyNode[] keyValues = new RubyNode[2 * restKeywordLabels.size()]; - + if (restKeywordLabels.size() > 0 + && !method.getSharedMethodInfo().getArity().hasKeyRest()) { + result[firstMarker] = new UnknownArgumentErrorNode(context, null, restKeywordLabels.get(0)); + } else if (restKeywordLabels.size() > 0) { + i = 0; + RubyNode[] keyValues = new RubyNode[2 * restKeywordLabels + .size()]; + for (String label : restKeywordLabels) { for (int j = 0; j < hashNode.size(); j++) { final String argLabel = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); @@ -162,7 +187,7 @@ public CachedBoxedDispatchNode( DispatchAction dispatchAction, RubyNode[] argumentNodes, boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, expandedArgumentNodes(context, method, argumentNodes), isSplatted); + super(context, cachedName, next, indirect, dispatchAction, expandedArgumentNodes(context, method, argumentNodes, isSplatted), isSplatted); this.expectedClass = expectedClass; this.unmodifiedAssumption = unmodifiedAssumption; diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java new file mode 100644 index 00000000000..efcecad0343 --- /dev/null +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java @@ -0,0 +1,26 @@ +package org.jruby.truffle.nodes.methods.arguments; + +import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.runtime.RubyContext; +import org.jruby.truffle.runtime.control.RaiseException; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; + +public class UnknownArgumentErrorNode extends RubyNode { + + private final String label; + + public UnknownArgumentErrorNode(RubyContext context, + SourceSection sourceSection, String label) { + super(context, sourceSection); + this.label = label; + } + + @Override + public Object execute(VirtualFrame frame) { + throw new RaiseException(getContext().getCoreLibrary().argumentError( + "unknown keyword: " + label, this)); + } + +} \ No newline at end of file diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java b/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java index 7b016c7923b..9483ebf0827 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java @@ -16,13 +16,15 @@ public class Arity { private final boolean allowsMore; private final int definedKeywords; private final boolean hasKeywords; + private final boolean hasKeyRest; - public Arity(int required, int optional, boolean allowsMore, boolean hasKeywords, int definedKeywords) { + public Arity(int required, int optional, boolean allowsMore, boolean hasKeywords, boolean hasKeyRest, int definedKeywords) { this.required = required; this.optional = optional; this.allowsMore = allowsMore; this.definedKeywords = definedKeywords; this.hasKeywords = hasKeywords; + this.hasKeyRest = hasKeyRest; } public int getRequired() { @@ -45,4 +47,8 @@ public int getCountKeywords() { return definedKeywords; } + public boolean hasKeyRest() { + return hasKeyRest; + } + } diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java b/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java index 5ec5ce6d43b..8fcb75eb780 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java @@ -36,7 +36,7 @@ public class SharedMethodInfo { private final boolean alwaysSplit; private final List keywordArguments; - private final int countKwArgs; + private final Arity arity; public SharedMethodInfo(SourceSection sourceSection, LexicalScope lexicalScope, String name, boolean isBlock, org.jruby.ast.Node parseTree, boolean alwaysSplit) { assert sourceSection != null; @@ -49,7 +49,7 @@ public SharedMethodInfo(SourceSection sourceSection, LexicalScope lexicalScope, this.parseTree = parseTree; this.alwaysSplit = alwaysSplit; this.keywordArguments = null; - this.countKwArgs = 0; + this.arity = null; } public SharedMethodInfo(SourceSection sourceSection, @@ -83,17 +83,27 @@ public SharedMethodInfo(SourceSection sourceSection, "unsupported keyword arg " + node); } } - countKwArgs = keywordArguments.size(); - } else { - countKwArgs = 0; } } else { keywordArguments = null; - countKwArgs = 0; } + + this.arity = getArity(argsNode); } - public SourceSection getSourceSection() { + // TODO: copied from MethodTranslator + private static Arity getArity(org.jruby.ast.ArgsNode argsNode) { + final int minimum = argsNode.getRequiredArgsCount(); + final int maximum = argsNode.getMaxArgumentsCount(); + return new Arity(minimum, argsNode.getOptionalArgsCount(), + maximum == -1, argsNode.hasKwargs(), argsNode.hasKeyRest(), argsNode.countKeywords()); + } + + public Arity getArity() { + return arity; + } + + public SourceSection getSourceSection() { return sourceSection; } diff --git a/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java index 69a56c87359..26abb56addd 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/BodyTranslator.java @@ -2611,7 +2611,7 @@ public RubyNode visitLambdaNode(org.jruby.ast.LambdaNode node) { final SourceSection sourceSection = translate(node.getPosition()); // TODO(cs): code copied and modified from visitIterNode - extract common - final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), "(lambda)", true, node, false); + final SharedMethodInfo sharedMethodInfo = new SharedMethodInfo(sourceSection, environment.getLexicalScope(), "(lambda)", true, node, false, node.getArgs()); final TranslatorEnvironment newEnvironment = new TranslatorEnvironment( context, environment, environment.getParser(), environment.getReturnID(), false, false, sharedMethodInfo, sharedMethodInfo.getName(), true); diff --git a/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java index 1252221b9f0..33999772973 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java @@ -68,7 +68,7 @@ public RubyNode compileFunctionNode(SourceSection sourceSection, String methodNa */ if (isBlock && argsNode.childNodes().size() == 2 && argsNode.getRestArgNode() instanceof org.jruby.ast.UnnamedRestArgNode) { - arityForCheck = new Arity(arity.getRequired(), 0, false, false, 0); + arityForCheck = new Arity(arity.getRequired(), 0, false, false, false, 0); } else { arityForCheck = arity; } @@ -236,7 +236,7 @@ public RubyNode compileFunctionNode(SourceSection sourceSection, String methodNa private static Arity getArity(org.jruby.ast.ArgsNode argsNode) { final int minimum = argsNode.getRequiredArgsCount(); final int maximum = argsNode.getMaxArgumentsCount(); - return new Arity(minimum, argsNode.getOptionalArgsCount(), maximum == -1, argsNode.hasKwargs(), argsNode.countKeywords()); + return new Arity(minimum, argsNode.getOptionalArgsCount(), maximum == -1, argsNode.hasKwargs(), argsNode.hasKeyRest(), argsNode.countKeywords()); } @Override From 922173777f8e811d79f331a0bd959e0f9c862906 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 12 Feb 2015 15:06:06 +0100 Subject: [PATCH 3/7] Fix indentation [ci skip] --- .../src/main/java/org/jruby/ast/ArgsNode.java | 30 +-- .../org/jruby/truffle/TruffleBridgeImpl.java | 2 +- .../dispatch/CachedBooleanDispatchNode.java | 8 +- .../dispatch/CachedBoxedDispatchNode.java | 174 +++++++++--------- .../nodes/dispatch/CachedDispatchNode.java | 7 +- .../nodes/dispatch/DispatchHeadNode.java | 6 +- .../nodes/literal/HashLiteralNode.java | 22 +-- .../truffle/nodes/methods/MarkerNode.java | 18 +- .../methods/arguments/CheckArityNode.java | 10 +- .../OptionalKeywordArgMissingNode.java | 24 +-- .../arguments/ReadKeywordArgumentNode.java | 30 +-- .../ReadKeywordRestArgumentNode.java | 34 ++-- .../arguments/UnknownArgumentErrorNode.java | 22 +-- .../jruby/truffle/runtime/RubyArguments.java | 32 ++-- .../jruby/truffle/runtime/methods/Arity.java | 18 +- .../runtime/methods/SharedMethodInfo.java | 110 +++++------ .../translator/LoadArgumentsTranslator.java | 12 +- 17 files changed, 278 insertions(+), 281 deletions(-) diff --git a/core/src/main/java/org/jruby/ast/ArgsNode.java b/core/src/main/java/org/jruby/ast/ArgsNode.java index 1c8c1201903..72fe931f8eb 100644 --- a/core/src/main/java/org/jruby/ast/ArgsNode.java +++ b/core/src/main/java/org/jruby/ast/ArgsNode.java @@ -155,17 +155,17 @@ public boolean hasKwargs() { return hasKwargs; } - public int countKeywords() { - if (hasKwargs) { - if (keywords == null) { - // Rest keyword argument - return 0; - } - return keywords.size(); - } else { - return 0; - } - } + public int countKeywords() { + if (hasKwargs) { + if (keywords == null) { + // Rest keyword argument + return 0; + } + return keywords.size(); + } else { + return 0; + } + } protected boolean hasMasgnArgs() { if (preCount > 0) for (Node node : pre.childNodes()) { @@ -264,11 +264,11 @@ public ListNode getKeywords() { public KeywordRestArgNode getKeyRest() { return keyRest; - } + } - public boolean hasKeyRest() { - return keyRest != null; - } + public boolean hasKeyRest() { + return keyRest != null; + } public void checkArgCount(Ruby runtime, int argsLength) { Arity.checkArgumentCount(runtime, argsLength, requiredArgsCount, maxArgsCount, hasKwargs); diff --git a/truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java b/truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java index f04d3768295..19d2d3320ad 100644 --- a/truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java +++ b/truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java @@ -147,7 +147,7 @@ public void init() { @Override public Object execute(final Object self, final org.jruby.ast.RootNode rootNode) { - return execute(TranslatorDriver.ParserContext.TOP_LEVEL, self, null, rootNode); + return execute(TranslatorDriver.ParserContext.TOP_LEVEL, self, null, rootNode); } public Object execute(final TranslatorDriver.ParserContext parserContext, final Object self, final MaterializedFrame parentFrame, final org.jruby.ast.RootNode rootNode) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java index e4846f0c43a..aed3d39ceee 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java @@ -53,10 +53,10 @@ public CachedBooleanDispatchNode( Object trueValue, InternalMethod trueMethod, boolean indirect, - DispatchAction dispatchAction, RubyNode[] argumentNodes, - boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, - argumentNodes, isSplatted); + DispatchAction dispatchAction, RubyNode[] argumentNodes, + boolean isSplatted) { + super(context, cachedName, next, indirect, dispatchAction, + argumentNodes, isSplatted); this.falseUnmodifiedAssumption = falseUnmodifiedAssumption; this.falseMethod = falseMethod; diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java index c077cb7c86f..5c248ce8fe1 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java @@ -70,99 +70,99 @@ public CachedBoxedDispatchNode( isSplatted); } - public static RubyNode[] expandedArgumentNodes(RubyContext context, - InternalMethod method, RubyNode[] argumentNodes, boolean isSplatted) { - final RubyNode[] result; + public static RubyNode[] expandedArgumentNodes(RubyContext context, + InternalMethod method, RubyNode[] argumentNodes, boolean isSplatted) { + final RubyNode[] result; - boolean shouldExpand = true; - if (method == null - || method.getSharedMethodInfo().getKeywordArguments() == null) { - // no keyword arguments in method definition - shouldExpand = false; - } else if (argumentNodes.length != 0 - && !(argumentNodes[argumentNodes.length - 1] instanceof HashLiteralNode)) { - // last argument is not a Hash that could be expanded - shouldExpand = false; - } else if (method.getSharedMethodInfo().getArity() == null - || method.getSharedMethodInfo().getArity().getRequired() >= argumentNodes.length) { - shouldExpand = false; - } else if (isSplatted - || method.getSharedMethodInfo().getArity().allowsMore()) { - // TODO: make optimization work if splat arguments are involed - // the problem is that Markers and keyword args are used when - // reading splatted args - shouldExpand = false; - } + boolean shouldExpand = true; + if (method == null + || method.getSharedMethodInfo().getKeywordArguments() == null) { + // no keyword arguments in method definition + shouldExpand = false; + } else if (argumentNodes.length != 0 + && !(argumentNodes[argumentNodes.length - 1] instanceof HashLiteralNode)) { + // last argument is not a Hash that could be expanded + shouldExpand = false; + } else if (method.getSharedMethodInfo().getArity() == null + || method.getSharedMethodInfo().getArity().getRequired() >= argumentNodes.length) { + shouldExpand = false; + } else if (isSplatted + || method.getSharedMethodInfo().getArity().allowsMore()) { + // TODO: make optimization work if splat arguments are involed + // the problem is that Markers and keyword args are used when + // reading splatted args + shouldExpand = false; + } - if (shouldExpand) { - List kwargs = method.getSharedMethodInfo().getKeywordArguments(); + if (shouldExpand) { + List kwargs = method.getSharedMethodInfo().getKeywordArguments(); + + int countArgNodes = argumentNodes.length + kwargs.size() + 1; + if (argumentNodes.length == 0) { + countArgNodes++; + } + + result = new RubyNode[countArgNodes]; + int i; + + for (i = 0; i < argumentNodes.length - 1; ++i) { + result[i] = argumentNodes[i]; + } - int countArgNodes = argumentNodes.length + kwargs.size() + 1; - if (argumentNodes.length == 0) { - countArgNodes++; - } - - result = new RubyNode[countArgNodes]; - int i; - - for (i = 0; i < argumentNodes.length - 1; ++i) { - result[i] = argumentNodes[i]; - } - - int firstMarker = i++; - result[firstMarker] = new MarkerNode(context, null); + int firstMarker = i++; + result[firstMarker] = new MarkerNode(context, null); - HashLiteralNode hashNode; - if (argumentNodes.length > 0) { - hashNode = (HashLiteralNode) argumentNodes[argumentNodes.length - 1]; - } else { - hashNode = HashLiteralNode.create(context, null, - new RubyNode[0]); - } + HashLiteralNode hashNode; + if (argumentNodes.length > 0) { + hashNode = (HashLiteralNode) argumentNodes[argumentNodes.length - 1]; + } else { + hashNode = HashLiteralNode.create(context, null, + new RubyNode[0]); + } - List restKeywordLabels = new ArrayList(); - for (int j = 0; j < hashNode.size(); j++) { - final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); - restKeywordLabels.add(label); - } + List restKeywordLabels = new ArrayList(); + for (int j = 0; j < hashNode.size(); j++) { + final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + restKeywordLabels.add(label); + } - for (String kwarg : kwargs) { - result[i] = new OptionalKeywordArgMissingNode(context, null); - for (int j = 0; j < hashNode.size(); j++) { - final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); - - if (label.equals(kwarg)) { - result[i] = hashNode.getValue(j); - restKeywordLabels.remove(label); - break; - } - } - i++; - } - result[i++] = new MarkerNode(context, null); + for (String kwarg : kwargs) { + result[i] = new OptionalKeywordArgMissingNode(context, null); + for (int j = 0; j < hashNode.size(); j++) { + final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + + if (label.equals(kwarg)) { + result[i] = hashNode.getValue(j); + restKeywordLabels.remove(label); + break; + } + } + i++; + } + result[i++] = new MarkerNode(context, null); - if (restKeywordLabels.size() > 0 - && !method.getSharedMethodInfo().getArity().hasKeyRest()) { - result[firstMarker] = new UnknownArgumentErrorNode(context, null, restKeywordLabels.get(0)); - } else if (restKeywordLabels.size() > 0) { - i = 0; - RubyNode[] keyValues = new RubyNode[2 * restKeywordLabels - .size()]; + if (restKeywordLabels.size() > 0 + && !method.getSharedMethodInfo().getArity().hasKeyRest()) { + result[firstMarker] = new UnknownArgumentErrorNode(context, null, restKeywordLabels.get(0)); + } else if (restKeywordLabels.size() > 0) { + i = 0; + RubyNode[] keyValues = new RubyNode[2 * restKeywordLabels + .size()]; - for (String label : restKeywordLabels) { - for (int j = 0; j < hashNode.size(); j++) { - final String argLabel = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); - - if (argLabel.equals(label)) { - keyValues[i++] = hashNode.getKey(j); - keyValues[i++] = hashNode.getValue(j); - } - } - } - - HashLiteralNode restHash = HashLiteralNode.create(context, null, keyValues); - result[firstMarker] = restHash; - } + for (String label : restKeywordLabels) { + for (int j = 0; j < hashNode.size(); j++) { + final String argLabel = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + + if (argLabel.equals(label)) { + keyValues[i++] = hashNode.getKey(j); + keyValues[i++] = hashNode.getValue(j); + } + } + } + + HashLiteralNode restHash = HashLiteralNode.create(context, null, keyValues); + result[firstMarker] = restHash; + } } else { @@ -252,8 +252,8 @@ public Object executeDispatch( CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); } else { - Object args = executeArguments(frame, argumentsObjects); - return callNode.call( + Object args = executeArguments(frame, argumentsObjects); + return callNode.call( frame, RubyArguments.pack( method, diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java index e38fc67940c..c9ca0b88d5f 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java @@ -90,9 +90,8 @@ public boolean isIndirect() { return indirect; } - protected RubyNode[] argumentsWithDecodedKwargsHash(RubyNode[] arguments, - InternalMethod method) { - return null; - } + protected RubyNode[] argumentsWithDecodedKwargsHash(RubyNode[] arguments, InternalMethod method) { + return null; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java index 1b7847db3d1..774b8136ebf 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java @@ -85,8 +85,8 @@ public void forceUncached() { first.replace(new UncachedDispatchNode(context, ignoreVisibility, dispatchAction, argumentNodes, isSplatted)); } - public RubyNode[] getArgumentNodes() { - return argumentNodes; - } + public RubyNode[] getArgumentNodes() { + return argumentNodes; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java index c240ba21386..f1dd097a346 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java @@ -34,20 +34,20 @@ protected HashLiteralNode(RubyContext context, SourceSection sourceSection, Ruby assert keyValues.length % 2 == 0; this.keyValues = keyValues; dupNode = DispatchHeadNodeFactory.createMethodCall(context); - freezeNode = DispatchHeadNodeFactory.createMethodCall(context); - } + freezeNode = DispatchHeadNodeFactory.createMethodCall(context); + } - public int size() { - return keyValues.length / 2; - } + public int size() { + return keyValues.length / 2; + } - public RubyNode getKey(int index) { - return keyValues[2 * index]; - } + public RubyNode getKey(int index) { + return keyValues[2 * index]; + } - public RubyNode getValue(int index) { - return keyValues[2 * index + 1]; - } + public RubyNode getValue(int index) { + return keyValues[2 * index + 1]; + } public static HashLiteralNode create(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) { if (keyValues.length == 0) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java index eb36db410b5..18be40d6cc3 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java @@ -8,17 +8,15 @@ public class MarkerNode extends RubyNode { - public MarkerNode(RubyContext context, SourceSection sourceSection) { - super(context, sourceSection); - } + public MarkerNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } - @Override - public Object execute(VirtualFrame frame) { - return new Marker(); - } + @Override + public Object execute(VirtualFrame frame) { + return new Marker(); + } - public static class Marker { - - } + public static class Marker {} } \ No newline at end of file diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java index 9bcb2ea0dcb..ae4ff90e93d 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/CheckArityNode.java @@ -49,11 +49,11 @@ public void executeVoid(VirtualFrame frame) { //TODO (MS): Check merge if (RubyArguments.isKwOptimized(frame.getArguments())) { - given = RubyArguments.getUserArgumentsCount(frame.getArguments()) - - arity.getCountKeywords() - 2; - } else { - given = RubyArguments.getUserArgumentsCount(frame.getArguments()); - } + given = RubyArguments.getUserArgumentsCount(frame.getArguments()) + - arity.getCountKeywords() - 2; + } else { + given = RubyArguments.getUserArgumentsCount(frame.getArguments()); + } if (arity.hasKeywords()) { keywordArguments = RubyArguments.getUserKeywordsHash(frameArguments, arity.getRequired()); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java index 289924f8307..ac042b3c846 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java @@ -8,18 +8,18 @@ public class OptionalKeywordArgMissingNode extends RubyNode { - public OptionalKeywordArgMissingNode(RubyContext context, - SourceSection sourceSection) { - super(context, sourceSection); - } + public OptionalKeywordArgMissingNode(RubyContext context, + SourceSection sourceSection) { + super(context, sourceSection); + } - public static class OptionalKeywordArgMissing { - - } + public static class OptionalKeywordArgMissing { + + } - @Override - public Object execute(VirtualFrame frame) { - return new OptionalKeywordArgMissing(); - } - + @Override + public Object execute(VirtualFrame frame) { + return new OptionalKeywordArgMissing(); + } + } \ No newline at end of file diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java index 11b88c20f40..7f5b9c87681 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordArgumentNode.java @@ -38,22 +38,22 @@ public ReadKeywordArgumentNode(RubyContext context, SourceSection sourceSection, } @Override - public Object execute(VirtualFrame frame) { - if (RubyArguments.isKwOptimized(frame.getArguments())) { - Object kwarg = argumentValueProfile - .profile(RubyArguments.getOptimizedKeywordArgument( - frame.getArguments(), kwIndex)); + public Object execute(VirtualFrame frame) { + if (RubyArguments.isKwOptimized(frame.getArguments())) { + Object kwarg = argumentValueProfile + .profile(RubyArguments.getOptimizedKeywordArgument( + frame.getArguments(), kwIndex)); + + if (kwarg instanceof OptionalKeywordArgMissingNode.OptionalKeywordArgMissing) { + return defaultValue.execute(frame); + } else { + return kwarg; + } + } else { + return lookupKeywordInHash(frame); + } + } - if (kwarg instanceof OptionalKeywordArgMissingNode.OptionalKeywordArgMissing) { - return defaultValue.execute(frame); - } else { - return kwarg; - } - } else { - return lookupKeywordInHash(frame); - } - } - public Object lookupKeywordInHash(VirtualFrame frame) { notDesignedForCompilation(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java index 2e83b454c4b..eec5b36a017 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/ReadKeywordRestArgumentNode.java @@ -37,25 +37,25 @@ public ReadKeywordRestArgumentNode(RubyContext context, SourceSection sourceSect } @Override - public Object execute(VirtualFrame frame) { - if (RubyArguments.isKwOptimized(frame.getArguments())) { - Object restHash = RubyArguments.getOptimizedKeywordArgument( - frame.getArguments(), kwIndex); + public Object execute(VirtualFrame frame) { + if (RubyArguments.isKwOptimized(frame.getArguments())) { + Object restHash = RubyArguments.getOptimizedKeywordArgument( + frame.getArguments(), kwIndex); - if (restHash instanceof MarkerNode.Marker) { - // no rest keyword args hash passed - return HashLiteralNode.create(getContext(), null, - new RubyNode[0]).execute(frame); - } else { - return restHash; - } - } else { - return lookupRestKeywordArgumentHash(frame); - } - } + if (restHash instanceof MarkerNode.Marker) { + // no rest keyword args hash passed + return HashLiteralNode.create(getContext(), null, + new RubyNode[0]).execute(frame); + } else { + return restHash; + } + } else { + return lookupRestKeywordArgumentHash(frame); + } + } - private Object lookupRestKeywordArgumentHash(VirtualFrame frame) { - notDesignedForCompilation(); + private Object lookupRestKeywordArgumentHash(VirtualFrame frame) { + notDesignedForCompilation(); final RubyHash hash = RubyArguments.getUserKeywordsHash(frame.getArguments(), minimum); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java index efcecad0343..4cad7e0f293 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/UnknownArgumentErrorNode.java @@ -9,18 +9,18 @@ public class UnknownArgumentErrorNode extends RubyNode { - private final String label; + private final String label; - public UnknownArgumentErrorNode(RubyContext context, - SourceSection sourceSection, String label) { - super(context, sourceSection); - this.label = label; - } + public UnknownArgumentErrorNode(RubyContext context, + SourceSection sourceSection, String label) { + super(context, sourceSection); + this.label = label; + } - @Override - public Object execute(VirtualFrame frame) { - throw new RaiseException(getContext().getCoreLibrary().argumentError( - "unknown keyword: " + label, this)); - } + @Override + public Object execute(VirtualFrame frame) { + throw new RaiseException(getContext().getCoreLibrary().argumentError( + "unknown keyword: " + label, this)); + } } \ No newline at end of file diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java b/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java index 556f6fb051a..1ca90660b3f 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/RubyArguments.java @@ -42,14 +42,14 @@ public static Object[] pack(InternalMethod method, MaterializedFrame declaration return packed; } - public static Object getOptimizedKeywordArgument(Object[] arguments, - int index) { - return arguments[arguments.length - 1 + index]; - } + public static Object getOptimizedKeywordArgument(Object[] arguments, + int index) { + return arguments[arguments.length - 1 + index]; + } - public static boolean isKwOptimized(Object[] arguments) { - return arguments[arguments.length - 1] instanceof MarkerNode.Marker; - } + public static boolean isKwOptimized(Object[] arguments) { + return arguments[arguments.length - 1] instanceof MarkerNode.Marker; + } public static InternalMethod getMethod(Object[] arguments) { return (InternalMethod) arguments[METHOD_INDEX]; @@ -90,15 +90,15 @@ public static int getUserArgumentsCount(Object[] internalArguments) { return internalArguments.length - RUNTIME_ARGUMENT_COUNT; } - public static int getNamedUserArgumentsCount(Object[] internalArguments) { - if (isKwOptimized(internalArguments)) { - return getUserArgumentsCount(internalArguments) - - getMethod(internalArguments).getSharedMethodInfo() - .getKeywordArguments().size() - 1; - } else { - return getUserArgumentsCount(internalArguments); - } - } + public static int getNamedUserArgumentsCount(Object[] internalArguments) { + if (isKwOptimized(internalArguments)) { + return getUserArgumentsCount(internalArguments) + - getMethod(internalArguments).getSharedMethodInfo() + .getKeywordArguments().size() - 1; + } else { + return getUserArgumentsCount(internalArguments); + } + } public static Object getUserArgument(Object[] internalArguments, int index) { return internalArguments[RUNTIME_ARGUMENT_COUNT + index]; diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java b/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java index 9483ebf0827..6523b6fb053 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/methods/Arity.java @@ -39,16 +39,16 @@ public boolean allowsMore() { return allowsMore; } - public boolean hasKeywords() { - return hasKeywords; - } + public boolean hasKeywords() { + return hasKeywords; + } - public int getCountKeywords() { - return definedKeywords; - } + public int getCountKeywords() { + return definedKeywords; + } - public boolean hasKeyRest() { - return hasKeyRest; - } + public boolean hasKeyRest() { + return hasKeyRest; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java b/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java index 8fcb75eb780..23ce17d4052 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/methods/SharedMethodInfo.java @@ -52,58 +52,58 @@ public SharedMethodInfo(SourceSection sourceSection, LexicalScope lexicalScope, this.arity = null; } - public SharedMethodInfo(SourceSection sourceSection, - LexicalScope lexicalScope, String name, boolean isBlock, - org.jruby.ast.Node parseTree, boolean alwaysSplit, ArgsNode argsNode) { - assert sourceSection != null; - assert name != null; - - this.sourceSection = sourceSection; - this.lexicalScope = lexicalScope; - this.name = name; - this.isBlock = isBlock; - this.parseTree = parseTree; - this.alwaysSplit = alwaysSplit; - - if (argsNode.hasKwargs()) { - keywordArguments = new ArrayList(); - if (argsNode.getKeywords() != null) { - for (Node node : argsNode.getKeywords().childNodes()) { - final KeywordArgNode kwarg = (KeywordArgNode) node; - final AssignableNode assignableNode = kwarg.getAssignable(); - - if (assignableNode instanceof LocalAsgnNode) { - keywordArguments.add(((LocalAsgnNode) assignableNode) - .getName()); - } else if (assignableNode instanceof DAsgnNode) { - keywordArguments.add(((DAsgnNode) assignableNode) - .getName()); - } else { - throw new UnsupportedOperationException( - "unsupported keyword arg " + node); - } - } - } - } else { - keywordArguments = null; - } - - this.arity = getArity(argsNode); - } - - // TODO: copied from MethodTranslator - private static Arity getArity(org.jruby.ast.ArgsNode argsNode) { - final int minimum = argsNode.getRequiredArgsCount(); - final int maximum = argsNode.getMaxArgumentsCount(); - return new Arity(minimum, argsNode.getOptionalArgsCount(), - maximum == -1, argsNode.hasKwargs(), argsNode.hasKeyRest(), argsNode.countKeywords()); - } - - public Arity getArity() { - return arity; - } - - public SourceSection getSourceSection() { + public SharedMethodInfo(SourceSection sourceSection, + LexicalScope lexicalScope, String name, boolean isBlock, + org.jruby.ast.Node parseTree, boolean alwaysSplit, ArgsNode argsNode) { + assert sourceSection != null; + assert name != null; + + this.sourceSection = sourceSection; + this.lexicalScope = lexicalScope; + this.name = name; + this.isBlock = isBlock; + this.parseTree = parseTree; + this.alwaysSplit = alwaysSplit; + + if (argsNode.hasKwargs()) { + keywordArguments = new ArrayList(); + if (argsNode.getKeywords() != null) { + for (Node node : argsNode.getKeywords().childNodes()) { + final KeywordArgNode kwarg = (KeywordArgNode) node; + final AssignableNode assignableNode = kwarg.getAssignable(); + + if (assignableNode instanceof LocalAsgnNode) { + keywordArguments.add(((LocalAsgnNode) assignableNode) + .getName()); + } else if (assignableNode instanceof DAsgnNode) { + keywordArguments.add(((DAsgnNode) assignableNode) + .getName()); + } else { + throw new UnsupportedOperationException( + "unsupported keyword arg " + node); + } + } + } + } else { + keywordArguments = null; + } + + this.arity = getArity(argsNode); + } + + // TODO: copied from MethodTranslator + private static Arity getArity(org.jruby.ast.ArgsNode argsNode) { + final int minimum = argsNode.getRequiredArgsCount(); + final int maximum = argsNode.getMaxArgumentsCount(); + return new Arity(minimum, argsNode.getOptionalArgsCount(), + maximum == -1, argsNode.hasKwargs(), argsNode.hasKeyRest(), argsNode.countKeywords()); + } + + public Arity getArity() { + return arity; + } + + public SourceSection getSourceSection() { return sourceSection; } @@ -142,8 +142,8 @@ public String toString() { return builder.toString(); } - public List getKeywordArguments() { - return keywordArguments; - } + public List getKeywordArguments() { + return keywordArguments; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java index 3a4ec35a852..1550ae5dc5e 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/LoadArgumentsTranslator.java @@ -128,12 +128,12 @@ public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) { } if (hasKeywordArguments) { - kwIndex = 0; - countKwArgs = 0; - for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) { - countKwArgs++; - } - + kwIndex = 0; + countKwArgs = 0; + for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) { + countKwArgs++; + } + for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) { sequence.add(arg.accept(this)); kwIndex++; From 69ce3074e2c94a75abd946229aaf562a6d78d8d6 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Fri, 13 Feb 2015 12:05:07 +0100 Subject: [PATCH 4/7] [Truffle] Execute block after arguments --- .../jruby/truffle/nodes/ReadConstantNode.java | 2 +- .../org/jruby/truffle/nodes/RubyCallNode.java | 30 +++++-------------- .../org/jruby/truffle/nodes/core/IONodes.java | 4 --- .../jruby/truffle/nodes/core/KernelNodes.java | 8 ++--- .../jruby/truffle/nodes/core/ModuleNodes.java | 2 +- .../dispatch/CachedBooleanDispatchNode.java | 19 ++++++++---- .../dispatch/CachedBoxedDispatchNode.java | 15 ++++++---- .../CachedBoxedMethodMissingDispatchNode.java | 8 +++-- .../CachedBoxedReturnMissingDispatchNode.java | 4 ++- .../CachedBoxedSymbolDispatchNode.java | 11 +++++-- .../nodes/dispatch/CachedDispatchNode.java | 4 ++- .../dispatch/CachedUnboxedDispatchNode.java | 11 +++++-- .../nodes/dispatch/CallDispatchHeadNode.java | 5 ++-- .../nodes/dispatch/DispatchHeadNode.java | 13 ++++++-- .../dispatch/DispatchHeadNodeFactory.java | 10 ++++++- .../truffle/nodes/dispatch/DispatchNode.java | 18 ++++++++++- .../dispatch/DoesRespondDispatchHeadNode.java | 5 ++-- .../nodes/dispatch/UncachedDispatchNode.java | 12 ++++++-- .../dispatch/UnresolvedDispatchNode.java | 28 +++++++++-------- .../nodes/respondto/RespondToNode.java | 2 +- .../nodes/rubinius/FixnumPrimitiveNodes.java | 2 +- .../truffle/runtime/core/CoreLibrary.java | 3 -- .../truffle/translator/MethodTranslator.java | 1 - 23 files changed, 132 insertions(+), 85 deletions(-) diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java index a0b7f5958e4..64a5d32f39f 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java @@ -32,7 +32,7 @@ public ReadConstantNode(RubyContext context, SourceSection sourceSection, String super(context, sourceSection); this.name = name; this.receiver = receiver; - dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, lexicalScope, DispatchAction.READ_CONSTANT, null, false); + dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, lexicalScope, DispatchAction.READ_CONSTANT, null, null, false); } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java index 5b3a24c6f5e..21f1469a733 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java @@ -20,7 +20,6 @@ import org.jruby.truffle.runtime.ModuleOperations; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; -import org.jruby.truffle.runtime.core.RubyProc; import org.jruby.truffle.runtime.methods.InternalMethod; public class RubyCallNode extends RubyNode { @@ -28,8 +27,6 @@ public class RubyCallNode extends RubyNode { private final String methodName; @Child private RubyNode receiver; - @Child private ProcOrNullNode block; - private final RubyNode[] arguments; private final boolean isVCall; @@ -52,19 +49,16 @@ public RubyCallNode(RubyContext context, SourceSection section, String methodNam super(context, section); this.methodName = methodName; - this.receiver = receiver; - if (block == null) { - this.block = null; - } else { - this.block = ProcOrNullNodeFactory.create(context, section, block); + ProcOrNullNode blockNode = null; + if (block != null) { + blockNode = ProcOrNullNodeFactory.create(context, section, block); } - this.arguments = arguments; this.isVCall = isVCall; - dispatchHead = DispatchHeadNodeFactory.createMethodCall(context, ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING, arguments, isSplatted); + dispatchHead = DispatchHeadNodeFactory.createMethodCall(context, ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING, arguments, blockNode, isSplatted); respondToMissing = DispatchHeadNodeFactory.createMethodCall(context, true, MissingBehavior.RETURN_MISSING); respondToMissingCast = BooleanCastNodeFactory.create(context, section, null); @@ -74,19 +68,9 @@ public RubyCallNode(RubyContext context, SourceSection section, String methodNam @Override public Object execute(VirtualFrame frame) { final Object receiverObject = receiver.execute(frame); - final RubyProc blockObject = executeBlock(frame); - - return dispatchHead.call(frame, receiverObject, methodName, blockObject, (Object[]) null); - } - - private RubyProc executeBlock(VirtualFrame frame) { - if (block != null) { - return block.executeRubyProc(frame); - } else { - return null; - } + return dispatchHead.call(frame, receiverObject, methodName, null, (Object[]) null); } - + @Override public Object isDefined(VirtualFrame frame) { notDesignedForCompilation(); @@ -95,7 +79,7 @@ public Object isDefined(VirtualFrame frame) { return getContext().getCoreLibrary().getNilObject(); } - for (RubyNode argument : arguments) { + for (RubyNode argument : dispatchHead.getArgumentNodes()) { if (argument.isDefined(frame) == getContext().getCoreLibrary().getNilObject()) { return getContext().getCoreLibrary().getNilObject(); } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/IONodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/IONodes.java index 5c5104de487..13b1e621833 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/IONodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/IONodes.java @@ -10,14 +10,10 @@ package org.jruby.truffle.nodes.core; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.jruby.truffle.runtime.RubyContext; -import org.jruby.truffle.runtime.UndefinedPlaceholder; import org.jruby.truffle.runtime.core.RubyArray; -import org.jruby.truffle.runtime.core.RubyFile; -import org.jruby.truffle.runtime.core.RubyProc; import org.jruby.truffle.runtime.core.RubyString; import java.io.BufferedReader; diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java index f0538205151..6c5cb0518ba 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java @@ -1060,8 +1060,8 @@ public abstract static class IntegerNode extends CoreMethodNode { public IntegerNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - toIntRespondTo = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, false); - toInt = new CallDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, false); + toIntRespondTo = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, null, false); + toInt = new CallDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, null, false); } public IntegerNode(IntegerNode prev) { @@ -1680,8 +1680,8 @@ public abstract static class RespondToNode extends CoreMethodNode { public RespondToNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, false); - dispatchIgnoreVisibility = new DoesRespondDispatchHeadNode(context, true, false, MissingBehavior.RETURN_MISSING, null, null, false); + dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, null, false); + dispatchIgnoreVisibility = new DoesRespondDispatchHeadNode(context, true, false, MissingBehavior.RETURN_MISSING, null, null, null, false); if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) { dispatch.forceUncached(); diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java index 803cc9d3242..b926bef7a9e 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java @@ -635,7 +635,7 @@ public abstract static class ConstGetNode extends CoreMethodNode { public ConstGetNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, null, DispatchAction.READ_CONSTANT, null, false); + dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, null, DispatchAction.READ_CONSTANT, null, null, false); } public ConstGetNode(ConstGetNode prev) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java index aed3d39ceee..890b3d2842a 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBooleanDispatchNode.java @@ -19,6 +19,7 @@ import com.oracle.truffle.api.utilities.BranchProfile; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyProc; @@ -53,10 +54,12 @@ public CachedBooleanDispatchNode( Object trueValue, InternalMethod trueMethod, boolean indirect, - DispatchAction dispatchAction, RubyNode[] argumentNodes, + DispatchAction dispatchAction, + RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { super(context, cachedName, next, indirect, dispatchAction, - argumentNodes, isSplatted); + argumentNodes, block, isSplatted); this.falseUnmodifiedAssumption = falseUnmodifiedAssumption; this.falseMethod = falseMethod; @@ -126,6 +129,8 @@ public Object executeDispatch( switch (getDispatchAction()) { case CALL_METHOD: { + argumentsObjects = executeArguments(frame, argumentsObjects); + blockObject = executeBlock(frame, blockObject); if (isIndirect()) { return indirectCallNode.call( frame, @@ -135,7 +140,7 @@ public Object executeDispatch( trueMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } else { return trueCallDirect.call( frame, @@ -144,7 +149,7 @@ public Object executeDispatch( trueMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } } @@ -174,6 +179,8 @@ public Object executeDispatch( switch (getDispatchAction()) { case CALL_METHOD: { + argumentsObjects = executeArguments(frame, argumentsObjects); + blockObject = executeBlock(frame, blockObject); if (isIndirect()) { return indirectCallNode.call( frame, @@ -183,7 +190,7 @@ public Object executeDispatch( falseMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } else { return falseCallDirect.call( frame, @@ -192,7 +199,7 @@ public Object executeDispatch( falseMethod.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java index 5c248ce8fe1..291cdc59af0 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java @@ -21,6 +21,7 @@ import com.oracle.truffle.api.nodes.InvalidAssumptionException; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.nodes.literal.HashLiteralNode; import org.jruby.truffle.nodes.literal.ObjectLiteralNode; import org.jruby.truffle.nodes.methods.MarkerNode; @@ -55,6 +56,7 @@ public CachedBoxedDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { this( context, @@ -67,6 +69,7 @@ public CachedBoxedDispatchNode( indirect, dispatchAction, argumentNodes, + block, isSplatted); } @@ -151,7 +154,7 @@ public static RubyNode[] expandedArgumentNodes(RubyContext context, for (String label : restKeywordLabels) { for (int j = 0; j < hashNode.size(); j++) { - final String argLabel = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + final String argLabel = ((ObjectLiteralNode) hashNode.getKey(j)).getObject().toString(); if (argLabel.equals(label)) { keyValues[i++] = hashNode.getKey(j); @@ -186,8 +189,9 @@ public CachedBoxedDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, expandedArgumentNodes(context, method, argumentNodes, isSplatted), isSplatted); + super(context, cachedName, next, indirect, dispatchAction, expandedArgumentNodes(context, method, argumentNodes, isSplatted), block, isSplatted); this.expectedClass = expectedClass; this.unmodifiedAssumption = unmodifiedAssumption; @@ -241,6 +245,8 @@ public Object executeDispatch( switch (getDispatchAction()) { case CALL_METHOD: { + argumentsObjects = executeArguments(frame, argumentsObjects); + blockObject = executeBlock(frame, blockObject); if (isIndirect()) { return indirectCallNode.call( frame, @@ -250,9 +256,8 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } else { - Object args = executeArguments(frame, argumentsObjects); return callNode.call( frame, RubyArguments.pack( @@ -260,7 +265,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(args, Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java index 5e8c9be1288..45275cc3fbf 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyBasicObject; @@ -44,8 +45,9 @@ public CachedBoxedMethodMissingDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, block, isSplatted); this.expectedClass = expectedClass; unmodifiedAssumption = expectedClass.getUnmodifiedAssumption(); @@ -114,7 +116,8 @@ public Object executeDispatch( final Object[] modifiedArgumentsObjects = new Object[1 + argumentsObjectsArray.length]; modifiedArgumentsObjects[0] = getCachedNameAsSymbol(); RubyArguments.arraycopy(argumentsObjectsArray, 0, modifiedArgumentsObjects, 1, argumentsObjectsArray.length); - + blockObject = executeBlock(frame, blockObject); + if (isIndirect()) { return indirectCallNode.call( frame, @@ -141,6 +144,7 @@ public Object executeDispatch( return false; case READ_CONSTANT: { + blockObject = executeBlock(frame, blockObject); if (isIndirect()) { return indirectCallNode.call( frame, diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java index 914928f1db2..f2449b2a798 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedReturnMissingDispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyBasicObject; import org.jruby.truffle.runtime.core.RubyClass; @@ -33,8 +34,9 @@ public CachedBoxedReturnMissingDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, block, isSplatted); assert expectedClass != null; this.expectedClass = expectedClass; diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java index c147c6d6c3f..7c6e637d46b 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedSymbolDispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyProc; @@ -43,8 +44,9 @@ public CachedBoxedSymbolDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, block, isSplatted); unmodifiedAssumption = context.getCoreLibrary().getSymbolClass().getUnmodifiedAssumption(); this.value = value; @@ -96,6 +98,9 @@ public Object executeDispatch( switch (getDispatchAction()) { case CALL_METHOD: { + argumentsObjects = executeArguments(frame, argumentsObjects); + blockObject = executeBlock(frame, blockObject); + if (isIndirect()) { return indirectCallNode.call( frame, @@ -105,7 +110,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } else { return callNode.call( frame, @@ -114,7 +119,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java index c9ca0b88d5f..92a15c7dbf6 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedDispatchNode.java @@ -12,6 +12,7 @@ import com.oracle.truffle.api.utilities.BranchProfile; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyString; import org.jruby.truffle.runtime.core.RubySymbol; @@ -34,8 +35,9 @@ public CachedDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, dispatchAction, argumentNodes, isSplatted); + super(context, dispatchAction, argumentNodes, block, isSplatted); assert (cachedName instanceof String) || (cachedName instanceof RubySymbol) || (cachedName instanceof RubyString); this.cachedName = cachedName; diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java index bf69ab2e385..73ec2ede568 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedUnboxedDispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.core.RubyBasicObject; @@ -46,8 +47,9 @@ public CachedUnboxedDispatchNode( boolean indirect, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, cachedName, next, indirect, dispatchAction, argumentNodes, isSplatted); + super(context, cachedName, next, indirect, dispatchAction, argumentNodes, block, isSplatted); this.expectedClass = expectedClass; this.unmodifiedAssumption = unmodifiedAssumption; @@ -95,6 +97,9 @@ public Object executeDispatch( switch (getDispatchAction()) { case CALL_METHOD: { + argumentsObjects = executeArguments(frame, argumentsObjects); + blockObject = executeBlock(frame, blockObject); + if (isIndirect()) { return indirectCallNode.call( frame, @@ -103,7 +108,7 @@ public Object executeDispatch( method, method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } else { return callNode.call( frame, @@ -111,7 +116,7 @@ public Object executeDispatch( method, method.getDeclarationFrame(), receiverObject, CompilerDirectives.unsafeCast(blockObject, RubyProc.class, true, false), - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java index 0d45f581948..821dede23fd 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CallDispatchHeadNode.java @@ -12,6 +12,7 @@ import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.nodes.cast.BooleanCastNode; import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.RubyContext; import org.jruby.truffle.runtime.control.RaiseException; @@ -24,8 +25,8 @@ public class CallDispatchHeadNode extends DispatchHeadNode { @Child private BooleanCastNode booleanCastNode; - public CallDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, RubyNode[] argumentNodes, boolean isSplatted) { - super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.CALL_METHOD, argumentNodes, isSplatted); + public CallDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, RubyNode[] argumentNodes, ProcOrNullNode block, boolean isSplatted) { + super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.CALL_METHOD, argumentNodes, block, isSplatted); } public Object call( diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java index 774b8136ebf..366848dcfe5 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.RubyContext; @@ -26,6 +27,7 @@ public class DispatchHeadNode extends Node { protected final DispatchAction dispatchAction; protected final boolean isSplatted; protected final RubyNode[] argumentNodes; + protected final ProcOrNullNode block; @Child private DispatchNode first; @@ -37,6 +39,7 @@ public DispatchHeadNode( LexicalScope lexicalScope, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { this.context = context; this.ignoreVisibility = ignoreVisibility; @@ -46,7 +49,8 @@ public DispatchHeadNode( this.dispatchAction = dispatchAction; this.argumentNodes = argumentNodes; this.isSplatted = isSplatted; - first = new UnresolvedDispatchNode(context, ignoreVisibility, indirect, missingBehavior, dispatchAction, argumentNodes, isSplatted); + this.block = block; + first = new UnresolvedDispatchNode(context, ignoreVisibility, indirect, missingBehavior, dispatchAction, argumentNodes, block, isSplatted); } public Object dispatch( @@ -65,7 +69,7 @@ public Object dispatch( public void reset(String reason) { first.replace(new UnresolvedDispatchNode( - context, ignoreVisibility, indirect, missingBehavior, dispatchAction, argumentNodes, isSplatted), reason); + context, ignoreVisibility, indirect, missingBehavior, dispatchAction, argumentNodes, block, isSplatted), reason); } public DispatchNode getFirstDispatchNode() { @@ -82,11 +86,14 @@ public DispatchAction getDispatchAction() { public void forceUncached() { adoptChildren(); - first.replace(new UncachedDispatchNode(context, ignoreVisibility, dispatchAction, argumentNodes, isSplatted)); + first.replace(new UncachedDispatchNode(context, ignoreVisibility, dispatchAction, argumentNodes, block, isSplatted)); } public RubyNode[] getArgumentNodes() { return argumentNodes; } + public ProcOrNullNode getBlock() { + return block; + } } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java index 54946dbe4c8..26cb9303430 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchHeadNodeFactory.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyContext; public class DispatchHeadNodeFactory { @@ -22,6 +23,7 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context) { MissingBehavior.CALL_METHOD_MISSING, null, null, + null, false); } @@ -33,6 +35,7 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean MissingBehavior.CALL_METHOD_MISSING, null, null, + null, false); } @@ -44,6 +47,7 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, Missing missingBehavior, null, null, + null, false); } @@ -55,6 +59,7 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean missingBehavior, null, null, + null, false); } @@ -66,10 +71,11 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean missingBehavior, null, null, + null, false); } - public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, RubyNode[] arguments, boolean isSplatted) { + public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, RubyNode[] arguments, ProcOrNullNode block, boolean isSplatted) { return new CallDispatchHeadNode( context, ignoreVisibility, @@ -77,6 +83,7 @@ public static CallDispatchHeadNode createMethodCall(RubyContext context, boolean missingBehavior, null, arguments, + block, isSplatted); } @@ -88,6 +95,7 @@ public static CallDispatchHeadNode createMethodCallOnSelf(RubyContext context) { MissingBehavior.CALL_METHOD_MISSING, null, null, + null, false); } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java index ea6f3fd1eee..d014fe04f50 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.ModuleOperations; import org.jruby.truffle.runtime.RubyConstant; @@ -45,11 +46,13 @@ public abstract class DispatchNode extends RubyNode { public static final Object MISSING = new Object(); @Children protected final RubyNode[] argumentNodes; + @Child protected ProcOrNullNode block; - public DispatchNode(RubyContext context, DispatchAction dispatchAction, RubyNode[] argumentNodes, boolean isSplatted) { + public DispatchNode(RubyContext context, DispatchAction dispatchAction, RubyNode[] argumentNodes, ProcOrNullNode block, boolean isSplatted) { super(context, null); this.dispatchAction = dispatchAction; this.argumentNodes = argumentNodes; + this.block = block; this.isSplatted = isSplatted; assert dispatchAction != null; } @@ -57,6 +60,7 @@ public DispatchNode(RubyContext context, DispatchAction dispatchAction, RubyNode public DispatchNode(DispatchNode prev) { super(prev); argumentNodes = prev.getHeadNode().getArgumentNodes(); + block = prev.getHeadNode().getBlock(); isSplatted = prev.isSplatted; dispatchAction = prev.dispatchAction; } @@ -216,6 +220,18 @@ protected Object executeArguments(VirtualFrame frame, Object argumentOverride) { } } + protected Object executeBlock(VirtualFrame frame, Object blockOverride) { + if (blockOverride != null) { + return blockOverride; + } + + if (block != null) { + return block.executeRubyProc(frame); + } else { + return null; + } + } + public boolean isSplatted() { return isSplatted(); } diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java index 6c9352ef248..dda5e2caffb 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/DoesRespondDispatchHeadNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.LexicalScope; import org.jruby.truffle.runtime.RubyContext; @@ -17,8 +18,8 @@ public class DoesRespondDispatchHeadNode extends DispatchHeadNode { - public DoesRespondDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, RubyNode[] argumentNodes, boolean isSplatted) { - super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.RESPOND_TO_METHOD, argumentNodes, isSplatted); + public DoesRespondDispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, MissingBehavior missingBehavior, LexicalScope lexicalScope, RubyNode[] argumentNodes, ProcOrNullNode block, boolean isSplatted) { + super(context, ignoreVisibility, indirect, missingBehavior, lexicalScope, DispatchAction.RESPOND_TO_METHOD, argumentNodes, block, isSplatted); } /** diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java index caeaa032258..894fc993b64 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.nodes.conversion.ToJavaStringNode; import org.jruby.truffle.nodes.conversion.ToJavaStringNodeFactory; import org.jruby.truffle.nodes.conversion.ToSymbolNode; @@ -40,8 +41,8 @@ public class UncachedDispatchNode extends DispatchNode { private final BranchProfile constantMissingProfile = BranchProfile.create(); private final BranchProfile methodMissingProfile = BranchProfile.create(); - public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, DispatchAction dispatchAction, RubyNode[] argumentNodes, boolean isSplatted) { - super(context, dispatchAction, argumentNodes, isSplatted); + public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, DispatchAction dispatchAction, RubyNode[] argumentNodes, ProcOrNullNode block,boolean isSplatted) { + super(context, dispatchAction, argumentNodes, block, isSplatted); this.ignoreVisibility = ignoreVisibility; callNode = Truffle.getRuntime().createIndirectCallNode(); toSymbolNode = ToSymbolNodeFactory.create(context, null, null); @@ -94,6 +95,9 @@ public Object executeDispatch( if (method != null) { if (dispatchAction == DispatchAction.CALL_METHOD) { + argumentsObjects = executeArguments(frame, argumentsObjects); + blockObject = executeBlock(frame, blockObject); + return callNode.call( frame, method.getCallTarget(), @@ -102,7 +106,7 @@ public Object executeDispatch( method.getDeclarationFrame(), receiverObject, (RubyProc) blockObject, - CompilerDirectives.unsafeCast(executeArguments(frame, argumentsObjects), Object[].class, true))); + CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true))); } else if (dispatchAction == DispatchAction.RESPOND_TO_METHOD) { return true; } else { @@ -133,6 +137,8 @@ public Object executeDispatch( RubyArguments.arraycopy(argumentsObjectsArray, 0, modifiedArgumentsObjects, 1, argumentsObjectsArray.length); + blockObject = executeBlock(frame, blockObject); + return callNode.call( frame, missingMethod.getCallTarget(), diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java index b017f1a6845..754842ec883 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java @@ -10,6 +10,7 @@ package org.jruby.truffle.nodes.dispatch; import org.jruby.truffle.nodes.RubyNode; +import org.jruby.truffle.nodes.cast.ProcOrNullNode; import org.jruby.truffle.runtime.RubyArguments; import org.jruby.truffle.runtime.RubyConstant; import org.jruby.truffle.runtime.RubyContext; @@ -40,8 +41,9 @@ public UnresolvedDispatchNode( MissingBehavior missingBehavior, DispatchAction dispatchAction, RubyNode[] argumentNodes, + ProcOrNullNode block, boolean isSplatted) { - super(context, dispatchAction, argumentNodes, isSplatted); + super(context, dispatchAction, argumentNodes, block, isSplatted); this.ignoreVisibility = ignoreVisibility; this.indirect = indirect; @@ -59,7 +61,7 @@ public Object executeDispatch( if (depth == Options.TRUFFLE_DISPATCH_POLYMORPHIC_MAX.load()) { return getHeadNode().getFirstDispatchNode() - .replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, isSplatted)) + .replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, block, isSplatted)) .executeDispatch(frame, receiverObject, methodName, blockObject, argumentsObjects); } @@ -136,7 +138,7 @@ private Object doUnboxedObject( methodName, first, falseUnmodifiedAssumption, null, falseMethod, trueUnmodifiedAssumption, null, trueMethod, indirect, getDispatchAction(), - argumentNodes, isSplatted); + argumentNodes, block, isSplatted); first.replace(newDispatch); @@ -146,7 +148,7 @@ trueUnmodifiedAssumption, null, trueMethod, indirect, getDispatchAction(), final CachedUnboxedDispatchNode newDispatch = new CachedUnboxedDispatchNode(getContext(), methodName, first, receiverObject.getClass(), getContext().getCoreLibrary().getLogicalClass(receiverObject).getUnmodifiedAssumption(), null, method, indirect, getDispatchAction(), - argumentNodes, isSplatted); + argumentNodes, block, isSplatted); first.replace(newDispatch); @@ -181,10 +183,10 @@ private Object doRubyBasicObject( final DispatchNode newDispatch; if (receiverObject instanceof RubySymbol) { - newDispatch = new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, null, method, indirect, getDispatchAction(), argumentNodes, isSplatted); + newDispatch = new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, null, method, indirect, getDispatchAction(), argumentNodes, block, isSplatted); } else { newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first, - getContext().getCoreLibrary().getMetaClass(receiverObject), null, method, indirect, getDispatchAction(), argumentNodes, isSplatted); + getContext().getCoreLibrary().getMetaClass(receiverObject), null, method, indirect, getDispatchAction(), argumentNodes, block, isSplatted); } first.replace(newDispatch); @@ -206,7 +208,7 @@ private Object doRubyBasicObject( // But we want to check the module assumption, not its singleton class assumption. final DispatchNode newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first, module.getSingletonClass(null), module.getUnmodifiedAssumption(), constant.getValue(), - null, indirect, getDispatchAction(), argumentNodes, isSplatted); + null, indirect, getDispatchAction(), argumentNodes, block, isSplatted); first.replace(newDispatch); return newDispatch.executeDispatch(frame, receiverObject, @@ -225,7 +227,7 @@ private DispatchNode createConstantMissingNode( switch (missingBehavior) { case RETURN_MISSING: { return first.replace(new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first, - receiverObject.getMetaClass(), indirect, getDispatchAction(), argumentNodes, isSplatted)); + receiverObject.getMetaClass(), indirect, getDispatchAction(), argumentNodes, block, isSplatted)); } case CALL_CONST_MISSING: { @@ -237,11 +239,11 @@ private DispatchNode createConstantMissingNode( } if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) { - return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, isSplatted)); + return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, block, isSplatted)); } return first.replace(new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first, - receiverObject.getMetaClass(), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction(), argumentNodes, isSplatted)); + receiverObject.getMetaClass(), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction(), argumentNodes, block, isSplatted)); } default: { @@ -258,7 +260,7 @@ private DispatchNode createMethodMissingNode( switch (missingBehavior) { case RETURN_MISSING: { return first.replace(new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first, - getContext().getCoreLibrary().getMetaClass(receiverObject), indirect, getDispatchAction(), argumentNodes, isSplatted)); + getContext().getCoreLibrary().getMetaClass(receiverObject), indirect, getDispatchAction(), argumentNodes, block, isSplatted)); } case CALL_METHOD_MISSING: { @@ -270,11 +272,11 @@ private DispatchNode createMethodMissingNode( } if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) { - return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, isSplatted)); + return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), argumentNodes, block, isSplatted)); } return first.replace(new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first, - getContext().getCoreLibrary().getMetaClass(receiverObject), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction(), argumentNodes, isSplatted)); + getContext().getCoreLibrary().getMetaClass(receiverObject), method, Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT.load(), getDispatchAction(), argumentNodes, block, isSplatted)); } default: { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java index 4ef4fcb9a9b..c8b427b5731 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/respondto/RespondToNode.java @@ -27,7 +27,7 @@ public RespondToNode(RubyContext context, SourceSection sourceSection, RubyNode super(context, sourceSection); this.methodName = methodName; this.child = child; - dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, false); + dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, null, false); } public boolean executeBoolean(VirtualFrame frame) { diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java b/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java index f15f8012570..98c927473d3 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/rubinius/FixnumPrimitiveNodes.java @@ -31,7 +31,7 @@ public static abstract class FixnumCoercePrimitiveNode extends RubiniusPrimitive public FixnumCoercePrimitiveNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); - toFRespond = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, false); + toFRespond = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, null, false); toF = DispatchHeadNodeFactory.createMethodCall(context); } diff --git a/truffle/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java b/truffle/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java index f91109313d1..460162bf48d 100644 --- a/truffle/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java +++ b/truffle/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java @@ -16,13 +16,11 @@ import org.jcodings.Encoding; import org.jcodings.EncodingDB; -import org.jcodings.specific.UTF8Encoding; import org.jruby.runtime.Constants; import org.jruby.runtime.encoding.EncodingService; import org.jruby.runtime.load.LoadServiceResource; import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.nodes.core.ArrayNodes; -import org.jruby.truffle.nodes.methods.SetMethodDeclarationContext; import org.jruby.truffle.nodes.objects.Allocator; import org.jruby.truffle.runtime.RubyCallStack; import org.jruby.truffle.runtime.RubyContext; @@ -32,7 +30,6 @@ import org.jruby.truffle.runtime.hash.HashOperations; import org.jruby.truffle.runtime.hash.KeyValue; import org.jruby.truffle.translator.NodeWrapper; -import org.jruby.truffle.translator.TranslatorDriver; import org.jruby.util.cli.Options; import org.jruby.util.cli.OutputStrings; diff --git a/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java b/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java index 33999772973..06413cc9195 100644 --- a/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java +++ b/truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java @@ -19,7 +19,6 @@ import org.jruby.truffle.nodes.RubyNode; import org.jruby.truffle.nodes.RubyRootNode; import org.jruby.truffle.nodes.cast.ArrayCastNodeFactory; -import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory; import org.jruby.truffle.nodes.control.IfNode; import org.jruby.truffle.nodes.control.SequenceNode; import org.jruby.truffle.nodes.literal.ObjectLiteralNode; From c05544e482f78bfd9afae9c1a509184d7ac913e6 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Fri, 13 Feb 2015 13:38:35 +0100 Subject: [PATCH 5/7] [Truffle] Do not optimize when keyword label is dynamic --- .../nodes/dispatch/CachedBoxedDispatchNode.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java index 2db2b5f90e7..b504818f652 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedDispatchNode.java @@ -33,6 +33,7 @@ import org.jruby.truffle.runtime.core.RubyBasicObject; import org.jruby.truffle.runtime.core.RubyClass; import org.jruby.truffle.runtime.core.RubyProc; +import org.jruby.truffle.runtime.core.RubySymbol; import org.jruby.truffle.runtime.methods.InternalMethod; public class CachedBoxedDispatchNode extends CachedDispatchNode { @@ -125,14 +126,23 @@ public static RubyNode[] expandedArgumentNodes(RubyContext context, List restKeywordLabels = new ArrayList(); for (int j = 0; j < hashNode.size(); j++) { - final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); - restKeywordLabels.add(label); + Object key = hashNode.getKey(j); + boolean keyIsSymbol = key instanceof ObjectLiteralNode && + ((ObjectLiteralNode) key).getObject() instanceof RubySymbol; + + if (!keyIsSymbol) { + // cannot optimize case where keyword label is dynamic (not a fixed RubySymbol) + return argumentNodes; + } + + final String label = ((ObjectLiteralNode) hashNode.getKey(j)).getObject().toString(); + restKeywordLabels.add(label); } for (String kwarg : kwargs) { result[i] = new OptionalKeywordArgMissingNode(context, null); for (int j = 0; j < hashNode.size(); j++) { - final String label = ((ObjectLiteralNode) hashNode.getKey(j)).execute(null).toString(); + final String label = ((ObjectLiteralNode) hashNode.getKey(j)).getObject().toString(); if (label.equals(kwarg)) { result[i] = hashNode.getValue(j); From 1d586329c3cd9b2d323f9dfe7b7200c85591f4ed Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Fri, 13 Feb 2015 19:13:01 +0100 Subject: [PATCH 6/7] MarkerNode.Marker is a singleton. --- .../java/org/jruby/truffle/nodes/methods/MarkerNode.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java index 18be40d6cc3..2ef515b2060 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/MarkerNode.java @@ -8,15 +8,17 @@ public class MarkerNode extends RubyNode { + private static Marker instance = new Marker(); + public MarkerNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); } @Override public Object execute(VirtualFrame frame) { - return new Marker(); + return instance; } public static class Marker {} -} \ No newline at end of file +} From d8e86ddfe9297bb5e43fff3811388b02d9a0c24f Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Fri, 13 Feb 2015 19:16:14 +0100 Subject: [PATCH 7/7] Make OptionalKeywordArgMissing a singleton. --- .../arguments/OptionalKeywordArgMissingNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java index ac042b3c846..0e45f69ef93 100644 --- a/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java +++ b/truffle/src/main/java/org/jruby/truffle/nodes/methods/arguments/OptionalKeywordArgMissingNode.java @@ -8,18 +8,18 @@ public class OptionalKeywordArgMissingNode extends RubyNode { + private static OptionalKeywordArgMissing instance = new OptionalKeywordArgMissing(); + public OptionalKeywordArgMissingNode(RubyContext context, SourceSection sourceSection) { super(context, sourceSection); } - public static class OptionalKeywordArgMissing { - - } + public static class OptionalKeywordArgMissing {} @Override public Object execute(VirtualFrame frame) { - return new OptionalKeywordArgMissing(); + return instance; } -} \ No newline at end of file +}