midenc_compile/stages/
link.rs1use alloc::{borrow::ToOwned, collections::BTreeMap, sync::Arc, vec::Vec};
2
3use midenc_hir::{interner::Symbol, BuilderExt, OpBuilder, SourceSpan};
4#[cfg(feature = "std")]
5use midenc_session::Path;
6use midenc_session::{
7 diagnostics::{Severity, Spanned},
8 InputType, ProjectType,
9};
10
11use super::*;
12
13#[derive(Clone)]
14pub struct LinkOutput {
15 pub world: builtin::WorldRef,
17 pub component: builtin::ComponentRef,
19 pub masm: Vec<Arc<miden_assembly::ast::Module>>,
22 pub mast: Vec<Arc<miden_assembly::Library>>,
26 pub packages: BTreeMap<Symbol, Arc<miden_mast_package::Package>>,
28}
29
30impl LinkOutput {
31 pub fn link_libraries_from(&mut self, session: &Session) -> Result<(), Report> {
33 assert!(self.mast.is_empty(), "link libraries already loaded!");
34 for link_lib in session.options.link_libraries.iter() {
35 log::debug!(
36 "registering link library '{}' ({}, from {:#?}) with linker",
37 link_lib.name,
38 link_lib.kind,
39 link_lib.path.as_ref()
40 );
41 let lib = link_lib.load(session).map(Arc::new)?;
42 self.mast.push(lib);
43 }
44
45 Ok(())
46 }
47}
48
49pub struct LinkStage;
55
56impl Stage for LinkStage {
57 type Input = Vec<ParseOutput>;
58 type Output = LinkOutput;
59
60 fn run(&mut self, inputs: Self::Input, context: Rc<Context>) -> CompilerResult<Self::Output> {
61 let world = {
63 let mut builder = OpBuilder::new(context.clone());
64 let world_builder = builder.create::<builtin::World, ()>(SourceSpan::default());
65 world_builder()?
66 };
67
68 let mut masm = Vec::default();
70 let mut mast = Vec::default();
71 let mut packages = BTreeMap::default();
72
73 let mut component_wasm = None;
75 for input in inputs {
76 match input {
77 ParseOutput::Wasm(wasm) => {
78 if component_wasm.is_some() {
79 return Err(Report::msg(
80 "only a single wasm input can be provided at a time",
81 ));
82 }
83 component_wasm = Some(wasm);
84 }
85 ParseOutput::Module(module) => {
86 if matches!(context.session().options.project_type, ProjectType::Library if module.is_executable())
87 {
88 return Err(context
89 .diagnostics()
90 .diagnostic(Severity::Error)
91 .with_message("invalid input")
92 .with_primary_label(
93 module.span(),
94 "cannot pass executable modules as input when compiling a library",
95 )
96 .into_report());
97 } else if module.is_executable() {
98 masm.push(module);
101 } else {
102 todo!("need type information for masm procedures")
105 }
106 }
107 ParseOutput::Library(lib) => {
108 mast.push(lib);
109 }
110 ParseOutput::Package(package) => {
111 packages.insert(Symbol::intern(&package.name), package);
112 }
113 }
114 }
115
116 let component_wasm =
118 component_wasm.ok_or_else(|| Report::msg("expected at least one wasm input"))?;
119 let component = match component_wasm {
120 #[cfg(feature = "std")]
121 InputType::Real(path) => parse_hir_from_wasm_file(&path, world, context.clone())?,
122 #[cfg(not(feature = "std"))]
123 InputType::Real(_path) => unimplemented!(),
124 InputType::Stdin { name, input } => {
125 let config = wasm::WasmTranslationConfig {
126 source_name: name.file_stem().unwrap().to_owned().into(),
127 world: Some(world),
128 ..Default::default()
129 };
130 parse_hir_from_wasm_bytes(&input, context.clone(), &config)?
131 }
132 };
133
134 let mut link_output = LinkOutput {
135 world,
136 component,
137 masm,
138 mast: Vec::with_capacity(context.session().options.link_libraries.len()),
139 packages,
140 };
141
142 link_output.link_libraries_from(context.session())?;
143
144 if context.session().parse_only() {
145 log::debug!("stopping compiler early (parse-only=true)");
146 return Err(CompilerStopped.into());
147 } else if context.session().analyze_only() {
148 log::debug!("stopping compiler early (analyze-only=true)");
149 return Err(CompilerStopped.into());
150 } else if context.session().options.link_only {
151 log::debug!("stopping compiler early (link-only=true)");
152 return Err(CompilerStopped.into());
153 }
154
155 Ok(link_output)
156 }
157}
158
159#[cfg(feature = "std")]
160fn parse_hir_from_wasm_file(
161 path: &Path,
162 world: builtin::WorldRef,
163 context: Rc<Context>,
164) -> CompilerResult<builtin::ComponentRef> {
165 use std::io::Read;
166
167 log::debug!("parsing hir from wasm at {}", path.display());
168 let mut file = std::fs::File::open(path)
169 .into_diagnostic()
170 .wrap_err("could not open input for reading")?;
171 let mut bytes = Vec::with_capacity(1024);
172 file.read_to_end(&mut bytes).into_diagnostic()?;
173 let file_name = path.file_stem().unwrap().to_str().unwrap().to_owned();
174 let config = wasm::WasmTranslationConfig {
175 source_name: file_name.into(),
176 world: Some(world),
177 ..Default::default()
178 };
179 parse_hir_from_wasm_bytes(&bytes, context, &config)
180}
181
182fn parse_hir_from_wasm_bytes(
183 bytes: &[u8],
184 context: Rc<Context>,
185 config: &wasm::WasmTranslationConfig,
186) -> CompilerResult<builtin::ComponentRef> {
187 let component = wasm::translate(bytes, config, context.clone())?;
188 log::debug!(
189 "parsed hir component from wasm bytes with first module name: {}",
190 component.borrow().id()
191 );
192
193 Ok(component)
194}