Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ version = "0.1.0"

[deps]
CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[weakdeps]

[extensions]

[compat]
CRC32c = "1"
Serialization = "1"
julia = "1.12"

[extras]
Expand Down
31 changes: 31 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ VerificationReport
ProtectionReport
ClosureReport
MissingDep
RefDescriptor
RefTarget
ImageSidecar
Sidecar
TranslationReport
CanonicalizeReport
```

## Header Inspection
Expand All @@ -36,6 +42,31 @@ resolve_all_deps
verify_closure
```

## Reference Translation

Re-point a private package image's cross-image references at a *consumer's own
rebuild* of its dependencies, using live reflection on both sides (the
productization of the bundle-v2 research pipeline, legs 4-5). The builder emits a
semantic [`Sidecar`](@ref); the consumer translates a copy of the image against
it, loads it, and runs the post-load type-hash repair.

Each target is described by a [`RefDescriptor`](@ref): a **named** entity
(`:module` / `:binding` / `:type` / `:typename` / `:function`), a nearest-named
`:anchor` + field path, or — for anonymous objects with no build-stable path
(format-spec / method-sig / type-cache svecs, interned const-data strings) — an
**order-independent content descriptor** (`:svec_content` / `:const_data`) that
the consumer re-locates in its own rebuilt dep blob by structural content match
rather than by offset or cache order.

```@docs
emit_sidecar
translate!
canonicalize!
load_translated
write_sidecar
read_sidecar
```

## Transparent Loading Hooks

```@docs
Expand Down
10 changes: 10 additions & 0 deletions src/JuliaStaticData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module JuliaStaticData

using Base: UUID
using CRC32c: crc32c
import Serialization

# Types (must be loaded first — other modules depend on these)
include("types.jl")
Expand All @@ -52,6 +53,9 @@ include("loader.jl")
# Identity stamping (nonce + self-consistent CRC) and pre-load verification
include("identity.jl")

# Reference translation (sidecar emit / consumer translate / canonicalize)
include("translate.jl")

# Optional monkey-patch mode
include("hooks.jl")

Expand All @@ -65,6 +69,8 @@ export PkgImageHeader, WorklistEntry, DepModEntry
export RemapSpec
export VerificationReport, ProtectionReport
export ClosureReport, MissingDep
export RefDescriptor, RefTarget, ImageSidecar, Sidecar
export TranslationReport, CanonicalizeReport

# Header
export parse_header, inspect
Expand All @@ -78,6 +84,10 @@ export load_package_image, resolve_dep, resolve_all_deps, verify_closure
# Identity stamping + verification
export stamp_identity!, dry_verify

# Reference translation
export emit_sidecar, translate!, canonicalize!, load_translated
export write_sidecar, read_sidecar

# Hooks
export install_hooks!, uninstall_hooks!

Expand Down
20 changes: 20 additions & 0 deletions src/loader.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@ function _find_loaded_module(name::AbstractString, build_id::UInt128)
return nothing
end

# The loaded module for a `PkgId`, searching `loaded_precompiles` (where
# `load_package_image` registers restored modules) as well as `loaded_modules` and
# the well-known roots. A private image loaded via `load_package_image` lands in
# `loaded_precompiles` but NOT `loaded_modules`, so a plain `root_module`/
# `loaded_modules` lookup misses it — this finds it (used when translating a
# downstream private that depends on an already-loaded private, and when remapping
# headers to the loaded universe).
function _loaded_module_by_pid(pid::Base.PkgId)
m = get(Base.loaded_modules, pid, nothing)
m === nothing || return m
for (p, mods) in Base.loaded_precompiles
(p.uuid === pid.uuid && p.name == pid.name) || continue
isempty(mods) || return last(mods)
end
for mod in (Core, Base, Main)
(pid.name == String(nameof(mod))) && return mod
end
return nothing
end

# All 128-bit build-ids under which a module `name` is currently loaded (across
# `loaded_precompiles` and `loaded_modules`). Used to distinguish a genuinely
# `:absent` dependency from a `:mixed_lineage` one (name present, wrong build-id).
Expand Down
Loading
Loading