diff options
Diffstat (limited to 'lib/dialyzer/src/dialyzer_codeserver.erl')
-rw-r--r-- | lib/dialyzer/src/dialyzer_codeserver.erl | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl new file mode 100644 index 0000000000..624501fc49 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -0,0 +1,282 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_codeserver.erl +%%% Author : Tobias Lindahl <[email protected]> +%%% Description : +%%% +%%% Created : 4 Apr 2005 by Tobias Lindahl <[email protected]> +%%%------------------------------------------------------------------- +-module(dialyzer_codeserver). + +-export([delete/1, + finalize_contracts/2, + finalize_records/2, + get_contracts/1, + get_exports/1, + get_records/1, + get_next_core_label/1, + get_temp_contracts/1, + get_temp_records/1, + insert/3, + insert_exports/2, + is_exported/2, + lookup_mod_code/2, + lookup_mfa_code/2, + lookup_mod_records/2, + lookup_mod_contracts/2, + lookup_mfa_contract/2, + new/0, + set_next_core_label/2, + set_temp_records/2, + store_records/3, + store_temp_records/3, + store_contracts/3, + store_temp_contracts/3]). + +-include("dialyzer.hrl"). + +%%-------------------------------------------------------------------- + +-record(dialyzer_codeserver, {table_pid :: pid(), + exports = sets:new() :: set(), % set(mfa()) + next_core_label = 0 :: label(), + records = dict:new() :: dict(), + temp_records = dict:new() :: dict(), + contracts = dict:new() :: dict(), + temp_contracts = dict:new() :: dict()}). + +-opaque codeserver() :: #dialyzer_codeserver{}. + +%%-------------------------------------------------------------------- + +-spec new() -> codeserver(). + +new() -> + #dialyzer_codeserver{table_pid = table__new()}. + +-spec delete(codeserver()) -> 'ok'. + +delete(#dialyzer_codeserver{table_pid = TablePid}) -> + table__delete(TablePid). + +-spec insert(module(), cerl:c_module(), codeserver()) -> codeserver(). + +insert(Mod, ModCode, CS) -> + NewTablePid = table__insert(CS#dialyzer_codeserver.table_pid, Mod, ModCode), + CS#dialyzer_codeserver{table_pid = NewTablePid}. + +-spec insert_exports([mfa()], codeserver()) -> codeserver(). + +insert_exports(List, #dialyzer_codeserver{exports = Exports} = CS) -> + Set = sets:from_list(List), + NewExports = sets:union(Exports, Set), + CS#dialyzer_codeserver{exports = NewExports}. + +-spec is_exported(mfa(), codeserver()) -> boolean(). + +is_exported(MFA, #dialyzer_codeserver{exports = Exports}) -> + sets:is_element(MFA, Exports). + +-spec get_exports(codeserver()) -> set(). % set(mfa()) + +get_exports(#dialyzer_codeserver{exports = Exports}) -> + Exports. + +-spec lookup_mod_code(module(), codeserver()) -> cerl:c_module(). + +lookup_mod_code(Mod, CS) when is_atom(Mod) -> + table__lookup(CS#dialyzer_codeserver.table_pid, Mod). + +-spec lookup_mfa_code(mfa(), codeserver()) -> {cerl:c_var(), cerl:c_fun()}. + +lookup_mfa_code({_M, _F, _A} = MFA, CS) -> + table__lookup(CS#dialyzer_codeserver.table_pid, MFA). + +-spec get_next_core_label(codeserver()) -> label(). + +get_next_core_label(#dialyzer_codeserver{next_core_label = NCL}) -> + NCL. + +-spec set_next_core_label(label(), codeserver()) -> codeserver(). + +set_next_core_label(NCL, CS) -> + CS#dialyzer_codeserver{next_core_label = NCL}. + +-spec store_records(module(), dict(), codeserver()) -> codeserver(). + +store_records(Mod, Dict, #dialyzer_codeserver{records = RecDict} = CS) + when is_atom(Mod) -> + case dict:size(Dict) =:= 0 of + true -> CS; + false -> CS#dialyzer_codeserver{records = dict:store(Mod, Dict, RecDict)} + end. + +-spec lookup_mod_records(module(), codeserver()) -> dict(). + +lookup_mod_records(Mod, #dialyzer_codeserver{records = RecDict}) + when is_atom(Mod) -> + case dict:find(Mod, RecDict) of + error -> dict:new(); + {ok, Dict} -> Dict + end. + +-spec get_records(codeserver()) -> dict(). + +get_records(#dialyzer_codeserver{records = RecDict}) -> + RecDict. + +-spec store_temp_records(module(), dict(), codeserver()) -> codeserver(). + +store_temp_records(Mod, Dict, #dialyzer_codeserver{temp_records = TempRecDict} = CS) + when is_atom(Mod) -> + case dict:size(Dict) =:= 0 of + true -> CS; + false -> CS#dialyzer_codeserver{temp_records = dict:store(Mod, Dict, TempRecDict)} + end. + +-spec get_temp_records(codeserver()) -> dict(). + +get_temp_records(#dialyzer_codeserver{temp_records = TempRecDict}) -> + TempRecDict. + +-spec set_temp_records(dict(), codeserver()) -> codeserver(). + +set_temp_records(Dict, CS) -> + CS#dialyzer_codeserver{temp_records = Dict}. + +-spec finalize_records(dict(), codeserver()) -> codeserver(). + +finalize_records(Dict, CS) -> + CS#dialyzer_codeserver{records = Dict, temp_records = dict:new()}. + +-spec store_contracts(module(), dict(), codeserver()) -> codeserver(). + +store_contracts(Mod, Dict, #dialyzer_codeserver{contracts = C} = CS) + when is_atom(Mod) -> + case dict:size(Dict) =:= 0 of + true -> CS; + false -> CS#dialyzer_codeserver{contracts = dict:store(Mod, Dict, C)} + end. + +-spec lookup_mod_contracts(module(), codeserver()) -> dict(). + +lookup_mod_contracts(Mod, #dialyzer_codeserver{contracts = ContDict}) + when is_atom(Mod) -> + case dict:find(Mod, ContDict) of + error -> dict:new(); + {ok, Dict} -> Dict + end. + +-spec lookup_mfa_contract(mfa(), codeserver()) -> + 'error' | {'ok', dialyzer_contracts:file_contract()}. + +lookup_mfa_contract({M,_F,_A} = MFA, #dialyzer_codeserver{contracts = ContDict}) -> + case dict:find(M, ContDict) of + error -> error; + {ok, Dict} -> dict:find(MFA, Dict) + end. + +-spec get_contracts(codeserver()) -> dict(). + +get_contracts(#dialyzer_codeserver{contracts = ContDict}) -> + ContDict. + +-spec store_temp_contracts(module(), dict(), codeserver()) -> codeserver(). + +store_temp_contracts(Mod, Dict, #dialyzer_codeserver{temp_contracts = C} = CS) + when is_atom(Mod) -> + case dict:size(Dict) =:= 0 of + true -> CS; + false -> CS#dialyzer_codeserver{temp_contracts = dict:store(Mod, Dict, C)} + end. + +-spec get_temp_contracts(codeserver()) -> dict(). + +get_temp_contracts(#dialyzer_codeserver{temp_contracts = TempContDict}) -> + TempContDict. + +-spec finalize_contracts(dict(), codeserver()) -> codeserver(). + +finalize_contracts(Dict, CS) -> + CS#dialyzer_codeserver{contracts = Dict, temp_contracts = dict:new()}. + +table__new() -> + spawn_link(fun() -> table__loop(none, dict:new()) end). + +table__delete(TablePid) -> + TablePid ! stop, + ok. + +table__lookup(TablePid, Key) -> + TablePid ! {self(), lookup, Key}, + receive + {TablePid, Key, Ans} -> Ans + end. + +table__insert(TablePid, Key, Val) -> + TablePid ! {insert, [{Key, term_to_binary(Val, [compressed])}]}, + TablePid. + +table__loop(Cached, Map) -> + receive + stop -> ok; + {Pid, lookup, {M, F, A} = MFA} -> + {NewCached, Ans} = + case Cached of + {M, Tree} -> + [Val] = [VarFun || {Var, _Fun} = VarFun <- cerl:module_defs(Tree), + cerl:fname_id(Var) =:= F, + cerl:fname_arity(Var) =:= A], + {Cached, Val}; + _ -> + Tree = fetch_and_expand(M, Map), + [Val] = [VarFun || {Var, _Fun} = VarFun <- cerl:module_defs(Tree), + cerl:fname_id(Var) =:= F, + cerl:fname_arity(Var) =:= A], + {{M, Tree}, Val} + end, + Pid ! {self(), MFA, Ans}, + table__loop(NewCached, Map); + {Pid, lookup, Mod} when is_atom(Mod) -> + Ans = case Cached of + {Mod, Tree} -> Tree; + _ -> fetch_and_expand(Mod, Map) + end, + Pid ! {self(), Mod, Ans}, + table__loop({Mod, Ans}, Map); + {insert, List} -> + NewMap = lists:foldl(fun({Key, Val}, AccMap) -> + dict:store(Key, Val, AccMap) + end, Map, List), + table__loop(Cached, NewMap) + end. + +fetch_and_expand(Mod, Map) -> + try + Bin = dict:fetch(Mod, Map), + binary_to_term(Bin) + catch + _:_ -> + S = atom_to_list(Mod), + Msg = "found no module named '" ++ S ++ "' in the analyzed files", + exit({error, Msg}) + end. |