Skip to content

Commit

Permalink
Exception#set_backtrace accept arrays of Backtrace::Location
Browse files Browse the repository at this point in the history
  • Loading branch information
headius committed Dec 18, 2024
1 parent 974b24f commit 5c23a96
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 23 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
Expand Up @@ -4442,7 +4442,7 @@ public RaiseException newStopIteration(IRubyObject result, String message) {
RubyException ex = RubyStopIteration.newInstance(context, result, message);

if (!RubyInstanceConfig.STOPITERATION_BACKTRACE) {
ex.setBacktrace(disabledBacktrace());
ex.setBacktrace(context, disabledBacktrace());
}

return ex.toThrowable();
Expand Down
59 changes: 51 additions & 8 deletions core/src/main/java/org/jruby/RubyException.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public RubyException unmarshalFrom(Ruby runtime, RubyClass type, UnmarshalStream
input.ivar(null, exc, null);

exc.setMessage((IRubyObject) exc.removeInternalVariable("mesg"));
exc.set_backtrace((IRubyObject) exc.removeInternalVariable("bt"));
exc.set_backtrace(runtime.getCurrentContext(), (IRubyObject) exc.removeInternalVariable("bt"));

return exc;
}
Expand Down Expand Up @@ -301,19 +301,62 @@ public IRubyObject backtrace() {
}

@JRubyMethod
public IRubyObject set_backtrace(IRubyObject obj) {
setBacktrace(obj);
public IRubyObject set_backtrace(ThreadContext context, IRubyObject obj) {
setBacktrace(context, obj);
return backtrace();
}

public void setBacktrace(IRubyObject obj) {
public void setBacktrace(ThreadContext context, IRubyObject obj) {
BacktraceData backtraceData = locationArrayToBacktrace(obj);
if (backtraceData != null) {
this.backtrace.backtraceData = backtraceData;
this.backtrace.backtraceObject = null;
this.backtrace.backtraceLocations = obj;
} else {
this.backtrace.backtraceObject = checkBacktrace(context, obj);
}
}

// MRI: rb_check_backtrace
private static IRubyObject checkBacktrace(ThreadContext context, IRubyObject obj) {
if (obj.isNil() || isArrayOfStrings(obj)) {
backtrace.backtraceObject = obj;
return obj;
} else if (obj instanceof RubyString) {
backtrace.backtraceObject = RubyArray.newArray(getRuntime(), obj);
return RubyArray.newArray(context.runtime, obj);
} else {
throw typeError(getRuntime().getCurrentContext(), "backtrace must be Array of String");
throw typeError(context, "backtrace must be an Array of String or an Array of Thread::Backtrace::Location");
}
}

// MRI: rb_location_ary_to_backtrace
private BacktraceData locationArrayToBacktrace(IRubyObject ary) {
if (!(ary instanceof RubyArray array) || array.size() == 0 || asBacktraceLocation(array.eltOk(0)) == null) {
return null;
}

int num_frames = array.size();
RubyStackTraceElement[] backtrace = new RubyStackTraceElement[num_frames];

for (int index = 0; index < num_frames; index++) {
RubyThread.Location locobj = asBacktraceLocation(array.eltOk(index));

if (locobj == null) {
return null;
}

backtrace[index] = locobj.getElement();
}

return new BacktraceData(backtrace);
}

// MRI: rb_frame_info_p
private static RubyThread.Location asBacktraceLocation(IRubyObject object) {
if (object instanceof RubyThread.Location) {
return (RubyThread.Location) object;
}

return null;
}

@JRubyMethod(omit = true)
Expand Down Expand Up @@ -498,7 +541,7 @@ public void printBacktrace(PrintStream errorStream, int skip) {
errorStream.print(string);
}

private boolean isArrayOfStrings(IRubyObject backtrace) {
private static boolean isArrayOfStrings(IRubyObject backtrace) {
if (!(backtrace instanceof RubyArray)) return false;

final RubyArray rTrace = ((RubyArray) backtrace);
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ public static IRubyObject raise(ThreadContext context, IRubyObject recv, IRubyOb
break;
default:
RubyException exception = convertToException(context, args[0], args[1]);
exception.setBacktrace(args[2]);
exception.setBacktrace(context, args[2]);
raise = exception.toThrowable();
break;
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubySystemCallError.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public Object unmarshalFrom(Ruby runtime, RubyClass type, UnmarshalStream input)

exc.message = (IRubyObject)exc.removeInternalVariable("mesg");
exc.errno = (IRubyObject)exc.removeInternalVariable("errno");
exc.set_backtrace((IRubyObject)exc.removeInternalVariable("bt"));
exc.set_backtrace(runtime.getCurrentContext(), (IRubyObject)exc.removeInternalVariable("bt"));

return exc;
}
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,10 @@ public IRubyObject to_s(ThreadContext context) {
return RubyStackTraceElement.to_s_mri(context, element);
}

public RubyStackTraceElement getElement() {
return element;
}

public static RubyArray newLocationArray(Ruby runtime, RubyStackTraceElement[] elements) {
return newLocationArray(runtime, elements, 0, elements.length);
}
Expand Down Expand Up @@ -1563,7 +1567,7 @@ public static IRubyObject prepareRaiseException(ThreadContext context, IRubyObje
exception = (RubyException) tmp;

if (args.length == 3) {
exception.set_backtrace(args[2]);
exception.set_backtrace(context, args[2]);
}

IRubyObject cause = errorInfo;
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/exceptions/RaiseException.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public static RaiseException from(Ruby runtime, RubyClass exceptionClass, String
*/
public static RaiseException from(Ruby runtime, RubyClass exceptionClass, String msg, IRubyObject backtrace) {
RubyException exception = RubyException.newException(runtime, exceptionClass, msg);
exception.setBacktrace(backtrace);
exception.setBacktrace(runtime.getCurrentContext(), backtrace);
return exception.toThrowable();
}

Expand Down Expand Up @@ -219,7 +219,7 @@ private void preRaise(ThreadContext context, IRubyObject backtrace, boolean capt
setStackTraceFromException();
}
} else {
exception.setBacktrace(backtrace);
exception.setBacktrace(context, backtrace);
if (!backtrace.isNil() && !isEmptyArray(backtrace)) {
if (requiresBacktrace(context)) exception.captureBacktrace(context);
}
Expand Down
22 changes: 13 additions & 9 deletions core/src/main/java/org/jruby/runtime/backtrace/BacktraceData.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ public BacktraceData(Stream<StackWalker.StackFrame> stackStream, Stream<Backtrac
this.excludeInternal = excludeInternal;
}

public static final BacktraceData EMPTY = new BacktraceData(
Stream.empty(),
Stream.empty(),
false,
false,
false,
false,
false);
public BacktraceData(RubyStackTraceElement[] backtraceElements) {
this.backtraceElements = backtraceElements;
this.stackStream = Stream.empty();
this.rubyTrace = Stream.empty();
this.fullTrace = false;
this.rawTrace = false;
this.maskNative = false;
this.includeNonFiltered = false;
this.excludeInternal = false;
}

public static final BacktraceData EMPTY = new BacktraceData(null);

public final RubyStackTraceElement[] getBacktrace(Ruby runtime) {
if (backtraceElements == null) {
Expand Down Expand Up @@ -79,7 +83,7 @@ private RubyStackTraceElement[] constructBacktrace(Map<String, Map<String, Strin

eachBacktrace(boundMethods, (elt) -> {trace.add(elt); return trace.size() < count;});

return trace.toArray(RubyStackTraceElement.EMPTY_ARRAY);
return trace.toArray((i) -> new RubyStackTraceElement[i]);
}

private void eachBacktrace(Map<String, Map<String, String>> boundMethods, Predicate<RubyStackTraceElement> consumer) {
Expand Down

0 comments on commit 5c23a96

Please sign in to comment.