Skip to main content

harn_vm/bytecode_cache/
graph.rs

1//! Shared projections from the bytecode cache's canonical graph walk.
2
3use std::path::Path;
4
5use crate::context_manifest::ContextManifest;
6use crate::module_artifact::ModuleCompilationContext;
7use crate::VmError;
8
9/// Recompute a store outcome from the graph as it exists after run setup.
10///
11/// Package materialization can change import resolution between the initial
12/// cache probe and compilation. Writers use this constructor after setup so a
13/// newly compiled chunk is never paired with the probe's older graph.
14pub fn prepare_entry_store(source_path: &Path, source: &str) -> super::LookupOutcome {
15    let source_hash = super::sha256(source.as_bytes());
16    let (context_hash, manifest) = super::GraphWalk::new(source_path, source).finish();
17    super::LookupOutcome {
18        key: super::CacheKey {
19            source_hash,
20            context_hash,
21            harn_version: std::borrow::Cow::Borrowed(super::HARN_VERSION),
22            compiler_tag: super::compiler_options_tag(super::CompilerOptions::from_env()),
23            provenance: super::ModuleProvenance::User,
24        },
25        chunk: None,
26        manifest,
27        link_table: None,
28    }
29}
30
31/// Derive an entry interface and the graph capture that keeps it reusable.
32pub(crate) fn derive_interface(
33    source_path: &Path,
34    source: &str,
35) -> Result<(ModuleCompilationContext, Option<ContextManifest>), VmError> {
36    let result = super::walk_import_graph_fingerprinted(
37        source_path,
38        source,
39        super::CODEGEN_FINGERPRINT,
40        true,
41    );
42    let context = match result.entry_compilation_context {
43        Some(context) => {
44            #[cfg(test)]
45            crate::module_artifact::INTERFACE_RESOLUTIONS.with(|count| count.set(count.get() + 1));
46            context
47        }
48        None => crate::module_artifact::module_compilation_context_for_source(source_path, source)?,
49    };
50    Ok((context, result.manifest))
51}
52
53/// Render `target` relative to `base` with `/` separators.
54pub(super) fn relative_path_label(base: &Path, target: &Path) -> Option<String> {
55    let base_components = base.components().collect::<Vec<_>>();
56    let target_components = target.components().collect::<Vec<_>>();
57    let common = base_components
58        .iter()
59        .zip(&target_components)
60        .take_while(|(left, right)| left == right)
61        .count();
62    if common == 0 && (base.is_absolute() || target.is_absolute()) {
63        return None;
64    }
65
66    let mut parts = Vec::new();
67    for component in &base_components[common..] {
68        if matches!(component, std::path::Component::Normal(_)) {
69            parts.push("..".to_string());
70        }
71    }
72    for component in &target_components[common..] {
73        match component {
74            std::path::Component::Normal(part) => parts.push(part.to_string_lossy().into_owned()),
75            std::path::Component::ParentDir => parts.push("..".to_string()),
76            std::path::Component::CurDir => {}
77            std::path::Component::RootDir | std::path::Component::Prefix(_) => return None,
78        }
79    }
80    Some(if parts.is_empty() {
81        ".".to_string()
82    } else {
83        parts.join("/")
84    })
85}