diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java index 640c4308b6dc1686f1c4c43788086b109215f0f0..b96edfab2806d17ceeee1e9b0baad9407961d119 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RNode.java @@ -162,6 +162,14 @@ public abstract class RNode extends Node { return RTypesGen.RTYPES.expectRSymbol(execute(frame)); } + public RLanguage executeRLanguage(VirtualFrame frame) throws UnexpectedResultException { + return RTypesGen.RTYPES.expectRLanguage(execute(frame)); + } + + public RPromise executeRPromise(VirtualFrame frame) throws UnexpectedResultException { + return RTypesGen.RTYPES.expectRPromise(execute(frame)); + } + public RAbstractContainer executeRAbstractContainer(VirtualFrame frame) throws UnexpectedResultException { return RTypesGen.RTYPES.expectRAbstractContainer(execute(frame)); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java index cbcdc50064be449ae2f5fd852f784df407e26304..77e518bfc4f8f328562d0a1266178bd7f1f07346 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RProxyNode.java @@ -217,7 +217,7 @@ public abstract class RProxyNode extends RNode { } @Specialization - protected REnvironment wrap(REnvironment x) { + public REnvironment wrap(REnvironment x) { return proxy(x); } @@ -226,7 +226,7 @@ public abstract class RProxyNode extends RNode { } @Specialization - protected RConnection wrap(RConnection x) { + public RConnection wrap(RConnection x) { return proxy(x); } @@ -235,7 +235,7 @@ public abstract class RProxyNode extends RNode { } @Specialization - protected RExpression wrap(RExpression x) { + public RExpression wrap(RExpression x) { return proxy(x); } @@ -244,7 +244,7 @@ public abstract class RProxyNode extends RNode { } @Specialization - protected RSymbol wrap(RSymbol x) { + public RSymbol wrap(RSymbol x) { return proxy(x); } @@ -252,8 +252,25 @@ public abstract class RProxyNode extends RNode { return (RSymbol) proxyScalar(x); } + public RLanguage wrap(RLanguage x) { + return proxy(x); + } + + protected RLanguage proxy(RLanguage x) { + return (RLanguage) proxyScalar(x); + } + + @Specialization + public RPromise wrap(RPromise x) { + return proxy(x); + } + + protected RPromise proxy(RPromise x) { + return (RPromise) proxyScalar(x); + } + @Specialization - protected Object[] wrap(Object[] x) { + public Object[] wrap(Object[] x) { return proxy(x); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java index ddf2c8fec52a5def5ef4c63964d33f4e65282093..9dd3bc8e9ec6357e81fc624e096beb76e60771d7 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTypes.java @@ -39,7 +39,8 @@ import com.oracle.truffle.r.runtime.data.model.*; @TypeSystem({byte.class, int.class, double.class, RRaw.class, RComplex.class, String.class, RIntSequence.class, RDoubleSequence.class, RIntVector.class, RDoubleVector.class, RRawVector.class, RComplexVector.class, RStringVector.class, RLogicalVector.class, RFunction.class, RNull.class, RMissing.class, REnvironment.class, RExpression.class, RConnection.class, MaterializedFrame.class, FrameSlot.class, RAbstractIntVector.class, RAbstractDoubleVector.class, RAbstractLogicalVector.class, RAbstractComplexVector.class, - RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RDataFrame.class, RSymbol.class, RAbstractContainer.class, Object[].class}) + RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RDataFrame.class, RSymbol.class, RPromise.class, RLanguage.class, RAbstractContainer.class, + Object[].class}) public class RTypes { @TypeCheck diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltin.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltin.java index f6df073e3a10c19114c2d222745316baf27c4c8c..2c0a0068da25d7c22e988e31d052b2d56141fe16 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltin.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltin.java @@ -27,14 +27,39 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) public @interface RBuiltin { + /** + * The "kind" of the builtin. + */ RBuiltinKind kind(); + /** + * The name of the builtin function in the R language. + */ String name(); + /** + * A list of aliases for {@code name()}. + */ String[] aliases() default {}; + /** + * Some primitives do not evaluate one or more of their arguments. This is either a list of + * indices for the non-evaluated arguments (zero based) or {@code -1} to mean none are + * evaluated. An empty array means all arguments are evaluated. N.B. The indices identify the + * arguments in the order they appear in the specification, i.e., after the re-ordering of named + * arguments. + * + */ + int[] nonEvalArgs() default {}; + + /** + * Special magic relating to "..." arguments. TODO Say more. + */ boolean isCombine() default false; + /** + * How is the last parameter treated? TODO Say more. + */ LastParameterKind lastParameterKind() default LastParameterKind.VALUE; public enum LastParameterKind { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java index 6b17a8307682982936e08c16a439dbc8fa9082e8..e61eb12e8436ff818a2ed13d59512dab157fafa3 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java @@ -107,16 +107,20 @@ public abstract class RBuiltinNode extends RCallNode implements VisibilityContro } protected RBuiltin getRBuiltin() { - GeneratedBy generatedBy = getClass().getAnnotation(GeneratedBy.class); - return generatedBy.value().getAnnotation(RBuiltin.class); + return getRBuiltin(getClass()); } - private static RBuiltinNode createNode(RBuiltinFactory factory, RNode[] builtinArguments, String[] argNames) { - RBuiltin rBuiltin = null; - GeneratedBy generatedBy = factory.getFactory().getClass().getAnnotation(GeneratedBy.class); + private static RBuiltin getRBuiltin(Class<?> klass) { + GeneratedBy generatedBy = klass.getAnnotation(GeneratedBy.class); if (generatedBy != null) { - rBuiltin = generatedBy.value().getAnnotation(RBuiltin.class); + return generatedBy.value().getAnnotation(RBuiltin.class); + } else { + return null; } + } + + private static RBuiltinNode createNode(RBuiltinFactory factory, RNode[] builtinArguments, String[] argNames) { + RBuiltin rBuiltin = getRBuiltin(factory.getFactory().getClass()); boolean isCombine = rBuiltin == null ? false : rBuiltin.isCombine(); Object[] args = new Object[(isCombine ? 3 : 2) + factory.getConstantArguments().length]; int index = 0; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java index 5ac31d530faf63a1bccd5872f7afa078ad9e898c..15ca85788dc63592d3a5131edf5b2ef4b3df16a9 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java @@ -119,11 +119,11 @@ public abstract class RBuiltinPackage { return builtins; } - public void loadSources(VirtualFrame frame) { + public void loadSources(VirtualFrame frame, REnvironment envForFrame) { List<Component> sources = rSources.get(getName()); if (sources != null) { for (Component src : sources) { - REngine.parseAndEval(src.libContents, frame, false); + REngine.parseAndEval(src.libContents, frame, envForFrame, false); } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java index 9de8df042e6c3e51bcb2a219db968c7ea7746ef1..95f1597a512a919c22f03013bf6df03971fe8856 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java @@ -48,6 +48,28 @@ public final class RBuiltinRootNode extends RRootNode { return builtin.inline(args); } + public boolean evaluatesArgs() { + RBuiltin rBuiltin = builtin.getRBuiltin(); + return rBuiltin == null || rBuiltin.nonEvalArgs().length == 0; + } + + public boolean evalArg(int index) { + RBuiltin rBuiltin = builtin.getRBuiltin(); + if (rBuiltin == null) { + return true; + } else { + int[] nonEvalArgs = rBuiltin.nonEvalArgs(); + for (int i = 0; i < nonEvalArgs.length; i++) { + int ix = nonEvalArgs[i]; + if (ix < 0 || ix == index) { + return false; + } + } + return true; + } + + } + @Override public String getSourceCode() { return builtin.getSourceCode(); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RDefaultBuiltinPackages.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RDefaultBuiltinPackages.java index 6b8111b70379e6a9eb8cdf4c51db0af55b7501e4..f22e0ef2a7d8b96b3657dc1d8a1ccb8e4cacad9b 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RDefaultBuiltinPackages.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RDefaultBuiltinPackages.java @@ -44,7 +44,7 @@ public final class RDefaultBuiltinPackages extends RBuiltinPackages { return instance; } - public static void load(String name, VirtualFrame frame) { + public static void load(String name, VirtualFrame frame, REnvironment envForFrame) { RBuiltinPackage pkg = null; switch (name) { case "debug": @@ -60,6 +60,6 @@ public final class RDefaultBuiltinPackages extends RBuiltinPackages { Utils.fail("unknown builtin package: " + name); } instance.add(pkg); - pkg.loadSources(frame); + pkg.loadSources(frame, envForFrame); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java index 854f3c9b7c9f62dcfb8704489428ffc36a7bbf29..76a44138105db992401b3cedcec5de0290dc6ca7 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/REngine.java @@ -68,7 +68,7 @@ public final class REngine implements RBuiltinLookupProvider { * @param crashOnFatalErrorArg if {@code true} any unhandled exception will terminate the * process. * @return a {@link VirtualFrame} that can be passed to - * {@link #parseAndEval(String, VirtualFrame, boolean)} + * {@link #parseAndEval(String, VirtualFrame, REnvironment, boolean)} */ public static VirtualFrame initialize(String[] commandArgs, ConsoleHandler consoleHandler, boolean crashOnFatalErrorArg, boolean headless) { startTime = System.nanoTime(); @@ -88,16 +88,16 @@ public final class REngine implements RBuiltinLookupProvider { ROptions.initialize(); RProfile.initialize(); // eval the system profile - REngine.parseAndEval(RProfile.systemProfile(), baseFrame, false); + REngine.parseAndEval(RProfile.systemProfile(), baseFrame, REnvironment.baseEnv(), false); REnvironment.packagesInitialize(RPackages.initialize()); RPackageVariables.initialize(); // TODO replace with R code String siteProfile = RProfile.siteProfile(); if (siteProfile != null) { - REngine.parseAndEval(siteProfile, baseFrame, false); + REngine.parseAndEval(siteProfile, baseFrame, REnvironment.baseEnv(), false); } String userProfile = RProfile.userProfile(); if (userProfile != null) { - REngine.parseAndEval(userProfile, globalFrame, false); + REngine.parseAndEval(userProfile, globalFrame, REnvironment.globalEnv(), false); } return globalFrame; } @@ -124,22 +124,25 @@ public final class REngine implements RBuiltinLookupProvider { } /** - * Parse and evaluate {@code rscript}. Value of {@code globalFrame} may be null. If - * {@code printResult == true}, the result of the evaluation is printed to the console. + * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the result + * of the evaluation is printed to the console. + * + * @param envForFrame the environment that {@code frame} is bound to. + * @return the object returned by the evaluation or {@code null} if an error occurred. */ - public static Object parseAndEval(String rscript, VirtualFrame globalFrame, boolean printResult) { - return parseAndEvalImpl(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<shell_input>"), globalFrame, printResult); + public static Object parseAndEval(String rscript, VirtualFrame frame, REnvironment envForFrame, boolean printResult) { + return parseAndEvalImpl(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<shell_input>"), frame, envForFrame, printResult); } /** * - * This is intended for use by the unit test environment, where a fresh global environment is + * This is intended for use by the unit test environment, where a "fresh" global environment is * desired for each evaluation. */ public static Object parseAndEvalTest(String rscript, boolean printResult) { VirtualFrame frame = RRuntime.createVirtualFrame(); REnvironment.resetForTest(frame); - return parseAndEvalImpl(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<shell_input>"), frame, printResult); + return parseAndEvalImpl(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<test_input>"), frame, REnvironment.globalEnv(), printResult); } public static class ParseException extends Exception { @@ -150,36 +153,99 @@ public final class REngine implements RBuiltinLookupProvider { } } - public static Object[] parse(String rscript) throws ParseException { + /** + * Parse an R expression and return an {@link RExpression} object representing the Truffle ASTs + * for the components. + */ + public static RExpression parse(String rscript) throws ParseException { try { Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<parse_input>")); ASTNode[] exprs = seq.getExprs(); - RExpression[] result = new RExpression[exprs.length]; + Object[] data = new Object[exprs.length]; for (int i = 0; i < exprs.length; i++) { - result[i] = new RExpression(exprs[i]); + data[i] = RDataFactory.createLanguage(transform(exprs[i], REnvironment.emptyEnv())); } - return result; + return RDataFactory.createExpression(RDataFactory.createList(data)); } catch (RecognitionException ex) { throw new ParseException(ex.getMessage()); } } - public static Object eval(RExpression expr, REnvironment envir, @SuppressWarnings("unused") REnvironment enclos) throws PutException { - RootCallTarget callTarget = transformToCallTarget((ASTNode) expr.getASTNode(), envir); - // to evaluate this we must create a new VirtualFrame - VirtualFrame frame = RRuntime.createVirtualFrame(); - Object result = runGlobal(callTarget, frame, false); - // now copy the values into the environment we were supposed to evaluate this in. - FrameDescriptor fd = frame.getFrameDescriptor(); - for (FrameSlot slot : fd.getSlots()) { - envir.put(slot.getIdentifier().toString(), frame.getValue(slot)); + /** + * Support for the {@code eval} builtin using an {@link RExpression}. + */ + public static Object eval(RExpression expr, REnvironment envir, REnvironment enclos) throws PutException { + Object result = null; + for (int i = 0; i < expr.getLength(); i++) { + result = eval((RLanguage) expr.getDataAt(i), envir, enclos); + } + return result; + } + + /** + * Support for the {@code eval} builtin. This is tricky because the {@link Frame} "f" associated + * with {@code envir} has been materialized so we can't evaluate in it directly. Instead we + * create a new {@link VirtualFrame}, that is a logical clone of "f", evaluate in that, and then + * update "f" on return. + */ + public static Object eval(RLanguage expr, REnvironment envir, @SuppressWarnings("unused") REnvironment enclos) throws PutException { + RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.globalEnv()); + MaterializedFrame envFrame = envir.getFrame(); + VirtualFrame vFrame = RRuntime.createVirtualFrame(); + RArguments.setEnclosingFrame(vFrame, envFrame); + RArguments.setEnvironment(vFrame, envir); + FrameDescriptor envfd = envFrame.getFrameDescriptor(); + FrameDescriptor vfd = vFrame.getFrameDescriptor(); + // Copy existing bindings + for (FrameSlot slot : envfd.getSlots()) { + FrameSlotKind slotKind = slot.getKind(); + FrameSlot vFrameSlot = vfd.addFrameSlot(slot.getIdentifier(), slotKind); + try { + switch (slotKind) { + case Byte: + vFrame.setByte(vFrameSlot, envFrame.getByte(slot)); + break; + case Int: + vFrame.setInt(vFrameSlot, envFrame.getInt(slot)); + break; + case Double: + vFrame.setDouble(vFrameSlot, envFrame.getDouble(slot)); + break; + case Object: + vFrame.setObject(vFrameSlot, envFrame.getObject(slot)); + break; + case Illegal: + break; + default: + throw new FrameSlotTypeException(); + } + } catch (FrameSlotTypeException ex) { + throw new RuntimeException("unexpected FrameSlot exception", ex); + } + + } + Object result = runCall(callTarget, vFrame, false); + if (result != null) { + FrameDescriptor fd = vFrame.getFrameDescriptor(); + for (FrameSlot slot : fd.getSlots()) { + envir.put(slot.getIdentifier().toString(), vFrame.getValue(slot)); + } } return result; } - private static Object parseAndEvalImpl(ANTLRStringStream stream, Source source, VirtualFrame globalFrame, boolean printResult) { + /** + * Evaluate a promise in the given frame (for a builtin, where we can use the + * {@link VirtualFrame}) of the caller directly). + */ + public static Object evalPromise(RPromise expr, VirtualFrame frame) { + RootCallTarget callTarget = makeCallTarget((RNode) expr.getRep(), REnvironment.emptyEnv()); + return expr.setValue(runCall(callTarget, frame, false)); + } + + private static Object parseAndEvalImpl(ANTLRStringStream stream, Source source, VirtualFrame frame, REnvironment envForFrame, boolean printResult) { try { - return runGlobal(parseToCallTarget(stream, source), globalFrame, printResult); + return runCall(makeCallTarget(parseToRNode(stream, source), envForFrame), frame, printResult); } catch (RecognitionException | RuntimeException e) { context.getConsoleHandler().println("Exception while parsing: " + e); e.printStackTrace(); @@ -187,24 +253,59 @@ public final class REngine implements RBuiltinLookupProvider { } } - private static RootCallTarget parseToCallTarget(ANTLRStringStream stream, Source source) throws RecognitionException { - return transformToCallTarget(ParseUtil.parseAST(stream, source), REnvironment.globalEnv()); + /** + * Parses a text stream into a Truffle AST. + * + * @param stream + * @param source + * @return the root node of the Truffle AST + * @throws RecognitionException on parse error + */ + private static RNode parseToRNode(ANTLRStringStream stream, Source source) throws RecognitionException { + return transform(ParseUtil.parseAST(stream, source), REnvironment.globalEnv()); } - private static RootCallTarget transformToCallTarget(ASTNode astNode, REnvironment environment) { + /** + * Transforms an AST produced by the parser into a Truffle AST. + * + * @param astNode parser AST instance + * @param environment the lexically enclosing environment that will be associated with top-level + * function definitions in {@code astNode} + * @return the root node of the Truffle AST + */ + private static RNode transform(ASTNode astNode, REnvironment environment) { RTruffleVisitor transform = new RTruffleVisitor(environment); - RNode node = transform.transform(astNode); - REnvironment.FunctionDefinition rootNodeEnvironment = new REnvironment.FunctionDefinition(REnvironment.globalEnv()); + return transform.transform(astNode); + } + + /** + * Wraps the Truffle AST in {@code node} in an anonymous function and returns a + * {@link RootCallTarget} for it. + * + * @param node + * @param enclosing the enclosing environment to use for the anonymous function (value probably + * does not matter) + */ + private static RootCallTarget makeCallTarget(RNode node, REnvironment enclosing) { + REnvironment.FunctionDefinition rootNodeEnvironment = new REnvironment.FunctionDefinition(enclosing); FunctionDefinitionNode rootNode = new FunctionDefinitionNode(null, rootNodeEnvironment, node, RArguments.EMPTY_OBJECT_ARRAY, "<main>", true); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode); return callTarget; } - private static Object runGlobal(RootCallTarget callTarget, VirtualFrame globalFrame, boolean printResult) { + /** + * Execute {@code callTarget} in {@code frame}, optionally printing any result. N.B. + * {@code callTarget.call} will create a new {@link VirtualFrame} called, say, {@code newFrame}, + * in which to execute the (anonymous) {@link FunctionDefinitionNode} associated with + * {@code callTarget}. When execution reaches {@link FunctionDefinitionNode#execute}, + * {@code frame} will be accessible via {@code newFrame.getArguments()[0]}, and the execution + * will continue using {@code frame}. + */ + private static Object runCall(RootCallTarget callTarget, VirtualFrame frame, boolean printResult) { Object result = null; try { try { - result = callTarget.call(globalFrame); + result = callTarget.call(frame); } catch (ControlFlowException cfe) { throw RError.getNoLoopForBreakNext(null); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java index e92fdd0ac81532fee4bde8a0e4e5abd3ef750a6c..bce004f6f0a7fd6840b8cb41929390365fc95850 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java @@ -97,7 +97,7 @@ public abstract class AsCharacter extends RBuiltinNode { @Specialization public String doSymbol(VirtualFrame frame, RSymbol value) { controlVisibility(); - return value.getValue(); + return value.getName(); } @Specialization 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 new file mode 100644 index 0000000000000000000000000000000000000000..ab281cd7961e6302d837d752fca32a324dc4e5bc --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.base; + +import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.nodes.access.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.REnvironment.PutException; +import com.oracle.truffle.r.runtime.data.*; + +/** + * {@code eval}, {@code evalq} and {@code local} are implemented as SUBSTITUTEs to ensure that the + * expression argument is not evaluated which, currently, is only possible for builtins. + * + */ +public class EvalFunctions { + public abstract static class EvalAdapter extends RBuiltinNode { + protected static final String[] PARAMETER_NAMES = new String[]{"expr", "envir", "enclosing"}; + + @Override + public Object[] getParameterNames() { + return PARAMETER_NAMES; + } + + @Override + public RNode[] getParameterValues() { + return new RNode[]{ConstantNode.create(RMissing.instance), ConstantNode.create(RMissing.instance), ConstantNode.create(RMissing.instance)}; + } + + protected Object error(String msg) throws RError { + CompilerDirectives.transferToInterpreter(); + throw RError.getGenericError(getEncapsulatingSourceSection(), msg); + } + + protected Object doEvalBody(Object exprArg, REnvironment envir, @SuppressWarnings("unused") RMissing enclos) { + Object expr = exprArg; + if (expr instanceof RSymbol) { + // We have to turn the string value back into a RLanguage object + expr = RDataFactory.createLanguage(ReadVariableNode.create(((RSymbol) expr).getName(), false)); + } + if (expr instanceof RExpression || expr instanceof RLanguage) { + try { + Object result; + if (expr instanceof RExpression) { + result = REngine.eval((RExpression) expr, envir, REnvironment.emptyEnv()); + } else { + result = REngine.eval((RLanguage) expr, envir, REnvironment.emptyEnv()); + } + if (result == null) { + RContext.setVisible(false); + result = RNull.instance; + } + return result; + } catch (PutException x) { + CompilerDirectives.transferToInterpreter(); + throw RError.getGenericError(getEncapsulatingSourceSection(), x.getMessage()); + } + } else { + // just return value + return expr; + } + } + } + + @RBuiltin(name = "eval", nonEvalArgs = {0}, kind = SUBSTITUTE) + public abstract static class Eval extends EvalAdapter { + + @Specialization + public Object doEval(VirtualFrame frame, RPromise expr, @SuppressWarnings("unused") RMissing envir, RMissing enclos) { + return doEval(frame, expr, REnvironment.globalEnv(), enclos); + } + + @Specialization + public Object doEval(VirtualFrame frame, RPromise expr, REnvironment envir, RMissing enclos) { + /* + * In the current hack expr is always a RPromise because we specified noEval for it in + * the RBuiltin The spec for eval says that expr is evaluated in the current scope (aka + * the caller's frame) so we do that first by evaluating the RPromise. Then we eval the + * result, which could do another eval, e.g. "eval(expression(1+2))" + * + * Note that builtins do not have a separate VirtualFrame, they use the frame of the + * caller, so we can evaluate the promise using frame. + */ + controlVisibility(); + Object exprVal = REngine.evalPromise(expr, frame); + return doEvalBody(exprVal, envir, enclos); + } + + } + + @RBuiltin(name = "evalq", nonEvalArgs = {0}, kind = SUBSTITUTE) + public abstract static class EvalQuote extends EvalAdapter { + @Specialization + public Object doEval(RPromise expr, @SuppressWarnings("unused") RMissing envir, RMissing enclos) { + return doEval(expr, REnvironment.globalEnv(), enclos); + } + + @Specialization + public Object doEval(RPromise expr, REnvironment envir, RMissing enclos) { + /* + * evalq does not evaluate it's first argument + */ + controlVisibility(); + return doEvalBody(expr, envir, enclos); + } + + } + + @RBuiltin(name = "local", nonEvalArgs = {0}, kind = SUBSTITUTE) + public abstract static class Local extends EvalAdapter { + @SuppressWarnings("hiding") protected static final String[] PARAMETER_NAMES = new String[]{"expr", "envir"}; + + @Override + public Object[] getParameterNames() { + return PARAMETER_NAMES; + } + + @Override + public RNode[] getParameterValues() { + return new RNode[]{ConstantNode.create(RMissing.instance), ConstantNode.create(RMissing.instance)}; + } + + @Specialization + public Object doEval(VirtualFrame frame, RPromise expr, @SuppressWarnings("unused") RMissing envir, RMissing enclos) { + return doEval(expr, new REnvironment.NewEnv(EnvFunctions.frameToEnvironment(frame), 0), enclos); + } + + @Specialization + public Object doEval(RPromise expr, REnvironment envir, RMissing enclos) { + /* + * local does not evaluate it's first argument + */ + controlVisibility(); + return doEvalBody(expr, envir, enclos); + } + + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java new file mode 100644 index 0000000000000000000000000000000000000000..10c7b25f541b467f89608124fe888dff03fed118 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.base; + +import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.nodes.builtin.RBuiltin.*; +import com.oracle.truffle.r.runtime.data.*; + +@RBuiltin(name = "expression", kind = PRIMITIVE, nonEvalArgs = {-1}, lastParameterKind = LastParameterKind.VAR_ARGS_ALWAYS_ARRAY) +public abstract class Expression extends RBuiltinNode { + private static final String[] PARAMETER_NAMES = new String[]{"..."}; + + @Override + public Object[] getParameterNames() { + return PARAMETER_NAMES; + } + + @Specialization + public Object doExpression(Object[] args) { + RList list = RDataFactory.createList(args); + return RDataFactory.createExpression(list); + } + + @Specialization + public Object doExpression(RLanguage language) { + RList list = RDataFactory.createList(new Object[]{language}); + return RDataFactory.createExpression(list); + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsExpression.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsExpression.java new file mode 100644 index 0000000000000000000000000000000000000000..1c95cf6f4908c37b04c33e257a0b40610560b8c8 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsExpression.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.base; + +import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; + +@RBuiltin(name = "is.expression", kind = PRIMITIVE) +public abstract class IsExpression extends IsTypeNode { + + @Override + @Specialization + public byte isType(RExpression expr) { + return RRuntime.LOGICAL_TRUE; + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsLanguage.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsLanguage.java new file mode 100644 index 0000000000000000000000000000000000000000..a65bc7a397b5dc9671db64e9d603af7fde539f6b --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsLanguage.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.builtin.base; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; + +@RBuiltin(name = "is.language", kind = RBuiltinKind.PRIMITIVE) +public abstract class IsLanguage extends IsTypeNode { + @Override + @Specialization + public byte isType(RSymbol value) { + return RRuntime.LOGICAL_TRUE; + } + + @Override + @Specialization + public byte isType(RExpression value) { + return RRuntime.LOGICAL_TRUE; + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeNode.java index 72b62c8860eb167f65913738d72f2ca15022f815..55705d0b32de0e4ec4eb5f75e312602ffe49b373 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeNode.java @@ -158,6 +158,21 @@ public abstract class IsTypeNode extends RBuiltinNode { return RRuntime.LOGICAL_FALSE; } + @Specialization + public byte isType(RLanguage value) { + return RRuntime.LOGICAL_FALSE; + } + + @Specialization + public byte isType(RPromise value) { + return RRuntime.LOGICAL_FALSE; + } + + @Specialization + public byte isType(RExpression value) { + return RRuntime.LOGICAL_FALSE; + } + @Specialization public byte isType(Object value) { controlVisibility(); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java index a66cc0b767084f5821b8aa538a6eb4bec9e5e0b3..9e640568c2d896566c1cc567fb167b1c463c50d8 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java @@ -43,5 +43,10 @@ public class NamespaceFunctions { } } + @Specialization + public Object doGetRegisteredNamespace(RSymbol name) { + controlVisibility(); + return doGetRegisteredNamespace(name.getName()); + } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java index e5e038c2d1929fa42e26f2c77bcef51622191525..15de4679025f9af8db0bb0b5ed61cc9f743f2aa2 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java @@ -25,19 +25,34 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*; import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.nodes.access.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.data.*; /** * Placeholder. {@code on.exit} is special (cf {@code .Internal} in that {@code expr} is not - * evaluated, but {@code add} is. + * evaluated, but {@code add} is. TODO arrange for the {@code expr} be stored with the currently + * evaluating function using a new slot in {@link RArguments} and run it on function exit. */ -@RBuiltin(name = "on.exit", kind = PRIMITIVE) +@RBuiltin(name = "on.exit", nonEvalArgs = {0}, kind = PRIMITIVE) public abstract class OnExit extends RInvisibleBuiltinNode { + private static final String[] PARAMETER_NAMES = new String[]{"expr", "add"}; + + @Override + public Object[] getParameterNames() { + return PARAMETER_NAMES; + } + + @Override + public RNode[] getParameterValues() { + return new RNode[]{ConstantNode.create(RNull.instance), ConstantNode.create(false)}; + } + @Specialization - public Object onExit(@SuppressWarnings("unused") Object expr, @SuppressWarnings("unused") Object add) { + public Object onExit(@SuppressWarnings("unused") RLanguage expr, @SuppressWarnings("unused") RLanguage add) { controlVisibility(); RContext.getInstance().setEvalWarning("on.exit ignored"); return RNull.instance; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java index bcf10fadd4d82262b6397fdc3884bb963739d30e..f712b2540629f3e85e34cd6c3ff7b670dac6a39f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java @@ -44,19 +44,56 @@ import com.oracle.truffle.r.runtime.data.*; @RBuiltin(name = "parse", kind = INTERNAL) public abstract class Parse extends RInvisibleBuiltinNode { + private Object error(String msg) throws RError { + CompilerDirectives.transferToInterpreter(); + throw RError.getGenericError(getEncapsulatingSourceSection(), msg); + } + + @SuppressWarnings("unused") + @Specialization(order = 0) + public Object parse(RConnection conn, RNull n, RNull text, String prompt, RNull srcFile, String encoding) { + controlVisibility(); + try { + String[] lines = conn.readLines(0); + return doParse(coalesce(lines)); + } catch (IOException | ParseException ex) { + throw RError.getGenericError(getEncapsulatingSourceSection(), "parse error"); + } + } + @SuppressWarnings("unused") - @Specialization - public Object parse(RConnection file, double n, RNull text, String prompt, RNull srcFile, String encoding) { + @Specialization(order = 1) + public Object parse(RConnection conn, double n, RNull text, String prompt, RNull srcFile, String encoding) { controlVisibility(); try { - String[] lines = file.readLines(0); - Object[] exprs = REngine.parse(coalesce(lines)); - return RDataFactory.createList(exprs); + String[] lines = conn.readLines((int) n); + return doParse(coalesce(lines)); } catch (IOException | ParseException ex) { throw RError.getGenericError(getEncapsulatingSourceSection(), "parse error"); } } + @SuppressWarnings("unused") + @Specialization(order = 2, guards = "isText") + public Object parse(RConnection conn, RNull n, String text, String prompt, RNull srcFile, String encoding) { + controlVisibility(); + try { + return doParse(text); + } catch (ParseException ex) { + return error("parse error"); + } + } + + @SuppressWarnings("unused") + public static boolean isText(RConnection conn, RNull n, String text, String prompt, RNull srcFile, String encoding) { + return text.length() > 0; + } + + @SlowPath + private static RExpression doParse(String script) throws ParseException { + return REngine.parse(script); + } + @SlowPath private static String coalesce(String[] lines) { StringBuffer sb = new StringBuffer(); @@ -67,11 +104,4 @@ public abstract class Parse extends RInvisibleBuiltinNode { return sb.toString(); } - @SuppressWarnings("unused") - @Specialization(order = 100) - public Object parseGeneric(Object file, Object n, Object text, Object prompt, Object srcFile, Object encoding) { - controlVisibility(); - CompilerDirectives.transferToInterpreter(); - throw RError.getGenericError(getEncapsulatingSourceSection(), "invalid arguments"); - } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java index 88037cd3fd2454755228f7c171683e7e87e724be..5f05cac3edea1e410a817b3449396c4dd0e78903 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java @@ -168,10 +168,6 @@ public abstract class PrettyPrinterNode extends RNode { return RRuntime.quoteString(operand); } - public static String prettyPrint(RSymbol operand) { - return operand.getValue(); - } - @Specialization(order = 50) public String prettyPrintVector(RRaw operand, Object listElementName) { return concat("[1] ", prettyPrint(operand)); @@ -181,12 +177,12 @@ public abstract class PrettyPrinterNode extends RNode { return operand.toString(); } - @Specialization + @Specialization(order = 60) public String prettyPrint(RFunction operand, Object listElementName) { return ((RRootNode) operand.getTarget().getRootNode()).getSourceCode(); } - @Specialization + @Specialization(order = 70) public String prettyPrint(VirtualFrame frame, REnvironment operand, Object listElementName) { RAttributes attributes = operand.getAttributes(); if (attributes == null) { @@ -200,6 +196,23 @@ public abstract class PrettyPrinterNode extends RNode { } } + @Specialization(order = 80) + public String prettyPrint(VirtualFrame frame, RExpression expr, Object listElementName) { + // TODO extract source of the elements + return "expression"; + } + + @Specialization(order = 85) + public String prettyPrintSymbol(RSymbol operand, Object listElementName) { + return operand.getName(); + } + + @Specialization(order = 86) + public String prettyPrint(VirtualFrame frame, RLanguage expr, Object listElementName) { + // TODO extract source of the element + return "language element"; + } + private String printAttributes(VirtualFrame frame, RAbstractVector vector, RAttributes attributes) { StringBuilder builder = new StringBuilder(); for (RAttribute attr : attributes) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java similarity index 66% rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java index 160868dc312d1d46596349fdbb5934add70f5957..e98994c03271f769f248535015dac971c46ea000 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Eval.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java @@ -22,34 +22,37 @@ */ package com.oracle.truffle.r.nodes.builtin.base; -import static com.oracle.truffle.r.nodes.builtin.RBuiltinKind.*; - import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.nodes.access.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.REnvironment.PutException; import com.oracle.truffle.r.runtime.data.*; -@RBuiltin(name = "eval", kind = INTERNAL) -public abstract class Eval extends RBuiltinNode { +@RBuiltin(name = "quote", nonEvalArgs = {0}, kind = RBuiltinKind.PRIMITIVE) +public abstract class Quote extends RBuiltinNode { + private static final String[] PARAMETER_NAMES = new String[]{"expr"}; - @Specialization - public Object doEval(RExpression expr, REnvironment envir, REnvironment enclos) { - controlVisibility(); - try { - return REngine.eval(expr, envir, enclos); - } catch (PutException x) { - throw RError.getGenericError(getEncapsulatingSourceSection(), x.getMessage()); - } + @Override + public Object[] getParameterNames() { + return PARAMETER_NAMES; + } + + @Override + public RNode[] getParameterValues() { + return new RNode[]{ConstantNode.create(RMissing.instance)}; } - @SuppressWarnings("unused") @Specialization - public Object doEval(Object expr, Object envr, Object enclos) { - controlVisibility(); + public RLanguage doQuote(@SuppressWarnings("unused") RMissing arg) { CompilerDirectives.transferToInterpreter(); - throw RError.getGenericError(getEncapsulatingSourceSection(), "invalid arguments"); + throw RError.getZ1ArgumentsPassed(getEncapsulatingSourceSection(), getRBuiltin().name()); } + @Specialization + public RLanguage doQuote(RLanguage arg) { + controlVisibility(); + return arg; + } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/eval.R b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/eval.R index 0c5192d7ad7b80b4c89676d7a873b02acfa1b6e8..726244797592a0f79e5000df528ef50336fe2afb 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/eval.R +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/R/eval.R @@ -19,21 +19,23 @@ .GlobalEnv <- environment() parent.frame <- function(n = 1) .Internal(parent.frame(n)) -eval <- - function(expr, envir = parent.frame(), - enclos = if(is.list(envir) || is.pairlist(envir)) - parent.frame() else baseenv()) - .Internal(eval(expr, envir, enclos)) +# currently implemented as a direct builtin +#eval <- +# function(expr, envir = parent.frame(), +# enclos = if(is.list(envir) || is.pairlist(envir)) +# parent.frame() else baseenv()) +# .Internal(eval(expr, envir, enclos)) eval.parent <- function(expr, n = 1) { p <- parent.frame(n + 1) eval(expr, p) } -evalq <- - function (expr, envir = parent.frame(), enclos = if (is.list(envir) || - is.pairlist(envir)) parent.frame() else baseenv()) - .Internal(eval(substitute(expr), envir, enclos)) +# currently implemented as a direct builtin +#evalq <- +# function (expr, envir = parent.frame(), enclos = if (is.list(envir) || +# is.pairlist(envir)) parent.frame() else baseenv()) +# .Internal(eval(substitute(expr), envir, enclos)) # currently implemented as a direct builtin #new.env <- function (hash = TRUE, parent = parent.frame(), size = 29L) @@ -45,9 +47,10 @@ parent.env <- function(env) `parent.env<-` <- function(env, value) .Internal("parent.env<-"(env, value)) -local <- - function (expr, envir = new.env()) - eval.parent(substitute(eval(quote(expr), envir))) +# currently implemented as a direct builtin +#local <- +# function (expr, envir = new.env()) +# eval.parent(substitute(eval(quote(expr), envir))) # Implemented as direct builtin #Recall <- function(...) .Internal(Recall(...)) diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java index d481dc76e3315a82d662dfe6e9b3de5ce3fa9a3a..c81d32f2599ac29d51cf6a9019f0c2201e5f9eff 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java @@ -140,6 +140,24 @@ public abstract class Typeof extends RBuiltinNode { return "symbol"; } + @Specialization() + public String typeof(RLanguage language) { + controlVisibility(); + return "language"; + } + + @Specialization() + public String typeof(RPromise promise) { + controlVisibility(); + return "promise"; + } + + @Specialization() + public String typeof(RExpression symbol) { + controlVisibility(); + return "expression"; + } + @Specialization(order = 100, guards = "isFunctionBuiltin") public String typeofBuiltin(RFunction obj) { controlVisibility(); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java index 4be0aeba052f8c8ce93e7a755e78b6114aa83f9d..a9e57bb24b883f6227834accc249dae5183ea44c 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java @@ -18,7 +18,9 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.nodes.*; import com.oracle.truffle.r.nodes.access.*; import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.nodes.builtin.base.UseMethodFactory.*; import com.oracle.truffle.r.nodes.control.*; +import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; @@ -48,25 +50,13 @@ public abstract class UseMethod extends RBuiltinNode { controlVisibility(); if (useMethodNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - useMethodNode = insert(new UseMethodUninitializedNode()); - } - return useMethodNode.execute(frame, generic, arg); - } - - private static final class UseMethodUninitializedNode extends UseMethodNode { - @Override - public Object execute(VirtualFrame frame, final String generic, Object obj) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - return specialize(obj).execute(frame, generic, obj); - } - - private UseMethodNode specialize(Object obj) { - CompilerAsserts.neverPartOfCompilation(); - if (obj instanceof RMissing) { - return this.replace(new UseMethodGenericOnlyNode()); + if (arg instanceof RMissing) { + useMethodNode = insert(new UseMethodGenericOnlyNode()); + } else { + useMethodNode = insert(new UseMethodGenericAndObjectNode()); } - return this.replace(new UseMethodGenericAndObjectNode()); } + throw new ReturnException(useMethodNode.execute(frame, generic, arg)); } /* @@ -83,7 +73,7 @@ public abstract class UseMethod extends RBuiltinNode { } Object enclosingArg = RArguments.getArgument(frame, 0); initDispatchedCallNode(generic); - throw new ReturnException(dispatchedCallNode.execute(frame, getClassHierarchy(enclosingArg))); + return dispatchedCallNode.execute(frame, classHierarchyNode.execute(frame, enclosingArg)); } } @@ -92,13 +82,14 @@ public abstract class UseMethod extends RBuiltinNode { @Override public Object execute(VirtualFrame frame, final String generic, Object obj) { initDispatchedCallNode(generic); - throw new ReturnException(dispatchedCallNode.execute(frame, getClassHierarchy(obj))); + return dispatchedCallNode.execute(frame, classHierarchyNode.execute(frame, obj)); } } private abstract static class UseMethodNode extends RNode { @Child protected DispatchedCallNode dispatchedCallNode; + @Child ClassHierarchyNode classHierarchyNode = ClassHierarchyNodeFactory.create(null); protected String lastGenericName; @Override @@ -115,28 +106,41 @@ public abstract class UseMethod extends RBuiltinNode { } } - protected RStringVector getClassHierarchy(Object anObj) { - if (anObj instanceof RAbstractContainer) { - return ((RAbstractContainer) anObj).getClassHierarchy(); - } - if (anObj instanceof Byte) { - return RDataFactory.createStringVector(RRuntime.TYPE_LOGICAL); - } - if (anObj instanceof String) { - return RDataFactory.createStringVector(RRuntime.TYPE_CHARACTER); - } - if (anObj instanceof Integer) { - return RDataFactory.createStringVector(RRuntime.TYPE_INTEGER); - } - if (anObj instanceof Double) { - return RDataFactory.createStringVector(RRuntime.CLASS_DOUBLE, RDataFactory.COMPLETE_VECTOR); - } - if (anObj instanceof RComplex) { - return RDataFactory.createStringVector(RRuntime.TYPE_COMPLEX); - } - throw new AssertionError(); + public abstract Object execute(VirtualFrame frame, final String generic, final Object o); + } + + protected abstract static class ClassHierarchyNode extends UnaryNode { + + public abstract RStringVector execute(VirtualFrame frame, Object arg); + + @Specialization(order = 0) + public RStringVector getClassHr(RAbstractContainer arg) { + return arg.getClassHierarchy(); } - public abstract Object execute(VirtualFrame frame, final String generic, final Object o); + @Specialization + public RStringVector getClassHr(@SuppressWarnings("unused") byte arg) { + return RDataFactory.createStringVector(RRuntime.TYPE_LOGICAL); + } + + @Specialization + public RStringVector getClassHr(@SuppressWarnings("unused") String arg) { + return RDataFactory.createStringVector(RRuntime.TYPE_CHARACTER); + } + + @Specialization + public RStringVector getClassHr(@SuppressWarnings("unused") int arg) { + return RDataFactory.createStringVector(RRuntime.TYPE_INTEGER); + } + + @Specialization + public RStringVector getClassHr(@SuppressWarnings("unused") double arg) { + return RDataFactory.createStringVector(RRuntime.CLASS_DOUBLE, RDataFactory.COMPLETE_VECTOR); + } + + @Specialization + public RStringVector getClassHr(@SuppressWarnings("unused") RComplex arg) { + return RDataFactory.createStringVector(RRuntime.TYPE_COMPLEX); + } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java index 8402109347d743b5f54de1d8b75c8184aae6a4c0..21e7168959c92c6670b29a04af44874c96bd527d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java @@ -102,6 +102,11 @@ public abstract class ForNode extends LoopNode { return RNull.instance; } + @Specialization + public Object doSequence(VirtualFrame frame, RExpression expr) { + return doSequence(frame, expr.getList()); + } + @Specialization public Object doSequence(VirtualFrame frame, int x) { int count = 0; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java index cca25557b428fdad4ff29ba2218628b29c91be5d..d19ff45cf1ccf805df0db1a616028dc776de236f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java @@ -26,6 +26,7 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.control.*; import com.oracle.truffle.r.runtime.*; @@ -41,21 +42,22 @@ public final class FunctionDefinitionNode extends RRootNode { private final String description; /** - * An instance of this node may be called from a global scope with the intention to have its - * execution leave a footprint behind in that scope, e.g., during library loading. In that case, - * {@code forGlobal} is {@code true}, and the {@link #execute(VirtualFrame)} method must be - * invoked with one argument, namely the {@link VirtualFrame} representing the global scope. - * Execution will then proceed in the context of that frame. + * An instance of this node may be called from {@link REngine#runCall} with the intention to + * have its execution leave a footprint behind in a specific frame/environment, e.g., during + * library loading, commands from the shell, or R's {@code eval} and its friends. In that case, + * {@code substituteFrame} is {@code true}, and the {@link #execute(VirtualFrame)} method must + * be invoked with one argument, namely the {@link VirtualFrame} to be side-effected. Execution + * will then proceed in the context of that frame. */ - private final boolean forGlobal; + private final boolean substituteFrame; - public FunctionDefinitionNode(SourceSection src, REnvironment.FunctionDefinition descriptor, RNode body, Object[] parameterNames, String description, boolean forGlobal) { + public FunctionDefinitionNode(SourceSection src, REnvironment.FunctionDefinition descriptor, RNode body, Object[] parameterNames, String description, boolean substituteFrame) { super(src, parameterNames, descriptor.getDescriptor()); this.descriptor = descriptor; this.uninitializedBody = NodeUtil.cloneNode(body); this.body = body; this.description = description; - this.forGlobal = forGlobal; + this.substituteFrame = substituteFrame; } public REnvironment getDescriptor() { @@ -65,7 +67,7 @@ public final class FunctionDefinitionNode extends RRootNode { @Override public Object execute(VirtualFrame frame) { try { - if (forGlobal) { + if (substituteFrame) { VirtualFrame vf = (VirtualFrame) frame.getArguments()[0]; Object result = body.execute(vf); return result; @@ -85,7 +87,7 @@ public final class FunctionDefinitionNode extends RRootNode { @Override public boolean isSplittable() { // don't bother splitting library-loading nodes - return !forGlobal; + return !substituteFrame; } @Override 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 new file mode 100644 index 0000000000000000000000000000000000000000..d4abcf6d0e93ad0ff0e6d419030531005c41fca7 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.function; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.runtime.data.*; + +public final class PromiseNode extends RNode { + RPromise promise; + + private PromiseNode(RPromise promise) { + this.promise = promise; + } + + public static PromiseNode create(RPromise promise) { + return new PromiseNode(promise); + } + + @Override + public Object execute(VirtualFrame frame) { + return promise; + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java index 1ca69e5ef60f4b9b85966e82e93cc31eb17ff972..0c45092a06004000ff76b851f60eb30420daedaa 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java @@ -248,8 +248,11 @@ public abstract class RCallNode extends RNode { CompilerDirectives.transferToInterpreter(); throw RError.getUnusedArgument(getEncapsulatingSourceSection(), unusedArgNode.getSourceSection().getCode()); } + RNode[] argumentNodes = arguments.getArguments(); + RNode[] origArgumentNodes = argumentNodes; + // Handle named args and varargs if (arguments.getNameCount() != 0 || hasVarArgs) { - RNode[] permuted = permuteArguments(arguments.getArguments(), rootNode.getParameterNames(), actualNames, new VarArgsAsObjectArrayNodeFactory()); + RNode[] permuted = permuteArguments(argumentNodes, rootNode.getParameterNames(), actualNames, new VarArgsAsObjectArrayNodeFactory()); if (!isBuiltin) { for (int i = 0; i < permuted.length; i++) { if (permuted[i] == null) { @@ -257,9 +260,45 @@ public abstract class RCallNode extends RNode { } } } - return CallArgumentsNode.create(permuted, arguments.getNames()); + argumentNodes = permuted; } - return arguments; + /* + * This is a temporary fix to create promises just for builtin functions that do not + * evaluate their arguments, e.g. expression, eval. We have do the check after + * permutation to get the correct index position. Unfortunately, ... args have been + * swept up into an array, so it's a bit trickier. + */ + if (isBuiltin && !((RBuiltinRootNode) rootNode).evaluatesArgs()) { + RBuiltinRootNode builtinRootNode = (RBuiltinRootNode) rootNode; + RNode[] modifiedArgs = new RNode[argumentNodes.length]; + int lix = 0; // logical index position + for (int i = 0; i < argumentNodes.length; i++) { + RNode argumentNode = argumentNodes[i]; + if (argumentNode instanceof VarArgsAsObjectArrayNode) { + VarArgsAsObjectArrayNode vArgumentNode = (VarArgsAsObjectArrayNode) argumentNode; + RNode[] modifiedVArgumentNodes = new RNode[vArgumentNode.elementNodes.length]; + for (int j = 0; j < vArgumentNode.elementNodes.length; j++) { + modifiedVArgumentNodes[j] = checkPromise(builtinRootNode, vArgumentNode.elementNodes[j], lix); + lix++; + } + modifiedArgs[i] = new VarArgsAsObjectArrayNode(modifiedVArgumentNodes); + } else { + modifiedArgs[i] = checkPromise(builtinRootNode, argumentNode, lix); + lix++; + } + } + argumentNodes = modifiedArgs; + } + return origArgumentNodes == argumentNodes ? arguments : CallArgumentsNode.create(argumentNodes, arguments.getNames()); + } + + private static RNode checkPromise(RBuiltinRootNode builtinRootNode, RNode argNode, int lix) { + if (!builtinRootNode.evalArg(lix)) { + return PromiseNode.create(new RPromise(argNode)); + } else { + return argNode; + } + } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java index 3f225681aa4dfc70aa05c067a304833b483cca12..786902a8254757df4ba90049fa22171ea2423534 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java @@ -73,4 +73,9 @@ public abstract class CastToContainerNode extends CastNode { return dataFrame; } + @Specialization + public RExpression cast(RExpression expression) { + return expression; + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java index d10b857987e19158e89c76e22dcb8c47e4e07bec..b23f912eb5d447ed3c8c6f0722734b61db38e572 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java @@ -59,6 +59,10 @@ import com.oracle.truffle.r.runtime.data.*; * All frame elements should <b>always</b> be accessed through the getter and setter functions * defined in this class, as they provide a means of accessing the frame contents that is * transparent to layout changes. + * + * The INDEX_ENVIRONMENT slot is typically not set for frames associated with function evaluations, + * because such environment instances are only created on demand. It is however, set for frames + * associated with packages and the global environment. */ // @formatter:on public final class RArguments { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java index 13f2a52fb14b47a8f8b666efece01354e10e87c1..67dc70202b99789cee4a169bd517ae5d40e30787 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvironment.java @@ -226,7 +226,7 @@ public abstract class REnvironment implements RAttributable { initSearchList(); // load base package first - RPackages.loadBuiltin("base", baseFrame); + RPackages.loadBuiltin("base", baseFrame, baseEnv); } public static void packagesInitialize(ArrayList<RPackage> rPackages) { @@ -234,8 +234,8 @@ public abstract class REnvironment implements RAttributable { REnvironment pkgParent = autoloadEnv; for (RPackage rPackage : rPackages) { VirtualFrame pkgFrame = RRuntime.createVirtualFrame(); - RPackages.loadBuiltin(rPackage.name, pkgFrame); Package pkgEnv = new Package(pkgParent, rPackage.name, pkgFrame, rPackage.path); + RPackages.loadBuiltin(rPackage.name, pkgFrame, pkgEnv); attach(2, pkgEnv); pkgParent = pkgEnv; } @@ -351,10 +351,7 @@ public abstract class REnvironment implements RAttributable { searchPath.add(bpos, env); // Now must adjust the Frame world so that unquoted variable lookup works MaterializedFrame aboveFrame = envAbove.frameAccess.getFrame(); - MaterializedFrame envFrame = env.frameAccess.getFrame(); - if (envFrame == null) { - envFrame = new REnvMaterializedFrame((REnvMapFrameAccess) env.frameAccess); - } + MaterializedFrame envFrame = env.getFrame(); RArguments.attachFrame(aboveFrame, envFrame); } @@ -491,11 +488,17 @@ public abstract class REnvironment implements RAttributable { } /** - * Return the {@link MaterializedFrame} associated with this environment, or {@code null} if - * there is none. + * Return the {@link MaterializedFrame} associated with this environment, installing one if + * there is none in the case of {@link NewEnv} environments. */ public MaterializedFrame getFrame() { - return frameAccess.getFrame(); + MaterializedFrame envFrame = frameAccess.getFrame(); + if (envFrame == null) { + if (this instanceof NewEnv) { + envFrame = new REnvMaterializedFrame((REnvMapFrameAccess) frameAccess); + } + } + return envFrame; } public void lock(boolean bindings) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java index 51320c4c4c97b3fb6a1070881d60a0e137dd509b..eb07fe02f736f4ae124f22cb2dc5a2b35c2133d0 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPackages.java @@ -68,11 +68,13 @@ public class RPackages { /** * Purely a workaround for project circularity between nodes and runtime. + * + * @param envForFrame TODO */ - public static void loadBuiltin(String name, VirtualFrame frame) { + public static void loadBuiltin(String name, VirtualFrame frame, REnvironment envForFrame) { try { - Method loadMethod = Class.forName("com.oracle.truffle.r.nodes.builtin.RDefaultBuiltinPackages").getDeclaredMethod("load", String.class, VirtualFrame.class); - loadMethod.invoke(null, new Object[]{name, frame}); + Method loadMethod = Class.forName("com.oracle.truffle.r.nodes.builtin.RDefaultBuiltinPackages").getDeclaredMethod("load", String.class, VirtualFrame.class, REnvironment.class); + loadMethod.invoke(null, new Object[]{name, frame, envForFrame}); } catch (Exception ex) { ex.printStackTrace(); Utils.fail("failed to load builtin package: " + name + ": " + ex); 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 ee86091e0b9dcede888b909ed9027790255d6691..79df1147d9fd0cfcdca7ca4299196649118c536e 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 @@ -291,6 +291,10 @@ public final class RDataFactory { return traceDataCreated(new RDataFrame(vector)); } + public static RExpression createExpression(RList list) { + return traceDataCreated(new RExpression(list)); + } + public static RVector createObjectVector(Object[] data, boolean completeVector) { if (data.length < 1) { return null; @@ -308,4 +312,9 @@ public final class RDataFactory { public static RSymbol createSymbol(String name) { return new RSymbol(name); } + + public static RLanguage createLanguage(Object rep) { + return new RLanguage(rep); + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java index 480872815946b50674df602847f879d0f24028a2..9a8d910cedcb72a37f054a9eb39d8e606acbe1ad 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java @@ -22,15 +22,70 @@ */ package com.oracle.truffle.r.runtime.data; -public class RExpression { - private final Object astNode; +import com.oracle.truffle.r.runtime.data.model.*; - public RExpression(Object astNode) { - this.astNode = astNode; +public class RExpression implements RAbstractContainer { + + private final RList data; + + public RExpression(RList data) { + this.data = data; + } + + public RList getList() { + return data; + } + + public Object getDataAt(int index) { + return data.getDataAt(index); + } + + public RAttributes initAttributes() { + return data.initAttributes(); + } + + public RAttributes getAttributes() { + return data.getAttributes(); + } + + public int getLength() { + return data.getLength(); + } + + public int[] getDimensions() { + return data.getDimensions(); + } + + public Class<?> getElementClass() { + return null; + } + + public RVector materializeNonSharedVector() { + return data.materializeNonSharedVector(); + } + + public Object getDataAtAsObject(int index) { + return data.getDataAtAsObject(index); + } + + public Object getNames() { + return data.getNames(); + } + + public RList getDimNames() { + return data.getDimNames(); + } + + public Object getRowNames() { + return data.getRowNames(); + } + + public RStringVector getClassHierarchy() { + return data.getClassHierarchy(); } - public Object getASTNode() { - return astNode; + public boolean isObject() { + return false; } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java new file mode 100644 index 0000000000000000000000000000000000000000..5400bcbeb4e977668a8a3c2edcd0e607b32b9b20 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.data; + +/** + * Denotes an (unevaluated) element of, e.g. an {@link RExpression}. The representation is not + * disclosed here, owing partly to import circularities, but it will typically be an {@code RNode} + * that captures the (unevaluated) AST for the element. + */ +@com.oracle.truffle.api.CompilerDirectives.ValueType +public class RLanguage { + private final Object rep; + + public RLanguage(Object rep) { + this.rep = rep; + } + + public Object getRep() { + return rep; + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..cb8ee3681354100adad6542c31113cec05e0b493 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.data; + +/** + * Denotes an R {@code promise}. It extends {@link RLanguage} with a (lazily) evaluated value. + */ +@com.oracle.truffle.api.CompilerDirectives.ValueType +public class RPromise extends RLanguage { + /** + * Denotes a promise that raised an error during evaluation. + */ + private static Object ERROR = new Object(); + + private Object value; + + /** + * Create the promise with a representation that allow evaluation later. + */ + public RPromise(Object rep) { + super(rep); + } + + /** + * 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}. + */ + public Object setValue(Object newValue) { + if (value == null) { + if (newValue == null) { + this.value = ERROR; + } else { + this.value = newValue; + } + } + return this.value; + } + + public Object getValue() { + return value; + } + + /** + * Returns {@code true} if this promise has been evaluated? + */ + public boolean hasValue() { + return value != null; + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSymbol.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSymbol.java index 885466dbd4a8f7623db78df7e46eed7f5a36374c..fca9e5e1f20b170b6f58cb901f47e24397c0c43a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSymbol.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSymbol.java @@ -22,15 +22,19 @@ */ package com.oracle.truffle.r.runtime.data; +/** + * Denotes an R "symbol" or "name". Its rep is a {@code String} but it's a different type in the + * Truffle sense. + */ @com.oracle.truffle.api.CompilerDirectives.ValueType public class RSymbol { private final String name; - RSymbol(String value) { - this.name = value; + public RSymbol(String name) { + this.name = name; } - public String getValue() { + public String getName() { return name; } diff --git a/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java b/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java index 986fbf954332e06f8affdebed1a7f0969996479d..38766750f87d889c967dca38732d4becf7ebb690 100644 --- a/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java +++ b/com.oracle.truffle.r.shell/src/com/oracle/truffle/r/shell/RCommand.java @@ -145,7 +145,7 @@ public class RCommand { String content = new String(bytes); JLineConsoleHandler consoleHandler = new JLineConsoleHandler(false, new ConsoleReader(null, System.out)); VirtualFrame frame = REngine.initialize(commandArgs, consoleHandler, true, true); - REngine.parseAndEval(content, frame, true); + REngine.parseAndEval(content, frame, REnvironment.globalEnv(), true); } catch (IOException ex) { Utils.fail("unexpected error reading file input"); } @@ -168,7 +168,7 @@ public class RCommand { continue; } - REngine.parseAndEval(line, globalFrame, true); + REngine.parseAndEval(line, globalFrame, REnvironment.globalEnv(), true); } } catch (UserInterruptException e) { // interrupted diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 22b11707b7d07c2dda6cb0869d4eae41f609aa93..11f58623677bf1e01fa1dc0696c2f7505574ccd7 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -6065,6 +6065,42 @@ Error in rm("foo", envir = baseenv()) : #{ x <- 1; ls(globalenv()) } [1] "x" +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ a <- 1; eval(a + 1) } +[1] 2 + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ a <- 1; eval(a) } +[1] 1 + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ a <- 1; eval(expression(a + 1)) } +[1] 2 + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ eval(2 ^ 2 ^ 3)} +[1] 256 + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ eval(x <- 1); ls() } +[1] "x" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ f <- function(x) { eval(x) }; f(1) } +[1] 1 + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ ne <- new.env(); eval(x <- 1, ne); ls() } +[1] "ne" "x" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ ne <- new.env(); evalq(envir=ne, expr=x <- 1); ls(ne) } +[1] "x" + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testEval +#{ ne <- new.env(); evalq(x <- 1, ne); ls(ne) } +[1] "x" + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testFloor #{ floor(c(0.2,-3.4,NA,0/0,1/0)) } [1] 0 -4 NA NaN Inf @@ -6647,6 +6683,14 @@ $z [1] 42 +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testLocal +#{ kk <- local({k <- function(x) {x*2}}); kk(8)} +[1] 16 + +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testLocal +#{ ne <- new.env(); local(a <- 1, ne); ls(ne) } +[1] "a" + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testLog #{ log(0) } [1] -Inf @@ -9959,6 +10003,10 @@ f second 1 ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodSimple #{f <- function(x){ UseMethod("f",x); };f.first <- function(x){cat("f first",x)};f.second <- function(x){cat("f second",x)};obj <-1;attr(obj,"class") <- "first";f(obj);attr(obj,"class") <- "second";} f first 1 +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodSimple +#{f<-function(x){UseMethod("f")};f.logical<-function(x){print("logical")};f(TRUE)} +[1] "logical" + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testVectorConstructor #{ vector("integer") } integer(0) diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java index b8d9739243fb635e62ffe878aea3a549606728dd..609c718e213e19ab7126433d42c860172c492f16 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java @@ -6994,23 +6994,53 @@ public class AllTests extends TestBase { } @Test - public void TestSimpleBuiltins_testEval_df5a9c0a0569879276fa81b87dddc5cf() { - assertEval("{ eval(quote(x+x), list(x=1)) }"); + public void TestSimpleBuiltins_testEval_62d7082404e34bddeb5e12c06a26534d() { + assertEval("{ eval(2 ^ 2 ^ 3)}"); } @Test - public void TestSimpleBuiltins_testEval_046c5969a889af57d7ea19d1fba119d6() { - assertEval("{ y <- 2; eval(quote(x+y), list(x=1)) }"); + public void TestSimpleBuiltins_testEval_920f5515f98fd5c10461346e2a805d15() { + assertEval("{ a <- 1; eval(a) }"); } @Test - public void TestSimpleBuiltins_testEval_5b956e0508e3402588200db72e33861f() { - assertEval("{ y <- 2; x <- 4; eval(x + y, list(x=1)) }"); + public void TestSimpleBuiltins_testEval_70c290279b8e4e173c5475c813a08ccf() { + assertEval("{ a <- 1; eval(a + 1) }"); } @Test - public void TestSimpleBuiltins_testEval_b2e8a12bd61dc527a9bc79b8c43a380f() { - assertEval("{ y <- 2; x <- 2 ; eval(quote(x+y), -1) }"); + public void TestSimpleBuiltins_testEval_a7934810753e30b16c1914a2b72ba6af() { + assertEval("{ a <- 1; eval(expression(a + 1)) }"); + } + + @Test + public void TestSimpleBuiltins_testEval_f3aa723185ed6d8b5841272ed37617fb() { + assertEval("{ f <- function(x) { eval(x) }; f(1) }"); + } + + @Test + public void TestSimpleBuiltins_testEval_ee2b49685a936778137c42f4d4704c32() { + assertEval("{ eval(x <- 1); ls() }"); + } + + @Test + public void TestSimpleBuiltins_testEval_986c43993435f0acec80a3ab9af828b4() { + assertEval("{ ne <- new.env(); eval(x <- 1, ne); ls() }"); + } + + @Test + public void TestSimpleBuiltins_testEval_1b7023a1265b2b0e3e2a3be42e1b0835() { + assertEval("{ ne <- new.env(); evalq(x <- 1, ne); ls(ne) }"); + } + + @Test + public void TestSimpleBuiltins_testEval_60c900fd0d555cd006601ea5616f7146() { + assertEval("{ ne <- new.env(); evalq(envir=ne, expr=x <- 1); ls(ne) }"); + } + + @Test + public void TestSimpleBuiltins_testEvalIgnore_a2bb4f39d740a0564a45a2fa5a7f8259() { + assertEval("{ eval({ xx <- pi; xx^2}) ; xx }"); } @Test @@ -7823,6 +7853,16 @@ public class AllTests extends TestBase { assertEval("{ x<-list(y=1, 2); c(a=x, c(y=7,z=42)) }"); } + @Test + public void TestSimpleBuiltins_testLocal_d50a7019d4d1ef55e7a8b31373b29f6f() { + assertEval("{ kk <- local({k <- function(x) {x*2}}); kk(8)}"); + } + + @Test + public void TestSimpleBuiltins_testLocal_15b8e154cdc0ad21b82f8a6ca290dd48() { + assertEval("{ ne <- new.env(); local(a <- 1, ne); ls(ne) }"); + } + @Test public void TestSimpleBuiltins_testLog_fd3ef6f859e8e0d8956abf0e2bec0c13() { assertEval("{ log(1) } "); @@ -12363,6 +12403,11 @@ public class AllTests extends TestBase { assertEval("{f <- function(x){ UseMethod(\"f\",x); };f.first <- function(x){cat(\"f first\",x)};f.second <- function(x){cat(\"f second\",x)};obj <-1;attr(obj,\"class\") <- \"first\";f(obj);attr(obj,\"class\") <- \"second\";}"); } + @Test + public void TestSimpleBuiltins_testUseMethodSimple_24e4b6579385856080f94ac48ee5406f() { + assertEval("{f<-function(x){UseMethod(\"f\")};f.logical<-function(x){print(\"logical\")};f(TRUE)}"); + } + @Test public void TestSimpleBuiltins_testVectorConstructor_629fc5f98d9d6659735740d0b0894210() { assertEval("{ vector() }"); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java index 8d325d67f728fba0212f230f7e1bedcaf51c9daf..54995a771b8af061f6b747ca1446f4ac2c38e275 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java @@ -1049,23 +1049,8 @@ public class FailingTests extends TestBase { } @Ignore - public void TestSimpleBuiltins_testEval_df5a9c0a0569879276fa81b87dddc5cf() { - assertEval("{ eval(quote(x+x), list(x=1)) }"); - } - - @Ignore - public void TestSimpleBuiltins_testEval_046c5969a889af57d7ea19d1fba119d6() { - assertEval("{ y <- 2; eval(quote(x+y), list(x=1)) }"); - } - - @Ignore - public void TestSimpleBuiltins_testEval_5b956e0508e3402588200db72e33861f() { - assertEval("{ y <- 2; x <- 4; eval(x + y, list(x=1)) }"); - } - - @Ignore - public void TestSimpleBuiltins_testEval_b2e8a12bd61dc527a9bc79b8c43a380f() { - assertEval("{ y <- 2; x <- 2 ; eval(quote(x+y), -1) }"); + public void TestSimpleBuiltins_testEvalIgnore_a2bb4f39d740a0564a45a2fa5a7f8259() { + assertEval("{ eval({ xx <- pi; xx^2}) ; xx }"); } @Ignore diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java index 7fdfb66c24c4c44492477e7dd933ed7b81826f81..0c25e086089d35378afdd078ff507dc6e0e04d44 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java @@ -2307,12 +2307,28 @@ public class TestSimpleBuiltins extends TestBase { } @Test - @Ignore public void testEval() { - assertEval("{ eval(quote(x+x), list(x=1)) }"); - assertEval("{ y <- 2; eval(quote(x+y), list(x=1)) }"); - assertEval("{ y <- 2; x <- 4; eval(x + y, list(x=1)) }"); - assertEval("{ y <- 2; x <- 2 ; eval(quote(x+y), -1) }"); + assertEval("{ eval(2 ^ 2 ^ 3)}"); + assertEval("{ a <- 1; eval(a) }"); + assertEval("{ a <- 1; eval(a + 1) }"); + assertEval("{ a <- 1; eval(expression(a + 1)) }"); + assertEval("{ f <- function(x) { eval(x) }; f(1) }"); + assertEval("{ eval(x <- 1); ls() }"); + assertEval("{ ne <- new.env(); eval(x <- 1, ne); ls() }"); + assertEval("{ ne <- new.env(); evalq(x <- 1, ne); ls(ne) }"); + assertEval("{ ne <- new.env(); evalq(envir=ne, expr=x <- 1); ls(ne) }"); + } + + @Test + @Ignore + public void testEvalIgnore() { + assertEval("{ eval({ xx <- pi; xx^2}) ; xx }"); // should print two values, xx^2 and xx + } + + @Test + public void testLocal() { + assertEval("{ kk <- local({k <- function(x) {x*2}}); kk(8)}"); + assertEval("{ ne <- new.env(); local(a <- 1, ne); ls(ne) }"); } @Test @@ -2570,6 +2586,7 @@ public class TestSimpleBuiltins extends TestBase { // Basic UseMethod assertEval("{f <- function(x){ UseMethod(\"f\",x); };" + "f.first <- function(x){cat(\"f first\",x)};" + "f.second <- function(x){cat(\"f second\",x)};" + "obj <-1;" + "attr(obj,\"class\") <- \"first\";" + "f(obj);" + "attr(obj,\"class\") <- \"second\";}"); + assertEval("{f<-function(x){UseMethod(\"f\")};f.logical<-function(x){print(\"logical\")};f(TRUE)}"); } @Test