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 5a7652c91b1d4b84bd89298d2422ee1cd6eb0189..ed7e87edb2e586f2f863dda24acc486d091afb84 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 @@ -28,6 +28,7 @@ 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.r.nodes.RASTUtils; +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.RBuiltin; @@ -66,17 +67,12 @@ public abstract class Call extends RBuiltinNode { @TruffleBoundary private static RLanguage makeCall(String name, RArgsValuesAndNames args) { - return makeCall0(name, false, args); - } - - @TruffleBoundary - private static RLanguage makeCall(RFunction function, RArgsValuesAndNames args) { - return makeCall0(function, false, args); + return makeCall0(ReadVariableNode.createFunctionLookup(RSyntaxNode.EAGER_DEPARSE, name), false, args); } @TruffleBoundary protected static RLanguage makeCallSourceUnavailable(String name, RArgsValuesAndNames args) { - return makeCall0(name, true, args); + return makeCall0(ReadVariableNode.createFunctionLookup(RSyntaxNode.EAGER_DEPARSE, name), true, args); } @TruffleBoundary @@ -92,6 +88,7 @@ public abstract class Call extends RBuiltinNode { */ @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(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java index 8bcf277f71c80bf715ad75b3809d4370326e4fcd..973ab1d8d24b20fc25cfc83da64e1ed292e5d173 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java @@ -155,7 +155,7 @@ public abstract class S3DispatchFunctions extends RBuiltinNode { return promiseCheckHelper.checkEvaluate(frame, enclosingArg); } - private Object getFirstNonMissingArg(VirtualFrame frame, int startIdx) { + private static Object getFirstNonMissingArg(VirtualFrame frame, int startIdx) { for (int i = startIdx; i < RArguments.getArgumentsLength(frame); i++) { Object arg = RArguments.getArgument(frame, i); if (arg instanceof RArgsValuesAndNames) { @@ -167,7 +167,7 @@ public abstract class S3DispatchFunctions extends RBuiltinNode { return null; } - private Object getFirstVarArg(RArgsValuesAndNames varArgs) { + private static Object getFirstVarArg(RArgsValuesAndNames varArgs) { return varArgs.isEmpty() ? null : varArgs.getArgument(0); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java index 746b59fe0ee6ec33372bfb7ed9f63b9faa3bbf33..5e9ec273085f2326f29af4ef0c1840b2c0b72ba8 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java @@ -40,7 +40,6 @@ import com.oracle.truffle.r.runtime.RBuiltin; 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.RVisibility; import com.oracle.truffle.r.runtime.data.RAttributable; import com.oracle.truffle.r.runtime.data.RAttributeProfiles; import com.oracle.truffle.r.runtime.data.RDataFactory; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java index 63ea192dae31a78996d4c6c15daa96a5eb0210ee..8561c8a3ae9128fc2bee70af49475caca1ab8652 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java @@ -38,7 +38,6 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen; import com.oracle.truffle.r.runtime.RBuiltin; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.RVisibility; import com.oracle.truffle.r.runtime.data.RAttributable; import com.oracle.truffle.r.runtime.data.RAttributeProfiles; import com.oracle.truffle.r.runtime.data.RList; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java index 6f8f43ac1550396976f0aef97a84e83caf28a977..73a903458df5bf8022897a21a3c5147503a8d751 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java @@ -32,7 +32,6 @@ import com.oracle.truffle.r.nodes.function.opt.ReuseNonSharedNode; import com.oracle.truffle.r.nodes.unary.CastIntegerNode; import com.oracle.truffle.r.runtime.RBuiltin; import com.oracle.truffle.r.runtime.RError; -import com.oracle.truffle.r.runtime.RVisibility; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RVector; import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java index fe00dd8e0b7dc7873484e444fb9b9200444500f7..d91d4a8fbbe67167fb4ef437b99865e6abca9fe0 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java @@ -38,7 +38,6 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen; import com.oracle.truffle.r.runtime.RBuiltin; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.RVisibility; import com.oracle.truffle.r.runtime.data.RAttributes; import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RNull; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java index 468a38c94047b395513141e9baafccf2f1bded1d..098ec29439080109c0b0c614fb0f34e0e2546096 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java @@ -227,20 +227,15 @@ public class RASTUtils { fn = ((ConstantNode) fn).getValue(); } SourceSection sourceSection = sourceUnavailable ? RSyntaxNode.SOURCE_UNAVAILABLE : RSyntaxNode.EAGER_DEPARSE; - if (fn instanceof String) { - return RCallNode.createCall(sourceSection, RASTUtils.createReadVariableNode(((String) fn)), signature, arguments); - } else if (fn instanceof ReadVariableNode) { + if (fn instanceof ReadVariableNode) { return RCallNode.createCall(sourceSection, (ReadVariableNode) fn, signature, arguments); } else if (fn instanceof NamedRNode) { return RCallNode.createCall(RSyntaxNode.SOURCE_UNAVAILABLE, (NamedRNode) fn, signature, arguments); - } else if (fn instanceof RFunction) { - RFunction rfn = (RFunction) fn; - return RCallNode.createCall(sourceSection, ConstantNode.create(rfn), signature, arguments); } else if (fn instanceof RCallNode) { return RCallNode.createCall(sourceSection, (RCallNode) fn, signature, arguments); } else { - // this of course would not make much sense if trying to evaluate this call, yet it's - // syntactically possible, for example as a result of: + // apart from RFunction, this of course would not make much sense if trying to evaluate + // this call, yet it's syntactically possible, for example as a result of: // f<-function(x,y) sys.call(); x<-f(7, 42); x[c(2,3)] return RCallNode.createCall(sourceSection, ConstantNode.create(fn), signature, arguments); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java index 69062c1655dec49aa44f39b57d06833321cd102f..aa919f2e6f4d7e37624ddeed5e6b753b343f5f4b 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java @@ -62,9 +62,9 @@ import com.oracle.truffle.r.runtime.nodes.RNode; * <p> * {@link ArgumentMatcher} serves the purpose of matching {@link CallArgumentsNode} to * {@link FormalArguments} of a specific function, see - * {@link #matchArguments(RRootNode, UnmatchedArguments, RBaseNode, boolean)} . The other match - * functions are used for special cases, where builtins make it necessary to re-match parameters, - * e.g.: + * {@link #matchArguments(RRootNode, UnmatchedArguments, S3DefaultArguments, RBaseNode, boolean)} . + * The other match functions are used for special cases, where builtins make it necessary to + * re-match parameters, e.g.: * {@link #matchArgumentsEvaluated(RRootNode, RArgsValuesAndNames, S3DefaultArguments, boolean, RBaseNode)} * for 'UseMethod'. * </p> diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java index ae91e5ca92e025d130c5df8032d301b55041bd42..13b9a157c77574f6ee21f603f8d72fa7281aebad 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java @@ -26,9 +26,11 @@ import java.util.function.Supplier; 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.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; +import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @@ -51,7 +53,18 @@ public final class RCallerHelper { * @param arguments array with arguments and corresponding names. This method strips any * {@code RMissing} arguments and unrolls all varargs withint arguments array. */ - public static Supplier<RSyntaxNode> createFromArguments(final Object function, final RArgsValuesAndNames arguments) { + public static Supplier<RSyntaxNode> createFromArguments(RFunction function, RArgsValuesAndNames arguments) { + return createFromArgumentsInternal(function, arguments); + } + + /** + * @see #createFromArguments(RFunction, RArgsValuesAndNames) + */ + public static Supplier<RSyntaxNode> createFromArguments(String function, RArgsValuesAndNames arguments) { + return createFromArgumentsInternal(function, arguments); + } + + public static Supplier<RSyntaxNode> createFromArgumentsInternal(final Object function, final RArgsValuesAndNames arguments) { return new Supplier<RSyntaxNode>() { RSyntaxNode syntaxNode = null; @@ -87,7 +100,8 @@ public final class RCallerHelper { index++; } } - syntaxNode = RASTUtils.createCall(function, true, ArgumentsSignature.get(signature), syntaxArguments); + Object replacedFunction = function instanceof String ? ReadVariableNode.createFunctionLookup(RSyntaxNode.EAGER_DEPARSE, (String) function) : function; + syntaxNode = RASTUtils.createCall(replacedFunction, true, ArgumentsSignature.get(signature), syntaxArguments); } return syntaxNode; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSubstitute.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSubstitute.java index 72e39909c7fa7747e7c51128393dc61f129d28ae..2311952c672034262afba9cc3be67c3675ad88bd 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSubstitute.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSubstitute.java @@ -23,6 +23,7 @@ package com.oracle.truffle.r.runtime; import java.util.ArrayList; +import java.util.Arrays; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.RError.Message; @@ -72,6 +73,10 @@ public class RSubstitute { } } + private static boolean isLookup(RSyntaxElement element, String identifier) { + return element instanceof RSyntaxLookup && identifier.equals(((RSyntaxLookup) element).getIdentifier()); + } + /** * This method returns a newly created AST fragment for the given original element, with the * given substitutions.<br/> @@ -87,8 +92,31 @@ public class RSubstitute { @Override protected T visit(RSyntaxCall element) { - ArrayList<Argument<T>> args = createArguments(element.getSyntaxSignature(), element.getSyntaxArguments()); - return builder.call(RSyntaxNode.LAZY_DEPARSE, accept(element.getSyntaxLHS()), args); + RSyntaxElement lhs = element.getSyntaxLHS(); + RSyntaxElement[] arguments = element.getSyntaxArguments(); + /* + * Handle the special case of replacements in a$b, a@b, a$b<- and a@b<-, where FastR + * currently uses a string constant instead of a lookup. + */ + if ((arguments.length == 2 && isLookup(lhs, "$") || isLookup(lhs, "@")) || (arguments.length == 3 && isLookup(lhs, "$<-") || isLookup(lhs, "@<-"))) { + if (arguments[1] instanceof RSyntaxConstant) { + String field = RRuntime.asStringLengthOne(((RSyntaxConstant) arguments[1]).getValue()); + if (field != null) { + RSyntaxElement substitute = substituteElement(env.get(field)); + if (field != null) { + if (substitute instanceof RSyntaxLookup) { + substitute = RSyntaxConstant.createDummyConstant(RSyntaxNode.LAZY_DEPARSE, ((RSyntaxLookup) substitute).getIdentifier()); + } + if (substitute instanceof RSyntaxConstant) { + arguments = Arrays.copyOf(arguments, arguments.length, RSyntaxElement[].class); + arguments[1] = substitute; + } + } + } + } + } + ArrayList<Argument<T>> args = createArguments(element.getSyntaxSignature(), arguments); + return builder.call(RSyntaxNode.LAZY_DEPARSE, accept(lhs), args); } private ArrayList<Argument<T>> createArguments(ArgumentsSignature signature, RSyntaxElement[] arguments) { @@ -126,8 +154,7 @@ public class RSubstitute { @Override protected T visit(RSyntaxLookup element) { // this takes care of replacing variable lookups - Object val = env.get(element.getIdentifier()); - RSyntaxElement substitute = substituteElement(val); + RSyntaxElement substitute = substituteElement(env.get(element.getIdentifier())); if (substitute != null) { return builder.process(substitute); } else { 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 543bc8292147d9518233da9e1430efd5b4c73080..d0cf9c52f50523c4590ae8f06e1b49487032207e 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 @@ -46473,6 +46473,42 @@ foo2({ #f<-function(..., list=character()) { substitute(list(...))[-1L] }; as.character(f("config")) [1] "config" +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x$name <- 5); f(foo, bar); foo <- new.env(); eval(f(foo,bar)); foo$bar +foo$bar <- 5 +[1] 5 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x$name); f(foo, bar) +foo$bar + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x$name); f(foo, bar); foo <- new.env(); foo$bar <- 1; eval(f(foo,bar)) +foo$bar +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x$name<-1); f(foo, bar) +foo$bar <- 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x@name <- 5); f(foo, bar); setClass('cl', representation(bar='numeric')); foo <- new('cl'); eval(f(foo,bar)); foo@bar +foo@bar <- 5 +[1] 5 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x@name); f(foo, bar) +foo@bar + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x@name); f(foo, bar); setClass('cl', representation(bar='numeric')); foo <- new('cl'); foo@bar <- 1; eval(f(foo,bar)) +foo@bar +[1] 1 + +##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute +#f<-function(x,name) substitute(x@name<-2); f(foo, bar) +foo@bar <- 2 + ##com.oracle.truffle.r.test.builtins.TestBuiltin_substitute.testSubstitute #{ delayedAssign("expr", a * b) ; substitute(expr) } expr diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java index 22ad8c1265b37e2d900a20acf35571a585a25f09..f77ea55ad9e0df862695904d94611606dfbe520c 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java @@ -78,5 +78,14 @@ public class TestBuiltin_substitute extends TestBase { assertEval("{ substitute({class(y) <- x; y}, list(x=42)) }"); assertEval("f<-function(...) { print(typeof(get('...'))); environment() }; e <- f(c(1,2), b=15, c=44); substitute(foo2({...}), e)"); + + assertEval("f<-function(x,name) substitute(x$name); f(foo, bar)"); + assertEval("f<-function(x,name) substitute(x@name); f(foo, bar)"); + assertEval("f<-function(x,name) substitute(x$name<-1); f(foo, bar)"); + assertEval("f<-function(x,name) substitute(x@name<-2); f(foo, bar)"); + assertEval("f<-function(x,name) substitute(x$name); f(foo, bar); foo <- new.env(); foo$bar <- 1; eval(f(foo,bar))"); + assertEval("f<-function(x,name) substitute(x$name <- 5); f(foo, bar); foo <- new.env(); eval(f(foo,bar)); foo$bar"); + assertEval("f<-function(x,name) substitute(x@name); f(foo, bar); setClass('cl', representation(bar='numeric')); foo <- new('cl'); foo@bar <- 1; eval(f(foo,bar))"); + assertEval("f<-function(x,name) substitute(x@name <- 5); f(foo, bar); setClass('cl', representation(bar='numeric')); foo <- new('cl'); eval(f(foo,bar)); foo@bar"); } }