diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java index dbcf174805a15697847c63c09643e27572a98e02..746a892680cd7f6674d955dd2c501a1d1b290e4c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java @@ -24,12 +24,13 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean; import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM; import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; -import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.MaterializedFrame; @@ -37,9 +38,11 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.access.FrameSlotNode; +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.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.nodes.builtin.base.GetFunctions.Get; import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen; import com.oracle.truffle.r.nodes.function.GetCallerFrameNode; import com.oracle.truffle.r.nodes.function.RCallBaseNode; @@ -47,8 +50,10 @@ import com.oracle.truffle.r.nodes.function.RCallNode; 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.RRuntime; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.builtins.RBuiltin; +import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RDataFactory; @@ -56,21 +61,22 @@ import com.oracle.truffle.r.runtime.data.REmpty; 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.RMissing; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.data.RPromise.PromiseState; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.RSymbol; import com.oracle.truffle.r.runtime.data.model.RAbstractListVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; // TODO Implement completely, this is a simple implementation that works when the envir argument is ignored -@RBuiltin(name = "do.call", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"what", "args", "envir"}, behavior = COMPLEX) +@RBuiltin(name = "do.call", visibility = CUSTOM, kind = RBuiltinKind.SUBSTITUTE, parameterNames = {"what", "args", "quote", "envir"}, behavior = COMPLEX) public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNodeChildren { - @Child private GetFunctions.Get getNode; @Child private GetCallerFrameNode getCallerFrame; @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); @@ -85,30 +91,52 @@ public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNode protected void createCasts(CastBuilder casts) { casts.arg("what").defaultError(Message.MUST_BE_STRING_OR_FUNCTION, "what").mustBe(instanceOf(RFunction.class).or(stringValue())); casts.arg("args").mustBe(RAbstractListVector.class, Message.SECOND_ARGUMENT_LIST); - casts.arg("envir").mustBe(REnvironment.class, Message.MUST_BE_ENVIRON, "envir"); + casts.arg("quote").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(toBoolean()); + casts.arg("envir").allowMissing().mustBe(REnvironment.class, Message.MUST_BE_ENVIRON, "envir"); } - @Specialization - protected Object doCall(VirtualFrame frame, String what, RList argsAsList, REnvironment env) { - RFunction func; - if (getNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - getNode = insert(GetNodeGen.create()); + protected static Get createGet() { + return GetNodeGen.create(); + } + + protected ReadVariableNode createRead(RAbstractStringVector what) { + if (what.getLength() != 1) { + CompilerDirectives.transferToInterpreter(); + throw RError.error(this, RError.Message.MUST_BE_STRING_OR_FUNCTION, "what"); } - func = (RFunction) getNode.execute(frame, what, env, RType.Function.getName(), true); - return doCall(frame, func, argsAsList, env); + return ReadVariableNode.createForcedFunctionLookup(RSyntaxNode.INTERNAL, what.getDataAt(0)); } @Specialization - protected Object doCall(VirtualFrame frame, RStringVector what, RList argsAsList, REnvironment env) { + protected Object doCall(VirtualFrame frame, RAbstractStringVector what, RList argsAsList, boolean quote, REnvironment env, + @Cached("createGet()") Get getNode) { + if (what.getLength() != 1) { + CompilerDirectives.transferToInterpreter(); + throw RError.error(this, RError.Message.MUST_BE_STRING_OR_FUNCTION, "what"); + } + RFunction func = (RFunction) getNode.execute(frame, what.getDataAt(0), env, RType.Function.getName(), true); + return doCall(frame, func, argsAsList, quote, env); + } + + @Specialization(limit = "3", guards = {"what.getLength() == 1", "read.getIdentifier() == what.getDataAt(0)"}) + protected Object doCallCached(VirtualFrame frame, @SuppressWarnings("unused") RAbstractStringVector what, RList argsAsList, boolean quote, RMissing env, + @Cached("createRead(what)") ReadVariableNode read) { + RFunction func = (RFunction) read.execute(frame); + return doCall(frame, func, argsAsList, quote, env); + } + + @Specialization(contains = "doCallCached") + protected Object doCall(VirtualFrame frame, RAbstractStringVector what, RList argsAsList, boolean quote, RMissing env) { if (what.getLength() != 1) { + CompilerDirectives.transferToInterpreter(); throw RError.error(this, RError.Message.MUST_BE_STRING_OR_FUNCTION, "what"); } - return doCall(frame, what.getDataAt(0), argsAsList, env); + RFunction func = ReadVariableNode.lookupFunction(what.getDataAt(0), frame.materialize()); + return doCall(frame, func, argsAsList, quote, env); } @Specialization - protected Object doCall(VirtualFrame frame, RFunction func, RList argsAsList, @SuppressWarnings("unused") REnvironment env) { + protected Object doCall(VirtualFrame frame, RFunction func, RList argsAsList, boolean quote, @SuppressWarnings("unused") Object env) { /* * To re-create the illusion of a normal call, turn the values in argsAsList into promises. */ @@ -125,22 +153,21 @@ public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNode } signature = ArgumentsSignature.get(argNames); } - MaterializedFrame callerFrame = null; - for (int i = 0; i < argValues.length; i++) { - Object arg = argValues[i]; - if (arg instanceof RLanguage) { - containsRLanguageProfile.enter(); - callerFrame = getCallerFrame(frame, callerFrame); - RLanguage lang = (RLanguage) arg; - argValues[i] = createRLanguagePromise(callerFrame, lang); - } else if (arg instanceof RSymbol) { - containsRSymbolProfile.enter(); - RSymbol symbol = (RSymbol) arg; - if (symbol.getName().isEmpty()) { - argValues[i] = REmpty.instance; - } else { - callerFrame = getCallerFrame(frame, callerFrame); - argValues[i] = createLookupPromise(callerFrame, symbol); + if (!quote) { + for (int i = 0; i < argValues.length; i++) { + Object arg = argValues[i]; + if (arg instanceof RLanguage) { + containsRLanguageProfile.enter(); + RLanguage lang = (RLanguage) arg; + argValues[i] = createRLanguagePromise(frame.materialize(), lang); + } else if (arg instanceof RSymbol) { + containsRSymbolProfile.enter(); + RSymbol symbol = (RSymbol) arg; + if (symbol.getName().isEmpty()) { + argValues[i] = REmpty.instance; + } else { + argValues[i] = createLookupPromise(frame.materialize(), symbol); + } } } } @@ -163,16 +190,4 @@ public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNode private static RPromise createRLanguagePromise(MaterializedFrame callerFrame, RLanguage lang) { return RDataFactory.createPromise(PromiseState.Supplied, RPromise.Closure.create(RASTUtils.cloneNode(lang.getRep())), callerFrame); } - - private MaterializedFrame getCallerFrame(VirtualFrame frame, MaterializedFrame callerFrame) { - if (getCallerFrame == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - getCallerFrame = insert(new GetCallerFrameNode()); - } - if (callerFrame == null) { - return getCallerFrame.execute(frame); - } else { - return callerFrame; - } - } } 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 8ceadd73e895f2be154390df61a544787cf96a18..52cdce96c0f091dc3124305c828f088c226c6bd1 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 @@ -18702,6 +18702,14 @@ NULL #argv <- structure(list(expr = expression(quote(x <- c(1, x)))), .Names = 'expr');do.call('.doTrace', argv) NULL +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall#Output.IgnoreErrorContext# +#typeof(do.call(function(x) x, list(as.symbol('foo')))) +Error in (function (x) : object 'foo' not found + +##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# +#typeof(do.call(function(x) x, list(as.symbol('foo')), quote=TRUE)) +[1] "symbol" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_docall.testDoCall# #v1 <- as.numeric_version('3.0.0'); v2 <- as.numeric_version('3.1.0'); do.call('<', list(quote(v1), quote(v2))) [1] TRUE @@ -50617,13 +50625,9 @@ foo.bar(y, 42) foo.bar(y, 42) ##com.oracle.truffle.r.test.builtins.TestBuiltin_syscall.testSysCall# -#{ x<-do.call(function() sys.call(0), list()); x[[1]] } -function() sys.call(0) - -##com.oracle.truffle.r.test.builtins.TestBuiltin_syscall.testSysCall# -#{ x<-do.call(function() sys.call(1), list()); list(x[[1]], x[[2]][[1]], x[[2]][[2]], x[[2]][[3]]) } +#{ x<-(function(f) f())(function() sys.call(1)); list(x[[1]], x[[2]][[1]], x[[2]][[2]], x[[2]][[3]]) } [[1]] -do.call +(function(f) f()) [[2]] `function` @@ -50635,6 +50639,10 @@ NULL sys.call(1) +##com.oracle.truffle.r.test.builtins.TestBuiltin_syscall.testSysCall# +#{ x<-do.call(function() sys.call(0), list()); x[[1]] } +function() sys.call(0) + ##com.oracle.truffle.r.test.builtins.TestBuiltin_syscalls.testSysCalls# #sys.calls() NULL @@ -54772,11 +54780,6 @@ NULL #argv <- list('raw', 0L); .Internal(vector(argv[[1]], argv[[2]])) raw(0) -##com.oracle.truffle.r.test.builtins.TestBuiltin_warning.testwarning# -#argv <- list('foo'); do.call('warning', argv) -Warning message: -In do.call("warning", argv) : foo - ##com.oracle.truffle.r.test.builtins.TestBuiltin_warning.testwarning# #f <- function() warning('foo'); f() Warning message: @@ -54787,6 +54790,11 @@ In f() : foo Warning message: In f() : foo +##com.oracle.truffle.r.test.builtins.TestBuiltin_warning.testwarning# +#warning('foo') +Warning message: +foo + ##com.oracle.truffle.r.test.builtins.TestBuiltin_weekdaysDate.testweekdaysDate1# #argv <- structure(list(x = structure(16352, class = 'Date')), .Names = 'x');do.call('weekdays.Date', argv) [1] "Thursday" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java index 7f13a13bc2f4c91616bbfce1fad3e28de2933bdd..73fbac9e68fd11f22099a70addf59940d87cd915 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_docall.java @@ -29,5 +29,7 @@ public class TestBuiltin_docall extends TestBase { assertEval("{ do.call(\"+\", list(quote(1), 2))}"); assertEval("v1 <- as.numeric_version('3.0.0'); v2 <- as.numeric_version('3.1.0'); do.call('<', list(v1, v2))"); assertEval("v1 <- as.numeric_version('3.0.0'); v2 <- as.numeric_version('3.1.0'); do.call('<', list(quote(v1), quote(v2)))"); + assertEval(Output.IgnoreErrorContext, "typeof(do.call(function(x) x, list(as.symbol('foo'))))"); + assertEval("typeof(do.call(function(x) x, list(as.symbol('foo')), quote=TRUE))"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_syscall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_syscall.java index d080a5666e064ce1e05208b78ee7d615cd7f4d90..54d6ce721f9d5d609956d7a6427022ceaf546596 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_syscall.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_syscall.java @@ -50,8 +50,8 @@ public class TestBuiltin_syscall extends TestBase { // these tests look a little weird as we seem to have some printing problems with language // objects (we should be able to simply print x, but the outputs don't quite match) assertEval("{ x<-do.call(function() sys.call(0), list()); x[[1]] }"); - assertEval("{ x<-do.call(function() sys.call(1), list()); list(x[[1]], x[[2]][[1]], x[[2]][[2]], x[[2]][[3]]) }"); + // whitespace in formatting of deparsed function + assertEval(Output.IgnoreWhitespace, "{ x<-(function(f) f())(function() sys.call(1)); list(x[[1]], x[[2]][[1]], x[[2]][[2]], x[[2]][[3]]) }"); } - } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_warning.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_warning.java index 5a5983658c5f28085de17f339f515b5c38ebc13e..32105348fa57130763f66c036128cdf7a7b1df13 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_warning.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_warning.java @@ -20,7 +20,7 @@ public class TestBuiltin_warning extends TestBase { @Test public void testwarning() { - assertEval("argv <- list('foo'); do.call('warning', argv)"); + assertEval("warning('foo')"); assertEval("f <- function() warning('foo'); f()"); assertEval("f <- function() warning('foo'); f2 <- function() f(); f2()"); }