60
60
import com .oracle .graal .python .builtins .objects .cext .CExtNodes ;
61
61
import com .oracle .graal .python .builtins .objects .cext .PythonAbstractNativeObject ;
62
62
import com .oracle .graal .python .builtins .objects .cext .PythonNativeClass ;
63
+ import com .oracle .graal .python .builtins .objects .common .HashingCollectionNodes .GetDictStorageNode ;
64
+ import com .oracle .graal .python .builtins .objects .common .HashingStorage ;
65
+ import com .oracle .graal .python .builtins .objects .common .HashingStorageLibrary ;
63
66
import com .oracle .graal .python .builtins .objects .common .PHashingCollection ;
67
+ import com .oracle .graal .python .builtins .objects .common .SequenceNodes .GetObjectArrayNode ;
68
+ import com .oracle .graal .python .builtins .objects .common .SequenceNodesFactory .GetObjectArrayNodeGen ;
64
69
import com .oracle .graal .python .builtins .objects .dict .PDict ;
65
70
import com .oracle .graal .python .builtins .objects .function .PBuiltinFunction ;
66
71
import com .oracle .graal .python .builtins .objects .function .PKeyword ;
67
72
import com .oracle .graal .python .builtins .objects .object .ObjectBuiltinsFactory .GetAttributeNodeFactory ;
68
73
import com .oracle .graal .python .builtins .objects .type .PythonAbstractClass ;
69
74
import com .oracle .graal .python .builtins .objects .type .PythonBuiltinClass ;
70
75
import com .oracle .graal .python .builtins .objects .type .TypeNodes ;
76
+ import com .oracle .graal .python .builtins .objects .type .TypeNodes .GetSuperClassNode ;
77
+ import com .oracle .graal .python .builtins .objects .type .TypeNodesFactory .GetSuperClassNodeGen ;
71
78
import com .oracle .graal .python .nodes .BuiltinNames ;
72
79
import com .oracle .graal .python .nodes .ErrorMessages ;
73
80
import com .oracle .graal .python .nodes .PGuards ;
74
81
import com .oracle .graal .python .nodes .SpecialAttributeNames ;
82
+ import static com .oracle .graal .python .nodes .SpecialMethodNames .__NEW__ ;
75
83
import com .oracle .graal .python .nodes .attributes .GetAttributeNode .GetFixedAttributeNode ;
76
84
import com .oracle .graal .python .nodes .attributes .LookupAttributeInMRONode ;
77
85
import com .oracle .graal .python .nodes .attributes .ReadAttributeFromObjectNode ;
106
114
import com .oracle .truffle .api .nodes .UnexpectedResultException ;
107
115
import com .oracle .truffle .api .profiles .BranchProfile ;
108
116
import com .oracle .truffle .api .profiles .ConditionProfile ;
117
+ import java .util .Arrays ;
109
118
110
119
@ CoreFunctions (extendClasses = PythonBuiltinClassType .PythonObject )
111
120
public class ObjectBuiltins extends PythonBuiltins {
@@ -118,10 +127,18 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
118
127
@ Builtin (name = __CLASS__ , minNumOfPositionalArgs = 1 , maxNumOfPositionalArgs = 2 , isGetter = true , isSetter = true )
119
128
@ GenerateNodeFactory
120
129
abstract static class ClassNode extends PythonBinaryBuiltinNode {
121
- @ Child private LookupAttributeInMRONode lookupSlotsInSelf ;
122
- @ Child private LookupAttributeInMRONode lookupSlotsInOther ;
130
+
131
+ @ Child private LookupAttributeInMRONode lookupSlotsNode ;
123
132
@ Child private TypeNodes .GetNameNode getTypeNameNode ;
124
133
134
+ @ Child private LookupAttributeInMRONode lookupNewNode ;
135
+ @ Child private GetSuperClassNode getBaseClassNode ;
136
+ @ Child private GetDictStorageNode getDictStorageNode ;
137
+ @ Child private LookupAndCallBinaryNode getDictNode ;
138
+ @ Child private HashingStorageLibrary hashingStorageLib ;
139
+ @ Child private PythonObjectLibrary objectLibrary ;
140
+ @ Child private GetObjectArrayNode getObjectArrayNode ;
141
+
125
142
private static final String ERROR_MESSAGE = "__class__ assignment only supported for heap types or ModuleType subclasses" ;
126
143
127
144
@ Specialization (guards = "isNoValue(value)" )
@@ -141,9 +158,8 @@ Object setClass(@SuppressWarnings("unused") Object self, @SuppressWarnings("unus
141
158
}
142
159
143
160
@ Specialization
144
- PNone setClass (PythonObject self , PythonAbstractClass value ,
161
+ PNone setClass (VirtualFrame frame , PythonObject self , PythonAbstractClass value ,
145
162
@ CachedLibrary (limit = "2" ) PythonObjectLibrary lib1 ,
146
- @ CachedLibrary (limit = "2" ) PythonObjectLibrary lib2 ,
147
163
@ Cached ("create()" ) BranchProfile errorValueBranch ,
148
164
@ Cached ("create()" ) BranchProfile errorSelfBranch ,
149
165
@ Cached ("create()" ) BranchProfile errorSlotsBranch ) {
@@ -156,32 +172,202 @@ PNone setClass(PythonObject self, PythonAbstractClass value,
156
172
errorSelfBranch .enter ();
157
173
throw raise (TypeError , ERROR_MESSAGE );
158
174
}
159
- Object selfSlots = getLookupSlotsInSelf ().execute (lazyClass );
160
- if (selfSlots != PNone .NO_VALUE ) {
161
- Object otherSlots = getLookupSlotsInOther ().execute (value );
162
- if (otherSlots == PNone .NO_VALUE || !lib2 .equals (selfSlots , otherSlots , lib2 )) {
163
- errorSlotsBranch .enter ();
164
- throw raise (TypeError , ErrorMessages .CLASS_ASIGMENT_S_LAYOUT_DIFFERS_FROM_S , getTypeName (value ), getTypeName (lazyClass ));
165
- }
175
+
176
+ if (!compatibleForAssignment (frame , lazyClass , value )) {
177
+ errorSlotsBranch .enter ();
178
+ throw raise (TypeError , ErrorMessages .CLASS_ASIGMENT_S_LAYOUT_DIFFERS_FROM_S , getTypeName (value ), getTypeName (lazyClass ));
166
179
}
167
180
lib1 .setLazyPythonClass (self , value );
168
181
return PNone .NONE ;
169
182
}
170
183
171
- private LookupAttributeInMRONode getLookupSlotsInSelf () {
172
- if (lookupSlotsInSelf == null ) {
184
+ /**
185
+ * Attempt to get as close as possible to typeobject.compatible_for_assignment()
186
+ */
187
+ private boolean compatibleForAssignment (VirtualFrame frame , Object self , PythonAbstractClass other ) {
188
+ Object newBase = other ;
189
+ Object oldBase = self ;
190
+
191
+ // TODO getBaseClassNode tends to fail with "get bestBase case not yet implemented"
192
+ Object newParent = getSuperClassNode ().execute (newBase );
193
+ while (newParent != null && compatibleWithBase (frame , newBase , newParent )) {
194
+ newBase = newParent ;
195
+ newParent = getSuperClassNode ().execute (newBase );
196
+ }
197
+
198
+ Object oldParent = getSuperClassNode ().execute (oldBase );
199
+ while (oldParent != null && compatibleWithBase (frame , oldBase , oldParent )) {
200
+ oldBase = oldParent ;
201
+ oldParent = getSuperClassNode ().execute (oldBase );
202
+ }
203
+
204
+ if (newBase != oldBase && (newParent != oldParent || !compareSlotsFromDict (frame , newBase , oldBase ))) {
205
+ return false ;
206
+ }
207
+ return true ;
208
+ }
209
+
210
+ /**
211
+ * Attempt to get as close as possible to typeobject.compatible_with_tp_base().
212
+ */
213
+ private boolean compatibleWithBase (VirtualFrame frame , Object child , Object parent ) {
214
+ if (PGuards .isNativeClass (child ) && PGuards .isNativeClass (parent )) {
215
+ // TODO: call C function 'compatible_for_assignment'
216
+ return false ;
217
+ }
218
+
219
+ // (child->tp_flags & Py_TPFLAGS_HAVE_GC) == (parent->tp_flags & Py_TPFLAGS_HAVE_GC)
220
+ if (PGuards .isNativeClass (child ) != PGuards .isNativeClass (parent )) {
221
+ return false ;
222
+ }
223
+
224
+ // instead of child->tp_dictoffset == parent->tp_dictoffset
225
+ if (hasDict (child ) != hasDict (parent )) {
226
+ return false ;
227
+ }
228
+
229
+ // instead of child->tp_basicsize == parent->tp_basicsize
230
+ // the assumption is made that a different "allocator" => different basic size, hm
231
+ Object childNewMethod = getLookupNewNode ().execute (child );
232
+ Object parentNewMethod = getLookupNewNode ().execute (parent );
233
+ if (childNewMethod != parentNewMethod ) {
234
+ return false ;
235
+ }
236
+
237
+ // instead of child->tp_itemsize == parent->tp_itemsize
238
+ Object childSlots = getSlotsFromDict (frame , child );
239
+ Object parentSlots = getSlotsFromDict (frame , parent );
240
+ if (childSlots == null && parentSlots == null ) {
241
+ return true ;
242
+ }
243
+ if (childSlots == null && parentSlots != null || childSlots != null && parentSlots == null ) {
244
+ return false ;
245
+ }
246
+ if (!compareSlots (parent , child , parentSlots , childSlots )) {
247
+ return false ;
248
+ }
249
+
250
+ return true ;
251
+ }
252
+
253
+ private boolean compareSlotsFromDict (VirtualFrame frame , Object a , Object b ) {
254
+ Object aSlots = getSlotsFromDict (frame , b );
255
+ Object bSlots = getSlotsFromDict (frame , a );
256
+ return compareSlots (a , b , aSlots , bSlots );
257
+ }
258
+
259
+ @ TruffleBoundary
260
+ private boolean compareSlots (Object aType , Object bType , Object aSlotsArg , Object bSlotsArg ) {
261
+ Object aSlots = aSlotsArg ;
262
+ Object bSlots = bSlotsArg ;
263
+
264
+ if (aSlots == null && bSlots == null ) {
265
+ return true ;
266
+ }
267
+
268
+ if (aSlots != null && bSlots != null ) {
269
+ Object [] aArray = getObjectArrayNode ().execute (aSlots );
270
+ Object [] bArray = getObjectArrayNode ().execute (bSlots );
271
+ if (bArray .length != aArray .length ) {
272
+ return false ;
273
+ }
274
+ aArray = Arrays .copyOf (aArray , aArray .length );
275
+ bArray = Arrays .copyOf (bArray , bArray .length );
276
+ // what cpython does in same_slots_added() is a compare on a sorted slots list
277
+ // ((PyHeapTypeObject *)a)->ht_slots which is populated in type_new() and
278
+ // NOT the same like the unsorted __slots__ attribute.
279
+ Arrays .sort (bArray );
280
+ Arrays .sort (aArray );
281
+ for (int i = 0 ; i < aArray .length ; i ++) {
282
+ if (!aArray [i ].equals (bArray [i ])) {
283
+ return false ;
284
+ }
285
+ }
286
+ return true ;
287
+ }
288
+
289
+ aSlots = getLookupSlots ().execute (aType );
290
+ bSlots = getLookupSlots ().execute (bType );
291
+ int aSize = aSlots != PNone .NO_VALUE ? getObjectLibrary ().length (aSlots ) : 0 ;
292
+ int bSize = bSlots != PNone .NO_VALUE ? getObjectLibrary ().length (bSlots ) : 0 ;
293
+ return aSize == bSize ;
294
+ }
295
+
296
+ private Object getSlotsFromDict (VirtualFrame frame , Object type ) {
297
+ Object dict = getDictNode ().executeObject (frame , type , __DICT__ );
298
+ if (dict != PNone .NO_VALUE ) {
299
+ HashingStorage storage = getDictStorageNode ().execute ((PHashingCollection ) dict );
300
+ return getHashingStorageLibrary ().getItem (storage , __SLOTS__ );
301
+ }
302
+ return null ;
303
+ }
304
+
305
+ private boolean hasDict (Object obj ) {
306
+ return getObjectLibrary ().lookupAttribute (obj , __DICT__ ) != PNone .NO_VALUE ;
307
+ }
308
+
309
+ private GetObjectArrayNode getObjectArrayNode () {
310
+ if (getObjectArrayNode == null ) {
311
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
312
+ getObjectArrayNode = insert (GetObjectArrayNodeGen .create ());
313
+ }
314
+ return getObjectArrayNode ;
315
+ }
316
+
317
+ private PythonObjectLibrary getObjectLibrary () {
318
+ if (objectLibrary == null ) {
319
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
320
+ objectLibrary = insert (PythonObjectLibrary .getFactory ().createDispatched (4 ));
321
+ }
322
+ return objectLibrary ;
323
+ }
324
+
325
+ private LookupAndCallBinaryNode getDictNode () {
326
+ if (getDictNode == null ) {
327
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
328
+ getDictNode = insert (LookupAndCallBinaryNode .create (__GETATTRIBUTE__ ));
329
+ }
330
+ return getDictNode ;
331
+ }
332
+
333
+ private GetDictStorageNode getDictStorageNode () {
334
+ if (getDictStorageNode == null ) {
335
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
336
+ getDictStorageNode = insert (GetDictStorageNode .create ());
337
+ }
338
+ return getDictStorageNode ;
339
+ }
340
+
341
+ private HashingStorageLibrary getHashingStorageLibrary () {
342
+ if (hashingStorageLib == null ) {
343
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
344
+ hashingStorageLib = insert (HashingStorageLibrary .getFactory ().createDispatched (4 ));
345
+ }
346
+ return hashingStorageLib ;
347
+ }
348
+
349
+ private LookupAttributeInMRONode getLookupSlots () {
350
+ if (lookupSlotsNode == null ) {
351
+ CompilerDirectives .transferToInterpreterAndInvalidate ();
352
+ lookupSlotsNode = insert (LookupAttributeInMRONode .create (__SLOTS__ ));
353
+ }
354
+ return lookupSlotsNode ;
355
+ }
356
+
357
+ private GetSuperClassNode getSuperClassNode () {
358
+ if (getBaseClassNode == null ) {
173
359
CompilerDirectives .transferToInterpreterAndInvalidate ();
174
- lookupSlotsInSelf = insert (LookupAttributeInMRONode .create (__SLOTS__ ));
360
+ getBaseClassNode = insert (GetSuperClassNodeGen .create ());
175
361
}
176
- return lookupSlotsInSelf ;
362
+ return getBaseClassNode ;
177
363
}
178
364
179
- private LookupAttributeInMRONode getLookupSlotsInOther () {
180
- if (lookupSlotsInOther == null ) {
365
+ private LookupAttributeInMRONode getLookupNewNode () {
366
+ if (lookupNewNode == null ) {
181
367
CompilerDirectives .transferToInterpreterAndInvalidate ();
182
- lookupSlotsInOther = insert (LookupAttributeInMRONode .create ( __SLOTS__ ));
368
+ lookupNewNode = insert (LookupAttributeInMRONode .createForLookupOfUnmanagedClasses ( __NEW__ ));
183
369
}
184
- return lookupSlotsInOther ;
370
+ return lookupNewNode ;
185
371
}
186
372
187
373
@ Specialization (guards = "!isPythonObject(self)" )
0 commit comments