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