KernelNodes.java
/*
* Copyright (c) 2013, 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.core;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.common.IRubyWarnings;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.BooleanCastNode;
import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory;
import org.jruby.truffle.nodes.control.WhileNode;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.nodes.globals.WrapInThreadLocalNode;
import org.jruby.truffle.nodes.literal.BooleanLiteralNode;
import org.jruby.truffle.nodes.objects.SingletonClassNode;
import org.jruby.truffle.nodes.objects.SingletonClassNodeFactory;
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.yield.YieldNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.backtrace.Activation;
import org.jruby.truffle.runtime.backtrace.Backtrace;
import org.jruby.truffle.runtime.backtrace.MRIBacktraceFormatter;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.control.ThrowException;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.hash.HashOperations;
import org.jruby.truffle.runtime.hash.KeyValue;
import org.jruby.truffle.runtime.methods.RubyMethod;
import org.jruby.util.ByteList;
import org.jruby.util.cli.Options;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@CoreClass(name = "Kernel")
public abstract class KernelNodes {
/**
* Check if operands are the same object or call #==.
* Known as rb_equal() in MRI. The fact Kernel#=== uses this is pure coincidence.
*/
@CoreMethod(names = "===", required = 1)
public abstract static class SameOrEqualNode extends CoreMethodNode {
@Child protected BasicObjectNodes.ReferenceEqualNode referenceEqualNode;
@Child protected PredicateDispatchHeadNode equalNode;
public SameOrEqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SameOrEqualNode(SameOrEqualNode prev) {
super(prev);
}
protected boolean areSame(VirtualFrame frame, Object left, Object right) {
if (referenceEqualNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
referenceEqualNode = insert(BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(getContext(), getSourceSection(), null, null));
}
return referenceEqualNode.executeReferenceEqual(frame, left, right);
}
protected boolean areEqual(VirtualFrame frame, Object left, Object right) {
if (equalNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
equalNode = insert(new PredicateDispatchHeadNode(getContext()));
}
return equalNode.call(frame, left, "==", null, right);
}
public abstract boolean executeSameOrEqual(VirtualFrame frame, Object a, Object b);
@Specialization
public boolean sameOrEqual(VirtualFrame frame, Object a, Object b) {
if (areSame(frame, a, b))
return true;
return areEqual(frame, a, b);
}
}
@CoreMethod(names = "=~", required = 1, needsSelf = false)
public abstract static class MatchNode extends CoreMethodNode {
public MatchNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public MatchNode(MatchNode prev) {
super(prev);
}
@Specialization
public RubyNilClass equal(Object other) {
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "!~", required = 1)
public abstract static class NotMatchNode extends CoreMethodNode {
@Child protected PredicateDispatchHeadNode matchNode;
public NotMatchNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
matchNode = new PredicateDispatchHeadNode(context);
}
public NotMatchNode(NotMatchNode prev) {
super(prev);
matchNode = prev.matchNode;
}
@Specialization
public boolean notMatch(VirtualFrame frame, Object self, Object other) {
return !matchNode.call(frame, self, "=~", null, other);
}
}
@CoreMethod(names = {"<=>"}, required = 1)
public abstract static class CompareNode extends CoreMethodNode {
@Child protected DispatchHeadNode equalNode;
@Child protected BooleanCastNode booleanCast;
public CompareNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
equalNode = new DispatchHeadNode(context);
booleanCast = BooleanCastNodeFactory.create(context, sourceSection, null);
}
public CompareNode(CompareNode prev) {
super(prev);
equalNode = prev.equalNode;
booleanCast = prev.booleanCast;
}
@Specialization
public Object compare(VirtualFrame frame, RubyBasicObject self, RubyBasicObject other) {
notDesignedForCompilation();
if ((self == other) || booleanCast.executeBoolean(frame, equalNode.call(frame, self, "==", null, other))) {
return 0;
}
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "abort", isModuleFunction = true)
public abstract static class AbortNode extends CoreMethodNode {
public AbortNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public AbortNode(AbortNode prev) {
super(prev);
}
@Specialization
public RubyNilClass abort() {
CompilerDirectives.transferToInterpreter();
System.exit(1);
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "Array", isModuleFunction = true, argumentsAsArray = true)
public abstract static class ArrayNode extends CoreMethodNode {
@Child ArrayBuilderNode arrayBuilderNode;
public ArrayNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
arrayBuilderNode = new ArrayBuilderNode.UninitializedArrayBuilderNode(context);
}
public ArrayNode(ArrayNode prev) {
super(prev);
arrayBuilderNode = prev.arrayBuilderNode;
}
@Specialization(guards = "isOneArrayElement")
public RubyArray arrayOneArrayElement(Object[] args) {
return (RubyArray) args[0];
}
@Specialization(guards = "!isOneArrayElement")
public RubyArray array(Object[] args) {
final int length = args.length;
Object store = arrayBuilderNode.start(length);
for (int n = 0; n < length; n++) {
store = arrayBuilderNode.append(store, n, args[n]);
}
return new RubyArray(getContext().getCoreLibrary().getArrayClass(), arrayBuilderNode.finish(store, length), length);
}
protected boolean isOneArrayElement(Object[] args) {
return args.length == 1 && args[0] instanceof RubyArray;
}
}
@CoreMethod(names = "at_exit", isModuleFunction = true, needsBlock = true)
public abstract static class AtExitNode extends CoreMethodNode {
public AtExitNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public AtExitNode(AtExitNode prev) {
super(prev);
}
@Specialization
public Object atExit(RubyProc block) {
notDesignedForCompilation();
getContext().getAtExitManager().add(block);
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "binding", isModuleFunction = true)
public abstract static class BindingNode extends CoreMethodNode {
public BindingNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public abstract RubyBinding executeBinding(VirtualFrame frame);
@Specialization
public RubyBinding binding() {
// Materialize the caller's frame - false means don't use a slow path to get it - we want to optimize it
final MaterializedFrame callerFrame = Truffle.getRuntime().getCallerFrame()
.getFrame(FrameInstance.FrameAccess.MATERIALIZE, false).materialize();
return new RubyBinding(
getContext().getCoreLibrary().getBindingClass(),
RubyArguments.getSelf(callerFrame.getArguments()),
callerFrame);
}
}
@CoreMethod(names = "block_given?", isModuleFunction = true)
public abstract static class BlockGivenNode extends CoreMethodNode {
public BlockGivenNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public BlockGivenNode(BlockGivenNode prev) {
super(prev);
}
@Specialization
public boolean blockGiven() {
notDesignedForCompilation();
return RubyArguments.getBlock(Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_ONLY, false).getArguments()) != null;
}
}
@CoreMethod(names = "caller", isModuleFunction = true, optional = 1)
public abstract static class CallerNode extends CoreMethodNode {
public CallerNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public CallerNode(CallerNode prev) {
super(prev);
}
@Specialization
public Object caller(UndefinedPlaceholder omit) {
return caller(1);
}
@Specialization
public Object caller(int omit) {
notDesignedForCompilation();
omit += 1; // Always ignore this node
Backtrace backtrace = RubyCallStack.getBacktrace(this);
List<Activation> activations = backtrace.getActivations();
int size = activations.size() - omit;
if (size < 0) {
return getContext().getCoreLibrary().getNilObject();
}
Object[] callers = new Object[size];
for (int n = 0; n < size; n++) {
callers[n] = getContext().makeString(MRIBacktraceFormatter.formatCallerLine(activations, n + omit));
}
return new RubyArray(getContext().getCoreLibrary().getArrayClass(), callers, callers.length);
}
}
@CoreMethod(names = "catch", isModuleFunction = true, needsBlock = true, required = 1)
public abstract static class CatchNode extends YieldingCoreMethodNode {
public CatchNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public CatchNode(CatchNode prev) {
super(prev);
}
@Specialization
public Object doCatch(VirtualFrame frame, Object tag, RubyProc block) {
notDesignedForCompilation();
try {
getContext().getThrowTags().add(tag);
return yield(frame, block);
} catch (ThrowException e) {
if (e.getTag().equals(tag)) {
// TODO(cs): unset rather than set to Nil?
notDesignedForCompilation();
getContext().getCoreLibrary().getGlobalVariablesObject().getOperations().setInstanceVariable(getContext().getCoreLibrary().getGlobalVariablesObject(), "$!", getContext().getCoreLibrary().getNilObject());
return e.getValue();
} else {
throw e;
}
} finally {
getContext().getThrowTags().remove();
}
}
}
@CoreMethod(names = "class")
public abstract static class ClassNode extends CoreMethodNode {
public ClassNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ClassNode(ClassNode prev) {
super(prev);
}
public abstract RubyClass executeGetClass(Object value);
@Specialization
public RubyClass getClass(boolean value) {
notDesignedForCompilation();
if (value) {
return getContext().getCoreLibrary().getTrueClass();
} else {
return getContext().getCoreLibrary().getFalseClass();
}
}
@Specialization
public RubyClass getClass(int value) {
return getContext().getCoreLibrary().getFixnumClass();
}
@Specialization
public RubyClass getClass(long value) {
return getContext().getCoreLibrary().getFixnumClass();
}
@Specialization
public RubyClass getClass(double value) {
return getContext().getCoreLibrary().getFloatClass();
}
@Specialization
public RubyClass getClass(RubyBasicObject self) {
return self.getLogicalClass();
}
}
@CoreMethod(names = "clone")
public abstract static class CloneNode extends CoreMethodNode {
@Child protected DispatchHeadNode initializeCloneNode;
public CloneNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
// Calls private initialize_clone on the new copy.
initializeCloneNode = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
}
public CloneNode(CloneNode prev) {
super(prev);
initializeCloneNode = prev.initializeCloneNode;
}
@Specialization
public Object clone(VirtualFrame frame, RubyBasicObject self) {
notDesignedForCompilation();
final RubyBasicObject newObject = self.getLogicalClass().allocate(this);
// Copy the singleton class if any.
if (self.getMetaClass().isSingleton()) {
newObject.getSingletonClass(this).initCopy(self.getMetaClass());
}
newObject.getOperations().setInstanceVariables(newObject, self.getOperations().getInstanceVariables(self));
initializeCloneNode.call(frame, newObject, "initialize_clone", null, self);
return newObject;
}
}
@CoreMethod(names = "dup")
public abstract static class DupNode extends CoreMethodNode {
@Child protected DispatchHeadNode initializeDupNode;
public DupNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
// Calls private initialize_dup on the new copy.
initializeDupNode = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
}
public DupNode(DupNode prev) {
super(prev);
initializeDupNode = prev.initializeDupNode;
}
@Specialization
public Object dup(VirtualFrame frame, RubyBasicObject self) {
// This method is pretty crappy for compilation - it should improve with the OM
final RubyBasicObject newObject = self.getLogicalClass().allocate(this);
newObject.getOperations().setInstanceVariables(newObject, self.getOperations().getInstanceVariables(self));
initializeDupNode.call(frame, newObject, "initialize_dup", null, self);
return newObject;
}
}
@CoreMethod(names = "eql?", required = 1)
public abstract static class EqlNode extends BasicObjectNodes.ReferenceEqualNode {
public EqlNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public EqlNode(EqlNode prev) {
super(prev);
}
}
@CoreMethod(names = "eval", isModuleFunction = true, required = 1, optional = 3)
public abstract static class EvalNode extends CoreMethodNode {
@Child protected DispatchHeadNode toStr;
@Child protected BindingNode bindingNode;
public EvalNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toStr = new DispatchHeadNode(context);
}
public EvalNode(EvalNode prev) {
super(prev);
toStr = prev.toStr;
}
protected RubyBinding getCallerBinding(VirtualFrame frame) {
if (bindingNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
bindingNode = insert(KernelNodesFactory.BindingNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{}));
}
return bindingNode.executeBinding(frame);
}
@Specialization
public Object eval(VirtualFrame frame, RubyString source, UndefinedPlaceholder binding, UndefinedPlaceholder filename, UndefinedPlaceholder lineNumber) {
notDesignedForCompilation();
return eval(source, getCallerBinding(frame), filename, lineNumber);
}
@Specialization
public Object eval(RubyString source, RubyBinding binding, UndefinedPlaceholder filename, UndefinedPlaceholder lineNumber) {
notDesignedForCompilation();
return getContext().eval(source.getBytes(), binding, this);
}
@Specialization
public Object eval(RubyString source, RubyBinding binding, RubyString filename, UndefinedPlaceholder lineNumber) {
notDesignedForCompilation();
// TODO (nirvdrum Dec. 29, 2014) Do something with the supplied filename.
return getContext().eval(source.getBytes(), binding, this);
}
@Specialization
public Object eval(RubyString source, RubyBinding binding, RubyString filename, int lineNumber) {
notDesignedForCompilation();
// TODO (nirvdrum Dec. 29, 2014) Do something with the supplied filename and lineNumber.
return getContext().eval(source.getBytes(), binding, this);
}
@Specialization(guards = "!isRubyString(arguments[0])")
public Object eval(VirtualFrame frame, RubyBasicObject object, UndefinedPlaceholder binding, UndefinedPlaceholder filename, UndefinedPlaceholder lineNumber) {
notDesignedForCompilation();
return eval(frame, object, getCallerBinding(frame), filename, lineNumber);
}
@Specialization(guards = "!isRubyString(arguments[0])")
public Object eval(VirtualFrame frame, RubyBasicObject object, RubyBinding binding, UndefinedPlaceholder filename, UndefinedPlaceholder lineNumber) {
notDesignedForCompilation();
Object coerced;
try {
coerced = toStr.call(frame, object, "to_str", null);
} catch (RaiseException e) {
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getNoMethodErrorClass()) {
throw new RaiseException(
getContext().getCoreLibrary().typeError(
String.format("no implicit conversion of %s into String", object.getLogicalClass().getName()),
this));
} else {
throw e;
}
}
if (coerced instanceof RubyString) {
return getContext().eval(((RubyString) coerced).getBytes(), binding, this);
} else {
throw new RaiseException(
getContext().getCoreLibrary().typeError(
String.format("can't convert %s to String (%s#to_str gives %s)",
object.getLogicalClass().getName(),
object.getLogicalClass().getName(),
getContext().getCoreLibrary().getLogicalClass(coerced).getName()),
this));
}
}
@Specialization(guards = "!isRubyBinding(arguments[1])")
public Object eval(RubyBasicObject source, RubyBasicObject badBinding, UndefinedPlaceholder filename, UndefinedPlaceholder lineNumber) {
throw new RaiseException(
getContext().getCoreLibrary().typeError(
String.format("wrong argument type %s (expected binding)",
badBinding.getLogicalClass().getName()),
this));
}
}
@CoreMethod(names = "exec", isModuleFunction = true, required = 1, argumentsAsArray = true)
public abstract static class ExecNode extends CoreMethodNode {
public ExecNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ExecNode(ExecNode prev) {
super(prev);
}
@Specialization
public Object require(Object[] args) {
notDesignedForCompilation();
final String[] commandLine = new String[args.length];
for (int n = 0; n < args.length; n++) {
commandLine[n] = args[n].toString();
}
exec(getContext(), commandLine);
return null;
}
@TruffleBoundary
private static void exec(RubyContext context, String[] commandLine) {
final ProcessBuilder builder = new ProcessBuilder(commandLine);
builder.inheritIO();
final RubyHash env = context.getCoreLibrary().getENV();
for (KeyValue keyValue : HashOperations.verySlowToKeyValues(env)) {
builder.environment().put(keyValue.getKey().toString(), keyValue.getValue().toString());
}
Process process;
try {
process = builder.start();
} catch (IOException e) {
// TODO(cs): proper Ruby exception
throw new RuntimeException(e);
}
int exitCode;
while (true) {
try {
exitCode = process.waitFor();
break;
} catch (InterruptedException e) {
continue;
}
}
System.exit(exitCode);
}
}
@CoreMethod(names = "exit", isModuleFunction = true, optional = 1, lowerFixnumParameters = 0)
public abstract static class ExitNode extends CoreMethodNode {
public ExitNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ExitNode(ExitNode prev) {
super(prev);
}
@Specialization
public Object exit(UndefinedPlaceholder exitCode) {
notDesignedForCompilation();
getContext().shutdown();
System.exit(0);
return null;
}
@Specialization
public Object exit(int exitCode) {
notDesignedForCompilation();
getContext().shutdown();
System.exit(exitCode);
return null;
}
}
@CoreMethod(names = "exit!", isModuleFunction = true)
public abstract static class ExitBangNode extends CoreMethodNode {
public ExitBangNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ExitBangNode(ExitBangNode prev) {
super(prev);
}
@Specialization
public RubyNilClass exit() {
CompilerDirectives.transferToInterpreter();
System.exit(1);
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "extend", argumentsAsArray = true, required = 1)
public abstract static class ExtendNode extends CoreMethodNode {
public ExtendNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ExtendNode(ExtendNode prev) {
super(prev);
}
@Specialization
public RubyBasicObject extend(RubyBasicObject self, Object[] args) {
notDesignedForCompilation();
for (int n = 0; n < args.length; n++) {
self.extend((RubyModule) args[n], this);
}
return self;
}
}
@CoreMethod(names = "fork", isModuleFunction = true, argumentsAsArray = true)
public abstract static class ForkNode extends CoreMethodNode {
public ForkNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ForkNode(ForkNode prev) {
super(prev);
}
@Specialization
public Object fork(Object[] args) {
notDesignedForCompilation();
getContext().getWarnings().warn("Kernel#fork not implemented - defined to satisfy some metaprogramming in RubySpec");
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "freeze")
public abstract static class FreezeNode extends CoreMethodNode {
public FreezeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public FreezeNode(FreezeNode prev) {
super(prev);
}
@Specialization
public RubyBasicObject freeze(RubyBasicObject self) {
notDesignedForCompilation();
self.freeze();
return self;
}
}
@CoreMethod(names = "frozen?")
public abstract static class FrozenNode extends CoreMethodNode {
public FrozenNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public FrozenNode(FrozenNode prev) {
super(prev);
}
@Specialization
public boolean isFrozen(RubyBasicObject self) {
notDesignedForCompilation();
return self.isFrozen();
}
}
@CoreMethod(names = "gets", isModuleFunction = true)
public abstract static class GetsNode extends CoreMethodNode {
public GetsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public GetsNode(GetsNode prev) {
super(prev);
}
@Specialization
public RubyString gets(VirtualFrame frame) {
notDesignedForCompilation();
final RubyContext context = getContext();
final Frame caller = Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_WRITE, false);
final String line;
final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();
try {
line = gets(context);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
}
final RubyString rubyLine = context.makeString(line);
// Set the local variable $_ in the caller
final FrameSlot slot = caller.getFrameDescriptor().findFrameSlot("$_");
if (slot != null) {
caller.setObject(slot, WrapInThreadLocalNode.wrap(getContext(), rubyLine));
}
return rubyLine;
}
@TruffleBoundary
private static String gets(RubyContext context) throws IOException {
// TODO(CS): having some trouble interacting with JRuby stdin - so using this hack
final StringBuilder builder = new StringBuilder();
while (true) {
final int c = context.getRuntime().getInstanceConfig().getInput().read();
if (c == -1 || c == '\r' || c == '\n') {
break;
}
builder.append((char) c);
}
return builder.toString();
}
}
@CoreMethod(names = "hash")
public abstract static class HashNode extends CoreMethodNode {
public HashNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public HashNode(HashNode prev) {
super(prev);
}
@Specialization
public int hash(int value) {
// TODO(CS): should check this matches MRI
return value;
}
@Specialization
public int hash(long value) {
// TODO(CS): should check this matches MRI
return Long.valueOf(value).hashCode();
}
@Specialization
public int hash(double value) {
// TODO(CS): should check this matches MRI
return Double.valueOf(value).hashCode();
}
@Specialization
public int hash(RubyBasicObject self) {
return self.hashCode();
}
}
@CoreMethod(names = "initialize_copy", visibility = Visibility.PRIVATE, required = 1)
public abstract static class InitializeCopyNode extends CoreMethodNode {
public InitializeCopyNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public InitializeCopyNode(InitializeCopyNode prev) {
super(prev);
}
@Specialization
public Object initializeCopy(RubyBasicObject self, RubyBasicObject from) {
notDesignedForCompilation();
if (self.getLogicalClass() != from.getLogicalClass()) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().typeError("initialize_copy should take same class object", this));
}
return self;
}
}
@CoreMethod(names = {"initialize_dup", "initialize_clone"}, visibility = Visibility.PRIVATE, required = 1)
public abstract static class InitializeDupCloneNode extends CoreMethodNode {
@Child protected DispatchHeadNode initializeCopyNode;
public InitializeDupCloneNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
initializeCopyNode = DispatchHeadNode.onSelf(context);
}
public InitializeDupCloneNode(InitializeDupCloneNode prev) {
super(prev);
initializeCopyNode = prev.initializeCopyNode;
}
@Specialization
public Object initializeDup(VirtualFrame frame, RubyBasicObject self, RubyBasicObject from) {
return initializeCopyNode.call(frame, self, "initialize_copy", null, from);
}
}
@CoreMethod(names = "instance_of?", required = 1)
public abstract static class InstanceOfNode extends CoreMethodNode {
public InstanceOfNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public InstanceOfNode(InstanceOfNode prev) {
super(prev);
}
@TruffleBoundary
@Specialization
public boolean instanceOf(Object self, RubyClass rubyClass) {
// TODO(CS): fast path
return getContext().getCoreLibrary().getLogicalClass(self) == rubyClass;
}
}
@CoreMethod(names = "instance_variable_defined?", required = 1)
public abstract static class InstanceVariableDefinedNode extends CoreMethodNode {
public InstanceVariableDefinedNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public InstanceVariableDefinedNode(InstanceVariableDefinedNode prev) {
super(prev);
}
@Specialization
public boolean isInstanceVariableDefined(RubyBasicObject object, RubyString name) {
notDesignedForCompilation();
return object.isFieldDefined(RubyContext.checkInstanceVariableName(getContext(), name.toString(), this));
}
@Specialization
public boolean isInstanceVariableDefined(RubyBasicObject object, RubySymbol name) {
notDesignedForCompilation();
return object.isFieldDefined(RubyContext.checkInstanceVariableName(getContext(), name.toString(), this));
}
}
@CoreMethod(names = "instance_variable_get", required = 1)
public abstract static class InstanceVariableGetNode extends CoreMethodNode {
public InstanceVariableGetNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public InstanceVariableGetNode(InstanceVariableGetNode prev) {
super(prev);
}
@Specialization
public Object isInstanceVariableGet(RubyBasicObject object, RubyString name) {
notDesignedForCompilation();
return object.getInstanceVariable(RubyContext.checkInstanceVariableName(getContext(), name.toString(), this));
}
@Specialization
public Object isInstanceVariableGet(RubyBasicObject object, RubySymbol name) {
notDesignedForCompilation();
return object.getInstanceVariable(RubyContext.checkInstanceVariableName(getContext(), name.toString(), this));
}
}
@CoreMethod(names = "instance_variable_set", required = 2)
public abstract static class InstanceVariableSetNode extends CoreMethodNode {
public InstanceVariableSetNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public InstanceVariableSetNode(InstanceVariableSetNode prev) {
super(prev);
}
@Specialization
public Object isInstanceVariableSet(RubyBasicObject object, RubyString name, Object value) {
notDesignedForCompilation();
notDesignedForCompilation();
object.getOperations().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}
@Specialization
public Object isInstanceVariableSet(RubyBasicObject object, RubySymbol name, Object value) {
notDesignedForCompilation();
notDesignedForCompilation();
object.getOperations().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}
}
@CoreMethod(names = "instance_variables")
public abstract static class InstanceVariablesNode extends CoreMethodNode {
public InstanceVariablesNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public InstanceVariablesNode(InstanceVariablesNode prev) {
super(prev);
}
@Specialization
public RubyArray instanceVariables(RubyBasicObject self) {
notDesignedForCompilation();
final Object[] instanceVariableNames = self.getOperations().getFieldNames(self);
Arrays.sort(instanceVariableNames);
final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass());
for (Object name : instanceVariableNames) {
if (name instanceof String) {
array.slowPush(getContext().getSymbolTable().getSymbol((String) name));
}
}
return array;
}
}
@CoreMethod(names = "Integer", isModuleFunction = true, required = 1)
public abstract static class IntegerNode extends CoreMethodNode {
@Child protected DispatchHeadNode toInt;
public IntegerNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toInt = new DispatchHeadNode(context);
}
public IntegerNode(IntegerNode prev) {
super(prev);
toInt = prev.toInt;
}
@Specialization
public int integer(int value) {
return value;
}
@Specialization
public long integer(long value) {
return value;
}
@Specialization
public RubyBignum integer(RubyBignum value) {
return value;
}
@Specialization
public int integer(double value) {
return (int) value;
}
@Specialization
public Object integer(RubyString value) {
notDesignedForCompilation();
if (value.toString().length() == 0) {
return 0;
}
try {
return Integer.parseInt(value.toString());
} catch (NumberFormatException e) {
return bignum(new BigInteger(value.toString()));
}
}
@Specialization
public Object integer(VirtualFrame frame, Object value) {
if (toInt.doesRespondTo(frame, "to_int", value)) {
return toInt.call(frame, value, "to_int", null);
} else {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().typeErrorCantConvertInto(value, getContext().getCoreLibrary().getIntegerClass(), this));
}
}
}
@CoreMethod(names = {"is_a?", "kind_of?"}, required = 1)
public abstract static class IsANode extends CoreMethodNode {
public IsANode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public IsANode(IsANode prev) {
super(prev);
}
public abstract boolean executeBoolean(Object self, RubyClass rubyClass);
@Specialization
public boolean isA(RubyBasicObject self, RubyNilClass nil) {
return false;
}
@TruffleBoundary
@Specialization
public boolean isA(Object self, RubyClass rubyClass) {
// TODO(CS): fast path
notDesignedForCompilation();
return ModuleOperations.assignableTo(getContext().getCoreLibrary().getMetaClass(self), rubyClass);
}
}
@CoreMethod(names = "lambda", isModuleFunction = true, needsBlock = true)
public abstract static class LambdaNode extends CoreMethodNode {
public LambdaNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public LambdaNode(LambdaNode prev) {
super(prev);
}
@Specialization
public RubyProc proc(RubyProc block) {
notDesignedForCompilation();
return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.LAMBDA,
block.getSharedMethodInfo(), block.getCallTargetForMethods(), block.getCallTargetForMethods(),
block.getDeclarationFrame(), block.getDeclaringModule(), block.getMethod(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
}
}
@CoreMethod(names = "load", isModuleFunction = true, required = 1)
public abstract static class LoadNode extends CoreMethodNode {
public LoadNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public LoadNode(LoadNode prev) {
super(prev);
}
@Specialization
public boolean load(RubyString file) {
notDesignedForCompilation();
getContext().loadFile(file.toString(), this);
return true;
}
}
@CoreMethod(names = "local_variables", needsSelf = false)
public abstract static class LocalVariablesNode extends CoreMethodNode {
public LocalVariablesNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public LocalVariablesNode(LocalVariablesNode prev) {
super(prev);
}
@Specialization
public RubyArray localVariables() {
notDesignedForCompilation();
final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass());
for (Object name : Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_ONLY, false).getFrameDescriptor().getIdentifiers()) {
if (name instanceof String) {
array.slowPush(getContext().newSymbol((String) name));
}
}
return array;
}
}
@CoreMethod(names = "loop", isModuleFunction = true)
public abstract static class LoopNode extends CoreMethodNode {
@Child protected WhileNode whileNode;
public LoopNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
whileNode = new WhileNode(context, sourceSection, BooleanCastNodeFactory.create(context, sourceSection,
new BooleanLiteralNode(context, sourceSection, true)),
new YieldNode(context, getSourceSection(), new RubyNode[]{}, false)
);
}
public LoopNode(LoopNode prev) {
super(prev);
whileNode = prev.whileNode;
}
@Specialization
public Object loop(VirtualFrame frame) {
return whileNode.execute(frame);
}
}
@CoreMethod(names = "methods", optional = 1)
public abstract static class MethodsNode extends CoreMethodNode {
public MethodsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public MethodsNode(MethodsNode prev) {
super(prev);
}
@Specialization
public RubyArray methods(RubyBasicObject self, UndefinedPlaceholder unused) {
return methods(self, true);
}
@Specialization
public RubyArray methods(RubyBasicObject self, boolean includeInherited) {
notDesignedForCompilation();
final RubyArray array = new RubyArray(self.getContext().getCoreLibrary().getArrayClass());
Map<String, RubyMethod> methods;
if (includeInherited) {
methods = ModuleOperations.getAllMethods(self.getMetaClass());
} else {
methods = self.getMetaClass().getMethods();
}
for (RubyMethod method : methods.values()) {
if (method.getVisibility() == Visibility.PUBLIC || method.getVisibility() == Visibility.PROTECTED) {
array.slowPush(self.getContext().newSymbol(method.getName()));
}
}
return array;
}
}
@CoreMethod(names = "nil?", needsSelf = false)
public abstract static class NilNode extends CoreMethodNode {
public NilNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public NilNode(NilNode prev) {
super(prev);
}
@Specialization
public boolean nil() {
return false;
}
}
@CoreMethod(names = "object_id")
public abstract static class ObjectIDNode extends BasicObjectNodes.IDNode {
public ObjectIDNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ObjectIDNode(ObjectIDNode prev) {
super(prev);
}
}
/*
* Kernel#pretty_inspect is normally part of stdlib, in pp.rb, but we aren't able to execute
* that file yet. Instead we implement a very simple version here, which is the solution
* suggested by RubySpec.
*/
@CoreMethod(names = "pretty_inspect")
public abstract static class PrettyInspectNode extends CoreMethodNode {
@Child protected DispatchHeadNode inspectNode;
public PrettyInspectNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
inspectNode = DispatchHeadNode.onSelf(context);
}
public PrettyInspectNode(PrettyInspectNode prev) {
super(prev);
inspectNode = prev.inspectNode;
}
@Specialization
public Object prettyInspect(VirtualFrame frame, Object self) {
return inspectNode.call(frame, self, "inspect", null);
}
}
@CoreMethod(names = "print", isModuleFunction = true, argumentsAsArray = true)
public abstract static class PrintNode extends CoreMethodNode {
@Child protected DispatchHeadNode toS;
public PrintNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toS = new DispatchHeadNode(context);
}
public PrintNode(PrintNode prev) {
super(prev);
toS = prev.toS;
}
@Specialization
public RubyNilClass print(final VirtualFrame frame, final Object[] args) {
final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();
try {
for (Object arg : args) {
write(((RubyString) toS.call(frame, arg, "to_s", null)).getBytes().bytes());
}
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
}
return getContext().getCoreLibrary().getNilObject();
}
@TruffleBoundary
private void write(byte[] bytes) {
try{
getContext().getRuntime().getInstanceConfig().getOutput().write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@CoreMethod(names = "private_methods", optional = 1)
public abstract static class PrivateMethodsNode extends CoreMethodNode {
public PrivateMethodsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public PrivateMethodsNode(PrivateMethodsNode prev) {
super(prev);
}
@Specialization
public RubyArray private_methods(RubyBasicObject self, UndefinedPlaceholder unused) {
return private_methods(self, true);
}
@Specialization
public RubyArray private_methods(RubyBasicObject self, boolean includeInherited) {
notDesignedForCompilation();
final RubyArray array = new RubyArray(self.getContext().getCoreLibrary().getArrayClass());
Map<String, RubyMethod> methods;
if (includeInherited) {
methods = ModuleOperations.getAllMethods(self.getMetaClass());
} else {
methods = self.getMetaClass().getMethods();
}
for (RubyMethod method : methods.values()) {
if (method.getVisibility() == Visibility.PRIVATE) {
array.slowPush(self.getContext().newSymbol(method.getName()));
}
}
return array;
}
}
@CoreMethod(names = "proc", isModuleFunction = true, needsBlock = true)
public abstract static class ProcNode extends CoreMethodNode {
public ProcNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ProcNode(ProcNode prev) {
super(prev);
}
@Specialization
public RubyProc proc(RubyProc block) {
notDesignedForCompilation();
return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.PROC,
block.getSharedMethodInfo(), block.getCallTarget(), block.getCallTargetForMethods(), block.getDeclarationFrame(),
block.getDeclaringModule(), block.getMethod(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
}
}
@CoreMethod(names = "public_methods", optional = 1)
public abstract static class PublicMethodsNode extends CoreMethodNode {
public PublicMethodsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public PublicMethodsNode(PublicMethodsNode prev) {
super(prev);
}
@Specialization
public RubyArray methods(RubyBasicObject self, boolean includeInherited) {
notDesignedForCompilation();
if (!includeInherited) {
getContext().getRuntime().getWarnings().warn(IRubyWarnings.ID.TRUFFLE, Truffle.getRuntime().getCallerFrame().getCallNode().getEncapsulatingSourceSection().getSource().getName(), Truffle.getRuntime().getCallerFrame().getCallNode().getEncapsulatingSourceSection().getStartLine(), "Object#methods always returns inherited methods at the moment");
}
return methods(self, UndefinedPlaceholder.INSTANCE);
}
@Specialization
public RubyArray methods(RubyBasicObject self, UndefinedPlaceholder includeInherited) {
notDesignedForCompilation();
final RubyArray array = new RubyArray(self.getContext().getCoreLibrary().getArrayClass());
final Map<String, RubyMethod> methods = self.getMetaClass().getMethods();
for (RubyMethod method : methods.values()) {
if (method.getVisibility() == Visibility.PUBLIC) {
array.slowPush(self.getContext().newSymbol(method.getName()));
}
}
return array;
}
}
@CoreMethod(names = "raise", isModuleFunction = true, optional = 3)
public abstract static class RaiseNode extends CoreMethodNode {
@Child protected DispatchHeadNode initialize;
public RaiseNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
initialize = new DispatchHeadNode(context);
}
public RaiseNode(RaiseNode prev) {
super(prev);
initialize = prev.initialize;
}
@Specialization
public Object raise(VirtualFrame frame, UndefinedPlaceholder undefined1, UndefinedPlaceholder undefined2, Object undefined3) {
notDesignedForCompilation();
return raise(frame, getContext().getCoreLibrary().getRuntimeErrorClass(), getContext().makeString("re-raised - don't have the current exception yet!"), undefined1);
}
@Specialization
public Object raise(VirtualFrame frame, RubyString message, UndefinedPlaceholder undefined1, Object undefined2) {
notDesignedForCompilation();
return raise(frame, getContext().getCoreLibrary().getRuntimeErrorClass(), message, undefined1);
}
@Specialization
public Object raise(VirtualFrame frame, RubyClass exceptionClass, UndefinedPlaceholder undefined1, Object undefined2) {
notDesignedForCompilation();
return raise(frame, exceptionClass, getContext().makeString(""), undefined1);
}
@Specialization
public Object raise(VirtualFrame frame, RubyClass exceptionClass, RubyString message, Object undefined1) {
notDesignedForCompilation();
final Object exception = exceptionClass.allocate(this);
initialize.call(frame, exception, "initialize", null, message);
if (!(exception instanceof RubyException)) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().typeError("exception class/object expected", this));
}
throw new RaiseException((RubyException) exception);
}
@Specialization
public Object raise(RubyException exception, UndefinedPlaceholder undefined1, Object undefined2) {
throw new RaiseException(exception);
}
}
@CoreMethod(names = "rand", isModuleFunction = true, optional = 1)
public abstract static class RandNode extends CoreMethodNode {
public RandNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public RandNode(RandNode prev) {
super(prev);
}
@Specialization
public double rand(UndefinedPlaceholder undefined) {
return getContext().getRandom().nextDouble();
}
@Specialization(guards = "isZero")
public double randZero(int max) {
return getContext().getRandom().nextDouble();
}
@Specialization(guards = "isNonZero")
public int randNonZero(int max) {
return getContext().getRandom().nextInt(max);
}
protected boolean isZero(int max) {
return max == 0;
}
protected boolean isNonZero(int max) {
return max != 0;
}
}
@CoreMethod(names = "require", isModuleFunction = true, required = 1)
public abstract static class RequireNode extends CoreMethodNode {
public RequireNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public RequireNode(RequireNode prev) {
super(prev);
}
@Specialization
public boolean require(RubyString feature) {
notDesignedForCompilation();
try {
getContext().getFeatureManager().require(feature.toString(), this);
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}
}
@CoreMethod(names = "require_relative", isModuleFunction = true, required = 1)
public abstract static class RequireRelativeNode extends CoreMethodNode {
public RequireRelativeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public RequireRelativeNode(RequireRelativeNode prev) {
super(prev);
}
@Specialization
public boolean require(VirtualFrame frame, RubyString feature) {
notDesignedForCompilation();
final String sourcePath = Truffle.getRuntime().getCallerFrame().getCallNode().getEncapsulatingSourceSection().getSource().getPath();
final String directoryPath = new File(sourcePath).getParent();
try {
getContext().getFeatureManager().requireInPath(directoryPath, feature.toString(), this);
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}
}
@CoreMethod(names = "respond_to?", required = 1, optional = 1)
public abstract static class RespondToNode extends CoreMethodNode {
@Child protected DispatchHeadNode dispatch;
@Child protected DispatchHeadNode dispatchIgnoreVisibility;
public RespondToNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
dispatch = new DispatchHeadNode(context, false, Dispatch.MissingBehavior.RETURN_MISSING);
dispatchIgnoreVisibility = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.RETURN_MISSING);
if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) {
dispatch.forceUncached();
dispatchIgnoreVisibility.forceUncached();
}
}
public RespondToNode(RespondToNode prev) {
super(prev);
dispatch = prev.dispatch;
dispatchIgnoreVisibility = prev.dispatchIgnoreVisibility;
}
public abstract boolean executeDoesRespondTo(VirtualFrame frame, Object object, Object name, boolean includePrivate);
@Specialization
public boolean doesRespondTo(VirtualFrame frame, Object object, RubyString name, UndefinedPlaceholder checkVisibility) {
return dispatch.doesRespondTo(frame, name, object);
}
@Specialization
public boolean doesRespondTo(VirtualFrame frame, Object object, RubyString name, boolean ignoreVisibility) {
if (ignoreVisibility) {
return dispatchIgnoreVisibility.doesRespondTo(frame, name, object);
} else {
return dispatch.doesRespondTo(frame, name, object);
}
}
@Specialization
public boolean doesRespondTo(VirtualFrame frame, Object object, RubySymbol name, UndefinedPlaceholder checkVisibility) {
return dispatch.doesRespondTo(frame, name, object);
}
@Specialization
public boolean doesRespondTo(VirtualFrame frame, Object object, RubySymbol name, boolean ignoreVisibility) {
if (ignoreVisibility) {
return dispatchIgnoreVisibility.doesRespondTo(frame, name, object);
} else {
return dispatch.doesRespondTo(frame, name, object);
}
}
}
@CoreMethod(names = "respond_to_missing?", required = 1, optional = 1, visibility = Visibility.PRIVATE)
public abstract static class RespondToMissingNode extends CoreMethodNode {
public RespondToMissingNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public RespondToMissingNode(RespondToMissingNode prev) {
super(prev);
}
@Specialization
public boolean doesRespondToMissing(Object object, RubyString name, UndefinedPlaceholder includeAll) {
return false;
}
@Specialization
public boolean doesRespondToMissing(Object object, RubySymbol name, UndefinedPlaceholder includeAll) {
return false;
}
@Specialization
public boolean doesRespondToMissing(Object object, RubySymbol name, boolean includeAll) {
return false;
}
@Specialization
public boolean doesRespondToMissing(Object object, RubyString name, boolean includeAll) {
return false;
}
}
@CoreMethod(names = "send", needsBlock = true, required = 1, argumentsAsArray = true)
public abstract static class SendNode extends BasicObjectNodes.SendNode {
public SendNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SendNode(SendNode prev) {
super(prev);
}
}
@CoreMethod(names = "set_trace_func", isModuleFunction = true, required = 1)
public abstract static class SetTraceFuncNode extends CoreMethodNode {
public SetTraceFuncNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SetTraceFuncNode(SetTraceFuncNode prev) {
super(prev);
}
@Specialization
public RubyNilClass setTraceFunc(RubyNilClass nil) {
notDesignedForCompilation();
getContext().getTraceManager().setTraceFunc(null);
return nil;
}
@Specialization
public RubyProc setTraceFunc(RubyProc traceFunc) {
notDesignedForCompilation();
getContext().getTraceManager().setTraceFunc(traceFunc);
return traceFunc;
}
}
@CoreMethod(names = "singleton_class")
public abstract static class SingletonClassMethodNode extends CoreMethodNode {
@Child protected SingletonClassNode singletonClassNode;
public SingletonClassMethodNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
singletonClassNode = SingletonClassNodeFactory.create(context, sourceSection, null);
}
public SingletonClassMethodNode(SingletonClassMethodNode prev) {
super(prev);
singletonClassNode = prev.singletonClassNode;
}
@Specialization
public RubyClass singletonClass(Object self) {
return singletonClassNode.executeSingletonClass(self);
}
}
@CoreMethod(names = "singleton_methods", optional = 1)
public abstract static class SingletonMethodsNode extends CoreMethodNode {
public SingletonMethodsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SingletonMethodsNode(SingletonMethodsNode prev) {
super(prev);
}
@Specialization
public RubyArray singletonMethods(RubyBasicObject self, boolean includeInherited) {
notDesignedForCompilation();
final RubyArray array = new RubyArray(self.getContext().getCoreLibrary().getArrayClass());
final Collection<RubyMethod> methods;
if (includeInherited) {
methods = ModuleOperations.getAllMethods(self.getSingletonClass(this)).values();
} else {
methods = self.getSingletonClass(this).getMethods().values();
}
for (RubyMethod method : methods) {
array.slowPush(RubySymbol.newSymbol(self.getContext(), method.getName()));
}
return array;
}
@Specialization
public RubyArray singletonMethods(RubyBasicObject self, UndefinedPlaceholder includeInherited) {
return singletonMethods(self, false);
}
}
@CoreMethod(names = "String", isModuleFunction = true, required = 1)
public abstract static class StringNode extends CoreMethodNode {
@Child protected DispatchHeadNode toS;
public StringNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toS = new DispatchHeadNode(context);
}
public StringNode(StringNode prev) {
super(prev);
toS = prev.toS;
}
@Specialization
public RubyString string(RubyString value) {
return value;
}
@Specialization(guards = "!isRubyString")
public Object string(VirtualFrame frame, Object value) {
return toS.call(frame, value, "to_s", null);
}
}
@CoreMethod(names = "sleep", isModuleFunction = true, optional = 1)
public abstract static class SleepNode extends CoreMethodNode {
public SleepNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SleepNode(SleepNode prev) {
super(prev);
}
@Specialization
public double sleep(UndefinedPlaceholder duration) {
return doSleep(0);
}
@Specialization
public double sleep(int duration) {
return doSleep(duration);
}
@Specialization
public double sleep(long duration) {
return doSleep(duration);
}
@Specialization
public double sleep(double duration) {
return doSleep(duration);
}
@TruffleBoundary
private double doSleep(final double duration) {
final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();
try {
final long start = System.nanoTime();
try {
Thread.sleep((long) (duration * 1000));
} catch (InterruptedException e) {
// Ignore interruption
}
final long end = System.nanoTime();
return (end - start) / 1e9;
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
}
}
}
@CoreMethod(names = "sprintf", isModuleFunction = true, argumentsAsArray = true)
public abstract static class SPrintfNode extends CoreMethodNode {
public SPrintfNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SPrintfNode(SPrintfNode prev) {
super(prev);
}
@Specialization
public RubyString sprintf(Object[] args) {
notDesignedForCompilation();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final PrintStream printStream = new PrintStream(outputStream);
if (args.length > 0) {
final String format = args[0].toString();
final List<Object> values = Arrays.asList(args).subList(1, args.length);
final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();
try {
StringFormatter.format(getContext(), printStream, format, values);
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
}
}
return getContext().makeString(new ByteList(outputStream.toByteArray()));
}
}
@CoreMethod(names = "system", isModuleFunction = true, argumentsAsArray = true)
public abstract static class SystemNode extends CoreMethodNode {
public SystemNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public SystemNode(SystemNode prev) {
super(prev);
}
@Specialization
public Object fork(Object[] args) {
notDesignedForCompilation();
getContext().getWarnings().warn("Kernel#system not implemented - defined to satisfy some metaprogramming in RubySpec");
return getContext().getCoreLibrary().getNilObject();
}
}
@CoreMethod(names = "taint")
public abstract static class TaintNode extends CoreMethodNode {
@Child protected WriteHeadObjectFieldNode writeTaintNode;
public TaintNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeTaintNode = new WriteHeadObjectFieldNode(RubyBasicObject.TAINTED_IDENTIFIER);
}
public TaintNode(TaintNode prev) {
super(prev);
writeTaintNode = prev.writeTaintNode;
}
@Specialization
public Object taint(boolean object) {
return frozen(object);
}
@Specialization
public Object taint(int object) {
return frozen(object);
}
@Specialization
public Object taint(long object) {
return frozen(object);
}
@Specialization
public Object taint(double object) {
return frozen(object);
}
private Object frozen(Object object) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(getContext().getCoreLibrary().getLogicalClass(object).getName(), this));
}
@Specialization
public Object taint(RubyBasicObject object) {
writeTaintNode.execute(object, true);
return object;
}
}
@CoreMethod(names = "tainted?")
public abstract static class TaintedNode extends CoreMethodNode {
@Child protected ReadHeadObjectFieldNode readTaintNode;
public TaintedNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
readTaintNode = new ReadHeadObjectFieldNode(RubyBasicObject.TAINTED_IDENTIFIER);
}
public TaintedNode(TaintedNode prev) {
super(prev);
readTaintNode = prev.readTaintNode;
}
@Specialization
public boolean tainted(boolean object) {
return false;
}
@Specialization
public boolean tainted(int object) {
return false;
}
@Specialization
public boolean tainted(long object) {
return false;
}
@Specialization
public boolean tainted(double object) {
return false;
}
@Specialization
public boolean tainted(RubyBasicObject object) {
try {
return readTaintNode.isSet(object) && readTaintNode.executeBoolean(object);
} catch (UnexpectedResultException e) {
throw new UnsupportedOperationException();
}
}
}
@CoreMethod(names = "throw", isModuleFunction = true, required = 1, optional = 1)
public abstract static class ThrowNode extends CoreMethodNode {
public ThrowNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ThrowNode(ThrowNode prev) {
super(prev);
}
@Specialization
public Object doThrow(Object tag, UndefinedPlaceholder value) {
return doThrow(tag, (Object) value);
}
@Specialization
public Object doThrow(Object tag, Object value) {
notDesignedForCompilation();
if (!getContext().getThrowTags().contains(tag)) {
throw new RaiseException(new RubyException(
getContext().getCoreLibrary().getArgumentErrorClass(),
getContext().makeString(String.format("uncaught throw \"%s\"", tag)),
RubyCallStack.getBacktrace(this)));
}
if (value instanceof UndefinedPlaceholder) {
throw new ThrowException(tag, getContext().getCoreLibrary().getNilObject());
} else {
throw new ThrowException(tag, value);
}
}
}
public abstract static class ToHexStringNode extends CoreMethodNode {
public ToHexStringNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}
public ToHexStringNode(ToHexStringNode prev) {
super(prev);
}
public abstract String executeToHexString(Object value);
@Specialization
public String toHexString(int value) {
return toHexString((long) value);
}
@Specialization
public String toHexString(long value) {
return Long.toHexString(value);
}
@Specialization
public String toHexString(RubyBignum value) {
return value.toHexString();
}
}
@CoreMethod(names = {"to_s", "inspect"})
public abstract static class ToSNode extends CoreMethodNode {
@Child protected ClassNode classNode;
@Child protected BasicObjectNodes.IDNode idNode;
@Child protected ToHexStringNode toHexStringNode;
public ToSNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
classNode = KernelNodesFactory.ClassNodeFactory.create(context, sourceSection, new RubyNode[]{null});
idNode = BasicObjectNodesFactory.IDNodeFactory.create(context, sourceSection, new RubyNode[]{null});
toHexStringNode = KernelNodesFactory.ToHexStringNodeFactory.create(context, sourceSection, new RubyNode[]{null});
}
public abstract RubyString executeToS(VirtualFrame frame, Object self);
@Specialization
public RubyString toS(VirtualFrame frame, Object self) {
notDesignedForCompilation();
String className = classNode.executeGetClass(self).getName();
if (className == null) {
className = "Class";
}
Object id = idNode.executeObjectID(frame, self);
String hexID = toHexStringNode.executeToHexString(id);
return getContext().makeString("#<" + className + ":0x" + hexID + ">");
}
}
@CoreMethod(names = "untaint")
public abstract static class UntaintNode extends CoreMethodNode {
@Child protected WriteHeadObjectFieldNode writeTaintNode;
public UntaintNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeTaintNode = new WriteHeadObjectFieldNode(RubyBasicObject.TAINTED_IDENTIFIER);
}
public UntaintNode(UntaintNode prev) {
super(prev);
writeTaintNode = prev.writeTaintNode;
}
@Specialization
public Object taint(boolean object) {
return frozen(object);
}
@Specialization
public Object taint(int object) {
return frozen(object);
}
@Specialization
public Object taint(long object) {
return frozen(object);
}
@Specialization
public Object taint(double object) {
return frozen(object);
}
private Object frozen(Object object) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().frozenError(getContext().getCoreLibrary().getLogicalClass(object).getName(), this));
}
@Specialization
public Object taint(RubyBasicObject object) {
writeTaintNode.execute(object, false);
return object;
}
}
}