diff --git a/src/luerl.erl b/src/luerl.erl index aebcd35..3ef1752 100644 --- a/src/luerl.erl +++ b/src/luerl.erl @@ -169,15 +169,20 @@ call_chunk(C, As, St0) -> call_function(Fp, As, St0) -> %% Encode the input arguments. + %% file:write_file("/Users/rv/lt", io_lib:format("cfp ~p\n", [Fp]), [append]), {Lfp,St1} = encode_list(Fp, St0), {Las,St2} = encode_list(As, St1), %% Find the function definition and call function. + %% file:write_file("/Users/rv/lt", io_lib:format("cfa ~p\n", [{Lfp,Las}]), [append]), {Lrs,St3} = call_function1(Lfp, Las, St2), + %% file:write_file("/Users/rv/lt", io_lib:format("cfl ~p\n", [Lrs]), [append]), Rs = decode_list(Lrs, St3), + %% file:write_file("/Users/rv/lt", io_lib:format("cfr ~p\n", [Rs]), [append]), {Rs,St3}. call_function1(Lfp, Las, St0) when is_list(Lfp) -> {F,St1} = luerl_emul:get_table_keys(Lfp, St0), + %% file:write_file("/Users/rv/lt", io_lib:format("cf1 ~p\n", [F]), [append]), luerl_emul:functioncall(F, Las, St1); call_function1(F, Las, St) -> luerl_emul:functioncall(F, Las, St). @@ -344,44 +349,38 @@ encode(false, St) -> {false,St}; encode(true, St) -> {true,St}; encode(B, St) when is_binary(B) -> {B,St}; encode(A, St) when is_atom(A) -> {atom_to_binary(A, utf8),St}; -encode(N, St) when is_number(N) -> {N,St}; %Integers and floats +encode(N, St) when is_number(N) -> {N,St}; %Integers and floats encode(F, St) when ?IS_MAP(F) -> encode(maps:to_list(F), St); encode(L, St0) when is_list(L) -> - {Es,{_,St1}} = lists:mapfoldl(fun ({K0,V0}, {I,S0}) -> - {K1,S1} = encode(K0, S0), - {V1,S2} = encode(V0, S1), - {{K1,V1},{I,S2}}; - (V0, {I,S0}) -> - {V1,S1} = encode(V0, S0), - {{I,V1},{I+1,S1}} - end, {1,St0}, L), + %% Encode the table elements in the list. + EncTab = fun ({K0,V0}, {I,S0}) -> + {K1,S1} = encode(K0, S0), + {V1,S2} = encode(V0, S1), + {{K1,V1},{I,S2}}; + (V0, {I,S0}) -> + {V1,S1} = encode(V0, S0), + {{I,V1},{I+1,S1}} + end, + {Es,{_,St1}} = lists:mapfoldl(EncTab, {1,St0}, L), {T,St2} = luerl_heap:alloc_table(Es, St1), - {T,St2}; %No more to do for now + {T,St2}; %No more to do for now encode(F, St) when is_function(F, 2) -> - F1 = fun(Args, State) -> - Args1 = decode_list(Args, State), - {Res, State1} = F(Args1, State), - encode_list(Res, State1) - end, + F1 = fun(Args, State) -> F(Args, State) end, {#erl_func{code=F1}, St}; encode(F, St) when is_function(F, 1) -> - F1 = fun(Args, State) -> - Args1 = decode_list(Args, State), - Res = F(Args1), - encode_list(Res, State) - end, + F1 = fun(Args, State) -> Res = F(Args), {Res,State} end, {#erl_func{code=F1}, St}; encode({M,F,A}, St) when is_atom(M) and is_atom(F) -> {#erl_mfa{m=M,f=F,a=A}, St}; encode({userdata,Data}, St) -> luerl_heap:alloc_userdata(Data, St); -% Table refs should not be re-encoded -encode(#tref{}=T, St) -> - case luerl_heap:chk_table(T, St) of - ok -> {T, St}; - error -> error(badarg) - end; -encode(_, _) -> error(badarg). %Can't encode anything else +%% % Table refs should not be re-encoded +%% encode(#tref{}=T, St) -> +%% case luerl_heap:chk_table(T, St) of +%% ok -> {T, St}; +%% error -> error(badarg) +%% end; +encode(Term, _) -> error({badarg,Term}). %Can't encode anything else %% decode_list([LuerlTerm], State) -> [Term]. %% decode(LuerlTerm, State) -> Term. @@ -398,42 +397,53 @@ decode(nil, _, _) -> nil; decode(false, _, _) -> false; decode(true, _, _) -> true; decode(B, _, _) when is_binary(B) -> B; -decode(N, _, _) when is_number(N) -> N; %Integers and floats +decode(N, _, _) when is_number(N) -> N; %Integers and floats decode(#tref{}=T, St, In) -> decode_table(T, St, In); -decode(#usdref{}=U, St, _) -> - decode_userdata(U, St); -decode(#funref{}=Fun, State, _) -> - F = fun(Args) -> - {Args1, State1} = encode_list(Args, State), - {Ret, State2} = luerl_emul:functioncall(Fun, Args1, State1), - decode_list(Ret, State2) - end, - F; %Just a bare fun -decode(#erl_func{code=Fun}, _, _) -> Fun; -decode(#erl_mfa{m=M,f=F,a=A}, _, _) -> {M,F,A}; -decode(_, _, _) -> error(badarg). %Shouldn't have anything else +decode(#usdref{}=U, St, In) -> + decode_userdata(U, St, In); +decode(#funref{}=Fun, St, In) -> + decode_luafunc(Fun, St, In); +decode(#erl_func{}=Fun, St, In) -> + decode_erlfunc(Fun, St, In); +decode(#erl_mfa{}=Mfa, St, In) -> + decode_erlmfa(Mfa, St, In); +decode(Lua, _, _) -> error({badarg,Lua}). %Shouldn't have anything else decode_table(#tref{i=N}=T, St, In0) -> case lists:member(N, In0) of - true -> error({recursive_table,T}); %Been here before - false -> - In1 = [N|In0], %We are in this as well - case luerl_heap:get_table(T, St) of - #table{a=Arr,d=Dict} -> - Fun = fun (K, V, Acc) -> - [{decode(K, St, In1),decode(V, St, In1)}|Acc] - end, - Ts = ttdict:fold(Fun, [], Dict), - array:sparse_foldr(Fun, Ts, Arr); - _Undefined -> error(badarg) - end + true -> error({recursive_table,T}); %Been here before + false -> + In1 = [N|In0], %We are in this as well + case luerl_heap:get_table(T, St) of + #table{a=Arr,d=Dict} -> + Fun = fun (K, V, Acc) -> + [{decode(K, St, In1),decode(V, St, In1)}|Acc] + end, + Ts = ttdict:fold(Fun, [], Dict), + array:sparse_foldr(Fun, Ts, Arr); + _Undefined -> error(badarg) + end end. -decode_userdata(U, St) -> +decode_userdata(U, St, _In) -> {#userdata{d=Data},_} = luerl_heap:get_userdata(U, St), {userdata,Data}. +decode_luafunc(Fun, _St, _In) -> + io:format("dec ~p\n", [Fun]), + fun(Args, State) -> + luerl_emul:functioncall(Fun, Args, State) + end. + +decode_erlfunc(#erl_func{code=Fun}=Ef, _St, _In) -> + io:format("dec ~p\n", [Ef]), + Fun. %Just the bare fun + +decode_erlmfa(#erl_mfa{m=Mod,f=Func,a=Arg}=Mfa, _St, _In) -> + io:format("mfa ~p\n", [Mfa]), + {Mod,Func,Arg}. + %% Externalize and Internalize ensure that the VM state passed in %% can be stored externally or can be recreated from external storage. %% Currently very simple: only random state needs special treatment. diff --git a/src/luerl_emul.erl b/src/luerl_emul.erl index d7e6cd3..221934e 100644 --- a/src/luerl_emul.erl +++ b/src/luerl_emul.erl @@ -304,15 +304,15 @@ load_chunk_i(I, Funrs, St) -> {I,Funrs,St}. call(Func, St) -> call(Func, [], St). -call(#funref{}=Funref, Args, St0) -> %Lua function +call(#funref{}=Funref, Args, St0) -> %Lua function {Ret,St1} = functioncall(Funref, Args, St0), %% Should do GC here. {Ret,St1}; -call(#erl_func{}=Func, Args, St0) -> %Erlang function +call(#erl_func{}=Func, Args, St0) -> %Erlang function {Ret,St1} = functioncall(Func, Args, St0), %% Should do GC here. {Ret,St1}; -call(#erl_mfa{}=Func, Args, St0) -> %Erlang function as MFA triplet +call(#erl_mfa{}=Func, Args, St0) -> %Erlang function as MFA triplet {Ret,St1} = functioncall(Func, Args, St0), {Ret,St1}. diff --git a/src/luerl_new.erl b/src/luerl_new.erl index 22185b1..b8e645f 100644 --- a/src/luerl_new.erl +++ b/src/luerl_new.erl @@ -378,30 +378,25 @@ encode(L, St0) when is_list(L) -> {T,St2} = luerl_heap:alloc_table(Es, St1), {T,St2}; %No more to do for now encode(F, St) when is_function(F, 2) -> - F1 = fun(Args, State) -> - Args1 = decode_list(Args, State), - {Res, State1} = F(Args1, State), - encode_list(Res, State1) - end, + F1 = fun(Args, State) -> F(Args, State) end, + io:format("enc ~p\n", [#erl_func{code=F1}]), {#erl_func{code=F1}, St}; encode(F, St) when is_function(F, 1) -> - F1 = fun(Args, State) -> - Args1 = decode_list(Args, State), - Res = F(Args1), - encode_list(Res, State) - end, + F1 = fun(Args, State) -> Res = F(Args), {Res,State} end, + io:format("enc ~p\n", [#erl_func{code=F1}]), {#erl_func{code=F1}, St}; encode({M,F,A}, St) when is_atom(M) and is_atom(F) -> + io:format("enc ~p\n", [#erl_mfa{m=M,f=F,a=A}]), {#erl_mfa{m=M,f=F,a=A}, St}; encode({userdata,Data}, St) -> luerl_heap:alloc_userdata(Data, St); -% Table refs should not be re-encoded -encode(#tref{}=T, St) -> - case luerl_heap:chk_table(T, St) of - ok -> {T, St}; - error -> error(badarg) - end; -encode(_, _) -> error(badarg). %Can't encode anything else +%% % Table refs should not be re-encoded +%% encode(#tref{}=T, St) -> +%% case luerl_heap:chk_table(T, St) of +%% ok -> {T, St}; +%% error -> error(badarg) +%% end; +encode(Term, _) -> error({badarg,Term}). %Can't encode anything else %% decode_list([LuerlTerm], State) -> [Term]. %% decode(LuerlTerm, State) -> Term. @@ -421,18 +416,15 @@ decode(B, _, _) when is_binary(B) -> B; decode(N, _, _) when is_number(N) -> N; %Integers and floats decode(#tref{}=T, St, In) -> decode_table(T, St, In); -decode(#usdref{}=U, St, _) -> - decode_userdata(U, St); -decode(#funref{}=Fun, State, _) -> - F = fun(Args) -> - {Args1, State1} = encode_list(Args, State), - {Ret, State2} = luerl_emul:functioncall(Fun, Args1, State1), - decode_list(Ret, State2) - end, - F; %Just a bare fun -decode(#erl_func{code=Fun}, _, _) -> Fun; -decode(#erl_mfa{m=M,f=F,a=A}, _, _) -> {M,F,A}; -decode(_, _, _) -> error(badarg). %Shouldn't have anything else +decode(#usdref{}=U, St, In) -> + decode_userdata(U, St, In); +decode(#funref{}=Fun, St, In) -> + decode_luafunc(Fun, St, In); +decode(#erl_func{}=Fun, St, In) -> + decode_erlfunc(Fun, St, In); +decode(#erl_mfa{}=Mfa, St, In) -> + decode_erlmfa(Mfa, St, In); +decode(Lua, _, _) -> error({badarg,Lua}). %Shouldn't have anything else decode_table(#tref{i=N}=T, St, In0) -> case lists:member(N, In0) of @@ -450,10 +442,23 @@ decode_table(#tref{i=N}=T, St, In0) -> end end. -decode_userdata(U, St) -> +decode_userdata(U, St, _In) -> {#userdata{d=Data},_} = luerl_heap:get_userdata(U, St), {userdata,Data}. +decode_luafunc(Fun, _St, _In) -> + io:format("dec ~p\n", [Fun]), + fun(Args, State) -> + luerl_emul:functioncall(Fun, Args, State) + end. + +decode_erlfunc(#erl_func{code=Fun}=Ef, _St, _In) -> + io:format("dec ~p\n", [Ef]), + Fun. %Just the bare fun + +decode_erlmfa(#erl_mfa{m=Mod,f=Func,a=Arg}=Mfa, _St, _In) -> + io:format("mfa ~p\n", [Mfa]), + {Mod,Func,Arg}. %% Externalize and Internalize ensure that the VM state passed in %% can be stored externally or can be recreated from external storage. diff --git a/test/luerl_funcall_tests.erl b/test/luerl_funcall_tests.erl index 5b37c13..0f237fd 100644 --- a/test/luerl_funcall_tests.erl +++ b/test/luerl_funcall_tests.erl @@ -23,30 +23,19 @@ external_fun_test() -> State = luerl:init(), - F = fun([A], S) -> - {[A + 2, [A + 3, A + 4]], S} - end, - State1 = luerl:set_table([<<"testFun">>], F, State), - {_, State2} = luerl:do(<<"function test(i)\n local a, b = testFun(i)\n return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4) end">>, State1), - {Res, _State3} = luerl:call_function([test], [2], State2), - [BoolVal, BoolVal2, BoolVal3] = Res, - ?assertEqual(true, BoolVal), - ?assertEqual(true, BoolVal2), - ?assertEqual(true, BoolVal3). - -external_nostate_fun_test() -> - State = luerl:init(), - F = fun([A]) -> - [A + 2, [A + 3, A + 4]] - end, + F = fun(Args, S) -> + %% Must decode the args and encode the return value. + [A] = luerl:decode_list(Args, S), + luerl:encode_list([A + 2, [A + 3, A + 4]], S) + end, State1 = luerl:set_table([<<"testFun">>], F, State), Chunk = <<"function test(i)\n" - " local a, b = testFun(i)\n" - " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" - "end">>, + " local a, b = testFun(i)\n" + " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" + "end">>, {_, State2} = luerl:do(Chunk, State1), {Res, _State3} = luerl:call_function([test], [2], State2), - [BoolVal, BoolVal2, BoolVal3] = Res, + [BoolVal, BoolVal2, BoolVal3] = Res = [true,true,true], ?assertEqual(true, BoolVal), ?assertEqual(true, BoolVal2), ?assertEqual(true, BoolVal3). @@ -61,48 +50,56 @@ return_lib_function_test() -> define_fun_in_lua_test() -> State = luerl:init(), Chunk = <<"function mkadder(incby)\n" - " return function(i)\n" - " print(\"Call into Luerl!\")\n" - " return i + incby\n" - " end\n" - "end\n">>, + " return function(i)\n" + " print(\"Call into Luerl!\")\n" + " return i + incby\n" + " end\n" + "end\n">>, {_, State1} = luerl:do(Chunk, State), - {[Fun], _State2} = luerl:call_function([mkadder], [1], State1), - {[Fun2], _State3} = luerl:call_function([mkadder], [2], State1), - ?assertEqual([5], Fun([4])), - ?assertEqual([5.0], Fun([4.0])), - ?assertEqual([6], Fun2([4])). + {[Fun2], State2} = luerl:call_function([mkadder], [1], State1), + {[Fun3], State3} = luerl:call_function([mkadder], [2], State1), + + %% Should really decode the return value, but it is only a number. + ?assertMatch({[5], _}, Fun2([4], State2)), + ?assertMatch({[5.0],_}, Fun2([4.0], State2)), + ?assertMatch({[6], _}, Fun3([4], State3)). define_fun2_in_lua_test() -> State = luerl:init(), Chunk = <<"function mklist(numentries)\n" - " return function(entryval)\n" - " local list = {}\n" - " for i = 1,numentries do\n" - " list[i] = entryval\n" - " end\n" - " return list\n" - " end\n" - "end\n">>, + " return function(entryval)\n" + " local list = {}\n" + " for i = 1,numentries do\n" + " list[i] = entryval\n" + " end\n" + " return list\n" + " end\n" + "end\n">>, {_, State1} = luerl:do(Chunk, State), - {[Fun], _State2} = luerl:call_function([mklist], [5], State1), - {[Fun2], _State3} = luerl:call_function([mklist], [10], State1), - ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], - Fun([4])), - ?assertEqual([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], - Fun([4.0])), - ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, - {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], - Fun2([4])). + {[Fun2], State2} = luerl:call_function1([<<"mklist">>], [5], State1), + + %% Must decode the return values as they are tables. + {Res21,State21} = luerl:call_function1(Fun2, [4], State2), + ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], + luerl:decode_list(Res21, State21)), + {Res22,State22} = luerl:call_function1(Fun2, [4.0], State2), + ?assertMatch([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], + luerl:decode_list(Res22, State22)), + + {[Fun3], State3} = luerl:call_function([<<"mklist">>], [10], State1), + {Res3, State31} = Fun3([4], State3), + ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, + {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], + luerl:decode_list(Res3, State31)). newindex_metamethod_test() -> State = luerl:init(), Chunk = <<"local t = {}\n" - "local m = setmetatable({}, {__newindex = function (tab, key, value)\n" - "t[key] = value\n" - "end})\n\n" - "m[123] = 456\n" - "return t[123], m[123]">>, + "local m = setmetatable({}, {__newindex = function (tab, key, value)\n" + "t[key] = value\n" + "end})\n\n" + "m[123] = 456\n" + "return t[123], m[123]">>, {[TVal, MVal], _State1} = luerl:do(Chunk, State), ?assertEqual(456, TVal), ?assertEqual(nil, MVal). diff --git a/test/luerl_new_tests.erl b/test/luerl_new_tests.erl index 17c647a..802a8f0 100644 --- a/test/luerl_new_tests.erl +++ b/test/luerl_new_tests.erl @@ -1,4 +1,4 @@ -- module(luerl_new_tests). +-module(luerl_new_tests). -include_lib("eunit/include/eunit.hrl"). @@ -10,17 +10,21 @@ encode_test() -> ?assertMatch({<<"binary">>, _State}, luerl_new:encode(<<"binary">>, State)), ?assertMatch({<<"atom">>, _State}, luerl_new:encode(atom, State)), ?assertMatch({5, _State}, luerl_new:encode(5, State)), - ?assertMatch({{tref, _}, _State}, luerl_new:encode(#{a => 1, b => 2}, State)). + ?assertMatch({{tref, _}, _State}, luerl_new:encode(#{a => 1, b => 2}, State)), + ?assertMatch({{tref, _}, _State}, luerl_new:encode([{a,1},{b,2}], State)). -encode_map_test() -> - ?assertMatch({{tref, _}, _State}, luerl_new:encode(#{a => 1}, luerl_new:init())). +encode_error_test() -> + State = luerl:init(), + ?assertException(error, {badarg, _}, luerl:encode({a,1}, State)). encode_table_test() -> {Table, State} = luerl_new:encode(#{a => 1}, luerl_new:init()), {ok, [], State1} = luerl_new:set_table_keys([<<"foo">>], Table, State), - ?assertMatch({ok, Table, _State2}, luerl_new:get_table_keys([<<"foo">>], State1)), - ?assertMatch({tref, _}, Table), - ?assertMatch({Table, _State}, luerl_new:encode(Table, State1)). + ?assertMatch({ok, Table, _State2}, + luerl_new:get_table_keys([<<"foo">>], State1)), + ?assertMatch({tref, _}, Table). -invalid_table_test() -> - ?assertException(error, badarg, luerl_new:encode({tref, 42}, luerl_new:init())). +invalid_value_test() -> + State = luerl_new:init(), + ?assertException(error, {badarg, {invalid, value}}, + luerl_new:encode({invalid, value}, State)). diff --git a/test/luerl_tests.erl b/test/luerl_tests.erl index 624d95c..f26d075 100644 --- a/test/luerl_tests.erl +++ b/test/luerl_tests.erl @@ -1,4 +1,4 @@ -- module(luerl_tests). +-module(luerl_tests). -include_lib("eunit/include/eunit.hrl"). @@ -10,15 +10,21 @@ encode_test() -> ?assertMatch({<<"binary">>, _State}, luerl:encode(<<"binary">>, State)), ?assertMatch({<<"atom">>, _State}, luerl:encode(atom, State)), ?assertMatch({5, _State}, luerl:encode(5, State)), - ?assertMatch({{tref, _}, _State}, luerl:encode(#{a => 1, b => 2}, State)). + ?assertMatch({{tref, _}, _State}, luerl:encode(#{a => 1, b => 2}, State)), + ?assertMatch({{tref, _}, _State}, luerl:encode([{a,1},{b,2}], State)). -encode_map_test() -> - ?assertMatch({{tref, _}, _State}, luerl:encode(#{a => 1}, luerl:init())). +encode_error_test() -> + State = luerl:init(), + ?assertException(error, {badarg, _}, luerl:encode({a,1}, State)). encode_table_test() -> - {Table, State} = luerl:encode(#{a => 1}, luerl:init()), - ?assertMatch({tref, _}, Table), - ?assertMatch({Table, _State}, luerl:encode(Table, State)). + {Table, State} = luerl:encode(#{a => 1}, luerl_new:init()), + State1 = luerl:set_table1([<<"foo">>], Table, State), + ?assertMatch({Table, _State2}, + luerl:get_table1([<<"foo">>], State1)), + ?assertMatch({tref, _}, Table). -invalid_table_test() -> - ?assertException(error, badarg, luerl:encode({tref, 42}, luerl:init())). +invalid_value_test() -> + State = luerl:init(), + ?assertException(error, {badarg, {invalid, value}}, + luerl:encode({invalid, value}, State)).