From 74112e4ca3c72f17da91353d8e69e10b045e81c6 Mon Sep 17 00:00:00 2001 From: stepan <stepan.sindelar@oracle.com> Date: Wed, 1 Feb 2017 17:45:57 +0100 Subject: [PATCH] paste and paste0 use as.character internally --- .../truffle/r/nodes/builtin/base/Paste.java | 102 ++++++++++++------ .../truffle/r/nodes/builtin/base/Paste0.java | 7 +- .../r/nodes/builtin/base/SeqFunctions.java | 59 +++++----- .../r/nodes/builtin/casts/PipelineStep.java | 7 +- .../call/RExplicitBaseEnvCallDispatcher.java | 75 +++++++++++++ .../function/call/RExplicitCallNode.java | 9 -- .../com/oracle/truffle/r/runtime/RError.java | 2 +- .../truffle/r/test/ExpectedTestOutput.test | 20 ++++ .../r/test/builtins/TestBuiltin_paste.java | 13 ++- 9 files changed, 211 insertions(+), 83 deletions(-) create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java index cdb0a92d5d..4c01c4204c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -22,20 +22,28 @@ */ package com.oracle.truffle.r.nodes.builtin.base; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.emptyStringVector; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; +import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder; +import static com.oracle.truffle.r.runtime.RError.Message.NON_STRING_ARG_TO_INTERNAL_PASTE; +import static com.oracle.truffle.r.runtime.RError.SHOW_CALLER; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.PrimitiveValueProfile; import com.oracle.truffle.api.profiles.ValueProfile; +import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; -import com.oracle.truffle.r.nodes.unary.CastStringNode; -import com.oracle.truffle.r.nodes.unary.CastStringNodeGen; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; +import com.oracle.truffle.r.nodes.function.call.RExplicitBaseEnvCallDispatcher; +import com.oracle.truffle.r.nodes.unary.CastNode; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.data.RDataFactory; @@ -43,24 +51,27 @@ import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractListVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; @RBuiltin(name = "paste", kind = INTERNAL, parameterNames = {"", "sep", "collapse"}, behavior = PURE) public abstract class Paste extends RBuiltinNode { private static final String[] ONE_EMPTY_STRING = new String[]{""}; - public abstract Object executeList(RList value, String sep, Object collapse); + public abstract Object executeList(VirtualFrame frame, RList value, String sep, Object collapse); - /** - * {@code paste} is specified to convert its arguments using {@code as.character}. - */ - @Child private AsCharacter asCharacterNode; - @Child private CastStringNode castCharacterNode; + @Child private ClassHierarchyNode classHierarchyNode; + @Child private CastNode asCharacterNode; + @Child private CastNode castAsCharacterResultNode; + @Child private RExplicitBaseEnvCallDispatcher asCharacterDispatcher; + @Child private BoxPrimitiveNode boxPrimitiveNode = BoxPrimitiveNode.create(); private final ValueProfile lengthProfile = PrimitiveValueProfile.createEqualityProfile(); private final ConditionProfile reusedResultProfile = ConditionProfile.createBinaryProfile(); private final BranchProfile nonNullElementsProfile = BranchProfile.create(); private final BranchProfile onlyNullElementsProfile = BranchProfile.create(); + private final ConditionProfile isNotStringProfile = ConditionProfile.createBinaryProfile(); + private final ConditionProfile hasNoClassProfile = ConditionProfile.createBinaryProfile(); @Override protected void createCasts(CastBuilder casts) { @@ -69,31 +80,33 @@ public abstract class Paste extends RBuiltinNode { casts.arg("collapse").allowNull().mustBe(stringValue()).asStringVector().findFirst(); } - /** - * FIXME The exact semantics needs checking regarding the use of {@code as.character}. Currently - * there are problem using it here, so we retain the previous implementation that just uses - * {@link CastStringNode}. - */ - private RStringVector castCharacterVector(Object o) { - if (castCharacterNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - castCharacterNode = insert(CastStringNodeGen.create(false, false, false)); + private RAbstractStringVector castCharacterVector(VirtualFrame frame, Object o) { + // Note: GnuR does not actually invoke as.character for character values, even if they have + // class and uses their value directly + Object result = o; + if (isNotStringProfile.profile(!(o instanceof String || o instanceof RAbstractStringVector))) { + result = castNonStringToCharacterVector(frame, result); } - Object ret = castCharacterNode.executeString(o); - if (ret instanceof String) { - return RDataFactory.createStringVector((String) ret); - } else if (ret == RNull.instance) { - return RDataFactory.createEmptyStringVector(); - } else { - return (RStringVector) ((RStringVector) ret).copyDropAttributes(); + // box String to RAbstractStringVector + return (RAbstractStringVector) boxPrimitiveNode.execute(result); + } + + private Object castNonStringToCharacterVector(VirtualFrame frame, Object result) { + RStringVector classVec = getClassHierarchyNode().execute(result); + if (hasNoClassProfile.profile(classVec == null || classVec.getLength() == 0)) { + // coerce non-string result to string, i.e. do what 'as.character' would do + return getAsCharacterNode().execute(result); } + // invoke the actual 'as.character' function (with its dispatch) + ensureAsCharacterFuncNodes(); + return castAsCharacterResultNode.execute(asCharacterDispatcher.call(frame, result)); } @Specialization - protected RStringVector pasteList(RAbstractListVector values, String sep, @SuppressWarnings("unused") RNull collapse) { + protected RStringVector pasteListNullSep(VirtualFrame frame, RAbstractListVector values, String sep, @SuppressWarnings("unused") RNull collapse) { int length = lengthProfile.profile(values.getLength()); if (hasNonNullElements(values, length)) { - String[] result = pasteListElements(values, sep, length); + String[] result = pasteListElements(frame, values, sep, length); return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR); } else { return RDataFactory.createEmptyStringVector(); @@ -101,10 +114,10 @@ public abstract class Paste extends RBuiltinNode { } @Specialization - protected String pasteList(RAbstractListVector values, String sep, String collapse) { + protected String pasteList(VirtualFrame frame, RAbstractListVector values, String sep, String collapse) { int length = lengthProfile.profile(values.getLength()); if (hasNonNullElements(values, length)) { - String[] result = pasteListElements(values, sep, length); + String[] result = pasteListElements(frame, values, sep, length); return collapseString(result, collapse); } else { return ""; @@ -122,12 +135,12 @@ public abstract class Paste extends RBuiltinNode { return false; } - private String[] pasteListElements(RAbstractListVector values, String sep, int length) { + private String[] pasteListElements(VirtualFrame frame, RAbstractListVector values, String sep, int length) { String[][] converted = new String[length][]; int maxLength = 1; for (int i = 0; i < length; i++) { Object element = values.getDataAt(i); - String[] array = castCharacterVector(element).getDataWithoutCopying(); + String[] array = castCharacterVector(frame, element).materialize().getDataWithoutCopying(); maxLength = Math.max(maxLength, array.length); converted[i] = array.length == 0 ? ONE_EMPTY_STRING : array; } @@ -203,4 +216,31 @@ public abstract class Paste extends RBuiltinNode { assert pos == stringLength; return new String(chars); } + + private void ensureAsCharacterFuncNodes() { + if (asCharacterDispatcher == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + asCharacterDispatcher = insert(RExplicitBaseEnvCallDispatcher.create("as.character")); + } + if (castAsCharacterResultNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + castAsCharacterResultNode = insert(newCastBuilder().mustBe(stringValue(), SHOW_CALLER, NON_STRING_ARG_TO_INTERNAL_PASTE).buildCastNode()); + } + } + + private ClassHierarchyNode getClassHierarchyNode() { + if (classHierarchyNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + classHierarchyNode = insert(ClassHierarchyNode.create()); + } + return classHierarchyNode; + } + + private CastNode getAsCharacterNode() { + if (asCharacterNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + asCharacterNode = insert(newCastBuilder().returnIf(nullValue(), emptyStringVector()).asStringVector().buildCastNode()); + } + return asCharacterNode; + } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java index 1cf530d651..ce0645cb65 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -27,6 +27,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.builtins.RBuiltin; @@ -48,7 +49,7 @@ public abstract class Paste0 extends RBuiltinNode { } @Specialization - protected Object paste0(RList values, Object collapse) { - return pasteNode.executeList(values, "", collapse); + protected Object paste0(VirtualFrame frame, RList values, Object collapse) { + return pasteNode.executeList(frame, values, "", collapse); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java index 8c16570d7c..6bb73698a3 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java @@ -12,7 +12,9 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notIntNA; import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size; +import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder; import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC; import static com.oracle.truffle.r.runtime.RError.NO_CALLER; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; @@ -27,7 +29,6 @@ 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.r.nodes.access.variables.LocalReadVariableNode; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetClassAttributeNode; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; @@ -42,9 +43,8 @@ import com.oracle.truffle.r.nodes.ffi.AsRealNode; import com.oracle.truffle.r.nodes.ffi.AsRealNodeGen; import com.oracle.truffle.r.nodes.function.CallMatcherNode.CallMatcherGenericNode; import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; -import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode; -import com.oracle.truffle.r.nodes.unary.CastIntegerNode; -import com.oracle.truffle.r.nodes.unary.FindFirstNode; +import com.oracle.truffle.r.nodes.function.call.RExplicitBaseEnvCallDispatcher; +import com.oracle.truffle.r.nodes.unary.CastNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RInternalError; @@ -66,7 +66,6 @@ import com.oracle.truffle.r.runtime.data.RTypesFlatLayout; import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; -import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.nodes.RFastPathNode; /** @@ -337,10 +336,17 @@ public final class SeqFunctions { return RDataFactory.createIntSequence(1, 1, length.executeInteger(frame, value)); } + /** + * Invokes the 'length' function, which may dispatch to some other function than the default + * length depending on the class of the argument. + */ @Specialization(guards = "hasClass(value)") protected RIntSequence seq(VirtualFrame frame, Object value, - @Cached("create()") LengthDispatcher dispatcher) { - return RDataFactory.createIntSequence(1, 1, dispatcher.execute(frame, value)); + @Cached("createLengthResultCast()") CastNode resultCast, + @Cached("createLengthDispatcher()") RExplicitBaseEnvCallDispatcher dispatcher, + @Cached("create()") BranchProfile errorProfile) { + int result = (Integer) resultCast.execute(dispatcher.call(frame, value)); + return RDataFactory.createIntSequence(1, 1, result); } boolean hasClass(Object obj) { @@ -348,35 +354,18 @@ public final class SeqFunctions { return classVec != null && classVec.getLength() != 0; } - /** - * Invokes the 'length' function, which may dispatch to some other function than default - * length depending on the class of the argument. - */ - static final class LengthDispatcher extends Node { - private final BranchProfile errorProfile = BranchProfile.create(); - @Child private LocalReadVariableNode readLength = LocalReadVariableNode.create("length", true); - @Child RExplicitCallNode callNode = RExplicitCallNode.create(); - @Child private CastIntegerNode castInteger = CastIntegerNode.createNonPreserving(); - @Child private FindFirstNode findFirst = FindFirstNode.create(Integer.class, NO_CALLER, Message.NEGATIVE_LENGTH_VECTORS_NOT_ALLOWED); - - public static LengthDispatcher create() { - return new LengthDispatcher(); - } - - public int execute(VirtualFrame frame, Object target) { - Object lengthFunction = readLength.execute(frame, REnvironment.baseEnv().getFrame()); - assert lengthFunction instanceof RFunction : "unexpected that 'length' in base environment is not a function"; - int result = castResult(callNode.call(frame, (RFunction) lengthFunction, target)); - if (result < 0 || RRuntime.isNA(result)) { - errorProfile.enter(); - throw RError.error(NO_CALLER, Message.NEGATIVE_LENGTH_VECTORS_NOT_ALLOWED); - } - return result; - } + RExplicitBaseEnvCallDispatcher createLengthDispatcher() { + return RExplicitBaseEnvCallDispatcher.create("length"); + } - private int castResult(Object result) { - return (Integer) findFirst.execute(castInteger.execute(result)); - } + CastNode createLengthResultCast() { + // @formatter:off + return newCastBuilder().asIntegerVector(false, false, false). + defaultError(NO_CALLER, Message.NEGATIVE_LENGTH_VECTORS_NOT_ALLOWED). + findFirst(). + mustBe(gte(0).and(notIntNA())). + buildCastNode(); + // @formatter:on } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java index 27704a9d4b..b4285ff050 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/casts/PipelineStep.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector; /** * Represents a single step in the cast pipeline. {@code PipelineStep}, {@code Mapper} and * {@code Filter} are only symbolic representation of the pipeline, these objects can be transformed - * to something useful by using corresponding visitors, e.g. {@linek PipelineStepVisitor}. Steps can + * to something useful by using corresponding visitors, e.g. {@link PipelineStepVisitor}. Steps can * be chained as a linked list by setting the next step in the chain using * {@link #setNext(PipelineStep)}. The order of steps should be the same as the order of cast * pipeline API invocations. @@ -197,8 +197,9 @@ public abstract class PipelineStep<T, R> { } /** - * Converts the value to a vector of given type (or a vector if Any, or Attributable). Null and - * missing values are forwarded. + * Converts the value to a vector of given type (or a vector if Any, or Attributable). Null, + * missing and primitive of given type are forwarded. Primitive values of other types are + * converted to primitive value of the target type. */ public static final class CoercionStep<T, V> extends PipelineStep<T, V> { public final RType type; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java new file mode 100644 index 0000000000..3987f7dfb1 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitBaseEnvCallDispatcher.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.function.call; + +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.r.nodes.access.variables.LocalReadVariableNode; +import com.oracle.truffle.r.nodes.function.GetBaseEnvFrameNode; +import com.oracle.truffle.r.runtime.ArgumentsSignature; +import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; +import com.oracle.truffle.r.runtime.data.RFunction; + +/** + * Helper node that allows to call a function from base environment by name. This node makes + * assumption that a function in base environment is not going to change and can be cached. + */ +public abstract class RExplicitBaseEnvCallDispatcher extends Node { + private final BranchProfile errorProfile = BranchProfile.create(); + @Child private LocalReadVariableNode readFunc; + @Child RExplicitCallNode callNode = RExplicitCallNode.create(); + @Child GetBaseEnvFrameNode getBaseEnvFrameNode = GetBaseEnvFrameNode.create(); + + public RExplicitBaseEnvCallDispatcher(LocalReadVariableNode readFunc) { + this.readFunc = readFunc; + } + + public static RExplicitBaseEnvCallDispatcher create(String funcName) { + return RExplicitBaseEnvCallDispatcherNodeGen.create(LocalReadVariableNode.create(funcName, true)); + } + + /** + * Helper method that wraps the argument into {@link RArgsValuesAndNames} and invokes the + * {@link #execute(VirtualFrame, RArgsValuesAndNames)} method. + */ + public Object call(VirtualFrame frame, Object target) { + return execute(frame, new RArgsValuesAndNames(new Object[]{target}, ArgumentsSignature.empty(1))); + } + + public abstract Object execute(VirtualFrame frame, RArgsValuesAndNames arguments); + + @Specialization + public Object doCached(VirtualFrame frame, RArgsValuesAndNames arguments, + @Cached("getFunction(frame)") RFunction function) { + return callNode.execute(frame, function, arguments); + } + + RFunction getFunction(VirtualFrame frame) { + Object function = readFunc.execute(frame, getBaseEnvFrameNode.execute()); + assert function instanceof RFunction : "unexpected that '" + readFunc.getIdentifier() + "' in base environment is not a function"; + return (RFunction) function; + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java index c2c0162bfa..7f6354db2f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/RExplicitCallNode.java @@ -30,7 +30,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.r.nodes.access.FrameSlotNode; import com.oracle.truffle.r.nodes.function.RCallBaseNode; import com.oracle.truffle.r.nodes.function.RCallNode; -import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RFunction; @@ -44,14 +43,6 @@ public abstract class RExplicitCallNode extends Node { public abstract Object execute(VirtualFrame frame, RFunction function, RArgsValuesAndNames args); - /** - * Helper method that wraps the argument into {@link RArgsValuesAndNames} and invokes the - * {@link #execute(VirtualFrame, RFunction, RArgsValuesAndNames)} method. - */ - public Object call(VirtualFrame frame, RFunction function, Object arg1) { - return execute(frame, function, new RArgsValuesAndNames(new Object[]{arg1}, ArgumentsSignature.empty(1))); - } - @Specialization Object doCall(VirtualFrame frame, RFunction function, RArgsValuesAndNames args, @SuppressWarnings("unused") @Cached("createArgsIdentifier()") Object argsIdentifier, diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index 8f3c3a3a6d..804775fef9 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -748,7 +748,7 @@ public final class RError extends RuntimeException { SYSTEM_CHAR_ARG("non-empty character argument expected"), SYSTEM_INTERN_NOT_NA("'intern' must be logical and not NA"), NO_SUCH_FILE("cannot open file '%s': No such file or directory"), - NON_STRING_ARG_TO_INTERNAL_PASTE("non-string argument to Internal paste"), + NON_STRING_ARG_TO_INTERNAL_PASTE("non-string argument to internal 'paste'"), INVALID_STRING_IN_STOP(" [invalid string in stop(.)]"), INVALID_STRING_IN_WARNING(" [invalid string in warning(.)]"), ERR_MSG_MUST_BE_STRING("error message must be a character string"), 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 c96eb6ba63..1fd73b12c7 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 @@ -39697,6 +39697,26 @@ character(0) #{ paste(sep="") } character(0) +##com.oracle.truffle.r.test.builtins.TestBuiltin_paste.testPasteWithS3AsCharacter# +#{ as.character.myc <- function(x) '42'; val <- 'hello'; class(val) <- 'myc'; paste(val, 'world') } +[1] "hello world" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_paste.testPasteWithS3AsCharacter# +#{ as.character.myc <- function(x) '42'; val <- 3.14; class(val) <- 'myc'; paste(val, 'world') } +[1] "42 world" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_paste.testPasteWithS3AsCharacter# +#{ as.character.myc <- function(x) 42; val <- 3.14; class(val) <- 'myc'; paste(val, 'world') } +Error in paste(val, "world") : non-string argument to internal 'paste' + +##com.oracle.truffle.r.test.builtins.TestBuiltin_paste.testPasteWithS3AsCharacter# +#{ as.character.myc <- function(x) NULL; val <- 3.14; class(val) <- 'myc'; paste(val, 'world') } +Error in paste(val, "world") : non-string argument to internal 'paste' + +##com.oracle.truffle.r.test.builtins.TestBuiltin_paste.testPasteWithS3AsCharacter# +#{ assign('as.character.myc', function(x) '42', envir=.__S3MethodsTable__.); val <- 3.14; class(val) <- 'myc'; res <- paste(val, 'world'); rm('as.character.myc', envir=.__S3MethodsTable__.); res } +[1] "42 world" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_paste.testpaste1# #argv <- list(list('%% ~~objects to See Also as', '\\code{\\link{~~fun~~}}, ~~~'), ' ', NULL); .Internal(paste(argv[[1]], argv[[2]], argv[[3]])) [1] "%% ~~objects to See Also as \\code{\\link{~~fun~~}}, ~~~" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_paste.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_paste.java index 1aadc8d800..15183cd6f3 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_paste.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_paste.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. */ @@ -81,4 +81,15 @@ public class TestBuiltin_paste extends TestBase { assertEval("{ paste(sep=\"\") }"); assertEval("{ paste(1:2, 1:3, FALSE, collapse=\"-\", sep=\"+\") }"); } + + @Test + public void testPasteWithS3AsCharacter() { + // Note the catch: class on strings is ignored.... + assertEval("{ as.character.myc <- function(x) '42'; val <- 'hello'; class(val) <- 'myc'; paste(val, 'world') }"); + // Next 2 tests should show error since 42, nor NULL is not character + assertEval("{ as.character.myc <- function(x) 42; val <- 3.14; class(val) <- 'myc'; paste(val, 'world') }"); + assertEval("{ as.character.myc <- function(x) NULL; val <- 3.14; class(val) <- 'myc'; paste(val, 'world') }"); + assertEval("{ as.character.myc <- function(x) '42'; val <- 3.14; class(val) <- 'myc'; paste(val, 'world') }"); + assertEval("{ assign('as.character.myc', function(x) '42', envir=.__S3MethodsTable__.); val <- 3.14; class(val) <- 'myc'; res <- paste(val, 'world'); rm('as.character.myc', envir=.__S3MethodsTable__.); res }"); + } } -- GitLab