use crate::loader::Library;
use crate::lower::{self, LowerError};
use crate::unit::{Clock, Observer, Remains};
use crate::{Compiled, files, oo, sql};
use rt::abend::{Abend, AbendCode, Ending};
use rt::unit::{FoundClass, LoadError, LoadedProgram, Loader};
pub use rt::vm::{Code, Halt};
use std::io::{BufRead, Write};
use std::rc::Rc;
use syntax::Pos;
pub struct VmLibrary(pub Library);
impl Loader<Rc<Code>> for VmLibrary {
type Class = ();
fn program(&mut self, name: &str) -> Result<LoadedProgram<Rc<Code>>, LoadError> {
let found = <Library as Loader<Rc<Compiled>>>::program(&mut self.0, name)?;
Ok(LoadedProgram { compiled: Rc::new(code(&found.compiled)), name: found.name, files: found.files, size: found.size, source: found.source })
}
fn holder(&self, entry: &str) -> Option<String> {
<Library as Loader<Rc<Compiled>>>::holder(&self.0, entry)
}
fn entry(program: &Rc<Code>, name: &str) -> Option<usize> {
program.entry(name)
}
fn shape(program: &Rc<Code>) -> (usize, usize) {
program.shape()
}
fn class(&mut self, _external: &str) -> Result<Option<FoundClass<()>>, String> {
Ok(None)
}
}
pub fn code(compiled: &Compiled) -> Code {
let lowered = lower::lower(compiled).map_err(|e| match e {
LowerError::Unsupported(what, _) => what.to_owned(),
other => other.to_string(),
});
held(compiled, lowered)
}
pub fn lowered(compiled: &Compiled) -> Result<Code, LowerError> {
Ok(held(compiled, Ok(lower::lower(compiled)?)))
}
fn held(compiled: &Compiled, lowered: Result<rt::lir::Program, String>) -> Code {
let entries = compiled.entries.iter().map(|e| e.name.clone()).collect();
Code::new(lowered, entries, compiled.program.files.len(), compiled.layout.size as usize, compiled.program.nested.clone())
}
#[allow(clippy::too_many_arguments)]
pub fn execute<'w>(
compiled: &Compiled,
code: &Code,
library: Library,
dds: files::Dds,
sysin: Option<Box<dyn BufRead + 'w>>,
clock: Clock,
database: Option<&'w mut (dyn sql::Database + '_)>,
out: &'w mut dyn Write,
err: &'w mut dyn Write,
observer: Option<Observer<'w>>,
parm: Option<&str>,
kept: &mut Option<Remains>,
) -> Result<(Ending, i16), Halt> {
oo::refuse_to_run(&compiled.program)?;
let statements = library.trace_statements.clone();
let mut run_unit = rt::unit::RunUnit::new(VmLibrary(library), dds, sysin, clock, out, err);
run_unit.observer = observer;
run_unit.statements = statements;
run_unit.sql = database.map(sql::Session::new);
let me = run_unit.add_named(None, compiled.program.id.to_ascii_uppercase(), compiled.program.files.len(), compiled.layout.size as usize);
let parm = parm.map(|p| run_unit.push_temporary(&rt::le::parm::parameter_area(rt::le::parm::program_arguments(p), compiled.options.code_page())));
let ending = rt::vm::run(code, me, &mut run_unit, &parm.map_or_else(Vec::new, |p| vec![Some(p)]));
let settled = run_unit.sql.as_mut().map_or(Ok(()), |s| s.settle(&compiled.program.id, ending.is_ok()).map(drop));
let closed = run_unit.close_all();
*kept = Some(Remains::of(&run_unit));
let ending = ending?;
settled.map_err(|a| Abend { code: a.code.into(), message: a.message, pos: Pos::default(), file: None })?;
closed.map_err(|m| Abend { code: AbendCode::Ironwork, message: m, pos: Pos::default(), file: None })?;
Ok((ending, run_unit.return_code()))
}