From eecbbf3a1f9542379dd550df2f7c76d3fbfba5f7 Mon Sep 17 00:00:00 2001 From: Tomas Stupka <tomas.stupka@oracle.com> Date: Thu, 26 Jan 2017 18:34:37 +0100 Subject: [PATCH] Proper "internal generic" lookup for cbind/rbind --- .../truffle/r/nodes/builtin/base/Bind.java | 187 +++++++++--------- .../r/test/builtins/TestBuiltin_cbind.java | 38 +++- .../r/test/builtins/TestBuiltin_rbind.java | 37 +++- 3 files changed, 165 insertions(+), 97 deletions(-) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java index dd7550312e..4519d18124 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java @@ -32,6 +32,7 @@ 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.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; @@ -43,10 +44,12 @@ import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAt import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; -import com.oracle.truffle.r.nodes.function.CallMatcherNode; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen; +import com.oracle.truffle.r.nodes.function.GetBaseEnvFrameNode; import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode; import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result; -import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode; +import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode; import com.oracle.truffle.r.nodes.unary.CastComplexNode; import com.oracle.truffle.r.nodes.unary.CastDoubleNode; import com.oracle.truffle.r.nodes.unary.CastIntegerNode; @@ -68,6 +71,7 @@ import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.builtins.RBuiltin; 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.RList; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise; @@ -101,9 +105,6 @@ public abstract class Bind extends RBaseNode { @Child private CastLogicalNode castLogical; @Child private GetDimAttributeNode getDimsNode; - @Child private S3FunctionLookupNode lookup; - @Child private CallMatcherNode callMatcher; - private final BindType type; private final ConditionProfile nullNamesProfile = ConditionProfile.createBinaryProfile(); @@ -114,17 +115,6 @@ public abstract class Bind extends RBaseNode { protected final ValueProfile resultProfile = ValueProfile.createClassProfile(); protected final ValueProfile vectorProfile = ValueProfile.createClassProfile(); - @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create(); - - protected int precedence(RArgsValuesAndNames args) { - int precedence = -1; - Object[] array = args.getArguments(); - for (int i = 0; i < array.length; i++) { - precedence = Math.max(precedence, precedenceNode.executeInteger(array[i], false)); - } - return precedence; - } - protected Bind(BindType type) { this.type = type; } @@ -154,32 +144,11 @@ public abstract class Bind extends RBaseNode { } @SuppressWarnings("unused") - @Specialization(guards = "precedence == NO_PRECEDENCE") + @Specialization(guards = {"precedence == NO_PRECEDENCE"}) protected RNull allNull(VirtualFrame frame, int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, int precedence) { return RNull.instance; } - private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("deparse.level", "..."); - - private static final RStringVector DATA_FRAME_CLASS = RDataFactory.createStringVectorFromScalar("data.frame"); - - @Specialization(guards = {"args.length > 0", "isDataFrame(args)"}) - protected Object allDataFrame(VirtualFrame frame, int deparseLevel, @SuppressWarnings("unused") Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence) { - if (lookup == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - lookup = insert(S3FunctionLookupNode.create(false, false)); - } - Result lookupResult = lookup.execute(frame, type.toString(), DATA_FRAME_CLASS, null, frame.materialize(), null); - if (lookupResult == null) { - throw RInternalError.shouldNotReachHere(); - } - if (callMatcher == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - callMatcher = insert(CallMatcherNode.create(false)); - } - return callMatcher.execute(frame, SIGNATURE, new Object[]{deparseLevel, promiseArgs}, lookupResult.function, lookupResult.targetFunctionName, lookupResult.createS3Args(frame)); - } - private Object bindInternal(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, CastNode castNode, boolean needsVectorCast, SetDimAttributeNode setDimNode, SetDimNamesAttributeNode setDimNamesNode, GetDimNamesAttributeNode getDimNamesNode, GetNamesAttributeNode getNamesNode) { ArgumentsSignature signature = promiseArgs.getSignature(); @@ -243,8 +212,8 @@ public abstract class Bind extends RBaseNode { } } - @Specialization(guards = {"precedence == LOGICAL_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"}) - protected Object allLogical(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, + @Specialization(guards = {"precedence == LOGICAL_PRECEDENCE", "args.length > 1"}) + protected Object allLogical(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, // @Cached("create()") CastLogicalNode cast, @Cached("create()") SetDimAttributeNode setDimNode, @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @@ -253,8 +222,8 @@ public abstract class Bind extends RBaseNode { return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode); } - @Specialization(guards = {"precedence == INT_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"}) - protected Object allInt(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, + @Specialization(guards = {"precedence == INT_PRECEDENCE", "args.length > 1"}) + protected Object allInt(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, // @Cached("create()") CastIntegerNode cast, @Cached("create()") SetDimAttributeNode setDimNode, @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @@ -263,8 +232,8 @@ public abstract class Bind extends RBaseNode { return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode); } - @Specialization(guards = {"precedence == DOUBLE_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"}) - protected Object allDouble(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, + @Specialization(guards = {"precedence == DOUBLE_PRECEDENCE", "args.length > 1"}) + protected Object allDouble(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, // @Cached("create()") CastDoubleNode cast, @Cached("create()") SetDimAttributeNode setDimNode, @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @@ -273,8 +242,8 @@ public abstract class Bind extends RBaseNode { return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode); } - @Specialization(guards = {"precedence == STRING_PRECEDENCE", "args.length> 1", "!isDataFrame(args)"}) - protected Object allString(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, + @Specialization(guards = {"precedence == STRING_PRECEDENCE", "args.length> 1"}) + protected Object allString(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, // @Cached("create()") CastStringNode cast, @Cached("create()") SetDimAttributeNode setDimNode, @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @@ -283,8 +252,8 @@ public abstract class Bind extends RBaseNode { return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode); } - @Specialization(guards = {"precedence == COMPLEX_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"}) - protected Object allComplex(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, + @Specialization(guards = {"precedence == COMPLEX_PRECEDENCE", "args.length > 1"}) + protected Object allComplex(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, // @Cached("create()") CastComplexNode cast, @Cached("create()") SetDimAttributeNode setDimNode, @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @@ -293,8 +262,8 @@ public abstract class Bind extends RBaseNode { return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode); } - @Specialization(guards = {"precedence == LIST_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"}) - protected Object allList(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, + @Specialization(guards = {"precedence == LIST_PRECEDENCE", "args.length > 1"}) + protected Object allList(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, // @Cached("create()") CastListNode cast, @Cached("create()") SetDimAttributeNode setDimNode, @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @@ -448,43 +417,6 @@ public abstract class Bind extends RBaseNode { return RRuntime.NAMES_ATTR_EMPTY_VALUE; } - @Child private InheritsCheckNode inheritsCheck = new InheritsCheckNode(RRuntime.CLASS_DATA_FRAME); - - protected boolean isDataFrame(Object[] args) { - for (int i = 0; i < args.length; i++) { - if (inheritsCheck.execute(args[i])) { - return true; - } - } - - return false; - } - - @RBuiltin(name = "cbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."}, behavior = COMPLEX) - public abstract static class CbindInternal extends RBuiltinNode { - - @Child private Bind bind = BindNodeGen.create(BindType.cbind); - @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create(); - - @Override - protected void createCasts(CastBuilder casts) { - casts.arg("deparse.level").asIntegerVector().findFirst(0); - } - - private int precedence(Object[] args) { - int precedence = -1; - for (int i = 0; i < args.length; i++) { - precedence = Math.max(precedence, precedenceNode.executeInteger(args[i], false)); - } - return precedence; - } - - @Specialization - protected Object bind(VirtualFrame frame, int deparseLevel, RArgsValuesAndNames args) { - return bind.execute(frame, deparseLevel, args.getArguments(), (RArgsValuesAndNames) RArguments.getArgument(frame, 0), precedence(args.getArguments())); - } - } - private final BranchProfile everSeenNotEqualRows = BranchProfile.create(); private final BranchProfile everSeenNotEqualColumns = BranchProfile.create(); @@ -525,7 +457,6 @@ public abstract class Bind extends RBaseNode { int[] dims = getDimensions(vec, rawDimensions); RVector<?> res = (RVector<?>) vec.copyWithNewDimensions(dims); setDimNamesNode.execute(res, RDataFactory.createList(type == BindType.cbind ? new Object[]{dimNamesA, dimNamesB} : new Object[]{dimNamesB, dimNamesA})); - res.copyRegAttributesFrom(vec); return res; } @@ -585,28 +516,94 @@ public abstract class Bind extends RBaseNode { return result; } + @RBuiltin(name = "cbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."}, behavior = COMPLEX) + public abstract static class CbindInternal extends AbstractBind { + public CbindInternal() { + super(BindType.cbind); + } + } + @RBuiltin(name = "rbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."}, behavior = COMPLEX) - public abstract static class RbindInternal extends RBuiltinNode { + public abstract static class RbindInternal extends AbstractBind { + public RbindInternal() { + super(BindType.rbind); + } + } + + protected abstract static class AbstractBind extends RBuiltinNode { + @Child private ClassHierarchyNode classHierarchy = ClassHierarchyNodeGen.create(false, false); + private final ConditionProfile hasClassProfile = ConditionProfile.createBinaryProfile(); + private final ConditionProfile hasDispatchFunction = ConditionProfile.createBinaryProfile(); + + @Child private Bind bind; + @Child private RExplicitCallNode dispatchCallNode; + @Child private PrecedenceNode precedenceNode; + + @Child private S3FunctionLookupNode lookup; + @Child private GetBaseEnvFrameNode getBaseEnv; + + private final BindType type; - @Child private Bind bind = BindNodeGen.create(BindType.rbind); - @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create(); + public AbstractBind(BindType type) { + this.type = type; + } @Override protected void createCasts(CastBuilder casts) { casts.arg("deparse.level").asIntegerVector().findFirst(0); } - private int precedence(Object[] args) { + @Specialization + protected Object bind(VirtualFrame frame, int deparseLevel, RArgsValuesAndNames args) { + RFunction dispatchFunction = createDispatchFunction(frame, args.getArguments()); + if (hasDispatchFunction.profile(dispatchFunction != null)) { + if (dispatchCallNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + dispatchCallNode = insert(RExplicitCallNode.create()); + } + return dispatchCallNode.execute(frame, dispatchFunction, (RArgsValuesAndNames) RArguments.getArgument(frame, 0)); + } else { + if (bind == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + bind = insert(BindNodeGen.create(type)); + } + return bind.execute(frame, deparseLevel, args.getArguments(), (RArgsValuesAndNames) RArguments.getArgument(frame, 0), precedence(args.getArguments())); + } + } + + protected int precedence(Object[] args) { int precedence = -1; + if (precedenceNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + precedenceNode = insert(PrecedenceNodeGen.create()); + } for (int i = 0; i < args.length; i++) { precedence = Math.max(precedence, precedenceNode.executeInteger(args[i], false)); } return precedence; } - @Specialization - protected Object bind(VirtualFrame frame, int deparseLevel, RArgsValuesAndNames args) { - return bind.execute(frame, deparseLevel, args.getArguments(), (RArgsValuesAndNames) RArguments.getArgument(frame, 0), precedence(args.getArguments())); + private RFunction createDispatchFunction(VirtualFrame frame, Object[] args) { + Result result = null; + for (Object arg : args) { + RStringVector clazz = classHierarchy.execute(arg); + if (hasClassProfile.profile(clazz != null)) { + if (lookup == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + lookup = insert(S3FunctionLookupNode.create(false, false)); + getBaseEnv = insert(GetBaseEnvFrameNode.create()); + } + Result r = lookup.execute(frame, type.toString(), clazz, null, frame.materialize(), getBaseEnv.execute()); + if (r != null) { + if (result == null) { + result = r; + } else if (!result.targetFunctionName.equals(r.targetFunctionName)) { + return null; + } + } + } + } + return result != null ? result.function : null; } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_cbind.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_cbind.java index d57ec648ca..4366b6226e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_cbind.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_cbind.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -92,5 +92,41 @@ public class TestBuiltin_cbind extends TestBase { assertEval("cbind(character(0), 'f')"); assertEval("cbind(55, character(0))"); assertEval("cbind(a=55, character(0))"); + + assertEval("v <- 1; attr(v, 'a') <- 'a'; cbind(v); cbind(v, v)"); + assertEval("v <- 1; attr(v, 'a') <- 'a'; attr(v, 'a1') <- 'a1'; cbind(v); cbind(v, v)"); + assertEval("v <- 1:3; attr(v, 'a') <- 'a'; attr(v, 'a1') <- 'a1'; cbind(v); cbind(v, v)"); + assertEval("v <- 1:3; v1<-1:3; attr(v, 'a') <- 'a'; attr(v1, 'a1') <- 'a1'; cbind(v, v1)"); } + + @Test + public void testGenericDispatch() { + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(...) 'foo'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(...) 'foo'; v2 <- 1; class(v2) <- 'foo'; cbind(v2) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; assign('cbind.foo', function(x) {'foo'}, envir=.__S3MethodsTable__.); cbind(v) ; rm('cbind.foo', envir=.__S3MethodsTable__.)}"); + + // segfault in gnur + assertEval(Ignored.ReferenceError, "{ v <- 1; class(v) <- 'foo'; cbind.foo <- length; cbind(v) }"); + assertEval(Ignored.WrongCaller, "{ v <- 1; class(v) <- 'foo'; cbind.foo <- rawToBits; cbind(v) }"); + + assertEval("{ v <- 1; class(v) <- 'foo'; cbind(v) }"); + assertEval("{ v <- 1; cbind.foo <- function(...) 'foo'; cbind(v) }"); + + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(deparse.level, ...) 'foo'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(deparse.level, x) 'foo'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(deparse.level, x1, x2) 'foo'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(x0, deparse.level, x1, x2) 'foo'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(x0, x1, x2) 'foo'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; cbind.foo <- function(x) 'foo'; cbind(v) }"); + assertEval(Ignored.WrongCaller, "{ v <- 1; class(v) <- 'foo'; cbind.foo <- function() 'foo'; cbind(v) }"); + + assertEval("{ v <- 1; class(v) <- c('foo1', 'foo2'); cbind.foo1 <- function(...) 'foo1'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- c('foo1', 'foo2'); cbind.foo2 <- function(...) 'foo2'; cbind(v) }"); + assertEval("{ v <- 1; class(v) <- c('foo1', 'foo2'); cbind.foo1 <- function(...) 'foo1'; cbind.foo2 <- function(...) 'foo2'; cbind(v) }"); + + assertEval("{ v1 <- 1; class(v1) <- 'foo1'; cbind.foo1 <- function(...) 'foo1'; v2 <- 2; class(v2) <- 'foo2'; cbind.foo2 <- function(...) 'foo2'; cbind(v1, v2) }"); + assertEval("{ v1 <- 1; class(v1) <- 'foo1'; cbind.foo1 <- function(...) 'foo1'; v2 <- 2; class(v2) <- 'foo2'; cbind(v1, v2) }"); + assertEval("{ v1 <- 1; class(v1) <- 'foo1'; v2 <- 2; class(v2) <- 'foo2'; cbind.foo2 <- function(...) 'foo2'; cbind(v1, v2) }"); + } + } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rbind.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rbind.java index a5a22ee9a4..af4dada520 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rbind.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_rbind.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -76,5 +76,40 @@ public class TestBuiltin_rbind extends TestBase { assertEval("rbind(character(0), 'f')"); assertEval("rbind(55, character(0))"); assertEval("rbind(a=55, character(0))"); + + assertEval("v <- 1; attr(v, 'a') <- 'a'; rbind(v); rbind(v, v)"); + assertEval("v <- 1; attr(v, 'a') <- 'a'; attr(v, 'a1') <- 'a1'; rbind(v); rbind(v, v)"); + assertEval("v <- 1:3; attr(v, 'a') <- 'a'; attr(v, 'a1') <- 'a1'; rbind(v); rbind(v, v)"); + assertEval("v <- 1:3; v1<-1:3; attr(v, 'a') <- 'a'; attr(v1, 'a1') <- 'a1'; rbind(v, v1)"); + } + + @Test + public void testGenericDispatch() { + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(...) 'foo'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(...) 'foo'; v2 <- 1; class(v2) <- 'foo'; rbind(v2) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; assign('rbind.foo', function(x) {'foo'}, envir=.__S3MethodsTable__.); rbind(v) ; rm('rbind.foo', envir=.__S3MethodsTable__.)}"); + + // segfault in gnur + assertEval(Ignored.ReferenceError, "{ v <- 1; class(v) <- 'foo'; rbind.foo <- length; rbind(v) }"); + assertEval(Ignored.WrongCaller, "{ v <- 1; class(v) <- 'foo'; rbind.foo <- rawToBits; rbind(v) }"); + + assertEval("{ v <- 1; class(v) <- 'foo'; rbind(v) }"); + assertEval("{ v <- 1; rbind.foo <- function(...) 'foo'; rbind(v) }"); + + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(deparse.level, ...) 'foo'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(deparse.level, x) 'foo'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(deparse.level, x1, x2) 'foo'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(x0, deparse.level, x1, x2) 'foo'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(x0, x1, x2) 'foo'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- 'foo'; rbind.foo <- function(x) 'foo'; rbind(v) }"); + assertEval(Ignored.WrongCaller, "{ v <- 1; class(v) <- 'foo'; rbind.foo <- function() 'foo'; rbind(v) }"); + + assertEval("{ v <- 1; class(v) <- c('foo1', 'foo2'); rbind.foo1 <- function(...) 'foo1'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- c('foo1', 'foo2'); rbind.foo2 <- function(...) 'foo2'; rbind(v) }"); + assertEval("{ v <- 1; class(v) <- c('foo1', 'foo2'); rbind.foo1 <- function(...) 'foo1'; rbind.foo2 <- function(...) 'foo2'; rbind(v) }"); + + assertEval("{ v1 <- 1; class(v1) <- 'foo1'; rbind.foo1 <- function(...) 'foo1'; v2 <- 2; class(v2) <- 'foo2'; rbind.foo2 <- function(...) 'foo2'; rbind(v1, v2) }"); + assertEval("{ v1 <- 1; class(v1) <- 'foo1'; rbind.foo1 <- function(...) 'foo1'; v2 <- 2; class(v2) <- 'foo2'; rbind(v1, v2) }"); + assertEval("{ v1 <- 1; class(v1) <- 'foo1'; v2 <- 2; class(v2) <- 'foo2'; rbind.foo2 <- function(...) 'foo2'; rbind(v1, v2) }"); } } -- GitLab