Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 827f20a3c831
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: eb3981075e3e
Choose a head ref
  • 6 commits
  • 10 files changed
  • 1 contributor

Commits on Dec 22, 2014

  1. Copy the full SHA
    c30c750 View commit details
  2. Copy the full SHA
    173a414 View commit details
  3. Copy the full SHA
    c15bdfa View commit details
  4. Copy the full SHA
    4408ff8 View commit details
  5. Copy the full SHA
    0291b96 View commit details
  6. Copy the full SHA
    eb39810 View commit details
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@
import org.jruby.truffle.runtime.hash.HashOperations;
import org.jruby.truffle.runtime.methods.*;

import java.util.Arrays;

/**
* Check arguments meet the arity of the method.
*/
@@ -51,10 +53,8 @@ public void executeVoid(VirtualFrame frame) {

if (!keywordsRest && arity.hasKeywords() && getKeywordsHash(frame) != null) {
for (KeyValue keyValue : HashOperations.verySlowToKeyValues(getKeywordsHash(frame))) {
for (String keyword : keywords) {
if (!keyword.toString().equals(keyValue.getKey().toString())) {
throw new RaiseException(getContext().getCoreLibrary().argumentError("unknown keyword: " + keyValue.getKey().toString(), this));
}
if (!keywordAllowed(keyValue.getKey().toString())) {
throw new RaiseException(getContext().getCoreLibrary().argumentError("unknown keyword: " + keyValue.getKey().toString(), this));
}
}
}
@@ -74,6 +74,16 @@ private boolean checkArity(VirtualFrame frame, int given) {
}
}

private boolean keywordAllowed(String keyword) {
for (String allowedKeyword : keywords) {
if (keyword.equals(allowedKeyword)) {
return true;
}
}

return false;
}

@Override
public Object execute(VirtualFrame frame) {
executeVoid(frame);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.methods.arguments;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;

public class MissingKeywordArgumentNode extends RubyNode {

private final String name;

public MissingKeywordArgumentNode(RubyContext context, SourceSection sourceSection, String name) {
super(context, sourceSection);
this.name = name;
}

@Override
public Object execute(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().argumentErrorMissingKeyword(name, this));
}

}
Original file line number Diff line number Diff line change
@@ -409,6 +409,11 @@ public RubyException argumentError(String message, Node currentNode) {
return new RubyException(argumentErrorClass, context.makeString(message), RubyCallStack.getBacktrace(currentNode));
}

public RubyException argumentErrorMissingKeyword(String name, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return argumentError(String.format("missing keyword: %s", name), currentNode);
}

public RubyException argumentError(int passed, int required, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return argumentError(String.format("wrong number of arguments (%d for %d)", passed, required), currentNode);
71 changes: 47 additions & 24 deletions core/src/main/java/org/jruby/truffle/translator/BodyTranslator.java
Original file line number Diff line number Diff line change
@@ -193,61 +193,84 @@ public RubyNode visitAttrAssignNodeExtraArgument(org.jruby.ast.AttrAssignNode no

// The last argument is the value we assign, and we need to return that as the whole result of this node

final FrameSlot frameSlot = environment.declareVar(environment.allocateLocalTemp("attrasgn"));
final WriteLocalVariableNode writeValue;

final org.jruby.ast.ArrayNode newArgsNode;

if (extraArgument == null) {
// Get that last argument out
final List<org.jruby.ast.Node> argChildNodes = new ArrayList<>(node.getArgsNode().childNodes());
final org.jruby.ast.Node valueNode = argChildNodes.get(argChildNodes.size() - 1);
argChildNodes.remove(argChildNodes.size() - 1);

// Evaluate the value and store it in a local variable
final FrameSlot frameSlot = environment.declareVar(environment.allocateLocalTemp("attrasgn"));
final WriteLocalVariableNode writeValue = WriteLocalVariableNodeFactory.create(context, sourceSection, frameSlot, valueNode.accept(this));
writeValue = WriteLocalVariableNodeFactory.create(context, sourceSection, frameSlot, valueNode.accept(this));

// Recreate the arguments array, reading that local instead of including the RHS for the last argument
argChildNodes.add(new ReadLocalDummyNode(sourceSection, frameSlot));
final org.jruby.ast.ArrayNode newArgsNode = new org.jruby.ast.ArrayNode(node.getPosition(), argChildNodes.get(0));
argChildNodes.add(new ReadLocalDummyNode(node.getPosition(), sourceSection, frameSlot));
newArgsNode = new org.jruby.ast.ArrayNode(node.getPosition(), argChildNodes.get(0));
argChildNodes.remove(0);
for (org.jruby.ast.Node child : argChildNodes) {
newArgsNode.add(child);
}

final CallNode callNode = new CallNode(node.getPosition(), node.getReceiverNode(), node.getName(), newArgsNode, null);
boolean isAccessorOnSelf = (node.getReceiverNode() instanceof org.jruby.ast.SelfNode);
final RubyNode actualCall = visitCallNodeExtraArgument(callNode, null, isAccessorOnSelf, false);

return SequenceNode.sequence(context, sourceSection,
writeValue,
actualCall,
ReadLocalVariableNodeFactory.create(context, sourceSection, frameSlot));
} else {
final RubyNode valueNode = extraArgument;

// Evaluate the value and store it in a local variable
final FrameSlot frameSlot = environment.declareVar(environment.allocateLocalTemp("attrasgn"));
final WriteLocalVariableNode writeValue = WriteLocalVariableNodeFactory.create(context, sourceSection, frameSlot, valueNode);
writeValue = WriteLocalVariableNodeFactory.create(context, sourceSection, frameSlot, valueNode);

// Recreate the arguments array, reading that local instead of including the RHS for the last argument
final List<org.jruby.ast.Node> argChildNodes = new ArrayList<>();
if (node.getArgsNode() != null) {
argChildNodes.addAll(node.getArgsNode().childNodes());
}
argChildNodes.add(new ReadLocalDummyNode(sourceSection, frameSlot));
final org.jruby.ast.ArrayNode newArgsNode = new org.jruby.ast.ArrayNode(node.getPosition(), argChildNodes.get(0));
argChildNodes.add(new ReadLocalDummyNode(node.getPosition(), sourceSection, frameSlot));
newArgsNode = new org.jruby.ast.ArrayNode(node.getPosition(), argChildNodes.get(0));
argChildNodes.remove(0);
for (org.jruby.ast.Node child : argChildNodes) {
newArgsNode.add(child);
}
}

final CallNode callNode = new CallNode(node.getPosition(), node.getReceiverNode(), node.getName(), newArgsNode, null);
boolean isAccessorOnSelf = (node.getReceiverNode() instanceof org.jruby.ast.SelfNode);
final RubyNode actualCall = visitCallNodeExtraArgument(callNode, null, isAccessorOnSelf, false);
/*
* If the original call was of the form:
*
(AttrAssignNode:[]= 10
(LocalVarNode:f 9)
(ArgsPushNode 9
(SplatNode 9
(LocalVarNode:x 9)
)
(FixnumNode 9)
)
)
*
* Then we will have lost that args push and we will have ended up with (Array (Splat (Local x) (Fixnum))
*
* Restory the args push.
*/

return SequenceNode.sequence(context, sourceSection,
writeValue,
actualCall,
ReadLocalVariableNodeFactory.create(context, sourceSection, frameSlot));
final org.jruby.ast.Node fixedArgsNode;

if (node.getArgsNode() instanceof org.jruby.ast.ArgsPushNode) {
if (newArgsNode.size() != 2) {
throw new UnsupportedOperationException();
}

fixedArgsNode = new org.jruby.ast.ArgsPushNode(newArgsNode.getPosition(), newArgsNode.childNodes().get(0), newArgsNode.childNodes().get(1));
} else {
fixedArgsNode = newArgsNode;
}

final CallNode callNode = new CallNode(node.getPosition(), node.getReceiverNode(), node.getName(), fixedArgsNode, null);
boolean isAccessorOnSelf = (node.getReceiverNode() instanceof org.jruby.ast.SelfNode);
final RubyNode actualCall = visitCallNodeExtraArgument(callNode, null, isAccessorOnSelf, false);

return SequenceNode.sequence(context, sourceSection,
writeValue,
actualCall,
ReadLocalVariableNodeFactory.create(context, sourceSection, frameSlot));
}

@Override
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.frame.FrameSlot;
import org.jruby.ast.RequiredKeywordArgumentValueNode;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.ArrayCastNodeFactory;
@@ -141,6 +142,12 @@ public RubyNode visitKeywordArgNode(org.jruby.ast.KeywordArgNode node) {

if (localAsgnNode.getValueNode() == null) {
defaultValue = new NilLiteralNode(context, sourceSection);
} else if (localAsgnNode.getValueNode() instanceof RequiredKeywordArgumentValueNode) {
/*
* This isn't a true default value - it's a marker to say there isn't one. This actually makes sense;
* the semantic action of executing this node is to report an error, and we do the same thing.
*/
defaultValue = new MissingKeywordArgumentNode(context, sourceSection, name);
} else {
defaultValue = localAsgnNode.getValueNode().accept(this);
}
Original file line number Diff line number Diff line change
@@ -14,16 +14,18 @@
import org.jruby.ast.Node;
import org.jruby.ast.NodeType;
import org.jruby.ast.visitor.NodeVisitor;
import org.jruby.lexer.yacc.ISourcePosition;

import java.util.Collections;
import java.util.List;

public class ReadLocalDummyNode extends org.jruby.ast.Node {

final SourceSection sourceSection;
final FrameSlot frameSlot;

public ReadLocalDummyNode(SourceSection sourceSection, FrameSlot frameSlot) {
super(null);
public ReadLocalDummyNode(ISourcePosition sourcePosition, SourceSection sourceSection, FrameSlot frameSlot) {
super(sourcePosition);
this.sourceSection = sourceSection;
this.frameSlot = frameSlot;
}
@@ -35,7 +37,7 @@ public <T> T accept(NodeVisitor<T> visitor) {

@Override
public List<Node> childNodes() {
throw new UnsupportedOperationException();
return Collections.EMPTY_LIST;
}

@Override
2 changes: 0 additions & 2 deletions spec/truffle/tags/language/array_tags.txt

This file was deleted.

34 changes: 0 additions & 34 deletions spec/truffle/tags/language/method_tags.txt
Original file line number Diff line number Diff line change
@@ -1,57 +1,23 @@
fails:An element assignment method send with a single splatted Object argument does not call #to_ary
fails:An element assignment method send with a single splatted Object argument calls #to_a
fails:An element assignment method send with a single splatted Object argument wraps the argument in an Array if #to_a returns nil
fails:An element assignment method send with a single splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An element assignment method send with a leading splatted Object argument does not call #to_ary
fails:An element assignment method send with a leading splatted Object argument calls #to_a
fails:An element assignment method send with a leading splatted Object argument wraps the argument in an Array if #to_a returns nil
fails:An element assignment method send with a leading splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An element assignment method send with a middle splatted Object argument does not call #to_ary
fails:An element assignment method send with a middle splatted Object argument calls #to_a
fails:An element assignment method send with a middle splatted Object argument wraps the argument in an Array if #to_a returns nil
fails:An element assignment method send with a middle splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An element assignment method send with a trailing splatted Object argument does not call #to_ary
fails:An element assignment method send with a trailing splatted Object argument calls #to_a
fails:An element assignment method send with a trailing splatted Object argument wraps the argument in an Array if #to_a returns nil
fails:An element assignment method send with a trailing splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An attribute assignment method send with a single splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An attribute assignment method send with a leading splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An attribute assignment method send with a middle splatted Object argument raises a TypeError if #to_a does not return an Array
fails:An attribute assignment method send with a trailing splatted Object argument raises a TypeError if #to_a does not return an Array
fails:"A method assigns local variables from method parameters for definition \n def m((*a, b)) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(**) end"
fails:"A method assigns local variables from method parameters for definition \n def m(**k) k end"
fails:"A method assigns local variables from method parameters for definition \n def m((*a), (*b)) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m((a, *b), (*c, d))\n [a, b, c, d]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m((a, b, *c, d), (*e, f, g), (*h))\n [a, b, c, d, e, f, g, h]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a, (b, (c, *d), *e))\n [a, b, c, d, e]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a, (b, (c, *d, (e, (*f)), g), (h, (i, j))))\n [a, b, c, d, e, f, g, h, i, j]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, (c, *d))) [a, b, c, d] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, (c, *d), *e)) [a, b, c, d, e] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (*b), (*c)) [a, b, c] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, *c), (*d, e))\n [a, b, c, d, e]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, *c), (d, (*e, f)))\n [a, b, c, d, e, f]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, b: 2) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(*, a:) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(*a, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(*, **) end"
fails:"A method assigns local variables from method parameters for definition \n def m(*a, **) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(*, **k) k end"
fails:"A method assigns local variables from method parameters for definition \n def m(*a, **k) [a, k] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:, b: 1) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:, **) a end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:, **k) [a, k] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a:, &b) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, b:) [a, b] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: def m(a: 1) a end, b:)\n [a, b]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)\n [a, b, c, d, e, f, g, h, k, l]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m a, b=1, *c, d, e:, f: 2, g:, **k, &l\n [a, b, c, d, e, f, g, k, l]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a, (b, c)) [a, b, c] end"
fails:"A method assigns local variables from method parameters for definition \n def m((a, b), (c, d))\n [a, b, c, d]\n end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, c)) [a, b, c] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a=1, (b, c), (d, e)) [a, b, c, d, e] end"
fails:"A method assigns local variables from method parameters for definition \n def m(a: 1, b: 2) [a, b] end"
5 changes: 0 additions & 5 deletions spec/truffle/tags/language/send_tags.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
fails:Invoking a method with optional argument(s), expands an array to arguments grouped in parentheses
fails:Invoking a method allows []= with a *args and multiple rhs args
fails:Invoking a method allows []= with multiple *args and does not unwrap the last splat
fails:Invoking a method allows []= with multiple *args
fails:Invoking a method allows []= with *args in the [] expanded to individual arguments
fails:Invoking a method expands an array to arguments grouped in parentheses which in turn takes rest arguments
fails:allows []= with arguments after splat with *args in the [] and post args
1 change: 0 additions & 1 deletion spec/truffle/tags/language/variables_tags.txt
Original file line number Diff line number Diff line change
@@ -39,7 +39,6 @@ fails:Multiple assignment with a single splatted RHS value assigns a grouped LHS
fails:Multiple assignment with a single splatted RHS value assigns a grouped LHS with splats
fails:Multiple assignment with a single splatted RHS value does not mutate a RHS Array
fails:Multiple assignment with a MRHS value calls #to_a to convert a splatted Object as part of a MRHS with a splat MLHS
fails:Multiple assignment with a MRHS value raises a TypeError if #to_a does not return an Array
fails:Multiple assignment with a MRHS value does not call #to_ary to convert a splatted Object as part of a MRHS with a splat MRHS
fails:Multiple assignment with a MRHS value assigns a grouped LHS without splat from a simple Array
fails:Multiple assignment with a MRHS value assigns a grouped LHS without splat from nested Arrays