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 c5bcf37bf11496fab7af17ef4999ba125513e539..69e36da32770b378ff24172297974186c13d207d 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 @@ -168,10 +168,14 @@ public final class REngine implements RContext.Engine { * on return. * * @param function the actual function that invoked the "eval", e.g. {@code eval}, {@code evalq} - * , {@code local}. + * , {@code local}, or {@code null} if identification isn't important. */ public Object eval(RFunction function, RLanguage expr, REnvironment envir, REnvironment enclos) throws PutException { - RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.globalEnv()); + return eval(function, (RNode) expr.getRep(), envir, enclos); + } + + private static Object eval(RFunction function, RNode exprRep, REnvironment envir, @SuppressWarnings("unused") REnvironment enclos) throws PutException { + RootCallTarget callTarget = makeCallTarget(exprRep, REnvironment.globalEnv()); MaterializedFrame envFrame = envir.getFrame(); VirtualFrame vFrame = RRuntime.createVirtualFrame(); // We make the new frame look like it was a real call to "function". @@ -219,7 +223,18 @@ public final class REngine implements RContext.Engine { public Object evalPromise(RPromise expr, VirtualFrame frame) throws RError { RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.emptyEnv()); - return expr.setValue(runCall(callTarget, frame, false, false)); + return runCall(callTarget, frame, false, false); + } + + public Object evalPromise(RPromise promise) throws RError { + // have to do the full out eval + try { + return eval(lookupBuiltin("eval"), (RNode) promise.getRep(), promise.getEnv(), null); + } catch (PutException ex) { + // TODO a new, rather unlikely, error + assert false; + return null; + } } private static Object parseAndEvalImpl(ANTLRStringStream stream, Source source, VirtualFrame frame, REnvironment envForFrame, boolean printResult) { diff --git a/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib b/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib index 8d58a619d4c489fe40556023a711d7e24dc88d08..7357b08e5493dfd3624a3345209e9f84f6d9a3a4 100755 Binary files a/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib and b/com.oracle.truffle.r.native/lib/darwin/libRDerived.dylib differ diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java index 1ebdab86068c86ac7db3aaa4c301d9ecbdb059dc..1878e1c16fc049375d3b7ee3a4743cf05d8a21ae 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java @@ -97,6 +97,8 @@ public class EvalFunctions { @RBuiltin(name = "eval", nonEvalArgs = {0}, kind = SUBSTITUTE) public abstract static class Eval extends EvalAdapter { + public abstract Object execute(VirtualFrame frame, RPromise expr, REnvironment envir, RMissing enclos); + @Specialization public Object doEval(VirtualFrame frame, RPromise expr, @SuppressWarnings("unused") RMissing envir, RMissing enclos) { return doEval(frame, expr, REnvironment.globalEnv(), enclos); @@ -114,7 +116,7 @@ public class EvalFunctions { * caller, so we can evaluate the promise using frame. */ controlVisibility(); - Object exprVal = RContext.getEngine().evalPromise(expr, frame); + Object exprVal = expr.getValue(frame); return doEvalBody(exprVal, envir, enclos); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java index 6a6fcdc898e150df98859a6c3e9ebe74637b6abf..0cf58769a4ec1b7a83963a8503d5f8d4c353ebe1 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java @@ -45,7 +45,7 @@ public final class PromiseNode extends RNode { */ @Override public Object execute(VirtualFrame frame) { - return new RPromise(languageRep.getRep()); + return RDataFactory.createPromise(languageRep.getRep()); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java index 395daa1647b3263c0637acfd4f7256b43f3ad2fc..01a62746c56d19c2e62083946b923d08cb7038b5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java @@ -168,11 +168,17 @@ public final class RContext extends ExecutionContext { Object eval(RFunction function, RLanguage expr, REnvironment envir, REnvironment enclos) throws PutException; /** - * Evaluate a promise in the given frame (for a builtin, where we can use the - * {@link VirtualFrame}) of the caller directly). + * Evaluate a promise in the given frame, where we can use the {@link VirtualFrame}) of the + * caller directly). This should <b>only</b> be called by the {@link RPromise} class. */ Object evalPromise(RPromise expr, VirtualFrame frame) throws RError; + /** + * Evaluate a promise in the {@link MaterializedFrame} stored with the promise. This should + * <b>only</b> be called by the {@link RPromise} class. + */ + Object evalPromise(RPromise expr) throws RError; + } private final HashMap<Object, RFunction> cachedFunctions = new HashMap<>(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java index cd9a544d3845e59bb577ea2035321ef2e2067011..e3da591a9e3982366c8ef2cc020e8134bda75cd9 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java @@ -317,11 +317,19 @@ public final class RDataFactory { } public static RSymbol createSymbol(String name) { - return new RSymbol(name); + return traceDataCreated(new RSymbol(name)); } public static RLanguage createLanguage(Object rep) { - return new RLanguage(rep); + return traceDataCreated(new RLanguage(rep)); + } + + public static RPromise createPromise(Object rep, REnvironment env) { + return traceDataCreated(new RPromise(rep, env)); + } + + public static RPromise createPromise(Object rep) { + return createPromise(rep, null); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java index b1244a0a25ee37357312b674114855636fbd2180..3fb095a02370bbefbf676222a5ffd1ecd2d56e6b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java @@ -22,43 +22,73 @@ */ package com.oracle.truffle.r.runtime.data; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.r.runtime.*; + /** * Denotes an R {@code promise}. It extends {@link RLanguageRep} with a (lazily) evaluated value. */ @com.oracle.truffle.api.CompilerDirectives.ValueType public class RPromise extends RLanguageRep { /** - * Denotes a promise that raised an error during evaluation. + * For promises associated with environments (frames) that are not top-level. + */ + private REnvironment env; + /** + * When {@code null} the promise has not been evaluated. */ - private static Object ERROR = new Object(); - private Object value; /** - * Create the promise with a representation that allow evaluation later. + * Create the promise with a representation that allows evaluation later in the "current" frame. + * The frame may need to be set if the promise is passed as an argument to another function. */ - public RPromise(Object rep) { + RPromise(Object rep) { + this(rep, null); + } + + /** + * Create the promise with a representation that allows evaluation later in a given frame. + */ + RPromise(Object rep, REnvironment env) { super(rep); + this.env = env; + } + + public REnvironment getEnv() { + return env; } /** - * This is a workaround for the fact that REngine can't be called from here (at the moment), - * otherwise the evaluation would be implicitly done in {@link #getValue}. + * Get the value of the promise, evaluating it if necessary in the associated environment. A + * promise is evaluate-once. */ - public Object setValue(Object newValue) { + public Object getValue() { if (value == null) { - if (newValue == null) { - this.value = ERROR; - } else { - this.value = newValue; + assert env != null; + try { + value = RContext.getEngine().evalPromise(this); + } catch (RError e) { + value = e; + throw e; } - } else { - assert false : "promise already has a value"; } - return this.value; + return value; } - public Object getValue() { + /** + * Get the value of the promise, evaluating it if necessary in the given {@link VirtualFrame}. A + * promise is evaluate-once. + */ + public Object getValue(VirtualFrame frame) { + if (value == null) { + try { + value = RContext.getEngine().evalPromise(this, frame); + } catch (RError e) { + value = e; + throw e; + } + } return value; }