UncachedDispatchNode.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.nodes.dispatch;

  11. import com.oracle.truffle.api.CompilerDirectives;
  12. import com.oracle.truffle.api.Truffle;
  13. import com.oracle.truffle.api.dsl.Specialization;
  14. import com.oracle.truffle.api.frame.VirtualFrame;
  15. import com.oracle.truffle.api.nodes.IndirectCallNode;
  16. import com.oracle.truffle.api.utilities.BranchProfile;
  17. import org.jruby.truffle.nodes.conversion.ToJavaStringNode;
  18. import org.jruby.truffle.nodes.conversion.ToJavaStringNodeFactory;
  19. import org.jruby.truffle.nodes.conversion.ToSymbolNode;
  20. import org.jruby.truffle.nodes.conversion.ToSymbolNodeFactory;
  21. import org.jruby.truffle.runtime.LexicalScope;
  22. import org.jruby.truffle.runtime.RubyArguments;
  23. import org.jruby.truffle.runtime.RubyConstant;
  24. import org.jruby.truffle.runtime.RubyContext;
  25. import org.jruby.truffle.runtime.control.RaiseException;
  26. import org.jruby.truffle.runtime.core.RubyClass;
  27. import org.jruby.truffle.runtime.core.RubyModule;
  28. import org.jruby.truffle.runtime.core.RubyProc;
  29. import org.jruby.truffle.runtime.methods.RubyMethod;

  30. public abstract class UncachedDispatchNode extends DispatchNode {

  31.     private final boolean ignoreVisibility;

  32.     @Child protected IndirectCallNode callNode;
  33.     @Child protected ToSymbolNode toSymbolNode;
  34.     @Child protected ToJavaStringNode toJavaStringNode;

  35.     private final BranchProfile constantMissingProfile = BranchProfile.create();
  36.     private final BranchProfile methodMissingProfile = BranchProfile.create();

  37.     public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility) {
  38.         super(context);
  39.         this.ignoreVisibility = ignoreVisibility;
  40.         callNode = Truffle.getRuntime().createIndirectCallNode();
  41.         toSymbolNode = ToSymbolNodeFactory.create(context, null, null);
  42.         toJavaStringNode = ToJavaStringNodeFactory.create(context, null, null);
  43.     }

  44.     public UncachedDispatchNode(UncachedDispatchNode prev) {
  45.         super(prev);
  46.         ignoreVisibility = prev.ignoreVisibility;
  47.         callNode = prev.callNode;
  48.         toSymbolNode = prev.toSymbolNode;
  49.         toJavaStringNode = prev.toJavaStringNode;
  50.     }

  51.     @Specialization(guards = "actionIsReadConstant")
  52.     public Object dispatchReadConstant(
  53.             VirtualFrame frame,
  54.             LexicalScope lexicalScope,
  55.             RubyModule receiverObject,
  56.             Object constantName,
  57.             Object blockObject,
  58.             Object argumentsObjects,
  59.             Dispatch.DispatchAction dispatchAction) {
  60.         final RubyConstant constant = lookupConstant(lexicalScope, receiverObject,
  61.                 toJavaStringNode.executeJavaString(frame, constantName), ignoreVisibility, dispatchAction);

  62.         if (constant != null) {
  63.             return constant.getValue();
  64.         }

  65.         constantMissingProfile.enter();

  66.         final RubyClass callerClass = ignoreVisibility ? null : getContext().getCoreLibrary().getMetaClass(RubyArguments.getSelf(frame.getArguments()));

  67.         final RubyMethod missingMethod = lookup(callerClass, receiverObject, "const_missing", ignoreVisibility,
  68.                 dispatchAction);

  69.         if (missingMethod == null) {
  70.             CompilerDirectives.transferToInterpreter();
  71.             throw new RaiseException(getContext().getCoreLibrary().runtimeError(
  72.                     receiverObject.toString() + " didn't have a #const_missing", this));
  73.         }

  74.         return callNode.call(
  75.                 frame,
  76.                 missingMethod.getCallTarget(),
  77.                 RubyArguments.pack(
  78.                         missingMethod,
  79.                         missingMethod.getDeclarationFrame(),
  80.                         receiverObject,
  81.                         null,
  82.                         new Object[]{toSymbolNode.executeRubySymbol(frame, constantName)}));
  83.     }

  84.     @Specialization(guards = "actionIsCallOrRespondToMethod")
  85.     public Object dispatch(
  86.             VirtualFrame frame,
  87.             LexicalScope lexicalScope,
  88.             Object receiverObject,
  89.             Object methodName,
  90.             Object blockObject,
  91.             Object argumentsObjects,
  92.             Dispatch.DispatchAction dispatchAction) {
  93.         final RubyClass callerClass = ignoreVisibility ? null : getContext().getCoreLibrary().getMetaClass(RubyArguments.getSelf(frame.getArguments()));

  94.         final RubyMethod method = lookup(callerClass, receiverObject, toJavaStringNode.executeJavaString(frame, methodName),
  95.                 ignoreVisibility, dispatchAction);

  96.         if (method != null) {
  97.             if (dispatchAction == Dispatch.DispatchAction.CALL_METHOD) {
  98.                 return callNode.call(
  99.                         frame,
  100.                         method.getCallTarget(),
  101.                         RubyArguments.pack(
  102.                                 method,
  103.                                 method.getDeclarationFrame(),
  104.                                 receiverObject,
  105.                                 (RubyProc) blockObject,
  106.                                 CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true)));
  107.             } else if (dispatchAction == Dispatch.DispatchAction.RESPOND_TO_METHOD) {
  108.                 return true;
  109.             } else {
  110.                 throw new UnsupportedOperationException();
  111.             }
  112.         }

  113.         methodMissingProfile.enter();

  114.         final RubyMethod missingMethod = lookup(callerClass, receiverObject, "method_missing", true,
  115.                 dispatchAction);

  116.         if (missingMethod == null) {
  117.             if (dispatchAction == Dispatch.DispatchAction.RESPOND_TO_METHOD) {
  118.                 return false;
  119.             } else {
  120.                 CompilerDirectives.transferToInterpreter();
  121.                 throw new RaiseException(getContext().getCoreLibrary().runtimeError(
  122.                         receiverObject.toString() + " didn't have a #method_missing", this));
  123.             }
  124.         }

  125.         if (dispatchAction == Dispatch.DispatchAction.CALL_METHOD) {
  126.             final Object[] argumentsObjectsArray = CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true);

  127.             final Object[] modifiedArgumentsObjects = new Object[1 + argumentsObjectsArray.length];

  128.             modifiedArgumentsObjects[0] = toSymbolNode.executeRubySymbol(frame, methodName);

  129.             RubyArguments.arraycopy(argumentsObjectsArray, 0, modifiedArgumentsObjects, 1, argumentsObjectsArray.length);

  130.             return callNode.call(
  131.                     frame,
  132.                     missingMethod.getCallTarget(),
  133.                     RubyArguments.pack(
  134.                             missingMethod,
  135.                             missingMethod.getDeclarationFrame(),
  136.                             receiverObject,
  137.                             (RubyProc) blockObject,
  138.                             modifiedArgumentsObjects));
  139.         } else if (dispatchAction == Dispatch.DispatchAction.RESPOND_TO_METHOD) {
  140.             return false;
  141.         } else {
  142.             throw new UnsupportedOperationException();
  143.         }
  144.     }


  145. }