File 5271-Add-support-for-option-maps-in-the-JER-backend.patch of Package erlang

From 0e8bc92d290b9cb451104e6302ec5f8c0c7ae084 Mon Sep 17 00:00:00 2001
From: Kenneth Lundin <kenneth@erlang.org>
Date: Wed, 20 Oct 2021 14:09:18 +0200
Subject: [PATCH] Add support for option 'maps' in the JER backend This solves
 GH-5129

---
 lib/asn1/src/asn1ct_gen_jer.erl |  8 ++++++--
 lib/asn1/src/asn1rtt_jer.erl    | 36 +++++++++++++++++++++++++++++++++
 lib/asn1/test/asn1_SUITE.erl    | 26 +++++++++++++++++++-----
 3 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/lib/asn1/src/asn1ct_gen_jer.erl b/lib/asn1/src/asn1ct_gen_jer.erl
index 041ea6b247..5d775cd27f 100644
--- a/lib/asn1/src/asn1ct_gen_jer.erl
+++ b/lib/asn1/src/asn1ct_gen_jer.erl
@@ -104,9 +104,13 @@ gen_encode_sequence(Gen, Typename, #type{}=D) ->
 	end,
     CompTypes = gen_enc_comptypes(Gen, Typename, CompList1, 1, EncObj, []),
     Prefix = asn1ct_gen:get_record_name_prefix(Gen),
-    {sequence,
+    {SequenceTag,NewCompTypes} = case Gen#gen.pack of
+        map -> {sequence_map,[{BinName,binary_to_atom(BinName),Type,OptOrDefault}||{BinName,Type,OptOrDefault} <- CompTypes]};
+        _ -> {sequence,CompTypes} 
+    end,
+    {SequenceTag,
      list_to_atom(lists:concat([Prefix,asn1ct_gen:list2name(Typename)])),
-     length(CompList1),CompTypes}.
+     length(CompList1),NewCompTypes}.
 
 gen_decode_sequence(_,_,_) -> ok.
 
diff --git a/lib/asn1/src/asn1rtt_jer.erl b/lib/asn1/src/asn1rtt_jer.erl
index 1efb9f1079..2c44ed2bd7 100644
--- a/lib/asn1/src/asn1rtt_jer.erl
+++ b/lib/asn1/src/asn1rtt_jer.erl
@@ -50,6 +50,8 @@ encode_jer({sequence_tab,Simple,Sname,Arity,CompInfos},Value)
 %%    Arity::integer() % number of components
 %%    CompInfos::[CompInfo()] % list of components with name, type etc
 %%    Value::record matching name and arity
+encode_jer({sequence_map,_Sname,_Arity,CompInfos},Value) when is_map(Value) ->
+    encode_jer_component_map(CompInfos,Value,[]);
 encode_jer({sequence,Sname,Arity,CompInfos},Value) 
   when tuple_size(Value) == Arity+1 ->
     [Sname|Clist] = tuple_to_list(Value),
@@ -167,6 +169,22 @@ encode_jer_component_tab([{Name, Type, _OptOrDefault} | CompInfos], [Value | Res
     encode_jer_component_tab(CompInfos, Rest, Simple, MapAcc#{Name => Enc});
 encode_jer_component_tab([], _, _Simple, MapAcc) ->
     MapAcc.
+encode_jer_component_map([{Name, AName, Type, _OptOrDefault} | CompInfos], MapVal, Acc) when is_map_key(AName,MapVal)->
+    Value = maps:get(AName, MapVal),
+    Enc = encode_jer(Type, Value),
+    encode_jer_component_map(CompInfos, MapVal, [{Name,Enc}|Acc]);
+encode_jer_component_map([{_Name, _AName, _Type, 'OPTIONAL'} | CompInfos], MapVal, Acc) ->
+    encode_jer_component_map(CompInfos, MapVal, Acc);
+encode_jer_component_map([{_Name, _AName, _Type, {'DEFAULT',_}} | CompInfos], MapVal, Acc) ->
+    encode_jer_component_map(CompInfos, MapVal, Acc);
+encode_jer_component_map([], MapVal, []) when map_size(MapVal) == 0->
+    #{}; % ensure that it is encoded as an empty object in JSON
+encode_jer_component_map([], MapVal, Acc) when map_size(MapVal) == length(Acc) ->
+    lists:reverse(Acc);
+encode_jer_component_map(_, MapVal, Acc) ->
+    ErroneousKeys = maps:keys(MapVal) -- [K || {K,_V} <- Acc],
+    exit({error,{asn1,{{encode,'SEQUENCE'},{erroneous_keys,ErroneousKeys}}}}).
+
 
 encode_jer_component([{_Name, _Type, 'OPTIONAL'} | CompInfos], [asn1_NOVALUE | Rest], Acc) ->
     encode_jer_component(CompInfos, Rest, Acc);
@@ -210,6 +228,9 @@ decode_jer({sequence,Sname,_Arity,CompInfos},Value)
   when is_map(Value) ->    
     DecodedComps = decode_jer_component(CompInfos,Value,[]),
     list_to_tuple([Sname|DecodedComps]);
+decode_jer({sequence_map,_Sname,_Arity,CompInfos},Value) 
+  when is_map(Value) ->    
+    decode_jer_component_map(CompInfos,Value,[]);
 
 %% Unfortunately we have to represent strings as lists to be compatible 
 %% with the other backends. Should add an option to the compiler in the future
@@ -298,6 +319,21 @@ decode_jer_component([{Name, _Type, _OptOrDefault} | _CompInfos], VMap, _Acc) ->
 decode_jer_component([], _, Acc) ->
     lists:reverse(Acc).
 
+decode_jer_component_map([{Name, AtomName, Type, _OptOrDefault} | CompInfos], VMap, Acc)
+    when is_map_key(Name, VMap) ->
+    Value = maps:get(Name, VMap),
+    Dec = decode_jer(Type, Value),
+    decode_jer_component_map(CompInfos, VMap, [{AtomName,Dec} | Acc]);
+decode_jer_component_map([{_Name, _AtomName, _Type, 'OPTIONAL'} | CompInfos], VMap, Acc) ->
+    decode_jer_component_map(CompInfos, VMap, Acc);
+decode_jer_component_map([{_Name, AtomName, _Type, {'DEFAULT',Dvalue}} | CompInfos], VMap, Acc) ->
+    decode_jer_component_map(CompInfos, VMap, [{AtomName, Dvalue} | Acc]);
+decode_jer_component_map([{Name, _AtomName, _Type, _OptOrDefault} | _CompInfos], VMap, _Acc) ->
+    exit({error,{asn1,{{decode,{mandatory_component_missing,Name}},VMap}}});
+decode_jer_component_map([], _, Acc) ->
+    %% not reusing the map from JSON decoder since it can contain non expected extra K,V pairs
+    maps:from_list(Acc).
+
 %% This is the default representation of octet string i.e binary
 json2octetstring2binary(Value) ->
     list_to_binary(json2octetstring(Value,[])).
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index a1cc32da86..59a4246577 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -198,10 +198,16 @@ end_per_testcase(_Func, Config) ->
 %% Test runners
 %%------------------------------------------------------------------------------
 
+have_jsonlib() ->
+    case code:which(jsx) of
+        non_existing -> false;
+    _ -> true
+    end.
+
 test(Config, TestF) ->
-    TestJer = case code:which(jsx) of
-                  non_existing -> [];
-                  _ -> [jer, {ber, [ber,jer]}]
+    TestJer = case have_jsonlib() of
+                  true -> [jer, {ber, [ber,jer]}];
+                  false -> []
               end,
     test(Config, TestF, [per,
                          uper,
@@ -424,11 +430,21 @@ testExtensionDefault(Config, Rule, Opts) ->
     end.
 
 testMaps(Config) ->
-    test(Config, fun testMaps/3,
+    Jer = case have_jsonlib() of
+        true -> [{jer,[maps,no_ok_wrapper]}];
+        false -> []
+    end,
+    RulesAndOptions = 
          [{ber,[maps,no_ok_wrapper]},
           {ber,[maps,der,no_ok_wrapper]},
           {per,[maps,no_ok_wrapper]},
-          {uper,[maps,no_ok_wrapper]}]).
+          {uper,[maps,no_ok_wrapper]}] ++ Jer,
+    test(Config, fun testMaps/3, RulesAndOptions),
+    case Jer of
+        [] -> {comment,"skipped JER"};
+        _ -> ok
+    end.
+
 testMaps(Config, Rule, Opts) ->
     asn1_test_lib:compile_all(['Maps'], Config, [Rule|Opts]),
     testMaps:main(Rule).
-- 
2.34.1

openSUSE Build Service is sponsored by