I am a per-session evaluation GenServer.
Each instance corresponds to a GT view's evaluation context
(LeSharedSnippetContext). All snippets within the same view
share one Eval process (same bindings).
I track object IDs registered in GtBridge.ObjectRegistry during
my lifetime. When I terminate (session closed), I batch-remove
all tracked objects from the registry.
Cleanup
GT's BeamSessionFinalizer sends POST /SESSION_CLOSE when the
per-view GtSharedVariablesBindings is GC'd (page/inspector closed).
The router calls EvalRegistry.remove/1 which terminates me, and
terminate/2 batch-removes all tracked objects from the registry.
Summary
Functions
I stop the eval running under command_id and answer its caller a
GtBridge.Eval.Error of kind :cancelled.
Returns a specification to start this module under a supervisor.
I encode an evaluation result for the wire.
I evaluate in the calling process and return the value, so the answer can travel back in the HTTP response rather than as a second message.
I drain and return all messages received by the eval process.
I return the current bindings as a map of name→serialized value. Internal bindings (:port, :command_id, :pid) are filtered out. Non-primitive values are registered in ObjectRegistry.
I return documentation for a module, function, or type.
I register each element of list and return what GT dresses as a
proxy, leaving primitives inline.
Remove an object from the registry. Called by GT when a proxy object is garbage collected.
Types
@type t() :: %GtBridge.Eval{ bindings: Code.binding() | nil, env: Macro.Env.t() | nil, locals: [{atom(), arity()}], port: non_neg_integer(), registered_ids: MapSet.t(non_neg_integer()), running: %{ optional(reference()) => {String.t() | nil, Task.t(), GenServer.from()} } }
Functions
@spec cancel(GenServer.server(), String.t() | nil) :: :ok
I stop the eval running under command_id and answer its caller a
GtBridge.Eval.Error of kind :cancelled.
I am how a stopped eval on the GT side stops costing anything here. Nothing else can interrupt user code: it runs in a task I own, and killing that task is the only way to take the session back.
I answer :ok whether or not there was anything to kill, so a
cancel that races the eval's own completion is not an error.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec complete(GenServer.server(), String.t(), String.t() | nil) :: [String.t()]
I encode an evaluation result for the wire.
I register complex values in GtBridge.ObjectRegistry and send an
%{exid, exclass} reference, so GT receives a proxy it can inspect
lazily rather than a fully materialised copy. Primitives travel by
value.
@spec eval(GenServer.server(), String.t(), String.t() | nil) :: any()
@spec eval_stateless_sync(String.t(), String.t() | nil, pos_integer() | nil) :: term()
I evaluate in the calling process and return the value, so the answer can travel back in the HTTP response rather than as a second message.
A request already runs in its own process, so running inline gives the same isolation a spawned Task would.
@spec flush() :: [term()]
I drain and return all messages received by the eval process.
Like IEx's flush/0. Useful when user code subscribes the eval
process to event brokers and you want to see what arrived.
flush()
@spec get_bindings(GenServer.server()) :: map()
I return the current bindings as a map of name→serialized value. Internal bindings (:port, :command_id, :pid) are filtered out. Non-primitive values are registered in ObjectRegistry.
I return documentation for a module, function, or type.
Bound as h in every eval session. Because I am a macro, I can
parse dot-syntax like h(Enum.map) and h(Enum.map/2).
h(Enum)
h(Enum.map)
h(Enum.map/2)
h({Enum, :map})
h({Enum, :map, 2})
I register each element of list and return what GT dresses as a
proxy, leaving primitives inline.
GtBridge.Serializer.to_json inlines a struct as a plain map, so a
list fetched through it arrives as data with no remote identity. I
give each element the same %{exclass, exid} reference an eval
result gets, in one call rather than one per element.
[1, URI.parse("http://a.b")] |> references()
@spec remove(non_neg_integer()) :: :ok
Remove an object from the registry. Called by GT when a proxy object is garbage collected.