Skip to content

Commit f7c17a7

Browse files
committed
added basic compatibleForAssignment impl to ObjectBuildins.ClassNode.setClass
1 parent 5ea1a2b commit f7c17a7

File tree

1 file changed

+205
-19
lines changed
  • graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object

1 file changed

+205
-19
lines changed

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

Lines changed: 205 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,26 @@
6060
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
6161
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
6262
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;
6366
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;
6469
import com.oracle.graal.python.builtins.objects.dict.PDict;
6570
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
6671
import com.oracle.graal.python.builtins.objects.function.PKeyword;
6772
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.GetAttributeNodeFactory;
6873
import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass;
6974
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
7075
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;
7178
import com.oracle.graal.python.nodes.BuiltinNames;
7279
import com.oracle.graal.python.nodes.ErrorMessages;
7380
import com.oracle.graal.python.nodes.PGuards;
7481
import com.oracle.graal.python.nodes.SpecialAttributeNames;
82+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEW__;
7583
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
7684
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
7785
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
@@ -106,6 +114,7 @@
106114
import com.oracle.truffle.api.nodes.UnexpectedResultException;
107115
import com.oracle.truffle.api.profiles.BranchProfile;
108116
import com.oracle.truffle.api.profiles.ConditionProfile;
117+
import java.util.Arrays;
109118

110119
@CoreFunctions(extendClasses = PythonBuiltinClassType.PythonObject)
111120
public class ObjectBuiltins extends PythonBuiltins {
@@ -118,10 +127,18 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
118127
@Builtin(name = __CLASS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
119128
@GenerateNodeFactory
120129
abstract static class ClassNode extends PythonBinaryBuiltinNode {
121-
@Child private LookupAttributeInMRONode lookupSlotsInSelf;
122-
@Child private LookupAttributeInMRONode lookupSlotsInOther;
130+
131+
@Child private LookupAttributeInMRONode lookupSlotsNode;
123132
@Child private TypeNodes.GetNameNode getTypeNameNode;
124133

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+
125142
private static final String ERROR_MESSAGE = "__class__ assignment only supported for heap types or ModuleType subclasses";
126143

127144
@Specialization(guards = "isNoValue(value)")
@@ -141,9 +158,8 @@ Object setClass(@SuppressWarnings("unused") Object self, @SuppressWarnings("unus
141158
}
142159

143160
@Specialization
144-
PNone setClass(PythonObject self, PythonAbstractClass value,
161+
PNone setClass(VirtualFrame frame, PythonObject self, PythonAbstractClass value,
145162
@CachedLibrary(limit = "2") PythonObjectLibrary lib1,
146-
@CachedLibrary(limit = "2") PythonObjectLibrary lib2,
147163
@Cached("create()") BranchProfile errorValueBranch,
148164
@Cached("create()") BranchProfile errorSelfBranch,
149165
@Cached("create()") BranchProfile errorSlotsBranch) {
@@ -156,32 +172,202 @@ PNone setClass(PythonObject self, PythonAbstractClass value,
156172
errorSelfBranch.enter();
157173
throw raise(TypeError, ERROR_MESSAGE);
158174
}
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));
166179
}
167180
lib1.setLazyPythonClass(self, value);
168181
return PNone.NONE;
169182
}
170183

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) {
173359
CompilerDirectives.transferToInterpreterAndInvalidate();
174-
lookupSlotsInSelf = insert(LookupAttributeInMRONode.create(__SLOTS__));
360+
getBaseClassNode = insert(GetSuperClassNodeGen.create());
175361
}
176-
return lookupSlotsInSelf;
362+
return getBaseClassNode;
177363
}
178364

179-
private LookupAttributeInMRONode getLookupSlotsInOther() {
180-
if (lookupSlotsInOther == null) {
365+
private LookupAttributeInMRONode getLookupNewNode() {
366+
if (lookupNewNode == null) {
181367
CompilerDirectives.transferToInterpreterAndInvalidate();
182-
lookupSlotsInOther = insert(LookupAttributeInMRONode.create(__SLOTS__));
368+
lookupNewNode = insert(LookupAttributeInMRONode.createForLookupOfUnmanagedClasses(__NEW__));
183369
}
184-
return lookupSlotsInOther;
370+
return lookupNewNode;
185371
}
186372

187373
@Specialization(guards = "!isPythonObject(self)")

0 commit comments

Comments
 (0)