diff options
Diffstat (limited to 'lib/diameter/src/compiler/diameter_codegen.erl')
-rw-r--r-- | lib/diameter/src/compiler/diameter_codegen.erl | 120 |
1 files changed, 92 insertions, 28 deletions
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index 91bab2205c..47da193457 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -33,11 +33,6 @@ -export([from_dict/4]). -%% Internal exports (for test). --export([file/1, - file/2, - file/3]). - -include("diameter_forms.hrl"). -include("diameter_vsn.hrl"). @@ -50,11 +45,11 @@ -spec from_dict(File, ParseD, Opts, Mode) -> ok - | list() + | term() when File :: string(), ParseD :: orddict:orddict(), Opts :: list(), - Mode :: dict | forms | erl | hrl. + Mode :: parse | forms | erl | hrl | beam. from_dict(File, ParseD, Opts, Mode) -> Outdir = proplists:get_value(outdir, Opts, "."), @@ -74,11 +69,12 @@ mod(_, {ok, Mod}) -> maybe_write(true, _, _, _, T) -> T; + maybe_write(_, Mode, Outdir, Mod, T) -> Path = filename:join(Outdir, Mod), %% minus extension do_write(Mode, [Path, $., ext(Mode)], T). -ext(dict) -> +ext(parse) -> "D"; ext(forms) -> "F"; @@ -86,9 +82,11 @@ ext(T) -> ?S(T). do_write(M, Path, T) - when M == dict; + when M == parse; M == forms -> write_term(Path, T); +do_write(beam, Path, {_Mod, Beam}) -> + write(Path, Beam); do_write(_, Path, T) -> write(Path, T). @@ -125,36 +123,35 @@ eraser(Key) -> %% =========================================================================== %% =========================================================================== -%% Generate from parsed dictionary in a file. - -file(F) -> - file(F, dict). - -file(F, Mode) -> - file(F, ".", Mode). - -file(F, Outdir, Mode) -> - {ok, [ParseD]} = file:consult(F), - from_dict(F, ParseD, Outdir, Mode). - -%% =========================================================================== -%% =========================================================================== - get_value(Key, Plist) -> proplists:get_value(Key, Plist, []). -gen(dict, ParseD, _Mod) -> +gen(parse, ParseD, _Mod) -> [?VERSION | ParseD]; -gen(hrl, ParseD, Mod) -> - gen_hrl(Mod, ParseD); - gen(forms, ParseD, Mod) -> erl_forms(Mod, ParseD); +gen(beam, ParseD, Mod) -> + compile(pp(erl_forms(Mod, ParseD))); + +gen(hrl, ParseD, Mod) -> + gen_hrl(Mod, ParseD); + gen(erl, ParseD, Mod) -> [header(), prettypr(erl_forms(Mod, ParseD)), $\n]. +compile({ok, Forms}) -> + case compile:forms(Forms, [debug_info, return]) of + {ok, Mod, Beam, _Warnings} -> + {Mod, Beam}; + {error, Errors, _Warnings} -> + erlang:error({compile, Errors}) + end; + +compile({error, Reason}) -> + erlang:error(Reason). + erl_forms(Mod, ParseD) -> Forms = [[{?attribute, module, Mod}, {?attribute, compile, {parse_transform, diameter_exprecs}}, @@ -851,3 +848,70 @@ prefix(ParseD) -> rec_name(Name, Prefix) -> Prefix ++ Name. + +%% =========================================================================== +%% pp/1 +%% +%% Preprocess forms as generated by 'forms' option. In particular, +%% replace the include_lib attributes in generated forms by the +%% corresponding forms, extracting the latter from an existing +%% dictionary (diameter_gen_relay). The resulting forms can be +%% compiled to beam using compile:forms/2 (which does no preprocessing +%% or it's own; DiY currently appears to be the only way to preprocess +%% a forms list). + +pp(Forms) -> + {_, Beam, _} = code:get_object_code(diameter_gen_relay), + pp(Forms, abstract_code(Beam)). + +pp(Forms, {ok, Code}) -> + Files = files(Code, []), + {ok, lists:flatmap(fun(T) -> include(T, Files) end, Forms)}; + +pp(_, {error, _} = No) -> + No. + +include({attribute, _, include_lib, Path}, Files) -> + Inc = filename:basename(Path), + [{Inc, Forms}] = [T || {F, _} = T <- Files, F == Inc], %% expect one + lists:flatmap(fun filter/1, Forms); + +include(T, _) -> + [T]. + +abstract_code(Beam) -> + case beam_lib:chunks(Beam, [abstract_code]) of + {ok, {_Mod, [{abstract_code, {_Vsn, Code}}]}} -> + {ok, Code}; + {ok, {_Mod, [{abstract_code, no_abstract_code = No}]}} -> + {error, No}; + {error = E, beam_lib, Reason} -> + {E, Reason} + end. + +files([{attribute, _, file, {Path, _}} | T], Acc) -> + {Body, Rest} = lists:splitwith(fun({attribute, _, file, _}) -> false; + (_) -> true + end, + T), + files(Rest, [{filename:basename(Path), Body} | Acc]); + +files([], Acc) -> + Acc. + +%% Only retain record diameter_avp and functions not generated by +%% diameter_exprecs. + +filter({attribute, _, record, {diameter_avp, _}} = T) -> + [T]; + +filter({function, _, Name, _, _} = T) -> + case ?S(Name) of + [$#|_] -> %% generated by diameter_exprecs + []; + _ -> + [T] + end; + +filter(_) -> + []. |