diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java index 15024aa6274a18607b024c96010739fb926e2c38..24ccf148b3164dbc2e6d0e8843534e1e951abfd3 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java @@ -44,6 +44,7 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.PrimitiveMethodsInfo; import com.oracle.truffle.r.runtime.PrimitiveMethodsInfo.MethodCode; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.context.RContext; @@ -189,10 +190,13 @@ public class MethodsListDispatch { return RRuntime.asLogical(prev); } boolean value = RRuntime.fromLogical(onOff); - RContext.getInstance().setMethodTableDispatchOn(value); - if (value != prev) { - // TODO + if (!value) { + warning(Message.GENERIC, "FastR does not support R_set_method_dispatch(FALSE) yet. S4 dispatch may not work correctly."); } + // StandardGeneric, the default one (true case) is currently implemented in FastR, + // the other one is in GnuR implemented by R_standardGeneric and is not implemented + // in FastR yet. + RContext.getInstance().setMethodTableDispatchOn(value); return RRuntime.asLogical(prev); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java index 1663b1ff5ace4d4cab3bd9c25712f45d953c911d..adc9076fd6b91177070fe04b597ced131219d695 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java @@ -27,16 +27,18 @@ import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC; import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; -import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode; -import com.oracle.truffle.r.nodes.access.vector.ExtractListElement; import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.nodes.helpers.AccessListField; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; @@ -44,39 +46,33 @@ import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.builtins.RSpecialFactory; import com.oracle.truffle.r.runtime.data.RList; -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.RAbstractVector; import com.oracle.truffle.r.runtime.nodes.RNode; +@NodeInfo(cost = NodeCost.NONE) @NodeChild(value = "arguments", type = RNode[].class) -abstract class AccessFieldSpecial extends SpecialsUtils.ListFieldSpecialBase { +abstract class AccessFieldSpecial extends RNode { - @Child private ExtractListElement extractListElement = ExtractListElement.create(); + @Child private AccessListField accessListField; - @Specialization(limit = "2", guards = {"getNamesNode.getNames(list) == cachedNames", "field == cachedField"}) - public Object doList(RList list, @SuppressWarnings("unused") String field, - @SuppressWarnings("unused") @Cached("list.getNames()") RStringVector cachedNames, - @SuppressWarnings("unused") @Cached("field") String cachedField, - @Cached("getIndex(cachedNames, field)") int index) { - if (index == -1) { - throw RSpecialFactory.throwFullCallNeeded(); + @Specialization + public Object doList(RList list, String field) { + if (accessListField == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + accessListField = insert(AccessListField.create()); } - return extractListElement.execute(list, index); - } - - @Specialization(replaces = "doList") - public Object doListDynamic(RList list, String field) { - int index = getIndex(getNamesNode.getNames(list), field); - if (index == -1) { + Object result = accessListField.execute(list, field); + if (result == null) { throw RSpecialFactory.throwFullCallNeeded(); + } else { + return accessListField.execute(list, field); } - return extractListElement.execute(list, index); } @Fallback @SuppressWarnings("unused") - public void doFallback(Object container, Object field) { + public Object doFallback(Object container, Object field) { throw RSpecialFactory.throwFullCallNeeded(); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java index 2d25546585e829459ca483a694e24bd4745cd46c..add7367ea2fa6a0b51217e0b763c8e0559dc305e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java @@ -23,7 +23,6 @@ package com.oracle.truffle.r.nodes.builtin.base.infix; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; @@ -32,7 +31,6 @@ import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.r.nodes.attributes.HasAttributesNode; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode; -import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode; import com.oracle.truffle.r.nodes.builtin.base.infix.SpecialsUtilsFactory.ConvertIndexNodeGen; import com.oracle.truffle.r.nodes.builtin.base.infix.SpecialsUtilsFactory.ConvertValueNodeGen; import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; @@ -40,7 +38,6 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.data.RDoubleVector; import com.oracle.truffle.r.runtime.data.RIntVector; -import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @@ -116,37 +113,6 @@ class SpecialsUtils { } } - /** - * Common code shared between specials accessing/updating fields. - */ - abstract static class ListFieldSpecialBase extends RNode { - - @Child protected GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); - - protected static int getIndex(RStringVector names, String field) { - if (names != null) { - int fieldHash = field.hashCode(); - for (int i = 0; i < names.getLength(); i++) { - String current = names.getDataAt(i); - if (current == field || hashCodeEquals(current, fieldHash) && contentsEquals(current, field)) { - return i; - } - } - } - return -1; - } - - @TruffleBoundary - private static boolean contentsEquals(String current, String field) { - return field.equals(current); - } - - @TruffleBoundary - private static boolean hashCodeEquals(String current, int fieldHash) { - return current.hashCode() == fieldHash; - } - } - @NodeInfo(cost = NodeCost.NONE) @NodeChild(value = "delegate", type = RNode.class) public abstract static class ConvertIndex extends RNode { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java index b73fcb9cc98050b48c2d5f5652c1c962323a10b9..c62b319f80d872b316e3686b99da2a6267ad8bf4 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java @@ -28,16 +28,17 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode; import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode; import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; -import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; +import com.oracle.truffle.r.nodes.helpers.UpdateListField; import com.oracle.truffle.r.nodes.unary.CastListNode; import com.oracle.truffle.r.nodes.unary.CastListNodeGen; import com.oracle.truffle.r.runtime.ArgumentsSignature; @@ -47,69 +48,43 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.builtins.RSpecialFactory; 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.RAbstractVector; import com.oracle.truffle.r.runtime.nodes.RNode; +@NodeInfo(cost = NodeCost.NONE) @NodeChild(value = "arguments", type = RNode[].class) -abstract class UpdateFieldSpecial extends SpecialsUtils.ListFieldSpecialBase { +abstract class UpdateFieldSpecial extends RNode { - @Child private ShareObjectNode shareObject = ShareObjectNode.create(); + @Child private UpdateListField updateListField; /** * {@link RNull} and lists have special handling when they are RHS of update. Nulls delete the * field and lists can cause cycles. */ - protected static boolean isNotRNullRList(Object value) { + static boolean isNotRNullRList(Object value) { return value != RNull.instance && !(value instanceof RList); } - @Specialization(limit = "2", guards = {"!list.isShared()", "getNamesNode.getNames(list) == cachedNames", "field == cachedField", "isNotRNullRList(value)"}) - public Object doList(RList list, @SuppressWarnings("unused") String field, Object value, - @SuppressWarnings("unused") @Cached("list.getNames()") RStringVector cachedNames, - @SuppressWarnings("unused") @Cached("field") String cachedField, - @Cached("getIndex(cachedNames, field)") int index) { - if (index == -1) { - throw RSpecialFactory.throwFullCallNeeded(value); - } - Object sharedValue = value; - // share only when necessary: - if (list.getDataAt(index) != value) { - sharedValue = getShareObjectNode().execute(value); + @Specialization(guards = {"!list.isShared()", "isNotRNullRList(value)"}) + public Object doList(RList list, String field, Object value) { + if (updateListField == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + updateListField = insert(UpdateListField.create()); } - list.setElement(index, sharedValue); - return list; - } - - @Specialization(replaces = "doList", guards = {"!list.isShared()", "list.getNames() != null", "isNotRNullRList(value)"}) - public RList doListDynamic(RList list, String field, Object value) { - int index = getIndex(getNamesNode.getNames(list), field); - if (index == -1) { + boolean result = updateListField.execute(list, field, value); + if (!result) { throw RSpecialFactory.throwFullCallNeeded(value); + } else { + return list; } - Object sharedValue = value; - // share only when necessary: - if (list.getDataAt(index) != value) { - sharedValue = getShareObjectNode().execute(value); - } - list.setElement(index, sharedValue); - return list; } @SuppressWarnings("unused") @Fallback - public void doFallback(Object container, Object field, Object value) { + public RList doFallback(Object container, Object field, Object value) { throw RSpecialFactory.throwFullCallNeeded(value); } - - private ShareObjectNode getShareObjectNode() { - if (shareObject == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - shareObject = insert(ShareObjectNode.create()); - } - return shareObject; - } } @RBuiltin(name = "$<-", kind = PRIMITIVE, parameterNames = {"", "", "value"}, dispatch = INTERNAL_GENERIC, behavior = PURE) diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java index f6e1fdd15a338630359f8e342e595a831ae2df34..903947939fed214361eb750ec927e7f480a2c503 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java @@ -53,6 +53,7 @@ import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RLogical; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.RS4Object; import com.oracle.truffle.r.runtime.data.RString; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.RTypedValue; @@ -89,6 +90,8 @@ final class CachedExtractVectorNode extends CachedVectorNode { @Child private ExtractDimNamesNode extractDimNames; + @Child private ExtractS4ObjectNode extractS4ObjectNode; + private final ConditionProfile resultHasDimensions = ConditionProfile.createBinaryProfile(); private final ConditionProfile promiseInEnvironment = ConditionProfile.createBinaryProfile(); @@ -109,7 +112,7 @@ final class CachedExtractVectorNode extends CachedVectorNode { this.exact = logicalAsBoolean(exact, DEFAULT_EXACT); this.dropDimensions = logicalAsBoolean(dropDimensions, DEFAULT_DROP_DIMENSION); this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, this.exact, false, recursive); - if (error == null && vectorType != RType.Null && vectorType != RType.Environment) { + if (error == null && vectorType != RType.Null && vectorType != RType.Environment && vectorType != RType.S4Object) { this.writeVectorNode = WriteIndexedVectorNode.create(vectorType, convertedPositions.length, true, false, false, false); } } @@ -148,6 +151,8 @@ final class CachedExtractVectorNode extends CachedVectorNode { * later. */ return doEnvironment((REnvironment) castVector, positions); + case S4Object: + return doS4Object((RS4Object) castVector, positions); case Integer: vector = (RAbstractContainer) castVector; break; @@ -268,6 +273,14 @@ final class CachedExtractVectorNode extends CachedVectorNode { throw error(RError.Message.WRONG_ARGS_SUBSET_ENV); } + private Object doS4Object(RS4Object object, Object[] positions) { + if (extractS4ObjectNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + extractS4ObjectNode = insert(new ExtractS4ObjectNode(mode, exact, dropDimensions)); + } + return extractS4ObjectNode.execute(object, positions); + } + private boolean isMissingSingleDimension() { return numberOfDimensions == 1 && positionsCheckNode.isMissing(); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java index 73b47fda388a25673352a3f2d033143354008629..40e456bf6c06a3da980ffc7cf9054e56d682aaef 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java @@ -57,6 +57,7 @@ import com.oracle.truffle.r.runtime.data.RList; import com.oracle.truffle.r.runtime.data.RMissing; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPairList; +import com.oracle.truffle.r.runtime.data.RS4Object; import com.oracle.truffle.r.runtime.data.RScalarVector; import com.oracle.truffle.r.runtime.data.RShareable; import com.oracle.truffle.r.runtime.data.RStringVector; @@ -102,7 +103,8 @@ final class CachedReplaceVectorNode extends CachedVectorNode { @Child private DeleteElementsNode deleteElementsNode; @Child private SetNamesAttributeNode setNamesNode; - CachedReplaceVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, Class<?> valueClass, RType valueType, boolean updatePositionNames, boolean recursive, boolean isValueGt1) { + CachedReplaceVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, Class<?> valueClass, RType valueType, boolean updatePositionNames, boolean recursive, + boolean ignoreRecursive, boolean isValueGt1) { super(mode, vector, positions, recursive); if (numberOfDimensions == 1 && positions[0] instanceof String || positions[0] instanceof RAbstractStringVector) { @@ -122,7 +124,9 @@ final class CachedReplaceVectorNode extends CachedVectorNode { Object[] convertedPositions = filterPositions(positions); this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, true, true, recursive); - if (castType != null && !castType.isNull()) { + if (vectorType == RType.S4Object) { + replaceS4ObjectNode = new ReplaceS4ObjectNode(mode, ignoreRecursive); + } else if (castType != null && !castType.isNull()) { this.writeVectorNode = WriteIndexedVectorNode.create(castType, convertedPositions.length, false, true, mode.isSubscript() && !isDeleteElements(), true); } } @@ -149,6 +153,12 @@ final class CachedReplaceVectorNode extends CachedVectorNode { Object castVector = vectorClass.cast(originalVector); Object castValue = valueClass.cast(originalValues); + if (vectorType == RType.Environment) { + return doEnvironment((REnvironment) castVector, positions, castValue); + } else if (vectorType == RType.S4Object) { + return doS4Object((RS4Object) castVector, positions, castValue); + } + Object value; if (valueType == RType.Null) { if (vectorType == RType.Null) { @@ -197,8 +207,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode { case PairList: vector = ((RPairList) castVector).toRList(); break; - case Environment: - return doEnvironment((REnvironment) castVector, positions, castValue); case Language: repType = RContext.getRRuntimeASTAccess().getRepType((RLanguage) castVector); vector = RContext.getRRuntimeASTAccess().asList((RLanguage) castVector); @@ -483,6 +491,12 @@ final class CachedReplaceVectorNode extends CachedVectorNode { return env; } + @Child private ReplaceS4ObjectNode replaceS4ObjectNode; + + private Object doS4Object(RS4Object obj, Object[] positions, Object originalValues) { + return replaceS4ObjectNode.execute(obj, positions, originalValues); + } + @NodeInfo(cost = NONE) public abstract static class ValueProfileNode extends Node { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java index bc1808ef44f397cffccff97f64d796d2dfa87c38..595c1452ef73f1fe6d3acff813ca57342c6b85fa 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java @@ -146,6 +146,7 @@ abstract class CachedVectorNode extends RBaseNode { case PairList: case Environment: case Expression: + case S4Object: return true; default: return false; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractS4ObjectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractS4ObjectNode.java new file mode 100644 index 0000000000000000000000000000000000000000..70df3131589553daab5ed53561f33f4a2fb20d76 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractS4ObjectNode.java @@ -0,0 +1,61 @@ +/* + * 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.access.vector; + +import static com.oracle.truffle.r.runtime.RError.Message.OP_NOT_DEFINED_FOR_S4_CLASS; + +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.nodes.objects.GetS4DataSlot; +import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RType; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RS4Object; +import com.oracle.truffle.r.runtime.data.RTypedValue; +import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; + +public class ExtractS4ObjectNode extends Node { + @Child private GetS4DataSlot getS4DataSlotNode = GetS4DataSlotNodeGen.create(RType.Environment); + @Child private ExtractVectorNode extract; + private final boolean exact; + private final boolean dropDimensions; + + public ExtractS4ObjectNode(ElementAccessMode accessMode, boolean exact, boolean dropDimensions) { + this.extract = ExtractVectorNode.create(accessMode, true); + this.exact = exact; + this.dropDimensions = dropDimensions; + } + + public Object execute(RS4Object obj, Object[] positions) { + RTypedValue dataSlot = getS4DataSlotNode.executeObject(obj); + if (dataSlot == RNull.instance) { + throw RError.error(RError.SHOW_CALLER, OP_NOT_DEFINED_FOR_S4_CLASS, "$"); + } + return extract.execute(dataSlot, positions, createLogical(exact), createLogical(dropDimensions)); + } + + private static RAbstractLogicalVector createLogical(boolean b) { + return RDataFactory.createLogicalVectorFromScalar(b); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceS4ObjectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceS4ObjectNode.java new file mode 100644 index 0000000000000000000000000000000000000000..afc04f304e9af843de7d835e03a384c1e7808832 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceS4ObjectNode.java @@ -0,0 +1,53 @@ +/* + * 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.access.vector; + +import static com.oracle.truffle.r.runtime.RError.Message.NO_METHOD_ASSIGNING_SUBSET_S4; + +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.nodes.objects.GetS4DataSlot; +import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RType; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RS4Object; +import com.oracle.truffle.r.runtime.data.RTypedValue; + +public class ReplaceS4ObjectNode extends Node { + @Child private GetS4DataSlot getS4DataSlotNode = GetS4DataSlotNodeGen.create(RType.Environment); + @Child private ReplaceVectorNode replaceVectorNode; + + public ReplaceS4ObjectNode(ElementAccessMode mode, boolean ignoreRecursive) { + replaceVectorNode = ReplaceVectorNode.create(mode, ignoreRecursive); + } + + public Object execute(RS4Object obj, Object[] positions, Object values) { + RTypedValue dataSlot = getS4DataSlotNode.executeObject(obj); + if (dataSlot == RNull.instance) { + throw RError.error(RError.SHOW_CALLER, NO_METHOD_ASSIGNING_SUBSET_S4); + } + // No need to update the data slot, the value is env and they have reference semantics. + replaceVectorNode.execute(dataSlot, positions, values); + return obj; + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java index cef08a1982e2309d3ad3e293ea3a8220dade57e3..f295281ded9fb2cd67094d7e21c4367b724dcc31 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java @@ -189,7 +189,7 @@ public abstract class ReplaceVectorNode extends RBaseNode { protected static CachedReplaceVectorNode createDefaultCached(ReplaceVectorNode node, Object vector, Object[] positions, Object value) { return new CachedReplaceVectorNode(node.mode, (RTypedValue) vector, positions, value.getClass(), RRuntime.isForeignObject(value) ? RType.TruffleObject : ((RTypedValue) value).getRType(), true, - node.recursive, CachedReplaceVectorNode.isValueLengthGreaterThanOne(value)); + node.recursive, node.ignoreRecursive, CachedReplaceVectorNode.isValueLengthGreaterThanOne(value)); } public ElementAccessMode getMode() { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java index ab744903fc74bb2b3cadb87d7d754738ed94428c..aecab8b403bb429c8b83a54b1f5f561f191b12bd 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java @@ -373,7 +373,7 @@ abstract class WriteIndexedVectorNode extends Node { case List: return new WriteListAction(setListElementAsObject, isReplace); default: - throw RInternalError.shouldNotReachHere(); + throw RInternalError.shouldNotReachHere("WriteIndexedScalarNode for " + type); } } 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 5e5e1c83c8ccae8f9d443739fd9bb5b15b5681d9..c9baefdb00e4c3eaa15eb028458d5ee2468b3bd7 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 @@ -604,7 +604,8 @@ public class ArgumentMatcher { } if (!matchedSuppliedArgs[suppliedIndex]) { String suppliedName = signature.getName(suppliedIndex); - if (suppliedName == null || suppliedName.isEmpty() || formalSignature.getName(formalIndex).isEmpty()) { + String formalName = formalSignature.getName(formalIndex); + if (suppliedName == null || suppliedName.isEmpty() || formalName == null || formalName.isEmpty()) { // unnamed parameter, match by position break; } @@ -732,6 +733,10 @@ public class ArgumentMatcher { /** * Searches for partial match of suppliedName inside formalNames and returns its (formal) index. + * If there are no varagrs and no match is found for given named actual argument, then this + * method raises "unused argument" error. However, if there are any null formal arguments, which + * may only happen in the case of builtins, then we let the argument matching proceed to + * positional matching. This situation may happen in S4 dispatch to a builtin, e.g. {@code $<-}. * * @return The position of the given suppliedName inside the formalNames. Throws errors if the * argument has been matched before @@ -739,6 +744,7 @@ public class ArgumentMatcher { private static <T> int findParameterPositionByPartialName(ArgumentsSignature formalsSignature, boolean[] formalsMatchedByExactName, String suppliedName, int[] resultPermutation, int suppliedIndex, boolean hasVarArgs, RBaseNode callingNode, int varArgIndex, IntFunction<String> errorString) { assert suppliedName != null && !suppliedName.isEmpty(); + boolean hasNullFormal = false; int found = MatchPermutation.UNMATCHED; for (int i = 0; i < formalsSignature.getLength(); i++) { if (formalsMatchedByExactName[i]) { @@ -747,6 +753,7 @@ public class ArgumentMatcher { } String formalName = formalsSignature.getName(i); + hasNullFormal |= formalName == null; if (formalName != null) { if (formalName.startsWith(suppliedName) && ((varArgIndex != ArgumentsSignature.NO_VARARG && i < varArgIndex) || varArgIndex == ArgumentsSignature.NO_VARARG)) { // partial-match only if the formal argument is positioned before ... @@ -760,7 +767,7 @@ public class ArgumentMatcher { } } } - if (found >= 0 || hasVarArgs) { + if (found >= 0 || hasVarArgs || hasNullFormal) { return found; } throw callingNode.error(RError.Message.UNUSED_ARGUMENT, errorString.apply(suppliedIndex)); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java index a0911e87fc0b1f6e02287cceb93bf9029f3308ec..19b503bd6b756266d92e884834bccd76bbdbe6fb 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java @@ -66,6 +66,7 @@ import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result; import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode; import com.oracle.truffle.r.nodes.function.call.PrepareArguments; import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; +import com.oracle.truffle.r.nodes.helpers.AccessListField; import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode; import com.oracle.truffle.r.nodes.profile.VectorLengthProfile; import com.oracle.truffle.r.runtime.Arguments; @@ -151,6 +152,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS // needed for INTERNAL_GENERIC calls: @Child private FunctionDispatch internalDispatchCall; + @Child private AccessListField accessListField; protected RCaller createCaller(VirtualFrame frame, RFunction function) { if (explicitArgs == null) { @@ -296,11 +298,13 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS if (isAttributableProfile.profile(dispatchObject instanceof RAttributeStorage) && isS4Profile.profile(((RAttributeStorage) dispatchObject).isS4())) { RList list = (RList) promiseHelper.checkEvaluate(frame, REnvironment.getRegisteredNamespace("methods").get(".BasicFunsList")); - // TODO create a node that looks up the name in the names attribute - int index = list.getElementIndexByName(builtin.getName()); - if (index != -1) { - RFunction basicFun = (RFunction) list.getDataAt(index); - Object result = internalDispatchCall.execute(frame, basicFun, lookupVarArgs(frame), null, null); + if (accessListField == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + accessListField = insert(AccessListField.create()); + } + Object basicFun = accessListField.execute(list, builtin.getName()); + if (basicFun != null) { + Object result = internalDispatchCall.execute(frame, (RFunction) basicFun, lookupVarArgs(frame), null, null); if (result != RRuntime.DEFERRED_DEFAULT_MARKER) { return result; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/AccessListField.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/AccessListField.java new file mode 100644 index 0000000000000000000000000000000000000000..3a9d5365527d8637acea1bb4c0f136e0081a86d7 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/AccessListField.java @@ -0,0 +1,59 @@ +/* + * 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.helpers; + +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.access.vector.ExtractListElement; +import com.oracle.truffle.r.runtime.data.RList; +import com.oracle.truffle.r.runtime.data.RStringVector; + +public abstract class AccessListField extends ListFieldNodeBase { + @Child private ExtractListElement extractListElement = ExtractListElement.create(); + + public static AccessListField create() { + return AccessListFieldNodeGen.create(); + } + + public abstract Object execute(RList list, Object field); + + @Specialization(limit = "2", guards = {"getNamesNode.getNames(list) == cachedNames", "field == cachedField"}) + Object doList(RList list, @SuppressWarnings("unused") String field, + @SuppressWarnings("unused") @Cached("list.getNames()") RStringVector cachedNames, + @SuppressWarnings("unused") @Cached("field") String cachedField, + @Cached("getIndex(cachedNames, field)") int index) { + if (index == -1) { + return null; + } + return extractListElement.execute(list, index); + } + + @Specialization(replaces = "doList") + Object doListDynamic(RList list, String field) { + int index = getIndex(getNamesNode.getNames(list), field); + if (index == -1) { + return null; + } + return extractListElement.execute(list, index); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/ListFieldNodeBase.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/ListFieldNodeBase.java new file mode 100644 index 0000000000000000000000000000000000000000..d35004865df29a277536d1f66fa28c6a5ad336e0 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/ListFieldNodeBase.java @@ -0,0 +1,58 @@ +/* + * 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.helpers; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode; +import com.oracle.truffle.r.runtime.data.RStringVector; + +/** + * Common code shared between nodes accessing/updating list fields by name. + */ +public abstract class ListFieldNodeBase extends Node { + @Child protected GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); + + protected static int getIndex(RStringVector names, String field) { + if (names != null) { + int fieldHash = field.hashCode(); + for (int i = 0; i < names.getLength(); i++) { + String current = names.getDataAt(i); + if (current == field || hashCodeEquals(current, fieldHash) && contentsEquals(current, field)) { + return i; + } + } + } + return -1; + } + + @TruffleBoundary + private static boolean contentsEquals(String current, String field) { + return field.equals(current); + } + + @TruffleBoundary + private static boolean hashCodeEquals(String current, int fieldHash) { + return current.hashCode() == fieldHash; + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/UpdateListField.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/UpdateListField.java new file mode 100644 index 0000000000000000000000000000000000000000..e4536f3561eea5dca299e143e801972fd3d3d6da --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/UpdateListField.java @@ -0,0 +1,82 @@ +/* + * 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.helpers; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; +import com.oracle.truffle.r.runtime.data.RList; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.RStringVector; + +public abstract class UpdateListField extends ListFieldNodeBase { + @Child private ShareObjectNode shareObject = ShareObjectNode.create(); + + public abstract boolean execute(RList target, String field, Object value); + + @Specialization(limit = "2", guards = {"getNamesNode.getNames(list) == cachedNames", "field == cachedField"}) + boolean doList(RList list, @SuppressWarnings("unused") String field, Object value, + @SuppressWarnings("unused") @Cached("list.getNames()") RStringVector cachedNames, + @SuppressWarnings("unused") @Cached("field") String cachedField, + @Cached("getIndex(cachedNames, field)") int index) { + assert value != RNull.instance && !(value instanceof RList) && value != null; + if (index == -1) { + return false; + } + Object sharedValue = value; + // share only when necessary: + if (list.getDataAt(index) != value) { + sharedValue = getShareObjectNode().execute(value); + } + list.setElement(index, sharedValue); + return true; + } + + @Specialization(replaces = "doList", guards = {"list.getNames() != null"}) + boolean doListDynamic(RList list, String field, Object value) { + int index = getIndex(getNamesNode.getNames(list), field); + if (index == -1) { + return false; + } + Object sharedValue = value; + // share only when necessary: + if (list.getDataAt(index) != value) { + sharedValue = getShareObjectNode().execute(value); + } + list.setElement(index, sharedValue); + return true; + } + + private ShareObjectNode getShareObjectNode() { + if (shareObject == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + shareObject = insert(ShareObjectNode.create()); + } + return shareObject; + } + + public static UpdateListField create() { + return UpdateListFieldNodeGen.create(); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java index 4098d1e5d501124ac77f8186066ce82ecb77dcb9..7b425b9f8fac56ae5a24ce3e17564e472dd0e378 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java @@ -23,7 +23,9 @@ import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode; import com.oracle.truffle.r.runtime.RCaller; +import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RStringVector; @@ -39,6 +41,7 @@ public abstract class DispatchGeneric extends RBaseNode { private final BranchProfile equalsMethodRequired = BranchProfile.create(); @Child private LoadMethod loadMethod = LoadMethodNodeGen.create(); @Child private ExecuteMethod executeMethod = new ExecuteMethod(); + @Child private InheritsCheckNode inheritsInternalDispatchCheckNode; @TruffleBoundary private static String createMultiDispatchString(RStringVector classes) { @@ -75,9 +78,11 @@ public abstract class DispatchGeneric extends RBaseNode { RFunction currentFunction = ReadVariableNode.lookupFunction(".InheritForDispatch", methodsEnv.getFrame(), true, true); method = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, RASTUtils.getOriginalCall(this)), true, null, classes, fdef, mtable); } + if (method.isBuiltin() || getInheritsInternalDispatchCheckNode().execute(method)) { + return RRuntime.DEFERRED_DEFAULT_MARKER; + } method = loadMethod.executeRFunction(frame, method, fname); - Object ret = executeMethod.executeObject(frame, method, fname); - return ret; + return executeMethod.executeObject(frame, method, fname); } @SuppressWarnings("unused") @@ -116,4 +121,12 @@ public abstract class DispatchGeneric extends RBaseNode { } return false; } + + private InheritsCheckNode getInheritsInternalDispatchCheckNode() { + if (inheritsInternalDispatchCheckNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + inheritsInternalDispatchCheckNode = insert(new InheritsCheckNode("internalDispatchMethod")); + } + return inheritsInternalDispatchCheckNode; + } } 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 b8abebdd2f56fc9ee65e451556b497bb2765297f..1253fd0cf72ca97ed5573ce9832a9b6545c36525 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 @@ -635,6 +635,7 @@ public final class RError extends RuntimeException implements TruffleException { POS_NOT_ALLOWED_WITH_NUMERIC("pos argument not allowed with a numeric value"), OBJ_CANNOT_BE_ATTRIBUTED("external object cannot be attributed"), CANNOT_COERCE_EXTERNAL_OBJECT_TO_VECTOR("no method for coercing this external object to a %s"), + NO_METHOD_ASSIGNING_SUBSET_S4("no method for assigning subsets of this S4 class"), CANNOT_COERCE_S4_TO_VECTOR("no method for coercing this S4 class to a vector"), // the following list is incomplete (but like GNU-R) INVALID_FORMAT_DOUBLE("invalid format '%s'; use format %%f, %%e, %%g or %%a for numeric objects"), @@ -723,6 +724,7 @@ public final class RError extends RuntimeException implements TruffleException { RNG_SYMBOL("%s not found in user rng library"), CUMMAX_UNDEFINED_FOR_COMPLEX("'cummax' not defined for complex numbers"), CUMMIN_UNDEFINED_FOR_COMPLEX("'cummin' not defined for complex numbers"), + OP_NOT_DEFINED_FOR_S4_CLASS("%s operator not defined for this S4 class"), NMAX_LESS_THAN_ONE("'nmax' must be positive"), CHAR_VEC_ARGUMENT("a character vector argument expected"), QUOTE_G_ONE("only the first character of 'quote' will be used"), 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 a427b2e44237e58209a85f48549e645fc59e6d1f..d0a65ff2df9f1741fc7bbb76308ea41b300ff3f0 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 @@ -467,6 +467,10 @@ Class "optionalMethod", by class "standardGeneric", distance 5 #{ x<-42; y<-asS4(x); isS4(y) } [1] TRUE +##com.oracle.truffle.r.test.S4.TestS4.testDispatchToS3ForBuiltins# +#{ setClass('TestS4S31', representation(f = 'numeric')); p <- new('TestS4S31', f = 2); `$.TestS4S31` <- function(...) 42; p$field } +[1] 42 + ##com.oracle.truffle.r.test.S4.TestS4.testInternalDispatch# #setClass('foo', representation(d='numeric')); setMethod(`$`, signature('foo'), function(x, name) 'FOO'); obj <- new('foo'); obj$asdf [1] "$" @@ -604,6 +608,49 @@ Error in validObject(.Object) : invalid class “SingleInt” object: FALSE #{ setClass('WrappedIntVec', representation(n = 'numeric')); x0 <- new('WrappedIntVec', n = 1); x1 <- x0; x1@n <- 2; x0@n == x1@n } [1] FALSE +##com.oracle.truffle.r.test.S4.TestS4.testRegularFieldAssign# +#{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); attr(obj, '.Data') <- new.env(); obj$fld2 <- 'value'; list(obj, as.list(attr(obj, '.Data')), obj$fld2); } +[[1]] +An object of class "TestS4CornerCases" +Slot "fld": +[1] "xyz" + + +[[2]] +[[2]]$fld2 +[1] "value" + + +[[3]] +[1] "value" + + +##com.oracle.truffle.r.test.S4.TestS4.testRegularFieldAssign# +#{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); attr(obj, '.xData') <- new.env(); obj$fld2 <- 'value'; list(obj, as.list(attr(obj, '.xData')), obj$fld2); } +[[1]] +An object of class "TestS4CornerCases" +Slot "fld": +[1] "xyz" + + +[[2]] +[[2]]$fld2 +[1] "value" + + +[[3]] +[1] "value" + + +##com.oracle.truffle.r.test.S4.TestS4.testRegularFieldAssign#Output.IgnoreErrorContext# +#{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); obj$fld2 <- 'value'; } +Error in `$<-`(`*tmp*`, fld2, value = "value") : + no method for assigning subsets of this S4 class + +##com.oracle.truffle.r.test.S4.TestS4.testRegularFieldAssign#Output.IgnoreErrorContext# +#{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); obj$fld2; } +Error in obj$fld2 : $ operator not defined for this S4 class + ##com.oracle.truffle.r.test.S4.TestS4.testSlotAccess#Output.IgnoreErrorContext# # { x<-42; attr(x, "foo")<-7; x@foo } Error: trying to get slot "foo" from an object of a basic class ("numeric") with no slots @@ -80639,6 +80686,12 @@ Error in rnorm(s = 1, s = 1) : #{ f <- match.fun(length) ; f(c(1,2,3)) } [1] 3 +##com.oracle.truffle.r.test.functions.TestFunctions.testMatching# +#`$<-`(someNonesense = list(), anotherNonesense = 'foo', 42) +$foo +[1] 42 + + ##com.oracle.truffle.r.test.functions.TestFunctions.testMatching# #list(`...`=NULL); $... @@ -159442,10 +159495,6 @@ Error: 'R_get_primname' called on a non-primitive #.Call(methods:::C_R_set_method_dispatch, TRUE) [1] TRUE -##com.oracle.truffle.r.test.library.stats.TestExternal_R_set_method_dispatch.testSetMethodDispatch# -#.Call(methods:::C_R_set_method_dispatch, c(FALSE,TRUE)) -[1] TRUE - ##com.oracle.truffle.r.test.library.stats.TestExternal_Rmd5.testRmd5# #.Call(tools:::Rmd5, "abc") Error in get(name, envir = asNamespace(pkg), inherits = FALSE) : diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java index e054c71b60788326bb17fb7676cf485e902cb1da..d660c79947e21c355d7ccf9d95220e8a66c8e9a9 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java @@ -187,4 +187,17 @@ public class TestS4 extends TestRBase { assertEval("makeActiveBinding('someSymbol10', function(x) { if(missing(x)) print('get0') else print('set0'); NULL }, .GlobalEnv); someSymbol10; someSymbol10 <- 1; makeActiveBinding('someSymbol10', function(x) { if(missing(x)) print('get1') else print('set1'); NULL }, .GlobalEnv); someSymbol10; someSymbol10 <- 1"); assertEval("makeActiveBinding('var_a', function(x) { if(missing(x)) { print('get'); return(123) } else { print('set'); return(x) } }, .GlobalEnv); inherits(var_a, 'numeric')"); } + + @Test + public void testRegularFieldAssign() { + assertEval(Output.IgnoreErrorContext, "{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); obj$fld2 <- 'value'; }"); + assertEval(Output.IgnoreErrorContext, "{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); obj$fld2; }"); + assertEval("{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); attr(obj, '.Data') <- new.env(); obj$fld2 <- 'value'; list(obj, as.list(attr(obj, '.Data')), obj$fld2); }"); + assertEval("{ setClass('TestS4CornerCases', representation(fld = 'character')); obj <- new('TestS4CornerCases', fld = 'xyz'); attr(obj, '.xData') <- new.env(); obj$fld2 <- 'value'; list(obj, as.list(attr(obj, '.xData')), obj$fld2); }"); + } + + @Test + public void testDispatchToS3ForBuiltins() { + assertEval("{ setClass('TestS4S31', representation(f = 'numeric')); p <- new('TestS4S31', f = 2); `$.TestS4S31` <- function(...) 42; p$field }"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java index 00c2f892ebbf33e9aaafe888689c8a07691ddfee..753308f58bab44b4d5610cdac02aae83f1ffce54 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java @@ -259,6 +259,7 @@ public class TestFunctions extends TestBase { // however, two partial matches produce error, even if one is "longer" assertEval("{ foo <- function(xaaa, ...) list(xaa=xaaa, ...); foo(xa=4,xaa=5); }"); assertEval("list(`...`=NULL);"); + assertEval("`$<-`(someNonesense = list(), anotherNonesense = 'foo', 42)"); } @Test diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_set_method_dispatch.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_set_method_dispatch.java index 732977bf7c1cd8b4c5e3368bb8ef3bac2f24d356..79019189b1d23941a26563dc2ac428395ecc4412 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_set_method_dispatch.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_set_method_dispatch.java @@ -31,7 +31,6 @@ public class TestExternal_R_set_method_dispatch extends TestBase { @Test public void testSetMethodDispatch() { assertEval(".Call(methods:::C_R_set_method_dispatch, TRUE)"); - assertEval(".Call(methods:::C_R_set_method_dispatch, c(FALSE,TRUE))"); assertEval(".Call(methods:::C_R_set_method_dispatch, NULL)"); assertEval(".Call(methods:::C_R_set_method_dispatch, 1)"); }