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(Debug, Clone, Copy, Default, PartialEq, Eq)]
243pub struct VarFacts {
244 pub single_def: bool,
246 pub single_use: bool,
248 pub bool_valued: bool,
250}
251
252#[derive(Debug, Clone, Default)]
255pub struct JavaFacts {
256 pub vars: HashMap<String, VarFacts>,
257 pub call_effects: HashMap<String, std::collections::HashSet<cure_engine::Effect>>,
258}
259
260impl JavaFacts {
261 pub fn var(&self, name: &str) -> VarFacts {
262 self.vars.get(name).copied().unwrap_or_default()
263 }
264}
265
266#[derive(Default, Debug)]
267pub struct JavaAst {
268 pub(crate) nodes: Vec<Node>,
269 pub facts: Option<JavaFacts>,
271 effect_cache: Vec<Option<Effect>>,
274 var_types: Vec<Option<JType>>,
277 param_scope: Vec<(String, JType)>,
279 pub const_fields: HashMap<String, JavaId>,
282 pub const_scalars: HashSet<String>,
286 pub noop_private_methods: HashMap<String, HashMap<usize, Vec<NoopCandidate>>>,
292 pub field_types: HashMap<String, JType>,
295 pub unit_shadows_lang: bool,
300 pub raw_generic_names: std::collections::HashSet<String>,
304 pub final_fields: HashSet<String>,
308 pub inline_methods: HashMap<String, (Vec<String>, JavaId, String)>,
314 string_identity: std::cell::Cell<Option<(JavaId, bool)>>,
317 pub unit_string_identity: bool,
322 names: NameTable,
326 events: EventStore<JavaId, u32>,
330 prepared_root: Option<JavaId>,
335}
336
337#[derive(Clone, PartialEq, Debug)]
343pub struct Param {
344 pub mods: String,
346 pub ty: JType,
347 pub varargs: bool,
349 pub name: String,
350}
351
352#[derive(Clone, PartialEq, Debug)]
354pub struct Declarator {
355 pub name: String,
356 pub extra_dims: u16,
358 pub init: Option<JavaId>,
360}
361
362#[derive(Clone, PartialEq, Debug)]
364pub enum Member {
365 Field { mods: String, ty: JType, declarators: Vec<Declarator> },
366 Method {
367 mods: String,
368 ty_params: String,
370 ret: JType,
371 name: String,
372 params: Vec<Param>,
373 throws: Vec<String>,
374 body: Option<JavaId>,
376 },
377 Constructor {
378 mods: String,
379 ty_params: String,
380 name: String,
381 params: Vec<Param>,
382 throws: Vec<String>,
383 body: Option<JavaId>,
384 compact: bool,
386 },
387 Initializer { is_static: bool, body: JavaId },
389 Type(Box<TypeDecl>),
391 Raw(String),
393}
394
395#[derive(Clone, Copy, PartialEq, Eq, Debug)]
396pub enum TypeKind {
397 Class,
398 Interface,
399 Enum,
400 Record,
401 Annotation,
402}
403
404#[derive(Clone, PartialEq, Debug)]
406pub struct TypeDecl {
407 pub kind: TypeKind,
408 pub mods: String,
410 pub name: String,
411 pub ty_params: String,
413 pub header: String,
415 pub extends: Vec<String>,
416 pub implements: Vec<String>,
417 pub permits: Vec<String>,
419 pub enum_constants: Vec<String>,
421 pub members: Vec<Member>,
422}
423
424#[derive(Clone, PartialEq, Debug, Default)]
426pub struct CompilationUnit {
427 pub package: Option<String>,
429 pub package_annotations: String,
432 pub imports: Vec<String>,
434 pub types: Vec<TypeDecl>,
435 pub raws: Vec<String>,
437}
438
439#[derive(Clone, PartialEq, Eq, Debug)]
441pub struct ParseError {
442 pub line: usize,
443 pub col: usize,
444 pub message: String,
445}
446
447impl JavaAst {
452 pub fn new() -> Self {
453 Self::default()
454 }
455
456 pub fn clone_node(&mut self, id: JavaId, children: Vec<JavaId>) -> JavaId {
458 let data = self.nodes[id.0 as usize].data.clone();
459 self.push(data, children)
460 }
461
462 fn push(&mut self, data: NodeData, children: Vec<JavaId>) -> JavaId {
463 let id = JavaId(self.nodes.len() as u32);
464 self.nodes.push(Node { data, children: ChildList::from_vec(children) });
465 id
466 }
467
468 pub fn node(&self, id: JavaId) -> &Node {
469 &self.nodes[id.0 as usize]
470 }
471 pub fn data(&self, id: JavaId) -> &NodeData {
472 &self.node(id).data
473 }
474
475 pub fn kind(&self, id: JavaId) -> NodeKind {
477 match &self.nodes[id.0 as usize].data {
478 NodeData::Block | NodeData::Group => NodeKind::Block,
479 NodeData::Empty => NodeKind::Empty,
480 NodeData::Yield => NodeKind::Yield,
481 NodeData::ExprStmt => NodeKind::ExprStmt,
482 NodeData::VarDecl { .. } => NodeKind::VarDecl,
483 NodeData::Assign { .. } => NodeKind::Assign,
484 NodeData::If => NodeKind::If,
485 NodeData::While => NodeKind::While,
486 NodeData::DoWhile => NodeKind::DoWhile,
487 NodeData::For { .. } => NodeKind::For,
488 NodeData::ForEach { .. } => NodeKind::ForEach,
489 NodeData::Return => NodeKind::Return,
490 NodeData::Break { .. } => NodeKind::Break,
491 NodeData::Continue { .. } => NodeKind::Continue,
492 NodeData::Throw => NodeKind::Throw,
493 NodeData::Try => NodeKind::Try,
494 NodeData::Catch { .. } => NodeKind::Catch,
495 NodeData::Synchronized => NodeKind::Synchronized,
496 NodeData::Switch => NodeKind::Switch,
497 NodeData::Case { .. } => NodeKind::Case,
498 NodeData::Label { .. } => NodeKind::Label,
499 NodeData::Assert => NodeKind::Assert,
500 NodeData::Raw { .. } => NodeKind::Raw,
501 NodeData::Binary { .. } => NodeKind::Binary,
502 NodeData::Unary { .. } => NodeKind::Unary,
503 NodeData::Call => NodeKind::Call,
504 NodeData::Member { .. } => NodeKind::Member,
505 NodeData::Index => NodeKind::Index,
506 NodeData::Cast { .. } => NodeKind::Cast,
507 NodeData::Paren => NodeKind::Paren,
508 NodeData::Ternary => NodeKind::Ternary,
509 NodeData::New { .. } => NodeKind::New,
510 NodeData::NewArray { .. } => NodeKind::NewArray,
511 NodeData::ArrayLit => NodeKind::ArrayLit,
512 NodeData::VarRef { .. } => NodeKind::VarRef,
513 NodeData::Literal(_) => NodeKind::Literal,
514 NodeData::This => NodeKind::This,
515 NodeData::Super => NodeKind::This,
516 NodeData::InstanceOf { .. } => NodeKind::InstanceOf,
517 NodeData::Lambda { .. } => NodeKind::Lambda,
518 NodeData::MethodRef { .. } => NodeKind::MethodRef,
519 }
520 }
521 pub fn node_count(&self) -> usize {
523 self.nodes.len()
524 }
525
526 pub fn children(&self, id: JavaId) -> &[JavaId] {
527 self.nodes[id.0 as usize].children.as_slice()
528 }
529
530 pub fn block(&mut self, stmts: Vec<JavaId>) -> JavaId {
533 self.push(NodeData::Block, stmts)
534 }
535 pub fn empty(&mut self) -> JavaId {
536 self.push(NodeData::Empty, vec![])
537 }
538 pub fn expr_stmt(&mut self, e: JavaId) -> JavaId {
539 self.push(NodeData::ExprStmt, vec![e])
540 }
541 pub fn var_decl(&mut self, name: &str, ty: JType, init: Option<JavaId>) -> JavaId {
542 self.var_decl_final(name, ty, init, false)
543 }
544 pub fn var_decl_final(
548 &mut self,
549 name: &str,
550 ty: JType,
551 init: Option<JavaId>,
552 final_mod: bool,
553 ) -> JavaId {
554 self.push(
555 NodeData::VarDecl {
556 name: name.into(),
557 ty,
558 final_mod,
559 },
560 init.into_iter().collect(),
561 )
562 }
563 pub fn assign(&mut self, target: JavaId, value: JavaId) -> JavaId {
564 self.push(NodeData::Assign { op: None }, vec![target, value])
565 }
566 pub fn assign_op(&mut self, op: BinOp, target: JavaId, value: JavaId) -> JavaId {
567 self.push(NodeData::Assign { op: Some(op) }, vec![target, value])
568 }
569 pub fn if_(&mut self, cond: JavaId, then: JavaId, els: Option<JavaId>) -> JavaId {
570 let mut ch = vec![cond, then];
571 ch.extend(els);
572 self.push(NodeData::If, ch)
573 }
574 pub fn while_(&mut self, cond: JavaId, body: JavaId) -> JavaId {
575 self.push(NodeData::While, vec![cond, body])
576 }
577 pub fn do_while(&mut self, body: JavaId, cond: JavaId) -> JavaId {
578 self.push(NodeData::DoWhile, vec![body, cond])
579 }
580 pub fn for_(
581 &mut self,
582 inits: Vec<JavaId>,
583 cond: Option<JavaId>,
584 steps: Vec<JavaId>,
585 body: JavaId,
586 ) -> JavaId {
587 let ni = inits.len();
588 let ns = steps.len();
589 let mut ch = inits;
590 let has_cond = cond.is_some();
591 ch.extend(cond);
592 ch.extend(steps);
593 ch.push(body);
594 self.push(
595 NodeData::For {
596 inits: ni as u8,
597 has_cond,
598 steps: ns as u8,
599 },
600 ch,
601 )
602 }
603 pub fn for_each(&mut self, name: &str, ty: JType, iterable: JavaId, body: JavaId) -> JavaId {
604 self.push(
605 NodeData::ForEach {
606 name: name.into(),
607 ty,
608 },
609 vec![iterable, body],
610 )
611 }
612 pub fn yield_(&mut self, expr: JavaId) -> JavaId {
613 self.push(NodeData::Yield, vec![expr])
614 }
615 pub fn ret(&mut self, value: Option<JavaId>) -> JavaId {
616 self.push(NodeData::Return, value.into_iter().collect())
617 }
618 pub fn break_(&mut self, label: Option<&str>) -> JavaId {
619 self.push(
620 NodeData::Break {
621 label: label.map(Into::into),
622 },
623 vec![],
624 )
625 }
626 pub fn continue_(&mut self, label: Option<&str>) -> JavaId {
627 self.push(
628 NodeData::Continue {
629 label: label.map(Into::into),
630 },
631 vec![],
632 )
633 }
634 pub fn throw(&mut self, e: JavaId) -> JavaId {
635 self.push(NodeData::Throw, vec![e])
636 }
637 pub fn try_(
638 &mut self,
639 resources: Vec<JavaId>,
640 try_block: JavaId,
641 catches: Vec<JavaId>,
642 finally: Option<JavaId>,
643 ) -> JavaId {
644 let mut ch = resources;
645 ch.push(try_block);
646 ch.extend(catches);
647 ch.extend(finally);
648 self.push(NodeData::Try, ch)
649 }
650 pub fn catch_(&mut self, ty_raw: &str, name: &str, block: JavaId) -> JavaId {
651 self.push(
652 NodeData::Catch {
653 ty_raw: ty_raw.into(),
654 name: name.into(),
655 },
656 vec![block],
657 )
658 }
659 pub fn synchronized(&mut self, lock: JavaId, block: JavaId) -> JavaId {
660 self.push(NodeData::Synchronized, vec![lock, block])
661 }
662 pub fn switch_(&mut self, subject: JavaId, cases: Vec<JavaId>) -> JavaId {
663 let mut ch = vec![subject];
664 ch.extend(cases);
665 self.push(NodeData::Switch, ch)
666 }
667 pub fn case_(
668 &mut self,
669 labels: Vec<JavaId>,
670 is_default: bool,
671 arrow: bool,
672 stmts: Vec<JavaId>,
673 ) -> JavaId {
674 let n = labels.len() as u16;
675 let mut ch = labels;
676 ch.extend(stmts);
677 self.push(
678 NodeData::Case {
679 labels: n,
680 is_default,
681 arrow,
682 },
683 ch,
684 )
685 }
686 pub fn label(&mut self, name: &str, stmt: JavaId) -> JavaId {
687 self.push(NodeData::Label { name: name.into() }, vec![stmt])
688 }
689 pub fn assert_(&mut self, cond: JavaId, msg: Option<JavaId>) -> JavaId {
690 let mut ch = vec![cond];
691 ch.extend(msg);
692 self.push(NodeData::Assert, ch)
693 }
694 pub fn raw(&mut self, text: &str) -> JavaId {
695 self.push(NodeData::Raw { text: text.into() }, vec![])
696 }
697
698 pub fn bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> JavaId {
701 self.push(NodeData::Binary { op }, vec![l, r])
702 }
703 pub fn un(&mut self, op: UnOp, e: JavaId) -> JavaId {
704 self.push(NodeData::Unary { op }, vec![e])
705 }
706 pub fn call(&mut self, callee: JavaId, args: Vec<JavaId>) -> JavaId {
707 let mut ch = vec![callee];
708 ch.extend(args);
709 self.push(NodeData::Call, ch)
710 }
711 pub fn member(&mut self, obj: JavaId, name: &str) -> JavaId {
712 let sym = self.intern_sym(name);
713 self.push(NodeData::Member { name: sym }, vec![obj])
714 }
715 pub fn index(&mut self, arr: JavaId, idx: JavaId) -> JavaId {
716 self.push(NodeData::Index, vec![arr, idx])
717 }
718 pub fn cast(&mut self, ty: JType, e: JavaId) -> JavaId {
719 self.push(NodeData::Cast { ty }, vec![e])
720 }
721 pub fn paren(&mut self, e: JavaId) -> JavaId {
722 self.push(NodeData::Paren, vec![e])
723 }
724 pub fn ternary(&mut self, c: JavaId, a: JavaId, b: JavaId) -> JavaId {
725 self.push(NodeData::Ternary, vec![c, a, b])
726 }
727 pub fn new_(&mut self, ty: JType, args: Vec<JavaId>) -> JavaId {
728 self.push(NodeData::New { ty, anon_raw: None }, args)
729 }
730 pub fn new_anon(&mut self, ty: JType, args: Vec<JavaId>, body_raw: String) -> JavaId {
731 self.push(
732 NodeData::New {
733 ty,
734 anon_raw: Some(body_raw),
735 },
736 args,
737 )
738 }
739 pub fn new_array(
740 &mut self,
741 ty: JType,
742 dims: u16,
743 sized: u16,
744 sizes: Vec<JavaId>,
745 init: Option<JavaId>,
746 ) -> JavaId {
747 let mut ch = sizes;
748 ch.extend(init);
749 self.push(NodeData::NewArray { ty, dims, sized }, ch)
750 }
751 pub fn group(&mut self, stmts: Vec<JavaId>) -> JavaId {
753 self.push(NodeData::Group, stmts)
754 }
755 pub fn array_lit(&mut self, elems: Vec<JavaId>) -> JavaId {
756 self.push(NodeData::ArrayLit, elems)
757 }
758 pub fn var(&mut self, name: &str) -> JavaId {
759 let sym = self.intern_sym(name);
760 self.push(NodeData::VarRef { name: sym }, vec![])
761 }
762 pub fn lit(&mut self, l: Lit) -> JavaId {
763 self.push(NodeData::Literal(l), vec![])
764 }
765 fn build_int_impl(&mut self, v: i64, long: bool) -> JavaId {
766 if long {
767 self.lit(Lit::Long(v))
768 } else {
769 self.lit(Lit::Int(v))
770 }
771 }
772 pub fn this(&mut self) -> JavaId {
773 self.push(NodeData::This, vec![])
774 }
775 pub fn super_(&mut self) -> JavaId {
776 self.push(NodeData::Super, vec![])
777 }
778 pub fn instance_of(&mut self, e: JavaId, ty: JType, bind: Option<&str>) -> JavaId {
779 self.push(
780 NodeData::InstanceOf {
781 ty,
782 bind: bind.map(Into::into),
783 },
784 vec![e],
785 )
786 }
787 pub fn lambda(&mut self, params_raw: &str, body: JavaId) -> JavaId {
788 self.push(
789 NodeData::Lambda {
790 params_raw: params_raw.into(),
791 },
792 vec![body],
793 )
794 }
795 pub fn method_ref(&mut self, receiver: JavaId, name: &str) -> JavaId {
796 let sym = self.intern_sym(name);
797 self.push(NodeData::MethodRef { name: sym }, vec![receiver])
798 }
799
800 pub fn method_call(&mut self, obj: JavaId, name: &str, args: Vec<JavaId>) -> JavaId {
804 let m = self.member(obj, name);
805 self.call(m, args)
806 }
807 pub fn plain_call(&mut self, name: &str, args: Vec<JavaId>) -> JavaId {
809 let f = self.var(name);
810 self.call(f, args)
811 }
812
813 pub fn var_type(&self, id: JavaId) -> Option<&JType> {
815 self.var_types.get(id.0 as usize).and_then(|t| t.as_ref())
816 }
817
818 pub fn any_string_identity(&self, roots: &[JavaId]) -> bool {
824 roots.iter().any(|&r| {
825 let hit = Self::scan_string_identity(self, r);
826 if hit {
827 self.string_identity.set(Some((r, true)));
828 }
829 hit
830 })
831 }
832
833 pub fn has_string_identity_compare(&self, root: JavaId) -> bool {
834 if let Some((r, v)) = self.string_identity.get() {
835 if r == root {
836 return v;
837 }
838 }
839 let v = Self::scan_string_identity(self, root);
840 self.string_identity.set(Some((root, v)));
841 v
842 }
843
844 fn scan_string_identity(&self, root: JavaId) -> bool {
845 let mut stack = vec![root];
846 while let Some(id) = stack.pop() {
847 if let NodeData::Binary { op: BinOp::Eq | BinOp::Ne } = self.data(id) {
848 let ch = self.children(id);
849 if ch.len() == 2
850 && self.expr_maybe_string(ch[0])
851 && self.expr_maybe_string(ch[1])
852 {
853 return true;
854 }
855 }
856 for &c in self.children(id) {
857 stack.push(c);
858 }
859 }
860 false
861 }
862
863 fn expr_maybe_string(&self, id: JavaId) -> bool {
865 match self.data(id) {
866 NodeData::Literal(Lit::Str(_)) => true,
867 NodeData::Literal(_) | NodeData::This | NodeData::Super
869 | NodeData::NewArray { .. } | NodeData::ArrayLit | NodeData::InstanceOf { .. } => false,
870 NodeData::VarRef { .. } => self.var_type(id).map_or(true, Self::type_maybe_string),
871 NodeData::Cast { ty } => Self::type_maybe_string(ty),
872 NodeData::Paren => self
873 .children(id)
874 .first()
875 .map_or(true, |&c| self.expr_maybe_string(c)),
876 NodeData::Binary { op } if *op == BinOp::Add => {
878 self.children(id).iter().any(|&c| self.expr_maybe_string(c))
879 }
880 NodeData::Binary { .. } => false,
881 NodeData::Ternary => self
882 .children(id)
883 .get(1..)
884 .map_or(true, |cs| cs.iter().any(|&c| self.expr_maybe_string(c))),
885 _ => true,
887 }
888 }
889
890 fn type_maybe_string(t: &JType) -> bool {
892 match t {
893 JType::Bool | JType::Byte | JType::Short | JType::Int | JType::Long
894 | JType::Char | JType::Float | JType::Double | JType::Void
895 | JType::Array(_) => false, JType::Var => true, JType::Ref(n) => {
898 let base = n.split('<').next().unwrap_or(n).trim();
899 let last = base.rsplit('.').next().unwrap_or(base);
900 matches!(
901 last,
902 "String" | "Object" | "CharSequence" | "Comparable" | "Serializable"
903 ) || n.contains('<') || (last.len() <= 2 && last.chars().next().is_some_and(|c| c.is_uppercase()))
906 }
907 }
908 }
909
910
911 pub fn set_param_scope(&mut self, params: &[(String, JType)]) {
913 self.param_scope = params.to_vec();
914 }
915
916 pub fn is_param_name(&self, name: &str) -> bool {
919 self.param_scope.iter().any(|(n, _)| n == name)
920 }
921
922 pub fn set_facts(&mut self, facts: JavaFacts) {
927 self.facts = Some(facts);
928 }
929 pub fn clear_facts(&mut self) {
931 self.facts = None;
932 }
933
934 pub fn name_of_key(&self, k: u32) -> Option<String> {
935 Some(self.names.name(k).to_string())
936 }
937 pub fn clear_param_scope(&mut self) {
938 self.param_scope.clear();
939 }
940}
941
942fn type_base_name(n: &str) -> &str {
950 let no_args = match n.find('<') {
951 Some(i) => &n[..i],
952 None => n,
953 };
954 no_args.rsplit('.').next().unwrap_or(no_args)
955}
956
957fn fold_i64(op: BinOp, a: i64, b: i64) -> Option<i64> {
964 use BinOp::*;
965 Some(match op {
966 Add => a.wrapping_add(b),
967 Sub => a.wrapping_sub(b),
968 Mul => a.wrapping_mul(b),
969 Div => {
970 if b == 0 {
971 return None;
972 }
973 a.wrapping_div(b)
974 }
975 Rem => {
976 if b == 0 {
977 return None;
978 }
979 a.wrapping_rem(b)
980 }
981 Shl => a.wrapping_shl((b as u64 & 63) as u32),
982 Shr => a.wrapping_shr((b as u64 & 63) as u32),
983 UShr => ((a as u64).wrapping_shr((b as u64 & 63) as u32)) as i64,
984 BitAnd => a & b,
985 BitXor => a ^ b,
986 BitOr => a | b,
987 _ => return None,
988 })
989}
990
991fn fold_i32(op: BinOp, a: i64, b: i64) -> Option<i64> {
993 use BinOp::*;
994 let (x, y) = (a as i32, b as i32);
995 Some(match op {
996 Add => x.wrapping_add(y) as i64,
997 Sub => x.wrapping_sub(y) as i64,
998 Mul => x.wrapping_mul(y) as i64,
999 Div => {
1000 if y == 0 {
1001 return None;
1002 }
1003 x.wrapping_div(y) as i64
1004 }
1005 Rem => {
1006 if y == 0 {
1007 return None;
1008 }
1009 x.wrapping_rem(y) as i64
1010 }
1011 Shl => x.wrapping_shl((y as u32) & 31) as i64,
1012 Shr => x.wrapping_shr((y as u32) & 31) as i64,
1013 UShr => (x as u32).wrapping_shr((y as u32) & 31) as i64,
1014 BitAnd => (x & y) as i64,
1015 BitXor => (x ^ y) as i64,
1016 BitOr => (x | y) as i64,
1017 _ => return None,
1018 })
1019}
1020
1021fn cmp_i64(op: BinOp, x: i64, y: i64) -> Option<bool> {
1023 use BinOp::*;
1024 Some(match op {
1025 Lt => x < y,
1026 Le => x <= y,
1027 Gt => x > y,
1028 Ge => x >= y,
1029 Eq => x == y,
1030 Ne => x != y,
1031 _ => return None,
1032 })
1033}
1034
1035impl Lang for JavaAst {
1036 type Id = JavaId;
1037 type NameKey = u32;
1038
1039 fn kind(&self, id: JavaId) -> NodeKind {
1040 JavaAst::kind(self, id)
1041 }
1042
1043 fn children(&self, id: JavaId) -> &[JavaId] {
1044 JavaAst::children(self, id)
1045 }
1046
1047 fn set_child(&mut self, parent: JavaId, index: usize, new: JavaId) {
1048 self.nodes[parent.0 as usize].children.set(index, new);
1049 }
1050 fn remove_child(&mut self, parent: JavaId, index: usize) {
1051 self.nodes[parent.0 as usize].children.remove(index);
1052 }
1053 fn splice(&mut self, node: JavaId, index: usize, remove: usize, insert: Vec<JavaId>) {
1054 self.nodes[node.0 as usize].children.splice(index, remove, insert);
1055 }
1056
1057 fn build_return(&mut self, value: Option<JavaId>) -> JavaId {
1058 self.ret(value)
1059 }
1060 fn build_block(&mut self, stmts: Vec<JavaId>) -> JavaId {
1061 self.block(stmts)
1062 }
1063 fn build_unary(&mut self, op: UnOp, operand: JavaId) -> JavaId {
1064 self.un(op, operand)
1065 }
1066 fn build_bool(&mut self, v: bool) -> JavaId {
1067 self.lit(Lit::Bool(v))
1068 }
1069 fn build_int(&mut self, v: i64, long: bool) -> JavaId {
1070 self.build_int_impl(v, long)
1071 }
1072 fn build_str(&mut self, s: &str) -> JavaId {
1073 self.lit(Lit::Str(s.to_string()))
1074 }
1075 fn build_char(&mut self, c: char) -> JavaId {
1076 self.lit(Lit::Char(c))
1077 }
1078 fn build_bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> JavaId {
1079 self.bin(op, l, r)
1080 }
1081 fn build_ternary(&mut self, c: JavaId, a: JavaId, b: JavaId) -> JavaId {
1082 self.ternary(c, a, b)
1083 }
1084 fn build_assign(&mut self, target: JavaId, value: JavaId) -> JavaId {
1085 self.assign(target, value)
1086 }
1087
1088 fn fold_lit_bin(&mut self, op: BinOp, l: JavaId, r: JavaId) -> Option<JavaId> {
1091 let ll = self.literal(l)?;
1092 let rr = self.literal(r)?;
1093 if op == BinOp::Add {
1097 let str_of = |x: LitRef<'_>| -> Option<String> {
1098 match x {
1099 LitRef::Str(s) => Some(s.to_string()),
1100 LitRef::Char(c) => Some(c.to_string()),
1101 LitRef::Int(v) => Some(v.to_string()),
1102 LitRef::Long(v) => Some(v.to_string()),
1103 LitRef::Bool(b) => Some(b.to_string()),
1104 _ => None,
1105 }
1106 };
1107 if matches!(ll, LitRef::Str(_)) || matches!(rr, LitRef::Str(_)) {
1108 if let (Some(a), Some(b)) = (str_of(ll), str_of(rr)) {
1109 return Some(self.build_str(&format!("{a}{b}")));
1110 }
1111 }
1112 }
1113 let int_of = |x: LitRef<'_>| -> Option<i64> {
1116 match x {
1117 LitRef::Int(v) | LitRef::Long(v) => Some(v),
1119 LitRef::Char(c) => Some(c as u32 as i64),
1120 _ => None,
1121 }
1122 };
1123 let (a, b) = (int_of(ll)?, int_of(rr)?);
1124 let wide = matches!(ll, LitRef::Long(_)) || matches!(rr, LitRef::Long(_));
1125 let v = if wide { fold_i64(op, a, b) } else { fold_i32(op, a, b) }?;
1126 Some(self.build_int(v, wide))
1127 }
1128
1129 fn fold_lit_cmp(&mut self, op: BinOp, l: JavaId, r: JavaId) -> Option<bool> {
1130 let ll = self.literal(l)?;
1131 let rr = self.literal(r)?;
1132 let int_of = |x: LitRef<'_>| -> Option<i64> {
1134 match x {
1135 LitRef::Int(v) | LitRef::Long(v) => Some(v),
1136 LitRef::Char(c) => Some(c as u32 as i64),
1137 _ => None,
1138 }
1139 };
1140 if let (Some(x), Some(y)) = (int_of(ll), int_of(rr)) {
1141 return cmp_i64(op, x, y);
1142 }
1143 if let (LitRef::Bool(x), LitRef::Bool(y)) = (ll, rr) {
1146 if matches!(op, BinOp::Eq | BinOp::Ne) {
1147 return Some(if op == BinOp::Eq { x == y } else { x != y });
1148 }
1149 }
1150 None
1152 }
1153
1154 fn fold_lit_neg(&mut self, v: i64, wide: bool) -> Option<i64> {
1155 let n = -v;
1157 if !wide && n > i32::MAX as i64 {
1158 return None;
1159 }
1160 Some(n)
1161 }
1162
1163 fn reassoc_delta(
1164 &mut self,
1165 op1: BinOp,
1166 k1: i64,
1167 op2: BinOp,
1168 k2: i64,
1169 wide: bool,
1170 ) -> Option<ReassocOutcome<JavaId>> {
1171 use BinOp::*;
1172 let raw: i64 = match (op1, op2) {
1173 (BitXor, BitXor) => k1 ^ k2,
1174 (a, b) if matches!(a, Add | Sub) && matches!(b, Add | Sub) => {
1175 let sign = |o: BinOp| if o == Sub { -1i64 } else { 1i64 };
1176 sign(a) * k1 + sign(b) * k2
1177 }
1178 _ => return None,
1179 };
1180 let d = if wide { raw } else { raw as i32 as i64 };
1183 if d == 0 {
1184 return Some(ReassocOutcome::Neutral);
1185 }
1186 if op1 == BitXor && op2 == BitXor {
1187 return Some(ReassocOutcome::Lit(self.build_int(d, wide), BitXor));
1188 }
1189 if d > 0 {
1190 Some(ReassocOutcome::Lit(self.build_int(d, wide), Add))
1191 } else {
1192 let nd = if wide { d.checked_neg().unwrap_or(d) } else { (d as i32).wrapping_neg() as i64 };
1196 if nd > 0 {
1197 Some(ReassocOutcome::Lit(self.build_int(nd, wide), Sub))
1198 } else {
1199 Some(ReassocOutcome::Lit(self.build_int(d, wide), Add))
1200 }
1201 }
1202 }
1203
1204 fn copy_subtree(&mut self, id: JavaId) -> JavaId {
1205 let old_children: Vec<JavaId> = self.nodes[id.0 as usize].children.as_slice().to_vec();
1206 let mut children = Vec::with_capacity(old_children.len());
1207 for c in old_children {
1208 children.push(self.copy_subtree(c));
1209 }
1210 let data = self.nodes[id.0 as usize].data.clone();
1211 self.push(data, children)
1212 }
1213
1214 fn effect(&self, id: JavaId) -> Effect {
1215 if let Some(e) = self.effect_cache.get(id.0 as usize).and_then(|x| *x) {
1216 return e;
1217 }
1218 let mut e = self.own_effect(id);
1220 for &c in self.nodes[id.0 as usize].children.as_slice() {
1221 e = e.worst(self.effect(c));
1222 }
1223 e
1224 }
1225
1226 fn invalidate_effect(&mut self, id: JavaId) {
1227 if let Some(slot) = self.effect_cache.get_mut(id.0 as usize) {
1228 *slot = None;
1229 }
1230 self.events.invalidate(id.0 as usize);
1233 }
1234
1235 fn debug_verify_events(&self, stmt: JavaId) {
1236 if !verify_events_on() {
1237 return;
1238 }
1239 if let Some(indexed) = self.region_events(stmt) {
1240 let fresh = cure_tree::collect_region_events(self, stmt);
1241 let key = |e: &cure_engine::kind::RegionEvent<JavaId, u32>| {
1243 (e.key, matches!(e.kind, cure_engine::kind::EventKind::Use), e.node.0)
1244 };
1245 let a: Vec<_> = indexed.iter().map(key).collect();
1246 let b: Vec<_> = fresh.iter().map(key).collect();
1247 if a != b {
1248 eprintln!(
1249 "[EVENTS-STALE] stmt #{} kind={:?}: indexed {} events, fresh {} events",
1250 stmt.0, self.kind(stmt), a.len(), b.len()
1251 );
1252 }
1253 }
1254 }
1255
1256 fn region_events(
1257 &self,
1258 id: JavaId,
1259 ) -> Option<&[cure_engine::kind::RegionEvent<JavaId, u32>]> {
1260 let v = self.events.slot(id.0 as usize);
1262 if v.is_empty() {
1263 None
1264 } else {
1265 Some(v)
1266 }
1267 }
1268
1269 fn var_key(&self, id: JavaId) -> Option<u32> {
1270 self.names.var_key(id.0 as usize)
1271 }
1272
1273 fn fact_single_def(&self, key: u32) -> bool {
1276 match (&self.facts, self.names.name(key)) {
1277 (Some(f), n) => f.var(n).single_def,
1278 _ => false,
1279 }
1280 }
1281 fn fact_single_use(&self, key: u32) -> bool {
1282 match (&self.facts, self.names.name(key)) {
1283 (Some(f), n) => f.var(n).single_use,
1284 _ => false,
1285 }
1286 }
1287 fn fact_bool_valued(&self, key: u32) -> bool {
1288 match (&self.facts, self.names.name(key)) {
1289 (Some(f), n) => f.var(n).bool_valued,
1290 _ => false,
1291 }
1292 }
1293 fn fact_call_effect(&self, name: &str) -> Option<cure_engine::Effect> {
1294 self.facts
1295 .as_ref()
1296 .and_then(|f| {
1297 f.call_effects.get(name).map(|set| {
1298 set.iter().copied().fold(Effect::Pure, Effect::worst)
1299 })
1300 })
1301 }
1302
1303 fn node_index(&self, id: JavaId) -> usize {
1304 id.0 as usize
1305 }
1306 fn id_of_index(&self, idx: usize) -> JavaId {
1307 JavaId(idx as u32)
1308 }
1309
1310 fn is_opaque(&self, id: JavaId) -> bool {
1311 match self.data(id) {
1312 NodeData::Raw { .. } => true,
1314 NodeData::New { anon_raw: Some(_), .. } => true,
1317 _ => false,
1318 }
1319 }
1320
1321 fn own_effect(&self, id: JavaId) -> Effect {
1322 match self.data(id) {
1323 NodeData::Literal(_)
1324 | NodeData::This
1325 | NodeData::Super
1326 | NodeData::Paren
1327 | NodeData::Block
1328 | NodeData::Group
1329 | NodeData::Empty
1330 | NodeData::VarDecl { .. }
1331 | NodeData::Break { .. }
1332 | NodeData::Continue { .. }
1333 | NodeData::Try
1334 | NodeData::Catch { .. }
1335 | NodeData::Switch
1336 | NodeData::Case { .. }
1337 | NodeData::Label { .. }
1338 | NodeData::ExprStmt
1339 | NodeData::Ternary
1340 | NodeData::ArrayLit
1341 | NodeData::Lambda { .. }
1342 | NodeData::MethodRef { .. }
1343 | NodeData::InstanceOf { .. } => Effect::Pure,
1344 NodeData::VarRef { .. } => Effect::MayRead,
1345 NodeData::Member { .. } => Effect::MayThrow,
1346 NodeData::Index => Effect::MayThrow,
1347 NodeData::New { .. } | NodeData::NewArray { .. } => Effect::MayThrow,
1348 NodeData::Call => Effect::Unknown,
1349 NodeData::Assign { .. } => Effect::MayWrite,
1350 NodeData::Unary { op } => {
1351 if op.is_incdec() {
1352 Effect::MayWrite
1353 } else {
1354 Effect::Pure
1355 }
1356 }
1357 NodeData::Binary { op } => {
1358 if op.may_div_zero() {
1359 Effect::MayThrow
1360 } else {
1361 Effect::Pure
1362 }
1363 }
1364 NodeData::Cast { ty } => {
1365 if ty.is_ref() {
1366 Effect::MayThrow
1368 } else {
1369 Effect::Pure
1370 }
1371 }
1372 NodeData::For { .. } | NodeData::ForEach { .. } | NodeData::If => Effect::MayRead,
1373 NodeData::While | NodeData::DoWhile => Effect::MayRead,
1374 NodeData::Return | NodeData::Yield => Effect::MayThrow,
1375 NodeData::Throw => Effect::MayThrow,
1376 NodeData::Assert => Effect::MayThrow,
1377 NodeData::Synchronized => Effect::MayWrite,
1378 NodeData::Raw { .. } => Effect::Unknown,
1379 }
1380 }
1381
1382 fn is_bool(&self, id: JavaId) -> bool {
1383 match self.data(id) {
1384 NodeData::Literal(l) => matches!(l, Lit::Bool(_)),
1385 NodeData::Unary { op } => *op == UnOp::Not,
1386 NodeData::Binary { op } => {
1387 op.is_comparison() || matches!(op, BinOp::And | BinOp::Or)
1388 }
1389 NodeData::Ternary => {
1390 let ch = self.children(id);
1391 self.is_bool(ch[1]) && self.is_bool(ch[2])
1392 }
1393 NodeData::Paren => self.is_bool(self.children(id)[0]),
1394 NodeData::InstanceOf { .. } => true,
1395 NodeData::VarRef { .. } => self
1396 .var_types
1397 .get(id.0 as usize)
1398 .and_then(|t| t.as_ref())
1399 .is_some_and(|t| t.is_bool()),
1400 _ => false,
1401 }
1402 }
1403
1404 fn is_exact_int(&self, id: JavaId) -> bool {
1405 match self.data(id) {
1406 NodeData::Literal(l) => matches!(
1407 l,
1408 Lit::Int(_) | Lit::Long(_) | Lit::Char(_) | Lit::NumRaw { val: NumVal::Int(_) | NumVal::Long(_), .. }
1409 ),
1410 NodeData::VarRef { .. } => {
1411 self.var_types.get(id.0 as usize).and_then(|t| t.as_ref()).is_some_and(|t| t.is_integral())
1412 }
1413 NodeData::Binary { op } => {
1414 let ch = self.children(id);
1415 (op.is_arith() || op.is_comparison())
1416 && self.is_exact_int(ch[0])
1417 && self.is_exact_int(ch[1])
1418 }
1419 NodeData::Cast { ty } => ty.is_integral(),
1420 NodeData::Paren => self.is_exact_int(self.children(id)[0]),
1421 NodeData::Ternary => {
1422 let ch = self.children(id);
1423 self.is_exact_int(ch[1]) && self.is_exact_int(ch[2])
1424 }
1425 _ => false,
1426 }
1427 }
1428
1429 fn is_local_var(&self, id: JavaId) -> bool {
1430 matches!(self.data(id), NodeData::VarRef { .. }) && self.var_type(id).is_some()
1436 }
1437
1438 fn is_inferred_decl(&self, decl: JavaId) -> bool {
1439 matches!(self.data(decl), NodeData::VarDecl { ty: JType::Var, .. })
1440 }
1441
1442 fn is_char_decl(&self, decl: JavaId) -> bool {
1443 matches!(self.data(decl), NodeData::VarDecl { ty: JType::Char, .. })
1444 }
1445
1446 fn has_pattern_bindings(&self, root: JavaId) -> bool {
1447 let mut stack = vec![root];
1448 while let Some(n) = stack.pop() {
1449 if let NodeData::InstanceOf { bind: Some(_), .. } = self.data(n) {
1450 return true;
1451 }
1452 for &c in self.children(n) {
1453 stack.push(c);
1454 }
1455 }
1456 false
1457 }
1458
1459 fn is_long_decl(&self, decl: JavaId) -> bool {
1460 matches!(self.data(decl), NodeData::VarDecl { ty: JType::Long, .. })
1461 }
1462 fn is_wide_decl(&self, decl: JavaId) -> bool {
1463 !matches!(
1466 self.data(decl),
1467 NodeData::VarDecl { ty: JType::Byte | JType::Short | JType::Char, .. }
1468 )
1469 }
1470
1471 fn receiver_propagation_unsound(
1472 &self,
1473 decl: JavaId,
1474 value: JavaId,
1475 use_parent: Option<JavaId>,
1476 use_at_head: bool,
1477 ) -> bool {
1478 let is_receiver = matches!(use_parent, Some(p) if {
1481 matches!(self.data(p), NodeData::Member { .. }) && use_at_head
1482 });
1483 if is_receiver {
1484 if self.receiver_boxing_unsound(decl, value) {
1486 return true;
1487 }
1488 if self.receiver_new_type_unsound(decl, value) {
1499 return true;
1500 }
1501 let raw_ref = match self.data(decl) {
1505 NodeData::VarDecl { ty: JType::Ref(n), .. } => {
1506 !n.contains('<')
1507 && n.rsplit('.').next().is_some_and(|base| {
1508 self.raw_generic_names.contains(base)
1509 })
1510 }
1511 _ => false,
1512 };
1513 if !raw_ref {
1514 return false;
1515 }
1516 match self.data(value) {
1518 NodeData::Call => true,
1519 NodeData::New { ty, .. } => {
1520 matches!(ty, JType::Ref(n) if n.contains('<'))
1521 }
1522 _ => false,
1523 }
1524 } else {
1525 if matches!(use_parent, Some(p) if {
1532 matches!(self.data(p), NodeData::Index) && use_at_head
1533 }) {
1534 return self.index_base_newarray_unsound(decl, value);
1535 }
1536 false
1537 }
1538 }
1539
1540 fn bin_op(&self, id: JavaId) -> Option<BinOp> {
1541 match self.data(id) {
1542 NodeData::Binary { op } => Some(*op),
1543 _ => None,
1544 }
1545 }
1546 fn un_op(&self, id: JavaId) -> Option<UnOp> {
1547 match self.data(id) {
1548 NodeData::Unary { op } => Some(*op),
1549 _ => None,
1550 }
1551 }
1552 fn literal(&self, id: JavaId) -> Option<LitRef<'_>> {
1553 match self.data(id) {
1554 NodeData::Literal(l) => Some(match l {
1555 Lit::Bool(b) => LitRef::Bool(*b),
1556 Lit::Int(v) => LitRef::Int(*v),
1557 Lit::Long(v) => LitRef::Long(*v),
1558 Lit::Float(v) => LitRef::Float(*v),
1559 Lit::Double(v) => LitRef::Double(*v),
1560 Lit::Char(c) => LitRef::Char(*c),
1561 Lit::Str(s) => LitRef::Str(s),
1562 Lit::TextBlock(_) => return None,
1568 Lit::NumRaw { val, .. } => match val {
1569 NumVal::Int(v) => LitRef::Int(*v),
1570 NumVal::Long(v) => LitRef::Long(*v),
1571 NumVal::Float(v) => LitRef::Float(*v),
1572 NumVal::Double(v) => LitRef::Double(*v),
1573 },
1574 Lit::Null => LitRef::Null,
1575 }),
1576 _ => None,
1577 }
1578 }
1579 fn var_name(&self, id: JavaId) -> Option<&str> {
1580 match self.data(id) {
1581 NodeData::VarRef { name } => Some(self.sn(*name)),
1582 NodeData::VarDecl { name, .. } => Some(name),
1583 NodeData::ForEach { name, .. } | NodeData::Catch { name, .. } => Some(name),
1584 _ => None,
1585 }
1586 }
1587 fn member_name(&self, id: JavaId) -> Option<&str> {
1588 match self.data(id) {
1589 NodeData::Member { name } => Some(self.sn(*name)),
1590 NodeData::MethodRef { name } => Some(self.sn(*name)),
1591 _ => None,
1592 }
1593 }
1594 fn is_field_name(&self, name: &str) -> bool {
1595 self.field_types.contains_key(name)
1596 }
1597 fn payload_equal(&self, a: JavaId, b: JavaId) -> bool {
1598 if let (NodeData::Literal(x), NodeData::Literal(y)) = (self.data(a), self.data(b)) {
1601 return lit_value_eq(x, y);
1602 }
1603 self.data(a) == self.data(b)
1604 }
1605 fn assign_op(&self, id: JavaId) -> Option<BinOp> {
1606 match self.data(id) {
1607 NodeData::Assign { op } => *op,
1608 _ => None,
1609 }
1610 }
1611
1612 fn prepare(&mut self, root: JavaId) {
1616
1617 if cnt_prepare_on() {
1618 PREPARE_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1619 PREPARE_NODES.fetch_add(self.nodes.len(), std::sync::atomic::Ordering::Relaxed);
1620 }
1621 let n_nodes = self.nodes.len();
1625 let key_slots = self.names.key_slots();
1626 if key_slots < n_nodes {
1627 let names: Vec<Option<String>> = (key_slots..n_nodes)
1628 .map(|i| self.var_name(JavaId(i as u32)).map(|n| n.to_string()))
1629 .collect();
1630 self.names.extend_owned(key_slots, &names);
1631 }
1632 self.string_identity.set(None);
1634 self.events.resize(n_nodes);
1645 if self.events.is_clean() {
1646 if self.prepared_root == Some(root) {
1647 return;
1648 }
1649 self.rebuild_var_types(root);
1652 self.prepared_root = Some(root);
1653 return;
1654 }
1655 self.prepared_root = Some(root);
1656 self.events.mark_clean();
1657 for sr in cure_tree::stmt_roots(self, root) {
1658 let idx = sr.0 as usize;
1659 if self.events.slot(idx).is_empty() {
1660 let events = cure_tree::collect_region_events(self, sr);
1661 self.events.put(idx, events);
1662 }
1663 }
1664 let effects = cure_tree::rebuild_effects(self, n_nodes);
1667 self.effect_cache = effects;
1668 self.rebuild_var_types(root);
1669 }
1670}
1671
1672impl JavaAst {
1673 pub(crate) fn rebuild_var_types(&mut self, root: JavaId) {
1675 self.var_types.clear();
1677 self.var_types.resize(self.nodes.len(), None);
1678 #[derive(Clone)]
1679 enum Frame {
1680 Enter(JavaId),
1681 Exit,
1682 PopScope,
1683 Bind(String, JType),
1684 }
1685 let mut base: HashMap<String, JType> = HashMap::new();
1686 for (n, t) in &self.param_scope {
1687 base.insert(n.clone(), t.clone());
1688 }
1689 let mut scopes: Vec<HashMap<String, JType>> = vec![base];
1690 let mut stack = vec![Frame::Enter(root)];
1691 while let Some(f) = stack.pop() {
1692 match f {
1693 Frame::Enter(id) => {
1694 let node = &self.nodes[id.0 as usize];
1695 enum Action {
1696 None,
1697 Scope,
1698 Bind(String, JType),
1699 }
1700 let action = match &node.data {
1701 NodeData::Block | NodeData::For { .. } | NodeData::Try => Action::Scope,
1702 NodeData::VarDecl { name, ty, .. } => {
1703 scopes.last_mut().unwrap().insert(name.clone(), ty.clone());
1704 Action::None
1705 }
1706 NodeData::VarRef { name } => {
1707 if let Some(t) = scopes
1708 .iter()
1709 .rev()
1710 .find_map(|s| s.get(self.sn(*name)))
1711 .cloned()
1712 {
1713 self.var_types[id.0 as usize] = Some(t);
1714 }
1715 Action::None
1716 }
1717 NodeData::Catch { name, .. } => {
1718 Action::Bind(name.clone(), JType::Var)
1719 }
1720 NodeData::ForEach { name, ty } => {
1721 Action::Bind(name.clone(), ty.clone())
1722 }
1723 _ => Action::None,
1724 };
1725 stack.push(Frame::Exit);
1728 if matches!(action, Action::Scope | Action::Bind(_, _)) {
1729 scopes.push(HashMap::new());
1730 stack.push(Frame::PopScope);
1731 }
1732 for &c in node.children.as_slice().iter().rev() {
1733 stack.push(Frame::Enter(c));
1734 }
1735 if let Action::Bind(n, t) = action {
1736 stack.push(Frame::Bind(n, t));
1737 }
1738 }
1739 Frame::Bind(name, ty) => {
1740 scopes.last_mut().unwrap().insert(name, ty);
1741 }
1742 Frame::PopScope => {
1743 scopes.pop();
1744 }
1745 Frame::Exit => {}
1746 }
1747 }
1748 }
1749}
1750
1751impl JavaAst {
1763 fn receiver_new_type_unsound(&self, decl: JavaId, value: JavaId) -> bool {
1775 let NodeData::New { ty, .. } = self.data(value) else {
1776 return false;
1777 };
1778 let decl_ty = match self.data(decl) {
1779 NodeData::VarDecl { ty, .. } => ty,
1780 _ => return false,
1781 };
1782 if matches!(decl_ty, JType::Var) {
1785 return false;
1786 }
1787 let name = match ty {
1788 JType::Ref(n) => n.as_str(),
1789 _ => return true,
1791 };
1792 if name.ends_with("<>") {
1794 return true;
1795 }
1796 match decl_ty {
1797 JType::Ref(d) => type_base_name(d) != type_base_name(name),
1798 _ => true,
1800 }
1801 }
1802
1803 fn index_base_newarray_unsound(&self, decl: JavaId, value: JavaId) -> bool {
1809 let NodeData::NewArray { ty, dims, .. } = self.data(value) else {
1810 return false;
1811 };
1812 let decl_ty = match self.data(decl) {
1813 NodeData::VarDecl { ty, .. } => ty,
1814 _ => return false,
1815 };
1816 if matches!(decl_ty, JType::Var) {
1817 return false;
1818 }
1819 let mut elem: &JType = decl_ty;
1821 let mut nest = 0u16;
1822 while let JType::Array(inner) = elem {
1823 elem = inner.as_ref();
1824 nest += 1;
1825 }
1826 nest != *dims || *elem != *ty
1827 }
1828
1829 fn receiver_boxing_unsound(&self, decl: JavaId, value: JavaId) -> bool {
1830 let declared_ref = match self.data(decl) {
1831 NodeData::VarDecl { ty, .. } => ty.is_ref(),
1832 _ => false,
1833 };
1834 if !declared_ref {
1835 return false;
1836 }
1837 match self.data(value) {
1838 NodeData::Literal(
1839 Lit::Int(_) | Lit::Long(_) | Lit::Float(_) | Lit::Double(_) | Lit::Bool(_)
1840 | Lit::Char(_),
1841 ) => true,
1842 NodeData::VarRef { .. } => matches!(
1844 self.var_type(value),
1845 Some(
1846 JType::Int
1847 | JType::Long
1848 | JType::Byte
1849 | JType::Short
1850 | JType::Char
1851 | JType::Float
1852 | JType::Double
1853 | JType::Bool
1854 )
1855 ),
1856 NodeData::Unary { .. } | NodeData::Binary { .. } => true,
1858 NodeData::Ternary => true,
1862 NodeData::Cast { .. } => true,
1864 NodeData::InstanceOf { .. } => true,
1866 NodeData::Call | NodeData::Member { .. } | NodeData::Index => {
1872 match self.data(decl) {
1873 NodeData::VarDecl { ty: JType::Ref(n), .. } => {
1874 let boxed = [
1875 "Character", "Integer", "Long", "Boolean", "Double",
1876 "Float", "Byte", "Short", "Number", "AtomicInteger",
1877 "AtomicLong",
1878 ];
1879 n.rsplit('.').next().is_some_and(|b| boxed.contains(&b))
1880 }
1881 _ => false,
1882 }
1883 }
1884 _ => false,
1885 }
1886 }
1887
1888 pub fn intern_sym(&mut self, name: &str) -> Sym {
1891 Sym(self.names.intern(name))
1892 }
1893
1894 pub fn sn(&self, sym: Sym) -> &str {
1896 self.names.name(sym.0)
1897 }
1898
1899
1900
1901
1902}
1903
1904
1905pub fn unit_metrics(ast: &JavaAst, unit: &CompilationUnit) -> TreeMetrics {
1913 let mut total = TreeMetrics::default();
1914 for ty in &unit.types {
1915 metrics_of_type(ast, ty, &mut total);
1916 }
1917 total
1918}
1919
1920fn metrics_of_type(ast: &JavaAst, ty: &TypeDecl, total: &mut TreeMetrics) {
1921 for m in &ty.members {
1922 match m {
1923 Member::Method { body: Some(b), .. } | Member::Constructor { body: Some(b), .. } => {
1924 total.add(subtree_metrics(ast, *b));
1925 }
1926 Member::Initializer { body, .. } => {
1927 total.add(subtree_metrics(ast, *body));
1928 }
1929 Member::Type(t) => metrics_of_type(ast, t, total),
1930 _ => {}
1931 }
1932 }
1933}
1934
1935static ENV_CACHED: std::sync::OnceLock<(bool, bool)> = std::sync::OnceLock::new();
1936fn env_flags() -> (bool, bool) {
1937 *ENV_CACHED.get_or_init(|| {
1938 (
1939 std::env::var("CURE_CNT_PREPARE").is_ok(),
1940 std::env::var("CURE_VERIFY_EVENTS").is_ok(),
1941 )
1942 })
1943}
1944fn cnt_prepare_on() -> bool { env_flags().0 }
1945fn verify_events_on() -> bool { env_flags().1 }
1946
1947pub static PREPARE_CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1948pub static PREPARE_NODES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1949
1950pub static COLLECT_CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1951
1952#[cfg(test)]
1957mod tests {
1958 use super::*;
1959
1960 #[test]
1961 fn builder_smoke() {
1962 let mut a = JavaAst::new();
1963 let one = a.lit(Lit::Int(1));
1964 let x = a.var_decl("x", JType::Int, Some(one));
1965 let xv = a.var("x");
1966 let r = a.ret(Some(xv));
1967 let body = a.block(vec![x, r]);
1968 assert_eq!(a.kind(body), NodeKind::Block);
1969 assert_eq!(a.children(body).len(), 2);
1970 }
1971
1972 #[test]
1973 fn effect_aggregation() {
1974 let mut a = JavaAst::new();
1975 let f = a.plain_call("foo", vec![]);
1976 let r = a.ret(Some(f));
1977 let body = a.block(vec![r]);
1978 a.prepare(body);
1979 assert_eq!(a.effect(f), Effect::Unknown);
1980 assert_eq!(a.effect(r), Effect::Unknown);
1981 assert_eq!(a.effect(body), Effect::Unknown);
1982
1983 let mut b = JavaAst::new();
1984 let two = b.lit(Lit::Int(2));
1985 let three = b.lit(Lit::Int(3));
1986 let add = b.bin(BinOp::Add, two, three);
1987 let r2 = b.ret(Some(add));
1988 let body2 = b.block(vec![r2]);
1989 b.prepare(body2);
1990 assert_eq!(b.effect(add), Effect::Pure);
1991 assert_eq!(b.effect(r2), Effect::MayThrow);
1993 assert_eq!(b.effect(body2), Effect::MayThrow);
1994 }
1995
1996 #[test]
1997 fn var_type_resolution_and_shadowing() {
1998 let mut a = JavaAst::new();
1999 let one = a.lit(Lit::Int(1));
2001 let b2_decl = a.var_decl("b2", JType::Int, Some(one));
2002 let inner_b2 = a.var("b2");
2003 let inner_stmt = a.expr_stmt(inner_b2);
2004 let inner = a.block(vec![b2_decl, inner_stmt]);
2005 let tv = a.lit(Lit::Bool(true));
2006 let decl_b = a.var_decl("b", JType::Bool, Some(tv));
2007 let outer_b = a.var("b");
2008 let outer_stmt = a.expr_stmt(outer_b);
2009 let body = a.block(vec![decl_b, inner, outer_stmt]);
2010 a.prepare(body);
2011 assert!(a.is_bool(outer_b));
2012 assert!(a.var_type(inner_b2).is_some());
2014 }
2015
2016 #[test]
2017 fn raw_and_foreach_semantics() {
2018 let mut a = JavaAst::new();
2019 let one = a.lit(Lit::Int(1));
2021 let dx = a.var_decl("x", JType::Int, Some(one));
2022 let r = a.raw("broken ~!@ code");
2023 let sv = a.var("s");
2024 let call = a.method_call(sv, "len", vec![]);
2025 let es = a.expr_stmt(call);
2026 let body = a.block(vec![es]);
2027 let lst = a.var("list");
2028 let fe = a.for_each("s", JType::Ref("String".into()), lst, body);
2029 let all = a.block(vec![dx, r, fe]);
2030 a.prepare(all);
2031 assert_eq!(a.kind(r), NodeKind::Raw);
2032 assert_eq!(a.effect(r), Effect::Unknown);
2033 assert!(a.var_type(sv).is_some()); }
2035
2036 #[test]
2037 fn num_raw_literal() {
2038 let mut a = JavaAst::new();
2039 let l = a.lit(Lit::NumRaw {
2040 text: "0x1F".into(),
2041 val: NumVal::Int(31),
2042 });
2043 a.prepare(l);
2044 assert!(a.is_exact_int(l));
2045 assert!(matches!(a.literal(l), Some(LitRef::Int(31))));
2046 }
2047}