Skip to content

Commit 59b01c5

Browse files
committed
Create native wrappers for methods with correct argument expansion.
1 parent 534f48a commit 59b01c5

File tree

6 files changed

+297
-20
lines changed

6 files changed

+297
-20
lines changed

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

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@
4343
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
4444
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.AsCharPointerNodeGen;
4545
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.AsPythonObjectNodeGen;
46+
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.ToJavaNodeGen;
4647
import com.oracle.graal.python.builtins.objects.cext.CExtNodesFactory.ToSulongNodeGen;
4748
import com.oracle.graal.python.builtins.objects.type.PythonClass;
4849
import com.oracle.graal.python.nodes.PBaseNode;
4950
import com.oracle.graal.python.nodes.PGuards;
5051
import com.oracle.graal.python.nodes.object.GetClassNode;
52+
import com.oracle.graal.python.runtime.exception.PythonErrorType;
5153
import com.oracle.truffle.api.CompilerDirectives;
5254
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
5355
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -61,7 +63,6 @@
6163
import com.oracle.truffle.api.interop.UnsupportedMessageException;
6264
import com.oracle.truffle.api.interop.UnsupportedTypeException;
6365
import com.oracle.truffle.api.nodes.Node;
64-
import com.oracle.truffle.api.profiles.ConditionProfile;
6566

6667
public abstract class CExtNodes {
6768

@@ -152,28 +153,55 @@ public static ToSulongNode create() {
152153
public abstract static class AsPythonObjectNode extends PBaseNode {
153154
public abstract Object execute(Object value);
154155

155-
@Child GetClassNode getClassNode = GetClassNode.create();
156-
ConditionProfile branchCond = ConditionProfile.createBinaryProfile();
156+
@Child GetClassNode getClassNode;
157157

158158
@Specialization
159-
Object run(PythonObjectNativeWrapper object) {
159+
PythonAbstractObject doNativeWrapper(PythonObjectNativeWrapper object) {
160160
return object.getPythonObject();
161161
}
162162

163+
@Specialization(guards = "isForeignObject(object)")
164+
PythonAbstractObject doNativeObject(TruffleObject object) {
165+
return factory().createNativeObjectWrapper(object);
166+
}
167+
168+
@Specialization
169+
PythonAbstractObject doPythonObject(PythonAbstractObject object) {
170+
return object;
171+
}
172+
163173
@Specialization
164-
Object run(PythonAbstractObject object) {
174+
String doString(String object) {
165175
return object;
166176
}
167177

178+
@Specialization
179+
int doLong(int i) {
180+
return i;
181+
}
182+
183+
@Specialization
184+
long doLong(long l) {
185+
return l;
186+
}
187+
188+
@Specialization
189+
double doDouble(double d) {
190+
return d;
191+
}
192+
168193
@Fallback
169194
Object run(Object obj) {
170-
if (branchCond.profile(getClassNode.execute(obj) == getCore().getForeignClass())) {
171-
// TODO: this should very likely only be done for objects that come from Sulong...
172-
// TODO: prevent calling this from any other place
173-
return factory().createNativeObjectWrapper(obj);
174-
} else {
175-
return obj;
195+
throw raise(PythonErrorType.SystemError, "invalid object from native: %s", obj);
196+
}
197+
198+
protected boolean isForeignObject(TruffleObject obj) {
199+
// TODO we could probably also just use 'PGuards.isForeignObject'
200+
if (getClassNode == null) {
201+
CompilerDirectives.transferToInterpreterAndInvalidate();
202+
getClassNode = insert(GetClassNode.create());
176203
}
204+
return getClassNode.execute(obj) == getCore().getForeignClass();
177205
}
178206

179207
@TruffleBoundary
@@ -192,15 +220,36 @@ public static AsPythonObjectNode create() {
192220
}
193221

194222
/**
195-
* Does the same conversion as the native function {@code to_java}.
223+
* Does the same conversion as the native function {@code to_java}. The node tries to avoid
224+
* calling the native function for resolving native handles.
196225
*/
197-
static class ToJavaNode extends PBaseNode {
198-
@Child private PCallNativeNode callNativeNode = PCallNativeNode.create(1);
226+
public abstract static class ToJavaNode extends PBaseNode {
227+
@Child private PCallNativeNode callNativeNode;
199228
@Child private AsPythonObjectNode toJavaNode = AsPythonObjectNode.create();
200229

201230
@CompilationFinal TruffleObject nativeToJavaFunction;
202231

203-
Object execute(Object value) {
232+
public abstract Object execute(Object value);
233+
234+
@Specialization
235+
PythonAbstractObject doPythonObject(PythonAbstractObject value) {
236+
return value;
237+
}
238+
239+
@Specialization
240+
Object doWrapper(PythonObjectNativeWrapper value) {
241+
return toJavaNode.execute(value);
242+
}
243+
244+
@Fallback
245+
Object doForeign(Object value) {
246+
if (callNativeNode == null) {
247+
CompilerDirectives.transferToInterpreterAndInvalidate();
248+
callNativeNode = insert(PCallNativeNode.create(1));
249+
}
250+
if (callNativeNode == null) {
251+
CompilerDirectives.transferToInterpreterAndInvalidate();
252+
}
204253
if (nativeToJavaFunction == null) {
205254
CompilerDirectives.transferToInterpreterAndInvalidate();
206255
nativeToJavaFunction = (TruffleObject) getContext().getEnv().importSymbol(NativeCAPISymbols.FUN_NATIVE_TO_JAVA);
@@ -209,9 +258,8 @@ Object execute(Object value) {
209258
}
210259

211260
public static ToJavaNode create() {
212-
return new ToJavaNode();
261+
return ToJavaNodeGen.create();
213262
}
214-
215263
}
216264

217265
public abstract static class AsCharPointer extends PBaseNode {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.oracle.graal.python.builtins.objects.cext;
2+
3+
import com.oracle.truffle.api.interop.ForeignAccess;
4+
import com.oracle.truffle.api.interop.TruffleObject;
5+
6+
/**
7+
* Wrappers for methods used by native code.
8+
*/
9+
public abstract class ManagedMethodWrappers {
10+
11+
public abstract static class MethodWrapper implements TruffleObject {
12+
private final Object method;
13+
14+
public MethodWrapper(Object method) {
15+
this.method = method;
16+
}
17+
18+
public Object getMethod() {
19+
return method;
20+
}
21+
22+
static boolean isInstance(TruffleObject o) {
23+
return o instanceof MethodWrapper;
24+
}
25+
26+
public ForeignAccess getForeignAccess() {
27+
return ManagedMethodWrappersMRForeign.ACCESS;
28+
}
29+
}
30+
31+
static class MethKeywords extends MethodWrapper {
32+
33+
public MethKeywords(Object method) {
34+
super(method);
35+
}
36+
}
37+
38+
static class MethVarargs extends MethodWrapper {
39+
40+
public MethVarargs(Object method) {
41+
super(method);
42+
}
43+
}
44+
45+
/**
46+
* Creates a wrapper for signature {@code meth(*args, **kwargs)}.
47+
*/
48+
public static MethodWrapper createKeywords(Object method) {
49+
return new MethKeywords(method);
50+
}
51+
52+
/**
53+
* Creates a wrapper for signature {@code meth(*args)}.
54+
*/
55+
public static MethodWrapper createVarargs(Object method) {
56+
return new MethVarargs(method);
57+
}
58+
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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.cext.CExtNodes.ToJavaNode;
42+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.ToSulongNode;
43+
import com.oracle.graal.python.builtins.objects.cext.ManagedMethodWrappers.MethKeywords;
44+
import com.oracle.graal.python.builtins.objects.cext.ManagedMethodWrappers.MethVarargs;
45+
import com.oracle.graal.python.builtins.objects.cext.ManagedMethodWrappers.MethodWrapper;
46+
import com.oracle.graal.python.builtins.objects.function.PKeyword;
47+
import com.oracle.graal.python.nodes.argument.ArityCheckNode;
48+
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
49+
import com.oracle.graal.python.nodes.argument.keywords.ExecuteKeywordStarargsNode;
50+
import com.oracle.graal.python.nodes.argument.positional.ExecutePositionalStarargsNode;
51+
import com.oracle.graal.python.nodes.argument.positional.PositionalArgumentsNode;
52+
import com.oracle.graal.python.nodes.call.CallDispatchNode;
53+
import com.oracle.graal.python.runtime.interop.PythonMessageResolution;
54+
import com.oracle.truffle.api.CompilerDirectives;
55+
import com.oracle.truffle.api.interop.ArityException;
56+
import com.oracle.truffle.api.interop.MessageResolution;
57+
import com.oracle.truffle.api.interop.Resolve;
58+
import com.oracle.truffle.api.nodes.ExplodeLoop;
59+
import com.oracle.truffle.api.nodes.Node;
60+
import com.oracle.truffle.api.profiles.ValueProfile;
61+
62+
@MessageResolution(receiverType = MethodWrapper.class)
63+
public class ManagedMethodWrappersMR {
64+
65+
@Resolve(message = "EXECUTE")
66+
abstract static class ExecuteNode extends Node {
67+
@Child PythonMessageResolution.ExecuteNode executeNode;
68+
@Child private ToJavaNode toJavaNode;
69+
@Child private ToSulongNode toSulongNode;
70+
71+
@Child private ExecutePositionalStarargsNode posStarargsNode = ExecutePositionalStarargsNode.create();
72+
@Child private PositionalArgumentsNode posArgsNode = PositionalArgumentsNode.create();
73+
@Child private ExecuteKeywordStarargsNode expandKwargsNode = ExecuteKeywordStarargsNode.create();
74+
@Child private CallDispatchNode dispatch;
75+
@Child private CreateArgumentsNode createArgs = CreateArgumentsNode.create();
76+
@Child private ArityCheckNode arityCheckNode = ArityCheckNode.create();
77+
final ValueProfile classProfile = ValueProfile.createClassProfile();
78+
79+
private CallDispatchNode getDispatchNode() {
80+
if (dispatch == null) {
81+
CompilerDirectives.transferToInterpreterAndInvalidate();
82+
dispatch = insert(CallDispatchNode.create("<foreign-invoke>"));
83+
}
84+
return dispatch;
85+
}
86+
87+
@ExplodeLoop
88+
public Object access(MethKeywords object, Object[] arguments) {
89+
if (executeNode == null) {
90+
CompilerDirectives.transferToInterpreterAndInvalidate();
91+
executeNode = insert(new PythonMessageResolution.ExecuteNode());
92+
}
93+
if (arguments.length != 2) {
94+
throw ArityException.raise(2, arguments.length);
95+
}
96+
97+
// convert args
98+
Object[] converted = new Object[arguments.length];
99+
for (int i = 0; i < arguments.length; i++) {
100+
converted[i] = getToJavaNode().execute(arguments[i]);
101+
}
102+
103+
Object[] userArgs = posArgsNode.executeWithArguments(null, posStarargsNode.executeWith(converted[0]));
104+
Object[] pArgs = createArgs.execute(userArgs);
105+
PKeyword[] kwargs = expandKwargsNode.executeWith(converted[1]);
106+
return getToSulongNode().execute(getDispatchNode().executeCall(object.getMethod(), pArgs, kwargs));
107+
}
108+
109+
@ExplodeLoop
110+
public Object access(MethVarargs object, Object[] arguments) {
111+
if (executeNode == null) {
112+
CompilerDirectives.transferToInterpreterAndInvalidate();
113+
executeNode = insert(new PythonMessageResolution.ExecuteNode());
114+
}
115+
if (arguments.length != 1) {
116+
throw ArityException.raise(1, arguments.length);
117+
}
118+
119+
// convert args
120+
return getToSulongNode().execute(executeNode.execute(object.getMethod(), new Object[]{getToJavaNode().execute(arguments[0])}));
121+
}
122+
123+
private ToJavaNode getToJavaNode() {
124+
if (toJavaNode == null) {
125+
CompilerDirectives.transferToInterpreterAndInvalidate();
126+
toJavaNode = insert(ToJavaNode.create());
127+
}
128+
return toJavaNode;
129+
}
130+
131+
private ToSulongNode getToSulongNode() {
132+
if (toSulongNode == null) {
133+
CompilerDirectives.transferToInterpreterAndInvalidate();
134+
toSulongNode = insert(ToSulongNode.create());
135+
}
136+
return toSulongNode;
137+
}
138+
}
139+
}

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
4747
import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
4848
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
49+
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.ToJavaNode;
4950
import com.oracle.graal.python.builtins.objects.cext.CExtNodes.ToSulongNode;
5051
import com.oracle.graal.python.builtins.objects.cext.PythonObjectNativeWrapperMRFactory.ReadNativeMemberNodeGen;
5152
import com.oracle.graal.python.builtins.objects.cext.PythonObjectNativeWrapperMRFactory.ToPyObjectNodeGen;
@@ -220,7 +221,7 @@ Object doTpAsBuffer(PythonClass object, @SuppressWarnings("unused") String key)
220221
@Specialization(guards = "eq(TP_NEW, key)")
221222
Object doTpNew(PythonClass object, @SuppressWarnings("unused") String key,
222223
@Cached("create()") GetAttributeNode getAttrNode) {
223-
return getToSulongNode().execute(getAttrNode.execute(object, SpecialAttributeNames.__NEW__));
224+
return ManagedMethodWrappers.createKeywords(getAttrNode.execute(object, SpecialAttributeNames.__NEW__));
224225
}
225226

226227
@Specialization(guards = "eq(TP_HASH, key)")
@@ -419,14 +420,28 @@ public static WriteNativeMemberNode create() {
419420
@Resolve(message = "EXECUTE")
420421
abstract static class ExecuteNode extends Node {
421422
@Child PythonMessageResolution.ExecuteNode executeNode;
423+
@Child private ToJavaNode toJavaNode;
422424
@Child private ToSulongNode toSulongNode;
423425

424426
public Object access(PythonObjectNativeWrapper object, Object[] arguments) {
425427
if (executeNode == null) {
426428
CompilerDirectives.transferToInterpreterAndInvalidate();
427429
executeNode = insert(new PythonMessageResolution.ExecuteNode());
428430
}
429-
return getToSulongNode().execute(executeNode.execute(object.getPythonObject(), arguments));
431+
// convert args
432+
Object[] converted = new Object[arguments.length];
433+
for (int i = 0; i < arguments.length; i++) {
434+
converted[i] = getToJavaNode().execute(arguments[i]);
435+
}
436+
return getToSulongNode().execute(executeNode.execute(object.getPythonObject(), converted));
437+
}
438+
439+
private ToJavaNode getToJavaNode() {
440+
if (toJavaNode == null) {
441+
CompilerDirectives.transferToInterpreterAndInvalidate();
442+
toJavaNode = insert(ToJavaNode.create());
443+
}
444+
return toJavaNode;
430445
}
431446

432447
private ToSulongNode getToSulongNode() {

0 commit comments

Comments
 (0)