SplatCastNode.java

  1. /*
  2.  * Copyright (c) 2013, 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.nodes.cast;

  11. import com.oracle.truffle.api.CompilerAsserts;
  12. import com.oracle.truffle.api.dsl.NodeChild;
  13. import com.oracle.truffle.api.dsl.Specialization;
  14. import com.oracle.truffle.api.frame.VirtualFrame;
  15. import com.oracle.truffle.api.source.SourceSection;
  16. import org.jruby.truffle.nodes.RubyNode;
  17. import org.jruby.truffle.nodes.core.ArrayDupNode;
  18. import org.jruby.truffle.nodes.core.ArrayDupNodeFactory;
  19. import org.jruby.truffle.nodes.dispatch.Dispatch;
  20. import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
  21. import org.jruby.truffle.runtime.RubyContext;
  22. import org.jruby.truffle.runtime.control.RaiseException;
  23. import org.jruby.truffle.runtime.core.RubyArray;
  24. import org.jruby.truffle.runtime.core.RubyNilClass;

  25. /**
  26.  * Splat as used to cast a value to an array if it isn't already, as in {@code *value}.
  27.  */
  28. @NodeChild("child")
  29. public abstract class SplatCastNode extends RubyNode {

  30.     public static enum NilBehavior {
  31.         EMPTY_ARRAY,
  32.         ARRAY_WITH_NIL,
  33.         NIL
  34.     }

  35.     private final NilBehavior nilBehavior;
  36.     private final boolean useToAry;

  37.     @Child protected ArrayDupNode dup;
  38.     @Child protected DispatchHeadNode respondToToA;
  39.     @Child protected BooleanCastNode respondToCast;
  40.     @Child protected DispatchHeadNode toA;

  41.     public SplatCastNode(RubyContext context, SourceSection sourceSection, NilBehavior nilBehavior, boolean useToAry) {
  42.         super(context, sourceSection);
  43.         this.nilBehavior = nilBehavior;
  44.         // Calling private #to_a is allowed for the *splat operator.
  45.         dup = ArrayDupNodeFactory.create(context, sourceSection, null);
  46.         respondToToA = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.RETURN_MISSING);
  47.         respondToCast = BooleanCastNodeFactory.create(context, sourceSection, null);
  48.         toA = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.RETURN_MISSING);
  49.         this.useToAry = useToAry;
  50.     }

  51.     public SplatCastNode(SplatCastNode prev) {
  52.         super(prev);
  53.         dup = prev.dup;
  54.         nilBehavior = prev.nilBehavior;
  55.         respondToToA = prev.respondToToA;
  56.         respondToCast = prev.respondToCast;
  57.         toA = prev.toA;
  58.         useToAry = prev.useToAry;
  59.     }

  60.     protected abstract RubyNode getChild();

  61.     @Specialization
  62.     public RubyArray splat(RubyNilClass nil) {
  63.         switch (nilBehavior) {
  64.             case EMPTY_ARRAY:
  65.                 return new RubyArray(getContext().getCoreLibrary().getArrayClass());

  66.             case ARRAY_WITH_NIL:
  67.                 return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), getContext().getCoreLibrary().getNilObject());

  68.             default: {
  69.                 CompilerAsserts.neverPartOfCompilation();
  70.                 throw new UnsupportedOperationException();
  71.             }
  72.         }
  73.     }

  74.     @Specialization
  75.     public RubyArray splat(VirtualFrame frame, RubyArray array) {
  76.         // TODO(cs): is it necessary to dup here in all cases?
  77.         // It is needed at least for [*ary] (parsed as just a SplatNode) and b = *ary.
  78.         return dup.executeDup(frame, array);
  79.     }

  80.     @Specialization(guards = {"!isRubyNilClass", "!isRubyArray"})
  81.     public RubyArray splat(VirtualFrame frame, Object object) {
  82.         notDesignedForCompilation();

  83.         final String method;

  84.         if (useToAry) {
  85.             method = "to_ary";
  86.         } else {
  87.             method = "to_a";
  88.         }

  89.         // MRI tries to call dynamic respond_to? here.
  90.         Object respondToResult = respondToToA.call(frame, object, "respond_to?", null, getContext().makeString(method), true);
  91.         if (respondToResult != Dispatch.MISSING && respondToCast.executeBoolean(frame, respondToResult)) {
  92.             final Object array = toA.call(frame, object, method, null);

  93.             if (array instanceof RubyArray) {
  94.                 return (RubyArray) array;
  95.             } else if (array instanceof RubyNilClass || array == Dispatch.MISSING) {
  96.                 return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), object);
  97.             } else {
  98.                 throw new RaiseException(getContext().getCoreLibrary().typeErrorCantConvertTo(
  99.                         getContext().getCoreLibrary().getLogicalClass(object).getName(),
  100.                         "Array",
  101.                         method,
  102.                         getContext().getCoreLibrary().getLogicalClass(array).getName(),
  103.                         this)
  104.                 );
  105.             }
  106.         }

  107.         return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), object);
  108.     }

  109. }