Skip to content

Commit 170b742

Browse files
committed
Support buffer protocol for Python bytes.
1 parent 019cf2e commit 170b742

14 files changed

+346
-56
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ PyObject * PyMapping_GetItemString(PyObject *o, const char *key) {
263263
return to_sulong(result);
264264
}
265265

266+
// taken from CPython "Objects/abstract.c"
266267
int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {
267268
PyBufferProcs *pb = obj->ob_type->tp_as_buffer;
268269

@@ -275,6 +276,19 @@ int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {
275276
return (*pb->bf_getbuffer)(obj, view, flags);
276277
}
277278

279+
// taken from CPython "Objects/abstract.c"
280+
void PyBuffer_Release(Py_buffer *view) {
281+
PyObject *obj = view->obj;
282+
PyBufferProcs *pb;
283+
if (obj == NULL)
284+
return;
285+
pb = Py_TYPE(obj)->tp_as_buffer;
286+
if (pb && pb->bf_releasebuffer)
287+
pb->bf_releasebuffer(obj, view);
288+
view->obj = NULL;
289+
Py_DECREF(obj);
290+
}
291+
278292
// taken from CPython "Objects/abstract.c"
279293
/* we do this in native code since we need to fill in the values in a given 'Py_buffer' struct */
280294
int PyBuffer_FillInfo(Py_buffer *view, PyObject *obj, void *buf, Py_ssize_t len, int readonly, int flags) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ PyTypeObject* PyObjectHandle_ForJavaType(void* jobj) {
223223
return jobj;
224224
}
225225

226+
/** to be used from Java code only; creates the deref handle for a sequence wrapper */
227+
void* NativeHandle_ForArray(void* jobj, ssize_t element_size) {
228+
// TODO do polyglot typecast depending on element_size
229+
return truffle_deref_handle_for_managed(jobj);
230+
}
231+
226232
const char* PyTruffle_StringToCstr(void* jlString) {
227233
return truffle_string_to_cstr(jlString);
228234
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import com.oracle.graal.python.builtins.objects.bytes.BytesBuiltins;
6161
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
6262
import com.oracle.graal.python.builtins.objects.cext.CExtNodes;
63+
import com.oracle.graal.python.builtins.objects.cext.PythonClassNativeWrapper;
6364
import com.oracle.graal.python.builtins.objects.cext.PythonObjectNativeWrapper;
6465
import com.oracle.graal.python.builtins.objects.cext.UnicodeObjectNodes.UnicodeAsWideCharNode;
6566
import com.oracle.graal.python.builtins.objects.complex.PComplex;
@@ -68,7 +69,6 @@
6869
import com.oracle.graal.python.builtins.objects.function.Arity;
6970
import com.oracle.graal.python.builtins.objects.function.PArguments;
7071
import com.oracle.graal.python.builtins.objects.ints.PInt;
71-
import com.oracle.graal.python.builtins.objects.object.PythonObject;
7272
import com.oracle.graal.python.builtins.objects.str.PString;
7373
import com.oracle.graal.python.builtins.objects.traceback.PTraceback;
7474
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
@@ -1057,9 +1057,10 @@ PythonObjectNativeWrapper doPythonObject(PythonObjectNativeWrapper nativeWrapper
10571057
abstract static class PyTruffle_SetBufferProcs extends NativeBuiltin {
10581058

10591059
@Specialization
1060-
Object doPythonObject(PythonObject obj, Object getBufferProc, Object releaseBufferProc) {
1061-
obj.setAttribute(SpecialAttributeNames.__GET_BUFFER__, getBufferProc);
1062-
obj.setAttribute(SpecialAttributeNames.__RELEASE_BUFFER__, releaseBufferProc);
1060+
Object doPythonObject(PythonClass obj, Object getBufferProc, Object releaseBufferProc) {
1061+
PythonClassNativeWrapper nativeWrapper = obj.getNativeWrapper();
1062+
nativeWrapper.setGetBufferProc(getBufferProc);
1063+
nativeWrapper.setReleaseBufferProc(releaseBufferProc);
10631064
return PNone.NO_VALUE;
10641065
}
10651066
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ Object runNativeObject(PythonAbstractObject object) {
123123

124124
@Fallback
125125
Object run(Object obj) {
126+
assert obj != null : "Java 'null' cannot be a Sulong value";
126127
return obj;
127128
}
128129

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
@@ -44,6 +44,7 @@ public abstract class NativeCAPISymbols {
4444
public static final String FUN_PY_TRUFFLE_STRING_TO_CSTR = "PyTruffle_StringToCstr";
4545
public static final String FUN_PY_OBJECT_HANDLE_FOR_JAVA_OBJECT = "PyObjectHandle_ForJavaObject";
4646
public static final String FUN_PY_OBJECT_HANDLE_FOR_JAVA_TYPE = "PyObjectHandle_ForJavaType";
47+
public static final String FUN_NATIVE_HANDLE_FOR_ARRAY = "NativeHandle_ForArray";
4748
public static final String FUN_PY_NONE_HANDLE = "PyNoneHandle";
4849
public static final String FUN_WHCAR_SIZE = "PyTruffle_Wchar_Size";
4950

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2018, Oracle and/or its affiliates.
3+
*
4+
* The Universal Permissive License (UPL), Version 1.0
5+
*
6+
* Subject to the condition set forth below, permission is hereby granted to any
7+
* person obtaining a copy of this software, associated documentation and/or data
8+
* (collectively the "Software"), free of charge and under any and all copyright
9+
* rights in the Software, and any and all patent rights owned or freely
10+
* licensable by each licensor hereunder covering either (i) the unmodified
11+
* Software as contributed to or provided by such licensor, or (ii) the Larger
12+
* Works (as defined below), to deal in both
13+
*
14+
* (a) the Software, and
15+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
16+
* one is included with the Software (each a "Larger Work" to which the
17+
* Software is contributed by such licensors),
18+
*
19+
* without restriction, including without limitation the rights to copy, create
20+
* derivative works of, display, perform, and distribute the Software and make,
21+
* use, sell, offer for sale, import, export, have made, and have sold the
22+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
* either these or other terms.
24+
*
25+
* This license is subject to the following condition:
26+
*
27+
* The above copyright notice and either this complete permission notice or at a
28+
* minimum a reference to the UPL must be included in all copies or substantial
29+
* portions of the Software.
30+
*
31+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37+
* SOFTWARE.
38+
*/
39+
package com.oracle.graal.python.builtins.objects.cext;
40+
41+
import com.oracle.graal.python.builtins.objects.type.PythonClass;
42+
import com.oracle.truffle.api.interop.ForeignAccess;
43+
import com.oracle.truffle.api.interop.TruffleObject;
44+
45+
public class PyBufferProcsWrapper implements TruffleObject {
46+
47+
private final PythonClass delegate;
48+
49+
public PyBufferProcsWrapper(PythonClass delegate) {
50+
this.delegate = delegate;
51+
}
52+
53+
public PythonClass getDelegate() {
54+
return delegate;
55+
}
56+
57+
static boolean isInstance(TruffleObject o) {
58+
return o instanceof PyBufferProcsWrapper;
59+
}
60+
61+
public ForeignAccess getForeignAccess() {
62+
return PyBufferProcsWrapperMRForeign.ACCESS;
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2018, Oracle and/or its affiliates.
3+
*
4+
* The Universal Permissive License (UPL), Version 1.0
5+
*
6+
* Subject to the condition set forth below, permission is hereby granted to any
7+
* person obtaining a copy of this software, associated documentation and/or data
8+
* (collectively the "Software"), free of charge and under any and all copyright
9+
* rights in the Software, and any and all patent rights owned or freely
10+
* licensable by each licensor hereunder covering either (i) the unmodified
11+
* Software as contributed to or provided by such licensor, or (ii) the Larger
12+
* Works (as defined below), to deal in both
13+
*
14+
* (a) the Software, and
15+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
16+
* one is included with the Software (each a "Larger Work" to which the
17+
* Software is contributed by such licensors),
18+
*
19+
* without restriction, including without limitation the rights to copy, create
20+
* derivative works of, display, perform, and distribute the Software and make,
21+
* use, sell, offer for sale, import, export, have made, and have sold the
22+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
* either these or other terms.
24+
*
25+
* This license is subject to the following condition:
26+
*
27+
* The above copyright notice and either this complete permission notice or at a
28+
* minimum a reference to the UPL must be included in all copies or substantial
29+
* portions of the Software.
30+
*
31+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37+
* SOFTWARE.
38+
*/
39+
package com.oracle.graal.python.builtins.objects.cext;
40+
41+
import com.oracle.graal.python.builtins.objects.PNone;
42+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.ToSulongNode;
43+
import com.oracle.truffle.api.CompilerDirectives;
44+
import com.oracle.truffle.api.interop.MessageResolution;
45+
import com.oracle.truffle.api.interop.Resolve;
46+
import com.oracle.truffle.api.interop.UnknownIdentifierException;
47+
import com.oracle.truffle.api.nodes.Node;
48+
49+
@MessageResolution(receiverType = PyBufferProcsWrapper.class)
50+
public class PyBufferProcsWrapperMR {
51+
52+
@Resolve(message = "READ")
53+
abstract static class ReadNode extends Node {
54+
@Child private ToSulongNode toSulongNode;
55+
56+
public Object access(PyBufferProcsWrapper object, String key) {
57+
// translate key to attribute name
58+
PythonClassNativeWrapper nativeWrapper = object.getDelegate().getNativeWrapper();
59+
// TODO handle case if nativeWrapper does not exist yet
60+
Object result;
61+
switch (key) {
62+
case "bf_getbuffer":
63+
result = nativeWrapper.getGetBufferProc();
64+
break;
65+
case "bf_releasebuffer":
66+
// TODO
67+
result = nativeWrapper.getReleaseBufferProc();
68+
break;
69+
default:
70+
// TODO extend list
71+
throw UnknownIdentifierException.raise(key);
72+
}
73+
// use NO_VALUE for NULL
74+
return getToSulongNode().execute(result == null ? PNone.NO_VALUE : result);
75+
}
76+
77+
private ToSulongNode getToSulongNode() {
78+
if (toSulongNode == null) {
79+
CompilerDirectives.transferToInterpreterAndInvalidate();
80+
toSulongNode = insert(ToSulongNode.create());
81+
}
82+
return toSulongNode;
83+
}
84+
}
85+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
/**
4545
* Wraps a sequence object (like a list) such that it behaves like a bare C array.
4646
*/
47-
public class PySequenceArrayWrapper implements TruffleObject {
47+
public class PySequenceArrayWrapper extends PythonNativeWrapper {
4848

4949
private final Object delegate;
5050

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,21 @@
4848
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
4949
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
5050
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltinsFactory;
51+
import com.oracle.graal.python.builtins.objects.type.PythonClass;
5152
import com.oracle.graal.python.nodes.SpecialMethodNames;
5253
import com.oracle.graal.python.nodes.truffle.PythonTypes;
5354
import com.oracle.truffle.api.CompilerDirectives;
55+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
5456
import com.oracle.truffle.api.dsl.Cached;
5557
import com.oracle.truffle.api.dsl.ImportStatic;
5658
import com.oracle.truffle.api.dsl.Specialization;
5759
import com.oracle.truffle.api.dsl.TypeSystemReference;
60+
import com.oracle.truffle.api.interop.ForeignAccess;
61+
import com.oracle.truffle.api.interop.Message;
5862
import com.oracle.truffle.api.interop.MessageResolution;
5963
import com.oracle.truffle.api.interop.Resolve;
64+
import com.oracle.truffle.api.interop.TruffleObject;
65+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
6066
import com.oracle.truffle.api.nodes.Node;
6167

6268
@MessageResolution(receiverType = PySequenceArrayWrapper.class)
@@ -186,4 +192,82 @@ public static WriteArrayItemNode create() {
186192
return WriteArrayItemNodeGen.create();
187193
}
188194
}
195+
196+
@Resolve(message = "TO_NATIVE")
197+
abstract static class ToNativeNode extends Node {
198+
@Child private ToNativeArrayNode toPyObjectNode = ToNativeArrayNode.create();
199+
200+
Object access(PySequenceArrayWrapper obj) {
201+
if (!obj.isNative()) {
202+
// TODO
203+
Object ptr = toPyObjectNode.execute(obj);
204+
obj.setNativePointer(ptr);
205+
}
206+
return obj;
207+
}
208+
}
209+
210+
static class ToNativeArrayNode extends TransformToNativeNode {
211+
@CompilationFinal private TruffleObject PyObjectHandle_FromJavaObject;
212+
@Child private PCallNativeNode callNativeBinary;
213+
214+
public Object execute(PySequenceArrayWrapper object) {
215+
// TODO correct element size
216+
return ensureIsPointer(callBinaryIntoCapi(getNativeHandleForArray(), object, 8L));
217+
}
218+
219+
private TruffleObject getNativeHandleForArray() {
220+
if (PyObjectHandle_FromJavaObject == null) {
221+
CompilerDirectives.transferToInterpreterAndInvalidate();
222+
PyObjectHandle_FromJavaObject = (TruffleObject) getContext().getEnv().importSymbol(NativeCAPISymbols.FUN_NATIVE_HANDLE_FOR_ARRAY);
223+
}
224+
return PyObjectHandle_FromJavaObject;
225+
}
226+
227+
protected boolean isNonNative(PythonClass klass) {
228+
return !(klass instanceof PythonNativeClass);
229+
}
230+
231+
private Object callBinaryIntoCapi(TruffleObject fun, Object arg0, Object arg1) {
232+
if (callNativeBinary == null) {
233+
CompilerDirectives.transferToInterpreterAndInvalidate();
234+
callNativeBinary = insert(PCallNativeNode.create(1));
235+
}
236+
return callNativeBinary.execute(fun, new Object[]{arg0, arg1});
237+
}
238+
239+
public static ToNativeArrayNode create() {
240+
return new ToNativeArrayNode();
241+
}
242+
}
243+
244+
@Resolve(message = "IS_POINTER")
245+
abstract static class IsPointerNode extends Node {
246+
Object access(PySequenceArrayWrapper obj) {
247+
return obj.isNative();
248+
}
249+
}
250+
251+
@Resolve(message = "AS_POINTER")
252+
abstract static class AsPointerNode extends Node {
253+
@Child private Node asPointerNode;
254+
255+
long access(PySequenceArrayWrapper obj) {
256+
// the native pointer object must either be a TruffleObject or a primitive
257+
Object nativePointer = obj.getNativePointer();
258+
if (nativePointer instanceof TruffleObject) {
259+
if (asPointerNode == null) {
260+
CompilerDirectives.transferToInterpreterAndInvalidate();
261+
asPointerNode = insert(Message.AS_POINTER.createNode());
262+
}
263+
try {
264+
return ForeignAccess.sendAsPointer(asPointerNode, (TruffleObject) nativePointer);
265+
} catch (UnsupportedMessageException e) {
266+
throw e.raise();
267+
}
268+
}
269+
return (long) nativePointer;
270+
}
271+
}
272+
189273
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
*/
4848
public class PythonClassNativeWrapper extends PythonObjectNativeWrapper {
4949
private final CStringWrapper nameWrapper;
50+
private Object getBufferProc;
51+
private Object releaseBufferProc;
5052

5153
public PythonClassNativeWrapper(PythonClass object) {
5254
super(object);
@@ -57,6 +59,22 @@ public CStringWrapper getNameWrapper() {
5759
return nameWrapper;
5860
}
5961

62+
public Object getGetBufferProc() {
63+
return getBufferProc;
64+
}
65+
66+
public void setGetBufferProc(Object getBufferProc) {
67+
this.getBufferProc = getBufferProc;
68+
}
69+
70+
public Object getReleaseBufferProc() {
71+
return releaseBufferProc;
72+
}
73+
74+
public void setReleaseBufferProc(Object releaseBufferProc) {
75+
this.releaseBufferProc = releaseBufferProc;
76+
}
77+
6078
public static PythonClassNativeWrapper wrap(PythonClass obj) {
6179
// important: native wrappers are cached
6280
PythonClassNativeWrapper nativeWrapper = obj.getNativeWrapper();

0 commit comments

Comments
 (0)