Skip to main content

ironwork_exec/
vm.rs

1//! Running a compiled program on the VM (`rt::vm`): CALL's loader over the interpreter's program
2//! library, lowering each program it compiles, and the run itself, as `Execute::execute_observed`
3//! makes the interpreter's.
4
5use crate::loader::Library;
6use crate::lower::{self, LowerError};
7use crate::unit::{Clock, Observer, Remains};
8use crate::{Compiled, files, oo, sql};
9use rt::abend::{Abend, AbendCode, Ending};
10use rt::unit::{FoundClass, LoadError, LoadedProgram, Loader};
11pub use rt::vm::{Code, Halt};
12use std::io::{BufRead, Write};
13use std::rc::Rc;
14use syntax::Pos;
15
16/// The program library, each program lowered as CALL loads it.
17pub struct VmLibrary(pub Library);
18
19impl Loader<Rc<Code>> for VmLibrary {
20    /// INVOKE is not run by the VM yet, so no class is loaded.
21    type Class = ();
22
23    fn program(&mut self, name: &str) -> Result<LoadedProgram<Rc<Code>>, LoadError> {
24        let found = <Library as Loader<Rc<Compiled>>>::program(&mut self.0, name)?;
25        Ok(LoadedProgram { compiled: Rc::new(code(&found.compiled)), name: found.name, files: found.files, size: found.size, source: found.source })
26    }
27
28    fn holder(&self, entry: &str) -> Option<String> {
29        <Library as Loader<Rc<Compiled>>>::holder(&self.0, entry)
30    }
31
32    fn entry(program: &Rc<Code>, name: &str) -> Option<usize> {
33        program.entry(name)
34    }
35
36    fn shape(program: &Rc<Code>) -> (usize, usize) {
37        program.shape()
38    }
39
40    fn class(&mut self, _external: &str) -> Result<Option<FoundClass<()>>, String> {
41        Ok(None)
42    }
43}
44
45/// A compiled program as the VM holds it: lowered, or with the construct lowering refused.
46pub fn code(compiled: &Compiled) -> Code {
47    let lowered = lower::lower(compiled).map_err(|e| match e {
48        LowerError::Unsupported(what, _) => what.to_owned(),
49        other => other.to_string(),
50    });
51    held(compiled, lowered)
52}
53
54/// A compiled program lowered for the VM, or why it does not lower, with where.
55pub fn lowered(compiled: &Compiled) -> Result<Code, LowerError> {
56    Ok(held(compiled, Ok(lower::lower(compiled)?)))
57}
58
59fn held(compiled: &Compiled, lowered: Result<rt::lir::Program, String>) -> Code {
60    let entries = compiled.entries.iter().map(|e| e.name.clone()).collect();
61    Code::new(lowered, entries, compiled.program.files.len(), compiled.layout.size as usize, compiled.program.nested.clone())
62}
63
64/// Runs `compiled`, lowered as `code`, as the first program of a run unit on the VM, with a job
65/// step's `parm` as `Execute::execute_main` takes it; `kept` takes what the run left in its run
66/// unit.
67#[allow(clippy::too_many_arguments)]
68pub fn execute<'w>(
69    compiled: &Compiled,
70    code: &Code,
71    library: Library,
72    dds: files::Dds,
73    sysin: Option<Box<dyn BufRead + 'w>>,
74    clock: Clock,
75    database: Option<&'w mut (dyn sql::Database + '_)>,
76    out: &'w mut dyn Write,
77    err: &'w mut dyn Write,
78    observer: Option<Observer<'w>>,
79    parm: Option<&str>,
80    kept: &mut Option<Remains>,
81) -> Result<(Ending, i16), Halt> {
82    oo::refuse_to_run(&compiled.program)?;
83    let statements = library.trace_statements.clone();
84    let mut run_unit = rt::unit::RunUnit::new(VmLibrary(library), dds, sysin, clock, out, err);
85    run_unit.observer = observer;
86    run_unit.statements = statements;
87    run_unit.sql = database.map(sql::Session::new);
88    let me = run_unit.add_named(None, compiled.program.id.to_ascii_uppercase(), compiled.program.files.len(), compiled.layout.size as usize);
89    let parm = parm.map(|p| run_unit.push_temporary(&rt::le::parm::parameter_area(rt::le::parm::program_arguments(p), compiled.options.code_page())));
90    let ending = rt::vm::run(code, me, &mut run_unit, &parm.map_or_else(Vec::new, |p| vec![Some(p)]));
91    let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.settle(&compiled.program.id, ending.is_ok()).map(drop));
92    let closed = run_unit.close_all();
93    *kept = Some(Remains::of(&run_unit));
94    let ending = ending?;
95    settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None })?;
96    closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None })?;
97    Ok((ending, run_unit.return_code()))
98}