diff --git a/src/glua.gleam b/src/glua.gleam index cc5ae51..eab69b9 100644 --- a/src/glua.gleam +++ b/src/glua.gleam @@ -72,6 +72,16 @@ pub fn float(v: Float) -> Value @external(erlang, "glua_ffi", "coerce") pub fn table(values: List(#(Value, Value))) -> Value +pub fn alloc_table(lua: Lua, values: List(#(Value, Value))) -> #(Lua, Value) { + let #(val, lua) = do_alloc_table(values, lua) + #(lua, val) +} + +pub fn alloc_userdata(lua: Lua, a: anything) -> #(Lua, Value) { + let #(val, lua) = do_alloc_userdata(a, lua) + #(lua, val) +} + pub fn table_decoder( keys_decoder: decode.Decoder(a), values_decoder: decode.Decoder(b), @@ -333,7 +343,7 @@ pub fn set( Ok(_) -> Ok(#(keys, lua)) Error(KeyNotFound) -> { - let #(tbl, lua) = alloc_table([], lua) + let #(tbl, lua) = do_alloc_table([], lua) do_set(lua, keys, tbl) |> result.map(fn(lua) { #(keys, lua) }) } @@ -400,8 +410,11 @@ 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, "luerl_heap", "alloc_table") +fn do_alloc_table(content: List(a), lua: Lua) -> #(Value, Lua) + +@external(erlang, "luerl_heap", "alloc_userdata") +fn do_alloc_userdata(a: 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 1edf9e7..8e4a612 100644 --- a/src/glua_ffi.erl +++ b/src/glua_ffi.erl @@ -4,7 +4,8 @@ -export([coerce/1, coerce_nil/0, coerce_userdata/1, 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]). + eval_file_dec/2, eval_chunk/2, eval_chunk_dec/2, call_function/3, call_function_dec/3, + alloc/2]). %% turn `{userdata, Data}` into `Data` to make it more easy to decode it in Gleam maybe_process_userdata(Lst) when is_list(Lst) -> @@ -58,6 +59,21 @@ 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}. + + %% TODO: Improve compiler errors handling and try to detect more errors map_error({error, [{_, luerl_parse, Errors} | _], _}) -> FormattedErrors = lists:map(fun(E) -> list_to_binary(E) end, Errors), @@ -97,6 +113,15 @@ coerce_nil() -> coerce_userdata(X) -> {userdata, X}. +alloc(St0, Value) when is_list(Value) -> + {Enc, St1} = luerl_heap:alloc_table(Value, St0), + {St1, Enc}; +alloc(St0, {usrdef,_}=Value) -> + {Enc, St1} = luerl_heap:alloc_userdata(Value, St0), + {St1, Enc}; +alloc(St0, Other) -> + {St0, Other}. + wrap_fun(Fun) -> fun(Args, State) -> Decoded = luerl:decode_list(Args, State), @@ -165,11 +190,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 184baa2..c69acb3 100644 --- a/test/glua_test.gleam +++ b/test/glua_test.gleam @@ -551,3 +551,22 @@ pub fn nested_function_references_test() { glua.call_function(state: lua, ref:, args: [arg], using: decode.float) assert result == 20.0 } + +pub fn alloc_test() { + let #(lua, table) = glua.alloc_table(glua.new(), []) + let proxy = + glua.function(fn(lua, _args) { #(lua, [glua.string("constant")]) }) + let metatable = glua.table([#(glua.string("__index"), proxy)]) + let assert Ok(#(lua, _)) = + glua.ref_call_function_by_name(lua, ["setmetatable"], [table, metatable]) + let assert Ok(lua) = glua.set(lua, ["test_table"], table) + + let assert Ok(#(_lua, [ret1])) = + glua.eval(lua, "return test_table.any_key", decode.string) + + let assert Ok(#(_lua, [ret2])) = + glua.eval(lua, "return test_table.other_key", decode.string) + + assert ret1 == "constant" + assert ret2 == "constant" +}