yog_script_analyze/
index.rs1use std::collections::HashMap;
8
9use yog_data::Schema;
10pub use yog_script_ids::jvm::NESTED;
12
13#[derive(Schema, Debug, Clone, Copy, Default, PartialEq, Eq)]
16pub enum Side {
17 #[default]
18 Shared,
19 Client,
20 Server,
21}
22
23#[derive(Schema, Debug, Clone, Default, PartialEq, Eq)]
26pub struct Member {
27 pub name: String,
28 pub desc: String,
29 pub access: u16,
30}
31
32#[derive(Schema, Debug, Clone, Default, PartialEq, Eq)]
34pub struct Class {
35 pub name: String,
36 pub side: Side,
37 pub access: u16,
38 pub extends: Option<String>,
39 pub implements: Vec<String>,
40 pub fields: Vec<Member>,
41 pub methods: Vec<Member>,
42}
43
44#[derive(Schema, Debug, Clone, Default, PartialEq, Eq)]
46pub struct ClassIndex {
47 pub mc: String,
48 pub classes: Vec<Class>,
49}
50
51pub mod access;
52pub mod namespace;
53
54pub struct Lookup<'i> {
57 by_name: HashMap<&'i str, &'i Class>,
58 by_simple: HashMap<&'i str, Vec<&'i Class>>,
59}
60
61impl ClassIndex {
62 pub fn lookup(&self) -> Lookup<'_> {
63 let mut by_name = HashMap::new();
64 let mut by_simple: HashMap<&str, Vec<&Class>> = HashMap::new();
65 for c in &self.classes {
66 by_name.insert(c.name.as_str(), c);
67 by_simple.entry(simple_name(&c.name)).or_default().push(c);
68 }
69 Lookup { by_name, by_simple }
70 }
71}
72
73pub fn simple_name(internal: &str) -> &str {
76 let tail = internal.rsplit('/').next().unwrap_or(internal);
77 tail.rsplit(NESTED).next().unwrap_or(tail)
78}
79
80impl<'i> Lookup<'i> {
81 pub fn class(&self, name: &str) -> Option<&'i Class> {
83 self.by_name.get(name).copied()
84 }
85
86 pub fn simple(&self, name: &str) -> &[&'i Class] {
88 self.by_simple
89 .get(name)
90 .map(Vec::as_slice)
91 .unwrap_or_default()
92 }
93
94 pub fn ancestry(&self, class: &'i Class) -> Vec<&'i Class> {
96 let mut out = vec![class];
97 while let Some(parent) = out
98 .last()
99 .and_then(|c| c.extends.as_deref())
100 .and_then(|p| self.class(p))
101 {
102 if out.iter().any(|c| c.name == parent.name) {
103 break;
104 }
105 out.push(parent);
106 }
107 out
108 }
109
110 pub fn methods(&self, class: &'i Class, name: &str) -> Vec<&'i Member> {
113 let mut out = Vec::new();
114 let mut seen = Vec::new();
115 self.collect_methods(class, name, &mut out, &mut seen);
116 out
117 }
118
119 fn collect_methods(
120 &self,
121 class: &'i Class,
122 name: &str,
123 out: &mut Vec<&'i Member>,
124 seen: &mut Vec<&'i str>,
125 ) {
126 if seen.contains(&class.name.as_str()) {
127 return;
128 }
129 seen.push(&class.name);
130 for m in class.methods.iter().filter(|m| m.name == name) {
131 if !out.iter().any(|o| o.desc == m.desc) {
132 out.push(m);
133 }
134 }
135 let parents = class.extends.iter().chain(&class.implements);
136 for p in parents.filter_map(|p| self.class(p)) {
137 self.collect_methods(p, name, out, seen);
138 }
139 }
140
141 pub fn field(&self, class: &'i Class, name: &str) -> Option<&'i Member> {
143 self.ancestry(class)
144 .into_iter()
145 .find_map(|c| c.fields.iter().find(|f| f.name == name))
146 }
147}