UncachedDispatchNode.java
- /*
- * 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.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.conversion.ToJavaStringNode;
- import org.jruby.truffle.nodes.conversion.ToJavaStringNodeFactory;
- import org.jruby.truffle.nodes.conversion.ToSymbolNode;
- import org.jruby.truffle.nodes.conversion.ToSymbolNodeFactory;
- import org.jruby.truffle.runtime.LexicalScope;
- import org.jruby.truffle.runtime.RubyArguments;
- 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.RubyClass;
- import org.jruby.truffle.runtime.core.RubyModule;
- import org.jruby.truffle.runtime.core.RubyProc;
- import org.jruby.truffle.runtime.methods.RubyMethod;
- public abstract class UncachedDispatchNode extends DispatchNode {
- private final boolean ignoreVisibility;
- @Child protected IndirectCallNode callNode;
- @Child protected ToSymbolNode toSymbolNode;
- @Child protected ToJavaStringNode toJavaStringNode;
- private final BranchProfile constantMissingProfile = BranchProfile.create();
- private final BranchProfile methodMissingProfile = BranchProfile.create();
- public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility) {
- super(context);
- this.ignoreVisibility = ignoreVisibility;
- callNode = Truffle.getRuntime().createIndirectCallNode();
- toSymbolNode = ToSymbolNodeFactory.create(context, null, null);
- toJavaStringNode = ToJavaStringNodeFactory.create(context, null, null);
- }
- public UncachedDispatchNode(UncachedDispatchNode prev) {
- super(prev);
- ignoreVisibility = prev.ignoreVisibility;
- callNode = prev.callNode;
- toSymbolNode = prev.toSymbolNode;
- toJavaStringNode = prev.toJavaStringNode;
- }
- @Specialization(guards = "actionIsReadConstant")
- public Object dispatchReadConstant(
- VirtualFrame frame,
- LexicalScope lexicalScope,
- RubyModule receiverObject,
- Object constantName,
- Object blockObject,
- Object argumentsObjects,
- Dispatch.DispatchAction dispatchAction) {
- final RubyConstant constant = lookupConstant(lexicalScope, receiverObject,
- toJavaStringNode.executeJavaString(frame, constantName), ignoreVisibility, dispatchAction);
- if (constant != null) {
- return constant.getValue();
- }
- constantMissingProfile.enter();
- final RubyClass callerClass = ignoreVisibility ? null : getContext().getCoreLibrary().getMetaClass(RubyArguments.getSelf(frame.getArguments()));
- final RubyMethod missingMethod = lookup(callerClass, receiverObject, "const_missing", ignoreVisibility,
- dispatchAction);
- if (missingMethod == null) {
- CompilerDirectives.transferToInterpreter();
- throw new RaiseException(getContext().getCoreLibrary().runtimeError(
- receiverObject.toString() + " didn't have a #const_missing", this));
- }
- return callNode.call(
- frame,
- missingMethod.getCallTarget(),
- RubyArguments.pack(
- missingMethod,
- missingMethod.getDeclarationFrame(),
- receiverObject,
- null,
- new Object[]{toSymbolNode.executeRubySymbol(frame, constantName)}));
- }
- @Specialization(guards = "actionIsCallOrRespondToMethod")
- public Object dispatch(
- VirtualFrame frame,
- LexicalScope lexicalScope,
- Object receiverObject,
- Object methodName,
- Object blockObject,
- Object argumentsObjects,
- Dispatch.DispatchAction dispatchAction) {
- final RubyClass callerClass = ignoreVisibility ? null : getContext().getCoreLibrary().getMetaClass(RubyArguments.getSelf(frame.getArguments()));
- final RubyMethod method = lookup(callerClass, receiverObject, toJavaStringNode.executeJavaString(frame, methodName),
- ignoreVisibility, dispatchAction);
- if (method != null) {
- if (dispatchAction == Dispatch.DispatchAction.CALL_METHOD) {
- return callNode.call(
- frame,
- method.getCallTarget(),
- RubyArguments.pack(
- method,
- method.getDeclarationFrame(),
- receiverObject,
- (RubyProc) blockObject,
- CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true)));
- } else if (dispatchAction == Dispatch.DispatchAction.RESPOND_TO_METHOD) {
- return true;
- } else {
- throw new UnsupportedOperationException();
- }
- }
- methodMissingProfile.enter();
- final RubyMethod missingMethod = lookup(callerClass, receiverObject, "method_missing", true,
- dispatchAction);
- if (missingMethod == null) {
- if (dispatchAction == Dispatch.DispatchAction.RESPOND_TO_METHOD) {
- return false;
- } else {
- CompilerDirectives.transferToInterpreter();
- throw new RaiseException(getContext().getCoreLibrary().runtimeError(
- receiverObject.toString() + " didn't have a #method_missing", this));
- }
- }
- if (dispatchAction == Dispatch.DispatchAction.CALL_METHOD) {
- final Object[] argumentsObjectsArray = CompilerDirectives.unsafeCast(argumentsObjects, Object[].class, true);
- final Object[] modifiedArgumentsObjects = new Object[1 + argumentsObjectsArray.length];
- modifiedArgumentsObjects[0] = toSymbolNode.executeRubySymbol(frame, methodName);
- RubyArguments.arraycopy(argumentsObjectsArray, 0, modifiedArgumentsObjects, 1, argumentsObjectsArray.length);
- return callNode.call(
- frame,
- missingMethod.getCallTarget(),
- RubyArguments.pack(
- missingMethod,
- missingMethod.getDeclarationFrame(),
- receiverObject,
- (RubyProc) blockObject,
- modifiedArgumentsObjects));
- } else if (dispatchAction == Dispatch.DispatchAction.RESPOND_TO_METHOD) {
- return false;
- } else {
- throw new UnsupportedOperationException();
- }
- }
- }