Skip to content

Commit 64153b5

Browse files
committed
[GR-12446] Add initial support for dictionaries in native types
PullRequest: graalpython/282
2 parents d4be7d6 + a4b23d5 commit 64153b5

31 files changed

+1227
-27
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ PyObject * PyNumber_Invert(PyObject *o) {
136136
return do_unaryop(o, INVERT);
137137
}
138138

139+
PyObject * PyNumber_Power(PyObject *v, PyObject *w, PyObject *z) {
140+
return UPCALL_O(PY_BUILTIN, polyglot_from_string("pow", SRC_CS), native_to_java(v), native_to_java(w), native_to_java(z));
141+
}
142+
139143
UPCALL_ID(PyNumber_Index);
140144
PyObject * PyNumber_Index(PyObject *o) {
141145
if (o == NULL) {

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,13 @@ declare_type(PyFloat_Type, float, PyFloatObject);
129129
declare_type(PySlice_Type, slice, PySliceObject);
130130
declare_type(PyByteArray_Type, bytearray, PyByteArrayObject);
131131
declare_type(PyCFunction_Type, builtin_function_or_method, PyCFunctionObject);
132+
declare_type(PyWrapperDescr_Type, method_descriptor, PyWrapperDescrObject); // LS: previously wrapper_descriptor
133+
// tfel: Both method_descriptor maps to both PyWrapperDescr_Type and
134+
// PyMethodDescr_Type. This reflects our interpreter, but we need to make sure
135+
// that the dynamic type for method_descriptor is always going to be
136+
// PyMethodDescr_Type, so these two declarations cannot be in the wrong order
132137
declare_type(PyMethodDescr_Type, method_descriptor, PyMethodDescrObject);
133138
declare_type(PyGetSetDescr_Type, getset_descriptor, PyGetSetDescrObject);
134-
declare_type(PyWrapperDescr_Type, method_descriptor, PyWrapperDescrObject); // LS: previously wrapper_descriptor
135139
declare_type(PyMemberDescr_Type, property, PyMemberDescrObject); // LS: previously member_descriptor
136140
declare_type(_PyExc_BaseException, BaseException, PyBaseExceptionObject);
137141
declare_type(PyBuffer_Type, buffer, PyBufferDecorator);
@@ -298,6 +302,10 @@ const char* PyTruffle_StringToCstr(void* o, int32_t strLen) {
298302
return str;
299303
}
300304

305+
const char* PyTruffle_CstrToString(const char* o) {
306+
return polyglot_from_string(o, SRC_CS);
307+
}
308+
301309
#define PRIMITIVE_ARRAY_TO_NATIVE(__jtype__, __ctype__, __polyglot_type__, __element_cast__) \
302310
void* PyTruffle_##__jtype__##ArrayToNative(const void* jarray, int64_t len) { \
303311
int64_t i; \

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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ 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));
236+
PyDict_SetItemString(native_members, "tp_dictoffset", PyLong_FromSsize_t(cls->tp_dictoffset));
235237
const char* class_name = cls->tp_name;
236238
PyTypeObject* javacls = polyglot_invoke(PY_TRUFFLE_CEXT,
237239
"PyType_Ready",

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.graal.python.builtins.Python3Core;
3838
import com.oracle.graal.python.builtins.objects.PNone;
3939
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
40+
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
4041
import com.oracle.graal.python.builtins.objects.code.PCode;
4142
import com.oracle.graal.python.builtins.objects.dict.PDict;
4243
import com.oracle.graal.python.builtins.objects.function.PArguments;
@@ -313,6 +314,8 @@ protected Object findMetaObject(PythonContext context, Object value) {
313314
if (value != null) {
314315
if (value instanceof PythonObject) {
315316
return ((PythonObject) value).asPythonClass();
317+
} else if (value instanceof PythonNativeObject) {
318+
return null;
316319
} else if (value instanceof PythonAbstractObject ||
317320
value instanceof Number ||
318321
value instanceof String ||

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ private static final String[] initializeCoreFiles() {
216216
"_sysconfig",
217217
"_socket",
218218
"_thread",
219+
"ctypes",
219220
"zipimport"));
220221

221222
return coreFiles.toArray(new String[coreFiles.size()]);

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/CtypesModuleBuiltins.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,46 @@
4040
*/
4141
package com.oracle.graal.python.builtins.modules;
4242

43-
import java.util.ArrayList;
4443
import java.util.List;
4544

45+
import com.oracle.graal.python.builtins.Builtin;
4646
import com.oracle.graal.python.builtins.CoreFunctions;
4747
import com.oracle.graal.python.builtins.PythonBuiltins;
48+
import com.oracle.graal.python.builtins.objects.PNone;
49+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
50+
import com.oracle.graal.python.builtins.objects.str.PString;
4851
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
52+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
53+
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
4954
import com.oracle.truffle.api.dsl.NodeFactory;
55+
import com.oracle.truffle.api.dsl.Specialization;
5056

5157
@CoreFunctions(defineModule = "ctypes")
5258
public class CtypesModuleBuiltins extends PythonBuiltins {
5359
@Override
5460
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
55-
return new ArrayList<>();
61+
return CtypesModuleBuiltinsFactory.getFactories();
62+
}
63+
64+
@Builtin(name = "c_char_p_", fixedNumOfPositionalArgs = 1)
65+
@GenerateNodeFactory
66+
abstract static class CCharP extends PythonUnaryBuiltinNode {
67+
@Child CExtNodes.AsCharPointer asCharPointer = CExtNodes.AsCharPointer.create();
68+
@Child CExtNodes.ToSulongNode toSulongNode = CExtNodes.ToSulongNode.create();
69+
70+
@Specialization
71+
Object defaultValue(@SuppressWarnings("unused") PNone noValue) {
72+
return toSulongNode.execute(PNone.NO_VALUE); // NULL
73+
}
74+
75+
@Specialization
76+
Object withValue(String value) {
77+
return asCharPointer.execute(value);
78+
}
79+
80+
@Specialization
81+
Object withValue(PString value) {
82+
return asCharPointer.execute(value.getValue());
83+
}
5684
}
5785
}

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
4545
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
4646
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
47-
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
4847

4948
import java.math.BigInteger;
5049
import java.nio.ByteBuffer;
@@ -82,6 +81,7 @@
8281
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.MayRaiseTernaryNodeGen;
8382
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.MayRaiseUnaryNodeGen;
8483
import com.oracle.graal.python.builtins.objects.cext.HandleCache;
84+
import com.oracle.graal.python.builtins.objects.cext.NativeMemberNames;
8585
import com.oracle.graal.python.builtins.objects.cext.NativeWrappers.PrimitiveNativeWrapper;
8686
import com.oracle.graal.python.builtins.objects.cext.NativeWrappers.PySequenceArrayWrapper;
8787
import com.oracle.graal.python.builtins.objects.cext.NativeWrappers.PythonClassInitNativeWrapper;
@@ -522,13 +522,15 @@ Object run(Object typestruct, PythonClass metaClass, PTuple baseClasses, PDict n
522522
}
523523

524524
// 'tp_name' contains the fully-qualified name, i.e., 'module.A.B...'
525-
String fqname = getStringItem(nativeMembers, "tp_name");
526-
String doc = getStringItem(nativeMembers, "tp_doc");
525+
String fqname = getStringItem(nativeMembers, NativeMemberNames.TP_NAME);
526+
String doc = getStringItem(nativeMembers, NativeMemberNames.TP_DOC);
527527
// the qualified name (i.e. without module name) like 'A.B...'
528528
String qualName = getQualName(fqname);
529529
PythonNativeClass cclass = factory().createNativeClassWrapper(typestruct, metaClass, qualName, bases);
530530
writeNode.execute(cclass, SpecialAttributeNames.__DOC__, doc);
531-
writeNode.execute(cclass, SpecialAttributeNames.__BASICSIZE__, getLongItem(nativeMembers, "tp_basicsize"));
531+
writeNode.execute(cclass, SpecialAttributeNames.__BASICSIZE__, getLongItem(nativeMembers, NativeMemberNames.TP_BASICSIZE));
532+
writeNode.execute(cclass, SpecialAttributeNames.__ITEMSIZE__, getLongItem(nativeMembers, NativeMemberNames.TP_ITEMSIZE));
533+
writeNode.execute(cclass, SpecialAttributeNames.__DICTOFFSET__, getLongItem(nativeMembers, NativeMemberNames.TP_DICTOFFSET));
532534
String moduleName = getModuleName(fqname);
533535
if (moduleName != null) {
534536
writeNode.execute(cclass, SpecialAttributeNames.__MODULE__, moduleName);
@@ -2003,7 +2005,7 @@ private static BigInteger convertToBigInteger(long n) {
20032005
@Specialization
20042006
Object doPointer(PythonNativeObject n, @SuppressWarnings("unused") int signed,
20052007
@Cached("create()") CExtNodes.ToSulongNode toSulongNode) {
2006-
return toSulongNode.execute(factory().createNativeVoidPtr((TruffleObject) n.object));
2008+
return toSulongNode.execute(factory().createNativeVoidPtr(n.object));
20072009
}
20082010
}
20092011

@@ -2143,10 +2145,7 @@ abstract static class PyBytes_FromStringAndSize extends NativeBuiltin {
21432145

21442146
@Specialization
21452147
PBytes doGeneric(PythonNativeObject object) {
2146-
if (object.object instanceof TruffleObject) {
2147-
return factory().createBytes(getByteArray((TruffleObject) object.object));
2148-
}
2149-
throw raise(TypeError, "invalid pointer: %s", object.object);
2148+
return factory().createBytes(getByteArray(object.object));
21502149
}
21512150
}
21522151

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 = insert(ToSulongNode.create());
1476+
toJava = insert(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
}

0 commit comments

Comments
 (0)