Skip to main content

lashlang/runtime/
entry_points.rs

1//! Public compile/execute entry points for lashlang programs.
2
3use crate::ast::Program;
4use crate::tracking::LashlangExecutionContext;
5use crate::{LinkedModule, ModuleArtifact, ProcessRef};
6
7use super::record::intern_symbol;
8use super::{
9    CompiledProgram, Compiler, ExecutionHost, ExecutionOutcome, ExecutionScratch, LASH_TYPE_KEY,
10    ProjectedBindings, RuntimeError, SlotState, State, Vm,
11};
12
13pub enum ExecutableProgram<'program> {
14    Program(&'program Program),
15    Compiled(&'program CompiledProgram),
16}
17
18impl<'program> From<&'program Program> for ExecutableProgram<'program> {
19    fn from(program: &'program Program) -> Self {
20        Self::Program(program)
21    }
22}
23
24impl<'program> From<&'program CompiledProgram> for ExecutableProgram<'program> {
25    fn from(program: &'program CompiledProgram) -> Self {
26        Self::Compiled(program)
27    }
28}
29
30pub fn compile(source: &str) -> Result<CompiledProgram, crate::parser::ParseError> {
31    crate::parse(source).map(|program| compile_program_internal(&program))
32}
33
34pub(crate) fn compile_program_internal(program: &Program) -> CompiledProgram {
35    let (chunk, compile_stats) = Compiler::compile_program(program);
36    CompiledProgram {
37        chunk,
38        compile_stats,
39    }
40}
41
42pub fn compile_linked(linked: &LinkedModule) -> CompiledProgram {
43    let (chunk, compile_stats) = Compiler::compile_linked_program(
44        linked.program(),
45        (&linked.artifact).into(),
46        LashlangExecutionContext::main(linked.artifact.module_ref.clone()),
47    );
48    CompiledProgram {
49        chunk,
50        compile_stats,
51    }
52}
53
54pub fn compile_process(
55    program: &Program,
56    process_name: &str,
57) -> Result<CompiledProgram, RuntimeError> {
58    let process = program
59        .process(process_name)
60        .ok_or_else(|| RuntimeError::ValueError {
61            message: format!("unknown process `{process_name}`"),
62        })?;
63    let process_program = Program {
64        declarations: program.declarations.clone(),
65        main: process.body.clone(),
66        declaration_spans: program.declaration_spans.clone(),
67        expression_spans: Vec::new(),
68    };
69    Ok(compile_program_internal(&process_program))
70}
71
72pub fn compile_linked_process(
73    linked: &LinkedModule,
74    process_name: &str,
75) -> Result<CompiledProgram, RuntimeError> {
76    let linked_program = linked.program();
77    let process = linked_program
78        .process(process_name)
79        .ok_or_else(|| RuntimeError::ValueError {
80            message: format!("unknown process `{process_name}`"),
81        })?;
82    let process_program = Program {
83        declarations: linked_program.declarations.clone(),
84        main: process.body.clone(),
85        declaration_spans: linked_program.declaration_spans.clone(),
86        expression_spans: Vec::new(),
87    };
88    let process_ref = linked
89        .artifact
90        .process_ref(process_name)
91        .cloned()
92        .ok_or_else(|| RuntimeError::ValueError {
93            message: format!("linked module does not export process `{process_name}`"),
94        })?;
95    let (chunk, compile_stats) = Compiler::compile_linked_process_program(
96        &process_program,
97        (&linked.artifact).into(),
98        LashlangExecutionContext::process(
99            linked.artifact.module_ref.clone(),
100            process_ref,
101            process_name,
102        ),
103    );
104    Ok(CompiledProgram {
105        chunk,
106        compile_stats,
107    })
108}
109
110pub fn compile_module_artifact_process(
111    artifact: &ModuleArtifact,
112    process_ref: &ProcessRef,
113) -> Result<CompiledProgram, RuntimeError> {
114    let process_name =
115        artifact
116            .process_name_for_ref(process_ref)
117            .ok_or_else(|| RuntimeError::ValueError {
118                message: format!(
119                    "module artifact `{}` does not export process ref {:?}",
120                    artifact.module_ref, process_ref
121                ),
122            })?;
123    let process =
124        artifact
125            .canonical_ir
126            .process(process_name)
127            .ok_or_else(|| RuntimeError::ValueError {
128                message: format!(
129                    "module artifact `{}` is missing process `{process_name}`",
130                    artifact.module_ref
131                ),
132            })?;
133    let process_program = Program {
134        declarations: artifact.canonical_ir.declarations.clone(),
135        main: process.body.clone(),
136        declaration_spans: artifact.canonical_ir.declaration_spans.clone(),
137        expression_spans: Vec::new(),
138    };
139    let (chunk, compile_stats) = Compiler::compile_linked_process_program(
140        &process_program,
141        artifact.into(),
142        LashlangExecutionContext::process(
143            artifact.module_ref.clone(),
144            process_ref.clone(),
145            process_name,
146        ),
147    );
148    Ok(CompiledProgram {
149        chunk,
150        compile_stats,
151    })
152}
153
154pub fn prewarm() {
155    for name in [
156        "ok",
157        "value",
158        "error",
159        "__handle__",
160        "handle",
161        LASH_TYPE_KEY,
162        "type",
163        "properties",
164        "required",
165        "items",
166        "enum",
167        "id",
168        "label",
169        "size",
170        "width",
171        "height",
172    ] {
173        intern_symbol(name);
174    }
175}
176
177pub async fn execute<'program, H: ExecutionHost>(
178    program: impl Into<ExecutableProgram<'program>>,
179    state: &mut State,
180    host: &H,
181) -> Result<ExecutionOutcome, RuntimeError> {
182    match program.into() {
183        ExecutableProgram::Program(program) => {
184            let compiled = compile_program_internal(program);
185            execute_compiled_internal(&compiled, state, host).await
186        }
187        ExecutableProgram::Compiled(compiled) => {
188            execute_compiled_internal(compiled, state, host).await
189        }
190    }
191}
192
193pub(crate) async fn execute_compiled_internal<H: ExecutionHost>(
194    program: &CompiledProgram,
195    state: &mut State,
196    host: &H,
197) -> Result<ExecutionOutcome, RuntimeError> {
198    let projected = host.projected_bindings();
199    if let Some(mut scratch) = host.take_scratch() {
200        let result =
201            execute_with_optional_scratch(program, state, host, &projected, Some(&mut scratch))
202                .await;
203        host.store_scratch(scratch);
204        result
205    } else {
206        execute_with_optional_scratch(program, state, host, &projected, None).await
207    }
208}
209
210async fn execute_with_optional_scratch<H: ExecutionHost>(
211    program: &CompiledProgram,
212    state: &mut State,
213    host: &H,
214    projected: &ProjectedBindings,
215    scratch: Option<&mut ExecutionScratch>,
216) -> Result<ExecutionOutcome, RuntimeError> {
217    if let Some(scratch) = scratch {
218        let slots = SlotState::from_globals_with_scratch(
219            std::mem::take(&mut state.globals),
220            &program.chunk.slot_names,
221            scratch,
222            projected,
223        );
224        let mut vm = Vm::new_with_scratch_and_mode(
225            &program.chunk,
226            slots,
227            host,
228            scratch,
229            host.execution_mode(),
230        );
231        let result = run_vm(program, host, &mut vm).await;
232        state.globals = vm.recycle_into_globals(scratch);
233        result
234    } else {
235        let slots = SlotState::from_globals(
236            std::mem::take(&mut state.globals),
237            &program.chunk.slot_names,
238            projected,
239        );
240        let mut vm = Vm::new_with_mode(&program.chunk, slots, host, host.execution_mode());
241        let result = run_vm(program, host, &mut vm).await;
242        state.globals = vm.into_globals();
243        result
244    }
245}
246
247async fn run_vm<H: ExecutionHost>(
248    program: &CompiledProgram,
249    host: &H,
250    vm: &mut Vm<'_, H>,
251) -> Result<ExecutionOutcome, RuntimeError> {
252    if host.profile_execution() {
253        vm.enable_profile();
254    }
255
256    let result = if host.trace_runtime_errors() {
257        vm.run_traced_for_mode().await.map_err(|failure| {
258            let error = failure.error.clone();
259            host.observe_runtime_failure(failure);
260            error
261        })
262    } else {
263        vm.run_for_mode().await
264    };
265
266    if host.profile_execution() {
267        let mut profile = vm.take_profile();
268        profile.compile_stats = program.compile_stats;
269        host.observe_profile(profile);
270    }
271
272    result
273}