1use super::{ClassCode, Instance, JAVA_LANG_OBJECT, LoadedClass, MAX_MEMORY, Objects, Part, Referent, Running};
8use crate::abend::{Abend, AbendCode, Ending};
9use crate::callee::{self, By, Callee};
10use crate::display::utf16_text;
11use crate::host::Values;
12use crate::jni;
13use crate::lir::{CallArg, InvokePlan, MethodName, Receiver, Step};
14use crate::storage::{Kind, Loc, Val};
15use crate::store::{self, ProgramFacts};
16use crate::unit::{ADDRESS_BASE, Loaded, Loader, RETURN_CODE, RunUnit, UnitHost};
17use crate::vocab::{Figurative, Pos};
18use numeric::assumptions::{EXPIRED_REFERENCE_ABENDS, LOCAL_FRAMES};
19use numeric::precision::{Fixed, Places};
20use std::cmp::Ordering;
21use std::rc::Rc;
22
23type R<T> = Result<T, Abend>;
24
25pub trait OoHost<'w, P: Copy, O, S>: Values<P, O> + UnitHost<'w> {
28 fn running(&self) -> Option<Running>;
29 fn program_id(&self) -> String;
30 fn symbol(&mut self, symbol: &S, pos: Pos) -> R<String>;
32 fn place_name(&self, place: P) -> String;
34 fn operand_name(&self, operand: &O) -> Option<String>;
36 fn invoke_argument(&mut self, operand: &O, java: &str, pos: Pos) -> R<Vec<u8>>;
38 fn initialize_data(&mut self, data: Self::Program, index: usize) -> R<()>;
40 fn run_method(&mut self, call: MethodCall<Self::Program>, pos: Pos) -> R<Returned>;
42}
43
44pub struct Returned {
46 pub ending: R<Ending>,
47 pub value: Option<(String, Val)>,
48}
49
50pub struct MethodCall<H> {
54 pub code: H,
55 pub storage: usize,
56 pub records: Vec<(usize, usize)>,
57 pub arguments: Vec<Option<usize>>,
58 pub running: Running,
59}
60
61enum Target {
63 Class(usize),
64 Object(u32),
65 Super { this: u32, start: usize, factory: bool },
66}
67
68enum Found {
70 Cobol { class: usize, method: usize },
71 ObjectEquals,
73 Java(String),
74 Missing,
75}
76
77enum Argument {
80 Bytes(Vec<u8>),
81 Object(Option<u32>),
82}
83
84const JNI_TAG: u32 = 0x7F00_0000;
86
87const OBJECT_METHODS: &[(&str, &[&str], Option<&str>)] = &[
89 ("hashCode", &[], Some("I")),
90 ("toString", &[], Some("Ljava/lang/String;")),
91 ("getClass", &[], Some("Ljava/lang/Class;")),
92 ("notify", &[], None),
93 ("notifyAll", &[], None),
94 ("wait", &[], None),
95 ("wait", &["J"], None),
96 ("wait", &["J", "I"], None),
97 ("clone", &[], Some("Ljava/lang/Object;")),
98 ("finalize", &[], None),
99];
100
101fn java(what: String, class: &str, pos: Pos) -> Abend {
102 Abend {
103 code: AbendCode::Java,
104 message: format!("{what} was reached: {class} is a Java class, and ironwork for COBOL checks Java classes but has no JVM to run them"),
105 pos,
106 file: None,
107 }
108}
109
110fn is_reference(java: &str) -> bool {
112 java.starts_with('L') || java.starts_with('[')
113}
114
115fn int(n: i128) -> Val {
116 Val::Num(Fixed::new(n, Places::new(9, 0)))
117}
118
119pub fn self_reference<H: Clone, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, running: Option<Running>, pos: Pos) -> R<Loc> {
121 let Some(m) = running else { return Err(Abend::ironwork("SELF outside a method", pos)) };
122 if unit.mem[m.cell..m.cell + 4] == [0; 4] {
123 let made = unit.oo.event(format!("SELF of {}", unit.oo.told(m.invoked)));
124 let reference = unit.oo.local_in(m.frame, m.this, made).map_err(|e| Abend::ironwork(e, pos))?;
125 unit.mem[m.cell..m.cell + 4].copy_from_slice(&reference.to_be_bytes());
126 }
127 Ok(Loc { offset: m.cell, len: 4, kind: Kind::ObjectReference, item: usize::MAX })
128}
129
130fn site<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, pos: Pos) -> String {
132 let id = x.program_id();
133 match x.running() {
134 Some(m) => format!("line {} of {}.{}", pos.line, x.unit().oo.classes[m.class].external, id),
135 None => format!("line {} of {}", pos.line, id),
136 }
137}
138
139fn referent<C>(objects: &Objects<C>, value: u32, what: &str, holder: &str, pos: Pos) -> R<Option<u32>> {
142 match objects.referent(value) {
143 Referent::Null => Ok(None),
144 Referent::Object(o) => Ok(Some(o)),
145 Referent::Expired(told) => Err(Abend::ironwork(format!("{what}: {holder} holds {told}, and IBM leaves using it unpredictable (see {EXPIRED_REFERENCE_ABENDS})"), pos)),
146 Referent::Unknown => Err(Abend::ironwork(format!("{what}: {holder} holds X'{value:08X}', which is not an object reference"), pos)),
147 }
148}
149
150fn local_reference<C>(objects: &mut Objects<C>, object: u32, made: String, pos: Pos) -> R<u32> {
152 let made = objects.event(made);
153 objects.local(object, made).map_err(|m| Abend::ironwork(m, pos))
154}
155
156fn self_object<H, L: Loader<H>>(unit: &RunUnit<'_, H, L>, m: Running, what: &str, pos: Pos) -> R<u32> {
158 let value = u32::from_be_bytes(unit.mem[m.cell..m.cell + 4].try_into().unwrap_or_default());
159 Ok(referent(&unit.oo, value, what, "SELF", pos)?.unwrap_or(m.this))
160}
161
162pub fn compare_references<C>(objects: &Objects<C>, names: impl FnOnce() -> (String, String), x: (&Val, Option<Loc>), y: (&Val, Option<Loc>), pos: Pos) -> R<Option<Ordering>> {
166 let reference = |l: Option<Loc>| l.is_some_and(|l| l.kind == Kind::ObjectReference);
167 let (Val::Address(p), Val::Address(q)) = (x.0, y.0) else { return Ok(None) };
168 if !reference(x.1) && !reference(y.1) {
169 return Ok(None);
170 }
171 let (a, b) = names();
172 let what = format!("{a} = {b}");
173 let same = referent(objects, *p, &what, &a, pos)? == referent(objects, *q, &what, &b, pos)?;
174 Ok(Some(if same { Ordering::Equal } else { Ordering::Less }))
175}
176
177fn room<H, L: Loader<H>>(unit: &RunUnit<'_, H, L>, size: usize, pos: Pos) -> R<()> {
178 if unit.mem.len() + size > MAX_MEMORY {
179 return Err(Abend::ironwork(format!("objects and classes take the run unit past ironwork's {MAX_MEMORY} bytes"), pos));
180 }
181 Ok(())
182}
183
184fn keep<H: Clone, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, compiled: Option<H>, bytes: &[u8], pos: Pos) -> R<usize> {
187 room(unit, bytes.len(), pos)?;
188 let base = unit.push_temporary(bytes);
189 let files = compiled.as_ref().map_or(0, |c| L::shape(c).0);
190 unit.programs.push(Loaded::new(compiled, String::new(), base, bytes.len(), files));
191 Ok(unit.programs.len() - 1)
192}
193
194pub fn jni_environment<H: Clone, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, pos: Pos) -> R<usize> {
196 if let Some(cell) = unit.oo.jni {
197 return Ok(cell);
198 }
199 let slots = jni::RESERVED + jni::FUNCTIONS.len();
200 let loaded = keep(unit, None, &vec![0; 8 + 4 * slots], pos)?;
201 let base = unit.programs[loaded].base;
202 let address = |offset: usize| ADDRESS_BASE + offset as u32;
203 let mut bytes = [address(base + 4).to_be_bytes(), address(base + 8).to_be_bytes()].concat();
204 for slot in 0..slots {
205 let value = if slot < jni::RESERVED { 0 } else { JNI_TAG + slot as u32 };
206 bytes.extend(value.to_be_bytes());
207 }
208 unit.mem[base..base + bytes.len()].copy_from_slice(&bytes);
209 unit.oo.jni = Some(base);
210 Ok(base)
211}
212
213fn part_storage<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, part: &Part<X::Program>, pos: Pos) -> R<usize> {
215 let data = part.data.clone();
216 let size = X::Loader::shape(&data).1;
217 let loaded = keep(x.unit(), Some(data.clone()), &vec![0; size], pos)?;
218 x.initialize_data(data, loaded)?;
219 x.unit().programs[loaded].active = false;
220 Ok(loaded)
221}
222
223fn load_class<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, external: &str, pos: Pos) -> R<usize> {
226 if let Some(c) = x.unit().oo.find(external) {
227 return Ok(c);
228 }
229 let (code, sources) = match x.unit().library.class(external).map_err(|m| Abend::ironwork(m, pos))? {
230 None => (None, Vec::new()),
231 Some(found) => (Some(found.code), found.sources),
232 };
233 let oo = &mut x.unit().oo;
234 let index = oo.classes.len();
235 let methods = code.as_ref().map_or(0, |c| c.methods.len());
236 oo.classes.push(LoadedClass { external: external.to_owned(), code: code.clone(), parent: None, factory_object: 0, factory_data: None, methods: vec![None; methods], sources });
237 let factory_object = oo.add_object(Instance { class: index, factory: true, parts: Vec::new() }).map_err(|m| Abend::ironwork(m, pos))?;
238 oo.classes[index].factory_object = factory_object;
239 let Some(code) = code else { return Ok(index) };
240 let parent = load_class(x, &code.parent, pos)?;
241 let oo = &mut x.unit().oo;
242 let mut at = Some(parent);
243 for _ in 0..=oo.classes.len() {
244 match at {
245 Some(c) if c == index => return Err(Abend::ironwork(format!("class {external} inherits from itself"), pos)),
246 Some(c) => at = oo.classes[c].parent,
247 None => break,
248 }
249 }
250 oo.classes[index].parent = Some(parent);
251 if let Some(part) = &code.factory {
252 let loaded = part_storage(x, part, pos)?;
253 x.unit().oo.classes[index].factory_data = Some(loaded);
254 }
255 Ok(index)
256}
257
258fn chain<C>(objects: &Objects<C>, class: usize) -> Vec<usize> {
260 let mut out = vec![class];
261 while let Some(p) = objects.classes[*out.last().unwrap()].parent {
262 if out.contains(&p) {
263 break;
264 }
265 out.push(p);
266 }
267 out
268}
269
270fn object<C>(objects: &Objects<C>, id: u32, what: &str, pos: Pos) -> R<(usize, bool)> {
271 objects.object(id).map(|o| (o.class, o.factory)).ok_or_else(|| Abend::ironwork(format!("{what}: no object {id}"), pos))
272}
273
274fn target<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, receiver: &Receiver<P, S>, what: &str, pos: Pos) -> R<Target> {
275 match receiver {
276 Receiver::SelfRef | Receiver::Super => {
277 let name = if matches!(receiver, Receiver::SelfRef) { "SELF" } else { "SUPER" };
278 let Some(m) = x.running() else { return Err(Abend::ironwork(format!("INVOKE {name} outside a method"), pos)) };
279 let unit = x.unit();
280 let this = self_object(unit, m, what, pos)?;
281 if matches!(receiver, Receiver::SelfRef) {
282 return Ok(Target::Object(this));
283 }
284 let start = unit.oo.classes[m.class].parent.ok_or_else(|| Abend::ironwork("INVOKE SUPER: the class has no parent", pos))?;
285 Ok(Target::Super { this, start, factory: m.factory })
286 }
287 Receiver::Class { external, .. } => {
288 let external = x.symbol(external, pos)?;
289 Ok(Target::Class(load_class(x, &external, pos)?))
290 }
291 Receiver::Object(place) => {
292 let loc = x.locate(*place, false)?;
293 let name = x.place_name(*place);
294 let value = match <[u8; 4]>::try_from(store::bytes(x.mem(), loc)) {
295 Ok(bytes) if loc.kind == Kind::ObjectReference => u32::from_be_bytes(bytes),
296 _ => return Err(Abend::ironwork(format!("INVOKE {name}: not an object reference"), pos)),
297 };
298 match referent(&x.unit().oo, value, what, &name, pos)? {
299 Some(object) => Ok(Target::Object(object)),
300 None => Err(Abend::ironwork(format!("{what}: the object reference {name} is NULL"), pos)),
301 }
302 }
303 }
304}
305
306fn target_name<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, receiver: &Receiver<P, S>, pos: Pos) -> R<String> {
308 Ok(match receiver {
309 Receiver::SelfRef => "SELF".into(),
310 Receiver::Super => "SUPER".into(),
311 Receiver::Class { name, .. } => x.symbol(name, pos)?,
312 Receiver::Object(place) => x.place_name(*place),
313 })
314}
315
316fn method_name<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, method: &MethodName<P, S>, pos: Pos) -> R<String> {
317 Ok(match method {
318 MethodName::New => "NEW".into(),
319 MethodName::Named(n) => x.symbol(n, pos)?,
320 MethodName::Dynamic(place) => {
321 let loc = x.locate(*place, false)?;
322 let page = x.facts().page();
323 let bytes = store::bytes(x.mem(), loc);
324 let text = if loc.kind == Kind::National { utf16_text(bytes) } else { page.decode(bytes) };
325 let name = text.trim_end_matches(' ').to_owned();
326 if name.is_empty() {
327 return Err(Abend::ironwork(format!("INVOKE: {} holds no method name", x.place_name(*place)), pos));
328 }
329 name
330 }
331 })
332}
333
334fn find<H>(objects: &Objects<Rc<ClassCode<H>>>, start: usize, factory: bool, name: &str, params: &[String], returns: Option<&str>) -> Found {
335 for class in chain(objects, start) {
336 let c = &objects.classes[class];
337 match &c.code {
338 Some(code) => {
339 if let Some(method) = code.methods.iter().position(|m| m.factory == factory && m.name == name && m.params == params && m.returns.as_deref() == returns) {
340 return Found::Cobol { class, method };
341 }
342 }
343 None if c.external == JAVA_LANG_OBJECT => {
344 if factory {
345 return Found::Missing;
346 }
347 if name == "equals" && params == ["Ljava/lang/Object;"] && returns == Some("Z") {
348 return Found::ObjectEquals;
349 }
350 let java = OBJECT_METHODS.iter().any(|(n, p, r)| *n == name && *p == params && *r == returns);
351 return if java { Found::Java(c.external.clone()) } else { Found::Missing };
352 }
353 None => return Found::Java(c.external.clone()),
354 }
355 }
356 Found::Missing
357}
358
359fn no_method<P, O, S>(plan: &InvokePlan<P, O, S>, what: String, pos: Pos) -> R<Step> {
360 if plan.on_exception {
361 return Ok(Step::Arm(1));
362 }
363 Err(Abend {
364 code: AbendCode::user(4038),
365 message: format!("{what}: no method matches it, and the INVOKE has no ON EXCEPTION (a severity-3 Language Environment condition)"),
366 pos,
367 file: None,
368 })
369}
370
371fn succeeded<P, O, S>(plan: &InvokePlan<P, O, S>) -> Step {
372 if plan.on_exception || plan.not_on_exception { Step::Arm(0) } else { Step::Next }
373}
374
375pub fn invoke<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, plan: &InvokePlan<P, O, S>, pos: Pos) -> R<Step> {
379 if x.unit().cics.is_some() {
380 return Err(Abend::ironwork("INVOKE was reached in a CICS task, where a COBOL program with object-oriented syntax cannot run", pos));
381 }
382 x.unit().unfollowed("object-oriented COBOL and calls through pointers");
383 let name = method_name(x, &plan.method, pos)?;
384 let written = target_name(x, &plan.receiver, pos)?;
385 let what = format!("INVOKE {written} \"{name}\"");
386 let receiver = target(x, &plan.receiver, &what, pos)?;
387 let mut params = Vec::new();
388 let mut arguments = Vec::new();
389 for (n, (op, java)) in plan.args.iter().enumerate() {
390 let java = x.symbol(java, pos)?;
391 let bytes = x.invoke_argument(op, &java, pos)?;
392 arguments.push(if is_reference(&java) {
393 let holder = x.operand_name(op).unwrap_or_else(|| format!("argument {}", n + 1));
394 Argument::Object(referent(&x.unit().oo, u32::from_be_bytes(bytes.as_slice().try_into().unwrap_or_default()), &what, &holder, pos)?)
395 } else {
396 Argument::Bytes(bytes)
397 });
398 params.push(java);
399 }
400 if matches!(plan.method, MethodName::New) {
401 let Target::Class(class) = receiver else { return Err(Abend::ironwork("INVOKE ... NEW takes a class-name", pos)) };
402 return new_object(x, plan, &written, class, ¶ms, pos);
403 }
404 let returns = match &plan.returning {
405 None => None,
406 Some((_, java)) => Some(x.symbol(java, pos)?),
407 };
408 let (this, start, factory) = match receiver {
409 Target::Class(c) => (x.unit().oo.classes[c].factory_object, c, true),
410 Target::Object(o) => {
411 let (class, factory) = object(&x.unit().oo, o, &what, pos)?;
412 (o, class, factory)
413 }
414 Target::Super { this, start, factory } => (this, start, factory),
415 };
416 match find(&x.unit().oo, start, factory, &name, ¶ms, returns.as_deref()) {
417 Found::Missing => no_method(plan, what, pos),
418 Found::Java(class) => Err(java(what, &class, pos)),
419 Found::ObjectEquals => {
420 let same = matches!(arguments.first(), Some(Argument::Object(Some(other))) if *other == this);
421 if let Some((place, _)) = &plan.returning {
422 let dest = x.locate(*place, false)?;
423 x.assign(dest, Val::Bytes(vec![u8::from(same)]), None, pos)?;
424 }
425 Ok(succeeded(plan))
426 }
427 Found::Cobol { class, method } => {
428 x.unit().enter(pos)?;
429 let step = run_method(x, plan, class, method, this, arguments, &what, pos);
430 x.unit().depth -= 1;
431 step
432 }
433 }
434}
435
436fn new_object<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, plan: &InvokePlan<P, O, S>, written: &str, class: usize, params: &[String], pos: Pos) -> R<Step> {
439 let what = format!("INVOKE {written} NEW");
440 let oo = &x.unit().oo;
441 let chain = chain(oo, class);
442 if let Some(&j) = chain.iter().find(|&&c| oo.classes[c].code.is_none() && oo.classes[c].external != JAVA_LANG_OBJECT) {
443 return Err(java(what, &oo.classes[j].external.clone(), pos));
444 }
445 if !params.is_empty() {
446 return no_method(plan, format!("{what} USING {} arguments", params.len()), pos);
447 }
448 let mut parts = Vec::new();
449 for &c in chain.iter().rev() {
450 if let Some(code) = x.unit().oo.classes[c].code.clone()
451 && let Some(part) = &code.object
452 {
453 parts.push((c, part_storage(x, part, pos)?));
454 }
455 }
456 let object = x.unit().oo.add_object(Instance { class, factory: false, parts }).map_err(|m| Abend::ironwork(m, pos))?;
457 let here = site(x, pos);
458 let made = format!("made by INVOKE {} NEW at {here}", x.unit().oo.classes[class].external);
459 let reference = local_reference(&mut x.unit().oo, object, made, pos)?;
460 if let Some((place, _)) = &plan.returning {
461 let dest = x.locate(*place, false)?;
462 x.assign(dest, Val::Address(reference), None, pos)?;
463 }
464 Ok(succeeded(plan))
465}
466
467#[allow(clippy::too_many_arguments)]
473fn run_method<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, plan: &InvokePlan<P, O, S>, class: usize, k: usize, this: u32, arguments: Vec<Argument>, what: &str, pos: Pos) -> R<Step> {
474 let code = x.unit().oo.classes[class].code.clone().ok_or_else(|| Abend::ironwork("not a COBOL class", pos))?;
475 let method = &code.methods[k];
476 let this = if method.factory { x.unit().oo.classes[class].factory_object } else { this };
477 let storage = match x.unit().oo.classes[class].methods[k] {
478 Some(s) => s,
479 None => {
480 let size = X::Loader::shape(&method.code).1;
481 let s = keep(x.unit(), Some(method.code.clone()), &vec![0; size], pos)?;
482 x.unit().oo.classes[class].methods[k] = Some(s);
483 s
484 }
485 };
486 let (part, data) = if method.factory {
487 (code.factory.as_ref(), x.unit().oo.classes[class].factory_data)
488 } else {
489 let data = x.unit().oo.object(this).and_then(|o| o.parts.iter().find(|(c, _)| *c == class)).map(|(_, l)| *l);
490 (code.object.as_ref(), data)
491 };
492 let here = site(x, pos);
493 let unit = x.unit();
494 let base = data.map(|d| unit.programs[d].base);
495 let return_code = [unit.mem[RETURN_CODE], unit.mem[RETURN_CODE + 1]];
496 let mark = unit.mem.len();
497 let invoked_text = format!("method \"{}\" of {}, invoked at {here}", method.name, unit.oo.classes[class].external);
498 let invoked = unit.oo.event(invoked_text.clone());
499 let (depth, frame) = unit.oo.push_frame(false);
500 let mut addresses = Vec::new();
501 for (n, argument) in arguments.into_iter().enumerate() {
502 let bytes = match argument {
503 Argument::Bytes(b) => b,
504 Argument::Object(None) => vec![0; 4],
505 Argument::Object(Some(object)) => {
506 let made = unit.oo.event(format!("received as argument {} by {invoked_text}", n + 1));
507 unit.oo.local_in(frame, object, made).map_err(|m| Abend::ironwork(m, pos))?.to_be_bytes().to_vec()
508 }
509 };
510 addresses.push(Some(unit.push_temporary(&bytes)));
511 }
512 let cell = unit.push_temporary(&[0; 4]);
513 let records = match (part, base) {
514 (Some(part), Some(base)) => part.records.iter().enumerate().map(|(n, offset)| (method.own_records + n, base + *offset as usize)).collect(),
515 _ => Vec::new(),
516 };
517 let running = Running { class, factory: method.factory, this, cell, frame, invoked };
518 let call = MethodCall { code: method.code.clone(), storage, records, arguments: addresses, running };
519 let callee = Callee { index: storage, by: By::Invoke, mark: Some(mark), pos };
520 let (ending, returned) = callee::run(x, &callee, |x| x.run_method(call, pos).map(|r| (r.ending, r.value)))?;
521 let unit = x.unit();
522 let ending = ending.map_err(|mut abend| {
523 if abend.file.is_none() {
524 abend.file = Some(unit.oo.classes[class].sources.get(abend.pos.file as usize).cloned().unwrap_or_default());
525 }
526 abend
527 });
528 if ending? == Ending::StopRun {
529 return Ok(Step::End(Ending::StopRun));
530 }
531 let mut returned_object = None;
532 if let Some((item, Val::Address(value))) = &returned
533 && method.returns.as_deref().is_some_and(is_reference)
534 {
535 let holder = format!("{item}, the RETURNING item of method \"{}\",", method.name);
536 returned_object = Some(referent(&unit.oo, *value, what, &holder, pos)?);
537 }
538 let mut returned = returned.map(|(_, value)| value);
539 let expired = unit.oo.event(format!("expired when {invoked_text}, returned"));
540 unit.oo.pop_frames(depth, expired);
541 if let Some(object) = returned_object {
542 let value = match object {
543 None => 0,
544 Some(o) => local_reference(&mut unit.oo, o, format!("returned by {invoked_text}"), pos)?,
545 };
546 returned = Some(Val::Address(value));
547 }
548 unit.mem[RETURN_CODE..RETURN_CODE + 2].copy_from_slice(&return_code);
549 if let (Some((place, _)), Some(val)) = (&plan.returning, returned) {
550 let dest = x.locate(*place, false)?;
551 x.assign(dest, val, None, pos)?;
552 }
553 Ok(succeeded(plan))
554}
555
556pub fn call_through_pointer<'w, P: Copy, O, S, X: OoHost<'w, P, O, S>>(x: &mut X, pointer: P, args: &[CallArg<P, O>], returning: Option<P>, pos: Pos) -> R<()> {
560 x.unit().unfollowed("object-oriented COBOL and calls through pointers");
561 let name = x.place_name(pointer);
562 let loc = x.locate(pointer, false)?;
563 let Ok(value) = <[u8; 4]>::try_from(store::bytes(x.mem(), loc)).map(u32::from_be_bytes) else {
564 return Err(Abend::ironwork(format!("CALL {name}: a reference-modified function-pointer"), pos));
565 };
566 let service = value.checked_sub(JNI_TAG).and_then(|slot| jni::function(slot as usize));
567 let Some(service) = service else {
568 return Err(Abend::ironwork(format!("CALL {name}: X'{value:08X}' is not a JNI service, the only entry a function-pointer can hold here"), pos));
569 };
570 let mut values = Vec::new();
571 for arg in args {
572 values.push(match arg {
573 CallArg::Value(op) => x.value(op, pos)?,
574 CallArg::Omitted => Val::Address(0),
575 CallArg::Reference(_) | CallArg::Content(_) => return Err(Abend::ironwork(format!("CALL {service}: a JNI service takes its arguments by value"), pos)),
576 });
577 }
578 let what = format!("CALL {service}");
579 let holder = |x: &X, n: usize| match args.get(n) {
580 Some(CallArg::Value(op)) => x.operand_name(op),
581 _ => None,
582 }
583 .unwrap_or_else(|| format!("argument {n}"));
584 let value = |n: usize| match values.get(n) {
585 Some(Val::Address(a)) => Ok(*a),
586 Some(Val::Fig(Figurative::Null)) => Ok(0),
587 _ => Err(Abend::ironwork(format!("CALL {service}: argument {n} is not an object reference"), pos)),
588 };
589 let referent_of = |x: &mut X, n: usize, value: u32| {
590 let holder = holder(x, n);
591 referent(&x.unit().oo, value, &what, &holder, pos)
592 };
593 let here = site(x, pos);
594 let result = match service {
595 "NewGlobalRef" | "NewLocalRef" => {
596 let object = referent_of(x, 1, value(1)?)?;
597 let reference = match object {
598 None => 0,
599 Some(o) => {
600 let oo = &mut x.unit().oo;
601 let made = oo.event(format!("made by {service} at {here}"));
602 let made = if service == "NewGlobalRef" { oo.global(o, made) } else { oo.local(o, made) };
603 made.map_err(|m| Abend::ironwork(m, pos))?
604 }
605 };
606 Some(Val::Address(reference))
607 }
608 "DeleteGlobalRef" | "DeleteLocalRef" => {
609 let reference = value(1)?;
610 if referent_of(x, 1, reference)?.is_some() {
611 let global = service == "DeleteGlobalRef";
612 if x.unit().oo.is_global(reference) != global {
613 let kind = if global { "local" } else { "global" };
614 return Err(Abend::ironwork(format!("{what}: {} holds a {kind} reference, which {service} does not delete (see {LOCAL_FRAMES})", holder(x, 1)), pos));
615 }
616 let oo = &mut x.unit().oo;
617 let event = oo.event(format!("was deleted by {service} at {here}"));
618 oo.free(reference, event);
619 }
620 None
621 }
622 "IsSameObject" => {
623 let same = referent_of(x, 1, value(1)?)? == referent_of(x, 2, value(2)?)?;
624 Some(Val::Bytes(vec![u8::from(same)]))
625 }
626 "GetObjectRefType" => {
627 let reference = value(1)?;
628 let kind = match referent_of(x, 1, reference)? {
629 None => 0,
630 Some(_) if x.unit().oo.is_global(reference) => 2,
631 Some(_) => 1,
632 };
633 Some(int(kind))
634 }
635 "PushLocalFrame" => {
636 x.unit().oo.push_frame(true);
637 Some(int(0))
638 }
639 "PopLocalFrame" => {
640 let Some(depth) = x.unit().oo.pushed_frame() else {
641 return Err(Abend::ironwork(format!("{what}: no frame PushLocalFrame pushed is open here (see {LOCAL_FRAMES})"), pos));
642 };
643 let object = referent_of(x, 1, value(1)?)?;
644 let oo = &mut x.unit().oo;
645 let expired = oo.event(format!("expired when PopLocalFrame at {here} freed its frame"));
646 oo.pop_frames(depth, expired);
647 let reference = match object {
648 None => 0,
649 Some(o) => local_reference(oo, o, format!("returned by PopLocalFrame at {here}"), pos)?,
650 };
651 Some(Val::Address(reference))
652 }
653 "EnsureLocalCapacity" => Some(int(0)),
654 "ExceptionOccurred" => Some(Val::Address(0)),
655 "ExceptionCheck" => Some(Val::Bytes(vec![0])),
656 "ExceptionClear" => None,
657 _ => {
658 return Err(Abend {
659 code: AbendCode::Java,
660 message: format!("CALL {name} was reached: {service} is a JNI service, and ironwork for COBOL has no JVM to run it"),
661 pos,
662 file: None,
663 });
664 }
665 };
666 if let (Some(target), Some(val)) = (returning, result) {
667 let dest = x.locate(target, false)?;
668 x.assign(dest, val, None, pos)?;
669 }
670 Ok(())
671}