Skip to content

Commit

Permalink
Showing 2 changed files with 118 additions and 0 deletions.
99 changes: 99 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/coerce/ToFNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2015 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.coerce;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyBasicObject;

@NodeChild(value = "child", type = RubyNode.class)
public abstract class ToFNode extends RubyNode {

@Child private CallDispatchHeadNode toFNode;

public ToFNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public double doDouble(VirtualFrame frame, Object value) {
final Object doubleObject = executeDouble(frame, value);

if (doubleObject instanceof Double) {
return (double) doubleObject;
}

CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException("executeDouble must return a double, instead it returned a " + doubleObject.getClass().getName());
}

public abstract Object executeDouble(VirtualFrame frame, Object value);

@Specialization
public double coerceInt(int value) {
return value;
}

@Specialization
public double coerceLong(long value) {
return value;
}

@Specialization
public double coerceDouble(double value) {
return value;
}

@Specialization
public double coerceBoolean(VirtualFrame frame, boolean value) {
return coerceObject(frame, value);
}

@Specialization
public double coerceRubyBasicObject(VirtualFrame frame, RubyBasicObject object) {
return coerceObject(frame, object);
}

private double coerceObject(VirtualFrame frame, Object object) {
if (toFNode == null) {
CompilerDirectives.transferToInterpreter();
toFNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object coerced;

try {
coerced = toFNode.call(frame, object, "to_f", null);
} catch (RaiseException e) {
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getNoMethodErrorClass()) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().typeErrorNoImplicitConversion(object, "Float", this));
} else {
throw e;
}
}

if (getContext().getCoreLibrary().getLogicalClass(coerced) == getContext().getCoreLibrary().getFloatClass()) {
return (double) coerced;
} else {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().typeErrorBadCoercion(object, "Float", "to_f", coerced, this));
}
}

}
Original file line number Diff line number Diff line change
@@ -9,12 +9,19 @@
*/
package org.jruby.truffle.pack.nodes.type;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.nodes.coerce.ToFNode;
import org.jruby.truffle.nodes.coerce.ToFNodeGen;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;

/**
* Convert a value to a {@code double}.
@@ -24,6 +31,8 @@
})
public abstract class ToDoubleNode extends PackNode {

@Child private ToFNode toFNode;

public ToDoubleNode(RubyContext context) {
super(context);
}
@@ -45,4 +54,14 @@ public double toDouble(VirtualFrame frame, double value) {
return value;
}

@Specialization
public Object toDouble(RubyBasicObject value) {
if (toFNode == null) {
CompilerDirectives.transferToInterpreter();
toFNode = insert(ToFNodeGen.create(getContext(), getSourceSection(), null));
}

final VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(RubyArguments.pack(null, null, value, null, new Object[]{}), new FrameDescriptor());

This comment has been minimized.

Copy link
@nirvdrum

nirvdrum Jun 30, 2015

Author Contributor

@chrisseaton @eregon This seems to work, but please let me know if there's a better way to build up a new frame for the call.

This comment has been minimized.

Copy link
@chrisseaton

chrisseaton Jun 30, 2015

Contributor

I don't get what you're trying to do - why can't we make a normal call?

This comment has been minimized.

Copy link
@nirvdrum

nirvdrum Jun 30, 2015

Author Contributor

I'm trying to coerce the object like we do with our other coercion nodes. For some object types, we don't need need to call to_f at all. For others, we need to handle failure cases in the to_f call: either the method doesn't exist or it does exist and returns something other than a Float.

This comment has been minimized.

Copy link
@eregon

eregon Jul 1, 2015

Member

Ah right the dynamic call to to_f expects a Ruby frame, but the #pack frame is different.
Looks good, the method should be filled with Array#pack if possible.

return toFNode.doDouble(frame, value);
}
}

0 comments on commit 9233716

Please sign in to comment.