Skip to content

Commit 8e6fa66

Browse files
committed
added basic set bases impl
1 parent 59e0243 commit 8e6fa66

File tree

6 files changed

+327
-271
lines changed

6 files changed

+327
-271
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_type.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40+
def assert_raises(err, fn, *args, **kwargs):
41+
raised = False
42+
try:
43+
fn(*args, **kwargs)
44+
except err:
45+
raised = True
46+
assert raised
4047

4148
def test_base():
4249
A = type('A', (), {})
@@ -57,6 +64,8 @@ class C(A, B):
5764

5865
assert C.__base__ == B
5966

67+
#-----------------------------------------------
68+
6069
class A(object):
6170
pass
6271

@@ -71,6 +80,8 @@ class C(A, B):
7180
C = type('C', (B, int), {'spam': lambda self: 'spam%s' % self})
7281
assert C.__base__ == int
7382

83+
#-----------------------------------------------
84+
7485
class A (object): pass
7586

7687
class BB (A): pass
@@ -84,7 +95,58 @@ class D (B, C): pass
8495

8596
assert D.__base__ == C
8697

98+
#-----------------------------------------------
99+
100+
class A: pass
101+
102+
class B: pass
103+
104+
class C(A): pass
105+
106+
C.__bases__ = (A, B)
107+
108+
assert C.__bases__ == (A, B)
109+
assert A.__subclasses__() == [C]
110+
assert B.__subclasses__() == [C]
111+
112+
#-----------------------------------------------
113+
114+
class A: pass
115+
116+
class B: pass
117+
118+
class C(A, B): pass
119+
120+
C.__bases__ == (A, B)
121+
A.__subclasses__() == [C]
122+
B.__subclasses__() == [C]
123+
124+
raised = False
125+
try:
126+
C.__bases__ = (int,)
127+
except TypeError:
128+
raised = True
129+
assert raised
130+
131+
assert C.__bases__ == (A, B)
132+
assert A.__subclasses__() == [C]
133+
assert B.__subclasses__() == [C]
87134

135+
#-----------------------------------------------
136+
137+
# class A: pass
138+
#
139+
# class B: pass
140+
#
141+
# class C: pass
142+
#
143+
# raised = False
144+
# try:
145+
# C.__bases__ = (A, B)
146+
# except TypeError:
147+
# raised = True
148+
# assert raised
149+
# assert C.__bases__ == [object]
88150

89151
def test_namespace_with_non_string_keys():
90152
class MyStr(str):

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

Lines changed: 10 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__CLASS__;
3030
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DICT__;
31-
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__SLOTS__;
3231
import static com.oracle.graal.python.nodes.SpecialMethodNames.RICHCMP;
3332
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELATTR__;
3433
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELETE__;
@@ -60,27 +59,20 @@
6059
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
6160
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
6261
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;
6662
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;
6963
import com.oracle.graal.python.builtins.objects.dict.PDict;
7064
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
7165
import com.oracle.graal.python.builtins.objects.function.PKeyword;
7266
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.GetAttributeNodeFactory;
7367
import com.oracle.graal.python.builtins.objects.str.StringNodes;
7468
import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass;
7569
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
76-
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
77-
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassNode;
78-
import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.GetBaseClassNodeGen;
70+
import com.oracle.graal.python.builtins.objects.type.TypeNodes.CheckCompatibleForAssigmentNode;
71+
import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.CheckCompatibleForAssigmentNodeGen;
7972
import com.oracle.graal.python.nodes.BuiltinNames;
8073
import com.oracle.graal.python.nodes.ErrorMessages;
8174
import com.oracle.graal.python.nodes.PGuards;
8275
import com.oracle.graal.python.nodes.SpecialAttributeNames;
83-
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEW__;
8476
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
8577
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
8678
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
@@ -128,16 +120,7 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
128120
@GenerateNodeFactory
129121
abstract static class ClassNode extends PythonBinaryBuiltinNode {
130122

131-
@Child private LookupAttributeInMRONode lookupSlotsNode;
132-
@Child private TypeNodes.GetNameNode getTypeNameNode;
133-
134-
@Child private LookupAttributeInMRONode lookupNewNode;
135-
@Child private GetBaseClassNode 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;
123+
@Child private CheckCompatibleForAssigmentNode compatibleForAssigmentNode;
141124

142125
private static final String ERROR_MESSAGE = "__class__ assignment only supported for heap types or ModuleType subclasses";
143126

@@ -161,8 +144,7 @@ Object setClass(@SuppressWarnings("unused") Object self, @SuppressWarnings("unus
161144
PNone setClass(VirtualFrame frame, PythonObject self, PythonAbstractClass value,
162145
@CachedLibrary(limit = "2") PythonObjectLibrary lib1,
163146
@Cached("create()") BranchProfile errorValueBranch,
164-
@Cached("create()") BranchProfile errorSelfBranch,
165-
@Cached("create()") BranchProfile errorSlotsBranch) {
147+
@Cached("create()") BranchProfile errorSelfBranch) {
166148
if (value instanceof PythonBuiltinClass || PGuards.isNativeClass(value)) {
167149
errorValueBranch.enter();
168150
throw raise(TypeError, ERROR_MESSAGE);
@@ -173,184 +155,12 @@ PNone setClass(VirtualFrame frame, PythonObject self, PythonAbstractClass value,
173155
throw raise(TypeError, ERROR_MESSAGE);
174156
}
175157

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));
179-
}
158+
getCheckCompatibleForAssigmentNode().execute(frame, lazyClass, value);
159+
180160
lib1.setLazyPythonClass(self, value);
181161
return PNone.NONE;
182162
}
183163

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-
Object newParent = getBaseClassNode().execute(frame, newBase);
192-
while (newParent != null && compatibleWithBase(frame, newBase, newParent)) {
193-
newBase = newParent;
194-
newParent = getBaseClassNode().execute(frame, newBase);
195-
}
196-
197-
Object oldParent = getBaseClassNode().execute(frame, oldBase);
198-
while (oldParent != null && compatibleWithBase(frame, oldBase, oldParent)) {
199-
oldBase = oldParent;
200-
oldParent = getBaseClassNode().execute(frame, oldBase);
201-
}
202-
203-
if (newBase != oldBase && (newParent != oldParent || !compareSlotsFromDict(frame, newBase, oldBase))) {
204-
return false;
205-
}
206-
return true;
207-
}
208-
209-
/**
210-
* Attempt to get as close as possible to typeobject.compatible_with_tp_base().
211-
*/
212-
private boolean compatibleWithBase(VirtualFrame frame, Object child, Object parent) {
213-
if (PGuards.isNativeClass(child) && PGuards.isNativeClass(parent)) {
214-
// TODO: call C function 'compatible_for_assignment'
215-
return false;
216-
}
217-
218-
// (child->tp_flags & Py_TPFLAGS_HAVE_GC) == (parent->tp_flags & Py_TPFLAGS_HAVE_GC)
219-
if (PGuards.isNativeClass(child) != PGuards.isNativeClass(parent)) {
220-
return false;
221-
}
222-
223-
// instead of child->tp_dictoffset == parent->tp_dictoffset
224-
if (hasDict(child) != hasDict(parent)) {
225-
return false;
226-
}
227-
228-
// instead of child->tp_basicsize == parent->tp_basicsize
229-
// the assumption is made that a different "allocator" => different basic size, hm
230-
Object childNewMethod = getLookupNewNode().execute(child);
231-
Object parentNewMethod = getLookupNewNode().execute(parent);
232-
if (childNewMethod != parentNewMethod) {
233-
return false;
234-
}
235-
236-
// instead of child->tp_itemsize == parent->tp_itemsize
237-
Object childSlots = getSlotsFromDict(frame, child);
238-
Object parentSlots = getSlotsFromDict(frame, parent);
239-
if (childSlots == null && parentSlots == null) {
240-
return true;
241-
}
242-
if (childSlots == null && parentSlots != null || childSlots != null && parentSlots == null) {
243-
return false;
244-
}
245-
if (!compareSlots(parent, child, parentSlots, childSlots)) {
246-
return false;
247-
}
248-
249-
return true;
250-
}
251-
252-
private boolean compareSlotsFromDict(VirtualFrame frame, Object a, Object b) {
253-
Object aSlots = getSlotsFromDict(frame, b);
254-
Object bSlots = getSlotsFromDict(frame, a);
255-
return compareSlots(a, b, aSlots, bSlots);
256-
}
257-
258-
private boolean compareSlots(Object aType, Object bType, Object aSlotsArg, Object bSlotsArg) {
259-
Object aSlots = aSlotsArg;
260-
Object bSlots = bSlotsArg;
261-
262-
if (aSlots == null && bSlots == null) {
263-
return true;
264-
}
265-
266-
if (aSlots != null && bSlots != null) {
267-
return TypeNodes.compareSortedSlots(aSlots, bSlots, getObjectArrayNode());
268-
}
269-
270-
aSlots = getLookupSlots().execute(aType);
271-
bSlots = getLookupSlots().execute(bType);
272-
int aSize = aSlots != PNone.NO_VALUE ? getObjectLibrary().length(aSlots) : 0;
273-
int bSize = bSlots != PNone.NO_VALUE ? getObjectLibrary().length(bSlots) : 0;
274-
return aSize == bSize;
275-
}
276-
277-
private Object getSlotsFromDict(VirtualFrame frame, Object type) {
278-
Object dict = getDictNode().executeObject(frame, type, __DICT__);
279-
if (dict != PNone.NO_VALUE) {
280-
HashingStorage storage = getDictStorageNode().execute((PHashingCollection) dict);
281-
return getHashingStorageLibrary().getItem(storage, __SLOTS__);
282-
}
283-
return null;
284-
}
285-
286-
private boolean hasDict(Object obj) {
287-
return getObjectLibrary().lookupAttribute(obj, __DICT__) != PNone.NO_VALUE;
288-
}
289-
290-
private GetObjectArrayNode getObjectArrayNode() {
291-
if (getObjectArrayNode == null) {
292-
CompilerDirectives.transferToInterpreterAndInvalidate();
293-
getObjectArrayNode = insert(GetObjectArrayNodeGen.create());
294-
}
295-
return getObjectArrayNode;
296-
}
297-
298-
private PythonObjectLibrary getObjectLibrary() {
299-
if (objectLibrary == null) {
300-
CompilerDirectives.transferToInterpreterAndInvalidate();
301-
objectLibrary = insert(PythonObjectLibrary.getFactory().createDispatched(4));
302-
}
303-
return objectLibrary;
304-
}
305-
306-
private LookupAndCallBinaryNode getDictNode() {
307-
if (getDictNode == null) {
308-
CompilerDirectives.transferToInterpreterAndInvalidate();
309-
getDictNode = insert(LookupAndCallBinaryNode.create(__GETATTRIBUTE__));
310-
}
311-
return getDictNode;
312-
}
313-
314-
private GetDictStorageNode getDictStorageNode() {
315-
if (getDictStorageNode == null) {
316-
CompilerDirectives.transferToInterpreterAndInvalidate();
317-
getDictStorageNode = insert(GetDictStorageNode.create());
318-
}
319-
return getDictStorageNode;
320-
}
321-
322-
private HashingStorageLibrary getHashingStorageLibrary() {
323-
if (hashingStorageLib == null) {
324-
CompilerDirectives.transferToInterpreterAndInvalidate();
325-
hashingStorageLib = insert(HashingStorageLibrary.getFactory().createDispatched(4));
326-
}
327-
return hashingStorageLib;
328-
}
329-
330-
private LookupAttributeInMRONode getLookupSlots() {
331-
if (lookupSlotsNode == null) {
332-
CompilerDirectives.transferToInterpreterAndInvalidate();
333-
lookupSlotsNode = insert(LookupAttributeInMRONode.create(__SLOTS__));
334-
}
335-
return lookupSlotsNode;
336-
}
337-
338-
private GetBaseClassNode getBaseClassNode() {
339-
if (getBaseClassNode == null) {
340-
CompilerDirectives.transferToInterpreterAndInvalidate();
341-
getBaseClassNode = insert(GetBaseClassNodeGen.create());
342-
}
343-
return getBaseClassNode;
344-
}
345-
346-
private LookupAttributeInMRONode getLookupNewNode() {
347-
if (lookupNewNode == null) {
348-
CompilerDirectives.transferToInterpreterAndInvalidate();
349-
lookupNewNode = insert(LookupAttributeInMRONode.createForLookupOfUnmanagedClasses(__NEW__));
350-
}
351-
return lookupNewNode;
352-
}
353-
354164
@Specialization(guards = "!isPythonObject(self)")
355165
Object getClass(@SuppressWarnings("unused") Object self, @SuppressWarnings("unused") PythonAbstractClass value) {
356166
throw raise(TypeError, ERROR_MESSAGE);
@@ -361,12 +171,12 @@ Object getClass(@SuppressWarnings("unused") Object self, Object value) {
361171
throw raise(TypeError, ErrorMessages.CLASS_MUST_BE_SET_TO_CLASS, value);
362172
}
363173

364-
private String getTypeName(Object clazz) {
365-
if (getTypeNameNode == null) {
174+
private CheckCompatibleForAssigmentNode getCheckCompatibleForAssigmentNode() {
175+
if (compatibleForAssigmentNode == null) {
366176
CompilerDirectives.transferToInterpreterAndInvalidate();
367-
getTypeNameNode = insert(TypeNodes.GetNameNode.create());
177+
compatibleForAssigmentNode = insert(CheckCompatibleForAssigmentNodeGen.create());
368178
}
369-
return getTypeNameNode.execute(clazz);
179+
return compatibleForAssigmentNode;
370180
}
371181
}
372182

0 commit comments

Comments
 (0)