From 8a03e239b384f523a538b07b4cd79026f7a74fa1 Mon Sep 17 00:00:00 2001 From: selenil Date: Tue, 13 Jan 2026 15:35:35 -0500 Subject: [PATCH 1/3] partially revert encoders --- src/glua.gleam | 33 ++++++++++++++++++++++--------- src/glua_ffi.erl | 36 ++++++++++++++++++++++------------ test/glua_test.gleam | 46 ++++++++++++++++++++------------------------ 3 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/glua.gleam b/src/glua.gleam index c475e1a..56e473c 100644 --- a/src/glua.gleam +++ b/src/glua.gleam @@ -7,6 +7,7 @@ import gleam/dynamic/decode import gleam/int import gleam/list import gleam/option +import gleam/pair import gleam/result import gleam/string @@ -247,8 +248,20 @@ pub fn int(v: Int) -> Value @external(erlang, "glua_ffi", "coerce") pub fn float(v: Float) -> Value -@external(erlang, "glua_ffi", "coerce") -pub fn table(values: List(#(Value, Value))) -> Value +pub fn table(lua: Lua, values: List(#(Value, Value))) -> #(Lua, Value) { + let #(lua, values) = + list.fold(values, #(lua, []), fn(acc, pair) { + let #(lua, values) = acc + let #(enc1, lua) = encode(pair.0, lua) + let #(enc2, lua) = encode(pair.1, lua) + #(lua, [#(enc1, enc2), ..values]) + }) + + do_table(values, lua) |> pair.swap +} + +@external(erlang, "luerl_heap", "alloc_table") +fn do_table(values: List(#(Value, Value)), lua: Lua) -> #(Value, Lua) pub fn table_decoder( keys_decoder: decode.Decoder(a), @@ -292,10 +305,11 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// } /// /// let state = glua.new() +/// let #(state, userdata) = glua.userdata(state, User(name: "Jhon Doe", is_admin: False)) /// let assert Ok(state) = glua.set( /// state:, /// keys: ["a_user"], -/// value: glua.userdata(User(name: "Jhon Doe", is_admin: False)) +/// value: userdata /// ) /// /// let assert Ok(#(_, [result])) = glua.eval(state:, code: "return a_user", using: user_decoder) @@ -308,17 +322,18 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// } /// /// let state = glua.new() +/// let #(state, userdata) = glua.userdata(state, Person(name: "Lucy", email: "lucy@example.com")) /// let assert Ok(lua) = glua.set( /// state:, /// keys: ["lucy"], -/// value: glua.userdata(Person(name: "Lucy", email: "lucy@example.com")) +/// value: userdata /// ) /// /// let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(_), _)) = /// glua.eval(state:, code: "return lucy.email", using: decode.string) /// ``` -@external(erlang, "glua_ffi", "coerce_userdata") -pub fn userdata(v: anything) -> Value +@external(erlang, "glua_ffi", "alloc_userdata") +pub fn userdata(lua: Lua, v: anything) -> #(Lua, Value) @external(erlang, "glua_ffi", "wrap_fun") fn wrap_function( @@ -511,7 +526,7 @@ pub fn set( Ok(_) -> Ok(#(keys, lua)) Error(KeyNotFound(_)) -> { - let #(tbl, lua) = alloc_table([], lua) + let #(tbl, lua) = do_table([], lua) do_set(lua, keys, tbl) |> result.map(fn(lua) { #(keys, lua) }) } @@ -578,8 +593,8 @@ pub fn set_lua_paths( set(lua, ["package", "path"], paths) } -@external(erlang, "luerl_emul", "alloc_table") -fn alloc_table(content: List(a), lua: Lua) -> #(a, Lua) +@external(erlang, "glua_ffi", "encode") +fn encode(value: anything, lua: Lua) -> #(Value, Lua) @external(erlang, "glua_ffi", "get_table_keys_dec") fn do_get(lua: Lua, keys: List(String)) -> Result(dynamic.Dynamic, LuaError) diff --git a/src/glua_ffi.erl b/src/glua_ffi.erl index 497a1b9..0577b78 100644 --- a/src/glua_ffi.erl +++ b/src/glua_ffi.erl @@ -1,7 +1,7 @@ -module(glua_ffi). -import(luerl_lib, [lua_error/2]). --export([get_stacktrace/1, coerce/1, coerce_nil/0, coerce_userdata/1, wrap_fun/1, sandbox_fun/1, get_table_keys/2, get_table_keys_dec/2, +-export([get_stacktrace/1, encode/2, coerce/1, coerce_nil/0, alloc_userdata/2, wrap_fun/1, sandbox_fun/1, get_table_keys/2, get_table_keys_dec/2, get_private/2, set_table_keys/3, load/2, load_file/2, eval/2, eval_dec/2, eval_file/2, eval_file_dec/2, eval_chunk/2, eval_chunk_dec/2, call_function/3, call_function_dec/3]). @@ -44,7 +44,7 @@ is_encoded(N) when is_number(N) -> true; is_encoded({tref,_}) -> true; -is_encoded({usrdef,_}) -> +is_encoded({usdref,_}) -> true; is_encoded({eref,_}) -> true; @@ -57,6 +57,20 @@ is_encoded({erl_mfa,_,_,_}) -> is_encoded(_) -> false. +encode(X, St0) -> + case is_encoded(X) of + true -> {X, St0}; + false -> luerl:encode(X, St0) + end. + +encode_list(L, St0) when is_list(L) -> + Enc = fun(X, {L1, St}) -> + {Enc, St1} = encode(X, St), + {[Enc | L1], St1} + end, + {L1, St1} = lists:foldl(Enc, {[], St0}, L), + {lists:reverse(L1), St1}. + map_error({error, Errors, _}) -> {lua_compile_failure, lists:map(fun map_compile_error/1, Errors)}; map_error({lua_error, {illegal_index, Value, Index}, State}) -> @@ -190,14 +204,15 @@ coerce(X) -> coerce_nil() -> nil. -coerce_userdata(X) -> - {userdata, X}. +alloc_userdata(Lua, X) -> + {Enc, St} = encode({userdata, X}, Lua), + {St, Enc}. wrap_fun(Fun) -> fun(Args, State) -> Decoded = luerl:decode_list(Args, State), {NewState, Ret} = Fun(State, Decoded), - luerl:encode_list(Ret, NewState) + encode_list(Ret, NewState) end. sandbox_fun(Msg) -> @@ -224,11 +239,8 @@ get_table_keys_dec(Lua, Keys) -> end. set_table_keys(Lua, Keys, Value) -> - SetFun = case is_encoded(Value) of - true -> fun luerl:set_table_keys/3; - false -> fun luerl:set_table_keys_dec/3 - end, - to_gleam(SetFun(Keys, Value, Lua)). + {Enc, St} = encode(Value, Lua), + to_gleam(luerl:set_table_keys(Keys, Enc, St)). load(Lua, Code) -> to_gleam(luerl:load( @@ -264,11 +276,11 @@ eval_file_dec(Lua, Path) -> unicode:characters_to_list(Path), Lua)). call_function(Lua, Fun, Args) -> - {EncodedArgs, State} = luerl:encode_list(Args, Lua), + {EncodedArgs, State} = encode_list(Args, Lua), to_gleam(luerl:call(Fun, EncodedArgs, State)). call_function_dec(Lua, Fun, Args) -> - {EncodedArgs, St1} = luerl:encode_list(Args, Lua), + {EncodedArgs, St1} = encode_list(Args, Lua), case luerl:call(Fun, EncodedArgs, St1) of {ok, Ret, St2} -> Values = luerl:decode_list(Ret, St2), diff --git a/test/glua_test.gleam b/test/glua_test.gleam index a7cce4e..b3dfeb8 100644 --- a/test/glua_test.gleam +++ b/test/glua_test.gleam @@ -21,11 +21,13 @@ pub fn get_table_test() { ] let cool_numbers = glua.function(fn(lua, _params) { - let table = + let #(lua, table) = glua.table( + lua, my_table - |> list.map(fn(pair) { #(glua.string(pair.0), glua.int(pair.1)) }), + |> list.map(fn(pair) { #(glua.string(pair.0), glua.int(pair.1)) }), ) + #(lua, [table]) }) @@ -119,17 +121,11 @@ pub fn new_sandboxed_test() { } pub fn encoding_and_decoding_nested_tables_test() { - let nested_table = [ - #( - glua.string("key"), - glua.table([ - #( - glua.int(1), - glua.table([#(glua.string("deeper_key"), glua.string("deeper_value"))]), - ), - ]), - ), - ] + let lua = glua.new() + let #(lua, tb1) = + glua.table(lua, [#(glua.string("deeper_key"), glua.string("deeper_value"))]) + let #(lua, tb2) = glua.table(lua, [#(glua.int(1), tb1)]) + let #(lua, tb3) = glua.table(lua, [#(glua.string("key"), tb2)]) let keys = ["my_nested_table"] @@ -141,9 +137,8 @@ pub fn encoding_and_decoding_nested_tables_test() { glua.table_decoder(decode.string, decode.string), ), ) - let tbl = glua.table(nested_table) - let assert Ok(lua) = glua.set(state: glua.new(), keys:, value: tbl) + let assert Ok(lua) = glua.set(state: lua, keys:, value: tb3) let assert Ok(result) = glua.get(state: lua, keys:, using: nested_table_decoder) @@ -157,22 +152,21 @@ pub type Userdata { pub fn userdata_test() { let lua = glua.new() - let userdata = Userdata("my-userdata", 1) + let #(lua, userdata) = glua.userdata(lua, Userdata("my-userdata", 1)) let userdata_decoder = { use foo <- decode.field(1, decode.string) use bar <- decode.field(2, decode.int) decode.success(Userdata(foo:, bar:)) } - let assert Ok(lua) = glua.set(lua, ["my_userdata"], glua.userdata(userdata)) + let assert Ok(lua) = glua.set(lua, ["my_userdata"], userdata) let assert Ok(#(lua, [result])) = glua.eval(lua, "return my_userdata", userdata_decoder) - assert result == userdata + assert result == Userdata("my-userdata", 1) - let userdata = Userdata("other_userdata", 2) - let assert Ok(lua) = - glua.set(lua, ["my_other_userdata"], glua.userdata(userdata)) + let #(lua, userdata) = glua.userdata(lua, Userdata("other-userdata", 2)) + let assert Ok(lua) = glua.set(lua, ["my_other_userdata"], userdata) let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(index, _), _)) = glua.eval(lua, "return my_other_userdata.foo", decode.string) @@ -236,8 +230,9 @@ pub fn set_test() { let keys = ["math", "squares"] - let encoded = + let #(lua, encoded) = glua.table( + lua, numbers |> list.map(fn(pair) { #(glua.int(pair.0), glua.int(pair.1)) }), ) let assert Ok(lua) = glua.set(lua, keys, encoded) @@ -260,8 +255,9 @@ pub fn set_test() { let encoded = glua.function(count_odd) let assert Ok(lua) = glua.set(glua.new(), ["count_odd"], encoded) - let arg = + let #(lua, arg) = glua.table( + lua, list.index_map(list.range(1, 10), fn(i, n) { #(glua.int(i + 1), glua.int(n)) }), @@ -277,8 +273,8 @@ pub fn set_test() { assert result == 5 - let tbl = - glua.table([ + let #(lua, tbl) = + glua.table(lua, [ #( glua.string("is_even"), glua.function(fn(lua, args) { From 5dd4aabf751ca0085c798cd827f5515ed6ab89df Mon Sep 17 00:00:00 2001 From: selenil Date: Tue, 13 Jan 2026 21:51:16 -0500 Subject: [PATCH 2/3] remove unnecessary ffi functions --- src/glua.gleam | 19 +++++--------- src/glua_ffi.erl | 66 ++++++++---------------------------------------- 2 files changed, 16 insertions(+), 69 deletions(-) diff --git a/src/glua.gleam b/src/glua.gleam index 56e473c..692cfa4 100644 --- a/src/glua.gleam +++ b/src/glua.gleam @@ -249,14 +249,6 @@ pub fn int(v: Int) -> Value pub fn float(v: Float) -> Value pub fn table(lua: Lua, values: List(#(Value, Value))) -> #(Lua, Value) { - let #(lua, values) = - list.fold(values, #(lua, []), fn(acc, pair) { - let #(lua, values) = acc - let #(enc1, lua) = encode(pair.0, lua) - let #(enc2, lua) = encode(pair.1, lua) - #(lua, [#(enc1, enc2), ..values]) - }) - do_table(values, lua) |> pair.swap } @@ -332,8 +324,12 @@ pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { /// let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(_), _)) = /// glua.eval(state:, code: "return lucy.email", using: decode.string) /// ``` -@external(erlang, "glua_ffi", "alloc_userdata") -pub fn userdata(lua: Lua, v: anything) -> #(Lua, Value) +pub fn userdata(lua: Lua, v: anything) -> #(Lua, Value) { + do_userdata(v, lua) |> pair.swap +} + +@external(erlang, "luerl_heap", "alloc_userdata") +fn do_userdata(v: anything, lua: Lua) -> #(Value, Lua) @external(erlang, "glua_ffi", "wrap_fun") fn wrap_function( @@ -593,9 +589,6 @@ pub fn set_lua_paths( set(lua, ["package", "path"], paths) } -@external(erlang, "glua_ffi", "encode") -fn encode(value: anything, lua: Lua) -> #(Value, Lua) - @external(erlang, "glua_ffi", "get_table_keys_dec") fn do_get(lua: Lua, keys: List(String)) -> Result(dynamic.Dynamic, LuaError) diff --git a/src/glua_ffi.erl b/src/glua_ffi.erl index 0577b78..7ef48f4 100644 --- a/src/glua_ffi.erl +++ b/src/glua_ffi.erl @@ -1,7 +1,7 @@ -module(glua_ffi). -import(luerl_lib, [lua_error/2]). --export([get_stacktrace/1, encode/2, coerce/1, coerce_nil/0, alloc_userdata/2, wrap_fun/1, sandbox_fun/1, get_table_keys/2, get_table_keys_dec/2, +-export([get_stacktrace/1, coerce/1, coerce_nil/0, wrap_fun/1, sandbox_fun/1, get_table_keys/2, get_table_keys_dec/2, get_private/2, set_table_keys/3, load/2, load_file/2, eval/2, eval_dec/2, eval_file/2, eval_file_dec/2, eval_chunk/2, eval_chunk_dec/2, call_function/3, call_function_dec/3]). @@ -30,47 +30,6 @@ to_gleam(Value) -> {error, {unknown_error, nil}} end. -%% helper to determine if a value is encoded or not -%% borrowed from https://github.com/tv-labs/lua/blob/main/lib/lua/util.ex#L19-L35 -is_encoded(nil) -> - true; -is_encoded(true) -> - true; -is_encoded(false) -> - true; -is_encoded(Binary) when is_binary(Binary) -> - true; -is_encoded(N) when is_number(N) -> - true; -is_encoded({tref,_}) -> - true; -is_encoded({usdref,_}) -> - true; -is_encoded({eref,_}) -> - true; -is_encoded({funref,_,_}) -> - true; -is_encoded({erl_func,_}) -> - true; -is_encoded({erl_mfa,_,_,_}) -> - true; -is_encoded(_) -> - false. - -encode(X, St0) -> - case is_encoded(X) of - true -> {X, St0}; - false -> luerl:encode(X, St0) - end. - -encode_list(L, St0) when is_list(L) -> - Enc = fun(X, {L1, St}) -> - {Enc, St1} = encode(X, St), - {[Enc | L1], St1} - end, - {L1, St1} = lists:foldl(Enc, {[], St0}, L), - {lists:reverse(L1), St1}. - map_error({error, Errors, _}) -> {lua_compile_failure, lists:map(fun map_compile_error/1, Errors)}; map_error({lua_error, {illegal_index, Value, Index}, State}) -> @@ -204,19 +163,17 @@ coerce(X) -> coerce_nil() -> nil. -alloc_userdata(Lua, X) -> - {Enc, St} = encode({userdata, X}, Lua), - {St, Enc}. - wrap_fun(Fun) -> - fun(Args, State) -> + {erl_func, fun(Args, State) -> Decoded = luerl:decode_list(Args, State), {NewState, Ret} = Fun(State, Decoded), - encode_list(Ret, NewState) - end. + {Ret, NewState} + end}. sandbox_fun(Msg) -> - fun(_, State) -> {error, map_error(lua_error({error_call, [Msg]}, State))} end. + {erl_func, fun(_, State) -> + {error, map_error(lua_error({error_call, [Msg]}, State))} + end}. get_table_keys(Lua, Keys) -> case luerl:get_table_keys(Keys, Lua) of @@ -239,8 +196,7 @@ get_table_keys_dec(Lua, Keys) -> end. set_table_keys(Lua, Keys, Value) -> - {Enc, St} = encode(Value, Lua), - to_gleam(luerl:set_table_keys(Keys, Enc, St)). + to_gleam(luerl:set_table_keys(Keys, Value, Lua)). load(Lua, Code) -> to_gleam(luerl:load( @@ -276,12 +232,10 @@ eval_file_dec(Lua, Path) -> unicode:characters_to_list(Path), Lua)). call_function(Lua, Fun, Args) -> - {EncodedArgs, State} = encode_list(Args, Lua), - to_gleam(luerl:call(Fun, EncodedArgs, State)). + to_gleam(luerl:call(Fun, Args, Lua)). call_function_dec(Lua, Fun, Args) -> - {EncodedArgs, St1} = encode_list(Args, Lua), - case luerl:call(Fun, EncodedArgs, St1) of + case luerl:call(Fun, Args, Lua) of {ok, Ret, St2} -> Values = luerl:decode_list(Ret, St2), {ok, {St2, Values}}; From 1088c412c73c18cc4e09d7345a57b321da1273f2 Mon Sep 17 00:00:00 2001 From: selenil Date: Wed, 14 Jan 2026 17:51:38 -0500 Subject: [PATCH 3/3] renaming --- src/glua.gleam | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/glua.gleam b/src/glua.gleam index 692cfa4..b9608b3 100644 --- a/src/glua.gleam +++ b/src/glua.gleam @@ -271,8 +271,7 @@ pub fn table_decoder( pub fn function( f: fn(Lua, List(dynamic.Dynamic)) -> #(Lua, List(Value)), ) -> Value { - // we need a little wrapper for functions to satisfy luerl's order of arguments and return value type - wrap_function(f) + do_function(f) } pub fn list(encoder: fn(a) -> Value, values: List(a)) -> List(Value) { @@ -332,7 +331,7 @@ pub fn userdata(lua: Lua, v: anything) -> #(Lua, Value) { fn do_userdata(v: anything, lua: Lua) -> #(Value, Lua) @external(erlang, "glua_ffi", "wrap_fun") -fn wrap_function( +fn do_function( fun: fn(Lua, List(dynamic.Dynamic)) -> #(Lua, List(Value)), ) -> Value