1mod run;
6
7pub use run::{MethodCall, OoHost, Returned, call_through_pointer, compare_references, invoke, jni_environment, self_reference};
8
9use numeric::assumptions::REFERENCES_KEPT;
10use std::collections::HashMap;
11
12pub const JAVA_LANG_OBJECT: &str = "java.lang.Object";
13
14pub const MAX_OBJECTS: usize = 1_000_000;
16pub const MAX_REFERENCES: usize = 1 << 23;
19pub const MAX_MEMORY: usize = 1 << 30;
21
22const GLOBAL: u32 = u32::MAX;
24
25pub struct Objects<C> {
28 pub classes: Vec<LoadedClass<C>>,
29 pub objects: Vec<Instance>,
30 pub jni: Option<usize>,
31 references: Vec<Reference>,
34 frames: Vec<LocalFrame>,
36 serials: u32,
37 events: Vec<String>,
39 event_index: HashMap<String, u32>,
40}
41
42struct Reference {
43 object: u32,
45 frame: u32,
47 made: u32,
48 freed: u32,
50}
51
52struct LocalFrame {
53 serial: u32,
54 first: u32,
56 pushed: bool,
58}
59
60pub enum Referent {
62 Null,
63 Object(u32),
64 Expired(String),
66 Unknown,
67}
68
69pub struct LoadedClass<C> {
70 pub external: String,
71 pub code: Option<C>,
73 pub parent: Option<usize>,
74 pub factory_object: u32,
76 pub factory_data: Option<usize>,
78 pub methods: Vec<Option<usize>>,
79 pub sources: Vec<String>,
81}
82
83pub struct Instance {
84 pub class: usize,
85 pub factory: bool,
87 pub parts: Vec<(usize, usize)>,
89}
90
91pub struct ClassCode<H> {
93 pub parent: String,
95 pub factory: Option<Part<H>>,
96 pub object: Option<Part<H>>,
97 pub methods: Vec<MethodCode<H>>,
98}
99
100pub struct Part<H> {
102 pub data: H,
103 pub records: Vec<u32>,
104}
105
106pub struct MethodCode<H> {
107 pub name: String,
108 pub factory: bool,
109 pub params: Vec<String>,
111 pub returns: Option<String>,
112 pub code: H,
113 pub own_records: usize,
115}
116
117#[derive(Clone, Copy, Debug, Default)]
119pub struct Frame {
120 pub method: Option<Running>,
121}
122
123#[derive(Clone, Copy, Debug)]
124pub struct Running {
125 pub class: usize,
127 pub factory: bool,
128 pub this: u32,
130 pub cell: usize,
133 pub frame: u32,
135 pub invoked: u32,
137}
138
139impl<C> Default for Objects<C> {
140 fn default() -> Self {
141 Self { classes: Vec::new(), objects: Vec::new(), jni: None, references: Vec::new(), frames: Vec::new(), serials: 0, events: Vec::new(), event_index: HashMap::new() }
142 }
143}
144
145impl<C> Objects<C> {
146 pub fn find(&self, external: &str) -> Option<usize> {
147 self.classes.iter().position(|c| c.external == external)
148 }
149
150 pub fn object(&self, id: u32) -> Option<&Instance> {
152 (id as usize).checked_sub(1).and_then(|i| self.objects.get(i))
153 }
154
155 pub fn add_object(&mut self, object: Instance) -> Result<u32, String> {
156 if self.objects.len() >= MAX_OBJECTS {
157 return Err(format!("the run unit created more than {MAX_OBJECTS} objects, which ironwork for COBOL never frees"));
158 }
159 self.objects.push(object);
160 Ok(self.objects.len() as u32)
161 }
162
163 pub fn event(&mut self, text: String) -> u32 {
165 if let Some(&n) = self.event_index.get(&text) {
166 return n;
167 }
168 let n = self.events.len() as u32;
169 self.event_index.insert(text.clone(), n);
170 self.events.push(text);
171 n
172 }
173
174 pub fn told(&self, event: u32) -> &str {
175 self.events.get(event as usize).map_or("", String::as_str)
176 }
177
178 fn make(&mut self, object: u32, frame: u32, made: u32) -> Result<u32, String> {
179 if self.references.len() >= MAX_REFERENCES {
180 return Err(format!(
181 "the run unit made more than {MAX_REFERENCES} object references, which ironwork for COBOL keeps so that an expired one can say where it expired (see {REFERENCES_KEPT})"
182 ));
183 }
184 self.references.push(Reference { object, frame, made, freed: 0 });
185 Ok(self.references.len() as u32)
186 }
187
188 pub fn local(&mut self, object: u32, made: u32) -> Result<u32, String> {
190 let frame = self.frames.last().map_or(0, |f| f.serial);
191 self.make(object, frame, made)
192 }
193
194 pub fn local_in(&mut self, frame: u32, object: u32, made: u32) -> Result<u32, String> {
196 self.make(object, frame, made)
197 }
198
199 pub fn global(&mut self, object: u32, made: u32) -> Result<u32, String> {
200 self.make(object, GLOBAL, made)
201 }
202
203 pub fn push_frame(&mut self, pushed: bool) -> (usize, u32) {
205 self.serials = if self.serials >= GLOBAL - 1 { 1 } else { self.serials + 1 };
206 self.frames.push(LocalFrame { serial: self.serials, first: self.references.len() as u32, pushed });
207 (self.frames.len() - 1, self.serials)
208 }
209
210 pub fn pop_frames(&mut self, depth: usize, event: u32) {
212 let Some(first) = self.frames.get(depth).map(|f| f.first as usize) else { return };
213 let serials: Vec<u32> = self.frames.drain(depth..).map(|f| f.serial).collect();
214 for r in self.references.iter_mut().skip(first) {
215 if r.freed == 0 && serials.contains(&r.frame) {
216 r.freed = event + 1;
217 }
218 }
219 }
220
221 pub fn pushed_frame(&self) -> Option<usize> {
223 self.frames.last().filter(|f| f.pushed).map(|_| self.frames.len() - 1)
224 }
225
226 pub fn referent(&self, value: u32) -> Referent {
227 if value == 0 {
228 return Referent::Null;
229 }
230 match self.references.get(value as usize - 1) {
231 None => Referent::Unknown,
232 Some(r) if r.freed == 0 => Referent::Object(r.object),
233 Some(r) => Referent::Expired(format!("{}; it {}", self.described(r), self.told(r.freed - 1))),
234 }
235 }
236
237 pub fn is_global(&self, value: u32) -> bool {
238 (value as usize).checked_sub(1).and_then(|i| self.references.get(i)).is_some_and(|r| r.frame == GLOBAL)
239 }
240
241 pub fn free(&mut self, value: u32, event: u32) {
243 if let Some(r) = (value as usize).checked_sub(1).and_then(|i| self.references.get_mut(i)) {
244 r.freed = event + 1;
245 }
246 }
247
248 fn described(&self, r: &Reference) -> String {
250 let kind = if r.frame == GLOBAL { "global" } else { "local" };
251 let object = match self.object(r.object) {
252 Some(o) if o.factory => format!("the factory object of {}", self.classes[o.class].external),
253 Some(o) => {
254 let class = &self.classes[o.class].external;
255 let article = if class.starts_with(['A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u']) { "an" } else { "a" };
256 format!("{article} {class} object")
257 }
258 None => "an object".into(),
259 };
260 format!("a {kind} reference to {object}, {}", self.told(r.made))
261 }
262}