11/*
2- * Copyright (c) 1998, 2025 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 1998, 2026 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
@@ -171,18 +171,16 @@ initState(JNIEnv *env, jthread thread, StepRequest *step)
171171 * Initial values that may be changed below
172172 */
173173 step -> fromLine = -1 ;
174- step -> fromNative = JNI_FALSE ;
174+ step -> notifyFramePopFailed = JNI_FALSE ;
175175 step -> frameExited = JNI_FALSE ;
176176 step -> fromStackDepth = getFrameCount (thread );
177177
178178 if (step -> fromStackDepth <= 0 ) {
179179 /*
180- * If there are no stack frames, treat the step as though
181- * from a native frame. This is most likely to occur at the
182- * beginning of a debug session, right after the VM_INIT event,
183- * so we need to do something intelligent.
180+ * If there are no stack frames, there is nothing more to do here. If we are
181+ * doing a step INTO, initEvents() will enable stepping. Otherwise it is
182+ * not enabled because there is nothing to step OVER or OUT of.
184183 */
185- step -> fromNative = JNI_TRUE ;
186184 return JVMTI_ERROR_NONE ;
187185 }
188186
@@ -196,7 +194,13 @@ initState(JNIEnv *env, jthread thread, StepRequest *step)
196194 error = JVMTI_FUNC_PTR (gdata -> jvmti ,NotifyFramePop )
197195 (gdata -> jvmti , thread , 0 );
198196 if (error == JVMTI_ERROR_OPAQUE_FRAME ) {
199- step -> fromNative = JNI_TRUE ;
197+ // OPAQUE_FRAME doesn't always mean native method. It's rare that it doesn't, and
198+ // means that there is something about the frame's statw that prevents setting up
199+ // a NotifyFramePop. One example is a frame that is in the process of returning,
200+ // which can happen if we start single stepping after getting a MethodExit event.
201+ // In either any case, we need to be aware that there will be no FramePop event
202+ // when this frame exits.
203+ step -> notifyFramePopFailed = JNI_TRUE ;
200204 error = JVMTI_ERROR_NONE ;
201205 /* continue without error */
202206 } else if (error == JVMTI_ERROR_DUPLICATE ) {
@@ -761,31 +765,28 @@ initEvents(jthread thread, StepRequest *step)
761765 }
762766
763767 }
768+
764769 /*
765- * Initially enable stepping:
766- * 1) For step into, always
767- * 2) For step over, unless right after the VM_INIT.
768- * Enable stepping for STEP_MIN or STEP_LINE with or without line numbers.
769- * If the class is redefined then non EMCP methods may not have line
770- * number info. So enable line stepping for non line number so that it
771- * behaves like STEP_MIN/STEP_OVER.
772- * 3) For step out, only if stepping from native, except right after VM_INIT
773- *
774- * (right after VM_INIT, a step->over or out is identical to running
775- * forever)
770+ * Enable step events if necessary. Note that right after VM_INIT, a
771+ * step OVER or OUT is identical to running forever, so we only enable
772+ * step events if fromStackDepth > 0.
776773 */
777774 switch (step -> depth ) {
778775 case JDWP_STEP_DEPTH (INTO ):
779776 enableStepping (thread );
780777 break ;
781778 case JDWP_STEP_DEPTH (OVER ):
782- if (step -> fromStackDepth > 0 && !step -> fromNative ) {
779+ // We need to always enable for OVER (except right after VM_INIT).
780+ // If we are in a native method, that is the only way to find out
781+ // that we have returned to a java method.
782+ if (step -> fromStackDepth > 0 ) {
783783 enableStepping (thread );
784784 }
785785 break ;
786786 case JDWP_STEP_DEPTH (OUT ):
787- if (step -> fromNative &&
788- (step -> fromStackDepth > 0 )) {
787+ // We rely on the FramePop event to tell us when we exit the current frame.
788+ // If NotifyFramePop failed, then we need to enable stepping.
789+ if (step -> notifyFramePopFailed && (step -> fromStackDepth > 0 )) {
789790 enableStepping (thread );
790791 }
791792 break ;
0 commit comments