diff --git a/.gitignore b/.gitignore index fe63b0a59d02279c6c99e502bd60a768794805ef..6b0dd1ea1285e0027b92a1c800821b3879b42efa 100644 --- a/.gitignore +++ b/.gitignore @@ -146,4 +146,5 @@ com.oracle.truffle.r.test.native/packages/testrffi/testrffi/tests/coerceTests.R com.oracle.truffle.r.native/version.built documentation/.pydevproject com.oracle.truffle.r.native/gnur/patch-build -/com.oracle.truffle.r.native/fficall/common.done \ No newline at end of file +com.oracle.truffle.r.test.native/embedded/embedded.actual.output +com.oracle.truffle.r.test.native/embedded/main.actual.output diff --git a/ci.hocon b/ci.hocon index 91c544d5569b556e32fff043d4b188f2f2627619..259a0e142ed2dc363b896a521cd05bed462b7e61 100644 --- a/ci.hocon +++ b/ci.hocon @@ -36,6 +36,8 @@ logfiles : [ "libdownloads/R-*/gnur_make.log" "libdownloads/R-*/Makeconf" "com.oracle.truffle.r.native/gnur/tests/log/all.diff" + "com.oracle.truffle.r.test.native/embedded/*.output" + "com.oracle.truffle.r.test.native/embedded/src/*.output" "*-tests/*.Rout" ] @@ -93,7 +95,7 @@ gateCmd : ["mx", "--strict-compliance", "rgate", "--strict-mode", "-t"] gateTestCommon : ${common} { run : [ - ${gateCmd} ["Versions,JDKReleaseInfo,BuildJavaWithJavac,UnitTests: with specials"] + ${gateCmd} ["Versions,JDKReleaseInfo,BuildJavaWithJavac,UnitTests: with specials,Rembedded"] ] } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java index ac33e442961a5780f765c997ff903018c2a2e93e..283b32fc899b0684a18c908a201752c1837ec14d 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java @@ -106,7 +106,6 @@ import com.oracle.truffle.r.runtime.env.frame.ActiveBinding; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.interop.R2Foreign; import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen; -import com.oracle.truffle.r.runtime.nodes.RCodeBuilder; import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @@ -616,7 +615,7 @@ final class REngine implements Engine, Engine.Timings { if (printResult && result != null) { assert topLevel; if (visibility.execute(vf)) { - printResultImpl(result); + printResultImpl(RContext.getInstance(), result); } } if (topLevel) { @@ -684,19 +683,23 @@ final class REngine implements Engine, Engine.Timings { } @Override - public void printResult(Object originalResult) { - printResultImpl(originalResult); + public void printResult(RContext ctx, Object originalResult) { + printResultImpl(ctx, originalResult); } @TruffleBoundary - static void printResultImpl(Object originalResult) { + static void printResultImpl(RContext ctx, Object originalResult) { Object result = evaluatePromise(originalResult); result = RRuntime.asAbstractVector(result); + MaterializedFrame callingFrame = REnvironment.globalEnv(ctx).getFrame(); + printValue(ctx, callingFrame, result); + } + + private static void printValue(RContext ctx, MaterializedFrame callingFrame, Object result) { if (result instanceof RTypedValue || result instanceof TruffleObject) { Object resultValue = ShareObjectNode.share(evaluatePromise(result)); - MaterializedFrame callingFrame = REnvironment.globalEnv().getFrame(); if (result instanceof RAttributable && ((RAttributable) result).isS4()) { - Object printMethod = REnvironment.getRegisteredNamespace("methods").get("show"); + Object printMethod = REnvironment.getRegisteredNamespace(ctx, "methods").get("show"); RFunction function = (RFunction) evaluatePromise(printMethod); CallRFunctionNode.executeSlowpath(function, RCaller.createInvalid(callingFrame), callingFrame, new Object[]{resultValue}, null); } else { @@ -719,27 +722,6 @@ final class REngine implements Engine, Engine.Timings { } } - /* - * This abstracts the calling convention, etc. behind the RASTBuilder, but creating large - * amounts of CallTargets during testing is too much overhead at the moment. - */ - @SuppressWarnings("unused") - private void printAlternative(Object result) { - Object printFunction; - if (result instanceof RAttributable && ((RAttributable) result).isS4()) { - printFunction = REnvironment.getRegisteredNamespace("methods").get("show"); - } else { - printFunction = REnvironment.getRegisteredNamespace("base").get("print"); - } - RFunction function = (RFunction) evaluatePromise(printFunction); - - MaterializedFrame callingFrame = REnvironment.globalEnv().getFrame(); - // create a piece of AST to perform the call - RCodeBuilder<RSyntaxNode> builder = RContext.getASTBuilder(); - RSyntaxNode call = builder.call(RSyntaxNode.LAZY_DEPARSE, builder.constant(RSyntaxNode.LAZY_DEPARSE, function), builder.constant(RSyntaxNode.LAZY_DEPARSE, result)); - doMakeCallTarget(call.asRNode(), RSource.Internal.EVAL_WRAPPER.string, false, false).call(callingFrame); - } - private static Object evaluatePromise(Object value) { return value instanceof RPromise ? PromiseHelperNode.evaluateSlowPath((RPromise) value) : value; } diff --git a/com.oracle.truffle.r.ffi.codegen/src/com/oracle/truffle/r/ffi/codegen/FFIVariablesCodeGen.java b/com.oracle.truffle.r.ffi.codegen/src/com/oracle/truffle/r/ffi/codegen/FFIVariablesCodeGen.java index 58e4f7c87abb5043dbf34512b9ddcbcf9f1d58c9..a0ebc7efa90c03ada77861aec09d50072dc1893f 100644 --- a/com.oracle.truffle.r.ffi.codegen/src/com/oracle/truffle/r/ffi/codegen/FFIVariablesCodeGen.java +++ b/com.oracle.truffle.r.ffi.codegen/src/com/oracle/truffle/r/ffi/codegen/FFIVariablesCodeGen.java @@ -36,7 +36,7 @@ public class FFIVariablesCodeGen { */ public static void main(String[] args) { System.out.println("// Update com.oracle.truffle.r.native/fficall/src/common/rffi_variablesindex.h with the following: \n"); - System.out.println("// Generated by RFFIVariables.java:\n"); + System.out.printf("// Generated by %s\n\n", FFIVariablesCodeGen.class.getSimpleName()); for (RFFIVariables var : RFFIVariables.values()) { System.out.printf("#define %s_x %d\n", var.name(), var.ordinal()); } @@ -44,7 +44,7 @@ public class FFIVariablesCodeGen { System.out.println("\n\n// Update com.oracle.truffle.r.native/fficall/src/truffle_nfi/variables.c with the following: \n"); - System.out.println("// Generated by RFFIVariables.java:\n"); + System.out.printf("// Generated by %s\n\n", FFIVariablesCodeGen.class.getSimpleName()); for (RFFIVariables val : RFFIVariables.values()) { if (val == RFFIVariables.R_Interactive) { diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java index ca17d26f435d74daa2f00e0468baf057a3d9dc08..c7984a2e4953a5c1b80e11efebc6db76bc7f5180 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java @@ -740,6 +740,21 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { throw implementedAsNode(); } + @Override + public Object SETCADDR(Object x, Object y) { + throw implementedAsNode(); + } + + @Override + public Object SETCADDDR(Object x, Object y) { + throw implementedAsNode(); + } + + @Override + public Object SETCAD4R(Object x, Object y) { + throw implementedAsNode(); + } + @Override @TruffleBoundary public Object SYMVALUE(Object x) { @@ -1589,6 +1604,11 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { return new DotSymbol(name, new SymbolHandle(fun), numArgs); } + @Override + public Object getEmbeddingDLLInfo() { + return DLL.getEmbeddingDLLInfo(); + } + protected abstract Object setSymbol(DLLInfo dllInfo, int nstOrd, Object routines, int index); @Override @@ -1997,6 +2017,11 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { return new VectorWrapper(guaranteeVectorOrNull(x, CharSXPWrapper.class)); } + @Override + public void Rf_PrintValue(Object value) { + throw implementedAsNode(); + } + private static TruffleObject guaranteeVectorOrNull(Object obj, Class<? extends TruffleObject> clazz) { if (obj == RNull.instance) { return RNull.instance; diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java index 4c3835c1d68b5de9b9fd93ccccaa24471d3f9b1a..a5bfb6562e1749fc6b52805032bb57fef37eb5e6 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java @@ -24,16 +24,22 @@ package com.oracle.truffle.r.ffi.impl.nfi; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.ffi.CallRFFI.HandleUpCallExceptionNode; import com.oracle.truffle.r.runtime.ffi.DownCallNodeFactory.DownCallNode; import com.oracle.truffle.r.runtime.ffi.NativeFunction; public class HandleNFIUpCallExceptionNode extends Node implements HandleUpCallExceptionNode { @Child private DownCallNode setFlagNode = TruffleNFI_DownCallNodeFactory.INSTANCE.createDownCallNode(NativeFunction.set_exception_flag); + private final ConditionProfile isEmbeddedTopLevel = ConditionProfile.createBinaryProfile(); @Override @TruffleBoundary public void execute(Throwable originalEx) { + if (isEmbeddedTopLevel.profile(RContext.isEmbedded() && isTopLevel())) { + return; + } setFlagNode.call(); RuntimeException ex; if (originalEx instanceof RuntimeException) { @@ -43,4 +49,8 @@ public class HandleNFIUpCallExceptionNode extends Node implements HandleUpCallEx } TruffleNFI_Context.getInstance().setLastUpCallException(ex); } + + private static boolean isTopLevel() { + return ((TruffleNFI_Context) RContext.getInstance().getRFFI()).getCallDepth() == 0; + } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java index 379adb56840d43fc6fecbed3ea96407bed54f423..41cc4ea43f5faab05c0b2aae65c98427ec8a036f 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java @@ -172,6 +172,52 @@ public final class ListAccessNodes { } } + @TypeSystemReference(RTypes.class) + public static final class SETCADDRNode extends FFIUpCallNode.Arg2 { + @Child CDDRNode cddr = CDDRNode.create(); + @Child SETCARNode setcarNode = SETCARNode.create(); + + @Override + public Object executeObject(Object x, Object val) { + return setcarNode.executeObject(cddr.executeObject(x), val); + } + + public static SETCADDRNode create() { + return new SETCADDRNode(); + } + } + + @TypeSystemReference(RTypes.class) + public static final class SETCADDDRNode extends FFIUpCallNode.Arg2 { + @Child CDDDRNode cdddr = CDDDRNode.create(); + @Child SETCARNode setcarNode = SETCARNode.create(); + + @Override + public Object executeObject(Object x, Object val) { + return setcarNode.executeObject(cdddr.executeObject(x), val); + } + + public static SETCADDDRNode create() { + return new SETCADDDRNode(); + } + } + + @TypeSystemReference(RTypes.class) + public static final class SETCAD4RNode extends FFIUpCallNode.Arg2 { + @Child CDDDRNode cdddr = CDDDRNode.create(); + @Child CDRNode cdr = CDRNode.create(); + @Child SETCARNode setcarNode = SETCARNode.create(); + + @Override + public Object executeObject(Object x, Object val) { + return setcarNode.executeObject(cdddr.executeObject(cdr.executeObject(x)), val); + } + + public static SETCAD4RNode create() { + return new SETCAD4RNode(); + } + } + @TypeSystemReference(RTypes.class) public abstract static class SETCARNode extends FFIUpCallNode.Arg2 { public static SETCARNode create() { diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java index 79eecdc8078f5bff3db1aa1d48989e59ae8177eb..1b2b55205c58fd9e3de6a7f9463a2ea1a1d20cd1 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java @@ -52,11 +52,13 @@ import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctionsFactory.S import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.GetFunctionEnvironmentNode; import com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; +import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode; import com.oracle.truffle.r.nodes.objects.NewObject; import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen; import com.oracle.truffle.r.nodes.unary.CastNode; import com.oracle.truffle.r.nodes.unary.SizeToOctalRawNode; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.CharSXPWrapper; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RFunction; @@ -360,4 +362,12 @@ public final class MiscNodes { return OctSizeNodeGen.create(); } } + + public static final class RfPrintValueNode extends FFIUpCallNode.Arg1 { + @Override + public Object executeObject(Object value) { + RContext.getEngine().printResult(RContext.getInstance(), value); + return RNull.instance; + } + } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/RfEvalNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/RfEvalNode.java index 8a12776808ffefc28f31dc13e277846572425e11..4b795471d4427658e9c5ea5a0f73e1015fcdf12d 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/RfEvalNode.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/RfEvalNode.java @@ -26,12 +26,15 @@ import static com.oracle.truffle.r.runtime.RError.Message.ARGUMENT_NOT_ENVIRONME import static com.oracle.truffle.r.runtime.RError.Message.ARGUMENT_NOT_FUNCTION; import static com.oracle.truffle.r.runtime.RError.Message.UNKNOWN_OBJECT; +import org.graalvm.polyglot.Value; + import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.function.PromiseHelperNode; import com.oracle.truffle.r.runtime.ArgumentsSignature; @@ -51,11 +54,18 @@ import com.oracle.truffle.r.runtime.env.REnvironment; public abstract class RfEvalNode extends FFIUpCallNode.Arg2 { @Child private PromiseHelperNode promiseHelper; + private final ConditionProfile envIsNullProfile = ConditionProfile.createBinaryProfile(); public static RfEvalNode create() { return RfEvalNodeGen.create(); } + @Specialization + @TruffleBoundary + Object handlePromise(RPromise expr, @SuppressWarnings("unused") RNull nulLEnv) { + return getPromiseHelper().evaluate(null, expr); + } + @Specialization @TruffleBoundary Object handlePromise(RPromise expr, @SuppressWarnings("unused") REnvironment env) { @@ -64,20 +74,21 @@ public abstract class RfEvalNode extends FFIUpCallNode.Arg2 { @Specialization @TruffleBoundary - Object handleExpression(RExpression expr, REnvironment env) { - return RContext.getEngine().eval(expr, env, null); + Object handleExpression(RExpression expr, Object envArg) { + return RContext.getEngine().eval(expr, getEnv(envArg), null); } @Specialization @TruffleBoundary - Object handleLanguage(RLanguage expr, REnvironment env) { - return RContext.getEngine().eval(expr, env, null); + Object handleLanguage(RLanguage expr, Object envArg) { + return RContext.getEngine().eval(expr, getEnv(envArg), null); } @Specialization @TruffleBoundary - Object handleSymbol(RSymbol expr, REnvironment env) { - Object result = ReadVariableNode.lookupAny(expr.getName(), env.getFrame(), false); + Object handleSymbol(RSymbol expr, Object envArg, + @Cached("createClassProfile()") ValueProfile accessProfile) { + Object result = ReadVariableNode.lookupAny(expr.getName(), getEnv(envArg).getFrame(accessProfile), false); if (result == null) { throw RError.error(RError.NO_CALLER, UNKNOWN_OBJECT, expr.getName()); } @@ -85,18 +96,23 @@ public abstract class RfEvalNode extends FFIUpCallNode.Arg2 { } @Specialization - Object handlePairList(RPairList l, REnvironment env, + Object handlePairList(RPairList l, Object envArg, @Cached("createBinaryProfile()") ConditionProfile isPromiseProfile, @Cached("createBinaryProfile()") ConditionProfile noArgsProfile) { + REnvironment env = getEnv(envArg); Object car = l.car(); - RFunction f; + RFunction f = null; if (isPromiseProfile.profile(car instanceof RPromise)) { car = getPromiseHelper().evaluate(null, (RPromise) car); } if (car instanceof RFunction) { f = (RFunction) car; - } else { + } else if (car instanceof RSymbol) { + f = ReadVariableNode.lookupFunction(((RSymbol) car).getName(), env.getFrame()); + } + + if (f == null) { throw RError.error(RError.NO_CALLER, ARGUMENT_NOT_FUNCTION); } @@ -119,10 +135,21 @@ public abstract class RfEvalNode extends FFIUpCallNode.Arg2 { if (env instanceof REnvironment) { return expr; } else { + CompilerDirectives.transferToInterpreter(); throw RError.error(RError.NO_CALLER, ARGUMENT_NOT_ENVIRONMENT); } } + private REnvironment getEnv(Object envArg) { + if (envIsNullProfile.profile(envArg == RNull.instance)) { + return REnvironment.globalEnv(RContext.getInstance()); + } else if (envArg instanceof REnvironment) { + return (REnvironment) envArg; + } + CompilerDirectives.transferToInterpreter(); + throw RError.error(RError.NO_CALLER, ARGUMENT_NOT_ENVIRONMENT); + } + private PromiseHelperNode getPromiseHelper() { if (promiseHelper == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java index 7048ab7c49dadf6d4f58c299f97fdbf43f0a0c92..7783c59bf988b1f200e47ac52d89c47a7a878689 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java @@ -22,35 +22,67 @@ */ package com.oracle.truffle.r.ffi.impl.nodes; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.InteropException; 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.ffi.impl.interop.UnsafeAdapter; import com.oracle.truffle.r.runtime.RErrorHandling; +import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.data.RNull; public final class TryRfEvalNode extends FFIUpCallNode.Arg4 { - @Child RfEvalNode rfEvalNode = RfEvalNode.create(); - @Child Node writeErrorFlagNode = Message.WRITE.createNode(); + @Child private RfEvalNode rfEvalNode = RfEvalNode.create(); + @Child private Node isNullNode = Message.IS_NULL.createNode(); + @Child private Node writeErrorFlagNode; + @Child private Node isPointerNode; + @Child private Node asPointerNode; @Override public Object executeObject(Object expr, Object env, Object errorFlag, Object silent) { Object handlerStack = RErrorHandling.getHandlerStack(); Object restartStack = RErrorHandling.getRestartStack(); + boolean ok = true; + Object result = RNull.instance; try { // TODO handle silent RErrorHandling.resetStacks(); - return rfEvalNode.executeObject(expr, env); + result = rfEvalNode.executeObject(expr, env); } catch (Throwable t) { - try { - ForeignAccess.sendWrite(writeErrorFlagNode, (TruffleObject) errorFlag, 0, 1); - } catch (InteropException e) { - // Ignore it, when using NFI, e.g., the errorFlag TO does not support the WRITE - // message - } - return null; + ok = false; + result = RNull.instance; } finally { RErrorHandling.restoreStacks(handlerStack, restartStack); } + TruffleObject errorFlagTO = (TruffleObject) errorFlag; + if (!ForeignAccess.sendIsNull(isNullNode, errorFlagTO)) { + if (isPointerNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + isPointerNode = insert(Message.IS_POINTER.createNode()); + } + if (ForeignAccess.sendIsPointer(isPointerNode, errorFlagTO)) { + if (asPointerNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + asPointerNode = insert(Message.AS_POINTER.createNode()); + } + long errorFlagPtr; + try { + errorFlagPtr = ForeignAccess.sendAsPointer(asPointerNode, errorFlagTO); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); + } + UnsafeAdapter.UNSAFE.putInt(errorFlagPtr, ok ? 0 : 1); + } else { + try { + ForeignAccess.sendWrite(writeErrorFlagNode, errorFlagTO, 0, 1); + } catch (InteropException e) { + throw RInternalError.shouldNotReachHere("Rf_tryEval errorFlag should be either pointer or support WRITE message"); + } + } + } + return result; } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/DLLUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/DLLUpCallsRFFI.java index 336062d6f09c01b5b1d83d6506610ab78bcd0eee..3064448d1d7ac885ba5276918b88d1c7f0440699 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/DLLUpCallsRFFI.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/DLLUpCallsRFFI.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 @@ -71,4 +71,11 @@ public interface DLLUpCallsRFFI { @RFFICpointer Object getCCallable(@RFFICstring String pkgName, @RFFICstring String functionName); + /** + * Returns special {@link com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo} instance that is + * supposed to be used when registering symbols from within code embedding R, i.e. code that + * cannot have its init method called by R runtime and must call {@code R_registerRoutines} + * itself. + */ + Object getEmbeddingDLLInfo(); } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIUnwrapNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIUnwrapNode.java index 4d5aafa461450125c8692ffabfe9d208b9cf9bc2..40a3f13e3ff2d2b2492106e3b005741412ff937c 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIUnwrapNode.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/FFIUnwrapNode.java @@ -32,6 +32,7 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.data.NativeDataAccess; +import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RTruffleObject; /** @@ -64,6 +65,11 @@ public final class FFIUnwrapNode extends Node { } try { long address = ForeignAccess.sendAsPointer(asPointer, xTo); + if (address == 0) { + // Users are expected to use R_NULL, but at least when embedding, GNU R + // seems to be tolerant to NULLs. + return RNull.instance; + } return NativeDataAccess.lookup(address); } catch (UnsupportedMessageException e) { if (isPointerNode == null) { diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java index 3bb818f4b30721b643f928494f6f2f5dfa0ef0ce..7151f48559114fb8d46b16b17ced7a418ab8e13e 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java @@ -45,6 +45,9 @@ import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CDARNode; import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CDDDRNode; import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CDDRNode; import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CDRNode; +import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.SETCAD4RNode; +import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.SETCADDDRNode; +import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.SETCADDRNode; import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.SETCADRNode; import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.SETCARNode; import com.oracle.truffle.r.ffi.impl.nodes.MatchNodes; @@ -263,6 +266,15 @@ public interface StdUpCallsRFFI { @RFFIUpCallNode(SETCADRNode.class) Object SETCADR(Object x, Object y); + @RFFIUpCallNode(SETCADDRNode.class) + Object SETCADDR(Object x, Object y); + + @RFFIUpCallNode(SETCADDDRNode.class) + Object SETCADDDR(Object x, Object y); + + @RFFIUpCallNode(SETCAD4RNode.class) + Object SETCAD4R(Object x, Object y); + Object SYMVALUE(Object x); void SET_SYMVALUE(Object x, Object v); @@ -455,4 +467,6 @@ public interface StdUpCallsRFFI { @RFFIUpCallNode(MiscNodes.OctSizeNode.class) Object octsize(Object size); + @RFFIUpCallNode(MiscNodes.RfPrintValueNode.class) + void Rf_PrintValue(Object value); } 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 673d871727f2ad590e1fc44bb8e17fe7cc4ca23e..307b1d2fcd52091e8055fa8d94262cfc8684ea77 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 @@ -290,6 +290,7 @@ typedef SEXP (*call_R_ParseVector)(SEXP text, int n, SEXP srcFile); typedef SEXPTYPE (*call_Rf_str2type)(const char *s); typedef SEXP (*call_CLOENV)(SEXP closure); typedef SEXP (*call_octsize)(SEXP size); +typedef void (*call_Rf_PrintValue)(SEXP x); // connections @@ -314,6 +315,7 @@ typedef void * (*call_setDotSymbolValues)(DllInfo *dllInfo, char *name, DL_FUNC typedef int (*call_forceSymbols)(DllInfo *dllInfo, Rboolean value); typedef int (*call_registerCCallable)(const char *pkgname, const char *name, void *fun); typedef void* (*call_getCCallable)(const char *pkgname, const char *name); +typedef DllInfo* (*call_getEmbeddingDLLInfo)(void); // memory diff --git a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h index be731d8ff016ecebcd73df8124bfd0222f8a4ce6..8c6c12994411ddc5c7c0fa7a6e43e5ce5c922238 100644 --- a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h +++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h @@ -1,4 +1,4 @@ -// GENERATED; DO NOT EDIT +// GENERATED by com.oracle.truffle.r.ffi.processor.FFIProcessor class; DO NOT EDIT #ifndef RFFI_UPCALLSINDEX_H #define RFFI_UPCALLSINDEX_H @@ -94,103 +94,108 @@ #define Rf_GetOption1_x 89 #define Rf_NonNullStringMatch_x 90 #define Rf_PairToVectorList_x 91 -#define Rf_ScalarDouble_x 92 -#define Rf_ScalarInteger_x 93 -#define Rf_ScalarLogical_x 94 -#define Rf_ScalarString_x 95 -#define Rf_VectorToPairList_x 96 -#define Rf_allocArray_x 97 -#define Rf_allocMatrix_x 98 -#define Rf_allocVector_x 99 -#define Rf_any_duplicated_x 100 -#define Rf_asChar_x 101 -#define Rf_asCharacterFactor_x 102 -#define Rf_asInteger_x 103 -#define Rf_asLogical_x 104 -#define Rf_asReal_x 105 -#define Rf_classgets_x 106 -#define Rf_coerceVector_x 107 -#define Rf_cons_x 108 -#define Rf_copyListMatrix_x 109 -#define Rf_copyMatrix_x 110 -#define Rf_copyMostAttrib_x 111 -#define Rf_dchisq_x 112 -#define Rf_defineVar_x 113 -#define Rf_dnchisq_x 114 -#define Rf_dunif_x 115 -#define Rf_duplicate_x 116 -#define Rf_error_x 117 -#define Rf_errorcall_x 118 -#define Rf_eval_x 119 -#define Rf_findFun_x 120 -#define Rf_findVar_x 121 -#define Rf_findVarInFrame_x 122 -#define Rf_findVarInFrame3_x 123 -#define Rf_getAttrib_x 124 -#define Rf_gsetVar_x 125 -#define Rf_inherits_x 126 -#define Rf_install_x 127 -#define Rf_installChar_x 128 -#define Rf_isNull_x 129 -#define Rf_isString_x 130 -#define Rf_lengthgets_x 131 -#define Rf_match_x 132 -#define Rf_mkCharLenCE_x 133 -#define Rf_namesgets_x 134 -#define Rf_ncols_x 135 -#define Rf_nrows_x 136 -#define Rf_pchisq_x 137 -#define Rf_pnchisq_x 138 -#define Rf_protect_x 139 -#define Rf_punif_x 140 -#define Rf_qchisq_x 141 -#define Rf_qnchisq_x 142 -#define Rf_qunif_x 143 -#define Rf_rchisq_x 144 -#define Rf_rnchisq_x 145 -#define Rf_runif_x 146 -#define Rf_setAttrib_x 147 -#define Rf_str2type_x 148 -#define Rf_unprotect_x 149 -#define Rf_unprotect_ptr_x 150 -#define Rf_warning_x 151 -#define Rf_warningcall_x 152 -#define Rprintf_x 153 -#define SETCADR_x 154 -#define SETCAR_x 155 -#define SETCDR_x 156 -#define SET_BODY_x 157 -#define SET_CLOENV_x 158 -#define SET_FORMALS_x 159 -#define SET_NAMED_FASTR_x 160 -#define SET_RDEBUG_x 161 -#define SET_RSTEP_x 162 -#define SET_S4_OBJECT_x 163 -#define SET_STRING_ELT_x 164 -#define SET_SYMVALUE_x 165 -#define SET_TAG_x 166 -#define SET_TYPEOF_FASTR_x 167 -#define SET_VECTOR_ELT_x 168 -#define STRING_ELT_x 169 -#define SYMVALUE_x 170 -#define TAG_x 171 -#define TYPEOF_x 172 -#define UNSET_S4_OBJECT_x 173 -#define VECTOR_ELT_x 174 -#define forceSymbols_x 175 -#define getCCallable_x 176 -#define getConnectionClassString_x 177 -#define getOpenModeString_x 178 -#define getSummaryDescription_x 179 -#define isSeekable_x 180 -#define octsize_x 181 -#define registerCCallable_x 182 -#define registerRoutines_x 183 -#define restoreHandlerStacks_x 184 -#define setDotSymbolValues_x 185 -#define unif_rand_x 186 -#define useDynamicSymbols_x 187 +#define Rf_PrintValue_x 92 +#define Rf_ScalarDouble_x 93 +#define Rf_ScalarInteger_x 94 +#define Rf_ScalarLogical_x 95 +#define Rf_ScalarString_x 96 +#define Rf_VectorToPairList_x 97 +#define Rf_allocArray_x 98 +#define Rf_allocMatrix_x 99 +#define Rf_allocVector_x 100 +#define Rf_any_duplicated_x 101 +#define Rf_asChar_x 102 +#define Rf_asCharacterFactor_x 103 +#define Rf_asInteger_x 104 +#define Rf_asLogical_x 105 +#define Rf_asReal_x 106 +#define Rf_classgets_x 107 +#define Rf_coerceVector_x 108 +#define Rf_cons_x 109 +#define Rf_copyListMatrix_x 110 +#define Rf_copyMatrix_x 111 +#define Rf_copyMostAttrib_x 112 +#define Rf_dchisq_x 113 +#define Rf_defineVar_x 114 +#define Rf_dnchisq_x 115 +#define Rf_dunif_x 116 +#define Rf_duplicate_x 117 +#define Rf_error_x 118 +#define Rf_errorcall_x 119 +#define Rf_eval_x 120 +#define Rf_findFun_x 121 +#define Rf_findVar_x 122 +#define Rf_findVarInFrame_x 123 +#define Rf_findVarInFrame3_x 124 +#define Rf_getAttrib_x 125 +#define Rf_gsetVar_x 126 +#define Rf_inherits_x 127 +#define Rf_install_x 128 +#define Rf_installChar_x 129 +#define Rf_isNull_x 130 +#define Rf_isString_x 131 +#define Rf_lengthgets_x 132 +#define Rf_match_x 133 +#define Rf_mkCharLenCE_x 134 +#define Rf_namesgets_x 135 +#define Rf_ncols_x 136 +#define Rf_nrows_x 137 +#define Rf_pchisq_x 138 +#define Rf_pnchisq_x 139 +#define Rf_protect_x 140 +#define Rf_punif_x 141 +#define Rf_qchisq_x 142 +#define Rf_qnchisq_x 143 +#define Rf_qunif_x 144 +#define Rf_rchisq_x 145 +#define Rf_rnchisq_x 146 +#define Rf_runif_x 147 +#define Rf_setAttrib_x 148 +#define Rf_str2type_x 149 +#define Rf_unprotect_x 150 +#define Rf_unprotect_ptr_x 151 +#define Rf_warning_x 152 +#define Rf_warningcall_x 153 +#define Rprintf_x 154 +#define SETCAD4R_x 155 +#define SETCADDDR_x 156 +#define SETCADDR_x 157 +#define SETCADR_x 158 +#define SETCAR_x 159 +#define SETCDR_x 160 +#define SET_BODY_x 161 +#define SET_CLOENV_x 162 +#define SET_FORMALS_x 163 +#define SET_NAMED_FASTR_x 164 +#define SET_RDEBUG_x 165 +#define SET_RSTEP_x 166 +#define SET_S4_OBJECT_x 167 +#define SET_STRING_ELT_x 168 +#define SET_SYMVALUE_x 169 +#define SET_TAG_x 170 +#define SET_TYPEOF_FASTR_x 171 +#define SET_VECTOR_ELT_x 172 +#define STRING_ELT_x 173 +#define SYMVALUE_x 174 +#define TAG_x 175 +#define TYPEOF_x 176 +#define UNSET_S4_OBJECT_x 177 +#define VECTOR_ELT_x 178 +#define forceSymbols_x 179 +#define getCCallable_x 180 +#define getConnectionClassString_x 181 +#define getEmbeddingDLLInfo_x 182 +#define getOpenModeString_x 183 +#define getSummaryDescription_x 184 +#define isSeekable_x 185 +#define octsize_x 186 +#define registerCCallable_x 187 +#define registerRoutines_x 188 +#define restoreHandlerStacks_x 189 +#define setDotSymbolValues_x 190 +#define unif_rand_x 191 +#define useDynamicSymbols_x 192 -#define UPCALLS_TABLE_SIZE 188 +#define UPCALLS_TABLE_SIZE 193 #endif // RFFI_UPCALLSINDEX_H diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h index aaf82384b184450d1e136097cbfa4b44f8dd95e1..82bf7b8f64b6c1c2a002a7b769f31f757865f43d 100644 --- a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h +++ b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h @@ -490,7 +490,8 @@ Rboolean Rf_isObject(SEXP s) { void Rf_PrintValue(SEXP x) { TRACE0(); - unimplemented("Rf_PrintValue"); + ((call_Rf_PrintValue) callbacks[Rf_PrintValue_x])(x); + checkExitCall(); } SEXP Rf_install(const char *name) { @@ -770,19 +771,20 @@ SEXP SETCADR(SEXP x, SEXP y) { SEXP SETCADDR(SEXP x, SEXP y) { TRACE0(); - unimplemented("SETCADDR"); + // note: signature is same, we reuse call_SETCADR + SEXP result = ((call_SETCADR) callbacks[SETCADDR_x])(x, y); return NULL; } SEXP SETCADDDR(SEXP x, SEXP y) { TRACE0(); - unimplemented("SETCADDDR"); + SEXP result = ((call_SETCADR) callbacks[SETCADDDR_x])(x, y); return NULL; } -SEXP SETCAD4R(SEXP e, SEXP y) { +SEXP SETCAD4R(SEXP x, SEXP y) { TRACE0(); - unimplemented("SETCAD4R"); + SEXP result = ((call_SETCADR) callbacks[SETCAD4R_x])(x, y); return NULL; } @@ -1663,6 +1665,13 @@ R_registerRoutines(DllInfo *info, const R_CMethodDef * const croutines, return 1; } +DllInfo *R_getEmbeddingDllInfo() { + TRACE0(); + DllInfo *result = ((call_getEmbeddingDLLInfo) callbacks[getEmbeddingDLLInfo_x])(); + checkExitCall(); + return result; +} + Rboolean R_useDynamicSymbols(DllInfo *dllInfo, Rboolean value) { TRACE0(); Rboolean result = ((call_useDynamicSymbols) callbacks[useDynamicSymbols_x])(dllInfo, value); 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 001cdcd36e7696e20fc4fac4b948fb187a68ae95..770deeca724b7c9b8a1a43f6fea0058e6e6bbab6 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 @@ -11,6 +11,7 @@ */ #include <dlfcn.h> +#include <unistd.h> #include <sys/utsname.h> #include <sys/stat.h> #define R_INTERFACE_PTRS @@ -25,6 +26,8 @@ // (in single threaded mode) by FastR. #undef R_Interactive +#define PATH_BUF_LEN 255 + extern char **environ; static JavaVM *javaVM; @@ -78,17 +81,17 @@ static int initializeFastR(int argc, char *argv[], int setupRmainloop); // initializes R, but user is expected (TODO: probably) to invoke the main loop after that int Rf_initialize_R(int argc, char *argv[]) { - return initializeFastR(argc, argv, 0); + return initializeFastR(argc, argv, 0); } // initializes R and the main loop without running it int Rf_initEmbeddedR(int argc, char *argv[]) { - return initializeFastR(argc, argv, 1); + return initializeFastR(argc, argv, 1); } 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 + // None of the size params make any sense for FastR rs->R_Quiet = FALSE; rs->R_Slave = FALSE; rs->R_Interactive = TRUE; @@ -104,131 +107,139 @@ void R_DefParams(Rstart rs) { // Allows to set-up some params before the main loop is initialized // 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); + 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); } // Runs the main REPL loop void Rf_mainloop(void) { - JNIEnv *jniEnv = getEnv(); - setupOverrides(); - jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1); - (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod); + JNIEnv *jniEnv = getEnv(); + setupOverrides(); + jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1); + (*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod); } void R_Suicide(const char *s) { ptr_R_Suicide(s); } void Rf_endEmbeddedR(int fatal) { // TODO: invoke com.oracle.truffle.r.engine.shell.REmbedded#endRmainloop - (*javaVM)->DestroyJavaVM(javaVM); - //TODO fatal + (*javaVM)->DestroyJavaVM(javaVM); + //TODO fatal } static int initializeFastR(int argc, char *argv[], int setupRmainloop) { - 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"); + 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[PATH_BUF_LEN]; + 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); + } + } + strncpy(jvmlib_path, java_home, sizeof(jvmlib_path)); + if (strcmp(utsname.sysname, "Linux") == 0) { + char jvmlib_path_copy[PATH_BUF_LEN]; + strcpy(jvmlib_path_copy, jvmlib_path); + strcat(jvmlib_path, "/jre/lib/amd64/server/libjvm.so"); + if (access(jvmlib_path, F_OK) == -1) { + // with Java 9 the location does not contain arch name + strcpy(jvmlib_path, jvmlib_path_copy); + strcat(jvmlib_path, "/lib/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;Z)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); - } - if (setupRmainloop) { + // 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;Z)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); + } + if (setupRmainloop) { setupOverrides(); - } - // Can't TRACE this upcall as system not initialized - (*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray, setupRmainloop); - initialized++; - return 0; + } + // Can't TRACE this upcall as system not initialized + (*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray, setupRmainloop); + initialized++; + return 0; } // ----------------------------------------------------------------------------------------------- @@ -245,19 +256,19 @@ CTXT R_getGlobalFunctionContext() { } CTXT R_getParentFunctionContext(CTXT c) { - return ((call_R_getParentFunctionContext) callbacks[R_getParentFunctionContext_x])(c); + return ((call_R_getParentFunctionContext) callbacks[R_getParentFunctionContext_x])(c); } SEXP R_getContextEnv(CTXT c) { - return ((call_R_getContextEnv) callbacks[R_getContextEnv_x])(c); + return ((call_R_getContextEnv) callbacks[R_getContextEnv_x])(c); } SEXP R_getContextFun(CTXT c) { - return ((call_R_getContextFun) callbacks[R_getContextFun_x])(c); + return ((call_R_getContextFun) callbacks[R_getContextFun_x])(c); } SEXP R_getContextCall(CTXT c) { - return ((call_R_getContextCall) callbacks[R_getContextCall_x])(c); + return ((call_R_getContextCall) callbacks[R_getContextCall_x])(c); } SEXP R_getContextSrcRef(CTXT c) { @@ -273,7 +284,7 @@ int R_isGlobal(CTXT c) { } int R_isEqual(void* x, void* y) { - return ((call_R_isEqual) callbacks[R_isEqual_x])(x, y); + return ((call_R_isEqual) callbacks[R_isEqual_x])(x, y); } // ----------------------------------------------------------------------------------------------- @@ -316,120 +327,120 @@ char* rembedded_read_console(const char* prompt) { // Unimplemented API functions (to make the linker happy and as a TODO list) void R_SaveGlobalEnvToFile(const char *f) { - unimplemented("R_SaveGlobalEnvToFile"); + unimplemented("R_SaveGlobalEnvToFile"); } void uR_ShowMessage(const char *x) { - unimplemented("R_ShowMessage"); + unimplemented("R_ShowMessage"); } int uR_ReadConsole(const char *a, unsigned char *b, int c, int d) { - unimplemented("R_ReadConsole"); - return 0; + unimplemented("R_ReadConsole"); + return 0; } void uR_WriteConsole(const char *x, int y) { - unimplemented("R_WriteConsole"); + unimplemented("R_WriteConsole"); } void uR_WriteConsoleEx(const char *x, int y, int z) { - unimplemented("R_WriteConsole"); + unimplemented("R_WriteConsole"); } void uR_ResetConsole(void) { - unimplemented("R_ResetConsole"); + unimplemented("R_ResetConsole"); } void uR_FlushConsole(void) { - unimplemented("R_FlushConsole"); + unimplemented("R_FlushConsole"); } void uR_ClearerrConsole(void) { - unimplemented("R_ClearerrConsole"); + unimplemented("R_ClearerrConsole"); } void uR_Busy(int x) { - unimplemented("R_Busy"); + unimplemented("R_Busy"); } void R_SizeFromEnv(Rstart rs) { - unimplemented("R_SizeFromEnv"); + unimplemented("R_SizeFromEnv"); } void R_common_command_line(int *a, char **b, Rstart rs) { - unimplemented("R_common_command_line"); + unimplemented("R_common_command_line"); } void R_set_command_line_arguments(int argc, char **argv) { - unimplemented("R_set_command_line_arguments"); + unimplemented("R_set_command_line_arguments"); } int uR_ShowFiles(int a, const char **b, const char **c, - const char *d, Rboolean e, const char *f) { - unimplemented("R_ShowFiles"); - return 0; + const char *d, Rboolean e, const char *f) { + unimplemented("R_ShowFiles"); + return 0; } int uR_ChooseFile(int a, char *b, int c) { - unimplemented("R_ChooseFile"); - return 0; + unimplemented("R_ChooseFile"); + return 0; } int uR_EditFile(const char *a) { - unimplemented("R_EditFile"); - return 0; + unimplemented("R_EditFile"); + return 0; } void uR_loadhistory(SEXP a, SEXP b, SEXP c, SEXP d) { - unimplemented("uR_loadhistory"); + unimplemented("uR_loadhistory"); } void uR_savehistory(SEXP a, SEXP b, SEXP c, SEXP d) { - unimplemented("R_savehistory"); + unimplemented("R_savehistory"); } void uR_addhistory(SEXP a, SEXP b, SEXP c, SEXP d) { - unimplemented("R_addhistory"); + unimplemented("R_addhistory"); } int uR_EditFiles(int a, const char **b, const char **c, const char *d) { - unimplemented("uR_EditFiles"); - return 0; + unimplemented("uR_EditFiles"); + return 0; } SEXP udo_selectlist(SEXP a, SEXP b, SEXP c, SEXP d) { - return unimplemented("R_EditFiles"); + return unimplemented("R_EditFiles"); } SEXP udo_dataentry(SEXP a, SEXP b, SEXP c, SEXP d) { - return unimplemented("do_dataentry"); + return unimplemented("do_dataentry"); } SEXP udo_dataviewer(SEXP a, SEXP b, SEXP c, SEXP d) { - return unimplemented("do_dataviewer"); + return unimplemented("do_dataviewer"); } void uR_ProcessEvents(void) { - unimplemented("R_ProcessEvents"); + unimplemented("R_ProcessEvents"); } void uR_PolledEvents(void) { - unimplemented("R_PolledEvents"); + unimplemented("R_PolledEvents"); } void Rf_jump_to_toplevel() { - unimplemented("Rf_jump_to_toplevel"); + unimplemented("Rf_jump_to_toplevel"); } #include <R_ext/eventloop.h> fd_set *R_checkActivity(int usec, int ignore_stdin) { - unimplemented("R_checkActivity"); - return NULL; + unimplemented("R_checkActivity"); + return NULL; } void R_runHandlers(InputHandler *handlers, fd_set *mask) { - unimplemented("R_runHandlers"); + unimplemented("R_runHandlers"); } // ----------------------------------------------------------------------------------------------- @@ -450,10 +461,10 @@ void R_runHandlers(InputHandler *handlers, fd_set *mask) { // 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); + 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_CleanUp(SA_TYPE x, int y, int z) { @@ -471,7 +482,7 @@ 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; + 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; @@ -488,25 +499,25 @@ void (* R_PolledEvents)(void) = uR_PolledEvents; // This call cannot be made via callbacks array because it may be invoked before FastR is fully initialized. 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); - } + 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); + } } // ----------------------------------------------------------------------------------------------- @@ -515,17 +526,17 @@ void setupOverrides(void) { // 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; + 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; } #include <sys/types.h> @@ -534,17 +545,17 @@ static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) #include <errno.h> static void perror_exit(char *msg) { - perror(msg); - exit(1); + perror(msg); + exit(1); } 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; + 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; } static JNIEnv* getEnv() { @@ -583,102 +594,102 @@ static jclass checkFindClass(JNIEnv *env, const char *name) { // 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; - } + 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; + 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); + 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++; - } + 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.native/version.source b/com.oracle.truffle.r.native/version.source index e373ee695f6e76d7d3f8f8c4e92d1d60995352e5..fb1e7bc86996a80d4a16529b990adda1d3434c92 100644 --- a/com.oracle.truffle.r.native/version.source +++ b/com.oracle.truffle.r.native/version.source @@ -1 +1 @@ -50 +54 diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java index 34c497312c3d81afee5bd7d0c35da9d39266736d..4df7b6905fc49ccf0a9c85da1bd25854a7a25507 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java @@ -5,7 +5,7 @@ * * Copyright (c) 1995, 1996 Robert Gentleman and Ross Ihaka * Copyright (c) 1997-2013, The R Core Team - * Copyright (c) 2016, 2017, Oracle and/or its affiliates + * Copyright (c) 2016, 2018, Oracle and/or its affiliates * * All rights reserved. */ @@ -94,12 +94,13 @@ final class AttributesPrinter implements ValuePrinter<RAttributable> { int origLen = buff.length(); buff.append(tag); - if (RContext.getInstance().isMethodTableDispatchOn() && utils.isS4(a.getValue())) { + RContext ctx = RContext.getInstance(); + if (ctx.isMethodTableDispatchOn() && utils.isS4(a.getValue())) { S4ObjectPrinter.printS4(printCtx, a.getValue()); // throw new UnsupportedOperationException("TODO"); } else { if (a.getValue() instanceof RAttributable && ((RAttributable) a.getValue()).isObject()) { - RContext.getEngine().printResult(a.getValue()); + RContext.getEngine().printResult(ctx, a.getValue()); } else { ValuePrinters.INSTANCE.print(a.getValue(), printCtx); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java index 65694c0578f9cc5c94f3c3b15bddb268ab7e8056..4fa95a508ed285bc4b6c9a80ccd6183c0d5d2677 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java @@ -5,7 +5,7 @@ * * Copyright (c) 1995, 1996 Robert Gentleman and Ross Ihaka * Copyright (c) 1997-2013, The R Core Team - * Copyright (c) 2016, 2017, Oracle and/or its affiliates + * Copyright (c) 2016, 2018, Oracle and/or its affiliates * * All rights reserved. */ @@ -203,7 +203,7 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> { out.println(tagbuf); Object si = s.getDataAt(i); if (si instanceof RAttributable && ((RAttributable) si).isObject()) { - RContext.getEngine().printResult(si); + RContext.getEngine().printResult(RContext.getInstance(), si); } else { ValuePrinters.INSTANCE.print(si, printCtx); ValuePrinters.printNewLine(printCtx); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java index a3f5d4b98c8e8c6e2c16d0e21b4d104ae7c6390b..998f2e895d819e4bfaff297aaa4242f2361e9039 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java @@ -256,7 +256,7 @@ public interface Engine { * Used by Truffle debugger; invokes the internal "print" support in R for {@code value}. * Essentially this is equivalent to {@link #evalFunction} using the {@code "print"} function. */ - void printResult(Object value); + void printResult(RContext ctx, Object value); /** * Return the "global" frame for this {@link Engine}, aka {@code globalEnv}. 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 6f6fc756782041735e9d83cbf01d6e443dbd7286..1253a0a8091e54e81e3e8f2c06ff270a0cab5b43 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 @@ -267,7 +267,14 @@ public abstract class REnvironment extends RAttributeStorage { * Value returned by {@code globalenv()}. */ public static REnvironment globalEnv() { - return RContext.getInstance().stateREnvironment.getGlobalEnv(); + return globalEnv(RContext.getInstance()); + } + + /** + * Value returned by {@code globalenv()}. + */ + public static REnvironment globalEnv(RContext ctx) { + return ctx.stateREnvironment.getGlobalEnv(); } /** 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 07b6aeba98290ab63ad0ee0bd7cb834ca8575171..ce08cd08089df9aadf00819b673c56f4cce2a236 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 @@ -217,6 +217,14 @@ public class DLL { return result; } + /** + * Embedding {@link DLLInfo} is just a placeholder. It does not represent any concrete dll + * and thus we e.g. cannot find any symbols in it. + */ + public boolean isEmbeddingDllInfo() { + return handle == null; + } + public void setNativeSymbols(int nstOrd, DotSymbol[] symbols) { nativeSymbols[nstOrd] = symbols; } @@ -758,6 +766,7 @@ public class DLL { */ public static SymbolHandle findSymbol(String name, DLLInfo dllInfo) { if (dllInfo != null) { + assert !dllInfo.isEmbeddingDllInfo() : "Dynamic symbols lookup is not supported for the embedding DLLInfo"; return (SymbolHandle) DLLRFFI.DLSymRootNode.create(RContext.getInstance()).call(dllInfo.handle, name); } else { return (SymbolHandle) RFindSymbolRootNode.create(RContext.getInstance()).call(name, null, RegisteredNativeSymbol.any()); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java index bc6bf9f3a9bbcbccdcbf17657e064bcfd6713c4b..c5de3d2ae536710aa313255f05c5a950ddef56d7 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java @@ -126,7 +126,7 @@ public abstract class RFFIContext extends RFFI { cooperativeGc(); } - public int getCallDepth() { + public final int getCallDepth() { return callDepth; } diff --git a/com.oracle.truffle.r.test.native/Makefile b/com.oracle.truffle.r.test.native/Makefile index e19effc9d83aeaa4e6152ac5a9614d5f4c0c3480..87084580644d9b0d7fd047a4b27c874c02172321 100644 --- a/com.oracle.truffle.r.test.native/Makefile +++ b/com.oracle.truffle.r.test.native/Makefile @@ -37,7 +37,10 @@ all: $(MAKE) -C urand $(MAKE) -C packages ifneq ($(OSNAME), SunOS) -ifeq ($(FASTR_RFFI),jni) +ifeq ($(FASTR_RFFI),nfi) + $(MAKE) -C embedded +endif +ifeq ($(FASTR_RFFI),) $(MAKE) -C embedded endif endif @@ -46,7 +49,10 @@ clean: $(MAKE) -C urand clean $(MAKE) -C packages clean ifneq ($(OSNAME), SunOS) -ifeq ($(FASTR_RFFI),jni) +ifeq ($(FASTR_RFFI),nfi) + $(MAKE) -C embedded clean +endif +ifeq ($(FASTR_RFFI),) $(MAKE) -C embedded clean endif endif diff --git a/com.oracle.truffle.r.test.native/embedded/Makefile b/com.oracle.truffle.r.test.native/embedded/Makefile index f8e3d2ad094a917280195a05ea510467c47750ea..7fb216f542cbb2a6c467feb4b6314833927a764e 100644 --- a/com.oracle.truffle.r.test.native/embedded/Makefile +++ b/com.oracle.truffle.r.test.native/embedded/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 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 @@ -45,24 +45,28 @@ FASTR_LIB_DIR = $(abspath ../../lib) .PHONY: all clean -OBJ = lib +BIN = bin SRC = src C_SOURCES := $(wildcard $(SRC)/*.c) -C_OBJECTS := $(subst $(SRC),$(OBJ),$(C_SOURCES:.c=.o)) +C_OBJECTS := $(subst $(SRC),$(BIN),$(C_SOURCES:.c=.o)) INCLUDE_DIR := $(NATIVE_PROJECT)/include -all: $(OBJ)/main Makefile +all: $(BIN)/main $(BIN)/embedded Makefile -$(OBJ)/main: | $(OBJ) +$(BIN)/main: | $(BIN) +$(BIN)/embedded: | $(BIN) -$(OBJ): - mkdir -p $(OBJ) +$(BIN): + mkdir -p $(BIN) +$(BIN)/main: $(SRC)/main.c + $(CC) $(CFLAGS) -I$(INCLUDE_DIR) $< -o $(BIN)/main -L $(FASTR_LIB_DIR) -ldl -lR $(LD_FLAGS) -Wl,-rpath,$(FASTR_LIB_DIR) -$(OBJ)/main: $(SRC)/main.c - $(CC) $(CFLAGS) -I$(INCLUDE_DIR) $< -o $(OBJ)/main -L $(FASTR_LIB_DIR) -ldl -lR $(LD_FLAGS) -Wl,-rpath,$(FASTR_LIB_DIR) +$(BIN)/embedded: $(SRC)/embedded.c + $(CC) $(CFLAGS) -I$(INCLUDE_DIR) $< -o $(BIN)/embedded -L $(FASTR_LIB_DIR) -ldl -lR $(LD_FLAGS) -Wl,-rpath,$(FASTR_LIB_DIR) + cp $(SRC)/*.R $(BIN) clean: - rm -rf $(OBJ) + rm -rf $(BIN) diff --git a/com.oracle.truffle.r.test.native/embedded/src/embedded.c b/com.oracle.truffle.r.test.native/embedded/src/embedded.c new file mode 100644 index 0000000000000000000000000000000000000000..84d129e2e60419537abee49a0e88030dc519ea12 --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/embedded.c @@ -0,0 +1,207 @@ +/* + * 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. + */ + +// Simple program testing FastR embedded mode where R is initialized and then evaluation is controlled by the embedder +// See also main.c for example where R is initialized and then the R's REPL is run. + +// Note: some of the examples were taken from GNU R tests/Embedded directory and slightly adapted + +#include <stdlib.h> +#include <stdio.h> +#include <dlfcn.h> +#include <sys/utsname.h> +#include <string.h> +#define R_INTERFACE_PTRS 1 +#include <Rinterface.h> +#include <Rembedded.h> +#include <R_ext/RStartup.h> +#include <R_ext/Rdynload.h> + +#define CALLDEF(name, n) {#name, (DL_FUNC) &name, n} + +SEXP twice(SEXP x) { + int *xi = INTEGER(x); + int len = LENGTH(x); + SEXP res; + PROTECT(res = allocVector(INTSXP, len)); + int *resi = INTEGER(res); + for (int i = 0; i < len; ++i) { + resi[i] = xi[i] * 2; + } + UNPROTECT(1); + return res; +} + +static void checkError(int errorOccurred, const char* context) { + if (errorOccurred) { + printf("Unexpected error occurred when %s.\n", context); + exit(1); + } +} + +static void source(const char* file) { + FILE *f; + if (f = fopen(file, "r")){ + fclose(f); + } else { + printf("File '%s' is not accessible. Are you running the program from within a directory that contains this file, e.g. 'obj'?\n", file); + exit(1); + } + + SEXP e; + PROTECT(e = lang2(install("source"), mkString(file))); + printf("Sourcing '%s'...\n", file); + int errorOccurred; + R_tryEval(e, R_GlobalEnv, &errorOccurred); + UNPROTECT(1); + checkError(errorOccurred, "sourcing a file"); +} + +/* + Call the function foo() with 3 arguments, 2 of which + are named. + foo(pch="+", id = 123, c(T,F)) + + Note that PrintValue() of the expression seg-faults. + We have to set the print name correctly. +*/ + +static void bar1() { + SEXP fun, pch; + SEXP e; + + PROTECT(e = allocVector(LANGSXP, 4)); + fun = findFun(install("foo"), R_GlobalEnv); + if(fun == R_NilValue) { + printf("No definition for function foo. Source foo.R and save the session.\n"); + UNPROTECT(1); + exit(1); + } + SETCAR(e, fun); + + SETCADR(e, mkString("+")); + SET_TAG(CDR(e), install("pch")); + + SETCADDR(e, ScalarInteger(123)); + SET_TAG(CDR(CDR(e)), install("id")); + + pch = allocVector(LGLSXP, 2); + LOGICAL(pch)[0] = TRUE; + LOGICAL(pch)[1] = FALSE; + SETCADDDR(e, pch); + + printf("Printing the expression to be eval'ed...\n"); + PrintValue(e); + printf("Eval'ing the expression...\n"); + eval(e, R_GlobalEnv); + + SETCAR(e, install("foo")); + printf("Printing the expression to be tryEval'ed...\n"); + PrintValue(e); + printf("TryEval'ing the expression...\n"); + R_tryEval(e, R_GlobalEnv, NULL); + + UNPROTECT(1); +} + +int main(int argc, char **argv) { + setbuf(stdout, NULL); + char *r_home = getenv("R_HOME"); + if (r_home == NULL) { + printf("R_HOME must be set\n"); + exit(1); + } + printf("Initializing R with Rf_initEmbeddedR...\n"); + Rf_initEmbeddedR(argc, argv); + + // ------------------------------ + // tests/Embedded/Rerror.c + + /* + Evaluates the two expressions: + source("error.R") + and then calls foo() twice + where foo is defined in the file error.R + */ + SEXP e; + int errorOccurred; + source("error.R"); + + PROTECT(e = lang1(install("foo"))); + printf("Invoking foo() via tryEval..."); + R_tryEval(e, R_GlobalEnv, &errorOccurred); + printf("errorOccurred=%d\n", errorOccurred); + printf("Invoking foo() via tryEval once more..."); + R_tryEval(e, R_GlobalEnv, &errorOccurred); + printf("errorOccurred=%d\n", errorOccurred); + UNPROTECT(1); + + // ------------------------------ + // tests/Embedded/tryEval.c + + printf("Trying sqrt with wrong and then correct argument...\n"); + PROTECT(e = lang2(install("sqrt"), mkString(""))); + SEXP val = R_tryEval(e, NULL, &errorOccurred); + // Note: even the official example is not PROTECTing the val + if(errorOccurred) { + printf("Caught an error calling sqrt(). Try again with a different argument.\n"); + } + SETCAR(CDR(e), ScalarInteger(9)); + val = R_tryEval(e, NULL, &errorOccurred); + if(errorOccurred) { + printf("Caught another error calling sqrt()\n"); + } else { + Rf_PrintValue(val); + } + UNPROTECT(1); + + // ------------------------------ + // tests/Embedded/RNamedCall.c + + source("foo.R"); + printf("Calling foo with named arguments...\n"); + bar1(); + + // ------------------------------ + // Register custom native symbols and invoke them + + printf("Calling R_getEmbeddingDllInfo...\n"); + DllInfo *eDllInfo = R_getEmbeddingDllInfo(); + R_CallMethodDef CallEntries[] = { + CALLDEF(twice, 2), + {NULL, NULL, 0} + }; + R_registerRoutines(eDllInfo, NULL, CallEntries, NULL, NULL); + source("embedding.R"); + PROTECT(e = lang1(install("runTwice"))); + SEXP twiceRes = R_tryEval(e, R_GlobalEnv, &errorOccurred); + checkError(errorOccurred, "evaluating runTwice"); + UNPROTECT(1); + Rf_PrintValue(twiceRes); + + + Rf_endEmbeddedR(0); + printf("DONE\n"); + return 0; +} + diff --git a/com.oracle.truffle.r.test.native/embedded/src/embedded.expected.output b/com.oracle.truffle.r.test.native/embedded/src/embedded.expected.output new file mode 100644 index 0000000000000000000000000000000000000000..4a76493713e8e62a45def473e837b219739b5b95 --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/embedded.expected.output @@ -0,0 +1,75 @@ +Initializing R with Rf_initEmbeddedR... +Sourcing 'error.R'... +Invoking foo() via tryEval...Error in foo() : Stopping in function foo + [1] 1 2 3 4 5 6 7 8 9 10 +errorOccurred=1 +Invoking foo() via tryEval once more...Error in foo() : Stopping in function foo + [1] 1 2 3 4 5 6 7 8 9 10 +errorOccurred=1 +Trying sqrt with wrong and then correct argument... +Error in sqrt("") : non-numeric argument to mathematical function +Caught an error calling sqrt(). Try again with a different argument. +[1] 3 +Sourcing 'foo.R'... +Calling foo with named arguments... +Printing the expression to be eval'ed... +[[1]] +function(...) +{ + args <- list(...) + print(args) + print(names(args)) + TRUE +} + +$pch +[1] "+" + +$id +[1] 123 + +[[4]] +[1] TRUE FALSE + +Eval'ing the expression... +$pch +[1] "+" + +$id +[1] 123 + +[[3]] +[1] TRUE FALSE + +[1] "pch" "id" "" +Printing the expression to be tryEval'ed... +[[1]] +foo + +$pch +[1] "+" + +$id +[1] 123 + +[[4]] +[1] TRUE FALSE + +TryEval'ing the expression... +$pch +[1] "+" + +$id +[1] 123 + +[[3]] +[1] TRUE FALSE + +[1] "pch" "id" "" +Calling R_getEmbeddingDllInfo... +Sourcing 'embedding.R'... +getDLLRegisteredRoutines('(embedding)'): + .Call .Call.numParameters +1 twice 2 +[1] 2 4 6 8 10 +DONE diff --git a/com.oracle.truffle.r.test.native/embedded/src/embedding.R b/com.oracle.truffle.r.test.native/embedded/src/embedding.R new file mode 100644 index 0000000000000000000000000000000000000000..4170f8be6123e436e341c270c2e1a318a25309fa --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/embedding.R @@ -0,0 +1,6 @@ + +runTwice <- function() { + cat("getDLLRegisteredRoutines('(embedding)'):\n") + print(getDLLRegisteredRoutines("(embedding)")) + .Call(getDLLRegisteredRoutines("(embedding)")[[".Call"]][[1]], 1:5); +} \ No newline at end of file diff --git a/com.oracle.truffle.r.test.native/embedded/src/error.R b/com.oracle.truffle.r.test.native/embedded/src/error.R new file mode 100644 index 0000000000000000000000000000000000000000..3f4a2b61f4d26e38542a67e005755c4362603197 --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/error.R @@ -0,0 +1,6 @@ +foo <- +function() +{ + on.exit(print(1:10)) + stop("Stopping in function foo") +} diff --git a/com.oracle.truffle.r.test.native/embedded/src/foo.R b/com.oracle.truffle.r.test.native/embedded/src/foo.R new file mode 100644 index 0000000000000000000000000000000000000000..06ef4aa9ee43023ac42d8bd9b28df54aff052cc7 --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/foo.R @@ -0,0 +1,8 @@ +foo <- +function(...) +{ + args <- list(...) + print(args) + print(names(args)) + TRUE +} 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 9de0840477855701f938db79008498e274a1e27f..03a6f18fb3718fe4e13bd038b2fbf5e049e12409 100644 --- a/com.oracle.truffle.r.test.native/embedded/src/main.c +++ b/com.oracle.truffle.r.test.native/embedded/src/main.c @@ -21,8 +21,8 @@ * questions. */ -// A simple test program for FastR embedded mode. -// compile with "gcc -I include main.c -ldl +// A simple program testing FastR embedded mode use case where R is initialized and then the R's REPL is run. +// See embedded.c for example where R is initialized and then evaluation is controlled by the embedder #include <stdlib.h> #include <stdio.h> @@ -40,23 +40,23 @@ void (*ptr_stdR_CleanUp)(SA_TYPE, int, int); void (*ptr_stdR_Suicide)(const char *); void testR_CleanUp(SA_TYPE x, int y, int z) { - printf("test Cleanup\n"); - (ptr_stdR_CleanUp)(x, y, z); + printf("test Cleanup\n"); + (ptr_stdR_CleanUp)(x, y, z); } void testR_Suicide(const char *msg) { - printf("testR_Suicide: %s\n",msg); - (ptr_stdR_Suicide(msg)); + printf("testR_Suicide: %s\n",msg); + (ptr_stdR_Suicide(msg)); } int testR_ReadConsole(const char *prompt, unsigned char *buf, int len, int h) { - fputs(prompt, stdout); - fflush(stdout); /* make sure prompt is output */ - if (fgets((char *)buf, len, stdin) == NULL) { - return 0; - } else { - return 1; - } + fputs(prompt, stdout); + fflush(stdout); /* make sure prompt is output */ + if (fgets((char *)buf, len, stdin) == NULL) { + return 0; + } else { + return 1; + } } void testR_WriteConsole(const char *buf, int len) { @@ -65,31 +65,28 @@ void testR_WriteConsole(const char *buf, int len) { } int main(int argc, char **argv) { - char *r_home = getenv("R_HOME"); - if (r_home == NULL) { - 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; - ptr_stdR_Suicide = ptr_R_Suicide; - ptr_R_Suicide = &testR_Suicide; - ptr_R_ReadConsole = &testR_ReadConsole; - ptr_R_WriteConsole = &testR_WriteConsole; - // 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"); + char *r_home = getenv("R_HOME"); + if (r_home == NULL) { + 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; + ptr_stdR_Suicide = ptr_R_Suicide; + ptr_R_Suicide = &testR_Suicide; + ptr_R_ReadConsole = &testR_ReadConsole; + ptr_R_WriteConsole = &testR_WriteConsole; + 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.native/embedded/src/main.expected.output b/com.oracle.truffle.r.test.native/embedded/src/main.expected.output new file mode 100644 index 0000000000000000000000000000000000000000..05a230a88c724b4ef17b790c41450e669eb0c0c7 --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/main.expected.output @@ -0,0 +1,8 @@ +Initializing R with Rf_initialize_R... +Initializing R with R_SetParams... +Running R with Rf_mainloop... +TODO prompt> [1] 2 4 6 8 6 8 10 12 10 12 +Warning message: +In 1:4 + 1:10 : + longer object length is not a multiple of shorter object length +TODO prompt>test Cleanup diff --git a/com.oracle.truffle.r.test.native/embedded/src/main.input b/com.oracle.truffle.r.test.native/embedded/src/main.input new file mode 100644 index 0000000000000000000000000000000000000000..38d61667bff343282e28792db2349d30e1f9160e --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/src/main.input @@ -0,0 +1,2 @@ +1:4 + 1:10 +q('no') diff --git a/com.oracle.truffle.r.test.native/embedded/test.sh b/com.oracle.truffle.r.test.native/embedded/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..38f05584f494acff07adee9bf307abfa9a166f20 --- /dev/null +++ b/com.oracle.truffle.r.test.native/embedded/test.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# 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. +# + +set -e + +# Resolve the location of this script +source="${BASH_SOURCE[0]}" +while [ -h "$source" ] ; do + prev_source="$source" + source="$(readlink "$source")"; + if [[ "$source" != /* ]]; then + # if the link was relative, it was relative to where it came from + dir="$( cd -P "$( dirname "$prev_source" )" && pwd )" + source="$dir/$source" + fi +done +dir="$( cd -P "$( dirname "$source" )" && pwd )" + +: ${R_HOME?"R_HOME must point to FastR directory"} +: ${NFI_LIB?"NFI_LIB must point to libtrufflenfi.so located in mxbuild directory of Truffle"} + +echo "Testing 'main' embedding example..." +(cd $dir/bin; ./main -Dtruffle.nfi.library=$NFI_LIB --vanilla < $dir/src/main.input > $dir/main.actual.output 2>&1) +if ! diff -q $dir/main.actual.output $dir/src/main.expected.output > /dev/null 2>&1; then + echo "'main' embedding test failed" + echo "for details see $dir/main.actual.output $dir/src/main.expected.output" + echo "to run this test: mx rembedtest" + exit 1 +fi + +echo "Testing 'embedded' embedding example..." +(cd $dir/bin; ./embedded -Dtruffle.nfi.library=$NFI_LIB --vanilla > $dir/embedded.actual.output 2>&1) +if ! diff -q $dir/embedded.actual.output $dir/src/embedded.expected.output > /dev/null 2>&1; then + echo "'embedded' embedding test failed" + echo "for details see $dir/embedded.actual.output $dir/src/embedded.expected.output" + echo "to run this test: mx rembedtest" + exit 2 +fi + +echo "DONE" diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index f3632cb367c8863d7e1b0476ef95b63aa919962c..a0e848c66578e575121b6092beb8c70f51dfdcc5 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -101,6 +101,7 @@ com.oracle.truffle.r.native/gnur/patch/src/library/stats/src/ts.h,no.copyright com.oracle.truffle.r.native/gnur/patch/src/library/tools/src/tools_dummy.c,no.copyright com.oracle.truffle.r.native/gnur/patch/src/library/utils/src/utils_dummy.c,no.copyright com.oracle.truffle.r.native/run/R.sh,oracle_bash.copyright +com.oracle.truffle.r.test.native/embedded/test.sh,oracle_bash.copyright com.oracle.truffle.r.native/run/Rclasspath.sh,oracle_bash.copyright com.oracle.truffle.r.native/run/Rscript_exec.sh,oracle_bash.copyright com.oracle.truffle.r.native/run/Rscript.sh,oracle_bash.copyright diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py index fbf291a74311d8fd357fc407ae12bf294d71a50f..700ba883572d9bf829100793adebb5662751bb9e 100644 --- a/mx.fastr/mx_fastr.py +++ b/mx.fastr/mx_fastr.py @@ -245,8 +245,25 @@ def rrepl(args, nonZeroIsFatal=True, extraVmArgs=None): run_r(args, 'rrepl') def rembed(args, nonZeroIsFatal=True, extraVmArgs=None): + ''' + Runs pure Java program that simulates the embedding scenario doing the same up-calls as embedded would call. + ''' run_r(args, 'rembed') +def rembedtest(args, nonZeroIsFatal=False, extraVmArgs=None): + ''' + Runs simple R embedding API tests located in com.oracle.truffle.r.test.native/embedded. + The tests should be compiled by mx build before they can be run. + Each test (native application) is run and its output compared to the expected output + file located next to the source file. + ''' + env = os.environ.copy() + env['R_HOME'] = _fastr_suite.dir + so_suffix = '.dylib' if platform.system().lower() == 'darwin' else '.so' + env['NFI_LIB'] = join(mx.suite('truffle').get_output_root(platformDependent=True), 'truffle-nfi-native/bin/libtrufflenfi' + so_suffix) + tests_script = join(_fastr_suite.dir, 'com.oracle.truffle.r.test.native/embedded/test.sh') + return mx.run([tests_script], env=env, nonZeroIsFatal=nonZeroIsFatal) + def _fastr_gate_runner(args, tasks): ''' The specific additional gates tasks provided by FastR: @@ -277,6 +294,11 @@ def _fastr_gate_runner(args, tasks): if t: mx_unittest.unittest(_apps_unit_tests()) + with mx_gate.Task('Rembedded', tasks) as t: + if t: + if rembedtest([]) != 0: + t.abort("Rembedded tests failed") + mx_gate.add_gate_runner(_fastr_suite, _fastr_gate_runner) def rgate(args): @@ -553,6 +575,7 @@ _commands = { 'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error] [--maxSweeps=N] [--outMaxLev=N]'], 'rrepl' : [rrepl, '[options]'], 'rembed' : [rembed, '[options]'], + 'rembedtest' : [rembedtest, '[options]'], 'r-cp' : [r_classpath, '[options]'], 'pkgtest' : [mx_fastr_pkgs.pkgtest, ['options']], 'pkgtest-cmp' : [mx_fastr_pkgs.pkgtest_cmp, ['gnur_path fastr_path']], diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py index c8030673c77775a83b320ea3a8f62fa274307e95..d3b790160193fc0f5e3a94d2849209ec7f9d7f90 100644 --- a/mx.fastr/suite.py +++ b/mx.fastr/suite.py @@ -7,7 +7,7 @@ suite = { { "name" : "truffle", "subdir" : True, - "version" : "3bd8ec58a9318d99953b338967f6579d7a8dc140", + "version" : "3e12bec05f7a74f9fbdcd2d2f45f262492fd1306", "urls" : [ {"url" : "https://github.com/graalvm/graal", "kind" : "git"}, {"url" : "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind" : "binary"},