LoadArgumentsTranslator.java

  1. /*
  2.  * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
  3.  * code is released under a tri EPL/GPL/LGPL license. You can use it,
  4.  * redistribute it and/or modify it under the terms of the:
  5.  *
  6.  * Eclipse Public License version 1.0
  7.  * GNU General Public License version 2
  8.  * GNU Lesser General Public License version 2.1
  9.  */
  10. package org.jruby.truffle.translator;

  11. import com.oracle.truffle.api.frame.FrameSlot;
  12. import com.oracle.truffle.api.source.Source;
  13. import com.oracle.truffle.api.source.SourceSection;
  14. import org.jruby.ast.RequiredKeywordArgumentValueNode;
  15. import org.jruby.ast.StarNode;
  16. import org.jruby.ast.types.INameNode;
  17. import org.jruby.lexer.yacc.ISourcePosition;
  18. import org.jruby.truffle.nodes.ReadNode;
  19. import org.jruby.truffle.nodes.RubyNode;
  20. import org.jruby.truffle.nodes.cast.ArrayCastNodeFactory;
  21. import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory;
  22. import org.jruby.truffle.nodes.control.IfNode;
  23. import org.jruby.truffle.nodes.control.SequenceNode;
  24. import org.jruby.truffle.nodes.core.ArrayIndexNodeFactory;
  25. import org.jruby.truffle.nodes.core.ArraySliceNodeFactory;
  26. import org.jruby.truffle.nodes.literal.ArrayLiteralNode;
  27. import org.jruby.truffle.nodes.literal.NilLiteralNode;
  28. import org.jruby.truffle.nodes.methods.arguments.*;
  29. import org.jruby.truffle.nodes.methods.locals.ReadLocalVariableNodeFactory;
  30. import org.jruby.truffle.nodes.methods.locals.WriteLocalVariableNodeFactory;
  31. import org.jruby.truffle.runtime.RubyContext;

  32. import java.util.*;

  33. public class LoadArgumentsTranslator extends Translator {

  34.     private static class ArraySlot {

  35.         private FrameSlot arraySlot;
  36.         private int previousIndex;

  37.         public ArraySlot(FrameSlot arraySlot, int previousIndex) {
  38.             this.arraySlot = arraySlot;
  39.             this.previousIndex = previousIndex;
  40.         }

  41.         public FrameSlot getArraySlot() {
  42.             return arraySlot;
  43.         }

  44.         public int getPreviousIndex() {
  45.             return previousIndex;
  46.         }
  47.     }

  48.     private final boolean isBlock;
  49.     private final BodyTranslator methodBodyTranslator;
  50.     private final Deque<ArraySlot> arraySlotStack = new ArrayDeque<>();

  51.     private enum State {
  52.         PRE,
  53.         OPT,
  54.         POST
  55.     }

  56.     private int required;
  57.     private int index;
  58.     private int indexFromEnd = 1;
  59.     private State state;
  60.     private boolean hasKeywordArguments;
  61.     private List<String> excludedKeywords = new ArrayList<>();

  62.     private org.jruby.ast.ArgsNode argsNode;

  63.     public LoadArgumentsTranslator(RubyNode currentNode, RubyContext context, Source source, boolean isBlock, BodyTranslator methodBodyTranslator) {
  64.         super(currentNode, context, source);
  65.         this.isBlock = isBlock;
  66.         this.methodBodyTranslator = methodBodyTranslator;
  67.     }

  68.     @Override
  69.     public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) {
  70.         argsNode = node;

  71.         final SourceSection sourceSection = translate(node.getPosition());

  72.         final List<RubyNode> sequence = new ArrayList<>();

  73.         if (node.getPre() != null) {
  74.             state = State.PRE;
  75.             index = 0;
  76.             for (org.jruby.ast.Node arg : node.getPre().childNodes()) {
  77.                 sequence.add(arg.accept(this));
  78.                 ++index;
  79.                 required++;
  80.             }
  81.         }

  82.         if (node.getOptArgs() != null) {
  83.             // (BlockNode 0, (OptArgNode:a 0, (LocalAsgnNode:a 0, (FixnumNode 0))), ...)
  84.             state = State.OPT;
  85.             index = argsNode.getPreCount();
  86.             for (org.jruby.ast.Node arg : node.getOptArgs().childNodes()) {
  87.                 sequence.add(arg.accept(this));
  88.                 ++index;
  89.             }
  90.         }

  91.         hasKeywordArguments = node.hasKwargs() && node.getKeywords() != null;

  92.         if (node.getRestArgNode() != null) {
  93.             methodBodyTranslator.getEnvironment().hasRestParameter = true;
  94.             sequence.add(node.getRestArgNode().accept(this));
  95.         }

  96.         if (node.getPost() != null) {
  97.             state = State.POST;
  98.             index = -1;
  99.             final List<org.jruby.ast.Node> children = new ArrayList<>(node.getPost().childNodes());
  100.             Collections.reverse(children);
  101.             for (org.jruby.ast.Node arg : children) {
  102.                 sequence.add(arg.accept(this));
  103.                 index--;
  104.                 required++;
  105.             }
  106.         }

  107.         if (hasKeywordArguments) {
  108.             for (org.jruby.ast.Node arg : node.getKeywords().childNodes()) {
  109.                 sequence.add(arg.accept(this));
  110.             }
  111.         }

  112.         if (node.getKeyRest() != null) {
  113.             sequence.add(node.getKeyRest().accept(this));
  114.         }

  115.         if (node.getBlock() != null) {
  116.             sequence.add(node.getBlock().accept(this));
  117.         }

  118.         return SequenceNode.sequence(context, sourceSection, sequence);
  119.     }

  120.     @Override
  121.     public RubyNode visitKeywordRestArgNode(org.jruby.ast.KeywordRestArgNode node) {
  122.         final SourceSection sourceSection = translate(node.getPosition());

  123.         final RubyNode readNode = new ReadKeywordRestArgumentNode(context, sourceSection, required, excludedKeywords.toArray(new String[excludedKeywords.size()]));
  124.         final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findOrAddFrameSlot(node.getName());

  125.         return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  126.     }

  127.     @Override
  128.     public RubyNode visitKeywordArgNode(org.jruby.ast.KeywordArgNode node) {
  129.         final SourceSection sourceSection = translate(node.getPosition());

  130.         final String name;
  131.         final RubyNode defaultValue;

  132.         final org.jruby.ast.Node firstChild = node.childNodes().get(0);

  133.         if (firstChild instanceof org.jruby.ast.LocalAsgnNode) {
  134.             final org.jruby.ast.LocalAsgnNode localAsgnNode = (org.jruby.ast.LocalAsgnNode) firstChild;
  135.             name = localAsgnNode.getName();

  136.             if (localAsgnNode.getValueNode() == null) {
  137.                 defaultValue = new NilLiteralNode(context, sourceSection);
  138.             } else if (localAsgnNode.getValueNode() instanceof RequiredKeywordArgumentValueNode) {
  139.                 /*
  140.                  * This isn't a true default value - it's a marker to say there isn't one. This actually makes sense;
  141.                  * the semantic action of executing this node is to report an error, and we do the same thing.
  142.                  */
  143.                 defaultValue = new MissingKeywordArgumentNode(context, sourceSection, name);
  144.             } else {
  145.                 defaultValue = localAsgnNode.getValueNode().accept(this);
  146.             }
  147.         } else if (firstChild instanceof org.jruby.ast.DAsgnNode) {
  148.             final org.jruby.ast.DAsgnNode dAsgnNode = (org.jruby.ast.DAsgnNode) firstChild;
  149.             name = dAsgnNode.getName();

  150.             if (dAsgnNode.getValueNode() == null) {
  151.                 defaultValue = new NilLiteralNode(context, sourceSection);
  152.             } else {
  153.                 defaultValue = dAsgnNode.getValueNode().accept(this);
  154.             }
  155.         } else {
  156.             throw new UnsupportedOperationException("unsupported keyword arg " + node);
  157.         }

  158.         excludedKeywords.add(name);

  159.         final RubyNode readNode = new ReadKeywordArgumentNode(context, sourceSection, required, name, defaultValue);
  160.         final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(name);

  161.         return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  162.     }

  163.     @Override
  164.     public RubyNode visitArgumentNode(org.jruby.ast.ArgumentNode node) {
  165.         final SourceSection sourceSection = translate(node.getPosition());

  166.         final RubyNode readNode = readArgument(sourceSection);
  167.         final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(node.getName());
  168.         return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  169.     }

  170.     private RubyNode readArgument(SourceSection sourceSection) {
  171.         if (useArray()) {
  172.             return ArrayIndexNodeFactory.create(context, sourceSection, index, loadArray(sourceSection));
  173.         } else {
  174.             if (state == State.PRE) {
  175.                 return new ReadPreArgumentNode(context, sourceSection, index, isBlock ? MissingArgumentBehaviour.NIL : MissingArgumentBehaviour.RUNTIME_ERROR);
  176.             } else if (state == State.POST) {
  177.                 return new ReadPostArgumentNode(context, sourceSection, index);
  178.             } else {
  179.                 throw new IllegalStateException();
  180.             }
  181.         }
  182.     }

  183.     @Override
  184.     public RubyNode visitRestArgNode(org.jruby.ast.RestArgNode node) {
  185.         final SourceSection sourceSection = translate(node.getPosition());

  186.         final RubyNode readNode;

  187.         int from = argsNode.getPreCount() + argsNode.getOptionalArgsCount();
  188.         int to = -argsNode.getPostCount();
  189.         if (useArray()) {
  190.             readNode = ArraySliceNodeFactory.create(context, sourceSection, from, to, loadArray(sourceSection));
  191.         } else {
  192.             readNode = new ReadRestArgumentNode(context, sourceSection, from, to, hasKeywordArguments);
  193.         }

  194.         final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(node.getName());
  195.         return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  196.     }

  197.     @Override
  198.     public RubyNode visitBlockArgNode(org.jruby.ast.BlockArgNode node) {
  199.         final SourceSection sourceSection = translate(node.getPosition());

  200.         final RubyNode readNode = new ReadBlockNode(context, sourceSection, context.getCoreLibrary().getNilObject());
  201.         final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(node.getName());
  202.         return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  203.     }

  204.     @Override
  205.     public RubyNode visitOptArgNode(org.jruby.ast.OptArgNode node) {
  206.         // (OptArgNode:a 0, (LocalAsgnNode:a 0, (FixnumNode 0)))
  207.         return node.getValue().accept(this);
  208.     }

  209.     @Override
  210.     public RubyNode visitLocalAsgnNode(org.jruby.ast.LocalAsgnNode node) {
  211.         return translateLocalAssignment(node.getPosition(), node.getName(), node.getValueNode());
  212.     }

  213.     @Override
  214.     public RubyNode visitDAsgnNode(org.jruby.ast.DAsgnNode node) {
  215.         return translateLocalAssignment(node.getPosition(), node.getName(), node.getValueNode());
  216.     }

  217.     private RubyNode translateLocalAssignment(ISourcePosition sourcePosition, String name, org.jruby.ast.Node valueNode) {
  218.         final SourceSection sourceSection = translate(sourcePosition);

  219.         final RubyNode readNode;

  220.         if (indexFromEnd == 1) {
  221.             if (valueNode instanceof org.jruby.ast.NilImplicitNode) {
  222.                 // Multiple assignment

  223.                 if (useArray()) {
  224.                     readNode = ArrayIndexNodeFactory.create(context, sourceSection, index, loadArray(sourceSection));
  225.                 } else {
  226.                     readNode = readArgument(sourceSection);
  227.                 }
  228.             } else {
  229.                 // Optional argument
  230.                 final RubyNode defaultValue = valueNode.accept(this);

  231.                 int minimum = index + 1 + argsNode.getPostCount();

  232.                 if (argsNode.hasKwargs()) {
  233.                     minimum += 1;
  234.                 }

  235.                 readNode = new ReadOptionalArgumentNode(context, sourceSection, index, minimum, defaultValue);
  236.             }
  237.         } else {
  238.             readNode = ArraySliceNodeFactory.create(context, sourceSection, index, indexFromEnd, loadArray(sourceSection));
  239.         }

  240.         final FrameSlot slot = methodBodyTranslator.getEnvironment().getFrameDescriptor().findOrAddFrameSlot(name);
  241.         return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  242.     }

  243.     @Override
  244.     public RubyNode visitArrayNode(org.jruby.ast.ArrayNode node) {
  245.         // (ArrayNode 0, (MultipleAsgn19Node 0, (ArrayNode 0, (LocalAsgnNode:a 0, ), (LocalAsgnNode:b 0, )), null, null)))
  246.         return node.childNodes().get(0).accept(this);
  247.     }

  248.     @Override
  249.     public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
  250.         final SourceSection sourceSection = translate(node.getPosition());

  251.         // (MultipleAsgn19Node 0, (ArrayNode 0, (LocalAsgnNode:a 0, ), (LocalAsgnNode:b 0, )), null, null))

  252.         final int arrayIndex = index;

  253.         final String arrayName = methodBodyTranslator.getEnvironment().allocateLocalTemp("destructure");
  254.         final FrameSlot arraySlot = methodBodyTranslator.getEnvironment().declareVar(arrayName);

  255.         pushArraySlot(arraySlot);

  256.         final List<org.jruby.ast.Node> childNodes;

  257.         if (node.childNodes() == null || node.childNodes().get(0) == null) {
  258.             childNodes = Collections.emptyList();
  259.         } else {
  260.             childNodes = node.childNodes().get(0).childNodes();
  261.         }

  262.         final List<RubyNode> notNilSequence = new ArrayList<>();

  263.         if (node.getPre() != null) {
  264.             index = 0;
  265.             for (org.jruby.ast.Node child : node.getPre().childNodes()) {
  266.                 notNilSequence.add(child.accept(this));
  267.                 index++;
  268.             }
  269.         }

  270.         if (node.getRest() != null) {
  271.             index = node.getPreCount();
  272.             indexFromEnd = -node.getPostCount();
  273.             notNilSequence.add(node.getRest().accept(this));
  274.             indexFromEnd = 1;
  275.         }

  276.         if (node.getPost() != null) {
  277.             index = -1;
  278.             final List<org.jruby.ast.Node> children = new ArrayList<>(node.getPost().childNodes());
  279.             Collections.reverse(children);
  280.             for (org.jruby.ast.Node child : children) {
  281.                 notNilSequence.add(child.accept(this));
  282.                 index--;
  283.             }
  284.         }

  285.         final RubyNode notNil = SequenceNode.sequence(context, sourceSection, notNilSequence);

  286.         popArraySlot(arraySlot);

  287.         final List<RubyNode> nilSequence = new ArrayList<>();

  288.         final ParameterCollector parametersToClearCollector = new ParameterCollector();

  289.         if (node.getPre() != null) {
  290.             for (org.jruby.ast.Node child : node.getPre().childNodes()) {
  291.                 child.accept(parametersToClearCollector);
  292.             }
  293.         }

  294.         if (node.getRest() != null) {
  295.             if (node.getRest() instanceof INameNode) {
  296.                 final String name = ((INameNode) node.getRest()).getName();
  297.                 nilSequence.add(((ReadNode) methodBodyTranslator.getEnvironment().findOrAddLocalVarNodeDangerous(name, sourceSection)).makeWriteNode(new ArrayLiteralNode.UninitialisedArrayLiteralNode(context, sourceSection, new RubyNode[]{})));
  298.             } else if (node.getRest() instanceof StarNode) {
  299.                 // Don't think we need to do anything
  300.             } else {
  301.                 throw new UnsupportedOperationException("unsupported rest node " + node.getRest());
  302.             }
  303.         }

  304.         if (node.getPost() != null) {
  305.             for (org.jruby.ast.Node child : node.getPost().childNodes()) {
  306.                 child.accept(parametersToClearCollector);
  307.             }
  308.         }

  309.         for (String parameterToClear : parametersToClearCollector.getParameters()) {
  310.             nilSequence.add(((ReadNode) methodBodyTranslator.getEnvironment().findOrAddLocalVarNodeDangerous(parameterToClear, sourceSection)).makeWriteNode(new NilLiteralNode(context, sourceSection)));
  311.         }

  312.         if (!childNodes.isEmpty()) {
  313.             // We haven't pushed a new array slot, so this will read the value which we couldn't convert to an array into the first destructured argument
  314.             index = arrayIndex;
  315.             nilSequence.add(childNodes.get(0).accept(this));
  316.         }

  317.         final RubyNode nil = SequenceNode.sequence(context, sourceSection, nilSequence);

  318.         return SequenceNode.sequence(context, sourceSection,
  319.                 WriteLocalVariableNodeFactory.create(context, sourceSection, arraySlot,
  320.                         ArrayCastNodeFactory.create(context, sourceSection,
  321.                                 readArgument(sourceSection))),
  322.                 new IfNode(context, sourceSection,
  323.                         BooleanCastNodeFactory.create(context, sourceSection,
  324.                                 new IsNilNode(context, sourceSection, ReadLocalVariableNodeFactory.create(context, sourceSection, arraySlot))),
  325.                         nil,
  326.                         notNil == null ? new NilLiteralNode(context, sourceSection) : notNil));
  327.     }

  328.     @Override
  329.     protected RubyNode defaultVisit(org.jruby.ast.Node node) {
  330.         // For normal expressions in the default value for optional arguments, use the normal body translator
  331.         return node.accept(methodBodyTranslator);
  332.     }

  333.     public void pushArraySlot(FrameSlot slot) {
  334.         arraySlotStack.push(new ArraySlot(slot, index));
  335.     }

  336.     public void popArraySlot(FrameSlot slot) {
  337.         index = arraySlotStack.pop().getPreviousIndex();
  338.     }

  339.     protected boolean useArray() {
  340.         return !arraySlotStack.isEmpty();
  341.     }

  342.     protected RubyNode loadArray(SourceSection sourceSection) {
  343.         return ReadLocalVariableNodeFactory.create(context, sourceSection, arraySlotStack.peek().getArraySlot());
  344.     }

  345.     @Override
  346.     protected String getIdentifier() {
  347.         return methodBodyTranslator.getIdentifier();
  348.     }

  349. }