Skip to content

Commit 9c57e90

Browse files
committed
Support callback on invalid handle access in debug mode
1 parent c970d6e commit 9c57e90

File tree

6 files changed

+98
-20
lines changed

6 files changed

+98
-20
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import com.oracle.graal.python.builtins.objects.cext.hpy.PDebugHandle;
6666
import com.oracle.graal.python.builtins.objects.function.PKeyword;
6767
import com.oracle.graal.python.builtins.objects.list.PList;
68+
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
6869
import com.oracle.graal.python.nodes.ErrorMessages;
6970
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
7071
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
@@ -224,4 +225,18 @@ PDebugHandle doGeneric(Object cls, Object[] args, PKeyword[] kwargs) {
224225
}
225226
}
226227

228+
@Builtin(name = "set_on_invalid_handle", //
229+
doc = "Set the function to call when we detect the usage of an invalid handle")
230+
@GenerateNodeFactory
231+
abstract static class HPyDebugSetOnInvalidHandleNode extends PythonUnaryBuiltinNode {
232+
@Specialization
233+
PNone doInt(VirtualFrame frame, Object callback) {
234+
GraalHPyDebugContext hpyDebugContext = getHPyDebugContext(frame, getLanguage(), this);
235+
if (!PythonObjectLibrary.getUncached().isCallable(callback)) {
236+
throw raise(TypeError, "Expected a callable object");
237+
}
238+
hpyDebugContext.setOnInvalidHandleCallback(callback);
239+
return PNone.NONE;
240+
}
241+
}
227242
}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,14 @@
147147
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContextFunctions.GraalHPyUnicodeFromString;
148148
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContextFunctions.GraalHPyUnicodeFromWchar;
149149
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyContextFunctions.ReturnType;
150-
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodes.HPyAttachFunctionTypeNode;
151150
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodes.PCallHPyFunction;
152151
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyAsPythonObjectNodeGen;
153152
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyGetNativeSpacePointerNodeGen;
154153
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyRaiseNodeGen;
155154
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyTransformExceptionToNativeNodeGen;
156155
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.PCallHPyFunctionNodeGen;
157156
import com.oracle.graal.python.builtins.objects.cext.hpy.HPyExternalFunctionNodes.HPyCheckFunctionResultNode;
157+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodes.HPyAttachFunctionTypeNode;
158158
import com.oracle.graal.python.builtins.objects.common.EmptyStorage;
159159
import com.oracle.graal.python.builtins.objects.common.HashMapStorage;
160160
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
@@ -2201,19 +2201,24 @@ private void allocateNativeSpacePointersMirror() {
22012201
}
22022202
}
22032203

2204-
public final synchronized GraalHPyHandle getObjectForHPyHandle(int handle) {
2204+
public synchronized GraalHPyHandle getObjectForHPyHandle(int handle) {
22052205
assert !GraalHPyBoxing.isBoxedInt(handle) && !GraalHPyBoxing.isBoxedDouble(handle) : "trying to lookup boxed primitive";
22062206
return hpyHandleTable[handle];
22072207
}
22082208

2209-
synchronized void releaseHPyHandleForObject(int handle) {
2209+
synchronized boolean releaseHPyHandleForObject(int handle) {
22102210
assert handle != 0 : "NULL handle cannot be released";
22112211
assert hpyHandleTable[handle] != null : PythonUtils.format("releasing handle that has already been released: %d", handle);
22122212
if (LOGGER.isLoggable(Level.FINER)) {
22132213
LOGGER.finer(() -> "releasing HPy handle " + handle);
22142214
}
22152215
hpyHandleTable[handle] = null;
22162216
freeStack.push(handle);
2217+
return true;
2218+
}
2219+
2220+
void onInvalidHandle(@SuppressWarnings("unused") int id) {
2221+
// nothing to do in the universal context
22172222
}
22182223

22192224
private static final class HandleStack {

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

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import java.util.LinkedList;
4545
import java.util.Queue;
4646

47+
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
48+
import com.oracle.graal.python.nodes.call.CallNode;
4749
import com.oracle.graal.python.util.PythonUtils;
4850
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4951

@@ -55,6 +57,7 @@ public final class GraalHPyDebugContext extends GraalHPyContext {
5557
private long[] debugHandleInfo = new long[]{0};
5658
private int closedHandlesQueueMaxSize = DEFAULT_CLOSED_HANDLES_QUEUE_MAX_SIZE;
5759
private final Queue<GraalHPyHandle> closedHandles = new LinkedList<>();
60+
private Object onInvalidHandleCallback;
5861

5962
public GraalHPyDebugContext(GraalHPyContext context) {
6063
super(context.getContext(), context.getLLVMLibrary());
@@ -73,6 +76,14 @@ public void setClosedHandlesQueueMaxSize(int closedHandlesQueueMaxSize) {
7376
this.closedHandlesQueueMaxSize = closedHandlesQueueMaxSize;
7477
}
7578

79+
public Object getOnInvalidHandleCallback() {
80+
return onInvalidHandleCallback;
81+
}
82+
83+
public void setOnInvalidHandleCallback(Object onInvalidHandleCallback) {
84+
this.onInvalidHandleCallback = onInvalidHandleCallback;
85+
}
86+
7687
/**
7788
* Since the initialization of the context members cannot use {@link #createHandle(Object)}, we
7889
* track the constants of the debug mode separately here. The reason why we can't use
@@ -136,16 +147,31 @@ public GraalHPyHandle createHandle(Object delegate) {
136147
return handle;
137148
}
138149

150+
public synchronized GraalHPyHandle getObjectForHPyHandle(int handle) {
151+
try {
152+
return super.getObjectForHPyHandle(handle);
153+
} catch (ArrayIndexOutOfBoundsException e) {
154+
onInvalidHandle(handle);
155+
return GraalHPyHandle.NULL_HANDLE;
156+
}
157+
}
158+
139159
@Override
140160
@TruffleBoundary
141-
public synchronized void releaseHPyHandleForObject(int handleId) {
142-
GraalHPyHandle handle = super.getObjectForHPyHandle(handleId);
143-
super.releaseHPyHandleForObject(handleId);
144-
debugHandleInfo[handleId] = -1;
145-
if (closedHandles.size() >= closedHandlesQueueMaxSize) {
146-
closedHandles.poll();
161+
public synchronized boolean releaseHPyHandleForObject(int handleId) {
162+
if (handleId < 0) {
163+
onInvalidHandle(handleId);
164+
return false;
165+
} else {
166+
GraalHPyHandle handle = super.getObjectForHPyHandle(handleId);
167+
super.releaseHPyHandleForObject(handleId);
168+
debugHandleInfo[handleId] = -1;
169+
if (closedHandles.size() >= closedHandlesQueueMaxSize) {
170+
closedHandles.poll();
171+
}
172+
closedHandles.add(handle);
173+
return true;
147174
}
148-
closedHandles.add(handle);
149175
}
150176

151177
private static int getGeneration(long bits) {
@@ -155,4 +181,14 @@ private static int getGeneration(long bits) {
155181
private static long toBits(int generation, int age) {
156182
return ((long) generation << Integer.SIZE) | age;
157183
}
184+
185+
@TruffleBoundary
186+
void onInvalidHandle(int id) {
187+
assert GraalHPyHandle.wasAllocated(id);
188+
if (onInvalidHandleCallback != null) {
189+
CallNode.getUncached().execute(onInvalidHandleCallback);
190+
} else {
191+
CExtCommonNodes.fatalError(null, getContext(), null, "Invalid usage of already closed handle", -1);
192+
}
193+
}
158194
}

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
@ExportLibrary(value = NativeTypeLibrary.class, useForAOT = false)
6868
@ExportLibrary(PythonNativeWrapperLibrary.class)
6969
public final class GraalHPyHandle implements TruffleObject {
70-
public static final int UNINITIALIZED = Integer.MIN_VALUE;
70+
private static final int UNINITIALIZED = Integer.MIN_VALUE;
7171

7272
public static final GraalHPyHandle NULL_HANDLE = new GraalHPyHandle();
7373
public static final String I = "_i";
@@ -144,7 +144,7 @@ long asPointer() throws UnsupportedMessageException {
144144
CompilerDirectives.transferToInterpreterAndInvalidate();
145145
throw UnsupportedMessageException.create();
146146
}
147-
if (id != -1) {
147+
if (id != UNINITIALIZED) {
148148
return GraalHPyBoxing.boxHandle(id);
149149
} else if (delegate instanceof Integer) {
150150
return GraalHPyBoxing.boxInt((Integer) delegate);
@@ -249,16 +249,25 @@ boolean isNull() {
249249
}
250250

251251
boolean isAllocated() {
252+
return id != UNINITIALIZED && id != 0;
253+
}
254+
255+
boolean isValid() {
252256
return id > 0;
253257
}
254258

255259
void closeAndInvalidate(GraalHPyContext hpyContext) {
256-
assert id != -1;
257-
hpyContext.releaseHPyHandleForObject(id);
258-
id = -id;
260+
assert id != UNINITIALIZED;
261+
if (hpyContext.releaseHPyHandleForObject(id)) {
262+
id = -id;
263+
}
259264
}
260265

261266
public GraalHPyHandle copy() {
262267
return new GraalHPyHandle(delegate);
263268
}
269+
270+
static boolean wasAllocated(int id) {
271+
return id != UNINITIALIZED;
272+
}
264273
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyMemberAccessNodes.HPyReadMemberNode;
8383
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyMemberAccessNodes.HPyWriteMemberNode;
8484
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyAllHandleCloseNodeGen;
85+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyAttachJNIFunctionTypeNodeGen;
86+
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyAttachNFIFunctionTypeNodeGen;
8587
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyGetSetSetterHandleCloseNodeGen;
8688
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyKeywordsHandleCloseNodeGen;
8789
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyRichcmptFuncArgsCloseNodeGen;
@@ -96,8 +98,6 @@
9698
import com.oracle.graal.python.builtins.objects.cext.hpy.HPyExternalFunctionNodes.HPyGetSetDescriptorSetterRootNode;
9799
import com.oracle.graal.python.builtins.objects.cext.hpy.HPyExternalFunctionNodes.HPyLegacyGetSetDescriptorGetterRoot;
98100
import com.oracle.graal.python.builtins.objects.cext.hpy.HPyExternalFunctionNodes.HPyLegacyGetSetDescriptorSetterRoot;
99-
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyAttachJNIFunctionTypeNodeGen;
100-
import com.oracle.graal.python.builtins.objects.cext.hpy.GraalHPyNodesFactory.HPyAttachNFIFunctionTypeNodeGen;
101101
import com.oracle.graal.python.builtins.objects.dict.PDict;
102102
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
103103
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -1259,6 +1259,10 @@ static int doOtherInt(@SuppressWarnings("unused") GraalHPyContext hpyContext, @S
12591259
@ImportStatic(GraalHPyBoxing.class)
12601260
public abstract static class HPyAsPythonObjectNode extends CExtToJavaNode {
12611261

1262+
static Assumption noDebugModeAssumption() {
1263+
return PythonLanguage.get(null).noHPyDebugModeAssumption;
1264+
}
1265+
12621266
protected final GraalHPyContext ensureContext(GraalHPyContext hpyContext) {
12631267
if (hpyContext == null) {
12641268
return getContext().getHPyContext();
@@ -1269,11 +1273,20 @@ protected final GraalHPyContext ensureContext(GraalHPyContext hpyContext) {
12691273

12701274
public abstract Object execute(GraalHPyContext hpyContext, long bits);
12711275

1272-
@Specialization
1276+
@Specialization(assumptions = "noDebugModeAssumption()")
12731277
static Object doHandle(@SuppressWarnings("unused") GraalHPyContext hpyContext, GraalHPyHandle handle) {
12741278
return handle.getDelegate();
12751279
}
12761280

1281+
@Specialization(replaces = "doHandle")
1282+
static Object doValidHandle(GraalHPyContext hpyContext, GraalHPyHandle handle) {
1283+
if (!handle.isValid()) {
1284+
hpyContext.onInvalidHandle(handle.getDebugId());
1285+
return PNone.NO_VALUE;
1286+
}
1287+
return handle.getDelegate();
1288+
}
1289+
12771290
@Specialization(guards = "isBoxedNullHandle(bits)")
12781291
@SuppressWarnings("unused")
12791292
static Object doNullLong(GraalHPyContext hpyContext, long bits) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public PDebugHandle(Object cls, Shape instanceShape, GraalHPyHandle handle) {
5959
public boolean eq(PDebugHandle other) {
6060
return handle == other.handle;
6161
}
62-
62+
6363
public Object getObj() {
6464
return handle.getDelegate();
6565
}
@@ -71,7 +71,7 @@ public long getId() {
7171

7272
@TruffleBoundary
7373
public void close(GraalHPyDebugContext debugContext) {
74-
handle.closeAndInvalidate(debugContext);
74+
handle.closeAndInvalidate(debugContext);
7575
}
7676

7777
@TruffleBoundary

0 commit comments

Comments
 (0)