An Elixir compiler tracer for Dyn compiles (Code.put_compiler_option(:tracers, ...)):
which modules of the generation a file needs at compile time, so
Rho.Dyn.Compiler knows when a file compiled for an earlier
generation can be reused as it is (Rho.Dyn.Reuse).
A file needs module M at compile time when it expands %M{}, imports
or requires M, uses one of its macros, or calls M outside a function
body (in a module attribute, say): its compiled code then depends on
M's code, not only on its name. Calls inside function bodies only name
M and don't count.
It records only while collect/2 runs in the calling process (the
tracer runs in the process that expands the code), so other compiles in
the VM are untouched. collect/2 also reports the modules the file
defined (:on_module); a caller that sees fewer than it compiled treats
the file's needs as unknown.
It is also the gate on what a Dyn compile may define, before anything
loads (guard/3, in the compiling process): when a module's definition
starts (:defmodule) it is reported to the compiler, which undoes what
the compile defined outside the generation, and refused (the compile of
that file fails) unless it is <ns>.G<n>.* (the namespace,
Rho.Dyn.Config) or named as a protocol's implementation for one
(Inspect.<ns>.G<n>.X, with
Inspect a protocol). A Protocol.derive/2 for a stdlib type or a
library macro's module elsewhere never replaces anything.
Summary
Functions
May a compile for generation own ("Elixir.Dyn.G<n>.") define
mod: one of its own, or a protocol's implementation for one?
Runs fun (one file's compile) and returns its result with what it
traced: the modules defined and the modules named prefix* it needed
at compile time.
From now on in this process, every module definition that starts is
sent to pid as {ref, :defmodule, module, prior} (prior: the md5
and file of the version loaded under that name until then, or nil), and
one outside generation n (see the moduledoc) raises instead of being
defined.
Adds this tracer to the compiler's (once per VM).
Functions
May a compile for generation own ("Elixir.Dyn.G<n>.") define
mod: one of its own, or a protocol's implementation for one?
@spec collect(String.t(), (-> result)) :: {result, %{defined: MapSet.t(module()), needs: MapSet.t(module())}} when result: term()
Runs fun (one file's compile) and returns its result with what it
traced: the modules defined and the modules named prefix* it needed
at compile time.
@spec guard(pos_integer(), pid(), reference()) :: :ok
From now on in this process, every module definition that starts is
sent to pid as {ref, :defmodule, module, prior} (prior: the md5
and file of the version loaded under that name until then, or nil), and
one outside generation n (see the moduledoc) raises instead of being
defined.
@spec install() :: :ok
Adds this tracer to the compiler's (once per VM).