@@ -246,14 +246,34 @@ fn make_local_variable(
246246 entry : & jvmtiLocalVariableEntry ,
247247 index : jint ,
248248) -> Result < ( ) > {
249+ // JVMTI defines a local's valid range as half-open: [start_location, start_location +
250+ // length). At the end offset the slot may already have been reused for another variable,
251+ // so reading it yields either an unrelated value or a JVMTI error.
249252 let in_scope = location >= entry. start_location
250- && location <= entry. start_location + i64:: from ( entry. length ) ;
253+ && location < entry. start_location + i64:: from ( entry. length ) ;
251254
252255 // Building the name string only matters for slots we actually emit.
253256 let local = if in_scope {
254- let name = jni_env. new_string_utf ( entry. name ) ?;
255- let value = get_local_value ( jvmti_env, jni_env, capture, thread, depth, entry) ?;
256- jni_env. new_object_StringL ( capture. local_variable , capture. local_variable_ctor , name, value) ?
257+ // A slot that cannot be read is dropped on its own. Propagating would abort the whole
258+ // capture, so a single unreadable slot would cost every frame on the stack its locals.
259+ match get_local_value ( jvmti_env, jni_env, capture, thread, depth, entry) {
260+ Ok ( value) => {
261+ let name = jni_env. new_string_utf ( entry. name ) ?;
262+ jni_env. new_object_StringL (
263+ capture. local_variable ,
264+ capture. local_variable_ctor ,
265+ name,
266+ value,
267+ ) ?
268+ }
269+ Err ( e) => {
270+ debug ! ( "skipping local variable in slot {}: {}" , entry. slot, e) ;
271+ // The failing paths clear the pending exception themselves; this is belt and
272+ // braces, because leaving one pending would break the next JNI call.
273+ jni_env. clear_pending_exception ( ) ;
274+ ptr:: null_mut ( )
275+ }
276+ }
257277 } else {
258278 ptr:: null_mut ( )
259279 } ;
@@ -314,7 +334,15 @@ fn get_local_value(
314334 b'F' => {
315335 let mut val: jfloat = 0.0 ;
316336 jvmti_env. get_local_float ( thread, depth, entry. slot , & mut val) ?;
317- box_value ( jni_env, & capture. float_box , val)
337+ // C varargs promote float to double, and HotSpot's argument pusher reads a
338+ // jdouble for an F parameter. rustc's E0617 cannot catch this because the value
339+ // reaches the variadic call through a generic; passing the jfloat unpromoted
340+ // makes the JVM read 8 bytes where 4 were written.
341+ box_value (
342+ jni_env,
343+ & capture. float_box ,
344+ val as :: std:: os:: raw:: c_double ,
345+ )
318346 }
319347 b'D' => {
320348 let mut val: jdouble = 0.0 ;
0 commit comments