Files
v/vlib/v/flat/source_paths.v
T
Alexander Medvednikov 75365168ad cache: reuse bootstrap builtin objects in test builds (#31219)
* cache: retain builtin objects across test compilations

* checker: recognize builtin lifecycle attributes

* cache: stabilize ignored local names across source reparsing

* transform: preserve generic struct map return types

* cache: preserve error dispatch IDs and portable make warmups
2026-10-27 11:46:33 +03:00

134 lines
4.7 KiB
V

module flat
import os
import hash
import v.token
// discard_name identifies an ignored local by its source file and byte span.
// File table indices change when cached headers replace parsed source files.
// Synthetic positions without a named source retain a separate index namespace.
pub fn (a &FlatAst) discard_name(pos token.Pos) string {
if file := a.source_files[pos.id] {
if file.name.len > 0 {
return '__discard_${hash.sum64_string(file.name, 0)}_${pos.offset}_${pos.end}'
}
}
return '__discard_synthetic_${pos.id}_${pos.offset}_${pos.end}'
}
// record_source_path returns os.real_path(path) through the table of resolved
// source paths. Until resolve_source_paths freezes the table it also adds the
// missing answers, so only the thread that owns the AST may call it, outside any
// disposable allocation scope. Once the table is frozen it only reads it, like
// real_source_path.
pub fn (mut a FlatAst) record_source_path(path string) string {
if a.source_paths_frozen {
return a.real_source_path(path)
}
if resolved := a.resolved_source_paths[path] {
return resolved
}
resolved := os.real_path(path)
a.resolved_source_paths[path] = resolved
return resolved
}
// record_listed_source_path is record_source_path for `path`, an entry of the
// directory listing that `real_dir` is the os.real_path of. Unless the entry is
// a symbolic link, os.real_path(path) is `real_dir` joined with the entry's own
// name, which one lstat confirms: resolving every listed file anew walked all of
// its parent directories once per file.
pub fn (mut a FlatAst) record_listed_source_path(path string, real_dir string) string {
if a.source_paths_frozen {
return a.real_source_path(path)
}
if resolved := a.resolved_source_paths[path] {
return resolved
}
resolved := listed_real_path(path, real_dir)
a.resolved_source_paths[path] = resolved
return resolved
}
// ListedSymlinks names the entries of one directory that are symbolic links
// (see scan_listed_symlinks).
pub struct ListedSymlinks {
pub:
// known is false when the listing did not report the type of every entry.
known bool
names map[string]bool
}
// record_listed_source_path_in is record_listed_source_path for an entry of the
// directory that `symlinks` was scanned from. An entry that the scan did not
// report as a symbolic link needs no lstat: one directory listing stands in for
// an lstat call per listed file.
pub fn (mut a FlatAst) record_listed_source_path_in(path string, real_dir string, symlinks &ListedSymlinks) string {
if !symlinks.known || a.source_paths_frozen {
return a.record_listed_source_path(path, real_dir)
}
if resolved := a.resolved_source_paths[path] {
return resolved
}
base := os.file_name(path)
resolved := if base in ['', '.', '..'] || base in symlinks.names || real_dir.len == 0
|| !os.is_abs_path(real_dir) {
os.real_path(path)
} else if real_dir.ends_with(os.path_separator) {
real_dir + base
} else {
real_dir + os.path_separator + base
}
a.resolved_source_paths[path] = resolved
return resolved
}
// listed_real_path returns os.real_path(path) for an entry of the directory
// that `real_dir` is the os.real_path of.
fn listed_real_path(path string, real_dir string) string {
$if windows {
return os.real_path(path)
} $else {
base := os.file_name(path)
if base in ['', '.', '..'] || real_dir.len == 0 || !os.is_abs_path(real_dir) {
return os.real_path(path)
}
st := os.lstat(path) or { return os.real_path(path) }
if st.get_filetype() == .symbolic_link {
return os.real_path(path)
}
return if real_dir.ends_with(os.path_separator) {
real_dir + base
} else {
real_dir + os.path_separator + base
}
}
}
// resolve_source_paths completes the table of resolved source paths with every
// parsed source file, reusing the answers record_source_path already recorded,
// and freezes it: nothing adds to it afterwards, including another call to
// resolve_source_paths. Call it on the thread that owns the AST, outside any
// disposable allocation scope and before other threads read the AST.
pub fn (mut a FlatAst) resolve_source_paths() {
if a.source_paths_frozen {
return
}
for _, file in a.source_files {
if file.name.len > 0 && file.name !in a.resolved_source_paths {
a.resolved_source_paths[file.name] = os.real_path(file.name)
}
}
a.source_paths_frozen = true
}
// real_source_path returns the same result as os.real_path(path), answering
// from the table of resolved source paths when it can. It never adds to the
// table, so any thread may call it once resolve_source_paths has frozen it.
pub fn (a &FlatAst) real_source_path(path string) string {
if resolved := a.resolved_source_paths[path] {
return resolved
}
return os.real_path(path)
}