Skip to content

Commit bb3c1fa

Browse files
committed
Merge native type dict storages if assign to 'tp_dict'.
1 parent 5ff82b9 commit bb3c1fa

File tree

7 files changed

+119
-14
lines changed

7 files changed

+119
-14
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,10 @@ int PyType_Ready(PyTypeObject* cls) {
233233
// remember the managed wrapper
234234
((PyObject*)cls)->ob_refcnt = truffle_handle_for_managed(javacls);
235235
if (cls->tp_dict != NULL) {
236-
// TODO: (tfel) is this always safe?
237-
PyDict_Update(javacls->tp_dict, cls->tp_dict);
236+
javacls->tp_dict = native_to_java(cls->tp_dict);
237+
} else {
238+
cls->tp_dict = javacls->tp_dict;
238239
}
239-
cls->tp_dict = javacls->tp_dict;
240240

241241
PyMethodDef* methods = cls->tp_methods;
242242
if (methods) {

graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,15 @@ def CPyExtType(name, code, **kwargs):
448448
{nb_inplace_floor_divide},
449449
{nb_inplace_true_divide},
450450
{nb_index},
451+
""" + ("""
451452
{nb_matrix_multiply},
452453
{nb_inplace_matrix_multiply},
454+
""" if sys.version_info.minor >= 6 else "") + """
455+
}};
456+
457+
static struct PyMethodDef {name}_methods[] = {{
458+
{tp_methods},
459+
{{NULL, NULL, 0, NULL}}
453460
}};
454461
455462
typedef struct {{
@@ -459,13 +466,13 @@ def CPyExtType(name, code, **kwargs):
459466
static PyTypeObject {name}Type = {{
460467
PyVarObject_HEAD_INIT(NULL, 0)
461468
"{name}.{name}",
462-
sizeof({name}Object), /* tp_basicsize */
463-
0, /* tp_itemsize */
464-
0, /* tp_dealloc */
469+
sizeof({name}Object), /* tp_basicsize */
470+
0, /* tp_itemsize */
471+
0, /* tp_dealloc */
465472
{tp_print},
466473
{tp_getattr},
467474
{tp_setattr},
468-
0, /* tp_reserved */
475+
0, /* tp_reserved */
469476
{tp_repr},
470477
&{name}_number_methods,
471478
{tp_as_sequence},
@@ -478,6 +485,24 @@ def CPyExtType(name, code, **kwargs):
478485
{tp_as_buffer},
479486
Py_TPFLAGS_DEFAULT,
480487
"",
488+
{tp_traverse}, /* tp_traverse */
489+
{tp_clear}, /* tp_clear */
490+
{tp_richcompare}, /* tp_richcompare */
491+
0, /* tp_weaklistoffset */
492+
{tp_iter}, /* tp_iter */
493+
{tp_iternext}, /* tp_iternext */
494+
{name}_methods, /* tp_methods */
495+
NULL, /* tp_members */
496+
0, /* tp_getset */
497+
0, /* tp_base */
498+
{tp_dict}, /* tp_dict */
499+
0, /* tp_descr_get */
500+
0, /* tp_descr_set */
501+
0, /* tp_dictoffset */
502+
{tp_init}, /* tp_init */
503+
PyType_GenericAlloc, /* tp_alloc */
504+
PyType_GenericNew, /* tp_new */
505+
PyObject_Del, /* tp_free */
481506
}};
482507
483508
static PyModuleDef {name}module = {{
@@ -493,10 +518,10 @@ def CPyExtType(name, code, **kwargs):
493518
{{
494519
PyObject* m;
495520
496-
{name}Type.tp_new = PyType_GenericNew;
497521
{ready_code}
498522
if (PyType_Ready(&{name}Type) < 0)
499523
return NULL;
524+
{post_ready_code}
500525
501526
m = PyModule_Create(&{name}module);
502527
if (m == NULL)
@@ -511,6 +536,8 @@ def CPyExtType(name, code, **kwargs):
511536
kwargs["name"] = name
512537
kwargs["code"] = code
513538
kwargs.setdefault("ready_code", "")
539+
kwargs.setdefault("post_ready_code", "")
540+
kwargs.setdefault("tp_methods", "{NULL, NULL, 0, NULL}")
514541
c_source = UnseenFormatter().format(template, **kwargs)
515542

516543
source_file = "%s/%s.c" % (__dir__, name)

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_object.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ def test_index(self):
100100
nb_index="test_index"
101101
)
102102
tester = TestIndex()
103-
assert [0,1][tester] == 1
103+
assert [0, 1][tester] == 1
104104

105105
def test_getattro(self):
106-
return # TODO: not working yet
106+
return # TODO: not working yet
107107
# XXX: Cludge to get type into C
108108
sys.modules["test_getattro_AttroClass"] = AttroClass
109109
try:
@@ -120,6 +120,31 @@ def test_getattro(self):
120120
tester = TestInt()
121121
assert tester.foo == "foo"
122122

123+
def test_dict(self):
124+
TestDict = CPyExtType("TestDict",
125+
"""static PyObject* custom_dict = NULL;
126+
static PyObject* get_dict(PyObject* self, PyObject* kwargs) {
127+
Py_INCREF(custom_dict);
128+
return custom_dict;
129+
}
130+
""",
131+
ready_code="""
132+
custom_dict = PyDict_New();
133+
PyDict_SetItemString(custom_dict, "hello", PyUnicode_FromString("first custom property"));
134+
TestDictType.tp_dict = custom_dict;
135+
""",
136+
post_ready_code="""
137+
PyDict_SetItemString(TestDictType.tp_dict, "world", PyUnicode_FromString("second custom property"));
138+
""",
139+
tp_methods='{"get_dict", get_dict, METH_NOARGS, ""}'
140+
)
141+
tester = TestDict()
142+
assert tester.hello == "first custom property"
143+
assert tester.world == "second custom property"
144+
assert "hello" in tester.get_dict().keys() and "world" in tester.get_dict().keys(), "was: %s" % tester.get_dict().keys()
145+
tester.get_dict()["extra"] = "blah"
146+
assert tester.extra == "blah"
147+
123148

124149
class TestObjectFunctions(CPyExtTestCase):
125150
def compile_module(self, name):

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

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,14 @@
5959
import com.oracle.graal.python.builtins.objects.cext.PythonObjectNativeWrapperMRFactory.ToPyObjectNodeGen;
6060
import com.oracle.graal.python.builtins.objects.cext.PythonObjectNativeWrapperMRFactory.WriteNativeMemberNodeGen;
6161
import com.oracle.graal.python.builtins.objects.cext.UnicodeObjectNodes.UnicodeAsWideCharNode;
62+
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
6263
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
6364
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.GetItemNode;
6465
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.SetItemNode;
66+
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
6567
import com.oracle.graal.python.builtins.objects.dict.PDict;
6668
import com.oracle.graal.python.builtins.objects.floats.PFloat;
69+
import com.oracle.graal.python.builtins.objects.mappingproxy.PMappingproxy;
6770
import com.oracle.graal.python.builtins.objects.memoryview.PBuffer;
6871
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
6972
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
@@ -80,6 +83,7 @@
8083
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
8184
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
8285
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
86+
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
8387
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
8488
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
8589
import com.oracle.graal.python.nodes.object.GetClassNode;
@@ -377,12 +381,26 @@ Object doState(PString object, @SuppressWarnings("unused") String key) {
377381
return new PyUnicodeState(object);
378382
}
379383

380-
@Specialization(guards = "eq(MD_DICT, key) || eq(TP_DICT, key)")
384+
@Specialization(guards = "eq(MD_DICT, key)")
381385
Object doMdDict(PythonObject object, @SuppressWarnings("unused") String key,
382386
@Cached("create(__GETATTRIBUTE__)") LookupAndCallBinaryNode getDictNode) {
383387
return getToSulongNode().execute(getDictNode.executeObject(object, SpecialAttributeNames.__DICT__));
384388
}
385389

390+
@Specialization(guards = "eq(TP_DICT, key)")
391+
Object doTpDict(PythonClass object, @SuppressWarnings("unused") String key) {
392+
PHashingCollection dict = object.getDict();
393+
if (!(dict instanceof PDict)) {
394+
assert dict instanceof PMappingproxy || dict == null;
395+
// If 'dict instanceof PMappingproxy', it seems that someone already used '__dict__'
396+
// on this type and created a mappingproxy object. We need to replace it by a dict.
397+
dict = factory().createDictFixedStorage(object);
398+
object.setDict(dict);
399+
}
400+
assert dict instanceof PDict;
401+
return getToSulongNode().execute(dict);
402+
}
403+
386404
@Specialization(guards = "eq(MD_DEF, key)")
387405
Object doMdDef(PythonObject object, @SuppressWarnings("unused") String key) {
388406
PythonObjectNativeWrapper nativeWrapper = ((PythonAbstractObject) object).getNativeWrapper();
@@ -519,7 +537,7 @@ public Object access(PythonNativeWrapper object, String key, Object value) {
519537
}
520538

521539
@ImportStatic({NativeMemberNames.class, PGuards.class})
522-
abstract static class WriteNativeMemberNode extends Node {
540+
abstract static class WriteNativeMemberNode extends PBaseNode {
523541
@Child private HashingStorageNodes.SetItemNode setItemNode;
524542

525543
abstract Object execute(Object receiver, String key, Object value);
@@ -571,6 +589,30 @@ Object doMdDef(PythonObject object, @SuppressWarnings("unused") String key, Obje
571589
return value;
572590
}
573591

592+
@Specialization(guards = "eq(TP_DICT, key)")
593+
Object doTpDict(PythonClass object, @SuppressWarnings("unused") String key, Object nativeValue,
594+
@Cached("create()") CExtNodes.AsPythonObjectNode asPythonObjectNode,
595+
@Cached("create()") WriteAttributeToObjectNode writeAttrNode) {
596+
Object value = asPythonObjectNode.execute(nativeValue);
597+
if (value instanceof PDict && ((PDict) value).getPythonClass() == getCore().lookupType(PDict.class)) {
598+
// special and fast case: commit items and change store
599+
PDict d = (PDict) value;
600+
for (Object k : d.keys()) {
601+
writeAttrNode.execute(object, k, d.getItem(k));
602+
}
603+
PHashingCollection existing = object.getDict();
604+
if (existing != null) {
605+
d.setDictStorage(existing.getDictStorage());
606+
} else {
607+
d.setDictStorage(new DynamicObjectStorage.PythonObjectDictStorage(object.getStorage()));
608+
}
609+
object.setDict(d);
610+
} else {
611+
// TODO custom mapping object
612+
}
613+
return value;
614+
}
615+
574616
@Specialization
575617
Object doMemoryview(PMemoryView object, String key, Object value,
576618
@Cached("create()") ReadAttributeFromObjectNode readAttrNode,

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@
5151
import com.oracle.graal.python.builtins.CoreFunctions;
5252
import com.oracle.graal.python.builtins.PythonBuiltins;
5353
import com.oracle.graal.python.builtins.objects.PNone;
54+
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
5455
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
56+
import com.oracle.graal.python.builtins.objects.dict.PDict;
5557
import com.oracle.graal.python.builtins.objects.function.PKeyword;
5658
import com.oracle.graal.python.builtins.objects.function.PythonCallable;
5759
import com.oracle.graal.python.builtins.objects.list.PList;
60+
import com.oracle.graal.python.builtins.objects.mappingproxy.PMappingproxy;
5861
import com.oracle.graal.python.builtins.objects.object.PythonObject;
5962
import com.oracle.graal.python.builtins.objects.type.TypeBuiltinsFactory.CallNodeFactory;
6063
import com.oracle.graal.python.nodes.SpecialMethodNames;
@@ -378,7 +381,11 @@ Object dict(PythonClass self) {
378381
if (dict == null) {
379382
dict = factory().createMappingproxy(self);
380383
self.setDict(dict);
384+
} else if (dict instanceof PDict) {
385+
// this is the case for types defined in native code
386+
dict = factory().createMappingproxy(new DynamicObjectStorage.PythonObjectHybridDictStorage(self.getStorage()));
381387
}
388+
assert dict instanceof PMappingproxy;
382389
return dict;
383390
}
384391
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ protected Object attrKey(Object key) {
103103
"checkShape(object, cachedObject, cachedShape)",
104104
"key == cachedKey",
105105
"!isNull(loc)",
106-
"loc.isAssumedFinal()"
106+
"loc.isAssumedFinal()",
107107
}, //
108108
assumptions = {
109109
"layoutAssumption",
@@ -132,7 +132,7 @@ private static boolean assertFinal(PythonObject object, Object key, Object cache
132132
guards = {
133133
"object.getStorage().getShape() == cachedShape",
134134
"key == cachedKey",
135-
"isNull(loc) || !loc.isAssumedFinal()"
135+
"isNull(loc) || !loc.isAssumedFinal()",
136136
}, //
137137
assumptions = "layoutAssumption")
138138
protected Object readDirect(PythonObject object, Object key,

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ public PMappingproxy createMappingproxy(PythonObject object) {
503503
return trace(new PMappingproxy(lookupClass(PythonBuiltinClassType.PMappingproxy), new PythonObjectDictStorage(object.getStorage())));
504504
}
505505

506+
public PMappingproxy createMappingproxy(HashingStorage storage) {
507+
return trace(new PMappingproxy(lookupClass(PythonBuiltinClassType.PMappingproxy), storage));
508+
}
509+
506510
public PMappingproxy createMappingproxy(PythonClass cls, PythonObject object) {
507511
return trace(new PMappingproxy(cls, new PythonObjectDictStorage(object.getStorage())));
508512
}

0 commit comments

Comments
 (0)