fix(lua): hand callbacks Lua.t regardless of entry point#379
Merged
Conversation
A two-arity Elixir callback (`fn args, state -> {results, state} end`)
received a different `state` depending on how it entered the VM. Functions
set directly at a path via `Lua.set!/3` and `deflua` functions were wrapped
so the callback saw the public `Lua.t`, but functions reaching the VM as an
encoded value — via `Lua.encode!/2` or nested inside a value passed to
`Lua.set!/3` — were stored bare and invoked with the raw `Lua.VM.State`.
The raw state is unusable with the public API, so the same closure worked
at a path and crashed as an encoded value.
Extract the `%Lua{}` wrap/unwrap/validate closure builder into a shared
`wrap_callback/3` and inject it into the `Value.encode/3` walk, so every
entry point speaks the documented `Lua.t` convention. Also unifies arity-1
and arity-2 under one builder, giving arity-1 the same return-encoding
validation regardless of entry point.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #377.
Problem
A two-arity Elixir callback (
fn args, state -> {results, state} end) received a differentstate— and had to return a different shape — depending on how it entered the VM:statehanded to the callbackLua.set!(lua, [:f], fun)— fn at the pathLua.t(%Lua{}) ✅deflua+Lua.load_api/3Lua.t(%Lua{}) ✅Lua.set!(lua, [:t], %{"f" => fun})— fn nested in a valueLua.VM.State❌Lua.encode!(lua, fun)— closure encoded at runtimeLua.VM.State❌Lua.set!/3'sis_functionclauses wrap the raw%Lua.VM.State{}into the public%Lua{}, call the callback, unwrap the returned%Lua{}back to its.state, and validate the return is encoded. But functions reaching the VM as an encoded value — viaLua.encode!/2or nested inside a value passed toLua.set!/3— go throughLua.VM.Value.encode/2, which stored them bare ({:native_func, fun}) with no wrapping. The VM then invoked them with the raw internal state, which the public API (Lua.decode!/2,Lua.get_private!/2, …) rejects withno function clause matching. So the same closure worked at a path and crashed as an encoded value.Fix
Lua.VM.Valueis deliberately ignorant of%Lua{}, so the wrapping stays in theLualayer and is now shared by every entry point:lib/lua.ex— extract the wrap/unwrap/validate closure builder into a sharedwrap_callback/3(+validate_encoded!/3). Bothset!-at-path clauses use it, and it is injected into the twoValue.encodecall sites (encode!/2and the nested-valueset!clause).lib/lua/vm/value.ex—encode/3takes an optionalfun_wrapperthreaded through the map/list recursion. The default preserves the prior raw behavior for low-level callers; the publicLuaentry points supply the%Lua{}wrapper.Behavior changes (clean break, intended)
encode!/2or nested in aset!value now receive%Lua{}and return{results, %Lua{}}(or bare results), matching the documented convention. Code relying on the rc.3 raw-state behavior or the%Lua{state: raw}workaround should drop the workaround.Verification
test/lua/callback_state_asymmetry_test.exs(the issue's test module). Its 3 encoded-value cases failed before the fix and pass after.mix test --include lua53— 2592 passed,mix format --check-formatted,mix dialyzer— 0 errors.