DispatchHeadNode.java
/*
* Copyright (c) 2013, 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.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import org.jruby.truffle.runtime.LexicalScope;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyProc;
public class DispatchHeadNode extends Node {
private final RubyContext context;
private final boolean ignoreVisibility;
private final boolean indirect;
private final Dispatch.MissingBehavior missingBehavior;
@Child protected DispatchNode first;
public static DispatchHeadNode onSelf(RubyContext context) {
return new DispatchHeadNode(context, true, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
}
public DispatchHeadNode(RubyContext context) {
this(context, false, false, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
}
public DispatchHeadNode(RubyContext context, boolean ignoreVisibility) {
this(context, ignoreVisibility, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
}
public DispatchHeadNode(RubyContext context, Dispatch.MissingBehavior missingBehavior) {
this(context, false, false, missingBehavior);
}
public DispatchHeadNode(RubyContext context, boolean ignoreVisibility, Dispatch.MissingBehavior missingBehavior) {
this(context, ignoreVisibility, false, missingBehavior);
}
public DispatchHeadNode(RubyContext context, boolean ignoreVisibility, boolean indirect, Dispatch.MissingBehavior missingBehavior) {
this.context = context;
this.ignoreVisibility = ignoreVisibility;
this.indirect = indirect;
this.missingBehavior = missingBehavior;
first = new UnresolvedDispatchNode(context, ignoreVisibility, indirect, missingBehavior);
}
public Object call(
VirtualFrame frame,
Object receiverObject,
Object methodName,
RubyProc blockObject,
Object... argumentsObjects) {
return dispatch(
frame,
null, // TODO(eregon): was RubyArguments.getSelf(frame.getArguments()),
receiverObject,
methodName,
blockObject,
argumentsObjects,
Dispatch.DispatchAction.CALL_METHOD);
}
public double callFloat(
VirtualFrame frame,
Object receiverObject,
Object methodName,
RubyProc blockObject,
Object... argumentsObjects) throws UseMethodMissingException {
final Object value = call(frame, receiverObject, methodName, blockObject, argumentsObjects);
if (missingBehavior == Dispatch.MissingBehavior.RETURN_MISSING && value == Dispatch.MISSING) {
throw new UseMethodMissingException();
}
if (value instanceof Double) {
return (double) value;
}
CompilerDirectives.transferToInterpreter();
final String message = String.format("%s (%s#%s gives %s)",
context.getCoreLibrary().getFloatClass().getName(),
context.getCoreLibrary().getLogicalClass(receiverObject).getName(),
methodName,
context.getCoreLibrary().getLogicalClass(value).getName());
throw new RaiseException(context.getCoreLibrary().typeErrorCantConvertTo(
context.getCoreLibrary().getLogicalClass(receiverObject).getName(),
message,
this));
}
public long callLongFixnum(
VirtualFrame frame,
Object receiverObject,
Object methodName,
RubyProc blockObject,
Object... argumentsObjects) throws UseMethodMissingException {
final Object value = call(frame, receiverObject, methodName, blockObject, argumentsObjects);
if (missingBehavior == Dispatch.MissingBehavior.RETURN_MISSING && value == Dispatch.MISSING) {
throw new UseMethodMissingException();
}
if (value instanceof Integer) {
return (int) value;
}
if (value instanceof Long) {
return (long) value;
}
CompilerDirectives.transferToInterpreter();
final String message = String.format("%s (%s#%s gives %s)",
context.getCoreLibrary().getFloatClass().getName(),
context.getCoreLibrary().getLogicalClass(receiverObject).getName(),
methodName,
context.getCoreLibrary().getLogicalClass(value).getName());
throw new RaiseException(context.getCoreLibrary().typeErrorCantConvertTo(
context.getCoreLibrary().getLogicalClass(receiverObject).getName(),
message,
this));
}
/**
* Check if a specific method is defined on the receiver object.
* This check is "static" and should only be used in a few VM operations.
* In many cases, a dynamic call to Ruby's respond_to? should be used instead.
* Similar to MRI rb_check_funcall().
*/
public boolean doesRespondTo(
VirtualFrame frame,
Object methodName,
Object receiverObject) {
// It's ok to cast here as we control what RESPOND_TO_METHOD returns
return (boolean) dispatch(
frame,
null, // TODO(eregon): was RubyArguments.getSelf(frame.getArguments()),
receiverObject,
methodName,
null,
null,
Dispatch.DispatchAction.RESPOND_TO_METHOD);
}
public Object dispatch(
VirtualFrame frame,
LexicalScope lexicalScope,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects,
Dispatch.DispatchAction dispatchAction) {
return first.executeDispatch(
frame,
lexicalScope,
receiverObject,
methodName,
blockObject,
argumentsObjects,
dispatchAction);
}
public void reset(String reason) {
first.replace(new UnresolvedDispatchNode(context, ignoreVisibility, indirect, missingBehavior), reason);
}
public DispatchNode getFirstDispatchNode() {
return first;
}
public void forceUncached() {
adoptChildren();
first.replace(UncachedDispatchNodeFactory.create(context, ignoreVisibility, null, null, null, null, null, null));
}
}