diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java index 5ad02cca608837419ec6fc089a807a26b7d0fc2e..5cc9d7e5b4364f57955e18f69513c626e87a271a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java @@ -25,27 +25,22 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.RASTUtils; -import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; -import com.oracle.truffle.r.nodes.builtin.base.Call.CallUtil; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.builtins.RBuiltin; -import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; -import com.oracle.truffle.r.runtime.data.RExpression; -import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RLanguage; -import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RStringVector; -import com.oracle.truffle.r.runtime.data.RSymbol; import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractListBaseVector; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @RBuiltin(name = "as.call", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE) public abstract class AsCall extends RBuiltinNode.Arg1 { @@ -57,53 +52,27 @@ public abstract class AsCall extends RBuiltinNode.Arg1 { Casts.noCasts(AsCall.class); } - @Child private CallUtil util = new CallUtil(); - @Specialization - protected RLanguage asCallFunction(RList x) { - // TODO error checks - RArgsValuesAndNames avn = makeNamesAndValues(x); - if (x.getDataAt(0) instanceof RSymbol) { - return util.makeCallSourceUnavailable(((RSymbol) x.getDataAt(0)).getName(), avn); - } else if (x.getDataAt(0) instanceof String) { - return util.makeCallSourceUnavailable((String) x.getDataAt(0), avn); - } else if (x.getDataAt(0) instanceof RAbstractStringVector) { - return util.makeCallSourceUnavailable(((RAbstractStringVector) x.getDataAt(0)).getDataAt(0), avn); - } else if (x.getDataAt(0) instanceof RFunction) { - return CallUtil.makeCallSourceUnavailable((RFunction) x.getDataAt(0), avn); - } else if (x.getDataAt(0) instanceof Integer) { - return CallUtil.makeCallSourceUnavailable((Integer) x.getDataAt(0), avn); - } else if (x.getDataAt(0) instanceof Double) { - return CallUtil.makeCallSourceUnavailable((Double) x.getDataAt(0), avn); - } else if (x.getDataAt(0) instanceof RLanguage) { - return (RLanguage) x.getDataAt(0); - } else { - throw RInternalError.unimplemented(); + @TruffleBoundary + protected RLanguage asCallFunction(RAbstractListBaseVector x) { + if (x.getLength() == 0) { + error(Message.INVALID_LEN_0_ARG); } - } + // separate the first element (call target) from the rest (arguments) - @Specialization - protected RLanguage asCallFunction(RExpression x) { - // TODO error checks - String f; - if (x.getDataAt(0) instanceof RSymbol) { - f = ((RSymbol) x.getDataAt(0)).getName(); - } else { - RLanguage l = (RLanguage) x.getDataAt(0); - f = ((ReadVariableNode) RASTUtils.unwrap(l.getRep())).getIdentifier(); + RSyntaxNode target = RASTUtils.createNodeForValue(x.getDataAt(0)).asRSyntaxNode(); + ArgumentsSignature signature = createSignature(x); + Object[] arguments = new Object[signature.getLength()]; + for (int i = 0; i < arguments.length; i++) { + arguments[i] = x.getDataAt(i + 1); } - return util.makeCallSourceUnavailable(f, makeNamesAndValues(x)); + return Call.makeCall(getRLanguage(), target, arguments, signature); } - private RArgsValuesAndNames makeNamesAndValues(RAbstractContainer x) { + private ArgumentsSignature createSignature(RAbstractContainer x) { int length = x.getLength() - 1; - Object[] values = new Object[length]; - for (int i = 0; i < length; i++) { - values[i] = x.getDataAtAsObject(i + 1); - } - ArgumentsSignature signature; if (nullNamesProfile.profile(getNamesNode.getNames(x) == null)) { - signature = ArgumentsSignature.empty(values.length); + return ArgumentsSignature.empty(length); } else { String[] names = new String[length]; // extract names, converting "" to null @@ -114,10 +83,8 @@ public abstract class AsCall extends RBuiltinNode.Arg1 { names[i] = name; } } - signature = ArgumentsSignature.get(names); + return ArgumentsSignature.get(names); } - - return new RArgsValuesAndNames(values, signature); } @Fallback diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java index c6f72e666cc5aa71de98f9c01a976e4a79c810e1..514b358ef2479f022bcba855f2fdf08fa636910a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java @@ -32,24 +32,23 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.r.nodes.RASTUtils; -import com.oracle.truffle.r.nodes.access.ConstantNode; -import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.TruffleRLanguage; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RDataFactory; -import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RLanguage; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; import com.oracle.truffle.r.runtime.data.RSymbol; -import com.oracle.truffle.r.runtime.nodes.RBaseNode; import com.oracle.truffle.r.runtime.nodes.RCodeBuilder; +import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** @@ -70,81 +69,48 @@ public abstract class Call extends RBuiltinNode.Arg2 { casts.arg("").mustBe(stringValue(), RError.Message.FIRST_ARG_MUST_BE_STRING).asStringVector().findFirst(); } - @Child private CallUtil util = new CallUtil(); - @Specialization + @TruffleBoundary protected RLanguage call(String name, RArgsValuesAndNames args) { - return util.makeCall(name, args); + return makeCall(getRLanguage(), RContext.getASTBuilder().lookup(RSyntaxNode.LAZY_DEPARSE, name, true), args.getArguments(), args.getSignature()); } - public static final class CallUtil extends RBaseNode { - - @TruffleBoundary - private RLanguage makeCall(String name, RArgsValuesAndNames args) { - return "function".equals(name) ? makeFunction(args) : makeCall0(ReadVariableNode.wrap(RSyntaxNode.LAZY_DEPARSE, ReadVariableNode.createFunctionLookup(name)), false, args); + @TruffleBoundary + public static RLanguage makeCall(TruffleRLanguage language, RSyntaxNode target, Object[] arguments, ArgumentsSignature signature) { + assert arguments.length == signature.getLength(); + if (target instanceof RSyntaxLookup && "function".equals(((RSyntaxLookup) target).getIdentifier())) { + return makeFunction(language, arguments); + } else { + return makeCall0(target, arguments, signature); } + } - private RLanguage makeFunction(RArgsValuesAndNames args) { - CompilerAsserts.neverPartOfCompilation(); - Object body = RNull.instance; - if (args.getLength() >= 2) { - body = args.getArgument(1); - } - Object argList = args.getLength() == 0 ? RNull.instance : args.getArgument(0); - ArrayList<RCodeBuilder.Argument<RSyntaxNode>> finalArgs = new ArrayList<>(); - while (argList != RNull.instance) { - if (!(argList instanceof RPairList)) { - throw RError.error(RError.SHOW_CALLER, Message.BAD_FUNCTION_EXPR); - } - RPairList pl = (RPairList) argList; - String name = ((RSymbol) pl.getTag()).getName(); - RSyntaxNode value = RASTUtils.createNodeForValue(pl.car()).asRSyntaxNode(); - finalArgs.add(RCodeBuilder.argument(RSyntaxNode.LAZY_DEPARSE, name, value)); - argList = pl.cdr(); + private static RLanguage makeFunction(TruffleRLanguage language, Object[] arguments) { + CompilerAsserts.neverPartOfCompilation(); + Object body = arguments.length <= 1 ? RNull.instance : arguments[1]; + Object argList = arguments.length == 0 ? RNull.instance : arguments[0]; + ArrayList<RCodeBuilder.Argument<RSyntaxNode>> finalArgs = new ArrayList<>(); + while (argList != RNull.instance) { + if (!(argList instanceof RPairList)) { + throw RError.error(RError.SHOW_CALLER, Message.BAD_FUNCTION_EXPR); } - RSyntaxNode function = RContext.getASTBuilder().function(getRLanguage(), RSyntaxNode.LAZY_DEPARSE, finalArgs, RASTUtils.createNodeForValue(body).asRSyntaxNode(), null); - return RDataFactory.createLanguage(function.asRNode()); - } - - @TruffleBoundary - protected RLanguage makeCallSourceUnavailable(String name, RArgsValuesAndNames args) { - return "function".equals(name) ? makeFunction(args) : makeCall0(ReadVariableNode.wrap(RSyntaxNode.LAZY_DEPARSE, ReadVariableNode.createFunctionLookup(name)), true, args); - } - - @TruffleBoundary - protected static RLanguage makeCallSourceUnavailable(int i, RArgsValuesAndNames args) { - return makeCall0(ConstantNode.create(i), true, args); + RPairList pl = (RPairList) argList; + String name = ((RSymbol) pl.getTag()).getName(); + RSyntaxNode value = RASTUtils.createNodeForValue(pl.car()).asRSyntaxNode(); + finalArgs.add(RCodeBuilder.argument(RSyntaxNode.LAZY_DEPARSE, name, value)); + argList = pl.cdr(); } + RSyntaxNode function = RContext.getASTBuilder().function(language, RSyntaxNode.LAZY_DEPARSE, finalArgs, RASTUtils.createNodeForValue(body).asRSyntaxNode(), null); + return RDataFactory.createLanguage(function.asRNode()); + } - @TruffleBoundary - protected static RLanguage makeCallSourceUnavailable(double d, RArgsValuesAndNames args) { - return makeCall0(ConstantNode.create(d), true, args); - } - - @TruffleBoundary - protected static RLanguage makeCallSourceUnavailable(RFunction function, RArgsValuesAndNames args) { - return makeCall0(function, true, args); - } - - /** - * - * @param fn an {@link RFunction} or {@link String} - * @param argsAndNames if not {@code null} the argument values and (optional) names - * @return the {@link RLanguage} instance denoting the call - */ - @TruffleBoundary - private static RLanguage makeCall0(Object fn, boolean sourceUnavailable, RArgsValuesAndNames argsAndNames) { - assert !(fn instanceof String); - int argLength = argsAndNames == null ? 0 : argsAndNames.getLength(); - RSyntaxNode[] args = new RSyntaxNode[argLength]; - Object[] values = argsAndNames == null ? null : argsAndNames.getArguments(); - ArgumentsSignature signature = argsAndNames == null ? ArgumentsSignature.empty(0) : argsAndNames.getSignature(); - - for (int i = 0; i < argLength; i++) { - args[i] = (RSyntaxNode) RASTUtils.createNodeForValue(values[i]); - } - - return RDataFactory.createLanguage(RASTUtils.createCall(fn, sourceUnavailable, signature, args).asRNode()); + @TruffleBoundary + private static RLanguage makeCall0(RSyntaxNode target, Object[] arguments, ArgumentsSignature signature) { + RSyntaxNode[] args = new RSyntaxNode[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + args[i] = (RSyntaxNode) RASTUtils.createNodeForValue(arguments[i]); } + RNode call = RContext.getASTBuilder().call(RSyntaxNode.LAZY_DEPARSE, target, RCodeBuilder.createArgumentList(signature, args)).asRNode(); + return RDataFactory.createLanguage(call); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index b992cca27d3bd844599708d34dcee51570a259b7..dd4aaab8b46974711c0040fa695013a13b086015 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -319,6 +319,7 @@ public final class RError extends RuntimeException implements TruffleException { MORE_SUPPLIED_REPLACE("more elements supplied than there are to replace"), NA_SUBSCRIPTED("NAs are not allowed in subscripted assignments"), INVALID_ARG_TYPE("invalid argument type"), + INVALID_LEN_0_ARG("invalid length 0 argument"), INVALID_ARG_UNARY("invalid argument to unary operator"), VECTOR_SIZE_NEGATIVE("vector size cannot be negative"), VECTOR_SIZE_NA("vector size cannot be NA"), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java index 3eaed47f55b88159cd36daa8df6e9feac6ecac1d..065e62a3aef3e7a822981b27da41aa9cd77af5be 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java @@ -163,6 +163,14 @@ public interface RCodeBuilder<T> { }.accept(original); } + static <T> ArrayList<Argument<T>> createArgumentList(ArgumentsSignature signature, T[] arguments) { + ArrayList<Argument<T>> args = new ArrayList<>(arguments.length); + for (int i = 0; i < arguments.length; i++) { + args.add(RCodeBuilder.argument(null, signature.getName(i), arguments[i] == null ? null : arguments[i])); + } + return args; + } + /** @see #process(RSyntaxElement) */ default T process(RSyntaxElement original, CodeBuilderContext context) { CodeBuilderContext saved = getContext(); 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 b3e608a98ac1ba093c23a45a47e39c5795c19b12..7048d696550a9650dbb236999505403e2b5c146f 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 @@ -5755,6 +5755,62 @@ character(0) a b 1 2 +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall# +#as.call(list('function', pairlist(a=1), 5)) +"function"(pairlist(a = 1), 5) + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#as.call(list(as.symbol('function'))) +function() NULL + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#as.call(list(as.symbol('function'), pairlist(a=1))) +function(a = 1) NULL + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#as.call(list(as.symbol('function'), pairlist(a=1), 5)) +function(a = 1) 5 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall# +#call('foo') +foo() + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#call('function') +function() NULL + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#call('function', 'a') +Error: badly formed function expression + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#call('function', pairlist(a=1)) +function(a = 1) NULL + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#call('function', pairlist(a=1), 3) +function(a = 1) 3 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#call('function', pairlist(a=1), 5) +function(a = 1) 5 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#call('function', pairlist(a=1), 5,3) +function(a = 1) 5 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall#Output.IgnoreWhitespace# +#e <- expression(function(a) b); as.call(list(e[[1]][[1]])) +function() NULL + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall# +#e <- expression(function(a) b); as.call(list(e[[1]][[2]])) +pairlist(a = )() + +##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall# +#e <- substitute(a$b(c)); as.call(lapply(e, function(x) x)) +a$b(c) + ##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall# #typeof(as.call(list(substitute(graphics::par)))) [1] "language" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java index 5715ff9d612ed6730d6b6feeabf9dddfb7d58810..864149114c81a19a9232cfe7e5b0b0bbe2e28780 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java @@ -63,5 +63,20 @@ public class TestBuiltin_ascall extends TestBase { assertEval("{ as.call(42) }"); assertEval("typeof(as.call(list(substitute(graphics::par))))"); + + assertEval("e <- substitute(a$b(c)); as.call(lapply(e, function(x) x))"); + assertEval(Output.IgnoreWhitespace, "e <- expression(function(a) b); as.call(list(e[[1]][[1]]))"); + assertEval("e <- expression(function(a) b); as.call(list(e[[1]][[2]]))"); + assertEval("call('foo')"); + assertEval(Output.IgnoreWhitespace, "call('function', 'a')"); + assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1))"); + assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1), 3)"); + assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1), 5,3)"); + assertEval(Output.IgnoreWhitespace, "call('function', pairlist(a=1), 5)"); + assertEval("as.call(list('function', pairlist(a=1), 5))"); + assertEval(Output.IgnoreWhitespace, "as.call(list(as.symbol('function'), pairlist(a=1), 5))"); + assertEval(Output.IgnoreWhitespace, "as.call(list(as.symbol('function'), pairlist(a=1)))"); + assertEval(Output.IgnoreWhitespace, "as.call(list(as.symbol('function')))"); + assertEval(Output.IgnoreWhitespace, "call('function')"); } }