mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-20 00:15:27 +00:00
Estimated hours taken: 5 Branches: main analysis/analysis.file.m: analysis/analysis.m: Add support for "extra info" sections in `.analysis' files. These are intended to contain any data that may be necessary but wouldn't fit as answer patterns. compiler/modules.m: Add `.imdg' and `.request' to the list of extensions treated specially by `make_file_name', so `--intermodule-analysis' works with `--use-grade-subdirs'.
837 lines
26 KiB
Mathematica
837 lines
26 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2003, 2005-2006 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
% File: analysis.file.m
|
|
% Main author: stayl
|
|
%
|
|
% An analysis file contains analysis results for a single module.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module analysis__file.
|
|
|
|
:- interface.
|
|
|
|
% read_module_overall_status(Compiler, ModuleId, MaybeModuleStatus,
|
|
% !IO)
|
|
%
|
|
% Attempt to read the overall status from a module `.analysis' file.
|
|
% If the module has outstanding requests, then an overall status of
|
|
% `optimal' is downgraded to `suboptimal'.
|
|
%
|
|
:- pred read_module_overall_status(Compiler::in, module_id::in,
|
|
maybe(analysis_status)::out, io::di, io::uo) is det
|
|
<= compiler(Compiler).
|
|
|
|
% read_module_analysis_results(AnalysisInfo, ModuleId,
|
|
% OverallStatus, AnalysisResults, ExtraInfo, !IO)
|
|
%
|
|
% Read the overall module status, analysis results and
|
|
% any extra info from a `.analysis' file.
|
|
%
|
|
:- pred read_module_analysis_results(analysis_info::in, module_id::in,
|
|
analysis_status::out, module_analysis_map(analysis_result)::out,
|
|
module_extra_info_map::out, io::di, io::uo) is det.
|
|
|
|
% write_module_analysis_results(AnalysisInfo, ModuleId,
|
|
% OverallStatus, AnalysisResults, ExtraInfo, !IO)
|
|
%
|
|
% Write the overall module status, analysis results and
|
|
% extra info to a `.analysis' file.
|
|
%
|
|
:- pred write_module_analysis_results(analysis_info::in,
|
|
module_id::in, analysis_status::in,
|
|
module_analysis_map(analysis_result)::in,
|
|
module_extra_info_map::in, io::di, io::uo) is det.
|
|
|
|
% read_module_analysis_requests(AnalysisInfo, ModuleId,
|
|
% ModuleRequests, !IO)
|
|
%
|
|
% Read outstanding analysis requests to a module from disk.
|
|
%
|
|
:- pred read_module_analysis_requests(analysis_info::in,
|
|
module_id::in, module_analysis_map(analysis_request)::out,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
% write_module_analysis_requests(AnalysisInfo, ModuleId,
|
|
% ModuleRequests, !IO)
|
|
%
|
|
% Write outstanding analysis requests for a module to disk.
|
|
%
|
|
:- pred write_module_analysis_requests(analysis_info::in,
|
|
module_id::in, module_analysis_map(analysis_request)::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
% read_module_imdg(AnalysisInfo, ModuleId, ModuleEntries, !IO)
|
|
%
|
|
% Read the intermodule dependencies graph entries for a module
|
|
% from disk.
|
|
%
|
|
:- pred read_module_imdg(analysis_info::in, module_id::in,
|
|
module_analysis_map(imdg_arc)::out, io::di, io::uo) is det.
|
|
|
|
% write_module_imdg(AnalysisInfo, ModuleId, ModuleEntries, !IO)
|
|
%
|
|
% Write the intermodule dependencies graph entries for a module
|
|
% to disk.
|
|
%
|
|
:- pred write_module_imdg(analysis_info::in, module_id::in,
|
|
module_analysis_map(imdg_arc)::in, io::di, io::uo) is det.
|
|
|
|
% empty_request_file(AnalysisInfo, ModuleId, !IO)
|
|
%
|
|
% Delete the file containing outstanding analysis requests for a
|
|
% module. This means all the analysis requests should have been
|
|
% satisfied already.
|
|
%
|
|
:- pred empty_request_file(analysis_info::in, module_id::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
% The format of an analysis result file is:
|
|
%
|
|
% version_number.
|
|
% module_status.
|
|
% extra_info(key, extra_info).
|
|
% analysis_name(analysis_version, func_id, call_pattern, answer_pattern,
|
|
% result_status).
|
|
%
|
|
% All extra_infos, if any, must come before the analysis results.
|
|
|
|
% The format of an IMDG file is:
|
|
%
|
|
% version_number.
|
|
% calling_module -> analysis_name(analysis_version, func_id, call_pattern).
|
|
|
|
% The format of an analysis request file is:
|
|
%
|
|
% version_number.
|
|
% analysis_name(analysis_version, func_id, call_pattern).
|
|
|
|
:- import_module bool, exception, parser, term, term_io, varset.
|
|
|
|
:- type invalid_analysis_file ---> invalid_analysis_file.
|
|
|
|
:- func version_number = int.
|
|
version_number = 2.
|
|
|
|
:- func analysis_registry_suffix = string.
|
|
analysis_registry_suffix = ".analysis".
|
|
|
|
:- func imdg_suffix = string.
|
|
imdg_suffix = ".imdg".
|
|
|
|
:- func request_suffix = string.
|
|
request_suffix = ".request".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
read_module_overall_status(Compiler, ModuleId, MaybeModuleStatus, !IO) :-
|
|
module_id_to_read_file_name(Compiler, ModuleId, analysis_registry_suffix,
|
|
MaybeAnalysisFileName, !IO),
|
|
(
|
|
MaybeAnalysisFileName = ok(AnalysisFileName),
|
|
read_module_overall_status_2(AnalysisFileName, MaybeModuleStatus0, !IO),
|
|
( MaybeModuleStatus0 = yes(optimal) ->
|
|
module_id_to_read_file_name(Compiler, ModuleId, request_suffix,
|
|
MaybeRequestFileName, !IO),
|
|
(
|
|
% There are outstanding requests for this module.
|
|
MaybeRequestFileName = ok(_),
|
|
MaybeModuleStatus = yes(suboptimal)
|
|
;
|
|
MaybeRequestFileName = error(_),
|
|
MaybeModuleStatus = MaybeModuleStatus0
|
|
)
|
|
;
|
|
MaybeModuleStatus = MaybeModuleStatus0
|
|
)
|
|
;
|
|
MaybeAnalysisFileName = error(_),
|
|
MaybeModuleStatus = no
|
|
).
|
|
|
|
:- pred read_module_overall_status_2(string::in, maybe(analysis_status)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_module_overall_status_2(AnalysisFileName, MaybeModuleStatus, !IO) :-
|
|
io__open_input(AnalysisFileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(Stream),
|
|
io__set_input_stream(Stream, OldStream, !IO),
|
|
|
|
promise_only_solution_io(
|
|
(pred(Status::out, !.IO::di, !:IO::uo) is cc_multi :-
|
|
try_io((pred(Status0::out, !.IO::di, !:IO::uo) is det :-
|
|
check_analysis_file_version_number(!IO),
|
|
read_module_status(Status0, !IO)
|
|
), Status, !IO)
|
|
), Result, !IO),
|
|
(
|
|
Result = succeeded(ModuleStatus),
|
|
MaybeModuleStatus = yes(ModuleStatus)
|
|
;
|
|
Result = failed,
|
|
MaybeModuleStatus = no
|
|
;
|
|
Result = exception(_),
|
|
% XXX Report error.
|
|
MaybeModuleStatus = no
|
|
),
|
|
io__set_input_stream(OldStream, _, !IO),
|
|
io__close_input(Stream, !IO)
|
|
;
|
|
OpenResult = error(_),
|
|
MaybeModuleStatus = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
read_module_analysis_results(Info, ModuleId, ModuleStatus, ModuleResults,
|
|
ExtraInfo, !IO) :-
|
|
% If the module's overall status is `invalid' then at least one of its
|
|
% results is invalid. However, we can't just discard the results as we
|
|
% want to know which results change after we reanalyse the module.
|
|
Compiler = Info ^ compiler,
|
|
module_id_to_read_file_name(Compiler, ModuleId, analysis_registry_suffix,
|
|
MaybeAnalysisFileName, !IO),
|
|
(
|
|
MaybeAnalysisFileName = ok(AnalysisFileName),
|
|
read_module_analysis_results_2(Compiler, AnalysisFileName,
|
|
ModuleStatus, ModuleResults, ExtraInfo, !IO)
|
|
;
|
|
MaybeAnalysisFileName = error(_),
|
|
ModuleStatus = optimal,
|
|
ModuleResults = map.init,
|
|
ExtraInfo = map.init
|
|
).
|
|
|
|
:- pred read_module_analysis_results_2(Compiler::in, string::in,
|
|
analysis_status::out, module_analysis_map(analysis_result)::out,
|
|
module_extra_info_map::out, io::di, io::uo) is det <= compiler(Compiler).
|
|
|
|
read_module_analysis_results_2(Compiler, AnalysisFileName,
|
|
ModuleStatus, ModuleResults, ExtraInfo, !IO) :-
|
|
ModuleResults0 = map.init,
|
|
io.open_input(AnalysisFileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(Stream),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Reading analysis registry file ", !IO),
|
|
io.print(AnalysisFileName, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
io.set_input_stream(Stream, OldStream, !IO),
|
|
|
|
check_analysis_file_version_number(!IO),
|
|
read_module_status(ModuleStatus, !IO),
|
|
read_module_extra_infos(map.init, ExtraInfo,
|
|
MaybeFirstResultEntry, !IO),
|
|
(
|
|
MaybeFirstResultEntry = yes(FirstResultEntry),
|
|
ParseEntry = parse_result_entry(Compiler),
|
|
promise_only_solution_io(
|
|
(pred(Results3::out, !.IO::di, !:IO::uo) is cc_multi :-
|
|
try_io((pred(Results2::out, !.IO::di, !:IO::uo) is det :-
|
|
ParseEntry(FirstResultEntry, ModuleResults0, Results1),
|
|
read_analysis_file_2(ParseEntry, Results1, Results2,
|
|
!IO)
|
|
), Results3, !IO)
|
|
), Results, !IO),
|
|
(
|
|
Results = succeeded(ModuleResults)
|
|
;
|
|
Results = failed,
|
|
ModuleResults = ModuleResults0
|
|
;
|
|
Results = exception(_),
|
|
% XXX Report error.
|
|
ModuleResults = ModuleResults0
|
|
)
|
|
;
|
|
MaybeFirstResultEntry = no,
|
|
ModuleResults = map.init
|
|
),
|
|
io.set_input_stream(OldStream, _, !IO),
|
|
io.close_input(Stream, !IO)
|
|
;
|
|
OpenResult = error(_),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Error reading analysis registry file: ", !IO),
|
|
io.print(AnalysisFileName, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
ModuleStatus = optimal,
|
|
ModuleResults = ModuleResults0,
|
|
ExtraInfo = map.init
|
|
).
|
|
|
|
:- pred read_module_status(analysis_status::out, io::di, io::uo) is det.
|
|
|
|
read_module_status(Status, !IO) :-
|
|
parser__read_term(TermResult `with_type` read_term, !IO),
|
|
( TermResult = term(_, term__functor(term__atom(String), [], _)) ->
|
|
( analysis_status_to_string(Status0, String) ->
|
|
Status = Status0
|
|
;
|
|
throw(invalid_analysis_file)
|
|
)
|
|
;
|
|
throw(invalid_analysis_file)
|
|
).
|
|
|
|
:- pred analysis_status_to_string(analysis_status, string).
|
|
:- mode analysis_status_to_string(in, out) is det.
|
|
:- mode analysis_status_to_string(out, in) is semidet.
|
|
|
|
analysis_status_to_string(invalid, "invalid").
|
|
analysis_status_to_string(suboptimal, "suboptimal").
|
|
analysis_status_to_string(optimal, "optimal").
|
|
|
|
:- pred read_module_extra_infos(module_extra_info_map::in,
|
|
module_extra_info_map::out, maybe(term)::out, io::di, io::uo) is det.
|
|
|
|
read_module_extra_infos(ExtraInfo0, ExtraInfo, MaybeFirstResultEntry, !IO) :-
|
|
parser.read_term(TermResult, !IO),
|
|
(
|
|
TermResult = eof,
|
|
ExtraInfo = ExtraInfo0,
|
|
MaybeFirstResultEntry = no
|
|
;
|
|
TermResult = error(_, _),
|
|
throw(invalid_analysis_file)
|
|
;
|
|
TermResult = term(_, Term),
|
|
(
|
|
Term = term.functor(atom("extra_info"), Args, _)
|
|
->
|
|
(
|
|
Args = [KeyTerm, ValueTerm],
|
|
KeyTerm = term.functor(string(Key), [], _),
|
|
ValueTerm = term.functor(string(Value), [], _)
|
|
->
|
|
map.det_insert(ExtraInfo0, Key, Value, ExtraInfo1),
|
|
read_module_extra_infos(ExtraInfo1, ExtraInfo,
|
|
MaybeFirstResultEntry, !IO)
|
|
;
|
|
throw(invalid_analysis_file)
|
|
)
|
|
;
|
|
ExtraInfo = ExtraInfo0,
|
|
MaybeFirstResultEntry = yes(Term)
|
|
)
|
|
).
|
|
|
|
:- pred parse_result_entry(Compiler::in)
|
|
`with_type` parse_entry(module_analysis_map(analysis_result))
|
|
`with_inst` parse_entry <= compiler(Compiler).
|
|
|
|
parse_result_entry(Compiler, Term, Results0, Results) :-
|
|
(
|
|
Term = term__functor(term__atom(AnalysisName),
|
|
[VersionNumberTerm, FuncIdTerm,
|
|
CallPatternTerm, AnswerPatternTerm, StatusTerm], _),
|
|
FuncIdTerm = term__functor(term__string(FuncId), [], _),
|
|
CallPatternTerm = term__functor(
|
|
term__string(CallPatternString), [], _),
|
|
AnswerPatternTerm = term__functor(
|
|
term__string(AnswerPatternString), [], _),
|
|
StatusTerm = term__functor(term__string(StatusString), [], _),
|
|
analysis_type(_ `with_type` unit(Call),
|
|
_ `with_type` unit(Answer)) =
|
|
analyses(Compiler, AnalysisName),
|
|
|
|
CallPattern = from_string(CallPatternString) `with_type` Call,
|
|
AnswerPattern = from_string(AnswerPatternString) `with_type` Answer,
|
|
analysis_status_to_string(Status, StatusString)
|
|
->
|
|
(
|
|
VersionNumber = analysis_version_number(
|
|
_ `with_type` Call,
|
|
_ `with_type` Answer),
|
|
VersionNumberTerm = term__functor(
|
|
term__integer(VersionNumber), [], _)
|
|
->
|
|
Result = 'new analysis_result'(CallPattern, AnswerPattern,
|
|
Status),
|
|
( AnalysisResults0 = map__search(Results0, AnalysisName) ->
|
|
AnalysisResults1 = AnalysisResults0
|
|
;
|
|
AnalysisResults1 = map__init
|
|
),
|
|
(
|
|
FuncResults0 = map__search(AnalysisResults1,
|
|
FuncId)
|
|
->
|
|
FuncResults = [Result | FuncResults0]
|
|
;
|
|
FuncResults = [Result]
|
|
),
|
|
Results = map__set(Results0, AnalysisName,
|
|
map__set(AnalysisResults1,
|
|
FuncId, FuncResults))
|
|
;
|
|
% Ignore results with an out-of-date version number.
|
|
Results = Results0
|
|
)
|
|
;
|
|
throw(invalid_analysis_file)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
read_module_analysis_requests(Info, ModuleId, ModuleRequests, !IO) :-
|
|
read_analysis_file(Info ^ compiler, ModuleId, request_suffix,
|
|
parse_request_entry(Info ^ compiler),
|
|
map__init, ModuleRequests, !IO).
|
|
|
|
:- pred parse_request_entry(Compiler::in)
|
|
`with_type` parse_entry(module_analysis_map(analysis_request))
|
|
`with_inst` parse_entry <= compiler(Compiler).
|
|
|
|
parse_request_entry(Compiler, Term, Requests0, Requests) :-
|
|
(
|
|
Term = term__functor(term__atom(AnalysisName),
|
|
[VersionNumberTerm, FuncIdTerm, CallPatternTerm], _),
|
|
FuncIdTerm = term__functor(term__string(FuncId), [], _),
|
|
CallPatternTerm = term__functor(
|
|
term__string(CallPatternString), [], _),
|
|
analysis_type(_ `with_type` unit(Call), _ `with_type` unit(Answer)) =
|
|
analyses(Compiler, AnalysisName),
|
|
CallPattern = from_string(CallPatternString) `with_type` Call
|
|
->
|
|
(
|
|
VersionNumber = analysis_version_number(
|
|
_ `with_type` Call,
|
|
_ `with_type` Answer),
|
|
VersionNumberTerm = term__functor(
|
|
term__integer(VersionNumber), [], _)
|
|
->
|
|
Result = 'new analysis_request'(CallPattern),
|
|
(
|
|
AnalysisRequests0 = map__search(Requests0,
|
|
AnalysisName)
|
|
->
|
|
AnalysisRequests1 = AnalysisRequests0
|
|
;
|
|
AnalysisRequests1 = map__init
|
|
),
|
|
(
|
|
FuncRequests0 = map__search(AnalysisRequests1,
|
|
FuncId)
|
|
->
|
|
FuncRequests = [Result | FuncRequests0]
|
|
;
|
|
FuncRequests = [Result]
|
|
),
|
|
Requests = map__set(Requests0, AnalysisName,
|
|
map__set(AnalysisRequests1,
|
|
FuncId, FuncRequests))
|
|
;
|
|
% Ignore requests with an out-of-date version number.
|
|
Requests = Requests0
|
|
)
|
|
;
|
|
throw(invalid_analysis_file)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
read_module_imdg(Info, ModuleId, ModuleEntries, !IO) :-
|
|
read_analysis_file(Info ^ compiler, ModuleId, imdg_suffix,
|
|
parse_imdg_arc(Info ^ compiler),
|
|
map.init, ModuleEntries, !IO).
|
|
|
|
:- pred parse_imdg_arc(Compiler::in)
|
|
`with_type` parse_entry(module_analysis_map(imdg_arc))
|
|
`with_inst` parse_entry <= compiler(Compiler).
|
|
|
|
parse_imdg_arc(Compiler, Term, Arcs0, Arcs) :-
|
|
(
|
|
Term = term.functor(atom("->"),
|
|
[term.functor(string(DependentModule), [], _), ResultTerm], _),
|
|
ResultTerm = functor(atom(AnalysisName),
|
|
[VersionNumberTerm, FuncIdTerm, CallPatternTerm], _),
|
|
FuncIdTerm = term.functor(term.string(FuncId), [], _),
|
|
CallPatternTerm = functor(string(CallPatternString), [], _),
|
|
analysis_type(_ : unit(Call), _ : unit(Answer))
|
|
= analyses(Compiler, AnalysisName),
|
|
CallPattern = from_string(CallPatternString) : Call
|
|
->
|
|
(
|
|
VersionNumber = analysis_version_number(_ : Call, _ : Answer),
|
|
VersionNumberTerm = term.functor(
|
|
term.integer(VersionNumber), [], _)
|
|
->
|
|
Arc = 'new imdg_arc'(CallPattern, DependentModule),
|
|
( AnalysisArcs0 = map.search(Arcs0, AnalysisName) ->
|
|
AnalysisArcs1 = AnalysisArcs0
|
|
;
|
|
AnalysisArcs1 = map.init
|
|
),
|
|
( FuncArcs0 = map.search(AnalysisArcs1, FuncId) ->
|
|
FuncArcs = [Arc | FuncArcs0]
|
|
;
|
|
FuncArcs = [Arc]
|
|
),
|
|
Arcs = map.set(Arcs0, AnalysisName,
|
|
map.set(AnalysisArcs1, FuncId, FuncArcs))
|
|
;
|
|
% Ignore results with an out-of-date version number.
|
|
% XXX: is that the right thing to do?
|
|
% do we really need a version number for the IMDG?
|
|
Arcs = Arcs0
|
|
)
|
|
;
|
|
throw(invalid_analysis_file)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type read_analysis_header(T) == pred(T, io, io).
|
|
:- inst read_analysis_header == (pred(out, di, uo) is det).
|
|
|
|
:- type parse_entry(T) == pred(term, T, T).
|
|
:- inst parse_entry == (pred(in, in, out) is det).
|
|
|
|
:- pred read_analysis_file(Compiler::in, module_id::in, string::in,
|
|
parse_entry(T)::in(parse_entry), T::in, T::out,
|
|
io__state::di, io__state::uo) is det <= compiler(Compiler).
|
|
|
|
read_analysis_file(Compiler, ModuleId, Suffix,
|
|
ParseEntry, ModuleResults0, ModuleResults, !IO) :-
|
|
module_id_to_read_file_name(Compiler, ModuleId,
|
|
Suffix, MaybeAnalysisFileName, !IO),
|
|
(
|
|
MaybeAnalysisFileName = ok(AnalysisFileName),
|
|
read_analysis_file(AnalysisFileName,
|
|
ParseEntry, ModuleResults0, ModuleResults, !IO)
|
|
;
|
|
MaybeAnalysisFileName = error(Message),
|
|
debug_msg(io.print("Couldn't open " ++ Suffix ++
|
|
" for module " ++ ModuleId ++
|
|
": " ++ Message ++ "\n"), !IO),
|
|
ModuleResults = ModuleResults0
|
|
).
|
|
|
|
:- pred read_analysis_file(string::in,
|
|
parse_entry(T)::in(parse_entry), T::in, T::out,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
read_analysis_file(AnalysisFileName,
|
|
ParseEntry, ModuleResults0, ModuleResults, !IO) :-
|
|
io__open_input(AnalysisFileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(Stream),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Reading analysis file ", !IO),
|
|
io.print(AnalysisFileName, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
io__set_input_stream(Stream, OldStream, !IO),
|
|
|
|
promise_only_solution_io(
|
|
(pred(R::out, di, uo) is cc_multi -->
|
|
try_io((pred(Results1::out, di, uo) is det -->
|
|
check_analysis_file_version_number,
|
|
read_analysis_file_2(ParseEntry,
|
|
ModuleResults0, Results1)
|
|
), R)
|
|
), Result, !IO),
|
|
(
|
|
Result = succeeded(ModuleResults)
|
|
;
|
|
Result = failed,
|
|
ModuleResults = ModuleResults0
|
|
;
|
|
Result = exception(_),
|
|
% XXX Report error.
|
|
ModuleResults = ModuleResults0
|
|
),
|
|
io__set_input_stream(OldStream, _, !IO),
|
|
io__close_input(Stream, !IO)
|
|
;
|
|
OpenResult = error(_),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Error reading analysis file: ", !IO),
|
|
io.print(AnalysisFileName, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
ModuleResults = ModuleResults0
|
|
).
|
|
|
|
:- pred check_analysis_file_version_number(io::di, io::uo) is det.
|
|
|
|
check_analysis_file_version_number(!IO) :-
|
|
parser__read_term(TermResult `with_type` read_term, !IO),
|
|
(
|
|
TermResult = term(_, term__functor(
|
|
term__integer(version_number), [], _))
|
|
->
|
|
true
|
|
;
|
|
throw(invalid_analysis_file)
|
|
).
|
|
|
|
:- pred read_analysis_file_2(parse_entry(T)::in(parse_entry),
|
|
T::in, T::out, io__state::di, io__state::uo) is det.
|
|
|
|
read_analysis_file_2(ParseEntry, Results0, Results, !IO) :-
|
|
parser__read_term(TermResult, !IO),
|
|
(
|
|
TermResult = term(_, Term) `with_type` read_term,
|
|
ParseEntry(Term, Results0, Results1),
|
|
read_analysis_file_2(ParseEntry, Results1, Results, !IO)
|
|
;
|
|
TermResult = eof,
|
|
Results = Results0
|
|
;
|
|
TermResult = error(_, _),
|
|
throw(invalid_analysis_file)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
write_module_analysis_results(Info, ModuleId, ModuleStatus, ModuleResults,
|
|
ExtraInfo, !IO) :-
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Writing module analysis results for ", !IO),
|
|
io.print(ModuleId, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
WriteHeader = write_module_status_and_extra_info(ModuleStatus, ExtraInfo),
|
|
write_analysis_file(Info ^ compiler,
|
|
ModuleId, analysis_registry_suffix,
|
|
WriteHeader, write_result_entry, ModuleResults, !IO).
|
|
|
|
:- pred write_module_status_and_extra_info(analysis_status::in,
|
|
module_extra_info_map::in, io::di, io::uo) is det.
|
|
|
|
write_module_status_and_extra_info(Status, ExtraInfo, !IO) :-
|
|
write_module_status(Status, !IO),
|
|
map.foldl(write_extra_info, ExtraInfo, !IO).
|
|
|
|
:- pred write_module_status(analysis_status::in, io::di, io::uo) is det.
|
|
|
|
write_module_status(Status, !IO) :-
|
|
term_io.write_term_nl(init:varset, Term, !IO),
|
|
Term = functor(atom(String), [], context_init),
|
|
analysis_status_to_string(Status, String).
|
|
|
|
:- pred write_extra_info(extra_info_key::in, string::in,
|
|
io::di, io::uo) is det.
|
|
|
|
write_extra_info(Key, Value, !IO) :-
|
|
term_io.write_term_nl(varset.init : varset, Term, !IO),
|
|
Term = functor(atom("extra_info"), [KeyTerm, ValueTerm], context_init),
|
|
KeyTerm = functor(string(Key), [], context_init),
|
|
ValueTerm = functor(string(Value), [], context_init).
|
|
|
|
:- pred write_result_entry `with_type` write_entry(analysis_result)
|
|
`with_inst` write_entry.
|
|
|
|
write_result_entry(AnalysisName, FuncId, Result, !IO) :-
|
|
Result = analysis_result(Call, Answer, Status),
|
|
VersionNumber = analysis_version_number(Call, Answer),
|
|
analysis_status_to_string(Status, StatusString),
|
|
term_io__write_term_nl(varset__init `with_type` varset,
|
|
functor(atom(AnalysisName), [
|
|
functor(integer(VersionNumber), [], context_init),
|
|
functor(string(FuncId), [], context_init),
|
|
functor(string(to_string(Call)), [], context_init),
|
|
functor(string(to_string(Answer)), [], context_init),
|
|
functor(string(StatusString), [], context_init)
|
|
], context_init), !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
write_module_analysis_requests(Info, ModuleId, ModuleRequests, !IO) :-
|
|
Compiler = Info ^ compiler,
|
|
module_id_to_write_file_name(Compiler, ModuleId, request_suffix,
|
|
AnalysisFileName, !IO),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Writing module analysis requests to ", !IO),
|
|
io.print(AnalysisFileName, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
io__open_input(AnalysisFileName, InputResult, !IO),
|
|
( InputResult = ok(InputStream) ->
|
|
%
|
|
% Request file already exists. Check it has the right version
|
|
% number, then append the new requests to the end.
|
|
%
|
|
io__set_input_stream(InputStream, OldInputStream, !IO),
|
|
parser__read_term(VersionResult `with_type` read_term, !IO),
|
|
io__set_input_stream(OldInputStream, _, !IO),
|
|
io__close_input(InputStream, !IO),
|
|
(
|
|
VersionResult = term(_, term__functor(
|
|
term__integer(version_number), [], _))
|
|
->
|
|
io__open_append(AnalysisFileName, AppendResult, !IO),
|
|
( AppendResult = ok(AppendStream) ->
|
|
io__set_output_stream(AppendStream,
|
|
OldOutputStream, !IO),
|
|
write_analysis_entries(
|
|
write_request_entry(Compiler),
|
|
ModuleRequests, !IO),
|
|
io__set_output_stream(OldOutputStream, _, !IO),
|
|
io__close_output(AppendStream, !IO),
|
|
Appended = yes
|
|
;
|
|
Appended = no
|
|
)
|
|
;
|
|
Appended = no
|
|
)
|
|
;
|
|
Appended = no
|
|
),
|
|
( Appended = no ->
|
|
write_analysis_file(AnalysisFileName,
|
|
nop, write_request_entry(Compiler),
|
|
ModuleRequests, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred write_request_entry(Compiler::in)
|
|
`with_type` write_entry(analysis_request)
|
|
`with_inst` write_entry <= compiler(Compiler).
|
|
|
|
write_request_entry(Compiler, AnalysisName, FuncId,
|
|
analysis_request(Call), !IO) :-
|
|
(
|
|
analysis_type(
|
|
_ `with_type` unit(Call),
|
|
_ `with_type` unit(Answer)) =
|
|
analyses(Compiler, AnalysisName)
|
|
->
|
|
VersionNumber = analysis_version_number(
|
|
_ `with_type` Call,
|
|
_ `with_type` Answer)
|
|
;
|
|
error("write_request_entry: unknown analysis type")
|
|
|
|
),
|
|
term_io__write_term_nl(varset__init `with_type` varset,
|
|
functor(atom(AnalysisName), [
|
|
functor(integer(VersionNumber), [], context_init),
|
|
functor(string(FuncId), [], context_init),
|
|
functor(string(to_string(Call)), [], context_init)
|
|
], context_init), !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
write_module_imdg(Info, ModuleId, ModuleEntries, !IO) :-
|
|
write_analysis_file(Info ^ compiler, ModuleId, imdg_suffix,
|
|
nop, write_imdg_arc(Info ^ compiler), ModuleEntries, !IO).
|
|
|
|
:- pred write_imdg_arc(Compiler::in)
|
|
`with_type` write_entry(imdg_arc)
|
|
`with_inst` write_entry <= compiler(Compiler).
|
|
|
|
write_imdg_arc(Compiler, AnalysisName, FuncId,
|
|
imdg_arc(Call, DependentModule), !IO) :-
|
|
(
|
|
analysis_type(_ : unit(Call), _ : unit(Answer))
|
|
= analyses(Compiler, AnalysisName)
|
|
->
|
|
VersionNumber = analysis_version_number(_ : Call, _ : Answer)
|
|
;
|
|
error("write_imdg_arc: unknown analysis type")
|
|
),
|
|
term_io.write_term_nl(varset.init : varset,
|
|
functor(atom("->"), [
|
|
functor(string(DependentModule), [], context_init),
|
|
ResultTerm
|
|
], context_init), !IO),
|
|
ResultTerm = functor(atom(AnalysisName), [
|
|
functor(integer(VersionNumber), [], context_init),
|
|
functor(string(FuncId), [], context_init),
|
|
functor(string(to_string(Call)), [], context_init)
|
|
], context_init).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type write_header == pred(io, io).
|
|
:- inst write_header == (pred(di, uo) is det).
|
|
|
|
:- type write_entry(T) == pred(analysis_name, func_id, T, io__state, io__state).
|
|
:- inst write_entry == (pred(in, in, in, di, uo) is det).
|
|
|
|
:- pred write_analysis_file(Compiler::in, module_id::in, string::in,
|
|
write_header::in(write_header),
|
|
write_entry(T)::in(write_entry), module_analysis_map(T)::in,
|
|
io__state::di, io__state::uo) is det <= compiler(Compiler).
|
|
|
|
write_analysis_file(Compiler, ModuleId, Suffix, WriteHeader, WriteEntry,
|
|
ModuleResults, !IO) :-
|
|
module_id_to_write_file_name(Compiler, ModuleId, Suffix,
|
|
AnalysisFileName, !IO),
|
|
write_analysis_file(AnalysisFileName, WriteHeader, WriteEntry,
|
|
ModuleResults, !IO).
|
|
|
|
:- pred write_analysis_file(string::in, write_header::in(write_header),
|
|
write_entry(T)::in(write_entry), module_analysis_map(T)::in,
|
|
io__state::di, io__state::uo) is det.
|
|
|
|
write_analysis_file(AnalysisFileName, WriteHeader, WriteEntry,
|
|
ModuleResults, !IO) :-
|
|
io__open_output(AnalysisFileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(Stream),
|
|
io__set_output_stream(Stream, OldOutput, !IO),
|
|
io__write_int(version_number, !IO),
|
|
io__write_string(".\n", !IO),
|
|
WriteHeader(!IO),
|
|
write_analysis_entries(WriteEntry, ModuleResults, !IO),
|
|
io__set_output_stream(OldOutput, _, !IO),
|
|
io__close_output(Stream, !IO)
|
|
;
|
|
OpenResult = error(Msg),
|
|
io__write_string("Error opening ", !IO),
|
|
io__write_string(AnalysisFileName, !IO),
|
|
io__write_string(" for output: ", !IO),
|
|
io__write_string(io__error_message(Msg), !IO),
|
|
io__nl(!IO)
|
|
).
|
|
|
|
:- pred write_analysis_entries(write_entry(T)::in(write_entry),
|
|
module_analysis_map(T)::in, io__state::di, io__state::uo) is det.
|
|
|
|
write_analysis_entries(WriteEntry, ModuleResults, !IO) :-
|
|
map__foldl(
|
|
(pred(AnalysisName::in, FuncResults::in, di, uo) is det -->
|
|
map__foldl(
|
|
(pred(FuncId::in, FuncResultList::in, di, uo) is det -->
|
|
list__foldl(
|
|
(pred(FuncResult::in, di, uo) is det -->
|
|
WriteEntry(AnalysisName, FuncId, FuncResult)
|
|
), FuncResultList)
|
|
), FuncResults)
|
|
), ModuleResults, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
empty_request_file(Info, ModuleId, !IO) :-
|
|
module_id_to_write_file_name(Info ^ compiler, ModuleId, request_suffix,
|
|
RequestFileName, !IO),
|
|
debug_msg((pred(!.IO::di, !:IO::uo) is det :-
|
|
io.print("Removing request file ", !IO),
|
|
io.print(RequestFileName, !IO),
|
|
io.nl(!IO)
|
|
), !IO),
|
|
io__remove_file(RequestFileName, _, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred nop(io::di, io::uo) is det.
|
|
|
|
nop(!IO).
|