khepri_fun (khepri v0.6.0)

Anonymous function extraction private 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 availability 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.

Their assembly code isn't available in the 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.

This module is private. The documentation is still visible because it may help understand some implementation details. However, this module should never be called directly outside of Khepri.

Link to this section Summary

Types

Options to tune the extraction of an anonymous function.

The result of an extraction, as returned by to_standalone_fun/2.

Functions

Executes a previously extracted anonymous function.

Extracts the given anonymous function

Extracts the given anonymous function

Link to this section Types

-type options() ::
    #{ensure_instruction_is_permitted => ensure_instruction_is_permitted_fun(),
      should_process_function => should_process_function_fun(),
      is_standalone_fun_still_needed => is_standalone_fun_still_needed_fun(),
      add_module_info => boolean()}.

Options to tune the extraction of an anonymous function.

  • ensure_instruction_is_permitted: a function which evaluates if an instruction is permitted or not.
  • should_process_function: a function which returns if a called module and function should be extracted as well or left alone.
  • is_standalone_fun_still_needed: a function which returns if, after the extraction is finished, the extracted function is still needed in comparison to keeping the initial anonymous function.
  • add_module_info: a boolean to indicate if the module_info/0 and module_info/1 functions should be added to the generated module. There are used by tracing and debugging tools in Erlang, but they take some space (about 140 bytes). Default is true.
Link to this type

standalone_fun/0

-type standalone_fun() :: #standalone_fun{} | fun().

The result of an extraction, as returned by to_standalone_fun/2.

It can be stored, passed between processes and Erlang nodes. To execute the extracted function, simply call exec/2 which works like erlang:apply/2.

Link to this section Functions

Link to this function

exec(StandaloneFun, Args)

-spec exec(StandaloneFun, Args) -> Ret
        when StandaloneFun :: standalone_fun(), Args :: [any()], Ret :: any().

Executes a previously extracted anonymous function.

This is the equivalent of erlang:apply/2 but it supports extracted anonymous functions.

The list of Args must match the arity of the anonymous function.
Link to this function

to_standalone_fun(Fun)

-spec to_standalone_fun(Fun) -> StandaloneFun when Fun :: fun(), StandaloneFun :: standalone_fun().

Extracts the given anonymous function

This is the same as:
  khepri_fun:to_standalone_fun(Fun, #{}).
Link to this function

to_standalone_fun(Fun, Options)

-spec to_standalone_fun(Fun, Options) -> StandaloneFun
                     when Fun :: fun(), Options :: options(), StandaloneFun :: standalone_fun().
Extracts the given anonymous function