Skip to content

Commit 7f03b14

Browse files
committed
first stab at __dict__ for subclasses of native types
1 parent 1417313 commit 7f03b14

File tree

12 files changed

+169
-38
lines changed

12 files changed

+169
-38
lines changed

graalpython/com.oracle.graal.python.cext/src/dictobject.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,47 @@ int PyDict_Update(PyObject *a, PyObject *b) {
122122
return 0;
123123
}
124124
}
125+
126+
PyObject* _PyObject_GenericGetDict(PyObject* obj) {
127+
PyObject** dictptr = _PyObject_GetDictPtr(obj);
128+
if (dictptr == NULL) {
129+
return NULL;
130+
}
131+
PyObject* dict = *dictptr;
132+
if (dict == NULL) {
133+
*dictptr = dict = PyDict_New();
134+
}
135+
return dict;
136+
}
137+
138+
PyObject* PyObject_GenericGetDict(PyObject* obj, void* context) {
139+
PyObject* d = _PyObject_GenericGetDict(obj);
140+
if (d == NULL) {
141+
PyErr_SetString(PyExc_AttributeError, "This object has no __dict__");
142+
}
143+
return d;
144+
}
145+
146+
PyObject** _PyObject_GetDictPtr(PyObject* obj) {
147+
Py_ssize_t dictoffset;
148+
PyTypeObject *tp = Py_TYPE(obj);
149+
150+
dictoffset = tp->tp_dictoffset;
151+
if (dictoffset == 0) {
152+
return NULL;
153+
}
154+
if (dictoffset < 0) {
155+
Py_ssize_t nitems = ((PyVarObject *)obj)->ob_size;
156+
if (nitems < 0) {
157+
nitems = -nitems;
158+
}
159+
160+
size_t size = tp->tp_basicsize + nitems * tp->tp_itemsize;
161+
if (size % SIZEOF_VOID_P != 0) {
162+
// round to full pointer boundary
163+
size += SIZEOF_VOID_P - (size % SIZEOF_VOID_P);
164+
}
165+
dictoffset += (long)size;
166+
}
167+
return (PyObject **) ((char *)obj + dictoffset);
168+
}

graalpython/com.oracle.graal.python.cext/src/typeobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ int PyType_Ready(PyTypeObject* cls) {
232232
PyDict_SetItemString(native_members, "tp_name", polyglot_from_string(cls->tp_name, SRC_CS));
233233
PyDict_SetItemString(native_members, "tp_doc", polyglot_from_string(cls->tp_doc ? cls->tp_doc : "", SRC_CS));
234234
PyDict_SetItemString(native_members, "tp_basicsize", PyLong_FromSsize_t(cls->tp_basicsize));
235+
PyDict_SetItemString(native_members, "tp_itemsize", PyLong_FromSsize_t(cls->tp_itemsize));
235236
PyDict_SetItemString(native_members, "tp_dictoffset", PyLong_FromSsize_t(cls->tp_dictoffset));
236237
const char* class_name = cls->tp_name;
237238
PyTypeObject* javacls = polyglot_invoke(PY_TRUFFLE_CEXT,

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinConstructors.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,11 @@
108108
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
109109
import com.oracle.graal.python.builtins.objects.type.PythonClass;
110110
import com.oracle.graal.python.nodes.PGuards;
111+
import com.oracle.graal.python.nodes.SpecialAttributeNames;
111112
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
112113
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
113114
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
115+
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
114116
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
115117
import com.oracle.graal.python.nodes.builtins.TupleNodes;
116118
import com.oracle.graal.python.nodes.call.CallDispatchNode;
@@ -1590,6 +1592,11 @@ public PFunction function(Object cls, Object method_def, Object def, Object name
15901592
@Builtin(name = TYPE, minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 4, takesVarKeywordArgs = true, constructsClass = PythonBuiltinClassType.PythonClass)
15911593
@GenerateNodeFactory
15921594
public abstract static class TypeNode extends PythonBuiltinNode {
1595+
private static final long SIZEOF_PY_OBJECT_PTR = 8L;
1596+
@Child private ReadAttributeFromObjectNode readAttrNode;
1597+
@Child private WriteAttributeToObjectNode writeAttrNode;
1598+
@Child private CastToIndexNode castToInt;
1599+
15931600
@Specialization(guards = {"isNoValue(bases)", "isNoValue(dict)"})
15941601
@SuppressWarnings("unused")
15951602
public Object type(Object cls, Object obj, PNone bases, PNone dict, PKeyword[] kwds,
@@ -1640,9 +1647,39 @@ private Object typeMetaclass(String name, PTuple bases, PDict namespace, PythonC
16401647
for (DictEntry entry : namespace.entries()) {
16411648
pythonClass.setAttribute(entry.getKey(), entry.getValue());
16421649
}
1650+
addDictIfNative(pythonClass);
16431651
return pythonClass;
16441652
}
16451653

1654+
private void addDictIfNative(PythonClass pythonClass) {
1655+
for (Object cls : pythonClass.getMethodResolutionOrder()) {
1656+
if (cls instanceof PythonNativeClass) {
1657+
if (readAttrNode == null) {
1658+
CompilerDirectives.transferToInterpreterAndInvalidate();
1659+
readAttrNode = insert(ReadAttributeFromObjectNode.create());
1660+
writeAttrNode = insert(WriteAttributeToObjectNode.create());
1661+
castToInt = insert(CastToIndexNode.create());
1662+
}
1663+
long dictoffset = castToInt.execute(readAttrNode.execute(cls, SpecialAttributeNames.__DICTOFFSET__));
1664+
long basicsize = castToInt.execute(readAttrNode.execute(cls, SpecialAttributeNames.__BASICSIZE__));
1665+
long itemsize = castToInt.execute(readAttrNode.execute(cls, SpecialAttributeNames.__ITEMSIZE__));
1666+
if (dictoffset == 0) {
1667+
// add_dict
1668+
if (itemsize != 0) {
1669+
dictoffset = -SIZEOF_PY_OBJECT_PTR;
1670+
} else {
1671+
dictoffset = basicsize;
1672+
basicsize += SIZEOF_PY_OBJECT_PTR;
1673+
}
1674+
}
1675+
writeAttrNode.execute(pythonClass, SpecialAttributeNames.__DICTOFFSET__, dictoffset);
1676+
writeAttrNode.execute(pythonClass, SpecialAttributeNames.__BASICSIZE__, basicsize);
1677+
writeAttrNode.execute(pythonClass, SpecialAttributeNames.__ITEMSIZE__, itemsize);
1678+
break;
1679+
}
1680+
}
1681+
}
1682+
16461683
private PythonClass calculate_metaclass(PythonClass cls, PTuple bases, GetClassNode getMetaclassNode) {
16471684
PythonClass winner = cls;
16481685
for (Object base : bases.getArray()) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TruffleCextBuiltins.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ Object run(Object typestruct, PythonClass metaClass, PTuple baseClasses, PDict n
529529
PythonNativeClass cclass = factory().createNativeClassWrapper(typestruct, metaClass, qualName, bases);
530530
writeNode.execute(cclass, SpecialAttributeNames.__DOC__, doc);
531531
writeNode.execute(cclass, SpecialAttributeNames.__BASICSIZE__, getLongItem(nativeMembers, NativeMemberNames.TP_BASICSIZE));
532+
writeNode.execute(cclass, SpecialAttributeNames.__ITEMSIZE__, getLongItem(nativeMembers, NativeMemberNames.TP_ITEMSIZE));
532533
writeNode.execute(cclass, SpecialAttributeNames.__DICTOFFSET__, getLongItem(nativeMembers, NativeMemberNames.TP_DICTOFFSET));
533534
String moduleName = getModuleName(fqname);
534535
if (moduleName != null) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/CExtNodes.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,4 +1460,31 @@ public static IsPointerNode create() {
14601460
return IsPointerNodeGen.create();
14611461
}
14621462
}
1463+
1464+
public static class GetObjectDictNode extends CExtBaseNode {
1465+
@CompilationFinal private TruffleObject func;
1466+
@Child private Node exec;
1467+
@Child private ToSulongNode toSulong;
1468+
@Child private ToJavaNode toJava;
1469+
1470+
public Object execute(Object self) {
1471+
if (func == null) {
1472+
CompilerDirectives.transferToInterpreterAndInvalidate();
1473+
func = importCAPISymbol(NativeCAPISymbols.FUN_PY_OBJECT_GENERIC_GET_DICT);
1474+
exec = insert(Message.EXECUTE.createNode());
1475+
toSulong = ToSulongNode.create();
1476+
toJava = ToJavaNode.create();
1477+
}
1478+
try {
1479+
return toJava.execute(ForeignAccess.sendExecute(exec, func, toSulong.execute(self)));
1480+
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
1481+
CompilerDirectives.transferToInterpreter();
1482+
throw e.raise();
1483+
}
1484+
}
1485+
1486+
public static GetObjectDictNode create() {
1487+
return new GetObjectDictNode();
1488+
}
1489+
}
14631490
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/NativeCAPISymbols.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,5 @@ public abstract class NativeCAPISymbols {
6262
public static final String FUN_PY_TRUFFLE_LONG_ARRAY_TO_NATIVE = "PyTruffle_LongArrayToNative";
6363
public static final String FUN_PY_TRUFFLE_DOUBLE_ARRAY_TO_NATIVE = "PyTruffle_DoubleArrayToNative";
6464
public static final String FUN_PY_TRUFFLE_OBJECT_ARRAY_TO_NATIVE = "PyTruffle_ObjectArrayToNative";
65+
public static final String FUN_PY_OBJECT_GENERIC_GET_DICT = "_PyObject_GenericGetDict";
6566
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/NativeMemberNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public final class NativeMemberNames {
5757
public static final String TP_NAME = "tp_name";
5858
public static final String TP_BASE = "tp_base";
5959
public static final String TP_BASICSIZE = "tp_basicsize";
60+
public static final String TP_ITEMSIZE = "tp_itemsize";
6061
public static final String TP_DICTOFFSET = "tp_dictoffset";
6162
public static final String TP_DOC = "tp_doc";
6263
public static final String TP_ALLOC = "tp_alloc";

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonObjectNativeWrapperMR.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
import com.oracle.graal.python.nodes.SpecialAttributeNames;
9999
import com.oracle.graal.python.nodes.SpecialMethodNames;
100100
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
101-
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
102101
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
103102
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
104103
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
@@ -467,7 +466,19 @@ Object doTpHash(PythonClass object, @SuppressWarnings("unused") String key,
467466

468467
@Specialization(guards = "eq(TP_BASICSIZE, key)")
469468
Object doTpBasicsize(PythonClass object, @SuppressWarnings("unused") String key,
470-
@Cached("create(__BASICSIZE__)") LookupInheritedAttributeNode getAttrNode) {
469+
@Cached("create(__BASICSIZE__)") LookupAttributeInMRONode getAttrNode) {
470+
return getAttrNode.execute(object);
471+
}
472+
473+
@Specialization(guards = "eq(TP_ITEMSIZE, key)")
474+
Object doTpItemsize(PythonClass object, @SuppressWarnings("unused") String key,
475+
@Cached("create(__ITEMSIZE__)") LookupAttributeInMRONode getAttrNode) {
476+
return getAttrNode.execute(object);
477+
}
478+
479+
@Specialization(guards = "eq(TP_DICTOFFSET, key)")
480+
Object doTpDictoffset(PythonClass object, @SuppressWarnings("unused") String key,
481+
@Cached("create(__DICTOFFSET__)") LookupAttributeInMRONode getAttrNode) {
471482
return getAttrNode.execute(object);
472483
}
473484

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

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,11 @@
6464
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
6565
import com.oracle.graal.python.builtins.objects.function.PKeyword;
6666
import com.oracle.graal.python.builtins.objects.function.PythonCallable;
67-
import com.oracle.graal.python.builtins.objects.ints.PInt;
6867
import com.oracle.graal.python.builtins.objects.type.LazyPythonClass;
6968
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
7069
import com.oracle.graal.python.builtins.objects.type.PythonClass;
7170
import com.oracle.graal.python.nodes.SpecialMethodNames;
7271
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
73-
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
7472
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
7573
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
7674
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
@@ -95,10 +93,6 @@
9593
import com.oracle.truffle.api.dsl.NodeFactory;
9694
import com.oracle.truffle.api.dsl.Specialization;
9795
import com.oracle.truffle.api.frame.VirtualFrame;
98-
import com.oracle.truffle.api.interop.ForeignAccess;
99-
import com.oracle.truffle.api.interop.Message;
100-
import com.oracle.truffle.api.interop.UnknownIdentifierException;
101-
import com.oracle.truffle.api.interop.UnsupportedMessageException;
10296
import com.oracle.truffle.api.nodes.Node;
10397
import com.oracle.truffle.api.nodes.UnexpectedResultException;
10498
import com.oracle.truffle.api.profiles.BranchProfile;
@@ -514,7 +508,7 @@ protected PNone doIt(Object object, Object key,
514508
static abstract class DictNode extends PythonBinaryBuiltinNode {
515509
private final IsBuiltinClassProfile exactObjInstanceProfile = IsBuiltinClassProfile.create();
516510
private final IsBuiltinClassProfile exactBuiltinInstanceProfile = IsBuiltinClassProfile.create();
517-
@Child private Node readNode;
511+
@Child private Node executeNode;
518512

519513
protected boolean isExactObjectInstance(PythonObject self) {
520514
return exactObjInstanceProfile.profileObject(self, PythonBuiltinClassType.PythonObject);
@@ -544,38 +538,16 @@ Object dict(PythonObject self, PDict dict) {
544538

545539
@Specialization(guards = "isNoValue(none)")
546540
Object dict(PythonNativeObject self, @SuppressWarnings("unused") PNone none,
547-
@Cached("create(__DICTOFFSET__)") LookupInheritedAttributeNode getDictoffset,
548-
@Cached("create()") BranchProfile noOffset,
549-
@Cached("create()") BranchProfile wrongType,
550-
@Cached("create()") CExtNodes.ToJavaNode toJava) {
551-
Object dictoffset = getDictoffset.execute(self);
552-
int offset;
553-
if (dictoffset instanceof Long) {
554-
offset = ((Long) dictoffset).intValue();
555-
} else if (dictoffset instanceof Integer) {
556-
offset = (Integer) dictoffset;
557-
} else if (dictoffset instanceof PInt) {
558-
offset = ((PInt) dictoffset).intValue();
559-
} else if (dictoffset instanceof PNone) {
560-
noOffset.enter();
561-
throw raise(AttributeError, "'%p' object has no attribute '__dict__'", self);
562-
} else {
563-
wrongType.enter();
564-
throw raise(TypeError, "tp_dictoffset of native type is not an integer, got '%p'", dictoffset);
565-
}
566-
if (readNode == null) {
567-
CompilerDirectives.transferToInterpreterAndInvalidate();
568-
readNode = insert(Message.READ.createNode());
569-
}
570-
try {
571-
return toJava.execute(ForeignAccess.sendRead(readNode, self.object, offset));
572-
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
573-
throw raise(AttributeError, "'%p' object has no native '__dict__'", self);
541+
@Cached("create()") CExtNodes.GetObjectDictNode getDictNode) {
542+
Object dict = getDictNode.execute(self);
543+
if (dict == PNone.NO_VALUE) {
544+
raise(self, none);
574545
}
546+
return dict;
575547
}
576548

577549
@Fallback
578-
Object dict(Object self, @SuppressWarnings("unused") Object dict) {
550+
Object raise(Object self, @SuppressWarnings("unused") Object dict) {
579551
throw raise(AttributeError, "'%p' object has no attribute '__dict__'", self);
580552
}
581553

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/SpecialAttributeNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ public abstract class SpecialAttributeNames {
7070
public static final String __NEW__ = "__new__";
7171
public static final Object __SLOTS__ = "__slots__";
7272
public static final String __DICTOFFSET__ = "__dictoffset__";
73+
public static final String __ITEMSIZE__ = "__itemsize__";
7374
}

0 commit comments

Comments
 (0)