diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java index e1c3ca08e3b0899498abbe29882a93dc486d7462..b200768c1127edf4101bdbf8d53dd8eed8a7b69b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java @@ -361,6 +361,7 @@ public class BasePackage extends RBuiltinPackage { add(DimNames.class, DimNamesNodeGen::create); add(DoCall.class, DoCallNodeGen::create); add(DPut.class, DPutNodeGen::create); + add(Dump.class, DumpNodeGen::create); add(Drop.class, DropNodeGen::create); add(DuplicatedFunctions.AnyDuplicated.class, DuplicatedFunctionsFactory.AnyDuplicatedNodeGen::create); add(DuplicatedFunctions.Duplicated.class, DuplicatedFunctionsFactory.DuplicatedNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dump.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dump.java new file mode 100644 index 0000000000000000000000000000000000000000..22ef993e33fb83761e59cb3d0bce3e00349cff74 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dump.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.base; + +import static com.oracle.truffle.r.runtime.RError.Message.INVALID_ARGUMENT; +import static com.oracle.truffle.r.runtime.RVisibility.OFF; +import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO; +import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; + +import java.io.IOException; +import java.util.Arrays; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef; +import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.nodes.function.PromiseHelperNode; +import com.oracle.truffle.r.runtime.RDeparse; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.builtins.RBuiltin; +import com.oracle.truffle.r.runtime.conn.RConnection; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.env.REnvironment; +import com.oracle.truffle.r.runtime.nodes.RBaseNode; + +@RBuiltin(name = "dump", visibility = OFF, kind = INTERNAL, parameterNames = {"list", "file", "envir", "opts", "evaluate"}, behavior = IO) +public abstract class Dump extends RBuiltinNode.Arg5 { + + @Child protected Helper helper; + + static { + Casts casts = new Casts(Dump.class); + casts.arg("list").mustNotBeMissing().mustBe(Predef.stringValue()).asStringVector(); + casts.arg("file").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst(); + casts.arg("envir").mustNotBeMissing().mustBe(REnvironment.class, INVALID_ARGUMENT, "envir"); + casts.arg("opts").mustNotBeMissing().asIntegerVector().findFirst(); + casts.arg("evaluate").mustNotBeMissing().asLogicalVector().findFirst().map(Predef.toBoolean()); + } + + @Specialization + protected Object dump(VirtualFrame frame, RAbstractStringVector x, int file, REnvironment envir, int opts, boolean evaluate) { + if (helper == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + helper = insert(new Helper()); + } + int retrieved = 0; + String[] retrievedNames = new String[x.getLength()]; + Object[] objects = new Object[x.getLength()]; + for (int i = 0; i < x.getLength(); i++) { + String name = x.getDataAt(i); + objects[i] = helper.getAndCheck(frame, name, envir, evaluate); + if (objects[i] != null) { + retrievedNames[retrieved++] = name; + } + } + writeToConnection(deparse(x, objects, prepareOpts(opts, evaluate)), file); + return RDataFactory.createStringVector(Arrays.copyOfRange(retrievedNames, 0, retrieved), RDataFactory.COMPLETE_VECTOR); + } + + @TruffleBoundary + private static String deparse(RAbstractStringVector x, Object[] objects, int opts) { + assert x.getLength() == objects.length; + StringBuilder code = new StringBuilder(); + for (int i = 0; i < objects.length; i++) { + if (objects[i] != null) { + if (i != 0) { + code.append(System.lineSeparator()); + } + code.append(x.getDataAt(i)).append(" <-").append(System.lineSeparator()).append(RDeparse.deparse(objects[i], RDeparse.DEFAULT_CUTOFF, true, opts, -1)); + } + } + return code.toString(); + } + + @TruffleBoundary + private void writeToConnection(String code, int file) { + try (RConnection openConn = RConnection.fromIndex(file).forceOpen("wt")) { + openConn.writeString(code, true); + } catch (IOException ex) { + throw error(RError.Message.GENERIC, ex.getMessage()); + } + } + + private static int prepareOpts(int opts, boolean evaluate) { + return evaluate ? opts : opts | RDeparse.DELAYPROMISES; + } + + private static final class Helper extends RBaseNode { + + @Child private PromiseHelperNode promiseHelper; + + @CompilationFinal private boolean firstExecution = true; + + protected void unknownObject(String x) { + warning(RError.Message.UNKNOWN_OBJECT, x); + } + + protected Object checkPromise(VirtualFrame frame, Object r, String identifier) { + if (r instanceof RPromise) { + if (firstExecution) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + firstExecution = false; + return ReadVariableNode.evalPromiseSlowPathWithName(identifier, frame, (RPromise) r); + } + if (promiseHelper == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + promiseHelper = insert(new PromiseHelperNode()); + } + return promiseHelper.evaluate(frame, (RPromise) r); + } else { + return r; + } + } + + protected Object getAndCheck(VirtualFrame frame, String x, REnvironment env, boolean evaluatePromise) { + Object obj; + if (evaluatePromise) { + obj = checkPromise(frame, env.get(x), x); + } else { + obj = env.get(x); + } + if (obj != null) { + return obj; + } else { + unknownObject(x); + return null; + } + } + } +} 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 239219264a4f9f23feaf094557fb6235d2c092e8..426fa0ea7c99e4765c2fe8bc6171cc6124328ee6 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 @@ -22909,6 +22909,43 @@ End = 1 Frequency = 1 [1] FALSE +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpData# +#{ x <- 10; .Internal(dump('x', stdout(), environment(), .deparseOpts('all'), TRUE))} +x <- +10 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpData# +#{ x <- 1:10; .Internal(dump('x', stdout(), environment(), .deparseOpts('all'), TRUE))} +x <- +1:10 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpData# +#{ x <- c(10,20,25); .Internal(dump('x', stdout(), environment(), .deparseOpts('all'), TRUE))} +x <- +c(10, 20, 25) + +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpData# +#{ x <- list(a=10,b=20); .Internal(dump('x', stdout(), environment(), .deparseOpts('all'), TRUE))} +x <- +structure(list(a = 10, b = 20), .Names = c("a", "b")) + +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpFunctions# +#{ foo <- function() cat('Hello'); bar <- function() cat('World'); .Internal(dump(c('foo', 'bar'), stdout(), environment(), .deparseOpts('all'), TRUE))} +foo <- +function() cat('Hello') +bar <- +function() cat('World') + +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpFunctions# +#{ fun <- function() print('Hello, World!'); .Internal(dump('fun', stdout(), environment(), .deparseOpts('all'), TRUE))} +fun <- +function() print('Hello, World!') + +##com.oracle.truffle.r.test.builtins.TestBuiltin_dump.testDumpLanguage# +#{ x <- quote(2 + 3); .Internal(dump('x', stdout(), environment(), .deparseOpts('all'), TRUE))} +x <- +quote(2 + 3) + ##com.oracle.truffle.r.test.builtins.TestBuiltin_duplicated.testDuplicated# #{ duplicated(NULL, 0); } logical(0) diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dump.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dump.java new file mode 100644 index 0000000000000000000000000000000000000000..8cc329e4ecccccf66ba34872fd267de9785074c5 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dump.java @@ -0,0 +1,41 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 2012-2014, Purdue University + * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.test.builtins; + +import org.junit.Test; + +import com.oracle.truffle.r.test.TestBase; + +// Checkstyle: stop line length check +public class TestBuiltin_dump extends TestBase { + + private static final String TEMPLATE = "{ %s; .Internal(dump(%s, stdout(), environment(), .deparseOpts('all'), TRUE))}"; + + @Test + public void testDumpFunctions() { + assertEval(String.format(TEMPLATE, "fun <- function() print('Hello, World!')", "'fun'")); + assertEval(String.format(TEMPLATE, "foo <- function() cat('Hello'); bar <- function() cat('World')", "c('foo', 'bar')")); + } + + @Test + public void testDumpData() { + assertEval(String.format(TEMPLATE, "x <- 10", "'x'")); + assertEval(String.format(TEMPLATE, "x <- list(a=10,b=20)", "'x'")); + assertEval(String.format(TEMPLATE, "x <- c(10,20,25)", "'x'")); + assertEval(String.format(TEMPLATE, "x <- 1:10", "'x'")); + } + + @Test + public void testDumpLanguage() { + assertEval(String.format(TEMPLATE, "x <- quote(2 + 3)", "'x'")); + } + +}