Skip to content

Commit 80dc54f

Browse files
committed
[GR-10431] Fixes for R-Python/Numpy interop (part 2).
PullRequest: graalpython/94
2 parents 45cb602 + 35c030c commit 80dc54f

File tree

4 files changed

+121
-31
lines changed

4 files changed

+121
-31
lines changed

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

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ static PyObject* wrap_setattrfunc(setattrfunc f, PyObject* obj, PyObject* unicod
201201
return f(explicit_cast(obj), as_char_pointer(unicode), explicit_cast(value));
202202
}
203203

204+
static PyObject* wrap_setattrofunc(setattrofunc f, PyObject* obj, PyObject* key, PyObject* item) {
205+
return PyLong_FromLong(f(explicit_cast(obj), explicit_cast(key), explicit_cast(item)));
206+
}
207+
204208
static PyObject* wrap_richcmpfunc(richcmpfunc f, PyObject* a, PyObject* b, PyObject* n) {
205209
return f(explicit_cast(a), explicit_cast(b), (int)PyLong_AsLong(n));
206210
}
@@ -209,10 +213,26 @@ static PyObject* wrap_ssizeobjargproc(ssizeobjargproc f, PyObject* a, PyObject*
209213
return PyLong_FromLong(f(explicit_cast(a), PyLong_AsSsize_t(size), explicit_cast(b)));
210214
}
211215

216+
static PyObject* wrap_ssizeargfunc(ssizeargfunc f, PyObject* a, PyObject* size) {
217+
return PyLong_FromLong(f(explicit_cast(a), PyLong_AsSsize_t(size)));
218+
}
219+
212220
static PyObject* wrap_initproc(initproc f, PyObject* a, PyObject* b, PyObject* c) {
213221
return PyLong_FromLong(f(explicit_cast(a), explicit_cast(b), explicit_cast(c)));
214222
}
215223

224+
static PyObject* wrap_objobjargproc(objobjargproc f, PyObject* a, PyObject* b, PyObject* c) {
225+
return PyLong_FromLong(f(explicit_cast(a), explicit_cast(b), explicit_cast(c)));
226+
}
227+
228+
static PyObject* wrap_objobjproc(objobjproc f, PyObject* a, PyObject* b) {
229+
return PyLong_FromLong(f(explicit_cast(a), explicit_cast(b)));
230+
}
231+
232+
static PyObject* wrap_inquiry(inquiry f, PyObject* a) {
233+
return PyLong_FromLong(f(explicit_cast(a)));
234+
}
235+
216236
/* very special case: operator '**' has an optional third arg */
217237
static PyObject* wrap_pow(ternaryfunc f, ...) {
218238
int nargs = polyglot_get_arg_count();
@@ -227,6 +247,15 @@ static PyObject* wrap_pow(ternaryfunc f, ...) {
227247
return native_to_java(NULL);
228248
}
229249

250+
static PyObject* wrap_lenfunc(lenfunc f, PyObject* a) {
251+
return PyLong_FromSsize_t(f(explicit_cast(a)));
252+
}
253+
254+
static Py_hash_t wrap_hashfunc(hashfunc f, PyObject* a) {
255+
return PyLong_FromSsize_t(f(explicit_cast(a)));
256+
}
257+
258+
230259
int PyType_Ready(PyTypeObject* cls) {
231260
#define ADD_IF_MISSING(attr, def) if (!(attr)) { attr = def; }
232261
#define ADD_METHOD(m) ADD_METHOD_OR_SLOT(m.ml_name, get_method_flags_cwrapper(m.ml_flags), m.ml_meth, m.ml_flags, m.ml_doc)
@@ -378,11 +407,11 @@ int PyType_Ready(PyTypeObject* cls) {
378407
ADD_SLOT_CONV("__getattr__", wrap_getattrfunc, cls->tp_getattr, -2);
379408
ADD_SLOT_CONV("__setattr__", wrap_setattrfunc, cls->tp_setattr, -3);
380409
ADD_SLOT("__repr__", cls->tp_repr, -1);
381-
ADD_SLOT("__hash__", cls->tp_hash, -1);
410+
ADD_SLOT_CONV("__hash__", wrap_hashfunc, cls->tp_hash, -1);
382411
ADD_SLOT("__call__", cls->tp_call, METH_KEYWORDS | METH_VARARGS);
383412
ADD_SLOT("__str__", cls->tp_str, -1);
384413
ADD_SLOT("__getattr__", cls->tp_getattro, -2);
385-
ADD_SLOT("__setattr__", cls->tp_getattro, -3);
414+
ADD_SLOT_CONV("__setattr__", wrap_setattrofunc, cls->tp_setattro, -3);
386415
ADD_SLOT("__clear__", cls->tp_clear, -1);
387416
ADD_SLOT_CONV("__compare__", wrap_richcmpfunc, cls->tp_richcompare, -3);
388417
ADD_SLOT("__iter__", cls->tp_iter, -1);
@@ -407,7 +436,7 @@ int PyType_Ready(PyTypeObject* cls) {
407436
ADD_SLOT("__neg__", numbers->nb_negative, -1);
408437
ADD_SLOT("__pos__", numbers->nb_positive, -1);
409438
ADD_SLOT("__abs__", numbers->nb_absolute, -1);
410-
ADD_SLOT("__bool__", numbers->nb_bool, -1);
439+
ADD_SLOT_CONV("__bool__", wrap_inquiry, numbers->nb_bool, -1);
411440
ADD_SLOT("__invert__", numbers->nb_invert, -1);
412441
ADD_SLOT("__lshift__", numbers->nb_lshift, -2);
413442
ADD_SLOT("__rshift__", numbers->nb_rshift, -2);
@@ -437,21 +466,21 @@ int PyType_Ready(PyTypeObject* cls) {
437466

438467
PySequenceMethods* sequences = cls->tp_as_sequence;
439468
if (sequences) {
440-
ADD_SLOT("__len__", sequences->sq_length, -1);
469+
ADD_SLOT_CONV("__len__", wrap_lenfunc, sequences->sq_length, -1);
441470
ADD_SLOT("__add__", sequences->sq_concat, -2);
442-
ADD_SLOT("__mul__", sequences->sq_repeat, -2);
443-
ADD_SLOT("__getitem__", sequences->sq_item, -2);
471+
ADD_SLOT_CONV("__mul__", wrap_ssizeargfunc, sequences->sq_repeat, -2);
472+
ADD_SLOT_CONV("__getitem__", wrap_ssizeargfunc, sequences->sq_item, -2);
444473
ADD_SLOT_CONV("__setitem__", wrap_ssizeobjargproc, sequences->sq_ass_item, -3);
445-
ADD_SLOT("__contains__", sequences->sq_contains, -2);
474+
ADD_SLOT_CONV("__contains__", wrap_objobjproc, sequences->sq_contains, -2);
446475
ADD_SLOT("__iadd__", sequences->sq_inplace_concat, -2);
447-
ADD_SLOT("__imul__", sequences->sq_inplace_repeat, -2);
476+
ADD_SLOT_CONV("__imul__", wrap_ssizeargfunc, sequences->sq_inplace_repeat, -2);
448477
}
449478

450479
PyMappingMethods* mappings = cls->tp_as_mapping;
451480
if (mappings) {
452-
ADD_SLOT("__len__", mappings->mp_length, -1);
481+
ADD_SLOT_CONV("__len__", wrap_lenfunc, mappings->mp_length, -1);
453482
ADD_SLOT("__getitem__", mappings->mp_subscript, -2);
454-
ADD_SLOT("__setitem__", mappings->mp_ass_subscript, -3);
483+
ADD_SLOT_CONV("__setitem__", wrap_objobjargproc, mappings->mp_ass_subscript, -3);
455484
}
456485

457486
PyAsyncMethods* async = cls->tp_as_async;
@@ -466,7 +495,6 @@ int PyType_Ready(PyTypeObject* cls) {
466495
// TODO ...
467496
}
468497

469-
// TODO link subclasses
470498
/* Link into each base class's list of subclasses */
471499
bases = cls->tp_bases;
472500
Py_ssize_t n = PyTuple_GET_SIZE(bases);

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

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,11 @@
4040
if sys.implementation.name == "graalpython":
4141
import polyglot
4242

43-
4443
def test_import():
4544
imported_cext = polyglot.import_value("python_cext")
4645
import python_cext
4746
assert imported_cext is python_cext
4847

49-
5048
class CustomObject():
5149
field = 42
5250

@@ -56,7 +54,6 @@ def __getitem__(self, item):
5654
def __len__(self):
5755
return 21
5856

59-
6057
class CustomMutable(CustomObject):
6158
_items = {}
6259

@@ -78,15 +75,13 @@ def __setitem__(self, key, item):
7875
def __delitem__(self, key):
7976
del self._items[key]
8077

81-
8278
def test_read():
8379
o = CustomObject()
8480
assert polyglot.__read__(o, "field") == o.field
8581
assert polyglot.__read__(o, 10) == o[10]
8682
assert polyglot.__read__(o, "@field") == o.field
8783
assert polyglot.__read__(o, "[field") == o["field"]
8884

89-
9085
def test_write():
9186
o = CustomMutable()
9287
o2 = CustomObject()
@@ -120,7 +115,6 @@ def test_write():
120115
polyglot.__write__(o2, non_string, 12)
121116
assert getattr(o2, non_string) == 12
122117

123-
124118
def test_remove():
125119
o = CustomMutable()
126120
o.direct_field = 111
@@ -141,26 +135,21 @@ def test_remove():
141135
polyglot.__remove__(o, "grrrr")
142136
assert "grrrr" not in list(o.keys())
143137

144-
145138
def test_execute():
146139
assert polyglot.__execute__(abs, -10) == 10
147140
o = CustomMutable()
148141
assert polyglot.__execute__(o.__getattribute__, "field") == o.field
149142

150-
151143
def test_invoke():
152144
o = CustomMutable()
153145
assert polyglot.__invoke__(o, "__getattribute__", "field") == o.field
154146

155-
156147
def test_new():
157148
assert isinstance(polyglot.__new__(CustomMutable), CustomMutable)
158149

159-
160150
def test_is_null():
161151
assert polyglot.__is_null__(None)
162152

163-
164153
def test_has_size():
165154
import array
166155

@@ -171,15 +160,16 @@ def test_has_size():
171160
assert polyglot.__has_size__(b"")
172161
assert polyglot.__has_size__("")
173162
assert polyglot.__has_size__(range(10))
163+
assert polyglot.__has_size__(CustomObject())
174164

175165
assert not polyglot.__has_size__({})
176166
assert not polyglot.__has_size__(object())
177-
assert not polyglot.__has_size__(CustomObject())
178-
179167

180168
def test_get_size():
181169
called = False
170+
182171
class LenObject():
172+
183173
def __getitem__(self, k):
184174
if k == 0:
185175
return 1
@@ -194,23 +184,20 @@ def __len__(self):
194184
assert polyglot.__get_size__(LenObject()) == 1
195185
assert called
196186

197-
198187
def test_has_keys():
199188
assert not polyglot.__has_keys__(True)
200189
assert polyglot.__has_keys__(None)
201190
assert polyglot.__has_keys__(NotImplemented)
202191
assert not polyglot.__has_keys__(False)
203192
assert polyglot.__has_keys__(object())
204193

205-
206194
def test_keys():
207195
o = CustomObject()
208196
assert len(polyglot.__keys__(o)) == 0
209197
o.my_field = 1
210198
assert len(polyglot.__keys__(o)) == 1
211199
assert "my_field" in polyglot.__keys__(o)
212200

213-
214201
def test_host_lookup():
215202
import java
216203
try:

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/datamodel/IsSequenceNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
import com.oracle.truffle.api.profiles.ConditionProfile;
4747

4848
public abstract class IsSequenceNode extends PDataModelEmulationNode {
49-
@Child private HasInheritedAttributeNode hasGetItemNode = HasInheritedAttributeNode.create(__LEN__);
50-
@Child private HasInheritedAttributeNode hasLenNode = HasInheritedAttributeNode.create(__GETITEM__);
49+
@Child private HasInheritedAttributeNode hasGetItemNode = HasInheritedAttributeNode.create(__GETITEM__);
50+
@Child private HasInheritedAttributeNode hasLenNode = HasInheritedAttributeNode.create(__LEN__);
5151

5252
private final ConditionProfile lenProfile = ConditionProfile.createBinaryProfile();
5353
private final ConditionProfile getItemProfile = ConditionProfile.createBinaryProfile();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/interop/PythonMessageResolution.java

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@
3838
*/
3939
package com.oracle.graal.python.runtime.interop;
4040

41+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
42+
4143
import java.util.Arrays;
4244

4345
import com.oracle.graal.python.builtins.modules.BuiltinFunctions;
4446
import com.oracle.graal.python.builtins.modules.BuiltinFunctionsFactory;
4547
import com.oracle.graal.python.builtins.objects.PNone;
4648
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
4749
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
50+
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
4851
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4952
import com.oracle.graal.python.builtins.objects.list.PList;
5053
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
@@ -60,6 +63,7 @@
6063
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
6164
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
6265
import com.oracle.graal.python.nodes.call.CallDispatchNode;
66+
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
6367
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
6468
import com.oracle.graal.python.nodes.datamodel.IsCallableNode;
6569
import com.oracle.graal.python.nodes.datamodel.IsMappingNode;
@@ -444,19 +448,90 @@ public Object access(Object object) {
444448

445449
@Resolve(message = "HAS_SIZE")
446450
abstract static class PForeignHasSizeNode extends Node {
451+
@Child private IsSequenceNode isSequenceNode;
452+
@Child private IsMappingNode isMappingNode;
453+
@Child private BuiltinFunctions.LenNode lenNode;
454+
@Child private PTypeUnboxNode unboxNode;
455+
@Child private LookupAndCallBinaryNode callGetItemNode;
456+
457+
private final ValueProfile profile = ValueProfile.createClassProfile();
458+
447459
public Object access(Object object) {
448-
return object instanceof PSequence;
460+
Object profiled = profile.profile(object);
461+
// A sequence object always has a size even if there is no '__len__' attribute. This is,
462+
// e.g., the case for 'array'.
463+
if (profiled instanceof PSequence) {
464+
return true;
465+
}
466+
if (profiled instanceof PHashingCollection) {
467+
return false;
468+
}
469+
if (getIsSequenceNode().execute(profiled) && !getIsMappingNode().execute(profiled)) {
470+
// also try to access using an integer index
471+
int len = (int) getUnboxNode().execute(getLenNode().executeWith(profiled));
472+
if (len > 0) {
473+
try {
474+
getCallGetItemNode().executeObject(profiled, 0);
475+
return true;
476+
} catch (PException e) {
477+
return false;
478+
}
479+
}
480+
return true;
481+
}
482+
return false;
483+
}
484+
485+
private BuiltinFunctions.LenNode getLenNode() {
486+
if (lenNode == null) {
487+
CompilerDirectives.transferToInterpreterAndInvalidate();
488+
lenNode = insert(BuiltinFunctionsFactory.LenNodeFactory.create());
489+
}
490+
return lenNode;
491+
}
492+
493+
private PTypeUnboxNode getUnboxNode() {
494+
if (unboxNode == null) {
495+
CompilerDirectives.transferToInterpreterAndInvalidate();
496+
unboxNode = insert(PTypeUnboxNode.create());
497+
}
498+
return unboxNode;
499+
}
500+
501+
private LookupAndCallBinaryNode getCallGetItemNode() {
502+
if (callGetItemNode == null) {
503+
CompilerDirectives.transferToInterpreterAndInvalidate();
504+
callGetItemNode = insert(LookupAndCallBinaryNode.create(__GETITEM__));
505+
}
506+
return callGetItemNode;
507+
}
508+
509+
private IsSequenceNode getIsSequenceNode() {
510+
if (isSequenceNode == null) {
511+
CompilerDirectives.transferToInterpreterAndInvalidate();
512+
isSequenceNode = insert(IsSequenceNode.create());
513+
}
514+
return isSequenceNode;
515+
}
516+
517+
private IsMappingNode getIsMappingNode() {
518+
if (isMappingNode == null) {
519+
CompilerDirectives.transferToInterpreterAndInvalidate();
520+
isMappingNode = insert(IsMappingNode.create());
521+
}
522+
return isMappingNode;
449523
}
450524
}
451525

452526
@Resolve(message = "GET_SIZE")
453527
abstract static class PForeignGetSizeNode extends Node {
454528
@Child IsSequenceNode isSeq = IsSequenceNode.create();
455529
@Child private BuiltinFunctions.LenNode lenNode = BuiltinFunctionsFactory.LenNodeFactory.create();
530+
@Child private PTypeUnboxNode unboxNode = PTypeUnboxNode.create();
456531

457532
public Object access(Object object) {
458533
if (isSeq.execute(object)) {
459-
return lenNode.executeWith(object);
534+
return unboxNode.execute(lenNode.executeWith(object));
460535
}
461536
throw UnsupportedMessageException.raise(Message.GET_SIZE);
462537
}

0 commit comments

Comments
 (0)