Skip to content

Commit 42062e2

Browse files
committed
Allow class assignment between mutable managed subtypes of native types
1 parent 7390f25 commit 42062e2

File tree

2 files changed

+53
-29
lines changed
  • graalpython

2 files changed

+53
-29
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_descr.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,18 @@ class TestDescr(CPyExtTestCase):
329329
callfunction="wrap_PyDescr_NewClassMethod",
330330
cmpfunc=unhandled_error_compare
331331
)
332+
333+
334+
def test_class_assignment_managed_subclass():
335+
class IntermediateType(CPyExtType("BaseForAssignment")):
336+
def __repr__(self):
337+
return "intermediate"
338+
339+
class ChildType(IntermediateType):
340+
def __repr__(self):
341+
return "child"
342+
343+
o = ChildType()
344+
assert repr(o) == "child"
345+
o.__class__ = IntermediateType
346+
assert repr(o) == "intermediate"

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

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
import com.oracle.graal.python.builtins.modules.BuiltinConstructorsFactory;
7676
import com.oracle.graal.python.builtins.objects.PNone;
7777
import com.oracle.graal.python.builtins.objects.PNotImplemented;
78+
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
79+
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
80+
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
7881
import com.oracle.graal.python.builtins.objects.dict.PDict;
7982
import com.oracle.graal.python.builtins.objects.function.PKeyword;
8083
import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrDeleteNode;
@@ -84,13 +87,14 @@
8487
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
8588
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsClinicProviders.FormatNodeClinicProviderGen;
8689
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsClinicProviders.ReduceExNodeClinicProviderGen;
87-
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.GetAttributeNodeFactory;
8890
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.DictNodeFactory;
91+
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.GetAttributeNodeFactory;
8992
import com.oracle.graal.python.builtins.objects.set.PSet;
9093
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
9194
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
9295
import com.oracle.graal.python.builtins.objects.type.TpSlots;
9396
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
97+
import com.oracle.graal.python.builtins.objects.type.TypeFlags;
9498
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
9599
import com.oracle.graal.python.builtins.objects.type.TypeNodes.CheckCompatibleForAssigmentNode;
96100
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassNode;
@@ -127,6 +131,7 @@
127131
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
128132
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
129133
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile;
134+
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassProfile;
130135
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsOtherBuiltinClassProfile;
131136
import com.oracle.graal.python.nodes.object.DeleteDictNode;
132137
import com.oracle.graal.python.nodes.object.GetClassNode;
@@ -146,7 +151,8 @@
146151
import com.oracle.truffle.api.dsl.Cached;
147152
import com.oracle.truffle.api.dsl.Cached.Exclusive;
148153
import com.oracle.truffle.api.dsl.Cached.Shared;
149-
import com.oracle.truffle.api.dsl.Fallback;
154+
import com.oracle.truffle.api.dsl.GenerateCached;
155+
import com.oracle.truffle.api.dsl.GenerateInline;
150156
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
151157
import com.oracle.truffle.api.dsl.Idempotent;
152158
import com.oracle.truffle.api.dsl.ImportStatic;
@@ -180,46 +186,49 @@ static Object getClass(Object self, @SuppressWarnings("unused") PNone value,
180186
return getClassNode.execute(inliningTarget, self);
181187
}
182188

183-
@Specialization(guards = "isNativeClass(klass)")
184-
static Object setClass(@SuppressWarnings("unused") Object self, @SuppressWarnings("unused") Object klass,
185-
@Shared @Cached PRaiseNode raiseNode) {
186-
throw raiseNode.raise(TypeError, ErrorMessages.CLASS_ASSIGNMENT_ONLY_SUPPORTED_FOR_HEAP_TYPES_OR_MODTYPE_SUBCLASSES);
187-
}
188-
189-
@Specialization(guards = "isPythonClass(value) || isPythonBuiltinClassType(value)")
190-
static PNone setClass(VirtualFrame frame, PythonObject self, Object value,
189+
@Specialization(guards = "!isNoValue(value)")
190+
static PNone setClass(VirtualFrame frame, Object self, Object value,
191191
@Bind("this") Node inliningTarget,
192-
@Cached HiddenAttr.WriteNode writeHiddenAttrNode,
193-
@Cached IsOtherBuiltinClassProfile classProfile1,
194-
@Cached IsOtherBuiltinClassProfile classProfile2,
192+
@Cached TypeNodes.IsTypeNode isTypeNode,
193+
@Cached IsBuiltinClassProfile isModuleProfile,
194+
@Cached TypeNodes.GetTypeFlagsNode getTypeFlagsNode,
195195
@Cached CheckCompatibleForAssigmentNode checkCompatibleForAssigmentNode,
196196
@Exclusive @Cached GetClassNode getClassNode,
197-
@Exclusive @Cached PRaiseNode.Lazy raiseNode) {
197+
@Cached SetClassNode setClassNode,
198+
@Cached PRaiseNode.Lazy raiseNode) {
199+
if (!isTypeNode.execute(inliningTarget, value)) {
200+
throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.CLASS_MUST_BE_SET_TO_CLASS, value);
201+
}
198202
Object type = getClassNode.execute(inliningTarget, self);
199-
if (isBuiltinClassNotModule(inliningTarget, value, classProfile1) || PGuards.isNativeClass(value) || isBuiltinClassNotModule(inliningTarget, type, classProfile2) ||
200-
PGuards.isNativeClass(type)) {
203+
boolean bothModuleSubtypes = isModuleProfile.profileClass(inliningTarget, type, PythonBuiltinClassType.PythonModule) &&
204+
isModuleProfile.profileClass(inliningTarget, value, PythonBuiltinClassType.PythonModule);
205+
boolean bothMutable = (getTypeFlagsNode.execute(type) & TypeFlags.IMMUTABLETYPE) == 0 && (getTypeFlagsNode.execute(value) & TypeFlags.IMMUTABLETYPE) == 0;
206+
if (!bothModuleSubtypes && !bothMutable) {
201207
throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.CLASS_ASSIGNMENT_ONLY_SUPPORTED_FOR_HEAP_TYPES_OR_MODTYPE_SUBCLASSES);
202208
}
203209

204210
checkCompatibleForAssigmentNode.execute(frame, type, value);
205-
writeHiddenAttrNode.execute(inliningTarget, self, HiddenAttr.CLASS, value);
211+
setClassNode.execute(inliningTarget, self, value);
212+
206213
return PNone.NONE;
207214
}
208215

209-
private static boolean isBuiltinClassNotModule(Node inliningTarget, Object type, IsOtherBuiltinClassProfile classProfile) {
210-
return classProfile.profileIsOtherBuiltinClass(inliningTarget, type, PythonBuiltinClassType.PythonModule);
211-
}
216+
@GenerateInline
217+
@GenerateCached(false)
218+
abstract static class SetClassNode extends Node {
219+
public abstract void execute(Node inliningTarget, Object self, Object newClass);
212220

213-
@Specialization(guards = {"isPythonClass(value) || isPythonBuiltinClassType(value)", "!isPythonObject(self)"})
214-
static Object getClass(@SuppressWarnings("unused") Object self, @SuppressWarnings("unused") Object value,
215-
@Shared @Cached PRaiseNode raiseNode) {
216-
throw raiseNode.raise(TypeError, ErrorMessages.CLASS_ASSIGNMENT_ONLY_SUPPORTED_FOR_HEAP_TYPES_OR_MODTYPE_SUBCLASSES);
217-
}
221+
@Specialization
222+
static void doPythonObject(Node inliningTarget, PythonObject self, Object newClass,
223+
@Cached HiddenAttr.WriteNode writeHiddenAttrNode) {
224+
writeHiddenAttrNode.execute(inliningTarget, self, HiddenAttr.CLASS, newClass);
225+
}
218226

219-
@Fallback
220-
static Object getClassError(@SuppressWarnings("unused") Object self, Object value,
221-
@Shared @Cached PRaiseNode raiseNode) {
222-
throw raiseNode.raise(TypeError, ErrorMessages.CLASS_MUST_BE_SET_TO_CLASS, value);
227+
@Specialization
228+
static void doNative(PythonAbstractNativeObject self, Object newClass,
229+
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObjectNewRefNode) {
230+
writeObjectNewRefNode.writeToObject(self, CFields.PyObject__ob_type, newClass);
231+
}
223232
}
224233
}
225234

0 commit comments

Comments
 (0)