1use std::collections::{HashMap, HashSet};
14
15use cure_engine::analysis::{subtree_metrics, TreeMetrics};
16use cure_engine::lang::ReassocOutcome;
17use cure_engine::Lang;
18pub use cure_tree::ChildList;
19use cure_tree::{EventStore, NameTable};
20
21pub use cure_engine::kind::{BinOp, LitRef, NodeKind, UnOp};
23pub use cure_engine::Effect;
24
25#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
31pub struct JavaId(pub u32);
32impl Default for JavaId {
33 fn default() -> Self {
34 JavaId(u32::MAX)
35 }
36}
37
38#[derive(Clone, PartialEq, Eq, Debug)]
40pub enum JType {
41 Bool,
42 Byte,
43 Short,
44 Int,
45 Long,
46 Char,
47 Float,
48 Double,
49 Void,
50 Ref(String),
52 Array(Box<JType>),
53 Var,
55}
56
57impl JType {
58 pub fn is_ref(&self) -> bool {
59 matches!(self, JType::Ref(_) | JType::Array(_))
60 }
61 pub fn is_integral(&self) -> bool {
62 matches!(
63 self,
64 JType::Byte | JType::Short | JType::Int | JType::Long | JType::Char
65 )
66 }
67 pub fn is_bool(&self) -> bool {
68 matches!(self, JType::Bool)
69 }
70}
71
72#[derive(Clone, Copy, PartialEq, Debug)]
74pub enum NumVal {
75 Int(i64),
76 Long(i64),
77 Float(f64),
78 Double(f64),
79}
80
81#[derive(Clone, PartialEq, Debug)]
83pub enum Lit {
84 Bool(bool),
85 Int(i64),
86 Long(i64),
87 Float(f64),
88 Double(f64),
89 Char(char),
90 Str(String),
91 TextBlock(String),
93 NumRaw { text: String, val: NumVal },
95 Null,
96}
97
98#[derive(Clone, PartialEq, Debug)]
100pub enum NodeData {
101 Block,
103 Group,
107 Empty,
108 Yield,
110
111 ExprStmt,
112 VarDecl { name: String, ty: JType, final_mod: bool },
113 Assign { op: Option<BinOp> },
114 If,
115 While,
116 DoWhile,
117 For { inits: u8, has_cond: bool, steps: u8 },
118 ForEach { name: String, ty: JType },
119 Return,
120 Break { label: Option<String> },
121 Continue { label: Option<String> },
122 Throw,
123 Try,
125 Catch { ty_raw: String, name: String },
127 Synchronized,
129 Switch,
131 Case { labels: u16, is_default: bool, arrow: bool },
133 Label { name: String },
135 Assert,
137 Raw { text: String },
139
140 Binary { op: BinOp },
142 Unary { op: UnOp },
143 Call,
145 Member { name: Sym },
147 Index,
149 Cast { ty: JType },
150 Paren,
151 Ternary,
152 New { ty: JType, anon_raw: Option<String> },
154 NewArray { ty: JType, dims: u16, sized: u16 },
157 ArrayLit,
158 VarRef { name: Sym },
159 Literal(Lit),
160 This,
161 Super,
163 InstanceOf { ty: JType, bind: Option<String> },
165 Lambda { params_raw: String },
167 MethodRef { name: Sym },
169}
170
171#[derive(Clone, PartialEq, Debug)]
172pub struct Node {
173 pub data: NodeData,
174 pub children: ChildList<JavaId>,
175}
176
177#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
182pub struct Sym(pub u32);
183
184fn lit_value_eq(x: &Lit, y: &Lit) -> bool {
186 fn r(l: &Lit) -> Option<cure_engine::kind::LitRef<'_>> {
187 Some(match l {
188 Lit::Bool(b) => LitRef::Bool(*b),
189 Lit::Int(v) => LitRef::Int(*v),
190 Lit::Long(v) => LitRef::Long(*v),
191 Lit::Float(v) => LitRef::Float(*v),
192 Lit::Double(v) => LitRef::Double(*v),
193 Lit::Char(c) => LitRef::Char(*c),
194 Lit::Str(s) => LitRef::Str(s.as_str()),
195 Lit::NumRaw { val, .. } => match val {
196 NumVal::Int(v) => LitRef::Int(*v),
197 NumVal::Long(v) => LitRef::Long(*v),
198 NumVal::Float(v) => LitRef::Float(*v),
199 NumVal::Double(v) => LitRef::Double(*v),
200 },
201 Lit::Null => LitRef::Null,
202 Lit::TextBlock(_) => return None,
203 })
204 }
205 match (r(x), r(y)) {
208 (Some(rx), Some(ry)) => {
209 if let (LitRef::Double(a), LitRef::Double(b)) = (rx, ry) {
210 return a == b && a.is_sign_negative() == b.is_sign_negative();
211 }
212 if let (LitRef::Float(a), LitRef::Float(b)) = (rx, ry) {
213 return a == b && a.is_sign_negative() == b.is_sign_negative();
214 }
215 rx == ry
216 }
217 _ => false,
218 }
219}
220
221#[derive(Clone, Debug)]
223pub struct NoopCandidate {
224 pub params: Vec<JType>,
226 pub noop: bool,
228}
229
230
231#[derive(Default, Debug)]
233pub struct JavaAst {
234 pub(crate) nodes: Vec<Node>,
235 effect_cache: Vec<Option<Effect>>,
238 var_types: Vec<Option<JType>>,
241 param_scope: Vec<(String, JType)>,
243 pub const_fields: HashMap<String, JavaId>,
246 pub const_scalars: HashSet<String>,
250 pub noop_private_methods: HashMap<String, HashMap<usize, Vec<NoopCandidate>>>,
256 pub field_types: HashMap<String, JType>,
259 pub unit_shadows_lang: bool,
264 pub raw_generic_names: std::collections::HashSet<String>,
268 pub final_fields: HashSet<String>,
272 pub inline_methods: HashMap<String, (Vec<String>, JavaId, String)>,
278 string_identity: std::cell::Cell<Option<(JavaId, bool)>>,
281 pub unit_string_identity: bool,
286 names: NameTable,
290 events: EventStore<JavaId, u32>,
294 prepared_root: Option<JavaId>,
299}
300
301#[derive(Clone, PartialEq, Debug)]
307pub struct Param {
308 pub mods: String,
310 pub ty: JType,
311 pub varargs: bool,
313 pub name: String,
314}
315
316#[derive(Clone, PartialEq, Debug)]
318pub struct Declarator {
319 pub name: String,
320 pub extra_dims: u16,
322 pub init: Option<JavaId>,
324}
325
326#[derive(Clone, PartialEq, Debug)]
328pub enum Member {
329 Field { mods: String, ty: JType, declarators: Vec<Declarator> },
330 Method {
331 mods: String,
332 ty_params: String,
334 ret: JType,
335 name: String,
336 params: Vec<Param>,
337 throws: Vec<String>,
338 body: Option<JavaId>,
340 },
341 Constructor {
342 mods: String,
343 ty_params: String,
344 name: String,
345 params: Vec<Param>,
346 throws: Vec<String>,
347 body: Option<JavaId>,
348 compact: bool,
350 },
351 Initializer { is_static: bool, body: JavaId },
353 Type(Box<TypeDecl>),
355 Raw(String),
357}
358
359#[derive(Clone, Copy, PartialEq, Eq, Debug)]
360pub enum TypeKind {
361 Class,
362 Interface,
363 Enum,
364 Record,
365 Annotation,
366}
367
368#[derive(Clone, PartialEq, Debug)]
370pub struct TypeDecl {
371 pub kind: TypeKind,
372 pub mods: String,
374 pub name: String,
375 pub ty_params: String,
377 pub header: String,
379 pub extends: Vec<String>,
380 pub implements: Vec<String>,
381 pub permits: Vec<String>,
383 pub enum_constants: Vec<String>,
385 pub members: Vec<Member>,
386}
387
388#[derive(Clone, PartialEq, Debug, Default)]
390pub struct CompilationUnit {
391 pub package: Option<String>,
393 pub package_annotations: String,
396 pub imports: Vec<String>,
398 pub types: Vec<TypeDecl>,
399 pub raws: Vec<String>,
401}
402
403#[derive(Clone, PartialEq, Eq, Debug)]
405pub struct ParseError {
406 pub line: usize,
407 pub col: usize,
408 pub message: String,
409}
410
411impl JavaAst {
416 pub fn new() -> Self {
417 Self::default()
418 }
419
420 pub fn clone_node(&mut self, id: JavaId, children: Vec<JavaId>) -> JavaId {
422 let data = self.nodes[id.0 as usize].data.clone();
423 self.push(data, children)
424 }
425
426 fn push(&mut self, data: NodeData, children: Vec<JavaId>) -> JavaId {
427 let id = JavaId(self.nodes.len() as u32);
428 self.nodes.push(Node { data, children: ChildList::from_vec(children) });
429 id
430 }
431
432 pub fn node(&self, id: JavaId) -> &Node {
433 &self.nodes[id.0 as usize]
434 }
435 pub fn data(&self, id: JavaId) -> &NodeData {
436 &self.node(id).data
437 }
438
439 pub fn kind(&self, id: JavaId) -> NodeKind {
441 match &self.nodes[id.0 as usize].data {
442 NodeData::Block | NodeData::Group => NodeKind::Block,
443 NodeData::Empty => NodeKind::Empty,
444 NodeData::Yield => NodeKind::Yield,
445 NodeData::ExprStmt => NodeKind::ExprStmt,
446 NodeData::VarDecl { .. } => NodeKind::VarDecl,
447 NodeData::Assign { .. } => NodeKind::Assign,
448 NodeData::If => NodeKind::If,
449 NodeData::While => NodeKind::While,
450 NodeData::DoWhile => NodeKind::DoWhile,
451 NodeData::For { .. } => NodeKind::For,
452 NodeData::ForEach { .. } => NodeKind::ForEach,
453 NodeData::Return => NodeKind::Return,
454 NodeData::Break { .. } => NodeKind::Break,
455 NodeData::Continue { .. } => NodeKind::Continue,
456 NodeData::Throw => NodeKind::Throw,
457 NodeData::Try => NodeKind::Try,
458 NodeData::Catch { .. } => NodeKind::Catch,
459 NodeData::Synchronized => NodeKind::Synchronized,
460 NodeData::Switch => NodeKind::Switch,
461 NodeData::Case { .. } => NodeKind::Case,
462 NodeData::Label { .. } => NodeKind::Label,
463 NodeData::Assert => NodeKind::Assert,
464 NodeData::Raw { .. } => NodeKind::Raw,
465 NodeData::Binary { .. } => NodeKind::Binary,
466 NodeData::Unary { .. } => NodeKind::Unary,
467 NodeData::Call => NodeKind::Call,
468 NodeData::Member { .. } => NodeKind::Member,
469 NodeData::Index => NodeKind::Index,
470 NodeData::Cast { .. } => NodeKind::Cast,
471 NodeData::Paren => NodeKind::Paren,
472 NodeData::Ternary => NodeKind::Ternary,
473 NodeData::New { .. } => NodeKind::New,
474 NodeData::NewArray { .. } => NodeKind::NewArray,
475 NodeData::ArrayLit => NodeKind::ArrayLit,
476 NodeData::VarRef { .. } => NodeKind::VarRef,
477 NodeData::Literal(_) => NodeKind::Literal,
478 NodeData::This => NodeKind::This,
479 NodeData::Super => NodeKind::This,
480 NodeData::InstanceOf { .. } => NodeKind::InstanceOf,
481 NodeData::Lambda { .. } => NodeKind::Lambda,
482 NodeData::MethodRef { .. } => NodeKind::MethodRef,
483 }
484 }
485 pub fn node_count(&self) -> usize {
487 self.nodes.len()
488 }
489
490 pub fn children(&self, id: JavaId) -> &[JavaId] {
491 self.nodes[id.0 as usize].children.as_slice()
492 }
493
494 pub fn block(&mut self, stmts: Vec<JavaId>) -> JavaId {
497 self.push(NodeData::Block, stmts)
498 }
499 pub fn empty(&mut self) -> JavaId {
500 self.push(NodeData::Empty, vec![])
501 }
502 pub fn expr_stmt(&mut self, e: JavaId) -> JavaId {
503 self.push(NodeData::ExprStmt, vec![e])
504 }
505 pub fn var_decl(&mut self, name: &str, ty: JType, init: Option<JavaId>) -> JavaId {
506 self.var_decl_final(name, ty, init, false)
507 }
508 pub fn var_decl_final(
512 &mut self,
513 name: &str,
514 ty: JType,
515 init: Option<JavaId>,
516 final_mod: bool,
517 ) -> JavaId {
518 self.push(
519 NodeData::VarDecl {
520 name: name.into(),
521 ty,
522 final_mod,
523 },
524 init.into_iter().collect(),
525 )
526 }
527 pub fn assign(&mut self, target: JavaId, value: JavaId) -> JavaId {
528 self.push(NodeData::Assign { op: None }, vec![target, value])
529 }
530 pub fn assign_op(&mut self, op: BinOp, target: JavaId, value: JavaId) -> JavaId {
531 self.push(NodeData::Assign { op: Some(op) }, vec![target, value])
532 }
533 pub fn if_(&mut self, cond: JavaId, then: JavaId, els: Option<JavaId>) -> JavaId {
534 let mut ch = vec![cond, then];
535 ch.extend(els);
536 self.push(NodeData::If, ch)
537 }
538 pub fn while_(&mut self, cond: JavaId, body: JavaId) -> JavaId {
539 self.push(NodeData::While, vec![cond, body])
540 }
541 pub fn do_while(&mut self, body: JavaId, cond: JavaId) -> JavaId {
542 self.push(NodeData::DoWhile, vec![body, cond])
543 }
544 pub fn for_(
545 &mut self,
546 inits: Vec<JavaId>,
547 cond: Option<JavaId>,
548 steps: Vec<JavaId>,
549 body: JavaId,
550 ) -> JavaId {
551 let ni = inits.len();
552 let ns = steps.len();
553 let mut ch = inits;
554 let has_cond = cond.is_some();
555 ch.extend(cond);
556 ch.extend(steps);
557 ch.push(body);
558 self.push(
559 NodeData::For {
560 inits: ni as u8,
561 has_cond,
562 steps: ns as u8,
563 },
564 ch,
565 )
566 }
567 pub fn for_each(&mut self, name: &str, ty: JType, iterable: JavaId, body: JavaId) -> JavaId {
568 self.push(
569 NodeData::ForEach {
570 name: name.into(),
571 ty,
572 },
573 vec![iterable, body],
574 )
575 }
576 pub fn yield_(&mut self, expr: JavaId) -> JavaId {
577 self.push(NodeData::Yield, vec![expr])
578 }
579 pub fn ret(&mut self, value: Option<JavaId>) -> JavaId {
580 self.push(NodeData::Return, value.into_iter().collect())
581 }
582 pub fn break_(&mut self, label: Option<&str>) -> JavaId {
583 self.push(
584 NodeData::Break {
585 label: label.map(Into::into),
586 },
587 vec![],
588 )
589 }
590 pub fn continue_(&mut self, label: Option<&str>) -> JavaId {
591 self.push(
592 NodeData::Continue {
593 label: label.map(Into::into),
594 },
595 vec![],
596 )
597 }
598 pub fn throw(&mut self, e: JavaId) -> JavaId {
599 self.push(NodeData::Throw, vec![e])
600 }
601 pub fn try_(
602 &mut self,
603 resources: Vec<JavaId>,
604 try_block: JavaId,
605 catches: Vec<JavaId>,
606 finally: Option<JavaId>,
607 ) -> JavaId {
608 let mut ch = resources;
609 ch.push(try_block);
610 ch.extend(catches);
611 ch.extend(finally);
612 self.push(NodeData::Try, ch)
613 }
614 pub fn catch_(&mut self, ty_raw: &str, name: &str, block: JavaId) -> JavaId {
615 self.push(
616 NodeData::Catch {
617 ty_raw: ty_raw.into(),
618 name: name.into(),
619 },
620 vec![block],
621 )
622 }
623 pub fn synchronized(&mut self, lock: JavaId, block: JavaId) -> JavaId {
624 self.push(NodeData::Synchronized, vec![lock, block])
625 }
626 pub fn switch_(&mut self, subject: JavaId, cases: Vec<JavaId>) -> JavaId {
627 let mut ch = vec![subject];
628 ch.extend(cases);
629 self.push(NodeData::Switch, ch)
630 }
631 pub fn case_(
632 &mut self,
633 labels: Vec<JavaId>,
634 is_default: bool,
635 arrow: bool,
636 stmts: Vec<JavaId>,
637 ) -> JavaId {
638 let n = labels.len() as u16;
639 let mut ch = labels;
640 ch.extend(stmts);
641 self.push(
642 NodeData::Case {
643 labels: n,
644 is_default,
645 arrow,
646 },
647 ch,
648 )
649 }
650 pub fn label(&mut self, name: &str, stmt: JavaId) -> JavaId {
651 self.push(NodeData::Label { name: name.into() }, vec![stmt])
652 }
653 pub fn assert_(&mut self, cond: JavaId, msg: Option<JavaId>) -> JavaId {
654 let mut ch = vec![cond];
655 ch.extend(msg);
656 self.push(NodeData::Assert, ch)
657 }
658 pub fn raw(&mut self, text: &str) -> JavaId {
659 self.push(NodeData::Raw { text: text.into() }, vec![])
660 }
661
662 pub fn bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> JavaId {
665 self.push(NodeData::Binary { op }, vec![l, r])
666 }
667 pub fn un(&mut self, op: UnOp, e: JavaId) -> JavaId {
668 self.push(NodeData::Unary { op }, vec![e])
669 }
670 pub fn call(&mut self, callee: JavaId, args: Vec<JavaId>) -> JavaId {
671 let mut ch = vec![callee];
672 ch.extend(args);
673 self.push(NodeData::Call, ch)
674 }
675 pub fn member(&mut self, obj: JavaId, name: &str) -> JavaId {
676 let sym = self.intern_sym(name);
677 self.push(NodeData::Member { name: sym }, vec![obj])
678 }
679 pub fn index(&mut self, arr: JavaId, idx: JavaId) -> JavaId {
680 self.push(NodeData::Index, vec![arr, idx])
681 }
682 pub fn cast(&mut self, ty: JType, e: JavaId) -> JavaId {
683 self.push(NodeData::Cast { ty }, vec![e])
684 }
685 pub fn paren(&mut self, e: JavaId) -> JavaId {
686 self.push(NodeData::Paren, vec![e])
687 }
688 pub fn ternary(&mut self, c: JavaId, a: JavaId, b: JavaId) -> JavaId {
689 self.push(NodeData::Ternary, vec![c, a, b])
690 }
691 pub fn new_(&mut self, ty: JType, args: Vec<JavaId>) -> JavaId {
692 self.push(NodeData::New { ty, anon_raw: None }, args)
693 }
694 pub fn new_anon(&mut self, ty: JType, args: Vec<JavaId>, body_raw: String) -> JavaId {
695 self.push(
696 NodeData::New {
697 ty,
698 anon_raw: Some(body_raw),
699 },
700 args,
701 )
702 }
703 pub fn new_array(
704 &mut self,
705 ty: JType,
706 dims: u16,
707 sized: u16,
708 sizes: Vec<JavaId>,
709 init: Option<JavaId>,
710 ) -> JavaId {
711 let mut ch = sizes;
712 ch.extend(init);
713 self.push(NodeData::NewArray { ty, dims, sized }, ch)
714 }
715 pub fn group(&mut self, stmts: Vec<JavaId>) -> JavaId {
717 self.push(NodeData::Group, stmts)
718 }
719 pub fn array_lit(&mut self, elems: Vec<JavaId>) -> JavaId {
720 self.push(NodeData::ArrayLit, elems)
721 }
722 pub fn var(&mut self, name: &str) -> JavaId {
723 let sym = self.intern_sym(name);
724 self.push(NodeData::VarRef { name: sym }, vec![])
725 }
726 pub fn lit(&mut self, l: Lit) -> JavaId {
727 self.push(NodeData::Literal(l), vec![])
728 }
729 fn build_int_impl(&mut self, v: i64, long: bool) -> JavaId {
730 if long {
731 self.lit(Lit::Long(v))
732 } else {
733 self.lit(Lit::Int(v))
734 }
735 }
736 pub fn this(&mut self) -> JavaId {
737 self.push(NodeData::This, vec![])
738 }
739 pub fn super_(&mut self) -> JavaId {
740 self.push(NodeData::Super, vec![])
741 }
742 pub fn instance_of(&mut self, e: JavaId, ty: JType, bind: Option<&str>) -> JavaId {
743 self.push(
744 NodeData::InstanceOf {
745 ty,
746 bind: bind.map(Into::into),
747 },
748 vec![e],
749 )
750 }
751 pub fn lambda(&mut self, params_raw: &str, body: JavaId) -> JavaId {
752 self.push(
753 NodeData::Lambda {
754 params_raw: params_raw.into(),
755 },
756 vec![body],
757 )
758 }
759 pub fn method_ref(&mut self, receiver: JavaId, name: &str) -> JavaId {
760 let sym = self.intern_sym(name);
761 self.push(NodeData::MethodRef { name: sym }, vec![receiver])
762 }
763
764 pub fn method_call(&mut self, obj: JavaId, name: &str, args: Vec<JavaId>) -> JavaId {
768 let m = self.member(obj, name);
769 self.call(m, args)
770 }
771 pub fn plain_call(&mut self, name: &str, args: Vec<JavaId>) -> JavaId {
773 let f = self.var(name);
774 self.call(f, args)
775 }
776
777 pub fn var_type(&self, id: JavaId) -> Option<&JType> {
779 self.var_types.get(id.0 as usize).and_then(|t| t.as_ref())
780 }
781
782 pub fn any_string_identity(&self, roots: &[JavaId]) -> bool {
788 roots.iter().any(|&r| {
789 let hit = Self::scan_string_identity(self, r);
790 if hit {
791 self.string_identity.set(Some((r, true)));
792 }
793 hit
794 })
795 }
796
797 pub fn has_string_identity_compare(&self, root: JavaId) -> bool {
798 if let Some((r, v)) = self.string_identity.get() {
799 if r == root {
800 return v;
801 }
802 }
803 let v = Self::scan_string_identity(self, root);
804 self.string_identity.set(Some((root, v)));
805 v
806 }
807
808 fn scan_string_identity(&self, root: JavaId) -> bool {
809 let mut stack = vec![root];
810 while let Some(id) = stack.pop() {
811 if let NodeData::Binary { op: BinOp::Eq | BinOp::Ne } = self.data(id) {
812 let ch = self.children(id);
813 if ch.len() == 2
814 && self.expr_maybe_string(ch[0])
815 && self.expr_maybe_string(ch[1])
816 {
817 return true;
818 }
819 }
820 for &c in self.children(id) {
821 stack.push(c);
822 }
823 }
824 false
825 }
826
827 fn expr_maybe_string(&self, id: JavaId) -> bool {
829 match self.data(id) {
830 NodeData::Literal(Lit::Str(_)) => true,
831 NodeData::Literal(_) | NodeData::This | NodeData::Super
833 | NodeData::NewArray { .. } | NodeData::ArrayLit | NodeData::InstanceOf { .. } => false,
834 NodeData::VarRef { .. } => self.var_type(id).map_or(true, Self::type_maybe_string),
835 NodeData::Cast { ty } => Self::type_maybe_string(ty),
836 NodeData::Paren => self
837 .children(id)
838 .first()
839 .map_or(true, |&c| self.expr_maybe_string(c)),
840 NodeData::Binary { op } if *op == BinOp::Add => {
842 self.children(id).iter().any(|&c| self.expr_maybe_string(c))
843 }
844 NodeData::Binary { .. } => false,
845 NodeData::Ternary => self
846 .children(id)
847 .get(1..)
848 .map_or(true, |cs| cs.iter().any(|&c| self.expr_maybe_string(c))),
849 _ => true,
851 }
852 }
853
854 fn type_maybe_string(t: &JType) -> bool {
856 match t {
857 JType::Bool | JType::Byte | JType::Short | JType::Int | JType::Long
858 | JType::Char | JType::Float | JType::Double | JType::Void
859 | JType::Array(_) => false, JType::Var => true, JType::Ref(n) => {
862 let base = n.split('<').next().unwrap_or(n).trim();
863 let last = base.rsplit('.').next().unwrap_or(base);
864 matches!(
865 last,
866 "String" | "Object" | "CharSequence" | "Comparable" | "Serializable"
867 ) || n.contains('<') || (last.len() <= 2 && last.chars().next().is_some_and(|c| c.is_uppercase()))
870 }
871 }
872 }
873
874
875 pub fn set_param_scope(&mut self, params: &[(String, JType)]) {
877 self.param_scope = params.to_vec();
878 }
879
880 pub fn is_param_name(&self, name: &str) -> bool {
883 self.param_scope.iter().any(|(n, _)| n == name)
884 }
885
886 pub fn name_of_key(&self, k: u32) -> Option<String> {
888 Some(self.names.name(k).to_string())
889 }
890 pub fn clear_param_scope(&mut self) {
891 self.param_scope.clear();
892 }
893}
894
895fn type_base_name(n: &str) -> &str {
903 let no_args = match n.find('<') {
904 Some(i) => &n[..i],
905 None => n,
906 };
907 no_args.rsplit('.').next().unwrap_or(no_args)
908}
909
910fn fold_i64(op: BinOp, a: i64, b: i64) -> Option<i64> {
917 use BinOp::*;
918 Some(match op {
919 Add => a.wrapping_add(b),
920 Sub => a.wrapping_sub(b),
921 Mul => a.wrapping_mul(b),
922 Div => {
923 if b == 0 {
924 return None;
925 }
926 a.wrapping_div(b)
927 }
928 Rem => {
929 if b == 0 {
930 return None;
931 }
932 a.wrapping_rem(b)
933 }
934 Shl => a.wrapping_shl((b as u64 & 63) as u32),
935 Shr => a.wrapping_shr((b as u64 & 63) as u32),
936 UShr => ((a as u64).wrapping_shr((b as u64 & 63) as u32)) as i64,
937 BitAnd => a & b,
938 BitXor => a ^ b,
939 BitOr => a | b,
940 _ => return None,
941 })
942}
943
944fn fold_i32(op: BinOp, a: i64, b: i64) -> Option<i64> {
946 use BinOp::*;
947 let (x, y) = (a as i32, b as i32);
948 Some(match op {
949 Add => x.wrapping_add(y) as i64,
950 Sub => x.wrapping_sub(y) as i64,
951 Mul => x.wrapping_mul(y) as i64,
952 Div => {
953 if y == 0 {
954 return None;
955 }
956 x.wrapping_div(y) as i64
957 }
958 Rem => {
959 if y == 0 {
960 return None;
961 }
962 x.wrapping_rem(y) as i64
963 }
964 Shl => x.wrapping_shl((y as u32) & 31) as i64,
965 Shr => x.wrapping_shr((y as u32) & 31) as i64,
966 UShr => (x as u32).wrapping_shr((y as u32) & 31) as i64,
967 BitAnd => (x & y) as i64,
968 BitXor => (x ^ y) as i64,
969 BitOr => (x | y) as i64,
970 _ => return None,
971 })
972}
973
974fn cmp_i64(op: BinOp, x: i64, y: i64) -> Option<bool> {
976 use BinOp::*;
977 Some(match op {
978 Lt => x < y,
979 Le => x <= y,
980 Gt => x > y,
981 Ge => x >= y,
982 Eq => x == y,
983 Ne => x != y,
984 _ => return None,
985 })
986}
987
988impl Lang for JavaAst {
989 type Id = JavaId;
990 type NameKey = u32;
991
992 fn kind(&self, id: JavaId) -> NodeKind {
993 JavaAst::kind(self, id)
994 }
995
996 fn children(&self, id: JavaId) -> &[JavaId] {
997 JavaAst::children(self, id)
998 }
999
1000 fn set_child(&mut self, parent: JavaId, index: usize, new: JavaId) {
1001 self.nodes[parent.0 as usize].children.set(index, new);
1002 }
1003 fn remove_child(&mut self, parent: JavaId, index: usize) {
1004 self.nodes[parent.0 as usize].children.remove(index);
1005 }
1006 fn splice(&mut self, node: JavaId, index: usize, remove: usize, insert: Vec<JavaId>) {
1007 self.nodes[node.0 as usize].children.splice(index, remove, insert);
1008 }
1009
1010 fn build_return(&mut self, value: Option<JavaId>) -> JavaId {
1011 self.ret(value)
1012 }
1013 fn build_block(&mut self, stmts: Vec<JavaId>) -> JavaId {
1014 self.block(stmts)
1015 }
1016 fn build_unary(&mut self, op: UnOp, operand: JavaId) -> JavaId {
1017 self.un(op, operand)
1018 }
1019 fn build_bool(&mut self, v: bool) -> JavaId {
1020 self.lit(Lit::Bool(v))
1021 }
1022 fn build_int(&mut self, v: i64, long: bool) -> JavaId {
1023 self.build_int_impl(v, long)
1024 }
1025 fn build_str(&mut self, s: &str) -> JavaId {
1026 self.lit(Lit::Str(s.to_string()))
1027 }
1028 fn build_char(&mut self, c: char) -> JavaId {
1029 self.lit(Lit::Char(c))
1030 }
1031 fn build_bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> JavaId {
1032 self.bin(op, l, r)
1033 }
1034 fn build_ternary(&mut self, c: JavaId, a: JavaId, b: JavaId) -> JavaId {
1035 self.ternary(c, a, b)
1036 }
1037 fn build_assign(&mut self, target: JavaId, value: JavaId) -> JavaId {
1038 self.assign(target, value)
1039 }
1040
1041 fn fold_lit_bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> Option<JavaId> {
1044 let ll = self.literal(l)?;
1045 let rr = self.literal(r)?;
1046 if op == BinOp::Add {
1050 let str_of = |x: LitRef<'_>| -> Option<String> {
1051 match x {
1052 LitRef::Str(s) => Some(s.to_string()),
1053 LitRef::Char(c) => Some(c.to_string()),
1054 LitRef::Int(v) => Some(v.to_string()),
1055 LitRef::Long(v) => Some(v.to_string()),
1056 LitRef::Bool(b) => Some(b.to_string()),
1057 _ => None,
1058 }
1059 };
1060 if matches!(ll, LitRef::Str(_)) || matches!(rr, LitRef::Str(_)) {
1061 if let (Some(a), Some(b)) = (str_of(ll), str_of(rr)) {
1062 return Some(self.build_str(&format!("{a}{b}")));
1063 }
1064 }
1065 }
1066 let int_of = |x: LitRef<'_>| -> Option<i64> {
1069 match x {
1070 LitRef::Int(v) | LitRef::Long(v) => Some(v),
1072 LitRef::Char(c) => Some(c as u32 as i64),
1073 _ => None,
1074 }
1075 };
1076 let (a, b) = (int_of(ll)?, int_of(rr)?);
1077 let wide = matches!(ll, LitRef::Long(_)) || matches!(rr, LitRef::Long(_));
1078 let v = if wide { fold_i64(op, a, b) } else { fold_i32(op, a, b) }?;
1079 Some(self.build_int(v, wide))
1080 }
1081
1082 fn fold_lit_cmp(&mut self, op: BinOp, l: JavaId, r: JavaId) -> Option<bool> {
1083 let ll = self.literal(l)?;
1084 let rr = self.literal(r)?;
1085 let int_of = |x: LitRef<'_>| -> Option<i64> {
1087 match x {
1088 LitRef::Int(v) | LitRef::Long(v) => Some(v),
1089 LitRef::Char(c) => Some(c as u32 as i64),
1090 _ => None,
1091 }
1092 };
1093 if let (Some(x), Some(y)) = (int_of(ll), int_of(rr)) {
1094 return cmp_i64(op, x, y);
1095 }
1096 if let (LitRef::Bool(x), LitRef::Bool(y)) = (ll, rr) {
1099 if matches!(op, BinOp::Eq | BinOp::Ne) {
1100 return Some(if op == BinOp::Eq { x == y } else { x != y });
1101 }
1102 }
1103 None
1105 }
1106
1107 fn fold_lit_neg(&mut self, v: i64, wide: bool) -> Option<i64> {
1108 let n = -v;
1110 if !wide && n > i32::MAX as i64 {
1111 return None;
1112 }
1113 Some(n)
1114 }
1115
1116 fn reassoc_delta(
1117 &mut self,
1118 op1: BinOp,
1119 k1: i64,
1120 op2: BinOp,
1121 k2: i64,
1122 wide: bool,
1123 ) -> Option<ReassocOutcome<JavaId>> {
1124 use BinOp::*;
1125 let raw: i64 = match (op1, op2) {
1126 (BitXor, BitXor) => k1 ^ k2,
1127 (a, b) if matches!(a, Add | Sub) && matches!(b, Add | Sub) => {
1128 let sign = |o: BinOp| if o == Sub { -1i64 } else { 1i64 };
1129 sign(a) * k1 + sign(b) * k2
1130 }
1131 _ => return None,
1132 };
1133 let d = if wide { raw } else { raw as i32 as i64 };
1136 if d == 0 {
1137 return Some(ReassocOutcome::Neutral);
1138 }
1139 if op1 == BitXor && op2 == BitXor {
1140 return Some(ReassocOutcome::Lit(self.build_int(d, wide), BitXor));
1141 }
1142 if d > 0 {
1143 Some(ReassocOutcome::Lit(self.build_int(d, wide), Add))
1144 } else {
1145 let nd = if wide { d.checked_neg().unwrap_or(d) } else { (d as i32).wrapping_neg() as i64 };
1149 if nd > 0 {
1150 Some(ReassocOutcome::Lit(self.build_int(nd, wide), Sub))
1151 } else {
1152 Some(ReassocOutcome::Lit(self.build_int(d, wide), Add))
1153 }
1154 }
1155 }
1156
1157 fn copy_subtree(&mut self, id: JavaId) -> JavaId {
1158 let old_children: Vec<JavaId> = self.nodes[id.0 as usize].children.as_slice().to_vec();
1159 let mut children = Vec::with_capacity(old_children.len());
1160 for c in old_children {
1161 children.push(self.copy_subtree(c));
1162 }
1163 let data = self.nodes[id.0 as usize].data.clone();
1164 self.push(data, children)
1165 }
1166
1167 fn effect(&self, id: JavaId) -> Effect {
1168 if let Some(e) = self.effect_cache.get(id.0 as usize).and_then(|x| *x) {
1169 return e;
1170 }
1171 let mut e = self.own_effect(id);
1173 for &c in self.nodes[id.0 as usize].children.as_slice() {
1174 e = e.worst(self.effect(c));
1175 }
1176 e
1177 }
1178
1179 fn invalidate_effect(&mut self, id: JavaId) {
1180 if let Some(slot) = self.effect_cache.get_mut(id.0 as usize) {
1181 *slot = None;
1182 }
1183 self.events.invalidate(id.0 as usize);
1186 }
1187
1188 fn debug_verify_events(&self, stmt: JavaId) {
1189 if !verify_events_on() {
1190 return;
1191 }
1192 if let Some(indexed) = self.region_events(stmt) {
1193 let fresh = cure_tree::collect_region_events(self, stmt);
1194 let key = |e: &cure_engine::kind::RegionEvent<JavaId, u32>| {
1196 (e.key, matches!(e.kind, cure_engine::kind::EventKind::Use), e.node.0)
1197 };
1198 let a: Vec<_> = indexed.iter().map(key).collect();
1199 let b: Vec<_> = fresh.iter().map(key).collect();
1200 if a != b {
1201 eprintln!(
1202 "[EVENTS-STALE] stmt #{} kind={:?}: indexed {} events, fresh {} events",
1203 stmt.0, self.kind(stmt), a.len(), b.len()
1204 );
1205 }
1206 }
1207 }
1208
1209 fn region_events(
1210 &self,
1211 id: JavaId,
1212 ) -> Option<&[cure_engine::kind::RegionEvent<JavaId, u32>]> {
1213 let v = self.events.slot(id.0 as usize);
1215 if v.is_empty() {
1216 None
1217 } else {
1218 Some(v)
1219 }
1220 }
1221
1222 fn var_key(&self, id: JavaId) -> Option<u32> {
1223 self.names.var_key(id.0 as usize)
1224 }
1225
1226 fn node_index(&self, id: JavaId) -> usize {
1227 id.0 as usize
1228 }
1229 fn id_of_index(&self, idx: usize) -> JavaId {
1230 JavaId(idx as u32)
1231 }
1232
1233 fn is_opaque(&self, id: JavaId) -> bool {
1234 match self.data(id) {
1235 NodeData::Raw { .. } => true,
1237 NodeData::New { anon_raw: Some(_), .. } => true,
1240 _ => false,
1241 }
1242 }
1243
1244 fn own_effect(&self, id: JavaId) -> Effect {
1245 match self.data(id) {
1246 NodeData::Literal(_)
1247 | NodeData::This
1248 | NodeData::Super
1249 | NodeData::Paren
1250 | NodeData::Block
1251 | NodeData::Group
1252 | NodeData::Empty
1253 | NodeData::VarDecl { .. }
1254 | NodeData::Break { .. }
1255 | NodeData::Continue { .. }
1256 | NodeData::Try
1257 | NodeData::Catch { .. }
1258 | NodeData::Switch
1259 | NodeData::Case { .. }
1260 | NodeData::Label { .. }
1261 | NodeData::ExprStmt
1262 | NodeData::Ternary
1263 | NodeData::ArrayLit
1264 | NodeData::Lambda { .. }
1265 | NodeData::MethodRef { .. }
1266 | NodeData::InstanceOf { .. } => Effect::Pure,
1267 NodeData::VarRef { .. } => Effect::MayRead,
1268 NodeData::Member { .. } => Effect::MayThrow,
1269 NodeData::Index => Effect::MayThrow,
1270 NodeData::New { .. } | NodeData::NewArray { .. } => Effect::MayThrow,
1271 NodeData::Call => Effect::Unknown,
1272 NodeData::Assign { .. } => Effect::MayWrite,
1273 NodeData::Unary { op } => {
1274 if op.is_incdec() {
1275 Effect::MayWrite
1276 } else {
1277 Effect::Pure
1278 }
1279 }
1280 NodeData::Binary { op } => {
1281 if op.may_div_zero() {
1282 Effect::MayThrow
1283 } else {
1284 Effect::Pure
1285 }
1286 }
1287 NodeData::Cast { ty } => {
1288 if ty.is_ref() {
1289 Effect::MayThrow
1291 } else {
1292 Effect::Pure
1293 }
1294 }
1295 NodeData::For { .. } | NodeData::ForEach { .. } | NodeData::If => Effect::MayRead,
1296 NodeData::While | NodeData::DoWhile => Effect::MayRead,
1297 NodeData::Return | NodeData::Yield => Effect::MayThrow,
1298 NodeData::Throw => Effect::MayThrow,
1299 NodeData::Assert => Effect::MayThrow,
1300 NodeData::Synchronized => Effect::MayWrite,
1301 NodeData::Raw { .. } => Effect::Unknown,
1302 }
1303 }
1304
1305 fn is_bool(&self, id: JavaId) -> bool {
1306 match self.data(id) {
1307 NodeData::Literal(l) => matches!(l, Lit::Bool(_)),
1308 NodeData::Unary { op } => *op == UnOp::Not,
1309 NodeData::Binary { op } => {
1310 op.is_comparison() || matches!(op, BinOp::And | BinOp::Or)
1311 }
1312 NodeData::Ternary => {
1313 let ch = self.children(id);
1314 self.is_bool(ch[1]) && self.is_bool(ch[2])
1315 }
1316 NodeData::Paren => self.is_bool(self.children(id)[0]),
1317 NodeData::InstanceOf { .. } => true,
1318 NodeData::VarRef { .. } => self
1319 .var_types
1320 .get(id.0 as usize)
1321 .and_then(|t| t.as_ref())
1322 .is_some_and(|t| t.is_bool()),
1323 _ => false,
1324 }
1325 }
1326
1327 fn is_exact_int(&self, id: JavaId) -> bool {
1328 match self.data(id) {
1329 NodeData::Literal(l) => matches!(
1330 l,
1331 Lit::Int(_) | Lit::Long(_) | Lit::Char(_) | Lit::NumRaw { val: NumVal::Int(_) | NumVal::Long(_), .. }
1332 ),
1333 NodeData::VarRef { .. } => {
1334 self.var_types.get(id.0 as usize).and_then(|t| t.as_ref()).is_some_and(|t| t.is_integral())
1335 }
1336 NodeData::Binary { op } => {
1337 let ch = self.children(id);
1338 (op.is_arith() || op.is_comparison())
1339 && self.is_exact_int(ch[0])
1340 && self.is_exact_int(ch[1])
1341 }
1342 NodeData::Cast { ty } => ty.is_integral(),
1343 NodeData::Paren => self.is_exact_int(self.children(id)[0]),
1344 NodeData::Ternary => {
1345 let ch = self.children(id);
1346 self.is_exact_int(ch[1]) && self.is_exact_int(ch[2])
1347 }
1348 _ => false,
1349 }
1350 }
1351
1352 fn is_local_var(&self, id: JavaId) -> bool {
1353 matches!(self.data(id), NodeData::VarRef { .. }) && self.var_type(id).is_some()
1359 }
1360
1361 fn is_inferred_decl(&self, decl: JavaId) -> bool {
1362 matches!(self.data(decl), NodeData::VarDecl { ty: JType::Var, .. })
1363 }
1364
1365 fn is_char_decl(&self, decl: JavaId) -> bool {
1366 matches!(self.data(decl), NodeData::VarDecl { ty: JType::Char, .. })
1367 }
1368
1369 fn has_pattern_bindings(&self, root: JavaId) -> bool {
1370 let mut stack = vec![root];
1371 while let Some(n) = stack.pop() {
1372 if let NodeData::InstanceOf { bind: Some(_), .. } = self.data(n) {
1373 return true;
1374 }
1375 for &c in self.children(n) {
1376 stack.push(c);
1377 }
1378 }
1379 false
1380 }
1381
1382 fn is_long_decl(&self, decl: JavaId) -> bool {
1383 matches!(self.data(decl), NodeData::VarDecl { ty: JType::Long, .. })
1384 }
1385 fn is_wide_decl(&self, decl: JavaId) -> bool {
1386 !matches!(
1389 self.data(decl),
1390 NodeData::VarDecl { ty: JType::Byte | JType::Short | JType::Char, .. }
1391 )
1392 }
1393
1394 fn receiver_propagation_unsound(
1395 &self,
1396 decl: JavaId,
1397 value: JavaId,
1398 use_parent: Option<JavaId>,
1399 use_at_head: bool,
1400 ) -> bool {
1401 let is_receiver = matches!(use_parent, Some(p) if {
1404 matches!(self.data(p), NodeData::Member { .. }) && use_at_head
1405 });
1406 if is_receiver {
1407 if self.receiver_boxing_unsound(decl, value) {
1409 return true;
1410 }
1411 if self.receiver_new_type_unsound(decl, value) {
1422 return true;
1423 }
1424 let raw_ref = match self.data(decl) {
1428 NodeData::VarDecl { ty: JType::Ref(n), .. } => {
1429 !n.contains('<')
1430 && n.rsplit('.').next().is_some_and(|base| {
1431 self.raw_generic_names.contains(base)
1432 })
1433 }
1434 _ => false,
1435 };
1436 if !raw_ref {
1437 return false;
1438 }
1439 match self.data(value) {
1441 NodeData::Call => true,
1442 NodeData::New { ty, .. } => {
1443 matches!(ty, JType::Ref(n) if n.contains('<'))
1444 }
1445 _ => false,
1446 }
1447 } else {
1448 if matches!(use_parent, Some(p) if {
1455 matches!(self.data(p), NodeData::Index) && use_at_head
1456 }) {
1457 return self.index_base_newarray_unsound(decl, value);
1458 }
1459 false
1460 }
1461 }
1462
1463 fn bin_op(&self, id: JavaId) -> Option<BinOp> {
1464 match self.data(id) {
1465 NodeData::Binary { op } => Some(*op),
1466 _ => None,
1467 }
1468 }
1469 fn un_op(&self, id: JavaId) -> Option<UnOp> {
1470 match self.data(id) {
1471 NodeData::Unary { op } => Some(*op),
1472 _ => None,
1473 }
1474 }
1475 fn literal(&self, id: JavaId) -> Option<LitRef<'_>> {
1476 match self.data(id) {
1477 NodeData::Literal(l) => Some(match l {
1478 Lit::Bool(b) => LitRef::Bool(*b),
1479 Lit::Int(v) => LitRef::Int(*v),
1480 Lit::Long(v) => LitRef::Long(*v),
1481 Lit::Float(v) => LitRef::Float(*v),
1482 Lit::Double(v) => LitRef::Double(*v),
1483 Lit::Char(c) => LitRef::Char(*c),
1484 Lit::Str(s) => LitRef::Str(s),
1485 Lit::TextBlock(_) => return None,
1491 Lit::NumRaw { val, .. } => match val {
1492 NumVal::Int(v) => LitRef::Int(*v),
1493 NumVal::Long(v) => LitRef::Long(*v),
1494 NumVal::Float(v) => LitRef::Float(*v),
1495 NumVal::Double(v) => LitRef::Double(*v),
1496 },
1497 Lit::Null => LitRef::Null,
1498 }),
1499 _ => None,
1500 }
1501 }
1502 fn var_name(&self, id: JavaId) -> Option<&str> {
1503 match self.data(id) {
1504 NodeData::VarRef { name } => Some(self.sn(*name)),
1505 NodeData::VarDecl { name, .. } => Some(name),
1506 NodeData::ForEach { name, .. } | NodeData::Catch { name, .. } => Some(name),
1507 _ => None,
1508 }
1509 }
1510 fn member_name(&self, id: JavaId) -> Option<&str> {
1511 match self.data(id) {
1512 NodeData::Member { name } => Some(self.sn(*name)),
1513 NodeData::MethodRef { name } => Some(self.sn(*name)),
1514 _ => None,
1515 }
1516 }
1517 fn is_field_name(&self, name: &str) -> bool {
1518 self.field_types.contains_key(name)
1519 }
1520 fn payload_equal(&self, a: JavaId, b: JavaId) -> bool {
1521 if let (NodeData::Literal(x), NodeData::Literal(y)) = (self.data(a), self.data(b)) {
1524 return lit_value_eq(x, y);
1525 }
1526 self.data(a) == self.data(b)
1527 }
1528 fn assign_op(&self, id: JavaId) -> Option<BinOp> {
1529 match self.data(id) {
1530 NodeData::Assign { op } => *op,
1531 _ => None,
1532 }
1533 }
1534
1535 fn prepare(&mut self, root: JavaId) {
1539
1540 if cnt_prepare_on() {
1541 PREPARE_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1542 PREPARE_NODES.fetch_add(self.nodes.len(), std::sync::atomic::Ordering::Relaxed);
1543 }
1544 let n_nodes = self.nodes.len();
1548 let key_slots = self.names.key_slots();
1549 if key_slots < n_nodes {
1550 let names: Vec<Option<String>> = (key_slots..n_nodes)
1551 .map(|i| self.var_name(JavaId(i as u32)).map(|n| n.to_string()))
1552 .collect();
1553 self.names.extend_owned(key_slots, &names);
1554 }
1555 self.string_identity.set(None);
1557 self.events.resize(n_nodes);
1568 if self.events.is_clean() {
1569 if self.prepared_root == Some(root) {
1570 return;
1571 }
1572 self.rebuild_var_types(root);
1575 self.prepared_root = Some(root);
1576 return;
1577 }
1578 self.prepared_root = Some(root);
1579 self.events.mark_clean();
1580 for sr in cure_tree::stmt_roots(self, root) {
1581 let idx = sr.0 as usize;
1582 if self.events.slot(idx).is_empty() {
1583 let events = cure_tree::collect_region_events(self, sr);
1584 self.events.put(idx, events);
1585 }
1586 }
1587 let effects = cure_tree::rebuild_effects(self, n_nodes);
1590 self.effect_cache = effects;
1591 self.rebuild_var_types(root);
1592 }
1593}
1594
1595impl JavaAst {
1596 pub(crate) fn rebuild_var_types(&mut self, root: JavaId) {
1598 self.var_types.clear();
1600 self.var_types.resize(self.nodes.len(), None);
1601 #[derive(Clone)]
1602 enum Frame {
1603 Enter(JavaId),
1604 Exit,
1605 PopScope,
1606 Bind(String, JType),
1607 }
1608 let mut base: HashMap<String, JType> = HashMap::new();
1609 for (n, t) in &self.param_scope {
1610 base.insert(n.clone(), t.clone());
1611 }
1612 let mut scopes: Vec<HashMap<String, JType>> = vec![base];
1613 let mut stack = vec![Frame::Enter(root)];
1614 while let Some(f) = stack.pop() {
1615 match f {
1616 Frame::Enter(id) => {
1617 let node = &self.nodes[id.0 as usize];
1618 enum Action {
1619 None,
1620 Scope,
1621 Bind(String, JType),
1622 }
1623 let action = match &node.data {
1624 NodeData::Block | NodeData::For { .. } | NodeData::Try => Action::Scope,
1625 NodeData::VarDecl { name, ty, .. } => {
1626 scopes.last_mut().unwrap().insert(name.clone(), ty.clone());
1627 Action::None
1628 }
1629 NodeData::VarRef { name } => {
1630 if let Some(t) = scopes
1631 .iter()
1632 .rev()
1633 .find_map(|s| s.get(self.sn(*name)))
1634 .cloned()
1635 {
1636 self.var_types[id.0 as usize] = Some(t);
1637 }
1638 Action::None
1639 }
1640 NodeData::Catch { name, .. } => {
1641 Action::Bind(name.clone(), JType::Var)
1642 }
1643 NodeData::ForEach { name, ty } => {
1644 Action::Bind(name.clone(), ty.clone())
1645 }
1646 _ => Action::None,
1647 };
1648 stack.push(Frame::Exit);
1651 if matches!(action, Action::Scope | Action::Bind(_, _)) {
1652 scopes.push(HashMap::new());
1653 stack.push(Frame::PopScope);
1654 }
1655 for &c in node.children.as_slice().iter().rev() {
1656 stack.push(Frame::Enter(c));
1657 }
1658 if let Action::Bind(n, t) = action {
1659 stack.push(Frame::Bind(n, t));
1660 }
1661 }
1662 Frame::Bind(name, ty) => {
1663 scopes.last_mut().unwrap().insert(name, ty);
1664 }
1665 Frame::PopScope => {
1666 scopes.pop();
1667 }
1668 Frame::Exit => {}
1669 }
1670 }
1671 }
1672}
1673
1674impl JavaAst {
1686 fn receiver_new_type_unsound(&self, decl: JavaId, value: JavaId) -> bool {
1698 let NodeData::New { ty, .. } = self.data(value) else {
1699 return false;
1700 };
1701 let decl_ty = match self.data(decl) {
1702 NodeData::VarDecl { ty, .. } => ty,
1703 _ => return false,
1704 };
1705 if matches!(decl_ty, JType::Var) {
1708 return false;
1709 }
1710 let name = match ty {
1711 JType::Ref(n) => n.as_str(),
1712 _ => return true,
1714 };
1715 if name.ends_with("<>") {
1717 return true;
1718 }
1719 match decl_ty {
1720 JType::Ref(d) => type_base_name(d) != type_base_name(name),
1721 _ => true,
1723 }
1724 }
1725
1726 fn index_base_newarray_unsound(&self, decl: JavaId, value: JavaId) -> bool {
1732 let NodeData::NewArray { ty, dims, .. } = self.data(value) else {
1733 return false;
1734 };
1735 let decl_ty = match self.data(decl) {
1736 NodeData::VarDecl { ty, .. } => ty,
1737 _ => return false,
1738 };
1739 if matches!(decl_ty, JType::Var) {
1740 return false;
1741 }
1742 let mut elem: &JType = decl_ty;
1744 let mut nest = 0u16;
1745 while let JType::Array(inner) = elem {
1746 elem = inner.as_ref();
1747 nest += 1;
1748 }
1749 nest != *dims || *elem != *ty
1750 }
1751
1752 fn receiver_boxing_unsound(&self, decl: JavaId, value: JavaId) -> bool {
1753 let declared_ref = match self.data(decl) {
1754 NodeData::VarDecl { ty, .. } => ty.is_ref(),
1755 _ => false,
1756 };
1757 if !declared_ref {
1758 return false;
1759 }
1760 match self.data(value) {
1761 NodeData::Literal(
1762 Lit::Int(_) | Lit::Long(_) | Lit::Float(_) | Lit::Double(_) | Lit::Bool(_)
1763 | Lit::Char(_),
1764 ) => true,
1765 NodeData::VarRef { .. } => matches!(
1767 self.var_type(value),
1768 Some(
1769 JType::Int
1770 | JType::Long
1771 | JType::Byte
1772 | JType::Short
1773 | JType::Char
1774 | JType::Float
1775 | JType::Double
1776 | JType::Bool
1777 )
1778 ),
1779 NodeData::Unary { .. } | NodeData::Binary { .. } => true,
1781 NodeData::Ternary => true,
1785 NodeData::Cast { .. } => true,
1787 NodeData::InstanceOf { .. } => true,
1789 NodeData::Call | NodeData::Member { .. } | NodeData::Index => {
1795 match self.data(decl) {
1796 NodeData::VarDecl { ty: JType::Ref(n), .. } => {
1797 let boxed = [
1798 "Character", "Integer", "Long", "Boolean", "Double",
1799 "Float", "Byte", "Short", "Number", "AtomicInteger",
1800 "AtomicLong",
1801 ];
1802 n.rsplit('.').next().is_some_and(|b| boxed.contains(&b))
1803 }
1804 _ => false,
1805 }
1806 }
1807 _ => false,
1808 }
1809 }
1810
1811 pub fn intern_sym(&mut self, name: &str) -> Sym {
1814 Sym(self.names.intern(name))
1815 }
1816
1817 pub fn sn(&self, sym: Sym) -> &str {
1819 self.names.name(sym.0)
1820 }
1821
1822
1823
1824
1825}
1826
1827
1828pub fn unit_metrics(ast: &JavaAst, unit: &CompilationUnit) -> TreeMetrics {
1836 let mut total = TreeMetrics::default();
1837 for ty in &unit.types {
1838 metrics_of_type(ast, ty, &mut total);
1839 }
1840 total
1841}
1842
1843fn metrics_of_type(ast: &JavaAst, ty: &TypeDecl, total: &mut TreeMetrics) {
1844 for m in &ty.members {
1845 match m {
1846 Member::Method { body: Some(b), .. } | Member::Constructor { body: Some(b), .. } => {
1847 total.add(subtree_metrics(ast, *b));
1848 }
1849 Member::Initializer { body, .. } => {
1850 total.add(subtree_metrics(ast, *body));
1851 }
1852 Member::Type(t) => metrics_of_type(ast, t, total),
1853 _ => {}
1854 }
1855 }
1856}
1857
1858static ENV_CACHED: std::sync::OnceLock<(bool, bool)> = std::sync::OnceLock::new();
1859fn env_flags() -> (bool, bool) {
1860 *ENV_CACHED.get_or_init(|| {
1861 (
1862 std::env::var("CURE_CNT_PREPARE").is_ok(),
1863 std::env::var("CURE_VERIFY_EVENTS").is_ok(),
1864 )
1865 })
1866}
1867fn cnt_prepare_on() -> bool { env_flags().0 }
1868fn verify_events_on() -> bool { env_flags().1 }
1869
1870pub static PREPARE_CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1871pub static PREPARE_NODES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1872
1873pub static COLLECT_CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1874
1875#[cfg(test)]
1880mod tests {
1881 use super::*;
1882
1883 #[test]
1884 fn builder_smoke() {
1885 let mut a = JavaAst::new();
1886 let one = a.lit(Lit::Int(1));
1887 let x = a.var_decl("x", JType::Int, Some(one));
1888 let xv = a.var("x");
1889 let r = a.ret(Some(xv));
1890 let body = a.block(vec![x, r]);
1891 assert_eq!(a.kind(body), NodeKind::Block);
1892 assert_eq!(a.children(body).len(), 2);
1893 }
1894
1895 #[test]
1896 fn effect_aggregation() {
1897 let mut a = JavaAst::new();
1898 let f = a.plain_call("foo", vec![]);
1899 let r = a.ret(Some(f));
1900 let body = a.block(vec![r]);
1901 a.prepare(body);
1902 assert_eq!(a.effect(f), Effect::Unknown);
1903 assert_eq!(a.effect(r), Effect::Unknown);
1904 assert_eq!(a.effect(body), Effect::Unknown);
1905
1906 let mut b = JavaAst::new();
1907 let two = b.lit(Lit::Int(2));
1908 let three = b.lit(Lit::Int(3));
1909 let add = b.bin(BinOp::Add, two, three);
1910 let r2 = b.ret(Some(add));
1911 let body2 = b.block(vec![r2]);
1912 b.prepare(body2);
1913 assert_eq!(b.effect(add), Effect::Pure);
1914 assert_eq!(b.effect(r2), Effect::MayThrow);
1916 assert_eq!(b.effect(body2), Effect::MayThrow);
1917 }
1918
1919 #[test]
1920 fn var_type_resolution_and_shadowing() {
1921 let mut a = JavaAst::new();
1922 let one = a.lit(Lit::Int(1));
1924 let b2_decl = a.var_decl("b2", JType::Int, Some(one));
1925 let inner_b2 = a.var("b2");
1926 let inner_stmt = a.expr_stmt(inner_b2);
1927 let inner = a.block(vec![b2_decl, inner_stmt]);
1928 let tv = a.lit(Lit::Bool(true));
1929 let decl_b = a.var_decl("b", JType::Bool, Some(tv));
1930 let outer_b = a.var("b");
1931 let outer_stmt = a.expr_stmt(outer_b);
1932 let body = a.block(vec![decl_b, inner, outer_stmt]);
1933 a.prepare(body);
1934 assert!(a.is_bool(outer_b));
1935 assert!(a.var_type(inner_b2).is_some());
1937 }
1938
1939 #[test]
1940 fn raw_and_foreach_semantics() {
1941 let mut a = JavaAst::new();
1942 let one = a.lit(Lit::Int(1));
1944 let dx = a.var_decl("x", JType::Int, Some(one));
1945 let r = a.raw("broken ~!@ code");
1946 let sv = a.var("s");
1947 let call = a.method_call(sv, "len", vec![]);
1948 let es = a.expr_stmt(call);
1949 let body = a.block(vec![es]);
1950 let lst = a.var("list");
1951 let fe = a.for_each("s", JType::Ref("String".into()), lst, body);
1952 let all = a.block(vec![dx, r, fe]);
1953 a.prepare(all);
1954 assert_eq!(a.kind(r), NodeKind::Raw);
1955 assert_eq!(a.effect(r), Effect::Unknown);
1956 assert!(a.var_type(sv).is_some()); }
1958
1959 #[test]
1960 fn num_raw_literal() {
1961 let mut a = JavaAst::new();
1962 let l = a.lit(Lit::NumRaw {
1963 text: "0x1F".into(),
1964 val: NumVal::Int(31),
1965 });
1966 a.prepare(l);
1967 assert!(a.is_exact_int(l));
1968 assert!(matches!(a.literal(l), Some(LitRef::Int(31))));
1969 }
1970}