Skip to content

Commit 9b28363

Browse files
committed
Emit MethodParameters attribute to allow Java tools to show function argument names cleanly (credit: mipastgt for the idea!)
1 parent fac04c1 commit 9b28363

File tree

9 files changed

+120
-40
lines changed

9 files changed

+120
-40
lines changed

Tester.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
import glob
77

88
# A constant for the base classpath needed by both test types
9-
# NOTE: You may need to update the kotlin version in the future
10-
RUNTIME_CLASSPATH_BASE = "library/build/distributions/library-0.1.0/lib/library-0.1.0.jar:library/build/distributions/library-0.1.0/lib/kotlin-stdlib-2.1.20.jar"
9+
# NOTE: We may need to update the kotlin version in the future
10+
RUNTIME_CLASSPATH_BASE = os.pathsep.join([
11+
"library/build/distributions/library-0.1.0/lib/library-0.1.0.jar",
12+
"library/build/distributions/library-0.1.0/lib/kotlin-stdlib-2.1.20.jar",
13+
])
1114

1215
def read_from_file(path: str) -> str:
1316
with open(path, "r") as f:

proguard/default.pro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
}
55
-keepclassmembers class * implements * {
66
<methods>;
7-
}
7+
}
8+
9+
-keepattributes MethodParameters

src/lib.rs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -427,12 +427,12 @@ impl CodegenBackend for MyBackend {
427427
// Replace self references with the trait name as to match signatures with the one we've put on the interface
428428
if of_trait.is_some() {
429429
match oomir_function.signature.params.get(0) {
430-
Some(Type::Class(name)) => {
430+
Some((param_name, Type::Class(name))) => {
431431
if *name != ident {
432432
continue;
433433
}
434434
oomir_function.signature.params[0] =
435-
Type::Class(of_trait.clone().unwrap());
435+
(param_name.clone(), Type::Class(of_trait.clone().unwrap()));
436436

437437
// insert a Cast instruction to cast the trait (interface) object to the specific type of this class
438438
// this is needed because the method signature will be different than what MIR expects
@@ -456,12 +456,15 @@ impl CodegenBackend for MyBackend {
456456
);
457457
}
458458
}
459-
Some(Type::MutableReference(box Type::Class(name))) => {
459+
Some((param_name, Type::MutableReference(box Type::Class(name)))) => {
460460
if *name != ident {
461461
continue;
462462
}
463-
oomir_function.signature.params[0] = Type::MutableReference(
464-
Box::new(Type::Class(of_trait.clone().unwrap())),
463+
oomir_function.signature.params[0] = (
464+
param_name.clone(),
465+
Type::MutableReference(
466+
Box::new(Type::Class(of_trait.clone().unwrap())),
467+
),
465468
);
466469

467470
// insert a Cast instruction to cast the trait (interface) object to the specific type of this class
@@ -500,23 +503,23 @@ impl CodegenBackend for MyBackend {
500503
let mut new_params = vec![];
501504
// replace any MutableReference(Class(of_trait)) or Class(of_trait) with MutableReference(Class(ident))/ Class(ident)
502505
// this is just for the helper function, the actual method will use oomir_function.signature and we're only modifying helper_sig
503-
for arg in &oomir_function.signature.params {
504-
if let Type::MutableReference(box Type::Class(name)) = arg {
506+
for (param_name, param_ty) in &oomir_function.signature.params {
507+
if let Type::MutableReference(box Type::Class(name)) = param_ty {
505508
if *name == of_trait.clone().unwrap() {
506-
new_params.push(Type::MutableReference(Box::new(Type::Class(
509+
new_params.push((param_name.clone(), Type::MutableReference(Box::new(Type::Class(
507510
ident.clone(),
508-
))));
511+
)))));
509512
} else {
510-
new_params.push(arg.clone());
513+
new_params.push((param_name.clone(), param_ty.clone()));
511514
}
512-
} else if let Type::Class(name) = arg {
515+
} else if let Type::Class(name) = param_ty {
513516
if *name == of_trait.clone().unwrap() {
514-
new_params.push(Type::Class(ident.clone()));
517+
new_params.push((param_name.clone(), Type::Class(ident.clone())));
515518
} else {
516-
new_params.push(arg.clone());
519+
new_params.push((param_name.clone(), param_ty.clone()));
517520
}
518521
} else {
519-
new_params.push(arg.clone());
522+
new_params.push((param_name.clone(), param_ty.clone()));
520523
}
521524
}
522525
helper_sig.params = new_params;
@@ -531,12 +534,11 @@ impl CodegenBackend for MyBackend {
531534
let mut instructions = vec![];
532535

533536
let mut idx = 1;
534-
for arg in &oomir_function.signature.params {
537+
for (_param_name, param_ty) in &oomir_function.signature.params {
535538
let arg_name = format!("_{idx}");
536-
let arg_ty = arg.clone();
537539
let arg = Operand::Variable {
538540
name: arg_name.clone(),
539-
ty: arg_ty,
541+
ty: param_ty.clone(),
540542
};
541543
args.push(arg);
542544
idx += 1;
@@ -723,16 +725,22 @@ impl CodegenBackend for MyBackend {
723725
let data_types = &mut HashMap::new(); // Consider if this should be shared across loop iterations or functions
724726

725727
// Use skip_binder here too, as inputs/outputs are bound by the same binder as the fn_sig
726-
let params_oomir_ty: Vec<oomir::Type> = params_ty
728+
let params_oomir: Vec<(String, oomir::Type)> = params_ty
727729
.skip_binder()
728730
.iter()
729-
.map(|ty| lower1::types::ty_to_oomir_type(*ty, tcx, data_types))
731+
.enumerate()
732+
.map(|(i, ty)| {
733+
// For trait methods, we don't have MIR, so use generic names
734+
let param_name = format!("arg{}", i);
735+
let oomir_type = lower1::types::ty_to_oomir_type(*ty, tcx, data_types);
736+
(param_name, oomir_type)
737+
})
730738
.collect();
731739
let return_oomir_ty: oomir::Type =
732740
lower1::types::ty_to_oomir_type(return_ty.skip_binder(), tcx, data_types);
733741

734742
let mut signature = oomir::Signature {
735-
params: params_oomir_ty,
743+
params: params_oomir,
736744
ret: Box::new(return_oomir_ty.clone()),
737745
};
738746
signature.replace_class_in_signature("Self", &ident);
@@ -741,9 +749,8 @@ impl CodegenBackend for MyBackend {
741749

742750
let mut args = vec![];
743751
let mut idx = 1;
744-
for arg in signature.clone().params {
752+
for (_arg_name_from_sig, arg_ty) in signature.clone().params {
745753
let arg_name = format!("_{idx}");
746-
let arg_ty = arg.clone();
747754
let arg = Operand::Variable {
748755
name: arg_name.clone(),
749756
ty: arg_ty,

src/lower1.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,38 @@ pub fn mir_to_oomir<'tcx>(
7171

7272
let data_types = &mut HashMap::new();
7373

74-
let params_oomir_ty: Vec<oomir::Type> = params_ty
74+
let params_oomir: Vec<(String, oomir::Type)> = params_ty
7575
.skip_binder()
7676
.iter()
77-
.map(|ty| ty_to_oomir_type(*ty, tcx, data_types))
77+
.enumerate()
78+
.map(|(i, ty)| {
79+
// Arguments start at MIR local 1. The index `i` starts at 0.
80+
let local_index = rustc_middle::mir::Local::from_usize(i + 1);
81+
82+
// Try to find the parameter name from var_debug_info
83+
let param_name = mir.var_debug_info
84+
.iter()
85+
.find_map(|var_info| {
86+
// Check if this debug info entry is for our parameter
87+
if let rustc_middle::mir::VarDebugInfoContents::Place(place) = &var_info.value {
88+
if place.local == local_index && place.projection.is_empty() {
89+
return Some(var_info.name.to_string());
90+
}
91+
}
92+
None
93+
})
94+
.unwrap_or_else(|| format!("arg{}", i));
95+
96+
let oomir_type = ty_to_oomir_type(*ty, tcx, data_types);
97+
98+
// Return the (name, type) tuple
99+
(param_name, oomir_type)
100+
})
78101
.collect();
79102
let return_oomir_ty: oomir::Type = ty_to_oomir_type(return_ty.skip_binder(), tcx, data_types);
80103

81104
let mut signature = oomir::Signature {
82-
params: params_oomir_ty,
105+
params: params_oomir,
83106
ret: Box::new(return_oomir_ty.clone()), // Clone here to pass to convert_basic_block
84107
};
85108

@@ -91,9 +114,9 @@ pub fn mir_to_oomir<'tcx>(
91114
if fn_name == "main" {
92115
// manually override the signature to match the JVM main method
93116
signature = oomir::Signature {
94-
params: vec![oomir::Type::Array(Box::new(oomir::Type::Class(
117+
params: vec![("args".to_string(), oomir::Type::Array(Box::new(oomir::Type::Class(
95118
"java/lang/String".to_string(),
96-
)))],
119+
))))],
97120
ret: Box::new(oomir::Type::Void),
98121
};
99122
}
@@ -132,7 +155,7 @@ pub fn mir_to_oomir<'tcx>(
132155
// But we receive: local 1 = tuple containing all args
133156

134157
// Get the tuple parameter type (should be the first parameter in the signature)
135-
if let Some(tuple_param_ty) = signature.params.first() {
158+
if let Some((_tuple_param_name, tuple_param_ty)) = signature.params.first() {
136159
// Check if it's a tuple/struct type that we need to unpack
137160
if let oomir::Type::Class(class_name) = tuple_param_ty {
138161
// Get the data type definition to see its fields

src/lower2.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@ pub fn oomir_to_jvm_bytecode(
7878
attributes: Vec::new(),
7979
};
8080

81+
// Create MethodParameters attribute to preserve parameter names
82+
let mut parameters_for_attribute = Vec::new();
83+
for (name, _) in &function.signature.params {
84+
let name_index = main_cp.add_utf8(name)?;
85+
parameters_for_attribute.push(jvm::attributes::MethodParameter {
86+
name_index,
87+
access_flags: MethodAccessFlags::empty(), // No special flags
88+
});
89+
}
90+
let method_parameters_attribute_name_index = main_cp.add_utf8("MethodParameters")?;
91+
let method_parameters_attribute = Attribute::MethodParameters {
92+
name_index: method_parameters_attribute_name_index,
93+
parameters: parameters_for_attribute,
94+
};
95+
8196
let mut method = jvm::Method::default();
8297
// Assume static for now, adjust if instance methods are needed
8398
method.access_flags = MethodAccessFlags::PUBLIC | MethodAccessFlags::STATIC;
@@ -88,6 +103,11 @@ pub fn oomir_to_jvm_bytecode(
88103
method.name_index = name_index;
89104
method.descriptor_index = descriptor_index;
90105
method.attributes.push(code_attribute);
106+
// Add MethodParameters attribute (skip for constructors as they often have synthetic params)
107+
if function.name != "<init>" {
108+
println!("Adding MethodParameters attribute for function: {}: {:?}", function.name, method_parameters_attribute);
109+
method.attributes.push(method_parameters_attribute);
110+
}
91111

92112
methods.push(method);
93113
}

src/lower2/consts.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,11 @@ pub fn load_constant(
221221
// 2. Determine constructor parameter types and signature descriptor
222222
let param_types = params
223223
.iter()
224-
.map(Type::from_constant) // Get oomir::Type from each oomir::Constant
224+
.enumerate()
225+
.map(|(i, const_val)| {
226+
let ty = Type::from_constant(const_val);
227+
(format!("arg{}", i), ty)
228+
})
225229
.collect::<Vec<_>>();
226230

227231
let constructor_signature = Signature {

src/lower2/jvm_gen.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,37 @@ pub(super) fn create_data_type_classfile_for_class(
205205
attributes: Vec::new(),
206206
};
207207

208+
// Create MethodParameters attribute to preserve parameter names
209+
let mut parameters_for_attribute = Vec::new();
210+
for (name, _) in &function.signature.params {
211+
let name_index = class_file.constant_pool.add_utf8(name)?;
212+
parameters_for_attribute.push(jvm::attributes::MethodParameter {
213+
name_index,
214+
access_flags: MethodAccessFlags::empty(), // No special flags
215+
});
216+
}
217+
let method_parameters_attribute_name_index = class_file.constant_pool.add_utf8("MethodParameters")?;
218+
let method_parameters_attribute = Attribute::MethodParameters {
219+
name_index: method_parameters_attribute_name_index,
220+
parameters: parameters_for_attribute,
221+
};
222+
208223
let name_index = class_file.constant_pool.add_utf8(method_name)?;
209224
let descriptor_index = class_file
210225
.constant_pool
211226
.add_utf8(&function.signature.to_string())?;
212227

228+
let mut attributes_vec = vec![code_attribute];
229+
// Skip MethodParameters for constructors and getVariantIdx
230+
if method_name != "<init>" && method_name != "getVariantIdx" {
231+
attributes_vec.push(method_parameters_attribute);
232+
}
233+
213234
let jvm_method = jvm::Method {
214235
access_flags: MethodAccessFlags::PUBLIC,
215236
name_index,
216237
descriptor_index,
217-
attributes: vec![code_attribute],
238+
attributes: attributes_vec,
218239
};
219240

220241
class_file.methods.push(jvm_method);
@@ -256,7 +277,7 @@ pub(super) fn create_data_type_classfile_for_interface(
256277
for (method_name, signature) in methods {
257278
// Construct the descriptor: (param1_desc param2_desc ...)return_desc
258279
let mut descriptor = String::from("(");
259-
for param_type in &signature.params {
280+
for (_param_name, param_type) in &signature.params {
260281
descriptor.push_str(&param_type.to_jvm_descriptor());
261282
}
262283
descriptor.push(')');

src/lower2/translator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl<'a, 'cp> FunctionTranslator<'a, 'cp> {
7070
let param_translator_name = format!("param_{}", i);
7171
// The name used in the OOMIR body, corresponding to MIR convention (_1, _2, ...)
7272
let param_oomir_name = format!("_{}", i + 1);
73-
let param_ty = &oomir_func.signature.params[i];
73+
let (_param_name, param_ty) = &oomir_func.signature.params[i];
7474

7575
// Use assign_local to allocate the slot using the *translator* name first.
7676
// This ensures the slot is reserved and next_local_index advances correctly.

src/oomir.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ pub struct Function {
188188

189189
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190190
pub struct Signature {
191-
pub params: Vec<Type>,
191+
pub params: Vec<(String, Type)>,
192192
pub ret: Box<Type>,
193193
}
194194

@@ -197,7 +197,7 @@ impl Signature {
197197
/// in the signature's parameters and return type.
198198
pub fn replace_class_in_signature(&mut self, old_class_name: &str, new_class_name: &str) {
199199
// Replace in parameters
200-
for param_type in self.params.iter_mut() {
200+
for (_param_name, param_type) in self.params.iter_mut() {
201201
param_type.replace_class(old_class_name, new_class_name);
202202
}
203203

@@ -211,8 +211,8 @@ impl Signature {
211211
pub fn to_string(&self) -> String {
212212
let mut result = String::new();
213213
result.push('(');
214-
for param in &self.params {
215-
result.push_str(&param.to_jvm_descriptor());
214+
for (_param_name, param_type) in &self.params {
215+
result.push_str(&param_type.to_jvm_descriptor());
216216
}
217217
result.push(')');
218218
result.push_str(&self.ret.to_jvm_descriptor());
@@ -958,7 +958,7 @@ impl Type {
958958
impl fmt::Display for Signature {
959959
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
960960
write!(f, "(")?;
961-
for param_ty in &self.params {
961+
for (_param_name, param_ty) in &self.params {
962962
write!(f, "{}", param_ty.to_jvm_descriptor())?;
963963
}
964964
write!(f, "){}", self.ret.to_jvm_descriptor())

0 commit comments

Comments
 (0)