Skip to content

Commit ec8dad4

Browse files
committed
Do not reference run-time objects in C ext root nodes
1 parent 3b14977 commit ec8dad4

File tree

10 files changed

+258
-147
lines changed

10 files changed

+258
-147
lines changed

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

Lines changed: 110 additions & 74 deletions
Large diffs are not rendered by default.

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

Lines changed: 83 additions & 59 deletions
Large diffs are not rendered by default.

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
package com.oracle.graal.python.builtins.objects.cext.common;
4242

4343
import com.oracle.graal.python.runtime.PythonContext;
44+
import com.oracle.truffle.api.Assumption;
45+
import com.oracle.truffle.api.CompilerDirectives;
46+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
47+
import com.oracle.truffle.api.Truffle;
4448

4549
public abstract class CExtContext {
4650

@@ -62,6 +66,12 @@ public abstract class CExtContext {
6266

6367
/** A factory for creating context-specific conversion nodes. */
6468
private final ConversionNodeSupplier supplier;
69+
70+
/**
71+
* A shared assumption that indicates that the delegate of a built-in function that decorates a
72+
* native member method or similar did not change.
73+
*/
74+
@CompilationFinal private Assumption callableStableAssumption;
6575

6676
public CExtContext(PythonContext context, Object llvmLibrary, ConversionNodeSupplier supplier) {
6777
this.context = context;
@@ -106,4 +116,11 @@ public static boolean isMethFastcallWithKeywords(int flags) {
106116
return (flags & METH_FASTCALL) != 0 && (flags & METH_KEYWORDS) != 0;
107117
}
108118

119+
public Assumption getCallableStableAssumption() {
120+
if (callableStableAssumption == null) {
121+
CompilerDirectives.transferToInterpreterAndInvalidate();
122+
callableStableAssumption = Truffle.getRuntime().createAssumption("method descriptor delegate stable assumption");
123+
}
124+
return callableStableAssumption;
125+
}
109126
}

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858

5959
import com.oracle.graal.python.PythonLanguage;
6060
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
61+
import com.oracle.graal.python.builtins.modules.ExternalFunctionNodes;
6162
import com.oracle.graal.python.builtins.modules.ExternalFunctionNodes.MethKeywordsRoot;
6263
import com.oracle.graal.python.builtins.modules.ExternalFunctionNodes.MethNoargsRoot;
6364
import com.oracle.graal.python.builtins.modules.ExternalFunctionNodes.MethORoot;
@@ -67,6 +68,7 @@
6768
import com.oracle.graal.python.builtins.modules.PythonCextBuiltins.MethONode;
6869
import com.oracle.graal.python.builtins.modules.PythonCextBuiltins.MethVarargsNode;
6970
import com.oracle.graal.python.builtins.objects.PNone;
71+
import com.oracle.graal.python.builtins.objects.cell.PCell;
7072
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
7173
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
7274
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ConvertPIntToPrimitiveNode;
@@ -444,8 +446,9 @@ static PBuiltinFunction doIt(GraalHPyContext context, Object methodDef,
444446

445447
// CPy-style methods
446448
// TODO(fa) support static and class methods
447-
PRootNode rootNode = createWrapperRootNode(language, flags, methodName, mlMethObj);
448-
PBuiltinFunction function = createWrapperFunction(factory, methodName, rootNode);
449+
PRootNode rootNode = createWrapperRootNode(language, flags, methodName);
450+
PCell[] closure = ExternalFunctionNodes.createPythonClosure(mlMethObj, factory, context.getCallableStableAssumption());
451+
PBuiltinFunction function = factory.createBuiltinFunction(methodName, null, 0, closure, PythonUtils.getOrCreateCallTarget(rootNode));
449452

450453
// write doc string; we need to directly write to the storage otherwise it is disallowed
451454
// writing to builtin types.
@@ -467,20 +470,15 @@ private static boolean checkLayout(Object methodDef) {
467470
}
468471

469472
@TruffleBoundary
470-
private static PBuiltinFunction createWrapperFunction(PythonObjectFactory factory, String name, PRootNode rootNode) {
471-
return factory.createBuiltinFunction(name, null, 0, PythonUtils.getOrCreateCallTarget(rootNode));
472-
}
473-
474-
@TruffleBoundary
475-
private static PRootNode createWrapperRootNode(PythonLanguage language, int flags, String name, Object callable) {
473+
private static PRootNode createWrapperRootNode(PythonLanguage language, int flags, String name) {
476474
if (CExtContext.isMethNoArgs(flags)) {
477-
return new MethNoargsRoot(language, name, callable, MethNoargsNode.METH_NOARGS_CONVERTER);
475+
return new MethNoargsRoot(language, name, MethNoargsNode.METH_NOARGS_CONVERTER);
478476
} else if (CExtContext.isMethO(flags)) {
479-
return new MethORoot(language, name, callable, MethONode.METH_O_CONVERTER);
477+
return new MethORoot(language, name, MethONode.METH_O_CONVERTER);
480478
} else if (CExtContext.isMethKeywords(flags)) {
481-
return new MethKeywordsRoot(language, name, callable, MethKeywordsNode.METH_KEYWORDS_CONVERTER);
479+
return new MethKeywordsRoot(language, name, MethKeywordsNode.METH_KEYWORDS_CONVERTER);
482480
} else if (CExtContext.isMethVarargs(flags)) {
483-
return new MethVarargsRoot(language, name, callable, MethVarargsNode.METH_VARARGS_CONVERTER);
481+
return new MethVarargsRoot(language, name, MethVarargsNode.METH_VARARGS_CONVERTER);
484482
}
485483
throw new IllegalStateException("illegal method flags");
486484
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.oracle.graal.python.builtins.BoundBuiltinCallable;
3434
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3535
import com.oracle.graal.python.builtins.objects.PNone;
36+
import com.oracle.graal.python.builtins.objects.cell.PCell;
3637
import com.oracle.graal.python.builtins.objects.function.PArguments.ThreadState;
3738
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
3839
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
@@ -72,10 +73,24 @@ public final class PBuiltinFunction extends PythonBuiltinObject implements Bound
7273
private final Object enclosingType;
7374
private final RootCallTarget callTarget;
7475
private final Signature signature;
76+
77+
/**
78+
* Built-in function can usually not have a closure but we also use this class to represent
79+
* method descriptors and for those, we need to store the actual receiver (e.g. a native
80+
* function pointer) and pass the closure to the appropriate root node. So, if the built-in
81+
* function is implemented in Java (which is the main use case), the closure will be
82+
* {@code null}.
83+
*/
84+
@CompilationFinal(dimensions = 1) private final PCell[] closure;
85+
7586
@CompilationFinal(dimensions = 1) private final PNone[] defaults;
7687
@CompilationFinal(dimensions = 1) private final PKeyword[] kwDefaults;
7788

7889
public PBuiltinFunction(PythonLanguage lang, String name, Object enclosingType, int numDefaults, RootCallTarget callTarget) {
90+
this(lang, name, enclosingType, numDefaults, null, callTarget);
91+
}
92+
93+
public PBuiltinFunction(PythonLanguage lang, String name, Object enclosingType, int numDefaults, PCell[] closure, RootCallTarget callTarget) {
7994
super(PythonBuiltinClassType.PBuiltinFunction, PythonBuiltinClassType.PBuiltinFunction.getInstanceShape(lang));
8095
this.name = name;
8196
if (enclosingType != null) {
@@ -93,6 +108,7 @@ public PBuiltinFunction(PythonLanguage lang, String name, Object enclosingType,
93108
for (int i = 0; i < keywordNames.length; i++) {
94109
kwDefaults[i] = new PKeyword(keywordNames[i], PNone.NO_VALUE);
95110
}
111+
this.closure = closure;
96112
}
97113

98114
public RootNode getFunctionRootNode() {
@@ -145,6 +161,10 @@ public Object getEnclosingType() {
145161
return enclosingType;
146162
}
147163

164+
public PCell[] getClosure() {
165+
return closure;
166+
}
167+
148168
@Override
149169
public String toString() {
150170
CompilerAsserts.neverPartOfCompilation();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/CallDispatchNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ protected Object callBuiltinFunctionCached(VirtualFrame frame, @SuppressWarnings
129129
protected Object callBuiltinFunctionCachedCt(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinFunction callee, Object[] arguments,
130130
@SuppressWarnings("unused") @Cached("callee.getCallTarget()") RootCallTarget ct,
131131
@Cached("createCtInvokeNode(callee)") CallTargetInvokeNode invoke) {
132-
return invoke.execute(frame, null, null, null, arguments);
132+
return invoke.execute(frame, null, null, callee.getClosure(), arguments);
133133
}
134134

135135
@Specialization(replaces = {"callFunctionCached", "callFunctionCachedCode", "callFunctionCachedCt"})

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/FunctionInvokeNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public static FunctionInvokeNode create(PFunction callee) {
128128
public static FunctionInvokeNode create(PBuiltinFunction callee) {
129129
RootCallTarget callTarget = getCallTarget(callee);
130130
boolean builtin = isBuiltin(callee);
131-
return FunctionInvokeNodeGen.create(null, callTarget, null, null, builtin, false);
131+
return FunctionInvokeNodeGen.create(null, callTarget, null, callee.getClosure(), builtin, false);
132132
}
133133

134134
/**

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/GenericInvokeNode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ Object invokeBuiltin(Frame frame, PBuiltinFunction callee, Object[] arguments,
158158
@Shared("isNullFrameProfile") @Cached("createBinaryProfile()") ConditionProfile isNullFrameProfile,
159159
@Shared("isClassBodyProfile") @Cached("createBinaryProfile()") ConditionProfile isClassBodyProfile,
160160
@Shared("isGeneratorFunctionProfile") @Cached("createBinaryProfile()") ConditionProfile isGeneratorFunctionProfile) {
161+
PArguments.setClosure(arguments, callee.getClosure());
161162
RootCallTarget callTarget = getCallTarget(callee);
162163
return doCall(frame, null, callTarget, arguments, context, callNode, callContext, isNullFrameProfile, isClassBodyProfile, isGeneratorFunctionProfile);
163164
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallSpecialMethodNode.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ private <T extends PythonBuiltinBaseNode> T getBuiltin(VirtualFrame frame, PBuil
9595
if (builtinNodeFactory == null) {
9696
return null; // see for example MethodDescriptorRoot and subclasses
9797
}
98+
// built-in function that have a closure cannot be AST-inlined otherwise we would
99+
// potentially overwrite the caller's closure
100+
if (func.getClosure() != null) {
101+
return null;
102+
}
98103
assert builtinNodeFactory.getNodeClass().getAnnotationsByType(Builtin.class).length > 0 : "PBuiltinFunction " + func + " is expected to have a Builtin annotated node.";
99104
if (builtinNodeFactory.getNodeClass().getAnnotationsByType(Builtin.class)[0].needsFrame() && frame == null) {
100105
return null;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,16 @@ public PBuiltinFunction createBuiltinFunction(String name, Object type, int numD
463463
return trace(new PBuiltinFunction(getLanguage(), name, type, numDefaults, callTarget));
464464
}
465465

466+
/**
467+
* Creates a built-in function that has a closure. This is mostly useful if the built-in
468+
* function is not implemented in Java and needs to delegate to a different receiver. Then the
469+
* closure can store this receiver. However, as soon as you have a non-null closure,
470+
* AST-inlining won't be possible.
471+
*/
472+
public PBuiltinFunction createBuiltinFunction(String name, Object type, int numDefaults, PCell[] closure, RootCallTarget callTarget) {
473+
return trace(new PBuiltinFunction(getLanguage(), name, type, numDefaults, closure, callTarget));
474+
}
475+
466476
public GetSetDescriptor createGetSetDescriptor(Object get, Object set, String name, Object type) {
467477
return trace(new GetSetDescriptor(getLanguage(), get, set, name, type));
468478
}

0 commit comments

Comments
 (0)