diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index 8c6219e025259da327baa3ef5e0c2db103981188..917a24bf4a5a52e7146a01577dc495f2eff96261 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -22,6 +22,12 @@ */ package com.oracle.truffle.r.engine; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -31,6 +37,8 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.r.engine.shell.RCommand; +import com.oracle.truffle.r.engine.shell.RscriptCommand; import com.oracle.truffle.r.nodes.RASTBuilder; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.RRootNode; @@ -658,4 +666,106 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { return DebugHandling.undebug(func); } + @Override + public Object rcommandMain(String[] args, boolean intern) { + IORedirect redirect = handleIORedirect(args, intern); + Object result = RCommand.doMain(redirect.args, false, redirect.in, redirect.out); + return redirect.getInternResult(result); + } + + @Override + public Object rscriptMain(String[] args, boolean intern) { + IORedirect redirect = handleIORedirect(args, intern); + Object result = RscriptCommand.doMain(redirect.args, false, redirect.in, redirect.out); + return redirect.getInternResult(result); + } + + private static final class IORedirect { + private final InputStream in; + private final OutputStream out; + private final String[] args; + private final boolean intern; + + private IORedirect(InputStream in, OutputStream out, String[] args, boolean intern) { + this.in = in; + this.out = out; + this.args = args; + this.intern = intern; + } + + private Object getInternResult(Object result) { + if (intern) { + int status = (int) result; + ByteArrayOutputStream bos = (ByteArrayOutputStream) out; + String s = new String(bos.toByteArray()); + RStringVector sresult = s.length() == 0 ? RDataFactory.createEmptyStringVector() : RDataFactory.createStringVectorFromScalar(s); + if (status != 0) { + sresult.setAttr("status", RDataFactory.createIntVectorFromScalar(status)); + } + return sresult; + } else { + return result; + } + + } + } + + private static IORedirect handleIORedirect(String[] args, boolean intern) { + /* + * This code is primarily intended to handle the "system" .Internal so the possible I/O + * redirects are taken from the system/system2 R code. N.B. stdout redirection is never set + * if "intern == true. Both input and output can be redirected to /dev/null. + */ + InputStream in = System.in; + OutputStream out = System.out; + ArrayList<String> newArgsList = new ArrayList<>(); + int i = 0; + while (i < args.length) { + String arg = args[i]; + if (arg.equals("<")) { + String file; + if (i < args.length - 1) { + file = Utils.tildeExpand(Utils.unShQuote(args[i + 1])); + } else { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, "redirect missing"); + } + try { + in = new FileInputStream(file); + } catch (IOException ex) { + throw RError.error(RError.NO_CALLER, RError.Message.NO_SUCH_FILE, file); + } + arg = null; + i++; + } else if (arg.startsWith("2>")) { + if (arg.equals("2>&1")) { + // happens anyway + } else { + assert !intern; + throw RError.nyi(RError.NO_CALLER, "stderr redirect"); + } + arg = null; + } else if (arg.startsWith(">")) { + assert !intern; + arg = null; + throw RError.nyi(RError.NO_CALLER, "stdout redirect"); + } + if (arg != null) { + newArgsList.add(arg); + } + i++; + } + String[] newArgs; + if (newArgsList.size() == args.length) { + newArgs = args; + } else { + newArgs = new String[newArgsList.size()]; + newArgsList.toArray(newArgs); + } + // to implement intern, we create a ByteArryOutputStream to capture the output + if (intern) { + out = new ByteArrayOutputStream(); + } + return new IORedirect(in, out, newArgs, intern); + } + } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java index 0e5040ee582fd8423eda42aeb596e77094ab5c8d..080dde533c0fc60ecdedd405382a14c34983badd 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java @@ -58,7 +58,7 @@ public class EmbeddedConsoleHandler implements ConsoleHandler { if (startParams.getNoReadline()) { delegate = new DefaultConsoleHandler(System.in, System.out); } else { - delegate = new JLineConsoleHandler(startParams); + delegate = new JLineConsoleHandler(startParams, System.in, System.out); } } } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java index dc3db25f769838496fc5eb497fa461721ed4e104..b43a2c2e2911c12453ab5b330f91c635124c2b67 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java @@ -24,6 +24,8 @@ package com.oracle.truffle.r.engine.shell; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -42,9 +44,9 @@ class JLineConsoleHandler implements ConsoleHandler { private final boolean isInteractive; private final PrintWriter printWriter; - JLineConsoleHandler(RStartParams startParams) { + JLineConsoleHandler(RStartParams startParams, InputStream inStream, OutputStream outStream) { try { - console = new ConsoleReader(System.in, System.out); + console = new ConsoleReader(inStream, outStream); console.setHandleUserInterrupt(true); console.setExpandEvents(false); } catch (IOException ex) { diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java index 04c66c9f1e98f0a38d1966e699a4142dfe2392ad..c62f93d7221477f9421f4419f702c46d2ffb19a1 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java @@ -29,6 +29,8 @@ import java.io.Console; import java.io.EOFException; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.util.List; @@ -66,14 +68,18 @@ public class RCommand { // CheckStyle: stop system..print check public static void main(String[] args) { - RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false); - options.printHelpAndVersion(); - PolyglotEngine vm = createPolyglotEngineFromCommandLine(options, false); + doMain(args, true, System.in, System.out); // never returns - readEvalPrint(vm); throw RInternalError.shouldNotReachHere(); } + public static int doMain(String[] args, boolean initial, InputStream inStream, OutputStream outStream) { + RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false); + options.printHelpAndVersion(); + PolyglotEngine vm = createPolyglotEngineFromCommandLine(options, false, initial, inStream, outStream); + return readEvalPrint(vm); + } + /** * The standard R script escapes spaces to "~+~" in "-e" and "-f" commands. */ @@ -81,7 +87,7 @@ public class RCommand { return input.replace("~+~", " "); } - static PolyglotEngine createPolyglotEngineFromCommandLine(RCmdOptions options, boolean embedded) { + static PolyglotEngine createPolyglotEngineFromCommandLine(RCmdOptions options, boolean embedded, boolean initial, InputStream inStream, OutputStream outStream) { RStartParams rsp = new RStartParams(options, embedded); String fileArg = options.getString(FILE); @@ -120,13 +126,21 @@ public class RCommand { List<String> lines; String filePath; try { - File file = new File(fileArg); + /* + * If initial==false, ~ expansion will not have been done and the open will fail. + * It's harmless to always do it. + */ + File file = new File(Utils.tildeExpand(fileArg)); lines = Files.readAllLines(file.toPath()); filePath = file.getCanonicalPath(); } catch (IOException e) { - throw Utils.rSuicide("cannot open file '" + fileArg + "': " + e.getMessage()); + if (initial) { + throw Utils.rSuicide(String.format(RError.Message.NO_SUCH_FILE.message, fileArg)); + } else { + throw RError.error(RError.NO_CALLER, RError.Message.NO_SUCH_FILE, fileArg); + } } - consoleHandler = new StringConsoleHandler(lines, System.out, filePath); + consoleHandler = new StringConsoleHandler(lines, outStream, filePath); } else if (options.getStringList(EXPR) != null) { List<String> exprs = options.getStringList(EXPR); for (int i = 0; i < exprs.size(); i++) { @@ -137,7 +151,7 @@ public class RCommand { } // cf GNU R rsp.setInteractive(false); - consoleHandler = new StringConsoleHandler(exprs, System.out, RSource.Internal.EXPRESSION_INPUT.string); + consoleHandler = new StringConsoleHandler(exprs, outStream, RSource.Internal.EXPRESSION_INPUT.string); } else { /* * GnuR behavior differs from the manual entry for {@code interactive} in that {@code @@ -148,23 +162,28 @@ public class RCommand { * should be lazy, as these may not be necessary and can cause hangs if stdin has been * redirected. */ - Console sysConsole = System.console(); + Console sysConsole = System.console(); // TODO fix for context sessions boolean isInteractive = options.getBoolean(INTERACTIVE) || sysConsole != null; if (!isInteractive && rsp.getSaveAction() != SA_TYPE.SAVE && rsp.getSaveAction() != SA_TYPE.NOSAVE) { - throw Utils.rSuicide("you must specify '--save', '--no-save' or '--vanilla'"); + String msg = "you must specify '--save', '--no-save' or '--vanilla'"; + if (initial) { + throw Utils.rSuicide(msg); + } else { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, msg); + } } if (embedded) { consoleHandler = new EmbeddedConsoleHandler(rsp); } else { boolean useReadLine = !rsp.getNoReadline(); if (useReadLine) { - consoleHandler = new JLineConsoleHandler(rsp); + consoleHandler = new JLineConsoleHandler(rsp, inStream, outStream); } else { - consoleHandler = new DefaultConsoleHandler(System.in, System.out); + consoleHandler = new DefaultConsoleHandler(inStream, outStream); } } } - return ContextInfo.create(rsp, ContextKind.SHARE_NOTHING, null, consoleHandler).createVM(); + return ContextInfo.create(rsp, ContextKind.SHARE_NOTHING, initial ? null : RContext.getInstance(), consoleHandler).createVM(); } private static final Source GET_ECHO = RSource.fromTextInternal("invisible(getOption('echo'))", RSource.Internal.GET_ECHO); @@ -181,9 +200,10 @@ public class RCommand { * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before * exiting. So,in either case, we never return. */ - static void readEvalPrint(PolyglotEngine vm) { + static int readEvalPrint(PolyglotEngine vm) { int lastStatus = 0; - ConsoleHandler consoleHandler = ContextInfo.getContextInfo(vm).getConsoleHandler(); + ContextInfo contextInfo = ContextInfo.getContextInfo(vm); + ConsoleHandler consoleHandler = contextInfo.getConsoleHandler(); try { // console.println("initialize time: " + (System.currentTimeMillis() - start)); REPL: for (;;) { @@ -241,8 +261,13 @@ public class RCommand { throw (RuntimeException) cause; } else if (cause instanceof ExitException) { // usually from quit - vm.dispose(); - Utils.systemExit(((ExitException) cause).getStatus()); + int status = ((ExitException) cause).getStatus(); + if (contextInfo.getParent() == null) { + vm.dispose(); + Utils.systemExit(status); + } else { + return status; + } } else { RInternalError.reportErrorAndConsoleLog(cause, consoleHandler, 0); // We continue the repl even though the system may be broken @@ -262,13 +287,18 @@ public class RCommand { } catch (Throwable e) { if (e.getCause() instanceof ExitException) { // normal quit, but with exit code based on lastStatus - Utils.systemExit(lastStatus); + if (contextInfo.getParent() == null) { + Utils.systemExit(lastStatus); + } else { + return lastStatus; + } } throw RInternalError.shouldNotReachHere(e); } } finally { vm.dispose(); } + return 0; } private static boolean doEcho(PolyglotEngine vm) { diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java index c4a0903de44e7231885b14bfc2dcfb98a58a58e2..d200eba9cb35d29b2f5a45318e5ef5becb4c9876 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java @@ -70,7 +70,7 @@ public class REmbedded { private static PolyglotEngine initializeR(String[] args) { RContext.setEmbedded(); RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, true); - PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, true); + PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, true, true, System.in, System.out); try { vm.eval(INIT); } catch (IOException ex) { diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java index 300f52e8acdf1ff2ae261947351ff9ba36d515bc..ce0f19aa0773a6387b9c3bb1050906ece806b1d2 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java @@ -29,6 +29,8 @@ import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.NO_RESTORE; import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.SLAVE; import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.VERSION; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import com.oracle.truffle.api.vm.PolyglotEngine; @@ -97,14 +99,19 @@ public class RscriptCommand { } public static void main(String[] args) { + doMain(args, true, null, null); + // never returns + throw RInternalError.shouldNotReachHere(); + } + + public static int doMain(String[] args, boolean initial, InputStream inStream, OutputStream outStream) { // Since many of the options are shared parse them from an RSCRIPT perspective. // Handle --help and --version specially, as they exit. RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.RSCRIPT, args, false); preprocessRScriptOptions(options); - PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, false); - // never returns - RCommand.readEvalPrint(vm); - throw RInternalError.shouldNotReachHere(); + PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, false, initial, inStream, outStream); + return RCommand.readEvalPrint(vm); + } private static void printVersionAndExit() { diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java index e2edc533d17467e02b41229e6293bc86c379f220..60af1f537c607a7152802ea422b86f8dd4459c78 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.engine.shell; +import java.io.OutputStream; import java.io.PrintStream; import java.util.List; @@ -35,9 +36,9 @@ class StringConsoleHandler implements ConsoleHandler { private String prompt; private int currentLine; - StringConsoleHandler(List<String> lines, PrintStream output, String inputDescription) { + StringConsoleHandler(List<String> lines, OutputStream output, String inputDescription) { this.lines = lines; - this.output = output; + this.output = output instanceof PrintStream ? (PrintStream) output : new PrintStream(output); this.inputDescription = inputDescription; } 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 842c52397077ded5530639fd9aa1201a406e55a3..b808d98b94ced2e7e52e284aca02c8cac50f6ae0 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 @@ -44,6 +44,8 @@ import com.oracle.truffle.r.nodes.builtin.base.foreign.DotC; import com.oracle.truffle.r.nodes.builtin.base.foreign.DotCNodeGen; import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctions; import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctionsFactory; +import com.oracle.truffle.r.nodes.builtin.base.system.SystemFunction; +import com.oracle.truffle.r.nodes.builtin.base.system.SystemFunctionNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext; import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory; import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebug; @@ -278,6 +280,8 @@ public class BasePackage extends RBuiltinPackage { add(WithVisible.class, WithVisibleNodeGen::create); add(Exists.class, ExistsNodeGen::create); add(Expression.class, ExpressionNodeGen::create); + add(FastRContext.R.class, FastRContextFactory.RNodeGen::create); + add(FastRContext.Rscript.class, FastRContextFactory.RscriptNodeGen::create); add(FastRContext.CloseChannel.class, FastRContextFactory.CloseChannelNodeGen::create); add(FastRContext.CreateChannel.class, FastRContextFactory.CreateChannelNodeGen::create); add(FastRContext.Eval.class, FastRContextFactory.EvalNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ContextSystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ContextSystemFunctionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8a20180fa664298496617342b267e2abc18d2795 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ContextSystemFunctionFactory.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016, 2016, 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.system; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.nodes.builtin.base.system.ContextSystemFunctionFactoryFactory.ContextRSystemFunctionNodeGen; +import com.oracle.truffle.r.nodes.builtin.base.system.ContextSystemFunctionFactoryFactory.ContextRscriptSystemFunctionNodeGen; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.nodes.RBaseNode; + +/** + * A variant that uses {@code .fastr.context.r} for R sub-processes and throws an error otherwise. + * Used in systems that do not support {@code ProcessBuilder}. + */ +public class ContextSystemFunctionFactory extends SystemFunctionFactory { + private abstract static class ContextSystemFunctionNode extends RBaseNode { + public abstract Object execute(VirtualFrame frame, Object command, Object intern); + + } + + public abstract static class ContextRSystemFunctionNode extends ContextSystemFunctionNode { + @Child FastRContext.R contextRNode; + + private void initContextRNode() { + if (contextRNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + contextRNode = insert(FastRContextFactory.RNodeGen.create(null)); + } + + } + + @Specialization + protected Object systemFunction(VirtualFrame frame, RAbstractStringVector args, boolean intern) { + initContextRNode(); + Object result = contextRNode.execute(frame, args, intern); + return result; + } + } + + public abstract static class ContextRscriptSystemFunctionNode extends ContextSystemFunctionNode { + @Child FastRContext.Rscript contextRscriptNode; + + private void initContextRscriptNode() { + if (contextRscriptNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + contextRscriptNode = insert(FastRContextFactory.RscriptNodeGen.create(null)); + } + + } + + @Specialization + protected Object systemFunction(VirtualFrame frame, RAbstractStringVector args, boolean intern) { + initContextRscriptNode(); + Object result = contextRscriptNode.execute(frame, args, intern); + return result; + } + } + + @Override + Object execute(VirtualFrame frame, String command, boolean intern) { + log(command, "Context"); + String[] parts = command.split(" "); + String rcommand = isFastR(parts[0]); + if (rcommand != null) { + String[] args = new String[parts.length - 1]; + System.arraycopy(parts, 1, args, 0, args.length); + ContextSystemFunctionNode node = rcommand.equals("R") ? ContextRSystemFunctionNodeGen.create() : ContextRscriptSystemFunctionNodeGen.create(); + Object result = node.execute(frame, RDataFactory.createStringVector(args, RDataFactory.COMPLETE_VECTOR), intern); + return result; + } else { + throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, command + " cannot be executed in a context"); + } + } + +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/PreferContextSystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/PreferContextSystemFunctionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..abead3646d06c1548a4658bb0d75be5b4163934c --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/PreferContextSystemFunctionFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, 2016, 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. + */ +/** + * A variant that prefers to use {@code .fastr.context.r} for R sub-processes but backs off to + * {@code ProcessBuilder}. + */ +package com.oracle.truffle.r.nodes.builtin.base.system; + +import com.oracle.truffle.api.frame.VirtualFrame; + +public class PreferContextSystemFunctionFactory extends SystemFunctionFactory { + private ContextSystemFunctionFactory contextSystemFunctionFactory; + private ProcessSystemFunctionFactory processSystemFunctionFactory; + + @Override + Object execute(VirtualFrame frame, String command, boolean intern) { + String[] parts = command.split(" "); + String rcommand = isFastR(parts[0]); + if (rcommand == null) { + if (processSystemFunctionFactory == null) { + processSystemFunctionFactory = new ProcessSystemFunctionFactory(); + } + return processSystemFunctionFactory.execute(frame, command, intern); + } else { + if (contextSystemFunctionFactory == null) { + contextSystemFunctionFactory = new ContextSystemFunctionFactory(); + } + return contextSystemFunctionFactory.execute(frame, command, intern); + + } + } +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java similarity index 76% rename from com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java rename to com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java index 0590d2becca1a0bc2cf94f3bafb57a962f85df7c..9be9ca380beaadf2f9c20a535849aadaeeb19a91 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2016, 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 @@ -20,11 +20,7 @@ * 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.RVisibility.CUSTOM; -import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; -import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; +package com.oracle.truffle.r.nodes.builtin.base.system; import java.io.IOException; import java.io.InputStream; @@ -32,31 +28,28 @@ import java.lang.ProcessBuilder.Redirect; import java.util.Map; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.r.runtime.ProcessOutputManager; -import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RStringVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; -@RBuiltin(name = "system", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX) -public abstract class SystemFunction extends RBuiltinNode { - @Specialization +public class ProcessSystemFunctionFactory extends SystemFunctionFactory { + + @Override + Object execute(VirtualFrame frame, String command, boolean intern) { + return execute(command, intern); + } + @TruffleBoundary - protected Object system(RAbstractStringVector command, byte internLogical) { + private Object execute(String command, boolean intern) { Object result; - boolean intern = RRuntime.fromLogical(internLogical); String shell = RContext.getInstance().stateREnvVars.get("SHELL"); if (shell == null) { shell = "/bin/sh"; } - if (RContext.getInstance().stateREnvVars.getMap().get("FASTR_LOGCHILD") != null) { - System.out.printf("FastR system: %s -c %s%n", shell, command.getDataAt(0)); - } - ProcessBuilder pb = new ProcessBuilder(shell, "-c", command.getDataAt(0)); + log(String.format("%s -c %s", shell, command), "Process"); + ProcessBuilder pb = new ProcessBuilder(shell, "-c", command); updateEnvironment(pb); pb.redirectInput(Redirect.INHERIT); if (intern) { @@ -110,4 +103,5 @@ public abstract class SystemFunction extends RBuiltinNode { } } } + } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..2b4d464807199ebf0eaf6898ce640aea8f900333 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, 2016, 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.system; + +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean; +import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM; +import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; +import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; + +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; +import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.builtins.RBuiltin; + +@RBuiltin(name = "system", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX) +public abstract class SystemFunction extends RBuiltinNode { + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("command").mustBe(stringValue(), RError.Message.SYSTEM_CHAR_ARG).asStringVector().findFirst(); + casts.arg("intern").asLogicalVector().findFirst().notNA(RError.Message.SYSTEM_INTERN_NOT_NA).map(toBoolean()); + } + + @Specialization + protected Object system(VirtualFrame frame, String command, boolean intern) { + return SystemFunctionFactory.getInstance().execute(frame, command.trim(), intern); + } + +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..259a7f5166e40bdfe0a07a021dfdb30a4e833bde --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016, 2016, 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.system; + +import java.io.IOException; +import java.nio.file.FileSystems; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.runtime.REnvVars; +import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.context.RContext; + +abstract class SystemFunctionFactory { + private static String kind; + private static SystemFunctionFactory theInstance; + + static { + kind = System.getProperty("fastr.systemfunction.factory.kind", "Process"); + String className = "com.oracle.truffle.r.nodes.builtin.base.system." + kind + "SystemFunctionFactory"; + try { + theInstance = (SystemFunctionFactory) Class.forName(className).newInstance(); + } catch (Exception ex) { + // CheckStyle: stop system..print check + Utils.rSuicide("Failed to instantiate class: " + className); + } + } + + @TruffleBoundary + public static SystemFunctionFactory getInstance() { + return theInstance; + } + + /** + * Implements the system {@code .Internal}. If {@code intern} is {@code true} the result is a + * character vector containing the output of the process, with a {@code status} attribute + * carrying the return code, else it is just the return code. + * + * {@code command} is a string with args separated by spaces with the first element enclosed in + * single quotes. + */ + abstract Object execute(VirtualFrame frame, String command, boolean intern); + + @TruffleBoundary + protected void log(String command, String useKind) { + if (RContext.getInstance().stateREnvVars.getMap().get("FASTR_LOG_SYSTEM") != null) { + System.out.printf("FastR system (%s): %s%n", useKind, command); + } + + } + + @TruffleBoundary + protected void log(String command) { + log(command, kind); + } + + /** + * Returns {@code true} iff, {@code command} is {@code R} or {@code Rscript}. + */ + protected static String isFastR(String command) { + // strip off quotes + String xc = Utils.unShQuote(command); + if (xc.equals("R") || xc.equals("Rscript")) { + return xc; + } + // often it is an absolute path + String rhome = REnvVars.rHome(); + if (isFullPath(rhome, "Rscript", xc)) { + return "Rscript"; + } + if (isFullPath(rhome, "R", xc)) { + return "R"; + } + return null; + } + + private static boolean isFullPath(String rhome, String rcmd, String command) { + try { + String rpath = FileSystems.getDefault().getPath(rhome, "bin", rcmd).toString(); + String cpath = FileSystems.getDefault().getPath(command).toRealPath().toString(); + if (cpath.equals(rpath)) { + return true; + } + } catch (IOException ex) { + // should not happen but just return false + } + return false; + } + +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java index d4e2fe9c14e54bcbd422b3c076807c755cc37efe..a45a017fe4c63a219c4d2646dfe1cd9b25be2514 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java @@ -37,6 +37,7 @@ import com.oracle.truffle.r.runtime.RChannel; import com.oracle.truffle.r.runtime.RCmdOptions; import com.oracle.truffle.r.runtime.RCmdOptions.Client; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RSource; import com.oracle.truffle.r.runtime.RStartParams; import com.oracle.truffle.r.runtime.builtins.RBuiltin; @@ -55,6 +56,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; */ public class FastRContext { + private static final String[] EMPTY = new String[0]; + private abstract static class CastHelper extends RBuiltinNode { protected void exprs(CastBuilder casts) { casts.arg("exprs").asStringVector().mustBe(nullValue().not().and(notEmpty())); @@ -65,10 +68,6 @@ public class FastRContext { equalTo(RContext.ContextKind.SHARE_NOTHING.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RW.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RO.name())))); } - protected void args(CastBuilder casts) { - casts.arg("args").asStringVector().mustBe(nullValue().not().and(notEmpty())); - } - protected void pc(CastBuilder casts) { casts.arg("pc").asIntegerVector().findFirst().notNA().mustBe(gt(0)); } @@ -89,11 +88,11 @@ public class FastRContext { * {@code .fastr.context.join}. * */ - @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind", "args"}, behavior = COMPLEX) + @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind"}, behavior = COMPLEX) public abstract static class Spawn extends CastHelper { @Override public Object[] getDefaultParameterValues() { - return new Object[]{RMissing.instance, 1, "SHARE_NOTHING", ""}; + return new Object[]{RMissing.instance, 1, "SHARE_NOTHING"}; } @Override @@ -101,17 +100,16 @@ public class FastRContext { exprs(casts); pc(casts); kind(casts); - args(casts); } @Specialization @TruffleBoundary - protected RIntVector spawn(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) { + protected RIntVector spawn(RAbstractStringVector exprs, int pc, String kind) { RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind); RContext.EvalThread[] threads = new RContext.EvalThread[pc]; int[] data = new int[pc]; for (int i = 0; i < pc; i++) { - ContextInfo info = createContextInfo(contextKind, args); + ContextInfo info = createContextInfo(contextKind); threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL)); data[i] = info.getId(); } @@ -162,11 +160,11 @@ public class FastRContext { * sublist contains the result of the evaluation with name "result". It may also have an * attribute "error" if the evaluation threw an exception, in which case the result will be NA. */ - @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind", "args"}, behavior = COMPLEX) + @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind"}, behavior = COMPLEX) public abstract static class Eval extends CastHelper { @Override public Object[] getDefaultParameterValues() { - return new Object[]{RMissing.instance, 1, "SHARE_NOTHING", ""}; + return new Object[]{RMissing.instance, 1, "SHARE_NOTHING"}; } @Override @@ -174,24 +172,23 @@ public class FastRContext { exprs(casts); pc(casts); kind(casts); - args(casts); } @Specialization @TruffleBoundary - protected Object eval(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) { + protected Object eval(RAbstractStringVector exprs, int pc, String kind) { RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind); Object[] results = new Object[pc]; if (pc == 1) { - ContextInfo info = createContextInfo(contextKind, args); + ContextInfo info = createContextInfo(contextKind); PolyglotEngine vm = info.createVM(); results[0] = RContext.EvalThread.run(vm, info, RSource.fromTextInternal(exprs.getDataAt(0), RSource.Internal.CONTEXT_EVAL)); } else { // separate threads that run in parallel; invoking thread waits for completion RContext.EvalThread[] threads = new RContext.EvalThread[pc]; for (int i = 0; i < pc; i++) { - ContextInfo info = createContextInfo(contextKind, args); + ContextInfo info = createContextInfo(contextKind); threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL)); } for (int i = 0; i < pc; i++) { @@ -211,8 +208,57 @@ public class FastRContext { } - private static ContextInfo createContextInfo(RContext.ContextKind contextKind, RAbstractStringVector args) { - RStartParams startParams = new RStartParams(RCmdOptions.parseArguments(Client.RSCRIPT, args.materialize().getDataCopy(), false), false); + @RBuiltin(name = ".fastr.context.r", kind = PRIMITIVE, visibility = OFF, parameterNames = {"args", "intern"}, behavior = COMPLEX) + public abstract static class R extends CastHelper { + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{RMissing.instance, RRuntime.LOGICAL_FALSE}; + } + + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("args").mustBe(missingValue().or(stringValue())); + casts.arg("intern").asLogicalVector().findFirst().map(toBoolean()); + } + + @Specialization + @TruffleBoundary + protected Object r(RAbstractStringVector args, boolean intern) { + Object rc = RContext.getRRuntimeASTAccess().rcommandMain(args.materialize().getDataCopy(), intern); + return rc; + } + + @Specialization + protected Object r(@SuppressWarnings("unused") RMissing arg, boolean intern) { + return r(RDataFactory.createEmptyStringVector(), intern); + } + + } + + @RBuiltin(name = ".fastr.context.rscript", kind = PRIMITIVE, visibility = OFF, parameterNames = {"args", "intern"}, behavior = COMPLEX) + public abstract static class Rscript extends CastHelper { + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{RMissing.instance, RRuntime.LOGICAL_FALSE}; + } + + @Override + protected void createCasts(CastBuilder casts) { + casts.arg("args").mustBe(stringValue(), RError.Message.GENERIC, "usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args]").asStringVector(); + casts.arg("intern").asLogicalVector().findFirst().map(toBoolean()); + } + + @Specialization + @TruffleBoundary + protected Object r(RAbstractStringVector args, boolean intern) { + Object rc = RContext.getRRuntimeASTAccess().rscriptMain(args.materialize().getDataCopy(), intern); + return rc; + } + + } + + private static ContextInfo createContextInfo(RContext.ContextKind contextKind) { + RStartParams startParams = new RStartParams(RCmdOptions.parseArguments(Client.RSCRIPT, EMPTY, false), false); ContextInfo info = ContextInfo.create(startParams, contextKind, RContext.getInstance(), RContext.getInstance().getConsoleHandler()); return info; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java index e65017eed639b37248305c574090978ab300f228..657de9e74701338bfaad2c4e7fccf1be75fddc62 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java @@ -368,7 +368,7 @@ public final class RCmdOptions { public static void printHelpAndExit(Client client) { printHelp(client); - System.exit(0); + throw new ExitException(0); } private static void printVersionAndExit() { 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 a2527d8e6e5a10fc63638a4654c25af041d201b2..19fdca79bccaab9ff7feb64cf32cd921e99aafab 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 @@ -688,6 +688,9 @@ public final class RError extends RuntimeException { ARGUMENTS_REQUIRED_COUNT("%d arguments to '%s' which requires %d"), ARGUMENT_LENGTH_0("argument of length 0"), MUST_BE_VECTOR_BUT_WAS("'%s' must be of a vector type, was '%s'"), + SYSTEM_CHAR_ARG("non-empty character argument expected"), + SYSTEM_INTERN_NOT_NA("'intern' must be logical and not NA"), + NO_SUCH_FILE("cannot open file '%s': No such file or directory"), CANNOT_BE_LENGTH("'%s' cannot be of length %d"); public final String message; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java index 2e76ac5fd5339dc612c4b842641b8277b85af9b2..7b5e9d7f20ae0a45a6189d93fc9c4de86c1f3adb 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java @@ -191,4 +191,16 @@ public interface RRuntimeASTAccess { boolean isDebugged(RFunction func); + /* + * Support for R/RScript sessions ("processes") in an isolated RContext, see + * .fastr.context.r/rscript. The args are everything you might legally enter into a + * shell,including I/O redirection. The result is an integer status code if "intern==false", + * otherwise it is a character vector of the output, with a 'status' attribute containing the + * status code. + */ + + Object rcommandMain(String[] args, boolean intern); + + Object rscriptMain(String[] args, boolean intern); + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java index 35ed26d1d5d52539a19cbc667e585d10b4a8b45a..afd902095d2407dfb0b78ade92fe34b125d058e0 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java @@ -304,6 +304,14 @@ public final class Utils { return tildeExpand(path, false); } + public static String unShQuote(String s) { + if (s.charAt(0) == '\'') { + return s.substring(1, s.length() - 1); + } else { + return s; + } + } + /** * Retrieve a frame from the call stack. N.B. To avoid the iterator overhead use * {@link #getActualCurrentFrame()} for the current frame.