Skip to content

Commit cc2d014

Browse files
committed
[GR-18163] Set $! global variable when an at_exit hook raises an exception
PullRequest: truffleruby/4255
2 parents d68ea09 + 5138732 commit cc2d014

File tree

6 files changed

+23
-4
lines changed

6 files changed

+23
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Compatibility:
2020
* Add `Regexp.linear_time?` method (#3039, @andrykonchin).
2121
* Allow null encoding pointer in `rb_enc_interned_str_cstr` (@thomasmarshall).
2222
* Allow anonymous memberless Struct (@simonlevasseur).
23+
* Set `$!` when a `Kernel#at_exit` hook raises an exception (#3535, @andrykonchin).
2324

2425
Performance:
2526
* Fix inline caching for Regexp creation from Strings (#3492, @andrykonchin, @eregon).

spec/ruby/shared/kernel/at_exit.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
result.lines.should.include?("The exception matches: true (message=foo)\n")
3131
end
3232

33+
it "gives access to an exception raised in a previous handler" do
34+
code = "#{@method} { print '$!.message = ' + $!.message }; #{@method} { raise 'foo' }"
35+
result = ruby_exe(code, args: "2>&1", exit_status: 1)
36+
result.lines.should.include?("$!.message = foo")
37+
end
38+
3339
it "both exceptions in a handler and in the main script are printed" do
3440
code = "#{@method} { raise 'at_exit_error' }; raise 'main_script_error'"
3541
result = ruby_exe(code, args: "2>&1", exit_status: 1)

spec/tags/core/kernel/at_exit_tags.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ slow:Kernel.at_exit decides the exit status if both at_exit and the main script
77
slow:Kernel.at_exit runs all handlers even if some raise exceptions
88
slow:Kernel.at_exit runs handlers even if the main script fails to parse
99
slow:Kernel.at_exit calls the nested handler right after the outer one if a handler is nested into another handler
10+
slow:Kernel.at_exit gives access to an exception raised in a previous handler

spec/tags/language/END_tags.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ slow:The END keyword runs only once for multiple calls
1111
slow:The END keyword warns when END is used in a method
1212
slow:The END keyword END blocks and at_exit callbacks are mixed runs them all in reverse order of registration
1313
slow:The END keyword is affected by the toplevel assignment
14+
slow:The END keyword gives access to an exception raised in a previous handler

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public final class RubyContext {
118118
private final MarkingService markingService;
119119
private final ObjectSpaceManager objectSpaceManager = new ObjectSpaceManager();
120120
private final SharedObjects sharedObjects = new SharedObjects(this);
121-
private final AtExitManager atExitManager = new AtExitManager(this);
121+
private final AtExitManager atExitManager;
122122
private final CallStackManager callStack;
123123
private final CoreExceptions coreExceptions;
124124
private final EncodingManager encodingManager;
@@ -202,6 +202,7 @@ public RubyContext(RubyLanguage language, TruffleLanguage.Env env) {
202202
finalizationService = new FinalizationService(referenceProcessor);
203203
markingService = new MarkingService();
204204
dataObjectFinalizationService = new DataObjectFinalizationService(language, referenceProcessor);
205+
atExitManager = new AtExitManager(this, language);
205206

206207
// We need to construct this at runtime
207208
random = createRandomInstance();

src/main/java/org/truffleruby/core/kernel/AtExitManager.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import com.oracle.truffle.api.exception.AbstractTruffleException;
1818
import org.truffleruby.RubyContext;
19+
import org.truffleruby.RubyLanguage;
20+
import org.truffleruby.core.exception.ExceptionOperations;
1921
import org.truffleruby.core.exception.RubyException;
2022
import org.truffleruby.core.exception.RubySystemExit;
2123
import org.truffleruby.core.proc.ProcOperations;
@@ -31,12 +33,14 @@
3133
public final class AtExitManager {
3234

3335
private final RubyContext context;
36+
private final RubyLanguage language;
3437

3538
private final Deque<RubyProc> atExitHooks = new ConcurrentLinkedDeque<>();
3639
private final Deque<RubyProc> systemExitHooks = new ConcurrentLinkedDeque<>();
3740

38-
public AtExitManager(RubyContext context) {
41+
public AtExitManager(RubyContext context, RubyLanguage language) {
3942
this.context = context;
43+
this.language = language;
4044
}
4145

4246
public void add(RubyProc block, boolean always) {
@@ -74,7 +78,7 @@ private AbstractTruffleException runExitHooks(Deque<RubyProc> stack) {
7478
try {
7579
ProcOperations.rootCall(block, NoKeywordArgumentsDescriptor.INSTANCE, RubyBaseNode.EMPTY_ARGUMENTS);
7680
} catch (AbstractTruffleException e) {
77-
handleAtExitException(context, e);
81+
handleAtExitException(context, language, e);
7882
lastException = e;
7983
}
8084
}
@@ -99,7 +103,12 @@ public static boolean isSilentException(RubyContext context, AbstractTruffleExce
99103
rubyException.getLogicalClass() == context.getCoreLibrary().signalExceptionClass;
100104
}
101105

102-
private static void handleAtExitException(RubyContext context, AbstractTruffleException exception) {
106+
private static void handleAtExitException(RubyContext context, RubyLanguage language,
107+
AbstractTruffleException exception) {
108+
// Set $! for the next at_exit handlers
109+
language.getCurrentThread().threadLocalGlobals.setLastException(ExceptionOperations
110+
.getExceptionObject(exception));
111+
103112
if (!isSilentException(context, exception)) {
104113
context.getDefaultBacktraceFormatter().printRubyExceptionOnEnvStderr("", exception);
105114
}

0 commit comments

Comments
 (0)