1use crate::{
2 decl_engine::DeclId,
3 language::{ty::TyFunctionDecl, CallPath, Inline, Purity, Trace},
4};
5
6use sway_ir::{Context, MetadataIndex, Metadatum, Value};
7use sway_types::{span::Source, Ident, SourceId, Span, Spanned};
8
9use std::{collections::HashMap, path::PathBuf, sync::Arc};
10
11#[derive(Default)]
20pub(crate) struct MetadataManager {
21 md_span_cache: HashMap<MetadataIndex, Span>,
31 md_tagged_span_cache: HashMap<MetadataIndex, (Span, &'static str)>,
33 md_file_loc_cache: HashMap<MetadataIndex, (Arc<PathBuf>, Source)>,
34 md_purity_cache: HashMap<MetadataIndex, Purity>,
35 md_inline_cache: HashMap<MetadataIndex, Inline>,
36 md_trace_cache: HashMap<MetadataIndex, Trace>,
37 md_test_decl_index_cache: HashMap<MetadataIndex, DeclId<TyFunctionDecl>>,
38
39 span_md_cache: HashMap<Span, MetadataIndex>,
40 tagged_span_md_cache: HashMap<(Span, &'static str), MetadataIndex>,
41 file_loc_md_cache: HashMap<SourceId, MetadataIndex>,
42 purity_md_cache: HashMap<Purity, MetadataIndex>,
43 inline_md_cache: HashMap<Inline, MetadataIndex>,
44 trace_md_cache: HashMap<Trace, MetadataIndex>,
45 test_decl_index_md_cache: HashMap<DeclId<TyFunctionDecl>, MetadataIndex>,
46}
47
48impl MetadataManager {
49 pub(crate) fn md_to_span(
50 &mut self,
51 context: &Context,
52 md_idx: Option<MetadataIndex>,
53 ) -> Option<Span> {
54 Self::for_each_md_idx(context, md_idx, |md_idx| {
55 self.md_span_cache.get(&md_idx).cloned().or_else(|| {
56 md_idx
57 .get_content(context)
58 .unwrap_struct("span", 3)
59 .and_then(|fields| {
60 let span = self.create_span_from_metadatum_fields(context, fields)?;
62 self.md_span_cache.insert(md_idx, span.clone());
63 Some(span)
64 })
65 })
66 })
67 }
68
69 pub(crate) fn md_to_tagged_span(
74 &mut self,
75 context: &Context,
76 md_idx: Option<MetadataIndex>,
77 tag: &'static str,
78 ) -> Option<Span> {
79 Self::for_each_md_idx(context, md_idx, |md_idx| {
80 let fields = md_idx.get_content(context).unwrap_struct(tag, 3);
81
82 match fields {
83 Some(fields) => self
84 .md_tagged_span_cache
85 .get(&md_idx)
86 .map(|span_and_tag| span_and_tag.0.clone())
87 .or_else(|| {
88 let span = self.create_span_from_metadatum_fields(context, fields)?;
90 self.md_tagged_span_cache
91 .insert(md_idx, (span.clone(), tag));
92 Some(span)
93 }),
94 None => None,
95 }
96 })
97 }
98
99 pub(crate) fn md_to_fn_name_span(
102 &mut self,
103 context: &Context,
104 md_idx: Option<MetadataIndex>,
105 ) -> Option<Span> {
106 self.md_to_tagged_span(context, md_idx, "fn_name_span")
107 }
108
109 pub(crate) fn md_to_fn_call_path_span(
112 &mut self,
113 context: &Context,
114 md_idx: Option<MetadataIndex>,
115 ) -> Option<Span> {
116 self.md_to_tagged_span(context, md_idx, "fn_call_path_span")
117 }
118
119 pub(crate) fn md_to_test_decl_index(
120 &mut self,
121 context: &Context,
122 md_idx: Option<MetadataIndex>,
123 ) -> Option<DeclId<TyFunctionDecl>> {
124 Self::for_each_md_idx(context, md_idx, |md_idx| {
125 self.md_test_decl_index_cache
126 .get(&md_idx)
127 .cloned()
128 .or_else(|| {
129 md_idx
131 .get_content(context)
132 .unwrap_struct("decl_index", 1)
133 .and_then(|fields| {
134 let index = fields[0]
135 .unwrap_integer()
136 .map(|index| DeclId::new(index as usize))?;
137 self.md_test_decl_index_cache.insert(md_idx, index);
138 Some(index)
139 })
140 })
141 })
142 }
143
144 pub(crate) fn md_to_purity(
145 &mut self,
146 context: &Context,
147 md_idx: Option<MetadataIndex>,
148 ) -> Purity {
149 Self::for_each_md_idx(context, md_idx, |md_idx| {
153 self.md_purity_cache.get(&md_idx).copied().or_else(|| {
154 md_idx
156 .get_content(context)
157 .unwrap_struct("purity", 1)
158 .and_then(|fields| {
159 fields[0].unwrap_string().and_then(|purity_str| {
160 let purity = match purity_str {
161 "reads" => Some(Purity::Reads),
162 "writes" => Some(Purity::Writes),
163 "readswrites" => Some(Purity::ReadsWrites),
164 _otherwise => panic!("Invalid purity metadata: {purity_str}."),
165 }?;
166
167 self.md_purity_cache.insert(md_idx, purity);
168
169 Some(purity)
170 })
171 })
172 })
173 })
174 .unwrap_or(Purity::Pure)
175 }
176
177 #[allow(dead_code)]
184 pub(crate) fn md_to_inline(
185 &mut self,
186 context: &Context,
187 md_idx: Option<MetadataIndex>,
188 ) -> Option<Inline> {
189 Self::for_each_md_idx(context, md_idx, |md_idx| {
190 self.md_inline_cache.get(&md_idx).copied().or_else(|| {
191 md_idx
193 .get_content(context)
194 .unwrap_struct("inline", 1)
195 .and_then(|fields| fields[0].unwrap_string())
196 .and_then(|inline_str| {
197 let inline = match inline_str {
198 "always" => Some(Inline::Always),
199 "never" => Some(Inline::Never),
200 _otherwise => None,
201 }?;
202
203 self.md_inline_cache.insert(md_idx, inline);
204
205 Some(inline)
206 })
207 })
208 })
209 }
210
211 #[allow(dead_code)]
212 pub(crate) fn md_to_trace(
213 &mut self,
214 context: &Context,
215 md_idx: Option<MetadataIndex>,
216 ) -> Option<Trace> {
217 Self::for_each_md_idx(context, md_idx, |md_idx| {
218 self.md_trace_cache.get(&md_idx).copied().or_else(|| {
219 md_idx
221 .get_content(context)
222 .unwrap_struct("trace", 1)
223 .and_then(|fields| fields[0].unwrap_string())
224 .and_then(|trace_str| {
225 let trace = match trace_str {
226 "always" => Some(Trace::Always),
227 "never" => Some(Trace::Never),
228 _otherwise => None,
229 }?;
230
231 self.md_trace_cache.insert(md_idx, trace);
232
233 Some(trace)
234 })
235 })
236 })
237 }
238
239 fn md_to_file_location(
240 &mut self,
241 context: &Context,
242 md: &Metadatum,
243 ) -> Option<(Arc<PathBuf>, Source)> {
244 md.unwrap_index().and_then(|md_idx| {
245 self.md_file_loc_cache.get(&md_idx).cloned().or_else(|| {
246 md_idx
248 .get_content(context)
249 .unwrap_source_id()
250 .and_then(|source_id| {
251 let path_buf = context.source_engine.get_path(source_id);
252 let src = std::fs::read_to_string(&path_buf).ok()?;
253 let path_and_src = (Arc::new(path_buf), src.as_str().into());
254
255 self.md_file_loc_cache.insert(md_idx, path_and_src.clone());
256
257 Some(path_and_src)
258 })
259 })
260 })
261 }
262
263 pub(crate) fn val_to_span(&mut self, context: &Context, value: Value) -> Option<Span> {
264 self.md_to_span(context, value.get_metadata(context))
265 }
266
267 pub(crate) fn span_to_md(
268 &mut self,
269 context: &mut Context,
270 span: &Span,
271 ) -> Option<MetadataIndex> {
272 self.span_md_cache.get(span).copied().or_else(|| {
273 span.source_id().and_then(|source_id| {
274 let md_idx = self.create_metadata_from_span(context, source_id, span, "span")?;
275 self.span_md_cache.insert(span.clone(), md_idx);
276 Some(md_idx)
277 })
278 })
279 }
280
281 pub(crate) fn tagged_span_to_md(
289 &mut self,
290 context: &mut Context,
291 span: &Span,
292 tag: &'static str,
293 ) -> Option<MetadataIndex> {
294 let span_and_tag = (span.clone(), tag);
295 self.tagged_span_md_cache
296 .get(&span_and_tag)
297 .copied()
298 .or_else(|| {
299 span.source_id().and_then(|source_id| {
300 let md_idx = self.create_metadata_from_span(context, source_id, span, tag)?;
301 self.tagged_span_md_cache.insert(span_and_tag, md_idx);
302 Some(md_idx)
303 })
304 })
305 }
306
307 pub(crate) fn fn_name_span_to_md(
315 &mut self,
316 context: &mut Context,
317 fn_name: &Ident,
318 ) -> Option<MetadataIndex> {
319 self.tagged_span_to_md(context, &fn_name.span(), "fn_name_span")
320 }
321
322 pub(crate) fn fn_call_path_span_to_md(
330 &mut self,
331 context: &mut Context,
332 call_path: &CallPath,
333 ) -> Option<MetadataIndex> {
334 self.tagged_span_to_md(context, &call_path.span(), "fn_call_path_span")
335 }
336
337 pub(crate) fn test_decl_index_to_md(
338 &mut self,
339 context: &mut Context,
340 decl_index: DeclId<TyFunctionDecl>,
341 ) -> Option<MetadataIndex> {
342 self.test_decl_index_md_cache
343 .get(&decl_index)
344 .copied()
345 .or_else(|| {
346 let md_idx = MetadataIndex::new_struct(
347 context,
348 "decl_index",
349 vec![Metadatum::Integer(decl_index.inner() as u64)],
350 );
351 self.test_decl_index_md_cache.insert(decl_index, md_idx);
352
353 Some(md_idx)
354 })
355 }
356
357 pub(crate) fn purity_to_md(
358 &mut self,
359 context: &mut Context,
360 purity: Purity,
361 ) -> Option<MetadataIndex> {
362 (purity != Purity::Pure).then(|| {
365 self.purity_md_cache
366 .get(&purity)
367 .copied()
368 .unwrap_or_else(|| {
369 let field = match purity {
371 Purity::Pure => unreachable!("Already checked for Pure above."),
372 Purity::Reads => "reads",
373 Purity::Writes => "writes",
374 Purity::ReadsWrites => "readswrites",
375 };
376 let md_idx = MetadataIndex::new_struct(
377 context,
378 "purity",
379 vec![Metadatum::String(field.to_owned())],
380 );
381
382 self.purity_md_cache.insert(purity, md_idx);
383
384 md_idx
385 })
386 })
387 }
388
389 pub(crate) fn inline_to_md(
390 &mut self,
391 context: &mut Context,
392 inline: Inline,
393 ) -> Option<MetadataIndex> {
394 Some(
395 self.inline_md_cache
396 .get(&inline)
397 .copied()
398 .unwrap_or_else(|| {
399 let field = match inline {
401 Inline::Always => "always",
402 Inline::Never => "never",
403 };
404 let md_idx = MetadataIndex::new_struct(
405 context,
406 "inline",
407 vec![Metadatum::String(field.to_owned())],
408 );
409
410 self.inline_md_cache.insert(inline, md_idx);
411
412 md_idx
413 }),
414 )
415 }
416
417 pub(crate) fn trace_to_md(
418 &mut self,
419 context: &mut Context,
420 trace: Trace,
421 ) -> Option<MetadataIndex> {
422 Some(self.trace_md_cache.get(&trace).copied().unwrap_or_else(|| {
423 let field = match trace {
425 Trace::Always => "always",
426 Trace::Never => "never",
427 };
428 let md_idx = MetadataIndex::new_struct(
429 context,
430 "trace",
431 vec![Metadatum::String(field.to_owned())],
432 );
433
434 self.trace_md_cache.insert(trace, md_idx);
435
436 md_idx
437 }))
438 }
439
440 fn file_location_to_md(
441 &mut self,
442 context: &mut Context,
443 source_id: SourceId,
444 ) -> Option<MetadataIndex> {
445 self.file_loc_md_cache.get(&source_id).copied().or_else(|| {
446 let md_idx = MetadataIndex::new_source_id(context, source_id);
447 self.file_loc_md_cache.insert(source_id, md_idx);
448
449 Some(md_idx)
450 })
451 }
452
453 fn for_each_md_idx<T, F: FnMut(MetadataIndex) -> Option<T>>(
454 context: &Context,
455 md_idx: Option<MetadataIndex>,
456 mut f: F,
457 ) -> Option<T> {
458 md_idx.and_then(|md_idx| {
460 if let Some(md_idcs) = md_idx.get_content(context).unwrap_list() {
461 md_idcs.iter().find_map(|md_idx| f(*md_idx))
462 } else {
463 f(md_idx)
464 }
465 })
466 }
467
468 fn create_span_from_metadatum_fields(
469 &mut self,
470 context: &Context,
471 fields: &[Metadatum],
472 ) -> Option<Span> {
473 let (path, src) = self.md_to_file_location(context, &fields[0])?;
474 let start = fields[1].unwrap_integer()?;
475 let end = fields[2].unwrap_integer()?;
476 let source_engine = context.source_engine();
477 let source_id = source_engine.get_source_id(&path);
478 let span = Span::new(src, start as usize, end as usize, Some(source_id))?;
479 Some(span)
480 }
481
482 fn create_metadata_from_span(
483 &mut self,
484 context: &mut Context,
485 source_id: &SourceId,
486 span: &Span,
487 tag: &'static str,
488 ) -> Option<MetadataIndex> {
489 let file_location_md_idx = self.file_location_to_md(context, *source_id)?;
490 let md_idx = MetadataIndex::new_struct(
491 context,
492 tag,
493 vec![
494 Metadatum::Index(file_location_md_idx),
495 Metadatum::Integer(span.start() as u64),
496 Metadatum::Integer(span.end() as u64),
497 ],
498 );
499 Some(md_idx)
500 }
501}