From 223fb6f4323dfa8817fd03591dd72297bf022ec5 Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Tue, 16 Jan 2018 20:17:26 +0100 Subject: [PATCH] Revive the GNU R compatible embedding API with NFI backend --- .../r/engine/TruffleRLanguageImpl.java | 6 +- .../engine/shell/EmbeddedConsoleHandler.java | 275 +++++--- .../truffle/r/engine/shell/REmbedded.java | 61 +- .../r/ffi/impl/llvm/TruffleLLVM_Context.java | 2 +- .../r/ffi/impl/llvm/TruffleLLVM_REmbed.java | 25 +- .../ffi/impl/managed/Managed_REmbedRFFI.java | 19 +- .../r/ffi/impl/nfi/TruffleNFI_Context.java | 2 +- .../r/ffi/impl/nfi/TruffleNFI_REmbed.java | 75 +- .../truffle/r/ffi/processor/FFIProcessor.java | 4 +- .../r/launcher/DelegatingConsoleHandler.java | 34 + .../oracle/truffle/r/launcher/RCommand.java | 21 +- .../truffle/r/launcher/RStartParams.java | 44 +- .../truffle/r/launcher/RscriptCommand.java | 4 +- com.oracle.truffle.r.native/fficall/Makefile | 6 +- .../fficall/src/common/rffi_upcalls.h | 13 +- .../fficall/src/truffle_common/Rembedded.c | 660 ----------------- .../fficall/src/truffle_nfi/Makefile | 5 +- .../fficall/src/truffle_nfi/Rembedded.c | 662 +++++++++++++++++- .../r/nodes/builtin/RBuiltinPackages.java | 8 +- .../truffle/r/nodes/builtin/base/Quit.java | 4 +- .../base/system/SystemFunctionFactory.java | 5 +- .../nodes/builtin/helpers/TraceHandling.java | 4 +- .../r/runtime/LazyResourceHandlerFactory.java | 3 +- .../oracle/truffle/r/runtime/RCleanUp.java | 52 +- .../oracle/truffle/r/runtime/REnvVars.java | 2 +- .../com/oracle/truffle/r/runtime/RError.java | 2 +- .../r/runtime/RInterfaceCallbacks.java | 7 +- .../truffle/r/runtime/RInternalError.java | 4 +- .../oracle/truffle/r/runtime/RPlatform.java | 4 +- .../oracle/truffle/r/runtime/RProfile.java | 4 +- .../oracle/truffle/r/runtime/RSuicide.java | 109 +++ .../truffle/r/runtime/TempPathName.java | 4 +- .../com/oracle/truffle/r/runtime/Utils.java | 34 +- .../r/runtime/conn/StdConnections.java | 4 +- .../truffle/r/runtime/env/REnvironment.java | 4 +- .../com/oracle/truffle/r/runtime/ffi/DLL.java | 26 +- .../truffle/r/runtime/ffi/NativeFunction.java | 6 + .../truffle/r/runtime/ffi/REmbedRFFI.java | 36 +- .../truffle/r/runtime/ffi/RFFIFactory.java | 8 +- .../embedded/src/main.c | 11 +- .../com/oracle/truffle/r/test/TestBase.java | 4 +- mx.fastr/copyrights/overrides | 2 +- 42 files changed, 1297 insertions(+), 968 deletions(-) create mode 100644 com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java delete mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java index 57e5be131a..e47345b704 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, 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 @@ -44,7 +44,7 @@ import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RAccuracyInfo; import com.oracle.truffle.r.runtime.RDeparse; import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException; import com.oracle.truffle.r.runtime.context.Engine.ParseException; import com.oracle.truffle.r.runtime.context.RContext; @@ -83,7 +83,7 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage { * Utils.rSuicide does. For now we catch it and exit the process. */ try { - Utils.rSuicide("error during R language initialization"); + RSuicide.rSuicide("error during R language initialization"); } catch (ExitException ex) { System.exit(ex.getStatus()); } 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 5b9c747e20..d0249e7a18 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -22,13 +22,25 @@ */ package com.oracle.truffle.r.engine.shell; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; + +import org.graalvm.polyglot.Context; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.r.launcher.ConsoleHandler; -import com.oracle.truffle.r.launcher.DefaultConsoleHandler; -import com.oracle.truffle.r.launcher.JLineConsoleHandler; -import com.oracle.truffle.r.launcher.RStartParams; +import com.oracle.truffle.r.launcher.DelegatingConsoleHandler; import com.oracle.truffle.r.runtime.RInterfaceCallbacks; -import com.oracle.truffle.r.runtime.ffi.REmbedRFFI; +import com.oracle.truffle.r.runtime.ffi.REmbedRFFI.ReadConsoleNode; +import com.oracle.truffle.r.runtime.ffi.REmbedRFFI.WriteConsoleBaseNode; import com.oracle.truffle.r.runtime.ffi.RFFIFactory; /** @@ -38,99 +50,192 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory; * N.B. At the time the constructor is created, we do not know if the console is overridden so we * have be lazy about that. * + * Since we do not have access to FastR internals in the launcher project. We're using internal + * FastR builtins to implement the functionality. + * */ -public class EmbeddedConsoleHandler extends ConsoleHandler { - - private final RStartParams startParams; - /** - * Only not {@code null} when console is not overridden. - */ +public final class EmbeddedConsoleHandler extends DelegatingConsoleHandler { + private Context context; + private Supplier<ConsoleHandler> delegateFactory; private ConsoleHandler delegate; - private REmbedRFFI rEmbedRFFI; - private String prompt; - EmbeddedConsoleHandler(RStartParams startParams) { - this.startParams = startParams; + private CallTarget readLineCallTarget; + private CallTarget writeCallTarget; + private CallTarget writeErrCallTarget; + + @Override + public void setContext(Context context) { + this.context = context; + } + + @Override + public void setDelegate(Supplier<ConsoleHandler> delegateFactory) { + this.delegateFactory = delegateFactory; + } + + @SuppressWarnings("try") + @Override + public String readLine() { + try (ContextClose ignored = inContext()) { + return isOverridden("R_ReadConsole") ? (String) getReadLineCallTarget().call("TODO prompt>") : getDelegate().readLine(); + } } - @TruffleBoundary - private REmbedRFFI getREmbedRFFI() { - if (rEmbedRFFI == null) { - rEmbedRFFI = RFFIFactory.getREmbedRFFI(); - if (!(RInterfaceCallbacks.R_WriteConsole.isOverridden() || RInterfaceCallbacks.R_ReadConsole.isOverridden())) { - if (startParams.noReadline()) { - delegate = new DefaultConsoleHandler(System.in, System.out, false); + @SuppressWarnings("try") + @Override + public void setPrompt(String prompt) { + try (ContextClose ignored = inContext()) { + if (!isOverridden("R_ReadConsole")) { + getDelegate().setPrompt(prompt); + } else { + // TODO: set prompt + } + } + } + + @Override + public InputStream createInputStream() { + return new InputStream() { + private String currentLine; + private int currentLineIdx; + private InputStream delegateInputStream; + + @SuppressWarnings("try") + @Override + public int read() throws IOException { + try (ContextClose ignored = inContext()) { + return readImpl(); + } + } + + private int readImpl() throws IOException { + if (!isOverridden("R_ReadConsole")) { + if (delegateInputStream == null) { + delegateInputStream = getDelegate().createInputStream(); + } + return delegateInputStream.read(); + } + if (currentLine == null || currentLineIdx >= currentLine.length()) { + currentLine = readLine(); + currentLineIdx = 0; + if (currentLine == null) { + return 0; + } + } + return currentLine.charAt(currentLineIdx++); + } + }; + } + + public OutputStream createStdOutputStream(OutputStream defaultValue) { + return createOutputSteam(this::getWriteCallTarget, defaultValue); + } + + public OutputStream createErrOutputStream(OutputStream defaultValue) { + return createOutputSteam(this::getWriteErrCallTarget, defaultValue); + } + + private OutputStream createOutputSteam(Supplier<CallTarget> writeCallTarget, OutputStream defaultStream) { + return new BufferedOutputStream(new EmbeddedConsoleOutputSteam(writeCallTarget, defaultStream), 128); + } + + private boolean isOverridden(String name) { + RInterfaceCallbacks clbk = RInterfaceCallbacks.valueOf(name); + return clbk.isOverridden(); + } + + private final class EmbeddedConsoleOutputSteam extends OutputStream { + private final OutputStream delegate; + private Supplier<CallTarget> writeCallTarget; + + EmbeddedConsoleOutputSteam(Supplier<CallTarget> writeCallTarget, OutputStream delegate) { + this.writeCallTarget = writeCallTarget; + this.delegate = delegate; + } + + @SuppressWarnings("try") + @Override + public void write(byte[] b, int off, int len) throws IOException { + // Note: R_WriteConsole callback is (seemingly...) used for both stdout and stderr + try (ContextClose ignored = inContext()) { + if (!isOverridden("R_WriteConsole")) { + delegate.write(b, off, len); } else { - delegate = new JLineConsoleHandler(System.in, System.out, startParams.isSlave()); + String str = StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(b, off, len)).toString(); + writeCallTarget.get().call(str); } } } - return rEmbedRFFI; - } - - // @Override - // @TruffleBoundary - // public void println(String s) { - // getREmbedRFFI(); - // if (delegate == null) { - // getREmbedRFFI().writeConsole(s); - // getREmbedRFFI().writeConsole("\n"); - // } else { - // delegate.println(s); - // } - // } - // - // @Override - // @TruffleBoundary - // public void print(String s) { - // getREmbedRFFI(); - // if (delegate == null) { - // rEmbedRFFI.writeConsole(s); - // } else { - // delegate.print(s); - // } - // } - // - // @Override - // @TruffleBoundary - // public void printErrorln(String s) { - // getREmbedRFFI(); - // if (delegate == null) { - // rEmbedRFFI.writeErrConsole(s); - // rEmbedRFFI.writeErrConsole("\n"); - // } else { - // delegate.printErrorln(s); - // } - // } - // - // @Override - // @TruffleBoundary - // public void printError(String s) { - // getREmbedRFFI(); - // if (delegate == null) { - // rEmbedRFFI.writeErrConsole(s); - // } else { - // delegate.printError(s); - // } - // } - @Override - @TruffleBoundary - public String readLine() { - getREmbedRFFI(); + @Override + public void write(int b) throws IOException { + write(new byte[]{(byte) b}, 0, 1); + } + } + + private ConsoleHandler getDelegate() { if (delegate == null) { - return rEmbedRFFI.readConsole(prompt); - } else { - return delegate.readLine(); + delegate = delegateFactory.get(); } + return delegate; } - @Override - @TruffleBoundary - public void setPrompt(String prompt) { - this.prompt = prompt; - if (delegate != null) { - delegate.setPrompt(prompt); + private ContextClose inContext() { + context.enter(); + return new ContextClose(); + } + + private final class ContextClose implements AutoCloseable { + @Override + public void close() { + context.leave(); + } + } + + private CallTarget getReadLineCallTarget() { + if (readLineCallTarget == null) { + readLineCallTarget = Truffle.getRuntime().createCallTarget(new RootNode(null) { + @Child private ReadConsoleNode readConsoleNode = RFFIFactory.getREmbedRFFI().createReadConsoleNode(); + + @Override + public Object execute(VirtualFrame frame) { + return readConsoleNode.execute((String) frame.getArguments()[0]); + } + }); + } + return readLineCallTarget; + } + + private CallTarget getWriteCallTarget() { + if (writeCallTarget == null) { + writeCallTarget = createWriteCallTarget(RFFIFactory.getREmbedRFFI().createWriteConsoleNode()); + } + return writeCallTarget; + } + + private CallTarget getWriteErrCallTarget() { + if (writeErrCallTarget == null) { + writeErrCallTarget = createWriteCallTarget(RFFIFactory.getREmbedRFFI().createWriteErrConsoleNode()); + } + return writeErrCallTarget; + } + + private CallTarget createWriteCallTarget(WriteConsoleBaseNode writeConsoleNode) { + return Truffle.getRuntime().createCallTarget(new WriteOutBaseRootNode(writeConsoleNode)); + } + + private static final class WriteOutBaseRootNode extends RootNode { + @Child private WriteConsoleBaseNode writeNode; + + WriteOutBaseRootNode(WriteConsoleBaseNode writeNode) { + super(null); + this.writeNode = writeNode; + } + + @Override + public Object execute(VirtualFrame frame) { + writeNode.execute((String) frame.getArguments()[0]); + return null; } } } 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 7dee1b6ac5..afb26eabdb 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -22,19 +22,20 @@ */ package com.oracle.truffle.r.engine.shell; +import java.io.InputStream; +import java.io.OutputStream; + import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Engine; import org.graalvm.polyglot.Source; -import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.r.launcher.ConsoleHandler; import com.oracle.truffle.r.launcher.RCmdOptions; import com.oracle.truffle.r.launcher.RCommand; import com.oracle.truffle.r.launcher.RStartParams; -import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RSource.Internal; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.Utils; -import com.oracle.truffle.r.runtime.context.ChildContextInfo; import com.oracle.truffle.r.runtime.context.RContext; /** @@ -53,13 +54,10 @@ import com.oracle.truffle.r.runtime.context.RContext; * Rf_mainloop(); * </pre> * - * {@code Rf_initialize_R} invokes {@link #initializeR(String[])}. This creates an - * {@link RStartParams} object in {@code embedded} mode that is recorded in the - * {@link ChildContextInfo} object which is itself stored as a global symbol in the associated - * {@link PolyglotEngine} instance. The FastR {@link PolyglotEngine} is then partially initialized. - * The call to {@code R_SetParams} will adjust the values stored in the {@link RStartParams} object - * and then {@code Rf_mainloop}, which calls {@link #setupRmainloop()} and then - * {@link #runRmainloop()}, which will complete the FastR initialization and enter the + * {@code Rf_initialize_R} invokes {@link #initializeR(String[])}, which creates new polyglot + * {@link Context}. The call to {@code R_SetParams} adjusts the values stored in the + * {@link RStartParams} object and then {@code Rf_mainloop}, which calls {@link #setupRmainloop()} + * and then {@link #runRmainloop()}, which will complete the FastR initialization and enter the * read-eval-print loop. */ public class REmbedded { @@ -78,12 +76,31 @@ public class REmbedded { RContext.setEmbedded(); RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false); - consoleHandler = RCommand.createConsoleHandler(options, true, System.in, System.out); - try (Context cntx = Context.newBuilder().allowHostAccess(true).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(System.out).err(System.err).build()) { - context = cntx; - consoleHandler.setContext(context); - context.eval(INIT); - } + EmbeddedConsoleHandler embeddedConsoleHandler = new EmbeddedConsoleHandler(); + + consoleHandler = RCommand.createConsoleHandler(options, embeddedConsoleHandler, System.in, System.out); + InputStream input = consoleHandler.createInputStream(); + boolean useEmbedded = consoleHandler == embeddedConsoleHandler; + OutputStream stdOut = useEmbedded ? embeddedConsoleHandler.createStdOutputStream(System.out) : System.out; + OutputStream stdErr = useEmbedded ? embeddedConsoleHandler.createErrOutputStream(System.err) : System.err; + context = Context.newBuilder().allowHostAccess(true).arguments("R", options.getArguments()).in(input).out(stdOut).err(stdErr).build(); + consoleHandler.setContext(context); + context.eval(INIT); + } + + /** + * Adjusts the values stored in {@link RStartParams}. Invoked from the native embedding code, + * i.e. not from a down-call, so the callbacks native array is not set-up properly. Moreover, + * this call is made during R initialization, so it not entirely clear if the FFI implementation + * has been fully initialized yet. + */ + @SuppressWarnings("unused") + private static void setParams(boolean quietA, boolean slaveA, boolean interactiveA, boolean verboseA, boolean loadSiteFileA, + boolean loadInitFileA, boolean debugInitFileA, int restoreActionA, int saveActionA, boolean noRenvironA) { + context.enter(); + RStartParams params = RContext.getInstance().getStartParams(); + params.setParams(quietA, slaveA, interactiveA, verboseA, loadSiteFileA, loadInitFileA, debugInitFileA, restoreActionA, saveActionA, noRenvironA); + context.leave(); } /** @@ -106,11 +123,10 @@ public class REmbedded { * native code after {@link #initializeR} returned. */ private static void runRmainloop() { + context.enter(); RContext.getInstance().completeEmbeddedInitialization(); - if (!RContext.getInstance().getStartParams().isQuiet()) { - System.out.println(RRuntime.WELCOME_MESSAGE); - } int status = RCommand.readEvalPrint(context, consoleHandler); + context.leave(); context.close(); Utils.systemExit(status); } @@ -127,13 +143,14 @@ public class REmbedded { // Checkstyle: stop method name check /** - * Upcalled from embedded mode to (really) commit suicide. This provides the default + * Upcalled from embedded mode via JNI to (really) commit suicide. This provides the default * implementation of the {@code R_Suicide} function in the {@code Rinterface} API. If an * embeddee overrides it, it typically will save this value and invoke it after its own * customization. */ @SuppressWarnings("unused") private static void R_Suicide(String msg) { - Utils.rSuicideDefault(msg); + RSuicide.rSuicideDefault(msg); } + } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java index c7ddced84a..c9365ee397 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java @@ -57,7 +57,7 @@ final class TruffleLLVM_Context extends RFFIContext { public ContextState initialize(RContext context) { if (context.isInitial()) { String librffiPath = LibPaths.getBuiltinLibPath("R"); - DLL.loadLibR(librffiPath); + DLL.loadLibR(context, librffiPath); } dllState.initialize(context); callState.initialize(context); diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java index d61d63cea4..7fa4235085 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -26,29 +26,18 @@ import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.ffi.REmbedRFFI; public class TruffleLLVM_REmbed implements REmbedRFFI { - - @Override - public void suicide(String x) { - throw RInternalError.unimplemented(); - } - - @Override - public void cleanUp(int type, int x, int y) { - throw RInternalError.unimplemented(); - } - @Override - public String readConsole(String prompt) { - throw RInternalError.unimplemented(); + public ReadConsoleNode createReadConsoleNode() { + throw RInternalError.unimplemented("TODO"); } @Override - public void writeConsole(String x) { - throw RInternalError.unimplemented(); + public WriteConsoleNode createWriteConsoleNode() { + throw RInternalError.unimplemented("TODO"); } @Override - public void writeErrConsole(String x) { - throw RInternalError.unimplemented(); + public WriteErrConsoleNode createWriteErrConsoleNode() { + throw RInternalError.unimplemented("TODO"); } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java index 17295f062a..7a99eaba80 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -24,31 +24,22 @@ package com.oracle.truffle.r.ffi.impl.managed; import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported; +import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.ffi.REmbedRFFI; public class Managed_REmbedRFFI implements REmbedRFFI { @Override - public void suicide(String x) { + public ReadConsoleNode createReadConsoleNode() { throw unsupported("REmbed"); } @Override - public void cleanUp(int type, int x, int y) { + public WriteConsoleNode createWriteConsoleNode() { throw unsupported("REmbed"); } @Override - public String readConsole(String prompt) { - throw unsupported("REmbed"); - } - - @Override - public void writeConsole(String x) { - throw unsupported("REmbed"); - } - - @Override - public void writeErrConsole(String x) { + public WriteErrConsoleNode createWriteErrConsoleNode() { throw unsupported("REmbed"); } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java index bf3dd59779..affec6643a 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java @@ -282,7 +282,7 @@ final class TruffleNFI_Context extends RFFIContext { try { String librffiPath = LibPaths.getBuiltinLibPath("R"); if (context.isInitial()) { - DLL.loadLibR(librffiPath); + DLL.loadLibR(context, librffiPath); } else { // force initialization of NFI DLLRFFI.DLOpenRootNode.create(context).call(librffiPath, false, false); diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java index fec06c845d..64c70971c2 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -22,33 +22,82 @@ */ package com.oracle.truffle.r.ffi.impl.nfi; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.ffi.NativeFunction; import com.oracle.truffle.r.runtime.ffi.REmbedRFFI; public class TruffleNFI_REmbed implements REmbedRFFI { - @Override - public void suicide(String x) { - throw RInternalError.unimplemented(); + private static class TruffleNFI_ReadConsoleNode extends TruffleNFI_DownCallNode implements ReadConsoleNode { + @Child private Node unboxNode; + + @Override + protected NativeFunction getFunction() { + return NativeFunction.rembedded_read_console; + } + + @Override + public String execute(String prompt) { + Object result = call(prompt); + if (result instanceof String) { + return (String) result; + } + assert result instanceof TruffleObject : "NFI is expected to send us TruffleObject or String"; + if (unboxNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + unboxNode = insert(Message.UNBOX.createNode()); + } + try { + return (String) ForeignAccess.sendUnbox(unboxNode, (TruffleObject) result); + } catch (ClassCastException | UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreter(); + throw RInternalError.shouldNotReachHere("Unboxing TruffleObject from NFI, which should be String wrapper, failed. " + e.getMessage()); + } + } } - @Override - public void cleanUp(int type, int x, int y) { - throw RInternalError.unimplemented(); + private static class TruffleNFI_WriteConsoleNode extends TruffleNFI_DownCallNode implements WriteConsoleNode { + @Override + protected NativeFunction getFunction() { + return NativeFunction.rembedded_write_console; + } + + @Override + public void execute(String x) { + call(x, x.length()); + } + } + + private static class TruffleNFI_WriteErrConsoleNode extends TruffleNFI_DownCallNode implements WriteErrConsoleNode { + @Override + protected NativeFunction getFunction() { + return NativeFunction.rembedded_write_err_console; + } + + @Override + public void execute(String x) { + call(x, x.length()); + } } @Override - public String readConsole(String prompt) { - throw RInternalError.unimplemented(); + public ReadConsoleNode createReadConsoleNode() { + return new TruffleNFI_ReadConsoleNode(); } @Override - public void writeConsole(String x) { - throw RInternalError.unimplemented(); + public WriteConsoleNode createWriteConsoleNode() { + return new TruffleNFI_WriteConsoleNode(); } @Override - public void writeErrConsole(String x) { - throw RInternalError.unimplemented(); + public WriteErrConsoleNode createWriteErrConsoleNode() { + return new TruffleNFI_WriteErrConsoleNode(); } } diff --git a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java index a50310fc8e..9934ccd6fa 100644 --- a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java +++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -143,7 +143,7 @@ public final class FFIProcessor extends AbstractProcessor { FileObject fileObj = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "com.oracle.truffle.r.ffi.impl.upcalls", "rffi_upcallsindex.h"); note("If you edited any UpCallsRFFI interfaces do: cp " + fileObj.toUri().getPath() + " com.oracle.truffle.r.native/fficall/src/common\n"); Writer w = fileObj.openWriter(); - w.append("// GENERATED; DO NOT EDIT\n"); + w.append("// GENERATED by com.oracle.truffle.r.ffi.processor.FFIProcessor class; DO NOT EDIT\n"); w.append("#ifndef RFFI_UPCALLSINDEX_H\n"); w.append("#define RFFI_UPCALLSINDEX_H\n"); w.append('\n'); diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java new file mode 100644 index 0000000000..3bd3c753ad --- /dev/null +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, 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.launcher; + +import java.util.function.Supplier; + +/** + * Represents a console handler that may delegate its operations to the given {@link ConsoleHandler} + * . This is used for R embedding where the user may provide custom overrides of some console + * functions. + */ +public abstract class DelegatingConsoleHandler extends ConsoleHandler { + public abstract void setDelegate(Supplier<ConsoleHandler> handler); +} diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java index 7519a55c5b..136388102c 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -35,6 +35,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import org.graalvm.options.OptionCategory; import org.graalvm.polyglot.Context; @@ -94,7 +95,7 @@ public class RCommand { public static RuntimeException fatal(String message, Object... args) { System.out.println("FATAL: " + String.format(message, args)); System.exit(-1); - return null; + return new RuntimeException(); } public static RuntimeException fatal(Throwable t, String message, Object... args) { @@ -163,7 +164,7 @@ public class RCommand { } RCmdOptions options = RCmdOptions.parseArguments(Client.R, argsList.toArray(new String[argsList.size()]), false); assert env == null || env.length == 0 : "re-enable setting environments"; - ConsoleHandler consoleHandler = createConsoleHandler(options, false, inStream, outStream); + ConsoleHandler consoleHandler = createConsoleHandler(options, null, inStream, outStream); try (Context context = Context.newBuilder().allowHostAccess(useJVM).options(polyglotOptions).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err( errStream).build()) { consoleHandler.setContext(context); @@ -173,7 +174,7 @@ public class RCommand { } } - public static ConsoleHandler createConsoleHandler(RCmdOptions options, boolean embedded, InputStream inStream, OutputStream outStream) { + public static ConsoleHandler createConsoleHandler(RCmdOptions options, DelegatingConsoleHandler useDelegatingWrapper, InputStream inStream, OutputStream outStream) { /* * Whether the input is from stdin, a file (-f), or an expression on the command line (-e) * it goes through the console. N.B. -f and -e can't be used together and this is already @@ -203,18 +204,20 @@ public class RCommand { } else { boolean isInteractive = options.getBoolean(RCmdOption.INTERACTIVE); if (!isInteractive && rsp.askForSave()) { - fatal("you must specify '--save', '--no-save' or '--vanilla'"); + throw fatal("you must specify '--save', '--no-save' or '--vanilla'"); } - if (embedded) { + boolean useReadLine = isInteractive && !rsp.noReadline(); + if (useDelegatingWrapper != null) { /* * If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler * should be lazy, as these may not be necessary and can cause hangs if stdin has * been redirected. */ - throw fatal("embedded mode disabled"); - // consoleHandler = new EmbeddedConsoleHandler(rsp, engine); + Supplier<ConsoleHandler> delegateFactory = useReadLine ? () -> new JLineConsoleHandler(inStream, outStream, rsp.isSlave()) + : () -> new DefaultConsoleHandler(inStream, outStream, isInteractive); + useDelegatingWrapper.setDelegate(delegateFactory); + return useDelegatingWrapper; } else { - boolean useReadLine = isInteractive && !rsp.noReadline(); if (useReadLine) { return new JLineConsoleHandler(inStream, outStream, rsp.isSlave()); } else { diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java index 82de6f3fab..4548d0e625 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -47,23 +47,23 @@ import java.util.List; */ public class RStartParams { - private final boolean quiet; - private final boolean slave; + private boolean quiet; + private boolean slave; /** * The setting of this value in GNU R is unusual and not simply based on the value of the * --interactive option, so we do not check the option in * {@link #RStartParams(RCmdOptions, boolean)}, but later in {@code RCommand}. */ - private final boolean interactive; - private final boolean verbose; - private final boolean loadSiteFile; - private final boolean loadInitFile; - private final boolean debugInitFile; + private boolean interactive; + private boolean verbose; + private boolean loadSiteFile; + private boolean loadInitFile; + private boolean debugInitFile; private final boolean restoreAction; private final boolean askForSave; private final boolean save; - private final boolean noRenviron; + private boolean noRenviron; /** * This is not a configurable option, but it is set on the command line and needs to be stored @@ -130,23 +130,19 @@ public class RStartParams { } /** - * Only upcalled from native code. + * Used for R embedding, allows to alter some of the values. */ - @SuppressWarnings("unused") - public static void setParams(boolean quietA, boolean slaveA, boolean interactiveA, boolean verboseA, boolean loadSiteFileA, + public void setParams(boolean quietA, boolean slaveA, boolean interactiveA, boolean verboseA, boolean loadSiteFileA, boolean loadInitFileA, boolean debugInitFileA, int restoreActionA, int saveActionA, boolean noRenvironA) { - assert false : "re-enable setParams"; - // RStartParams params = RContext.getInstance().getStartParams(); - // params.setQuiet(quietA); - // params.setSlave(slaveA); - // params.setInteractive(interactiveA); - // params.setVerbose(verboseA); - // params.setLoadSiteFile(loadSiteFileA); - // params.setLoadInitFile(loadInitFileA); - // params.setDebugInitFile(debugInitFileA); - // params.setSaveAction(SA_TYPE.values()[saveActionA]); - // params.setRestoreAction(SA_TYPE.values()[restoreActionA]); - // params.setNoRenviron(noRenvironA); + quiet = quietA; + slave = slaveA; + interactive = interactiveA; + verbose = verboseA; + loadSiteFile = loadSiteFileA; + loadInitFile = loadInitFileA; + debugInitFile = debugInitFileA; + // TODO: save and restore actions? + noRenviron = noRenvironA; } public boolean isQuiet() { diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java index 9fffb1b9ef..e4ca8fa47d 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -136,7 +136,7 @@ public class RscriptCommand { RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, argsList.toArray(new String[argsList.size()]), false); String[] arguments = preprocessRScriptOptions(launcher, options); - ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, false, inStream, outStream); + ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, null, inStream, outStream); try (Context context = Context.newBuilder().allowHostAccess(useJVM).options(polyglotOptions).arguments("R", arguments).in(consoleHandler.createInputStream()).out(outStream).err( errStream).build()) { consoleHandler.setContext(context); diff --git a/com.oracle.truffle.r.native/fficall/Makefile b/com.oracle.truffle.r.native/fficall/Makefile index 278d222f0c..f6176fd2d0 100644 --- a/com.oracle.truffle.r.native/fficall/Makefile +++ b/com.oracle.truffle.r.native/fficall/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2018, 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 @@ -56,7 +56,7 @@ ifeq ($(FASTR_RFFI),managed) # nop else ifeq ($(OS_NAME),Darwin) - $(DYLIB_LD) $(DYLIB_LDFLAGS) -Wl,-rpath,@loader_path/ -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -lRblas -lRlapack -lpcre -lz $(VERSION_FLAGS) + $(DYLIB_LD) $(DYLIB_LDFLAGS) -Wl,-rpath,@loader_path/ -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -ldl -lRblas -lRlapack -lpcre -lz $(VERSION_FLAGS) ifneq ($(FASTR_RFFI),llvm) install_name_tool -change libRblas.dylib @rpath/libRblas.dylib $(R_LIB) install_name_tool -change libRlapack.dylib @rpath/libRlapack.dylib $(R_LIB) @@ -65,7 +65,7 @@ ifneq ($(FASTR_RFFI),llvm) mx rupdatelib $(FASTR_LIB_DIR) endif else - $(DYLIB_LD) $(DYLIB_LDFLAGS) $(shell echo $(PKG_LDFLAGS_OVERRIDE)) -Wl,-rpath,'$$ORIGIN' -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -lRblas -lRlapack -lpcre -lz + $(DYLIB_LD) $(DYLIB_LDFLAGS) $(shell echo $(PKG_LDFLAGS_OVERRIDE)) -Wl,-rpath,'$$ORIGIN' -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -lRblas -lRlapack -ldl -lpcre -lz endif endif # managed diff --git a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h index e0d64df093..b0eca857a1 100644 --- a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h +++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -319,4 +319,15 @@ typedef void (*call_R_ReleaseObject)(SEXP x); typedef void* (*call_R_alloc)(int n, int size); +// IDEs and Tools +typedef void* (*call_R_getGlobalFunctionContext)(); +typedef void* (*call_R_getParentFunctionContext)(void* c); +typedef void* (*call_R_getContextEnv)(void* c); +typedef void* (*call_R_getContextFun)(void* c); +typedef void* (*call_R_getContextCall)(void* c); +typedef void* (*call_R_getContextSrcRef)(void* c); +typedef int (*call_R_insideBrowser)(); +typedef int (*call_R_isGlobal)(void* c); +typedef int (*call_R_isEqual)(void* x, void* y); + #endif diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c deleted file mode 100644 index 8b96980452..0000000000 --- a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * 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) 1995-2015, The R Core Team - * Copyright (c) 2003, The R Foundation - * Copyright (c) 2016, 2017, Oracle and/or its affiliates - * - * All rights reserved. - */ -#include <dlfcn.h> -#include <sys/utsname.h> -#include <sys/stat.h> -#include <rffiutils.h> -#define R_INTERFACE_PTRS -#include <R_ext/RStartup.h> -#include <Rinterface.h> - -extern char **environ; - -static JavaVM *javaVM; -static int initialized = 0; -static char *java_home; - -static jclass rembeddedClass; -static jclass rStartParamsClass; -static jclass rInterfaceCallbacksClass; - -int R_running_as_main_program; -int R_SignalHandlers; -FILE * R_Consolefile; -FILE * R_Outputfile; -int R_DirtyImage; // TODO update this -void *R_GlobalContext; // TODO what? -SA_TYPE SaveAction; // ?? - -typedef jint (JNICALL *JNI_CreateJavaVMFunc) - (JavaVM **pvm, void **penv, void *args); - - -static void *dlopen_jvmlib(char *libpath) { - void *handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW); - if (handle == NULL) { - fprintf(stderr, "Rf_initialize_R: cannot dlopen %s: %s\n", libpath, dlerror()); - exit(1); - } - return handle; -} - -// separate vm args from user args -static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) { - int vcount = 0; - int ucount = 0; - for (int i = 0; i < argc; i++) { - char *arg = argv[i]; - if ((arg[0] == '-' && arg[1] == 'X') || (arg[0] == '-' && arg[1] == 'D')) { - vmargv[vcount++] = arg; - } else { - uargv[ucount++] = arg; - } - } - return vcount; -} - -static char **update_environ_with_java_home(void); -static void print_environ(char **env); -static char *get_classpath(char *r_home); - -# define JMP_BUF sigjmp_buf - -int Rf_initialize_R(int argc, char *argv[]) { - if (initialized) { - fprintf(stderr, "%s", "R is already initialized\n"); - exit(1); - } - // print_environ(environ); - char *r_home = getenv("R_HOME"); - if (r_home == NULL) { - fprintf(stderr, "R_HOME must be set\n"); - exit(1); - } - struct utsname utsname; - uname(&utsname); - char jvmlib_path[256]; - java_home = getenv("JAVA_HOME"); - if (java_home == NULL) { - if (strcmp(utsname.sysname, "Linux") == 0) { - char *jvmdir = "/usr/java/latest"; - struct stat statbuf; - if (stat(jvmdir, &statbuf) == 0) { - java_home = jvmdir; - } - } else if (strcmp(utsname.sysname, "Darwin") == 0) { - char *jvmdir = "/Library/Java/JavaVirtualMachines/jdk.latest"; - struct stat statbuf; - if (stat(jvmdir, &statbuf) == 0) { - java_home = (char*)malloc(strlen(jvmdir) + 32); - strcpy(java_home, jvmdir); - strcat(java_home, "/Contents/Home"); - } - } - if (java_home == NULL) { - fprintf(stderr, "Rf_initialize_R: can't find a JAVA_HOME\n"); - exit(1); - } - } - strcpy(jvmlib_path, java_home); - if (strcmp(utsname.sysname, "Linux") == 0) { - strcat(jvmlib_path, "/jre/lib/amd64/server/libjvm.so"); - } else if (strcmp(utsname.sysname, "Darwin") == 0) { - strcat(jvmlib_path, "/jre/lib/server/libjvm.dylib"); - // Must also load libjli to avoid going through framework - // and failing to find our JAVA_HOME runtime - char jlilib_path[256]; - strcpy(jlilib_path, java_home); - strcat(jlilib_path, "/jre/lib/jli/libjli.dylib"); - dlopen_jvmlib(jlilib_path); - } else { - fprintf(stderr, "unsupported OS: %s\n", utsname.sysname); - exit(1); - } - void *vm_handle = dlopen_jvmlib(jvmlib_path); - JNI_CreateJavaVMFunc createJavaVMFunc = (JNI_CreateJavaVMFunc) dlsym(vm_handle, "JNI_CreateJavaVM"); - if (createJavaVMFunc == NULL) { - fprintf(stderr, "Rf_initialize_R: cannot find JNI_CreateJavaVM\n"); - exit(1); - } - - char *vm_cp = get_classpath(r_home); - //printf("cp %s\n", vm_cp); - - char **vmargs = malloc(argc * sizeof(char*)); - char **uargs = malloc(argc * sizeof(char*)); - int vmargc = process_vmargs(argc, argv, vmargs, uargs); - argc -= vmargc; - argv = uargs; - JavaVMOption vm_options[1 + vmargc]; - - vm_options[0].optionString = vm_cp; - for (int i = 0; i < vmargc; i++) { - vm_options[i + 1].optionString = vmargs[i]; - } - - JavaVMInitArgs vm_args; - vm_args.version = JNI_VERSION_1_8; - vm_args.nOptions = 1 + vmargc; - vm_args.options = vm_options; - vm_args.ignoreUnrecognized = JNI_TRUE; - - JNIEnv *jniEnv; - jint flag = (*createJavaVMFunc)(&javaVM, (void**) - &jniEnv, &vm_args); - if (flag == JNI_ERR) { - fprintf(stderr, "Rf_initEmbeddedR: error creating Java VM, exiting...\n"); - return 1; - } - - setEmbedded(); - setEnv(jniEnv); - rInterfaceCallbacksClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RInterfaceCallbacks"); - rembeddedClass = checkFindClass(jniEnv, "com/oracle/truffle/r/engine/shell/REmbedded"); - rStartParamsClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RStartParams"); - jclass stringClass = checkFindClass(jniEnv, "java/lang/String"); - jmethodID initializeMethod = checkGetMethodID(jniEnv, rembeddedClass, "initializeR", "([Ljava/lang/String;)V", 1); - jobjectArray argsArray = (*jniEnv)->NewObjectArray(jniEnv, argc, stringClass, NULL); - for (int i = 0; i < argc; i++) { - jstring arg = (*jniEnv)->NewStringUTF(jniEnv, argv[i]); - (*jniEnv)->SetObjectArrayElement(jniEnv, argsArray, i, arg); - } - // Can't TRACE this upcall as system not initialized - (*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray); - initialized++; - return 0; -} - -char *R_HomeDir(void) { - JNIEnv *jniEnv = getEnv(); - jmethodID R_HomeDirMethodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_HomeDir", "()Ljava/lang/String;", 0); - jstring homeDir = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, R_HomeDirMethodID); - const char *homeDirChars = stringToChars(jniEnv, homeDir); - return (char *)homeDirChars; -} - -void R_SaveGlobalEnvToFile(const char *f) { - unimplemented("R_SaveGlobalEnvToFile"); -} - -void R_Suicide(const char *s) { ptr_R_Suicide(s); } - -#undef R_Interactive - -void R_DefParams(Rstart rs) { - // These are the GnuR defaults and correspond to the settings in RStartParams - // None of the size params make any sense for FastR - rs->R_Quiet = FALSE; - rs->R_Slave = FALSE; - rs->R_Interactive = TRUE; - rs->R_Verbose = FALSE; - rs->RestoreAction = SA_RESTORE; - rs->SaveAction = SA_SAVEASK; - rs->LoadSiteFile = TRUE; - rs->LoadInitFile = TRUE; - rs->DebugInitFile = FALSE; -// rs->vsize = R_VSIZE; -// rs->nsize = R_NSIZE; -// rs->max_vsize = R_SIZE_T_MAX; -// rs->max_nsize = R_SIZE_T_MAX; -// rs->ppsize = R_PPSSIZE; - rs->NoRenviron = FALSE; -// R_SizeFromEnv(Rp); -} - -void R_SetParams(Rstart rs) { - JNIEnv *jniEnv = getEnv(); - jmethodID setParamsMethodID = checkGetMethodID(jniEnv, rStartParamsClass, "setParams", "(ZZZZZZZIIZ)V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rStartParamsClass, setParamsMethodID, rs->R_Quiet, rs->R_Slave, rs->R_Interactive, - rs->R_Verbose, rs->LoadSiteFile, rs->LoadInitFile, rs->DebugInitFile, - rs->RestoreAction, rs->SaveAction, rs->NoRenviron); -} - -void R_SizeFromEnv(Rstart rs) { - unimplemented("R_SizeFromEnv"); -} - -void R_common_command_line(int *a, char **b, Rstart rs) { - unimplemented("R_common_command_line"); -} - -void R_set_command_line_arguments(int argc, char **argv) { - unimplemented("R_set_command_line_arguments"); -} - - -int Rf_initEmbeddedR(int argc, char *argv[]) { - Rf_initialize_R(argc, argv); -// R_Interactive = TRUE; - setup_Rmainloop(); - return 1; -} - -void Rf_endEmbeddedR(int fatal) { - (*javaVM)->DestroyJavaVM(javaVM); - //TODO fatal -} - -static void setupOverrides(void); - -void setup_Rmainloop(void) { - JNIEnv *jniEnv = getEnv(); - jmethodID setupMethod = checkGetMethodID(jniEnv, rembeddedClass, "setupRmainloop", "()V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, setupMethod); -} - -void run_Rmainloop(void) { - JNIEnv *jniEnv = getEnv(); - setupOverrides(); - jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod); -} - -void Rf_mainloop(void) { - setup_Rmainloop(); - run_Rmainloop(); -} - -// functions that can be assigned by an embedded client to change behavior - -void uR_Suicide(const char *x) { - JNIEnv *jniEnv = getEnv(); - jstring msg = (*jniEnv)->NewStringUTF(jniEnv, x); - jmethodID suicideMethod = checkGetMethodID(jniEnv, rembeddedClass, "R_Suicide", "(Ljava/lang/String;)V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, suicideMethod, msg); -} - -void uR_ShowMessage(const char *x) { - unimplemented("R_ShowMessage"); -} - -int uR_ReadConsole(const char *a, unsigned char *b, int c, int d) { - return (int) unimplemented("R_ReadConsole"); -} - -void uR_WriteConsole(const char *x, int y) { - unimplemented("R_WriteConsole"); -} - -void uR_WriteConsoleEx(const char *x, int y, int z) { - unimplemented("R_WriteConsole"); -} - -void uR_ResetConsole(void) { - unimplemented("R_ResetConsole"); -} - -void uR_FlushConsole(void) { - unimplemented("R_FlushConsole"); -} - -void uR_ClearerrConsole(void) { - unimplemented("R_ClearerrConsole"); -} - -void uR_Busy(int x) { - unimplemented("R_Busy"); -} - -void uR_CleanUp(SA_TYPE x, int y, int z) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_CleanUp", "(III)I", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, UpCallsRFFIClass, methodID, x, y, z); -} - -int uR_ShowFiles(int a, const char **b, const char **c, - const char *d, Rboolean e, const char *f) { - return (int) unimplemented("R_ShowFiles"); -} - -int uR_ChooseFile(int a, char *b, int c) { - return (int) unimplemented("R_ChooseFile"); -} - -int uR_EditFile(const char *a) { - return (int) unimplemented("R_EditFile"); -} - -void uR_loadhistory(SEXP a, SEXP b, SEXP c, SEXP d) { - unimplemented("uR_loadhistory"); -} - -void uR_savehistory(SEXP a, SEXP b, SEXP c, SEXP d) { - unimplemented("R_savehistory"); -} - -void uR_addhistory(SEXP a, SEXP b, SEXP c, SEXP d) { - unimplemented("R_addhistory"); -} - -int uR_EditFiles(int a, const char **b, const char **c, const char *d) { - return (int)unimplemented(""); -} - -SEXP udo_selectlist(SEXP a, SEXP b, SEXP c, SEXP d) { - return unimplemented("R_EditFiles"); -} - -SEXP udo_dataentry(SEXP a, SEXP b, SEXP c, SEXP d) { - return unimplemented("do_dataentry"); -} - -SEXP udo_dataviewer(SEXP a, SEXP b, SEXP c, SEXP d) { - return unimplemented("do_dataviewer"); -} - -void uR_ProcessEvents(void) { - unimplemented("R_ProcessEvents"); -} - - -void (*ptr_R_Suicide)(const char *) = uR_Suicide; -void (*ptr_R_ShowMessage)(const char *) = uR_ShowMessage; -int (*ptr_R_ReadConsole)(const char *, unsigned char *, int, int) = uR_ReadConsole; -void (*ptr_R_WriteConsole)(const char *, int) = uR_WriteConsole; -void (*ptr_R_WriteConsoleEx)(const char *, int, int) = uR_WriteConsoleEx; -void (*ptr_R_ResetConsole)(void) = uR_ResetConsole; -void (*ptr_R_FlushConsole)(void) = uR_FlushConsole; -void (*ptr_R_ClearerrConsole)(void) = uR_ClearerrConsole; -void (*ptr_R_Busy)(int) = uR_Busy; -void (*ptr_R_CleanUp)(SA_TYPE, int, int) = uR_CleanUp; -int (*ptr_R_ShowFiles)(int, const char **, const char **, - const char *, Rboolean, const char *) = uR_ShowFiles; -int (*ptr_R_ChooseFile)(int, char *, int) = uR_ChooseFile; -int (*ptr_R_EditFile)(const char *) = uR_EditFile; -void (*ptr_R_loadhistory)(SEXP, SEXP, SEXP, SEXP) = uR_loadhistory; -void (*ptr_R_savehistory)(SEXP, SEXP, SEXP, SEXP) = uR_savehistory; -void (*ptr_R_addhistory)(SEXP, SEXP, SEXP, SEXP) = uR_addhistory; - -int (*ptr_R_EditFiles)(int, const char **, const char **, const char *) = uR_EditFiles; - -SEXP (*ptr_do_selectlist)(SEXP, SEXP, SEXP, SEXP) = udo_selectlist; -SEXP (*ptr_do_dataentry)(SEXP, SEXP, SEXP, SEXP) = udo_dataentry; -SEXP (*ptr_do_dataviewer)(SEXP, SEXP, SEXP, SEXP) = udo_dataviewer; -void (*ptr_R_ProcessEvents)() = uR_ProcessEvents; - -void setupOverrides(void) { - JNIEnv *jniEnv = getEnv(); - jmethodID ovrMethodID = checkGetMethodID(jniEnv, rInterfaceCallbacksClass, "override", "(Ljava/lang/String;)V", 1); - jstring name; - if (ptr_R_Suicide != uR_Suicide) { - name = (*jniEnv)->NewStringUTF(jniEnv, "R_Suicide"); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); - } - if (*ptr_R_CleanUp != uR_CleanUp) { - name = (*jniEnv)->NewStringUTF(jniEnv, "R_CleanUp"); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); - } - if (*ptr_R_ReadConsole != uR_ReadConsole) { - name = (*jniEnv)->NewStringUTF(jniEnv, "R_ReadConsole"); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); - } - if (*ptr_R_WriteConsole != uR_WriteConsole) { - name = (*jniEnv)->NewStringUTF(jniEnv, "R_WriteConsole"); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); - } -} - -static void REmbed_nativeWriteConsole(JNIEnv *jniEnv, jclass c, jstring string, int otype) { - jmp_buf error_jmpbuf; - callEnter(jniEnv, &error_jmpbuf); - if (!setjmp(error_jmpbuf)) { - int len = (*jniEnv)->GetStringUTFLength(jniEnv, string); - const char *cbuf = (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL); - if (ptr_R_WriteConsole == NULL) { - (*ptr_R_WriteConsoleEx)(cbuf, len, otype); - } else { - (*ptr_R_WriteConsole)(cbuf, len); - } - (*jniEnv)->ReleaseStringUTFChars(jniEnv, string, cbuf); - } - callExit(jniEnv); -} - -JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeWriteConsole(JNIEnv *jniEnv, jclass c, jstring string) { - REmbed_nativeWriteConsole(jniEnv, c, string, 0); -} - -JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeWriteErrConsole(JNIEnv *jniEnv, jclass c, jstring string) { - REmbed_nativeWriteConsole(jniEnv, c, string, 1); -} - -JNIEXPORT jstring JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeReadConsole(JNIEnv *jniEnv, jclass c, jstring prompt) { - jmp_buf error_jmpbuf; - jstring result = NULL; - callEnter(jniEnv, &error_jmpbuf); - if (!setjmp(error_jmpbuf)) { - const char *cprompt = (*jniEnv)->GetStringUTFChars(jniEnv, prompt, NULL); - unsigned char cbuf[1024]; - int n = (*ptr_R_ReadConsole)(cprompt, cbuf, 1024, 0); - result = (*jniEnv)->NewStringUTF(jniEnv, (const char *)cbuf); - (*jniEnv)->ReleaseStringUTFChars(jniEnv, prompt, cprompt); - } - callExit(jniEnv); - return result; -} - -JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeCleanUp(JNIEnv *jniEnv, jclass c, jint x, jint y, jint z) { - jmp_buf error_jmpbuf; - callEnter(jniEnv, &error_jmpbuf); - if (!setjmp(error_jmpbuf)) { - (*ptr_R_CleanUp)(x, y, z); - } - callExit(jniEnv); -} - -JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeSuicide(JNIEnv *jniEnv, jclass c, jstring string) { - jmp_buf error_jmpbuf; - callEnter(jniEnv, &error_jmpbuf); - if (!setjmp(error_jmpbuf)) { - const char *cbuf = (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL); - (*ptr_R_Suicide)(cbuf); - } - callExit(jniEnv); -} - -void uR_PolledEvents(void) { - unimplemented("R_PolledEvents"); -} - -void (* R_PolledEvents)(void) = uR_PolledEvents; - -void Rf_jump_to_toplevel() { - unimplemented("Rf_jump_to_toplevel"); -} - -#include <R_ext/eventloop.h> - -fd_set *R_checkActivity(int usec, int ignore_stdin) { - return (fd_set*) unimplemented("R_checkActivity"); -} - -void R_runHandlers(InputHandler *handlers, fd_set *mask) { - unimplemented("R_runHandlers"); -} - -int R_wait_usec; - -#include <unistd.h> -#include <errno.h> - -static void perror_exit(char *msg) { - perror(msg); - exit(1); -} - -// support for getting the correct classpath for the VM -// We use $R_HOME/bin/execRextras/Rclasspath to do this to emulate what happens -// during normal execution -static char *get_classpath(char *r_home) { - char **env = update_environ_with_java_home(); - //print_environ(env); - int pipefd[2]; - if (pipe(pipefd) == -1) { - perror_exit("pipe"); - } - pid_t pid = fork(); - if (pid == -1) { - perror("fork"); - } - if (pid == 0) { - // child - char path[1024]; - strcpy(path, r_home); - strcat(path, "/bin/execRextras/Rclasspath"); - while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} - close(pipefd[1]); - close(pipefd[0]); - int rc = execle(path, path, (char *)NULL, env); - if (rc == -1) { - perror_exit("exec"); - } - return NULL; - } else { - // parent - const char *cpdef = "-Djava.class.path="; - char *buf = malloc(4096); - strcpy(buf, cpdef); - char *bufptr = buf + strlen(cpdef); - int max = 4096 - strlen(cpdef); - close(pipefd[1]); - while (1) { - int count = read(pipefd[0], bufptr, max); - if (count == -1) { - if (errno == EINTR) { - continue; - } else { - perror_exit("read"); - } - } else if (count == 0) { - // scrub any newline - bufptr--; - if (*bufptr != '\n') { - bufptr++; - } - *bufptr = 0; - break; - } else { - bufptr += count; - max -= count; - } - } - close(pipefd[0]); - wait(NULL); - return buf; - } -} - -// debugging -static void print_environ(char **env) { - fprintf(stdout, "## Environment variables at %p\n", env); - char **e = env; - while (*e != NULL) { - fprintf(stdout, "%s\n", *e); - e++; - } -} - -static char **update_environ(char *def) { - int count = 0; - char **e = environ; - while (*e != NULL) { - e++; - count++; - } - char **new_env = malloc(sizeof(char *) * (count + 2)); - e = environ; - char **ne = new_env; - while (*e != NULL) { - *ne++ = *e++; - } - *ne++ = def; - *ne = (char*) NULL; - return new_env; -} - -static char **update_environ_with_java_home(void) { - char **e = environ; - while (*e != NULL) { - if (strstr(*e, "JAVA_HOME=")) { - return environ; - } - e++; - } - char *java_home_env = malloc(strlen(java_home) + 10); - strcpy(java_home_env, "JAVA_HOME="); - strcat(java_home_env, java_home); - return update_environ(java_home_env); -} - -CTXT R_getGlobalFunctionContext() { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getGlobalFunctionContext", "()Ljava/lang/Object;", 0); - CTXT result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID); - SEXP new_result = checkRef(jniEnv, result); - return new_result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0); -} - -CTXT R_getParentFunctionContext(CTXT c) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getParentFunctionContext", "(Ljava/lang/Object;)Ljava/lang/Object;", 0); - CTXT result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, c); - SEXP new_result = checkRef(jniEnv, result); - return new_result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0); -} - -SEXP R_getContextEnv(CTXT context) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextEnv", "(Ljava/lang/Object;)Ljava/lang/Object;", 0); - SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context); - return checkRef(jniEnv, result); -} - -SEXP R_getContextFun(CTXT context) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextFun", "(Ljava/lang/Object;)Ljava/lang/Object;", 0); - SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context); - return checkRef(jniEnv, result); -} - -SEXP R_getContextCall(CTXT context) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextCall", "(Ljava/lang/Object;)Ljava/lang/Object;", 0); - SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context); - return checkRef(jniEnv, result); -} - -SEXP R_getContextSrcRef(CTXT context) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextSrcRef", "(Ljava/lang/Object;)Ljava/lang/Object;", 0); - SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context); - result = checkRef(jniEnv, result); - return result == R_NilValue ? NULL : result; -} - -int R_insideBrowser() { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_insideBrowser", "()I", 0); - return (*jniEnv)->CallIntMethod(jniEnv, UpCallsRFFIObject, methodID); -} - -int R_isGlobal(CTXT context) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_isGlobal", "(Ljava/lang/Object;)I", 0); - return (*jniEnv)->CallIntMethod(jniEnv, UpCallsRFFIObject, methodID, context); -} - -int R_isEqual(void* x, void* y) { - JNIEnv *jniEnv = getEnv(); - jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_isEqual", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0); - return (*jniEnv)->CallIntMethod(jniEnv, UpCallsRFFIObject, methodID, x, y); -} diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile index 1e6384f5d0..624b810ed0 100644 --- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile +++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2018, 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 @@ -45,8 +45,9 @@ C_OBJECTS := $(LOCAL_C_OBJECTS) $(TRUFFLE_COMMON_C_OBJECTS) FFI_INCLUDES = -I$(TOPDIR)/include -I$(TOPDIR)/include/R_ext #NFI_INCLUDES is set in environment (by mx) LOCAL_INCLUDES = -I . -I $(abspath ../include) -I $(abspath ../common) +JNI_INCLUDES = -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/$(JDK_OS_DIR) -INCLUDES := $(LOCAL_INCLUDES) $(FFI_INCLUDES) $(NFI_INCLUDES) +INCLUDES := $(LOCAL_INCLUDES) $(FFI_INCLUDES) $(NFI_INCLUDES) $(JNI_INCLUDES) # uncomment to see exactly where headers are being read from #CFLAGS := $(CFLAGS) -H diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c index d611803139..147982d7a0 100644 --- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c +++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c @@ -1,30 +1,654 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * 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 * - * 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. + * Copyright (c) 1995-2015, The R Core Team + * Copyright (c) 2003, The R Foundation + * Copyright (c) 2016, 2018, Oracle and/or its affiliates * - * 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. + * All rights reserved. */ + +#include <dlfcn.h> +#include <sys/utsname.h> +#include <sys/stat.h> +#define R_INTERFACE_PTRS #include <Rinterface.h> #include <rffiutils.h> +#include <R_ext/RStartup.h> +#include <jni.h> #include "../common/rffi_upcalls.h" +// R_Interactive is actually field in the startup parameters structure, moreover, we'll also +// set-up the value of the actual external global R_Interactive exported and supported +// (in single threaded mode) by FastR. +#undef R_Interactive + +extern char **environ; + +static JavaVM *javaVM; +static JNIEnv *jniEnv = NULL; +static int initialized = 0; +static char *java_home; + +static jclass rembeddedClass; +static jclass rStartParamsClass; +static jclass rInterfaceCallbacksClass; + +int R_running_as_main_program; +int R_SignalHandlers; +FILE * R_Consolefile; +FILE * R_Outputfile; +int R_DirtyImage; // TODO update this +void *R_GlobalContext; // TODO what? +SA_TYPE SaveAction; // ?? + +typedef jint (JNICALL *JNI_CreateJavaVMFunc)(JavaVM **pvm, void **penv, void *args); + +static void *dlopen_jvmlib(char *libpath) { + void *handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "Rf_initialize_R: cannot dlopen %s: %s\n", libpath, dlerror()); + exit(1); + } + return handle; +} + +// -------------------- +// JNI helpers + +static JNIEnv* getEnv() { + return jniEnv; +} + +static jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic) { + jmethodID methodID = isStatic ? (*env)->GetStaticMethodID(env, klass, name, sig) : (*env)->GetMethodID(env, klass, name, sig); + if (methodID == NULL) { + char buf[1024]; + strcpy(buf, "failed to find "); + strcat(buf, isStatic ? "static" : "instance"); + strcat(buf, " method "); + strcat(buf, name); + strcat(buf, "("); + strcat(buf, sig); + strcat(buf, ")"); + (*env)->FatalError(env, buf); + } + return methodID; +} + +jclass checkFindClass(JNIEnv *env, const char *name) { + jclass klass = (*env)->FindClass(env, name); + if (klass == NULL) { + char buf[1024]; + strcpy(buf, "failed to find class "); + strcat(buf, name); + strcat(buf, ".\nDid you set R_HOME to the correct location?"); + (*env)->FatalError(env, buf); + } + return (*env)->NewGlobalRef(env, klass); +} + + +// --------------------- +// UpCalls + +// IDE and tools up-calls + +CTXT R_getGlobalFunctionContext() { + return ((call_R_getGlobalFunctionContext) callbacks[R_getGlobalFunctionContext_x])(); +} + +CTXT R_getParentFunctionContext(CTXT c) { + return ((call_R_getParentFunctionContext) callbacks[R_getParentFunctionContext_x])(c); +} + +SEXP R_getContextEnv(CTXT c) { + return ((call_R_getContextEnv) callbacks[R_getContextEnv_x])(c); +} + +SEXP R_getContextFun(CTXT c) { + return ((call_R_getContextFun) callbacks[R_getContextFun_x])(c); +} + +SEXP R_getContextCall(CTXT c) { + return ((call_R_getContextCall) callbacks[R_getContextCall_x])(c); +} + +SEXP R_getContextSrcRef(CTXT c) { + return ((call_R_getContextSrcRef) callbacks[R_getContextSrcRef_x])(c); +} + +int R_insideBrowser() { + return ((call_R_insideBrowser) callbacks[R_insideBrowser_x])(); +} + +int R_isGlobal(CTXT c) { + return ((call_R_isGlobal) callbacks[R_isGlobal_x])(c); +} + +int R_isEqual(void* x, void* y) { + return ((call_R_isEqual) callbacks[R_isEqual_x])(x, y); +} + + +// separate vm args from user args +static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) { + int vcount = 0; + int ucount = 0; + for (int i = 0; i < argc; i++) { + char *arg = argv[i]; + if ((arg[0] == '-' && arg[1] == 'X') || (arg[0] == '-' && arg[1] == 'D')) { + vmargv[vcount++] = arg; + } else { + uargv[ucount++] = arg; + } + } + return vcount; +} + +// Forward declarations of helper functions +static char **update_environ_with_java_home(void); +static void print_environ(char **env); +static char *get_classpath(char *r_home); + +int Rf_initialize_R(int argc, char *argv[]) { + if (initialized) { + fprintf(stderr, "%s", "R is already initialized\n"); + exit(1); + } + // print_environ(environ); + char *r_home = getenv("R_HOME"); + if (r_home == NULL) { + fprintf(stderr, "R_HOME must be set\n"); + exit(1); + } + struct utsname utsname; + uname(&utsname); + char jvmlib_path[256]; + java_home = getenv("JAVA_HOME"); + if (java_home == NULL) { + if (strcmp(utsname.sysname, "Linux") == 0) { + char *jvmdir = "/usr/java/latest"; + struct stat statbuf; + if (stat(jvmdir, &statbuf) == 0) { + java_home = jvmdir; + } + } else if (strcmp(utsname.sysname, "Darwin") == 0) { + char *jvmdir = "/Library/Java/JavaVirtualMachines/jdk.latest"; + struct stat statbuf; + if (stat(jvmdir, &statbuf) == 0) { + java_home = (char*)malloc(strlen(jvmdir) + 32); + strcpy(java_home, jvmdir); + strcat(java_home, "/Contents/Home"); + } + } + if (java_home == NULL) { + fprintf(stderr, "Rf_initialize_R: can't find a JAVA_HOME\n"); + exit(1); + } + } + strcpy(jvmlib_path, java_home); + if (strcmp(utsname.sysname, "Linux") == 0) { + strcat(jvmlib_path, "/jre/lib/amd64/server/libjvm.so"); + } else if (strcmp(utsname.sysname, "Darwin") == 0) { + strcat(jvmlib_path, "/jre/lib/server/libjvm.dylib"); + // Must also load libjli to avoid going through framework + // and failing to find our JAVA_HOME runtime + char jlilib_path[256]; + strcpy(jlilib_path, java_home); + strcat(jlilib_path, "/jre/lib/jli/libjli.dylib"); + dlopen_jvmlib(jlilib_path); + } else { + fprintf(stderr, "unsupported OS: %s\n", utsname.sysname); + exit(1); + } + void *vm_handle = dlopen_jvmlib(jvmlib_path); + JNI_CreateJavaVMFunc createJavaVMFunc = (JNI_CreateJavaVMFunc) dlsym(vm_handle, "JNI_CreateJavaVM"); + if (createJavaVMFunc == NULL) { + fprintf(stderr, "Rf_initialize_R: cannot find JNI_CreateJavaVM\n"); + exit(1); + } + + char *vm_cp = get_classpath(r_home); + //printf("cp %s\n", vm_cp); + + char **vmargs = malloc(argc * sizeof(char*)); + char **uargs = malloc(argc * sizeof(char*)); + int vmargc = process_vmargs(argc, argv, vmargs, uargs); + argc -= vmargc; + argv = uargs; + JavaVMOption vm_options[1 + vmargc]; + + vm_options[0].optionString = vm_cp; + for (int i = 0; i < vmargc; i++) { + vm_options[i + 1].optionString = vmargs[i]; + } + + JavaVMInitArgs vm_args; + vm_args.version = JNI_VERSION_1_8; + vm_args.nOptions = 1 + vmargc; + vm_args.options = vm_options; + vm_args.ignoreUnrecognized = JNI_TRUE; + + jint flag = (*createJavaVMFunc)(&javaVM, (void**) + &jniEnv, &vm_args); + if (flag == JNI_ERR) { + fprintf(stderr, "Rf_initEmbeddedR: error creating Java VM, exiting...\n"); + return 1; + } + + rInterfaceCallbacksClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RInterfaceCallbacks"); + rembeddedClass = checkFindClass(jniEnv, "com/oracle/truffle/r/engine/shell/REmbedded"); + jclass stringClass = checkFindClass(jniEnv, "java/lang/String"); + jmethodID initializeMethod = checkGetMethodID(jniEnv, rembeddedClass, "initializeR", "([Ljava/lang/String;)V", 1); + jobjectArray argsArray = (*jniEnv)->NewObjectArray(jniEnv, argc, stringClass, NULL); + for (int i = 0; i < argc; i++) { + jstring arg = (*jniEnv)->NewStringUTF(jniEnv, argv[i]); + (*jniEnv)->SetObjectArrayElement(jniEnv, argsArray, i, arg); + } + // Can't TRACE this upcall as system not initialized + (*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray); + initialized++; + return 0; +} + char *R_HomeDir(void) { - return ((call_R_HomeDir) callbacks[R_HomeDir_x])(); + // TODO: find out if this function could be invoked before the JVM and FastR get initialized + // in which case the access to callbacks will cause SIGSEGV + return ((call_R_HomeDir) callbacks[R_HomeDir_x])(); +} + +void R_SaveGlobalEnvToFile(const char *f) { + unimplemented("R_SaveGlobalEnvToFile"); +} + +void R_Suicide(const char *s) { ptr_R_Suicide(s); } + +void R_DefParams(Rstart rs) { + // These are the GnuR defaults and correspond to the settings in RStartParams + // None of the size params make any sense for FastR + rs->R_Quiet = FALSE; + rs->R_Slave = FALSE; + rs->R_Interactive = TRUE; + rs->R_Verbose = FALSE; + rs->RestoreAction = SA_RESTORE; + rs->SaveAction = SA_SAVEASK; + rs->LoadSiteFile = TRUE; + rs->LoadInitFile = TRUE; + rs->DebugInitFile = FALSE; +// rs->vsize = R_VSIZE; +// rs->nsize = R_NSIZE; +// rs->max_vsize = R_SIZE_T_MAX; +// rs->max_nsize = R_SIZE_T_MAX; +// rs->ppsize = R_PPSSIZE; + rs->NoRenviron = FALSE; +// R_SizeFromEnv(Rp); +} + +// This call has to be made via JNI as we are not in a down-call, i.e. in truffle context, when this gets executed. +void R_SetParams(Rstart rs) { + JNIEnv *jniEnv = getEnv(); + jmethodID setParamsMethodID = checkGetMethodID(jniEnv, rembeddedClass, "setParams", "(ZZZZZZZIIZ)V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rStartParamsClass, setParamsMethodID, rs->R_Quiet, rs->R_Slave, rs->R_Interactive, + rs->R_Verbose, rs->LoadSiteFile, rs->LoadInitFile, rs->DebugInitFile, + rs->RestoreAction, rs->SaveAction, rs->NoRenviron); +} + +void R_SizeFromEnv(Rstart rs) { + unimplemented("R_SizeFromEnv"); +} + +void R_common_command_line(int *a, char **b, Rstart rs) { + unimplemented("R_common_command_line"); +} + +void R_set_command_line_arguments(int argc, char **argv) { + unimplemented("R_set_command_line_arguments"); +} + + +int Rf_initEmbeddedR(int argc, char *argv[]) { + Rf_initialize_R(argc, argv); +// R_Interactive = TRUE; + setup_Rmainloop(); + return 1; +} + +void Rf_endEmbeddedR(int fatal) { + (*javaVM)->DestroyJavaVM(javaVM); + //TODO fatal +} + +static void setupOverrides(void); + +void Rf_mainloop(void) { + JNIEnv *jniEnv = getEnv(); + setupOverrides(); + jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod); +} + +// functions that can be assigned by an embedded client to change behavior + +// Note: pointer to this function is typically saved by the user to be called from that +// user's R_Suicide override to actually really commit the suicide. We invoke this through +// JNI intentionally to avoid any potential problems with NFI being called while destroying the VM. +void uR_Suicide(const char *x) { + JNIEnv *jniEnv = getEnv(); + jstring msg = (*jniEnv)->NewStringUTF(jniEnv, x); + jmethodID suicideMethod = checkGetMethodID(jniEnv, rembeddedClass, "R_Suicide", "(Ljava/lang/String;)V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, suicideMethod, msg); +} + +void uR_ShowMessage(const char *x) { + unimplemented("R_ShowMessage"); +} + +int uR_ReadConsole(const char *a, unsigned char *b, int c, int d) { + return (int) unimplemented("R_ReadConsole"); } +void uR_WriteConsole(const char *x, int y) { + unimplemented("R_WriteConsole"); +} + +void uR_WriteConsoleEx(const char *x, int y, int z) { + unimplemented("R_WriteConsole"); +} + +void uR_ResetConsole(void) { + unimplemented("R_ResetConsole"); +} + +void uR_FlushConsole(void) { + unimplemented("R_FlushConsole"); +} + +void uR_ClearerrConsole(void) { + unimplemented("R_ClearerrConsole"); +} + +void uR_Busy(int x) { + unimplemented("R_Busy"); +} + +int uR_ShowFiles(int a, const char **b, const char **c, + const char *d, Rboolean e, const char *f) { + return (int) unimplemented("R_ShowFiles"); +} + +int uR_ChooseFile(int a, char *b, int c) { + return (int) unimplemented("R_ChooseFile"); +} + +int uR_EditFile(const char *a) { + return (int) unimplemented("R_EditFile"); +} + +void uR_loadhistory(SEXP a, SEXP b, SEXP c, SEXP d) { + unimplemented("uR_loadhistory"); +} + +void uR_savehistory(SEXP a, SEXP b, SEXP c, SEXP d) { + unimplemented("R_savehistory"); +} + +void uR_addhistory(SEXP a, SEXP b, SEXP c, SEXP d) { + unimplemented("R_addhistory"); +} + +int uR_EditFiles(int a, const char **b, const char **c, const char *d) { + return (int)unimplemented(""); +} + +SEXP udo_selectlist(SEXP a, SEXP b, SEXP c, SEXP d) { + return unimplemented("R_EditFiles"); +} + +SEXP udo_dataentry(SEXP a, SEXP b, SEXP c, SEXP d) { + return unimplemented("do_dataentry"); +} + +SEXP udo_dataviewer(SEXP a, SEXP b, SEXP c, SEXP d) { + return unimplemented("do_dataviewer"); +} + +void uR_ProcessEvents(void) { + unimplemented("R_ProcessEvents"); +} + +void uR_CleanUp(SA_TYPE x, int y, int z) { + return ((call_R_CleanUp) callbacks[R_CleanUp_x])(x, y, z); +} + +void (*ptr_R_Suicide)(const char *) = uR_Suicide; +void (*ptr_R_ShowMessage)(const char *) = uR_ShowMessage; +int (*ptr_R_ReadConsole)(const char *, unsigned char *, int, int) = uR_ReadConsole; +void (*ptr_R_WriteConsole)(const char *, int) = uR_WriteConsole; +void (*ptr_R_WriteConsoleEx)(const char *, int, int) = uR_WriteConsoleEx; +void (*ptr_R_ResetConsole)(void) = uR_ResetConsole; +void (*ptr_R_FlushConsole)(void) = uR_FlushConsole; +void (*ptr_R_ClearerrConsole)(void) = uR_ClearerrConsole; +void (*ptr_R_Busy)(int) = uR_Busy; +void (*ptr_R_CleanUp)(SA_TYPE, int, int) = uR_CleanUp; +int (*ptr_R_ShowFiles)(int, const char **, const char **, + const char *, Rboolean, const char *) = uR_ShowFiles; +int (*ptr_R_ChooseFile)(int, char *, int) = uR_ChooseFile; +int (*ptr_R_EditFile)(const char *) = uR_EditFile; +void (*ptr_R_loadhistory)(SEXP, SEXP, SEXP, SEXP) = uR_loadhistory; +void (*ptr_R_savehistory)(SEXP, SEXP, SEXP, SEXP) = uR_savehistory; +void (*ptr_R_addhistory)(SEXP, SEXP, SEXP, SEXP) = uR_addhistory; + +int (*ptr_R_EditFiles)(int, const char **, const char **, const char *) = uR_EditFiles; + +SEXP (*ptr_do_selectlist)(SEXP, SEXP, SEXP, SEXP) = udo_selectlist; +SEXP (*ptr_do_dataentry)(SEXP, SEXP, SEXP, SEXP) = udo_dataentry; +SEXP (*ptr_do_dataviewer)(SEXP, SEXP, SEXP, SEXP) = udo_dataviewer; +void (*ptr_R_ProcessEvents)() = uR_ProcessEvents; + +// This call cannot be made via NFI because it is invoked from Rf_mainloop, +// which is exported C function expected to be invoked by the embedded before actually starting R engine. +void setupOverrides(void) { + JNIEnv *jniEnv = getEnv(); + jmethodID ovrMethodID = checkGetMethodID(jniEnv, rInterfaceCallbacksClass, "override", "(Ljava/lang/String;)V", 1); + jstring name; + if (ptr_R_Suicide != uR_Suicide) { + name = (*jniEnv)->NewStringUTF(jniEnv, "R_Suicide"); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); + } + if (*ptr_R_CleanUp != uR_CleanUp) { + name = (*jniEnv)->NewStringUTF(jniEnv, "R_CleanUp"); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); + } + if (*ptr_R_ReadConsole != uR_ReadConsole) { + name = (*jniEnv)->NewStringUTF(jniEnv, "R_ReadConsole"); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); + } + if (*ptr_R_WriteConsole != uR_WriteConsole) { + name = (*jniEnv)->NewStringUTF(jniEnv, "R_WriteConsole"); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name); + } +} + +static void writeConsoleImpl(char *cbuf, int len, int otype) { + if (ptr_R_WriteConsole == NULL) { + // otype gives std (0) or err (1) + (*ptr_R_WriteConsoleEx)(cbuf, len, otype); + } else { + (*ptr_R_WriteConsole)(cbuf, len); + } +} + +void uR_PolledEvents(void) { + unimplemented("R_PolledEvents"); +} + +void (* R_PolledEvents)(void) = uR_PolledEvents; + +void Rf_jump_to_toplevel() { + unimplemented("Rf_jump_to_toplevel"); +} + +#include <R_ext/eventloop.h> + +fd_set *R_checkActivity(int usec, int ignore_stdin) { + return (fd_set*) unimplemented("R_checkActivity"); +} + +void R_runHandlers(InputHandler *handlers, fd_set *mask) { + unimplemented("R_runHandlers"); +} + +// ----------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------------- +// Downcalls from Java. We invoke these functions via .Call interface so that the callbacks +// variable gets properly initialized and in general the R API is available. These functions +// delegate to user provided C routines that may want to access the R API. +// NOTE: those two functions are looked up by name! + +SEXP invokeCleanUp(SEXP x, SEXP y, SEXP z) { + ptr_R_CleanUp(Rf_asInteger(x), Rf_asInteger(y), Rf_asInteger(z)); + return R_NilValue; +} + +SEXP invokeSuicide(SEXP msg) { + ptr_R_Suicide(R_CHAR(STRING_ELT(msg, 0))); + return R_NilValue; +} + +// TODO: these 3 are not yet invoked via .Call + +void rembedded_write_console(char *cbuf, int len) { + writeConsoleImpl(cbuf, len, 0); +} + +void rembedded_write_err_console(char *cbuf, int len) { + writeConsoleImpl(cbuf, len, 1); +} + +char* rembedded_read_console(const char* prompt) { + char* cbuf = malloc(sizeof(char) * 1024); + int n = (*ptr_R_ReadConsole)(prompt, cbuf, 1024, 0); + return cbuf; +} + +// ----------------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------------- +// Helpers + + +int R_wait_usec; // TODO: necessary to resolve externals? otherwise dead code + +#include <unistd.h> +#include <errno.h> + +static void perror_exit(char *msg) { + perror(msg); + exit(1); +} + +// support for getting the correct classpath for the VM +// We use $R_HOME/bin/execRextras/Rclasspath to do this to emulate what happens +// during normal execution +static char *get_classpath(char *r_home) { + char **env = update_environ_with_java_home(); + //print_environ(env); + int pipefd[2]; + if (pipe(pipefd) == -1) { + perror_exit("pipe"); + } + pid_t pid = fork(); + if (pid == -1) { + perror("fork"); + } + if (pid == 0) { + // child + char path[1024]; + strcpy(path, r_home); + strcat(path, "/bin/execRextras/Rclasspath"); + while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} + close(pipefd[1]); + close(pipefd[0]); + int rc = execle(path, path, (char *)NULL, env); + if (rc == -1) { + perror_exit("exec"); + } + return NULL; + } else { + // parent + const char *cpdef = "-Djava.class.path="; + char *buf = malloc(4096); + strcpy(buf, cpdef); + char *bufptr = buf + strlen(cpdef); + int max = 4096 - strlen(cpdef); + close(pipefd[1]); + while (1) { + int count = read(pipefd[0], bufptr, max); + if (count == -1) { + if (errno == EINTR) { + continue; + } else { + perror_exit("read"); + } + } else if (count == 0) { + // scrub any newline + bufptr--; + if (*bufptr != '\n') { + bufptr++; + } + *bufptr = 0; + break; + } else { + bufptr += count; + max -= count; + } + } + close(pipefd[0]); + wait(NULL); + return buf; + } +} + +static char **update_environ(char *def) { + int count = 0; + char **e = environ; + while (*e != NULL) { + e++; + count++; + } + char **new_env = malloc(sizeof(char *) * (count + 2)); + e = environ; + char **ne = new_env; + while (*e != NULL) { + *ne++ = *e++; + } + *ne++ = def; + *ne = (char*) NULL; + return new_env; +} + +static char **update_environ_with_java_home(void) { + char **e = environ; + while (*e != NULL) { + if (strstr(*e, "JAVA_HOME=")) { + return environ; + } + e++; + } + char *java_home_env = malloc(strlen(java_home) + 10); + strcpy(java_home_env, "JAVA_HOME="); + strcat(java_home_env, java_home); + return update_environ(java_home_env); +} + +// debugging +static void print_environ(char **env) { + fprintf(stdout, "## Environment variables at %p\n", env); + char **e = env; + while (*e != NULL) { + fprintf(stdout, "%s\n", *e); + e++; + } +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java index 2d774b7c73..58acd5b4ae 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -44,7 +44,7 @@ import com.oracle.truffle.r.runtime.REnvVars; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RSource; -import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; @@ -89,7 +89,7 @@ public final class RBuiltinPackages implements RBuiltinLookup { baseEnv.put(methodName, function); baseEnv.lockBinding(methodName); } catch (PutException ex) { - Utils.rSuicide("failed to install builtin function: " + methodName); + RSuicide.rSuicide("failed to install builtin function: " + methodName); } } } @@ -100,7 +100,7 @@ public final class RBuiltinPackages implements RBuiltinLookup { try { baseSource = RSource.fromFileName(basePathbase.toString(), true); } catch (IOException ex) { - throw Utils.rSuicide(String.format("unable to open the base package %s", basePathbase)); + throw RSuicide.rSuicide(String.format("unable to open the base package %s", basePathbase)); } // Load the (stub) DLL for base if (FastROptions.LoadPackagesNativeCode.getBooleanValue()) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java index 015e798f80..d7627a2652 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java @@ -6,7 +6,7 @@ * Copyright (c) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka * Copyright (c) 1995-2014, The R Core Team * Copyright (c) 2002-2008, The R Foundation - * Copyright (c) 2013, 2017, Oracle and/or its affiliates + * Copyright (c) 2013, 2018, Oracle and/or its affiliates * * All rights reserved. */ @@ -68,7 +68,7 @@ public abstract class Quit extends RBuiltinNode.Arg3 { warning(RError.Message.QUIT_INVALID_RUNLAST); runLast = RRuntime.LOGICAL_FALSE; } - RCleanUp.cleanUp(ask, status, RRuntime.fromLogical(runLast)); + RCleanUp.cleanUp(RContext.getInstance(), ask, status, RRuntime.fromLogical(runLast)); throw RInternalError.shouldNotReachHere("cleanup returned"); } } 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 index a384de71c6..1c1f2649c4 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -29,6 +29,7 @@ import java.util.ArrayList; 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.RSuicide; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.RContext; @@ -42,7 +43,7 @@ public abstract class SystemFunctionFactory { theInstance = (SystemFunctionFactory) Class.forName(className).newInstance(); } catch (Exception ex) { // CheckStyle: stop system..print check - Utils.rSuicide("Failed to instantiate class: " + className); + RSuicide.rSuicide("Failed to instantiate class: " + className); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java index 633f882eff..2bfa09ae8d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java @@ -40,7 +40,7 @@ import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.conn.StdConnections; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RFunction; @@ -286,7 +286,7 @@ public class TraceHandling { try { fileWriter = new FileWriter("fastr_tracecalls.log"); } catch (IOException e) { - Utils.rSuicide("failed to open 'fastr_tracecalls.log'" + e.getMessage()); + RSuicide.rSuicide("failed to open 'fastr_tracecalls.log'" + e.getMessage()); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java index 800c076625..aa6ee73ec4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java @@ -37,6 +37,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import com.oracle.truffle.r.runtime.ResourceHandlerFactory.Handler; +import com.oracle.truffle.r.runtime.context.RContext; /** * Default implementation uses the default mechanism in {@code java.lang.Class}. @@ -105,7 +106,7 @@ class LazyResourceHandlerFactory extends ResourceHandlerFactory implements Handl } return result; } catch (Exception ex) { - Utils.rSuicide(ex, "Could not load R files from resources. Details: " + ex.getMessage()); + RSuicide.rSuicide(RContext.getInstance(), ex, "Could not load R files from resources. Details: " + ex.getMessage()); return null; } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java index 37065d16a1..4d5f97ecf4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java @@ -5,7 +5,7 @@ * * Copyright (c) 1995, 1996 Robert Gentleman and Ross Ihaka * Copyright (c) 1997-2014, The R Core Team - * Copyright (c) 2013, 2017, Oracle and/or its affiliates + * Copyright (c) 2013, 2018, Oracle and/or its affiliates * * All rights reserved. */ @@ -13,14 +13,27 @@ package com.oracle.truffle.r.runtime; import java.util.ArrayList; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.r.launcher.RStartParams; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.RContext.ConsoleIO; -import com.oracle.truffle.r.runtime.ffi.RFFIFactory; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RIntVector; +import com.oracle.truffle.r.runtime.ffi.CallRFFI.InvokeCallNode; +import com.oracle.truffle.r.runtime.ffi.DLL.RFindSymbolNode; +import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle; +import com.oracle.truffle.r.runtime.ffi.NativeCallInfo; import com.oracle.truffle.r.runtime.gnur.SA_TYPE; import com.oracle.truffle.r.runtime.instrument.InstrumentationState; -public class RCleanUp { +public abstract class RCleanUp { + + private RCleanUp() { + } private static ArrayList<InstrumentationState.CleanupHandler> cleanupHandlers = new ArrayList<>(); @@ -28,9 +41,11 @@ public class RCleanUp { cleanupHandlers.add(cleanupHandler); } - public static void cleanUp(SA_TYPE saveType, int status, boolean runLast) { + public static void cleanUp(RContext ctx, SA_TYPE saveType, int status, boolean runLast) { if (RInterfaceCallbacks.R_CleanUp.isOverridden()) { - RFFIFactory.getREmbedRFFI().cleanUp(saveType.ordinal(), status, runLast ? 1 : 0); + RootCallTarget invokeUserCleanup = ctx.getOrCreateCachedCallTarget(UserDefinedCleanUpRootNode.class, () -> new UserDefinedCleanUpRootNode(ctx).getCallTarget()); + Object[] args = new Object[]{asVector(saveType.ordinal()), asVector(status), asVector(runLast ? 1 : 0)}; + invokeUserCleanup.call(args); } else { stdCleanUp(saveType, status, runLast); } @@ -118,4 +133,31 @@ public class RCleanUp { // TODO errors should return to toplevel if interactive RContext.getEngine().checkAndRunStartupShutdownFunction(".Last.sys"); } + + private static RIntVector asVector(int value) { + return RDataFactory.createIntVectorFromScalar(value); + } + + private static final class UserDefinedCleanUpRootNode extends RootNode { + protected UserDefinedCleanUpRootNode(RContext ctx) { + super(null); + invokeCallNode = ctx.getRFFI().callRFFI.createInvokeCallNode(); + Truffle.getRuntime().createCallTarget(this); + } + + private static final String SYMBOL_NAME = "invokeCleanUp"; + @Child InvokeCallNode invokeCallNode; + @Child RFindSymbolNode findSymbolNode = RFindSymbolNode.create(); + + @Override + public Object execute(VirtualFrame frame) { + SymbolHandle invokeCleanUp = findSymbolNode.execute(SYMBOL_NAME, null, null); + if (invokeCleanUp == null) { + CompilerDirectives.transferToInterpreter(); + throw RInternalError.shouldNotReachHere("Cannot find " + SYMBOL_NAME + " symbol which should be declared in Rembedded.c"); + } + invokeCallNode.dispatch(new NativeCallInfo(SYMBOL_NAME, invokeCleanUp, null), frame.getArguments()); + return null; + } + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java index acd462de0f..c9103b3d8c 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java @@ -175,7 +175,7 @@ public final class REnvVars implements RContext.ContextState { rHomePath = Paths.get(rHome); } if (!validateRHome(rHomePath, markerFile())) { - Utils.rSuicide("R_HOME is not set correctly"); + RSuicide.rSuicide("R_HOME is not set correctly"); } rHome = rHomePath.toString(); } 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 6adae8c519..a0282f634e 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 @@ -273,7 +273,7 @@ public final class RError extends RuntimeException implements TruffleException { /** * A temporary error that indicates an unimplemented feature where terminating the VM using - * {@link Utils#rSuicide(String)} would be inappropriate. + * {@link RSuicide#rSuicide(String)} would be inappropriate. */ public static RError nyi(RBaseNode node, String msg) { CompilerDirectives.transferToInterpreter(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java index 6a1b49c4a5..0ce6fde2ef 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -23,7 +23,10 @@ package com.oracle.truffle.r.runtime; /** - * Support for callbacks in embedded mode for certain VM operations. + * Support for callbacks in embedded mode for certain VM operations. If the embedding code overrides + * certain operation, the flag for that operation is updated here and when FastR is about to invoke + * one of those operations, normally implemented by default by FastR in Java, it checks the flag and + * eventually down-calls to the user provided handler. * */ public enum RInterfaceCallbacks { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java index 1c6a328bec..e1b9a36110 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -198,7 +198,7 @@ public final class RInternalError extends Error implements TruffleException { } System.err.println(message + " (see fastr_errors.log" + suffix + ")"); if (RContext.isEmbedded()) { - Utils.rSuicide("FastR internal error"); + RSuicide.rSuicide("FastR internal error"); } } if (!FastROptions.PrintErrorStacktraces.getBooleanValue() && !FastROptions.PrintErrorStacktracesToFile.getBooleanValue()) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java index eca6c3fc56..1ee08bc2ca 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -49,7 +49,7 @@ public class RPlatform { default: osSubDir = null; libExt = null; - Utils.rSuicide("CallRFFI: unsupported OS: " + osName); + RSuicide.rSuicide("CallRFFI: unsupported OS: " + osName); } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java index 9c7682b523..f726727a4a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -96,7 +96,7 @@ public final class RProfile implements RContext.ContextState { Path path = FileSystems.getDefault().getPath(REnvVars.rHome(), "library", "base", "R", "Rprofile"); Source source = getProfile(path.toString(), true); if (source == null) { - Utils.rSuicide("can't find system profile"); + RSuicide.rSuicide("can't find system profile"); } return source; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java new file mode 100644 index 0000000000..632e7b2594 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018, 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.runtime; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.RootNode; +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.ffi.CallRFFI.InvokeCallNode; +import com.oracle.truffle.r.runtime.ffi.DLL.RFindSymbolNode; +import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle; +import com.oracle.truffle.r.runtime.ffi.NativeCallInfo; +import com.oracle.truffle.r.runtime.ffi.RFFIFactory; + +public abstract class RSuicide { + private RSuicide() { + } + + /** + * Please use {@link #rSuicide(RContext, String)} overload whenever the context is available. + */ + public static RuntimeException rSuicide(String msg) { + throw rSuicide(RContext.getInstance(), msg); + } + + /** + * Called when the system encounters a fatal internal error and must commit suicide (i.e. + * terminate). It allows an embedded client to override the default (although they typically + * invoke the default eventually). + */ + public static RuntimeException rSuicide(RContext ctx, String msg) { + invokeUserDefinedSuicide(ctx, msg); + throw rSuicideDefault(msg); + } + + public static RuntimeException rSuicide(RContext ctx, Throwable cause, String msg) { + invokeUserDefinedSuicide(ctx, msg); + throw rSuicideDefault(msg); + } + + /** + * The default, non-overrideable, suicide call. It prints the message and throws + * {@link ExitException}. + * + * @param msg + */ + public static RuntimeException rSuicideDefault(String msg) { + System.err.println("FastR unexpected failure: " + msg); + throw new ExitException(2, false); + } + + private static RStringVector asVector(String value) { + return RDataFactory.createStringVector(value); + } + + private static void invokeUserDefinedSuicide(RContext ctx, String msg) { + if (ctx != null && RInterfaceCallbacks.R_Suicide.isOverridden()) { + RootCallTarget invokeUserCleanup = ctx.getOrCreateCachedCallTarget(UserDefinedSuicideRootNode.class, () -> new UserDefinedSuicideRootNode(ctx).getCallTarget()); + invokeUserCleanup.call(new Object[]{asVector(msg)}); + } + } + + private static final class UserDefinedSuicideRootNode extends RootNode { + protected UserDefinedSuicideRootNode(RContext ctx) { + super(null); + invokeCallNode = ctx.getRFFI().callRFFI.createInvokeCallNode(); + Truffle.getRuntime().createCallTarget(this); + } + + private static final String SYMBOL_NAME = "invokeSuicide"; + @Child InvokeCallNode invokeCallNode; + @Child RFindSymbolNode findSymbolNode = RFindSymbolNode.create(); + + @Override + public Object execute(VirtualFrame frame) { + SymbolHandle invokeSuicide = findSymbolNode.execute(SYMBOL_NAME, null, null); + if (invokeSuicide == null) { + CompilerDirectives.transferToInterpreter(); + throw RInternalError.shouldNotReachHere("Cannot find " + SYMBOL_NAME + " symbol which should be declared in Rembedded.c"); + } + invokeCallNode.dispatch(new NativeCallInfo(SYMBOL_NAME, invokeSuicide, null), frame.getArguments()); + return null; + } + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java index fc28f3a8eb..e6775d5e3d 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -72,7 +72,7 @@ public class TempPathName implements RContext.ContextState { if (t != null) { tempDirPath = t; } else { - Utils.rSuicide("cannot create 'R_TempDir'"); + RSuicide.rSuicide("cannot create 'R_TempDir'"); } // initialize deparse directory 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 18e5348b3e..9f542e9f7a 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 @@ -60,7 +60,6 @@ import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.MultiSlotData; import com.oracle.truffle.r.runtime.ffi.BaseRFFI; -import com.oracle.truffle.r.runtime.ffi.RFFIFactory; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; public final class Utils { @@ -139,7 +138,7 @@ public final class Utils { } catch (IOException ex) { } } - throw Utils.rSuicide("resource " + resourceName + " not found"); + throw RSuicide.rSuicide("resource " + resourceName + " not found"); } private static String getResourceAsString(InputStream is) throws IOException { @@ -170,37 +169,6 @@ public final class Utils { } - /** - * Called when the system encounters a fatal internal error and must commit suicide (i.e. - * terminate). It allows an embedded client to override the default (although they typically - * invoke the default eventually). - */ - public static RuntimeException rSuicide(String msg) { - if (RInterfaceCallbacks.R_Suicide.isOverridden()) { - RFFIFactory.getREmbedRFFI().suicide(msg); - } - throw rSuicideDefault(msg); - } - - public static RuntimeException rSuicide(Throwable cause, String msg) { - cause.printStackTrace(); - if (RInterfaceCallbacks.R_Suicide.isOverridden()) { - RFFIFactory.getREmbedRFFI().suicide(msg); - } - throw rSuicideDefault(msg); - } - - /** - * The default, non-overrideable, suicide call. It prints the message and throws - * {@link ExitException}. - * - * @param msg - */ - public static RuntimeException rSuicideDefault(String msg) { - System.err.println("FastR unexpected failure: " + msg); - throw new ExitException(2, false); - } - /** * This the real, final, non-overrideable, exit of the entire R system. TODO well, modulo how * quit() is interpreted when R is started implicitly from a Polyglot shell that is running diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java index 9925131aad..d8b16e8530 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java @@ -32,7 +32,7 @@ import java.util.EnumSet; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode; import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection; import com.oracle.truffle.r.runtime.context.RContext; @@ -70,7 +70,7 @@ public class StdConnections { stdout = new StdoutConnection(console); stderr = new StderrConnection(console); } catch (IOException ex) { - throw Utils.rSuicide("failed to open stdconnections:"); + throw RSuicide.rSuicide("failed to open stdconnections:"); } return this; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java index 32908201d1..6f6fc75678 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java @@ -44,8 +44,8 @@ import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.RErrorException; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.RType; -import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.VirtualEvalFrame; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.RContext.ContextKind; @@ -858,7 +858,7 @@ public abstract class REnvironment extends RAttributeStorage { try { put(key, value); } catch (PutException ex) { - Utils.rSuicide("exception in safePut"); + RSuicide.rSuicide("exception in safePut"); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java index 8bc97e9541..07b6aeba98 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java @@ -32,6 +32,7 @@ import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RError.RErrorException; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.ReturnException; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.RContext; @@ -432,11 +433,20 @@ public class DLL { /** * Loads a the {@code libR} library. This is an implementation specific library. */ - public static void loadLibR(String path) { - RContext context = RContext.getInstance(); - Object handle = DLLRFFI.DLOpenRootNode.create(context).call(path, false, false); + public static void loadLibR(RContext context, String path) { + Object handle = null; + try { + handle = DLLRFFI.DLOpenRootNode.create(context).call(path, false, false); + } catch (UnsatisfiedLinkError ex) { + throw RSuicide.rSuicide(context, "error loading libR from: " + path + ".\n" + + "If running on NFI backend, did you provide location of libtrufflenfi.so as value of system " + + "property 'truffle.nfi.library'?\nThe current value is '" + + System.getProperty("truffle.nfi.library") + "'. \nDetails: " + ex.getMessage()); + } catch (Throwable ex) { + throw RSuicide.rSuicide(context, "error loading libR from: " + path + ". Details: " + ex.getMessage()); + } if (handle == null) { - throw Utils.rSuicide("error loading libR from: " + path + "\n"); + throw RSuicide.rSuicide(context, "error loading libR from: " + path + "\n"); } ContextStateImpl dllContext = context.stateDLL; dllContext.addLibR(DLLInfo.create(libName(path), path, true, handle, false)); @@ -493,7 +503,7 @@ public class DLL { if (RContext.isInitialContextInitialized()) { throw new DLLException(ex, RError.Message.DLL_RINIT_ERROR); } else { - throw Utils.rSuicide(ex, RError.Message.DLL_RINIT_ERROR.message + " on default package: " + path); + throw RSuicide.rSuicide(RContext.getInstance(), ex, RError.Message.DLL_RINIT_ERROR.message + " on default package: " + path); } } } catch (UnsatisfiedLinkError ex) { @@ -506,8 +516,8 @@ public class DLL { * There is no sense in throwing an RError if we fail to load/init a (default) package * during initial context initialization, as it is essentially fatal for any of the standard * packages and likely indicates a bug in the RFFI implementation. So we call - * {@link Utils#rSuicide(String)} instead. When the system is stable, we can undo this, so - * that errors loading (user) packages added to R_DEFAULT_PACKAGES do throw RErrors. + * {@link RSuicide#rSuicide(String)} instead. When the system is stable, we can undo this, + * so that errors loading (user) packages added to R_DEFAULT_PACKAGES do throw RErrors. */ private synchronized DLLInfo doLoad(String absPath, boolean local, boolean now, boolean addToList) throws DLLException { RFFIContext stateRFFI = RContext.getInstance().getStateRFFI(); @@ -521,7 +531,7 @@ public class DLL { if (RContext.isInitialContextInitialized()) { throw new DLLException(ex, RError.Message.DLL_LOAD_ERROR, absPath, dlError); } else { - throw Utils.rSuicide(ex, "error loading default package: " + absPath + "\n" + dlError); + throw RSuicide.rSuicide(RContext.getInstance(), ex, "error loading default package: " + absPath + "\n" + dlError); } } finally { stateRFFI.afterDowncall(before); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java index 0534eaa651..f30a5dfbf4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java @@ -83,6 +83,12 @@ public enum NativeFunction { lminfl("([double], sint32, sint32, sint32, sint32, [double], [double], [double], [double], [double], double): void", "call_stats_", "stats"), // FastR helpers set_exception_flag("(): void"), + // FastR internal helper for R embedded mode + rembedded_write_console("(string, sint32):void"), + rembedded_write_err_console("(string, sint32):void"), + rembedded_read_console("(string):string"), + rembedded_native_clean_up("(sint32, sint32, sint32):void"), + rembedded_native_suicide("(string):void"), // user-defined RNG unif_init("(sint32): void", "user_", anyLibrary()), norm_rand("(): pointer", "user_", anyLibrary()), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java index 0be0b85c6a..929f2558b4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -22,18 +22,40 @@ */ package com.oracle.truffle.r.runtime.ffi; +import com.oracle.truffle.api.nodes.NodeInterface; + /** - * Function downcalls related to the embedded API. + * Function down-calls related to the embedded API. TODO: these all should be invoked as proper + * down-calls because the user code may want to use R API. */ public interface REmbedRFFI { - void suicide(String x); + interface ReadConsoleNode extends NodeInterface { + String execute(String prompt); + + static REmbedRFFI.ReadConsoleNode create() { + return RFFIFactory.getREmbedRFFI().createReadConsoleNode(); + } + } + + interface WriteConsoleBaseNode extends NodeInterface { + void execute(String x); + } - void cleanUp(int type, int x, int y); + interface WriteConsoleNode extends WriteConsoleBaseNode { + static REmbedRFFI.WriteConsoleNode create() { + return RFFIFactory.getREmbedRFFI().createWriteConsoleNode(); + } + } - String readConsole(String prompt); + interface WriteErrConsoleNode extends WriteConsoleBaseNode { + static REmbedRFFI.WriteErrConsoleNode create() { + return RFFIFactory.getREmbedRFFI().createWriteErrConsoleNode(); + } + } - void writeConsole(String x); + ReadConsoleNode createReadConsoleNode(); - void writeErrConsole(String x); + WriteConsoleNode createWriteConsoleNode(); + WriteErrConsoleNode createWriteErrConsoleNode(); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java index dcd77427f7..a09f842578 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -23,7 +23,7 @@ package com.oracle.truffle.r.runtime.ffi; import com.oracle.truffle.r.runtime.FastRConfig; -import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.RContext.ContextState; @@ -71,7 +71,7 @@ public abstract class RFFIFactory { try { rffiFactory = (RFFIFactory) Class.forName(klassName).newInstance(); } catch (Exception ex) { - throw Utils.rSuicide("Failed to instantiate class: " + klassName + ": " + ex); + throw RSuicide.rSuicide("Failed to instantiate class: " + klassName + ": " + ex); } } @@ -83,7 +83,7 @@ public abstract class RFFIFactory { try { return Type.valueOf(prop.toUpperCase()); } catch (IllegalArgumentException ex) { - throw Utils.rSuicide("No RFFI factory: " + prop); + throw RSuicide.rSuicide("No RFFI factory: " + prop); } } diff --git a/com.oracle.truffle.r.test.native/embedded/src/main.c b/com.oracle.truffle.r.test.native/embedded/src/main.c index 9a01de741f..9de0840477 100644 --- a/com.oracle.truffle.r.test.native/embedded/src/main.c +++ b/com.oracle.truffle.r.test.native/embedded/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -70,11 +70,13 @@ int main(int argc, char **argv) { printf("R_HOME must be set\n"); exit(1); } + printf("Initializing R with Rf_initialize_R...\n"); Rf_initialize_R(argc, argv); structRstart rp; Rstart Rp = &rp; R_DefParams(Rp); Rp->SaveAction = SA_SAVEASK; + printf("Initializing R with R_SetParams...\n"); R_SetParams(Rp); ptr_stdR_CleanUp = ptr_R_CleanUp; ptr_R_CleanUp = &testR_CleanUp; @@ -82,7 +84,12 @@ int main(int argc, char **argv) { ptr_R_Suicide = &testR_Suicide; ptr_R_ReadConsole = &testR_ReadConsole; ptr_R_WriteConsole = &testR_WriteConsole; - DllInfo *eDllInfo = R_getEmbeddingDllInfo(); + // TODO: + // printf("Calling R_getEmbeddingDllInfo...\n"); + // DllInfo *eDllInfo = R_getEmbeddingDllInfo(); + printf("Running R with Rf_mainloop...\n"); Rf_mainloop(); + printf("Closing R with Rf_endEmbeddedR...\n"); Rf_endEmbeddedR(0); + printf("Done"); } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java index 70ac71adbb..8dd00386cd 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java @@ -45,8 +45,8 @@ import org.junit.runner.Result; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RSuicide; import com.oracle.truffle.r.runtime.ResourceHandlerFactory; -import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.RContext.ContextKind; import com.oracle.truffle.r.test.generate.FastRSession; @@ -230,7 +230,7 @@ public class TestBase { if (updated) { if (expectedOutputManager.checkOnly) { // fail fast - Utils.rSuicideDefault("Test file:" + expectedOutputManager.outputFile + " is out of sync with unit tests"); + RSuicide.rSuicideDefault("Test file:" + expectedOutputManager.outputFile + " is out of sync with unit tests"); } System.out.println("updating " + expectedOutputManager.outputFile); } diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index e129a62211..f3632cb367 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -58,7 +58,7 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.ja com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java,gnu_r.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Download.java,gnu_r_gentleman_ihaka2.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java,gnu_r.copyright -com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c,gnu_r.copyright +com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c,gnu_r.copyright com.oracle.truffle.r.native/fficall/src/common/arithmetic_fastr.c,gnu_r_gentleman_ihaka.copyright com.oracle.truffle.r.native/fficall/src/common/coerce_fastr.c,gnu_r_gentleman_ihaka.copyright com.oracle.truffle.r.native/fficall/src/common/errors_fastr.c,gnu_r.core.copyright -- GitLab