Skip to main content

ironwork_rt/oo/
mod.rs

1//! The run unit's classes and objects, and the references a program holds to them. `C` is the
2//! executor's handle to a loaded class definition, which the run unit keeps without looking inside.
3//! `run` runs INVOKE, SELF and the JNI services, with `C` an `Rc<ClassCode<H>>`.
4
5mod 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
14/// Objects a run unit may create before it abends; they are never freed.
15pub const MAX_OBJECTS: usize = 1_000_000;
16/// References a run unit may make before it abends; each is kept, so that an expired one can say
17/// where it expired.
18pub const MAX_REFERENCES: usize = 1 << 23;
19/// Run-unit memory past which creating objects and classes abends.
20pub const MAX_MEMORY: usize = 1 << 30;
21
22/// The frame a global reference belongs to.
23const GLOBAL: u32 = u32::MAX;
24
25/// The run unit's classes and objects, the references to them, and the JNI environment once a
26/// program uses JNIENVPTR.
27pub struct Objects<C> {
28    pub classes: Vec<LoadedClass<C>>,
29    pub objects: Vec<Instance>,
30    pub jni: Option<usize>,
31    /// Every reference made, local or global; an object reference holds a reference's position
32    /// plus one, and positions are never reused.
33    references: Vec<Reference>,
34    /// Local reference frames above the run unit's own, innermost last.
35    frames: Vec<LocalFrame>,
36    serials: u32,
37    /// How references were made and how they expired, each told once.
38    events: Vec<String>,
39    event_index: HashMap<String, u32>,
40}
41
42struct Reference {
43    /// The object's position plus one.
44    object: u32,
45    /// The serial of the local frame it belongs to (0 for the run unit's own), or GLOBAL.
46    frame: u32,
47    made: u32,
48    /// The event that freed it, plus one; 0 while it is valid.
49    freed: u32,
50}
51
52struct LocalFrame {
53    serial: u32,
54    /// The first reference made after the frame was pushed.
55    first: u32,
56    /// Pushed by PushLocalFrame, not by a method's invocation.
57    pushed: bool,
58}
59
60/// What an object reference's four bytes name.
61pub enum Referent {
62    Null,
63    Object(u32),
64    /// A reference that was freed, and the message that says how.
65    Expired(String),
66    Unknown,
67}
68
69pub struct LoadedClass<C> {
70    pub external: String,
71    /// None for a Java class, java.lang.Object among them.
72    pub code: Option<C>,
73    pub parent: Option<usize>,
74    /// The factory object's reference.
75    pub factory_object: u32,
76    /// Loaded storage of the factory data, and of each method's WORKING-STORAGE once it has run.
77    pub factory_data: Option<usize>,
78    pub methods: Vec<Option<usize>>,
79    /// The class's source table, its own source first by path when a program library supplied it.
80    pub sources: Vec<String>,
81}
82
83pub struct Instance {
84    pub class: usize,
85    /// A factory object, on which INVOKE runs factory methods.
86    pub factory: bool,
87    /// The loaded storage of each COBOL class's instance data, by class.
88    pub parts: Vec<(usize, usize)>,
89}
90
91/// A COBOL class, compiled; `H` is the executor's handle to a compiled program.
92pub struct ClassCode<H> {
93    /// The external name of the class it inherits.
94    pub parent: String,
95    pub factory: Option<Part<H>>,
96    pub object: Option<Part<H>>,
97    pub methods: Vec<MethodCode<H>>,
98}
99
100/// FACTORY or OBJECT WORKING-STORAGE, laid out as a program's, and where each record starts in it.
101pub 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    /// Java types of the parameters and of the returned item, as a JNI signature spells them.
110    pub params: Vec<String>,
111    pub returns: Option<String>,
112    pub code: H,
113    /// LINKAGE records the method declares; the records of its paragraph's data follow them.
114    pub own_records: usize,
115}
116
117/// The method an activation runs.
118#[derive(Clone, Copy, Debug, Default)]
119pub struct Frame {
120    pub method: Option<Running>,
121}
122
123#[derive(Clone, Copy, Debug)]
124pub struct Running {
125    /// The run-unit class that defines the method.
126    pub class: usize,
127    pub factory: bool,
128    /// The object SELF refers to, or the class's factory object.
129    pub this: u32,
130    /// Where SELF's four bytes are: zero until the method first reads SELF, then a local reference
131    /// of the method's frame.
132    pub cell: usize,
133    /// The serial of the method's local frame.
134    pub frame: u32,
135    /// The event that tells which method this is and where it was invoked.
136    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    /// An object by its position plus one.
151    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    /// An event's number, the same for the same words.
164    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    /// A local reference in the innermost frame.
189    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    /// A local reference in the frame with this serial.
195    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    /// A new local frame, for a method's invocation or PushLocalFrame: its depth and serial.
204    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    /// Pops the frame at `depth` and every frame above it, freeing their local references.
211    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    /// The depth of the innermost frame when PushLocalFrame pushed it.
222    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    /// Frees a valid reference, as DeleteLocalRef or DeleteGlobalRef does.
242    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    /// `a local reference to an Account object, made by ...`
249    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}