Anonymous function extraction API.
This module is responsible for extracting the code of an anonymous function. The goal is to be able to store the extracted function and execute it later, regardless of the available of the initial Erlang module which declared it.
This module also provides a way for the caller to indicate forbidden operations or function calls.
This module works on assembly code to perform all checks and prepare the
storable copy of a function. It uses beam_disasm:file/1
from the
compiler
application to extract the assembly code. After the assembly
code was extracted and modified, the compiler is used again to compile the
code back to an executable module.
If the anonymous function calls other functions, either in the same module or in another one, the code of the called functions is extracted and copied as well. This is to make sure the result is completely standalone.
To avoid any copies of standard Erlang APIs or Khepri itself, it is possible to specify a list of modules which should not be copied. In this case, calls to functions in those modules are left unmodified.
Once the code was extracted and verified, a new module is generated as an
"assembly form", ready to be compiled again to an executable module. The
generated module has a single run/N
function. This function contains the
code of the extracted anonymous function.
Because this process works on the assembly code, it means that if the initial module hosting the anonymous function was compiled with Erlang version N, it will probably not compile or run on older versions of Erlang. The reason is that a newer compiler may use instructions which are unknown to older runtimes.
There is a special treatment for anonymous functions evaluated by
erl_eval
(e.g. in the Erlang shell). "erl_eval functions" are lambdas
parsed from text and are evaluated using erl_eval
.
This kind of lambdas becomes a local function in the erl_eval
module.
erl_eval
module. However, the
abstract code (i.e. after parsing but before compilation) is available in
the env
. We compile that abstract code and extract the assembly from that
compiled beam.
beam_instr() = atom() | tuple()
ensure_instruction_is_permitted_fun() = fun((beam_instr()) -> ok)
options() = #{ensure_instruction_is_permitted => ensure_instruction_is_permitted_fun(), should_process_function => should_process_function_fun(), validate => validate_fun()}
should_process_function_fun() = fun((module(), atom(), arity(), module()) -> boolean())
standalone_fun() = #standalone_fun{module = module(), beam = binary(), arity = arity(), env = list()} | function()
validate_fun() = fun((#{calls := #{mfa() => true}, errors := [any()]}) -> any())
to_standalone_fun/2 | |
exec/2 |
to_standalone_fun(Fun, Options) -> StandaloneFun
Fun = function()
Options = options()
StandaloneFun = standalone_fun()
exec(StandaloneFun, Args) -> Ret
StandaloneFun = standalone_fun()
Args = [any()]
Ret = any()
Generated by EDoc