Skip to content

Commit

Permalink
Showing 45 changed files with 934 additions and 403 deletions.
3 changes: 0 additions & 3 deletions core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -127,7 +127,6 @@ public class Options {
public static final Option<Boolean> IR_WRITING_DEBUG = bool(IR, "ir.writing.debug", false, "Debug writing JRuby IR file.");
public static final Option<String> IR_INLINE_COMPILER_PASSES = string(IR, "ir.inline_passes", "Specify comma delimeted list of passes to run after inlining a method.");

public static final Option<Boolean> TRUFFLE_PRINT_RUNTIME = bool(TRUFFLE, "truffle.printRuntime", false, "Print the name of the Truffle runtime on startup.");
public static final Option<Boolean> TRUFFLE_RUNTIME_VERSION_CHECK = bool(TRUFFLE, "truffle.runtime.version_check", true, "Check the version of Truffle supplied by the JVM before starting.");

public static final Option<Integer> TRUFFLE_DISPATCH_POLYMORPHIC_MAX = integer(TRUFFLE, "truffle.dispatch.polymorphic.max", 8, "Maximum size of a polymorphic call site cache.");
@@ -136,8 +135,6 @@ public class Options {
public static final Option<Integer> TRUFFLE_ARRAYS_SMALL = integer(TRUFFLE, "truffle.arrays.small", 3, "Maximum size of an Array to consider small for optimisations.");
public static final Option<Integer> TRUFFLE_HASH_PACKED_ARRAY_MAX = integer(TRUFFLE, "truffle.hash.packed_array_max", 3, "Maximum size of a Hash to use with the packed array storage strategy.");

public static final Option<Boolean> TRUFFLE_LOAD_CORE = bool(TRUFFLE, "truffle.load_core", true, "Load the Truffle core library.");

public static final Option<Integer> TRUFFLE_PASSALOT = integer(TRUFFLE, "truffle.passalot", 0, "Probabilty between 0 and 100 to randomly insert Thread.pass at a given line.");
public static final Option<Integer> TRUFFLE_INSTRUMENTATION_SERVER_PORT = integer(TRUFFLE, "truffle.instrumentation_server_port", 0, "Port number to run an HTTP server on that provides instrumentation services");
public static final Option<String> TRUFFLE_TRANSLATOR_PRINT_AST = string(TRUFFLE, "truffle.translator.print_asts", "", "Comma delimited list of method names to print the AST of after translation.");
1 change: 0 additions & 1 deletion spec/truffle/tags/core/file/link_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:File.link link a file with another
fails:File.link raises an Errno::EEXIST if the target already exists
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/file/new_tags.txt

This file was deleted.

2 changes: 2 additions & 0 deletions spec/truffle/tags/core/file/stat/grpowned_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
fails:File::Stat#grpowned? takes non primary groups into account
fails(windows):File::Stat#grpowned? returns false if the file exist
fails:File::Stat#grpowned? returns true if the file exist
fails:File::Stat#grpowned? accepts an object that has a #to_path method
1 change: 0 additions & 1 deletion spec/truffle/tags/core/file/symlink_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:File.symlink creates a symlink between a source and target file
fails:File.symlink raises an Errno::EEXIST if the target already exists
2 changes: 2 additions & 0 deletions spec/truffle/tags/core/filetest/grpowned_tags.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
fails:FileTest.grpowned? takes non primary groups into account
fails:FileTest.grpowned? returns true if the file exist
fails:FileTest.grpowned? accepts an object that has a #to_path method
9 changes: 9 additions & 0 deletions spec/truffle/tags/core/thread/alive_tags.txt
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
fails:Thread#alive? describes a dying sleeping thread
fails(flaky):Thread#alive? can check it's own status
fails(flaky):Thread#alive? describes a running thread
fails(flaky):Thread#alive? describes a sleeping thread
fails(flaky):Thread#alive? describes a blocked thread
fails(flaky):Thread#alive? describes a completed thread
fails(flaky):Thread#alive? describes a killed thread
fails(flaky):Thread#alive? describes a thread with an uncaught exception
fails(flaky):Thread#alive? describes a dying running thread
fails(flaky):Thread#alive? returns true for a killed but still running thread
10 changes: 1 addition & 9 deletions truffle/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@
*/
package org.jruby.truffle;

import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.source.BytesDecoder;
import com.oracle.truffle.api.source.Source;
@@ -31,7 +30,6 @@
import org.jruby.truffle.runtime.core.RubyException;
import org.jruby.truffle.translator.NodeWrapper;
import org.jruby.truffle.translator.TranslatorDriver;
import org.jruby.util.cli.Options;

import java.io.File;
import java.io.IOException;
@@ -41,8 +39,6 @@

public class TruffleBridgeImpl implements TruffleBridge {

private static final boolean PRINT_RUNTIME = Options.TRUFFLE_PRINT_RUNTIME.load();

private final org.jruby.Ruby runtime;
private final RubyContext truffleContext;

@@ -58,10 +54,6 @@ public TruffleBridgeImpl(org.jruby.Ruby runtime) {

@Override
public void init() {
if (PRINT_RUNTIME) {
runtime.getInstanceConfig().getError().println("jruby: using " + Truffle.getRuntime().getName());
}

// Bring in core method nodes

RubyClass rubyObjectClass = truffleContext.getCoreLibrary().getObjectClass();
@@ -176,7 +168,7 @@ public Object execute(final Object self, final org.jruby.ast.RootNode rootNode)
}

public Object execute(final TranslatorDriver.ParserContext parserContext, final Object self, final MaterializedFrame parentFrame, final org.jruby.ast.RootNode rootNode) {
truffleContext.getCoreLibrary().getGlobalVariablesObject().getOperations().setInstanceVariable(
truffleContext.getCoreLibrary().getGlobalVariablesObject().getObjectType().setInstanceVariable(
truffleContext.getCoreLibrary().getGlobalVariablesObject(), "$0",
truffleContext.toTruffle(runtime.getGlobalVariables().get("$0")));

Original file line number Diff line number Diff line change
@@ -83,7 +83,6 @@ public Object coerceBasicObject(VirtualFrame frame, RubyBasicObject object) {
return coerceObject(frame, object);
}

@CompilerDirectives.TruffleBoundary
private Object coerceObject(VirtualFrame frame, Object object) {
if (toIntNode == null) {
CompilerDirectives.transferToInterpreter();
@@ -97,9 +96,7 @@ private Object coerceObject(VirtualFrame frame, Object object) {
} catch (RaiseException e) {
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getNoMethodErrorClass()) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(
getContext().getCoreLibrary().typeErrorNoImplicitConversion(object, "Integer", this));
throw new RaiseException(getContext().getCoreLibrary().typeErrorNoImplicitConversion(object, "Integer", this));
} else {
throw e;
}
Original file line number Diff line number Diff line change
@@ -9,11 +9,9 @@
*/
package org.jruby.truffle.nodes.control;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;
import org.jruby.truffle.nodes.RubyNode;
@@ -85,7 +83,7 @@ private Object handleException(VirtualFrame frame, RaiseException exception) {
CompilerDirectives.transferToInterpreter();

final RubyBasicObject threadLocals = getContext().getThreadManager().getCurrentThread().getThreadLocals();
threadLocals.getOperations().setInstanceVariable(threadLocals, "$!", exception.getRubyException());
threadLocals.getObjectType().setInstanceVariable(threadLocals, "$!", exception.getRubyException());

for (RescueNode rescue : rescueParts) {
if (rescue.canHandle(frame, exception.getRubyException())) {
Original file line number Diff line number Diff line change
@@ -435,8 +435,6 @@ public Object append(Object store, int index, RubyArray array) {
CompilerDirectives.transferToInterpreterAndInvalidate();

if (otherStore instanceof int[]) {
// TODO CS 5-Feb-15 hack to get things working with empty int[] store

for (int n = 0; n < array.getSize(); n++) {
((Object[]) store)[index + n] = ((int[]) otherStore)[n];
}
@@ -445,20 +443,16 @@ public Object append(Object store, int index, RubyArray array) {
}

if (otherStore instanceof long[]) {
// TODO CS 5-Feb-15 hack to get things working with empty long[] store

if (((long[]) otherStore).length > 0) {
throw new UnsupportedOperationException();
for (int n = 0; n < array.getSize(); n++) {
((Object[]) store)[index + n] = ((long[]) otherStore)[n];
}

return store;
}

if (otherStore instanceof double[]) {
// TODO CS 5-Feb-15 hack to get things working with empty double[] store

if (((double[]) otherStore).length > 0) {
throw new UnsupportedOperationException();
for (int n = 0; n < array.getSize(); n++) {
((Object[]) store)[index + n] = ((double[]) otherStore)[n];
}

return store;
Original file line number Diff line number Diff line change
@@ -674,7 +674,6 @@ public Object initialize(RubyHash hash, Object defaultValue, RubyProc block) {

}

// TODO CS 8-Mar-15 visibility = Visibility.PRIVATE
@CoreMethod(names = {"initialize_copy", "replace"}, required = 1, raiseIfFrozenSelf = true)
@ImportStatic(HashGuards.class)
public abstract static class InitializeCopyNode extends CoreMethodArrayArgumentsNode {
152 changes: 126 additions & 26 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -9,18 +9,20 @@
*/
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.CallTarget;
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.CreateCast;
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.dsl.*;
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;
import org.jcodings.Encoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.RubyThread.Status;
import org.jruby.common.IRubyWarnings;
import org.jruby.runtime.Visibility;
@@ -36,17 +38,19 @@
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.rubinius.ObjectPrimitiveNodes;
import org.jruby.truffle.nodes.rubinius.ObjectPrimitiveNodesFactory;
import org.jruby.truffle.pack.parser.FormatParser;
import org.jruby.truffle.pack.runtime.PackResult;
import org.jruby.truffle.pack.runtime.exceptions.*;
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.core.*;
import org.jruby.truffle.runtime.hash.HashOperations;
import org.jruby.truffle.runtime.hash.KeyValue;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.subsystems.FeatureManager;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingActionWithoutGlobalLock;
import org.jruby.truffle.runtime.util.ArrayUtils;
import org.jruby.util.ByteList;

import java.io.*;
@@ -373,7 +377,7 @@ public RubyBasicObject copy(VirtualFrame frame, RubyBasicObject self) {

final RubyBasicObject newObject = self.getLogicalClass().allocate(this);

newObject.getOperations().setInstanceVariables(newObject, self.getOperations().getInstanceVariables(self));
newObject.getObjectType().setInstanceVariables(newObject, self.getObjectType().getInstanceVariables(self));

return newObject;
}
@@ -885,14 +889,14 @@ public InstanceVariableSetNode(RubyContext context, SourceSection sourceSection)
@TruffleBoundary
@Specialization
public Object instanceVariableSet(RubyBasicObject object, RubyString name, Object value) {
object.getOperations().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
object.getObjectType().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}

@TruffleBoundary
@Specialization
public Object instanceVariableSet(RubyBasicObject object, RubySymbol name, Object value) {
object.getOperations().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
object.getObjectType().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}

@@ -909,7 +913,7 @@ public InstanceVariablesNode(RubyContext context, SourceSection sourceSection) {
public RubyArray instanceVariables(RubyBasicObject self) {
notDesignedForCompilation();

final Object[] instanceVariableNames = self.getOperations().getFieldNames(self);
final Object[] instanceVariableNames = self.getObjectType().getFieldNames(self);

Arrays.sort(instanceVariableNames);

@@ -1604,35 +1608,131 @@ public Long block() throws InterruptedException {

}

@CoreMethod(names = { "sprintf", "format" }, isModuleFunction = true, argumentsAsArray = true)
public abstract static class SPrintfNode extends CoreMethodArrayArgumentsNode {
@CoreMethod(names = {"format", "sprintf"}, isModuleFunction = true, argumentsAsArray = true, required = 1, taintFromParameter = 0)
public abstract static class FormatNode extends CoreMethodArrayArgumentsNode {

public SPrintfNode(RubyContext context, SourceSection sourceSection) {
@Child private TaintNode taintNode;

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

@TruffleBoundary
@Specialization
public RubyString sprintf(Object[] args) {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@Specialization(guards = {"isRubyString(firstArgument(arguments))", "byteListsEqual(asRubyString(firstArgument(arguments)), cachedFormat)"})
public RubyString formatCached(
VirtualFrame frame,
Object[] arguments,
@Cached("privatizeByteList(asRubyString(firstArgument(arguments)))") ByteList cachedFormat,
@Cached("create(compileFormat(asRubyString(firstArgument(arguments))))") DirectCallNode callPackNode) {
final Object[] store = ArrayUtils.extractRange(arguments, 1, arguments.length);

final PrintStream printStream;
final PackResult result;

try {
printStream = new PrintStream(outputStream, true, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
result = (PackResult) callPackNode.call(frame, new Object[]{store, store.length});
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
}

if (args.length > 0) {
final String format = args[0].toString();
final List<Object> values = Arrays.asList(args).subList(1, args.length);
return finishFormat(cachedFormat, result);
}

@Specialization(guards = "isRubyString(firstArgument(arguments))", contains = "formatCached")
public RubyString formatUncached(
VirtualFrame frame,
Object[] arguments,
@Cached("create()") IndirectCallNode callPackNode) {
final RubyString format = (RubyString) arguments[0];
final Object[] store = ArrayUtils.extractRange(arguments, 1, arguments.length);

final PackResult result;

StringFormatter.format(getContext(), printStream, format, values);
try {
result = (PackResult) callPackNode.call(frame, compileFormat((RubyString) arguments[0]), new Object[]{store, store.length});
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
}

return getContext().makeString(new ByteList(outputStream.toByteArray()));
return finishFormat(format.getByteList(), result);
}

private RuntimeException handleException(PackException exception) {
try {
throw exception;
} catch (TooFewArgumentsException e) {
return new RaiseException(getContext().getCoreLibrary().argumentError("too few arguments", this));
} catch (NoImplicitConversionException e) {
return new RaiseException(getContext().getCoreLibrary().typeErrorNoImplicitConversion(e.getObject(), e.getTarget(), this));
} catch (OutsideOfStringException e) {
return new RaiseException(getContext().getCoreLibrary().argumentError("X outside of string", this));
} catch (CantCompressNegativeException e) {
return new RaiseException(getContext().getCoreLibrary().argumentError("can't compress negative numbers", this));
} catch (RangeException e) {
return new RaiseException(getContext().getCoreLibrary().rangeError(e.getMessage(), this));
} catch (CantConvertException e) {
return new RaiseException(getContext().getCoreLibrary().typeError(e.getMessage(), this));
}
}

private RubyString finishFormat(ByteList format, PackResult result) {
final RubyString string = getContext().makeString(new ByteList(result.getOutput(), 0, result.getOutputLength()));

if (format.length() == 0) {
string.forceEncoding(USASCIIEncoding.INSTANCE);
} else {
switch (result.getEncoding()) {
case DEFAULT:
case ASCII_8BIT:
break;
case US_ASCII:
string.forceEncoding(USASCIIEncoding.INSTANCE);
break;
case UTF_8:
string.forceEncoding(UTF8Encoding.INSTANCE);
break;
default:
throw new UnsupportedOperationException();
}
}

if (result.isTainted()) {
if (taintNode == null) {
CompilerDirectives.transferToInterpreter();
taintNode = insert(TaintNodeGen.create(getContext(), getEncapsulatingSourceSection(), null));
}

taintNode.executeTaint(string);
}

return string;
}

protected ByteList privatizeByteList(RubyString string) {
return string.getByteList().dup();
}

protected boolean byteListsEqual(RubyString string, ByteList byteList) {
return string.getByteList().equal(byteList);
}

protected CallTarget compileFormat(RubyString format) {
try {
return new FormatParser(getContext()).parse(format.getByteList());
} catch (FormatException e) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().argumentError(e.getMessage(), this));
}
}

protected Object firstArgument(Object[] args) {
return args[0];
}

protected RubyString asRubyString(Object arg) {
return (RubyString) arg;
}

}

@CoreMethod(names = "system", isModuleFunction = true, needsSelf = false, required = 1)
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.runtime.ModuleOperations;
import org.jruby.truffle.runtime.ObjectIDOperations;
import org.jruby.truffle.runtime.object.ObjectIDOperations;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.UndefinedPlaceholder;
import org.jruby.truffle.runtime.core.*;
Original file line number Diff line number Diff line change
@@ -387,35 +387,6 @@ private RubyException charRangeException(Number value) {
}
}

@CoreMethod(names = "%", required = 1, argumentsAsArray = true)
public abstract static class FormatNode extends CoreMethodArrayArgumentsNode {

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

private final BranchProfile singleArrayProfile = BranchProfile.create();
private final BranchProfile multipleArgumentsProfile = BranchProfile.create();

@Specialization
public RubyString format(RubyString format, Object[] args) {
return formatSlow(format, args);
}

@CompilerDirectives.TruffleBoundary
private RubyString formatSlow(RubyString format, Object[] args) {
final RubyContext context = getContext();

if (args.length == 1 && args[0] instanceof RubyArray) {
singleArrayProfile.enter();
return context.makeString(StringFormatter.format(getContext(), format.toString(), Arrays.asList(((RubyArray) args[0]).slowToArray())), format.getByteList().getEncoding());
} else {
multipleArgumentsProfile.enter();
return context.makeString(StringFormatter.format(getContext(), format.toString(), Arrays.asList(args)), format.getByteList().getEncoding());
}
}
}

@CoreMethod(names = {"[]", "slice"}, required = 1, optional = 1, lowerFixnumParameters = {0, 1}, taintFromSelf = true)
public abstract static class GetIndexNode extends CoreMethodArrayArgumentsNode {

@@ -899,8 +870,9 @@ public Object deleteBang(VirtualFrame frame, RubyString string, Object... otherS
return deleteBangSlow(frame, string, otherStrings);
}

@CompilerDirectives.TruffleBoundary
private Object deleteBangSlow(VirtualFrame frame, RubyString string, Object... args) {
notDesignedForCompilation();

RubyString[] otherStrings = new RubyString[args.length];

for (int i = 0; i < args.length; i++) {
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ public void execute(RubyBasicObject object, Object value) {
newProperty = currentProperty;
newProperty.setSafe(object.getDynamicObject(), value, null);
} else {
object.getOperations().setInstanceVariable(object, name, value);
object.getObjectType().setInstanceVariable(object, name, value);
newShape = object.getDynamicObject().getShape();
newProperty = newShape.getProperty(name);

Original file line number Diff line number Diff line change
@@ -27,6 +27,8 @@ public abstract class ExceptionPrimitiveNodes {
public static abstract class ExceptionErrnoErrorPrimitiveNode extends RubiniusPrimitiveNode {

protected final int ENOENT = Errno.ENOENT.intValue();
protected final int EBADF = Errno.EBADF.intValue();
protected final int EEXIST = Errno.EEXIST.intValue();

public ExceptionErrnoErrorPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -37,8 +39,29 @@ public RubyException enoent(RubyString message, int errno) {
return getContext().getCoreLibrary().fileNotFoundError(message.toString(), this);
}

@Specialization(guards = "errno == ENOENT")
public RubyException enoent(RubyNilClass message, int errno) {
return getContext().getCoreLibrary().fileNotFoundError("nil", this);
}

@Specialization(guards = "errno == EBADF")
public RubyException ebadf(RubyNilClass message, int errno) {
return getContext().getCoreLibrary().badFileDescriptor(this);
}

@Specialization(guards = "errno == EEXIST")
public RubyException eexist(RubyString message, int errno) {
return getContext().getCoreLibrary().fileExistsError(message.toString(), this);
}

@Specialization(guards = "errno == EEXIST")
public RubyException eexist(RubyNilClass message, int errno) {
return getContext().getCoreLibrary().fileExistsError("nil", this);
}


@CompilerDirectives.TruffleBoundary
@Specialization(guards = "errno != ENOENT")
@Specialization(guards = "!isExceptionSupported(errno)")
public RubyException unsupported(RubyString message, int errno) {
final Errno errnoObject = Errno.valueOf(errno);

@@ -49,13 +72,8 @@ public RubyException unsupported(RubyString message, int errno) {
}
}

@Specialization(guards = "errno == ENOENT")
public RubyException enoent(RubyNilClass message, int errno) {
return getContext().getCoreLibrary().fileNotFoundError("nil", this);
}

@CompilerDirectives.TruffleBoundary
@Specialization(guards = "errno != ENOENT")
@Specialization(guards = "!isExceptionSupported(errno)")
public RubyException unsupported(RubyNilClass message, int errno) {
final Errno errnoObject = Errno.valueOf(errno);

@@ -66,6 +84,10 @@ public RubyException unsupported(RubyNilClass message, int errno) {
}
}

public static boolean isExceptionSupported(int errno) {
return Errno.ENOENT.intValue() == errno || Errno.EBADF.intValue() == errno || Errno.EEXIST.intValue() == errno;
}

}

}
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@
import org.jruby.truffle.nodes.objects.IsTaintedNodeGen;
import org.jruby.truffle.nodes.objects.TaintNode;
import org.jruby.truffle.nodes.objects.TaintNodeGen;
import org.jruby.truffle.runtime.ObjectIDOperations;
import org.jruby.truffle.runtime.object.ObjectIDOperations;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyNilClass;
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ public RegexpSetLastMatchPrimitiveNode(RubyContext context, SourceSection source
public Object setLastMatch(RubyClass regexpClass, Object matchData) {
notDesignedForCompilation();

getContext().getThreadManager().getCurrentThread().getThreadLocals().getOperations().setInstanceVariable(
getContext().getThreadManager().getCurrentThread().getThreadLocals().getObjectType().setInstanceVariable(
getContext().getThreadManager().getCurrentThread().getThreadLocals(), "$~", matchData);

return matchData;
Original file line number Diff line number Diff line change
@@ -31,8 +31,17 @@ public ReadTimeZoneNode(RubyContext context, SourceSection sourceSection) {

@Override
public RubyString executeRubyString(VirtualFrame frame) {
// TODO CS 4-Mar-15 cast
return (RubyString) hashNode.call(frame, getContext().getCoreLibrary().getENV(), "[]", null, TZ);
final Object tz = hashNode.call(frame, getContext().getCoreLibrary().getENV(), "[]", null, TZ);

// TODO CS 4-May-15 not sure how TZ ends up being nil

if (tz == nil()) {
return getContext().makeString("UTC");
} else if (tz instanceof RubyString) {
return (RubyString) tz;
} else {
throw new UnsupportedOperationException();
}
}

@Override
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.pack.nodes.format;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.truffle.pack.parser.FormatDirective;
import org.jruby.util.ByteList;

import java.nio.charset.StandardCharsets;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class FormatFloatNode extends PackNode {

private final int spacePadding;
private final int zeroPadding;
private final int precision;
private final char format;

public FormatFloatNode(int spacePadding, int zeroPadding, int precision, char format) {
this.spacePadding = spacePadding;
this.zeroPadding = zeroPadding;
this.precision = precision;
this.format = format;
}

@Specialization
public ByteList format(double value) {
// TODO CS 3-May-15 write this without building a string and formatting

if (format == 'G' || format == 'g') {
/**
* General approach taken from StackOverflow: http://stackoverflow.com/questions/703396/how-to-nicely-format-floating-numbers-to-string-without-unnecessary-decimal-0
* Answers provided by JasonD (http://stackoverflow.com/users/1288598/jasond) and Darthenius (http://stackoverflow.com/users/974531/darthenius)
* Licensed by cc-wiki license: http://creativecommons.org/licenses/by-sa/3.0/
*/

// TODO (nirvdrum 09-Mar-15) Make this adhere to the MRI invariant: "single-precision, network (big-endian) byte order"

// TODO CS 4-May-15 G/g? Space padding? Zero padding? Precision?

// If the value is a long value stuffed in a double, cast it so we don't print a trailing ".0".
if ((value - Math.rint(value)) == 0) {
return ByteList.create(String.valueOf((long) value));
} else {
return ByteList.create(String.valueOf(value));
}
} else {
final StringBuilder builder = new StringBuilder();

builder.append("%");

if (spacePadding != FormatDirective.DEFAULT) {
builder.append(" ");
builder.append(spacePadding);

if (zeroPadding != FormatDirective.DEFAULT) {
builder.append(".");
builder.append(zeroPadding);
}
} else if (zeroPadding != FormatDirective.DEFAULT) {
builder.append("0");
builder.append(zeroPadding);
}

if (precision != FormatDirective.DEFAULT) {
builder.append(".");
builder.append(precision);
}

builder.append(format);

return new ByteList(String.format(builder.toString(), value).getBytes(StandardCharsets.US_ASCII));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.pack.nodes.format;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.truffle.pack.parser.FormatDirective;
import org.jruby.util.ByteList;

import java.nio.charset.StandardCharsets;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class FormatIntegerNode extends PackNode {

private final int spacePadding;
private final int zeroPadding;
private final char format;

public FormatIntegerNode(int spacePadding, int zeroPadding, char format) {
this.spacePadding = spacePadding;
this.zeroPadding = zeroPadding;
this.format = format;
}

@Specialization
public ByteList format(int value) {
return doFormat(value);
}

@Specialization
public ByteList format(long value) {
return doFormat(value);
}

@CompilerDirectives.TruffleBoundary
protected ByteList doFormat(Object value) {
// TODO CS 3-May-15 write this without building a string and formatting

final StringBuilder builder = new StringBuilder();

builder.append("%");

if (spacePadding != FormatDirective.DEFAULT) {
builder.append(" ");
builder.append(spacePadding);

if (zeroPadding != FormatDirective.DEFAULT) {
builder.append(".");
builder.append(zeroPadding);
}
} else if (zeroPadding != FormatDirective.DEFAULT) {
builder.append("0");
builder.append(zeroPadding);
}

builder.append(format);

return new ByteList(String.format(builder.toString(), value).getBytes(StandardCharsets.US_ASCII));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.pack.nodes.read;

import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.util.ByteList;

public class LiteralBytesNode extends PackNode {

private final ByteList bytes;

public LiteralBytesNode(ByteList bytes) {
this.bytes = bytes;
}

@Override
public Object execute(VirtualFrame frame) {
return bytes;
}

}
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@
import org.jruby.truffle.pack.nodes.SourceNode;
import org.jruby.truffle.pack.nodes.type.ToDoubleNode;
import org.jruby.truffle.pack.nodes.type.ToDoubleNodeGen;
import org.jruby.truffle.pack.nodes.write.NullNode;

/**
* Read a {@code double} value from the source.
@@ -59,7 +58,7 @@ public double read(VirtualFrame frame, double[] source) {
public double read(VirtualFrame frame, Object[] source) {
if (toDoubleNode == null) {
CompilerDirectives.transferToInterpreter();
toDoubleNode = insert(ToDoubleNodeGen.create(new NullNode()));
toDoubleNode = insert(ToDoubleNodeGen.create(null));
}

return toDoubleNode.executeToDouble(frame, source[advanceSourcePosition(frame)]);
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@
import org.jruby.truffle.pack.nodes.SourceNode;
import org.jruby.truffle.pack.nodes.type.ToLongNode;
import org.jruby.truffle.pack.nodes.type.ToLongNodeGen;
import org.jruby.truffle.pack.nodes.write.NullNode;
import org.jruby.truffle.runtime.RubyContext;

/**
@@ -67,7 +66,7 @@ public long read(VirtualFrame frame, double[] source) {
public long read(VirtualFrame frame, Object[] source) {
if (toLongNode == null) {
CompilerDirectives.transferToInterpreter();
toLongNode = insert(ToLongNodeGen.create(context, new NullNode()));
toLongNode = insert(ToLongNodeGen.create(context, null));
}

return toLongNode.executeToLong(frame, source[advanceSourcePosition(frame)]);
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@
import org.jruby.truffle.pack.nodes.SourceNode;
import org.jruby.truffle.pack.nodes.type.ToLongNode;
import org.jruby.truffle.pack.nodes.type.ToLongNodeGen;
import org.jruby.truffle.pack.nodes.write.NullNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBignum;

@@ -46,7 +45,7 @@ public ReadLongOrBigIntegerNode(RubyContext context) {
}

@Specialization(guards = "isNull(source)")
public long read(VirtualFrame frame, Object source) {
public void read(VirtualFrame frame, Object source) {
CompilerDirectives.transferToInterpreter();

// Advance will handle the error
@@ -56,7 +55,7 @@ public long read(VirtualFrame frame, Object source) {
}

@Specialization
public long read(VirtualFrame frame, int[] source) {
public int read(VirtualFrame frame, int[] source) {
return source[advanceSourcePosition(frame)];
}

@@ -65,11 +64,6 @@ public long read(VirtualFrame frame, long[] source) {
return source[advanceSourcePosition(frame)];
}

@Specialization
public long read(VirtualFrame frame, double[] source) {
return (long) source[advanceSourcePosition(frame)];
}

@Specialization
public Object read(VirtualFrame frame, Object[] source) {
final Object value = source[advanceSourcePosition(frame)];
@@ -79,7 +73,7 @@ public Object read(VirtualFrame frame, Object[] source) {
} else {
if (toLongNode == null) {
CompilerDirectives.transferToInterpreter();
toLongNode = insert(ToLongNodeGen.create(context, new NullNode()));
toLongNode = insert(ToLongNodeGen.create(context, null));
}

return toLongNode.executeToLong(frame, value);
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
import org.jruby.truffle.pack.nodes.SourceNode;
import org.jruby.truffle.pack.nodes.type.ToStringNode;
import org.jruby.truffle.pack.nodes.type.ToStringNodeGen;
import org.jruby.truffle.pack.nodes.write.NullNode;
import org.jruby.truffle.pack.nodes.write.WriteByteNode;
import org.jruby.truffle.runtime.RubyContext;

/**
@@ -33,15 +33,18 @@ public abstract class ReadStringNode extends PackNode {
private final boolean convertNumbersToStrings;
private final String conversionMethod;
private final boolean inspectOnConversionFailure;
private final Object valueOnNil;

@Child private ToStringNode toStringNode;

public ReadStringNode(RubyContext context, boolean convertNumbersToStrings,
String conversionMethod, boolean inspectOnConversionFailure) {
String conversionMethod, boolean inspectOnConversionFailure,
Object valueOnNil) {
this.context = context;
this.convertNumbersToStrings = convertNumbersToStrings;
this.conversionMethod = conversionMethod;
this.inspectOnConversionFailure = inspectOnConversionFailure;
this.valueOnNil = valueOnNil;
}

@Specialization(guards = "isNull(source)")
@@ -78,7 +81,7 @@ private Object readAndConvert(VirtualFrame frame, Object value) {
if (toStringNode == null) {
CompilerDirectives.transferToInterpreter();
toStringNode = insert(ToStringNodeGen.create(context, convertNumbersToStrings,
conversionMethod, inspectOnConversionFailure, new NullNode()));
conversionMethod, inspectOnConversionFailure, valueOnNil, new WriteByteNode((byte) 0)));
}

return toStringNode.executeToString(frame, value);
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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.pack.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
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.VirtualFrame;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.truffle.pack.nodes.SourceNode;

@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadValueNode extends PackNode {

@Specialization(guards = "isNull(source)")
public void read(VirtualFrame frame, Object source) {
CompilerDirectives.transferToInterpreter();

// Advance will handle the error
advanceSourcePosition(frame);

throw new IllegalStateException();
}

@Specialization
public long read(VirtualFrame frame, int[] source) {
return source[advanceSourcePosition(frame)];
}

@Specialization
public long read(VirtualFrame frame, long[] source) {
return source[advanceSourcePosition(frame)];
}

@Specialization
public long read(VirtualFrame frame, double[] source) {
return (long) source[advanceSourcePosition(frame)];
}

@Specialization
public Object read(VirtualFrame frame, Object[] source) {
return source[advanceSourcePosition(frame)];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.pack.nodes.type;

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.VirtualFrame;
import org.jruby.truffle.pack.nodes.PackNode;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class ToIntegerNode extends PackNode {

public abstract Object executeToInteger(VirtualFrame frame, Object object);

@Specialization
public int toInteger(VirtualFrame frame, int value) {
return value;
}

@Specialization
public long toInteger(VirtualFrame frame, long value) {
return value;
}

@Specialization
public long toInteger(VirtualFrame frame, double value) {
return (long) value;
}

}
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@ public abstract class ToStringNode extends PackNode {
protected final boolean convertNumbersToStrings;
private final String conversionMethod;
private final boolean inspectOnConversionFailure;
private final Object valueOnNil;

@Child private CallDispatchHeadNode toStrNode;
@Child private CallDispatchHeadNode toSNode;
@@ -55,19 +56,21 @@ public abstract class ToStringNode extends PackNode {
private final ConditionProfile taintedProfile = ConditionProfile.createBinaryProfile();

public ToStringNode(RubyContext context, boolean convertNumbersToStrings,
String conversionMethod, boolean inspectOnConversionFailure) {
String conversionMethod, boolean inspectOnConversionFailure,
Object valueOnNil) {
this.context = context;
this.convertNumbersToStrings = convertNumbersToStrings;
this.conversionMethod = conversionMethod;
this.inspectOnConversionFailure = inspectOnConversionFailure;
this.valueOnNil = valueOnNil;
isTaintedNode = IsTaintedNodeGen.create(context, getEncapsulatingSourceSection(), null);
}

public abstract Object executeToString(VirtualFrame frame, Object object);

@Specialization
public RubyNilClass toString(VirtualFrame frame, RubyNilClass nil) {
return nil;
public Object toString(VirtualFrame frame, RubyNilClass nil) {
return valueOnNil;
}

// TODO CS 31-Mar-15 these boundaries and slow versions are not ideal
Original file line number Diff line number Diff line change
@@ -31,6 +31,18 @@
})
public abstract class WriteBERNode extends PackNode {

@Specialization
public Object doWrite(VirtualFrame frame, int value) {
if (value < 0) {
CompilerDirectives.transferToInterpreter();
throw new CantCompressNegativeException();
}

writeBytes(frame, encode(value));

return null;
}

@Specialization
public Object doWrite(VirtualFrame frame, long value) {
if (value < 0) {
Original file line number Diff line number Diff line change
@@ -13,15 +13,19 @@
import org.jruby.truffle.pack.nodes.PackNode;

/**
* Write a null byte.
* <pre>
* [1, 2, 3].pack('x') # => "\x00"
* Simply write a single bytes.
*/
public class NullNode extends PackNode {
public class WriteByteNode extends PackNode {

private final byte value;

public WriteByteNode(byte value) {
this.value = value;
}

@Override
public Object execute(VirtualFrame frame) {
writeBytes(frame, (byte) 0);
writeBytes(frame, value);
return null;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.pack.nodes.write;

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.VirtualFrame;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.util.ByteList;

/**
* Simply write bytes.
*/
@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class WriteBytesNode extends PackNode {

@Specialization
public Object write(VirtualFrame frame, ByteList bytes) {
writeBytes(frame, bytes);
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.pack.parser;

/*
* A single format directive from a printf-style format string.
*
* %[space padding][zero padding][.precision]type
*/
public class FormatDirective {

public static final int DEFAULT = -1;

private final int spacePadding;
private final int zeroPadding;
private final int precision;
private final char type;

public FormatDirective(int spacePadding, int zeroPadding, int precision, char type) {
this.spacePadding = spacePadding;
this.zeroPadding = zeroPadding;
this.precision = precision;
this.type = type;
}

public int getSpacePadding() {
return spacePadding;
}

public int getZeroPadding() {
return zeroPadding;
}

public int getPrecision() {
return precision;
}

public char getType() {
return type;
}

}
143 changes: 143 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/pack/parser/FormatParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* 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.pack.parser;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import org.jruby.truffle.pack.nodes.PackNode;
import org.jruby.truffle.pack.nodes.PackRootNode;
import org.jruby.truffle.pack.nodes.SourceNode;
import org.jruby.truffle.pack.nodes.control.SequenceNode;
import org.jruby.truffle.pack.nodes.format.FormatFloatNodeGen;
import org.jruby.truffle.pack.nodes.format.FormatIntegerNodeGen;
import org.jruby.truffle.pack.nodes.read.LiteralBytesNode;
import org.jruby.truffle.pack.nodes.read.ReadStringNodeGen;
import org.jruby.truffle.pack.nodes.read.ReadValueNodeGen;
import org.jruby.truffle.pack.nodes.type.ToDoubleNodeGen;
import org.jruby.truffle.pack.nodes.type.ToIntegerNodeGen;
import org.jruby.truffle.pack.nodes.write.WriteByteNode;
import org.jruby.truffle.pack.nodes.write.WriteBytesNodeGen;
import org.jruby.truffle.pack.runtime.PackEncoding;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.util.ByteList;

import java.util.ArrayList;
import java.util.List;

/**
* Parses a format expression into a tree of Truffle nodes.
*/
public class FormatParser {

private final RubyContext context;

private PackEncoding encoding = PackEncoding.DEFAULT;

public FormatParser(RubyContext context) {
this.context = context;
}

public CallTarget parse(ByteList format) {
final FormatTokenizer tokenizer = new FormatTokenizer(format);
final PackNode body = parse(tokenizer);
return Truffle.getRuntime().createCallTarget(new PackRootNode(PackParser.describe(format.toString()), encoding, body));
}

public PackNode parse(FormatTokenizer tokenizer) {
final List<PackNode> sequenceChildren = new ArrayList<>();

while (true) {
Object token = tokenizer.next();

if (token == null) {
break;
}

final PackNode node;

if (token instanceof ByteList) {
node = WriteBytesNodeGen.create(new LiteralBytesNode((ByteList) token));
} else if (token instanceof FormatDirective) {
final FormatDirective directive = (FormatDirective) token;

switch (directive.getType()) {
case '%':
node = new WriteByteNode((byte) '%');
break;
case 's':
node = WriteBytesNodeGen.create(ReadStringNodeGen.create(
context, true, "to_s", false, new ByteList(), new SourceNode()));
break;
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
final int spacePadding = directive.getSpacePadding();

final int zeroPadding;

/*
* Precision and zero padding both set zero padding -
* but precision has priority and explicit zero padding
* is actually ignored if it's set.
*/

if (directive.getPrecision() != FormatDirective.DEFAULT) {
zeroPadding = directive.getPrecision();
} else {
zeroPadding = directive.getZeroPadding();
}

final char format;

switch (directive.getType()) {
case 'd':
case 'i':
case 'u':
format = 'd';
break;
case 'x':
case 'X':
format = directive.getType();
break;
default:
throw new UnsupportedOperationException();
}

node = WriteBytesNodeGen.create(
FormatIntegerNodeGen.create(spacePadding, zeroPadding, format,
ToIntegerNodeGen.create(
ReadValueNodeGen.create(new SourceNode()))));
break;
case 'f':
case 'g':
case 'G':
node = WriteBytesNodeGen.create(
FormatFloatNodeGen.create(directive.getSpacePadding(),
directive.getZeroPadding(), directive.getPrecision(),
directive.getType(),
ToDoubleNodeGen.create(
ReadValueNodeGen.create(new SourceNode()))));
break;
default:
throw new UnsupportedOperationException();
}
} else {
throw new UnsupportedOperationException();
}

sequenceChildren.add(node);
}

return new SequenceNode(sequenceChildren.toArray(new PackNode[sequenceChildren.size()]));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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.pack.parser;

import org.jruby.util.ByteList;

/**
* Tokenizes a format expression into a stream of {@link String} and
* {@link FormatDirective} objects.
*/
public class FormatTokenizer {

private static final String TYPE_CHARS = "%sdiuxXfg";

private final ByteList format;
private int position;
private Object peek;

/**
* Construct a tokenizer.
* @param format the pack expression
*/
public FormatTokenizer(ByteList format) {
this.format = format;
}

public Object peek() {
if (peek == null) {
peek = next();
}

return peek;
}

public Object next() {
if (peek != null) {
final Object token = peek;
peek = null;
return token;
}

if (position >= format.length()) {
return null;
}

final char c = format.charAt(position);

if (c != '%') {
final int stringStart = position;

position++;

while (position < format.length() && format.charAt(position) != '%') {
position++;
}

return format.subSequence(stringStart, position);
}

position++;

int spacePadding;
int zeroPadding;

if (format.charAt(position) == ' ') {
position++;
spacePadding = readInt();
zeroPadding = FormatDirective.DEFAULT;
} else {
spacePadding = FormatDirective.DEFAULT;

if (format.charAt(position) == '0') {
position++;
zeroPadding = readInt();
} else {
zeroPadding = FormatDirective.DEFAULT;
}
}

final int precision;

if (format.charAt(position) == '.') {
position++;
precision = readInt();
} else {
precision = FormatDirective.DEFAULT;
}

if (Character.isDigit(format.charAt(position))) {
spacePadding = readInt();
}

final char type = format.charAt(position);

if (TYPE_CHARS.indexOf(type) == -1) {
throw new UnsupportedOperationException("Unknown format type '" + format.charAt(position) + "'");
}

position++;

return new FormatDirective(spacePadding, zeroPadding, precision, type);
}

private int readInt() {
final int start = position;

while (Character.isDigit(format.charAt(position))) {
position++;
}

return Integer.parseInt(format.subSequence(start, position).toString());
}

}
Original file line number Diff line number Diff line change
@@ -226,7 +226,8 @@ public PackNode parse(PackTokenizer tokenizer, boolean inParens) {

node = WriteBinaryStringNodeGen.create(pad, padOnNull,
width, padding, takeAll, appendNull,
ReadStringNodeGen.create(context, true, "to_str", false, new SourceNode()));
ReadStringNodeGen.create(context, true, "to_str",
false, context.getCoreLibrary().getNilObject(), new SourceNode()));
} break;
case 'H':
case 'h': {
@@ -254,7 +255,8 @@ public PackNode parse(PackTokenizer tokenizer, boolean inParens) {
}

node = WriteHexStringNodeGen.create(endianness, length,
ReadStringNodeGen.create(context, true, "to_str", false, new SourceNode()));
ReadStringNodeGen.create(context, true, "to_str",
false, context.getCoreLibrary().getNilObject(), new SourceNode()));
} break;
case 'B':
case 'b': {
@@ -287,7 +289,8 @@ public PackNode parse(PackTokenizer tokenizer, boolean inParens) {
}

node = WriteBitStringNodeGen.create(endianness, star, length,
ReadStringNodeGen.create(context, true, "to_str", false, new SourceNode()));
ReadStringNodeGen.create(context, true, "to_str",
false, context.getCoreLibrary().getNilObject(), new SourceNode()));
} break;
case 'M': {
encoding = encoding.unifyWith(PackEncoding.US_ASCII);
@@ -305,7 +308,8 @@ public PackNode parse(PackTokenizer tokenizer, boolean inParens) {
}

node = WriteMIMEStringNodeGen.create(length,
ReadStringNodeGen.create(context, true, "to_s", true, new SourceNode()));
ReadStringNodeGen.create(context, true, "to_s",
true, context.getCoreLibrary().getNilObject(), new SourceNode()));
} break;
case 'm':
case 'u': {
@@ -325,11 +329,13 @@ public PackNode parse(PackTokenizer tokenizer, boolean inParens) {
switch ((char) token) {
case 'm':
node = WriteBase64StringNodeGen.create(length, ignoreStar,
ReadStringNodeGen.create(context, false, "to_str", false, new SourceNode()));
ReadStringNodeGen.create(context, false, "to_str",
false, context.getCoreLibrary().getNilObject(), new SourceNode()));
break;
case 'u':
node = WriteUUStringNodeGen.create(length, ignoreStar,
ReadStringNodeGen.create(context, false, "to_str", false, new SourceNode()));
ReadStringNodeGen.create(context, false, "to_str",
false, context.getCoreLibrary().getNilObject(), new SourceNode()));
break;
default:
throw new UnsupportedOperationException();
@@ -343,7 +349,7 @@ public PackNode parse(PackTokenizer tokenizer, boolean inParens) {
node = new BackNode();
break;
case 'x':
node = new NullNode();
node = new WriteByteNode((byte) 0);
break;
case '@': {
final int position;
@@ -509,7 +515,7 @@ private String recoverLoop(String format) {
* Provide a simple string describing the format expression that is short
* enough to be used in Truffle and Graal diagnostics.
*/
private String describe(String format) {
public static String describe(String format) {
format = format.replace("\\s+", "");

if (format.length() > 10) {
Original file line number Diff line number Diff line change
@@ -36,6 +36,8 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.object.ObjectIDOperations;
import org.jruby.truffle.runtime.object.RubyObjectType;
import org.jruby.truffle.runtime.subsystems.*;
import org.jruby.truffle.translator.NodeWrapper;
import org.jruby.truffle.translator.TranslatorDriver;
@@ -125,7 +127,7 @@ public RubyContext(Ruby runtime) {
// Object space manager needs to come early before we create any objects
objectSpaceManager = new ObjectSpaceManager(this);

emptyShape = RubyBasicObject.LAYOUT.createShape(new RubyOperations(this));
emptyShape = RubyBasicObject.LAYOUT.createShape(new RubyObjectType(this));

coreLibrary = new CoreLibrary(this);
rootLexicalScope = new LexicalScope(null, coreLibrary.getObjectClass());
@@ -389,7 +391,7 @@ public IRubyObject toJRuby(RubyEncoding encoding) {
public org.jruby.RubyString toJRuby(RubyString string) {
final org.jruby.RubyString jrubyString = runtime.newString(string.getByteList().dup());

final Object tainted = string.getOperations().getInstanceVariable(string, RubyBasicObject.TAINTED_IDENTIFIER);
final Object tainted = string.getObjectType().getInstanceVariable(string, RubyBasicObject.TAINTED_IDENTIFIER);

if (tainted instanceof Boolean && (boolean) tainted) {
jrubyString.setTaint(true);
@@ -448,7 +450,7 @@ public RubyString toTruffle(org.jruby.RubyString jrubyString) {
final RubyString truffleString = new RubyString(getCoreLibrary().getStringClass(), jrubyString.getByteList().dup());

if (jrubyString.isTaint()) {
truffleString.getOperations().setInstanceVariable(truffleString, RubyBasicObject.TAINTED_IDENTIFIER, true);
truffleString.getObjectType().setInstanceVariable(truffleString, RubyBasicObject.TAINTED_IDENTIFIER, true);
}

return truffleString;
Original file line number Diff line number Diff line change
@@ -50,7 +50,6 @@

public class CoreLibrary {

private static final boolean LOAD_CORE = Options.TRUFFLE_LOAD_CORE.load();
private static final String CLI_RECORD_SEPARATOR = Options.CLI_RECORD_SEPARATOR.load();

private final RubyContext context;
@@ -364,25 +363,25 @@ private void initializeGlobalVariables() {

RubyBasicObject globals = globalVariablesObject;

globals.getOperations().setInstanceVariable(globals, "$LOAD_PATH", new RubyArray(arrayClass));
globals.getOperations().setInstanceVariable(globals, "$LOADED_FEATURES", new RubyArray(arrayClass));
globals.getOperations().setInstanceVariable(globals, "$:", globals.getInstanceVariable("$LOAD_PATH"));
globals.getOperations().setInstanceVariable(globals, "$\"", globals.getInstanceVariable("$LOADED_FEATURES"));
globals.getOperations().setInstanceVariable(globals, "$,", nilObject);
globals.getOperations().setInstanceVariable(globals, "$0", context.toTruffle(context.getRuntime().getGlobalVariables().get("$0")));
globals.getObjectType().setInstanceVariable(globals, "$LOAD_PATH", new RubyArray(arrayClass));
globals.getObjectType().setInstanceVariable(globals, "$LOADED_FEATURES", new RubyArray(arrayClass));
globals.getObjectType().setInstanceVariable(globals, "$:", globals.getInstanceVariable("$LOAD_PATH"));
globals.getObjectType().setInstanceVariable(globals, "$\"", globals.getInstanceVariable("$LOADED_FEATURES"));
globals.getObjectType().setInstanceVariable(globals, "$,", nilObject);
globals.getObjectType().setInstanceVariable(globals, "$0", context.toTruffle(context.getRuntime().getGlobalVariables().get("$0")));

globals.getOperations().setInstanceVariable(globals, "$DEBUG", context.getRuntime().isDebug());
globals.getObjectType().setInstanceVariable(globals, "$DEBUG", context.getRuntime().isDebug());

Object value = context.getRuntime().warningsEnabled() ? context.getRuntime().isVerbose() : nilObject;
globals.getOperations().setInstanceVariable(globals, "$VERBOSE", value);
globals.getObjectType().setInstanceVariable(globals, "$VERBOSE", value);

final RubyString defaultRecordSeparator = RubyString.fromJavaString(stringClass, CLI_RECORD_SEPARATOR);
defaultRecordSeparator.freeze();

// TODO (nirvdrum 05-Feb-15) We need to support the $-0 alias as well.
globals.getOperations().setInstanceVariable(globals, "$/", defaultRecordSeparator);
globals.getObjectType().setInstanceVariable(globals, "$/", defaultRecordSeparator);

globals.getOperations().setInstanceVariable(globals, "$SAFE", 0);
globals.getObjectType().setInstanceVariable(globals, "$SAFE", 0);
}

private void initializeConstants() {
@@ -476,21 +475,19 @@ public void initializeAfterMethodsAdded() {

// Load Ruby core

if (LOAD_CORE) {
try {
state = State.LOADING_RUBY_CORE;
loadRubyCore("core.rb");
} catch (RaiseException e) {
final RubyException rubyException = e.getRubyException();

for (String line : Backtrace.DISPLAY_FORMATTER.format(getContext(), rubyException, rubyException.getBacktrace())) {
System.err.println(line);
}
try {
state = State.LOADING_RUBY_CORE;
loadRubyCore("core.rb");
} catch (RaiseException e) {
final RubyException rubyException = e.getRubyException();

throw new TruffleFatalException("couldn't load the core library", e);
} finally {
state = State.LOADED;
for (String line : Backtrace.DISPLAY_FORMATTER.format(getContext(), rubyException, rubyException.getBacktrace())) {
System.err.println(line);
}

throw new TruffleFatalException("couldn't load the core library", e);
} finally {
state = State.LOADED;
}
}

@@ -818,7 +815,7 @@ public RubyException typeErrorWrongArgumentType(Object object, String expectedTy
public RubyException nameError(String message, String name, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
RubyException nameError = new RubyException(nameErrorClass, context.makeString(message), RubyCallStack.getBacktrace(currentNode));
nameError.getOperations().setInstanceVariable(nameError, "@name", context.getSymbolTable().getSymbol(name));
nameError.getObjectType().setInstanceVariable(nameError, "@name", context.getSymbolTable().getSymbol(name));
return nameError;
}

@@ -870,7 +867,7 @@ public RubyException nameErrorPrivateMethod(String name, RubyModule module, Node
public RubyException noMethodError(String message, String name, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
RubyException noMethodError = new RubyException(context.getCoreLibrary().getNoMethodErrorClass(), context.makeString(message), RubyCallStack.getBacktrace(currentNode));
noMethodError.getOperations().setInstanceVariable(noMethodError, "@name", context.getSymbolTable().getSymbol(name));
noMethodError.getObjectType().setInstanceVariable(noMethodError, "@name", context.getSymbolTable().getSymbol(name));
return noMethodError;
}

@@ -934,6 +931,16 @@ public RubyException ioError(String fileName, Node currentNode) {
return new RubyException(ioErrorClass, context.makeString(String.format("Error reading file - %s", fileName)), RubyCallStack.getBacktrace(currentNode));
}

public RubyException badFileDescriptor(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(getErrnoClass(Errno.EBADF), context.makeString("Bad file descriptor"), RubyCallStack.getBacktrace(currentNode));
}

public RubyException fileExistsError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(getErrnoClass(Errno.EEXIST), context.makeString(String.format("File exists - %s", fileName)), RubyCallStack.getBacktrace(currentNode));
}

public RubyException fileNotFoundError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(getErrnoClass(Errno.ENOENT), context.makeString(String.format("No such file or directory - %s", fileName)), RubyCallStack.getBacktrace(currentNode));
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
import org.jruby.truffle.runtime.DebugOperations;
import org.jruby.truffle.runtime.ModuleOperations;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.RubyOperations;
import org.jruby.truffle.runtime.object.RubyObjectType;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.subsystems.ObjectSpaceManager;

@@ -144,24 +144,24 @@ public void setInstanceVariables(Map<Object, Object> instanceVariables) {

assert instanceVariables != null;

getOperations().setInstanceVariables(this, instanceVariables);
getObjectType().setInstanceVariables(this, instanceVariables);
}


public Map<Object, Object> getInstanceVariables() {
RubyNode.notDesignedForCompilation();

return getOperations().getInstanceVariables(this);
return getObjectType().getInstanceVariables(this);
}

public Object[] getFieldNames() {
return getOperations().getFieldNames(this);
return getObjectType().getFieldNames(this);
}

public Object getInstanceVariable(String name) {
RubyNode.notDesignedForCompilation();

final Object value = getOperations().getInstanceVariable(this, name);
final Object value = getObjectType().getInstanceVariable(this, name);

if (value == null) {
return getContext().getCoreLibrary().getNilObject();
@@ -171,7 +171,7 @@ public Object getInstanceVariable(String name) {
}

public boolean isFieldDefined(String name) {
return getOperations().isFieldDefined(this, name);
return getObjectType().isFieldDefined(this, name);
}

@Override
@@ -183,7 +183,7 @@ public final void visitObjectGraph(ObjectSpaceManager.ObjectGraphVisitor visitor
if (visitor.visit(this)) {
metaClass.visitObjectGraph(visitor);

for (Object instanceVariable : getOperations().getInstanceVariables(this).values()) {
for (Object instanceVariable : getObjectType().getInstanceVariables(this).values()) {
if (instanceVariable instanceof RubyBasicObject) {
((RubyBasicObject) instanceVariable).visitObjectGraph(visitor);
}
@@ -208,8 +208,8 @@ public Shape getObjectLayout() {
return dynamicObject.getShape();
}

public RubyOperations getOperations() {
return (RubyOperations) dynamicObject.getShape().getObjectType();
public RubyObjectType getObjectType() {
return (RubyObjectType) dynamicObject.getShape().getObjectType();
}

public RubyClass getLogicalClass() {
Original file line number Diff line number Diff line change
@@ -239,7 +239,7 @@ public void setThread(String name, Object value) {
assert value != null;

RubyNode.notDesignedForCompilation();
getContext().getThreadManager().getCurrentThread().getThreadLocals().getOperations().setInstanceVariable(getContext().getThreadManager().getCurrentThread().getThreadLocals(), name, value);
getContext().getThreadManager().getCurrentThread().getThreadLocals().getObjectType().setInstanceVariable(getContext().getThreadManager().getCurrentThread().getThreadLocals(), name, value);
}

@CompilerDirectives.TruffleBoundary

This file was deleted.

Original file line number Diff line number Diff line change
@@ -7,9 +7,10 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.runtime;
package org.jruby.truffle.runtime.object;

import com.oracle.truffle.api.ExactMath;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBignum;

import java.math.BigInteger;
Original file line number Diff line number Diff line change
@@ -7,24 +7,25 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.runtime;
package org.jruby.truffle.runtime.object;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.ObjectType;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class RubyOperations extends ObjectType {
public class RubyObjectType extends ObjectType {

private final RubyContext context;

public RubyOperations(RubyContext context) {
public RubyObjectType(RubyContext context) {
this.context = context;
}

9 changes: 5 additions & 4 deletions truffle/src/main/ruby/core/string.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Copyright (c) 2014, 2015 Oracle and/or its affiliates. All rights reserved. This
# 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

# These are implemented just to get other stuff working - we'll go back and
# implement these properly later.

class String

def %(args)
sprintf(self, *args)
end

end

0 comments on commit fc94c95

Please sign in to comment.