30
30
import static com .oracle .graal .python .nodes .SpecialAttributeNames .__DICT__ ;
31
31
import static com .oracle .graal .python .nodes .SpecialAttributeNames .__MODULE__ ;
32
32
import static com .oracle .graal .python .nodes .SpecialAttributeNames .__QUALNAME__ ;
33
+ import static com .oracle .graal .python .nodes .SpecialAttributeNames .__SLOTS__ ;
33
34
import static com .oracle .graal .python .nodes .SpecialMethodNames .RICHCMP ;
34
35
import static com .oracle .graal .python .nodes .SpecialMethodNames .__BOOL__ ;
35
36
import static com .oracle .graal .python .nodes .SpecialMethodNames .__DELATTR__ ;
@@ -108,6 +109,10 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
108
109
@ Builtin (name = __CLASS__ , minNumOfPositionalArgs = 1 , maxNumOfPositionalArgs = 2 , isGetter = true , isSetter = true )
109
110
@ GenerateNodeFactory
110
111
abstract static class ClassNode extends PythonBinaryBuiltinNode {
112
+ @ Child LookupAttributeInMRONode lookupSlotsInSelf ;
113
+ @ Child LookupAttributeInMRONode lookupSlotsInOther ;
114
+ @ Child BinaryComparisonNode slotsAreEqual ;
115
+
111
116
private static final String ERROR_MESSAGE = "__class__ assignment only supported for heap types or ModuleType subclasses" ;
112
117
113
118
@ Specialization (guards = "isNoValue(value)" )
@@ -130,6 +135,7 @@ PythonClass setClass(@SuppressWarnings("unused") Object self, @SuppressWarnings(
130
135
PNone setClass (PythonObject self , PythonClass value ,
131
136
@ Cached ("create()" ) BranchProfile errorValueBranch ,
132
137
@ Cached ("create()" ) BranchProfile errorSelfBranch ,
138
+ @ Cached ("create()" ) BranchProfile errorSlotsBranch ,
133
139
@ Cached ("create()" ) GetLazyClassNode getLazyClass ) {
134
140
if (value instanceof PythonBuiltinClass || value instanceof PythonNativeClass ) {
135
141
errorValueBranch .enter ();
@@ -140,10 +146,42 @@ PNone setClass(PythonObject self, PythonClass value,
140
146
errorSelfBranch .enter ();
141
147
throw raise (TypeError , ERROR_MESSAGE );
142
148
}
149
+ Object selfSlots = getLookupSlotsInSelf ().execute (lazyClass );
150
+ if (selfSlots != PNone .NO_VALUE ) {
151
+ Object otherSlots = getLookupSlotsInOther ().execute (value );
152
+ if (otherSlots == PNone .NO_VALUE || !getSlotsAreEqual ().executeBool (selfSlots , otherSlots )) {
153
+ errorSlotsBranch .enter ();
154
+ throw raise (TypeError , "__class__ assignment: '%s' object layout differs from '%s'" , value .getName (), lazyClass .getName ());
155
+ }
156
+ }
143
157
self .setLazyPythonClass (value );
144
158
return PNone .NONE ;
145
159
}
146
160
161
+ private BinaryComparisonNode getSlotsAreEqual () {
162
+ if (slotsAreEqual == null ) {
163
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
164
+ slotsAreEqual = insert (BinaryComparisonNode .create (__EQ__ , null , "==" ));
165
+ }
166
+ return slotsAreEqual ;
167
+ }
168
+
169
+ private LookupAttributeInMRONode getLookupSlotsInSelf () {
170
+ if (lookupSlotsInSelf == null ) {
171
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
172
+ lookupSlotsInSelf = insert (LookupAttributeInMRONode .create (__SLOTS__ ));
173
+ }
174
+ return lookupSlotsInSelf ;
175
+ }
176
+
177
+ private LookupAttributeInMRONode getLookupSlotsInOther () {
178
+ if (lookupSlotsInOther == null ) {
179
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
180
+ lookupSlotsInOther = insert (LookupAttributeInMRONode .create (__SLOTS__ ));
181
+ }
182
+ return lookupSlotsInOther ;
183
+ }
184
+
147
185
@ Specialization (guards = "!isPythonObject(self)" )
148
186
PythonClass getClass (@ SuppressWarnings ("unused" ) Object self , @ SuppressWarnings ("unused" ) PythonClass value ) {
149
187
throw raise (TypeError , ERROR_MESSAGE );
0 commit comments