Documentation
¶
Overview ¶
Package resolver provides file resolution infrastructure for the Scheme compiler. It owns the concrete FileResolver implementations that locate and open source files on the OS filesystem, embedded filesystems, and virtual fs.FS instances.
The resolver package deals in file paths, not library names. Library identity and semantics are handled by the compilation package.
FileResolver is defined in environment.FileResolver and is not redeclared here. The FileEnumerator interface extends resolvers with file discovery capability.
Index ¶
- Constants
- func IsNotFound(err error) bool
- func LibraryExtensions() []string
- func SelectAuthorizer(ctx context.Context, env *environment.EnvironmentFrame) security.Authorizer
- func SelectLoadStack(ctx context.Context) *sourceload.LoadStack
- func WalkOSSchemeFiles(baseDir string, auth security.Authorizer, fn func(relPath string)) error
- type ChainFileResolver
- type EmbedFileResolver
- type FSFileResolver
- type FileEnumerator
- type OSFileResolver
- type SourceGate
Constants ¶
const SchemeIncludePathEnv = "SCHEME_INCLUDE_PATH"
SchemeIncludePathEnv is the environment variable name for the Scheme include path.
Variables ¶
This section is empty.
Functions ¶
func IsNotFound ¶ added in v1.20.0
IsNotFound reports whether err means the file is genuinely absent, which always licenses continuing a search: falling through to the next resolver in a chain, or from a library's .sld to its .scm. (A chain continues past a denial too, under the narrower condition ChainFileResolver.continuesPast states; absence is the unconditional half.)
It accepts both sentinels because both are minted for absence and neither implies the other: werr.ErrFileNotFound is a bare static sentinel with no fs.ErrNotExist in its chain, and a raw fs.ErrNotExist arrives from the virtual-filesystem side without ever being relabelled.
func LibraryExtensions ¶
func LibraryExtensions() []string
LibraryExtensions returns a copy of the recognized Scheme library file extensions.
func SelectAuthorizer ¶ added in v1.20.0
func SelectAuthorizer(ctx context.Context, env *environment.EnvironmentFrame) security.Authorizer
SelectAuthorizer returns the Authorizer that governs this resolution. It is the resolver-side selection point for the security policy, and it exists for the same reason SelectLoadStack does: the env a resolver holds is the ROOT env captured once at engine construction, not the caller's. A caller with a stricter policy — a child Namespace, or a root whose authorizer was tightened after construction — can only reach the resolver through ctx.
The two-branch shape here is deliberate and is NOT SelectLoadStack's (whose env fallback was deleted): LoadLibrary is the only installer, so every other caller leaves ctx bare and gets the captured env's authorizer, unchanged.
func SelectLoadStack ¶
func SelectLoadStack(ctx context.Context) *sourceload.LoadStack
SelectLoadStack returns the LoadStack that governs directory-relative resolution for this call. It is the single selection point shared by the FS/OS resolvers (which read the current directory from it) and the (include …) compiler (which pushes the included file onto it), so the push target and the read target are guaranteed to be the same object — that identity is what closes the per-thread include-resolution race.
The stack is carried on ctx and nowhere else, so it is per load CHAIN: concurrent library loads, concurrent (load …) calls on separate SRFI-18 threads, and an embedder's own Engine.ContextWithLoadPath each get their own. Returns nil outside any load, which is not an error — a top-level (include …) with no enclosing load resolves against the search paths.
This used to fall back to a single LoadStack hung off the Namespace when ctx carried none. That stack was shared by every thread in the engine, so two threads loading files in different directories interleaved their pushes and one compiled the other's file — a source substitution, invisible to -race because it is not a data race. The fallback is gone and the per-namespace stack with it; there is one discipline now, not two.
func WalkOSSchemeFiles ¶
func WalkOSSchemeFiles(baseDir string, auth security.Authorizer, fn func(relPath string)) error
WalkOSSchemeFiles walks baseDir on the OS filesystem, calling fn with the slash-separated path of each .sld/.scm file relative to baseDir. Hidden directories and unauthorized files are silently skipped. Returns the WalkDir error so callers can observe unexpected walk failures.
Types ¶
type ChainFileResolver ¶
type ChainFileResolver struct {
// contains filtered or unexported fields
}
ChainFileResolver tries multiple resolvers in order, falling through to the next on absence, and on a denial only when some later resolver will re-ask under a different source (see continuesPast). Anything else — a permission failure, any other I/O error — propagates immediately, so a higher-priority file that exists but cannot be read is never silently replaced by a lower-priority one.
func NewChainFileResolver ¶
func NewChainFileResolver(resolvers []environment.FileResolver) *ChainFileResolver
NewChainFileResolver creates a resolver that tries each resolver in order. Panics if resolvers is empty.
func (*ChainFileResolver) EnumerateFiles ¶
func (p *ChainFileResolver) EnumerateFiles() ([]string, error)
EnumerateFiles unions file enumerations from all child resolvers that implement FileEnumerator. Results are concatenated in resolver order with no deduplication; ordering implies priority. Best-effort: walk errors are accumulated and returned alongside partial results, matching OSFileResolver and FSFileResolver semantics.
func (*ChainFileResolver) ResolveAndOpen ¶
func (p *ChainFileResolver) ResolveAndOpen(ctx context.Context, path string) (fs.File, string, error)
ResolveAndOpen tries each resolver in order, returning the first successful result. The reported failure is the last resolver's, which is the only one that searched to the end of the chain.
type EmbedFileResolver ¶
type EmbedFileResolver struct {
// contains filtered or unexported fields
}
EmbedFileResolver resolves files from an embedded filesystem (or any fs.FS). No path resolution or security checks — paths are looked up directly.
EmbedFileResolver does NOT implement FileEnumerator because embedded filesystems typically contain a known, fixed set of files that do not need runtime discovery.
func NewEmbedFileResolver ¶
func NewEmbedFileResolver(fsys fs.FS) *EmbedFileResolver
NewEmbedFileResolver creates a resolver backed by the given filesystem. Panics if fsys is nil.
func (*EmbedFileResolver) ResolveAndOpen ¶
ResolveAndOpen finds a file by name and returns an open handle plus the resolved path.
type FSFileResolver ¶
type FSFileResolver struct {
// contains filtered or unexported fields
}
FSFileResolver resolves files from a virtual filesystem (fs.FS). Used when an embedder provides WithSourceFS. All paths are relative to the FS root. Absolute paths are rejected.
Resolution priority:
- Relative to current load directory (from LoadPathStack)
- Library registry search paths
- Relative to FS root (path as-is)
func NewFSFileResolver ¶
func NewFSFileResolver(fsys fs.FS, env *environment.EnvironmentFrame) *FSFileResolver
NewFSFileResolver creates a resolver backed by the given filesystem. Panics if fsys is nil.
func (*FSFileResolver) AuthorizedSource ¶ added in v1.20.0
func (*FSFileResolver) AuthorizedSource() string
AuthorizedSource reports the source every candidate is authorized under: the virtual filesystem this resolver serves. See SourceGate.
func (*FSFileResolver) EnumerateFiles ¶
func (p *FSFileResolver) EnumerateFiles() ([]string, error)
EnumerateFiles walks the virtual filesystem to discover all .sld/.scm files. When registry search paths are configured, only those directories are walked. When no registry paths exist, the FS root "." is walked as a fallback. Hidden directories (starting with ".") are skipped.
Best-effort: non-existent directories and unauthorized files are skipped. Walk errors are joined and returned alongside partial results.
func (*FSFileResolver) ResolveAndOpen ¶
ResolveAndOpen finds a file by name and returns an open handle plus the resolved path.
type FileEnumerator ¶
FileEnumerator is an optional interface that FileResolvers can implement to support file discovery. EnumerateFiles returns slash-separated relative paths to .sld/.scm files found by the resolver.
Results are returned in discovery order with no deduplication; callers are responsible for library-level dedup and interpretation of paths.
type OSFileResolver ¶
type OSFileResolver struct {
// contains filtered or unexported fields
}
OSFileResolver resolves files from the operating system filesystem, using the load path stack, library registry, SCHEME_INCLUDE_PATH, and CWD as fallback directories. It also enforces security authorization.
func NewOSFileResolver ¶
func NewOSFileResolver(env *environment.EnvironmentFrame) *OSFileResolver
NewOSFileResolver creates a resolver that finds files on the OS filesystem.
func (*OSFileResolver) AuthorizedSource ¶ added in v1.20.0
func (*OSFileResolver) AuthorizedSource() string
AuthorizedSource reports the source every candidate is authorized under: the host OS filesystem, which security.AccessRequest spells as the zero value. See SourceGate.
func (*OSFileResolver) EnumerateFiles ¶
func (p *OSFileResolver) EnumerateFiles() ([]string, error)
EnumerateFiles walks the OS filesystem to discover .sld/.scm files. Searches osSearchDirs: library registry paths, SCHEME_INCLUDE_PATH, and CWD. Unlike ResolveAndOpen it does NOT consult the load-path stack's current directory, nor fall back to the filesystem root.
Best-effort: non-existent directories and unauthorized files are skipped. Walk errors are joined and returned alongside partial results.
func (*OSFileResolver) ResolveAndOpen ¶
ResolveAndOpen finds a file by name and returns an open handle plus the resolved path.
Both arms — absolute path and search-path-relative — order identically: authorize the candidate, then open it. The open goes through os.Root when the authorizer confines filesystem access (see confined.go), so a path component swapped between the check and the open cannot redirect the open out of the confinement root.
type SourceGate ¶ added in v1.20.0
type SourceGate interface {
AuthorizedSource() string
}
SourceGate is implemented by a FileResolver that authorizes every candidate before opening it, and names the security.AccessRequest.TargetSource it authorizes them under ("" = the host OS filesystem).
It exists so ChainFileResolver can tell a refusal it may look past from one it may not: a resolver that implements SourceGate under a different source asks the authorizer a different question, while one that does not implement it asks none at all (EmbedFileResolver) and must never be reached with a denial standing. ChainFileResolver itself deliberately does not implement it: a chain authorizes under as many sources as it has members, so there is no single answer, and the conservative reading of "not gated" is the safe one.