aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/src/dialyzer_codeserver.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src/dialyzer_codeserver.erl')
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl476
1 files changed, 306 insertions, 170 deletions
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index b2097f7e53..a5bb4e209c 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -2,18 +2,19 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2015. 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/.
+%% 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
%%
-%% 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.
+%% 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%
%%
@@ -28,116 +29,234 @@
-module(dialyzer_codeserver).
-export([delete/1,
- finalize_contracts/2,
+ store_temp_contracts/4,
+ give_away/2,
+ finalize_contracts/1,
finalize_exported_types/2,
finalize_records/2,
get_contracts/1,
+ get_callbacks/1,
get_exported_types/1,
get_exports/1,
get_records/1,
get_next_core_label/1,
- get_temp_contracts/1,
+ get_temp_contracts/2,
+ contracts_modules/1,
+ store_contracts/4,
get_temp_exported_types/1,
get_temp_records/1,
insert/3,
insert_exports/2,
insert_temp_exported_types/2,
+ insert_fun_meta_info/2,
is_exported/2,
lookup_mod_code/2,
lookup_mfa_code/2,
+ lookup_mfa_var_label/2,
lookup_mod_records/2,
lookup_mod_contracts/2,
lookup_mfa_contract/2,
+ lookup_meta_info/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]).
+ translate_fake_file/3]).
--export_type([codeserver/0]).
+-export_type([codeserver/0, fun_meta_info/0, contracts/0]).
-include("dialyzer.hrl").
%%--------------------------------------------------------------------
--record(codeserver, {table_pid :: pid(),
- exported_types = sets:new() :: set(), % set(mfa())
- temp_exported_types = sets:new() :: set(), % set(mfa())
- 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()}).
+-type dict_ets() :: ets:tid().
+-type map_ets() :: ets:tid().
+-type set_ets() :: ets:tid().
+
+-type types() :: erl_types:type_table().
+-type mod_records() :: erl_types:mod_records().
+
+-type contracts() :: #{mfa() => dialyzer_contracts:file_contract()}.
+-type mod_contracts() :: dict:dict(module(), contracts()).
+
+%% A property-list of data compiled from -compile and -dialyzer attributes.
+-type meta_info() :: [{{'nowarn_function' | dial_warn_tag()},
+ 'mod' | 'func'}].
+-type fun_meta_info() :: [{mfa(), meta_info()}
+ | {module(), [dial_warn_tag()]}].
+
+-record(codeserver, {next_core_label = 0 :: label(),
+ code :: dict_ets(),
+ exported_types :: set_ets(), % set(mfa())
+ records :: map_ets(),
+ contracts :: map_ets(),
+ callbacks :: map_ets(),
+ fun_meta_info :: dict_ets(), % {mfa(), meta_info()}
+ exports :: 'clean' | set_ets(), % set(mfa())
+ temp_exported_types :: 'clean' | set_ets(), % set(mfa())
+ temp_records :: 'clean' | map_ets(),
+ temp_contracts :: 'clean' | map_ets(),
+ temp_callbacks :: 'clean' | map_ets()
+ }).
-opaque codeserver() :: #codeserver{}.
%%--------------------------------------------------------------------
+ets_dict_find(Key, Table) ->
+ try ets:lookup_element(Table, Key, 2) of
+ Val -> {ok, Val}
+ catch
+ _:_ -> error
+ end.
+
+ets_map_store(Key, Element, Table) ->
+ true = ets:insert(Table, {Key, Element}),
+ Table.
+
+ets_dict_store_dict(Dict, Table) ->
+ true = ets:insert(Table, dict:to_list(Dict)).
+
+ets_dict_to_dict(Table) ->
+ Fold = fun({Key,Value}, Dict) -> dict:store(Key, Value, Dict) end,
+ ets:foldl(Fold, dict:new(), Table).
+
+ets_set_is_element(Key, Table) ->
+ ets:lookup(Table, Key) =/= [].
+
+ets_set_insert_set(Set, Table) ->
+ ets_set_insert_list(sets:to_list(Set), Table).
+
+ets_set_insert_list(List, Table) ->
+ true = ets:insert(Table, [{E} || E <- List]).
+
+ets_set_to_set(Table) ->
+ Fold = fun({E}, Set) -> sets:add_element(E, Set) end,
+ ets:foldl(Fold, sets:new(), Table).
+
+%%--------------------------------------------------------------------
+
-spec new() -> codeserver().
new() ->
- #codeserver{table_pid = table__new()}.
+ CodeOptions = [compressed, public, {read_concurrency, true}],
+ Code = ets:new(dialyzer_codeserver_code, CodeOptions),
+ ReadOptions = [compressed, {read_concurrency, true}],
+ [Contracts, Callbacks, Records, ExportedTypes] =
+ [ets:new(Name, ReadOptions) ||
+ Name <- [dialyzer_codeserver_contracts,
+ dialyzer_codeserver_callbacks,
+ dialyzer_codeserver_records,
+ dialyzer_codeserver_exported_types]],
+ TempOptions = [public, {write_concurrency, true}],
+ [Exports, FunMetaInfo, TempExportedTypes, TempRecords, TempContracts,
+ TempCallbacks] =
+ [ets:new(Name, TempOptions) ||
+ Name <-
+ [dialyzer_codeserver_exports, dialyzer_codeserver_fun_meta_info,
+ dialyzer_codeserver_temp_exported_types,
+ dialyzer_codeserver_temp_records, dialyzer_codeserver_temp_contracts,
+ dialyzer_codeserver_temp_callbacks]],
+ #codeserver{code = Code,
+ exports = Exports,
+ fun_meta_info = FunMetaInfo,
+ exported_types = ExportedTypes,
+ records = Records,
+ contracts = Contracts,
+ callbacks = Callbacks,
+ temp_exported_types = TempExportedTypes,
+ temp_records = TempRecords,
+ temp_contracts = TempContracts,
+ temp_callbacks = TempCallbacks}.
-spec delete(codeserver()) -> 'ok'.
-delete(#codeserver{table_pid = TablePid}) ->
- table__delete(TablePid).
+delete(#codeserver{code = Code, exported_types = ExportedTypes,
+ records = Records, contracts = Contracts,
+ callbacks = Callbacks}) ->
+ lists:foreach(fun ets:delete/1,
+ [Code, ExportedTypes, Records, Contracts, Callbacks]).
-spec insert(atom(), cerl:c_module(), codeserver()) -> codeserver().
insert(Mod, ModCode, CS) ->
- NewTablePid = table__insert(CS#codeserver.table_pid, Mod, ModCode),
- CS#codeserver{table_pid = NewTablePid}.
+ Name = cerl:module_name(ModCode),
+ Exports = cerl:module_exports(ModCode),
+ Attrs = cerl:module_attrs(ModCode),
+ Defs = cerl:module_defs(ModCode),
+ {Files, SmallDefs} = compress_file_anno(Defs),
+ As = cerl:get_ann(ModCode),
+ Funs =
+ [{{Mod, cerl:fname_id(Var), cerl:fname_arity(Var)},
+ Val, {Var, cerl_trees:get_label(Fun)}} || Val = {Var, Fun} <- SmallDefs],
+ Keys = [Key || {Key, _Value, _Label} <- Funs],
+ ModEntry = {Mod, {Name, Exports, Attrs, Keys, As}},
+ ModFileEntry = {{mod, Mod}, Files},
+ true = ets:insert(CS#codeserver.code, [ModEntry, ModFileEntry|Funs]),
+ CS.
+
+-spec get_temp_exported_types(codeserver()) -> sets:set(mfa()).
+
+get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) ->
+ ets_set_to_set(TempExpTypes).
--spec insert_temp_exported_types(set(), codeserver()) -> codeserver().
+-spec insert_temp_exported_types(sets:set(mfa()), codeserver()) -> codeserver().
insert_temp_exported_types(Set, CS) ->
- CS#codeserver{temp_exported_types = Set}.
+ TempExportedTypes = CS#codeserver.temp_exported_types,
+ true = ets_set_insert_set(Set, TempExportedTypes),
+ CS.
-spec insert_exports([mfa()], codeserver()) -> codeserver().
insert_exports(List, #codeserver{exports = Exports} = CS) ->
- Set = sets:from_list(List),
- NewExports = sets:union(Exports, Set),
- CS#codeserver{exports = NewExports}.
+ true = ets_set_insert_list(List, Exports),
+ CS.
+
+-spec insert_fun_meta_info(fun_meta_info(), codeserver()) -> codeserver().
+
+insert_fun_meta_info(List, #codeserver{fun_meta_info = FunMetaInfo} = CS) ->
+ true = ets:insert(FunMetaInfo, List),
+ CS.
-spec is_exported(mfa(), codeserver()) -> boolean().
is_exported(MFA, #codeserver{exports = Exports}) ->
- sets:is_element(MFA, Exports).
+ ets_set_is_element(MFA, Exports).
--spec get_exported_types(codeserver()) -> set(). % set(mfa())
+-spec get_exported_types(codeserver()) -> sets:set(mfa()).
get_exported_types(#codeserver{exported_types = ExpTypes}) ->
- ExpTypes.
-
--spec get_temp_exported_types(codeserver()) -> set().
+ ets_set_to_set(ExpTypes).
-get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) ->
- TempExpTypes.
-
--spec get_exports(codeserver()) -> set(). % set(mfa())
+-spec get_exports(codeserver()) -> sets:set(mfa()).
get_exports(#codeserver{exports = Exports}) ->
- Exports.
+ ets_set_to_set(Exports).
--spec finalize_exported_types(set(), codeserver()) -> codeserver().
+-spec finalize_exported_types(sets:set(mfa()), codeserver()) -> codeserver().
-finalize_exported_types(Set, CS) ->
- CS#codeserver{exported_types = Set, temp_exported_types = sets:new()}.
+finalize_exported_types(Set,
+ #codeserver{exported_types = ExportedTypes,
+ temp_exported_types = TempETypes} = CS) ->
+ true = ets_set_insert_set(Set, ExportedTypes),
+ true = ets:delete(TempETypes),
+ CS#codeserver{temp_exported_types = clean}.
-spec lookup_mod_code(atom(), codeserver()) -> cerl:c_module().
lookup_mod_code(Mod, CS) when is_atom(Mod) ->
- table__lookup(CS#codeserver.table_pid, Mod).
+ table__lookup(CS#codeserver.code, Mod).
-spec lookup_mfa_code(mfa(), codeserver()) -> {cerl:c_var(), cerl:c_fun()}.
lookup_mfa_code({_M, _F, _A} = MFA, CS) ->
- table__lookup(CS#codeserver.table_pid, MFA).
+ table__lookup(CS#codeserver.code, MFA).
+
+-spec lookup_mfa_var_label(mfa(), codeserver()) -> {cerl:c_var(), label()}.
+
+lookup_mfa_var_label({_M, _F, _A} = MFA, CS) ->
+ ets:lookup_element(CS#codeserver.code, MFA, 3).
-spec get_next_core_label(codeserver()) -> label().
@@ -149,161 +268,178 @@ get_next_core_label(#codeserver{next_core_label = NCL}) ->
set_next_core_label(NCL, CS) ->
CS#codeserver{next_core_label = NCL}.
--spec store_records(atom(), dict(), codeserver()) -> codeserver().
+-spec lookup_mod_records(atom(), codeserver()) -> types().
-store_records(Mod, Dict, #codeserver{records = RecDict} = CS)
- when is_atom(Mod) ->
- case dict:size(Dict) =:= 0 of
- true -> CS;
- false -> CS#codeserver{records = dict:store(Mod, Dict, RecDict)}
+lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) ->
+ case ets_dict_find(Mod, RecDict) of
+ error -> maps:new();
+ {ok, Map} -> Map
end.
--spec lookup_mod_records(atom(), codeserver()) -> dict().
-
-lookup_mod_records(Mod, #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().
+-spec get_records(codeserver()) -> mod_records().
get_records(#codeserver{records = RecDict}) ->
- RecDict.
+ ets_dict_to_dict(RecDict).
--spec store_temp_records(atom(), dict(), codeserver()) -> codeserver().
+-spec store_temp_records(module(), types(), codeserver()) -> codeserver().
-store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS)
+store_temp_records(Mod, Map, #codeserver{temp_records = TempRecDict} = CS)
when is_atom(Mod) ->
- case dict:size(Dict) =:= 0 of
+ case maps:size(Map) =:= 0 of
true -> CS;
- false -> CS#codeserver{temp_records = dict:store(Mod, Dict, TempRecDict)}
+ false -> CS#codeserver{temp_records = ets_map_store(Mod, Map, TempRecDict)}
end.
--spec get_temp_records(codeserver()) -> dict().
+-spec get_temp_records(codeserver()) -> mod_records().
get_temp_records(#codeserver{temp_records = TempRecDict}) ->
- TempRecDict.
+ ets_dict_to_dict(TempRecDict).
--spec set_temp_records(dict(), codeserver()) -> codeserver().
+-spec set_temp_records(mod_records(), codeserver()) -> codeserver().
set_temp_records(Dict, CS) ->
- CS#codeserver{temp_records = Dict}.
-
--spec finalize_records(dict(), codeserver()) -> codeserver().
-
-finalize_records(Dict, CS) ->
- CS#codeserver{records = Dict, temp_records = dict:new()}.
+ true = ets:delete(CS#codeserver.temp_records),
+ TempRecords = ets:new(dialyzer_codeserver_temp_records,[]),
+ true = ets_dict_store_dict(Dict, TempRecords),
+ CS#codeserver{temp_records = TempRecords}.
--spec store_contracts(atom(), dict(), codeserver()) -> codeserver().
+-spec finalize_records(mod_records(), codeserver()) -> codeserver().
-store_contracts(Mod, Dict, #codeserver{contracts = C} = CS) when is_atom(Mod) ->
- case dict:size(Dict) =:= 0 of
- true -> CS;
- false -> CS#codeserver{contracts = dict:store(Mod, Dict, C)}
- end.
+finalize_records(Dict, #codeserver{temp_records = TmpRecords,
+ records = Records} = CS) ->
+ true = ets:delete(TmpRecords),
+ true = ets_dict_store_dict(Dict, Records),
+ CS#codeserver{temp_records = clean}.
--spec lookup_mod_contracts(atom(), codeserver()) -> dict().
+-spec lookup_mod_contracts(atom(), codeserver()) -> contracts().
lookup_mod_contracts(Mod, #codeserver{contracts = ContDict})
when is_atom(Mod) ->
- case dict:find(Mod, ContDict) of
- error -> dict:new();
- {ok, Dict} -> Dict
+ case ets_dict_find(Mod, ContDict) of
+ error -> maps:new();
+ {ok, Keys} ->
+ maps:from_list([get_file_contract(Key, ContDict)|| Key <- Keys])
end.
+get_file_contract(Key, ContDict) ->
+ {Key, ets:lookup_element(ContDict, Key, 2)}.
+
-spec lookup_mfa_contract(mfa(), codeserver()) ->
'error' | {'ok', dialyzer_contracts:file_contract()}.
-lookup_mfa_contract({M,_F,_A} = MFA, #codeserver{contracts = ContDict}) ->
- case dict:find(M, ContDict) of
- error -> error;
- {ok, Dict} -> dict:find(MFA, Dict)
- end.
-
--spec get_contracts(codeserver()) -> dict().
+lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) ->
+ ets_dict_find(MFA, ContDict).
-get_contracts(#codeserver{contracts = ContDict}) ->
- ContDict.
+-spec lookup_meta_info(module() | mfa(), codeserver()) -> meta_info().
--spec store_temp_contracts(atom(), dict(), codeserver()) -> codeserver().
-
-store_temp_contracts(Mod, Dict, #codeserver{temp_contracts = C} = CS)
- when is_atom(Mod) ->
- case dict:size(Dict) =:= 0 of
- true -> CS;
- false -> CS#codeserver{temp_contracts = dict:store(Mod, Dict, C)}
+lookup_meta_info(MorMFA, #codeserver{fun_meta_info = FunMetaInfo}) ->
+ case ets_dict_find(MorMFA, FunMetaInfo) of
+ error -> [];
+ {ok, PropList} -> PropList
end.
--spec get_temp_contracts(codeserver()) -> dict().
+-spec get_contracts(codeserver()) -> mod_contracts().
-get_temp_contracts(#codeserver{temp_contracts = TempContDict}) ->
- TempContDict.
+get_contracts(#codeserver{contracts = ContDict}) ->
+ ets_dict_to_dict(ContDict).
--spec finalize_contracts(dict(), codeserver()) -> codeserver().
+-spec get_callbacks(codeserver()) -> list().
-finalize_contracts(Dict, CS) ->
- CS#codeserver{contracts = Dict, temp_contracts = dict:new()}.
+get_callbacks(#codeserver{callbacks = CallbDict}) ->
+ ets:tab2list(CallbDict).
-table__new() ->
- spawn_link(fun() -> table__loop(none, dict:new()) end).
+-spec store_temp_contracts(module(), contracts(), contracts(), codeserver()) ->
+ codeserver().
-table__delete(TablePid) ->
- TablePid ! stop,
+store_temp_contracts(Mod, SpecMap, CallbackMap,
+ #codeserver{temp_contracts = Cn,
+ temp_callbacks = Cb} = CS)
+ when is_atom(Mod) ->
+ CS1 = CS#codeserver{temp_contracts = ets_map_store(Mod, SpecMap, Cn)},
+ CS1#codeserver{temp_callbacks = ets_map_store(Mod, CallbackMap, Cb)}.
+
+-spec contracts_modules(codeserver()) -> [module()].
+
+contracts_modules(#codeserver{temp_contracts = TempContTable}) ->
+ ets:select(TempContTable, [{{'$1', '$2'}, [], ['$1']}]).
+
+-spec store_contracts(module(), contracts(), contracts(), codeserver()) ->
+ codeserver().
+
+store_contracts(Mod, SpecMap, CallbackMap, CS) ->
+ #codeserver{contracts = SpecDict, callbacks = CallbackDict} = CS,
+ Keys = maps:keys(SpecMap),
+ true = ets:insert(SpecDict, maps:to_list(SpecMap)),
+ true = ets:insert(SpecDict, {Mod, Keys}),
+ true = ets:insert(CallbackDict, maps:to_list(CallbackMap)),
+ CS.
+
+-spec get_temp_contracts(module(), codeserver()) ->
+ {contracts(), contracts()}.
+
+get_temp_contracts(Mod, #codeserver{temp_contracts = TempContDict,
+ temp_callbacks = TempCallDict}) ->
+ [{Mod, Contracts}] = ets:lookup(TempContDict, Mod),
+ true = ets:delete(TempContDict, Mod),
+ [{Mod, Callbacks}] = ets:lookup(TempCallDict, Mod),
+ true = ets:delete(TempCallDict, Mod),
+ {Contracts, Callbacks}.
+
+-spec give_away(codeserver(), pid()) -> 'ok'.
+
+give_away(#codeserver{temp_records = TempRecords,
+ temp_contracts = TempContracts,
+ temp_callbacks = TempCallbacks,
+ records = Records,
+ contracts = Contracts,
+ callbacks = Callbacks}, Pid) ->
+ _ = [true = ets:give_away(Table, Pid, any) ||
+ Table <- [TempRecords, TempContracts, TempCallbacks,
+ Records, Contracts, Callbacks],
+ Table =/= clean],
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.
+-spec finalize_contracts(codeserver()) -> codeserver().
+
+finalize_contracts(#codeserver{temp_contracts = TempContDict,
+ temp_callbacks = TempCallDict} = CS) ->
+ true = ets:delete(TempContDict),
+ true = ets:delete(TempCallDict),
+ CS#codeserver{temp_contracts = clean, temp_callbacks = clean}.
+
+-spec translate_fake_file(codeserver(), module(), file:filename()) ->
+ file:filename().
+
+translate_fake_file(#codeserver{code = Code}, Module, FakeFile) ->
+ Files = ets:lookup_element(Code, {mod, Module}, 2),
+ {FakeFile, File} = lists:keyfind(FakeFile, 1, Files),
+ File.
+
+table__lookup(TablePid, M) when is_atom(M) ->
+ {Name, Exports, Attrs, Keys, As} = ets:lookup_element(TablePid, M, 2),
+ Defs = [table__lookup(TablePid, Key) || Key <- Keys],
+ cerl:ann_c_module(As, Name, Exports, Attrs, Defs);
+table__lookup(TablePid, MFA) ->
+ ets:lookup_element(TablePid, MFA, 2).
+
+compress_file_anno(Term) ->
+ {Files, SmallTerm} = compress_file_anno(Term, []),
+ {[{FakeFile, File} || {File, {file, FakeFile}} <- Files], SmallTerm}.
+
+compress_file_anno({file, F}, Fs) when is_list(F) ->
+ case lists:keyfind(F, 1, Fs) of
+ false ->
+ I = integer_to_list(length(Fs)),
+ FileI = {file, I},
+ NFs = [{F, FileI}|Fs],
+ {NFs, FileI};
+ {F, FileI} -> {Fs, FileI}
+ end;
+compress_file_anno(T, Fs) when is_tuple(T) ->
+ {NFs, NL} = compress_file_anno(tuple_to_list(T), Fs),
+ {NFs, list_to_tuple(NL)};
+compress_file_anno([E|L], Fs) ->
+ {Fs1, NE} = compress_file_anno(E, Fs),
+ {NFs, NL} = compress_file_anno(L, Fs1),
+ {NFs, [NE|NL]};
+compress_file_anno(T, Fs) -> {Fs, T}.