use super::{Code, Halt, R, Vm, not_yet};
use crate::abend::{Abend, Ending};
use crate::arith;
use crate::bms::Mapset;
use crate::callee;
use crate::cics::{self, CicsCommand, CicsHost, ExitTarget, Handlers};
use crate::lir::{Base, BlockId, Chars, CicsId, Operand, ParaId, PlaceId, Step, SymId};
use crate::storage::Loc;
use crate::store::{self, ProgramFacts};
use crate::unit::{Loader, RunUnit};
use crate::vocab::Pos;
use std::rc::Rc;
pub fn run_task<L: Loader<Rc<Code>>>(code: &Code, me: usize, unit: &mut RunUnit<'_, Rc<Code>, L>, commarea: Option<usize>, length: usize) -> Result<Ending, Halt> {
let lowered = code.lowered.as_ref().map_err(|why| not_yet(format!("a program that does not lower ({why})")))?;
let mut vm = Vm::activation(lowered, me, unit, true)?;
cics::begin_task(vm.unit, lowered.program.options.options.code_page(), length);
let eib = vm.unit.eib;
vm.bind_level(Some(eib), commarea);
vm.run_level()
}
impl<'p, L: Loader<Rc<Code>>> Vm<'p, '_, '_, L> {
pub(super) fn cics(&mut self, id: CicsId, pos: Pos) -> R<Step> {
let p = self.p;
let command = &p.services.cics[id as usize];
cics::in_task(self.unit, self.sym(command.name), pos)?;
if self.unit.observed() {
self.cics_sinks(command, pos)?;
}
let flow = cics::run(self, command, pos);
Ok(self.settle(flow)?.into())
}
fn cics_sinks(&mut self, command: &'p CicsCommand, pos: Pos) -> R<()> {
for &(place, sink) in &command.sinks {
match self.loc(place) {
Ok(loc) => {
let text = self.facts().page().decode(store::bytes(&self.unit.mem, loc));
self.sink(sink.kind(), pos, &text);
}
Err(Halt::Abend(_)) => {}
Err(stopped) => return Err(stopped),
}
}
Ok(())
}
pub(super) fn run_level(&mut self) -> R<Ending> {
self.run_taking_exits(None, true)
}
pub(super) fn run_called(&mut self, at: Option<(ParaId, BlockId)>) -> R<Ending> {
self.run_taking_exits(at, false)
}
fn run_taking_exits(&mut self, at: Option<(ParaId, BlockId)>, runs_level: bool) -> R<Ending> {
let mut ending = self.run_from(at);
loop {
let abend = match ending {
Err(Halt::Abend(abend)) => abend,
done => return done,
};
match cics::abend_exit(self.unit, &mut self.cics_handlers, &abend, self.serial, runs_level)? {
None => return Err(abend.into()),
Some(ExitTarget::Label { paragraph, at, .. }) => ending = self.go_to(paragraph, at),
Some(ExitTarget::Program { name, commarea }) => {
let ending = cics::enter_exit_program(self, &name, commarea, abend.pos);
let ending = self.settle(ending)?;
return Ok(if ending == Ending::StopRun { ending } else { Ending::Goback });
}
}
}
}
fn bind_level(&mut self, eib: Option<usize>, commarea: Option<usize>) {
let p = self.p;
for (&record, address) in p.storage.using.iter().zip([eib, commarea]) {
self.linkage[usize::from(record)] = address;
}
}
fn level(&mut self, code: Option<&Code>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending> {
let first = self.first;
let lowered = match code {
Some(code) => code.lowered.as_ref().map_err(|why| not_yet(format!("EXEC CICS LINK or XCTL of a program that does not lower ({why})")))?,
None => first.ok_or_else(|| Abend::ironwork("the CICS task's first program cannot be LINKed or XCTLed to from a function or a method", Pos::default()))?,
};
let (eib, handlers) = (self.unit.eib, if xctl { self.cics_handlers.xctl() } else { Handlers::default() });
let mut callee = Vm::activation(lowered, index, &mut *self.unit, self.main && xctl)?;
callee.bind_level(Some(eib), commarea);
(callee.cics_handlers, callee.first) = (handlers, first);
callee.run_level()
}
fn integer_value(&mut self, o: Operand, pos: Pos) -> R<i64> {
let val = self.value(o)?;
let v = arith::fixed_operand(val, 0, pos)?;
Ok(super::place::whole(&v, pos)?)
}
}
impl<'w, L: Loader<Rc<Code>>> CicsHost<'w, PlaceId, Operand, SymId> for Vm<'_, '_, 'w, L> {
fn handlers(&mut self) -> &mut Handlers {
&mut self.cics_handlers
}
fn content(&mut self, operand: &Operand, pos: Pos) -> Result<Vec<u8>, Abend> {
callee::content(self, &Chars::Value(*operand), pos)
}
fn integer_of(&mut self, operand: &Operand, pos: Pos) -> Result<i64, Abend> {
let n = self.integer_value(*operand, pos);
self.lift(n, pos)
}
fn text(&self, text: &SymId) -> String {
self.sym(*text).to_owned()
}
fn activation(&self) -> u64 {
self.serial
}
fn program_id(&self) -> String {
self.sym(self.p.id).to_owned()
}
fn commarea(&self) -> Option<usize> {
let records = self.p.items.iter().filter(|i| i.parent.is_none());
let record = records.filter_map(|i| Some((i.name?, i.linkage?))).find(|&(name, _)| self.sym(name) == "DFHCOMMAREA")?.1;
self.linkage.get(usize::from(record)).copied().flatten()
}
fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>> {
self.unit.library.mapset(name)
}
fn item_named(&mut self, _name: &str, pos: Pos) -> Result<Option<Loc>, Abend> {
let found = Err(not_yet("SEND MAP with no FROM, whose MAP is no literal that names the symbolic map of a data item"));
self.lift(found, pos)
}
fn locate_named(&mut self, _name: &str, pos: Pos) -> Result<Loc, Abend> {
let found = Err(not_yet("RECEIVE MAP with no INTO or SET, whose MAP is no literal that names the symbolic map of a data item"));
self.lift(found, pos)
}
fn unaddressed(&mut self, place: PlaceId) -> Option<usize> {
let place = &self.p.places[place as usize];
matches!(place.base, Base::Linkage(record) if self.linkage[usize::from(record)].is_none()).then_some(place.len as usize)
}
fn run_program(&mut self, program: Option<Rc<Code>>, index: usize, commarea: Option<usize>, xctl: bool) -> Result<Ending, Abend> {
let ending = self.level(program.as_deref(), index, commarea, xctl);
self.lift(ending, Pos::default())
}
}