diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java index 43eebefbe804371f2196f24c453fd9ed6dc54828..064c0481a1080807faa0493ff5f780460191d021 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java @@ -55,6 +55,7 @@ import java.nio.ByteOrder; import java.nio.DoubleBuffer; import java.nio.IntBuffer; import java.nio.channels.ByteChannel; +import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.util.ArrayList; @@ -96,6 +97,7 @@ import com.oracle.truffle.r.runtime.data.RExpression; import com.oracle.truffle.r.runtime.data.RIntVector; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RLogicalVector; +import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RRaw; import com.oracle.truffle.r.runtime.data.RRawVector; @@ -1335,27 +1337,30 @@ public abstract class ConnectionFunctions { } } - @RBuiltin(name = ".fastr.channelConnection", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"description", "channel", "open", "encoding"}, behavior = IO) + @RBuiltin(name = ".fastr.channelConnection", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"channel", "open", "encoding"}, behavior = IO) public abstract static class ChannelConnection extends RBuiltinNode { static { Casts casts = new Casts(ChannelConnection.class); - CastsHelper.description(casts); casts.arg("channel").mustNotBeMissing().mustBe(nullValue().not().and(instanceOf(TruffleObject.class))); CastsHelper.openMode(casts); CastsHelper.encoding(casts); } + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{RMissing.instance, RMissing.instance, Charset.defaultCharset().name()}; + } + @Specialization @TruffleBoundary - protected RAbstractIntVector channelConnection(String description, TruffleObject channel, String open, String encoding) { + protected RAbstractIntVector channelConnection(TruffleObject channel, String open, String encoding) { try { if (JavaInterop.isJavaObject(ByteChannel.class, channel)) { ByteChannel ch = JavaInterop.asJavaObject(ByteChannel.class, channel); - return new ChannelRConnection(description, ch, open, encoding).asVector(); + return new ChannelRConnection("", ch, open, encoding).asVector(); } - // TODO improve error handling - throw new RInternalError("invalid object type %s", JavaInterop.unbox(channel).getClass()); + throw error(RError.Message.INVALID_CHANNEL_OBJECT, JavaInterop.unbox(channel).getClass()); } catch (IOException ex) { throw RInternalError.shouldNotReachHere(); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index 8a7f66a247258f803b5df49579b82c53cdd83071..12d944ccc087bbfe42d0fcd5d2124de5af982738 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -846,7 +846,8 @@ public final class RError extends RuntimeException { TRUNCATE_ONLY_OPEN_CONN("can only truncate an open connection"), TRUNCATE_NOT_ENABLED("truncation not enabled for this connection"), TRUNCATE_UNSUPPORTED_FOR_CONN("cannot truncate connection: %s"), - INCOMPLETE_STRING_AT_EOF_DISCARDED("incomplete string at end of file has been discarded"); + INCOMPLETE_STRING_AT_EOF_DISCARDED("incomplete string at end of file has been discarded"), + INVALID_CHANNEL_OBJECT("invalid channel object type: %s"); public final String message; final boolean hasArgs; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ChannelConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ChannelConnections.java index a5ebc5068edc20c8f1ef3a65d86e78eaeca3491f..f5d8cd180262a621df563f2311e3ece0121fca12 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ChannelConnections.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ChannelConnections.java @@ -45,6 +45,7 @@ public class ChannelConnections { super(ConnectionClass.CHANNEL, modeString, AbstractOpenMode.Read, encoding); this.description = description; this.channel = Objects.requireNonNull(channel); + openNonLazyConnection(); } @Override diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java index 256b4e4a99fa60e7db6cdf5f274bd5e1eca561b5..7476dc54267c8089785ea1fdb99c401363d34367 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/SeekableMemoryByteChannel.java @@ -226,7 +226,11 @@ public class SeekableMemoryByteChannel implements SeekableByteChannel { @Override public void close() throws IOException { - open = false; + setOpen(false); + } + + public void setOpen(boolean open) { + this.open = open; } public InputStream getInputStream() { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 319431438de984e29e13ed3614c32b17a6f564f8..bb1f1db8422ae9b40d717832ea17eb5d6d77345f 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -9373,7 +9373,7 @@ Error in bitwXor(c("r"), c(16, 17)) : 'a' and 'b' must have the same type #argv <- list(function (from, strict = TRUE) from); .Internal(body(argv[[1]])) from -##com.oracle.truffle.r.test.builtins.TestBuiltin_body.testbody4#Ignored.Unknown# +##com.oracle.truffle.r.test.builtins.TestBuiltin_body.testbody4# #argv <- list(.Primitive('/')); .Internal(body(argv[[1]])) NULL @@ -29709,7 +29709,7 @@ Coefficients: #require(stats); lm(formula = y ~ x) Error in eval(expr, envir, enclos) : object 'y' not found -##com.oracle.truffle.r.test.builtins.TestBuiltin_lockEnvironment.testlockEnvironment#Ignored.MissingBuiltin# +##com.oracle.truffle.r.test.builtins.TestBuiltin_lockEnvironment.testlockEnvironment# #e <- new.env(); e$a <- 'foo'; lockEnvironment(e, 'a'); e$a <- 123 Error in e$a <- 123 : cannot change value of locked binding for 'a' @@ -33620,7 +33620,7 @@ Error in nchar("aasd", type = "charsxzy") : invalid 'type' argument #argv <- list(character(0), 'c', FALSE, FALSE); .Internal(nchar(argv[[1]], argv[[2]], argv[[3]], argv[[4]])) integer(0) -##com.oracle.truffle.r.test.builtins.TestBuiltin_nchar.testnchar13#Ignored.Unimplemented# +##com.oracle.truffle.r.test.builtins.TestBuiltin_nchar.testnchar13# #argv <- list(structure(c('rpart', 'recommended', '4.1-1', '2013-03-20', 'c(person(\'Terry\', \'Therneau\', role = \'aut\',\n\t email = \'therneau@mayo.edu\'),\n person(\'Beth\', \'Atkinson\', role = \'aut\',\t\n\t email = \'atkinson@mayo.edu\'),\n person(\'Brian\', \'Ripley\', role = c(\'aut\', \'trl\', \'cre\'),\n email = \'ripley@stats.ox.ac.uk\',\n\t\t comment = \'author of R port\'))', 'Recursive partitioning and regression trees', 'Recursive Partitioning', 'R (>= 2.14.0), graphics, stats, grDevices', 'survival', 'GPL-2 | GPL-3', 'yes', 'yes', 'Maintainers are not available to give advice on using a package\nthey did not author.', '2013-03-20 07:27:05 UTC; ripley', 'Terry Therneau [aut],\n Beth Atkinson [aut],\n Brian Ripley [aut, trl, cre] (author of R port)', 'Brian Ripley <ripley@stats.ox.ac.uk>'), .Names = c('Package', 'Priority', 'Version', 'Date', 'Authors@R', 'Description', 'Title', 'Depends', 'Suggests', 'License', 'LazyData', 'ByteCompile', 'Note', 'Packaged', 'Author', 'Maintainer')), 'c', TRUE, FALSE); .Internal(nchar(argv[[1]], argv[[2]], argv[[3]], argv[[4]])) Package Priority Version Date Authors@R Description 5 11 5 10 345 43 @@ -73276,6 +73276,14 @@ Read 4 items Summary of Residuals: +##com.oracle.truffle.r.test.library.base.TestConnections.testChannelConnection# +#if (length(grep("FastR", R.Version()$version.string)) != 1) { NULL } else { v <- .fastr.interop.import('_fastr_channel0'); zz <- .fastr.channelConnection(v, 'r+', 'native.enc'); writeLines('hi there', zz); close(zz); NULL } +NULL + +##com.oracle.truffle.r.test.library.base.TestConnections.testChannelConnection# +#if (length(grep("FastR", R.Version()$version.string)) != 1) { c('Hello, World!', 'second line') } else { v <- .fastr.interop.import('_fastr_channel0'); zz <- .fastr.channelConnection(v, 'r+', 'native.enc'); res <- readLines(zz); close(zz); res } +[1] "Hello, World!" "second line" + ##com.oracle.truffle.r.test.library.base.TestConnections.testEncoding# #fin <- file('', "w+", encoding = "___inexistingCharSet___") Error in file("", "w+", encoding = "___inexistingCharSet___") : @@ -73290,7 +73298,7 @@ Error in file("", "w+", encoding = "___inexistingCharSet___") : fifo TRUE -##com.oracle.truffle.r.test.library.base.TestConnections.testFifoOpenInexisting#Output.IgnoreErrorContext#Output.IgnoreWarningContext# +##com.oracle.truffle.r.test.library.base.TestConnections.testFifoOpenInexisting# #{ fn <- '___fifo_2367253765'; zz <- fifo(fn, 'r', blocking = TRUE); close(zz); unlink(fn) } Error in fifo(fn, "r", blocking = TRUE) : cannot open the connection In addition: Warning message: @@ -73728,48 +73736,6 @@ Error in textConnection(NULL, "r") : invalid 'text' argument #{ con <- textConnection(c("1", "2", "3","4")); readLines(con, 2); readLines(con, 2); readLines(con, 2) } character(0) -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#fn <- '__tmp_77253842367367'; zz <- file(fn, 'w'); writeLines(c('Hello', 'wonderful', 'World'), zz); seek(zz, 0); truncate(zz); close(zz); readLines(file(fn)); unlink(fn) -[1] 22 -NULL -character(0) - -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#fn <- '__tmp_98723669834556'; zz <- file(fn, 'w'); writeLines(c('Hello', 'wonderful', 'World'), zz); close(zz); zz <- file(fn, 'r'); truncate(zz); unlink(fn) -Error in truncate.connection(zz) : - can only truncate connections open for writing - -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#truncate(fifo('__fifo_872636743', 'w+', blocking=T)); unlink('__fifo_872636743') -Error in truncate.connection(fifo("__fifo_872636743", "w+", blocking = T)) : - truncation not enabled for this connection - -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#truncate(fifo('__fifo_982346798', 'r', blocking=T)); unlink('__fifo_982346798') -Error in fifo("__fifo_982346798", "r", blocking = T) : - cannot open the connection -In addition: Warning message: -In fifo("__fifo_982346798", "r", blocking = T) : - cannot open fifo '__fifo_982346798' - -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#truncate(pipe('ls')) -Error in truncate.connection(pipe("ls")) : - can only truncate an open connection - -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#zz <- file(''); writeLines(c('Hello', 'wonderful', 'World'), zz); seek(zz, 0); truncate(zz); flush(zz); readLines(zz) -[1] 22 -NULL -character(0) - -##com.oracle.truffle.r.test.library.base.TestConnections.testTruncate# -#zz <- rawConnection(raw(0), 'r+'); writeLines(c('hello', 'world'), zz); rawConnectionValue(zz); seek(zz, 5); truncate(zz); rawConnectionValue(zz); close(zz) - [1] 68 65 6c 6c 6f 0a 77 6f 72 6c 64 0a -[1] 12 -NULL -[1] 68 65 6c 6c 6f - ##com.oracle.truffle.r.test.library.base.TestConnections.testWriteTextConnection# #c <- textConnection('out', 'w'); cat('testtext', file=c); isIncomplete(c); cat('testtext2\n', file=c); isIncomplete(c); close(c); out [1] TRUE diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java index 641e936737ab50519a442531b421d471e8d3d139..16b5b133705b0a2a99cb86e61983b80b95e80c49 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestConnections.java @@ -24,6 +24,7 @@ package com.oracle.truffle.r.test.library.base; import java.io.IOException; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.file.Files; import java.nio.file.Path; @@ -45,6 +46,8 @@ import com.oracle.truffle.r.test.TestRBase; // Checkstyle: stop line length check public class TestConnections extends TestRBase { + private static final String CHANNEL_NAME = "_fastr_channel0"; + private static final class TestDir { private final Path testDirPath; @@ -272,15 +275,39 @@ public class TestConnections extends TestRBase { public void testChannelConnection() throws IOException { final String line0 = "Hello, World!\n"; - final String line1 = "incomplete"; + final String line1 = "second line\n"; CHANNEL.write(line0.getBytes()); CHANNEL.write(line1.getBytes()); + long oldPos = CHANNEL.position(); CHANNEL.position(0); - assertEval(" v <- .fastr.interop.import('_fastr_channel0'); zz <- .fastr.channelConnection('hello', v, 'r+', 'native.enc'); res <- readLines(zz); close(zz); res", line0 + line1); + assertEvalFastR(String.format("v <- .fastr.interop.import('%s'); zz <- .fastr.channelConnection(v, 'r+', 'native.enc'); res <- readLines(zz); close(zz); res", + CHANNEL_NAME), + "c('Hello, World!', 'second line')"); + + if (!generatingExpected()) { + // test if FastR consumed the data + Assert.assertEquals(oldPos, CHANNEL.position()); + + // re-open channel + CHANNEL.setOpen(true); + CHANNEL.position(0); + } + + final String response = "hi there"; + assertEvalFastR(String.format("v <- .fastr.interop.import('%s'); zz <- .fastr.channelConnection(v, 'r+', 'native.enc'); writeLines('%s', zz); close(zz); NULL ", CHANNEL_NAME, response), + "NULL"); + + if (!generatingExpected()) { + ByteBuffer buf = ByteBuffer.allocate(response.length()); + CHANNEL.setOpen(true); + CHANNEL.position(0); + CHANNEL.read(buf); + Assert.assertEquals(response, new String(buf.array())); + } } @Override public void addPolyglotSymbols(Builder builder) { - builder.globalSymbol("_fastr_channel0", JavaInterop.asTruffleObject("hehe")); + builder.globalSymbol(CHANNEL_NAME, JavaInterop.asTruffleObject(CHANNEL)); } }