1use 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
16pub struct VmLibrary(pub Library);
18
19impl Loader<Rc<Code>> for VmLibrary {
20 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
45pub 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
54pub 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#[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}