From 2deacdd081d8c0a0368cc0c14b8a1d3409f128d1 Mon Sep 17 00:00:00 2001 From: Adam Welc <adam.welc@oracle.com> Date: Fri, 30 Jan 2015 17:28:58 -0800 Subject: [PATCH] Added support for calling NextMethod explicitly which enabled accessing factors via access methods defined in R. --- .../truffle/r/nodes/builtin/base/DoCall.java | 4 +- .../builtin/base/InfixEmulationFunctions.java | 62 ++++++++++++++++++ .../r/nodes/builtin/base/NextMethod.java | 19 ++++-- .../truffle/r/nodes/builtin/base/R/factor.R | 40 ++++++------ .../truffle/r/nodes/builtin/base/R/sort.R | 4 +- .../r/nodes/function/ArgumentMatcher.java | 29 +++++---- .../r/nodes/function/DispatchedCallNode.java | 17 +++-- .../nodes/function/GroupDispatchCallNode.java | 4 +- .../function/NextMethodDispatchNode.java | 64 ++++++++++++++++--- .../r/nodes/function/S3DispatchNode.java | 5 +- .../nodes/function/UseMethodDispatchNode.java | 4 +- .../r/nodes/unary/CastToVectorNode.java | 8 ++- .../oracle/truffle/r/runtime/RArguments.java | 41 +++++++++--- .../truffle/r/test/ExpectedTestOutput.test | 39 +++++++++++ .../oracle/truffle/r/test/all/AllTests.java | 40 ++++++++++++ .../r/test/simple/TestSimpleVectors.java | 12 ++++ 16 files changed, 323 insertions(+), 69 deletions(-) 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 127a3f25a9..bd46ca59d2 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -69,7 +69,7 @@ public abstract class DoCall extends RBuiltinNode { Object n = argsAsList.getNames(); String[] argNames = n == RNull.instance ? null : ((RStringVector) n).getDataNonShared(); EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, argNames); - EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, getEncapsulatingSourceSection(), promiseHelper); + EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, getEncapsulatingSourceSection(), promiseHelper, false); Object[] callArgs = RArguments.create(func, callCache.getSourceSection(), RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames()); RArguments.setIsIrregular(callArgs, true); return callCache.execute(frame, func.getTarget(), callArgs); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java index 3027f3f2bc..203a6ba7b7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java @@ -34,6 +34,8 @@ import com.oracle.truffle.r.nodes.access.array.ArrayPositionCast.*; import com.oracle.truffle.r.nodes.access.array.ArrayPositionCastNodeGen.*; import com.oracle.truffle.r.nodes.access.array.read.*; import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctionsFactory.PromiseEvaluatorNodeGen; +import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; @@ -99,10 +101,70 @@ public class InfixEmulationFunctions { } + @NodeChild(value = "op") + protected abstract static class PromiseEvaluator extends RNode { + + protected abstract Object execute(VirtualFrame frame, Object op); + + @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode(); + @Child private PromiseEvaluator evalRecursive; + + protected Object evalRecursive(VirtualFrame frame, Object op) { + if (evalRecursive == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + evalRecursive = insert(PromiseEvaluatorNodeGen.create(null)); + } + return evalRecursive.execute(frame, op); + } + + @Specialization + protected Object eval(VirtualFrame frame, RPromise p) { + return promiseHelper.evaluate(frame, p); + } + + @Specialization + protected RAbstractVector eval(RAbstractVector op) { + return op; + } + + @ExplodeLoop + @Specialization(guards = "!argsEmpty") + protected RArgsValuesAndNames eval(VirtualFrame frame, RArgsValuesAndNames args) { + Object[] values = args.getValues(); + for (int i = 0; i < values.length; i++) { + values[i] = evalRecursive(frame, values[i]); + } + return args; + } + + @Specialization(guards = "argsEmpty") + protected RArgsValuesAndNames evalEmpty(RArgsValuesAndNames args) { + return args; + } + + @Fallback + protected Object eval(Object op) { + return op; + } + + protected boolean argsEmpty(RArgsValuesAndNames args) { + return args.length() == 0; + } + + } + public abstract static class AccessArrayBuiltin extends RBuiltinNode { @Child private AccessArrayNode accessNode; @Child private AccessPositions positions; + @CreateCast("arguments") + public RNode[] castArguments(RNode[] arguments) { + for (int i = 0; i < arguments.length; i++) { + arguments[i] = PromiseEvaluatorNodeGen.create(arguments[i]); + } + return arguments; + } + @ExplodeLoop protected Object access(VirtualFrame frame, Object vector, byte exact, RArgsValuesAndNames inds, Object dropDim, boolean isSubset) { if (accessNode == null || positions.getLength() != inds.length()) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java index d759f9128a..fa5d032e6c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java @@ -33,6 +33,7 @@ public abstract class NextMethod extends RBuiltinNode { @Child private DispatchedCallNode dispatchedCallNode; @Child private ReadVariableNode rvnClass; @Child private ReadVariableNode rvnGeneric; + @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode(); @CompilationFinal private String lastGenericName; private final BranchProfile errorProfile = BranchProfile.create(); @@ -46,14 +47,19 @@ public abstract class NextMethod extends RBuiltinNode { protected Object nextMethod(VirtualFrame frame, String genericMethod, @SuppressWarnings("unused") Object obj, Object[] args) { controlVisibility(); final RStringVector type = readType(frame); - final String genericName = readGenericName(frame, genericMethod); + final String genericName = genericMethod == null ? readGenericName(frame, genericMethod) : genericMethod; if (genericName == null) { errorProfile.enter(); throw RError.error(getEncapsulatingSourceSection(), RError.Message.GEN_FUNCTION_NOT_SPECIFIED); } if (dispatchedCallNode == null || !lastGenericName.equals(genericName)) { CompilerDirectives.transferToInterpreterAndInvalidate(); - DispatchedCallNode dcn = DispatchedCallNode.create(genericName, RRuntime.NEXT_METHOD, args); + RFunction enclosingFunction = RArguments.getFunction(frame); + String enclosingFunctionName = null; + if (!RArguments.hasS3Args(frame)) { + enclosingFunctionName = enclosingFunction.getRootNode().toString(); + } + DispatchedCallNode dcn = DispatchedCallNode.create(genericName, enclosingFunctionName, RRuntime.NEXT_METHOD, args); dispatchedCallNode = dispatchedCallNode == null ? insert(dcn) : dispatchedCallNode.replace(dcn); lastGenericName = genericName; } @@ -73,11 +79,16 @@ public abstract class NextMethod extends RBuiltinNode { } private RStringVector getAlternateClassHr(VirtualFrame frame) { - if (RArguments.getArgumentsLength(frame) == 0 || RArguments.getArgument(frame, 0) == null || !(RArguments.getArgument(frame, 0) instanceof RAbstractVector)) { + if (RArguments.getArgumentsLength(frame) == 0 || RArguments.getArgument(frame, 0) == null || + (!(RArguments.getArgument(frame, 0) instanceof RAbstractVector) && !(RArguments.getArgument(frame, 0) instanceof RPromise))) { errorProfile.enter(); throw RError.error(getEncapsulatingSourceSection(), RError.Message.OBJECT_NOT_SPECIFIED); } - RAbstractVector enclosingArg = (RAbstractVector) RArguments.getArgument(frame, 0); + Object arg = RArguments.getArgument(frame, 0); + if (arg instanceof RPromise) { + arg = promiseHelper.evaluate(frame, (RPromise) arg); + } + RAbstractContainer enclosingArg = (RAbstractContainer) arg; if (!enclosingArg.isObject()) { errorProfile.enter(); throw RError.error(getEncapsulatingSourceSection(), RError.Message.OBJECT_NOT_SPECIFIED); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R index c2b1e266f5..a17ff95059 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R @@ -200,17 +200,17 @@ Ops.factor <- function(e1, e2) ### NB for next four: ### a factor has levels before class in attribute list (PR#6799) -#`[.factor` <- function(x, ..., drop = FALSE) -#{ -# y <- NextMethod("[") -# attr(y,"contrasts") <- attr(x,"contrasts") -# attr(y,"levels") <- attr(x,"levels") -# class(y) <- oldClass(x) -# lev <- levels(x) -# if (drop) -# factor(y, exclude = if(anyNA(levels(x))) NULL else NA ) else y -#} -# +`[.factor` <- function(x, ..., drop = FALSE) +{ + y <- NextMethod("[") + attr(y,"contrasts") <- attr(x,"contrasts") + attr(y,"levels") <- attr(x,"levels") + class(y) <- oldClass(x) + lev <- levels(x) + if (drop) + factor(y, exclude = if(anyNA(levels(x))) NULL else NA ) else y +} + #`[<-.factor` <- function(x, ..., value) #{ # lx <- levels(x) @@ -226,15 +226,15 @@ Ops.factor <- function(e1, e2) # x #} # -#`[[.factor` <- function(x, ...) -#{ -# y <- NextMethod("[[") -# attr(y,"contrasts") <- attr(x,"contrasts") -# attr(y,"levels") <- attr(x,"levels") -# class(y) <- oldClass(x) -# y -#} -# +`[[.factor` <- function(x, ...) +{ + y <- NextMethod("[[") + attr(y,"contrasts") <- attr(x,"contrasts") + attr(y,"levels") <- attr(x,"levels") + class(y) <- oldClass(x) + y +} + ### added for 2.12.0 #`[[<-.factor` <- function(x, ..., value) #{ diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R index d3557b5963..926707dab2 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R @@ -125,7 +125,9 @@ order <- function(..., na.last = TRUE, decreasing = FALSE) sort.list <- function(x, partial = NULL, na.last = TRUE, decreasing = FALSE, method = c("shell", "quick", "radix")) { - if (missing(method) && is.factor(x) && nlevels(x) < 100000) method <-"radix" + # TODO: implement radix sort + if (missing(method) && is.factor(x) && nlevels(x) < 100000) method <-"shell" +# if (missing(method) && is.factor(x) && nlevels(x) < 100000) method <-"radix" method <- match.arg(method) if(!is.atomic(x)) stop("'x' must be atomic for 'sort.list'\nHave you called 'sort' on a list?") 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 b09d0317eb..bf545cbf47 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 @@ -47,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.RPromise.RPromiseFactory; * {@link #matchArguments(VirtualFrame, RFunction, UnmatchedArguments, SourceSection, SourceSection)} * . The other match functions are used for special cases, where builtins make it necessary to * re-match parameters, e.g.: - * {@link #matchArgumentsEvaluated(VirtualFrame, RFunction, EvaluatedArguments, SourceSection, PromiseHelperNode)} + * {@link #matchArgumentsEvaluated(VirtualFrame, RFunction, EvaluatedArguments, SourceSection, PromiseHelperNode, boolean)} * for 'UseMethod' and * {@link #matchArgumentsInlined(VirtualFrame, RFunction, UnmatchedArguments, SourceSection, SourceSection)} * for builtins which are implemented in Java ( @see {@link RBuiltinNode#inline(InlinedArguments)} @@ -156,15 +156,17 @@ public class ArgumentMatcher { * @param evaluatedArgs The arguments which are already in evaluated form (as they are directly * taken from the stack) * @param callSrc The source code of the call + * @param forNextMethod matching when evaluating NextMethod * * @return A Fresh {@link EvaluatedArguments} containing the arguments rearranged and stuffed * with default values (in the form of {@link RPromise}s where needed) */ - public static EvaluatedArguments matchArgumentsEvaluated(VirtualFrame frame, RFunction function, EvaluatedArguments evaluatedArgs, SourceSection callSrc, PromiseHelperNode promiseHelper) { + public static EvaluatedArguments matchArgumentsEvaluated(VirtualFrame frame, RFunction function, EvaluatedArguments evaluatedArgs, SourceSection callSrc, PromiseHelperNode promiseHelper, + boolean forNextMethod) { RRootNode rootNode = (RRootNode) function.getTarget().getRootNode(); FormalArguments formals = rootNode.getFormalArguments(); Object[] evaledArgs = permuteArguments(function, evaluatedArgs.getEvaluatedArgs(), evaluatedArgs.getNames(), formals, new VarArgsAsObjectArrayFactory(), new ObjectArrayFactory(), callSrc, - null); + null, forNextMethod); // Replace RMissing with default value! RNode[] defaultArgs = formals.getDefaultArgs(); @@ -220,7 +222,7 @@ public class ArgumentMatcher { * @return A list of {@link RNode}s which consist of the given arguments in the correct order * and wrapped into the proper {@link PromiseNode}s * @see #permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory, - * ArrayFactory, SourceSection, SourceSection) + * ArrayFactory, SourceSection, SourceSection, boolean) */ private static RNode[] matchNodes(VirtualFrame frame, RFunction function, RNode[] suppliedArgs, String[] suppliedNames, SourceSection callSrc, SourceSection argsSrc, boolean isForInlinedBuiltin, ClosureCache closureCache) { @@ -229,7 +231,7 @@ public class ArgumentMatcher { FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments(); // Rearrange arguments - RNode[] resultArgs = permuteArguments(function, suppliedArgs, suppliedNames, formals, new VarArgsAsObjectArrayNodeFactory(), new RNodeArrayFactory(), callSrc, argsSrc); + RNode[] resultArgs = permuteArguments(function, suppliedArgs, suppliedNames, formals, new VarArgsAsObjectArrayNodeFactory(), new RNodeArrayFactory(), callSrc, argsSrc, false); PromiseWrapper wrapper = isForInlinedBuiltin ? new BuiltinInitPromiseWrapper() : new DefaultPromiseWrapper(); return wrapInPromises(function, resultArgs, formals, wrapper, closureCache, callSrc); @@ -247,13 +249,14 @@ public class ArgumentMatcher { * @param arrFactory An abstraction for the generic creation of type safe arrays * @param callSrc The source of the function call currently executed * @param argsSrc The source code encapsulating the arguments, for debugging purposes + * @param forNextMethod matching when evaluating NextMethod * * @param <T> The type of the given arguments * @return An array of type <T> with the supplied arguments in the correct order */ @TruffleBoundary private static <T> T[] permuteArguments(RFunction function, T[] suppliedArgs, String[] suppliedNames, FormalArguments formals, VarArgsFactory<T> listFactory, ArrayFactory<T> arrFactory, - SourceSection callSrc, SourceSection argsSrc) { + SourceSection callSrc, SourceSection argsSrc, boolean forNextMethod) { String[] formalNames = formals.getNames(); // Preparations @@ -272,7 +275,7 @@ public class ArgumentMatcher { } // Search for argument name inside formal arguments - int fi = findParameterPosition(formalNames, suppliedNames[si], matchedFormalArgs, si, hasVarArgs, suppliedArgs[si], callSrc, argsSrc, varArgIndex); + int fi = findParameterPosition(formalNames, suppliedNames[si], matchedFormalArgs, si, hasVarArgs, suppliedArgs[si], callSrc, argsSrc, varArgIndex, forNextMethod); if (fi >= 0) { resultArgs[fi] = suppliedArgs[si]; matchedSuppliedArgs.set(si); @@ -289,8 +292,11 @@ public class ArgumentMatcher { for (int fi = 0; fi < resultArgs.length; fi++) { // Unmatched? if (!matchedFormalArgs.get(fi)) { - while (siCursor.hasNext() && siCursor.nextIndex() < suppliedNames.length && suppliedNames[siCursor.nextIndex()] != null && !suppliedNames[siCursor.nextIndex()].isEmpty()) { + while (siCursor.hasNext() && siCursor.nextIndex() < suppliedNames.length && suppliedNames[siCursor.nextIndex()] != null && !suppliedNames[siCursor.nextIndex()].isEmpty() && + !forNextMethod) { // Slide over named parameters and find subsequent location of unnamed parameter + // (if processing args for NextMethod, try to match yet unmatched named + // parameters - do not slide over them) siCursor.next(); } boolean followsDots = hasVarArgs && fi >= varArgIndex; @@ -374,7 +380,7 @@ public class ArgumentMatcher { /** * Used in - * {@link ArgumentMatcher#permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory, ArrayFactory, SourceSection, SourceSection)} + * {@link ArgumentMatcher#permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory, ArrayFactory, SourceSection, SourceSection, boolean)} * for iteration over suppliedArgs. * * @param <T> @@ -461,12 +467,13 @@ public class ArgumentMatcher { * @param callSrc * @param argsSrc * @param varArgIndex + * @param forNextMethod * * @return The position of the given suppliedName inside the formalNames. Throws errors if the * argument has been matched before */ private static <T> int findParameterPosition(String[] formalNames, String suppliedName, BitSet matchedSuppliedArgs, int suppliedIndex, boolean hasVarArgs, T debugArgNode, SourceSection callSrc, - SourceSection argsSrc, int varArgIndex) { + SourceSection argsSrc, int varArgIndex, boolean forNextMethod) { int found = -1; for (int i = 0; i < formalNames.length; i++) { if (formalNames[i] == null) { @@ -494,7 +501,7 @@ public class ArgumentMatcher { matchedSuppliedArgs.set(found); } } - if (found >= 0 || hasVarArgs) { + if (found >= 0 || hasVarArgs || forNextMethod) { return found; } // Error! diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java index 6d0b3f9b7a..400b6c1d7b 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2014, Purdue University - * Copyright (c) 2014, Oracle and/or its affiliates + * Copyright (c) 2014, 2015, Oracle and/or its affiliates * * All rights reserved. */ @@ -28,7 +28,11 @@ public abstract class DispatchedCallNode extends RNode { } public static DispatchedCallNode create(final String genericName, final String dispatchType, final Object[] args) { - return new UninitializedDispatchedCallNode(genericName, dispatchType, args); + return new UninitializedDispatchedCallNode(genericName, null, dispatchType, args); + } + + public static DispatchedCallNode create(final String genericName, final String enclosingName, final String dispatchType, final Object[] args) { + return new UninitializedDispatchedCallNode(genericName, enclosingName, dispatchType, args); } @Override @@ -49,23 +53,26 @@ public abstract class DispatchedCallNode extends RNode { private static final class UninitializedDispatchedCallNode extends DispatchedCallNode { private final int depth; private final String genericName; + private final String enclosingName; private final String dispatchType; @CompilationFinal private final Object[] args; - public UninitializedDispatchedCallNode(final String genericName, final String dispatchType, Object[] args) { + public UninitializedDispatchedCallNode(final String genericName, final String enclosingName, final String dispatchType, Object[] args) { this.genericName = genericName; + this.enclosingName = enclosingName; this.depth = 0; this.dispatchType = dispatchType; this.args = args; } public UninitializedDispatchedCallNode(final String genericName, final String dispatchType) { - this(genericName, dispatchType, null); + this(genericName, dispatchType, null, null); } private UninitializedDispatchedCallNode(final UninitializedDispatchedCallNode copy, final int depth) { this.depth = depth; this.genericName = copy.genericName; + this.enclosingName = copy.enclosingName; this.dispatchType = copy.dispatchType; this.args = null; } @@ -98,7 +105,7 @@ public abstract class DispatchedCallNode extends RNode { return new UseMethodDispatchNode(this.genericName, type); } if (this.dispatchType == RRuntime.NEXT_METHOD) { - return new NextMethodDispatchNode(this.genericName, type, this.args); + return new NextMethodDispatchNode(this.genericName, type, this.args, enclosingName); } throw RInternalError.shouldNotReachHere(); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java index 728650323c..5a23fca15b 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java @@ -350,14 +350,14 @@ class GroupDispatchNode extends S3DispatchNode { protected Object callBuiltin(VirtualFrame frame, final Object[] evaluatedArgs, final String[] argNames) { initBuiltin(frame); - EvaluatedArguments reorderedArgs = reorderArgs(frame, builtinFunc, evaluatedArgs, argNames, this.hasVararg, this.callSrc); + EvaluatedArguments reorderedArgs = reorderArgs(frame, builtinFunc, evaluatedArgs, argNames, this.hasVararg, this.callSrc, false); Object[] argObject = RArguments.create(builtinFunc, this.callSrc, RArguments.getDepth(frame), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames()); indirectCallNode.assignSourceSection(this.callSrc); return indirectCallNode.call(frame, builtinFunc.getTarget(), argObject); } protected Object executeHelper(VirtualFrame frame, final Object[] evaluatedArgs, final String[] argNames) { - EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argNames, this.hasVararg, this.callSrc); + EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argNames, this.hasVararg, this.callSrc, false); Object[] argObject = RArguments.createS3Args(targetFunction, this.callSrc, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames()); // todo: cannot create frame descriptors in compiled code FrameDescriptor s3VarDefFrameDescriptor = new FrameDescriptor(); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java index 369fff8a0f..3035bc15ca 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java @@ -39,10 +39,11 @@ public class NextMethodDispatchNode extends S3DispatchNode { private boolean lastHasGroup; @CompilationFinal private final Object[] args; - NextMethodDispatchNode(String genericName, RStringVector type, Object[] args) { + NextMethodDispatchNode(String genericName, RStringVector type, Object[] args, String storedFunctionName) { this.genericName = genericName; this.type = type; this.args = args; + this.storedFunctionName = storedFunctionName; } @Override @@ -63,13 +64,53 @@ public class NextMethodDispatchNode extends S3DispatchNode { return executeHelper(frame); } + private EvaluatedArguments processArgs(VirtualFrame frame) { + int argsLength = args == null ? 0 : args.length; + // Extract arguments from current frame... + int funArgsLength = RArguments.getArgumentsLength(frame); + assert RArguments.getNamesLength(frame) == 0 || RArguments.getNamesLength(frame) == funArgsLength; + boolean hasNames = RArguments.getNamesLength(frame) > 0; + Object[] argValues = new Object[funArgsLength + argsLength]; + String[] argNames = hasNames ? new String[funArgsLength + argsLength] : null; + int index = 0; + for (int fi = 0; fi < funArgsLength; fi++) { + Object argVal = RArguments.getArgument(frame, fi); + if (argVal instanceof RArgsValuesAndNames) { + RArgsValuesAndNames varArgs = (RArgsValuesAndNames) argVal; + int varArgsLength = varArgs.length(); + if (varArgsLength > 1) { + argValues = Utils.resizeArray(argValues, argValues.length + varArgsLength - 1); + } + System.arraycopy(varArgs.getValues(), 0, argValues, index, varArgsLength); + if (hasNames) { + if (varArgsLength > 1) { + argNames = Utils.resizeArray(argNames, argNames.length + varArgsLength - 1); + } + if (varArgs.getNames() != null) { + System.arraycopy(varArgs.getNames(), 0, argNames, index, varArgsLength); + } + } else if (varArgs.getNames() != null) { + argNames = new String[funArgsLength + argsLength]; + System.arraycopy(varArgs.getNames(), 0, argNames, index, varArgsLength); + } + index += varArgsLength; + } else { + argValues[index] = argVal; + if (hasNames) { + argNames[index] = RArguments.getName(frame, fi); + } + index++; + } + } + if (argsLength > 0) { + System.arraycopy(args, 0, argValues, RArguments.getArgumentsLength(frame), args.length); + } + return reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection(), true); + } + private Object executeHelper(VirtualFrame frame) { - // Merge arguments passed to current function with arguments passed to NextMethod call. - final Object[] mergedArgs = new Object[RArguments.getArgumentsLength(frame) + args.length]; - RArguments.copyArgumentsInto(frame, mergedArgs); - System.arraycopy(args, 0, mergedArgs, RArguments.getArgumentsLength(frame), args.length); - // TODO: implement names passing - Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), RArguments.getDepth(frame) + 1, mergedArgs, RArguments.EMPTY_STRING_ARRAY); + EvaluatedArguments evaledArgs = processArgs(frame); + Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), RArguments.getDepth(frame) + 1, evaledArgs.getEvaluatedArgs(), evaledArgs.getNames()); // todo: cannot create frame descriptors in compiled code FrameDescriptor frameDescriptor = new FrameDescriptor(); FrameSlotChangeMonitor.initializeFrameDescriptor(frameDescriptor, true); @@ -145,7 +186,9 @@ public class NextMethodDispatchNode extends S3DispatchNode { private void readGenericVars(VirtualFrame frame) { genDefEnv = RArguments.getS3DefEnv(frame); - // TODO if(genDefEnv == null) genDefEnv = globalenv + if (genDefEnv == null) { + genDefEnv = RArguments.getEnclosingFrame(frame); + } genCallEnv = RArguments.getS3CallEnv(frame); if (genCallEnv == null) { genCallEnv = frame.materialize(); @@ -156,7 +199,10 @@ public class NextMethodDispatchNode extends S3DispatchNode { } else { handlePresentGroup(); } - storedFunctionName = RArguments.getS3Method(frame); + String functionName = RArguments.getS3Method(frame); + if (functionName != null) { + storedFunctionName = functionName; + } } private void handleMissingGroup() { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java index 11cc9d291f..3b3b9a05c5 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java @@ -46,7 +46,8 @@ public abstract class S3DispatchNode extends DispatchNode { // TODO: the executeHelper methods share quite a bit of code, but is it better or worse from // having one method with a rather convoluted control flow structure? - protected EvaluatedArguments reorderArgs(VirtualFrame frame, RFunction func, Object[] evaluatedArgs, final String[] argNames, final boolean hasVarArgs, final SourceSection callSrc) { + protected EvaluatedArguments reorderArgs(VirtualFrame frame, RFunction func, Object[] evaluatedArgs, final String[] argNames, final boolean hasVarArgs, final SourceSection callSrc, + boolean forNextMethod) { String[] evaluatedArgNames = null; Object[] evaluatedArgsValues = evaluatedArgs; int argCount = evaluatedArgs.length; @@ -85,7 +86,7 @@ public abstract class S3DispatchNode extends DispatchNode { } EvaluatedArguments evaledArgs = EvaluatedArguments.create(evaluatedArgsValues, evaluatedArgNames); // ...to match them against the chosen function's formal arguments - EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, callSrc, promiseHelper); + EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, callSrc, promiseHelper, forNextMethod); return reorderedArgs; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java index 6215fd0655..72b10dd14d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java @@ -86,7 +86,7 @@ public class UseMethodDispatchNode extends S3DispatchNode { argNames[fi] = RArguments.getName(frame, fi); } } - EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection()); + EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection(), false); return executeHelper2(callerFrame, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames()); } @@ -115,7 +115,7 @@ public class UseMethodDispatchNode extends S3DispatchNode { // ...and use them as 'supplied' arguments... EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, null); // ...to match them against the chosen function's formal arguments - EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(callerFrame, targetFunction, evaledArgs, getEncapsulatingSourceSection(), promiseHelper); + EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(callerFrame, targetFunction, evaledArgs, getEncapsulatingSourceSection(), promiseHelper, false); return executeHelper2(callerFrame, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames()); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java index ef73dffc18..194ab5c5e8 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, 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 @@ -62,4 +62,10 @@ public abstract class CastToVectorNode extends CastNode { protected RAbstractVector cast(RAbstractVector vector) { return vector; } + + @Specialization + protected RAbstractVector cast(RFactor factor) { + return factor.getVector(); + } + } 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 6c48875aee..a9d49a7e57 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 @@ -213,6 +213,12 @@ public final class RArguments { return a; } + public static boolean hasS3Args(Frame frame) { + Object[] args = getArgumentsWithEvalCheck(frame); + int s3StartIndex = getS3StartIndex(args); + return args.length > s3StartIndex; + } + public static String getS3Generic(Frame frame) { Object[] args = getArgumentsWithEvalCheck(frame); int s3StartIndex = getS3StartIndex(args); @@ -230,8 +236,11 @@ public final class RArguments { public static RStringVector getS3Class(Frame frame) { Object[] args = getArgumentsWithEvalCheck(frame); int s3StartIndex = getS3StartIndex(args); - assert (args.length > s3StartIndex); - return (RStringVector) args[s3StartIndex + S3_INDEX_CLASS]; + if (args.length <= s3StartIndex) { + return null; + } else { + return (RStringVector) args[s3StartIndex + S3_INDEX_CLASS]; + } } public static void setS3Class(Frame frame, final RStringVector klass) { @@ -244,8 +253,11 @@ public final class RArguments { public static String getS3Method(Frame frame) { Object[] args = getArgumentsWithEvalCheck(frame); int s3StartIndex = getS3StartIndex(args); - assert (args.length > s3StartIndex); - return (String) args[s3StartIndex + S3_INDEX_METHOD]; + if (args.length <= s3StartIndex) { + return null; + } else { + return (String) args[s3StartIndex + S3_INDEX_METHOD]; + } } public static void setS3Method(Frame frame, final String method) { @@ -258,8 +270,11 @@ public final class RArguments { public static Frame getS3DefEnv(Frame frame) { Object[] args = getArgumentsWithEvalCheck(frame); int s3StartIndex = getS3StartIndex(args); - assert (args.length > s3StartIndex); - return (Frame) args[s3StartIndex + S3_INDEX_DEF_ENV]; + if (args.length <= s3StartIndex) { + return null; + } else { + return (Frame) args[s3StartIndex + S3_INDEX_DEF_ENV]; + } } public static void setS3DefEnv(Frame frame, Frame defEnv) { @@ -272,8 +287,11 @@ public final class RArguments { public static Frame getS3CallEnv(Frame frame) { Object[] args = getArgumentsWithEvalCheck(frame); int s3StartIndex = getS3StartIndex(args); - assert (args.length > s3StartIndex); - return (Frame) args[s3StartIndex + S3_INDEX_CALL_ENV]; + if (args.length <= s3StartIndex) { + return null; + } else { + return (Frame) args[s3StartIndex + S3_INDEX_CALL_ENV]; + } } public static void setS3CallEnv(Frame frame, Frame callEnv) { @@ -286,8 +304,11 @@ public final class RArguments { public static String getS3Group(Frame frame) { Object[] args = getArgumentsWithEvalCheck(frame); int s3StartIndex = getS3StartIndex(args); - assert (args.length > s3StartIndex); - return (String) args[s3StartIndex + S3_INDEX_GROUP]; + if (args.length <= s3StartIndex) { + return null; + } else { + return (String) args[s3StartIndex + S3_INDEX_GROUP]; + } } public static void setS3Group(Frame frame, final String group) { 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 922317c582..dad12f6b3b 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 @@ -16699,6 +16699,11 @@ Error in typeof(...) : unused arguments (2, 3, 4) #{ x<-factor(c("a", "b", "a")); typeof(x) } [1] "integer" +##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUnique +#{x<-factor(c("a", "b", "a")); unique(x) } +[1] a b +Levels: a b + ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUnlist #{ names(unlist(list(a=list(b=list("1"))))) } [1] "a.b" @@ -58178,6 +58183,40 @@ Error in x[[NA]] <- c(7, 42) : attempt to select more than one element #{ x<-list(1,2,3,4); dim(x)<-c(2,2); x[[NA]]<-c(7, 42, 1); x } Error in x[[NA]] <- c(7, 42, 1) : attempt to select more than one element +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", "b", "a")); `[.factor`(x, 1) } +[1] a +Levels: a b + +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", "b", "a")); `[.factor`(x, 1, drop=FALSE) } +[1] a +Levels: a b + +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", "b", "a")); `[.factor`(x, 1, drop=TRUE) } +[1] a +Levels: a + +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", "b", "a")); `[[.factor`(x, 1) } +[1] a +Levels: a b + +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", z="b", "a")); `[[.factor`(x, "z") } +[1] b +Levels: a b + +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", zz="b", "a")); `[[.factor`(x, "z", exact=FALSE) } +[1] b +Levels: a b + +##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess +#{ x<-factor(c("a", zz="b", "a")); `[[.factor`(x, "z", exact=TRUE) } +Error in `[[.default`(x, "z", exact = TRUE) : subscript out of bounds + ##com.oracle.truffle.r.test.simple.TestSimpleVectors.testPrint #{ mp<-getOption("max.print"); options(max.print=3); x<-c(1,2,3,4,5); attr(x, "foo")<-"foo"; print(x); options(max.print=mp) } [1] 1 2 3 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 75005563a3..a28e3163a9 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 @@ -16464,6 +16464,11 @@ public class AllTests extends TestBase { assertEval("{ typeof(seq(1,2)) }"); } + @Test + public void TestSimpleBuiltins_testUnique_525d049c6d816b0aa8f72e1deeb58241() { + assertEval("{x<-factor(c(\"a\", \"b\", \"a\")); unique(x) }"); + } + @Test public void TestSimpleBuiltins_testUnlist_52964c4cb43a47670c1f4d283abd1e1d() { assertEval("{ unlist(list(\"hello\", \"hi\")) }"); @@ -24984,6 +24989,41 @@ public class AllTests extends TestBase { assertEvalError("{ x<-list(1,2,3,4); dim(x)<-c(2,2); x[[NA, 1]]<-c(7, 42, 1); x }"); } + @Test + public void TestSimpleVectors_testObjectDirectAccess_01eabfb2c4e15bb5a7006319a4337fd2() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1) }"); + } + + @Test + public void TestSimpleVectors_testObjectDirectAccess_6f3b4849970406b2315eabe72503d201() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=TRUE) }"); + } + + @Test + public void TestSimpleVectors_testObjectDirectAccess_1be9c6acb933e025c3d74895ff9d7d02() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=FALSE) }"); + } + + @Test + public void TestSimpleVectors_testObjectDirectAccess_b79b4f8ad5b7cc5a833d720266dd1a1d() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[[.factor`(x, 1) }"); + } + + @Test + public void TestSimpleVectors_testObjectDirectAccess_660880a286cd3118a6c3ffb5f976c827() { + assertEval("{ x<-factor(c(\"a\", z=\"b\", \"a\")); `[[.factor`(x, \"z\") }"); + } + + @Test + public void TestSimpleVectors_testObjectDirectAccess_c1ed9317efa96b573485632db9fb0241() { + assertEval("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=FALSE) }"); + } + + @Test + public void TestSimpleVectors_testObjectDirectAccess_68b89b4e74c36289fd7aef12dbd3c682() { + assertEvalError("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=TRUE) }"); + } + @Test public void TestSimpleVectors_testPrint_ac880bff260f234821af9ee036453e82() { assertEval("{ x<-1:8; dim(x)<-c(2, 4); x }"); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java index e66c5dd561..51c5c1358e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java @@ -16,6 +16,18 @@ import com.oracle.truffle.r.test.*; public class TestSimpleVectors extends TestBase { + @Test + public void testObjectDirectAccess() { + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1) }"); + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=TRUE) }"); + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=FALSE) }"); + + assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[[.factor`(x, 1) }"); + assertEval("{ x<-factor(c(\"a\", z=\"b\", \"a\")); `[[.factor`(x, \"z\") }"); + assertEvalError("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=TRUE) }"); + assertEval("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=FALSE) }"); + } + @Test public void testFunctionAccess() { assertEval("{ x<-matrix(1:4, ncol=2); y<-`[`(x); y }"); -- GitLab