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 8ab81d07892bad11c616121f38c557f08bda981c..f8776dbb218a5030515d5565857f811ebbcaa00a 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 @@ -48,7 +48,6 @@ import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; -import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RCleanUp; @@ -70,7 +69,6 @@ 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; import com.oracle.truffle.r.runtime.data.CharSXPWrapper; -import com.oracle.truffle.r.runtime.data.Closure; import com.oracle.truffle.r.runtime.data.RAttributable; import com.oracle.truffle.r.runtime.data.RAttributesLayout; import com.oracle.truffle.r.runtime.data.RDataFactory; @@ -791,55 +789,9 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { return result; } - private static final Object processResult(Object value) { - if (value instanceof Integer) { - int v = (int) value; - return RDataFactory.createIntVector(new int[]{v}, RRuntime.isNA(v)); - } else if (value instanceof Double) { - double v = (double) value; - return RDataFactory.createDoubleVector(new double[]{v}, RRuntime.isNA(v)); - } else if (value instanceof Byte) { - byte v = (byte) value; - return RDataFactory.createLogicalVector(new byte[]{v}, RRuntime.isNA(v)); - } else if (value instanceof String) { - String v = (String) value; - return RDataFactory.createStringVector(new String[]{v}, RRuntime.isNA(v)); - } else { - return value; - } - } - @Override - @TruffleBoundary public Object Rf_eval(Object expr, Object env) { - guaranteeInstanceOf(env, REnvironment.class); - Object result; - if (expr instanceof RPromise) { - result = RContext.getRRuntimeASTAccess().forcePromise(null, expr); - } else if (expr instanceof RExpression) { - result = RContext.getEngine().eval((RExpression) expr, (REnvironment) env, RCaller.topLevel); - } else if (expr instanceof RLanguage) { - result = RContext.getEngine().eval((RLanguage) expr, (REnvironment) env, RCaller.topLevel); - } else if (expr instanceof RPairList) { - RPairList l = (RPairList) expr; - RFunction f = (RFunction) l.car(); - Object args = l.cdr(); - if (args == RNull.instance) { - result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, true, null, new Object[0]); - } else { - RList argsList = ((RPairList) args).toRList(); - result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), RCaller.topLevel, true, - ArgumentsSignature.fromNamesAttribute(argsList.getNames()), - argsList.getDataTemp()); - } - } else if (expr instanceof RSymbol) { - RSyntaxNode lookup = RContext.getASTBuilder().lookup(RSyntaxNode.LAZY_DEPARSE, ((RSymbol) expr).getName(), false); - result = RContext.getEngine().eval(RDataFactory.createLanguage(Closure.createLanguageClosure(lookup.asRNode())), (REnvironment) env, RCaller.topLevel); - } else { - // just return value - result = expr; - } - return processResult(result); + throw implementedAsNode(); } @Override 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 new file mode 100644 index 0000000000000000000000000000000000000000..2ed5333a7a15aa662b393b56384c94a3cd66d16b --- /dev/null +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/RfEvalNode.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.ffi.impl.nodes; + +import static com.oracle.truffle.r.runtime.RError.Message.ARGUMENT_NOT_ENVIRONMENT; +import static com.oracle.truffle.r.runtime.RError.Message.ARGUMENT_NOT_FUNCTION; +import static com.oracle.truffle.r.runtime.RError.Message.UNKNOWN_OBJECT; + +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.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.function.PromiseHelperNode; +import com.oracle.truffle.r.runtime.ArgumentsSignature; +import com.oracle.truffle.r.runtime.RCaller; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RExpression; +import com.oracle.truffle.r.runtime.data.RFunction; +import com.oracle.truffle.r.runtime.data.RLanguage; +import com.oracle.truffle.r.runtime.data.RList; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RPairList; +import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.RSymbol; +import com.oracle.truffle.r.runtime.env.REnvironment; + +public abstract class RfEvalNode extends FFIUpCallNode.Arg2 { + + @Child private PromiseHelperNode promiseHelper; + + public static RfEvalNode create() { + return RfEvalNodeGen.create(); + } + + @Specialization + @TruffleBoundary + Object handlePromise(RPromise expr, @SuppressWarnings("unused") REnvironment env) { + return getPromiseHelper().evaluate(null, expr); + } + + @Specialization + @TruffleBoundary + Object handleExpression(RExpression expr, REnvironment env) { + return RContext.getEngine().eval(expr, env, RCaller.topLevel); + } + + @Specialization + @TruffleBoundary + Object handleLanguage(RLanguage expr, REnvironment env) { + return RContext.getEngine().eval(expr, env, RCaller.topLevel); + } + + @Specialization + @TruffleBoundary + Object handleSymbol(RSymbol expr, REnvironment env) { + Object result = ReadVariableNode.lookupAny(expr.getName(), env.getFrame(), false); + if (result == null) { + throw RError.error(RError.NO_CALLER, UNKNOWN_OBJECT, expr.getName()); + } + return result; + } + + @Specialization + Object handlePairList(RPairList l, REnvironment env, + @Cached("createBinaryProfile()") ConditionProfile isPromiseProfile, + @Cached("createBinaryProfile()") ConditionProfile noArgsProfile) { + Object car = l.car(); + RFunction f; + if (isPromiseProfile.profile(car instanceof RPromise)) { + car = getPromiseHelper().evaluate(null, (RPromise) car); + } + + if (car instanceof RFunction) { + f = (RFunction) car; + } else { + throw RError.error(RError.NO_CALLER, ARGUMENT_NOT_FUNCTION); + } + + Object args = l.cdr(); + if (noArgsProfile.profile(args == RNull.instance)) { + return evalFunction(f, env, null); + } else { + RList argsList = ((RPairList) args).toRList(); + return evalFunction(f, env, ArgumentsSignature.fromNamesAttribute(argsList.getNames()), argsList.getDataTemp()); + } + } + + @TruffleBoundary + private Object evalFunction(RFunction f, REnvironment env, ArgumentsSignature argsNames, Object... args) { + return RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : env.getFrame(), RCaller.topLevel, true, argsNames, args); + } + + @Fallback + Object handleOthers(Object expr, Object env) { + if (env instanceof REnvironment) { + return expr; + } else { + throw RError.error(RError.NO_CALLER, ARGUMENT_NOT_ENVIRONMENT); + } + } + + private PromiseHelperNode getPromiseHelper() { + if (promiseHelper == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + promiseHelper = insert(new PromiseHelperNode()); + } + return promiseHelper; + } +} 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 a26fa2347b0f22e96558002764ce2c776b4ce37d..70dbd8d13a804fb8b7b1a3a095a449573f3aa841 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 @@ -50,6 +50,7 @@ import com.oracle.truffle.r.ffi.impl.nodes.MatchNodes; import com.oracle.truffle.r.ffi.impl.nodes.MiscNodes; import com.oracle.truffle.r.ffi.impl.nodes.MiscNodes.LENGTHNode; import com.oracle.truffle.r.ffi.impl.nodes.RandFunctionsNodes; +import com.oracle.truffle.r.ffi.impl.nodes.RfEvalNode; import com.oracle.truffle.r.ffi.processor.RFFICstring; import com.oracle.truffle.r.ffi.processor.RFFIRunGC; import com.oracle.truffle.r.ffi.processor.RFFIUpCallNode; @@ -253,6 +254,7 @@ public interface StdUpCallsRFFI { Object R_FindNamespace(Object name); @RFFIRunGC + @RFFIUpCallNode(RfEvalNode.class) Object Rf_eval(Object expr, Object env); Object Rf_findFun(Object symbolObj, Object envObj); 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 19e6f1d16a4c89b89f4fd647d639159d309f5a7d..ef15cc0dbd5cacdf878afcbe124123320488decb 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 @@ -304,7 +304,7 @@ public final class FFIProcessor extends AbstractProcessor { w.append(" }\n"); w.append(" RFFIContext ctx = RContext.getInstance().getStateRFFI();\n"); if (returnKind != TypeKind.VOID) { - w.append(" Object resultRObj;"); + w.append(" Object resultRObj;\n"); } w.append(" ctx.beforeUpcall(" + canRunGc + ");\n"); w.append(" try {\n"); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/TruffleRLanguage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/TruffleRLanguage.java index b7f120bf34bf4374b9209411baafbf05f697e303..93004fa0a9c32454acced25fe8579994214a9ca4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/TruffleRLanguage.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/TruffleRLanguage.java @@ -31,4 +31,10 @@ public abstract class TruffleRLanguage extends TruffleLanguage<RContext> { public abstract HashMap<String, RFunction> getBuiltinFunctionCache(); + @Override + protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { + // FastR does not support access to a single context from multiple threads, mainly because + // it has to maintain thread local variables on the native side. + return Thread.currentThread() == thread; + } } diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R index b4e7c16b80117cd3d128c809319d4f0170b720cf..214488fecfa13c364a9eddaf364dc30970e62758 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/R/testrffi.R @@ -192,3 +192,7 @@ rffi.createNativeConnection <- function() { rffi.parseVector <- function(x) { .Call('test_ParseVector', x); } + +rffi.RfEvalWithPromiseInPairList <- function() { + .Call('test_RfEvalWithPromiseInPairList') +} diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/init.c b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/init.c index 51107ad6a2b6e86f848f60a4238b2378d0235bb5..8e711c3bbd1b3bc16fe9264e0bf64c1de5528238 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/init.c +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/init.c @@ -81,6 +81,7 @@ static const R_CallMethodDef CallEntries[] = { CALLDEF(test_readConnection, 1), CALLDEF(test_createNativeConnection, 0), CALLDEF(test_ParseVector, 1), + CALLDEF(test_RfEvalWithPromiseInPairList, 0), {NULL, NULL, 0} }; diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c index 3a2abf0fd5eea5e7051f7b1027c1f0d59467e8c5..3e6edb7ee6e8f2879ee3a141e89a384faa3fab82 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.c @@ -540,3 +540,17 @@ SEXP test_ParseVector(SEXP src) { UNPROTECT(2); return result; } + +SEXP test_RfEvalWithPromiseInPairList() { + SEXP fun = Rf_findVarInFrame(R_FindNamespace(ScalarString(mkChar("stats"))), Rf_install("runif")); + if (TYPEOF(fun) != PROMSXP) { + printf("ERROR: Rf_findVarInFrame evaluated the promise!"); + } + SEXP e, ptr; + PROTECT(e = Rf_allocVector(LANGSXP, 2)); + SETCAR(e, fun); ptr = CDR(e); + SETCAR(ptr, ScalarInteger(5)); + SEXP result = Rf_eval(e, R_GlobalEnv); + UNPROTECT(1); + return result; +} diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.h b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.h index 48b2e53621cdbebfa0803079dd2930272781068d..2fbab660b916bb16973f4340025af10b5b585646 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.h +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/src/testrffi.h @@ -105,3 +105,5 @@ extern SEXP test_readConnection(SEXP conn); extern SEXP test_createNativeConnection(void); extern SEXP test_ParseVector(SEXP src); + +extern SEXP test_RfEvalWithPromiseInPairList(void); diff --git a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/tests/simpleTests.R b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/tests/simpleTests.R index c01e673ea3dfe0f557fa4ebf5e9e4cbf28b08bd1..e1fea9c3ccf24afc755177189c3ff422497f0e28 100644 --- a/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/tests/simpleTests.R +++ b/com.oracle.truffle.r.test.native/packages/testrffi/testrffi/tests/simpleTests.R @@ -92,3 +92,8 @@ for(i in seq(5000)) { obj <- preserved_objects[[i]] rffi.release_object(obj) } + +# Note: runif must not be used before this test so that it is still a promise!!! +# Following code calls Rf_eval with a language object that contains a promise instead of the expected function +set.seed(42) +rffi.RfEvalWithPromiseInPairList()