use super::*;
use super::cics_bind;
use crate::cics::{self, CicsHost};
use rt::bms::Mapset;
use rt::unit::Loader;
use std::rc::Rc;
pub(super) use crate::cics::Handlers;
fn flow(f: cics::Flow) -> Flow {
match f {
cics::Flow::Next => Flow::Next,
cics::Flow::GoTo(p) => Flow::GoTo(p as usize),
cics::Flow::End(e) => Flow::End(e),
}
}
impl<'p> Machine<'p, '_, '_> {
pub(super) fn cics(&mut self, block: &'p ExecBlock) -> R<Flow> {
cics::in_task(self.unit, &block.command, block.pos)?;
if self.unit.observed() {
self.cics_sinks(block);
}
let command = self.bind_cics(block)?;
cics::run(self, &command, block.pos).map(flow)
}
fn cics_sinks(&mut self, block: &ExecBlock) {
for (r, sink) in cics_bind::sinks(block) {
if let Ok(loc) = self.locate(r) {
let text = self.page.decode(store::bytes(&self.unit.mem, loc));
self.sink(sink.kind(), block.pos, &text);
}
}
}
pub(crate) fn run_level(&mut self) -> R<Ending> {
self.run_taking_exits(None, true)
}
pub(super) fn run_called(&mut self, at: Option<(usize, usize)>) -> R<Ending> {
self.run_taking_exits(at, false)
}
fn run_taking_exits(&mut self, at: Option<(usize, usize)>, runs_level: bool) -> R<Ending> {
let mut ending = self.run_from(at);
loop {
let abend = match ending {
Err(abend) => abend,
done => return done,
};
match cics::abend_exit(self.unit, &mut self.cics_handlers, &abend, self.serial, runs_level)? {
None => return Err(abend),
Some(cics::ExitTarget::Label { paragraph, at, .. }) => ending = self.go_to(paragraph as usize, at),
Some(cics::ExitTarget::Program { name, commarea }) => {
let ending = cics::enter_exit_program(self, &name, commarea, abend.pos)?;
return Ok(if ending == Ending::StopRun { ending } else { Ending::Goback });
}
}
}
}
pub(crate) fn begin_task(&mut self, commarea: Option<usize>, length: usize) {
cics::begin_task(self.unit, self.page, length);
let eib = self.unit.eib;
self.bind(&[Some(eib), commarea]);
}
fn named(name: &str, pos: Pos) -> Ref {
Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos }
}
}
impl<'a, 'w> CicsHost<'w, &'a Ref, &'a Operand, &'a str> for Machine<'_, '_, 'w> {
fn handlers(&mut self) -> &mut Handlers {
&mut self.cics_handlers
}
fn content(&mut self, operand: &&'a Operand, pos: Pos) -> R<Vec<u8>> {
callee::content(self, &super::facts::chars(operand), pos)
}
fn integer_of(&mut self, operand: &&'a Operand, pos: Pos) -> R<i64> {
Machine::integer(self, &Expr::Operand((*operand).clone()), pos)
}
fn text(&self, text: &&'a str) -> String {
(*text).to_owned()
}
fn activation(&self) -> u64 {
self.serial
}
fn program_id(&self) -> String {
self.program.id.clone()
}
fn commarea(&self) -> Option<usize> {
let ordinal = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some("DFHCOMMAREA"));
ordinal.and_then(|o| self.linkage[o])
}
fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>> {
self.unit.library.mapset(name)
}
fn item_named(&mut self, name: &str, pos: Pos) -> R<Option<Loc>> {
let r = Self::named(name, pos);
match self.resolve(&r) {
Ok(Resolved::Item(_)) => self.locate(&r).map(Some),
_ => Ok(None),
}
}
fn locate_named(&mut self, name: &str, pos: Pos) -> R<Loc> {
self.locate(&Self::named(name, pos))
}
fn unaddressed(&mut self, place: &'a Ref) -> Option<usize> {
let Ok(Resolved::Item(i)) = self.resolve(place) else { return None };
let item = &self.layout.items[i];
item.linkage.filter(|&l| self.linkage[l as usize].is_none()).map(|_| item.size as usize)
}
fn run_program(&mut self, program: Option<Rc<Compiled>>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending> {
let first = self.first;
let Some(compiled) = program.as_deref().or(first) else {
return Err(Abend::ironwork("the CICS task's first program cannot be LINKed or XCTLed to from a function or a method", Pos::default()));
};
let handlers = if xctl { self.cics_handlers.xctl() } else { Handlers::default() };
Machine::activation(compiled, index, &mut *self.unit, self.main && xctl).and_then(|mut callee| {
let eib = callee.unit.eib;
callee.bind(&[Some(eib), commarea]);
(callee.cics_handlers, callee.first) = (handlers, first);
callee.run_level()
})
}
}
#[cfg(test)]
mod tests {
use crate::cics::{EIBAID, EIBCALEN, EIBCPOSN, EIBDATE, EIBFN, EIBRESP, EIBRESP2, EIBRSRCE, EIBTASKN, EIBTIME, EIBTRMID, EIBTRNID};
#[test]
fn eib_offsets_match_the_dfheiblk_layout() {
let source = " IDENTIFICATION DIVISION.\n PROGRAM-ID. EIBT.\n PROCEDURE DIVISION.\n EXEC CICS RETURN END-EXEC.\n";
let program = syntax::parse_all_with(source, &syntax::copy::Libraries::default()).unwrap().remove(0);
let compiled = crate::compile(program, &[]).unwrap_or_else(|e| panic!("{e:?}"));
let offset = |name: &str| compiled.layout.items.iter().find(|i| i.name.as_deref() == Some(name)).map(|i| i.offset as usize);
let fields = [
("EIBTIME", EIBTIME),
("EIBDATE", EIBDATE),
("EIBTRNID", EIBTRNID),
("EIBTASKN", EIBTASKN),
("EIBTRMID", EIBTRMID),
("EIBCPOSN", EIBCPOSN),
("EIBCALEN", EIBCALEN),
("EIBAID", EIBAID),
("EIBFN", EIBFN),
("EIBRSRCE", EIBRSRCE),
("EIBRESP", EIBRESP),
("EIBRESP2", EIBRESP2),
];
for (name, at) in fields {
assert_eq!(offset(name), Some(at), "{name}");
}
}
}