Skip to content

Commit

Permalink
Add support for a rescue trace event
Browse files Browse the repository at this point in the history
Discussed in CRuby core developer meeting May 10, 2023 at
RubyKaigi.

One test exists for this in the 3.3 ruby/test_settracefunc.rb and
will be merged in once we update CRuby tests.

See https://bugs.ruby-lang.org/issues/19572
  • Loading branch information
headius committed Jan 17, 2024
1 parent b96df9a commit 11eb167
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 9 deletions.
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4632,6 +4632,8 @@ private void buildRescueBodyInternal(RescueBodyNode rescueBodyNode, Variable rv,
// Caught exception case -- build rescue body
addInstr(new LabelInstr(caughtLabel));
Node realBody = rescueBodyNode.getBodyNode();
int line = realBody instanceof NilImplicitNode ? rescueBodyNode.getLine() : realBody.getLine();
addInstr(new RuntimeHelperCall(createTemporaryVariable(), TRACE_RESCUE, new Operand[]{new FrozenString(getFileName()), new Integer(line)}));
Operand x = build(realBody);
if (x != U_NIL) { // can be U_NIL if the rescue block has an explicit return
// Set up node return value 'rv'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Boolean;
import org.jruby.ir.operands.*;
import org.jruby.ir.operands.Fixnum;
import org.jruby.ir.operands.FrozenString;
import org.jruby.ir.operands.Integer;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Stringable;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
Expand All @@ -27,7 +32,8 @@ public enum Methods {
HANDLE_PROPAGATED_BREAK, HANDLE_NONLOCAL_RETURN, HANDLE_BREAK_AND_RETURNS_IN_LAMBDA,
IS_DEFINED_BACKREF, IS_DEFINED_NTH_REF, IS_DEFINED_GLOBAL,
IS_DEFINED_CLASS_VAR, IS_DEFINED_SUPER, IS_DEFINED_METHOD, IS_DEFINED_CALL,
IS_DEFINED_CONSTANT_OR_METHOD, MERGE_KWARGS, IS_HASH_EMPTY, HASH_CHECK, ARRAY_LENGTH;
IS_DEFINED_CONSTANT_OR_METHOD, MERGE_KWARGS, IS_HASH_EMPTY, HASH_CHECK, ARRAY_LENGTH,
TRACE_RESCUE;

private static final Methods[] VALUES = values();

Expand Down Expand Up @@ -96,13 +102,12 @@ public Object callHelper(ThreadContext context, StaticScope currScope, DynamicSc
Object[] temp, Block block) {
Operand[] operands = getOperands();

if (helperMethod == Methods.IS_DEFINED_BACKREF) {
return IRRuntimeHelpers.isDefinedBackref(
context,
(IRubyObject) operands[0].retrieve(context, self, currScope, currDynScope, temp));
}

// These have special operands[0] that we may not want to execute
switch (helperMethod) {
case IS_DEFINED_BACKREF:
return IRRuntimeHelpers.isDefinedBackref(
context,
(IRubyObject) operands[0].retrieve(context, self, currScope, currDynScope, temp));
case IS_DEFINED_NTH_REF:
return IRRuntimeHelpers.isDefinedNthRef(
context,
Expand All @@ -113,6 +118,9 @@ public Object callHelper(ThreadContext context, StaticScope currScope, DynamicSc
context,
((Stringable) operands[0]).getString(),
(IRubyObject) operands[1].retrieve(context, self, currScope, currDynScope, temp));
case TRACE_RESCUE:
IRRuntimeHelpers.traceRescue(context, ((Stringable) operands[0]).getString(), (int) ((Integer) operands[1]).getValue());
return context.nil;
}

Object arg1 = operands[0].retrieve(context, self, currScope, currDynScope, temp);
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -2460,6 +2460,13 @@ public static void traceRaise(ThreadContext context) {
}
}

public static void traceRescue(ThreadContext context, String file, int line) {
TraceEventManager traceEvents = context.traceEvents;
if (traceEvents.hasEventHooks()) {
traceEvents.callEventHooks(context, RubyEvent.RESCUE, file, line + 1, null, context.getErrorInfo());
}
}

@JIT
public static void callTrace(ThreadContext context, Block selfBlock, RubyEvent event, String name, String filename, int line) {
TraceEventManager traceEvents = context.traceEvents;
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2415,6 +2415,12 @@ public void RuntimeHelperCall(RuntimeHelperCall runtimehelpercall) {
jvmMethod().invokeIRHelper("arrayLength", sig(int.class, RubyArray.class));
jvmStoreLocal(runtimehelpercall.getResult());
break;
case TRACE_RESCUE:
jvmMethod().loadContext();
jvmAdapter().ldc(((Stringable) runtimehelpercall.getOperands()[0]).getString());
visit(runtimehelpercall.getOperands()[1]);
jvmMethod().invokeIRHelper("traceRescue", sig(void.class, ThreadContext.class, String.class, int.class));
break;
default:
throw new NotCompilableException("Unknown IR runtime helper method: " + runtimehelpercall.getHelperMethod() + "; INSTR: " + this);
}
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/runtime/RubyEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public enum RubyEvent {
// A_CALL is CALL + B_CALL + C_CALL
A_CALL ("a_call"),
// A_RETURN is RETURN + B_RETURN + C_RETURN
A_RETURN ("a_return");
A_RETURN ("a_return"),
RESCUE ("rescue", false);

public static final Set<RubyEvent> ALL_EVENTS = Collections.synchronizedSet(EnumSet.allOf(RubyEvent.class));
public static final EnumSet ALL_EVENTS_ENUMSET = EnumSet.copyOf(RubyEvent.ALL_EVENTS);
Expand Down

0 comments on commit 11eb167

Please sign in to comment.