Skip to content

Commit d2d8607

Browse files
committed
[GR-26970] Backports for 20.3 batch 2
PullRequest: truffleruby/2128
2 parents d48121d + e1dc3a3 commit d2d8607

File tree

24 files changed

+127
-45
lines changed

24 files changed

+127
-45
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ Bug fixes:
1919
* Fixed `SystemStackError` sometimes replaced by an internal Java `NoClassDefFoundError` on JVM (#1743).
2020
* Fixed constant/identifier detection in lexer for non-ASCII encodings (#2079, #2102, @ivoanjo).
2121
* Fixed parsing of `--jvm` as an application argument (#2108).
22+
* Fix `rb_rescue2` to ignore the end marker `(VALUE)0` (#2127, #2130).
23+
* Fix `String#{chomp, chomp!}` issue with invalid encoded strings (#2133).
24+
* Fix status and output when SystemExit is subclassed and raised (#2128)
2225

2326
Compatibility:
2427

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require_relative '../../spec_helper'
2+
3+
describe "SystemExit" do
4+
it "sets the exit status and exits silently when raised" do
5+
code = 'raise SystemExit.new(7)'
6+
result = ruby_exe(code, args: "2>&1")
7+
result.should == ""
8+
$?.exitstatus.should == 7
9+
end
10+
11+
it "sets the exit status and exits silently when raised when subclassed" do
12+
code = 'class CustomExit < SystemExit; end; raise CustomExit.new(8)'
13+
result = ruby_exe(code, args: "2>&1")
14+
result.should == ""
15+
$?.exitstatus.should == 8
16+
end
17+
end

spec/ruby/core/string/chomp_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
$/ = "cdef"
5656
"abcdef".chomp.should == "ab"
5757
end
58+
59+
it "removes one trailing newline for string with invalid encoding" do
60+
"\xa0\xa1\n".chomp.should == "\xa0\xa1"
61+
end
5862
end
5963

6064
describe "when passed nil" do
@@ -108,6 +112,10 @@
108112
it "returns an empty String when self is empty" do
109113
"".chomp("").should == ""
110114
end
115+
116+
it "removes one trailing newline for string with invalid encoding" do
117+
"\xa0\xa1\n".chomp("").should == "\xa0\xa1"
118+
end
111119
end
112120

113121
describe "when passed '\\n'" do

spec/ruby/optional/capi/kernel_spec.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,12 +396,20 @@ def proc_caller
396396
proc = -> x { x }
397397
arg_error_proc = -> *_ { raise ArgumentError, '' }
398398
run_error_proc = -> *_ { raise RuntimeError, '' }
399-
type_error_proc = -> *_ { raise TypeError, '' }
399+
type_error_proc = -> *_ { raise Exception, 'custom error' }
400400
@s.rb_rescue2(arg_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc
401401
@s.rb_rescue2(run_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc
402402
-> {
403403
@s.rb_rescue2(type_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError)
404-
}.should raise_error(TypeError)
404+
}.should raise_error(Exception, 'custom error')
405+
end
406+
407+
ruby_bug "#17305", ""..."2.7" do
408+
it "raises TypeError if one of the passed exceptions is not a Module" do
409+
-> {
410+
@s.rb_rescue2(-> *_ { raise RuntimeError, "foo" }, :no_exc, -> x { x }, :exc, Object.new, 42)
411+
}.should raise_error(TypeError, /class or module required/)
412+
end
405413
end
406414
end
407415

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
slow:SystemExit sets the exit status and exits silently when raised
2+
slow:SystemExit sets the exit status and exits silently when raised when subclassed

src/main/c/cext/exception.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ VALUE rb_rescue2(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*r_proc)(ANYARGS)
8585
// arguments using the polyglot api.
8686
for (;n < total; n++) {
8787
VALUE arg = polyglot_get_arg(n);
88+
if (arg == (VALUE)0) {
89+
break;
90+
}
91+
8892
rb_ary_push(rescued, arg);
8993
}
9094
return cext_rb_rescue2(b_proc, data1, r_proc, data2, rb_tr_unwrap(rescued));

src/main/java/org/truffleruby/core/array/ArrayNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,7 @@ private RubyString finishPack(int formatLength, BytesResult result) {
15911591

15921592
@TruffleBoundary
15931593
protected RootCallTarget compileFormat(RubyString format) {
1594-
return new PackCompiler(getContext(), this).compile(format.toString());
1594+
return new PackCompiler(getContext(), this).compile(format.getJavaString());
15951595
}
15961596

15971597
protected int getCacheLimit() {

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ public void showExceptionIfDebug(RubyClass rubyClass, Object message, Backtrace
8989
}
9090
}
9191

92+
public String inspectReceiver(Object receiver) {
93+
RubyString rubyString = (RubyString) context.send(
94+
context.getCoreLibrary().truffleExceptionOperationsModule,
95+
"receiver_string",
96+
receiver);
97+
return rubyString.getJavaString();
98+
}
99+
92100
// ArgumentError
93101

94102
public RubyException argumentErrorOneHashRequired(Node currentNode) {
@@ -488,17 +496,17 @@ public RubyException typeErrorCantConvertInto(Object from, String toClass, Node
488496

489497
@TruffleBoundary
490498
public RubyException typeErrorIsNotA(Object value, String expectedType, Node currentNode) {
491-
return typeErrorIsNotA(value.toString(), expectedType, currentNode);
499+
return typeErrorIsNotA(inspectReceiver(value), expectedType, currentNode);
492500
}
493501

494502
@TruffleBoundary
495503
public RubyException typeErrorIsNotA(String value, String expectedType, Node currentNode) {
496-
return typeError(StringUtils.format("%s is not a %s", value, expectedType), currentNode);
504+
return typeError(value + " is not a " + expectedType, currentNode);
497505
}
498506

499507
@TruffleBoundary
500508
public RubyException typeErrorIsNotAClassModule(Object value, Node currentNode) {
501-
return typeError(StringUtils.format("%s is not a class/module", value), currentNode);
509+
return typeError(inspectReceiver(value) + " is not a class/module", currentNode);
502510
}
503511

504512
@TruffleBoundary

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616

1717
import org.truffleruby.RubyContext;
1818
import org.truffleruby.core.exception.RubyException;
19-
import org.truffleruby.core.klass.RubyClass;
2019
import org.truffleruby.core.proc.ProcOperations;
2120
import org.truffleruby.core.proc.RubyProc;
2221
import org.truffleruby.language.control.ExitException;
2322
import org.truffleruby.language.control.RaiseException;
2423

2524
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
25+
import org.truffleruby.language.objects.IsANode;
2626

2727
public class AtExitManager {
2828

@@ -83,9 +83,9 @@ public List<RubyProc> getHandlers() {
8383
}
8484

8585
public static boolean isSilentException(RubyContext context, RubyException rubyException) {
86-
final RubyClass logicalClass = rubyException.getLogicalClass();
87-
return logicalClass == context.getCoreLibrary().systemExitClass ||
88-
logicalClass == context.getCoreLibrary().signalExceptionClass;
86+
final IsANode isANode = IsANode.getUncached();
87+
return isANode.executeIsA(rubyException, context.getCoreLibrary().systemExitClass) ||
88+
isANode.executeIsA(rubyException, context.getCoreLibrary().signalExceptionClass);
8989
}
9090

9191
private static void handleAtExitException(RubyContext context, RubyException rubyException) {

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ public RubyConstant setConstant(RubyContext context, Node currentNode, String na
343343
@TruffleBoundary
344344
public void setAutoloadConstant(RubyContext context, Node currentNode, String name, RubyString filename) {
345345
RubyConstant autoloadConstant = setConstantInternal(context, currentNode, name, filename, true);
346+
if (autoloadConstant == null) {
347+
return;
348+
}
349+
346350
if (context.getOptions().LOG_AUTOLOAD) {
347351
RubyLanguage.LOGGER.info(() -> String.format(
348352
"%s: setting up autoload %s with %s",
@@ -351,7 +355,7 @@ public void setAutoloadConstant(RubyContext context, Node currentNode, String na
351355
filename));
352356
}
353357
final ReentrantLockFreeingMap<String> fileLocks = getContext().getFeatureLoader().getFileLocks();
354-
final ReentrantLock lock = fileLocks.get(filename.toString());
358+
final ReentrantLock lock = fileLocks.get(filename.getJavaString());
355359
if (lock.isLocked()) {
356360
// We need to handle the new autoload constant immediately
357361
// if Object.autoload(name, filename) is executed from filename.rb
@@ -367,10 +371,22 @@ private RubyConstant setConstantInternal(RubyContext context, Node currentNode,
367371

368372
SharedObjects.propagate(context, rubyModule, value);
369373

374+
final String autoloadPath = autoload ? ((RubyString) value).getJavaString() : null;
370375
RubyConstant previous;
371376
RubyConstant newConstant;
372377
do {
373378
previous = constants.get(name);
379+
if (autoload && previous != null) {
380+
if (previous.hasValue()) {
381+
// abort, do not set an autoload constant, the constant already has a value
382+
return null;
383+
} else if (previous.isAutoload() &&
384+
previous.getAutoloadConstant().getAutoloadPath().equals(autoloadPath)) {
385+
// already an autoload constant with the same path,
386+
// do nothing so we don't replace the AutoloadConstant#autoloadLock which might be already acquired
387+
return null;
388+
}
389+
}
374390
newConstant = newConstant(currentNode, name, value, autoload, previous);
375391
} while (!ConcurrentOperations.replace(constants, name, previous, newConstant));
376392

src/main/java/org/truffleruby/core/module/ModuleNodes.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,7 @@ protected Object autoload(RubyModule module, String name, RubyString filename) {
580580
throw new RaiseException(getContext(), coreExceptions().argumentError("empty file name", this));
581581
}
582582

583-
final RubyConstant constant = module.fields.getConstant(name);
584-
if (constant == null || !constant.hasValue()) {
585-
module.fields.setAutoloadConstant(getContext(), this, name, filename);
586-
}
587-
583+
module.fields.setAutoloadConstant(getContext(), this, name, filename);
588584
return nil;
589585
}
590586
}

src/main/java/org/truffleruby/core/regexp/MatchDataNodes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,11 @@ private int getBackRefFromRope(RubyMatchData matchData, Object index, Rope value
389389
0,
390390
value.byteLength(),
391391
matchData.region);
392-
} catch (final ValueException e) {
392+
} catch (ValueException e) {
393393
throw new RaiseException(
394394
getContext(),
395395
coreExceptions().indexError(
396-
StringUtils.format("undefined group name reference: %s", index.toString()),
396+
StringUtils.format("undefined group name reference: %s", index),
397397
this));
398398
}
399399
}

src/main/java/org/truffleruby/core/rope/ManagedRope.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ public final byte[] getBytes() {
5454

5555
@Override
5656
public final String toString() {
57-
assert ALLOW_TO_STRING;
58-
59-
final byte[] bytesBefore = bytes;
60-
final String string = RopeOperations.decodeOrEscapeBinaryRope(this, RopeOperations.flattenBytes(this));
61-
assert bytes == bytesBefore : "bytes should not be modified by Rope#toString() as otherwise inspecting a Rope would have a side effect";
62-
return string;
57+
if (DEBUG_ROPE_BYTES) {
58+
final byte[] bytesBefore = bytes;
59+
final String string = RopeOperations.decodeOrEscapeBinaryRope(this, RopeOperations.flattenBytes(this));
60+
assert bytes == bytesBefore : "bytes should not be modified by Rope#toString() as otherwise inspecting a Rope would have a side effect";
61+
return string;
62+
} else {
63+
return RopeOperations.decodeRope(this);
64+
}
6365
}
6466

6567
}

src/main/java/org/truffleruby/core/rope/NativeRope.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ public int hashCode() {
184184

185185
@Override
186186
public String toString() {
187-
assert ALLOW_TO_STRING;
188187
return toLeafRope().toString();
189188
}
190189

src/main/java/org/truffleruby/core/rope/Rope.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public abstract class Rope implements Comparable<Rope> {
2020
// NativeRope, RepeatingRope, 3 LeafRope, ConcatRope, SubstringRope, 1 LazyRope
2121
public static final int NUMBER_OF_CONCRETE_CLASSES = 8;
2222

23-
// Useful for debugging. Setting to false allow to catch wrong usages.
24-
protected static final boolean ALLOW_TO_STRING = true;
23+
// Useful for debugging. Setting to true avoids ManagedRope#toString to populate bytes as a side-effect of the debugger calling toString().
24+
protected static final boolean DEBUG_ROPE_BYTES = false;
2525

2626
public final Encoding encoding;
2727
private final int byteLength;

src/main/java/org/truffleruby/core/thread/ThreadManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ private void threadMain(RubyThread thread, Node currentNode, Supplier<Object> ta
315315
final String message = StringUtils
316316
.format("%s terminated with internal error:", Thread.currentThread().getName());
317317
final RuntimeException runtimeException = new RuntimeException(message, e);
318+
// Immediately print internal exceptions, in case they would cause a deadlock
319+
runtimeException.printStackTrace();
318320
rethrowOnMainThread(currentNode, runtimeException);
319321
setThreadValue(context, thread, Nil.INSTANCE);
320322
} finally {

src/main/java/org/truffleruby/language/SafepointManager.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,12 @@ private void driveArrivalAtPhaser() {
209209
}
210210

211211
private void printStacktracesOfBlockedThreads() {
212+
final Thread drivingThread = Thread.currentThread();
213+
212214
System.err.println("Dumping stacktraces of all threads:");
213215
for (Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) {
214216
final Thread thread = entry.getKey();
215-
if (thread != Thread.currentThread() && runningThreads.contains(thread)) {
217+
if (runningThreads.contains(thread)) {
216218
final StackTraceElement[] stackTrace = entry.getValue();
217219
boolean blocked = true;
218220

@@ -225,7 +227,8 @@ private void printStacktracesOfBlockedThreads() {
225227
}
226228
}
227229

228-
System.err.println((blocked ? "BLOCKED: " : "IN SAFEPOINT: ") + thread);
230+
String kind = thread == drivingThread ? "DRIVER" : (blocked ? "BLOCKED" : "IN SAFEPOINT");
231+
System.err.println(kind + ": " + thread);
229232
for (StackTraceElement stackTraceElement : stackTrace) {
230233
System.err.println(stackTraceElement);
231234
}

src/main/java/org/truffleruby/language/exceptions/TopLevelRaiseHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.truffleruby.language.dispatch.DispatchNode;
2121

2222
import com.oracle.truffle.api.CompilerDirectives;
23+
import org.truffleruby.language.objects.IsANodeGen;
2324

2425
public class TopLevelRaiseHandler extends RubyContextNode {
2526

@@ -66,7 +67,7 @@ public int execute(Runnable body) {
6667
}
6768

6869
private int statusFromException(RubyException exception) {
69-
if (exception.getLogicalClass() == coreLibrary().systemExitClass) {
70+
if (IsANodeGen.getUncached().executeIsA(exception, coreLibrary().systemExitClass)) {
7071
final Object status = DynamicObjectLibrary.getUncached().getOrDefault(exception, "@status", null);
7172
return IntegerCastNodeGen.getUncached().executeCastInt(status);
7273
} else {

src/main/java/org/truffleruby/language/objects/IsANode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ public static IsANode create() {
2828
return IsANodeGen.create();
2929
}
3030

31+
public static IsANode getUncached() {
32+
return IsANodeGen.getUncached();
33+
}
34+
3135
public abstract boolean executeIsA(Object self, RubyModule module);
3236

3337
@Specialization(

src/main/java/org/truffleruby/parser/parser/RubyParser.java

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/java/org/truffleruby/parser/parser/RubyParser.y

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,7 @@ rel_expr : arg relop arg %prec tGT {
12591259
$$ = support.getOperatorCallNode($1, $2, $3, lexer.getPosition());
12601260
}
12611261
| rel_expr relop arg %prec tGT {
1262-
support.warning(lexer.getPosition(), "comparison '" + $2 + "' after comparison");
1262+
support.warning(lexer.getPosition(), "comparison '" + $2.getString() + "' after comparison");
12631263
$$ = support.getOperatorCallNode($1, $2, $3, lexer.getPosition());
12641264
}
12651265

src/main/ruby/truffleruby/core/string.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ def chomp!(sep=undefined)
844844
if j = Primitive.string_previous_byte_index(self, bytes)
845845
chr = Primitive.string_chr_at(self, j)
846846

847-
if chr.ord == 13
847+
if !Primitive.nil?(chr) && chr.ord == 13
848848
bytes = j
849849
end
850850
end
@@ -857,13 +857,13 @@ def chomp!(sep=undefined)
857857

858858
while i = Primitive.string_previous_byte_index(self, bytes)
859859
chr = Primitive.string_chr_at(self, i)
860-
break unless chr.ord == 10
860+
break unless !Primitive.nil?(chr) && chr.ord == 10
861861

862862
bytes = i
863863

864864
if j = Primitive.string_previous_byte_index(self, i)
865865
chr = Primitive.string_chr_at(self, j)
866-
if chr.ord == 13
866+
if !Primitive.nil?(chr) && chr.ord == 13
867867
bytes = j
868868
end
869869
end

0 commit comments

Comments
 (0)