Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ce6903f61fd6
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e5ed970f7974
Choose a head ref
  • 2 commits
  • 3 files changed
  • 2 contributors

Commits on Mar 1, 2015

  1. Copy the full SHA
    b989d66 View commit details
  2. Merge pull request #2634 from bjfish/truffle_io_binread

    [Truffle] Implemented IO.binread
    chrisseaton committed Mar 1, 2015
    Copy the full SHA
    e5ed970 View commit details
7 changes: 0 additions & 7 deletions spec/truffle/tags/core/io/binread_tags.txt
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
fails:IO.binread reads the contents of a file
fails:IO.binread reads the contents of a file up to a certain size when specified
fails:IO.binread reads the contents of a file from an offset of a specific size when specified
fails:IO.binread returns a String in ASCII-8BIT encoding
fails:IO.binread returns a String in ASCII-8BIT encoding regardless of Encoding.default_internal
fails:IO.binread raises an ArgumentError when not passed a valid length
fails:IO.binread raises an Errno::EINVAL when not passed a valid offset
104 changes: 104 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/IONodes.java
Original file line number Diff line number Diff line change
@@ -16,20 +16,27 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;

import org.jcodings.specific.ASCIIEncoding;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.ToSNodeFactory;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.UndefinedPlaceholder;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyFile;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.core.RubyString;
import org.jruby.truffle.runtime.subsystems.ThreadManager;
import org.jruby.util.ByteList;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
@@ -76,6 +83,103 @@ public RubyArray readLines(RubyString file) {

}

@CoreMethod(names = "binread", onSingleton = true, required = 1, optional = 2)
public abstract static class BinReadNode extends CoreMethodNode {

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

public BinReadNode(BinReadNode prev) {
super(prev);
}

@Specialization
public Object binaryRead(RubyString file, UndefinedPlaceholder size, UndefinedPlaceholder offset) {
notDesignedForCompilation();
return readFile(getContext(), file, null, null, this);
}

@Specialization
public Object binaryRead(RubyString file, int size, UndefinedPlaceholder offset) {
notDesignedForCompilation();
return readFile(getContext(), file, size, null, this);
}

@Specialization
public Object binaryRead(RubyString file, int size, int offset) {
notDesignedForCompilation();
return readFile(getContext(), file, size, offset, this);
}

private static Object readFile(RubyContext context, RubyString rubyString, Integer requestedLength, Integer offset, BinReadNode node) {
final File file = new File(RubyFile.expandPath(context, rubyString.toString()));
final int length;
if (offset != null) {
if (offset < 0) {
throw new RaiseException(context.getCoreLibrary().invalidArgumentError(Integer.toString(offset), node));
}
} else {
offset = 0;
}
final int fileLength = longToInt(file.length());
if (requestedLength != null) {
if (requestedLength < 0) {
throw new RaiseException(context.getCoreLibrary().argumentError("length cannot be negative", node));
} else if (requestedLength == 0) {
return context.makeString("", ASCIIEncoding.INSTANCE);
}
if (offset > fileLength) {
return context.getCoreLibrary().getNilObject();
} else if ((offset + requestedLength) > fileLength) {
length = fileLength - offset;
} else {
length = requestedLength;
}
} else {
length = fileLength;
}
try (InputStream in = new BufferedInputStream(new FileInputStream(file), length)) {
final ByteList byteList = new ByteList(readWithOffset(in, length, offset), ASCIIEncoding.INSTANCE);
return context.makeString(byteList);
} catch (EOFException e) {
return context.getCoreLibrary().getNilObject();
} catch (FileNotFoundException e) {
throw new RaiseException(context.getCoreLibrary().fileNotFoundError(rubyString.toString(), node));
} catch (IOException e) {
throw new RaiseException(context.getCoreLibrary().ioError(rubyString.toString(), node));
}
}

private static byte[] readWithOffset(InputStream input, int length, int offset) throws IOException {
int read = 0;
int n;
final byte[] bytes = new byte[length];
while (offset > 0) {
offset -= input.skip(offset);
}
for (int start = 0; read < length; read += n) {
n = input.read(bytes, start + read, length - read);
if (n == -1) {
if (read == 0) {
throw new EOFException();
}
break;
}
}
return bytes;
}

private static int longToInt(long lon) {
int integer = (int) lon;
if ((long) integer != lon) {
throw new IllegalArgumentException("size too large to fit in int");
}
return integer;
}

}

@CoreMethod(names = "write", needsSelf = false, required = 1)
@NodeChild(value = "string")
public abstract static class WriteNode extends RubyNode {
Original file line number Diff line number Diff line change
@@ -121,6 +121,8 @@ public class CoreLibrary {
private final RubyModule truffleModule;
private final RubyModule truffleDebugModule;
private final RubyClass edomClass;
private final RubyClass einvalClass;
private final RubyClass enoentClass;
private final RubyClass encodingConverterClass;
private final RubyClass encodingCompatibilityErrorClass;
private final RubyClass methodClass;
@@ -225,10 +227,11 @@ public CoreLibrary(RubyContext context) {
new RubyClass(context, errnoModule, systemCallErrorClass, "EACCES");
edomClass = new RubyClass(context, errnoModule, systemCallErrorClass, "EDOM");
new RubyClass(context, errnoModule, systemCallErrorClass, "EEXIST");
new RubyClass(context, errnoModule, systemCallErrorClass, "ENOENT");
enoentClass = new RubyClass(context, errnoModule, systemCallErrorClass, "ENOENT");
new RubyClass(context, errnoModule, systemCallErrorClass, "ENOTEMPTY");
new RubyClass(context, errnoModule, systemCallErrorClass, "EPERM");
new RubyClass(context, errnoModule, systemCallErrorClass, "EXDEV");
einvalClass = new RubyClass(context, errnoModule, systemCallErrorClass, "EINVAL");

// ScriptError
RubyClass scriptErrorClass = defineClass(exceptionClass, "ScriptError");
@@ -842,6 +845,21 @@ public RubyException mathDomainError(String method, Node currentNode) {
return new RubyException(edomClass, context.makeString(String.format("Numerical argument is out of domain - \"%s\"", method)), RubyCallStack.getBacktrace(currentNode));
}

public RubyException invalidArgumentError(String value, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(einvalClass, context.makeString(String.format("Invalid argument - %s", value)), RubyCallStack.getBacktrace(currentNode));
}

public RubyException ioError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(ioErrorClass, context.makeString(String.format("Error reading file - %s", fileName)), RubyCallStack.getBacktrace(currentNode));
}

public RubyException fileNotFoundError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(enoentClass, context.makeString(String.format("No such file or directory - %s", fileName)), RubyCallStack.getBacktrace(currentNode));
}

public RubyException rangeError(int code, RubyEncoding encoding, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return rangeError(String.format("invalid codepoint %x in %s", code, encoding.getEncoding()), currentNode);