|
1 | 1 | /* |
2 | | - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * The Universal Permissive License (UPL), Version 1.0 |
|
43 | 43 | import static com.oracle.graal.python.builtins.objects.thread.AbstractPythonLock.TIMEOUT_MAX; |
44 | 44 | import static com.oracle.graal.python.nodes.BuiltinNames.J_EXIT; |
45 | 45 | import static com.oracle.graal.python.nodes.BuiltinNames.J__THREAD; |
| 46 | +import static com.oracle.graal.python.nodes.BuiltinNames.T_STDERR; |
| 47 | +import static com.oracle.graal.python.nodes.BuiltinNames.T___EXCEPTHOOK__; |
46 | 48 | import static com.oracle.graal.python.nodes.BuiltinNames.T__THREAD; |
47 | 49 | import static com.oracle.graal.python.util.PythonUtils.tsLiteral; |
48 | 50 |
|
|
70 | 72 | import com.oracle.graal.python.builtins.objects.type.TypeNodes; |
71 | 73 | import com.oracle.graal.python.lib.PyNumberAsSizeNode; |
72 | 74 | import com.oracle.graal.python.lib.PyObjectLookupAttr; |
| 75 | +import com.oracle.graal.python.lib.PyObjectSetAttr; |
| 76 | +import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; |
73 | 77 | import com.oracle.graal.python.nodes.ErrorMessages; |
74 | 78 | import com.oracle.graal.python.nodes.PRaiseNode; |
75 | 79 | import com.oracle.graal.python.nodes.WriteUnraisableNode; |
|
109 | 113 | public final class ThreadModuleBuiltins extends PythonBuiltins { |
110 | 114 |
|
111 | 115 | public static final StructSequence.BuiltinTypeDescriptor EXCEPTHOOK_ARGS_DESC = new StructSequence.BuiltinTypeDescriptor( |
112 | | - PythonBuiltinClassType.PExceptHookArgs, |
113 | | - 4, |
114 | | - new String[]{ |
115 | | - "exc_type", "exc_value", "exc_traceback", "thread"}, |
116 | | - new String[]{ |
117 | | - "Exception type", "Exception value", "Exception traceback", |
118 | | - "Exception thread"}); |
| 116 | + PythonBuiltinClassType.PExceptHookArgs, |
| 117 | + 4, |
| 118 | + new String[]{ |
| 119 | + "exc_type", "exc_value", "exc_traceback", "thread"}, |
| 120 | + new String[]{ |
| 121 | + "Exception type", "Exception value", "Exception traceback", |
| 122 | + "Exception thread"}); |
119 | 123 |
|
120 | 124 | @Override |
121 | 125 | protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() { |
@@ -198,57 +202,84 @@ static long getStackSize(VirtualFrame frame, Object stackSizeObj, |
198 | 202 | @GenerateNodeFactory |
199 | 203 | abstract static class GetThreadExceptHookNode extends PythonBinaryBuiltinNode { |
200 | 204 | @Specialization |
201 | | - @TruffleBoundary |
202 | | - Object getExceptHook(PythonModule self, |
203 | | - Object exceptHookArgs, |
204 | | - @Cached PRaiseNode raiseNode) { |
| 205 | + Object getExceptHook(@SuppressWarnings("unused") PythonModule self, |
| 206 | + Object exceptHookArgs, |
| 207 | + @Bind Node inliningTarget, |
| 208 | + @Cached PRaiseNode raiseNode, |
| 209 | + @Cached CallNode callNode, |
| 210 | + @Cached PyObjectLookupAttr lookupAttr, |
| 211 | + @Cached PyObjectSetAttr setAttr, |
| 212 | + @Cached PyObjectStrAsTruffleStringNode strNode) { |
205 | 213 |
|
206 | 214 | Object argsType = GetClassNode.GetPythonObjectClassNode.executeUncached((PythonObject) exceptHookArgs); |
207 | | - if (!TypeNodes.IsSameTypeNode.executeUncached(argsType, PythonBuiltinClassType.PExceptHookArgs)) |
| 215 | + if (!TypeNodes.IsSameTypeNode.executeUncached(argsType, PythonBuiltinClassType.PExceptHookArgs)) { |
208 | 216 | throw PRaiseNode.getUncached().raise(raiseNode, PythonBuiltinClassType.TypeError, ErrorMessages.ARG_TYPE_MUST_BE, "_thread.excepthook", "ExceptHookArgs"); |
209 | | - |
| 217 | + } |
210 | 218 | SequenceStorage seq = ((PTuple) exceptHookArgs).getSequenceStorage(); |
211 | | - if (seq.length() != 4) |
| 219 | + if (seq.length() != 4) { |
212 | 220 | throw PRaiseNode.getUncached().raise(raiseNode, PythonBuiltinClassType.TypeError, ErrorMessages.TAKES_EXACTLY_D_ARGUMENTS_D_GIVEN, 4, seq.length()); |
| 221 | + } |
213 | 222 |
|
214 | 223 | Object excType = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 0); |
215 | 224 |
|
216 | | - if (TypeNodes.IsSameTypeNode.executeUncached(excType, PythonBuiltinClassType.SystemExit)) |
| 225 | + if (TypeNodes.IsSameTypeNode.executeUncached(excType, PythonBuiltinClassType.SystemExit)) { |
217 | 226 | return PNone.NONE; |
218 | | - |
| 227 | + } |
219 | 228 | Object excValue = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 1); |
220 | 229 | Object excTraceback = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 2); |
221 | 230 | Object thread = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 3); |
222 | 231 |
|
223 | | - CallNode callNode = CallNode.create(); |
224 | | - Object name = null; |
| 232 | + TruffleString name; |
225 | 233 |
|
226 | | - Object nameAttr = PyObjectLookupAttr.executeUncached(thread, tsLiteral("_name")); |
| 234 | + Object nameAttr = lookupAttr.execute(null, inliningTarget, thread, tsLiteral("_name")); |
227 | 235 | if (nameAttr != null && nameAttr != PNone.NONE && nameAttr != PNone.NO_VALUE) { |
228 | | - name = nameAttr.toString(); |
229 | | - } |
230 | | - |
231 | | - if (name == null) { |
232 | | - Object getIdentBuiltin = PyObjectLookupAttr.executeUncached(thread, tsLiteral("get_ident")); |
| 236 | + name = strNode.execute(null, inliningTarget, nameAttr); |
| 237 | + } else { |
| 238 | + Object getIdentBuiltin = lookupAttr.execute(null, inliningTarget, thread, tsLiteral("get_ident")); |
233 | 239 | Object ident = callNode.executeWithoutFrame(getIdentBuiltin); |
234 | | - name = ident != null ? ident.toString() : "<unknown>"; |
| 240 | + name = ident != null ? strNode.execute(null, inliningTarget, ident) : tsLiteral("<unknown>"); |
235 | 241 | } |
236 | 242 |
|
237 | | - PrintWriter pw = new PrintWriter(getContext().getEnv().err(), true); |
238 | | - pw.printf("Exception in thread %s:\n", name); |
| 243 | + Object sysMod = getContext().getSysModule(); |
| 244 | + Object stdErr = lookupAttr.execute(null, inliningTarget, sysMod, T_STDERR); |
239 | 245 |
|
240 | | - PException pException; |
241 | | - if (excValue instanceof PException) |
242 | | - pException = (PException) excValue; |
243 | | - else if (excValue instanceof PBaseException base) { |
244 | | - pException = PException.fromObject(base, base.getException().getLocation(), false); |
245 | | - pException.materializeMessage(); |
246 | | - } else { |
247 | | - pw.println(excTraceback.toString()); |
248 | | - return PNone.NONE; |
| 246 | + boolean stdErrInvalid = stdErr == null || stdErr == PNone.NONE || stdErr == PNone.NO_VALUE; |
| 247 | + |
| 248 | + if (stdErrInvalid) { |
| 249 | + if (thread != null && thread != PNone.NONE && thread != PNone.NO_VALUE) { |
| 250 | + stdErr = lookupAttr.execute(null, inliningTarget, thread, tsLiteral("_stderr")); |
| 251 | + } |
| 252 | + if (stdErr == null || stdErr == PNone.NONE || stdErr == PNone.NO_VALUE) { |
| 253 | + return PNone.NONE; |
| 254 | + } |
249 | 255 | } |
250 | 256 |
|
251 | | - ExceptionUtils.printPythonLikeStackTrace(getContext(), pException); |
| 257 | + Object write = lookupAttr.execute(null, inliningTarget, stdErr, tsLiteral("write")); |
| 258 | + Object flush = lookupAttr.execute(null, inliningTarget, stdErr, tsLiteral("flush")); |
| 259 | + |
| 260 | + callNode.executeWithoutFrame(write, tsLiteral("Exception in thread ")); |
| 261 | + callNode.executeWithoutFrame(write, name); |
| 262 | + callNode.executeWithoutFrame(write, tsLiteral(":\n")); |
| 263 | + callNode.executeWithoutFrame(flush); |
| 264 | + |
| 265 | + Object sysExcepthook = lookupAttr.execute(null, inliningTarget, sysMod, T___EXCEPTHOOK__); |
| 266 | + if (sysExcepthook != PNone.NO_VALUE && sysExcepthook != PNone.NONE) { |
| 267 | + if (!stdErrInvalid) { |
| 268 | + callNode.executeWithoutFrame(sysExcepthook, excType, excValue, excTraceback); |
| 269 | + } else { |
| 270 | + Object oldStdErr = lookupAttr.execute(null, inliningTarget, sysMod, T_STDERR); |
| 271 | + try { |
| 272 | + setAttr.execute(inliningTarget, sysMod, T_STDERR, stdErr); |
| 273 | + callNode.executeWithoutFrame(sysExcepthook, excType, excValue, excTraceback); |
| 274 | + } finally { |
| 275 | + setAttr.execute(inliningTarget, sysMod, T_STDERR, oldStdErr == PNone.NO_VALUE ? PNone.NONE : oldStdErr); |
| 276 | + } |
| 277 | + } |
| 278 | + callNode.executeWithoutFrame(flush); |
| 279 | + } else if (excValue instanceof PBaseException) { |
| 280 | + callNode.executeWithoutFrame(write, strNode.execute(null, inliningTarget, excValue)); |
| 281 | + callNode.executeWithoutFrame(flush); |
| 282 | + } |
252 | 283 | return PNone.NONE; |
253 | 284 | } |
254 | 285 | } |
|
0 commit comments