diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java index 6b92ed012bc8d64b274e128c4d31bc06a2a734bd..711f5ae303de668a2623bf03ef81ce4163385146 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java @@ -509,6 +509,7 @@ public class BasePackage extends RBuiltinPackage { add(UpdateLevels.class, UpdateLevelsNodeGen::create); add(UpdateNames.class, UpdateNamesNodeGen::create); add(UpdateOldClass.class, UpdateOldClassNodeGen::create); + add(UpdateSlot.class, UpdateSlotNodeGen::create); add(UpdateStorageMode.class, UpdateStorageModeNodeGen::create); add(UpdateSubstr.class, UpdateSubstrNodeGen::create); add(UpperTri.class, UpperTriNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java index 019d0c5541331f5cb48de4bfd9dfcefc63bb3e5a..9498cd4c134972b677f26757f08241c46ae41c22 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java @@ -13,34 +13,21 @@ package com.oracle.truffle.r.nodes.builtin.base; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.access.AccessSlotNode; +import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen; import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; -import com.oracle.truffle.r.nodes.attributes.AttributeAccess; -import com.oracle.truffle.r.nodes.attributes.AttributeAccessNodeGen; import com.oracle.truffle.r.nodes.builtin.*; -import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; -import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen; import com.oracle.truffle.r.nodes.function.WrapArgumentNode; -import com.oracle.truffle.r.nodes.unary.TypeofNode; -import com.oracle.truffle.r.nodes.unary.TypeofNodeGen; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.context.RContext; -import com.oracle.truffle.r.runtime.data.*; -import com.oracle.truffle.r.runtime.data.model.*; -import com.oracle.truffle.r.runtime.env.REnvironment; +import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.RSymbol; @RBuiltin(name = "@", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = 1) public abstract class Slot extends RBuiltinNode { - private final RAttributeProfiles attrProfiles = RAttributeProfiles.create(); - @Child private ClassHierarchyNode classHierarchy; - @Child private TypeofNode typeofNode; - - protected AttributeAccess createAttrAccess(String name) { - return AttributeAccessNodeGen.create(name); - } + @Child AccessSlotNode accessSlotNode = AccessSlotNodeGen.create(null, null); protected String getName(Object nameObj) { if (nameObj instanceof RPromise) { @@ -63,103 +50,9 @@ public abstract class Slot extends RBuiltinNode { throw RError.error(this, RError.Message.GENERIC, "invalid type or length for slot name"); } - private Object getSlotS4Internal(RS4Object object, String name, Object value) { - if (value == null) { - if (name == RRuntime.DOT_S3_CLASS) { - // TODO: this will not work if `@` function is called directly, as in: - // `@`(x, ".S3Class") - // in general, treatment of the name parameter has to be finessed to be - // fully compatible with GNU R on direct calls to `@` function - if (classHierarchy == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - classHierarchy = insert(ClassHierarchyNodeGen.create(true)); - } - return classHierarchy.execute(object); - } else if (name == RRuntime.DOT_DATA) { - return getDataPart(object); - } else if (name == RRuntime.NAMES_ATTR_KEY && object instanceof RAbstractVector) { - assert false; // RS4Object can never be a vector? - return RNull.instance; - } - - RStringVector classAttr = object.getClassAttr(attrProfiles); - if (classAttr == null) { - if (typeofNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - typeofNode = insert(TypeofNodeGen.create()); - } - throw RError.error(this, RError.Message.SLOT_CANNOT_GET, name, typeofNode.execute(object).getName()); - } else { - throw RError.error(this, RError.Message.SLOT_NONE, name, classAttr.getLength() == 0 ? RRuntime.STRING_NA : classAttr.getDataAt(0)); - } - } - if (value == RRuntime.NULL_STR_VECTOR) { - return RNull.instance; - } else { - return value; - } - } - - @Specialization(guards = "getName(nameObj) == cachedName") - protected Object getSlotS4Cached(RS4Object object, @SuppressWarnings("unused") Object nameObj, @Cached("getName(nameObj)") String cachedName, - @Cached("createAttrAccess(cachedName)") AttributeAccess attrAccess) { - Object value = attrAccess.execute(object.getAttributes()); - return getSlotS4Internal(object, cachedName, value); - } - - @Specialization(contains = "getSlotS4Cached") - protected Object getSlotS4(RS4Object object, Object nameObj) { - String name = getName(nameObj); - Object value = object.getAttr(attrProfiles, name.intern()); - return getSlotS4Internal(object, name, value); - } - - protected RFunction getDataPartFunction(REnvironment methodsNamespace) { - Object f = methodsNamespace.findFunction("getDataPart"); - return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f); - } - - protected REnvironment getMethodsNamespace() { - return REnvironment.getRegisteredNamespace("methods"); - } - - private Object getDataPart(Object object) { - // TODO: any way to cache it or use a mechanism similar to overrides? - REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); - RFunction dataPart = getDataPartFunction(methodsNamespace); - return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), object); - } - - @SuppressWarnings("unused") - @Specialization(guards = "isDotData(getName(nameObj))") - protected Object getSlotNonS4(RAbstractContainer object, Object nameObj) { - return getDataPart(object); + @Specialization + protected Object getSlot(Object object, Object nameObj) { + return accessSlotNode.executeAccess(object, getName(nameObj)); } - // this is really a fallback specialization but @Fallback does not work here (because of the - // type of "object"?) - @Specialization(guards = "!isDotData(getName(nameObj))") - protected Object getSlot(RAbstractContainer object, Object nameObj) { - // first argument is wrong - String name = getName(nameObj); - - RStringVector classAttr = object.getClassAttr(attrProfiles); - if (classAttr == null) { - RStringVector implicitClassVec = object.getImplicitClass(); - assert implicitClassVec.getLength() > 0; - throw RError.error(this, RError.Message.SLOT_BASIC_CLASS, name, implicitClassVec.getDataAt(0)); - } else { - assert classAttr.getLength() > 0; - throw RError.error(this, RError.Message.SLOT_NON_S4, name, classAttr.getDataAt(0)); - } - } - - protected boolean isS4(Object o) { - return o instanceof RS4Object; - } - - protected boolean isDotData(String name) { - // see comment on usinq object equality in getSlotS4() - return name == RRuntime.DOT_DATA; - } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java new file mode 100644 index 0000000000000000000000000000000000000000..e852ca0caec51da35ceaec92dbfc26080d393fd0 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java @@ -0,0 +1,102 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka + * Copyright (c) 1995-2014, The R Core Team + * Copyright (c) 2002-2008, The R Foundation + * Copyright (c) 2015, Oracle and/or its affiliates + * + * All rights reserved. + */ + +package com.oracle.truffle.r.nodes.builtin.base; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.nodes.access.ConstantNode; +import com.oracle.truffle.r.nodes.access.UpdateSlotNode; +import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen; +import com.oracle.truffle.r.nodes.function.RCallNode; +import com.oracle.truffle.r.nodes.function.WrapArgumentNode; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RAttributable; +import com.oracle.truffle.r.runtime.data.RFunction; +import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.RS4Object; +import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.data.RSymbol; +import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; +import com.oracle.truffle.r.runtime.env.REnvironment; + +@RBuiltin(name = "@<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "", ""}, nonEvalArgs = 1) +public abstract class UpdateSlot extends RBuiltinNode { + + @CompilationFinal RFunction checkSlotAssign; + @Child private ClassHierarchyNode objClassHierarchy; + @Child private ClassHierarchyNode valClassHierarchy; + @Child UpdateSlotNode updateSlotNode = com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen.create(null, null, null); + + protected String getName(Object nameObj) { + if (nameObj instanceof RPromise) { + Object rep = ((RPromise) nameObj).getRep(); + if (rep instanceof WrapArgumentNode) { + rep = ((WrapArgumentNode) rep).getOperand(); + } + if (rep instanceof ConstantNode) { + Object val = ((ConstantNode) rep).getValue(); + if (val instanceof String) { + return (String) val; + } + if (val instanceof RSymbol) { + return ((RSymbol) val).getName(); + } + } else if (rep instanceof ReadVariableNode) { + return ((ReadVariableNode) rep).getIdentifier(); + } else if (rep instanceof RCallNode) { + throw RError.error(this, RError.Message.SLOT_INVALID_TYPE, "language"); + } + } + // TODO: this is not quite correct, but I wonder if we even reach here (can also be + // augmented on demand) + throw RError.error(this, RError.Message.SLOT_INVALID_TYPE, nameObj.getClass().toString()); + } + + private void checkSlotAssign(VirtualFrame frame, RAttributable object, String name, Object value) { + // TODO: optimize using a mechanism similar to overrides? + if (checkSlotAssign == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); + Object f = methodsNamespace.findFunction("checkAtAssignment"); + checkSlotAssign = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f); + assert objClassHierarchy == null && valClassHierarchy == null; + objClassHierarchy = insert(ClassHierarchyNodeGen.create(true)); + valClassHierarchy = insert(ClassHierarchyNodeGen.create(true)); + + } + RStringVector objClass = objClassHierarchy.execute(object); + RStringVector valClass = objClassHierarchy.execute(value); + RContext.getEngine().evalFunction(checkSlotAssign, frame.materialize(), objClass, name, valClass); + } + + @Specialization + protected Object updateSlot(VirtualFrame frame, RS4Object object, Object nameObj, Object value) { + String name = getName(nameObj); + checkSlotAssign(frame, object, name, value); + return updateSlotNode.executeUpdate(object, name, value); + } + + @Specialization + protected Object updateSlot(VirtualFrame frame, RAbstractContainer object, Object nameObj, Object value) { + String name = getName(nameObj); + checkSlotAssign(frame, object, name, value); + return updateSlotNode.executeUpdate(object, name, value); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java index 7ac82e548d2df902165d96e1a778d1581416aa34..cdbb0cdd86aede92e42822eab018c540faf3f5e3 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java @@ -423,7 +423,7 @@ public final class RTruffleVisitor extends BasicVisitor<RSyntaxNode> { replacementArg = callArgAst.accept(this); RCallNode replacementCall = prepareReplacementCall(fAst, args, tmpSymbol, rhsSymbol, false); assignFromTemp = doReplacementLeftHandSide(callArgAst.getLhs(), true, replacementCall, replacement.isSuper(), replacement.getSource(), (receiver, rhsAccess) -> { - return createFieldUpdate(null, receiver, rhsAccess, callArgAst.getFieldName()); + return createFieldUpdate(null, receiver, rhsAccess, callArgAst.getFieldName(), callArgAst.isAt()); }).asRNode(); } RSyntaxNode result = constructReplacementSuffix(rhs, replacementArg, true, assignFromTemp, tmpSymbol, rhsSymbol, replacement.getSource()); @@ -509,8 +509,8 @@ public final class RTruffleVisitor extends BasicVisitor<RSyntaxNode> { return RCallNode.createCall(callSource, function, ArgumentsSignature.empty(2), lhs, ConstantNode.create(callSource, access.getFieldName())); } - private static RCallNode createFieldUpdate(SourceSection source, RSyntaxNode receiver, RSyntaxNode rhs, String fieldName) { - ReadVariableNode function = ReadVariableNode.createForced(source, "$<-", RType.Function); + private static RCallNode createFieldUpdate(SourceSection source, RSyntaxNode receiver, RSyntaxNode rhs, String fieldName, boolean at) { + ReadVariableNode function = ReadVariableNode.createForced(source, at ? "@<-" : "$<-", RType.Function); return RCallNode.createCall(source, function, ArgumentsSignature.empty(3), receiver, ConstantNode.create(source, fieldName), rhs); } @@ -564,7 +564,7 @@ public final class RTruffleVisitor extends BasicVisitor<RSyntaxNode> { RCallNode updateOp = updateFunction.apply(accessAST.accept(this), rhs); checkAssignSourceSection(updateOp, source); result = doReplacementLeftHandSide(accessAST.getLhs(), false, updateOp, isSuper, source, (receiver1, rhsAccess1) -> { - return createFieldUpdate(null, receiver1, rhsAccess1, accessAST.getFieldName()); + return createFieldUpdate(null, receiver1, rhsAccess1, accessAST.getFieldName(), accessAST.isAt()); }); } else { throw RInternalError.unimplemented(); @@ -594,7 +594,7 @@ public final class RTruffleVisitor extends BasicVisitor<RSyntaxNode> { FieldAccess a = u.getVector(); RSyntaxNode rhs = u.getRHS().accept(this); return doReplacementLeftHandSide(a.getLhs(), true, rhs, u.isSuper(), u.getSource(), (receiver, rhsAccess) -> { - return createFieldUpdate(u.getSource(), receiver, rhsAccess, a.getFieldName()); + return createFieldUpdate(u.getSource(), receiver, rhsAccess, a.getFieldName(), a.isAt()); }); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java new file mode 100644 index 0000000000000000000000000000000000000000..ea32f12a3f15bbfb46bc3c91a2cf3ca794517854 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java @@ -0,0 +1,159 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka + * Copyright (c) 1995-2014, The R Core Team + * Copyright (c) 2002-2008, The R Foundation + * Copyright (c) 2015, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.nodes.access; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.attributes.AttributeAccess; +import com.oracle.truffle.r.nodes.attributes.AttributeAccessNodeGen; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen; +import com.oracle.truffle.r.nodes.function.WrapArgumentNode; +import com.oracle.truffle.r.nodes.unary.TypeofNode; +import com.oracle.truffle.r.nodes.unary.TypeofNodeGen; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.env.*; +import com.oracle.truffle.r.runtime.nodes.*; + +/** + * Perform a slot access. This node represents the {@code @} operator in R. + */ +@NodeChildren({@NodeChild(value = "object", type = RNode.class), @NodeChild(value = "name", type = RNode.class)}) +public abstract class AccessSlotNode extends RNode { + + public abstract Object executeAccess(Object o, String name); + + private final RAttributeProfiles attrProfiles = RAttributeProfiles.create(); + @Child private ClassHierarchyNode classHierarchy; + @Child private TypeofNode typeofNode; + + protected AttributeAccess createAttrAccess(String name) { + return AttributeAccessNodeGen.create(name); + } + + protected String getName(Object nameObj) { + if (nameObj instanceof RPromise) { + Object rep = ((RPromise) nameObj).getRep(); + if (rep instanceof WrapArgumentNode) { + rep = ((WrapArgumentNode) rep).getOperand(); + } + if (rep instanceof ConstantNode) { + Object val = ((ConstantNode) rep).getValue(); + if (val instanceof String) { + return (String) val; + } + if (val instanceof RSymbol) { + return ((RSymbol) val).getName(); + } + } else if (rep instanceof ReadVariableNode) { + return ((ReadVariableNode) rep).getIdentifier(); + } + } + throw RError.error(this, RError.Message.GENERIC, "invalid type or length for slot name"); + } + + private Object getSlotS4Internal(RS4Object object, String name, Object value) { + if (value == null) { + if (name == RRuntime.DOT_S3_CLASS) { + // TODO: this will not work if `@` function is called directly, as in: + // `@`(x, ".S3Class") + // in general, treatment of the name parameter has to be finessed to be + // fully compatible with GNU R on direct calls to `@` function + if (classHierarchy == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + classHierarchy = insert(ClassHierarchyNodeGen.create(true)); + } + return classHierarchy.execute(object); + } else if (name == RRuntime.DOT_DATA) { + return getDataPart(object); + } else if (name == RRuntime.NAMES_ATTR_KEY && object instanceof RAbstractVector) { + assert false; // RS4Object can never be a vector? + return RNull.instance; + } + + RStringVector classAttr = object.getClassAttr(attrProfiles); + if (classAttr == null) { + if (typeofNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + typeofNode = insert(TypeofNodeGen.create()); + } + throw RError.error(this, RError.Message.SLOT_CANNOT_GET, name, typeofNode.execute(object).getName()); + } else { + throw RError.error(this, RError.Message.SLOT_NONE, name, classAttr.getLength() == 0 ? RRuntime.STRING_NA : classAttr.getDataAt(0)); + } + } + if (value == RRuntime.NULL_STR_VECTOR) { + return RNull.instance; + } else { + return value; + } + } + + @Specialization(guards = "name == cachedName") + protected Object getSlotS4Cached(RS4Object object, @SuppressWarnings("unused") String name, @Cached("name") String cachedName, @Cached("createAttrAccess(cachedName)") AttributeAccess attrAccess) { + Object value = attrAccess.execute(object.getAttributes()); + return getSlotS4Internal(object, cachedName, value); + } + + @Specialization(contains = "getSlotS4Cached") + protected Object getSlotS4(RS4Object object, String name) { + Object value = object.getAttr(attrProfiles, name.intern()); + return getSlotS4Internal(object, name, value); + } + + protected RFunction getDataPartFunction(REnvironment methodsNamespace) { + Object f = methodsNamespace.findFunction("getDataPart"); + return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f); + } + + protected REnvironment getMethodsNamespace() { + return REnvironment.getRegisteredNamespace("methods"); + } + + private Object getDataPart(Object object) { + // TODO: any way to cache it or use a mechanism similar to overrides? + REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); + RFunction dataPart = getDataPartFunction(methodsNamespace); + return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), object); + } + + @SuppressWarnings("unused") + @Specialization(guards = "isDotData(name)") + protected Object getSlotNonS4(RAbstractContainer object, String name) { + return getDataPart(object); + } + + // this is really a fallback specialization but @Fallback does not work here (because of the + // type of "object"?) + @Specialization(guards = "!isDotData(name)") + protected Object getSlot(RAbstractContainer object, String name) { + RStringVector classAttr = object.getClassAttr(attrProfiles); + if (classAttr == null) { + RStringVector implicitClassVec = object.getImplicitClass(); + assert implicitClassVec.getLength() > 0; + throw RError.error(this, RError.Message.SLOT_BASIC_CLASS, name, implicitClassVec.getDataAt(0)); + } else { + assert classAttr.getLength() > 0; + throw RError.error(this, RError.Message.SLOT_NON_S4, name, classAttr.getDataAt(0)); + } + } + + protected boolean isDotData(String name) { + // see comment on usinq object equality in getSlotS4() + return name == RRuntime.DOT_DATA; + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java new file mode 100644 index 0000000000000000000000000000000000000000..c5c76344168ed2056b8d601a9a1d985816611dfa --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java @@ -0,0 +1,76 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (c) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka + * Copyright (c) 1995-2014, The R Core Team + * Copyright (c) 2002-2008, The R Foundation + * Copyright (c) 2015, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.nodes.access; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.attributes.PutAttributeNode; +import com.oracle.truffle.r.nodes.attributes.PutAttributeNodeGen; +import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.nodes.*; + +@NodeChildren({@NodeChild(value = "object", type = RNode.class), @NodeChild(value = "name", type = RNode.class), @NodeChild(value = "value", type = RNode.class)}) +public abstract class UpdateSlotNode extends RNode { + + public abstract Object executeUpdate(Object object, String name, Object value); + + @CompilationFinal RFunction checkSlotAssign; + @Child private ClassHierarchyNode objClassHierarchy; + @Child private ClassHierarchyNode valClassHierarchy; + + protected PutAttributeNode createAttrUpdate(String name) { + return PutAttributeNodeGen.create(name); + } + + private static Object getActualValue(Object value) { + if (value == RNull.instance) { + return RRuntime.NULL_STR_VECTOR; + } else { + return value; + } + } + + @SuppressWarnings("unused") + @Specialization(guards = "name == cachedName") + protected Object updateSlotS4Cached(RS4Object object, String name, Object value, @Cached("name") String cachedName, @Cached("createAttrUpdate(cachedName)") PutAttributeNode attributeUpdate) { + Object actualValue = getActualValue(value); + attributeUpdate.execute(object.getAttributes(), actualValue); + return object; + } + + @Specialization(contains = "updateSlotS4Cached") + protected Object updateSlotS4(RS4Object object, String name, Object value) { + Object actualValue = getActualValue(value); + object.setAttr(name.intern(), actualValue); + return object; + } + + @SuppressWarnings("unused") + @Specialization(guards = "name == cachedName") + protected Object updateSlotCached(RAbstractContainer object, String name, Object value, @Cached("name") String cachedName, @Cached("createAttrUpdate(cachedName)") PutAttributeNode attributeUpdate) { + Object actualValue = getActualValue(value); + attributeUpdate.execute(object.getAttributes(), actualValue); + return object; + } + + @Specialization(contains = "updateSlotCached") + protected Object updateSlot(RAbstractContainer object, String name, Object value) { + Object actualValue = getActualValue(value); + object.setAttr(name.intern(), actualValue); + return object; + } + +} 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 2bb431e255a98124e0f2b9943128e03dba715043..2d10890fec592c02e08704c68b344f40a63498c3 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 @@ -636,7 +636,8 @@ public final class RError extends RuntimeException { SLOT_BASIC_CLASS("trying to get slot \"%s\" from an object of a basic class (\"%s\") with no slots"), SLOT_NON_S4("trying to get slot \"%s\" from an object (class \"%s\") that is not an S4 object "), SLOT_CANNOT_GET("cannot get a slot (\"%s\") from an object of type \"%s\""), - SLOT_NONE("no slot of name \"%s\" for this object of class \"%s\""); + SLOT_NONE("no slot of name \"%s\" for this object of class \"%s\""), + SLOT_INVALID_TYPE("invalid type '%s' for slot name"); public final String message; final boolean hasArgs; 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 40080dfa9b45280f60b77130bd34851032bf07ce..f155e4810d97a6e8284d8c0a53697f43b5fe4eac 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 @@ -41,6 +41,10 @@ public class TestS4 extends TestBase { assertEval(Output.ContainsError, "{ getClass(\"ClassUnionRepresentation\")@foo }"); assertEval(Output.ContainsError, "{ c(42)@foo }"); assertEval(Output.ContainsError, "{ x<-c(42); class(x)<-\"bar\"; x@foo }"); + } + @Test + public void testSlotUpdate() { + assertEval("{ x<-getClass(\"ClassUnionRepresentation\"); x@virtual<-TRUE; x@virtual }"); } } diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index 728d6ff93332e1bbc1b71c51631dc3c2c8e1b02a..ac032cb5d484de91ebaee7d460faf21483696a61 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -136,7 +136,10 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/U com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java,gnu_r.copyright com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java,purdue.copyright com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java,purdue.copyright +com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java,gnu_r_gentleman_ihaka.copyright com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java,purdue.copyright +com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java,gnu_r_gentleman_ihaka.copyright +com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java,gnu_r_gentleman_ihaka.copyright com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java,purdue.copyright com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java,purdue.copyright com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java,purdue.copyright