1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21-module(asn1ct_gen_per).
22
23%% Generate erlang module which handles (PER) encode and decode for
24%% all types in an ASN.1 module
25
26-include("asn1_records.hrl").
27
28-export([gen_dec_imm/2]).
29-export([gen_dec_prim/3,gen_encode_prim_imm/3]).
30-export([gen_obj_code/3,gen_objectset_code/2]).
31-export([gen_decode/2, gen_decode/3]).
32-export([gen_encode/2, gen_encode/3]).
33-export([gen_dec_external/2]).
34-export([extaddgroup2sequence/1]).
35-export([dialyzer_suppressions/1]).
36
37-import(asn1ct_gen, [emit/1]).
38-import(asn1ct_func, [call/3]).
39
40
41%%****************************************
42%% Generate ENCODING
43%%****************************************
44
45dialyzer_suppressions(#gen{erule=per,aligned=Aligned}) ->
46    Mod = case Aligned of
47              false -> uper;
48              true -> per
49          end,
50    suppress({Mod,complete,1}),
51    suppress({per_common,to_bitstring,2}),
52    emit(["    ok.",nl]).
53
54suppress({M,F,A}=MFA) ->
55    case asn1ct_func:is_used(MFA) of
56	false ->
57	    ok;
58	true ->
59	    Args =
60                [lists:concat(["element(",I,", Arg)"])
61                 || I <- lists:seq(1, A)],
62	    emit(["    ",{call,M,F,Args},com,nl])
63    end.
64
65gen_encode(Erules,Type) when is_record(Type,typedef) ->
66    gen_encode_user(Erules,Type).
67
68gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) ->
69    NewTypename = [Cname|Typename],
70    gen_encode(Erules,NewTypename,Type);
71
72gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
73    InnerType = asn1ct_gen:get_inner(Type#type.def),
74    ObjFun =
75	case lists:keysearch(objfun,1,Type#type.tablecinf) of
76	    {value,{_,_Name}} ->
77		", ObjFun";
78	    false ->
79		""
80	end,
81    case asn1ct_gen:type(InnerType) of
82	{constructed,bif} ->
83            Func = enc_func(asn1ct_gen:list2name(Typename)),
84            emit([{asis,Func},"(Val",ObjFun,") ->",nl]),
85	    asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
86	_ ->
87	    true
88    end.
89
90
91gen_encode_user(Erules,D) when is_record(D,typedef) ->
92    CurrMod = get(currmod),
93    Typename = [D#typedef.name],
94    Def = D#typedef.typespec,
95    InnerType = asn1ct_gen:get_inner(Def#type.def),
96    Func = enc_func(asn1ct_gen:list2name(Typename)),
97    emit([{asis,Func},"(Val) ->",nl]),
98    case asn1ct_gen:type(InnerType) of
99	{primitive,bif} ->
100	    gen_encode_prim(Erules, Def),
101	    emit([".",nl]);
102	'ASN1_OPEN_TYPE' ->
103	    gen_encode_prim(Erules, Def#type{def='ASN1_OPEN_TYPE'}),
104	    emit([".",nl]);
105	{constructed,bif} ->
106	    asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
107	#'Externaltypereference'{module=CurrMod,type=Etype} ->
108            emit([{asis,enc_func(Etype)},"(Val).",nl]);
109	#'Externaltypereference'{module=Emod,type=Etype} ->
110            emit([{asis,Emod},":",{asis,enc_func(Etype)},"(Val).",nl])
111    end.
112
113
114gen_encode_prim(Erules, D) ->
115    Value = {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(val)))},
116    gen_encode_prim(Erules, D, Value).
117
118gen_encode_prim(#gen{erule=per,aligned=Aligned}, #type{}=D, Value) ->
119    Imm = gen_encode_prim_imm(Value, D, Aligned),
120    asn1ct_imm:enc_cg(Imm, Aligned).
121
122gen_encode_prim_imm(Val, #type{def=Type0,constraint=Constraint}, Aligned) ->
123    case simplify_type(Type0) of
124	k_m_string ->
125	    Type = case Type0 of
126		       'GeneralizedTime' -> 'VisibleString';
127		       'UTCTime' -> 'VisibleString';
128		       _ -> Type0
129		   end,
130	    asn1ct_imm:per_enc_k_m_string(Val, Type, Constraint, Aligned);
131	restricted_string ->
132	    ToBinary = {erlang,iolist_to_binary},
133	    asn1ct_imm:per_enc_restricted_string(Val, ToBinary, Aligned);
134	{'ENUMERATED',NNL} ->
135	    asn1ct_imm:per_enc_enumerated(Val, NNL, Aligned);
136	'INTEGER' ->
137	    asn1ct_imm:per_enc_integer(Val, Constraint, Aligned);
138	{'INTEGER',NNL} ->
139	    asn1ct_imm:per_enc_integer(Val, NNL, Constraint, Aligned);
140	'REAL' ->
141	    ToBinary = {real_common,encode_real},
142	    asn1ct_imm:per_enc_restricted_string(Val, ToBinary, Aligned);
143	{'BIT STRING',NNL} ->
144	    case asn1ct:use_legacy_types() of
145		false ->
146		    asn1ct_imm:per_enc_bit_string(Val, NNL,
147						  Constraint, Aligned);
148		true ->
149		    asn1ct_imm:per_enc_legacy_bit_string(Val, NNL,
150							 Constraint, Aligned)
151	    end;
152	'NULL' ->
153	    asn1ct_imm:per_enc_null(Val, Aligned);
154	'OBJECT IDENTIFIER' ->
155	    ToBinary = {per_common,encode_oid},
156	    asn1ct_imm:per_enc_restricted_string(Val, ToBinary, Aligned);
157	'RELATIVE-OID' ->
158	    ToBinary = {per_common,encode_relative_oid},
159	    asn1ct_imm:per_enc_restricted_string(Val, ToBinary, Aligned);
160	'BOOLEAN' ->
161	    asn1ct_imm:per_enc_boolean(Val, Aligned);
162	'OCTET STRING' ->
163	    case asn1ct:use_legacy_types() of
164		false ->
165		    asn1ct_imm:per_enc_octet_string(Val, Constraint, Aligned);
166		true ->
167		    asn1ct_imm:per_enc_legacy_octet_string(Val, Constraint,
168							   Aligned)
169	    end;
170	'ASN1_OPEN_TYPE' ->
171	    case Constraint of
172		[#'Externaltypereference'{type=Tname}] ->
173		    EncFunc = enc_func(Tname),
174		    Imm = [{apply,{local,EncFunc,[]},[Val]}],
175		    asn1ct_imm:per_enc_open_type(Imm, Aligned);
176		[] ->
177		    Imm = [{call,erlang,iolist_to_binary,[Val]}],
178		    asn1ct_imm:per_enc_open_type(Imm, Aligned)
179	    end
180    end.
181
182dec_func(Tname) ->
183    list_to_atom(lists:concat(["dec_",Tname])).
184
185enc_func(Tname) ->
186    list_to_atom(lists:concat(["enc_",Tname])).
187
188simplify_type(Type) ->
189    case Type of
190	'BMPString'       -> k_m_string;
191	'IA5String'       -> k_m_string;
192	'NumericString'   -> k_m_string;
193	'PrintableString' -> k_m_string;
194	'VisibleString'   -> k_m_string;
195	'UniversalString' -> k_m_string;
196	'GeneralizedTime' -> k_m_string;
197	'UTCTime'         -> k_m_string;
198	'TeletexString'   -> restricted_string;
199	'T61String'       -> restricted_string;
200	'VideotexString'  -> restricted_string;
201	'GraphicString'   -> restricted_string;
202	'GeneralString'   -> restricted_string;
203	'UTF8String'      -> restricted_string;
204	'ObjectDescriptor' -> restricted_string;
205	Other             -> Other
206    end.
207
208%% Object code generating for encoding and decoding
209%% ------------------------------------------------
210
211gen_obj_code(_Erules, _Module, #typedef{}) ->
212    ok.
213
214%% Object Set code generating for encoding and decoding
215%% ----------------------------------------------------
216gen_objectset_code(_Erules, _ObjSet) ->
217    ok.
218
219%% DECODING *****************************
220%%***************************************
221
222gen_decode(Erules, #typedef{}=Type) ->
223    DecFunc = dec_func(Type#typedef.name),
224    emit([nl,nl,{asis,DecFunc},"(Bytes) ->",nl]),
225    gen_decode_user(Erules, Type).
226
227gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
228    NewTname = [Cname|Tname],
229    gen_decode(Erules,NewTname,Type);
230
231gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
232    InnerType = asn1ct_gen:get_inner(Type#type.def),
233    case asn1ct_gen:type(InnerType) of
234	{constructed,bif} ->
235	    ObjFun =
236		case Type#type.tablecinf of
237		    [{objfun,_}|_R] ->
238			", ObjFun";
239		    _ ->
240			""
241		end,
242	    emit([nl,
243		  {asis,dec_func(asn1ct_gen:list2name(Typename))},
244		  "(Bytes",ObjFun,") ->",nl]),
245	    asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
246	_ ->
247	    true
248    end.
249
250gen_decode_user(Erules,D) when is_record(D,typedef) ->
251    Typename = [D#typedef.name],
252    Def = D#typedef.typespec,
253    InnerType = asn1ct_gen:get_inner(Def#type.def),
254    case asn1ct_gen:type(InnerType) of
255	{primitive,bif} ->
256	    gen_dec_prim(Erules,Def,"Bytes"),
257	    emit([".",nl,nl]);
258	'ASN1_OPEN_TYPE' ->
259	    gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"),
260	    emit([".",nl,nl]);
261	{constructed,bif} ->
262	    asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
263	#'Externaltypereference'{}=Etype ->
264	    gen_dec_external(Etype, "Bytes"),
265	    emit([".",nl,nl])
266    end.
267
268gen_dec_external(Ext, BytesVar) ->
269    CurrMod = get(currmod),
270    #'Externaltypereference'{module=Mod,type=Type} = Ext,
271    emit([case CurrMod of
272	      Mod -> [];
273	      _ -> [{asis,Mod},":"]
274	  end,{asis,dec_func(Type)},"(",BytesVar,")"]).
275
276gen_dec_imm(#gen{erule=per,aligned=Aligned}, #type{def=Name,constraint=C}) ->
277    gen_dec_imm_1(Name, C, Aligned).
278
279gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) ->
280    imm_decode_open_type(Constraint, Aligned);
281gen_dec_imm_1({'BIT STRING',NNL}, Constr0, Aligned) ->
282    Constr = asn1ct_imm:effective_constraint(bitstring, Constr0),
283    Imm = asn1ct_imm:per_dec_raw_bitstring(Constr, Aligned),
284    case NNL of
285	[] ->
286	    case asn1ct:get_bit_string_format() of
287		compact ->
288		    gen_dec_bit_string(decode_compact_bit_string,
289				       Imm);
290		legacy ->
291		    gen_dec_bit_string(decode_legacy_bit_string,
292				       Imm);
293		bitstring ->
294		    gen_dec_copy_bitstring(Imm)
295	    end;
296	[_|_] ->
297	    D = fun(V, Buf) ->
298			As = [V,{asis,NNL}],
299			Call = {call,per_common,decode_named_bit_string,As},
300			emit(["{",Call,com,Buf,"}"])
301		end,
302	    {call,D,Imm}
303    end;
304gen_dec_imm_1('NULL', _Constr, _Aligned) ->
305    {value,'NULL'};
306gen_dec_imm_1('BOOLEAN', _Constr, _Aligned) ->
307    asn1ct_imm:per_dec_boolean();
308gen_dec_imm_1({'ENUMERATED',{Base,Ext}}, _Constr, Aligned) ->
309    asn1ct_imm:per_dec_enumerated(Base, Ext, Aligned);
310gen_dec_imm_1({'ENUMERATED',NamedNumberList}, _Constr, Aligned) ->
311    asn1ct_imm:per_dec_enumerated(NamedNumberList, Aligned);
312gen_dec_imm_1('INTEGER', Constr, Aligned) ->
313    asn1ct_imm:per_dec_integer(Constr, Aligned);
314gen_dec_imm_1({'INTEGER',NamedNumberList}, Constraint, Aligned) ->
315    asn1ct_imm:per_dec_named_integer(Constraint,
316				     NamedNumberList,
317				     Aligned);
318gen_dec_imm_1('BMPString'=Type, Constraint, Aligned) ->
319    gen_dec_k_m_string(Type, Constraint, Aligned);
320gen_dec_imm_1('NumericString'=Type, Constraint, Aligned) ->
321    gen_dec_k_m_string(Type, Constraint, Aligned);
322gen_dec_imm_1('PrintableString'=Type, Constraint, Aligned) ->
323    gen_dec_k_m_string(Type, Constraint, Aligned);
324gen_dec_imm_1('VisibleString'=Type, Constraint, Aligned) ->
325    gen_dec_k_m_string(Type, Constraint, Aligned);
326gen_dec_imm_1('IA5String'=Type, Constraint, Aligned) ->
327    gen_dec_k_m_string(Type, Constraint, Aligned);
328gen_dec_imm_1('UniversalString'=Type, Constraint, Aligned) ->
329    gen_dec_k_m_string(Type, Constraint, Aligned);
330gen_dec_imm_1('UTCTime', Constraint, Aligned) ->
331    gen_dec_k_m_string('VisibleString', Constraint, Aligned);
332gen_dec_imm_1('GeneralizedTime', Constraint, Aligned) ->
333    gen_dec_k_m_string('VisibleString', Constraint, Aligned);
334gen_dec_imm_1('OCTET STRING', Constraint, Aligned) ->
335    SzConstr = asn1ct_imm:effective_constraint(bitstring, Constraint),
336    Imm = asn1ct_imm:per_dec_octet_string(SzConstr, Aligned),
337    case asn1ct:use_legacy_types() of
338	false -> {convert,{binary,copy},Imm};
339	true -> {convert,binary_to_list,Imm}
340    end;
341gen_dec_imm_1('TeletexString', _Constraint, Aligned) ->
342    gen_dec_restricted_string(Aligned);
343gen_dec_imm_1('T61String', _Constraint, Aligned) ->
344    gen_dec_restricted_string(Aligned);
345gen_dec_imm_1('VideotexString', _Constraint, Aligned) ->
346    gen_dec_restricted_string(Aligned);
347gen_dec_imm_1('GraphicString', _Constraint, Aligned) ->
348    gen_dec_restricted_string(Aligned);
349gen_dec_imm_1('GeneralString', _Constraint, Aligned) ->
350    gen_dec_restricted_string(Aligned);
351gen_dec_imm_1('ObjectDescriptor', _Constraint, Aligned) ->
352    gen_dec_restricted_string(Aligned);
353gen_dec_imm_1('OBJECT IDENTIFIER', _Constraint, Aligned) ->
354    Dec = fun(V, Buf) ->
355		  emit(["{",{call,per_common,decode_oid,[V]},com,
356			Buf,"}"])
357	  end,
358    {call,Dec,gen_dec_restricted_string(Aligned)};
359gen_dec_imm_1('RELATIVE-OID', _Constraint, Aligned) ->
360    Dec = fun(V, Buf) ->
361		  emit(["{",{call,per_common,decode_relative_oid,[V]},com,
362			Buf,"}"])
363	  end,
364    {call,Dec,gen_dec_restricted_string(Aligned)};
365gen_dec_imm_1('UTF8String', _Constraint, Aligned) ->
366    asn1ct_imm:per_dec_restricted_string(Aligned);
367gen_dec_imm_1('REAL', _Constraint, Aligned) ->
368    asn1ct_imm:per_dec_real(Aligned).
369
370gen_dec_bit_string(F, Imm) ->
371    D = fun(V, Buf) ->
372		emit(["{",{call,per_common,F,[V]},com,Buf,"}"])
373	end,
374    {call,D,Imm}.
375
376gen_dec_copy_bitstring(Imm) ->
377    D = fun(V, Buf) ->
378		emit(["{list_to_bitstring([",V,"]),",Buf,"}"])
379	end,
380    {call,D,Imm}.
381
382gen_dec_k_m_string(Type, Constraint, Aligned) ->
383    asn1ct_imm:per_dec_k_m_string(Type, Constraint, Aligned).
384
385gen_dec_restricted_string(Aligned) ->
386    Imm = asn1ct_imm:per_dec_restricted_string(Aligned),
387    {convert,binary_to_list,Imm}.
388
389gen_dec_prim(Erule, Type, BytesVar) ->
390    Imm = gen_dec_imm(Erule, Type),
391    asn1ct_imm:dec_code_gen(Imm, BytesVar).
392
393
394%% For PER the ExtensionAdditionGroup notation has significance for
395%% the encoding and decoding. The components within the
396%% ExtensionAdditionGroup is treated in a similar way as if they have
397%% been specified within a SEQUENCE. Therefore we construct a fake
398%% sequence type here so that we can generate code for it.
399extaddgroup2sequence(ExtList) ->
400    extaddgroup2sequence(ExtList,0,[]).
401
402extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],ExtNum,Acc) ->
403    Number = case Number0 of undefined -> 1; _ -> Number0 end,
404    {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} =
405     lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T),
406    extaddgroup2sequence(T2,ExtNum+1,
407			 [#'ComponentType'{
408			     name=list_to_atom("ExtAddGroup"++
409						   integer_to_list(ExtNum+1)),
410			     typespec=#type{def=#'SEQUENCE'{
411					      extaddgroup=Number,
412					      components=ExtGroupComps}},
413			     prop='OPTIONAL'}|Acc]);
414extaddgroup2sequence([C|T],ExtNum,Acc) ->
415    extaddgroup2sequence(T,ExtNum,[C|Acc]);
416extaddgroup2sequence([],_,Acc) ->
417    lists:reverse(Acc).
418
419imm_decode_open_type([#'Externaltypereference'{type=Tname}], Aligned) ->
420    imm_dec_open_type_1(Tname, Aligned);
421imm_decode_open_type([#type{def=#'Externaltypereference'{type=Tname}}],
422		     Aligned) ->
423    imm_dec_open_type_1(Tname, Aligned);
424imm_decode_open_type(_, Aligned) ->
425    asn1ct_imm:per_dec_open_type(Aligned).
426
427imm_dec_open_type_1(Type, Aligned) ->
428    D = fun(OpenType, Buf) ->
429		asn1ct_name:new(tmpval),
430		emit(["begin",nl,
431		      "{",{curr,tmpval},",_} = ",
432		      {asis,dec_func(Type)},"(",OpenType,"),",nl,
433		      "{",{curr,tmpval},com,Buf,"}",nl,
434		      "end"])
435	end,
436    {call,D,asn1ct_imm:per_dec_open_type(Aligned)}.
437