diff options
Diffstat (limited to 'lib/asn1')
38 files changed, 2111 insertions, 1296 deletions
diff --git a/lib/asn1/doc/src/asn1_getting_started.xml b/lib/asn1/doc/src/asn1_getting_started.xml index d40b294c39..d2b73d63c3 100644 --- a/lib/asn1/doc/src/asn1_getting_started.xml +++ b/lib/asn1/doc/src/asn1_getting_started.xml @@ -187,6 +187,14 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn</pre> <item> <p>DER encoding rule. Only when using option <c>-ber</c>.</p> </item> + <tag><c>+maps</c></tag> + <item> + <p>Use maps instead of records to represent the <c>SEQUENCE</c> and + <c>SET</c> types. No <c>.hrl</c> files will be generated. + See the Section <seealso marker="asn1_getting_started#MAP_SEQ_SET"> + Map representation for SEQUENCE and SET</seealso> + for more information.</p> + </item> <tag><c>+asn1config</c></tag> <item> <p>This functionality works together with option @@ -766,8 +774,11 @@ Pdu ::= SEQUENCE { b REAL, c OBJECT IDENTIFIER, d NULL } </pre> - <p>This is a 4-component structure called <c>Pdu</c>. The record format - is the major format for representation of <c>SEQUENCE</c> in Erlang. + <p>This is a 4-component structure called <c>Pdu</c>. By default, + a <c>SEQUENCE</c> is represented by a record in Erlang. + It can also be represented as a map; see + <seealso marker="asn1_getting_started#MAP_SEQ_SET"> + Map representation for SEQUENCE and SET</seealso>. For each <c>SEQUENCE</c> and <c>SET</c> in an ASN.1 module an Erlang record declaration is generated. For <c>Pdu</c>, a record like the following is defined:</p> @@ -878,6 +889,48 @@ SExt ::= SEQUENCE { </section> <section> + <marker id="MAP_SEQ_SET"></marker> + <title>Map representation for SEQUENCE and SET</title> + <p>If the ASN.1 module has been compiled with option <c>maps</c>, + the types <c>SEQUENCE</c> and <c>SET</c> are represented as maps.</p> + <p>In the following example, this ASN.1 specification is used:</p> + <pre> +File DEFINITIONS AUTOMATIC TAGS ::= +BEGIN +Seq1 ::= SEQUENCE { + a INTEGER DEFAULT 42, + b BOOLEAN OPTIONAL, + c IA5String +} +END </pre> + + <p>Optional fields are to be omitted from the map if they have + no value:</p> + + <pre> +1> <input>asn1ct:compile('File', [per,maps]).</input> +ok +2> <input>{ok,E} = 'File':encode('Seq1', #{a=>0,c=>"string"}).</input> +{ok,<<128,1,0,6,115,116,114,105,110,103>>} </pre> + + <p>When decoding, optional fields will be omitted from the map:</p> + + <pre> +3> <input>'File':decode('Seq1', E).</input> +{ok,#{a => 0,c => "string"}} </pre> + + <p>Default values can be omitted from the map:</p> + <pre> +4> <input>{ok,E2} = 'File':encode('Seq1', #{c=>"string"}).</input> +{ok,<<0,6,115,116,114,105,110,103>>} +5> <input>'File':decode('Seq1', E2).</input> +{ok,#{a => 42,c => "string"}} </pre> + + <note><p>It is not allowed to use the atoms <c>asn1_VALUE</c> and + <c>asn1_DEFAULT</c> with maps.</p></note> + </section> + + <section> <marker id="CHOICE"></marker> <title>CHOICE</title> <p>The type <c>CHOICE</c> is a space saver and is similar to the @@ -1004,11 +1057,16 @@ T ::= CHOICE { <section> <title>Naming of Records in .hrl Files</title> + <p>When the option <c>maps</c> is given, no <c>.hrl</c> files + will be generated. The rest of this section describes the behavior + of the compiler when <c>maps</c> is not used.</p> + <p>When an ASN.1 specification is compiled, all defined types of type - <c>SET</c> or <c>SEQUENCE</c> result in a corresponding record in the - generated <c>.hrl</c> file. This is because the values for - <c>SET</c> and <c>SEQUENCE</c> are represented as records as - mentioned earlier.</p> + <c>SET</c> or <c>SEQUENCE</c> result in a corresponding record in the + generated <c>.hrl</c> file. This is because the values for + <c>SET</c> and <c>SEQUENCE</c> are represented as records + by default.</p> + <p>Some special cases of this functionality are presented in the next section.</p> @@ -1144,9 +1202,10 @@ SS ::= SET { <p>This example shows that a function is generated by the compiler that returns a valid Erlang representation of the value, although the value is of a complex type.</p> - <p>Furthermore, a macro is generated for each value in the <c>.hrl</c> - file. So, the defined value <c>tt</c> can also be extracted by - <c>?tt</c> in application code.</p> + <p>Furthermore, if the option <c>maps</c> is not used, + a macro is generated for each value in the <c>.hrl</c> + file. So, the defined value <c>tt</c> can also be extracted by + <c>?tt</c> in application code.</p> </section> <section> diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml index ebe1ce44dc..859d6a50bb 100644 --- a/lib/asn1/doc/src/asn1ct.xml +++ b/lib/asn1/doc/src/asn1ct.xml @@ -170,11 +170,24 @@ File3.asn</pre> as for <c>ber</c>. </p> </item> + <tag><c>maps</c></tag> + <item> + <p>This option changes the representation of the types + <c>SEQUENCE</c> and <c>SET</c> to use maps (instead of + records). This option also suppresses the generation of + <c>.hrl</c> files.</p> + <p>For details, see Section + <seealso marker="asn1_getting_started#MAP_SEQ_SET"> + Map representation for SEQUENCE and SET</seealso> + in the User's Guide. + </p> + </item> <tag><c>compact_bit_string</c></tag> <item> <p> The <c>BIT STRING</c> type is decoded to "compact notation". <em>This option is not recommended for new code.</em> + This option cannot be combined with the option <c>maps</c>. </p> <p>For details, see Section <seealso marker="asn1_getting_started#BIT STRING"> @@ -188,6 +201,7 @@ File3.asn</pre> The <c>BIT STRING</c> type is decoded to the legacy format, that is, a list of zeroes and ones. <em>This option is not recommended for new code.</em> + This option cannot be combined with the option <c>maps</c>. </p> <p>For details, see Section <seealso marker="asn1_getting_started#BIT STRING">BIT STRING</seealso> @@ -202,7 +216,8 @@ File3.asn</pre> marker="asn1_getting_started#BIT STRING">BIT STRING</seealso> and Section <seealso marker="asn1_getting_started#OCTET STRING">OCTET STRING</seealso> in the User's Guide.</p> - <p><em>This option is not recommended for new code.</em></p> + <p><em>This option is not recommended for new code.</em> + This option cannot be combined with the option <c>maps</c>.</p> </item> <tag><c>{n2n, EnumTypeName}</c></tag> <item> diff --git a/lib/asn1/examples/recordnames.txt b/lib/asn1/examples/recordnames.txt index 78e30ab510..9b890b4aa7 100644 --- a/lib/asn1/examples/recordnames.txt +++ b/lib/asn1/examples/recordnames.txt @@ -1,6 +1,6 @@ For each ASN1 types SET and SEQUENCE a record is generated in the .hrl file with the same name as the corresponding type. -A decoded value is also returned as a record with the apropriate name. +A decoded value is also returned as a record with the appropriate name. An internally defined type as the type in component 'a' in the following example will result in a record with name 'Seq_a': diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl index 869ea310aa..a3e45ca915 100644 --- a/lib/asn1/src/asn1_db.erl +++ b/lib/asn1/src/asn1_db.erl @@ -20,7 +20,7 @@ %% -module(asn1_db). --export([dbstart/1,dbnew/2,dbload/1,dbload/3,dbsave/2,dbput/2, +-export([dbstart/1,dbnew/3,dbload/1,dbload/4,dbsave/2,dbput/2, dbput/3,dbget/2]). -export([dbstop/0]). @@ -37,13 +37,13 @@ dbstart(Includes0) -> put(?MODULE, spawn_link(fun() -> init(Parent, Includes) end)), ok. -dbload(Module, Erule, Mtime) -> - req({load, Module, Erule, Mtime}). +dbload(Module, Erule, Maps, Mtime) -> + req({load, Module, {Erule,Maps}, Mtime}). dbload(Module) -> req({load, Module, any, {{0,0,0},{0,0,0}}}). -dbnew(Module, Erule) -> req({new, Module, Erule}). +dbnew(Module, Erule, Maps) -> req({new, Module, {Erule,Maps}}). dbsave(OutFile, Module) -> cast({save, OutFile, Module}). dbput(Module, K, V) -> cast({set, Module, K, V}). dbput(Module, Kvs) -> cast({set, Module, Kvs}). @@ -110,19 +110,19 @@ loop(#state{parent = Parent, monitor = MRef, table = Table, ok = ets:tab2file(Mtab, TempFile), ok = file:rename(TempFile, OutFile), loop(State); - {From, {new, Mod, Erule}} -> + {From, {new, Mod, EruleMaps}} -> [] = ets:lookup(Table, Mod), %Assertion. ModTableId = ets:new(list_to_atom(lists:concat(["asn1_",Mod])), []), ets:insert(Table, {Mod, ModTableId}), - ets:insert(ModTableId, {?MAGIC_KEY, info(Erule)}), + ets:insert(ModTableId, {?MAGIC_KEY, info(EruleMaps)}), reply(From, ok), loop(State); - {From, {load, Mod, Erule, Mtime}} -> + {From, {load, Mod, EruleMaps, Mtime}} -> case ets:member(Table, Mod) of true -> reply(From, ok); false -> - case load_table(Mod, Erule, Mtime, Includes) of + case load_table(Mod, EruleMaps, Mtime, Includes) of {ok, ModTableId} -> ets:insert(Table, {Mod, ModTableId}), reply(From, ok); @@ -151,20 +151,20 @@ lookup(Tab, K) -> [{K,V}] -> V end. -info(Erule) -> - {asn1ct:vsn(),Erule}. +info(EruleMaps) -> + {asn1ct:vsn(),EruleMaps}. -load_table(Mod, Erule, Mtime, Includes) -> +load_table(Mod, EruleMaps, Mtime, Includes) -> Base = lists:concat([Mod, ".asn1db"]), case path_find(Includes, Mtime, Base) of error -> error; - {ok,ModTab} when Erule =:= any -> + {ok,ModTab} when EruleMaps =:= any -> {ok,ModTab}; {ok,ModTab} -> Vsn = asn1ct:vsn(), case ets:lookup(ModTab, ?MAGIC_KEY) of - [{_,{Vsn,Erule}}] -> + [{_,{Vsn,EruleMaps}}] -> %% Correct version and encoding rule. {ok,ModTab}; _ -> diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl index af10c1771c..06a9e3ab03 100644 --- a/lib/asn1/src/asn1_records.hrl +++ b/lib/asn1/src/asn1_records.hrl @@ -28,6 +28,7 @@ -define('COMPLETE_ENCODE',1). -define('TLV_DECODE',2). +-define(MISSING_IN_MAP, asn1__MISSING_IN_MAP). -record(module,{pos,name,defid,tagdefault='EXPLICIT',exports={exports,[]},imports={imports,[]}, extensiondefault=empty,typeorval}). @@ -96,6 +97,28 @@ error_context %Top-level thingie (contains line numbers) }). +%% Code generation parameters and options. +-record(gen, + {erule=ber :: 'ber' | 'per', + der=false :: boolean(), + aligned=false :: boolean(), + rec_prefix="" :: string(), + macro_prefix="" :: string(), + pack=record :: 'record' | 'map', + options=[] :: [any()] + }). + +%% Abstract intermediate representation. +-record(abst, + {name :: module(), %Name of module. + types, %Types. + values, %Values. + ptypes, %Parameterized types. + classes, %Classes. + objects, %Objects. + objsets %Object sets. + }). + %% state record used by back-end at partial decode %% active is set to 'yes' when a partial decode function is generated. %% prefix is set to 'dec-inc-' or 'dec-partial-' is for diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 4e030861f5..9f77a557e5 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -193,7 +193,7 @@ check_pass(#st{code=M,file=File,includes=Includes, erule=Erule,dbfile=DbFile,opts=Opts, inputmodules=InputModules}=St) -> start(Includes), - case asn1ct_check:storeindb(#state{erule=Erule}, M) of + case asn1ct_check:storeindb(#state{erule=Erule,options=Opts}, M) of ok -> Module = asn1_db:dbget(M#module.name, 'MODULE'), State = #state{mname=Module#module.name, @@ -216,8 +216,8 @@ check_pass(#st{code=M,file=File,includes=Includes, {error,St#st{error=Reason}} end. -save_pass(#st{code=M,erule=Erule}=St) -> - ok = asn1ct_check:storeindb(#state{erule=Erule}, M), +save_pass(#st{code=M,erule=Erule,opts=Opts}=St) -> + ok = asn1ct_check:storeindb(#state{erule=Erule,options=Opts}, M), {ok,St}. parse_listing(#st{code=Code,outfile=OutFile0}=St) -> @@ -236,12 +236,8 @@ abs_listing(#st{code={M,_},outfile=OutFile}) -> generate_pass(#st{code=Code,outfile=OutFile,erule=Erule,opts=Opts}=St0) -> St = St0#st{code=undefined}, %Reclaim heap space - case generate(Code, OutFile, Erule, Opts) of - {error,Reason} -> - {error,St#st{error=Reason}}; - ok -> - {ok,St} - end. + generate(Code, OutFile, Erule, Opts), + {ok,St}. compile_pass(#st{outfile=OutFile,opts=Opts0}=St) -> asn1_db:dbstop(), %Reclaim memory. @@ -834,37 +830,55 @@ delete_double_of_symbol1([],Acc) -> %%*********************************** -generate({M,GenTOrV}, OutFile, EncodingRule, Options) -> +generate({M,CodeTuple}, OutFile, EncodingRule, Options) -> + {Types,Values,Ptypes,Classes,Objects,ObjectSets} = CodeTuple, + Code = #abst{name=M#module.name, + types=Types,values=Values,ptypes=Ptypes, + classes=Classes,objects=Objects,objsets=ObjectSets}, debug_on(Options), setup_bit_string_format(Options), setup_legacy_erlang_types(Options), - put(encoding_options,Options), asn1ct_table:new(check_functions), + Gen = init_gen_record(EncodingRule, Options), + + check_maps_option(Gen), + %% create decoding function names and taglists for partial decode - case (catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options)) of - {error, Reason} -> warning("Error in configuration file: ~n~p~n", - [Reason], Options, - "Error in configuration file"); - _ -> ok + try + specialized_decode_prepare(Gen, M) + catch + throw:{error, Reason} -> + warning("Error in configuration file: ~n~p~n", + [Reason], Options, + "Error in configuration file") end, - Result = - case (catch asn1ct_gen:pgen(OutFile,EncodingRule, - M#module.name,GenTOrV,Options)) of - {'EXIT',Reason2} -> - error("~p~n",[Reason2],Options), - {error,Reason2}; - _ -> - ok - end, + asn1ct_gen:pgen(OutFile, Gen, Code), debug_off(Options), - erase(encoding_options), cleanup_bit_string_format(), erase(tlv_format), % used in ber erase(class_default_type),% used in ber asn1ct_table:delete(check_functions), - Result. + ok. + +init_gen_record(EncodingRule, Options) -> + Erule = case EncodingRule of + uper -> per; + _ -> EncodingRule + end, + Der = proplists:get_bool(der, Options), + Aligned = EncodingRule =:= per, + RecPrefix = proplists:get_value(record_name_prefix, Options, ""), + MacroPrefix = proplists:get_value(macro_name_prefix, Options, ""), + Pack = case proplists:get_value(maps, Options, false) of + true -> map; + false -> record + end, + #gen{erule=Erule,der=Der,aligned=Aligned, + rec_prefix=RecPrefix,macro_prefix=MacroPrefix, + pack=Pack,options=Options}. + setup_legacy_erlang_types(Opts) -> F = case lists:member(legacy_erlang_types, Opts) of @@ -910,6 +924,26 @@ cleanup_bit_string_format() -> get_bit_string_format() -> get(bit_string_format). +check_maps_option(#gen{pack=map}) -> + case get_bit_string_format() of + bitstring -> + ok; + _ -> + Message1 = "The 'maps' option must not be combined with " + "'compact_bit_string' or 'legacy_bit_string'", + exit({error,{asn1,Message1}}) + end, + case use_legacy_types() of + false -> + ok; + true -> + Message2 = "The 'maps' option must not be combined with " + "'legacy_erlang_types'", + exit({error,{asn1,Message2}}) + end; +check_maps_option(#gen{}) -> + ok. + %% parse_and_save parses an asn1 spec and saves the unchecked parse %% tree in a data base file. @@ -919,22 +953,27 @@ parse_and_save(Module,S) -> SourceDir = S#state.sourcedir, Includes = [I || {i,I} <- Options], Erule = S#state.erule, + Maps = lists:member(maps, Options), case get_input_file(Module, [SourceDir|Includes]) of %% search for asn1 source {file,SuffixedASN1source} -> Mtime = filelib:last_modified(SuffixedASN1source), - case asn1_db:dbload(Module, Erule, Mtime) of + case asn1_db:dbload(Module, Erule, Maps, Mtime) of ok -> ok; error -> parse_and_save1(S, SuffixedASN1source, Options) end; - Err -> + Err when not Maps -> case asn1_db:dbload(Module) of ok -> + %% FIXME: This should be an error. warning("could not do a consistency check of the ~p file: no asn1 source file was found.~n", [lists:concat([Module,".asn1db"])],Options); error -> ok end, + {error,{asn1,input_file_error,Err}}; + Err -> + %% Always fail directly when the 'maps' option is used. {error,{asn1,input_file_error,Err}} end. @@ -997,9 +1036,8 @@ input_file_type(File) -> end end; ".asn1config" -> - case read_config_file(File,asn1_module) of + case read_config_file_info(File, asn1_module) of {ok,Asn1Module} -> -% put(asn1_config_file,File), input_file_type(Asn1Module); Error -> Error @@ -1092,16 +1130,27 @@ translate_options([H|T]) -> translate_options([]) -> []. remove_asn_flags(Options) -> - [X || X <- Options, - X /= get_rule(Options), - X /= optimize, - X /= compact_bit_string, - X /= legacy_bit_string, - X /= legacy_erlang_types, - X /= debug, - X /= asn1config, - X /= record_name_prefix]. - + [X || X <- Options, not is_asn1_flag(X)]. + +is_asn1_flag(asn1config) -> true; +is_asn1_flag(ber) -> true; +is_asn1_flag(compact_bit_string) -> true; +is_asn1_flag(debug) -> true; +is_asn1_flag(der) -> true; +is_asn1_flag(legacy_bit_string) -> true; +is_asn1_flag({macro_name_prefix,_}) -> true; +is_asn1_flag({n2n,_}) -> true; +is_asn1_flag(noobj) -> true; +is_asn1_flag(no_ok_wrapper) -> true; +is_asn1_flag(optimize) -> true; +is_asn1_flag(per) -> true; +is_asn1_flag({record_name_prefix,_}) -> true; +is_asn1_flag(undec_rec) -> true; +is_asn1_flag(uper) -> true; +is_asn1_flag(verbose) -> true; +%% 'warnings_as_errors' is intentionally passed through to the compiler. +is_asn1_flag(_) -> false. + debug_on(Options) -> case lists:member(debug,Options) of true -> @@ -1370,25 +1419,26 @@ prepare_bytes(Bytes) -> list_to_binary(Bytes). vsn() -> ?vsn. -specialized_decode_prepare(Erule,M,TsAndVs,Options) -> - case lists:member(asn1config,Options) of +specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, M) -> + case lists:member(asn1config, Options) of true -> - partial_decode_prepare(Erule,M,TsAndVs,Options); - _ -> + special_decode_prepare_1(Gen, M); + false -> ok - end. + end; +specialized_decode_prepare(_, _) -> + ok. + %% Reads the configuration file if it exists and stores information %% about partial decode and incomplete decode -partial_decode_prepare(ber,M,TsAndVs,Options) when is_tuple(TsAndVs) -> +special_decode_prepare_1(#gen{options=Options}=Gen, M) -> %% read configure file - - ModName = - case lists:keysearch(asn1config,1,Options) of - {value,{_,MName}} -> MName; - _ -> M#module.name - end, + ModName = case lists:keyfind(asn1config, 1, Options) of + {_,MName} -> MName; + false -> M#module.name + end, %% io:format("ModName: ~p~nM#module.name: ~p~n~n",[ModName,M#module.name]), - case read_config_file(ModName) of + case read_config_file(Gen, ModName) of no_config_file -> ok; CfgList -> @@ -1407,11 +1457,7 @@ partial_decode_prepare(ber,M,TsAndVs,Options) when is_tuple(TsAndVs) -> Part_inc_tlv_tags = tlv_tags(CommandList2), save_config(partial_incomplete_decode,Part_inc_tlv_tags), save_gen_state(exclusive_decode,ExclusiveDecode,Part_inc_tlv_tags) - end; -partial_decode_prepare(_,_,_,_) -> - ok. - - + end. %% create_partial_inc_decode_gen_info/2 %% @@ -1863,46 +1909,38 @@ tlv_tag1(<<0:1,PartialTag:7>>,Acc) -> tlv_tag1(<<1:1,PartialTag:7,Buffer/binary>>,Acc) -> tlv_tag1(Buffer,(Acc bsl 7) bor PartialTag). -%% reads the content from the configuration file and returns the -%% selected part choosen by InfoType. Assumes that the config file +%% Reads the content from the configuration file and returns the +%% selected part chosen by InfoType. Assumes that the config file %% content is an Erlang term. -read_config_file(ModuleName,InfoType) when is_atom(InfoType) -> - CfgList = read_config_file(ModuleName), - get_config_info(CfgList,InfoType). +read_config_file_info(ModuleName, InfoType) when is_atom(InfoType) -> + Name = ensure_ext(ModuleName, ".asn1config"), + CfgList = read_config_file0(Name, []), + get_config_info(CfgList, InfoType). +read_config_file(#gen{options=Options}, ModuleName) -> + Name = ensure_ext(ModuleName, ".asn1config"), + Includes = [I || {i,I} <- Options], + read_config_file0(Name, ["."|Includes]). -read_config_file(ModuleName) -> - case file:consult(lists:concat([ModuleName,'.asn1config'])) of +read_config_file0(Name, [D|Dirs]) -> + case file:consult(filename:join(D, Name)) of {ok,CfgList} -> CfgList; {error,enoent} -> - Options = get(encoding_options), - Includes = [I || {i,I} <- Options], - read_config_file1(ModuleName,Includes); + read_config_file0(Name, Dirs); {error,Reason} -> Error = "error reading asn1 config file: " ++ file:format_error(Reason), throw({error,Error}) - end. -read_config_file1(ModuleName,[]) -> - case filename:extension(ModuleName) of - ".asn1config" -> - no_config_file; - _ -> - read_config_file(lists:concat([ModuleName,".asn1config"])) end; -read_config_file1(ModuleName,[H|T]) -> -% File = filename:join([H,lists:concat([ModuleName,'.asn1config'])]), - File = filename:join([H,ModuleName]), - case file:consult(File) of - {ok,CfgList} -> - CfgList; - {error,enoent} -> - read_config_file1(ModuleName,T); - {error,Reason} -> - Error = "error reading asn1 config file: " ++ - file:format_error(Reason), - throw({error,Error}) +read_config_file0(_, []) -> + no_config_file. + +ensure_ext(ModuleName, Ext) -> + Name = filename:join([ModuleName]), + case filename:extension(Name) of + Ext -> Name; + _ -> Name ++ Ext end. get_config_info(CfgList,InfoType) -> @@ -2382,8 +2420,10 @@ format_error({write_error,File,Reason}) -> io_lib:format("writing output file ~s failed: ~s", [File,file:format_error(Reason)]). -is_error(S) when is_record(S, state) -> - is_error(S#state.options); +is_error(#state{options=Opts}) -> + is_error(Opts); +is_error(#gen{options=Opts}) -> + is_error(Opts); is_error(O) -> lists:member(errors, O) orelse is_verbose(O). @@ -2392,8 +2432,10 @@ is_warning(S) when is_record(S, state) -> is_warning(O) -> lists:member(warnings, O) orelse is_verbose(O). -is_verbose(S) when is_record(S, state) -> - is_verbose(S#state.options); +is_verbose(#state{options=Opts}) -> + is_verbose(Opts); +is_verbose(#gen{options=Opts}) -> + is_verbose(Opts); is_verbose(O) -> lists:member(verbose, O). diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index f2c895bfaa..321f4147f5 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2239,12 +2239,18 @@ normalized_record(SorS,S,Value,Components,NameList) -> case is_record_normalized(S,NewName,Value,length(Components)) of true -> Value; - _ -> + false -> NoComps = length(Components), ListOfVals = normalize_seq_or_set(SorS,S,Value,Components,NameList,[]), - NoComps = length(ListOfVals), %% Assert - list_to_tuple([NewName|ListOfVals]) + NoComps = length(ListOfVals), %Assertion. + case use_maps(S) of + false -> + list_to_tuple([NewName|ListOfVals]); + true -> + create_map_value(Components, ListOfVals) + end end. + is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) -> case get_referenced_type(S,V) of {_M,#valuedef{type=_T1,value=V2}} -> @@ -2253,9 +2259,20 @@ is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) -> end; is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) -> (tuple_size(Value) =:= (NumComps + 1)) andalso (element(1, Value) =:= Name); +is_record_normalized(_S, _Name, Value, _NumComps) when is_map(Value) -> + true; is_record_normalized(_,_,_,_) -> false. +use_maps(#state{options=Opts}) -> + lists:member(maps, Opts). + +create_map_value(Components, ListOfVals) -> + Zipped = lists:zip(Components, ListOfVals), + L = [{Name,V} || {#'ComponentType'{name=Name},V} <- Zipped, + V =/= asn1_NOVALUE], + maps:from_list(L). + normalize_seq_or_set(SorS, S, [{#seqtag{val=Cname},V}|Vs], [#'ComponentType'{name=Cname,typespec=TS}|Cs], @@ -4192,7 +4209,7 @@ iof_associated_type1(S,C) -> %% fieldname=[{typefieldreference,'Type'}], fieldname={'Type',[]}, type=Typefield_type}, - IOFComponents = + IOFComponents0 = [#'ComponentType'{name='type-id', typespec=#type{tag=C1TypeTag, def=ObjectIdentifier, @@ -4209,6 +4226,7 @@ iof_associated_type1(S,C) -> tablecinf=Comp2tablecinf}, prop=mandatory, tags=[{'CONTEXT',0}]}], + IOFComponents = textual_order(IOFComponents0), #'SEQUENCE'{tablecinf=TableCInf, components=simplify_comps(IOFComponents)}. @@ -5673,7 +5691,8 @@ storeindb(S0, #module{name=ModName,typeorval=TVlist0}=M) -> storeindb_1(S, #module{name=ModName}=M, TVlist0, TVlist) -> NewM = M#module{typeorval=findtypes_and_values(TVlist0)}, - asn1_db:dbnew(ModName, S#state.erule), + Maps = lists:member(maps, S#state.options), + asn1_db:dbnew(ModName, S#state.erule, Maps), asn1_db:dbput(ModName, 'MODULE', NewM), asn1_db:dbput(ModName, TVlist), include_default_class(S, NewM#module.name), diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 325bea5879..16af09bca9 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -32,7 +32,7 @@ -include("asn1_records.hrl"). --import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/0]). +-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]). -define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2). @@ -57,7 +57,7 @@ %%=============================================================================== %%=============================================================================== -gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> +gen_encode_sequence(Gen, Typename, #type{}=D) -> asn1ct_name:start(), asn1ct_name:new(term), asn1ct_name:new(bytes), @@ -67,8 +67,12 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> ValName = case Typename of ['EXTERNAL'] -> + Tr = case Gen of + #gen{pack=record} -> transform_to_EXTERNAL1990; + #gen{pack=map} -> transform_to_EXTERNAL1990_maps + end, emit([indent(4),"NewVal = ", - {call,ext,transform_to_EXTERNAL1990,["Val"]}, + {call,ext,Tr,["Val"]}, com,nl]), "NewVal"; _ -> @@ -90,18 +94,9 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> {Rl,El} -> Rl ++ El; _ -> CompList end, - -%% don't match recordname for now, because of compatibility reasons -%% emit(["{'",asn1ct_gen:list2rname(Typename),"'"]), - emit(["{_"]), - case length(CompList1) of - 0 -> - true; - CompListLen -> - emit([","]), - mkcindexlist([Tc || Tc <- lists:seq(1,CompListLen)]) - end, - emit(["} = ",ValName,",",nl]), + + enc_match_input(Gen, ValName, CompList1), + EncObj = case TableConsInfo of #simpletableattributes{usedclassfield=Used, @@ -125,7 +120,7 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> emit([ObjectEncode," = ",nl, " ",{asis,ObjSetMod},":'getenc_",ObjSetName, "'("]), - ValueMatch = value_match(ValueIndex, + ValueMatch = value_match(Gen, ValueIndex, lists:concat(["Cindex",N])), emit([indent(35),ValueMatch,"),",nl]), {AttrN,ObjectEncode}; @@ -144,7 +139,7 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> end end, - gen_enc_sequence_call(Erules,Typename,CompList1,1,Ext,EncObj), + gen_enc_sequence_call(Gen, Typename, CompList1, 1, Ext, EncObj), emit([nl," BytesSoFar = "]), case SeqOrSet of @@ -168,7 +163,36 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) -> call(encode_tags, ["TagIn","BytesSoFar","LenSoFar"]), emit([".",nl]). -gen_decode_sequence(Erules,Typename,D) when is_record(D,type) -> +enc_match_input(#gen{pack=record}, ValName, CompList) -> + Len = length(CompList), + Vars = [lists:concat(["Cindex",N]) || N <- lists:seq(1, Len)], + RecordName = "_", + emit(["{",lists:join(",", [RecordName|Vars]),"} = ",ValName,com,nl]); +enc_match_input(#gen{pack=map}, ValName, CompList) -> + Len = length(CompList), + Vars = [lists:concat(["Cindex",N]) || N <- lists:seq(1, Len)], + Zipped = lists:zip(CompList, Vars), + M = [[{asis,Name},":=",Var] || + {#'ComponentType'{prop=mandatory,name=Name},Var} <- Zipped], + case M of + [] -> + ok; + [_|_] -> + emit(["#{",lists:join(",", M),"} = ",ValName,com,nl]) + end, + Os0 = [{Name,Var} || + {#'ComponentType'{prop=Prop,name=Name},Var} <- Zipped, + Prop =/= mandatory], + F = fun({Name,Var}) -> + [Var," = case ",ValName," of\n" + " #{",{asis,Name},":=",Var,"_0} -> ", + Var,"_0;\n" + " _ -> ",atom_to_list(?MISSING_IN_MAP),"\n" + "end"] + end, + emit(lists:join(",\n", [F(E) || E <- Os0]++[[]])). + +gen_decode_sequence(Gen, Typename, #type{}=D) -> asn1ct_name:start(), asn1ct_name:new(tag), #'SEQUENCE'{tablecinf=TableConsInfo,components=CList0} = D#type.def, @@ -225,15 +249,20 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) -> _ -> {false,false} end, - RecordName = lists:concat([get_record_name_prefix(), - asn1ct_gen:list2rname(Typename)]), - case gen_dec_sequence_call(Erules,Typename,CompList2,Ext,DecObjInf) of - no_terms -> % an empty sequence - emit([nl,nl]), - demit(["Result = "]), %dbg - %% return value as record + RecordName0 = lists:concat([get_record_name_prefix(Gen), + asn1ct_gen:list2rname(Typename)]), + RecordName = list_to_atom(RecordName0), + case gen_dec_sequence_call(Gen, Typename, CompList2, Ext, DecObjInf) of + no_terms -> % an empty sequence asn1ct_name:new(rb), - emit([" {'",RecordName,"'}.",nl,nl]); + case Gen of + #gen{pack=record} -> + emit([nl,nl, + " {'",RecordName,"'}.",nl,nl]); + #gen{pack=map} -> + emit([nl,nl, + " #{}.",nl,nl]) + end; {LeadingAttrTerm,PostponedDecArgs} -> emit([nl]), case {LeadingAttrTerm,PostponedDecArgs} of @@ -243,7 +272,7 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) -> ok; {[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} -> DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])), - ValueMatch = value_match(ValueIndex,Term), + ValueMatch = value_match(Gen, ValueIndex,Term), {ObjSetMod,ObjSetName} = ObjSetRef, emit([DecObj," =",nl, " ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(", @@ -263,22 +292,64 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) -> "end,",nl]) end, asn1ct_name:new(rb), - case Typename of - ['EXTERNAL'] -> - emit([" OldFormat={'",RecordName, - "', "]), - mkvlist(asn1ct_name:all(term)), - emit(["},",nl]), - emit([" ", - {call,ext,transform_to_EXTERNAL1994, - ["OldFormat"]},".",nl]); - _ -> - emit([" {'",RecordName,"', "]), - mkvlist(asn1ct_name:all(term)), - emit(["}.",nl,nl]) - end + gen_dec_pack(Gen, RecordName, Typename, CompList), + emit([".",nl]) end. +gen_dec_pack(Gen, RecordName, Typename, CompList) -> + case Typename of + ['EXTERNAL'] -> + dec_external(Gen, RecordName); + _ -> + asn1ct_name:new(res), + gen_dec_do_pack(Gen, RecordName, CompList), + emit([com,nl, + {curr,res}]) + end. + +dec_external(#gen{pack=record}, RecordName) -> + All = [{var,Term} || Term <- asn1ct_name:all(term)], + Record = [{asis,RecordName}|All], + emit(["OldFormat={",lists:join(",", Record),"},",nl, + {call,ext,transform_to_EXTERNAL1994, + ["OldFormat"]}]); +dec_external(#gen{pack=map}, _RecordName) -> + Vars = asn1ct_name:all(term), + Names = ['direct-reference','indirect-reference', + 'data-value-descriptor',encoding], + Zipped = lists:zip(Names, Vars), + MapInit = lists:join(",", [["'",N,"'=>",{var,V}] || {N,V} <- Zipped]), + emit(["OldFormat = #{",MapInit,"}",com,nl, + "ASN11994Format =",nl, + {call,ext,transform_to_EXTERNAL1994_maps, + ["OldFormat"]}]). + +gen_dec_do_pack(#gen{pack=record}, RecordName, _CompList) -> + All = asn1ct_name:all(term), + L = [{asis,RecordName}|[{var,Var} || Var <- All]], + emit([{curr,res}," = {",lists:join(",", L),"}"]); +gen_dec_do_pack(#gen{pack=map}, _, CompList) -> + Zipped = lists:zip(CompList, asn1ct_name:all(term)), + PF = fun({#'ComponentType'{prop='OPTIONAL'},_}) -> false; + ({_,_}) -> true + end, + {Mandatory,Optional} = lists:partition(PF, Zipped), + L = [[{asis,Name},"=>",{var,Var}] || + {#'ComponentType'{name=Name},Var} <- Mandatory], + emit([{curr,res}," = #{",lists:join(",", L),"}"]), + gen_dec_map_optional(Optional). + +gen_dec_map_optional([{#'ComponentType'{name=Name},Var}|T]) -> + asn1ct_name:new(res), + emit([com,nl, + {curr,res}," = case ",{var,Var}," of",nl, + " asn1_NOVALUE -> ",{prev,res},";",nl, + " _ -> ",{prev,res},"#{",{asis,Name},"=>",{var,Var},"}",nl, + "end"]), + gen_dec_map_optional(T); +gen_dec_map_optional([]) -> + ok. + gen_dec_postponed_decs(_,[]) -> emit(nl); gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term, @@ -327,7 +398,7 @@ emit_opt_or_mand_check(Value,TmpTerm) -> gen_encode_set(Erules,Typename,D) when is_record(D,type) -> gen_encode_sequence(Erules,Typename,D). -gen_decode_set(Erules,Typename,D) when is_record(D,type) -> +gen_decode_set(Gen, Typename, #type{}=D) -> asn1ct_name:start(), %% asn1ct_name:new(term), asn1ct_name:new(tag), @@ -393,7 +464,7 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) -> _ -> emit(["SetFun = fun(FunTlv) ->", nl]), emit(["case FunTlv of ",nl]), - NextNum = gen_dec_set_cases(Erules,Typename,CompList,1), + NextNum = gen_dec_set_cases(Gen, Typename, CompList, 1), emit([indent(6), {curr,else}," -> ",nl, indent(9),"{",NextNum,", ",{curr,else},"}",nl]), emit([indent(3),"end",nl]), @@ -405,14 +476,17 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) -> asn1ct_name:new(tlv) end, - RecordName = lists:concat([get_record_name_prefix(), - asn1ct_gen:list2rname(Typename)]), - case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of - no_terms -> % an empty sequence - emit([nl,nl]), - demit(["Result = "]), %dbg - %% return value as record - emit([" {'",RecordName,"'}.",nl]); + RecordName0 = lists:concat([get_record_name_prefix(Gen), + asn1ct_gen:list2rname(Typename)]), + RecordName = list_to_atom(RecordName0), + case gen_dec_sequence_call(Gen, Typename, CompList, Ext, DecObjInf) of + no_terms -> % an empty SET + case Gen of + #gen{pack=record} -> + emit([nl,nl," {'",RecordName,"'}.",nl,nl]); + #gen{pack=map} -> + emit([nl,nl," #{}.",nl,nl]) + end; {LeadingAttrTerm,PostponedDecArgs} -> emit([nl]), case {LeadingAttrTerm,PostponedDecArgs} of @@ -422,7 +496,7 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) -> ok; {[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} -> DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])), - ValueMatch = value_match(ValueIndex,Term), + ValueMatch = value_match(Gen, ValueIndex, Term), {ObjSetMod,ObjSetName} = ObjSetRef, emit([DecObj," =",nl, " ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(", @@ -441,9 +515,8 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) -> "}}}) % extra fields not allowed",nl, "end,",nl]) end, - emit([" {'",RecordName,"', "]), - mkvlist(asn1ct_name:all(term)), - emit(["}.",nl]) + gen_dec_pack(Gen, RecordName, Typename, CompList), + emit([".",nl]) end. @@ -504,10 +577,8 @@ gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) -> emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]). -gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont) - when is_record(Cont,type)-> - - {Objfun,Objfun_novar,EncObj} = +gen_encode_sof_components(Gen, Typename, SeqOrSetOf, #type{}=Cont) -> + {Objfun,Objfun_novar,EncObj} = case Cont#type.tablecinf of [{objfun,_}|_R] -> {", ObjFun",", _",{no_attr,"ObjFun"}}; @@ -517,20 +588,19 @@ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont) emit(["'enc_",asn1ct_gen:list2name(Typename), "_components'([]",Objfun_novar,", AccBytes, AccLen) -> ",nl]), - case catch lists:member(der,get(encoding_options)) of - true when SeqOrSetOf=='SET OF'-> + case {Gen,SeqOrSetOf} of + {#gen{der=true},'SET OF'} -> asn1ct_func:need({ber,dynamicsort_SETOF,1}), emit([indent(3), "{dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]); - _ -> + {_,_} -> emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl]) end, emit(["'enc_",asn1ct_gen:list2name(Typename), "_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]), TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def), - gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",3, -% mandatory,"{EncBytes,EncLen} = ",EncObj), - mandatory,EncObj), + gen_enc_line(Gen, Typename, TypeNameSuffix, Cont, "H", 3, + mandatory, EncObj), emit([",",nl]), emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename), "_components'(T",Objfun,","]), @@ -1028,35 +1098,44 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj) emit([nl,indent(7),"end"]) end. -gen_optormand_case(mandatory, _Erules, _TopType, _Cname, _Type, _Element) -> +gen_optormand_case(mandatory, _Gen, _TopType, _Cname, _Type, _Element) -> ok; -gen_optormand_case('OPTIONAL', Erules, _TopType, _Cname, _Type, Element) -> +gen_optormand_case('OPTIONAL', Gen, _TopType, _Cname, _Type, Element) -> emit([" case ",Element," of",nl]), - emit([indent(9),"asn1_NOVALUE -> {", - empty_lb(Erules),",0};",nl]), + Missing = case Gen of + #gen{pack=record} -> asn1_NOVALUE; + #gen{pack=map} -> ?MISSING_IN_MAP + end, + emit([indent(9),Missing," -> {", + empty_lb(Gen),",0};",nl]), emit([indent(9),"_ ->",nl,indent(12)]); -gen_optormand_case({'DEFAULT',DefaultValue}, Erules, _TopType, +gen_optormand_case({'DEFAULT',DefaultValue}, Gen, _TopType, _Cname, Type, Element) -> CurrMod = get(currmod), - case catch lists:member(der,get(encoding_options)) of - true -> - asn1ct_gen_check:emit(Type, DefaultValue, Element); - _ -> - emit([" case ",Element," of",nl]), - emit([indent(9),"asn1_DEFAULT -> {", - empty_lb(Erules), - ",0};",nl]), - case DefaultValue of - #'Externalvaluereference'{module=CurrMod, - value=V} -> - emit([indent(9),"?",{asis,V}," -> {", - empty_lb(Erules),",0};",nl]); - _ -> - emit([indent(9),{asis, - DefaultValue}," -> {", - empty_lb(Erules),",0};",nl]) - end, - emit([indent(9),"_ ->",nl,indent(12)]) + case Gen of + #gen{erule=ber,der=true} -> + asn1ct_gen_check:emit(Gen, Type, DefaultValue, Element); + #gen{erule=ber,der=false,pack=Pack} -> + Ind9 = indent(9), + DefMarker = case Pack of + record -> asn1_DEFAULT; + map -> ?MISSING_IN_MAP + end, + emit([" case ",Element," of",nl, + Ind9,{asis,DefMarker}," ->",nl, + Ind9,indent(3),"{",empty_lb(Gen),",0};",nl, + Ind9,"_ when ",Element," =:= "]), + Dv = case DefaultValue of + #'Externalvaluereference'{module=CurrMod, + value=V} -> + ["?",{asis,V}]; + _ -> + [{asis,DefaultValue}] + end, + emit(Dv++[" ->",nl, + Ind9,indent(3),"{",empty_lb(Gen),",0};",nl, + Ind9,"_ ->",nl, + indent(12)]) end. %% Use for SEQUENCE OF and CHOICE. @@ -1207,7 +1286,7 @@ gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandC (Type#type.def)#'ObjectClassFieldType'.fieldname, [{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)), asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}]; -gen_dec_call(InnerType, _Erules, TopType, Cname, Type, BytesVar, +gen_dec_call(InnerType, Gen, TopType, Cname, Type, BytesVar, Tag, _PrimOptOrMand, _OptOrMand, DecObjInf,_) -> WhatKind = asn1ct_gen:type(InnerType), gen_dec_call1(WhatKind, InnerType, TopType, Cname, @@ -1215,7 +1294,7 @@ gen_dec_call(InnerType, _Erules, TopType, Cname, Type, BytesVar, case DecObjInf of {Cname,{_,OSet,_UniqueFName,ValIndex}} -> Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)), - ValueMatch = value_match(ValIndex,Term), + ValueMatch = value_match(Gen, ValIndex, Term), {ObjSetMod,ObjSetName} = OSet, emit([",",nl,"ObjFun = ",{asis,ObjSetMod},":'getdec_",ObjSetName, "'(",ValueMatch,")"]); @@ -1340,19 +1419,6 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) -> indent(N) -> lists:duplicate(N,32). % 32 = space -mkcindexlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ " - emit(["Cindex",H,Sep]), - mkcindexlist([T1|T], Sep); -mkcindexlist([H|T], Sep) -> - emit(["Cindex",H]), - mkcindexlist(T, Sep); -mkcindexlist([], _) -> - true. - -mkcindexlist(L) -> - mkcindexlist(L,", "). - - mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ " emit([{var,H},Sep]), mkvlist([T1|T], Sep); @@ -1429,19 +1495,25 @@ mkfuncname(TopType,Cname,WhatKind,Prefix,Suffix) -> {F, "?MODULE", F} end. -empty_lb(ber) -> +empty_lb(#gen{erule=ber}) -> "<<>>". -value_match(Index,Value) when is_atom(Value) -> - value_match(Index,atom_to_list(Value)); -value_match([],Value) -> +value_match(#gen{pack=record}, VIs, Value) -> + value_match_rec(VIs, Value); +value_match(#gen{pack=map}, VIs, Value) -> + value_match_map(VIs, Value). + +value_match_rec([], Value) -> + Value; +value_match_rec([{VI,_}|VIs], Value0) -> + Value = value_match_rec(VIs, Value0), + lists:concat(["element(",VI,", ",Value,")"]). + +value_match_map([], Value) -> Value; -value_match([{VI,_}|VIs],Value) -> - value_match1(Value,VIs,lists:concat(["element(",VI,","]),1). -value_match1(Value,[],Acc,Depth) -> - Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")")); -value_match1(Value,[{VI,_}|VIs],Acc,Depth) -> - value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1). +value_match_map([{_,Name}|VIs], Value0) -> + Value = value_match_map(VIs, Value0), + lists:concat(["maps:get(",Name,", ",Value,")"]). call(F, Args) -> asn1ct_func:call(ber, F, Args). diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index a34b25182c..b7579c8065 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -32,17 +32,27 @@ -include("asn1_records.hrl"). %-compile(export_all). --import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/0]). --import(asn1ct_func, [call/3]). +-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]). + +-type type_name() :: any(). + %% ENCODE GENERATOR FOR SEQUENCE TYPE ** ********** -gen_encode_set(Erules,TypeName,D) -> - gen_encode_constructed(Erules,TypeName,D). +-spec gen_encode_set(Gen, TypeName, #type{}) -> 'ok' when + Gen :: #gen{}, + TypeName :: type_name(). + +gen_encode_set(Gen, TypeName, D) -> + gen_encode_constructed(Gen, TypeName, D). + +-spec gen_encode_sequence(Gen, TypeName, #type{}) -> 'ok' when + Gen :: #gen{}, + TypeName :: type_name(). -gen_encode_sequence(Erules,TypeName,D) -> - gen_encode_constructed(Erules,TypeName,D). +gen_encode_sequence(Gen, TypeName, D) -> + gen_encode_constructed(Gen, TypeName, D). gen_encode_constructed(Erule, Typename, #type{}=D) -> asn1ct_name:start(), @@ -50,88 +60,23 @@ gen_encode_constructed(Erule, Typename, #type{}=D) -> asn1ct_imm:enc_cg(Imm, is_aligned(Erule)), emit([".",nl]). -gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> - {ExtAddGroup,TmpCompList,TableConsInfo} = - case D#type.def of - #'SEQUENCE'{tablecinf=TCI,components=CL,extaddgroup=ExtAddGroup0} -> - {ExtAddGroup0,CL,TCI}; - #'SET'{tablecinf=TCI,components=CL} -> - {undefined,CL,TCI} - end, - - CompList = case ExtAddGroup of - undefined -> - TmpCompList; - _ when is_integer(ExtAddGroup) -> - %% This is a fake SEQUENCE representing an ExtensionAdditionGroup - %% Reset the textual order so we get the right - %% index of the components - [Comp#'ComponentType'{textual_order=undefined}|| - Comp<-TmpCompList] - end, - ExternalImm = - case Typename of - ['EXTERNAL'] -> - Next = asn1ct_gen:mk_var(asn1ct_name:next(val)), - Curr = asn1ct_gen:mk_var(asn1ct_name:curr(val)), - asn1ct_name:new(val), - [{call,ext,transform_to_EXTERNAL1990,[{var,Curr}],{var,Next}}]; - _ -> - [] - end, - Aligned = is_aligned(Erule), - Value0 = make_var(val), +gen_encode_constructed_imm(Gen, Typename, #type{}=D) -> + {CompList,TableConsInfo} = enc_complist(D), + ExternalImm = external_imm(Gen, Typename), Optionals = optionals(to_textual_order(CompList)), - ImmOptionals = [asn1ct_imm:per_enc_optional(Value0, Opt, Aligned) || - Opt <- Optionals], + ImmOptionals = enc_optionals(Gen, Optionals), Ext = extensible_enc(CompList), + Aligned = is_aligned(Gen), ExtImm = case Ext of {ext,ExtPos,NumExt} when NumExt > 0 -> - gen_encode_extaddgroup(CompList), + gen_encode_extaddgroup(Gen, CompList), Value = make_var(val), - asn1ct_imm:per_enc_extensions(Value, ExtPos, - NumExt, Aligned); + enc_extensions(Gen, Value, ExtPos, NumExt, Aligned); _ -> [] end, - {EncObj,ObjSetImm} = - case TableConsInfo of - #simpletableattributes{usedclassfield=Used, - uniqueclassfield=Unique} when Used /= Unique -> - {false,[]}; - %% ObjectSet, name of the object set in constraints - %% - %%{ObjectSet,AttrN,N,UniqueFieldName} -> %% N is index of attribute that determines constraint - #simpletableattributes{objectsetname=ObjectSet, - c_name=AttrN, - c_index=N, - usedclassfield=UniqueFieldName, - uniqueclassfield=UniqueFieldName, - valueindex=ValueIndex0 - } -> %% N is index of attribute that determines constraint - {Module,ObjSetName} = ObjectSet, - #typedef{typespec=#'ObjectSet'{gen=Gen}} = - asn1_db:dbget(Module, ObjSetName), - case Gen of - true -> - ValueIndex = ValueIndex0 ++ [{N+1,top}], - Val = make_var(val), - {ObjSetImm0,Dst} = enc_dig_out_value(ValueIndex, Val), - {{AttrN,Dst},ObjSetImm0}; - false -> - {false,[]} - end; - _ -> - case D#type.tablecinf of - [{objfun,_}|_] -> - %% when the simpletableattributes was at an outer - %% level and the objfun has been passed through the - %% function call - {{"got objfun through args",{var,"ObjFun"}},[]}; - _ -> - {false,[]} - end - end, + MatchImm = enc_map_match(Gen, CompList), + {EncObj,ObjSetImm} = enc_table(Gen, TableConsInfo, D), ImmSetExt = case Ext of {ext,_Pos,NumExt2} when NumExt2 > 0 -> @@ -141,38 +86,195 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> _ -> [] end, - ImmBody = gen_enc_components_call(Erule, Typename, CompList, EncObj, Ext), - ExternalImm ++ ExtImm ++ ObjSetImm ++ + ImmBody = gen_enc_components_call(Gen, Typename, CompList, EncObj, Ext), + ExternalImm ++ MatchImm ++ ExtImm ++ ObjSetImm ++ asn1ct_imm:enc_append([ImmSetExt] ++ ImmOptionals ++ ImmBody). -gen_encode_extaddgroup(CompList) -> +external_imm(Gen, ['EXTERNAL']) -> + Next = asn1ct_gen:mk_var(asn1ct_name:next(val)), + Curr = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + asn1ct_name:new(val), + F = case Gen of + #gen{pack=record} -> transform_to_EXTERNAL1990; + #gen{pack=map} -> transform_to_EXTERNAL1990_maps + end, + [{call,ext,F,[{var,Curr}],{var,Next}}]; +external_imm(_, _) -> + []. + +enc_extensions(#gen{pack=record}, Value, ExtPos, NumExt, Aligned) -> + asn1ct_imm:per_enc_extensions(Value, ExtPos, NumExt, Aligned); +enc_extensions(#gen{pack=map}, Value, ExtPos, NumExt, Aligned) -> + Vars = [{var,lists:concat(["Input@",Pos])} || + Pos <- lists:seq(ExtPos, ExtPos+NumExt-1)], + Undefined = atom_to_list(?MISSING_IN_MAP), + asn1ct_imm:per_enc_extensions_map(Value, Vars, Undefined, Aligned). + +enc_complist(#type{def=Def}) -> + case Def of + #'SEQUENCE'{tablecinf=TCI,components=CL0,extaddgroup=ExtAddGroup} -> + case ExtAddGroup of + undefined -> + {CL0,TCI}; + _ when is_integer(ExtAddGroup) -> + %% This is a fake SEQUENCE representing an + %% ExtensionAdditionGroup. Renumber the textual + %% order so we get the right index of the + %% components. + CL = add_textual_order(CL0), + {CL,TCI} + end; + #'SET'{tablecinf=TCI,components=CL} -> + {CL,TCI} + end. + +enc_table(Gen, #simpletableattributes{objectsetname=ObjectSet, + c_name=AttrN, + c_index=N, + usedclassfield=UniqueFieldName, + uniqueclassfield=UniqueFieldName, + valueindex=ValueIndex0}, _) -> + {Module,ObjSetName} = ObjectSet, + #typedef{typespec=#'ObjectSet'{gen=MustGen}} = + asn1_db:dbget(Module, ObjSetName), + case MustGen of + true -> + ValueIndex = ValueIndex0 ++ [{N+1,'ASN1_top'}], + Val = make_var(val), + {ObjSetImm,Dst} = enc_dig_out_value(Gen, ValueIndex, Val), + {{AttrN,Dst},ObjSetImm}; + false -> + {false,[]} + end; +enc_table(_Gen, #simpletableattributes{}, _) -> + {false,[]}; +enc_table(_Gen, _, #type{tablecinf=TCInf}) -> + case TCInf of + [{objfun,_}|_] -> + %% The simpletableattributes was at an outer + %% level and the objfun has been passed through the + %% function call. + {{"got objfun through args",{var,"ObjFun"}},[]}; + _ -> + {false,[]} + end. + +enc_optionals(Gen, Optionals) -> + Var = make_var(val), + enc_optionals_1(Gen, Optionals, Var). + +enc_optionals_1(#gen{pack=record}=Gen, [{Pos,DefVals}|T], Var) -> + {Imm0,Element} = asn1ct_imm:enc_element(Pos+1, Var), + Imm = asn1ct_imm:per_enc_optional(Element, DefVals), + [Imm0++Imm|enc_optionals_1(Gen, T, Var)]; +enc_optionals_1(#gen{pack=map}=Gen, [{Pos,DefVals0}|T], V) -> + Var = {var,lists:concat(["Input@",Pos])}, + DefVals = translate_missing_value(Gen, DefVals0), + Imm = asn1ct_imm:per_enc_optional(Var, DefVals), + [Imm|enc_optionals_1(Gen, T, V)]; +enc_optionals_1(_, [], _) -> + []. + +enc_map_match(#gen{pack=record}, _Cs) -> + []; +enc_map_match(#gen{pack=map}, Cs0) -> + Var0 = "Input", + Cs = enc_flatten_components(Cs0), + M = [[quote_atom(Name),":=",lists:concat([Var0,"@",Order])] || + #'ComponentType'{prop=mandatory,name=Name, + textual_order=Order} <- Cs], + Mand = case M of + [] -> + []; + [_|_] -> + Patt = {expr,lists:flatten(["#{",lists:join(",", M),"}"])}, + [{assign,Patt,{var,asn1ct_name:curr(val)}}] + end, + + Os0 = [{Name,Order} || + #'ComponentType'{prop=Prop,name=Name, + textual_order=Order} <- Cs, + Prop =/= mandatory], + {var,Val} = make_var(val), + F = fun({Name,Order}) -> + Var = lists:concat([Var0,"@",Order]), + P0 = ["case ",Val," of\n" + " #{",quote_atom(Name),":=",Var,"_0} -> ", + Var,"_0;\n" + " _ -> ",atom_to_list(?MISSING_IN_MAP),"\n" + "end"], + P = lists:flatten(P0), + {assign,{var,Var},P} + end, + Os = [F(O) || O <- Os0], + Mand ++ Os. + +enc_flatten_components({Root1,Ext0,Root2}=CL) -> + {_,Gs} = extgroup_pos_and_length(CL), + Ext = wrap_extensionAdditionGroups(Ext0, Gs), + Root1 ++ Root2 ++ [mark_optional(C) || C <- Ext]; +enc_flatten_components({Root,Ext}) -> + enc_flatten_components({Root,Ext,[]}); +enc_flatten_components(Cs) -> + Cs. + +gen_encode_extaddgroup(#gen{pack=record}, CompList) -> case extgroup_pos_and_length(CompList) of {extgrouppos,[]} -> ok; {extgrouppos,ExtGroupPosLenList} -> - _ = [do_gen_encode_extaddgroup(G) || G <- ExtGroupPosLenList], + _ = [gen_encode_eag_record(G) || + G <- ExtGroupPosLenList], ok - end. + end; +gen_encode_extaddgroup(#gen{pack=map}, Cs0) -> + Cs = enc_flatten_components(Cs0), + gen_encode_eag_map(Cs). + +gen_encode_eag_map([#'ComponentType'{name=Group,typespec=Type}|Cs]) -> + case Type of + #type{def=#'SEQUENCE'{extaddgroup=G,components=GCs0}} + when is_integer(G) -> + Ns = [N || #'ComponentType'{name=N,prop=mandatory} <- GCs0], + test_for_mandatory(Ns, Group), + gen_encode_eag_map(Cs); + _ -> + gen_encode_eag_map(Cs) + end; +gen_encode_eag_map([]) -> + ok. + +test_for_mandatory([Mand|_], Group) -> + emit([{next,val}," = case ",{curr,val}," of",nl, + "#{",quote_atom(Mand),":=_} -> ", + {curr,val},"#{",{asis,Group},"=>",{curr,val},"};",nl, + "#{} -> ",{curr,val},nl, + "end,",nl]), + asn1ct_name:new(val); +test_for_mandatory([], _) -> + ok. -do_gen_encode_extaddgroup({ActualGroupPos,GroupVirtualPos,GroupLen}) -> +gen_encode_eag_record({ActualPos,VirtualPos,Len}) -> Val = asn1ct_gen:mk_var(asn1ct_name:curr(val)), - Elements = make_elements(GroupVirtualPos+1, - Val, - lists:seq(1, GroupLen)), - Expr = any_non_value(GroupVirtualPos+1, Val, GroupLen, ""), + Elements = get_input_vars(Val, VirtualPos, Len), + Expr = any_non_value(Val, VirtualPos, Len), emit([{next,val}," = case ",Expr," of",nl, - "false -> setelement(",{asis,ActualGroupPos+1},", ", + "false -> setelement(",{asis,ActualPos+1},", ", {curr,val},", asn1_NOVALUE);",nl, - "true -> setelement(",{asis,ActualGroupPos+1},", ", + "true -> setelement(",{asis,ActualPos+1},", ", {curr,val},", {extaddgroup,", Elements,"})",nl, "end,",nl]), asn1ct_name:new(val). -any_non_value(_, _, 0, _) -> +any_non_value(Val, Pos, N) -> + L = any_non_value_1(Val, Pos, N), + lists:join(" orelse ", L). + +any_non_value_1(_, _, 0) -> []; -any_non_value(Pos, Val, N, Sep) -> - Sep ++ [make_element(Pos, Val)," =/= asn1_NOVALUE"] ++ - any_non_value(Pos+1, Val, N-1, [" orelse",nl]). +any_non_value_1(Val, Pos, N) -> + Var = get_input_var(Val, Pos), + [Var ++ " =/= asn1_NOVALUE"|any_non_value_1(Val, Pos+1, N-1)]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% generate decode function for SEQUENCE and SET @@ -306,55 +408,105 @@ gen_dec_constructed_imm(Erule, Typename, #type{}=D) -> {DecObjInf,_,_} = ObjSetInfo, EmitComp = gen_dec_components_call(Erule, Typename, CompList, DecObjInf, Ext, length(Optionals)), - EmitRest = fun({AccTerm,AccBytes}) -> - gen_dec_constructed_imm_2(Erule, Typename, - CompList, - ObjSetInfo, - AccTerm, AccBytes) - end, - [EmitExt,EmitOpt|EmitComp++[{safe,EmitRest}]]. + EmitObjSets = gen_dec_objsets_fun(Erule, ObjSetInfo), + EmitPack = fun(_) -> + gen_dec_pack(Erule, Typename, CompList) + end, + RestGroup = {group,[{safe,EmitObjSets},{safe,EmitPack}]}, + [EmitExt,EmitOpt|EmitComp++[RestGroup]]. + +gen_dec_objsets_fun(Gen, ObjSetInfo) -> + fun({AccTerm,AccBytes}) -> + {_,_UniqueFName,ValueIndex} = ObjSetInfo, + case {AccTerm,AccBytes} of + {[],[]} -> + ok; + {_,[]} -> + ok; + {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} -> + ValueMatch = value_match(Gen, ValueIndex, Term), + _ = [begin + gen_dec_open_type(Gen, ValueMatch, ObjSet, + LeadingAttr, T), + emit([com,nl]) + end || T <- ListOfOpenTypes], + ok + end + end. -gen_dec_constructed_imm_2(Erule, Typename, CompList, - ObjSetInfo, AccTerm, AccBytes) -> - {_,_UniqueFName,ValueIndex} = ObjSetInfo, - case {AccTerm,AccBytes} of - {[],[]} -> - ok; - {_,[]} -> - ok; - {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} -> - ValueMatch = value_match(ValueIndex, Term), - _ = [begin - gen_dec_open_type(Erule, ValueMatch, ObjSet, - LeadingAttr, T), - emit([com,nl]) - end || T <- ListOfOpenTypes], - ok - end, - %% we don't return named lists any more Cnames = mkcnamelist(CompList), - demit({"Result = "}), %dbg - %% return value as record - RecordName = record_name(Typename), +gen_dec_pack(Gen, Typename, CompList) -> case Typename of ['EXTERNAL'] -> - emit({" OldFormat={'",RecordName, - "'"}), - mkvlist(asn1ct_name:all(term)), - emit({"},",nl}), - emit([" ASN11994Format =",nl, - " ", - {call,ext,transform_to_EXTERNAL1994, - ["OldFormat"]},com,nl]), - emit(" {ASN11994Format,"); + dec_external(Gen, Typename); _ -> - emit(["{{'",RecordName,"'"]), - %% CompList is used here because we don't want - %% ExtensionAdditionGroups to be wrapped in SEQUENCES when - %% we are ordering the fields according to textual order - mkvlist(textual_order(to_encoding_order(CompList),asn1ct_name:all(term))), - emit("},") - end, - emit({{curr,bytes},"}"}). + asn1ct_name:new(res), + gen_dec_do_pack(Gen, Typename, CompList), + emit([com,nl, + "{",{curr,res},",",{curr,bytes},"}"]) + end. + +dec_external(#gen{pack=record}=Gen, Typename) -> + RecordName = list_to_atom(record_name(Gen, Typename)), + All = [{var,Term} || Term <- asn1ct_name:all(term)], + Record = [{asis,RecordName}|All], + emit(["OldFormat={",lists:join(",", Record),"},",nl, + "ASN11994Format =",nl, + {call,ext,transform_to_EXTERNAL1994, + ["OldFormat"]},com,nl, + "{ASN11994Format,",{curr,bytes},"}"]); +dec_external(#gen{pack=map}, _Typename) -> + Vars = asn1ct_name:all(term), + Names = ['direct-reference','indirect-reference', + 'data-value-descriptor',encoding], + Zipped = lists:zip(Names, Vars), + MapInit = lists:join(",", [["'",N,"'=>",{var,V}] || {N,V} <- Zipped]), + emit(["OldFormat = #{",MapInit,"}",com,nl, + "ASN11994Format =",nl, + {call,ext,transform_to_EXTERNAL1994_maps, + ["OldFormat"]},com,nl, + "{ASN11994Format,",{curr,bytes},"}"]). + +gen_dec_do_pack(#gen{pack=record}=Gen, TypeName, CompList) -> + Zipped0 = zip_components(CompList, asn1ct_name:all(term)), + Zipped = textual_order(Zipped0), + RecordName = ["'",record_name(Gen, TypeName),"'"], + L = [RecordName|[{var,Var} || {_,Var} <- Zipped]], + emit([{curr,res}," = {",lists:join(",", L),"}"]); +gen_dec_do_pack(#gen{pack=map}, _, CompList0) -> + CompList = enc_flatten_components(CompList0), + Zipped0 = zip_components(CompList, asn1ct_name:all(term)), + Zipped = textual_order(Zipped0), + PF = fun({#'ComponentType'{prop='OPTIONAL'},_}) -> false; + ({_,_}) -> true + end, + {Mandatory,Optional} = lists:partition(PF, Zipped), + L = [[{asis,Name},"=>",{var,Var}] || + {#'ComponentType'{name=Name},Var} <- Mandatory], + emit([{curr,res}," = #{",lists:join(",", L),"}"]), + gen_dec_map_optional(Optional), + gen_dec_merge_maps(asn1ct_name:all(map)). + +gen_dec_map_optional([{#'ComponentType'{name=Name},Var}|T]) -> + asn1ct_name:new(res), + emit([com,nl, + {curr,res}," = case ",{var,Var}," of",nl, + " asn1_NOVALUE -> ",{prev,res},";",nl, + " _ -> ",{prev,res},"#{",{asis,Name},"=>",{var,Var},"}",nl, + "end"]), + gen_dec_map_optional(T); +gen_dec_map_optional([]) -> + ok. + +gen_dec_merge_maps([M|Ms]) -> + asn1ct_name:new(res), + emit([com,nl, + {curr,res}," = maps:merge(",{prev,res},", ",{var,M},")"]), + gen_dec_merge_maps(Ms); +gen_dec_merge_maps([]) -> + ok. + +quote_atom(A) when is_atom(A) -> + io_lib:format("~p", [A]). %% record_name([TypeName]) -> RecordNameString %% Construct a record name for the constructed type, ignoring any @@ -362,10 +514,10 @@ gen_dec_constructed_imm_2(Erule, Typename, CompList, %% group. Such fake sequences never appear as a top type, and their %% name always start with "ExtAddGroup". -record_name(Typename0) -> +record_name(Gen, Typename0) -> [TopType|Typename1] = lists:reverse(Typename0), Typename = filter_ext_add_groups(Typename1, [TopType]), - lists:concat([get_record_name_prefix(), + lists:concat([get_record_name_prefix(Gen), asn1ct_gen:list2rname(Typename)]). filter_ext_add_groups([H|T], Acc) when is_atom(H) -> @@ -379,17 +531,26 @@ filter_ext_add_groups([H|T], Acc) -> filter_ext_add_groups(T, [H|Acc]); filter_ext_add_groups([], Acc) -> Acc. -textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) -> - TermList; -textual_order(CompList,TermList) when is_list(CompList) -> - OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList], - [Term||{_,Term}<- - lists:sort(lists:zip(OrderList, - lists:sublist(TermList,length(OrderList))))]; - %% sublist is just because Termlist can sometimes be longer than - %% OrderList, which it really shouldn't -textual_order({Root,Ext},TermList) -> - textual_order(Root ++ Ext,TermList). +zip_components({Root,Ext}, Vars) -> + zip_components({Root,Ext,[]}, Vars); +zip_components({R1,Ext0,R2}, Vars) -> + Ext = [mark_optional(C) || C <- Ext0], + zip_components(R1++R2++Ext, Vars); +zip_components(Cs, Vars) when is_list(Cs) -> + zip_components_1(Cs, Vars). + +zip_components_1([#'ComponentType'{}=C|Cs], [V|Vs]) -> + [{C,V}|zip_components_1(Cs, Vs)]; +zip_components_1([_|Cs], Vs) -> + zip_components_1(Cs, Vs); +zip_components_1([], []) -> + []. + +textual_order([{#'ComponentType'{textual_order=undefined},_}|_]=L) -> + L; +textual_order(L0) -> + L = [{Ix,P} || {#'ComponentType'{textual_order=Ix},_}=P <- L0], + [C || {_,C} <- lists:sort(L)]. to_textual_order({Root,Ext}) -> {to_textual_order(Root),Ext}; @@ -458,7 +619,7 @@ dec_objset_default(N, _, _, true) -> end]). dec_objset_1(Erule, N, {Id,Obj}, RestFields, Typename) -> - emit([{asis,N},"(Bytes, ",{asis,Id},") ->",nl]), + emit([{asis,N},"(Bytes, Id) when Id =:= ",{asis,Id}," ->",nl]), dec_objset_2(Erule, Obj, RestFields, Typename). dec_objset_2(Erule, Obj, RestFields0, Typename) -> @@ -595,8 +756,7 @@ do_gen_decode_sof(Erules, Typename, SeqOrSetOf, D) -> emit([",",nl, {asis,F},"(",Num,", ",Buf,ObjFun,", [])"]). -is_aligned(per) -> true; -is_aligned(uper) -> false. +is_aligned(#gen{erule=per,aligned=Aligned}) -> Aligned. gen_decode_length(Constraint, Erule) -> emit(["%% Length with constraint ",{asis,Constraint},nl]), @@ -640,22 +800,7 @@ gen_decode_sof_components(Erule, Name, Typename, SeqOrSetOf, Cont) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% General and special help functions (not exported) - -mkvlist([H|T]) -> - emit(","), - mkvlist2([H|T]); -mkvlist([]) -> - true. -mkvlist2([H,T1|T]) -> - emit({{var,H},","}), - mkvlist2([T1|T]); -mkvlist2([H|T]) -> - emit({{var,H}}), - mkvlist2(T); -mkvlist2([]) -> - true. - +%% General and special help functions (not exported) extensible_dec(CompList) when is_list(CompList) -> noext; @@ -728,28 +873,26 @@ gen_dec_optionals(Optionals) -> {imm,Imm0,E}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Produce a list with positions (in the Value record) where -%% there are optional components, start with 2 because first element -%% is the record name - -optionals({L1,Ext,L2}) -> - Opt1 = optionals(L1,[],2), - ExtComps = length([C||C = #'ComponentType'{}<-Ext]), - Opt2 = optionals(L2,[],2+length(L1)+ExtComps), - Opt1 ++ Opt2; -optionals({L,_Ext}) -> optionals(L,[],2); -optionals(L) -> optionals(L,[],2). -optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Acc, Pos) -> - optionals(Rest, [Pos|Acc], Pos+1); -optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Rest], - Acc, Pos) -> +optionals({Root1,Ext,Root2}) -> + Opt1 = optionals(Root1, 1), + ExtComps = length([C || C = #'ComponentType'{} <- Ext]), + Opt2 = optionals(Root2, 1 + length(Root1) + ExtComps), + Opt1 ++ Opt2; +optionals({L,_Ext}) -> + optionals(L, 1); +optionals(L) -> + optionals(L, 1). + +optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Pos) -> + [{Pos,[asn1_NOVALUE]}|optionals(Rest, Pos+1)]; +optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Cs], Pos) -> Vals = def_values(T, Val), - optionals(Rest, [{Pos,Vals}|Acc], Pos+1); -optionals([#'ComponentType'{}|Rest], Acc, Pos) -> - optionals(Rest, Acc, Pos+1); -optionals([], Acc, _) -> - lists:reverse(Acc). + [{Pos,Vals}|optionals(Cs, Pos+1)]; +optionals([#'ComponentType'{}|Rest], Pos) -> + optionals(Rest, Pos+1); +optionals([], _) -> + []. %%%%%%%%%%%%%%%%%%%%%% %% create_optionality_table(Cs=[#'ComponentType'{textual_order=undefined}|_]) -> @@ -779,13 +922,6 @@ get_optionality_pos(TextPos,OptTable) -> no_num end. -to_encoding_order(Cs) when is_list(Cs) -> - Cs; -to_encoding_order(Cs = {_Root,_Ext}) -> - Cs; -to_encoding_order({R1,Ext,R2}) -> - {R1++R2,Ext}. - add_textual_order(Cs) when is_list(Cs) -> {NewCs,_} = add_textual_order1(Cs,1), NewCs; @@ -810,69 +946,77 @@ add_textual_order1(Cs,NumIn) -> end, NumIn,Cs). -gen_enc_components_call(Erule,TopType,{Root,ExtList}, DynamicEnc,Ext) -> - gen_enc_components_call(Erule,TopType,{Root,ExtList,[]}, DynamicEnc,Ext); -gen_enc_components_call(Erule,TopType,CL={Root,ExtList,Root2}, DynamicEnc,Ext) -> - %% The type has extensionmarker - {Imm0,Rpos} = gen_enc_components_call1(Erule,TopType,Root++Root2,1, DynamicEnc,noext,[]), +gen_enc_components_call(Erule, TopType, {Root,ExtList}, DynamicEnc, Ext) -> + gen_enc_components_call(Erule, TopType, {Root,ExtList,[]}, DynamicEnc, Ext); +gen_enc_components_call(Erule, TopType, {R1,ExtList0,R2}=CL, DynamicEnc, Ext) -> + Root = R1 ++ R2, + Imm0 = gen_enc_components_call1(Erule, TopType, Root, DynamicEnc, noext), ExtImm = case Ext of {ext,_,ExtNum} when ExtNum > 0 -> [{var,"Extensions"}]; _ -> [] end, - %handle extensions {extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL), - NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen), - {Imm1,_} = gen_enc_components_call1(Erule,TopType,NewExtList,Rpos,DynamicEnc,Ext,[]), + ExtList1 = wrap_extensionAdditionGroups(ExtList0, ExtGroupPosLen), + ExtList = [mark_optional(C) || C <- ExtList1], + Imm1 = gen_enc_components_call1(Erule, TopType, ExtList, DynamicEnc, Ext), Imm0 ++ [ExtImm|Imm1]; -gen_enc_components_call(Erule,TopType, CompList, DynamicEnc, Ext) -> - %% The type has no extensionmarker - {Imm,_} = gen_enc_components_call1(Erule,TopType,CompList,1,DynamicEnc,Ext,[]), - Imm. - -gen_enc_components_call1(Erule,TopType, - [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest], - Tpos, - DynamicEnc, Ext, Acc) -> - - TermNo = - case C#'ComponentType'.textual_order of - undefined -> - Tpos; - CanonicalNum -> - CanonicalNum - end, - Val = make_var(val), - {Imm0,Element} = asn1ct_imm:enc_element(TermNo+1, Val), - Imm1 = gen_enc_line_imm(Erule, TopType, Cname, Type, Element, DynamicEnc, Ext), - Category = case {Prop,Ext} of - {'OPTIONAL',_} -> - optional; - {{'DEFAULT',DefVal},_} -> - {default,DefVal}; - {_,{ext,ExtPos,_}} when Tpos >= ExtPos -> - optional; - {_,_} -> - mandatory - end, - Imm2 = case Category of +gen_enc_components_call(Erule, TopType, CompList, DynamicEnc, Ext) -> + %% No extension marker. + gen_enc_components_call1(Erule, TopType, CompList, DynamicEnc, Ext). + +mark_optional(#'ComponentType'{prop=Prop0}=C) -> + Prop = case Prop0 of + mandatory -> 'OPTIONAL'; + 'OPTIONAL'=Keep -> Keep; + {'DEFAULT',_}=Keep -> Keep + end, + C#'ComponentType'{prop=Prop}; +mark_optional(Other) -> + Other. + +gen_enc_components_call1(Gen, TopType, [C|Rest], DynamicEnc, Ext) -> + #'ComponentType'{name=Cname,typespec=Type, + prop=Prop,textual_order=Num} = C, + {Imm0,Element} = enc_fetch_field(Gen, Num, Prop), + Imm1 = gen_enc_line_imm(Gen, TopType, Cname, Type, + Element, DynamicEnc, Ext), + Imm2 = case Prop of mandatory -> Imm1; - optional -> - asn1ct_imm:enc_absent(Element, [asn1_NOVALUE], Imm1); - {default,Def} -> + 'OPTIONAL' -> + enc_absent(Gen, Element, [asn1_NOVALUE], Imm1); + {'DEFAULT',Def} -> DefValues = def_values(Type, Def), - asn1ct_imm:enc_absent(Element, DefValues, Imm1) + enc_absent(Gen, Element, DefValues, Imm1) end, Imm = case Imm2 of [] -> []; _ -> Imm0 ++ Imm2 end, - gen_enc_components_call1(Erule, TopType, Rest, Tpos+1, DynamicEnc, Ext, [Imm|Acc]); -gen_enc_components_call1(_Erule,_TopType,[],Pos,_,_, Acc) -> - ImmList = lists:reverse(Acc), - {ImmList,Pos}. + [Imm|gen_enc_components_call1(Gen, TopType, Rest, DynamicEnc, Ext)]; +gen_enc_components_call1(_Gen, _TopType, [], _, _) -> + []. + +enc_absent(Gen, Var, Absent0, Imm) -> + Absent = translate_missing_value(Gen, Absent0), + asn1ct_imm:enc_absent(Var, Absent, Imm). + +translate_missing_value(#gen{pack=record}, Optionals) -> + Optionals; +translate_missing_value(#gen{pack=map}, Optionals) -> + case Optionals of + [asn1_NOVALUE|T] -> [?MISSING_IN_MAP|T]; + [asn1_DEFAULT|T] -> [?MISSING_IN_MAP|T]; + {call,_,_,_} -> Optionals + end. + +enc_fetch_field(#gen{pack=record}, Num, _Prop) -> + Val = make_var(val), + asn1ct_imm:enc_element(Num+1, Val); +enc_fetch_field(#gen{pack=map}, Num, _) -> + {[],{var,lists:concat(["Input@",Num])}}. def_values(#type{def=#'Externaltypereference'{module=Mod,type=Type}}, Def) -> #typedef{typespec=T} = asn1_db:dbget(Mod, Type), @@ -1115,27 +1259,31 @@ gen_dec_components_call(Erule, TopType, {Root,ExtList}, DecInfObj, Ext, NumberOfOptionals) -> gen_dec_components_call(Erule,TopType,{Root,ExtList,[]}, DecInfObj,Ext,NumberOfOptionals); -gen_dec_components_call(Erule,TopType,CL={Root1,ExtList,Root2}, - DecInfObj,Ext,NumberOfOptionals) -> +gen_dec_components_call(Gen, TopType, {Root1,ExtList,Root2}=CL, + DecInfObj, Ext, NumberOfOptionals) -> %% The type has extensionmarker OptTable = create_optionality_table(Root1++Root2), Init = {ignore,fun(_) -> {[],[]} end}, {EmitRoot,Tpos} = - gen_dec_comp_calls(Root1++Root2, Erule, TopType, OptTable, + gen_dec_comp_calls(Root1++Root2, Gen, TopType, OptTable, DecInfObj, noext, NumberOfOptionals, 1, []), - EmitGetExt = gen_dec_get_extension(Erule), + EmitGetExt = gen_dec_get_extension(Gen), {extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL), NewExtList = wrap_extensionAdditionGroups(ExtList, ExtGroupPosLen), - {EmitExts,_} = gen_dec_comp_calls(NewExtList, Erule, TopType, OptTable, + {EmitExts,_} = gen_dec_comp_calls(NewExtList, Gen, TopType, OptTable, DecInfObj, Ext, NumberOfOptionals, Tpos, []), NumExtsToSkip = ext_length(ExtList), Finish = fun(St) -> emit([{next,bytes},"= "]), - call(Erule, skipextensions, - [{curr,bytes},NumExtsToSkip+1,"Extensions"]), + Mod = case Gen of + #gen{erule=per,aligned=false} -> uper; + #gen{erule=per,aligned=true} -> per + end, + asn1ct_func:call(Mod, skipextensions, + [{curr,bytes},NumExtsToSkip+1,"Extensions"]), asn1ct_name:new(bytes), St end, @@ -1178,7 +1326,7 @@ gen_dec_comp_calls([C|Cs], Erule, TopType, OptTable, DecInfObj, gen_dec_comp_calls([], _, _, _, _, _, _, Tpos, Acc) -> {lists:append(lists:reverse(Acc)),Tpos}. -gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, +gen_dec_comp_call(Comp, Gen, TopType, Tpos, OptTable, DecInfObj, Ext, NumberOfOptionals) -> #'ComponentType'{typespec=Type,prop=Prop,textual_order=TextPos} = Comp, Pos = case Ext of @@ -1219,15 +1367,9 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, _ -> case Type of #type{def=#'SEQUENCE'{ - extaddgroup=Number1, - components=ExtGroupCompList1}} when is_integer(Number1)-> - fun(St) -> - emit(["{{_,"]), - emit_extaddgroupTerms(term,ExtGroupCompList1), - emit(["}"]), - emit([",",{next,bytes},"} = "]), - St - end; + extaddgroup=GroupNum, + components=CompList}} when is_integer(GroupNum)-> + dec_match_extadd_fun(Gen, CompList); _ -> fun(St) -> asn1ct_name:new(term), @@ -1237,9 +1379,9 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, end end end, - {Pre,Post} = comp_call_pre_post(Ext, Prop, Pos, Type, TextPos, + {Pre,Post} = comp_call_pre_post(Gen, Ext, Prop, Pos, Type, TextPos, OptTable, NumberOfOptionals, Ext), - Lines = gen_dec_seq_line_imm(Erule, TopType, Comp, Tpos, DecInfObj, Ext), + Lines = gen_dec_seq_line_imm(Gen, TopType, Comp, Tpos, DecInfObj, Ext), AdvBuffer = {ignore,fun(St) -> asn1ct_name:new(bytes), St @@ -1247,9 +1389,24 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, [{group,[{safe,Comment},{safe,Preamble}] ++ Pre ++ Lines ++ Post ++ [{safe,AdvBuffer}]}]. -comp_call_pre_post(noext, mandatory, _, _, _, _, _, _) -> +dec_match_extadd_fun(#gen{pack=record}, CompList) -> + fun(St) -> + emit(["{{_,"]), + emit_extaddgroupTerms(term, CompList), + emit(["}"]), + emit([",",{next,bytes},"} = "]), + St + end; +dec_match_extadd_fun(#gen{pack=map}, _CompList) -> + fun(St) -> + asn1ct_name:new(map), + emit(["{",{curr,map},",",{next,bytes},"} = "]), + St + end. + +comp_call_pre_post(_Gen, noext, mandatory, _, _, _, _, _, _) -> {[],[]}; -comp_call_pre_post(noext, Prop, _, Type, TextPos, +comp_call_pre_post(_Gen, noext, Prop, _, Type, TextPos, OptTable, NumOptionals, Ext) -> %% OPTIONAL or DEFAULT OptPos = get_optionality_pos(TextPos, OptTable), @@ -1273,32 +1430,53 @@ comp_call_pre_post(noext, Prop, _, Type, TextPos, "end"]), St end]}; -comp_call_pre_post({ext,_,_}, Prop, Pos, Type, _, _, _, Ext) -> +comp_call_pre_post(Gen, {ext,_,_}, Prop, Pos, Type, _, _, _, Ext) -> %% Extension {[fun(St) -> emit(["case Extensions of",nl, " <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]), St end], - [fun(St) -> - emit([";",nl, - "_ ->",nl, - "{"]), - case Type of - #type{def=#'SEQUENCE'{ - extaddgroup=Number2, - components=ExtGroupCompList2}} - when is_integer(Number2)-> - emit("{extAddGroup,"), - gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2), - emit("}"); - _ -> - gen_dec_component_no_val(Ext, Type, Prop) - end, - emit([",",{curr,bytes},"}",nl, - "end"]), - St - end]}. + [extadd_group_fun(Gen, Prop, Type, Ext)]}. + +extadd_group_fun(#gen{pack=record}, Prop, Type, Ext) -> + fun(St) -> + emit([";",nl, + "_ ->",nl, + "{"]), + case Type of + #type{def=#'SEQUENCE'{ + extaddgroup=Number2, + components=ExtGroupCompList2}} + when is_integer(Number2)-> + emit("{extAddGroup,"), + gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2), + emit("}"); + _ -> + gen_dec_component_no_val(Ext, Type, Prop) + end, + emit([",",{curr,bytes},"}",nl, + "end"]), + St + end; +extadd_group_fun(#gen{pack=map}, Prop, Type, Ext) -> + fun(St) -> + emit([";",nl, + "_ ->",nl, + "{"]), + case Type of + #type{def=#'SEQUENCE'{ + extaddgroup=Number2, + components=Comp}} + when is_integer(Number2)-> + dec_map_extaddgroup_no_val(Ext, Type, Comp); + _ -> + gen_dec_component_no_val(Ext, Type, Prop) + end, + emit([",",{curr,bytes},"}",nl, + "end"]), + St + end. is_mandatory_predef_tab_c(noext, mandatory, {"got objfun through args","ObjFun"}) -> @@ -1325,7 +1503,20 @@ gen_dec_component_no_val(_, _, 'OPTIONAL') -> emit({"asn1_NOVALUE"}); gen_dec_component_no_val({ext,_,_}, _, mandatory) -> emit({"asn1_NOVALUE"}). - + +dec_map_extaddgroup_no_val(Ext, Type, Comp) -> + L0 = [dec_map_extaddgroup_no_val_1(N, P, Ext, Type) || + #'ComponentType'{name=N,prop=P} <- Comp], + L = [E || E <- L0, E =/= []], + emit(["#{",lists:join(",", L),"}"]). + +dec_map_extaddgroup_no_val_1(Name, {'DEFAULT',DefVal0}, _Ext, Type) -> + DefVal = asn1ct_gen:conform_value(Type, DefVal0), + [Name,"=>",{asis,DefVal}]; +dec_map_extaddgroup_no_val_1(_Name, 'OPTIONAL', _, _) -> + []; +dec_map_extaddgroup_no_val_1(_Name, mandatory, {ext,_,_}, _) -> + []. gen_dec_choice_line(Erule, TopType, Comp, Pre) -> Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre), @@ -1461,29 +1652,29 @@ gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, Prop}],PrevSt} end end; -gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj) -> - case gen_dec_line_other(Erule, Atype, TopType, Comp) of +gen_dec_line_special(Gen, Atype, TopType, Comp, DecInfObj) -> + case gen_dec_line_other(Gen, Atype, TopType, Comp) of Fun when is_function(Fun, 1) -> fun({BytesVar,PrevSt}) -> Fun(BytesVar), - gen_dec_line_dec_inf(Comp, DecInfObj), + gen_dec_line_dec_inf(Gen,Comp, DecInfObj), {[],PrevSt} end; Imm0 -> {imm,Imm0, fun(Imm, {BytesVar,PrevSt}) -> asn1ct_imm:dec_code_gen(Imm, BytesVar), - gen_dec_line_dec_inf(Comp, DecInfObj), + gen_dec_line_dec_inf(Gen, Comp, DecInfObj), {[],PrevSt} end} end. -gen_dec_line_dec_inf(Comp, DecInfObj) -> +gen_dec_line_dec_inf(Gen, Comp, DecInfObj) -> #'ComponentType'{name=Cname} = Comp, case DecInfObj of {Cname,{_,_OSet,_UniqueFName,ValIndex}} -> Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)), - ValueMatch = value_match(ValIndex,Term), + ValueMatch = value_match(Gen, ValIndex,Term), emit([",",nl, "ObjFun = ",ValueMatch]); _ -> @@ -1705,20 +1896,17 @@ gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Pre) -> gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Pre); gen_dec_choice2(_, _, [], _, _, _) -> ok. -make_elements(I,Val,ExtCnames) -> - make_elements(I,Val,ExtCnames,[]). +get_input_vars(Val, I, N) -> + L = get_input_vars_1(Val, I, N), + lists:join(",", L). -make_elements(I,Val,[_ExtCname],Acc)-> % the last one, no comma needed - Element = make_element(I, Val), - make_elements(I+1,Val,[],[Element|Acc]); -make_elements(I,Val,[_ExtCname|Rest],Acc)-> - Element = make_element(I, Val), - make_elements(I+1,Val,Rest,[", ",Element|Acc]); -make_elements(_I,_,[],Acc) -> - lists:reverse(Acc). +get_input_vars_1(_Val, _I, 0) -> + []; +get_input_vars_1(Val, I, N) -> + [get_input_var(Val, I)|get_input_vars_1(Val, I+1, N-1)]. -make_element(I, Val) -> - lists:flatten(io_lib:format("element(~w, ~s)", [I,Val])). +get_input_var(Val, I) -> + lists:flatten(io_lib:format("element(~w, ~s)", [I+1,Val])). emit_extaddgroupTerms(VarSeries,[_]) -> asn1ct_name:new(VarSeries), @@ -1735,62 +1923,66 @@ flat_complist({Rl1,El,Rl2}) -> Rl1 ++ El ++ Rl2; flat_complist({Rl,El}) -> Rl ++ El; flat_complist(CompList) -> CompList. -%%wrap_compList({Root1,Ext,Root2}) -> -%% {Root1,wrap_extensionAdditionGroups(Ext),Root2}; -%%wrap_compList({Root1,Ext}) -> -%% {Root1,wrap_extensionAdditionGroups(Ext)}; -%%wrap_compList(CompList) -> -%% CompList. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Will convert all componentTypes following 'ExtensionAdditionGroup' +%% Convert all componentTypes following 'ExtensionAdditionGroup' %% up to the matching 'ExtensionAdditionGroupEnd' into one componentType -%% of type SEQUENCE with the componentTypes as components +%% of type SEQUENCE with the componentTypes as components. %% -wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen) -> - wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen,[],0,0). +wrap_extensionAdditionGroups(ExtCompList, ExtGroupPosLen) -> + wrap_eags(ExtCompList, ExtGroupPosLen, 0, 0). -wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest], - [{ActualPos,_,_}|ExtGroupPosLenRest],Acc,_ExtAddGroupDiff,ExtGroupNum) -> - {ExtGroupCompList,['ExtensionAdditionGroupEnd'|Rest2]} = +wrap_eags([{'ExtensionAdditionGroup',_Number}|T0], + [{ActualPos,_,_}|Gs], _ExtAddGroupDiff, ExtGroupNum) -> + {ExtGroupCompList,['ExtensionAdditionGroupEnd'|T]} = lists:splitwith(fun(#'ComponentType'{}) -> true; (_) -> false - end, - Rest), - wrap_extensionAdditionGroups(Rest2,ExtGroupPosLenRest, - [#'ComponentType'{ - name=list_to_atom("ExtAddGroup"++ - integer_to_list(ExtGroupNum+1)), - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=ExtGroupNum+1, - components=ExtGroupCompList}}, - textual_order = ActualPos, - prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1, - ExtGroupNum+1); -wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T], - ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) when is_integer(Tord) -> - wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H#'ComponentType'{ - textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff,ExtGroupNum); -wrap_extensionAdditionGroups([H|T],ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) -> - wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H|Acc],ExtAddGroupDiff,ExtGroupNum); -wrap_extensionAdditionGroups([],_,Acc,_,_) -> - lists:reverse(Acc). - -value_match(Index,Value) when is_atom(Value) -> - value_match(Index,atom_to_list(Value)); -value_match([],Value) -> + end, T0), + Name = list_to_atom(lists:concat(["ExtAddGroup",ExtGroupNum+1])), + Seq = #type{def=#'SEQUENCE'{extaddgroup=ExtGroupNum+1, + components=ExtGroupCompList}}, + Comp = #'ComponentType'{name=Name, + typespec=Seq, + textual_order=ActualPos, + prop='OPTIONAL'}, + [Comp|wrap_eags(T, Gs, length(ExtGroupCompList)-1, ExtGroupNum+1)]; +wrap_eags([#'ComponentType'{textual_order=Tord}=H|T], + ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum) + when is_integer(Tord) -> + Comp = H#'ComponentType'{textual_order=Tord - ExtAddGroupDiff}, + [Comp|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)]; +wrap_eags([H|T], ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum) -> + [H|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)]; +wrap_eags([], _, _, _) -> + []. + +value_match(#gen{pack=record}, VIs, Value) -> + value_match_rec(VIs, Value); +value_match(#gen{pack=map}, VIs, Value) -> + value_match_map(VIs, Value). + +value_match_rec([], Value) -> Value; -value_match([{VI,_}|VIs],Value) -> - value_match1(Value,VIs,lists:concat(["element(",VI,","]),1). -value_match1(Value,[],Acc,Depth) -> - Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")")); -value_match1(Value,[{VI,_}|VIs],Acc,Depth) -> - value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1). - -enc_dig_out_value([], Value) -> +value_match_rec([{VI,_}|VIs], Value0) -> + Value = value_match_rec(VIs, Value0), + lists:concat(["element(",VI,", ",Value,")"]). + +value_match_map([], Value) -> + Value; +value_match_map([{_,Name}|VIs], Value0) -> + Value = value_match_map(VIs, Value0), + lists:concat(["maps:get(",Name,", ",Value,")"]). + +enc_dig_out_value(_Gen, [], Value) -> {[],Value}; -enc_dig_out_value([{N,_}|T], Value) -> - {Imm0,Dst0} = enc_dig_out_value(T, Value), +enc_dig_out_value(#gen{pack=record}=Gen, [{N,_}|T], Value) -> + {Imm0,Dst0} = enc_dig_out_value(Gen, T, Value), {Imm,Dst} = asn1ct_imm:enc_element(N, Dst0), + {Imm0++Imm,Dst}; +enc_dig_out_value(#gen{pack=map}, [{N,'ASN1_top'}], _Value) -> + {[],{var,lists:concat(["Input@",N-1])}}; +enc_dig_out_value(#gen{pack=map}=Gen, [{_,Name}|T], Value) -> + {Imm0,Dst0} = enc_dig_out_value(Gen, T, Value), + {Imm,Dst} = asn1ct_imm:enc_maps_get(Name, Dst0), {Imm0++Imm,Dst}. make_var(Base) -> diff --git a/lib/asn1/src/asn1ct_eval_ext.funcs b/lib/asn1/src/asn1ct_eval_ext.funcs index 5761901f89..01c67e7b5a 100644 --- a/lib/asn1/src/asn1ct_eval_ext.funcs +++ b/lib/asn1/src/asn1ct_eval_ext.funcs @@ -1 +1,2 @@ {ext,transform_to_EXTERNAL1994,1}. +{ext,transform_to_EXTERNAL1994_maps,1}. diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index bfaffa13bf..9943bd056a 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -34,10 +34,10 @@ insert_once/2, ct_gen_module/1, index2suffix/1, - get_record_name_prefix/0, + get_record_name_prefix/1, conform_value/2, named_bitstring_value/2]). --export([pgen/5, +-export([pgen/3, mk_var/1, un_hyphen_var/1]). -export([gen_encode_constructed/4, @@ -45,23 +45,19 @@ -define(SUPPRESSION_FUNC, 'dialyzer-suppressions'). + %% pgen(Outfile, Erules, Module, TypeOrVal, Options) -%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module -%% .hrl file is only generated if necessary -%% Erules = per | ber -%% Module = atom() -%% TypeOrVal = {TypeList,ValueList} -%% TypeList = ValueList = [atom()] -%% Options = [Options] from asn1ct:compile() - -pgen(OutFile,Erules,Module,TypeOrVal,Options) -> - pgen_module(OutFile,Erules,Module,TypeOrVal,Options,true). - - -pgen_module(OutFile,Erules,Module, - TypeOrVal = {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets}, - Options,Indent) -> - N2nConvEnums = [CName|| {n2n,CName} <- get(encoding_options)], +%% Generate Erlang module (.erl) and (.hrl) file corresponding to +%% an ASN.1 module. The .hrl file is only generated if necessary. + +-spec pgen(Outfile, Gen, Code) -> 'ok' when + Outfile :: any(), + Gen :: #gen{}, + Code :: #abst{}. + +pgen(OutFile, #gen{options=Options}=Gen, Code) -> + #abst{name=Module,types=Types} = Code, + N2nConvEnums = [CName|| {n2n,CName} <- Options], case N2nConvEnums -- Types of [] -> ok; @@ -69,30 +65,30 @@ pgen_module(OutFile,Erules,Module, exit({"Non existing ENUMERATION types used in n2n option", UnmatchedTypes}) end, - put(outfile,OutFile), - HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent), + put(outfile, OutFile), + put(currmod, Module), + HrlGenerated = pgen_hrl(Gen, Code), asn1ct_name:start(), ErlFile = lists:concat([OutFile,".erl"]), _ = open_output_file(ErlFile), asn1ct_func:start_link(), - gen_head(Erules,Module,HrlGenerated), - pgen_exports(Erules,Module,TypeOrVal), - pgen_dispatcher(Erules,Module,TypeOrVal), + gen_head(Gen, Module, HrlGenerated), + pgen_exports(Gen, Code), + pgen_dispatcher(Gen, Types), pgen_info(), - pgen_typeorval(Erules,Module,N2nConvEnums,TypeOrVal), - pgen_partial_incomplete_decode(Erules), -% gen_vars(asn1_db:mod_to_vars(Module)), -% gen_tag_table(AllTypes), + pgen_typeorval(Gen, N2nConvEnums, Code), + pgen_partial_incomplete_decode(Gen), emit([nl, "%%%",nl, "%%% Run-time functions.",nl, "%%%",nl]), - dialyzer_suppressions(Erules), + dialyzer_suppressions(Gen), Fd = get(gen_file_out), asn1ct_func:generate(Fd), close_output_file(), _ = erase(outfile), - asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options). + asn1ct:verbose("--~p--~n", [{generated,ErlFile}], Gen), + ok. dialyzer_suppressions(Erules) -> emit([nl, @@ -100,20 +96,27 @@ dialyzer_suppressions(Erules) -> Rtmod = ct_gen_module(Erules), Rtmod:dialyzer_suppressions(Erules). -pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) -> +pgen_typeorval(Erules, N2nConvEnums, Code) -> + #abst{name=Module,types=Types,values=Values, + objects=Objects,objsets=ObjectSets} = Code, Rtmod = ct_gen_module(Erules), pgen_types(Rtmod,Erules,N2nConvEnums,Module,Types), - pgen_values(Erules,Module,Values), + pgen_values(Values, Module), pgen_objects(Rtmod,Erules,Module,Objects), pgen_objectsets(Rtmod,Erules,Module,ObjectSets), pgen_partial_decode(Rtmod,Erules,Module). -pgen_values(_,_,[]) -> - true; -pgen_values(Erules,Module,[H|T]) -> - Valuedef = asn1_db:dbget(Module,H), - gen_value(Valuedef), - pgen_values(Erules,Module,T). +%% Generate a function 'V'/0 for each Value V defined in the ASN.1 module. +%% The function returns the value in an Erlang representation which can be +%% used as input to the runtime encode functions. + +pgen_values([H|T], Module) -> + #valuedef{name=Name,value=Value} = asn1_db:dbget(Module, H), + emit([{asis,Name},"() ->",nl, + {asis,Value},".",nl,nl]), + pgen_values(T, Module); +pgen_values([], _) -> + ok. pgen_types(_, _, _, _, []) -> true; @@ -181,10 +184,10 @@ pgen_objectsets(Rtmod,Erules,Module,[H|T]) -> Rtmod:gen_objectset_code(Erules,TypeDef), pgen_objectsets(Rtmod,Erules,Module,T). -pgen_partial_decode(Rtmod,Erule,Module) when Erule == ber -> - pgen_partial_inc_dec(Rtmod,Erule,Module), - pgen_partial_dec(Rtmod,Erule,Module); -pgen_partial_decode(_,_,_) -> +pgen_partial_decode(Rtmod, #gen{erule=ber}=Gen, Module) -> + pgen_partial_inc_dec(Rtmod, Gen, Module), + pgen_partial_dec(Rtmod, Gen, Module); +pgen_partial_decode(_, _, _) -> ok. pgen_partial_inc_dec(Rtmod,Erules,Module) -> @@ -225,7 +228,7 @@ pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) -> pgen_partial_inc_dec1(_,_,_,[]) -> ok. -gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber -> +gen_partial_inc_dec_refed_funcs(Rtmod, #gen{erule=ber}=Gen) -> case asn1ct:next_refed_func() of [] -> ok; @@ -233,19 +236,17 @@ gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber -> TypeDef = asn1_db:dbget(M,Name), asn1ct:update_gen_state(namelist,Pattern), asn1ct:set_current_sindex(Sindex), - Rtmod:gen_inc_decode(Erule,TypeDef), - gen_dec_part_inner_constr(Rtmod,Erule,TypeDef,[Name]), - gen_partial_inc_dec_refed_funcs(Rtmod,Erule); + Rtmod:gen_inc_decode(Gen, TypeDef), + gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, [Name]), + gen_partial_inc_dec_refed_funcs(Rtmod, Gen); {Name,Sindex,Pattern,Type} -> TypeDef=#typedef{name=asn1ct_gen:list2name(Name),typespec=Type}, asn1ct:update_gen_state(namelist,Pattern), asn1ct:set_current_sindex(Sindex), - Rtmod:gen_inc_decode(Erule,TypeDef), - gen_dec_part_inner_constr(Rtmod,Erule,TypeDef,Name), - gen_partial_inc_dec_refed_funcs(Rtmod,Erule) - end; -gen_partial_inc_dec_refed_funcs(_,_) -> - ok. + Rtmod:gen_inc_decode(Gen, TypeDef), + gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, Name), + gen_partial_inc_dec_refed_funcs(Rtmod, Gen) + end. pgen_partial_dec(_Rtmod,Erules,_Module) -> Type_pattern = asn1ct:get_gen_state_field(type_pattern), @@ -254,16 +255,16 @@ pgen_partial_dec(_Rtmod,Erules,_Module) -> pgen_partial_types(Erules,Type_pattern), ok. -pgen_partial_types(Erules,Type_pattern) -> - % until this functionality works on all back-ends - Options = get(encoding_options), - case lists:member(asn1config,Options) of +pgen_partial_types(#gen{options=Options}=Gen, TypePattern) -> + %% until this functionality works on all back-ends + case lists:member(asn1config, Options) of true -> - pgen_partial_types1(Erules,Type_pattern); - _ -> ok + pgen_partial_types1(Gen, TypePattern); + false -> + ok end. - + pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) -> % emit([FuncName,"(Bytes) ->",nl]), CurrMod = get(currmod), @@ -441,7 +442,8 @@ pgen_partial_incomplete_decode(Erule) -> _ -> ok end. -pgen_partial_incomplete_decode1(ber) -> + +pgen_partial_incomplete_decode1(#gen{erule=ber}) -> case asn1ct:read_config_data(partial_incomplete_decode) of undefined -> ok; @@ -451,7 +453,7 @@ pgen_partial_incomplete_decode1(ber) -> GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs), % io:format("GeneratedFs :~n~p~n",[GeneratedFs]), gen_part_decode_funcs(GeneratedFs,0); -pgen_partial_incomplete_decode1(_) -> ok. +pgen_partial_incomplete_decode1(#gen{}) -> ok. emit_partial_incomplete_decode({FuncName,TopType,Pattern}) -> TypePattern = asn1ct:get_gen_state_field(inc_type_pattern), @@ -578,18 +580,6 @@ un_hyphen_var([H|T]) -> un_hyphen_var([]) -> []. -%% Generate value functions *************** -%% **************************************** -%% Generates a function 'V'/0 for each Value V defined in the ASN.1 module -%% the function returns the value in an Erlang representation which can be -%% used as input to the runtime encode functions - -gen_value(Value) when is_record(Value,valuedef) -> -%% io:format(" ~w ",[Value#valuedef.name]), - emit({"'",Value#valuedef.name,"'() ->",nl}), - V = Value#valuedef.value, - emit([{asis,V},".",nl,nl]). - gen_encode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) -> Rtmod = ct_constructed_module(Erules), case InnerType of @@ -654,78 +644,32 @@ gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) -> gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec). -pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) -> - emit(["-export([encoding_rule/0,bit_string_format/0,",nl, +pgen_exports(#gen{options=Options}=Gen, Code) -> + #abst{types=Types,values=Values,objects=Objects,objsets=ObjectSets} = 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 Types of - [] -> ok; - _ -> - emit({"-export([",nl}), - case Erules of - ber -> - gen_exports1(Types,"enc_",2); - _ -> - gen_exports1(Types,"enc_",1) - end, - emit({"-export([",nl}), - case Erules of - ber -> - gen_exports1(Types, "dec_", 2); - _ -> - gen_exports1(Types, "dec_", 1) - end - end, - case [X || {n2n,X} <- get(encoding_options)] of - [] -> ok; - A2nNames -> - emit({"-export([",nl}), - gen_exports1(A2nNames,"name2num_",1), - emit({"-export([",nl}), - gen_exports1(A2nNames,"num2name_",1) - end, - case Values of - [] -> ok; - _ -> - emit({"-export([",nl}), - gen_exports1(Values,"",0) + case Gen of + #gen{erule=ber} -> + gen_exports(Types, "enc_", 2), + gen_exports(Types, "dec_", 2), + gen_exports(Objects, "enc_", 3), + gen_exports(Objects, "dec_", 3), + gen_exports(ObjectSets, "getenc_", 1), + gen_exports(ObjectSets, "getdec_", 1); + #gen{erule=per} -> + gen_exports(Types, "enc_", 1), + gen_exports(Types, "dec_", 1) end, - case Objects of - [] -> ok; - _ -> - case erule(Erules) of - per -> - ok; - ber -> - emit({"-export([",nl}), - gen_exports1(Objects,"enc_",3), - emit({"-export([",nl}), - gen_exports1(Objects,"dec_",3) - end - end, - case ObjectSets of - [] -> ok; - _ -> - case erule(Erules) of - per -> - ok; - ber -> - emit({"-export([",nl}), - gen_exports1(ObjectSets, "getenc_",1), - emit({"-export([",nl}), - gen_exports1(ObjectSets, "getdec_",1) - end - end, - emit({"-export([info/0]).",nl}), - gen_partial_inc_decode_exports(), - gen_selected_decode_exports(), - emit({nl,nl}). -gen_exports1([F1,F2|T],Prefix,Arity) -> - emit({"'",Prefix,F1,"'/",Arity,com,nl}), - gen_exports1([F2|T],Prefix,Arity); -gen_exports1([Flast|_T],Prefix,Arity) -> - emit({"'",Prefix,Flast,"'/",Arity,nl,"]).",nl,nl}). + A2nNames = [X || {n2n,X} <- Options], + gen_exports(A2nNames, "name2num_", 1), + gen_exports(A2nNames, "num2name_", 1), + + gen_exports(Values, "", 0), + emit(["-export([info/0]).",nl,nl]), + gen_partial_inc_decode_exports(), + gen_selected_decode_exports(). gen_partial_inc_decode_exports() -> case {asn1ct:read_config_data(partial_incomplete_decode), @@ -734,66 +678,54 @@ gen_partial_inc_decode_exports() -> ok; {_,undefined} -> ok; - {Data,_} -> - gen_partial_inc_decode_exports(Data), - emit(["-export([decode_part/2]).",nl]) + {Data0,_} -> + Data = [Name || {Name,_,_} <- Data0], + gen_exports(Data, "", 1), + emit(["-export([decode_part/2]).",nl,nl]) end. -gen_partial_inc_decode_exports([]) -> - ok; -gen_partial_inc_decode_exports([{Name,_,_}|Rest]) -> - emit(["-export([",Name,"/1"]), - gen_partial_inc_decode_exports1(Rest); -gen_partial_inc_decode_exports([_|Rest]) -> - gen_partial_inc_decode_exports(Rest). - -gen_partial_inc_decode_exports1([]) -> - emit(["]).",nl]); -gen_partial_inc_decode_exports1([{Name,_,_}|Rest]) -> - emit([", ",Name,"/1"]), - gen_partial_inc_decode_exports1(Rest); -gen_partial_inc_decode_exports1([_|Rest]) -> - gen_partial_inc_decode_exports1(Rest). gen_selected_decode_exports() -> case asn1ct:get_gen_state_field(type_pattern) of undefined -> ok; - L -> - gen_selected_decode_exports(L) + Data0 -> + Data = [Name || {Name,_} <- Data0], + gen_exports(Data, "", 1) end. -gen_selected_decode_exports([]) -> +gen_exports([], _Prefix, _Arity) -> ok; -gen_selected_decode_exports([{FuncName,_}|Rest]) -> - emit(["-export([",FuncName,"/1"]), - gen_selected_decode_exports1(Rest). -gen_selected_decode_exports1([]) -> - emit(["]).",nl,nl]); -gen_selected_decode_exports1([{FuncName,_}|Rest]) -> - emit([",",nl," ",FuncName,"/1"]), - gen_selected_decode_exports1(Rest). - -pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) -> +gen_exports([_|_]=L0, Prefix, Arity) -> + FF = fun(F0) -> + F = list_to_atom(lists:concat([Prefix,F0])), + [{asis,F},"/",Arity] + end, + L = lists:join(",\n", [FF(F) || F <- L0]), + emit(["-export([",nl, + L,nl, + "]).",nl,nl]). + +pgen_dispatcher(Erules, []) -> gen_info_functions(Erules); -pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) -> +pgen_dispatcher(Gen, Types) -> emit(["-export([encode/2,decode/2]).",nl,nl]), - gen_info_functions(Erules), + gen_info_functions(Gen), - Options = get(encoding_options), + Options = Gen#gen.options, NoFinalPadding = lists:member(no_final_padding, Options), NoOkWrapper = proplists:get_bool(no_ok_wrapper, Options), - Call = case Erules of - per -> - asn1ct_func:need({Erules,complete,1}), + Call = case Gen of + #gen{erule=per,aligned=true} -> + asn1ct_func:need({per,complete,1}), "complete(encode_disp(Type, Data))"; - ber -> + #gen{erule=ber} -> "iolist_to_binary(element(1, encode_disp(Type, Data)))"; - uper when NoFinalPadding == true -> - asn1ct_func:need({Erules,complete_NFP,1}), + #gen{erule=per,aligned=false} when NoFinalPadding -> + asn1ct_func:need({uper,complete_NFP,1}), "complete_NFP(encode_disp(Type, Data))"; - uper -> - asn1ct_func:need({Erules,complete,1}), + #gen{erule=per,aligned=false} -> + asn1ct_func:need({uper,complete,1}), "complete(encode_disp(Type, Data))" end, @@ -809,36 +741,36 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) -> end, emit([nl,nl]), - Return_rest = proplists:get_bool(undec_rest, Options), - Data = case {Erules,Return_rest} of - {ber,true} -> "Data0"; - _ -> "Data" + ReturnRest = proplists:get_bool(undec_rest, Gen#gen.options), + Data = case Gen#gen.erule =:= ber andalso ReturnRest of + true -> "Data0"; + false -> "Data" end, - emit(["decode(Type,",Data,") ->",nl]), + emit(["decode(Type, ",Data,") ->",nl]), DecWrap = - case {Erules,Return_rest} of - {ber,false} -> + case {Gen,ReturnRest} of + {#gen{erule=ber},false} -> asn1ct_func:need({ber,ber_decode_nif,1}), "element(1, ber_decode_nif(Data))"; - {ber,true} -> + {#gen{erule=ber},true} -> asn1ct_func:need({ber,ber_decode_nif,1}), emit(["{Data,Rest} = ber_decode_nif(Data0),",nl]), "Data"; - _ -> + {_,_} -> "Data" end, emit([case NoOkWrapper of false -> "try"; true -> "case" end, " decode_disp(Type, ",DecWrap,") of",nl]), - case erule(Erules) of - ber -> + case Gen of + #gen{erule=ber} -> emit([" Result ->",nl]); - per -> + #gen{erule=per} -> emit([" {Result,Rest} ->",nl]) end, - case Return_rest of + case ReturnRest of false -> result_line(NoOkWrapper, ["Result"]); true -> result_line(NoOkWrapper, ["Result","Rest"]) end, @@ -849,18 +781,11 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) -> emit([nl,"end.",nl,nl]) end, - gen_decode_partial_incomplete(Erules), + gen_decode_partial_incomplete(Gen), + gen_partial_inc_dispatcher(Gen), - case Erules of - ber -> - gen_dispatcher(Types,"encode_disp","enc_",""), - gen_dispatcher(Types,"decode_disp","dec_",""), - gen_partial_inc_dispatcher(); - _PerOrPer_bin -> - gen_dispatcher(Types,"encode_disp","enc_",""), - gen_dispatcher(Types,"decode_disp","dec_","") - end, - emit([nl,nl]). + gen_dispatcher(Types, "encode_disp", "enc_"), + gen_dispatcher(Types, "decode_disp", "dec_"). result_line(NoOkWrapper, Items) -> S = [" "|case NoOkWrapper of @@ -885,15 +810,26 @@ try_catch() -> " end",nl, "end."]. -gen_info_functions(Erules) -> +gen_info_functions(Gen) -> + Erule = case Gen of + #gen{erule=ber} -> ber; + #gen{erule=per,aligned=false} -> uper; + #gen{erule=per,aligned=true} -> per + end, + Maps = case Gen of + #gen{pack=record} -> false; + #gen{pack=map} -> true + end, emit(["encoding_rule() -> ", - {asis,Erules},".",nl,nl, + {asis,Erule},".",nl,nl, + "maps() -> ", + {asis,Maps},".",nl,nl, "bit_string_format() -> ", {asis,asn1ct:get_bit_string_format()},".",nl,nl, "legacy_erlang_types() -> ", {asis,asn1ct:use_legacy_types()},".",nl,nl]). -gen_decode_partial_incomplete(ber) -> +gen_decode_partial_incomplete(#gen{erule=ber}) -> case {asn1ct:read_config_data(partial_incomplete_decode), asn1ct:get_gen_state_field(inc_type_pattern)} of {undefined,_} -> @@ -931,10 +867,10 @@ gen_decode_partial_incomplete(ber) -> EmitCaseClauses(), emit([".",nl,nl]) end; -gen_decode_partial_incomplete(_Erule) -> +gen_decode_partial_incomplete(#gen{}) -> ok. -gen_partial_inc_dispatcher() -> +gen_partial_inc_dispatcher(#gen{erule=ber}) -> case {asn1ct:read_config_data(partial_incomplete_decode), asn1ct:get_gen_state_field(inc_type_pattern)} of {undefined,_} -> @@ -944,7 +880,9 @@ gen_partial_inc_dispatcher() -> {Data1,Data2} -> % io:format("partial_incomplete_decode: ~p~ninc_type_pattern: ~p~n",[Data,Data2]), gen_partial_inc_dispatcher(Data1, Data2, "") - end. + end; +gen_partial_inc_dispatcher(#gen{}) -> + ok. gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest], TypePattern, Sep) -> TPattern = @@ -968,12 +906,18 @@ gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest], TypePattern, Sep) gen_partial_inc_dispatcher([], _, _) -> emit([".",nl]). -gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) -> - emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]), - gen_dispatcher([F2|T],FuncName,Prefix,ExtraArg); -gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) -> - emit([FuncName,"('",Flast,"',Data) -> '",Prefix,Flast,"'(Data",ExtraArg,")",";",nl]), - emit([FuncName,"(","Type",",_Data) -> exit({error,{asn1,{undefined_type,Type}}}).",nl,nl,nl]). +gen_dispatcher(L, DispFunc, Prefix) -> + gen_dispatcher_1(L, DispFunc, Prefix), + emit([DispFunc,"(","Type",", _Data) ->" + " exit({error,{asn1,{undefined_type,Type}}}).",nl,nl]). + +gen_dispatcher_1([F|T], FuncName, Prefix) -> + Func = list_to_atom(lists:concat([Prefix,F])), + emit([FuncName,"(",{asis,F},", Data) -> ", + {asis,Func},"(Data)",";",nl]), + gen_dispatcher_1(T, FuncName, Prefix); +gen_dispatcher_1([], _, _) -> + ok. pgen_info() -> emit(["info() ->",nl, @@ -1092,22 +1036,21 @@ open_output_file(F) -> close_output_file() -> ok = file:close(erase(gen_file_out)). -pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> - put(currmod,Module), - {Types,Values,Ptypes,_,_,_} = TypeOrVal, +pgen_hrl(#gen{pack=record}=Gen, Code) -> + #abst{name=Module,types=Types,values=Values,ptypes=Ptypes} = Code, Ret = - case pgen_hrltypes(Erules,Module,Ptypes++Types,0) of + case pgen_hrltypes(Gen, Module, Ptypes++Types, 0) of 0 -> case Values of [] -> 0; _ -> - open_hrl(get(outfile),get(currmod)), - pgen_macros(Erules,Module,Values), + open_hrl(get(outfile), Module), + pgen_macros(Gen, Module, Values), 1 end; X -> - pgen_macros(Erules,Module,Values), + pgen_macros(Gen, Module, Values), X end, case Ret of @@ -1119,62 +1062,61 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> close_output_file(), asn1ct:verbose("--~p--~n", [{generated,lists:concat([get(outfile),".hrl"])}], - Options), + Gen), Y - end. + end; +pgen_hrl(#gen{pack=map}, _) -> + 0. pgen_macros(_,_,[]) -> true; -pgen_macros(Erules,Module,[H|T]) -> - Valuedef = asn1_db:dbget(Module,H), - gen_macro(Valuedef), - pgen_macros(Erules,Module,T). +pgen_macros(Gen, Module, [H|T]) -> + Valuedef = asn1_db:dbget(Module, H), + gen_macro(Gen, Valuedef), + pgen_macros(Gen, Module, T). pgen_hrltypes(_,_,[],NumRecords) -> NumRecords; -pgen_hrltypes(Erules,Module,[H|T],NumRecords) -> -% io:format("records = ~p~n",NumRecords), - Typedef = asn1_db:dbget(Module,H), - AddNumRecords = gen_record(Typedef,NumRecords), - pgen_hrltypes(Erules,Module,T,NumRecords+AddNumRecords). +pgen_hrltypes(Gen, Module, [H|T], NumRecords) -> + Typedef = asn1_db:dbget(Module, H), + AddNumRecords = gen_record(Gen, Typedef, NumRecords), + pgen_hrltypes(Gen, Module, T, NumRecords+AddNumRecords). %% Generates a macro for value Value defined in the ASN.1 module -gen_macro(Value) when is_record(Value,valuedef) -> - Prefix = get_macro_name_prefix(), - emit({"-define('",Prefix,Value#valuedef.name,"', ", - {asis,Value#valuedef.value},").",nl}). +gen_macro(Gen, #valuedef{name=Name,value=Value}) -> + Prefix = get_macro_name_prefix(Gen), + emit(["-define('",Prefix,Name,"', ",{asis,Value},").",nl]). %% Generate record functions ************** %% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1 %% module. If no SEQUENCE or SET is found there is no .hrl file generated -gen_record(Tdef,NumRecords) when is_record(Tdef,typedef) -> +gen_record(Gen, #typedef{}=Tdef, NumRecords) -> Name = [Tdef#typedef.name], Type = Tdef#typedef.typespec, - gen_record(type,Name,Type,NumRecords); - -gen_record(Tdef,NumRecords) when is_record(Tdef,ptypedef) -> + gen_record(Gen, type, Name, Type, NumRecords); +gen_record(Gen, #ptypedef{}=Tdef, NumRecords) -> Name = [Tdef#ptypedef.name], Type = Tdef#ptypedef.typespec, - gen_record(ptype,Name,Type,NumRecords). - -gen_record(TorPtype,Name,[#'ComponentType'{name=Cname,typespec=Type}|T],Num) -> - Num2 = gen_record(TorPtype,[Cname|Name],Type,Num), - gen_record(TorPtype,Name,T,Num2); -gen_record(TorPtype,Name,{Clist1,Clist2},Num) + gen_record(Gen, ptype, Name, Type, NumRecords). + +gen_record(Gen, TorPtype, Name, + [#'ComponentType'{name=Cname,typespec=Type}|T], Num) -> + Num2 = gen_record(Gen, TorPtype, [Cname|Name], Type, Num), + gen_record(Gen, TorPtype, Name, T, Num2); +gen_record(Gen, TorPtype, Name, {Clist1,Clist2}, Num) when is_list(Clist1), is_list(Clist2) -> - gen_record(TorPtype,Name,Clist1++Clist2,Num); -gen_record(TorPtype,Name,{Clist1,EClist,Clist2},Num) + gen_record(Gen, TorPtype, Name, Clist1++Clist2, Num); +gen_record(Gen, TorPtype, Name, {Clist1,EClist,Clist2}, Num) when is_list(Clist1), is_list(EClist), is_list(Clist2) -> - gen_record(TorPtype,Name,Clist1++EClist++Clist2,Num); -gen_record(TorPtype,Name,[_|T],Num) -> % skip EXTENSIONMARK - gen_record(TorPtype,Name,T,Num); -gen_record(_TorPtype,_Name,[],Num) -> + gen_record(Gen, TorPtype, Name, Clist1++EClist++Clist2, Num); +gen_record(Gen, TorPtype, Name, [_|T], Num) -> % skip EXTENSIONMARK + gen_record(Gen, TorPtype, Name, T, Num); +gen_record(_Gen, _TorPtype, _Name, [], Num) -> Num; - -gen_record(TorPtype,Name,Type,Num) when is_record(Type,type) -> +gen_record(Gen, TorPtype, Name, #type{}=Type, Num) -> Def = Type#type.def, Rec = case Def of Seq when is_record(Seq,'SEQUENCE') -> @@ -1209,127 +1151,103 @@ gen_record(TorPtype,Name,Type,Num) when is_record(Type,type) -> 0 -> open_hrl(get(outfile),get(currmod)); _ -> true end, - Prefix = get_record_name_prefix(), - emit({"-record('",Prefix,list2name(Name),"',{",nl}), - RootList = case CompList of - _ when is_list(CompList) -> - CompList; - {Rl,_} -> Rl; - {Rl1,_Ext,_Rl2} -> Rl1 - end, - gen_record2(Name,'SEQUENCE',RootList), - NewCompList = + do_gen_record(Gen, Name, CompList), + NewCompList = case CompList of {CompList1,[]} -> - emit({"}). % with extension mark",nl,nl}), CompList1; {Tr,ExtensionList2} -> - case Tr of - [] -> true; - _ -> emit({",",nl}) - end, - emit({"%% with extensions",nl}), - gen_record2(Name, 'SEQUENCE', ExtensionList2, - "", ext), - emit({"}).",nl,nl}), Tr ++ ExtensionList2; {Rootl1,Extl,Rootl2} -> - case Rootl1 =/= [] andalso Extl++Rootl2 =/= [] of - true -> emit([com]); - false -> ok - end, - case Rootl1 of - [_|_] -> emit([nl]); - [] -> ok - end, - emit(["%% with extensions",nl]), - gen_record2(Name,'SEQUENCE',Extl,"",ext), - case Extl =/= [] andalso Rootl2 =/= [] of - true -> emit([com]); - false -> ok - end, - case Extl of - [_|_] -> emit([nl]); - [] -> ok - end, - emit(["%% end of extensions",nl]), - gen_record2(Name,'SEQUENCE',Rootl2,"",noext), - emit(["}).",nl,nl]), Rootl1++Extl++Rootl2; - _ -> - emit({"}).",nl,nl}), + _ -> CompList end, - gen_record(TorPtype,Name,NewCompList,Num+1); + gen_record(Gen, TorPtype, Name, NewCompList, Num+1); {inner,{'CHOICE', CompList}} -> - gen_record(TorPtype,Name,CompList,Num); + gen_record(Gen, TorPtype, Name, CompList, Num); {NewName,{_, CompList}} -> - gen_record(TorPtype,NewName,CompList,Num) + gen_record(Gen, TorPtype, NewName, CompList, Num) end; -gen_record(_,_,_,NumRecords) -> % skip CLASS etc for now. +gen_record(_, _, _, _, NumRecords) -> % skip CLASS etc for now. NumRecords. - -gen_head(Erules,Mod,Hrl) -> - Options = get(encoding_options), - case Erules of - per -> - emit(["%% Generated by the Erlang ASN.1 PER-" - "compiler version, utilizing bit-syntax:", - asn1ct:vsn(),nl]); - ber -> - emit(["%% Generated by the Erlang ASN.1 BER_V2-" - "compiler version, utilizing bit-syntax:", - asn1ct:vsn(),nl]); - uper -> - emit(["%% Generated by the Erlang ASN.1 UNALIGNED" - " PER-compiler version, utilizing bit-syntax:", - asn1ct:vsn(),nl]) + +do_gen_record(Gen, Name, CL0) -> + CL = case CL0 of + {Root,[]} -> + Root ++ [{comment,"with extension mark"}]; + {Root,Ext} -> + Root ++ [{comment,"with exensions"}] ++ + only_components(Ext); + {Root1,Ext,Root2} -> + Root1 ++ [{comment,"with exensions"}] ++ + only_components(Ext) ++ + [{comment,"end of extensions"}] ++ Root2; + _ when is_list(CL0) -> + CL0 + end, + Prefix = get_record_name_prefix(Gen), + emit(["-record('",Prefix,list2name(Name),"', {"] ++ + do_gen_record_1(CL) ++ + [nl,"}).",nl,nl]). + +only_components(CL) -> + [C || #'ComponentType'{}=C <- CL]. + +do_gen_record_1([#'ComponentType'{name=Name,prop=Prop}|T]) -> + Val = case Prop of + 'OPTIONAL' -> + " = asn1_NOVALUE"; + {'DEFAULT',_} -> + " = asn1_DEFAULT"; + _ -> + [] + end, + Com = case needs_trailing_comma(T) of + true -> [com]; + false -> [] end, - emit({"%% Purpose: encoder and decoder to the types in mod ",Mod,nl,nl}), - emit({"-module('",Mod,"').",nl}), - put(currmod,Mod), - emit({"-compile(nowarn_unused_vars).",nl}), - emit({"-dialyzer(no_improper_lists).",nl}), + [nl," ",{asis,Name},Val,Com|do_gen_record_1(T)]; +do_gen_record_1([{comment,Text}|T]) -> + [nl," %% ",Text|do_gen_record_1(T)]; +do_gen_record_1([]) -> + []. + +needs_trailing_comma([#'ComponentType'{}|_]) -> true; +needs_trailing_comma([_|T]) -> needs_trailing_comma(T); +needs_trailing_comma([]) -> false. + +gen_head(#gen{options=Options}=Gen, Mod, Hrl) -> + Name = case Gen of + #gen{erule=per,aligned=false} -> + "PER (unaligned)"; + #gen{erule=per,aligned=true} -> + "PER (aligned)"; + #gen{erule=ber} -> + "BER" + end, + emit(["%% Generated by the Erlang ASN.1 ",Name, + " compiler. Version: ",asn1ct:vsn(),nl, + "%% Purpose: Encoding and decoding of the types in ", + Mod,".",nl,nl, + "-module('",Mod,"').",nl, + "-compile(nowarn_unused_vars).",nl, + "-dialyzer(no_improper_lists).",nl]), case Hrl of 0 -> ok; - _ -> emit({"-include(\"",Mod,".hrl\").",nl}) + _ -> emit(["-include(\"",Mod,".hrl\").",nl]) end, emit(["-asn1_info([{vsn,'",asn1ct:vsn(),"'},",nl, " {module,'",Mod,"'},",nl, " {options,",io_lib:format("~p",[Options]),"}]).",nl,nl]). - - -gen_hrlhead(Mod) -> - emit({"%% Generated by the Erlang ASN.1 compiler version:",asn1ct:vsn(),nl}), - emit({"%% Purpose: Erlang record definitions for each named and unnamed",nl}), - emit({"%% SEQUENCE and SET, and macro definitions for each value",nl}), - emit({"%% definition,in module ",Mod,nl,nl}), - emit({nl,nl}). -gen_record2(Name,SeqOrSet,Comps) -> - gen_record2(Name,SeqOrSet,Comps,"",noext). -gen_record2(_Name,_SeqOrSet,[],_Com,_Extension) -> - true; -gen_record2(_Name,_SeqOrSet,[H = #'ComponentType'{name=Cname}],Com,Extension) -> - emit(Com), - emit({asis,Cname}), - gen_record_default(H, Extension); -gen_record2(Name,SeqOrSet,[H = #'ComponentType'{name=Cname}|T],Com, Extension) -> - emit(Com), - emit({asis,Cname}), - gen_record_default(H, Extension), - gen_record2(Name,SeqOrSet,T,", ", Extension); -gen_record2(Name,SeqOrSet,[_|T],Com,Extension) -> - %% skip EXTENSIONMARK, ExtensionAdditionGroup and other markers - gen_record2(Name,SeqOrSet,T,Com,Extension). - -gen_record_default(#'ComponentType'{prop='OPTIONAL'}, _)-> - emit(" = asn1_NOVALUE"); -gen_record_default(#'ComponentType'{prop={'DEFAULT',_}}, _)-> - emit(" = asn1_DEFAULT"); -gen_record_default(_, _) -> - true. +gen_hrlhead(Mod) -> + emit(["%% Generated by the Erlang ASN.1 compiler. Version: ", + asn1ct:vsn(),nl, + "%% Purpose: Erlang record definitions for each named and unnamed",nl, + "%% SEQUENCE and SET, and macro definitions for each value",nl, + "%% definition in module ",Mod,".",nl,nl]). %% May only be a list or a two-tuple. to_textual_order({Root,Ext}) -> @@ -1585,27 +1503,19 @@ constructed_suffix('SEQUENCE OF',_) -> constructed_suffix('SET OF',_) -> 'SETOF'. -erule(ber) -> ber; -erule(per) -> per; -erule(uper) -> per. - index2suffix(0) -> ""; index2suffix(N) -> lists:concat(["_",N]). -ct_gen_module(ber) -> +ct_gen_module(#gen{erule=ber}) -> asn1ct_gen_ber_bin_v2; -ct_gen_module(per) -> - asn1ct_gen_per; -ct_gen_module(uper) -> +ct_gen_module(#gen{erule=per}) -> asn1ct_gen_per. -ct_constructed_module(ber) -> +ct_constructed_module(#gen{erule=ber}) -> asn1ct_constructed_ber_bin_v2; -ct_constructed_module(per) -> - asn1ct_constructed_per; -ct_constructed_module(uper) -> +ct_constructed_module(#gen{erule=per}) -> asn1ct_constructed_per. get_constraint(C,Key) -> @@ -1617,19 +1527,9 @@ get_constraint(C,Key) -> {value,Cnstr} -> Cnstr end. - -get_record_name_prefix() -> - case lists:keysearch(record_name_prefix,1,get(encoding_options)) of - false -> - ""; - {value,{_,Prefix}} -> - Prefix - end. -get_macro_name_prefix() -> - case lists:keysearch(macro_name_prefix,1,get(encoding_options)) of - false -> - ""; - {value,{_,Prefix}} -> - Prefix - end. +get_record_name_prefix(#gen{rec_prefix=Prefix}) -> + Prefix. + +get_macro_name_prefix(#gen{macro_prefix=Prefix}) -> + Prefix. diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index b884d14b0d..6c6d4193f3 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -1200,11 +1200,13 @@ gen_objset_enc(Erules, ObjSetName, UniqueName, {no_mod,no_name} -> gen_inlined_enc_funs(Fields, ClFields, ObjSetName, Val, NthObj); {CurrMod,Name} -> - emit(["'getenc_",ObjSetName,"'(",{asis,Val},") ->",nl, + emit(["'getenc_",ObjSetName,"'(Id) when Id =:= ", + {asis,Val}," ->",nl, " fun 'enc_",Name,"'/3;",nl]), {[],NthObj}; {ModuleName,Name} -> - emit(["'getenc_",ObjSetName,"'(",{asis,Val},") ->",nl]), + emit(["'getenc_",ObjSetName,"'(Id) when Id =:= ", + {asis,Val}," ->",nl]), emit_ext_fun(enc,ModuleName,Name), emit([";",nl]), {[],NthObj}; @@ -1382,11 +1384,13 @@ gen_objset_dec(Erules, ObjSName, UniqueName, [{ObjName,Val,Fields}|T], {no_mod,no_name} -> gen_inlined_dec_funs(Fields,ClFields,ObjSName,Val,NthObj); {CurrMod,Name} -> - emit(["'getdec_",ObjSName,"'(",{asis,Val},") ->",nl, + emit(["'getdec_",ObjSName,"'(Id) when Id =:= ", + {asis,Val}," ->",nl, " fun 'dec_",Name,"'/3;", nl]), NthObj; {ModuleName,Name} -> - emit(["'getdec_",ObjSName,"'(",{asis,Val},") ->",nl]), + emit(["'getdec_",ObjSName,"'(Id) when Id =:= ", + {asis,Val}," ->",nl]), emit_ext_fun(dec,ModuleName,Name), emit([";",nl]), NthObj; diff --git a/lib/asn1/src/asn1ct_gen_check.erl b/lib/asn1/src/asn1ct_gen_check.erl index abe77dd0cb..ccc62a3ce3 100644 --- a/lib/asn1/src/asn1ct_gen_check.erl +++ b/lib/asn1/src/asn1ct_gen_check.erl @@ -21,45 +21,51 @@ %% -module(asn1ct_gen_check). --export([emit/3]). +-export([emit/4]). -import(asn1ct_gen, [emit/1]). -include("asn1_records.hrl"). -emit(Type, Default, Value) -> +emit(Gen, Type, Default, Value) -> Key = {Type,Default}, - Gen = fun(Fd, Name) -> - file:write(Fd, gen(Name, Type, Default)) - end, + DoGen = fun(Fd, Name) -> + file:write(Fd, gen(Gen, Name, Type, Default)) + end, emit(" case "), - asn1ct_func:call_gen("is_default_", Key, Gen, [Value]), + asn1ct_func:call_gen("is_default_", Key, DoGen, [Value]), emit([" of",nl, "true -> {[],0};",nl, "false ->",nl]). -gen(Name, #type{def=T}, Default) -> +gen(#gen{pack=Pack}=Gen, Name, #type{def=T}, Default) -> + DefMarker = case Pack of + record -> "asn1_DEFAULT"; + map -> atom_to_list(?MISSING_IN_MAP) + end, NameStr = atom_to_list(Name), - [NameStr,"(asn1_DEFAULT) ->\n", - "true;\n"|case do_gen(T, Default) of - {literal,Literal} -> - [NameStr,"(",term2str(Literal),") ->\n","true;\n", - NameStr,"(_) ->\n","false.\n\n"]; - {exception,Func,Args} -> - [NameStr,"(Value) ->\n", - "try ",Func,"(Value",arg2str(Args),") of\n", - "_ -> true\n" - "catch throw:false -> false\n" - "end.\n\n"] - end]. + [NameStr,"(",DefMarker,") ->\n", + "true;\n"| + case do_gen(Gen, T, Default) of + {literal,Literal} -> + [NameStr,"(Def) when Def =:= ",term2str(Literal)," ->\n", + "true;\n", + NameStr,"(_) ->\n","false.\n\n"]; + {exception,Func,Args} -> + [NameStr,"(Value) ->\n", + "try ",Func,"(Value",arg2str(Args),") of\n", + "_ -> true\n" + "catch throw:false -> false\n" + "end.\n\n"] + end]. -do_gen(_, asn1_NOVALUE) -> +do_gen(_Gen, _, asn1_NOVALUE) -> {literal,asn1_NOVALUE}; -do_gen(#'Externaltypereference'{module=M,type=T}, Default) -> +do_gen(Gen, #'Externaltypereference'{module=M,type=T}, Default) -> #typedef{typespec=#type{def=Td}} = asn1_db:dbget(M, T), - do_gen(Td, Default); -do_gen('BOOLEAN', Default) -> + do_gen(Gen, Td, Default); +do_gen(_Gen, 'BOOLEAN', Default) -> {literal,Default}; -do_gen({'BIT STRING',[]}, Default) -> +do_gen(_Gen, {'BIT STRING',[]}, Default) -> true = is_bitstring(Default), %Assertion. case asn1ct:use_legacy_types() of false -> @@ -67,17 +73,17 @@ do_gen({'BIT STRING',[]}, Default) -> true -> {exception,need(check_legacy_bitstring, 2),[Default]} end; -do_gen({'BIT STRING',[_|_]=NBL}, Default) -> +do_gen(_Gen, {'BIT STRING',[_|_]=NBL}, Default) -> do_named_bitstring(NBL, Default); -do_gen({'ENUMERATED',_}, Default) -> +do_gen(_Gen, {'ENUMERATED',_}, Default) -> {literal,Default}; -do_gen('INTEGER', Default) -> +do_gen(_Gen, 'INTEGER', Default) -> {literal,Default}; -do_gen({'INTEGER',NNL}, Default) -> +do_gen(_Gen, {'INTEGER',NNL}, Default) -> {exception,need(check_int, 3),[Default,NNL]}; -do_gen('NULL', Default) -> +do_gen(_Gen, 'NULL', Default) -> {literal,Default}; -do_gen('OCTET STRING', Default) -> +do_gen(_Gen, 'OCTET STRING', Default) -> true = is_binary(Default), %Assertion. case asn1ct:use_legacy_types() of false -> @@ -85,34 +91,34 @@ do_gen('OCTET STRING', Default) -> true -> {exception,need(check_octetstring, 2),[Default]} end; -do_gen('OBJECT IDENTIFIER', Default0) -> +do_gen(_Gen, 'OBJECT IDENTIFIER', Default0) -> Default = pre_process_oid(Default0), {exception,need(check_objectidentifier, 2),[Default]}; -do_gen({'CHOICE',Cs}, Default) -> +do_gen(Gen, {'CHOICE',Cs}, Default) -> {Tag,Value} = Default, [Type] = [Type || #'ComponentType'{name=T,typespec=Type} <- Cs, T =:= Tag], - case do_gen(Type#type.def, Value) of + case do_gen(Gen, Type#type.def, Value) of {literal,Lit} -> {literal,{Tag,Lit}}; {exception,Func0,Args} -> Key = {Tag,Func0,Args}, - Gen = fun(Fd, Name) -> - S = gen_choice(Name, Tag, Func0, Args), - ok = file:write(Fd, S) + DoGen = fun(Fd, Name) -> + S = gen_choice(Name, Tag, Func0, Args), + ok = file:write(Fd, S) end, - Func = asn1ct_func:call_gen("is_default_choice", Key, Gen), + Func = asn1ct_func:call_gen("is_default_choice", Key, DoGen), {exception,atom_to_list(Func),[]} end; -do_gen(#'SEQUENCE'{components=Cs}, Default) -> - do_seq_set(Cs, Default); -do_gen({'SEQUENCE OF',Type}, Default) -> - do_sof(Type, Default); -do_gen(#'SET'{components=Cs}, Default) -> - do_seq_set(Cs, Default); -do_gen({'SET OF',Type}, Default) -> - do_sof(Type, Default); -do_gen(Type, Default) -> +do_gen(Gen, #'SEQUENCE'{components=Cs}, Default) -> + do_seq_set(Gen, Cs, Default); +do_gen(Gen, {'SEQUENCE OF',Type}, Default) -> + do_sof(Gen, Type, Default); +do_gen(Gen, #'SET'{components=Cs}, Default) -> + do_seq_set(Gen, Cs, Default); +do_gen(Gen, {'SET OF',Type}, Default) -> + do_sof(Gen, Type, Default); +do_gen(_Gen, Type, Default) -> case asn1ct_gen:unify_if_string(Type) of restrictedstring -> {exception,need(check_restrictedstring, 2),[Default]}; @@ -136,39 +142,58 @@ do_named_bitstring(_, Default) when is_bitstring(Default) -> end, {exception,need(Func, 3),[Default,bit_size(Default)]}. -do_seq_set(Cs0, Default) -> +do_seq_set(#gen{pack=record}=Gen, Cs0, Default) -> Tag = element(1, Default), Cs1 = [T || #'ComponentType'{typespec=T} <- Cs0], - Cs = components(Cs1, tl(tuple_to_list(Default))), + Cs = components(Gen, Cs1, tl(tuple_to_list(Default))), case are_all_literals(Cs) of true -> Literal = list_to_tuple([Tag|[L || {literal,L} <- Cs]]), {literal,Literal}; false -> Key = {Cs,Default}, - Gen = fun(Fd, Name) -> - S = gen_components(Name, Tag, Cs), - ok = file:write(Fd, S) - end, - Func = asn1ct_func:call_gen("is_default_cs_", Key, Gen), + DoGen = fun(Fd, Name) -> + S = gen_components(Name, Tag, Cs), + ok = file:write(Fd, S) + end, + Func = asn1ct_func:call_gen("is_default_cs_", Key, DoGen), + {exception,atom_to_list(Func),[]} + end; +do_seq_set(#gen{pack=map}=Gen, Cs0, Default) -> + Cs1 = [{N,T} || #'ComponentType'{name=N,typespec=T} <- Cs0], + Cs = map_components(Gen, Cs1, Default), + AllLiterals = lists:all(fun({_,{literal,_}}) -> true; + ({_,_}) -> false + end, Cs), + case AllLiterals of + true -> + L = [{Name,Lit} || {Name,{literal,Lit}} <- Cs], + {literal,maps:from_list(L)}; + false -> + Key = {Cs,Default}, + DoGen = fun(Fd, Name) -> + S = gen_map_components(Name, Cs), + ok = file:write(Fd, S) + end, + Func = asn1ct_func:call_gen("is_default_cs_", Key, DoGen), {exception,atom_to_list(Func),[]} end. -do_sof(Type, Default0) -> +do_sof(Gen, Type, Default0) -> Default = lists:sort(Default0), Cs0 = lists:duplicate(length(Default), Type), - Cs = components(Cs0, Default), + Cs = components(Gen, Cs0, Default), case are_all_literals(Cs) of true -> Literal = [Lit || {literal,Lit} <- Cs], {exception,need(check_literal_sof, 2),[Literal]}; false -> Key = Cs, - Gen = fun(Fd, Name) -> - S = gen_sof(Name, Cs), - ok = file:write(Fd, S) + DoGen = fun(Fd, Name) -> + S = gen_sof(Name, Cs), + ok = file:write(Fd, S) end, - Func = asn1ct_func:call_gen("is_default_sof", Key, Gen), + Func = asn1ct_func:call_gen("is_default_sof", Key, DoGen), {exception,atom_to_list(Func),[]} end. @@ -199,6 +224,39 @@ gen_cs_2([], _) -> "throw(false)\n" "end.\n"]. +gen_map_components(Name, Cs) -> + [atom_to_list(Name),"(Value) ->\n", + "case Value of\n", + "#{"|gen_map_cs_1(Cs, 1, "", [])]. + +gen_map_cs_1([{Name,{literal,Lit}}|T], I, Sep, Acc) -> + Var = "E"++integer_to_list(I), + G = Var ++ " =:= " ++ term2str(Lit), + [Sep,term2str(Name),":=",Var| + gen_map_cs_1(T, I+1, ",\n", [{guard,G}|Acc])]; +gen_map_cs_1([{Name,Exc}|T], I, Sep, Acc) -> + Var = "E"++integer_to_list(I), + [Sep,term2str(Name),":=",Var| + gen_map_cs_1(T, I+1, ",\n", [{exc,{Var,Exc}}|Acc])]; +gen_map_cs_1([], _, _, Acc) -> + G = lists:join(", ", [S || {guard,S} <- Acc]), + Exc = [E || {exc,E} <- Acc], + Body = gen_map_cs_2(Exc, ""), + case G of + [] -> + ["} ->\n"|Body]; + [_|_] -> + ["} when ",G," ->\n"|Body] + end. + +gen_map_cs_2([{Var,{exception,Func,Args}}|T], Sep) -> + [Sep,Func,"(",Var,arg2str(Args),")"|gen_map_cs_2(T, ",\n")]; +gen_map_cs_2([], _) -> + [";\n", + "_ ->\n" + "throw(false)\n" + "end.\n"]. + gen_sof(Name, Cs) -> [atom_to_list(Name),"(Value) ->\n", "case length(Value) of\n", @@ -221,9 +279,18 @@ gen_sof_1([{exception,Func,Args}|Cs], I) -> gen_sof_1([], _) -> ".\n". -components([#type{def=Def}|Ts], [V|Vs]) -> - [do_gen(Def, V)|components(Ts, Vs)]; -components([], []) -> []. +components(Gen, [#type{def=Def}|Ts], [V|Vs]) -> + [do_gen(Gen, Def, V)|components(Gen, Ts, Vs)]; +components(_Gen, [], []) -> []. + +map_components(Gen, [{Name,#type{def=Def}}|Ts], Value) -> + case maps:find(Name, Value) of + {ok,V} -> + [{Name,do_gen(Gen, Def, V)}|map_components(Gen, Ts, Value)]; + error -> + map_components(Gen, Ts, Value) + end; +map_components(_Gen, [], _Value) -> []. gen_choice(Name, Tag, Func, Args) -> NameStr = atom_to_list(Name), diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index aa7223904e..9671a566bf 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -113,11 +113,7 @@ gen_encode_prim(Erules, D) -> Value = {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(val)))}, gen_encode_prim(Erules, D, Value). -gen_encode_prim(Erules, #type{}=D, Value) -> - Aligned = case Erules of - uper -> false; - per -> true - end, +gen_encode_prim(#gen{erule=per,aligned=Aligned}, #type{}=D, Value) -> Imm = gen_encode_prim_imm(Value, D, Aligned), asn1ct_imm:enc_cg(Imm, Aligned). @@ -284,11 +280,7 @@ gen_dec_external(Ext, BytesVar) -> _ -> [{asis,Mod},":"] end,{asis,dec_func(Type)},"(",BytesVar,")"]). -gen_dec_imm(Erule, #type{def=Name,constraint=C}) -> - Aligned = case Erule of - uper -> false; - per -> true - end, +gen_dec_imm(#gen{erule=per,aligned=Aligned}, #type{def=Name,constraint=C}) -> gen_dec_imm_1(Name, C, Aligned). gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) -> diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 8b96242c56..2ab848652e 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -37,9 +37,11 @@ per_enc_open_type/2, per_enc_restricted_string/3, per_enc_small_number/2]). --export([per_enc_extension_bit/2,per_enc_extensions/4,per_enc_optional/3]). +-export([per_enc_extension_bit/2,per_enc_extensions/4, + per_enc_extensions_map/4, + per_enc_optional/2]). -export([per_enc_sof/5]). --export([enc_absent/3,enc_append/1,enc_element/2]). +-export([enc_absent/3,enc_append/1,enc_element/2,enc_maps_get/2]). -export([enc_cg/2]). -export([optimize_alignment/1,optimize_alignment/2, dec_slim_cg/2,dec_code_gen/2]). @@ -349,27 +351,32 @@ per_enc_extensions(Val0, Pos0, NumBits, Aligned) when NumBits > 0 -> ['_'|Length ++ PutBits]]}], {var,"Extensions"}}]. -per_enc_optional(Val0, {Pos,DefVals}, _Aligned) when is_integer(Pos), - is_list(DefVals) -> - {B,Val} = enc_element(Pos, Val0), +per_enc_extensions_map(Val0, Vars, Undefined, Aligned) -> + NumBits = length(Vars), + {B,[_Val,Bitmap]} = mk_vars(Val0, [bitmap]), + Length = per_enc_small_length(NumBits, Aligned), + PutBits = case NumBits of + 1 -> [{put_bits,1,1,[1]}]; + _ -> [{put_bits,Bitmap,NumBits,[1]}] + end, + BitmapExpr = extensions_bitmap(Vars, Undefined), + B++[{assign,Bitmap,BitmapExpr}, + {list,[{'cond',[[{eq,Bitmap,0}], + ['_'|Length ++ PutBits]]}], + {var,"Extensions"}}]. + +per_enc_optional(Val, DefVals) when is_list(DefVals) -> Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, - B++[{'cond', - [[{eq,Val,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}]; -per_enc_optional(Val0, {Pos,{call,M,F,A}}, _Aligned) when is_integer(Pos) -> - {B,Val} = enc_element(Pos, Val0), + [{'cond', + [[{eq,Val,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}]; +per_enc_optional(Val, {call,M,F,A}) -> {[],[[],Tmp]} = mk_vars([], [tmp]), Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, - B++[{call,M,F,[Val|A],Tmp}, - {'cond', - [[{eq,Tmp,true},Zero],['_',One]]}]; -per_enc_optional(Val0, Pos, _Aligned) when is_integer(Pos) -> - {B,Val} = enc_element(Pos, Val0), - Zero = {put_bits,0,1,[1]}, - One = {put_bits,1,1,[1]}, - B++[{'cond',[[{eq,Val,asn1_NOVALUE},Zero], - ['_',One]]}]. + [{call,M,F,[Val|A],Tmp}, + {'cond', + [[{eq,Tmp,true},Zero],['_',One]]}]. per_enc_sof(Val0, Constraint, ElementVar, ElementImm, Aligned) -> {B,[Val,Len]} = mk_vars(Val0, [len]), @@ -423,6 +430,13 @@ enc_element(N, Val0) -> {[],[Val,Dst]} = mk_vars(Val0, [element]), {[{call,erlang,element,[N,Val],Dst}],Dst}. +enc_maps_get(N, Val0) -> + {[],[Val,Dst0]} = mk_vars(Val0, [element]), + {var,Dst} = Dst0, + DstExpr = {expr,lists:concat(["#{",N,":=",Dst,"}"])}, + {var,SrcVar} = Val, + {[{assign,DstExpr,SrcVar}],Dst0}. + enc_cg(Imm0, false) -> Imm1 = enc_cse(Imm0), Imm2 = enc_pre_cg(Imm1), @@ -1240,6 +1254,20 @@ enc_length(Len, {Lb,Ub}, Aligned) when is_integer(Lb) -> enc_length(Len, Sv, _Aligned) when is_integer(Sv) -> [{'cond',[[{eq,Len,Sv}]]}]. +extensions_bitmap(Vs, Undefined) -> + Highest = 1 bsl (length(Vs)-1), + Cs = extensions_bitmap_1(Vs, Undefined, Highest), + lists:flatten(lists:join(" bor ", Cs)). + +extensions_bitmap_1([{var,V}|Vs], Undefined, Power) -> + S = ["case ",V," of\n", + " ",Undefined," -> 0;\n" + " _ -> ",integer_to_list(Power),"\n" + "end"], + [S|extensions_bitmap_1(Vs, Undefined, Power bsr 1)]; +extensions_bitmap_1([], _, _) -> + []. + put_bits_binary(Bin, _Unit, Aligned) when is_binary(Bin) -> Sz = byte_size(Bin), <<Int:Sz/unit:8>> = Bin, diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index b3d41dd9f3..8bd99d995b 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -64,7 +64,11 @@ from_type(M,Typename,Type) when is_record(Type,type) -> end; {constructed,bif} when Typename == ['EXTERNAL'] -> Val=from_type_constructed(M,Typename,InnerType,Type), - asn1ct_eval_ext:transform_to_EXTERNAL1994(Val); + T = case M:maps() of + false -> transform_to_EXTERNAL1994; + true -> transform_to_EXTERNAL1994_maps + end, + asn1ct_eval_ext:T(Val); {constructed,bif} -> from_type_constructed(M,Typename,InnerType,Type) end; @@ -118,11 +122,13 @@ get_sequence(M,Typename,Type) -> #'SEQUENCE'{components=Cl} -> {'SEQUENCE',Cl}; #'SET'{components=Cl} -> {'SET',to_textual_order(Cl)} end, - case get_components(M,Typename,CompList) of - [] -> - {list_to_atom(asn1ct_gen:list2rname(Typename))}; - C -> - list_to_tuple([list_to_atom(asn1ct_gen:list2rname(Typename))|C]) + Cs = get_components(M, Typename, CompList), + case M:maps() of + false -> + RecordTag = list_to_atom(asn1ct_gen:list2rname(Typename)), + list_to_tuple([RecordTag|[Val || {_,Val} <- Cs]]); + true -> + maps:from_list(Cs) end. get_components(M,Typename,{Root,Ext}) -> @@ -130,9 +136,9 @@ get_components(M,Typename,{Root,Ext}) -> %% Should enhance this *** HERE *** with proper handling of extensions -get_components(M,Typename,[H|T]) -> - [from_type(M,Typename,H)| - get_components(M,Typename,T)]; +get_components(M, Typename, [H|T]) -> + #'ComponentType'{name=Name} = H, + [{Name,from_type(M, Typename, H)}|get_components(M, Typename, T)]; get_components(_,_,[]) -> []. diff --git a/lib/asn1/src/asn1rtt_ext.erl b/lib/asn1/src/asn1rtt_ext.erl index 3bf01823db..161b2db691 100644 --- a/lib/asn1/src/asn1rtt_ext.erl +++ b/lib/asn1/src/asn1rtt_ext.erl @@ -19,7 +19,8 @@ %% -module(asn1rtt_ext). --export([transform_to_EXTERNAL1990/1,transform_to_EXTERNAL1994/1]). +-export([transform_to_EXTERNAL1990/1,transform_to_EXTERNAL1990_maps/1, + transform_to_EXTERNAL1994/1,transform_to_EXTERNAL1994_maps/1]). transform_to_EXTERNAL1990({_,_,_,_}=Val) -> transform_to_EXTERNAL1990(tuple_to_list(Val), []); @@ -51,6 +52,30 @@ transform_to_EXTERNAL1990([Data_value], Acc) list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])). +transform_to_EXTERNAL1990_maps(#{identification:=Id,'data-value':=Value}=V) -> + M0 = case Id of + {syntax,DRef} -> + #{'direct-reference'=>DRef}; + {'presentation-context-id',IndRef} -> + #{'indirect-reference'=>IndRef}; + {'context-negotiation', + #{'presentation-context-id':=IndRef, + 'transfer-syntax':=DRef}} -> + #{'direct-reference'=>DRef, + 'indirect-reference'=>IndRef} + end, + M = case V of + #{'data-value-descriptor':=Dvd} -> + M0#{'data-value-descriptor'=>Dvd}; + #{} -> + M0 + end, + M#{encoding=>{'octet-aligned',Value}}; +transform_to_EXTERNAL1990_maps(#{encoding:=_}=V) -> + %% Already in the EXTERNAL 1990 format. + V. + + transform_to_EXTERNAL1994({'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}=V) -> Identification = case {DRef,IndRef} of @@ -71,3 +96,38 @@ transform_to_EXTERNAL1994({'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}=V) -> %% information. V end. + +transform_to_EXTERNAL1994_maps(V0) -> + Identification = + case V0 of + #{'direct-reference':=DRef, + 'indirect-reference':=asn1_NOVALUE} -> + {syntax,DRef}; + #{'direct-reference':=asn1_NOVALUE, + 'indirect-reference':=IndRef} -> + {'presentation-context-id',IndRef}; + #{'direct-reference':=DRef, + 'indirect-reference':=IndRef} -> + {'context-negotiation', + #{'transfer-syntax'=>DRef, + 'presentation-context-id'=>IndRef}} + end, + case V0 of + #{encoding:={'octet-aligned',Val}} + when is_list(Val); is_binary(Val) -> + %% Transform to the EXTERNAL 1994 definition. + V = #{identification=>Identification, + 'data-value'=>Val}, + case V0 of + #{'data-value-descriptor':=asn1_NOVALUE} -> + V; + #{'data-value-descriptor':=Dvd} -> + V#{'data-value-descriptor'=>Dvd} + end; + _ -> + %% Keep the EXTERNAL 1990 definition to avoid losing + %% information. + V = [{K,V} || {K,V} <- maps:to_list(V0), + V =/= asn1_NOVALUE], + maps:from_list(V) + end. diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile index 40575e8a2f..afd063aa8e 100644 --- a/lib/asn1/test/Makefile +++ b/lib/asn1/test/Makefile @@ -82,6 +82,7 @@ MODULES= \ testInfObjExtract \ testParameterizedInfObj \ testFragmented \ + testMaps \ testMergeCompile \ testMultipleLevels \ testDeepTConstr \ @@ -114,8 +115,7 @@ MODULES= \ testImporting \ testExtensibilityImplied \ asn1_test_lib \ - asn1_app_test \ - asn1_appup_test \ + asn1_app_SUITE \ asn1_SUITE \ error_SUITE \ syntax_SUITE diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index b6430134ab..580c919b9d 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -21,6 +21,9 @@ -module(asn1_SUITE). +%% Suppress compilation of an addititional module compiled for maps. +-define(NO_MAPS_MODULE, asn1_test_lib_no_maps). + -define(only_ber(Func), if Rule =:= ber -> Func; true -> ok @@ -39,10 +42,11 @@ suite() -> {timetrap,{minutes,60}}]. all() -> - [{group, compile}, + [xref, + xref_export_all, + + {group, compile}, {group, parallel}, - {group, app_test}, - {group, appup_test}, % TODO: Investigate parallel running of these: testComment, @@ -64,13 +68,8 @@ groups() -> ber_optional, tagdefault_automatic]}, - {app_test, [], [{asn1_app_test, all}]}, - - {appup_test, [], [{asn1_appup_test, all}]}, - {parallel, Parallel, [cover, - xref, {group, ber}, % Uses 'P-Record', 'Constraints', 'MEDIA-GATEWAY-CONTROL'... {group, [], [parse, @@ -102,6 +101,7 @@ groups() -> testMultipleLevels, testOpt, testSeqDefault, + testMaps, % Uses 'External' {group, [], [testExternal, testSeqExtension]}, @@ -176,8 +176,11 @@ groups() -> {performance, [], [testTimer_ber, + testTimer_ber_maps, testTimer_per, - testTimer_uper]}]. + testTimer_per_maps, + testTimer_uper, + testTimer_uper_maps]}]. %%------------------------------------------------------------------------------ %% Init/end @@ -441,6 +444,16 @@ testDEFAULT(Config, Rule, Opts) -> testDef:main(Rule), testSeqSetDefaultVal:main(Rule, Opts). +testMaps(Config) -> + test(Config, fun testMaps/3, + [{ber,[maps,no_ok_wrapper]}, + {ber,[maps,der,no_ok_wrapper]}, + {per,[maps,no_ok_wrapper]}, + {uper,[maps,no_ok_wrapper]}]). +testMaps(Config, Rule, Opts) -> + asn1_test_lib:compile_all(['Maps'], Config, [Rule|Opts]), + testMaps:main(Rule). + testOpt(Config) -> test(Config, fun testOpt/3). testOpt(Config, Rule, Opts) -> asn1_test_lib:compile("Opt", Config, [Rule|Opts]), @@ -614,12 +627,12 @@ parse(Config) -> [asn1_test_lib:compile(M, Config, [abs]) || M <- test_modules()]. per(Config) -> - test(Config, fun per/3, [per,uper]). + test(Config, fun per/3, [per,uper,{per,[maps]},{uper,[maps]}]). per(Config, Rule, Opts) -> [module_test(M, Config, Rule, Opts) || M <- per_modules()]. ber_other(Config) -> - test(Config, fun ber_other/3, [ber]). + test(Config, fun ber_other/3, [ber,{ber,[maps]}]). ber_other(Config, Rule, Opts) -> [module_test(M, Config, Rule, Opts) || M <- ber_modules()]. @@ -628,7 +641,7 @@ der(Config) -> asn1_test_lib:compile_all(ber_modules(), Config, [der]). module_test(M0, Config, Rule, Opts) -> - asn1_test_lib:compile(M0, Config, [Rule|Opts]), + asn1_test_lib:compile(M0, Config, [Rule,?NO_MAPS_MODULE|Opts]), case list_to_atom(M0) of 'LDAP' -> %% Because of the recursive definition of 'Filter' in @@ -995,7 +1008,9 @@ testS1AP(Config, Rule, Opts) -> testRfcs() -> [{timetrap,{minutes,90}}]. -testRfcs(Config) -> test(Config, fun testRfcs/3, [{ber,[der]}]). +testRfcs(Config) -> test(Config, fun testRfcs/3, + [{ber,[der,?NO_MAPS_MODULE]}, + {ber,[der,maps]}]). testRfcs(Config, Rule, Opts) -> case erlang:system_info(system_architecture) of "sparc-sun-solaris2.10" -> @@ -1010,7 +1025,8 @@ test_compile_options(Config) -> ok = test_compile_options:path(Config), ok = test_compile_options:noobj(Config), ok = test_compile_options:record_name_prefix(Config), - ok = test_compile_options:verbose(Config). + ok = test_compile_options:verbose(Config), + ok = test_compile_options:maps(Config). testDoubleEllipses(Config) -> test(Config, fun testDoubleEllipses/3). testDoubleEllipses(Config, Rule, Opts) -> @@ -1027,18 +1043,6 @@ test_modified_x420(Config, Rule, Opts) -> test_modified_x420:test(Config). -testX420() -> - [{timetrap,{minutes,90}}]. -testX420(Config) -> - case erlang:system_info(system_architecture) of - "sparc-sun-solaris2.10" -> - {skip,"Too slow for an old Sparc"}; - _ -> - Rule = ber, - testX420:compile(Rule, [der], Config), - ok = testX420:ticket7759(Rule, Config) - end. - test_x691(Config) -> test(Config, fun test_x691/3, [per, uper]). test_x691(Config, Rule, Opts) -> @@ -1069,7 +1073,7 @@ test_x691(Config, Rule, Opts) -> ok. ticket_6143(Config) -> - ok = test_compile_options:ticket_6143(Config). + asn1_test_lib:compile("AA1", Config, [?NO_MAPS_MODULE]). testExtensionAdditionGroup(Config) -> test(Config, fun testExtensionAdditionGroup/3). @@ -1157,20 +1161,33 @@ END ok = asn1ct:compile(File, [{outdir, PrivDir}]). -timer_compile(Config, Rule) -> - asn1_test_lib:compile_all(["H235-SECURITY-MESSAGES", "H323-MESSAGES"], - Config, [no_ok_wrapper,Rule]). +timer_compile(Config, Opts0) -> + Files = ["H235-SECURITY-MESSAGES", "H323-MESSAGES"], + Opts = [no_ok_wrapper,?NO_MAPS_MODULE|Opts0], + asn1_test_lib:compile_all(Files, Config, Opts). testTimer_ber(Config) -> - timer_compile(Config, ber), + timer_compile(Config, [ber]), testTimer:go(). testTimer_per(Config) -> - timer_compile(Config, per), + timer_compile(Config, [per]), testTimer:go(). testTimer_uper(Config) -> - timer_compile(Config, uper), + timer_compile(Config, [uper]), + testTimer:go(). + +testTimer_ber_maps(Config) -> + timer_compile(Config, [ber,maps]), + testTimer:go(). + +testTimer_per_maps(Config) -> + timer_compile(Config, [per,maps]), + testTimer:go(). + +testTimer_uper_maps(Config) -> + timer_compile(Config, [uper,maps]), testTimer:go(). %% Test of multiple-line comment, OTP-8043 @@ -1179,9 +1196,11 @@ testComment(Config) -> asn1_test_lib:roundtrip('Comment', 'Seq', {'Seq',12,true}). testName2Number(Config) -> - N2NOptions = [{n2n,Type} || Type <- ['CauseMisc', 'CauseProtocol', - 'CauseRadioNetwork', - 'CauseTransport','CauseNas']], + N2NOptions0 = [{n2n,Type} || + Type <- ['CauseMisc', 'CauseProtocol', + 'CauseRadioNetwork', + 'CauseTransport','CauseNas']], + N2NOptions = [?NO_MAPS_MODULE|N2NOptions0], asn1_test_lib:compile("S1AP-IEs", Config, N2NOptions), 0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'), @@ -1191,8 +1210,9 @@ testName2Number(Config) -> %% Test that n2n option generates name2num and num2name functions supporting %% values not within the extension root if the enumeration type has an %% extension marker. - N2NOptionsExt = [{n2n, 'NoExt'}, {n2n, 'Ext'}, {n2n, 'Ext2'}], + N2NOptionsExt = [?NO_MAPS_MODULE,{n2n,'NoExt'},{n2n,'Ext'},{n2n,'Ext2'}], asn1_test_lib:compile("EnumN2N", Config, N2NOptionsExt), + %% Previously, name2num and num2name was not generated if the type didn't %% have an extension marker: 0 = 'EnumN2N':name2num_NoExt('blue'), @@ -1210,9 +1230,11 @@ testName2Number(Config) -> ok. ticket_7407(Config) -> - asn1_test_lib:compile("EUTRA-extract-7407", Config, [uper]), + Opts = [uper,?NO_MAPS_MODULE], + asn1_test_lib:compile("EUTRA-extract-7407", Config, Opts), ticket_7407_code(true), - asn1_test_lib:compile("EUTRA-extract-7407", Config, [uper,no_final_padding]), + asn1_test_lib:compile("EUTRA-extract-7407", Config, + [no_final_padding|Opts]), ticket_7407_code(false). ticket_7407_code(FinalPadding) -> @@ -1287,16 +1309,72 @@ ticket7904(Config) -> {ok,_} = 'RANAPextract1':encode('InitiatingMessage', Val1), {ok,_} = 'RANAPextract1':encode('InitiatingMessage', Val1). + +%% Make sure that functions exported from other modules are +%% actually used. + xref(_Config) -> - xref:start(s), - xref:set_default(s, [{verbose,false},{warnings,false},{builtins,true}]), + S = ?FUNCTION_NAME, + xref:start(S), + xref:set_default(S, [{verbose,false},{warnings,false},{builtins,true}]), Test = filename:dirname(code:which(?MODULE)), - {ok,_PMs} = xref:add_directory(s, Test), - UnusedExports = "X - XU - asn1_appup_test - asn1_app_test - \".*_SUITE\" : Mod", - case xref:q(s, UnusedExports) of + {ok,_PMs} = xref:add_directory(S, Test), + Q = "X - XU - \".*_SUITE\" : Mod", + UnusedExports = xref:q(S, Q), + xref:stop(S), + case UnusedExports of {ok,[]} -> ok; {ok,[_|_]=Res} -> io:format("Exported, but unused: ~p\n", [Res]), ?t:fail() end. + +%% Ensure that all functions that are implicitly exported by +%% 'export_all' in this module are actually used. + +xref_export_all(_Config) -> + S = ?FUNCTION_NAME, + xref:start(S), + xref:set_default(S, [{verbose,false},{warnings,false},{builtins,true}]), + {ok,_PMs} = xref:add_module(S, code:which(?MODULE)), + AllCalled = all_called(), + Def = "Called := " ++ lists:flatten(io_lib:format("~p", [AllCalled])), + {ok,_} = xref:q(S, Def), + {ok,Unused} = xref:q(S, "X - Called - range (closure E | Called)"), + xref:stop(S), + case Unused of + [] -> + ok; + [_|_] -> + S = [io_lib:format("~p:~p/~p\n", [M,F,A]) || {M,F,A} <- Unused], + io:format("There are unused functions:\n\n~s\n", [S]), + ?t:fail(unused_functions) + end. + +%% Collect all functions that common_test will call in this module. + +all_called() -> + [{?MODULE,end_per_group,2}, + {?MODULE,end_per_suite,1}, + {?MODULE,end_per_testcase,2}, + {?MODULE,init_per_group,2}, + {?MODULE,init_per_suite,1}, + {?MODULE,init_per_testcase,2}, + {?MODULE,suite,0}] ++ + all_called_1(all() ++ groups()). + +all_called_1([{_,_}|T]) -> + all_called_1(T); +all_called_1([{_Name,_Flags,Fs}|T]) -> + all_called_1(Fs ++ T); +all_called_1([F|T]) when is_atom(F) -> + L = case erlang:function_exported(?MODULE, F, 0) of + false -> + [{?MODULE,F,1}]; + true -> + [{?MODULE,F,0},{?MODULE,F,1}] + end, + L ++ all_called_1(T); +all_called_1([]) -> + []. diff --git a/lib/asn1/test/asn1_SUITE_data/Maps.asn1 b/lib/asn1/test/asn1_SUITE_data/Maps.asn1 new file mode 100644 index 0000000000..fd5f373e45 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/Maps.asn1 @@ -0,0 +1,17 @@ +Maps DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + +XY ::= SEQUENCE { x INTEGER DEFAULT 0, y INTEGER DEFAULT 0 } + +xy1 XY ::= { x 42, y 17 } +xy2 XY ::= { } +xy3 XY ::= { y 999 } + +S ::= SEQUENCE { + xy XY DEFAULT { x 100, y 100 }, + os OCTET STRING OPTIONAL +} + +s1 S ::= {} + +END diff --git a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 index 5fda19303a..e866ef2f4f 100644 --- a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 @@ -48,6 +48,17 @@ SeqExt6 ::= SEQUENCE [[ i6 [106] INTEGER, i7 [107] INTEGER ]] } +SeqExt7 ::= SEQUENCE +{ + -- The spaces between the ellipsis and the comma will prevent them + -- from being removed. + ... , + [[ a INTEGER (0..65535) OPTIONAL, + b OCTET STRING OPTIONAL, + c BOOLEAN + ]] +} + SeqExt1X ::= XSeqExt1 SeqExt2X ::= XSeqExt2 diff --git a/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Discriptions.asn b/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Descriptions.asn index b9be9934e4..12a4475422 100644 --- a/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Discriptions.asn +++ b/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Descriptions.asn @@ -4,7 +4,7 @@ --
-- **************************************************************
-NBAP-PDU-Discriptions {
+NBAP-PDU-Descriptions {
itu-t (0) identified-organization (4) etsi (0) mobileDomain (0)
umts-Access (20) modules (3) nbap (2) version1 (1) nbap-PDU-Descriptions (0) }
diff --git a/lib/asn1/test/asn1_SUITE_data/test_records.erl b/lib/asn1/test/asn1_SUITE_data/test_records.erl index 9fd07c1449..afb1c8c80b 100644 --- a/lib/asn1/test/asn1_SUITE_data/test_records.erl +++ b/lib/asn1/test/asn1_SUITE_data/test_records.erl @@ -25,7 +25,7 @@ -define(line,put(test_server_loc,{?MODULE,?LINE}),). --include("NBAP-PDU-Discriptions.hrl"). +-include("NBAP-PDU-Descriptions.hrl"). -include("NBAP-PDU-Contents.hrl"). -include("NBAP-Containers.hrl"). -include("NBAP-CommonDataTypes.hrl"). diff --git a/lib/asn1/test/asn1_app_test.erl b/lib/asn1/test/asn1_app_SUITE.erl index 028322f555..c089a7267c 100644 --- a/lib/asn1/test/asn1_app_test.erl +++ b/lib/asn1/test/asn1_app_SUITE.erl @@ -21,23 +21,24 @@ %%---------------------------------------------------------------------- %% Purpose: Verify the application specifics of the asn1 application %%---------------------------------------------------------------------- --module(asn1_app_test). - --compile(export_all). +-module(asn1_app_SUITE). +-export([all/0,groups/0,init_per_group/2,end_per_group/2, + init_per_suite/1,end_per_suite/1, + appup/1,fields/1,modules/1,export_all/1,app_depend/1]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all() -> - [fields, modules, exportall, app_depend]. +all() -> + [appup, fields, modules, export_all, app_depend]. -groups() -> +groups() -> []. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -65,12 +66,15 @@ is_app(App) -> end_per_suite(Config) when is_list(Config) -> Config. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +appup(Config) when is_list(Config) -> + ok = test_server:appup_test(asn1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% . fields(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), + AppFile = key1find(app_file, Config), Fields = [vsn, description, modules, registered, applications], case check_fields(Fields, AppFile, []) of [] -> @@ -96,10 +100,9 @@ check_field(Name, AppFile, Missing) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% . modules(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Mods = key1search(modules, AppFile), + AppFile = key1find(app_file, Config), + Mods = key1find(modules, AppFile), EbinList = get_ebin_mods(asn1), case missing_modules(Mods, EbinList, []) of [] -> @@ -112,10 +115,9 @@ modules(Config) when is_list(Config) -> ok; Extra -> check_asn1ct_modules(Extra) -% throw({error, {extra_modules, Extra}}) end, {ok, Mods}. - + get_ebin_mods(App) -> LibDir = code:lib_dir(App), EbinDir = filename:join([LibDir,"ebin"]), @@ -166,10 +168,9 @@ extra_modules(Mods, [Mod|Ebins], Extra) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% . -exportall(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Mods = key1search(modules, AppFile), +export_all(Config) when is_list(Config) -> + AppFile = key1find(app_file, Config), + Mods = key1find(modules, AppFile), check_export_all(Mods). @@ -180,10 +181,10 @@ check_export_all([Mod|Mods]) -> {'EXIT', {undef, _}} -> check_export_all(Mods); O -> - case lists:keysearch(options, 1, O) of + case lists:keyfind(options, 1, O) of false -> check_export_all(Mods); - {value, {options, List}} -> + {options, List} -> case lists:member(export_all, List) of true -> throw({error, {export_all, Mod}}); @@ -193,13 +194,12 @@ check_export_all([Mod|Mods]) -> end end. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% . app_depend(Config) when is_list(Config) -> - AppFile = key1search(app_file, Config), - Apps = key1search(applications, AppFile), + AppFile = key1find(app_file, Config), + Apps = key1find(applications, AppFile), check_apps(Apps). @@ -220,10 +220,10 @@ check_apps([App|Apps]) -> fail(Reason) -> exit({suite_failed, Reason}). -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> +key1find(Key, L) -> + case lists:keyfind(Key, 1, L) of + false -> fail({not_found, Key, L}); - {value, {Key, Value}} -> + {Key, Value} -> Value end. diff --git a/lib/asn1/test/asn1_appup_test.erl b/lib/asn1/test/asn1_appup_test.erl deleted file mode 100644 index 54540e53cc..0000000000 --- a/lib/asn1/test/asn1_appup_test.erl +++ /dev/null @@ -1,58 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2016. 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% -%% -%% -%%---------------------------------------------------------------------- -%% Purpose: Verify the application specifics of the asn1 application -%%---------------------------------------------------------------------- --module(asn1_appup_test). --compile(export_all). --include_lib("common_test/include/ct.hrl"). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -all() -> - [appup]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init_per_suite(Config) when is_list(Config) -> - Config. - - -end_per_suite(Config) when is_list(Config) -> - Config. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -appup() -> - [{doc, "perform a simple check of the asn1 appup file"}]. -appup(Config) when is_list(Config) -> - ok = ?t:appup_test(asn1). diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index dc614db4f2..a79958d229 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -25,7 +25,8 @@ hex_to_bin/1, match_value/2, parallel/0, - roundtrip/3,roundtrip/4,roundtrip_enc/3,roundtrip_enc/4]). + roundtrip/3,roundtrip/4,roundtrip_enc/3,roundtrip_enc/4, + map_roundtrip/3]). -include_lib("common_test/include/ct.hrl"). @@ -94,15 +95,58 @@ module(F0) -> list_to_atom(F). %% filename:join(CaseDir, F ++ ".beam"). -compile_file(File, Options) -> +compile_file(File, Options0) -> + Options = [warnings_as_errors|Options0], try - ok = asn1ct:compile(File, [warnings_as_errors|Options]) + ok = asn1ct:compile(File, Options), + ok = compile_maps(File, Options) catch _:Reason -> ct:print("Failed to compile ~s\n~p", [File,Reason]), error end. +compile_maps(File, Options) -> + unload_map_mod(File), + Incompat = [abs,compact_bit_string,legacy_bit_string, + legacy_erlang_types,maps,asn1_test_lib_no_maps], + case lists:any(fun(E) -> lists:member(E, Incompat) end, Options) of + true -> + ok; + false -> + compile_maps_1(File, Options) + end. + +compile_maps_1(File, Options) -> + ok = asn1ct:compile(File, [maps,no_ok_wrapper,noobj|Options]), + OutDir = proplists:get_value(outdir, Options), + Base0 = filename:rootname(filename:basename(File)), + Base = case filename:extension(Base0) of + ".set" -> + filename:rootname(Base0); + _ -> + Base0 + end, + ErlBase = Base ++ ".erl", + ErlFile = filename:join(OutDir, ErlBase), + {ok,Erl0} = file:read_file(ErlFile), + Erl = re:replace(Erl0, <<"-module\\('">>, "&maps_"), + MapsErlFile = filename:join(OutDir, "maps_" ++ ErlBase), + ok = file:write_file(MapsErlFile, Erl), + {ok,_} = compile:file(MapsErlFile, [report,{outdir,OutDir},{i,OutDir}]), + ok. + +unload_map_mod(File0) -> + File1 = filename:basename(File0), + File2 = filename:rootname(File1, ".asn"), + File3 = filename:rootname(File2, ".asn1"), + File4 = filename:rootname(File3, ".py"), + File = filename:rootname(File4, ".set"), + MapMod = list_to_atom("maps_"++File), + code:delete(MapMod), + code:purge(MapMod), + ok. + compile_erlang(Mod, Config, Options) -> DataDir = proplists:get_value(data_dir, Config), CaseDir = proplists:get_value(case_dir, Config), @@ -147,24 +191,60 @@ roundtrip(Mod, Type, Value) -> roundtrip(Mod, Type, Value, Value). roundtrip(Mod, Type, Value, ExpectedValue) -> - {ok,Encoded} = Mod:encode(Type, Value), - {ok,ExpectedValue} = Mod:decode(Type, Encoded), - test_ber_indefinite(Mod, Type, Encoded, ExpectedValue), - ok. + roundtrip_enc(Mod, Type, Value, ExpectedValue). roundtrip_enc(Mod, Type, Value) -> roundtrip_enc(Mod, Type, Value, Value). roundtrip_enc(Mod, Type, Value, ExpectedValue) -> - {ok,Encoded} = Mod:encode(Type, Value), - {ok,ExpectedValue} = Mod:decode(Type, Encoded), + case Mod:encode(Type, Value) of + {ok,Encoded} -> + {ok,ExpectedValue} = Mod:decode(Type, Encoded); + Encoded when is_binary(Encoded) -> + ExpectedValue = Mod:decode(Type, Encoded) + end, + map_roundtrip(Mod, Type, Encoded), test_ber_indefinite(Mod, Type, Encoded, ExpectedValue), Encoded. +map_roundtrip(Mod, Type, Encoded) -> + MapMod = list_to_atom("maps_"++atom_to_list(Mod)), + try MapMod:maps() of + true -> + map_roundtrip_1(MapMod, Type, Encoded) + catch + error:undef -> + ok + end. + %%% %%% Internal functions. %%% +map_roundtrip_1(Mod, Type, Encoded) -> + Decoded = Mod:decode(Type, Encoded), + case Mod:encode(Type, Decoded) of + Encoded -> + ok; + OtherEncoding -> + case is_named_bitstring(Decoded) of + true -> + %% In BER, named BIT STRINGs with different number of + %% trailing zeroes decode to the same value. + ok; + false -> + error({encode_mismatch,Decoded,Encoded,OtherEncoding}) + end + end, + ok. + +is_named_bitstring([H|T]) -> + is_atom(H) andalso is_named_bitstring(T); +is_named_bitstring([]) -> + true; +is_named_bitstring(_) -> + false. + hex2num(C) when $0 =< C, C =< $9 -> C - $0; hex2num(C) when $A =< C, C =< $F -> C - $A + 10; hex2num(C) when $a =< C, C =< $f -> C - $a + 10. @@ -179,7 +259,12 @@ test_ber_indefinite(Mod, Type, Encoded, ExpectedValue) -> case Mod:encoding_rule() of ber -> Indefinite = iolist_to_binary(ber_indefinite(Encoded)), - {ok,ExpectedValue} = Mod:decode(Type, Indefinite); + case Mod:decode(Type, Indefinite) of + {ok,ExpectedValue} -> + ok; + ExpectedValue -> + ok + end; _ -> ok end. diff --git a/lib/asn1/test/h323test.erl b/lib/asn1/test/h323test.erl index 935af0ba09..41a9159335 100644 --- a/lib/asn1/test/h323test.erl +++ b/lib/asn1/test/h323test.erl @@ -27,6 +27,8 @@ run(per) -> run(); run(_Rules) -> ok. run() -> + roundtrip('EndpointType', endpoint()), + roundtrip('Alerting-UUIE', alerting_uuie()), roundtrip('H323-UserInformation', alerting_val(), alerting_enc()), roundtrip('H323-UserInformation', connect_val(), connect_enc()), general_string(), @@ -36,18 +38,24 @@ alerting_val() -> {'H323-UserInformation', {'H323-UU-PDU', {alerting, - {'Alerting-UUIE', - {0,0,8,2250,0,2}, - {'EndpointType',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, - asn1_NOVALUE,asn1_NOVALUE, - {'TerminalInfo',asn1_NOVALUE}, - false,false}, - asn1_NOVALUE, - {'CallIdentifier',<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}, - asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}}, + alerting_uuie()}, asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}, asn1_NOVALUE}. +endpoint() -> + {'EndpointType',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, + asn1_NOVALUE,asn1_NOVALUE, + {'TerminalInfo',asn1_NOVALUE}, + false,false}. + +alerting_uuie() -> + {'Alerting-UUIE', + {0,0,8,2250,0,2}, + endpoint(), + asn1_NOVALUE, + {'CallIdentifier',<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}, + asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}. + alerting_enc() -> "0380060008914a0002020120110000000000000000000000000000000000". @@ -82,6 +90,9 @@ general_string() -> UI = <<109,64,1,57>>, {ok, _V} = 'MULTIMEDIA-SYSTEM-CONTROL':decode(Type, UI). +roundtrip(T, V) -> + asn1_test_lib:roundtrip('H323-MESSAGES', T, V). + roundtrip(T, V, HexString) -> Enc = asn1_test_lib:hex_to_bin(HexString), Enc = asn1_test_lib:roundtrip_enc('H323-MESSAGES', T, V), diff --git a/lib/asn1/test/testContextSwitchingTypes.erl b/lib/asn1/test/testContextSwitchingTypes.erl index 10012908a9..5688d8afd6 100644 --- a/lib/asn1/test/testContextSwitchingTypes.erl +++ b/lib/asn1/test/testContextSwitchingTypes.erl @@ -90,5 +90,6 @@ check_object_identifier(Tuple) when is_tuple(Tuple) -> enc_dec(T, V0) -> M = 'ContextSwitchingTypes', {ok,Enc} = M:encode(T, V0), + asn1_test_lib:map_roundtrip(M, T, Enc), {ok,V} = M:decode(T, Enc), V. diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index 5a9f47d865..c519c70cdf 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -197,5 +197,6 @@ roundtrip(M, T, V) -> enc_dec(M, T, V0) -> {ok,Enc} = M:encode(T, V0), + asn1_test_lib:map_roundtrip(M, T, Enc), {ok,V} = M:decode(T, Enc), V. diff --git a/lib/asn1/test/testMaps.erl b/lib/asn1/test/testMaps.erl new file mode 100644 index 0000000000..45dd2255ba --- /dev/null +++ b/lib/asn1/test/testMaps.erl @@ -0,0 +1,50 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(testMaps). + +-export([main/1]). + +main(_) -> + M = 'Maps', + true = M:maps(), + + true = M:xy1() =:= #{x=>42,y=>17}, + true = M:xy2() =:= #{x=>0,y=>0}, + true = M:xy3() =:= #{x=>0,y=>999}, + true = M:s1() =:= #{xy=>#{x=>100,y=>100}}, + + roundtrip('XY', M:xy1()), + roundtrip('XY', M:xy2()), + roundtrip('XY', M:xy3()), + roundtrip('XY', #{}, #{x=>0,y=>0}), + + roundtrip('S', M:s1()), + roundtrip('S', #{}, #{xy=>#{x=>100,y=>100}}), + roundtrip('S', #{os=><<1,2,3>>}, #{xy=>#{x=>100,y=>100}, + os=><<1,2,3>>}), + + ok. + +roundtrip(Type, Value) -> + roundtrip(Type, Value, Value). + +roundtrip(Type, Value, Expected) -> + asn1_test_lib:roundtrip('Maps', Type, Value, Expected). diff --git a/lib/asn1/test/testMultipleLevels.erl b/lib/asn1/test/testMultipleLevels.erl index c610e59f3d..e9d83665aa 100644 --- a/lib/asn1/test/testMultipleLevels.erl +++ b/lib/asn1/test/testMultipleLevels.erl @@ -24,5 +24,7 @@ main(_) -> Data = {'Top',{short,"abc"},{long,"a long string follows here"}}, - {ok,B} = 'MultipleLevels':encode('Top', Data), - {ok,Data} = 'MultipleLevels':decode('Top', iolist_to_binary(B)). + roundtrip('Top', Data). + +roundtrip(T, V) -> + asn1_test_lib:roundtrip('MultipleLevels', T, V). diff --git a/lib/asn1/test/testNBAPsystem.erl b/lib/asn1/test/testNBAPsystem.erl index 1af283af42..8d61ca18ce 100644 --- a/lib/asn1/test/testNBAPsystem.erl +++ b/lib/asn1/test/testNBAPsystem.erl @@ -84,7 +84,7 @@ compile(Config, Options) -> M <- ["NBAP-CommonDataTypes.asn", "NBAP-IEs.asn", "NBAP-PDU-Contents.asn", - "NBAP-PDU-Discriptions.asn", + "NBAP-PDU-Descriptions.asn", "NBAP-Constants.asn", "NBAP-Containers.asn"]], asn1_test_lib:compile_all(Fs, Config, Options), @@ -98,16 +98,16 @@ test(_Erule,Config) -> ticket_5812(Config) -> Msg = v_5812(), - {ok,B2} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg), + {ok,B2} = 'NBAP-PDU-Descriptions':encode('NBAP-PDU', Msg), V = <<0,28,74,0,3,48,0,0,1,0,123,64,41,0,0,0,126,64,35,95,208,2,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,145,0,1,205,0,0,0,0,2,98,64,1,128>>, ok = compare(V,B2), - {ok,Msg2} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B2), + {ok,Msg2} = 'NBAP-PDU-Descriptions':decode('NBAP-PDU', B2), ok = check_record_names(Msg2,Config). enc_audit_req_msg() -> Msg = {initiatingMessage, audit_req_msg()}, - {ok,B} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg), - {ok,_Msg} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B), + {ok,B} = 'NBAP-PDU-Descriptions':encode('NBAP-PDU', Msg), + {ok,_Msg} = 'NBAP-PDU-Descriptions':decode('NBAP-PDU', B), {initiatingMessage, #'InitiatingMessage'{value=#'AuditRequest'{protocolIEs=[{_,114,ignore,_}], protocolExtensions = asn1_NOVALUE}}} = _Msg, @@ -116,8 +116,8 @@ enc_audit_req_msg() -> cell_setup_req_msg_test() -> Msg = {initiatingMessage, cell_setup_req_msg()}, - {ok,B} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg), - {ok,_Msg} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B), + {ok,B} = 'NBAP-PDU-Descriptions':encode('NBAP-PDU', Msg), + {ok,_Msg} = 'NBAP-PDU-Descriptions':decode('NBAP-PDU', B), io:format("Msg: ~P~n~n_Msg: ~P~n",[Msg,15,_Msg,15]), ok. diff --git a/lib/asn1/test/testRfcs.erl b/lib/asn1/test/testRfcs.erl index da7333ef98..20176e35eb 100644 --- a/lib/asn1/test/testRfcs.erl +++ b/lib/asn1/test/testRfcs.erl @@ -35,22 +35,27 @@ compile(Config, Erules, Options0) -> asn1_test_lib:compile_all(Specs, Config, [Erules,{i,CaseDir}|Options]). test() -> - {1,3,6,1,5,5,7,48,1,2} = - IdPkixOcspNonce = - 'OCSP-2009':'id-pkix-ocsp-nonce'(), - roundtrip('OCSP-2009', 'OCSPRequest', - {'OCSPRequest', - {'TBSRequest', - 0, - {rfc822Name,"name string"}, - [{'Request', - {'CertID',{'_',{2,9,3,4,5},asn1_NOVALUE}, - <<"POTATOHASH">>,<<"HASHBROWN">>,42}, - [{'_',IdPkixOcspNonce,true,<<34,159,16,57,199>>}]}], - asn1_NOVALUE}, - asn1_NOVALUE}), - otp_7759(), - ok. + M = 'OCSP-2009', + case M:maps() of + false -> + {1,3,6,1,5,5,7,48,1,2} = + IdPkixOcspNonce = + 'OCSP-2009':'id-pkix-ocsp-nonce'(), + roundtrip('OCSP-2009', 'OCSPRequest', + {'OCSPRequest', + {'TBSRequest', + 0, + {rfc822Name,"name string"}, + [{'Request', + {'CertID',{'_',{2,9,3,4,5},asn1_NOVALUE}, + <<"POTATOHASH">>,<<"HASHBROWN">>,42}, + [{'_',IdPkixOcspNonce,true,<<34,159,16,57,199>>}]}], + asn1_NOVALUE}, + asn1_NOVALUE}), + otp_7759(records); + true -> + otp_7759(maps) + end. roundtrip(Module, Type, Value0) -> Enc = Module:encode(Type, Value0), @@ -58,7 +63,7 @@ roundtrip(Module, Type, Value0) -> asn1_test_lib:match_value(Value0, Value1), ok. -otp_7759() -> +otp_7759(Pack) -> %% The release note for asn-1.6.6 says: %% Decode of an open_type when the value was empty tagged %% type encoded with indefinite length failed. @@ -66,10 +71,15 @@ otp_7759() -> Encoded = encoded_msg(), ContentInfo = Mod:decode('ContentInfo', Encoded), io:format("~p\n", [ContentInfo]), - {'ContentInfo',_Id,PKCS7_content} = ContentInfo, - X = Mod:decode('SignedData', PKCS7_content), + Content = case ContentInfo of + {'ContentInfo',_Id,Content0} when Pack =:= records -> + Content0; + #{'content-type':=_,'pkcs7-content':=Content0} + when Pack =:= maps -> + Content0 + end, + X = Mod:decode('SignedData', Content), io:format("~p\n", [X]), - io:nl(), ok. encoded_msg() -> diff --git a/lib/asn1/test/testSeqExtension.erl b/lib/asn1/test/testSeqExtension.erl index f7885cb002..be1d1c2490 100644 --- a/lib/asn1/test/testSeqExtension.erl +++ b/lib/asn1/test/testSeqExtension.erl @@ -31,6 +31,7 @@ -record('SeqExt4',{bool, int}). -record('SeqExt5',{name, shoesize}). -record('SeqExt6',{i1,i2,i3,i4,i5,i6,i7}). +-record('SeqExt7',{a=asn1_NOVALUE,b=asn1_NOVALUE,c}). -record('SuperSeq',{s1,s2,s3,s4,s5,s6,i}). main(Erule, DataDir, Opts) -> @@ -45,8 +46,35 @@ main(Erule, DataDir, Opts) -> roundtrip('SeqExt4', #'SeqExt4'{bool=true,int=12345}), roundtrip('SeqExt4', #'SeqExt4'{bool=false,int=123456}), + case Erule of + ber -> + %% BER currently does not handle Extension Addition Groups + %% correctly. + ok; + _ -> + v_roundtrip3('SeqExt5', #'SeqExt5'{name=asn1_NOVALUE, + shoesize=asn1_NOVALUE}, + Erule, #{per=>"00", + uper=>"00"}), + v_roundtrip3('SeqExt7', #'SeqExt7'{c=asn1_NOVALUE}, + Erule, #{per=>"00", + uper=>"00"}) + end, roundtrip('SeqExt5', #'SeqExt5'{name = <<"Arne">>,shoesize=47}), + v_roundtrip3('SeqExt7', #'SeqExt7'{c=false}, + Erule, #{per=>"80800100", + uper=>"80808000"}), + v_roundtrip3('SeqExt7', #'SeqExt7'{c=true}, + Erule, #{per=>"80800120", + uper=>"80809000"}), + v_roundtrip3('SeqExt7', #'SeqExt7'{a=777,b = <<16#AA>>,c=false}, + Erule, #{per=>"808006C0 030901AA 00", + uper=>"8082E061 20354000"}), + v_roundtrip3('SeqExt7', #'SeqExt7'{a=8888,c=false}, + Erule, #{per=>"80800480 22B800", + uper=>"8081C457 0000"}), + %% Encode a value with this version of the specification. BigInt = 128638468966, SuperSeq = #'SuperSeq'{s1=#'SeqExt1'{}, @@ -106,6 +134,7 @@ main(Erule, DataDir, Opts) -> v_roundtrip2(Erule, 'SeqExt130', list_to_tuple(['SeqExt130'| lists:duplicate(129, asn1_NOVALUE)++[199]])), + ok. roundtrip(Type, Value) -> @@ -118,6 +147,15 @@ v_roundtrip2(Erule, Type, Value) -> roundtrip2(Type, Value) -> asn1_test_lib:roundtrip_enc('SeqExtension2', Type, Value). +v_roundtrip3(Type, Value, Erule, Map) -> + case maps:find(Erule, Map) of + {ok,Hex} -> + Encoded = asn1_test_lib:hex_to_bin(Hex), + Encoded = asn1_test_lib:roundtrip_enc('SeqExtension', Type, Value); + error -> + asn1_test_lib:roundtrip('SeqExtension', Type, Value) + end. + v(ber, 'SeqExt66') -> "30049F41 017D"; v(per, 'SeqExt66') -> "C0420000 00000000 00004001 FA"; v(uper, 'SeqExt66') -> "D0800000 00000000 00101FA0"; diff --git a/lib/asn1/test/testTCAP.erl b/lib/asn1/test/testTCAP.erl index 422ae1f0fc..a6f0f9fad7 100644 --- a/lib/asn1/test/testTCAP.erl +++ b/lib/asn1/test/testTCAP.erl @@ -92,5 +92,6 @@ test_asn1config() -> enc_dec(T, V0) -> M = 'TCAPPackage', {ok,Enc} = M:encode(T, V0), + asn1_test_lib:map_roundtrip(M, T, Enc), {ok,V} = M:decode(T, Enc), V. diff --git a/lib/asn1/test/testTimer.erl b/lib/asn1/test/testTimer.erl index bd8da85735..3edeb1b712 100644 --- a/lib/asn1/test/testTimer.erl +++ b/lib/asn1/test/testTimer.erl @@ -25,7 +25,42 @@ -define(times, 5000). -val() -> +go() -> + Module = 'H323-MESSAGES', + Type = 'H323-UserInformation', + Value = case Module:maps() of + false -> val_records(); + true -> val_maps() + end, + Bytes = Module:encode(Type, Value), + Value = Module:decode(Type, Bytes), + + {ValWr,done} = timer:tc(fun() -> encode(?times, Module, Type, Value) end), + io:format("ASN.1 encoding: ~p micro~n", [ValWr / ?times]), + + done = decode(2, Module, Type, Bytes), + + {ValRead,done} = timer:tc(fun() -> decode(?times, Module, Type, Bytes) end), + io:format("ASN.1 decoding: ~p micro~n", [ValRead /?times]), + + Comment = "encode: "++integer_to_list(round(ValWr/?times)) ++ + " micro, decode: "++integer_to_list(round(ValRead /?times)) ++ + " micro. [" ++ atom_to_list(Module:encoding_rule()) ++ "]", + {comment,Comment}. + +encode(0, _Module,_Type,_Value) -> + done; +encode(N, Module,Type,Value) -> + Module:encode(Type, Value), + encode(N-1, Module, Type, Value). + +decode(0, _Module, _Type, _Value) -> + done; +decode(N, Module, Type, Value) -> + Module:decode(Type, Value), + decode(N-1, Module, Type, Value). + +val_records() -> {'H323-UserInformation',{'H323-UU-PDU', {callProceeding, {'CallProceeding-UUIE', @@ -126,34 +161,66 @@ val() -> {'H323-UserInformation_user-data',24,<<"O">>}}. -go() -> - Module = 'H323-MESSAGES', - Type = 'H323-UserInformation', - Value = val(), - Bytes = Module:encode(Type, Value), - Value = Module:decode(Type, Bytes), - - {ValWr,done} = timer:tc(fun() -> encode(?times, Module, Type, Value) end), - io:format("ASN.1 encoding: ~p micro~n", [ValWr / ?times]), - - done = decode(2, Module, Type, Bytes), - - {ValRead,done} = timer:tc(fun() -> decode(?times, Module, Type, Bytes) end), - io:format("ASN.1 decoding: ~p micro~n", [ValRead /?times]), - - Comment = "encode: "++integer_to_list(round(ValWr/?times)) ++ - " micro, decode: "++integer_to_list(round(ValRead /?times)) ++ - " micro. [" ++ atom_to_list(Module:encoding_rule()) ++ "]", - {comment,Comment}. - -encode(0, _Module,_Type,_Value) -> - done; -encode(N, Module,Type,Value) -> - Module:encode(Type, Value), - encode(N-1, Module, Type, Value). - -decode(0, _Module, _Type, _Value) -> - done; -decode(N, Module, Type, Value) -> - Module:decode(Type, Value), - decode(N-1, Module, Type, Value). +val_maps() -> +#{'h323-uu-pdu' => #{h245Control => [], + h245Tunneling => true, + 'h323-message-body' => {callProceeding,#{callIdentifier => #{guid => <<"OCTET STRINGOCTE">>}, + cryptoTokens => [{cryptoGKPwdEncr,#{algorithmOID => {1,18,467,467}, + encryptedData => <<"OC">>, + paramS => #{iv8 => <<"OCTET ST">>, + ranInt => -7477016}}}, + {cryptoGKPwdEncr,#{algorithmOID => {1,19,486,486}, + encryptedData => <<>>, + paramS => #{iv8 => <<"OCTET ST">>, + ranInt => -2404513}}}], + destinationInfo => #{gatekeeper => #{nonStandardData => #{data => <<"O">>, + nonStandardIdentifier => {object,{0,10,260}}}}, + gateway => #{nonStandardData => #{data => <<"O">>, + nonStandardIdentifier => {object,{0,13,326}}}, + protocol => [{h320,#{dataRatesSupported => [#{channelMultiplier => 78, + channelRate => 1290470518, + nonStandardData => #{data => <<"O">>, + nonStandardIdentifier => {object,{0,11,295}}}}], + nonStandardData => #{data => <<"O">>, + nonStandardIdentifier => {object,{0,11,282}}}, + supportedPrefixes => [#{nonStandardData => #{data => <<"O">>, + nonStandardIdentifier => {object,{0,12,312}}}, + prefix => {'h323-ID',"BM"}}]}}]}, + mc => true, + mcu => #{nonStandardData => #{data => <<"OC">>, + nonStandardIdentifier => {object,{1,13,340,340}}}}, + nonStandardData => #{data => <<"O">>,nonStandardIdentifier => {object,{0,9,237}}}, + terminal => #{nonStandardData => #{data => <<"OC">>, + nonStandardIdentifier => {object,{1,14,353,354}}}}, + undefinedNode => true, + vendor => #{productId => <<"OC">>, + vendor => #{manufacturerCode => 16282, + t35CountryCode => 62, + t35Extension => 63}, + versionId => <<"OC">>}}, + fastStart => [], + h245Address => {ipxAddress,#{netnum => <<"OCTE">>, + node => <<"OCTET ">>, + port => <<"OC">>}}, + h245SecurityMode => {noSecurity,'NULL'}, + protocolIdentifier => {0,8,222}, + tokens => [#{certificate => #{certificate => <<"OC">>,type => {1,16,405,406}}, + challenge => <<"OCTET STR">>, + dhkey => #{generator => <<1:1>>,halfkey => <<1:1>>,modSize => <<1:1>>}, + generalID => "BMP", + nonStandard => #{data => <<"OC">>,nonStandardIdentifier => {1,16,414,415}}, + password => "BM", + random => -26430296, + timeStamp => 1667517741}, + #{certificate => #{certificate => <<"OC">>,type => {1,17,442,443}}, + challenge => <<"OCTET STRI">>, + dhkey => #{generator => <<1:1>>,halfkey => <<1:1>>,modSize => <<1:1>>}, + generalID => "BMP", + nonStandard => #{data => <<"OC">>,nonStandardIdentifier => {1,18,452,452}}, + password => "BMP", + random => -16356110, + timeStamp => 1817656756}]}}, + h4501SupplementaryService => [], + nonStandardControl => [], + nonStandardData => #{data => <<>>,nonStandardIdentifier => {object,{0,3,84}}}}, + 'user-data' => #{'protocol-discriminator' => 24,'user-information' => <<"O">>}}. diff --git a/lib/asn1/test/testUniqueObjectSets.erl b/lib/asn1/test/testUniqueObjectSets.erl index 4d3ec94391..30cbceb577 100644 --- a/lib/asn1/test/testUniqueObjectSets.erl +++ b/lib/asn1/test/testUniqueObjectSets.erl @@ -27,6 +27,7 @@ seq_roundtrip(I, D0) -> M = 'UniqueObjectSets', try {ok,Enc} = M:encode('Seq', {'Seq',I,D0}), + asn1_test_lib:map_roundtrip(M, 'Seq', Enc), {ok,{'Seq',I,D}} = M:decode('Seq', Enc), D catch C:E -> diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl index ac74470537..c15e61550c 100644 --- a/lib/asn1/test/test_compile_options.erl +++ b/lib/asn1/test/test_compile_options.erl @@ -24,8 +24,8 @@ -include_lib("common_test/include/ct.hrl"). --export([wrong_path/1,comp/2,path/1,ticket_6143/1,noobj/1, - record_name_prefix/1,verbose/1]). +-export([wrong_path/1,comp/2,path/1,noobj/1, + record_name_prefix/1,verbose/1,maps/1]). %% OTP-5689 wrong_path(Config) -> @@ -64,8 +64,6 @@ path(Config) -> file:set_cwd(CWD), ok. -ticket_6143(Config) -> asn1_test_lib:compile("AA1", Config, []). - noobj(Config) -> DataDir = proplists:get_value(data_dir,Config), OutDir = proplists:get_value(priv_dir,Config), @@ -130,6 +128,28 @@ verbose(Config) when is_list(Config) -> [] = test_server:capture_get(), ok. +maps(Config) -> + DataDir = proplists:get_value(data_dir, Config), + OutDir = proplists:get_value(case_dir, Config), + InFile = filename:join(DataDir, "P-Record"), + + do_maps(ber, InFile, OutDir), + do_maps(per, InFile, OutDir), + do_maps(uper, InFile, OutDir). + +do_maps(Erule, InFile, OutDir) -> + Opts = [Erule,maps,{outdir,OutDir}], + ok = asn1ct:compile(InFile, Opts), + + %% Make sure that no .hrl files are generated. + [] = filelib:wildcard(filename:join(OutDir, "*.hrl")), + + %% Remove all generated files. + All = filelib:wildcard(filename:join(OutDir, "*")), + _ = [file:delete(N) || N <- All], + + ok. + outfiles_check(OutDir) -> outfiles_check(OutDir,outfiles1()). |