Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API for storing and retrieving "private" data #195

Merged
merged 8 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/src/luerl.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,12 @@ Decode a term in the Luerl form into its Erlang representation.

#### luerl:decode_list([LuerlTerm], State) -> [Term]
Decode a list of Luerl terms into a list of Erlang representations.

#### luerl:put_private(Key, Term, State) -> State.
Puts a private value under key that is not exposed to the runtime.

#### luerl:get_private(Key, State) -> Term.
Get a private value for the given key.

#### luerl:delete_private(Key, State) -> Term.
Deletes the private value for the given key.
72 changes: 39 additions & 33 deletions include/luerl.hrl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%% Copyright (c) 2013-2019 Robert Virding
%% Copyright (c) 2013-2024 Robert Virding
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand All @@ -25,16 +25,17 @@
envs, %Environment table
usds, %Userdata table
fncs, %Function table
g, %Global table
%%
stk=[], %Current stack
cs=[], %Current call stack
%%
meta=[], %Data type metatables
rand, %Random state
g, %Global table
%%
stk=[], %Current stack
cs=[], %Current call stack
%%
meta=[], %Data type metatables
rand, %Random state
tag, %Unique tag
trace_func=none, %Trace function
trace_data %Trace data
trace_data, %Trace data
private=#{}
}).

%% Table structure.
Expand All @@ -46,10 +47,10 @@
%% Metatables for atomic datatypes.

-record(meta, {nil=nil,
boolean=nil,
number=nil,
string=nil
}).
boolean=nil,
number=nil,
string=nil
}).

%% Frames for the call stack.
%% Call return frame
Expand Down Expand Up @@ -111,10 +112,10 @@
body}). %Code block
-define(IS_LUAFUNC(F), is_record(F, lua_func)).

-record(erl_func,{code}). %Erlang code (fun)
-record(erl_func,{code}). %Erlang code (fun)
-define(IS_ERLFUNC(F), is_record(F, erl_func)).

-record(erl_mfa,{m,f,a}). %Erlang code (MFA)
-record(erl_mfa,{m,f,a}). %Erlang code (MFA)
-define(IS_ERLMFA(F), is_record(F, erl_mfa)).

%% Test if it a function, of either sort.
Expand Down Expand Up @@ -145,28 +146,31 @@
-define(SET_TABLE(N, T, Ts), maps:put(N, T, Ts)).
-define(UPD_TABLE(N, Upd, Ts), maps:update_with(N, Upd, Ts)).
-define(DEL_TABLE(N, Ts), maps:remove(N, Ts)).
-define(CHK_TABLE(N, Ts), maps:is_key(N, Ts)).
-define(FILTER_TABLES(Pred, Ts), maps:filter(Pred, Ts)).
-define(FOLD_TABLES(Fun, Acc, Ts), maps:fold(Fun, Acc, Ts)).
-endif.

-ifdef(TS_USE_ARRAY).
%% Use arrays to handle tables.
%% Use arrays to handle tables. We leave the default value as undefined.
-define(MAKE_TABLE(), array:new()).
-define(GET_TABLE(N, Ar), array:get(N, Ar)).
-define(SET_TABLE(N, T, Ar), array:set(N, T, Ar)).
-define(UPD_TABLE(N, Upd, Ar),
array:set(N, (Upd)(array:get(N, Ar)), Ar)).
array:set(N, (Upd)(array:get(N, Ar)), Ar)).
-define(DEL_TABLE(N, Ar), array:reset(N, Ar)).
-define(CHK_TABLE(N, Ar),
((N >= 0) andalso (array:get(N, Ar) =/= undefined))).
-define(FILTER_TABLES(Pred, Ar),
((fun (___Def) ->
___Fil = fun (___K, ___V) ->
case Pred(___K, ___V) of
true -> ___V;
false -> ___Def
end
end,
array:sparse_map(___Fil, Ar)
end)(array:default(Ar)))).
((fun (___Def) ->
___Fil = fun (___K, ___V) ->
case Pred(___K, ___V) of
true -> ___V;
false -> ___Def
end
end,
array:sparse_map(___Fil, Ar)
end)(array:default(Ar)))).
-define(FOLD_TABLES(Fun, Acc, Ar), array:sparse_foldl(Fun, Acc, Ar)).
-endif.

Expand All @@ -177,6 +181,7 @@
-define(SET_TABLE(N, T, Ts), orddict:store(N, T, Ts)).
-define(UPD_TABLE(N, Upd, Ts), orddict:update(N, Upd, Ts)).
-define(DEL_TABLE(N, Ts), orddict:erase(N, Ts)).
-define(CHK_TABLE(N, Ts), orddict:is_key(N, Ts)).
-define(FILTER_TABLES(Pred, Ts), orddict:filter(Pred, Ts)).
-define(FOLD_TABLES(Fun, Acc, Ts), orddict:fold(Fun, Acc, Ts)).
-endif.
Expand All @@ -188,8 +193,9 @@
-define(SET_TABLE(N, T, Pd), put(N, T)).
-define(UPD_TABLE(N, Upd, Pd), put(N, (Upd)(get(N)))).
-define(DEL_TABLE(N, Pd), erase(N)).
-define(FILTER_TABLES(Pred, Pd), Pd). %This needs work
-define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work
-define(CHK_TABLE(N, Pd), (get(N) =/= undefined)).
-define(FILTER_TABLES(Pred, Pd), Pd). %This needs work
-define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work
-endif.

-ifdef(TS_USE_ETS).
Expand All @@ -198,13 +204,13 @@
-define(GET_TABLE(N, E), ets:lookup_element(E, N, 2)).
-define(SET_TABLE(N, T, E), begin ets:insert(E, {N,T}), E end).
-define(UPD_TABLE(N, Upd, E),
begin ets:update_element(E, N, {2,(Upd)(ets:lookup_element(E, N, 2))}),
E end).
begin ets:update_element(E, N, {2,(Upd)(ets:lookup_element(E, N, 2))}),
E end).
-define(DEL_TABLE(N, E), begin ets:delete(E, N), E end).
-define(FILTER_TABLES(Pred, E), E). %This needs work
-define(FILTER_TABLES(Pred, E), E). %This needs work
-define(FOLD_TABLES(Fun, Acc, E),
ets:foldl(fun ({___K, ___T}, ___Acc) -> Fun(___K, ___T, ___Acc) end,
Acc, E)).
ets:foldl(fun ({___K, ___T}, ___Acc) -> Fun(___K, ___T, ___Acc) end,
Acc, E)).
-endif.

%% Define CATCH to handle deprecated get_stacktrace/0
Expand Down
19 changes: 19 additions & 0 deletions src/Elixir.Luerl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

-module('Elixir.Luerl').

-include("luerl.hrl").

%% Basic user API to luerl.
-export([init/0,gc/1,
load/2,load/3,loadfile/2,loadfile/3,
Expand All @@ -49,6 +51,9 @@
%%Helping with storing VM state
-export([externalize/1,internalize/1]).

%% Storing and retrieving private data
-export([put_private/3,get_private/2,delete_private/2]).

init() ->
luerl:init().

Expand Down Expand Up @@ -180,3 +185,17 @@ externalize(St) ->

internalize(St) ->
luerl:internalize(St).

put_private(St, K, V) ->
luerl:put_private(K, V, St).

get_private(St, Key) ->
maps:get(Key, St#luerl.private, nil).

delete_private(St, K) ->
try
luerl:delete_private(K, St)
catch
error:{badkey, _} ->
St
end.
20 changes: 20 additions & 0 deletions src/luerl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
%% Helping with storing VM state
-export([externalize/1,internalize/1]).

%% Storing and retrieving private data
-export([put_private/3,get_private/2,delete_private/2]).

%% init() -> State.

init() ->
Expand Down Expand Up @@ -506,3 +509,20 @@ externalize(S) ->

internalize(S) ->
luerl_lib_math:internalize(S).

%% put_private(Key, Value, State) ->
%% State.
%% get_private(Key, State) ->
%% Value.
%% delete_private(Key, State) ->
%% Value.
put_private(Key, Value, S) ->
Private = maps:put(Key, Value, S#luerl.private),
S#luerl{private=Private}.

get_private(Key, S) ->
maps:get(Key, S#luerl.private).

delete_private(Key, S) ->
Private = maps:remove(Key, S#luerl.private),
S#luerl{private=Private}.
3 changes: 2 additions & 1 deletion src/luerl.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
rand, %Random state
tag, %Unique tag
trace_func=none, %Trace function
trace_data %Trace data
trace_data, %Trace data
private=#{}
}).

%% Table structure.
Expand Down
25 changes: 25 additions & 0 deletions test/Elixir.Luerl_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
%% Copyright (C) 2024 Robert Virding
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.

-module('Elixir.Luerl_tests').

-include_lib("eunit/include/eunit.hrl").

private_test() ->
State1 = 'Elixir.Luerl':init(),
State2 = 'Elixir.Luerl':put_private(State1, secret, <<"mysecret">>),
?assertMatch(<<"mysecret">>, 'Elixir.Luerl':get_private(State2, secret)),
?assertMatch(nil, 'Elixir.Luerl':get_private(State2, missing)),
State3 = 'Elixir.Luerl':delete_private(State2, secret),
?assertMatch(nil, 'Elixir.Luerl':get_private(State3, secret)).
8 changes: 8 additions & 0 deletions test/luerl_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ invalid_value_test() ->
State = luerl:init(),
?assertException(error, {badarg, {invalid, value}},
luerl:encode({invalid, value}, State)).

private_test() ->
State1 = luerl:init(),
State2 = luerl:put_private(secret, <<"mysecret">>, State1),
?assertMatch(<<"mysecret">>, luerl:get_private(secret, State2)),
?assertException(error, {badkey, missing}, luerl:get_private(missing, State2)),
State3 = luerl:delete_private(secret, State2),
?assertException(error, {badkey, secret}, luerl:get_private(secret, State3)).
Loading