diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java index cdd90a090899456d7e339fbb92da3e3f5d0f06a8..6194da9a6cd385a21cac054592523df563b7df4f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java @@ -106,9 +106,9 @@ public abstract class StandardGeneric extends RBuiltinNode.Arg2 { } if (collectArgumentsNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - collectArgumentsNode = insert(CollectGenericArgumentsNodeGen.create(sigArgs.getDataWithoutCopying(), sigLength)); + collectArgumentsNode = insert(CollectGenericArgumentsNodeGen.create(sigLength)); } - RStringVector classes = collectArgumentsNode.execute(frame, sigArgs, sigLength); + RStringVector classes = collectArgumentsNode.execute(frame, sigLength); Object ret = dispatchGeneric.executeObject(frame, mtable, classes, def, fname); return ret; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java index c5fe1e156efc33ac413d46e50f47f837a7cc0f4c..1eb635ee7b513906f05845454a3eccf69fbe6f2d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java @@ -22,107 +22,119 @@ */ package com.oracle.truffle.r.nodes.objects; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.FrameDescriptor; -import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.SlowPathException; import com.oracle.truffle.api.profiles.ConditionProfile; -import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode; import com.oracle.truffle.r.nodes.function.ClassHierarchyScalarNode; import com.oracle.truffle.r.nodes.function.ClassHierarchyScalarNodeGen; import com.oracle.truffle.r.nodes.function.PromiseHelperNode; import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode; -import com.oracle.truffle.r.runtime.Utils; +import com.oracle.truffle.r.runtime.RArguments; +import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.REmpty; -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.RStringVector; -import com.oracle.truffle.r.runtime.data.RSymbol; -import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.nodes.RBaseNode; -// transcribed from /src/library/methods/src/methods_list_dispatch.c (R_dispatch_generic function) +// transcribed from /src/library/methods/src/methods_list_dispatch.c (R_dispatchGeneric function) /* * Used to collect arguments of the generic function for S4 method dispatch. Modeled after {@link CollectArgumentsNode}. + * The way GnuR determines the classes of the arguments is by looking up the names of the formal arguments in the dispatching function. + * However, the dispatching function may define default values for arguments that can change the signature of the actual arguments. + * The function lookup must be done by using the original actual arguments (i.e. actual arguments without default values). + * Since the arguments have already been matched and are ordered, we can just look at the arguments in the frame. + * Varargs do not appear in the formal signature, therefore any vararg parameter must be skipped. */ public abstract class CollectGenericArgumentsNode extends RBaseNode { // TODO: re-do with a multi-element cache? (list comparison will have some cost, though) - @Children private final LocalReadVariableNode[] argReads; @Children private final ClassHierarchyScalarNode[] classHierarchyNodes; @Child private ClassHierarchyScalarNode classHierarchyNodeSlowPath; @Child private PromiseCheckHelperNode promiseHelper = new PromiseCheckHelperNode(); + private final int nProvidedArgs; + private final ConditionProfile valueMissingProfile = ConditionProfile.createBinaryProfile(); - public abstract RStringVector execute(VirtualFrame frame, RList arguments, int argLength); + public abstract RStringVector execute(VirtualFrame frame, int argLength); - protected CollectGenericArgumentsNode(Object[] arguments, int argLength) { - LocalReadVariableNode[] reads = new LocalReadVariableNode[argLength]; + protected CollectGenericArgumentsNode(int argLength) { ClassHierarchyScalarNode[] hierarchyNodes = new ClassHierarchyScalarNode[argLength]; for (int i = 0; i < argLength; i++) { - RSymbol s = (RSymbol) arguments[i]; - reads[i] = LocalReadVariableNode.create(s.getName(), true); hierarchyNodes[i] = ClassHierarchyScalarNodeGen.create(); } - argReads = insert(reads); - classHierarchyNodes = insert(hierarchyNodes); + nProvidedArgs = argLength; + classHierarchyNodes = hierarchyNodes; } @ExplodeLoop @Specialization(rewriteOn = SlowPathException.class) - protected RStringVector combineCached(VirtualFrame frame, RList arguments, int argLength) throws SlowPathException { - if (argLength != argReads.length) { + protected RStringVector combineCached(VirtualFrame frame, int argLength) throws SlowPathException { + int nActualArgs = RArguments.getArgumentsLength(frame); + if (argLength != nProvidedArgs || !(nActualArgs == nProvidedArgs || nActualArgs == nProvidedArgs + 1)) { throw new SlowPathException(); } - String[] result = new String[argReads.length]; - for (int i = 0; i < argReads.length; i++) { - Object cachedId = argReads[i].getIdentifier(); - String id = ((RSymbol) (arguments.getDataAt(i))).getName(); - assert cachedId instanceof String && Utils.isInterned((String) cachedId) && Utils.isInterned(id); - if (cachedId != id) { - throw new SlowPathException(); + String[] result = new String[nProvidedArgs]; + + // The length of the actual and formal arguments may not be equal because "..." is just + // ignored in formals (i.e. '.SigArgs'). + assert nActualArgs == result.length || nActualArgs == result.length + 1; + + // Intentionally using 'i' as loop variable since nActualArgs >= + // signatureArgumentNames.length + int j = 0; + for (int i = 0; i < nProvidedArgs; i++) { + Object value = RArguments.getArgument(frame, j); + if (value instanceof RArgsValuesAndNames) { + j++; + value = RArguments.getArgument(frame, j); } - Object value = argReads[i].execute(frame); if (value == REmpty.instance || value == RMissing.instance) { value = null; } - result[i] = valueMissingProfile.profile(value == null) ? "missing" : classHierarchyNodes[i].executeString(promiseHelper.checkEvaluate(frame, value)); + Object evaledArg = promiseHelper.checkEvaluate(frame, value); + assert !(evaledArg instanceof RArgsValuesAndNames); + result[i] = valueMissingProfile.profile(value == null) ? "missing" : classHierarchyNodes[i].executeString(evaledArg); + j++; } return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR); } @Specialization - protected RStringVector combine(VirtualFrame frame, RList arguments, int argLength) { - return readFromMaterialized(frame.materialize(), arguments, argLength); + protected RStringVector combine(VirtualFrame frame, int argLength) { + return readFromMaterialized(frame.materialize(), argLength); } - @TruffleBoundary - private RStringVector readFromMaterialized(MaterializedFrame frame, RList arguments, int argLength) { - CompilerAsserts.neverPartOfCompilation(); - classHierarchyNodeSlowPath = insert(ClassHierarchyScalarNodeGen.create()); + private RStringVector readFromMaterialized(MaterializedFrame frame, int argLength) { + if (classHierarchyNodeSlowPath == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + classHierarchyNodeSlowPath = insert(ClassHierarchyScalarNodeGen.create()); + } + + int nActualArgs = RArguments.getArgumentsLength(frame); + assert nActualArgs >= argLength; + String[] result = new String[argLength]; - FrameDescriptor desc = frame.getFrameDescriptor(); - for (int i = 0; i < argLength; i++) { - RSymbol s = (RSymbol) arguments.getDataAt(i); - FrameSlot slot = desc.findFrameSlot(s.getName()); - if (slot == null) { - result[i] = "missing"; - } else { - Object value = FrameSlotChangeMonitor.getValue(slot, frame); + for (int j = 0, i = 0; i < argLength && j < nActualArgs; j++) { + Object value = RArguments.getArgument(frame, j); + if (value == REmpty.instance || value == RMissing.instance) { + value = null; + } + if (!(value instanceof RArgsValuesAndNames)) { if (value instanceof RPromise) { value = PromiseHelperNode.evaluateSlowPath((RPromise) value); } - result[i] = classHierarchyNodeSlowPath.executeString(value); + assert !(value instanceof RArgsValuesAndNames); + result[i] = value == null ? "missing" : classHierarchyNodeSlowPath.executeString(value); + i++; } } return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java index c7403d171a61c224ed320599ac40e8a8cbe4b18b..baf6272c4fe748045beb93bf5c76293563c11db4 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java @@ -19,6 +19,7 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.function.CallMatcherNode; import com.oracle.truffle.r.nodes.function.signature.CollectArgumentsNode; import com.oracle.truffle.r.nodes.function.signature.CollectArgumentsNodeGen; +import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RArguments.S4Args; import com.oracle.truffle.r.runtime.RRuntime; @@ -41,13 +42,18 @@ final class ExecuteMethod extends RBaseNode { CompilerDirectives.transferToInterpreterAndInvalidate(); collectArgs = insert(CollectArgumentsNodeGen.create()); } + ArgumentsSignature signature = RArguments.getSignature(frame); + + // Collect arguments; we cannot use the arguments of the original call because there might + // be overriding default arguments. + Object[] cumulativeArgs = collectArgs.execute(frame, signature); + if (callMatcher == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); callMatcher = insert(CallMatcherNode.create(false)); } S4Args s4Args = new S4Args(readDefined.execute(frame), readMethod.execute(frame), readTarget.execute(frame), readGeneric.execute(frame), readMethods.execute(frame)); - - return callMatcher.execute(frame, RArguments.getSignature(frame), RArguments.getArguments(frame), fdef, fname, s4Args); + return callMatcher.execute(frame, signature, cumulativeArgs, fdef, fname, s4Args); } } 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 05bc42255cc726b50f0a2a4ac27d88f59eb5f37e..3a2a1dc162c5e7f6c790858209f562616f1f2f4e 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 @@ -310,8 +310,14 @@ Creating a generic function from function ‘foo.bar’ in the global environmen [1] "primitive, A, B" [1] "primitive, B, A" +##com.oracle.truffle.r.test.S4.TestS4.runRSourceTests# +#{ source("tmptest/S4/dispatchDefaultArgValues.R") } +subsetFooMissingDrop[2,3,drop=missing] +subsetFoo[2,3,drop=TRUE] + ##com.oracle.truffle.r.test.S4.TestS4.runRSourceTests# #{ source("tmptest/S4/groupGenericS4Dispatch.R") } +[1] "s4 dispatched" ##com.oracle.truffle.r.test.S4.TestS4.runRSourceTests#Output.IgnoreErrorContext# #{ source("tmptest/S4/refClasses0.R") } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/dispatchDefaultArgValues.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/dispatchDefaultArgValues.R new file mode 100644 index 0000000000000000000000000000000000000000..2a549a10be102eefafe6992869f20134cdec206c --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/dispatchDefaultArgValues.R @@ -0,0 +1,19 @@ +setClassUnion("DDAVindex", members = c("numeric", "logical", "character")) +setClass("DDAVFoo", representation(a = "numeric", b = "numeric")) + +subsetFoo <- function(x, i, j, drop) { + cat(paste0("subsetFoo[",i,",",j,",drop=",drop,"]\n")) + c(x@a[[i]], x@b[[j]]) +} + +subsetFooMissingDrop <- function(x, i, j, drop) { + cat(paste0("subsetFooMissingDrop[",i,",",j,",drop=missing]\n")) + c(x@a[[i]], x@b[[j]]) +} +setMethod("[", signature(x = "DDAVFoo", i = "DDAVindex", j = "DDAVindex", drop = "logical"), subsetFoo) +setMethod("[", signature(x = "DDAVFoo", i = "DDAVindex", j = "DDAVindex", drop = "missing"), subsetFooMissingDrop) + +obj <- new("DDAVFoo", a=c(1,2,3),b=c(4,5,6)) +obj[2,3] + +obj[drop=T,j=3,i=2] \ No newline at end of file diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R index 4d972b633c32067a7fb9261095a309f68036c114..187101d1930fc2786d6cfc6e0ca1c9c2c947c9e0 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R @@ -4,4 +4,5 @@ setMethod("%*%", signature(x = "ANY", y = "Foo1234"), function(x, y) { "s4 dispa obj <- new("Foo1234") x <- matrix(1.1:16.1, 4, 4) obj@a <- runif(10) -x %*% obj +res <- x %*% obj +print(res) \ No newline at end of file