Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:23
erlang
7501-ASN.1-JER-Refactor-type-information-handli...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 7501-ASN.1-JER-Refactor-type-information-handling.patch of Package erlang
From 101bd6eb8b66a5424955da3cf490bf199fa3270b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Tue, 5 Sep 2023 07:00:23 +0200 Subject: [PATCH] ASN.1 JER: Refactor type-information handling This commit refactors how type information for ASN.1 is handled for the JER back-end, and is a compatible change for applications that use the documented API. However, for a group of ASN.1 modules that depend on each other (such a `S1AP-PDU-Descriptions`, `S1AP-Contents`, `S1AP-IEs`, and so on), all members of the group must be recompiled if any one of the group members is recompiled. The internal changes are intended to help tools that need to process type information for ASN.1 specifications, but note that the format of the type information may be updated in a future release. Tool makers must be prepared to update their tools if the format changes. To explain the changes, I will use this ASN.1 specification as an example: Containing DEFINITIONS AUTOMATIC TAGS ::= BEGIN Config ::= SEQUENCE { a INTEGER, b BOOLEAN } Str ::= OCTET STRING (CONTAINING Config) END The first change is that instead having one `typeinfo_Type/0` function for each ASN.1 type in a module, there is now a single `typeinfo/1` function. For the example, the call `'Containing':typeinfo('Str')` retrieves the type information for the `Str` type. The second change is that for types having a `CONTAINING` constraint, the constraint will be preserved in the type information. Thus, the type information returned for the `Str` type will be: {container,octet_string,{typeinfo,{'Containing','Config'}}}. --- lib/asn1/src/asn1ct_gen.erl | 67 +++++++++---------- lib/asn1/src/asn1ct_gen_jer.erl | 57 +++++++++------- lib/asn1/src/asn1rtt_jer.erl | 32 +++++---- lib/asn1/test/Makefile | 1 + lib/asn1/test/asn1_SUITE.erl | 18 ++++- lib/asn1/test/asn1_SUITE_data/Containing.asn1 | 20 ++++++ lib/asn1/test/testContaining.erl | 52 ++++++++++++++ 7 files changed, 171 insertions(+), 76 deletions(-) create mode 100644 lib/asn1/test/asn1_SUITE_data/Containing.asn1 create mode 100644 lib/asn1/test/testContaining.erl diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index cc1a33149c..2c8d8528fe 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -101,9 +101,18 @@ pgen_typeorval(Erules, N2nConvEnums, Code) -> objects=Objects,objsets=ObjectSets} = Code, Rtmod = ct_gen_module(Erules), pgen_types(Rtmod,Erules,N2nConvEnums,Module,Types), - pgen_values(Values, Module), - pgen_objects(Rtmod,Erules,Module,Objects), - pgen_objectsets(Rtmod,Erules,Module,ObjectSets), + case Erules of + #gen{erule=jer} -> + pgen_objects(Rtmod, Erules, Module, Objects), + pgen_objectsets(Rtmod, Erules, Module, ObjectSets), + emit(["typeinfo(Type) ->",nl, + " exit({error,{asn1,{undefined_type,Type}}}).",nl,nl]), + pgen_values(Values, Module); + #gen{} -> + pgen_values(Values, Module), + pgen_objects(Rtmod, Erules, Module, Objects), + pgen_objectsets(Rtmod, Erules, Module, ObjectSets) + end, pgen_partial_decode(Rtmod,Erules,Module), %% If the encoding rule is ber, per or uper and jer is also given as option %% then we generate "extra" support for jer in the same file @@ -111,7 +120,10 @@ pgen_typeorval(Erules, N2nConvEnums, Code) -> true -> NewErules = Erules#gen{erule=jer,jer=false}, JER_Rtmod = ct_gen_module(NewErules), - pgen_types(JER_Rtmod,Erules#gen{erule=jer,jer=false},[],Module,Types); + pgen_types(JER_Rtmod, Erules#gen{erule=jer,jer=false}, + [], Module, Types), + emit(["typeinfo(Type) ->",nl, + " exit({error,{asn1,{undefined_type,Type}}}).",nl,nl]); false -> ok end. @@ -129,7 +141,7 @@ pgen_values([], _) -> ok. pgen_types(_, _, _, _, []) -> - true; + ok; pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) -> asn1ct_name:clear(), Typedef = asn1_db:dbget(Module,H), @@ -659,6 +671,12 @@ pgen_exports(#gen{options=Options}=Gen, Code) -> emit(["-export([encoding_rule/0,maps/0,bit_string_format/0,",nl, " legacy_erlang_types/0]).",nl]), emit(["-export([",{asis,?SUPPRESSION_FUNC},"/1]).",nl]), + case Gen of + #gen{erule=Erule,jer=Jer} when Erule =:= jer; Jer -> + emit(["-export([typeinfo/1]).",nl]); + #gen{} -> + ok + end, case Gen of #gen{erule=ber} -> gen_exports(Types, "enc_", 2), @@ -666,26 +684,12 @@ pgen_exports(#gen{options=Options}=Gen, Code) -> gen_exports(Objects, "enc_", 3), gen_exports(Objects, "dec_", 3), gen_exports(ObjectSets, "getenc_", 1), - gen_exports(ObjectSets, "getdec_", 1), - case Gen#gen.jer of - true -> - gen_exports(Types, "typeinfo_", 0); - _ -> - true - end; + gen_exports(ObjectSets, "getdec_", 1); #gen{erule=per} -> gen_exports(Types, "enc_", 1), - gen_exports(Types, "dec_", 1), - case Gen#gen.jer of - true -> - gen_exports(Types, "typeinfo_", 0); - _ -> - true - end; + gen_exports(Types, "dec_", 1); #gen{erule=jer} -> - gen_exports(Types, "typeinfo_", 0), - gen_exports(ObjectSets, "typeinfo_", 0) -%% gen_exports(Types, "dec_", 1) + ok end, A2nNames = [X || {n2n,X} <- Options], @@ -758,10 +762,7 @@ pgen_dispatcher(Gen, Types) -> #gen{erule=ber} -> "iolist_to_binary(element(1, encode_disp(Type, Data)))"; #gen{erule=jer} -> - ["?JSON_ENCODE(", - {call,jer,encode_jer,[CurrMod, - "list_to_existing_atom(lists:concat([typeinfo_,Type]))", - "Data"]},")"]; + ["?JSON_ENCODE(",{call,jer,encode_jer,[CurrMod,"Type","Data"]},")"]; #gen{erule=per,aligned=false} when NoFinalPadding -> asn1ct_func:need({uper,complete_NFP,1}), "complete_NFP(encode_disp(Type, Data))"; @@ -785,11 +786,7 @@ pgen_dispatcher(Gen, Types) -> case Gen#gen.jer of true -> emit(["jer_encode(Type, Data) ->",nl]), - JerCall = ["?JSON_ENCODE(", - {call,jer,encode_jer, - [CurrMod, - "list_to_existing_atom(lists:concat([typeinfo_,Type]))", - "Data"]},")"], + JerCall = ["?JSON_ENCODE(",{call,jer,encode_jer,[CurrMod,"Type","Data"]},")"], case NoOkWrapper of true -> emit([" ",JerCall,"."]); @@ -843,9 +840,7 @@ pgen_dispatcher(Gen, Types) -> emit([" Result = ",DecodeDisp,",",nl]), result_line(NoOkWrapper, ["Result"]); {#gen{erule=jer},false} -> - emit([" Result = ",{call,jer,decode_jer,[ CurrMod, - "list_to_existing_atom(lists:concat([typeinfo_,Type]))", - DecWrap]},",",nl]), + emit([" Result = ",{call,jer,decode_jer,[CurrMod,"Type",DecWrap]},",",nl]), result_line(NoOkWrapper, ["Result"]); @@ -875,9 +870,7 @@ pgen_dispatcher(Gen, Types) -> emit([" Result = ", {call,jer, decode_jer, - [CurrMod, - "list_to_existing_atom(lists:concat([typeinfo_,Type]))", - JerDecWrap]},",",nl]), + [CurrMod,"Type",JerDecWrap]},",",nl]), result_line(false, ["Result"]), case NoOkWrapper of false -> diff --git a/lib/asn1/src/asn1ct_gen_jer.erl b/lib/asn1/src/asn1ct_gen_jer.erl index cfd16ea7ca..382707465f 100644 --- a/lib/asn1/src/asn1ct_gen_jer.erl +++ b/lib/asn1/src/asn1ct_gen_jer.erl @@ -410,11 +410,7 @@ gen_encode_user(Erules, #typedef{}=D, _Wrapper) -> Typename = [D#typedef.name], Type = D#typedef.typespec, InnerType = asn1ct_gen:get_inner(Type#type.def), - emit([nl,nl,"%%================================"]), - emit([nl,"%% ",Typename]), - emit([nl,"%%================================",nl]), - FuncName = {asis,typeinfo_func(asn1ct_gen:list2name(Typename))}, - emit([FuncName,"() ->",nl]), + emit([typeinfo,"('",asn1ct_gen:list2name(Typename),"') ->",nl]), CurrentMod = get(currmod), TypeInfo = case asn1ct_gen:type(InnerType) of @@ -431,7 +427,7 @@ gen_encode_user(Erules, #typedef{}=D, _Wrapper) -> Type#type{def='ASN1_OPEN_TYPE'}, "Val") end, - emit([{asis,TypeInfo},".",nl,nl]). + emit([" ",{asis,TypeInfo},";",nl]). gen_typeinfo(Erules, Typename, Type) -> InnerType = asn1ct_gen:get_inner(Type#type.def), @@ -451,9 +447,10 @@ gen_typeinfo(Erules, Typename, Type) -> "Val") end. -gen_encode_prim(_Erules, #type{}=D, _Value) -> - BitStringConstraint = get_size_constraint(D#type.constraint), - IntConstr = int_constr(D#type.constraint), +gen_encode_prim(Erules, #type{constraint=C}=D, _Value) -> + BitStringConstraint = get_size_constraint(C), + IntConstr = int_constr(C), + Containing = containing_constraint(Erules, C), %% MaxBitStrSize = case BitStringConstraint of %% [] -> none; @@ -485,11 +482,13 @@ gen_encode_prim(_Erules, #type{}=D, _Value) -> {'ENUMERATED',NNL} -> {'ENUMERATED',maps:from_list(NNL)}; Other -> Other end, - case IntConstr of - [] -> % No constraint + case {IntConstr,Containing} of + {[],[]} -> Type; - _ -> - {Type,IntConstr} + {_,[]} -> + {Type,IntConstr}; + {[],_} -> + {container,Type,Containing} end. maybe_legacy_octet_string() -> @@ -541,8 +540,6 @@ gen_decode(_,_,_) -> ok. gen_dec_prim(_Att, _BytesVar) -> ok. -%% Simplify an integer constraint so that we can efficiently test it. --spec int_constr(term()) -> [] | {integer(),integer()|'MAX'}. int_constr(C) -> case asn1ct_imm:effective_constraint(integer, C) of [{_,[]}] -> @@ -559,25 +556,37 @@ int_constr(C) -> [] end. +containing_constraint(Erules, [{contentsconstraint,#type{def=Def}=OuterType,[]}|_]) -> + %% This is a CONTAINING constraint without an ENCODED BY clause. + InnerType = asn1ct_gen:get_inner(Def), + case asn1ct_gen:type(InnerType) of + #'Externaltypereference'{module=Mod,type=TypeName} -> + {typeinfo,{Mod,typeinfo_func(TypeName)}}; + {primitive,bif} -> + gen_encode_prim(Erules, OuterType, "Val"); + _ -> + [] + end; +containing_constraint(Erules, [_|Cs]) -> + containing_constraint(Erules, Cs); +containing_constraint(_Erules, []) -> + []. + gen_obj_code(_Erules,_Module,_Obj) -> ok. gen_objectset_code(Erules,ObjSet) -> ObjSetName = ObjSet#typedef.name, Def = ObjSet#typedef.typespec, Set = Def#'ObjectSet'.set, - emit([nl,nl,nl, - "%%================================",nl, - "%% ",ObjSetName,nl, - "%%================================",nl]), - FuncName = {asis,typeinfo_func(asn1ct_gen:list2name([ObjSetName]))}, - SelectValMap = + TypeName = {asis,typeinfo_func(asn1ct_gen:list2name([ObjSetName]))}, + SelectValMap = maps:from_list([{SelectVal, maps:from_list( gen_enc_classtypes(Erules,ObjSetName, [TNameType || TNameType = {_TypeName,#typedef{}} <-TypeList], []))} || {_,SelectVal,TypeList} <- Set]), - emit([FuncName,"() ->",nl]), - emit([{asis,SelectValMap},".",nl]). + emit([typeinfo,"(",TypeName,") ->",nl]), + emit([" ",{asis,SelectValMap},";",nl]). get_size_constraint(C) -> @@ -602,7 +611,7 @@ extaddgroup2sequence(ExtList) when is_list(ExtList) -> end, ExtList). typeinfo_func(Tname) -> - list_to_atom(lists:concat(["typeinfo_",Tname])). + Tname. enc_func(Tname) -> list_to_atom(lists:concat(["enc_",Tname])). diff --git a/lib/asn1/src/asn1rtt_jer.erl b/lib/asn1/src/asn1rtt_jer.erl index b7e9390940..3727a9d5ee 100644 --- a/lib/asn1/src/asn1rtt_jer.erl +++ b/lib/asn1/src/asn1rtt_jer.erl @@ -31,9 +31,9 @@ %% Common code for all JER encoding/decoding %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -encode_jer(Module,InfoFunc,Val) -> - Info = Module:InfoFunc(), - encode_jer(Info,Val). +encode_jer(Module, Type, Val) -> + Info = Module:typeinfo(Type), + encode_jer(Info, Val). %% {sequence, %% Name::atom() % The record name used for the sequence @@ -106,10 +106,11 @@ encode_jer({'ENUMERATED_EXT',_EnumMap},Val) when is_atom(Val) -> encode_jer({Type = {'ENUMERATED_EXT',_EnumList},_Constr}, Val) -> encode_jer(Type,Val); -encode_jer({typeinfo,{Module,Func}},Val) -> - TypeInfo = Module:Func(), - encode_jer(TypeInfo,Val); - + +encode_jer({typeinfo,{Module,Type}},Val) -> + TypeInfo = Module:typeinfo(Type), + encode_jer(TypeInfo, Val); + encode_jer({sof,Type},Vals) when is_list(Vals) -> [encode_jer(Type,Val)||Val <- Vals]; encode_jer({choice,Choices},{Alt,Value}) -> @@ -155,7 +156,8 @@ encode_jer({'ObjClassFieldType',_,_},Val) when is_binary(Val)-> Val; encode_jer('ASN1_OPEN_TYPE',Val) when is_binary(Val) -> Val; - +encode_jer({container,Type,_Containing}, Val) -> + encode_jer(Type, Val); encode_jer(Type,Val) -> exit({error,{asn1,{{encode,Type},Val}}}). @@ -198,9 +200,9 @@ encode_jer_component([], _, []) -> encode_jer_component([], _, Acc) -> lists:reverse(Acc). -decode_jer(Module,InfoFunc,Val) -> - Info = Module:InfoFunc(), - decode_jer(Info,Val). +decode_jer(Module, Type, Val) -> + TypeInfo = Module:typeinfo(Type), + decode_jer(TypeInfo, Val). %% FIXME probably generate EnumList as a map with binaries as keys %% and check if the Value is in the map. Also take the extensionmarker into %% account and in that case allow any value but return as binary since it @@ -221,9 +223,9 @@ decode_jer({'ENUMERATED_EXT',EnumList}, Val) -> decode_jer({Type = {'ENUMERATED_EXT',_EnumList},_Constr}, Val) -> decode_jer(Type,Val); -decode_jer({typeinfo,{Module,Func}},Val) -> - TypeInfo = Module:Func(), - decode_jer(TypeInfo,Val); +decode_jer({typeinfo,{Module,Type}}, Val) -> + TypeInfo = Module:typeinfo(Type), + decode_jer(TypeInfo, Val); decode_jer({sequence,Sname,_Arity,CompInfos},Value) when is_map(Value) -> DecodedComps = decode_jer_component(CompInfos,Value,[]), @@ -302,6 +304,8 @@ decode_jer({'ObjClassFieldType',_,_},Bin) when is_binary(Bin) -> Bin; decode_jer('ASN1_OPEN_TYPE',Bin) when is_binary(Bin) -> Bin; +decode_jer({container,Type,_Containing}, Val) -> + decode_jer(Type, Val); decode_jer(Type,Val) -> exit({error,{asn1,{{decode,Type},Val}}}). diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile index de2a38e752..51a4b53812 100644 --- a/lib/asn1/test/Makefile +++ b/lib/asn1/test/Makefile @@ -42,6 +42,7 @@ MODULES= \ testChoTypeRefSeq \ testChoTypeRefSet \ testConstraints \ + testContaining \ testDef \ testExtensionDefault \ testOpt \ diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index b45a62ceab..3b2689694a 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -137,6 +137,7 @@ all() -> testNortel, test_WS_ParamClass, test_modified_x420, + testContaining, %% Some heavy tests. testTcapsystem, @@ -627,7 +628,8 @@ parse(Config) -> [asn1_test_lib:compile(M, Config, [abs]) || M <- test_modules()]. per(Config) -> - test(Config, fun per/3, [per,uper,{per,[maps]},{uper,[maps]}]). + test(Config, fun per/3, + [per,uper,{per,[maps]},{uper,[maps]},{per,[jer]}]). per(Config, Rule, Opts) -> module_test(per_modules(), Config, Rule, Opts). @@ -1139,6 +1141,20 @@ testExtensionAdditionGroup(Config, Rule, Opts) -> [Rule,{record_name_prefix,"RRC-"}|Opts]), extensionAdditionGroup:run(Rule). +testContaining(Config) -> + test(Config, fun testContaining/3). +testContaining(Config, Rule, Opts) -> + asn1_test_lib:compile("Containing", Config, [Rule|Opts]), + testContaining:containing(Rule), + case {Rule,have_jsonlib()} of + {per,true} -> + io:format("Testing with both per and jer...\n"), + asn1_test_lib:compile("Containing", Config, [jer,Rule|Opts]), + testContaining:containing(per_jer); + _ -> + ok + end. + per_modules() -> [X || X <- test_modules()]. diff --git a/lib/asn1/test/asn1_SUITE_data/Containing.asn1 b/lib/asn1/test/asn1_SUITE_data/Containing.asn1 new file mode 100644 index 0000000000..8d2b758891 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/Containing.asn1 @@ -0,0 +1,20 @@ +Containing DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + Config ::= SEQUENCE { + a INTEGER, + b BOOLEAN + } + + Seq ::= SEQUENCE { + tag INTEGER, + contains BIT STRING (CONTAINING Config) + } + + Str ::= OCTET STRING (CONTAINING Seq) + + -- CONTAINING is ignored when ENCODED BY is present + Other ::= OCTET STRING (CONTAINING INTEGER ENCODED BY {joint-iso-itu-t(2) asn1(1) + packed-encoding(3) basic(0) aligned(0)}) + -- CONTAINING is ignored for nameless constructed types + NewSequence ::= BIT STRING (CONTAINING SEQUENCE { z BOOLEAN }) +END diff --git a/lib/asn1/test/testContaining.erl b/lib/asn1/test/testContaining.erl new file mode 100644 index 0000000000..54619df565 --- /dev/null +++ b/lib/asn1/test/testContaining.erl @@ -0,0 +1,52 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2023. All Rights Reserved. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% +-module(testContaining). + +-export([containing/1]). + +containing(Rules) -> + RoundTrip = + case Rules of + per_jer -> + fun roundtrip_per_jer/2; + _ -> + fun roundtrip/2 + end, + RoundTrip('Config', {'Config',42,true}), + RoundTrip('Seq', {'Seq',17,<<"abc">>}), + RoundTrip('Str', <<"xyz">>), + RoundTrip('Other', <<"other">>), + RoundTrip('NewSequence', <<1,2,3,4>>), + + ok. + +roundtrip(Type, Value) -> + asn1_test_lib:roundtrip('Containing', Type, Value). + +roundtrip_per_jer(Type, Value) -> + Mod = 'Containing', + case Mod:jer_encode(Type, Value) of + {ok,Encoded} -> + {ok,Value} = Mod:jer_decode(Type, Encoded); + Encoded when is_binary(Encoded) -> + Value = Mod:jer_decode(Type, Encoded) + end, + Encoded. -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor