Skip to content

Commit 7c18290

Browse files
committed
fix(java): reject stale foreach source types
1 parent c0f4632 commit 7c18290

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

  • dexdec/src/language/java

dexdec/src/language/java/dex.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4588,6 +4588,14 @@ impl JavaDialect for DexJavaDialect {
45884588
};
45894589
let ty = self
45904590
.source_register_type(register)
4591+
// A source variable can span disjoint DEX register lifetimes. Do
4592+
// not let an earlier primitive lifetime type a reference-valued
4593+
// enhanced-for binding (or vice versa).
4594+
.filter(|source| {
4595+
let source_is_primitive = matches!(source, JavaType::Primitive(_));
4596+
!(source_is_primitive && binding_type.is_reference()
4597+
|| !source_is_primitive && binding_type.is_primitive())
4598+
})
45914599
.cloned()
45924600
.map(Ok)
45934601
.unwrap_or_else(|| self.source_type(binding_type))?;
@@ -4890,6 +4898,73 @@ impl JavaDialect for DexJavaDialect {
48904898
}
48914899
}
48924900

4901+
#[cfg(test)]
4902+
mod tests {
4903+
use std::collections::BTreeMap;
4904+
use std::sync::Arc;
4905+
4906+
use crate::ir::analysis::SourceTypeEnvironment;
4907+
use crate::ir::{ArgType, RegisterArg};
4908+
4909+
use super::{DexJavaDialect, JavaDialect, JavaMemberNames, JavaType};
4910+
4911+
#[test]
4912+
fn foreach_binding_ignores_a_reused_primitive_source_variable_type() {
4913+
let erased = ArgType::object("java/util/ArrayList");
4914+
let reference = JavaType::source_class("java.util.ArrayList");
4915+
let mut dialect = DexJavaDialect::new(
4916+
true,
4917+
None,
4918+
&[],
4919+
&[],
4920+
&SourceTypeEnvironment::default(),
4921+
BTreeMap::from([(erased.clone(), reference.clone())]),
4922+
Arc::new(JavaMemberNames::default()),
4923+
)
4924+
.expect("static dialect");
4925+
dialect.source_variable_types.insert(6, JavaType::int());
4926+
let register = RegisterArg {
4927+
reg_num: 1,
4928+
ty: erased,
4929+
ssa_version: Some(3),
4930+
code_var: Some(6),
4931+
};
4932+
4933+
let (ty, _) = JavaDialect::loop_variable(&mut dialect, &register)
4934+
.expect("reference-valued foreach binding");
4935+
4936+
assert_eq!(ty, reference);
4937+
}
4938+
4939+
#[test]
4940+
fn foreach_binding_keeps_a_reference_source_type_over_object_erasure() {
4941+
let erased = ArgType::object("java/lang/Object");
4942+
let reference = JavaType::source_class("example.Element");
4943+
let mut dialect = DexJavaDialect::new(
4944+
true,
4945+
None,
4946+
&[],
4947+
&[],
4948+
&SourceTypeEnvironment::default(),
4949+
BTreeMap::from([(erased.clone(), JavaType::source_class("java.lang.Object"))]),
4950+
Arc::new(JavaMemberNames::default()),
4951+
)
4952+
.expect("static dialect");
4953+
dialect.source_variable_types.insert(6, reference.clone());
4954+
let register = RegisterArg {
4955+
reg_num: 1,
4956+
ty: erased,
4957+
ssa_version: Some(3),
4958+
code_var: Some(6),
4959+
};
4960+
4961+
let (ty, _) =
4962+
JavaDialect::loop_variable(&mut dialect, &register).expect("reference source type");
4963+
4964+
assert_eq!(ty, reference);
4965+
}
4966+
}
4967+
48934968
#[derive(Default)]
48944969
struct SourceBindings {
48954970
registers: Vec<(SemanticBindingKind, RegisterArg)>,

0 commit comments

Comments
 (0)