-
- Many bugs have been eliminated in the the ASN.1 compiler
+ Many bugs have been eliminated in the ASN.1 compiler
so that it can now successfully compile many more ASN.1
specifications. Error messages have also been improved.
@@ -977,7 +1025,7 @@
-
The ASN.1 compiler would fail to compile a constraint
- with values given for for the extension part (such as
+ with values given for the extension part (such as
INTEGER (1..10, ..., 11..20)).
Own Id: OTP-11504
diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile
index 9e13d02c8a86..06329840c4fb 100644
--- a/lib/asn1/src/Makefile
+++ b/lib/asn1/src/Makefile
@@ -66,6 +66,7 @@ CT_MODULES= \
asn1ct_tok \
asn1ct_parser2 \
asn1ct_table \
+ asn1ct_partial_decode \
$(EVAL_CT_MODULES)
RT_MODULES= \
diff --git a/lib/asn1/src/asn1.app.src b/lib/asn1/src/asn1.app.src
index 12a6de88bc18..793b70df4e02 100644
--- a/lib/asn1/src/asn1.app.src
+++ b/lib/asn1/src/asn1.app.src
@@ -10,5 +10,5 @@
]},
{env, []},
{applications, [kernel, stdlib]},
- {runtime_dependencies, ["stdlib-3.13","kernel-7.0","erts-11.0"]}
+ {runtime_dependencies, ["stdlib-5.0","kernel-9.0","erts-14.0"]}
]}.
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index c7799d076227..a8101edb27e6 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -106,7 +106,9 @@ loop(#state{parent = Parent, monitor = MRef, table = Table,
loop(State);
{save, OutFile, Mod} ->
Mtab = ets:lookup_element(Table, Mod, 2),
- TempFile = OutFile ++ ".#temp",
+ TempFile = OutFile ++
+ integer_to_list(erlang:unique_integer([positive])) ++
+ ".#temp",
ok = ets:tab2file(Mtab, TempFile),
ok = file:rename(TempFile, OutFile),
loop(State);
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 3788f7778923..54436e5b9724 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -42,14 +42,13 @@
maybe_rename_function/3,current_sindex/0,
set_current_sindex/1,maybe_saved_sindex/2,
parse_and_save/2,verbose/3,warning/3,warning/4,error/3,format_error/1]).
+-export([save_config/2,save_gen_state/2,save_gen_state/3]).
-export([get_bit_string_format/0,use_legacy_types/0]).
-include("asn1_records.hrl").
-include_lib("stdlib/include/erl_compile.hrl").
-include_lib("kernel/include/file.hrl").
--import(asn1ct_gen_ber_bin_v2,[encode_tag_val/3,decode_class/1]).
-
-ifndef(vsn).
-define(vsn,"0.0.1").
-endif.
@@ -59,24 +58,6 @@
-define(dupl_equaldefs,2).
-define(dupl_eqdefs_uniquedefs,?dupl_equaldefs bor ?dupl_uniquedefs).
--define(CONSTRUCTED, 2#00100000).
-
-%% macros used for partial decode commands
--define(CHOOSEN,choosen).
--define(SKIP,skip).
--define(SKIP_OPTIONAL,skip_optional).
-
-%% macros used for partial incomplete decode commands
--define(MANDATORY,mandatory).
--define(DEFAULT,default).
--define(OPTIONAL,opt).
--define(OPTIONAL_UNDECODED,opt_undec).
--define(PARTS,parts).
--define(UNDECODED,undec).
--define(ALTERNATIVE,alt).
--define(ALTERNATIVE_UNDECODED,alt_undec).
--define(ALTERNATIVE_PARTS,alt_parts).
-
%% Removed functions
-removed({decode,'_',"use Mod:decode/2 instead"}).
@@ -259,8 +240,12 @@ 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
- generate(Code, OutFile, Erule, Opts),
- {ok,St}.
+ case generate(Code, OutFile, Erule, Opts) of
+ ok ->
+ {ok,St};
+ {error,Errors} ->
+ {error,St#st{error=Errors}}
+ end.
compile_pass(#st{outfile=OutFile,opts=Opts0}=St) ->
asn1_db:dbstop(), %Reclaim memory.
@@ -335,11 +320,21 @@ clean_errors(Errors) when is_list(Errors) ->
{Structured,Structured ++ AdHoc};
clean_errors(AdHoc) -> {[],AdHoc}.
-print_structured_errors([_|_]=Errors) ->
- _ = [io:format("~ts:~w: ~ts\n", [F,L,M:format_error(E)]) ||
- {structured_error,{F,L},M,E} <- Errors],
- ok;
-print_structured_errors(_) -> ok.
+print_structured_errors(Errors) when is_list(Errors) ->
+ _ = [print_structured_error(F, M, E) ||
+ {structured_error,F,M,E} <- Errors],
+ ok.
+
+print_structured_error(F, M, Error) ->
+ Formatted = M:format_error(Error),
+ case F of
+ none ->
+ io:format("~ts\n", [Formatted]);
+ {File,none} ->
+ io:format("~ts: ~ts\n", [File,Formatted]);
+ {File,Line} when is_integer(Line) ->
+ io:format("~ts:~p: ~ts\n", [File,Line,Formatted])
+ end.
compile1(File, #st{opts=Opts}=St0) ->
compiler_verbose(File, Opts),
@@ -864,21 +859,17 @@ generate({M,CodeTuple}, OutFile, EncodingRule, Options) ->
check_maps_option(Gen),
%% create decoding function names and taglists for partial decode
- 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,
-
- asn1ct_gen:pgen(OutFile, Gen, Code),
- cleanup_bit_string_format(),
- erase(tlv_format), % used in ber
- erase(class_default_type),% used in ber
- asn1ct_table:delete(check_functions),
- ok.
+ case specialized_decode_prepare(Gen, M) of
+ {error,_}=Error ->
+ Error;
+ ok ->
+ asn1ct_gen:pgen(OutFile, Gen, Code),
+ cleanup_bit_string_format(),
+ erase(tlv_format), % used in ber
+ erase(class_default_type), % used in ber
+ asn1ct_table:delete(check_functions),
+ ok
+ end.
init_gen_record(EncodingRule, Options) ->
Erule = case EncodingRule of
@@ -1446,205 +1437,27 @@ prepare_bytes(Bytes) -> list_to_binary(Bytes).
vsn() ->
?vsn.
-specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, M) ->
+specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, #module{name=Mod}) ->
case lists:member(asn1config, Options) of
- true ->
- special_decode_prepare_1(Gen, M);
- false ->
- ok
+ true ->
+ case read_config_file(Gen, Mod) of
+ {ok,ConfigName,ConfigItems} ->
+ try
+ asn1ct_partial_decode:prepare(ConfigItems, Mod)
+ catch
+ throw:{structured_error,Error} ->
+ {error,[{structured_error,{ConfigName,none},
+ asn1ct_partial_decode,Error}]}
+ end;
+ no_config_file ->
+ ok
+ end;
+ false ->
+ ok
end;
specialized_decode_prepare(_, _) ->
ok.
-%% Reads the configuration file if it exists and stores information
-%% about partial decode and incomplete decode
-special_decode_prepare_1(#gen{options=Options}=Gen, M) ->
- %% read configure file
- 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(Gen, ModName) of
- no_config_file ->
- ok;
- CfgList ->
- SelectedDecode = get_config_info(CfgList,selective_decode),
- ExclusiveDecode = get_config_info(CfgList,exclusive_decode),
- CommandList = create_partial_decode_gen_info(M#module.name,
- SelectedDecode),
- %% To convert CommandList to a proper list for the driver change
- %% the list:[[choosen,Tag1],skip,[skip_optional,Tag2]] to L =
- %% [5,2,Tag1,0,1,Tag2] where 5 is the length, and call
- %% port_control(asn1_driver_port,3,[L| Bin])
- save_config(partial_decode,CommandList),
- save_gen_state(selective_decode,SelectedDecode),
- CommandList2 = create_partial_inc_decode_gen_info(M#module.name,
- ExclusiveDecode),
- 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.
-
-%% create_partial_inc_decode_gen_info/2
-%%
-%% Creates a list of tags out of the information in TypeNameList that
-%% tells which value will be incomplete decoded, i.e. each end
-%% component/type in TypeNameList. The significant types/components in
-%% the path from the toptype must be specified in the
-%% TypeNameList. Significant elements are all constructed types that
-%% branches the path to the leaf and the leaf it self.
-%%
-%% Returns a list of elements, where an element may be one of
-%% mandatory|[opt,Tag]|[bin,Tag]. mandatory correspond to a mandatory
-%% element that shall be decoded as usual. [opt,Tag] matches an
-%% OPTIONAL or DEFAULT element that shall be decoded as
-%% usual. [bin,Tag] corresponds to an element, mandatory, OPTIONAL or
-%% DEFAULT, that shall be left encoded (incomplete decoded).
-create_partial_inc_decode_gen_info(ModName,{Mod,[{Name,L}|Ls]}) when is_list(L) ->
- TopTypeName = partial_inc_dec_toptype(L),
- [{Name,TopTypeName,
- create_partial_inc_decode_gen_info1(ModName,TopTypeName,{Mod,L})}|
- create_partial_inc_decode_gen_info(ModName,{Mod,Ls})];
-create_partial_inc_decode_gen_info(_,{_,[]}) ->
- [];
-create_partial_inc_decode_gen_info(_,[]) ->
- [].
-
-create_partial_inc_decode_gen_info1(ModName,TopTypeName,{ModName,
- [_TopType|Rest]}) ->
- case asn1_db:dbget(ModName,TopTypeName) of
- #typedef{typespec=TS} ->
- TagCommand = get_tag_command(TS,?MANDATORY,mandatory),
- create_pdec_inc_command(ModName,get_components(TS#type.def),
- Rest,[TagCommand]);
- _ ->
- throw({error,{"wrong type list in asn1 config file",
- TopTypeName}})
- end;
-create_partial_inc_decode_gen_info1(M1,_,{M2,_}) when M1 /= M2 ->
- throw({error,{"wrong module name in asn1 config file",
- M2}});
-create_partial_inc_decode_gen_info1(_,_,TNL) ->
- throw({error,{"wrong type list in asn1 config file",
- TNL}}).
-
-%%
-%% Only when there is a 'ComponentType' the config data C1 may be a
-%% list, where the incomplete decode is branched. So, C1 may be a
-%% list, a "binary tuple", a "parts tuple" or an atom. The second
-%% element of a binary tuple and a parts tuple is an atom.
-create_pdec_inc_command(_ModName,_,[],Acc) ->
- lists:reverse(Acc);
-create_pdec_inc_command(ModName,{Comps1,Comps2},TNL,Acc)
- when is_list(Comps1),is_list(Comps2) ->
- create_pdec_inc_command(ModName,Comps1 ++ Comps2,TNL,Acc);
-%% The following two clauses match on the type after the top
-%% type. This one if the top type had no tag, i.e. a CHOICE.
-create_pdec_inc_command(ModN,Clist,[CL|_Rest],[[]]) when is_list(CL) ->
- create_pdec_inc_command(ModN,Clist,CL,[]);
-create_pdec_inc_command(ModN,Clist,[CL|_Rest],Acc) when is_list(CL) ->
- InnerDirectives=create_pdec_inc_command(ModN,Clist,CL,[]),
- lists:reverse([InnerDirectives|Acc]);
-create_pdec_inc_command(ModName,
- CList=[#'ComponentType'{name=Name,typespec=TS,
- prop=Prop}|Comps],
- TNL=[C1|Cs],Acc) ->
- case C1 of
- {Name,undecoded} ->
- TagCommand = get_tag_command(TS,?UNDECODED,Prop),
- create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc));
- {Name,parts} ->
- TagCommand = get_tag_command(TS,?PARTS,Prop),
- create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc));
- L when is_list(L) ->
- %% I guess this never happens due to previous clause.
- %% This case is only possible as the first element after
- %% the top type element, when top type is SEGUENCE or SET.
- %% Follow each element in L. Must note every tag on the
- %% way until the last command is reached, but it ought to
- %% be enough to have a "complete" or "complete optional"
- %% command for each component that is not specified in the
- %% config file. Then in the TLV decode the components with
- %% a "complete" command will be decoded by an ordinary TLV
- %% decode.
- create_pdec_inc_command(ModName,CList,L,Acc);
- {Name,RestPartsList} when is_list(RestPartsList) ->
- %% Same as previous, but this may occur at any place in
- %% the structure. The previous is only possible as the
- %% second element.
- case get_tag_command(TS,?MANDATORY,Prop) of
- ?MANDATORY ->
- InnerDirectives=
- create_pdec_inc_command(ModName,TS#type.def,
- RestPartsList,[]),
- create_pdec_inc_command(ModName,Comps,Cs,
- [[?MANDATORY,InnerDirectives]|Acc]);
- [Opt,EncTag] ->
- InnerDirectives =
- create_pdec_inc_command(ModName,TS#type.def,
- RestPartsList,[]),
- create_pdec_inc_command(ModName,Comps,Cs,
- [[Opt,EncTag,InnerDirectives]|Acc])
- end;
- _ ->
- %% this component may not be in the config list
- TagCommand = get_tag_command(TS,?MANDATORY,Prop),
- create_pdec_inc_command(ModName,Comps,TNL,concat_sequential(TagCommand,Acc))
- end;
-create_pdec_inc_command(ModName,
- {'CHOICE',[#'ComponentType'{name=C1,
- typespec=TS,
- prop=Prop}|Comps]},
- [{C1,Directive}|Rest],Acc) ->
- case Directive of
- List when is_list(List) ->
- TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
- CompAcc =
- create_pdec_inc_command(ModName,
- get_components(TS#type.def),List,[]),
- NewAcc = case TagCommand of
- [Command,Tag] when is_atom(Command) ->
- [[Command,Tag,CompAcc]|Acc];
- [L1,_L2|Rest] when is_list(L1) ->
- case lists:reverse(TagCommand) of
- [Atom|Comms] when is_atom(Atom) ->
- [concat_sequential(lists:reverse(Comms),
- [Atom,CompAcc])|Acc];
- [[Command2,Tag2]|Comms] ->
- [concat_sequential(lists:reverse(Comms),
- [[Command2,Tag2,CompAcc]])|Acc]
- end
- end,
- create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
- NewAcc);
- undecoded ->
- TagCommand = get_tag_command(TS,?ALTERNATIVE_UNDECODED,Prop),
- create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
- concat_sequential(TagCommand,Acc));
- parts ->
- TagCommand = get_tag_command(TS,?ALTERNATIVE_PARTS,Prop),
- create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
- concat_sequential(TagCommand,Acc))
- end;
-create_pdec_inc_command(ModName,
- {'CHOICE',[#'ComponentType'{typespec=TS,
- prop=Prop}|Comps]},
- TNL,Acc) ->
- TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
- create_pdec_inc_command(ModName,{'CHOICE',Comps},TNL,
- concat_sequential(TagCommand,Acc));
-create_pdec_inc_command(M,{'CHOICE',{Cs1,Cs2}},TNL,Acc)
- when is_list(Cs1),is_list(Cs2) ->
- create_pdec_inc_command(M,{'CHOICE',Cs1 ++ Cs2},TNL,Acc);
-create_pdec_inc_command(ModName,#'Externaltypereference'{module=M,type=Name},
- TNL,Acc) ->
- #type{def=Def} = get_referenced_type(M,Name),
- create_pdec_inc_command(ModName,get_components(Def),TNL,Acc);
-create_pdec_inc_command(_,_,TNL,_) ->
- throw({error,{"unexpected error when creating partial "
- "decode command",TNL}}).
-
partial_inc_dec_toptype([T|_]) when is_atom(T) ->
T;
partial_inc_dec_toptype([{T,_}|_]) when is_atom(T) ->
@@ -1654,272 +1467,12 @@ partial_inc_dec_toptype([L|_]) when is_list(L) ->
partial_inc_dec_toptype(_) ->
throw({error,{"no top type found for partial incomplete decode"}}).
-
-%% Creates a list of tags out of the information in TypeList and Types
-%% that tells which value will be decoded. Each constructed type that
-%% is in the TypeList will get a "choosen" command. Only the last
-%% type/component in the TypeList may be a primitive type. Components
-%% "on the way" to the final element may get the "skip" or the
-%% "skip_optional" command.
-%% CommandList = [Elements]
-%% Elements = {choosen,Tag}|{skip_optional,Tag}|skip
-%% Tag is a binary with the tag BER encoded.
-create_partial_decode_gen_info(ModName,{ModName,TypeLists}) ->
- [create_partial_decode_gen_info1(ModName,TL) || TL <- TypeLists];
-create_partial_decode_gen_info(_,[]) ->
- [];
-create_partial_decode_gen_info(_M1,{M2,_}) ->
- throw({error,{"wrong module name in asn1 config file",
- M2}}).
-
-create_partial_decode_gen_info1(ModName,{FuncName,TypeList}) ->
- case TypeList of
- [TopType|Rest] ->
- case asn1_db:dbget(ModName,TopType) of
- #typedef{typespec=TS} ->
- TagCommand = get_tag_command(TS,?CHOOSEN),
- Ret=create_pdec_command(ModName,
- get_components(TS#type.def),
- Rest,concat_tags(TagCommand,[])),
- {FuncName,Ret};
- _ ->
- throw({error,{"wrong type list in asn1 config file",
- TypeList}})
- end;
- _ ->
- []
- end;
-create_partial_decode_gen_info1(_,_) ->
- ok.
-
-%% create_pdec_command/4 for each name (type or component) in the
-%% third argument, TypeNameList, a command is created. The command has
-%% information whether the component/type shall be skipped, looked
-%% into or returned. The list of commands is returned.
-create_pdec_command(_ModName,_,[],Acc) ->
- Remove_empty_lists =
- fun([[]|L],Res,Fun) ->
- Fun(L,Res,Fun);
- ([],Res,_) ->
- Res;
- ([H|L],Res,Fun) ->
- Fun(L,[H|Res],Fun)
- end,
- Remove_empty_lists(Acc,[],Remove_empty_lists);
-create_pdec_command(ModName,[#'ComponentType'{name=C1,typespec=TS}|_Comps],
- [C1|Cs],Acc) ->
- %% this component is a constructed type or the last in the
- %% TypeNameList otherwise the config spec is wrong
- TagCommand = get_tag_command(TS,?CHOOSEN),
- create_pdec_command(ModName,get_components(TS#type.def),
- Cs,concat_tags(TagCommand,Acc));
-create_pdec_command(ModName,[#'ComponentType'{typespec=TS,
- prop=Prop}|Comps],
- [C2|Cs],Acc) ->
- TagCommand =
- case Prop of
- mandatory ->
- get_tag_command(TS,?SKIP);
- _ ->
- get_tag_command(TS,?SKIP_OPTIONAL)
- end,
- create_pdec_command(ModName,Comps,[C2|Cs],concat_tags(TagCommand,Acc));
-create_pdec_command(ModName,{'CHOICE',[Comp=#'ComponentType'{name=C1}|_]},TNL=[C1|_Cs],Acc) ->
- create_pdec_command(ModName,[Comp],TNL,Acc);
-create_pdec_command(ModName,{'CHOICE',[#'ComponentType'{}|Comps]},TNL,Acc) ->
- create_pdec_command(ModName,{'CHOICE',Comps},TNL,Acc);
-create_pdec_command(ModName,{'CHOICE',{Cs1,Cs2}},TNL,Acc)
- when is_list(Cs1),is_list(Cs2) ->
- create_pdec_command(ModName,{'CHOICE',Cs1 ++ Cs2},TNL,Acc);
-create_pdec_command(ModName,#'Externaltypereference'{module=M,type=C1},
- TypeNameList,Acc) ->
- #type{def=Def} = get_referenced_type(M,C1),
- create_pdec_command(ModName,get_components(Def),TypeNameList,
- Acc);
-create_pdec_command(ModName,TS=#type{def=Def},[C1|Cs],Acc) ->
- %% This case when we got the "components" of a SEQUENCE/SET OF
- case C1 of
- [1] ->
- %% A list with an integer is the only valid option in a 'S
- %% OF', the other valid option would be an empty
- %% TypeNameList saying that the entire 'S OF' will be
- %% decoded.
- TagCommand = get_tag_command(TS,?CHOOSEN),
- create_pdec_command(ModName,Def,Cs,concat_tags(TagCommand,Acc));
- [N] when is_integer(N) ->
- TagCommand = get_tag_command(TS,?SKIP),
- create_pdec_command(ModName,Def,[[N-1]|Cs],
- concat_tags(TagCommand,Acc));
- Err ->
- throw({error,{"unexpected error when creating partial "
- "decode command",Err}})
- end;
-create_pdec_command(_,_,TNL,_) ->
- throw({error,{"unexpected error when creating partial "
- "decode command",TNL}}).
-
-get_components(#'SEQUENCE'{components={C1,C2}}) when is_list(C1),is_list(C2) ->
- C1++C2;
-get_components(#'SEQUENCE'{components=Components}) ->
- Components;
-get_components(#'SET'{components={C1,C2}}) when is_list(C1),is_list(C2) ->
- C1++C2;
-get_components(#'SET'{components=Components}) ->
- Components;
-get_components({'SEQUENCE OF',Components}) ->
- Components;
-get_components({'SET OF',Components}) ->
- Components;
-get_components(Def) ->
- Def.
-
-concat_sequential(L=[A,B],Acc) when is_atom(A),is_binary(B) ->
- [L|Acc];
-concat_sequential(L,Acc) when is_list(L) ->
- concat_sequential1(lists:reverse(L),Acc);
-concat_sequential(A,Acc) ->
- [A|Acc].
-concat_sequential1([],Acc) ->
- Acc;
-concat_sequential1([[]],Acc) ->
- Acc;
-concat_sequential1([El|RestEl],Acc) when is_list(El) ->
- concat_sequential1(RestEl,[El|Acc]);
-concat_sequential1([mandatory|RestEl],Acc) ->
- concat_sequential1(RestEl,[mandatory|Acc]);
-concat_sequential1(L,Acc) ->
- [L|Acc].
-
-
-many_tags([?SKIP])->
- false;
-many_tags([?SKIP_OPTIONAL,_]) ->
- false;
-many_tags([?CHOOSEN,_]) ->
- false;
-many_tags(_) ->
- true.
-
-concat_tags(Ts,Acc) ->
- case many_tags(Ts) of
- true when is_list(Ts) ->
- lists:reverse(Ts)++Acc;
- true ->
- [Ts|Acc];
- false ->
- [Ts|Acc]
- end.
-%% get_tag_command(Type,Command)
-
-%% Type is the type that has information about the tag Command tells
-%% what to do with the encoded value with the tag of Type when
-%% decoding.
-get_tag_command(#type{tag=[]},_) ->
- [];
-%% SKIP and SKIP_OPTIONAL shall return only one tag command regardless
-get_tag_command(#type{},?SKIP) ->
- ?SKIP;
-get_tag_command(#type{tag=Tags},?SKIP_OPTIONAL) ->
- Tag=hd(Tags),
- [?SKIP_OPTIONAL,encode_tag_val(decode_class(Tag#tag.class),
- Tag#tag.form,Tag#tag.number)];
-get_tag_command(#type{tag=[Tag]},Command) ->
- %% encode the tag according to BER
- [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
- Tag#tag.number)];
-get_tag_command(T=#type{tag=[Tag|Tags]},Command) ->
- TC = get_tag_command(T#type{tag=[Tag]},Command),
- TCs = get_tag_command(T#type{tag=Tags},Command),
- case many_tags(TCs) of
- true when is_list(TCs) ->
- [TC|TCs];
- _ -> [TC|[TCs]]
- end.
-
-%% get_tag_command/3 used by create_pdec_inc_command
-get_tag_command(#type{tag=[]},_,_) ->
- [];
-get_tag_command(#type{tag=[Tag]},?MANDATORY,Prop) ->
- case Prop of
- mandatory ->
- ?MANDATORY;
- {'DEFAULT',_} ->
- [?DEFAULT,encode_tag_val(decode_class(Tag#tag.class),
- Tag#tag.form,Tag#tag.number)];
- _ -> [?OPTIONAL,encode_tag_val(decode_class(Tag#tag.class),
- Tag#tag.form,Tag#tag.number)]
- end;
-get_tag_command(#type{tag=[Tag]},Command,Prop) ->
- [anonymous_dec_command(Command,Prop),encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form, Tag#tag.number)];
-get_tag_command(#type{tag=Tag},Command,Prop) when is_record(Tag,tag) ->
- get_tag_command(#type{tag=[Tag]},Command,Prop);
-get_tag_command(T=#type{tag=[Tag|Tags]},Command,Prop) ->
- [get_tag_command(T#type{tag=[Tag]},Command,Prop)|[
- get_tag_command(T#type{tag=Tags},Command,Prop)]].
-
-anonymous_dec_command(?UNDECODED,'OPTIONAL') ->
- ?OPTIONAL_UNDECODED;
-anonymous_dec_command(Command,_) ->
- Command.
-
-get_referenced_type(M,Name) ->
- case asn1_db:dbget(M,Name) of
- #typedef{typespec=TS} ->
- case TS of
- #type{def=#'Externaltypereference'{module=M2,type=Name2}} ->
- %% The tags have already been taken care of in the
- %% first reference where they were gathered in a
- %% list of tags.
- get_referenced_type(M2,Name2);
- #type{} -> TS;
- _ ->
- throw({error,{"unexpected element when"
- " fetching referenced type",TS}})
- end;
- T ->
- throw({error,{"unexpected element when fetching "
- "referenced type",T}})
- end.
-
-
-tlv_tags([]) ->
- [];
-tlv_tags([mandatory|Rest]) ->
- [mandatory|tlv_tags(Rest)];
-tlv_tags([[Command,Tag]|Rest]) when is_atom(Command),is_binary(Tag) ->
- [[Command,tlv_tag(Tag)]|tlv_tags(Rest)];
-tlv_tags([[Command,Directives]|Rest]) when is_atom(Command),is_list(Directives) ->
- [[Command,tlv_tags(Directives)]|tlv_tags(Rest)];
-%% remove all empty lists
-tlv_tags([[]|Rest]) ->
- tlv_tags(Rest);
-tlv_tags([{Name,TopType,L1}|Rest]) when is_list(L1),is_atom(TopType) ->
- [{Name,TopType,tlv_tags(L1)}|tlv_tags(Rest)];
-tlv_tags([[Command,Tag,L1]|Rest]) when is_list(L1),is_binary(Tag) ->
- [[Command,tlv_tag(Tag),tlv_tags(L1)]|tlv_tags(Rest)];
-tlv_tags([[mandatory|Rest]]) ->
- [[mandatory|tlv_tags(Rest)]];
-tlv_tags([L=[L1|_]|Rest]) when is_list(L1) ->
- [tlv_tags(L)|tlv_tags(Rest)].
-
-tlv_tag(<>) when TagNo < 31 ->
- (Cl bsl 16) + TagNo;
-tlv_tag(<>) ->
- (Cl bsl 16) + TagNo;
-tlv_tag(<>) ->
- TagNo = tlv_tag1(Buffer,0),
- (Cl bsl 16) + TagNo.
-tlv_tag1(<<0:1,PartialTag:7>>,Acc) ->
- (Acc bsl 7) bor PartialTag;
-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 chosen by InfoType. Assumes that the config file
%% content is an Erlang term.
read_config_file_info(ModuleName, InfoType) when is_atom(InfoType) ->
Name = ensure_ext(ModuleName, ".asn1config"),
- CfgList = read_config_file0(Name, []),
+ {ok,_,CfgList} = read_config_file0(Name, []),
get_config_info(CfgList, InfoType).
read_config_file(#gen{options=Options}, ModuleName) ->
@@ -1927,12 +1480,13 @@ read_config_file(#gen{options=Options}, ModuleName) ->
Includes = [I || {i,I} <- Options],
read_config_file0(Name, ["."|Includes]).
-read_config_file0(Name, [D|Dirs]) ->
- case file:consult(filename:join(D, Name)) of
+read_config_file0(Name0, [Dir|Dirs]) ->
+ Name = filename:join(Dir, Name0),
+ case file:consult(Name) of
{ok,CfgList} ->
- CfgList;
+ {ok,Name,CfgList};
{error,enoent} ->
- read_config_file0(Name, Dirs);
+ read_config_file0(Name0, Dirs);
{error,Reason} ->
Error = "error reading asn1 config file: " ++
file:format_error(Reason),
@@ -2396,7 +1950,7 @@ verbose(Format, Args, S) ->
end.
format_error({write_error,File,Reason}) ->
- io_lib:format("writing output file ~s failed: ~s",
+ io_lib:format(<<"writing output file ~ts failed: ~s">>,
[File,file:format_error(Reason)]).
is_error(#state{options=Opts}) ->
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index f1e8a1912895..4c7f63ee92ee 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -4934,7 +4934,7 @@ componentrelation_leadingattr(S,CompList) ->
%% get_simple_table_if_used/2 should find out whether there are any
%% component relation constraints in the entire tree of Cs1 that
%% relates to this level. It returns information about the simple
- %% table constraint necessary for the the call to
+ %% table constraint necessary for the call to
%% componentrelation_leadingattr/6. The step when the leading
%% attribute and the syntax tree is modified to support the code
%% generating.
diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index 84680107269f..d6049a12a345 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -1194,14 +1194,23 @@ gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
Pdec;
_ ->
- emit(["[{",{asis,FirstTag},
- ",",{curr,v},"}|Temp",
- {curr,tlv},
- "] ->",nl]),
+ DecTag =
+ case asn1ct:get_gen_state_field(namelist) of
+ [{Cname,undecoded}|_] ->
+ emit(["[",{curr,v},"|Temp",{curr,tlv},"] ",
+ "when is_binary(",{curr,v},") ->",nl]),
+ Tag;
+ _ ->
+ emit(["[{",{asis,FirstTag},
+ ",",{curr,v},"}|Temp",
+ {curr,tlv},
+ "] ->",nl]),
+ RestTag
+ end,
emit([indent(4),"{"]),
Pdec=
gen_dec_call(InnerType,Erules,TopType,Cname,
- Type,BytesVar,RestTag,mandatory,
+ Type,BytesVar,DecTag,mandatory,
", mandatory, ",DecObjInf,
OptOrMand),
@@ -1358,10 +1367,9 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
%% This is to prepare SEQUENCE OF value in
%% partial incomplete decode for a later
%% part-decode, i.e. skip %% the tag.
- asn1ct:add_generated_refed_func({[Cname|TopType],
- parts,
- [],Type}),
- emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',"]),
+ Id = [parts,Cname|TopType],
+ asn1ct:add_generated_refed_func({Id,parts,[],Type}),
+ emit(["{'",asn1ct_gen:list2name(Id),"',"]),
asn1ct_func:need({ber,match_tags,2}),
EmitDecFunCall("match_tags"),
emit("}");
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index 5305260f95c7..308781491ac2 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -295,7 +295,7 @@ pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) ->
CurrMod = get(currmod),
TypeDef = asn1_db:dbget(CurrMod,TopType),
traverse_type_structure(Erules,TypeDef,RestTypes,FuncName,
- TypeDef#typedef.name),
+ [TypeDef#typedef.name]),
pgen_partial_types1(Erules,Rest);
pgen_partial_types1(_,[]) ->
ok;
@@ -479,24 +479,20 @@ pgen_partial_incomplete_decode1(#gen{erule=ber}) ->
gen_part_decode_funcs(GeneratedFs,0);
pgen_partial_incomplete_decode1(#gen{}) -> ok.
-emit_partial_incomplete_decode({FuncName,TopType,Pattern}) ->
+emit_partial_incomplete_decode({FuncName,TopType,Pattern})
+ when is_atom(TopType) ->
TypePattern = asn1ct:get_gen_state_field(inc_type_pattern),
- TPattern =
- case lists:keysearch(FuncName,1,TypePattern) of
- {value,{_,TP}} -> TP;
- _ -> exit({error,{asn1_internal_error,exclusive_decode}})
- end,
+ {_,TPattern} = lists:keyfind(FuncName, 1, TypePattern),
TopTypeName =
- case asn1ct:maybe_saved_sindex(TopType,TPattern) of
- I when is_integer(I),I>0 ->
- lists:concat([TopType,"_",I]);
- _ ->
- atom_to_list(TopType)
- end,
+ case asn1ct:maybe_saved_sindex(TopType, TPattern) of
+ I when is_integer(I), I > 0 ->
+ list_to_atom(lists:concat([TopType,"_",I]));
+ _ ->
+ TopType
+ end,
emit([{asis,FuncName},"(Bytes) ->",nl,
- " decode_partial_incomplete('",TopTypeName,"',Bytes,",{asis,Pattern},").",nl]);
-emit_partial_incomplete_decode(D) ->
- throw({error,{asn1,{"bad data in asn1config file",D}}}).
+ " decode_partial_incomplete(",{asis,TopTypeName},", Bytes, ",
+ {asis,Pattern},").",nl]).
gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
InnerType =
@@ -507,12 +503,18 @@ gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
get_inner(Type#type.def)
end,
WhatKind = type(InnerType),
- TypeName=list2name(Name),
+ DispatchId = list_to_atom(list2name(Name)),
+ TypeName = case Name of
+ [parts|TypeName0] ->
+ list2name(TypeName0);
+ _ ->
+ list2name(Name)
+ end,
if
N > 0 -> emit([";",nl]);
true -> ok
end,
- emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]),
+ emit(["decode_inc_disp(",{asis,DispatchId},",Data) ->",nl]),
gen_part_decode_funcs(WhatKind,TypeName,Data),
gen_part_decode_funcs(GeneratedFs,N+1);
gen_part_decode_funcs([_H|T],N) ->
@@ -779,7 +781,7 @@ pgen_dispatcher(Gen, Types) ->
emit(["try ",Call," of",nl,
" Bytes ->",nl,
" {ok,Bytes}",nl,
- try_catch()])
+ try_catch(),".",nl])
end,
emit([nl,nl]),
@@ -794,7 +796,7 @@ pgen_dispatcher(Gen, Types) ->
emit(["try ",JerCall," of",nl,
" Bytes ->",nl,
" {ok,Bytes}",nl,
- try_catch()])
+ try_catch(),".",nl])
end,
emit([nl,nl]);
false ->
@@ -854,7 +856,7 @@ pgen_dispatcher(Gen, Types) ->
case NoOkWrapper of
false ->
- emit([nl,try_catch(),nl,nl]);
+ emit([nl,try_catch(),".",nl,nl]);
true ->
emit([".",nl,nl])
end,
@@ -874,7 +876,7 @@ pgen_dispatcher(Gen, Types) ->
result_line(false, ["Result"]),
case NoOkWrapper of
false ->
- emit([nl,try_catch(),nl,nl]);
+ emit([nl,try_catch(),".",nl,nl]);
true ->
emit([".",nl,nl])
end;
@@ -884,7 +886,7 @@ pgen_dispatcher(Gen, Types) ->
%% REST of MODULE
- gen_decode_partial_incomplete(Gen),
+ gen_decode_partial_incomplete(Gen, NoOkWrapper),
gen_partial_inc_dispatcher(Gen),
case Gen of
@@ -916,7 +918,7 @@ try_catch() ->
" Reason ->",nl,
" {error,{asn1,{Reason,Stk}}}",nl,
" end",nl,
- "end."].
+ "end"].
gen_info_functions(Gen) ->
Erule = case Gen of
@@ -938,7 +940,7 @@ gen_info_functions(Gen) ->
"legacy_erlang_types() -> ",
{asis,asn1ct:use_legacy_types()},".",nl,nl]).
-gen_decode_partial_incomplete(#gen{erule=ber}) ->
+gen_decode_partial_incomplete(#gen{erule=ber}, NoOkWrapper) ->
case {asn1ct:read_config_data(partial_incomplete_decode),
asn1ct:get_gen_state_field(inc_type_pattern)} of
{undefined,_} ->
@@ -946,37 +948,44 @@ gen_decode_partial_incomplete(#gen{erule=ber}) ->
{_,undefined} ->
ok;
_ ->
- EmitCaseClauses =
- fun() ->
- emit([" {'EXIT',{error,Reason}} ->",nl,
- " {error,Reason};",nl,
- " {'EXIT',Reason} ->",nl,
- " {error,{asn1,Reason}};",nl,
- " Result ->",nl,
- " {ok,Result}",nl,
- " end"])
- end,
- emit(["decode_partial_incomplete(Type,Data0,",
- "Pattern) ->",nl]),
- emit([" {Data,_RestBin} =",nl,
- " ",{call,ber,decode_primitive_incomplete,
- ["Pattern","Data0"]},com,nl,
- " case catch decode_partial_inc_disp(Type,",
- "Data) of",nl]),
- EmitCaseClauses(),
- emit([".",nl,nl]),
- emit(["decode_part(Type, Data0) "
- "when is_binary(Data0) ->",nl]),
- emit([" case catch decode_inc_disp(Type,element(1, ",
- {call,ber,ber_decode_nif,["Data0"]},")) of",nl]),
- EmitCaseClauses(),
- emit([";",nl]),
- emit(["decode_part(Type, Data0) ->",nl]),
- emit([" case catch decode_inc_disp(Type, Data0) of",nl]),
- EmitCaseClauses(),
- emit([".",nl,nl])
+ emit(["decode_partial_incomplete(Type, Data0, Pattern) ->",nl,
+ " {Data,_RestBin} =",nl,
+ " ",{call,ber,decode_primitive_incomplete,
+ ["Pattern","Data0"]},com,nl]),
+ case NoOkWrapper of
+ true ->
+ emit([" decode_partial_inc_disp(Type, Data)",nl]);
+ false ->
+ emit([" try {ok,decode_partial_inc_disp(Type, Data)}",nl,
+ try_catch()])
+ end,
+ emit([".",nl,nl]),
+
+ emit(["decode_part(Type, Data0) when is_binary(Data0) ->",nl]),
+ case NoOkWrapper of
+ true ->
+ emit([" decode_inc_disp(Type, element(1, ",
+ {call,ber,ber_decode_nif,["Data0"]},
+ "))",nl]);
+ false ->
+ emit([" try {ok,decode_inc_disp(Type, element(1, ",
+ {call,ber,ber_decode_nif,["Data0"]},
+ "))}",nl,
+ try_catch()])
+ end,
+ emit([";",nl]),
+
+ emit(["decode_part(Type, Data0) ->",nl]),
+ case NoOkWrapper of
+ true ->
+ emit([" decode_inc_disp(Type, Data0)"]);
+ false ->
+ emit([" try {ok,decode_inc_disp(Type, Data0)}",nl,
+ try_catch()])
+ end,
+ emit([".",nl,nl])
end;
-gen_decode_partial_incomplete(#gen{}) ->
+gen_decode_partial_incomplete(#gen{}, _) ->
ok.
gen_partial_inc_dispatcher(#gen{erule=ber}) ->
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index 0e7d07e92acd..1f0ac295828b 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -30,7 +30,7 @@
-export([gen_encode_prim/4]).
-export([gen_dec_prim/3]).
-export([gen_objectset_code/2, gen_obj_code/3]).
--export([encode_tag_val/3]).
+-export([encode_tag_val/3,tag_to_integer/1]).
-export([gen_inc_decode/2,gen_decode_selected/3]).
-export([extaddgroup2sequence/1]).
-export([dialyzer_suppressions/1]).
@@ -72,16 +72,33 @@ dialyzer_suppressions(_) ->
false -> ok;
true -> suppress({ber,encode_bit_string,4})
end,
- suppress({ber,decode_selective,2}),
+
+ %% The `no_match` option for dialyzer will suppress clauses whose
+ %% patterns will never match. Here we must ensure that dialyzer
+ %% sees calls to all helper functions to avoid warnings for
+ %% functions that will never be called.
+ %%
+ %% We provide argument lists that avoid dialyzer warnings,
+ %% but stills allows the compiler to do some optimizations.
+
+ Args1 = ["element(5, Arg)", "[skip,{skip_optional,<<0>>},{chosen,<<0>>}]"],
+ suppress({ber,decode_selective,2}, Args1),
+
+ Args2 = ["[{undecoded,0},{alt_parts,0}]", "element(6, Arg)"],
+ suppress({ber,decode_primitive_incomplete,2}, Args2),
+
emit([" ok.",nl]).
-suppress({M,F,A}=MFA) ->
+suppress({_,_,A}=MFA) ->
+ Args = [lists:concat(["element(",I,", Arg)"]) || I <- lists:seq(1, A)],
+ suppress(MFA, Args).
+
+suppress({M,F,_}=MFA, Args) ->
case asn1ct_func:is_used(MFA) of
false ->
ok;
true ->
- Args = [lists:concat(["element(",I,", Arg)"]) || I <- lists:seq(1, A)],
- emit([" ",{call,M,F,Args},com,nl])
+ emit([" _ = ",{call,M,F,Args},com,nl])
end.
%%===============================================================================
@@ -353,19 +370,25 @@ gen_inc_decode(Erules,Type) when is_record(Type,typedef) ->
gen_decode_selected(Erules,Type,FuncName) ->
emit([FuncName,"(Bin) ->",nl]),
Patterns = asn1ct:read_config_data(partial_decode),
- Pattern =
- case lists:keysearch(FuncName,1,Patterns) of
- {value,{_,P}} -> P;
- false -> exit({error,{internal,no_pattern_saved}})
- end,
+ {_,Pattern} = lists:keyfind(FuncName, 1, Patterns),
emit([" case ",{call,ber,decode_selective,
[{asis,Pattern},"Bin"]}," of",nl,
" {ok,Bin2} when is_binary(Bin2) ->",nl,
" {Tlv,_} = ", {call,ber,ber_decode_nif,["Bin2"]},com,nl]),
- emit("{ok,"),
- gen_decode_selected_type(Erules,Type),
- emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl,
- " end.",nl]).
+ NoOkWrapper = proplists:get_bool(no_ok_wrapper, Erules#gen.options),
+ case NoOkWrapper of
+ true -> ok;
+ false -> emit("{ok,")
+ end,
+ gen_decode_selected_type(Erules, Type),
+ case NoOkWrapper of
+ true ->
+ ok;
+ false ->
+ emit(["};",nl,
+ " Err -> exit({error,{selective_decode,Err}})"])
+ end,
+ emit([" end.",nl]).
gen_decode_selected_type(_Erules,TypeDef) ->
Def = TypeDef#typedef.typespec,
@@ -382,14 +405,9 @@ gen_decode_selected_type(_Erules,TypeDef) ->
asn1ct_name:new(len),
gen_dec_prim(Def, BytesVar, Tag);
{constructed,bif} ->
- TopType = case TypeDef#typedef.name of
- A when is_atom(A) -> [A];
- N -> N
- end,
- DecFunName = lists:concat(["'",dec,"_",
- asn1ct_gen:list2name(TopType),"'"]),
- emit([DecFunName,"(",BytesVar,
- ", ",{asis,Tag},")"]);
+ TopType = TypeDef#typedef.name,
+ DecFunName = dec_func(asn1ct_gen:list2name(TopType)),
+ emit([DecFunName,"(",BytesVar,", ",{asis,Tag},")"]);
TheType ->
DecFunName = mkfuncname(TheType,dec),
emit([DecFunName,"(",BytesVar,
@@ -1527,6 +1545,10 @@ get_object_field(Name,ObjectFields) ->
false -> false
end.
+tag_to_integer(#tag{class=Class,number=N})
+ when is_integer(N), 0 =< N, N =< 1 bsl 16 ->
+ decode_class(Class) bsl 10 bor N.
+
%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
%% 8bit Int | binary
encode_tag_val(Class, Form, TagNo) when (TagNo =< 30) ->
diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl
index c2ec27e19592..1f1cf81bd4fb 100644
--- a/lib/asn1/src/asn1ct_imm.erl
+++ b/lib/asn1/src/asn1ct_imm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -498,7 +498,7 @@ is_aligned(k_m_string, _Lb, Ub) ->
Ub >= 16.
%%%
-%%% Generating the intermediate format format for decoding.
+%%% Generating the intermediate format for decoding.
%%%
dec_string(Sv, U, Aligned0, T) when is_integer(Sv) ->
@@ -1791,7 +1791,7 @@ enc_pre_cg_nonbuilding(Imm, _) -> Imm.
%%% an expensive complete/1 implementation). If we can be sure that
%%% complete/1 will be called with an iolist (no 'align' atoms or
%%% bitstrings in the list), we can call iolist_to_binary/1
-%%% instead. If the list may include bitstrings, we can can call
+%%% instead. If the list may include bitstrings, we can call
%%% list_to_bitstring/1 (note that list_to_bitstring/1 does not accept
%%% a binary or bitstring, so we MUST be sure that we only pass it a
%%% list). If complete/1 is called with a binary, we can omit the
@@ -2199,7 +2199,7 @@ propagate({var,Var}, Propagate, #ost{sym=Sym0}=St) when is_function(Propagate, 2
%%% any Anything.
%%%
%%% align Basically iodata, but the list may contain bitstrings
-%%% and the the atom 'align'. Can be passed to complete/1
+%%% and the atom 'align'. Can be passed to complete/1
%%% to construct a binary. Only used for aligned PER (per).
%%%
%%% bitstring An Erlang bitstring.
diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl
index 2bdc284b1271..a86fd7f3399c 100644
--- a/lib/asn1/src/asn1ct_parser2.erl
+++ b/lib/asn1/src/asn1ct_parser2.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -283,7 +283,7 @@ parse_Assignment([{typereference,_,_},{'{',_}|_]=Tokens) ->
%% 2) ValueSet{...} Type ::= ...
%% ObjectSet{...} CLASS-NAME ::= CLASS {...}
%% 3) CLASS-NAME{...} ::= CLASS {...}
- %% A parameterized value set and and a parameterized object set
+ %% A parameterized value set and a parameterized object set
%% cannot be distinguished from each other without type information.
Flist = [fun parse_ParameterizedTypeAssignment/1,
fun parse_ParameterizedValueSetTypeAssignment/1,
diff --git a/lib/asn1/src/asn1ct_partial_decode.erl b/lib/asn1/src/asn1ct_partial_decode.erl
new file mode 100644
index 000000000000..c4df9be4df87
--- /dev/null
+++ b/lib/asn1/src/asn1ct_partial_decode.erl
@@ -0,0 +1,396 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2023. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(asn1ct_partial_decode).
+-export([prepare/2,format_error/1]).
+
+-include("asn1_records.hrl").
+
+prepare(Items, Mod) ->
+ _ = [prepare_item(Item, Mod) || Item <- Items],
+ ok.
+
+format_error({bad_decode_instruction,Term}) ->
+ io_lib:format(<<"badly formed exclusive decode instruction: ~p">>,
+ [Term]);
+format_error({bad_exclusive_decode,Term}) ->
+ io_lib:format(<<"badly formed exclusive decode instructions: ~p">>,
+ [Term]);
+format_error({bad_module_name,Mod,ShouldBe}) ->
+ io_lib:format(<<"the module name is ~p; expected it to be the same as the name"
+ " of the ASN.1 module (~p)">>,
+ [Mod,ShouldBe]);
+format_error({bad_selective_decode,Term}) ->
+ io_lib:format(<<"badly formed selective decode instructions: ~p">>,
+ [Term]);
+format_error({bad_selective_decode_element,Term}) ->
+ io_lib:format(<<"badly formed element in selective decode instruction: ~p">>,
+ [Term]);
+format_error({bad_selective_decode_type_list,Term}) ->
+ io_lib:format(<<"badly formed type list in selective decode instruction: ~p">>,
+ [Term]);
+format_error({stepping_into_primitive,Path}) ->
+ io_lib:format(<<"the tail end of selective decode instructions attempts to ",
+ "step into a primitive type:\n ~p">>,
+ [Path]);
+format_error({undefined_name,Type}) ->
+ io_lib:format(<<"name ~p not found">>, [Type]);
+format_error({undefined_type,Type}) ->
+ io_lib:format(<<"type ~p does not exist">>, [Type]).
+
+%%%
+%%% Common macros.
+%%%
+
+-define(ASN1CT_GEN_BER, asn1ct_gen_ber_bin_v2).
+
+%%%
+%%% Start of local functions.
+%%%
+
+prepare_item({selective_decode,SelectedDecode}, Mod) ->
+ CommandList = selective_decode(Mod, SelectedDecode),
+ asn1ct:save_config(partial_decode, CommandList),
+ asn1ct:save_gen_state(selective_decode, SelectedDecode),
+ ok;
+prepare_item({exclusive_decode,ExclusiveDecode}, Mod) ->
+ ExclusiveCommands = exclusive_decode(Mod, ExclusiveDecode),
+ asn1ct:save_config(partial_incomplete_decode, ExclusiveCommands),
+ asn1ct:save_gen_state(exclusive_decode, ExclusiveDecode, ExclusiveCommands),
+ ok.
+
+%%%
+%%% Handle exclusive decode.
+%%%
+
+-define(MANDATORY, mandatory).
+-define(DEFAULT, default).
+-define(DEFAULT_UNDECODED, default_undecoded).
+-define(OPTIONAL, opt).
+-define(OPTIONAL_UNDECODED, opt_undecoded).
+-define(PARTS, parts).
+-define(UNDECODED, undecoded).
+-define(ALTERNATIVE, alt).
+-define(ALTERNATIVE_UNDECODED, alt_undecoded).
+-define(ALTERNATIVE_PARTS, alt_parts).
+
+exclusive_decode(Mod, {ModI,Instructions}) when is_list(Instructions) ->
+ if
+ Mod =:= ModI ->
+ exclusive_decode_1(Mod, Instructions);
+ true ->
+ cfg_error({bad_module_name,ModI,Mod})
+ end;
+exclusive_decode(_Mod, Term) ->
+ cfg_error({bad_exclusive_decode,Term}).
+
+exclusive_decode_1(Mod, [{FunName,[TopType,Directives0]}|Is])
+ when is_atom(FunName), is_atom(TopType), is_list(Directives0) ->
+ Directives = exclusive_decode_map(Directives0),
+ [{FunName,TopType,exclusive_decode_2(Mod, TopType, Directives)} |
+ exclusive_decode_1(Mod, Is)];
+exclusive_decode_1(_Mod, []) ->
+ [];
+exclusive_decode_1(_Mod, Term) ->
+ cfg_error({bad_exclusive_decode,Term}).
+
+exclusive_decode_2(ModName, TopType, Directives) ->
+ case asn1_db:dbget(ModName, TopType) of
+ #typedef{typespec=TS} ->
+ Acc = get_tag_command(TS, ?MANDATORY, mandatory),
+ exclusive_decode_command(get_components(TS#type.def),
+ Directives, Acc);
+ undefined ->
+ cfg_error({undefined_type,TopType})
+ end.
+
+exclusive_decode_map(Commands) ->
+ exclusive_decode_map(Commands, []).
+
+exclusive_decode_map([H|T], Acc) ->
+ case H of
+ {Name,Command0} when is_atom(Name) ->
+ Command =
+ if
+ Command0 =:= ?UNDECODED; Command0 =:= ?PARTS ->
+ Command0;
+ is_list(Command0) ->
+ exclusive_decode_map(Command0, []);
+ true ->
+ cfg_error({bad_decode_instruction,H})
+ end,
+ exclusive_decode_map(T, [{Name,Command}|Acc]);
+ _ ->
+ cfg_error({bad_decode_instruction,H})
+ end;
+exclusive_decode_map([], Acc) ->
+ maps:from_list(Acc).
+
+exclusive_decode_command(_, Commands, Acc) when map_size(Commands) =:= 0 ->
+ lists:reverse(Acc);
+exclusive_decode_command([#'ComponentType'{name=Name,typespec=TS,
+ prop=Prop}|Comps],
+ Commands0, Acc) ->
+ case maps:take(Name, Commands0) of
+ {Command,Commands} when is_atom(Command) ->
+ TagCommands = get_tag_command(TS, Command, Prop),
+ exclusive_decode_command(Comps, Commands, TagCommands++Acc);
+ {InnerCommands0,Commands} when is_map(Commands0) ->
+ InnerCommands = exclusive_decode_command(TS#type.def,
+ InnerCommands0, []),
+ case get_tag_command(TS, ?MANDATORY, Prop) of
+ [?MANDATORY] ->
+ exclusive_decode_command(Comps, Commands,
+ [{?MANDATORY,InnerCommands}|Acc]);
+ [{Opt,EncTag}] ->
+ exclusive_decode_command(Comps, Commands,
+ [{Opt,EncTag,InnerCommands}|Acc])
+ end;
+ error ->
+ case get_tag_command(TS, ?MANDATORY, Prop) of
+ [] ->
+ case TS of
+ #type{def=#'Externaltypereference'{}} ->
+ exclusive_decode_command(Comps, Commands0, [mandatory|Acc]);
+ _ ->
+ exclusive_decode_command(Comps, Commands0, Acc)
+ end;
+ [_|_]=TagCommands ->
+ exclusive_decode_command(Comps, Commands0, TagCommands ++ Acc)
+ end
+ end;
+exclusive_decode_command({'CHOICE',[_|_]=Cs}, Commands, Acc) ->
+ exclusive_decode_choice_cs(Cs, Commands, Acc);
+exclusive_decode_command({'CHOICE',{Cs1,Cs2}}, Commands, Acc)
+ when is_list(Cs1), is_list(Cs2) ->
+ exclusive_decode_choice_cs(Cs1 ++ Cs2, Commands, Acc);
+exclusive_decode_command(#'Externaltypereference'{module=M,type=Name},
+ Commands, Acc) ->
+ #type{def=Def} = get_referenced_type(M, Name),
+ exclusive_decode_command(get_components(Def), Commands, Acc);
+exclusive_decode_command([], Commands, _) ->
+ [{Name,_}|_] = maps:to_list(maps:iterator(Commands, ordered)),
+ cfg_error({undefined_name,Name}).
+
+exclusive_decode_choice_cs(_, Commands, Acc) when map_size(Commands) =:= 0 ->
+ lists:reverse(Acc);
+exclusive_decode_choice_cs([#'ComponentType'{name=Name,typespec=TS}|Cs],
+ Commands0, Acc) ->
+ case maps:take(Name, Commands0) of
+ {Inner,Commands} ->
+ case Inner of
+ ?UNDECODED ->
+ TagCommands = get_tag_command(TS, ?ALTERNATIVE_UNDECODED, mandatory),
+ exclusive_decode_choice_cs(Cs, Commands, TagCommands ++ Acc);
+ ?PARTS ->
+ TagCommands = get_tag_command(TS, ?ALTERNATIVE_PARTS, mandatory),
+ exclusive_decode_choice_cs(Cs, Commands, TagCommands ++ Acc);
+ _ when is_map(Inner) ->
+ [{Command,Tag}] = get_tag_command(TS, ?ALTERNATIVE, mandatory),
+ CompAcc = exclusive_decode_command(get_components(TS#type.def), Inner, []),
+ exclusive_decode_choice_cs(Cs, Commands, [{Command,Tag,CompAcc}|Acc])
+ end;
+ error ->
+ TagCommands = get_tag_command(TS, ?ALTERNATIVE, mandatory),
+ exclusive_decode_choice_cs(Cs, Commands0, TagCommands ++ Acc)
+ end.
+
+get_tag_command(#type{tag=[]}, _, _) ->
+ [];
+get_tag_command(#type{tag=[Tag]}, ?MANDATORY, Prop) ->
+ [case Prop of
+ mandatory ->
+ ?MANDATORY;
+ {'DEFAULT',_} ->
+ {?DEFAULT,?ASN1CT_GEN_BER:tag_to_integer(Tag)};
+ _ ->
+ {?OPTIONAL,?ASN1CT_GEN_BER:tag_to_integer(Tag)}
+ end];
+get_tag_command(#type{tag=[_|_]=Tags}, ?PARTS=Command, Prop) ->
+ [{anonymous_dec_command(Command, Prop),
+ [?ASN1CT_GEN_BER:tag_to_integer(Tag) || Tag <- Tags]}];
+get_tag_command(#type{tag=[_|_]=Tags}, ?UNDECODED=Command, Prop) ->
+ [{anonymous_dec_command(Command, Prop),
+ [?ASN1CT_GEN_BER:tag_to_integer(Tag) || Tag <- Tags]}];
+get_tag_command(#type{tag=[Tag]}, Command, Prop) ->
+ [{anonymous_dec_command(Command, Prop),
+ ?ASN1CT_GEN_BER:tag_to_integer(Tag)}];
+get_tag_command(#type{tag=[_|_]=Tags}=Type, Command, Prop) ->
+ lists:reverse([hd(get_tag_command(Type#type{tag=[Tag]}, Command, Prop)) ||
+ Tag <- Tags]).
+
+anonymous_dec_command(?UNDECODED, 'OPTIONAL') ->
+ ?OPTIONAL_UNDECODED;
+anonymous_dec_command(?UNDECODED, {'DEFAULT',_}) ->
+ ?DEFAULT_UNDECODED;
+anonymous_dec_command(Command,_) ->
+ Command.
+
+%%%
+%%% Selective decode.
+%%%
+
+-define(CHOSEN, chosen).
+-define(SKIP, skip).
+-define(SKIP_OPTIONAL, skip_optional).
+
+selective_decode(Mod, {ModI,TypeLists}) ->
+ if
+ Mod =:= ModI ->
+ selective_decode1(Mod, TypeLists);
+ true ->
+ cfg_error({bad_module_name,ModI,Mod})
+ end;
+selective_decode(_, Bad) ->
+ cfg_error({bad_selective_decode,Bad}).
+
+selective_decode1(Mod, [TL|TypeLists]) ->
+ [selective_decode2(Mod, TL) |
+ selective_decode1(Mod, TypeLists)];
+selective_decode1(_, []) ->
+ [];
+selective_decode1(_, Bad) ->
+ cfg_error({bad_selective_decode,Bad}).
+
+selective_decode2(ModName, {FuncName,TypeList}) ->
+ case TypeList of
+ [TopType|Types] ->
+ case asn1_db:dbget(ModName, TopType) of
+ #typedef{typespec=TS} ->
+ TagCommand = get_tag_command(TS, ?CHOSEN),
+ Ret = selective_decode_command(get_components(TS#type.def),
+ Types, concat_tags(TagCommand, [])),
+ {FuncName,Ret};
+ undefined ->
+ cfg_error({undefined_type,TopType})
+ end;
+ _ ->
+ cfg_error({bad_selective_decode_type_list,TypeList})
+ end;
+selective_decode2(_, Bad) ->
+ cfg_error({bad_selective_decode,Bad}).
+
+selective_decode_command(_, [], Acc) ->
+ lists:reverse(Acc);
+selective_decode_command([#'ComponentType'{name=Name,typespec=TS}|_],
+ [Name], Acc) ->
+ TagCommand = get_tag_command(TS, ?CHOSEN),
+ lists:reverse(concat_tags(TagCommand, Acc));
+selective_decode_command([#'ComponentType'{name=Name,typespec=TS}|_],
+ [Name|Cs], Acc) ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(TS#type.def)) of
+ {primitive,bif} ->
+ cfg_error({stepping_into_primitive,[Name|Cs]});
+ _ ->
+ TagCommand = get_tag_command(TS, ?CHOSEN),
+ selective_decode_command(get_components(TS#type.def),
+ Cs, concat_tags(TagCommand, Acc))
+ end;
+selective_decode_command([#'ComponentType'{typespec=TS,
+ prop=Prop}|Comps],
+ [_|_]=Cs, Acc) ->
+ TagCommand = case Prop of
+ mandatory ->
+ get_tag_command(TS, ?SKIP);
+ _ ->
+ get_tag_command(TS, ?SKIP_OPTIONAL)
+ end,
+ selective_decode_command(Comps, Cs, concat_tags(TagCommand, Acc));
+selective_decode_command({'CHOICE',[_|_]=Cs}, [Name|_]=TNL, Acc) ->
+ case lists:keyfind(Name, #'ComponentType'.name, Cs) of
+ #'ComponentType'{}=C ->
+ selective_decode_command([C], TNL, Acc);
+ false ->
+ cfg_error({undefined_name,Name})
+ end;
+selective_decode_command({'CHOICE',{Cs1,Cs2}}, TNL, Acc)
+ when is_list(Cs1), is_list(Cs2) ->
+ selective_decode_command({'CHOICE',Cs1 ++ Cs2}, TNL, Acc);
+selective_decode_command(#'Externaltypereference'{module=M,type=C1},
+ TypeNameList, Acc) ->
+ #type{def=Def} = get_referenced_type(M, C1),
+ selective_decode_command(get_components(Def), TypeNameList, Acc);
+selective_decode_command(#type{def=Def}=TS, [C1|Cs], Acc0) ->
+ case C1 of
+ [N] when is_integer(N), N >= 1 ->
+ SkipTags = lists:duplicate(N - 1, ?SKIP),
+ Acc = SkipTags ++ Acc0,
+ TagCommand = get_tag_command(TS, ?CHOSEN),
+ selective_decode_command(Def, Cs, concat_tags(TagCommand, Acc));
+ Bad ->
+ cfg_error({bad_selective_decode_element,Bad})
+ end;
+selective_decode_command(_, [Name], _) ->
+ cfg_error({undefined_name,Name}).
+
+get_tag_command(#type{tag=[]}, _) ->
+ [];
+get_tag_command(#type{}, ?SKIP) ->
+ [?SKIP];
+get_tag_command(#type{tag=[Tag|_]}, ?SKIP_OPTIONAL) ->
+ #tag{class=Class,form=Form,number=TagNo} = Tag,
+ [{?SKIP_OPTIONAL,
+ ?ASN1CT_GEN_BER:encode_tag_val(?ASN1CT_GEN_BER:decode_class(Class),
+ Form, TagNo)}];
+get_tag_command(#type{tag=[Tag]}, Command) ->
+ #tag{class=Class,form=Form,number=TagNo} = Tag,
+ [{Command,
+ ?ASN1CT_GEN_BER:encode_tag_val(?ASN1CT_GEN_BER:decode_class(Class),
+ Form, TagNo)}];
+get_tag_command(T=#type{tag=[Tag|Tags]}, Command) ->
+ TC = get_tag_command(T#type{tag=[Tag]}, Command),
+ TCs = get_tag_command(T#type{tag=Tags}, Command),
+ TC ++ TCs.
+
+concat_tags(Ts, Acc) when is_list(Ts) ->
+ lists:reverse(Ts, Acc).
+
+%%%
+%%% Common utilities.
+%%%
+
+get_components(#'SEQUENCE'{components={Cs1,Cs2}}) when is_list(Cs1), is_list(Cs2) ->
+ Cs1 ++ Cs2;
+get_components(#'SEQUENCE'{components=Cs}) when is_list(Cs) ->
+ Cs;
+get_components(#'SET'{components={Cs1,Cs2}}) when is_list(Cs1), is_list(Cs2) ->
+ Cs1 ++ Cs2;
+get_components(#'SET'{components=Cs}) when is_list(Cs) ->
+ Cs;
+get_components({'SEQUENCE OF',#type{}=Component})->
+ Component;
+get_components({'SET OF',#type{}=Component}) ->
+ Component;
+get_components(Def) ->
+ Def.
+
+get_referenced_type(M0, Name0) ->
+ #typedef{typespec=TS} = asn1_db:dbget(M0, Name0),
+ case TS of
+ #type{def=#'Externaltypereference'{module=M,type=Name}} ->
+ %% The tags have already been taken care of in the first
+ %% reference where they were gathered in a list of tags.
+ get_referenced_type(M, Name);
+ #type{} ->
+ TS
+ end.
+
+cfg_error(Error) ->
+ throw({structured_error,Error}).
diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl
index f2458d25c538..13df1c73276e 100644
--- a/lib/asn1/src/asn1rtt_ber.erl
+++ b/lib/asn1/src/asn1rtt_ber.erl
@@ -160,22 +160,29 @@ decode_constructed_indefinite(Bin,Acc) ->
%% decode_primitive_incomplete/2 decodes an encoded message incomplete
%% by help of the pattern attribute (first argument).
-decode_primitive_incomplete([[default,TagNo]],Bin) -> %default
+decode_primitive_incomplete([{default,TagNo}], Bin) ->
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,[],Rest);
+ decode_incomplete2(Form, TagNo, V, [], Rest);
_ ->
asn1_NOVALUE
end;
-decode_primitive_incomplete([[default,TagNo,Directives]],Bin) ->
+decode_primitive_incomplete([{default,TagNo,Directives}], Bin) ->
%% default, constructed type, Directives points into this type
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,Directives,Rest);
+ decode_incomplete2(Form, TagNo, V, Directives, Rest);
+ _ ->
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([{default_undecoded,[Tag|_]}], Bin) ->
+ case decode_tag_and_length(Bin) of
+ {_,Tag,_,_} ->
+ decode_incomplete_bin(Bin);
_ ->
asn1_NOVALUE
end;
-decode_primitive_incomplete([[opt,TagNo]],Bin) ->
+decode_primitive_incomplete([{opt,TagNo}],Bin) ->
%% optional
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
@@ -183,74 +190,79 @@ decode_primitive_incomplete([[opt,TagNo]],Bin) ->
_ ->
asn1_NOVALUE
end;
-decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) ->
+decode_primitive_incomplete([{opt,TagNo,Directives}], Bin) ->
%% optional
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
- decode_incomplete2(Form,TagNo,V,Directives,Rest);
+ decode_incomplete2(Form, TagNo, V, Directives, Rest);
_ ->
asn1_NOVALUE
end;
%% An optional that shall be undecoded
-decode_primitive_incomplete([[opt_undec,Tag]],Bin) ->
+decode_primitive_incomplete([{opt_undecoded,[Tag|_]}], Bin) ->
case decode_tag_and_length(Bin) of
{_,Tag,_,_} ->
- decode_incomplete_bin(Bin);
+ decode_incomplete_bin(Bin);
_ ->
asn1_NOVALUE
end;
%% A choice alternative that shall be undecoded
-decode_primitive_incomplete([[alt_undec,TagNo]|RestAlts],Bin) ->
+decode_primitive_incomplete([{alt_undecoded,TagNo}|RestAlts], Bin) ->
case decode_tag_and_length(Bin) of
{_,TagNo,_,_} ->
decode_incomplete_bin(Bin);
_ ->
- decode_primitive_incomplete(RestAlts,Bin)
+ decode_primitive_incomplete(RestAlts, Bin)
end;
-decode_primitive_incomplete([[alt,TagNo]|RestAlts],Bin) ->
+decode_primitive_incomplete([{alt,TagNo}|RestAlts], Bin) ->
case decode_tag_and_length(Bin) of
{_Form,TagNo,V,Rest} ->
{{TagNo,V},Rest};
_ ->
decode_primitive_incomplete(RestAlts,Bin)
end;
-decode_primitive_incomplete([[alt,TagNo,Directives]|RestAlts],Bin) ->
+decode_primitive_incomplete([{alt,TagNo,Directives}|RestAlts], Bin) ->
case decode_tag_and_length(Bin) of
{Form,TagNo,V,Rest} ->
decode_incomplete2(Form,TagNo,V,Directives,Rest);
_ ->
decode_primitive_incomplete(RestAlts,Bin)
end;
-decode_primitive_incomplete([[alt_parts,TagNo]],Bin) ->
+decode_primitive_incomplete([{alt_parts,TagNo}], Bin) ->
case decode_tag_and_length(Bin) of
{_Form,TagNo,V,Rest} ->
{{TagNo,V},Rest};
_ ->
asn1_NOVALUE
end;
-decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) ->
+decode_primitive_incomplete([{alt_parts,TagNo}|RestAlts], Bin) ->
case decode_tag_and_length(Bin) of
{_Form,TagNo,V,Rest} ->
{{TagNo,decode_parts_incomplete(V)},Rest};
_ ->
- decode_primitive_incomplete(RestAlts,Bin)
+ decode_primitive_incomplete(RestAlts, Bin)
end;
-decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) ->
- %% incomlete decode
+decode_primitive_incomplete([{undecoded,_TagNo}|_RestTag], Bin) ->
decode_incomplete_bin(Bin);
-decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) ->
+decode_primitive_incomplete([{parts,[TagNo|MoreTags]}|_RestTag], Bin) ->
case decode_tag_and_length(Bin) of
{_Form,TagNo,V,Rest} ->
- {{TagNo,decode_parts_incomplete(V)},Rest};
+ case MoreTags of
+ [] ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ [TagNo2] ->
+ {_,TagNo2,V2,<<>>} = decode_tag_and_length(V),
+ {{TagNo,{TagNo2,decode_parts_incomplete(V2)}},Rest}
+ end;
Err ->
{error,{asn1,"tag failure",TagNo,Err}}
end;
-decode_primitive_incomplete([mandatory|RestTag],Bin) ->
+decode_primitive_incomplete([mandatory|RestTag], Bin) ->
{Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
decode_incomplete2(Form,TagNo,V,RestTag,Rest);
%% A choice that is a toptype or a mandatory component of a
%% SEQUENCE or SET.
-decode_primitive_incomplete([[mandatory|Directives]],Bin) ->
+decode_primitive_incomplete([{mandatory,Directives}], Bin) ->
{Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
decode_incomplete2(Form,TagNo,V,Directives,Rest);
decode_primitive_incomplete([],Bin) ->
@@ -269,7 +281,7 @@ decode_parts_incomplete(Bin) ->
%% decode_incomplete2 checks if V is a value of a constructed or
-%% primitive type, and continues the decode propeerly.
+%% primitive type, and continues the decode properly.
decode_incomplete2(_Form=2,TagNo,V,TagMatch,_) ->
%% constructed indefinite length
{Vlist,Rest2} = decode_constr_indef_incomplete(TagMatch,V,[]),
@@ -288,16 +300,16 @@ decode_constructed_incomplete(_TagMatch,<<>>) ->
decode_constructed_incomplete([mandatory|RestTag],Bin) ->
{Tlv,Rest} = decode_primitive(Bin),
[Tlv|decode_constructed_incomplete(RestTag,Rest)];
-decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
- when Alt =:= alt_undec; Alt =:= alt; Alt =:= alt_parts ->
+decode_constructed_incomplete([{Alt,_}|_]=Directives, Bin)
+ when Alt =:= alt_undecoded; Alt =:= alt; Alt =:= alt_parts ->
{_Form,TagNo,V,Rest} = decode_tag_and_length(Bin),
case incomplete_choice_alt(TagNo, Directives) of
- {alt_undec,_} ->
+ {alt_undecoded,_} ->
LenA = byte_size(Bin) - byte_size(Rest),
<> = Bin,
A;
{alt,InnerDirectives} ->
- {Tlv,Rest} = decode_primitive_incomplete(InnerDirectives,V),
+ {Tlv,Rest} = decode_primitive_incomplete(InnerDirectives, V),
{TagNo,Tlv};
{alt_parts,_} ->
[{TagNo,decode_parts_incomplete(V)}];
@@ -305,7 +317,7 @@ decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
%% if a choice alternative was encoded that
%% was not specified in the config file,
%% thus decode component anonomous.
- {Tlv,_}=decode_primitive(Bin),
+ {Tlv,_} = decode_primitive(Bin),
Tlv
end;
decode_constructed_incomplete([TagNo|RestTag],Bin) ->
@@ -337,12 +349,12 @@ decode_incomplete_bin(Bin) ->
<> = Bin,
{IncBin,Ret}.
-incomplete_choice_alt(TagNo,[[Alt,TagNo]|Directives]) ->
+incomplete_choice_alt(TagNo, [{Alt,TagNo}|Directives]) ->
{Alt,Directives};
-incomplete_choice_alt(TagNo,[D]) when is_list(D) ->
- incomplete_choice_alt(TagNo,D);
-incomplete_choice_alt(TagNo,[_H|Directives]) ->
- incomplete_choice_alt(TagNo,Directives);
+incomplete_choice_alt(TagNo, [D]) when is_list(D) ->
+ incomplete_choice_alt(TagNo, D);
+incomplete_choice_alt(TagNo, [_H|Directives]) ->
+ incomplete_choice_alt(TagNo, Directives);
incomplete_choice_alt(_,[]) ->
no_match.
@@ -353,35 +365,35 @@ incomplete_choice_alt(_,[]) ->
%% Returns {ok,Value} or {error,Reason}
%% Value is a binary that in turn must be decoded to get the decoded
%% value.
-decode_selective([],Binary) ->
+decode_selective([], Binary) ->
{ok,Binary};
-decode_selective([skip|RestPattern],Binary)->
- {ok,RestBinary}=skip_tag(Binary),
- {ok,RestBinary2}=skip_length_and_value(RestBinary),
- decode_selective(RestPattern,RestBinary2);
-decode_selective([[skip_optional,Tag]|RestPattern],Binary) ->
- case skip_optional_tag(Tag,Binary) of
- {ok,RestBinary} ->
- {ok,RestBinary2}=skip_length_and_value(RestBinary),
- decode_selective(RestPattern,RestBinary2);
- missing ->
- decode_selective(RestPattern,Binary)
+decode_selective([skip|RestPattern], Binary)->
+ {ok,RestBinary} = skip_tag(Binary),
+ {ok,RestBinary2} = skip_length_and_value(RestBinary),
+ decode_selective(RestPattern, RestBinary2);
+decode_selective([{skip_optional,Tag}|RestPattern], Binary) ->
+ case skip_optional_tag(Tag, Binary) of
+ {ok,RestBinary} ->
+ {ok,RestBinary2} = skip_length_and_value(RestBinary),
+ decode_selective(RestPattern, RestBinary2);
+ missing ->
+ decode_selective(RestPattern, Binary)
end;
-decode_selective([[choosen,Tag]],Binary) ->
- return_value(Tag,Binary);
-decode_selective([[choosen,Tag]|RestPattern],Binary) ->
- case skip_optional_tag(Tag,Binary) of
- {ok,RestBinary} ->
- {ok,Value} = get_value(RestBinary),
- decode_selective(RestPattern,Value);
- missing ->
- {ok,<<>>}
+decode_selective([{chosen,Tag}], Binary) ->
+ return_value(Tag, Binary);
+decode_selective([{chosen,Tag}|RestPattern], Binary) ->
+ case skip_optional_tag(Tag, Binary) of
+ {ok,RestBinary} ->
+ {ok,Value} = get_value(RestBinary),
+ decode_selective(RestPattern,Value);
+ missing ->
+ {ok,<<>>}
end;
decode_selective(P,_) ->
{error,{asn1,{partial_decode,"bad pattern",P}}}.
return_value(Tag,Binary) ->
- {ok,{Tag,RestBinary}}=get_tag(Binary),
+ {ok,{Tag,RestBinary}} = get_tag(Binary),
{ok,{LenVal,_RestBinary2}} = get_length_and_value(RestBinary),
{ok,<>}.
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index e531b346f85a..e4e3b0b7679c 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -965,11 +965,15 @@ specialized_decodes(Config, Rule, Opts) ->
"PartialDecSeq2.asn",
"PartialDecSeq3.asn",
"PartialDecMyHTTP.asn",
- "MEDIA-GATEWAY-CONTROL.asn",
"P-Record",
- "PartialDecChoExtension.asn"],
+ "PartialDecChoExtension.asn",
+ "OCSP-2013-88.asn1",
+ "PKIX1Explicit88.asn1"],
Config,
- [Rule,legacy_erlang_types,asn1config|Opts]),
+ [Rule,asn1config|Opts]),
+ asn1_test_lib:compile("MEDIA-GATEWAY-CONTROL.asn",
+ Config,
+ [Rule,legacy_erlang_types,asn1config|Opts]),
test_partial_incomplete_decode:test(Config),
test_selective_decode:test().
diff --git a/lib/asn1/test/asn1_SUITE_data/BasicOCSPResponse.ber b/lib/asn1/test/asn1_SUITE_data/BasicOCSPResponse.ber
new file mode 100644
index 000000000000..016ce972c3c4
Binary files /dev/null and b/lib/asn1/test/asn1_SUITE_data/BasicOCSPResponse.ber differ
diff --git a/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config b/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config
index b7dba3c95c8b..cff991dbc3b0 100644
--- a/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config
+++ b/lib/asn1/test/asn1_SUITE_data/MEDIA-GATEWAY-CONTROL.asn1config
@@ -1,7 +1,9 @@
+%% -*- erlang -*-
{exclusive_decode,
- {'MEDIA-GATEWAY-CONTROL',
- [{decode_MegacoMessage_exclusive,['MegacoMessage',[{authHeader,undecoded},{mess,[{mId,undecoded},{messageBody,undecoded}]}]]},
- {decode_Message_version,['Message',[{mId,undecoded},{messageBody,undecoded}]]}]}}.
+ {'MEDIA-GATEWAY-CONTROL',
+ [{decode_MegacoMessage_exclusive,
+ ['MegacoMessage',[{authHeader,undecoded},{mess,[{mId,undecoded},{messageBody,undecoded}]}]]},
+ {decode_Message_version,['Message',[{mId,undecoded},{messageBody,undecoded}]]}]}}.
{selective_decode,
- {'MEDIA-GATEWAY-CONTROL',
- [{decode_MegacoMessage_selective,['MegacoMessage',mess,version]}]}}.
+ {'MEDIA-GATEWAY-CONTROL',
+ [{decode_MegacoMessage_selective,['MegacoMessage',mess,version]}]}}.
diff --git a/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1 b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1
new file mode 100644
index 000000000000..32b1eed96224
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1
@@ -0,0 +1,149 @@
+-- OCSP definition from RFC6960, 1998 Syntax
+
+OCSP-2013-88 {
+ iso(1) identified-organization(3) dod(6) internet(1)
+ security(5) mechanisms(5) pkix(7) id-mod(0)
+ id-mod-ocsp-2013-88(81)
+}
+
+DEFINITIONS EXPLICIT TAGS ::=
+
+BEGIN
+
+IMPORTS
+
+ -- PKIX Certificate Extensions
+ AuthorityInfoAccessSyntax, CRLReason, GeneralName
+ FROM PKIX1Implicit88 { iso(1) identified-organization(3)
+ dod(6) internet(1) security(5) mechanisms(5) pkix(7)
+ id-mod(0) id-pkix1-implicit(19) }
+
+ Name, CertificateSerialNumber, Extensions,
+ id-kp, id-ad-ocsp, Certificate, AlgorithmIdentifier
+ FROM PKIX1Explicit88 { iso(1) identified-organization(3)
+ dod(6) internet(1) security(5) mechanisms(5) pkix(7)
+ id-mod(0) id-pkix1-explicit(18) };
+
+OCSPRequest ::= SEQUENCE {
+ tbsRequest TBSRequest,
+ optionalSignature [0] EXPLICIT Signature OPTIONAL }
+
+TBSRequest ::= SEQUENCE {
+ version [0] EXPLICIT Version DEFAULT v1,
+ requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ requestList SEQUENCE OF Request,
+ requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+
+Signature ::= SEQUENCE {
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+Version ::= INTEGER { v1(0) }
+
+Request ::= SEQUENCE {
+ reqCert CertID,
+ singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+
+CertID ::= SEQUENCE {
+ hashAlgorithm AlgorithmIdentifier,
+ issuerNameHash OCTET STRING, -- Hash of issuer's DN
+ issuerKeyHash OCTET STRING, -- Hash of issuer's public key
+ serialNumber CertificateSerialNumber }
+
+OCSPResponse ::= SEQUENCE {
+ responseStatus OCSPResponseStatus,
+ responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
+
+OCSPResponseStatus ::= ENUMERATED {
+ successful (0), -- Response has valid confirmations
+ malformedRequest (1), -- Illegal confirmation request
+ internalError (2), -- Internal error in issuer
+ tryLater (3), -- Try again later
+ -- (4) is not used
+ sigRequired (5), -- Must sign the request
+ unauthorized (6) -- Request unauthorized
+}
+
+ResponseBytes ::= SEQUENCE {
+ responseType OBJECT IDENTIFIER,
+ response OCTET STRING }
+
+BasicOCSPResponse ::= SEQUENCE {
+ tbsResponseData ResponseData,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+ResponseData ::= SEQUENCE {
+ version [0] EXPLICIT Version DEFAULT v1,
+ responderID ResponderID,
+ producedAt GeneralizedTime,
+ responses SEQUENCE OF SingleResponse,
+ responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+
+ResponderID ::= CHOICE {
+ byName [1] Name,
+ byKey [2] KeyHash }
+
+KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
+ -- (i.e., the SHA-1 hash of the value of the
+ -- BIT STRING subjectPublicKey [excluding
+ -- the tag, length, and number of unused
+ -- bits] in the responder's certificate)
+
+SingleResponse ::= SEQUENCE {
+ certID CertID,
+ certStatus CertStatus,
+ thisUpdate GeneralizedTime,
+ nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+ singleExtensions [1] EXPLICIT Extensions OPTIONAL }
+
+CertStatus ::= CHOICE {
+ good [0] IMPLICIT NULL,
+ revoked [1] IMPLICIT RevokedInfo,
+ unknown [2] IMPLICIT UnknownInfo }
+
+RevokedInfo ::= SEQUENCE {
+ revocationTime GeneralizedTime,
+ revocationReason [0] EXPLICIT CRLReason OPTIONAL }
+
+UnknownInfo ::= NULL
+
+ArchiveCutoff ::= GeneralizedTime
+
+AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER
+
+ServiceLocator ::= SEQUENCE {
+ issuer Name,
+ locator AuthorityInfoAccessSyntax }
+
+CrlID ::= SEQUENCE {
+ crlUrl [0] EXPLICIT IA5String OPTIONAL,
+ crlNum [1] EXPLICIT INTEGER OPTIONAL,
+ crlTime [2] EXPLICIT GeneralizedTime OPTIONAL }
+
+PreferredSignatureAlgorithms ::= SEQUENCE OF PreferredSignatureAlgorithm
+
+PreferredSignatureAlgorithm ::= SEQUENCE {
+ sigIdentifier AlgorithmIdentifier,
+ certIdentifier AlgorithmIdentifier OPTIONAL }
+
+Nonce ::= OCTET STRING
+
+-- Object Identifiers
+
+-- Already defined in PKIX1Implicit88
+--id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
+id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
+id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
+id-pkix-ocsp-nonce OBJECT IDENTIFIER ::= { id-pkix-ocsp 2 }
+id-pkix-ocsp-crl OBJECT IDENTIFIER ::= { id-pkix-ocsp 3 }
+id-pkix-ocsp-response OBJECT IDENTIFIER ::= { id-pkix-ocsp 4 }
+id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 }
+id-pkix-ocsp-archive-cutoff OBJECT IDENTIFIER ::= { id-pkix-ocsp 6 }
+id-pkix-ocsp-service-locator OBJECT IDENTIFIER ::= { id-pkix-ocsp 7 }
+id-pkix-ocsp-pref-sig-algs OBJECT IDENTIFIER ::= { id-pkix-ocsp 8 }
+id-pkix-ocsp-extended-revoke OBJECT IDENTIFIER ::= { id-pkix-ocsp 9 }
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config
new file mode 100644
index 000000000000..45aadf179d00
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/OCSP-2013-88.asn1config
@@ -0,0 +1,23 @@
+%% -*- erlang -*-
+{exclusive_decode,
+ {'OCSP-2013-88',
+ [
+ {decode_version_undec,
+ ['BasicOCSPResponse',[{tbsResponseData,[{version,undecoded}]}]]},
+ {decode_responderID_undec,
+ ['BasicOCSPResponse',[{tbsResponseData,[{responderID,undecoded}]}]]},
+ {decode_producedAt_undec,
+ ['BasicOCSPResponse',[{tbsResponseData,[{producedAt,undecoded}]}]]},
+ {decode_responses_undec,
+ ['BasicOCSPResponse',[{tbsResponseData,[{responses,undecoded}]}]]},
+ {decode_responses_parts,
+ ['BasicOCSPResponse',[{tbsResponseData,[{responses,parts}]}]]},
+ {decode_tbsResponseData_undec,
+ ['BasicOCSPResponse',[{tbsResponseData,undecoded}]]},
+ {decode_BasicOCSPResponse_signature_undec,
+ ['BasicOCSPResponse',[{signature,undecoded}]]},
+ {decode_BasicOCSPResponse_certs_undec,
+ ['BasicOCSPResponse',[{certs,undecoded}]]},
+ {decode_BasicOCSPResponse_certs_parts,
+ ['BasicOCSPResponse',[{certs,parts}]]}
+ ]}}.
diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config
index 0d91e0c3b3dd..bf388c0754ad 100644
--- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config
+++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq.asn1config
@@ -1,14 +1,19 @@
-{selective_decode,{'PartialDecSeq',
- [{selected_decode_F1,['F',fb,b,[1],a]},
- {selected_decode_F2,['F',fb,b]},
- {selected_decode_F3,['F',fb,b,[1]]},
- {selected_decode_F4,['F',fb,d,da,[1],b,a]},
- {selected_decode_E1,['E',d,dc,dcc,dcca]}]}}.
-{exclusive_decode,{'PartialDecSeq',
- [{decode_F_fb_incomplete,['F',[{fb,[{b,parts},{d,undecoded}]}]]},
- {decode_D_incomplete,['D',[{a,undecoded}]]},
- {decode_F_fb_exclusive2,['F',[{fb,[{b,parts},{d,[{da,parts}]}]}]]}, {decode_F_fb_exclusive3,['F',[{fb,[{b,parts},{d,[{da,parts},{dc,[{dcc,undecoded}]}]}]}]]}]}}.
-{module_name,'Seq.asn1'}.
-{compile_options,[ber,debug_info]}.
-{multifile_compile,['M1.asn','M2.asn']}.
-
+%% -*- erlang -*-
+{selective_decode,
+ {'PartialDecSeq',
+ [{selected_decode_F1_1,['F',fb,b,[1],a]},
+ {selected_decode_F1_2,['F',fb,b,[2],a]},
+ {selected_decode_F2,['F',fb,b]},
+ {selected_decode_F3,['F',fb,b,[1]]},
+ {selected_decode_F4,['F',fb,d,da,[1],b,a]},
+ {selected_decode_F5,['F',fb]},
+ {selected_decode_E1,['E',d,dc,dcc,dcca]},
+ {selected_decode_E2,['E',b]}
+ ]}}.
+{exclusive_decode,
+ {'PartialDecSeq',
+ [{decode_E_b_incomplete,['E',[{b,parts}]]},
+ {decode_F_fb_incomplete,['F',[{fb,[{b,parts},{d,undecoded}]}]]},
+ {decode_D_incomplete,['D',[{a,undecoded}]]},
+ {decode_F_fb_exclusive2,['F',[{fb,[{b,parts},{d,[{da,parts}]}]}]]},
+ {decode_F_fb_exclusive3,['F',[{fb,[{b,parts},{d,[{da,parts},{dc,[{dcc,undecoded}]}]}]}]]}]}}.
diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn
index 2e77d250d268..33a88048b1e3 100644
--- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn
+++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn
@@ -8,6 +8,13 @@ B ::= CHOICE {
c S
}
+Bext ::= CHOICE {
+ a INTEGER,
+ b SEQUENCE {aa INTEGER, ba INTEGER},
+ ...,
+ c S
+}
+
S ::= SEQUENCE {
a BOOLEAN,
b BOOLEAN
@@ -26,4 +33,14 @@ D ::= SEQUENCE {
b BOOLEAN
}
+SeqChoice ::= SEQUENCE {
+ c CHOICE {
+ b BOOLEAN,
+ i INTEGER,
+ s VisibleString
+ },
+ d OCTET STRING
+}
+
+
END
diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config
index d9967d995867..776c106ab475 100644
--- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config
+++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq2.asn1config
@@ -1,4 +1,15 @@
-%{partial_decode,{{module,'Seq2'},['F',fb,b,[1],a]}}.
-{exclusive_decode,{'PartialDecSeq2',
- [{decode_A_c_b_incomplete,['A',[{c,[{a,undecoded},{b,undecoded}]}]]},
- {decode_D_incomplete,['D',[{a,undecoded}]]}]}}.
+%% -*- erlang -*-
+{exclusive_decode,
+ {'PartialDecSeq2',
+ [{decode_A_a_incomplete,['A',[{a,undecoded}]]},
+ {decode_A_c_b_incomplete,['A',[{c,[{a,undecoded},{b,undecoded}]}]]},
+ {decode_D_incomplete,['D',[{a,undecoded}]]},
+ {decode_Bext_c_incomplete,['Bext',[{c,undecoded}]]},
+ {decode_Bext_c_b_incomplete,['Bext',[{c,[{b,undecoded}]}]]},
+ {decode_SeqChoice_c_b_d_incomplete,
+ ['SeqChoice',[{c,[{b,undecoded}]},
+ {d,undecoded}]]},
+ {decode_SeqChoice_c_bis_incomplete,
+ ['SeqChoice',
+ [{c,[{b,undecoded},{i,undecoded},{s,undecoded}]}]]}
+ ]}}.
diff --git a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config
index 44d22aa1d426..72423d682d24 100644
--- a/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config
+++ b/lib/asn1/test/asn1_SUITE_data/PartialDecSeq3.asn1config
@@ -1,2 +1,7 @@
-{exclusive_decode,{'PartialDecSeq3',
- [{decode_S1_incomplete,['S1',[{b,[{c,parts}]},{c,[{a,parts}]},{d,parts}]]}]}}.
+%% -*- erlang -*-
+{exclusive_decode,
+ {'PartialDecSeq3',
+ [{decode_S1_incomplete,['S1',[{b,[{c,parts}]},{c,[{a,parts}]},{d,parts}]]},
+ {decode_S1_b_incomplete,['S1',[{b,undecoded}]]},
+ {decode_S3_second,['S3',[{second,undecoded}]]}
+ ]}}.
diff --git a/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config b/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config
index b0ccd7d34cc1..74c95105d1b1 100644
--- a/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config
+++ b/lib/asn1/test/asn1_SUITE_data/TCAPPackage.asn1config
@@ -1,12 +1,15 @@
-{exclusive_decode, {'TCAPPackage',
- [{decode_PackageType, ['PackageType',
- [{unidirectional, undecoded},
- {queryWithPerm, [{componentPortion, parts}]},
- {queryWithoutPerm, undecoded},
- {response, [{componentPortion, parts}]},
- {conversationWithPerm, undecoded},
- {conversationWithoutPerm, undecoded},
- {abort, undecoded}]]},
- {decode_TransactionPDU, ['TransactionPDU',
- [{componentPortion, parts}]]}]}}.
-
+%% -*- erlang -*-
+{exclusive_decode,
+ {'TCAPPackage',
+ [{decode_PackageType,
+ ['PackageType',
+ [{unidirectional, undecoded},
+ {queryWithPerm, [{componentPortion, parts}]},
+ {queryWithoutPerm, undecoded},
+ {response, [{componentPortion, parts}]},
+ {conversationWithPerm, undecoded},
+ {conversationWithoutPerm, undecoded},
+ {abort, undecoded}]]},
+ {decode_TransactionPDU,
+ ['TransactionPDU',
+ [{componentPortion, parts}]]}]}}.
diff --git a/lib/asn1/test/asn1_app_SUITE.erl b/lib/asn1/test/asn1_app_SUITE.erl
index 12b0b509ebb4..1cbac5a62cb8 100644
--- a/lib/asn1/test/asn1_app_SUITE.erl
+++ b/lib/asn1/test/asn1_app_SUITE.erl
@@ -133,7 +133,8 @@ check_asn1ct_modules(Extra) ->
asn1ct_gen_ber_bin_v2,asn1ct_value,
asn1ct_tok,asn1ct_parser2,asn1ct_table,
asn1ct_imm,asn1ct_func,asn1ct_rtt,
- asn1ct_eval_ext,asn1ct_gen_jer],
+ asn1ct_eval_ext,asn1ct_gen_jer,
+ asn1ct_partial_decode],
case Extra -- ASN1CTMods of
[] ->
ok;
diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl
index aa2b36bbd7e7..eeb83deb08ec 100644
--- a/lib/asn1/test/error_SUITE.erl
+++ b/lib/asn1/test/error_SUITE.erl
@@ -20,7 +20,9 @@
-module(error_SUITE).
-export([suite/0,all/0,groups/0,
- already_defined/1,bitstrings/1,
+ already_defined/1,
+ bad_config_exclusive/1,bad_config_selective/1,
+ bitstrings/1,
classes/1,constraints/1,constructed/1,enumerated/1,
imports_exports/1,instance_of/1,integers/1,objects/1,
object_field_extraction/1,oids/1,rel_oids/1,
@@ -38,6 +40,8 @@ groups() ->
[{p,parallel(),
[already_defined,
bitstrings,
+ bad_config_exclusive,
+ bad_config_selective,
classes,
constraints,
constructed,
@@ -91,6 +95,99 @@ already_defined(Config) ->
} = run(P, Config),
ok.
+bad_config_exclusive(Config) ->
+ M = 'BadConfigExclusive',
+ P = {M,
+ <<"BadConfigExclusive DEFINITIONS AUTOMATIC TAGS ::= BEGIN
+ Seq ::= SEQUENCE {
+ a INTEGER,
+ b SEQUENCE OF INTEGER,
+ c BOOLEAN
+ }
+ END\n">>},
+
+ Conf1 = [{exclusive_decode,{'WrongModuleName',[whatever]}}],
+ {error,{bad_module_name,'WrongModuleName',M}} = run(P, Config, Conf1),
+
+ Conf2 = [{exclusive_decode,wrong}],
+ {error,{bad_exclusive_decode,wrong}} = run(P, Config, Conf2),
+
+ Conf3 = [{exclusive_decode,{M,[wrong]}}],
+ {error,{bad_exclusive_decode,[wrong]}} = run(P, Config, Conf3),
+
+ Conf4 = [{exclusive_decode,{M,{42,[top_type,[]]}}}],
+ {error,{bad_exclusive_decode,_}} = run(P, Config, Conf4),
+
+ Conf5 = [{exclusive_decode,
+ {M, [{exclusive_Seq, ['TopType', [{b,parts}]]}]}}],
+ {error,{undefined_type,'TopType'}} = run(P, Config, Conf5),
+
+ Conf6 = [{exclusive_decode,
+ {M, [{exclusive_Seq, ['Seq', [{d,parts}]]}]}}],
+ {error,{undefined_name,d}} = run(P, Config, Conf6),
+
+ Conf7 = [{exclusive_decode,
+ {M, [{exclusive_Seq, ['Seq', [{b,whatever}]]}]}}],
+ {error,{bad_decode_instruction,{b,whatever}}} = run(P, Config, Conf7),
+
+ Conf8 = [{exclusive_decode,
+ {M, [{exclusive_Seq, ['Seq', [{a,b,c}]]}]}}],
+ {error,{bad_decode_instruction,{a,b,c}}} = run(P, Config, Conf8),
+
+ ok.
+
+bad_config_selective(Config) ->
+ M = 'BadConfigSelective',
+ P = {M,
+ <<"BadConfigSelective DEFINITIONS AUTOMATIC TAGS ::= BEGIN
+ Seq ::= SEQUENCE {
+ a INTEGER,
+ b SEQUENCE OF INTEGER,
+ c BOOLEAN,
+ cc CHOICE {
+ ca INTEGER,
+ cb BOOLEAN
+ }
+ }
+ END\n">>},
+
+ Conf1 = [{selective_decode,{'WrongModuleName',[whatever]}}],
+ {error,{bad_module_name,'WrongModuleName',M}} = run(P, Config, Conf1),
+
+ Conf2 = [{selective_decode,wrong}],
+ {error,{bad_selective_decode,wrong}} = run(P, Config, Conf2),
+
+ Conf3 = [{selective_decode,{M,[wrong]}}],
+ {error,{bad_selective_decode,wrong}} = run(P, Config, Conf3),
+
+ Conf4 = [{selective_decode,{M,[{f,not_list}]}}],
+ {error,{bad_selective_decode_type_list,not_list}} = run(P, Config, Conf4),
+
+ Conf5 = [{selective_decode,{M,{42,[top_type,[]]}}}],
+ {error,{bad_selective_decode,{42,_}}} = run(P, Config, Conf5),
+
+ Conf6 = [{selective_decode,
+ {M, [{only_Seq_b, ['TopType', [{b,parts}]]}]}}],
+ {error,{undefined_type,'TopType'}} = run(P, Config, Conf6),
+
+ Conf7 = [{selective_decode,
+ {M, [{only_Seq_b, ['Seq', d]}]}}],
+ {error,{undefined_name,d}} = run(P, Config, Conf7),
+
+ Conf8 = [{selective_decode,
+ {M, [{only_Seq_b, ['Seq', b, whatever]}]}}],
+ {error,{bad_selective_decode_element,whatever}} = run(P, Config, Conf8),
+
+ Conf9 = [{selective_decode,
+ {M, [{only_Seq_something, ['Seq', cc, whatever]}]}}],
+ {error,{undefined_name,whatever}} = run(P, Config, Conf9),
+
+ Conf10 = [{selective_decode,
+ {M, [{only_Seq_something, ['Seq', a, x]}]}}],
+ {error,{stepping_into_primitive,[a,x]}} = run(P, Config, Conf10),
+
+ ok.
+
bitstrings(Config) ->
M = 'Bitstrings',
P = {M,
@@ -928,7 +1025,6 @@ values(Config) ->
} = run(P, Config),
ok.
-
run({Mod,Spec}, Config) ->
Base = atom_to_list(Mod) ++ ".asn1",
File = filename:join(proplists:get_value(priv_dir, Config), Base),
@@ -936,3 +1032,22 @@ run({Mod,Spec}, Config) ->
Include = filename:join(filename:dirname(Include0), "asn1_SUITE_data"),
ok = file:write_file(File, Spec),
asn1ct:compile(File, [{i, Include}]).
+
+run({Mod,Spec}, Config, Asn1ConfigTerm) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+
+ Asn1ConfigFile = filename:join(PrivDir, atom_to_list(Mod) ++ ".asn1config"),
+ Asn1ConfigData = [io_lib:format("~p. \n", [Term]) || Term <- Asn1ConfigTerm],
+ ok = file:write_file(Asn1ConfigFile, Asn1ConfigData),
+
+ Base = atom_to_list(Mod) ++ ".asn1",
+ File = filename:join(PrivDir, Base),
+ ok = file:write_file(File, Spec),
+
+ case asn1ct:compile(File, [{i, PrivDir}, asn1config]) of
+ {error,[{structured_error,{Asn1ConfigFile,none},
+ asn1ct_partial_decode,Error}]} ->
+ {error,Error};
+ Other ->
+ Other
+ end.
diff --git a/lib/asn1/test/test_partial_incomplete_decode.erl b/lib/asn1/test/test_partial_incomplete_decode.erl
index 7c5cfab10a82..f4c46f3cb087 100644
--- a/lib/asn1/test/test_partial_incomplete_decode.erl
+++ b/lib/asn1/test/test_partial_incomplete_decode.erl
@@ -25,49 +25,89 @@
-include_lib("common_test/include/ct.hrl").
test(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ test_PartialDecSeq(),
+ test_PartialDecSeq2(),
+ test_PartialDecSeq3(),
+ test_MyHTTPMsg(),
+ test_megaco(DataDir),
+ test_OCSP(DataDir),
+ ok.
+
+test_PartialDecSeq() ->
+ M = 'PartialDecSeq',
+
FMsg = msg('F'),
- Bytes1 = roundtrip('PartialDecSeq', 'F', FMsg),
- {ok,IncFMsg} = 'PartialDecSeq':decode_F_fb_incomplete(Bytes1),
- decode_parts('F', IncFMsg),
- {ok,IncF2Msg} = 'PartialDecSeq':decode_F_fb_exclusive2(Bytes1),
- decode_parts('F2', IncF2Msg),
-
+ test_exclusive(fun M:decode_F_fb_incomplete/1, 'F', FMsg),
+
DMsg = msg('D'),
- Bytes2 = roundtrip('PartialDecSeq', 'D', DMsg),
- {ok,IncDMsg} = 'PartialDecSeq':decode_D_incomplete(Bytes2),
- decode_parts('D', IncDMsg),
+ test_exclusive(fun M:decode_D_incomplete/1, 'D', DMsg),
F3Msg = msg('F3'),
- BytesF3 = roundtrip('PartialDecSeq', 'F', F3Msg),
- {ok,IncF3Msg} = 'PartialDecSeq':decode_F_fb_exclusive3(BytesF3),
- decode_parts('F3', IncF3Msg),
-
- AMsg = msg('A'),
- Bytes3 = roundtrip('PartialDecSeq2', 'A', AMsg),
- {ok,IncFMsg3} = 'PartialDecSeq2':decode_A_c_b_incomplete(Bytes3),
- decode_parts('A', IncFMsg3),
-
- MyHTTPMsg = msg('GetRequest'),
- Bytes4 = roundtrip('PartialDecMyHTTP', 'GetRequest', MyHTTPMsg),
- {ok,IncFMsg4} = 'PartialDecMyHTTP':decode_GetRequest_incomplete(Bytes4),
- decode_parts('GetRequest', IncFMsg4),
-
+ test_exclusive(fun M:decode_F_fb_exclusive3/1, 'F', F3Msg),
+
+ EMsg = msg('E'),
+ test_exclusive(fun M:decode_E_b_incomplete/1, 'E', EMsg),
+
+ ok.
+
+test_PartialDecSeq2() ->
+ M = 'PartialDecSeq2',
+
+ %% Test DEFAULT value.
+ AMsg1 = msg('A_1'),
+ AMsg1Encoded = roundtrip(M, 'A', AMsg1),
+ {ok,AMsg1} = M:decode_A_a_incomplete(AMsg1Encoded),
+
+ AMsg2 = msg('A_2'),
+ test_exclusive(fun M:decode_A_a_incomplete/1, 'A', AMsg2),
+ test_exclusive(fun M:decode_A_c_b_incomplete/1, 'A', AMsg2),
+
+ SMsg = {'S',true,false},
+ BextMsg = {c,SMsg},
+
+ test_exclusive(fun M:decode_Bext_c_incomplete/1, 'Bext', BextMsg),
+ test_exclusive(fun M:decode_Bext_c_b_incomplete/1, 'Bext', BextMsg),
+
+ T = 'SeqChoice',
+
+ SeqChoiceMsg1 = {'SeqChoice',{b,true},<<"abc">>},
+ test_exclusive(fun M:decode_SeqChoice_c_b_d_incomplete/1, T, SeqChoiceMsg1),
+
+ test_exclusive(fun M:decode_SeqChoice_c_bis_incomplete/1, T, SeqChoiceMsg1),
+
+ SeqChoiceMsg2 = {'SeqChoice',{i,42},<<"cde">>},
+ test_exclusive(fun M:decode_SeqChoice_c_bis_incomplete/1, T, SeqChoiceMsg2),
+
+ SeqChoiceMsg3 = {'SeqChoice',{s,"xyz"},<<"fgh">>},
+ test_exclusive(fun M:decode_SeqChoice_c_bis_incomplete/1, T, SeqChoiceMsg3),
+
+ ok.
+
+test_PartialDecSeq3() ->
+ M = 'PartialDecSeq3',
+
MsgS1_1 = msg('S1_1'),
- Bytes5 = roundtrip('PartialDecSeq3', 'S1', MsgS1_1),
- {ok,IncFMsg5} = 'PartialDecSeq3':decode_S1_incomplete(Bytes5),
- decode_parts('S1_1', IncFMsg5),
+ test_exclusive(fun M:decode_S1_incomplete/1, 'S1', MsgS1_1),
+ test_exclusive(fun M:decode_S1_b_incomplete/1, 'S1', MsgS1_1),
MsgS1_2 = msg('S1_2'),
- Bytes6 = roundtrip('PartialDecSeq3', 'S1', MsgS1_2),
- {ok,IncFMsg6} = 'PartialDecSeq3':decode_S1_incomplete(Bytes6),
- decode_parts('S1_2', IncFMsg6),
+ test_exclusive(fun M:decode_S1_incomplete/1, 'S1', MsgS1_2),
+
+ MsgS3 = msg('S3'),
+ test_exclusive(fun M:decode_S3_second/1, 'S3', MsgS3),
- %% test of MEDIA-GATEWAY-CONTROL
- test_megaco(Config),
ok.
-test_megaco(Config) ->
- DataDir = proplists:get_value(data_dir, Config),
+test_MyHTTPMsg() ->
+ MyHTTPMsg = msg('GetRequest'),
+ Bytes1 = roundtrip('PartialDecMyHTTP', 'GetRequest', MyHTTPMsg),
+ {ok,IncFMsg4} = 'PartialDecMyHTTP':decode_GetRequest_incomplete(Bytes1),
+ decode_parts('GetRequest', IncFMsg4),
+
+ ok.
+
+test_megaco(DataDir) ->
Files = filelib:wildcard(filename:join([DataDir,megacomessages,"*.val"])),
Mod = 'MEDIA-GATEWAY-CONTROL',
lists:foreach(fun(File) ->
@@ -87,34 +127,67 @@ exclusive_decode(Bin,F) ->
{ok,_} = Mod:decode_part(MsgMBodyKey,MsgMBody),
ok.
-decode_parts('F',PartDecMsg) ->
- {fb,{'E',35,{NameE_b,ListBinE_b},false,{NameE_d,BinE_d}}} = PartDecMsg,
- {ok,[{'D',3,true}|_]} = 'PartialDecSeq':decode_part(NameE_b,ListBinE_b),
- {ok,{'D',3,true}} = 'PartialDecSeq':decode_part(NameE_b,
- hd(ListBinE_b)),
- {ok,{da,[{'A',16,{'D',17,true}}]}} =
- 'PartialDecSeq':decode_part(NameE_d,BinE_d),
- ok;
-decode_parts('F2',PartDecMsg) ->
- {fb,{'E',35,{E_bkey,E_b},false,{da,{E_d_akey,E_d_a}}}} = PartDecMsg,
- {ok,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]} = 'PartialDecSeq':decode_part(E_bkey,E_b),
- {ok,[{'A',16,{'D',17,true}}]} = 'PartialDecSeq':decode_part(E_d_akey,E_d_a);
+test_OCSP(DataDir) ->
+ Mod = 'OCSP-2013-88',
+
+ ResponseData = {'ResponseData',
+ v1, %Version
+ {byKey,<<"key hash">>},
+ "factory",
+ [],
+ asn1_NOVALUE},
+
+ Type = 'BasicOCSPResponse',
+
+ BasicMsg = {Type,
+ ResponseData,
+ {'AlgorithmIdentifier',Mod:'id-pkix-ocsp-basic'(),asn1_NOVALUE},
+ <<"signature">>,
+ []},
+
+ test_exclusive(fun Mod:decode_version_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_responderID_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_producedAt_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_responses_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_responses_parts/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_tbsResponseData_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_BasicOCSPResponse_signature_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_undec/1, Type, BasicMsg),
+ test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_parts/1, Type, BasicMsg),
+
+ %% Test undecoded/parts for an absent element.
+ MsgWithoutCerts =
+ {Type,
+ ResponseData,
+ {'AlgorithmIdentifier',Mod:'id-pkix-ocsp-basic'(),asn1_NOVALUE},
+ <<"signature">>,
+ asn1_NOVALUE},
+ {ok,Enc} = Mod:encode(Type, MsgWithoutCerts),
+ {ok,MsgWithoutCerts} = Mod:decode_BasicOCSPResponse_certs_undec(Enc),
+ {ok,MsgWithoutCerts} = Mod:decode_BasicOCSPResponse_certs_parts(Enc),
+
+ DataFileName = filename:join(DataDir, "BasicOCSPResponse.ber"),
+ {ok,CannedData} = file:read_file(DataFileName),
+ {ok,HugeMsg} = Mod:decode('BasicOCSPResponse', CannedData),
+
+ %% Decode version with a default value.
+ {ok,HugeMsg} = Mod:decode_version_undec(CannedData),
+
+ test_exclusive(fun Mod:decode_responderID_undec/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_producedAt_undec/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_responses_undec/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_responses_parts/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_tbsResponseData_undec/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_BasicOCSPResponse_signature_undec/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_undec/1, Type, HugeMsg),
+ test_exclusive(fun Mod:decode_BasicOCSPResponse_certs_parts/1, Type, HugeMsg),
+
+ ok.
decode_parts('F3',PartDecMsg) ->
{fb,{'E',10,{E_bkey,E_b},false,{dc,{'E_d_dc',13,true,{E_d_dc_dcckey,E_d_dc_dcc}}}}} = PartDecMsg,
{ok,[{'D',11,true},{'D',12,false}]} = 'PartialDecSeq':decode_part(E_bkey,E_b),
{ok,{'E_d_dc_dcc',14,15}} = 'PartialDecSeq':decode_part(E_d_dc_dcckey,E_d_dc_dcc);
-
-
-decode_parts('D',PartDecMsg) ->
- {'D',{NameD_a,BinD_a},true} = PartDecMsg,
- {ok,123} = 'PartialDecSeq':decode_part(NameD_a,BinD_a),
- ok;
-decode_parts('A',PartDecMsg) ->
- {'A',12,{c,{'S',true,false}},{b,{NameA_c_b,BinA_c_b}}} = PartDecMsg,
- {ok,{'A_c_b',false,false}} =
- 'PartialDecSeq2':decode_part(NameA_c_b,BinA_c_b),
- ok;
decode_parts('GetRequest',PartDecMsg) ->
{'GetRequest',true,false,
{'AcceptTypes',[html,'plain-text',gif,jpeg],
@@ -126,34 +199,17 @@ decode_parts('GetRequest',PartDecMsg) ->
{ok,"hell"} =
'PartialDecMyHTTP':decode_part(NameAcceptTypes_others,
hd(ListBinAcceptTypes_others)),
- ok;
-decode_parts('S1_1',PartDecMsg) ->
- {'S1',14,{'S2',false,12,{NameS2c,BinS2c}},
- {_,{NameS1c_a,ListBinS1c_a}},{NameS1d,BinS1d}} = PartDecMsg,
- {ok,[{'S3',10,"PrintableString","OCTETSTRING",
- [one,two,three,four]}|_Rest1]} =
- 'PartialDecSeq3':decode_part(NameS2c,BinS2c),
- {ok,[{'S3',10,"PrintableString","OCTETSTRING",
- [one,two,three,four]}|_Rest2]} =
- 'PartialDecSeq3':decode_part(NameS1c_a,ListBinS1c_a),
- {ok,{'S3',10,"PrintableString","OCTETSTRING",
- [one,two,three,four]}} =
- 'PartialDecSeq3':decode_part(NameS1c_a,hd(ListBinS1c_a)),
- {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} =
- 'PartialDecSeq3':decode_part(NameS1d,BinS1d),
- ok;
-decode_parts('S1_2',PartDecMsg) ->
- {'S1',14,{'S2',false,12,_S2c},S1c_b,{NameS1d,BinS1d}} = PartDecMsg,
- {b,{'C1_b',11,true,
- {'S4',{'Name',"Hans","HCA","Andersen"},"MSc"}}}=S1c_b,
- {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} =
- 'PartialDecSeq3':decode_part(NameS1d,BinS1d),
ok.
-
-
+msg('E') ->
+ {'E',35,msg('D_many'),false,{da,[{'A',16,{'D',17,true}}]}};
+
+msg('D_many') ->
+ [{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},
+ {'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}];
+
msg('F') ->
- {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}};
+ {fb,msg('E')};
msg('F3') ->
{fb,{'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}};
@@ -161,8 +217,10 @@ msg('F3') ->
msg('D') ->
{'D',123,true};
-msg('A') ->
- {'A',12,{c,{'S',true,false}},{b,{'A_c_b',false,false}}};
+msg('A_1') ->
+ {'A',15,{c,{'S',true,false}},{b,{'A_c_b',false,false}}};
+msg('A_2') ->
+ {'A',42,{c,{'S',true,false}},{b,{'A_c_b',false,false}}};
msg('GetRequest') ->
{'GetRequest',true,false,
@@ -181,7 +239,7 @@ msg('C1_a') ->
msg('C1_b') ->
{b,{'C1_b',11,true,msg('S4')}};
msg('S3') ->
- {'S3',10,"PrintableString","OCTETSTRING",[one,two,three,four]};
+ {'S3',10,"PrintableString",<<"OCTETSTRING">>,[one,two,three,four]};
msg('S4') ->
{'S4',msg('Name'),"MSc"};
msg('SO1') ->
@@ -191,3 +249,52 @@ msg('Name') ->
roundtrip(M, T, V) ->
asn1_test_lib:roundtrip_enc(M, T, V).
+
+test_exclusive(DecodeFun, Type, Msg) ->
+ {module,Mod} = erlang:fun_info(DecodeFun, module),
+ Encoded = roundtrip(Mod, Type, Msg),
+ case DecodeFun(Encoded) of
+ {ok,Msg} ->
+ error({should_be_different,Msg});
+ {ok,Decoded} ->
+ case dec_parts(Decoded, Msg, Mod) of
+ Msg ->
+ ok;
+ OtherMsg ->
+ io:format("""
+ Partial decoding:
+ ~p
+
+ Expected:
+ ~p
+
+ Got:
+ ~p
+ """, [Decoded,Msg,OtherMsg]),
+ error(full_and_partial_decode_differ)
+ end
+ end.
+
+dec_parts(Same, Same, _Mod) ->
+ Same;
+dec_parts({Name,Parts}, Expected, Mod) when is_atom(Name), is_list(Parts), is_list(Expected) ->
+ [begin
+ {ok,Dec} = Mod:decode_part(Name, Bin),
+ Dec
+ end || Bin <- Parts];
+dec_parts({Name,Undec}, _Expected, Mod) when is_atom(Name), is_binary(Undec) ->
+ {ok,Dec} = Mod:decode_part(Name, Undec),
+ Dec;
+dec_parts({Name,{Tag,_}=Undec}, _Expected, Mod) when is_atom(Name), is_integer(Tag) ->
+ {ok,Dec} = Mod:decode_part(Name, Undec),
+ Dec;
+dec_parts(Tuple0, Expected, Mod) when is_tuple(Tuple0), is_tuple(Expected) ->
+ Tuple = dec_parts_list(tuple_to_list(Tuple0), tuple_to_list(Expected), Mod),
+ list_to_tuple(Tuple);
+dec_parts(List, Expected, Mod) when is_list(List), is_list(Expected) ->
+ dec_parts_list(List, Expected, Mod).
+
+dec_parts_list([H1|T1], [H2|T2], Mod) ->
+ [dec_parts(H1, H2, Mod) | dec_parts_list(T1, T2, Mod)];
+dec_parts_list([], [], _Mod) ->
+ [].
diff --git a/lib/asn1/test/test_selective_decode.erl b/lib/asn1/test/test_selective_decode.erl
index 3220ccc2ed09..5953292f0f86 100644
--- a/lib/asn1/test/test_selective_decode.erl
+++ b/lib/asn1/test/test_selective_decode.erl
@@ -26,17 +26,22 @@
test() ->
FMsg = msg('F'),
Bytes = roundtrip('PartialDecSeq', 'F', FMsg),
- {ok,3} = 'PartialDecSeq':selected_decode_F1(Bytes),
+ {ok,3} = 'PartialDecSeq':selected_decode_F1_1(Bytes),
+ {ok,4} = 'PartialDecSeq':selected_decode_F1_2(Bytes),
{ok,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},
{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},
{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]} =
'PartialDecSeq':selected_decode_F2(Bytes),
{ok,{'D',3,true}} = 'PartialDecSeq':selected_decode_F3(Bytes),
{ok,17} = 'PartialDecSeq':selected_decode_F4(Bytes),
-
+ E_from_F = element(2, FMsg),
+ {ok,E_from_F} = 'PartialDecSeq':selected_decode_F5(Bytes),
+
EMsg = msg('E'),
Bytes2 = roundtrip('PartialDecSeq', 'E', EMsg),
{ok,14} = 'PartialDecSeq':selected_decode_E1(Bytes2),
+ Emsg_b = msg('E_b'),
+ {ok,Emsg_b} = 'PartialDecSeq':selected_decode_E2(Bytes2),
MGCMsg = msg('M-G-C'),
Bytes3 = roundtrip('MEDIA-GATEWAY-CONTROL', 'MegacoMessage', MGCMsg),
@@ -61,7 +66,10 @@ msg('F') ->
{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}};
msg('E') ->
- {'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}};
+ {'E',10,msg('E_b'),false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}};
+
+msg('E_b') ->
+ [{'D',11,true},{'D',12,false}];
msg('M-G-C') ->
{'MegacoMessage',asn1_NOVALUE,{'Message',1,{ip4Address,{'IP4Address',[125,125,125,111],55555}},{transactions,[{transactionReply,{'TransactionReply',50007,asn1_NOVALUE,{actionReplies,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,[{auditValueReply,{auditResult,{'AuditResult',{'TerminationID',[],[255,255,255]},[{mediaDescriptor,{'MediaDescriptor',asn1_NOVALUE,{multiStream,[{'StreamDescriptor',1,{'StreamParms',{'LocalControlDescriptor',sendRecv,asn1_NOVALUE,asn1_NOVALUE,[{'PropertyParm',[0,11,0,7],[[52,48]],asn1_NOVALUE}]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,53,46,49,50,53,46,49,50,53,46,49,49,49]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,49,49,49,49,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,52,46,49,50,52,46,49,50,52,46,50,50,50]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,50,50,50,50,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]}}}]}}},{packagesDescriptor,[{'PackagesItem',[0,11],1},{'PackagesItem',[0,11],1}]},{statisticsDescriptor,[{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},{'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},{'StatisticsParameter',[0,12,0,5],[[55,48,48]]},{'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},{'StatisticsParameter',[0,12,0,6],[[48,46,50]]},{'StatisticsParameter',[0,12,0,7],[[50,48]]},{'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}};
diff --git a/lib/asn1/test/test_special_decode_performance.erl b/lib/asn1/test/test_special_decode_performance.erl
index 35c396575b50..6814bbfaf3c4 100644
--- a/lib/asn1/test/test_special_decode_performance.erl
+++ b/lib/asn1/test/test_special_decode_performance.erl
@@ -120,7 +120,7 @@ loop2(Mod,FS,Bin,N) ->
get_selective_funcs('PartialDecSeq') ->
% [selected_decode_F1,selected_decode_F2,selected_decode_F3,selected_decode_F4];
- [selected_decode_F1,selected_decode_F3,selected_decode_F4];
+ [selected_decode_F1_1,selected_decode_F3,selected_decode_F4];
get_selective_funcs('MEDIA-GATEWAY-CONTROL') ->
[decode_MegacoMessage_selective].
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 6dc4dc0c87d2..c27b02997eeb 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1 +1 @@
-ASN1_VSN = 5.2
+ASN1_VSN = 5.2.1
diff --git a/lib/common_test/doc/src/basics_chapter.xml b/lib/common_test/doc/src/basics_chapter.xml
index 5f4da5f44b36..5b6b1737d516 100644
--- a/lib/common_test/doc/src/basics_chapter.xml
+++ b/lib/common_test/doc/src/basics_chapter.xml
@@ -4,7 +4,7 @@
- 20032022
+ 20032023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index a74777c56e25..7267c32249ef 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -4,7 +4,7 @@
- 20032020
+ 20032023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml
index 2ab9259dfbff..ebd02687b78d 100644
--- a/lib/common_test/doc/src/config_file_chapter.xml
+++ b/lib/common_test/doc/src/config_file_chapter.xml
@@ -4,7 +4,7 @@
- 20042021
+ 20042023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/ct_cover.xml b/lib/common_test/doc/src/ct_cover.xml
index 600e3e51d623..69c80e80d185 100644
--- a/lib/common_test/doc/src/ct_cover.xml
+++ b/lib/common_test/doc/src/ct_cover.xml
@@ -4,7 +4,7 @@
- 20102020
+ 20102023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/dependencies_chapter.xml b/lib/common_test/doc/src/dependencies_chapter.xml
index 7458533984e6..07901b63b143 100644
--- a/lib/common_test/doc/src/dependencies_chapter.xml
+++ b/lib/common_test/doc/src/dependencies_chapter.xml
@@ -4,7 +4,7 @@
- 20062021
+ 20062023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/example_chapter.xml b/lib/common_test/doc/src/example_chapter.xml
index e355e6ae7ce7..84f9fb22793c 100644
--- a/lib/common_test/doc/src/example_chapter.xml
+++ b/lib/common_test/doc/src/example_chapter.xml
@@ -4,7 +4,7 @@
- 20032016
+ 20032023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml
index 0a12a9653695..3b5143e7f7ae 100644
--- a/lib/common_test/doc/src/getting_started_chapter.xml
+++ b/lib/common_test/doc/src/getting_started_chapter.xml
@@ -4,7 +4,7 @@
- 20072021
+ 20072023
Ericsson AB. All Rights Reserved.
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index e304a309c9ca..bee62838e935 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,51 @@
notes.xml
+Common_Test 1.26
+
+ Fixed Bugs and Malfunctions
+
+ -
+
+ With this change, common_test returns an error when suite
+ with a badly defined group is executed.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ Own Id: OTP-18728 Aux Id: PR-7487, PR-7674
+
+ -
+
+ With this change, stylesheet option is applied to all
+ HTML report pages.
+
+ Own Id: OTP-18760
+
+ -
+
+ Update all <tt> html tags to be <code>
+ instead.
+
+ Own Id: OTP-18799 Aux Id: PR-7695
+
+
+
+
+
+ Improvements and New Features
+
+ -
+
+ This change fixes docs, so that historically deprecated
+ ?config macro is no longer recommended to be used.
+
+ Own Id: OTP-18858 Aux Id: PR-7825
+
+
+
+
+
+
Common_Test 1.25.1
Fixed Bugs and Malfunctions
@@ -3991,7 +4036,7 @@
-
- If a timetrap timeout occurred during execution of of a
+ If a timetrap timeout occurred during execution of a
function in a lib module (i.e. a function called directly
or indirectly from a test case), the Suite argument in
the end_tc/3 framework callback function would not
@@ -5082,7 +5127,7 @@
-
The rx library, included with common_test, failed to
- build on on some architectures because the -fPIC compiler
+ build on some architectures because the -fPIC compiler
option was missing.
Own Id: OTP-7111
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index b84c54041bc8..6063127bf0a0 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -817,7 +817,7 @@
In the data directory, data_dir, the test module has
its own files needed for the testing. The name of data_dir
- is the the name of the test suite followed by "_data".
+ is the name of the test suite followed by "_data".
For example, "some_path/foo_SUITE.beam" has the data directory
"some_path/foo_SUITE_data/". Use this directory for portability,
that is, to avoid hardcoding directory names in your suite. As the data
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 07108cf7c90d..063259a2b43a 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2021. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 52e82078eaee..fd0f7ca18c8c 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl
index cb000bafd62a..05cb807ec4f1 100644
--- a/lib/common_test/src/erl2html2.erl
+++ b/lib/common_test/src/erl2html2.erl
@@ -274,7 +274,13 @@ possibly_enhance(Str,false) ->
%%%-----------------------------------------------------------------
%%% End of the file
footer() ->
- "".
+ %% If the URL has an anchor part at the end (# with line number),
+ %% color that line to make it easier to find on the screen.
+ "\n".
%%%-----------------------------------------------------------------
%%% Read encoding from source file
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 7e3b0d995c42..3301840dc3a6 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -466,7 +466,7 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
From ! {self(),Tag,ok},
run_test_case_msgloop(St);
{abort_current_testcase,_,_}=Abort when St0#st.status =:= starting ->
- %% we're in init phase, must must postpone this operation
+ %% we're in init phase, must postpone this operation
%% until test case execution is in progress (or FW:init_tc
%% gets killed)
self() ! Abort,
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index 35fb69698adf..e5a361bd332d 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -4632,7 +4632,7 @@ update_config(Config, []) ->
%% simple list of test cases to call, when executing the test suite.
%%
%% CurMod is the "current" module, that is, the module the last instruction
-%% was read from. May be be set to 'none' initially.
+%% was read from. May be set to 'none' initially.
%%
%% SkipList is the list of test cases to skip and requirements to deny.
%%
diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl
index f1d783fc8da5..8a305b820572 100644
--- a/lib/common_test/test/ct_misc_1_SUITE.erl
+++ b/lib/common_test/test/ct_misc_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/common_test/test/ct_misc_1_SUITE_data/bad_groups_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE_data/bad_groups_SUITE.erl
index 8c5180ddf7a1..06d5daaea5c6 100644
--- a/lib/common_test/test/ct_misc_1_SUITE_data/bad_groups_SUITE.erl
+++ b/lib/common_test/test/ct_misc_1_SUITE_data/bad_groups_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
diff --git a/lib/common_test/test/erl2html2_SUITE_data/m1.erl b/lib/common_test/test/erl2html2_SUITE_data/m1.erl
index 1d405963a5c3..ed01f62fdcfa 100644
--- a/lib/common_test/test/erl2html2_SUITE_data/m1.erl
+++ b/lib/common_test/test/erl2html2_SUITE_data/m1.erl
@@ -43,7 +43,7 @@ ok. % indentation error, OTP-9710
%% Function inside macro definition
?MACRO_DEFINING_A_FUNCTION.
-%% Two function one one line
+%% Two function on one line
quuux() -> ok. quuuux() -> ok.
%% do_something/0 does something
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 16f1df9645a2..fc57703c9e7a 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.25.1
+COMMON_TEST_VSN = 1.26
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 9cd6d93783a4..9214c15ec0f9 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -131,7 +131,8 @@ blockify([I|Is0]=IsAll, Acc) ->
error -> blockify(Is0, [I|Acc]);
Instr when is_tuple(Instr) ->
{Block0,Is} = collect_block(IsAll),
- Block = sort_moves(Block0),
+ Block1 = sort_moves(Block0),
+ Block = swap_opt_block(Block1, []),
blockify(Is, [{block,Block}|Acc])
end;
blockify([], Acc) -> reverse(Acc).
@@ -170,6 +171,10 @@ collect({put_map,{f,0},Op,S,D,R,{list,Puts}}) ->
collect({fmove,S,D}) -> {set,[D],[S],fmove};
collect({fconv,S,D}) -> {set,[D],[S],fconv};
collect({executable_line,Line}) -> {set,[],[],{executable_line,Line}};
+collect({swap,D1,D2}) ->
+ Regs = [D1,D2],
+ {set,Regs,Regs,swap};
+collect({make_fun3,F,I,U,D,{list,Ss}}) -> {set,[D],Ss,{make_fun3,F,I,U}};
collect(_) -> error.
%% embed_lines([Instruction]) -> [Instruction]
@@ -223,6 +228,51 @@ sort_on_yreg([{set,[Dst],[Src],move}|_]=Moves) ->
keysort(3, Moves)
end.
+%% Attempt to replace a swap instruction with a move instruction.
+swap_opt_block([{set,[D1,D2],_,swap}=I|Is], [{set,[Dst],Ss,Op}|Acc]=Acc0) ->
+ case Op of
+ {get_tuple_element,_} ->
+ %% Don't separate from other get_tuple_element instructions or
+ %% tuple testing instructions.
+ swap_opt_block(Is, [I|Acc0]);
+ {alloc,_,_} ->
+ %% Potentially unsafe.
+ swap_opt_block(Is, [I|Acc0]);
+ get_hd ->
+ %% Don't separate from get_tl.
+ swap_opt_block(Is, [I|Acc0]);
+ get_tl ->
+ %% Don't separate from get_hd.
+ swap_opt_block(Is, [I|Acc0]);
+ _ ->
+ case is_used(Dst, Ss) of
+ true ->
+ swap_opt_block(Is, [I|Acc0]);
+ false ->
+ OtherDst = case Dst of
+ D1 -> D2;
+ D2 -> D1;
+ _ -> none
+ end,
+ case OtherDst of
+ none ->
+ swap_opt_block(Is, [I|Acc0]);
+ _ ->
+ swap_opt_block(Is, [{set,[OtherDst],Ss,Op},
+ {set,[Dst],[OtherDst],move}|Acc])
+ end
+ end
+ end;
+swap_opt_block([I|Is], Acc) ->
+ swap_opt_block(Is, [I|Acc]);
+swap_opt_block([], Acc) ->
+ reverse(Acc).
+
+is_used(D, [D|_]) -> true;
+is_used(D, [{tr,D,_}|_]) -> true;
+is_used(D, [_|As]) -> is_used(D, As);
+is_used(_, []) -> false.
+
%%%
%%% Coalesce adjacent get_map_elements and has_map_fields instructions.
%%%
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index 54cb9ec30947..3cc6001881ca 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -23,19 +23,22 @@
-export([module/2]).
+-import(lists, [reverse/1]).
+
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.
module({Mod,Exp,Attr,Fs0,_}, Opts) ->
- Order = [Lbl || {function,_,_,Lbl,_} <- Fs0],
- All = #{Lbl => Func || {function,_,_,Lbl,_}=Func <- Fs0},
- WorkList = rootset(Fs0, Exp, Attr),
+ Fs1 = move_out_funs(Fs0),
+ Order = [Lbl || {function,_,_,Lbl,_} <- Fs1],
+ All = #{Lbl => Func || {function,_,_,Lbl,_}=Func <- Fs1},
+ WorkList = rootset(Fs1, Exp, Attr),
Used = find_all_used(WorkList, All, sets:from_list(WorkList, [{version, 2}])),
- Fs1 = remove_unused(Order, Used, All),
- {Fs2,Lc} = clean_labels(Fs1),
- Fs3 = fix_bs_create_bin(Fs2, Opts),
- Fs4 = fix_badrecord(Fs3, Opts),
- Fs = maybe_remove_lines(Fs4, Opts),
+ Fs2 = remove_unused(Order, Used, All),
+ {Fs3,Lc} = clean_labels(Fs2),
+ Fs4 = fix_bs_create_bin(Fs3, Opts),
+ Fs5 = fix_badrecord(Fs4, Opts),
+ Fs = maybe_remove_lines(Fs5, Opts),
{ok,{Mod,Exp,Attr,Fs,Lc}}.
%% Determine the rootset, i.e. exported functions and
@@ -78,6 +81,31 @@ add_to_work_list(F, {Fs,Used}=Sets) ->
false -> {[F|Fs],sets:add_element(F, Used)}
end.
+%% Move out make_fun3 instructions from blocks. That is necessary because
+%% they contain labels that must be seen and renumbered.
+
+move_out_funs([{function,Name,Arity,Entry,Is0}|Fs]) ->
+ Is = move_out_funs_is(Is0),
+ [{function,Name,Arity,Entry,Is}|move_out_funs(Fs)];
+move_out_funs([]) -> [].
+
+move_out_funs_is([{block,Bl}|Is]) ->
+ move_out_funs_block(Bl, Is, []);
+move_out_funs_is([I|Is]) ->
+ [I|move_out_funs_is(Is)];
+move_out_funs_is([]) -> [].
+
+move_out_funs_block([{set,[D],Ss,{make_fun3,F,I,U}}|Bl], Is, Acc) ->
+ make_block(Acc) ++
+ [{make_fun3,F,I,U,D,{list,Ss}} |
+ move_out_funs_block(Bl, Is, [])];
+move_out_funs_block([B|Bl], Is, Acc) ->
+ move_out_funs_block(Bl, Is, [B|Acc]);
+move_out_funs_block([], Is, Acc) ->
+ make_block(Acc) ++ move_out_funs_is(Is).
+
+make_block([_|_]=Is) -> [{block,reverse(Is)}];
+make_block([]) -> [].
%%%
%%% Coalesce adjacent labels. Renumber all labels to eliminate gaps.
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 9a29b1ad503e..de11f6300970 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -63,7 +63,8 @@ norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],{line,_}=Line}) -> Line;
-norm({set,[],[],{executable_line,_}=Line}) -> Line.
+norm({set,[],[],{executable_line,_}=Line}) -> Line;
+norm({set,[D1,D2],[D1,D2],swap}) -> {swap,D1,D2}.
norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
[{test_heap,Nh,Regs}];
diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl
index b5ff21bbaca1..bfc6992aac56 100644
--- a/lib/compiler/src/beam_trim.erl
+++ b/lib/compiler/src/beam_trim.erl
@@ -292,9 +292,6 @@ remap([{recv_marker_clear,Ref}|Is], Remap) ->
remap([{recv_marker_reserve,Mark}|Is], Remap) ->
I = {recv_marker_reserve,remap_arg(Mark, Remap)},
[I|remap(Is, Remap)];
-remap([{swap,Reg1,Reg2}|Is], Remap) ->
- I = {swap,remap_arg(Reg1, Remap),remap_arg(Reg2, Remap)},
- [I|remap(Is, Remap)];
remap([{test,Name,Fail,Ss}|Is], Remap) ->
I = {test,Name,Fail,remap_args(Ss, Remap)},
[I|remap(Is, Remap)];
@@ -549,12 +546,6 @@ do_usage([{recv_marker_reserve,Src}|Is], Safe, Regs0, Ns, Acc) ->
Regs = ordsets:union(Regs0, yregs([Src])),
U = {Regs,Ns},
do_usage(Is, Safe, Regs, Ns, [U|Acc]);
-do_usage([{swap,R1,R2}|Is], Safe, Regs0, Ns0, Acc) ->
- Ds = yregs([R1,R2]),
- Regs = ordsets:union(Regs0, Ds),
- Ns = ordsets:union(Ns0, Ds),
- U = {Regs,Ns},
- do_usage(Is, Safe, Regs, Ns, [U|Acc]);
do_usage([{test,_,Fail,Ss}|Is], Safe, Regs0, Ns, Acc) ->
case is_safe_branch(Fail, Safe) of
true ->
diff --git a/lib/compiler/src/beam_types.erl b/lib/compiler/src/beam_types.erl
index 3fc2476898e1..b81bbc06164d 100644
--- a/lib/compiler/src/beam_types.erl
+++ b/lib/compiler/src/beam_types.erl
@@ -45,7 +45,6 @@
make_boolean/0,
make_cons/2,
make_float/1,
- make_float/2,
make_integer/1,
make_integer/2]).
@@ -169,10 +168,16 @@ mts_records([{Key, A} | RsA], [{Key, B} | RsB], Acc) ->
none -> mts_records(RsA, RsB, Acc);
T -> mts_records(RsA, RsB, [{Key, T} | Acc])
end;
-mts_records([{KeyA, _} | _ ]=RsA, [{KeyB, _} | RsB], Acc) when KeyA > KeyB ->
- mts_records(RsA, RsB, Acc);
-mts_records([{KeyA, _} | RsA], [{KeyB, _} | _] = RsB, Acc) when KeyA < KeyB ->
- mts_records(RsA, RsB, Acc);
+mts_records([{KeyA, _} | _]=RsA, [{KeyB, _} | _]=RsB, Acc) ->
+ %% We must use total ordering rather than plain '<' as -0.0 differs from
+ %% +0.0
+ case total_compare(KeyA, KeyB, fun erlang:'<'/2) of
+ true ->
+ mts_records(tl(RsA), RsB, Acc);
+ false ->
+ true = KeyA =/= KeyB, %Assertion.
+ mts_records(RsA, tl(RsB), Acc)
+ end;
mts_records(_RsA, [], [_|_]=Acc) ->
reverse(Acc);
mts_records([], _RsB, [_|_]=Acc) ->
@@ -320,10 +325,16 @@ jts_records(RsA, RsB, N, Acc) when N > ?TUPLE_SET_LIMIT ->
#t_tuple{} = normalize_tuple_set(Acc, B);
jts_records([{Key, A} | RsA], [{Key, B} | RsB], N, Acc) ->
jts_records(RsA, RsB, N + 1, [{Key, lub(A, B)} | Acc]);
-jts_records([{KeyA, _} | _]=RsA, [{KeyB, B} | RsB], N, Acc) when KeyA > KeyB ->
- jts_records(RsA, RsB, N + 1, [{KeyB, B} | Acc]);
-jts_records([{KeyA, A} | RsA], [{KeyB, _} | _] = RsB, N, Acc) when KeyA < KeyB ->
- jts_records(RsA, RsB, N + 1, [{KeyA, A} | Acc]);
+jts_records([{KeyA, A} | _]=RsA, [{KeyB, B} | _]=RsB, N, Acc) ->
+ %% We must use total ordering rather than plain '<' as -0.0 differs from
+ %% +0.0
+ case total_compare(KeyA, KeyB, fun erlang:'<'/2) of
+ true ->
+ jts_records(tl(RsA), RsB, N + 1, [{KeyA, A} | Acc]);
+ false ->
+ true = KeyA =/= KeyB, %Assertion.
+ jts_records(RsA, tl(RsB), N + 1, [{KeyB, B} | Acc])
+ end;
jts_records([{KeyA, A} | RsA], [], N, Acc) ->
jts_records(RsA, [], N + 1, [{KeyA, A} | Acc]);
jts_records([], [{KeyB, B} | RsB], N, Acc) ->
@@ -479,8 +490,7 @@ is_bs_matchable_type(Type) ->
Result :: {ok, term()} | error.
get_singleton_value(#t_atom{elements=[Atom]}) ->
{ok, Atom};
-get_singleton_value(#t_float{elements={Float,Float}}) when Float /= 0 ->
- %% 0.0 is not actually a singleton as it has two encodings: 0.0 and -0.0
+get_singleton_value(#t_float{elements={Float,Float}}) ->
{ok, Float};
get_singleton_value(#t_integer{elements={Int,Int}}) ->
{ok, Int};
@@ -697,11 +707,7 @@ make_cons(Head0, Tail) ->
-spec make_float(float()) -> type().
make_float(Float) when is_float(Float) ->
- make_float(Float, Float).
-
--spec make_float(float(), float()) -> type().
-make_float(Min, Max) when is_float(Min), is_float(Max), Min =< Max ->
- #t_float{elements={Min, Max}}.
+ #t_float{elements={Float,Float}}.
-spec make_integer(integer()) -> type().
make_integer(Int) when is_integer(Int) ->
@@ -882,7 +888,7 @@ glb(#t_integer{elements=R1}, #t_integer{elements=R2}) ->
glb(#t_integer{elements=R1}, #t_number{elements=R2}) ->
integer_from_range(glb_ranges(R1, R2));
glb(#t_float{elements=R1}, #t_number{elements=R2}) ->
- float_from_range(glb_ranges(R1, R2));
+ float_from_range(glb_ranges(R1, number_to_float_range(R2)));
glb(#t_list{type=TypeA,terminator=TermA},
#t_list{type=TypeB,terminator=TermB}) ->
%% A list is a union of `[type() | _]` and `[]`, so we're left with
@@ -903,7 +909,7 @@ glb(#t_number{elements=R1}, #t_number{elements=R2}) ->
glb(#t_number{elements=R1}, #t_integer{elements=R2}) ->
integer_from_range(glb_ranges(R1, R2));
glb(#t_number{elements=R1}, #t_float{elements=R2}) ->
- float_from_range(glb_ranges(R1, R2));
+ float_from_range(glb_ranges(number_to_float_range(R1), R2));
glb(#t_map{super_key=SKeyA,super_value=SValueA},
#t_map{super_key=SKeyB,super_value=SValueB}) ->
%% Note the use of meet/2; elements don't need to be normal types.
@@ -1132,6 +1138,14 @@ lub_ranges({MinA,MaxA}, {MinB,MaxB}) ->
lub_ranges(_, _) ->
any.
+%% Expands integer 0 to `-0.0 .. +0.0`
+number_to_float_range({Min, 0}) ->
+ number_to_float_range({Min, +0.0});
+number_to_float_range({0, Max}) ->
+ number_to_float_range({-0.0, Max});
+number_to_float_range(Other) ->
+ Other.
+
lub_bs_matchable(UnitA, UnitB) ->
#t_bs_matchable{tail_unit=gcd(UnitA, UnitB)}.
@@ -1179,12 +1193,13 @@ float_from_range(none) ->
none;
float_from_range(any) ->
#t_float{};
-float_from_range({Min0,Max0}) ->
- case {safe_float(Min0),safe_float(Max0)} of
+float_from_range({Min0, Max0}) ->
+ true = inf_le(Min0, Max0), %Assertion.
+ case {safe_float(Min0), safe_float(Max0)} of
{'-inf','+inf'} ->
#t_float{};
- {Min,Max} ->
- #t_float{elements={Min,Max}}
+ {Min, Max} ->
+ #t_float{elements={Min, Max}}
end.
safe_float(N) when is_number(N) ->
@@ -1218,21 +1233,48 @@ number_from_range(N) ->
none
end.
-inf_le('-inf', _) -> true;
-inf_le(A, B) -> A =< B.
-
-inf_ge(_, '-inf') -> true;
-inf_ge('-inf', _) -> false;
-inf_ge(A, B) -> A >= B.
+inf_le('-inf', _) ->
+ true;
+inf_le(A, B) when is_float(A), is_float(B) ->
+ %% When float ranges are compared to float ranges, the total ordering
+ %% function must be used to preserve `-0.0 =/= +0.0`.
+ total_compare(A, B, fun erlang:'=<'/2);
+inf_le(A, B) ->
+ A =< B.
+
+inf_ge(_, '-inf') ->
+ true;
+inf_ge('-inf', _) ->
+ false;
+inf_ge(A, B) when is_float(A), is_float(B) ->
+ total_compare(A, B, fun erlang:'>='/2);
+inf_ge(A, B) ->
+ A >= B.
+
+inf_min(A, B) when A =:= '-inf'; B =:= '-inf' ->
+ '-inf';
+inf_min(A, B) when is_float(A), is_float(B) ->
+ case total_compare(A, B, fun erlang:'=<'/2) of
+ true -> A;
+ false -> B
+ end;
+inf_min(A, B) ->
+ min(A, B).
-inf_min(A, B) when A =:= '-inf'; B =:= '-inf' -> '-inf';
-inf_min(A, B) when A =< B -> A;
-inf_min(A, B) when A > B -> B.
+inf_max('-inf', B) ->
+ B;
+inf_max(A, '-inf') ->
+ A;
+inf_max(A, B) when is_float(A), is_float(B) ->
+ case total_compare(A, B, fun erlang:'>='/2) of
+ true -> A;
+ false -> B
+ end;
+inf_max(A, B) ->
+ max(A, B).
-inf_max('-inf', B) -> B;
-inf_max(A, '-inf') -> A;
-inf_max(A, B) when A >= B -> A;
-inf_max(A, B) when A < B -> B.
+total_compare(A, B, Order) ->
+ Order(erts_internal:cmp_term(A, B), 0).
%%
diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl
index 49b5cb6e745c..69bfcbe7b448 100644
--- a/lib/compiler/test/beam_type_SUITE.erl
+++ b/lib/compiler/test/beam_type_SUITE.erl
@@ -31,7 +31,7 @@
switch_fail_inference/1,failures/1,
cover_maps_functions/1,min_max_mixed_types/1,
not_equal/1,infer_relops/1,binary_unit/1,premature_concretization/1,
- funs/1,will_succeed/1]).
+ funs/1,will_succeed/1,float_confusion/1]).
%% Force id/1 to return 'any'.
-export([id/1]).
@@ -76,7 +76,8 @@ groups() ->
binary_unit,
premature_concretization,
funs,
- will_succeed
+ will_succeed,
+ float_confusion
]}].
init_per_suite(Config) ->
@@ -1505,6 +1506,47 @@ will_succeed_1(_V0, _V1)
will_succeed_1(_, _) ->
b.
+%% GH-7901: Range operations did not honor the total order of floats.
+float_confusion(_Config) ->
+ ok = float_confusion_1(catch (true = ok), -0.0),
+ ok = float_confusion_1(ok, 0.0),
+ {'EXIT', _} = catch float_confusion_2(),
+ {'EXIT', _} = catch float_confusion_3(id(0.0)),
+ ok = float_confusion_4(id(1)),
+ {'EXIT', _} = catch float_confusion_5(),
+ ok.
+
+float_confusion_1(_, _) ->
+ ok.
+
+float_confusion_2() ->
+ [ok || _ := _ <- ok,
+ float_confusion_crash(catch float_confusion_crash(ok, -1), -0.0)].
+
+float_confusion_crash(_, 18446744073709551615) ->
+ ok.
+
+float_confusion_3(V) ->
+ -0.0 = abs(V),
+ ok.
+
+float_confusion_4(V) when -0.0 < floor(V band 1) ->
+ ok.
+
+float_confusion_5() ->
+ -0.0 =
+ case
+ fun() ->
+ ok
+ end
+ of
+ _V2 when (_V2 > ok) ->
+ 2147483647.0;
+ _ ->
+ -2147483648
+ end * 0,
+ ok.
+
%%%
%%% Common utilities.
%%%
diff --git a/lib/crypto/c_src/crypto_callback.c b/lib/crypto/c_src/crypto_callback.c
index e62bc17d4072..4cfcf39b2d45 100644
--- a/lib/crypto/c_src/crypto_callback.c
+++ b/lib/crypto/c_src/crypto_callback.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2014-2020. All Rights Reserved.
+ * Copyright Ericsson AB 2014-2023. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/lib/crypto/c_src/dss.c b/lib/crypto/c_src/dss.c
index 0756e9076d15..fdb9c6edd0bf 100644
--- a/lib/crypto/c_src/dss.c
+++ b/lib/crypto/c_src/dss.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010-2022. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2023. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/lib/crypto/doc/src/algorithm_details.xml b/lib/crypto/doc/src/algorithm_details.xml
index 64cbbd3fc45b..a6b0d61aa67c 100644
--- a/lib/crypto/doc/src/algorithm_details.xml
+++ b/lib/crypto/doc/src/algorithm_details.xml
@@ -4,7 +4,7 @@
- 20142022
+ 20142023
Ericsson AB. All Rights Reserved.
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index a7504a7440fe..a1d93faa3dea 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -3,7 +3,7 @@
- 19992022
+ 19992023
Ericsson AB. All Rights Reserved.
diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml
index ce527d60f486..249841d851fe 100644
--- a/lib/crypto/doc/src/crypto_app.xml
+++ b/lib/crypto/doc/src/crypto_app.xml
@@ -52,7 +52,7 @@
The crypto app is tested daily with at least one version of each of the
OpenSSL 1.0.1, 1.0.2, 1.1.0, 1.1.1 and 3.0. FIPS mode is also tested for 1.0.1, 1.0.2 and 3.0.
- Using OpenSSL 3.0 with Engines is supported since OTP @OTP-18832@.
+ Using OpenSSL 3.0 with Engines is supported since OTP 26.2.
Source releases of OpenSSL can be downloaded from the OpenSSL project home page,
or mirror sites listed there.
@@ -99,4 +99,3 @@
application(3)