1use super::*;
5use super::cics_bind;
6use crate::cics::{self, CicsHost};
7use rt::bms::Mapset;
8use rt::unit::Loader;
9use std::rc::Rc;
10
11pub(super) use crate::cics::Handlers;
12
13fn flow(f: cics::Flow) -> Flow {
14 match f {
15 cics::Flow::Next => Flow::Next,
16 cics::Flow::GoTo(p) => Flow::GoTo(p as usize),
17 cics::Flow::End(e) => Flow::End(e),
18 }
19}
20
21impl<'p> Machine<'p, '_, '_> {
22 pub(super) fn cics(&mut self, block: &'p ExecBlock) -> R<Flow> {
23 cics::in_task(self.unit, &block.command, block.pos)?;
24 if self.unit.observed() {
25 self.cics_sinks(block);
26 }
27 let command = self.bind_cics(block)?;
28 cics::run(self, &command, block.pos).map(flow)
29 }
30
31 fn cics_sinks(&mut self, block: &ExecBlock) {
35 for (r, sink) in cics_bind::sinks(block) {
36 if let Ok(loc) = self.locate(r) {
37 let text = self.page.decode(store::bytes(&self.unit.mem, loc));
38 self.sink(sink.kind(), block.pos, &text);
39 }
40 }
41 }
42
43 pub(crate) fn run_level(&mut self) -> R<Ending> {
48 self.run_taking_exits(None, true)
49 }
50
51 pub(super) fn run_called(&mut self, at: Option<(usize, usize)>) -> R<Ending> {
54 self.run_taking_exits(at, false)
55 }
56
57 fn run_taking_exits(&mut self, at: Option<(usize, usize)>, runs_level: bool) -> R<Ending> {
58 let mut ending = self.run_from(at);
59 loop {
60 let abend = match ending {
61 Err(abend) => abend,
62 done => return done,
63 };
64 match cics::abend_exit(self.unit, &mut self.cics_handlers, &abend, self.serial, runs_level)? {
65 None => return Err(abend),
66 Some(cics::ExitTarget::Label { paragraph, at, .. }) => ending = self.go_to(paragraph as usize, at),
67 Some(cics::ExitTarget::Program { name, commarea }) => {
68 let ending = cics::enter_exit_program(self, &name, commarea, abend.pos)?;
69 return Ok(if ending == Ending::StopRun { ending } else { Ending::Goback });
70 }
71 }
72 }
73 }
74
75 pub(crate) fn begin_task(&mut self, commarea: Option<usize>, length: usize) {
78 cics::begin_task(self.unit, self.page, length);
79 let eib = self.unit.eib;
80 self.bind(&[Some(eib), commarea]);
81 }
82
83 fn named(name: &str, pos: Pos) -> Ref {
85 Ref { name: name.to_owned(), qualifiers: Vec::new(), subscripts: Vec::new(), refmod: None, pos }
86 }
87}
88
89impl<'a, 'w> CicsHost<'w, &'a Ref, &'a Operand, &'a str> for Machine<'_, '_, 'w> {
90 fn handlers(&mut self) -> &mut Handlers {
91 &mut self.cics_handlers
92 }
93
94 fn content(&mut self, operand: &&'a Operand, pos: Pos) -> R<Vec<u8>> {
95 callee::content(self, &super::facts::chars(operand), pos)
96 }
97
98 fn integer_of(&mut self, operand: &&'a Operand, pos: Pos) -> R<i64> {
99 Machine::integer(self, &Expr::Operand((*operand).clone()), pos)
100 }
101
102 fn text(&self, text: &&'a str) -> String {
103 (*text).to_owned()
104 }
105
106 fn activation(&self) -> u64 {
107 self.serial
108 }
109
110 fn program_id(&self) -> String {
111 self.program.id.clone()
112 }
113
114 fn commarea(&self) -> Option<usize> {
115 let ordinal = self.layout.linkage_roots.iter().position(|&i| self.layout.items[i].name.as_deref() == Some("DFHCOMMAREA"));
116 ordinal.and_then(|o| self.linkage[o])
117 }
118
119 fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>> {
120 self.unit.library.mapset(name)
121 }
122
123 fn item_named(&mut self, name: &str, pos: Pos) -> R<Option<Loc>> {
124 let r = Self::named(name, pos);
125 match self.resolve(&r) {
126 Ok(Resolved::Item(_)) => self.locate(&r).map(Some),
127 _ => Ok(None),
128 }
129 }
130
131 fn locate_named(&mut self, name: &str, pos: Pos) -> R<Loc> {
132 self.locate(&Self::named(name, pos))
133 }
134
135 fn unaddressed(&mut self, place: &'a Ref) -> Option<usize> {
136 let Ok(Resolved::Item(i)) = self.resolve(place) else { return None };
137 let item = &self.layout.items[i];
138 item.linkage.filter(|&l| self.linkage[l as usize].is_none()).map(|_| item.size as usize)
139 }
140
141 fn run_program(&mut self, program: Option<Rc<Compiled>>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending> {
142 let first = self.first;
143 let Some(compiled) = program.as_deref().or(first) else {
144 return Err(Abend::ironwork("the CICS task's first program cannot be LINKed or XCTLed to from a function or a method", Pos::default()));
145 };
146 let handlers = if xctl { self.cics_handlers.xctl() } else { Handlers::default() };
147 Machine::activation(compiled, index, &mut *self.unit, self.main && xctl).and_then(|mut callee| {
148 let eib = callee.unit.eib;
149 callee.bind(&[Some(eib), commarea]);
150 (callee.cics_handlers, callee.first) = (handlers, first);
151 callee.run_level()
152 })
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use crate::cics::{EIBAID, EIBCALEN, EIBCPOSN, EIBDATE, EIBFN, EIBRESP, EIBRESP2, EIBRSRCE, EIBTASKN, EIBTIME, EIBTRMID, EIBTRNID};
159
160 #[test]
161 fn eib_offsets_match_the_dfheiblk_layout() {
162 let source = " IDENTIFICATION DIVISION.\n PROGRAM-ID. EIBT.\n PROCEDURE DIVISION.\n EXEC CICS RETURN END-EXEC.\n";
163 let program = syntax::parse_all_with(source, &syntax::copy::Libraries::default()).unwrap().remove(0);
164 let compiled = crate::compile(program, &[]).unwrap_or_else(|e| panic!("{e:?}"));
165 let offset = |name: &str| compiled.layout.items.iter().find(|i| i.name.as_deref() == Some(name)).map(|i| i.offset as usize);
166 let fields = [
167 ("EIBTIME", EIBTIME),
168 ("EIBDATE", EIBDATE),
169 ("EIBTRNID", EIBTRNID),
170 ("EIBTASKN", EIBTASKN),
171 ("EIBTRMID", EIBTRMID),
172 ("EIBCPOSN", EIBCPOSN),
173 ("EIBCALEN", EIBCALEN),
174 ("EIBAID", EIBAID),
175 ("EIBFN", EIBFN),
176 ("EIBRSRCE", EIBRSRCE),
177 ("EIBRESP", EIBRESP),
178 ("EIBRESP2", EIBRESP2),
179 ];
180 for (name, at) in fields {
181 assert_eq!(offset(name), Some(at), "{name}");
182 }
183 }
184}