Skip to content

Commit 50e8470

Browse files
committed
Check for immutable types in type.__setattr__
1 parent aa51793 commit 50e8470

File tree

3 files changed

+117
-18
lines changed

3 files changed

+117
-18
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -558,16 +558,15 @@ public static GetAttributeNode create() {
558558

559559
@Builtin(name = J___SETATTR__, minNumOfPositionalArgs = 3)
560560
@GenerateNodeFactory
561-
public abstract static class SetattrNode extends ObjectNodes.AbstractSetattrNode {
562-
@Child WriteAttributeToObjectNode writeNode;
561+
public abstract static class SetattrNode extends PythonTernaryBuiltinNode {
563562

564-
@Override
565-
protected boolean writeAttribute(Object object, TruffleString key, Object value) {
566-
if (writeNode == null) {
567-
CompilerDirectives.transferToInterpreterAndInvalidate();
568-
writeNode = insert(WriteAttributeToObjectNode.create());
569-
}
570-
return writeNode.execute(object, key, value);
563+
@Specialization
564+
Object set(VirtualFrame frame, Object object, Object key, Object value,
565+
@Bind("this") Node inliningTarget,
566+
@Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode,
567+
@Cached WriteAttributeToObjectNode write) {
568+
genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write);
569+
return PNone.NONE;
571570
}
572571

573572
@NeverDefault

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,14 @@
118118
import com.oracle.graal.python.nodes.PGuards;
119119
import com.oracle.graal.python.nodes.PNodeWithContext;
120120
import com.oracle.graal.python.nodes.PNodeWithState;
121+
import com.oracle.graal.python.nodes.PRaiseNode;
121122
import com.oracle.graal.python.nodes.SpecialAttributeNames;
122123
import com.oracle.graal.python.nodes.StringLiterals;
123124
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
124125
import com.oracle.graal.python.nodes.attributes.LookupCallableSlotInMRONode;
125126
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromDynamicObjectNode;
126127
import com.oracle.graal.python.nodes.attributes.WriteAttributeToDynamicObjectNode;
128+
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
127129
import com.oracle.graal.python.nodes.call.CallNode;
128130
import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
129131
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
@@ -147,6 +149,8 @@
147149
import com.oracle.truffle.api.dsl.Cached;
148150
import com.oracle.truffle.api.dsl.Cached.Shared;
149151
import com.oracle.truffle.api.dsl.Fallback;
152+
import com.oracle.truffle.api.dsl.GenerateCached;
153+
import com.oracle.truffle.api.dsl.GenerateInline;
150154
import com.oracle.truffle.api.dsl.GenerateUncached;
151155
import com.oracle.truffle.api.dsl.ImportStatic;
152156
import com.oracle.truffle.api.dsl.NeverDefault;
@@ -920,6 +924,85 @@ public static DefaultObjectReprNode create() {
920924
}
921925
}
922926

927+
@GenerateInline
928+
@GenerateCached(false)
929+
@GenerateUncached
930+
public abstract static class GenericSetAttrNode extends Node {
931+
public abstract void execute(Node inliningTarget, VirtualFrame frame, Object object, Object key, Object value, WriteAttributeToObjectNode writeNode);
932+
933+
@Specialization
934+
static void doStringKey(Node inliningTarget, VirtualFrame frame, Object object, TruffleString key, Object value, WriteAttributeToObjectNode writeNode,
935+
@Shared @Cached InlinedGetClassNode getClassNode,
936+
@Shared @Cached CallSetHelper callSetHelper,
937+
@Shared @Cached LookupAttributeInMRONode.Dynamic getExisting,
938+
@Shared @Cached PRaiseNode raiseNode) {
939+
Object type = getClassNode.execute(inliningTarget, object);
940+
Object descr = getExisting.execute(type, key);
941+
boolean calledSet = callSetHelper.execute(inliningTarget, frame, descr, object, value);
942+
if (calledSet) {
943+
return;
944+
}
945+
boolean wroteAttr = writeNode.execute(object, key, value);
946+
if (wroteAttr) {
947+
return;
948+
}
949+
if (descr != PNone.NO_VALUE) {
950+
throw raiseNode.raise(AttributeError, ErrorMessages.ATTR_S_READONLY, key);
951+
} else {
952+
throw raiseNode.raise(AttributeError, ErrorMessages.HAS_NO_ATTR, object, key);
953+
}
954+
}
955+
956+
@Specialization(replaces = "doStringKey")
957+
static void doIt(Node inliningTarget, VirtualFrame frame, Object object, Object keyObject, Object value, WriteAttributeToObjectNode writeNode,
958+
@Shared @Cached InlinedGetClassNode getClassNode,
959+
@Shared @Cached CallSetHelper callSetHelper,
960+
@Shared @Cached LookupAttributeInMRONode.Dynamic getExisting,
961+
@Shared @Cached PRaiseNode raiseNode,
962+
@Cached CastToTruffleStringNode castKeyToStringNode) {
963+
TruffleString key;
964+
try {
965+
key = castKeyToStringNode.execute(keyObject);
966+
} catch (CannotCastException e) {
967+
throw raiseNode.raise(PythonBuiltinClassType.TypeError, ATTR_NAME_MUST_BE_STRING, keyObject);
968+
}
969+
doStringKey(inliningTarget, frame, object, key, value, writeNode, getClassNode, callSetHelper, getExisting, raiseNode);
970+
}
971+
972+
public static GenericSetAttrNode getUncached() {
973+
return ObjectNodesFactory.GenericSetAttrNodeGen.getUncached();
974+
}
975+
976+
@GenerateInline
977+
@GenerateCached(false)
978+
@GenerateUncached
979+
@ImportStatic({SpecialMethodSlot.class, PGuards.class})
980+
abstract static class CallSetHelper extends Node {
981+
abstract boolean execute(Node inliningTarget, VirtualFrame frame, Object descr, Object object, Object value);
982+
983+
@Specialization(guards = "isNoValue(descr)")
984+
@SuppressWarnings("unused")
985+
static boolean call(Node inliningTarget, VirtualFrame frame, Object descr, Object object, Object value) {
986+
return false;
987+
}
988+
989+
@Specialization(guards = "!isNoValue(descr)")
990+
static boolean call(Node inliningTarget, VirtualFrame frame, Object descr, Object object, Object value,
991+
@Cached InlinedGetClassNode getClassNode,
992+
@Cached(parameters = "Set") LookupCallableSlotInMRONode lookup,
993+
@Cached CallTernaryMethodNode call) {
994+
Object descrClass = getClassNode.execute(inliningTarget, descr);
995+
Object setMethod = lookup.execute(descrClass);
996+
if (setMethod == PNone.NO_VALUE) {
997+
return false;
998+
} else {
999+
call.execute(frame, setMethod, descr, object, value);
1000+
return true;
1001+
}
1002+
}
1003+
}
1004+
}
1005+
9231006
public abstract static class AbstractSetattrNode extends PythonTernaryBuiltinNode {
9241007
@Child GetClassNode getDescClassNode;
9251008
@Child LookupCallableSlotInMRONode lookupSetNode;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@
111111
import com.oracle.graal.python.builtins.objects.method.PMethod;
112112
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory;
113113
import com.oracle.graal.python.builtins.objects.object.ObjectNodes;
114-
import com.oracle.graal.python.builtins.objects.object.ObjectNodes.AbstractSetattrNode;
115114
import com.oracle.graal.python.builtins.objects.object.PythonObject;
116115
import com.oracle.graal.python.builtins.objects.set.PSet;
117116
import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode;
@@ -131,11 +130,13 @@
131130
import com.oracle.graal.python.builtins.objects.types.GenericTypeNodes;
132131
import com.oracle.graal.python.lib.PyObjectIsTrueNode;
133132
import com.oracle.graal.python.lib.PyObjectLookupAttr;
133+
import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode;
134134
import com.oracle.graal.python.nodes.ErrorMessages;
135135
import com.oracle.graal.python.nodes.PConstructAndRaiseNode;
136136
import com.oracle.graal.python.nodes.PGuards;
137137
import com.oracle.graal.python.nodes.PNodeWithContext;
138138
import com.oracle.graal.python.nodes.PNodeWithRaise;
139+
import com.oracle.graal.python.nodes.PRaiseNode;
139140
import com.oracle.graal.python.nodes.SpecialAttributeNames;
140141
import com.oracle.graal.python.nodes.StringLiterals;
141142
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
@@ -155,6 +156,7 @@
155156
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
156157
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
157158
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
159+
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
158160
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
159161
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
160162
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.InlineIsBuiltinClassProfile;
@@ -165,6 +167,7 @@
165167
import com.oracle.graal.python.nodes.util.CannotCastException;
166168
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
167169
import com.oracle.graal.python.nodes.util.SplitArgsNode;
170+
import com.oracle.graal.python.runtime.PythonContext;
168171
import com.oracle.graal.python.runtime.exception.PException;
169172
import com.oracle.graal.python.runtime.exception.PythonErrorType;
170173
import com.oracle.graal.python.util.PythonUtils;
@@ -745,16 +748,30 @@ private Object lookupValueGet(Object value) {
745748

746749
@Builtin(name = J___SETATTR__, minNumOfPositionalArgs = 3)
747750
@GenerateNodeFactory
748-
public abstract static class SetattrNode extends AbstractSetattrNode {
749-
@Child WriteAttributeToObjectNode writeNode;
751+
public abstract static class SetattrNode extends PythonTernaryBuiltinNode {
752+
@Specialization(guards = "!isImmutable(object)")
753+
static Object set(VirtualFrame frame, Object object, Object key, Object value,
754+
@Bind("this") Node inliningTarget,
755+
@Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode,
756+
@Cached("createForceType()") WriteAttributeToObjectNode write) {
757+
genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write);
758+
return PNone.NONE;
759+
}
750760

751-
@Override
752-
protected boolean writeAttribute(Object object, TruffleString key, Object value) {
753-
if (writeNode == null) {
754-
CompilerDirectives.transferToInterpreterAndInvalidate();
755-
writeNode = insert(WriteAttributeToObjectNode.createForceType());
761+
@Specialization(guards = "isImmutable(object)")
762+
@TruffleBoundary
763+
Object setBuiltin(Object object, Object key, Object value) {
764+
if (PythonContext.get(this).isInitialized()) {
765+
throw PRaiseNode.raiseUncached(this, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, PyObjectReprAsTruffleStringNode.getUncached().execute(null, key), object);
766+
} else {
767+
set(null, object, key, value, null, ObjectNodes.GenericSetAttrNode.getUncached(), WriteAttributeToObjectNode.getUncached(true));
768+
return PNone.NONE;
756769
}
757-
return writeNode.execute(object, key, value);
770+
}
771+
772+
protected static boolean isImmutable(Object type) {
773+
// TODO should also check Py_TPFLAGS_IMMUTABLETYPE
774+
return type instanceof PythonBuiltinClass || type instanceof PythonBuiltinClassType;
758775
}
759776
}
760777

0 commit comments

Comments
 (0)