|
49 | 49 | import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
|
50 | 50 | import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
|
51 | 51 | import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectBorrowed;
|
| 52 | +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectPtr; |
52 | 53 | import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
|
53 | 54 | import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyThreadState;
|
54 | 55 | import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void;
|
|
83 | 84 | import com.oracle.graal.python.builtins.modules.cext.PythonCextFileBuiltins.PyFile_WriteObject;
|
84 | 85 | import com.oracle.graal.python.builtins.objects.PNone;
|
85 | 86 | import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.ClearCurrentExceptionNode;
|
| 87 | +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PyErrFetchNode; |
86 | 88 | import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PyErrOccurredNode;
|
87 | 89 | import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.TransformExceptionToNativeNode;
|
88 | 90 | import com.oracle.graal.python.builtins.objects.cext.capi.PThreadState;
|
| 91 | +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode; |
89 | 92 | import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
|
| 93 | +import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; |
90 | 94 | import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
|
91 | 95 | import com.oracle.graal.python.builtins.objects.dict.DictBuiltins.SetItemNode;
|
92 | 96 | import com.oracle.graal.python.builtins.objects.dict.PDict;
|
|
96 | 100 | import com.oracle.graal.python.builtins.objects.function.PKeyword;
|
97 | 101 | import com.oracle.graal.python.builtins.objects.module.PythonModule;
|
98 | 102 | import com.oracle.graal.python.builtins.objects.traceback.LazyTraceback;
|
99 |
| -import com.oracle.graal.python.builtins.objects.traceback.MaterializeLazyTracebackNode; |
100 | 103 | import com.oracle.graal.python.builtins.objects.traceback.PTraceback;
|
101 | 104 | import com.oracle.graal.python.builtins.objects.tuple.PTuple;
|
102 | 105 | import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
|
|
116 | 119 | import com.oracle.graal.python.nodes.util.ExceptionStateNodes.GetCaughtExceptionNode;
|
117 | 120 | import com.oracle.graal.python.runtime.PythonContext;
|
118 | 121 | import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
|
| 122 | +import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; |
119 | 123 | import com.oracle.graal.python.runtime.PythonOptions;
|
120 | 124 | import com.oracle.graal.python.runtime.exception.ExceptionUtils;
|
121 | 125 | import com.oracle.graal.python.runtime.exception.PException;
|
@@ -188,43 +192,56 @@ static Object run(Object typ, Object val, Object tb,
|
188 | 192 | }
|
189 | 193 | }
|
190 | 194 |
|
191 |
| - @CApiBuiltin(ret = PyObjectTransfer, call = Ignored) |
192 |
| - abstract static class PyTruffleErr_Fetch extends CApiNullaryBuiltinNode { |
| 195 | + @CApiBuiltin(ret = Void, args = {PyObjectPtr, PyObjectPtr, PyObjectPtr}, call = Ignored) |
| 196 | + abstract static class PyTruffleErr_Fetch extends CApiTernaryBuiltinNode { |
193 | 197 | @Specialization
|
194 |
| - Object run( |
| 198 | + static Object doGeneric(Object pType, Object pValue, Object pTraceback, |
195 | 199 | @Bind("this") Node inliningTarget,
|
196 | 200 | @Cached GetThreadStateNode getThreadStateNode,
|
197 |
| - @Cached GetClassNode getClassNode, |
198 |
| - @Cached MaterializeLazyTracebackNode materializeTraceback, |
199 |
| - @Cached PythonObjectFactory factory, |
200 |
| - @Cached ClearCurrentExceptionNode clearCurrentExceptionNode) { |
| 201 | + @Cached PyErrFetchNode pyErrFetchNode, |
| 202 | + @Cached PythonToNativeNewRefNode toNativeNewRefNode, |
| 203 | + @Cached CStructAccess.WritePointerNode writePointerNode) { |
201 | 204 | PythonContext.PythonThreadState threadState = getThreadStateNode.execute(inliningTarget);
|
202 |
| - PException currentException = threadState.getCurrentException(); |
203 |
| - Object result; |
204 |
| - if (currentException == null) { |
205 |
| - result = getNativeNull(); |
| 205 | + Object[] exceptionState = pyErrFetchNode.execute(inliningTarget, threadState); |
| 206 | + // null or [type, value, traceback] |
| 207 | + assert exceptionState == null || exceptionState.length == 3; |
| 208 | + if (exceptionState == null) { |
| 209 | + /* |
| 210 | + * This should be caught in native by checking 'PyErr_Occurred' and avoiding the |
| 211 | + * upcall. But let's be defensive and treat that case on a slow path. |
| 212 | + */ |
| 213 | + doNoException(pType, pValue, pTraceback); |
206 | 214 | } else {
|
207 |
| - Object exception = currentException.getEscapedException(); |
208 |
| - Object traceback = null; |
209 |
| - if (threadState.getCurrentTraceback() != null) { |
210 |
| - traceback = materializeTraceback.execute(inliningTarget, threadState.getCurrentTraceback()); |
211 |
| - } |
212 |
| - if (traceback == null) { |
213 |
| - traceback = getNativeNull(); |
214 |
| - } |
215 |
| - result = factory.createTuple(new Object[]{getClassNode.execute(inliningTarget, exception), exception, traceback}); |
216 |
| - clearCurrentExceptionNode.execute(inliningTarget, threadState); |
| 215 | + assert exceptionState[0] != null; |
| 216 | + assert exceptionState[1] != null; |
| 217 | + /* |
| 218 | + * NOTE: We need cannot use 'WriteObjectNewRefNode' because we are writing to out |
| 219 | + * variables (C type 'PyObject **out') where the previous value (i.e. '*out') of |
| 220 | + * those is unspecified. 'WriteObjectNewRefNode' would try to decref the previous |
| 221 | + * object and we MUST NOT do that. Therefore, we use the combination of |
| 222 | + * 'WritePointerNode' and 'PythonToNativeNewRefNode'. |
| 223 | + */ |
| 224 | + writePointerNode.write(pType, toNativeNewRefNode.execute(exceptionState[0])); |
| 225 | + writePointerNode.write(pValue, toNativeNewRefNode.execute(exceptionState[1])); |
| 226 | + writePointerNode.write(pTraceback, toNativeNewRefNode.execute(exceptionState[2] != null ? exceptionState[2] : PNone.NO_VALUE)); |
217 | 227 | }
|
218 |
| - return result; |
| 228 | + return PNone.NO_VALUE; |
| 229 | + } |
| 230 | + |
| 231 | + @TruffleBoundary |
| 232 | + private static void doNoException(Object pType, Object pValue, Object pTraceback) { |
| 233 | + CStructAccess.WritePointerNode.writeUncached(pType, 0, 0L); |
| 234 | + CStructAccess.WritePointerNode.writeUncached(pValue, 0, 0L); |
| 235 | + CStructAccess.WritePointerNode.writeUncached(pTraceback, 0, 0L); |
219 | 236 | }
|
220 | 237 | }
|
221 | 238 |
|
222 | 239 | @CApiBuiltin(ret = PyObjectBorrowed, args = {PyThreadState}, call = Ignored)
|
223 | 240 | abstract static class _PyTruffleErr_Occurred extends CApiUnaryBuiltinNode {
|
224 | 241 | @Specialization
|
225 | 242 | Object run(PThreadState state,
|
226 |
| - @Bind("this") Node inliningTarget, |
227 |
| - @Cached PyErrOccurredNode pyErrOccurredNode) { |
| 243 | + @Bind("this") Node inliningTarget, |
| 244 | + @Cached PyErrOccurredNode pyErrOccurredNode) { |
228 | 245 | Object excType = pyErrOccurredNode.execute(inliningTarget, state.getThreadState());
|
229 | 246 | return excType != null ? excType : getNativeNull();
|
230 | 247 | }
|
@@ -426,31 +443,32 @@ static Object raise(int set_sys_last_vars,
|
426 | 443 | @Cached PyErr_Restore restoreNode,
|
427 | 444 | @Cached PyFile_WriteObject writeFileNode,
|
428 | 445 | @Cached ExitNode exitNode,
|
429 |
| - @Cached PyTruffleErr_Fetch fetchNode, |
430 | 446 | @Cached PyErr_Display errDisplayNode) {
|
431 | 447 | PythonContext context = PythonContext.get(null);
|
432 | 448 | NativePointer nativeNull = context.getNativeNull();
|
433 | 449 |
|
434 |
| - Object err = PyErrOccurredNode.executeUncached(context.getThreadState(PythonLanguage.get(null))); |
| 450 | + PythonThreadState threadState = context.getThreadState(PythonLanguage.get(null)); |
| 451 | + Object err = PyErrOccurredNode.executeUncached(threadState); |
435 | 452 | PythonModule sys = context.getSysModule();
|
436 | 453 | if (err != nativeNull && IsBuiltinObjectProfile.profileObjectUncached(err, PythonBuiltinClassType.SystemExit)) {
|
437 | 454 | handleSystemExit(excInfoNode, getItemNode, isInstanceNode, restoreNode, (SysModuleBuiltins) sys.getBuiltins(), writeFileNode, exitNode);
|
438 | 455 | }
|
439 |
| - Object fetched = fetchNode.execute(null); |
| 456 | + Object[] fetched = PyErrFetchNode.executeUncached(threadState); |
| 457 | + // null or [type, value, traceback] |
| 458 | + assert fetched == null || fetched.length == 3; |
440 | 459 | Object type = null;
|
441 | 460 | Object val = null;
|
442 | 461 | Object tb = null;
|
443 | 462 |
|
444 |
| - if (fetched != nativeNull) { |
445 |
| - PTuple fetchedTuple = (PTuple) fetched; |
446 |
| - type = getItemNode.execute(null, fetchedTuple, 0); |
447 |
| - val = getItemNode.execute(null, fetchedTuple, 1); |
448 |
| - tb = getItemNode.execute(null, fetchedTuple, 2); |
| 463 | + if (fetched != null) { |
| 464 | + type = fetched[0]; |
| 465 | + val = fetched[1]; |
| 466 | + tb = fetched[2]; |
449 | 467 | }
|
450 | 468 | if (type == null || type == PNone.NONE) {
|
451 | 469 | return PNone.NONE;
|
452 | 470 | }
|
453 |
| - if (tb == nativeNull) { |
| 471 | + if (tb == null) { |
454 | 472 | tb = PNone.NONE;
|
455 | 473 | }
|
456 | 474 | if (PyObjectLookupAttr.executeUncached(val, T___TRACEBACK__) == PNone.NONE) {
|
|
0 commit comments