View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2012-2016, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(prolog_codewalk,
   36          [ prolog_walk_code/1,         % +Options
   37            prolog_program_clause/2     % -ClauseRef, +Options
   38          ]).   39:- use_module(library(option)).   40:- use_module(library(record)).   41:- use_module(library(debug)).   42:- use_module(library(apply)).   43:- use_module(library(lists)).   44:- use_module(library(prolog_metainference)).

Prolog code walker

This module walks over the loaded program, searching for callable predicates. It started as part of library(prolog_autoload) and has been turned into a seperate module to facilitate operations that require the same reachability analysis, such as finding references to a predicate, finding unreachable code, etc.

For example, the following determins the call graph of the loaded program. By using source(true), The exact location of the call in the source file is passed into _Where.

:- dynamic
        calls/2.

assert_call_graph :-
        retractall(calls(_, _)),
        prolog_walk_code([ trace_reference(_),
                           on_trace(assert_edge),
                           source(false)
                         ]),
        predicate_property(calls(_,_), number_of_clauses(N)),
        format('Got ~D edges~n', [N]).

assert_edge(Callee, Caller, _Where) :-
        calls(Caller, Callee), !.
assert_edge(Callee, Caller, _Where) :-
        assertz(calls(Caller, Callee)).

*/

   78:- meta_predicate
   79    prolog_walk_code(:).   80
   81:- multifile
   82    prolog:called_by/4,
   83    prolog:called_by/2.   84
   85:- predicate_options(prolog_walk_code/1, 1,
   86                     [ undefined(oneof([ignore,error,trace])),
   87                       autoload(boolean),
   88                       clauses(list),
   89                       module(atom),
   90                       module_class(list(oneof([user,system,library,
   91                                                test,development]))),
   92                       source(boolean),
   93                       trace_reference(any),
   94                       on_trace(callable),
   95                       infer_meta_predicates(oneof([false,true,all])),
   96                       evaluate(boolean),
   97                       verbose(boolean)
   98                     ]).   99
  100:- record
  101    walk_option(undefined:oneof([ignore,error,trace])=ignore,
  102                autoload:boolean=true,
  103                source:boolean=true,
  104                module:atom,                % Only analyse given module
  105                module_class:list(oneof([user,system,library,
  106                                         test,development]))=[user,library],
  107                infer_meta_predicates:oneof([false,true,all])=true,
  108                clauses:list,               % Walk only these clauses
  109                trace_reference:any=(-),
  110                on_trace:callable,          % Call-back on trace hits
  111                                            % private stuff
  112                clause,                     % Processed clause
  113                caller,                     % Head of the caller
  114                initialization,             % Initialization source
  115                undecided,                  % Error to throw error
  116                evaluate:boolean,           % Do partial evaluation
  117                verbose:boolean=false).     % Report progress
  118
  119:- thread_local
  120    multifile_predicate/3.          % Name, Arity, Module
 prolog_walk_code(+Options) is det
Walk over all loaded (user) Prolog code. The following code is processed:
  1. The bodies of all clauses in all user and library modules. This steps collects, but does not scan multifile predicates to avoid duplicate work.
  2. All multi-file predicates collected.
  3. All goals registered with initialization/1

Options processed:

undefined(+Action)
Action defines what happens if the analysis finds a definitely undefined predicate. One of ignore or error (default is ignore).
autoload(+Boolean)
Try to autoload code while walking. This is enabled by default to obtain as much as possible information about goals and find references from autoloaded libraries.
clauses(+ListOfClauseReferences)
Only process the given clauses. Can be used to find clauses quickly using source(false) and then process only interesting clauses with source information.
module(+Module)
Only process the given module
module_class(+ModuleClass)
Limit processing to modules of this class. See module_property/2 for details on module classes. Default is to scan the classes user and library.
infer_meta_predicates(+BooleanOrAll)
Use infer_meta_predicate/2 on predicates with clauses that call known meta-predicates. The analysis is restarted until a fixed point is reached. If true (default), analysis is only restarted if the inferred meta-predicate contains a callable argument. If all, it will be restarted until no more new meta-predicates can be found.
trace_reference(Callable)
Print all calls to goals that subsume Callable. Goals are represented as Module:Callable (i.e., they are always qualified). See also subsumes_term/2.
on_trace(:OnTrace)
If a reference to trace_reference is found, call call(OnTrace, Callee, Caller, Location), where Location is one of these:
  • clause_term_position(+ClauseRef, +TermPos)
  • clause(+ClauseRef)
  • file_term_position(+Path, +TermPos)
  • file(+File, +Line, -1, _)
  • a variable (unknown)

Caller is the qualified head of the calling clause or the atom '<initialization>'.

source(+Boolean)
If false (default true), to not try to obtain detailed source information for printed messages.
verbose(+Boolean)
If true (default false), report derived meta-predicates and iterations.
@compat OnTrace was called using Caller-Location in older versions.
  196prolog_walk_code(Options) :-
  197    meta_options(is_meta, Options, QOptions),
  198    prolog_walk_code(1, QOptions).
  199
  200prolog_walk_code(Iteration, Options) :-
  201    statistics(cputime, CPU0),
  202    make_walk_option(Options, OTerm, _),
  203    (   walk_option_clauses(OTerm, Clauses),
  204        nonvar(Clauses)
  205    ->  walk_clauses(Clauses, OTerm)
  206    ;   forall(( walk_option_module(OTerm, M),
  207                 current_module(M),
  208                 scan_module(M, OTerm)
  209               ),
  210               find_walk_from_module(M, OTerm)),
  211        walk_from_multifile(OTerm),
  212        walk_from_initialization(OTerm)
  213    ),
  214    infer_new_meta_predicates(New, OTerm),
  215    statistics(cputime, CPU1),
  216    (   New \== []
  217    ->  CPU is CPU1-CPU0,
  218        (   walk_option_verbose(OTerm, true)
  219        ->  Level = informational
  220        ;   Level = silent
  221        ),
  222        print_message(Level,
  223                      codewalk(reiterate(New, Iteration, CPU))),
  224        succ(Iteration, Iteration2),
  225        prolog_walk_code(Iteration2, Options)
  226    ;   true
  227    ).
  228
  229is_meta(on_trace).
 walk_clauses(+Clauses, +OTerm) is det
Walk the given clauses.
  236walk_clauses(Clauses, OTerm) :-
  237    must_be(list, Clauses),
  238    forall(member(ClauseRef, Clauses),
  239           ( user:clause(CHead, Body, ClauseRef),
  240             (   CHead = Module:Head
  241             ->  true
  242             ;   Module = user,
  243                 Head = CHead
  244             ),
  245             walk_option_clause(OTerm, ClauseRef),
  246             walk_option_caller(OTerm, Module:Head),
  247             walk_called_by_body(Body, Module, OTerm)
  248           )).
 scan_module(+Module, +OTerm) is semidet
True if we must scan Module according to OTerm.
  254scan_module(M, OTerm) :-
  255    walk_option_module_class(OTerm, Classes),
  256    module_property(M, class(Class)),
  257    memberchk(Class, Classes).
 walk_from_initialization(+OTerm)
Find initialization/1,2 directives and process what they are calling. Skip
bug
- Relies on private '$init_goal'/3 database.
  266walk_from_initialization(OTerm) :-
  267    walk_option_caller(OTerm, '<initialization>'),
  268    forall('$init_goal'(_File, Goal, SourceLocation),
  269           ( walk_option_initialization(OTerm, SourceLocation),
  270             walk_from_initialization(Goal, OTerm))).
  271
  272walk_from_initialization(M:Goal, OTerm) :-
  273    scan_module(M, OTerm),
  274    !,
  275    walk_called_by_body(Goal, M, OTerm).
  276walk_from_initialization(_, _).
 find_walk_from_module(+Module, +OTerm) is det
Find undefined calls from the bodies of all clauses that belong to Module.
  284find_walk_from_module(M, OTerm) :-
  285    debug(autoload, 'Analysing module ~q', [M]),
  286    forall(predicate_in_module(M, PI),
  287           walk_called_by_pred(M:PI, OTerm)).
  288
  289walk_called_by_pred(Module:Name/Arity, _) :-
  290    multifile_predicate(Name, Arity, Module),
  291    !.
  292walk_called_by_pred(Module:Name/Arity, _) :-
  293    functor(Head, Name, Arity),
  294    predicate_property(Module:Head, multifile),
  295    !,
  296    assertz(multifile_predicate(Name, Arity, Module)).
  297walk_called_by_pred(Module:Name/Arity, OTerm) :-
  298    functor(Head, Name, Arity),
  299    (   no_walk_property(Property),
  300        predicate_property(Module:Head, Property)
  301    ->  true
  302    ;   walk_option_caller(OTerm, Module:Head),
  303        walk_option_clause(OTerm, ClauseRef),
  304        forall(catch(clause(Module:Head, Body, ClauseRef), _, fail),
  305               walk_called_by_body(Body, Module, OTerm))
  306    ).
  307
  308no_walk_property(number_of_rules(0)).   % no point walking only facts
  309no_walk_property(foreign).              % cannot walk foreign code
 walk_from_multifile(+OTerm)
Process registered multifile predicates.
  315walk_from_multifile(OTerm) :-
  316    forall(retract(multifile_predicate(Name, Arity, Module)),
  317           walk_called_by_multifile(Module:Name/Arity, OTerm)).
  318
  319walk_called_by_multifile(Module:Name/Arity, OTerm) :-
  320    functor(Head, Name, Arity),
  321    forall(catch(clause_not_from_development(
  322                     Module:Head, Body, ClauseRef, OTerm),
  323                 _, fail),
  324           ( walk_option_clause(OTerm, ClauseRef),
  325             walk_option_caller(OTerm, Module:Head),
  326             walk_called_by_body(Body, Module, OTerm)
  327           )).
 clause_not_from_development(:Head, -Body, ?Ref, +Options) is nondet
Enumerate clauses for a multifile predicate, but omit those from a module that is specifically meant to support development.
  335clause_not_from_development(Module:Head, Body, Ref, OTerm) :-
  336    clause(Module:Head, Body, Ref),
  337    \+ ( clause_property(Ref, file(File)),
  338         module_property(LoadModule, file(File)),
  339         \+ scan_module(LoadModule, OTerm)
  340       ).
 walk_called_by_body(+Body, +Module, +OTerm) is det
Check the Body term when executed in the context of Module. Options:
undefined(+Action)
One of ignore, error
  350walk_called_by_body(True, _, _) :-
  351    True == true,
  352    !.                % quickly deal with facts
  353walk_called_by_body(Body, Module, OTerm) :-
  354    set_undecided_of_walk_option(error, OTerm, OTerm1),
  355    set_evaluate_of_walk_option(false, OTerm1, OTerm2),
  356    catch(walk_called(Body, Module, _TermPos, OTerm2),
  357          missing(Missing),
  358          walk_called_by_body(Missing, Body, Module, OTerm)),
  359    !.
  360walk_called_by_body(Body, Module, OTerm) :-
  361    format(user_error, 'Failed to analyse:~n', []),
  362    portray_clause(('<head>' :- Body)),
  363    debug_walk(Body, Module, OTerm).
  364
  365% recompile this library after `debug(codewalk(trace))` and re-try
  366% for debugging failures.
  367:- if(debugging(codewalk(trace))).  368debug_walk(Body, Module, OTerm) :-
  369    gtrace,
  370    walk_called_by_body(Body, Module, OTerm).
  371:- else.  372debug_walk(_,_,_).
  373:- endif.
 walk_called_by_body(+Missing, +Body, +Module, +OTerm)
Restart the analysis because the previous analysis provided insufficient information.
  380walk_called_by_body(Missing, Body, _, OTerm) :-
  381    debugging(codewalk),
  382    format(user_error, 'Retrying due to ~w (~p)~n', [Missing, OTerm]),
  383    portray_clause(('<head>' :- Body)), fail.
  384walk_called_by_body(undecided_call, Body, Module, OTerm) :-
  385    catch(forall(walk_called(Body, Module, _TermPos, OTerm),
  386                 true),
  387          missing(Missing),
  388          walk_called_by_body(Missing, Body, Module, OTerm)).
  389walk_called_by_body(subterm_positions, Body, Module, OTerm) :-
  390    (   (   walk_option_clause(OTerm, ClauseRef), nonvar(ClauseRef),
  391            clause_info(ClauseRef, _, TermPos, _NameOffset),
  392            TermPos = term_position(_,_,_,_,[_,BodyPos])
  393        ->  WBody = Body
  394        ;   walk_option_initialization(OTerm, SrcLoc),
  395            ground(SrcLoc), SrcLoc = _File:_Line,
  396            initialization_layout(SrcLoc, Module:Body, WBody, BodyPos)
  397        )
  398    ->  catch(forall(walk_called(WBody, Module, BodyPos, OTerm),
  399                     true),
  400              missing(subterm_positions),
  401              walk_called_by_body(no_positions, Body, Module, OTerm))
  402    ;   set_source_of_walk_option(false, OTerm, OTerm2),
  403        forall(walk_called(Body, Module, _BodyPos, OTerm2),
  404               true)
  405    ).
  406walk_called_by_body(no_positions, Body, Module, OTerm) :-
  407    set_source_of_walk_option(false, OTerm, OTerm2),
  408    forall(walk_called(Body, Module, _NoPos, OTerm2),
  409           true).
 walk_called(+Goal, +Module, +TermPos, +OTerm) is multi
Perform abstract interpretation of Goal, touching all sub-goals that are directly called or immediately reachable through meta-calls. The actual auto-loading is performed by the predicate_property/2 call for meta-predicates.

If Goal is disjunctive, walk_called succeeds with a choice-point. Backtracking analyses the alternative control path(s).

Options:

undecided(+Action)
How to deal with insifficiently instantiated terms in the call-tree. Values are:
ignore
Silently ignore such goals
error
Throw undecided_call
evaluate(+Boolean)
If true (default), evaluate some goals. Notably =/2.
To be done
- Analyse e.g. assert((Head:-Body))?
  439walk_called(Term, Module, parentheses_term_position(_,_,Pos), OTerm) :-
  440    nonvar(Pos),
  441    !,
  442    walk_called(Term, Module, Pos, OTerm).
  443walk_called(Var, _, TermPos, OTerm) :-
  444    var(Var),                              % Incomplete analysis
  445    !,
  446    undecided(Var, TermPos, OTerm).
  447walk_called(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :-
  448    !,
  449    (   nonvar(M)
  450    ->  walk_called(G, M, Pos, OTerm)
  451    ;   undecided(M, MPos, OTerm)
  452    ).
  453walk_called((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  454    !,
  455    walk_called(A, M, PA, OTerm),
  456    walk_called(B, M, PB, OTerm).
  457walk_called((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  458    !,
  459    walk_called(A, M, PA, OTerm),
  460    walk_called(B, M, PB, OTerm).
  461walk_called((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  462    !,
  463    walk_called(A, M, PA, OTerm),
  464    walk_called(B, M, PB, OTerm).
  465walk_called(\+(A), M, term_position(_,_,_,_,[PA]), OTerm) :-
  466    !,
  467    \+ \+ walk_called(A, M, PA, OTerm).
  468walk_called((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  469    !,
  470    (   walk_option_evaluate(OTerm, Eval), Eval == true
  471    ->  Goal = (A;B),
  472        setof(Goal,
  473              (   walk_called(A, M, PA, OTerm)
  474              ;   walk_called(B, M, PB, OTerm)
  475              ),
  476              Alts0),
  477        variants(Alts0, Alts),
  478        member(Goal, Alts)
  479    ;   \+ \+ walk_called(A, M, PA, OTerm), % do not propagate bindings
  480        \+ \+ walk_called(B, M, PB, OTerm)
  481    ).
  482walk_called(Goal, Module, TermPos, OTerm) :-
  483    walk_option_trace_reference(OTerm, To), To \== (-),
  484    (   subsumes_term(To, Module:Goal)
  485    ->  M2 = Module
  486    ;   predicate_property(Module:Goal, imported_from(M2)),
  487        subsumes_term(To, M2:Goal)
  488    ),
  489    print_reference(M2:Goal, TermPos, trace, OTerm),
  490    fail.                                   % Continue search
  491walk_called(Goal, Module, _, OTerm) :-
  492    evaluate(Goal, Module, OTerm),
  493    !.
  494walk_called(Goal, M, TermPos, OTerm) :-
  495    (   (   predicate_property(M:Goal, imported_from(IM))
  496        ->  true
  497        ;   IM = M
  498        ),
  499        prolog:called_by(Goal, IM, M, Called)
  500    ;   prolog:called_by(Goal, Called)
  501    ),
  502    Called \== [],
  503    !,
  504    walk_called_by(Called, M, Goal, TermPos, OTerm).
  505walk_called(Meta, M, term_position(_,E,_,_,ArgPosList), OTerm) :-
  506    (   walk_option_autoload(OTerm, false)
  507    ->  nonvar(M),
  508        '$get_predicate_attribute'(M:Meta, defined, 1)
  509    ;   true
  510    ),
  511    (   predicate_property(M:Meta, meta_predicate(Head))
  512    ;   inferred_meta_predicate(M:Meta, Head)
  513    ),
  514    !,
  515    walk_option_clause(OTerm, ClauseRef),
  516    register_possible_meta_clause(ClauseRef),
  517    walk_meta_call(1, Head, Meta, M, ArgPosList, E-E, OTerm).
  518walk_called(Closure, _, _, _) :-
  519    blob(Closure, closure),
  520    !,
  521    '$closure_predicate'(Closure, Module:Name/Arity),
  522    functor(Head, Name, Arity),
  523    '$get_predicate_attribute'(Module:Head, defined, 1).
  524walk_called(ClosureCall, _, _, _) :-
  525    compound(ClosureCall),
  526    functor(ClosureCall, Closure, _),
  527    blob(Closure, closure),
  528    !,
  529    '$closure_predicate'(Closure, Module:Name/Arity),
  530    functor(Head, Name, Arity),
  531    '$get_predicate_attribute'(Module:Head, defined, 1).
  532walk_called(Goal, Module, _, _) :-
  533    nonvar(Module),
  534    '$get_predicate_attribute'(Module:Goal, defined, 1),
  535    !.
  536walk_called(Goal, Module, TermPos, OTerm) :-
  537    callable(Goal),
  538    !,
  539    undefined(Module:Goal, TermPos, OTerm).
  540walk_called(Goal, _Module, TermPos, OTerm) :-
  541    not_callable(Goal, TermPos, OTerm).
 undecided(+Variable, +TermPos, +OTerm)
  545undecided(Var, TermPos, OTerm) :-
  546    walk_option_undecided(OTerm, Undecided),
  547    (   var(Undecided)
  548    ->  Action = ignore
  549    ;   Action = Undecided
  550    ),
  551    undecided(Action, Var, TermPos, OTerm).
  552
  553undecided(ignore, _, _, _) :- !.
  554undecided(error,  _, _, _) :-
  555    throw(missing(undecided_call)).
 evaluate(Goal, Module, OTerm) is nondet
  559evaluate(Goal, Module, OTerm) :-
  560    walk_option_evaluate(OTerm, Evaluate),
  561    Evaluate \== false,
  562    evaluate(Goal, Module).
  563
  564evaluate(A=B, _) :-
  565    unify_with_occurs_check(A, B).
 undefined(:Goal, +TermPos, +OTerm)
The analysis trapped a definitely undefined predicate.
  571undefined(_, _, OTerm) :-
  572    walk_option_undefined(OTerm, ignore),
  573    !.
  574undefined(Goal, _, _) :-
  575    predicate_property(Goal, autoload(_)),
  576    !.
  577undefined(Goal, TermPos, OTerm) :-
  578    (   walk_option_undefined(OTerm, trace)
  579    ->  Why = trace
  580    ;   Why = undefined
  581    ),
  582    print_reference(Goal, TermPos, Why, OTerm).
 not_callable(+Goal, +TermPos, +OTerm)
We found a reference to a non-callable term
  588not_callable(Goal, TermPos, OTerm) :-
  589    print_reference(Goal, TermPos, not_callable, OTerm).
 print_reference(+Goal, +TermPos, +Why, +OTerm)
Print a reference to Goal, found at TermPos.
Arguments:
Why- is one of trace or undefined
  598print_reference(Goal, TermPos, Why, OTerm) :-
  599    walk_option_clause(OTerm, Clause), nonvar(Clause),
  600    !,
  601    (   compound(TermPos),
  602        arg(1, TermPos, CharCount),
  603        integer(CharCount)          % test it is valid
  604    ->  From = clause_term_position(Clause, TermPos)
  605    ;   walk_option_source(OTerm, false)
  606    ->  From = clause(Clause)
  607    ;   From = _,
  608        throw(missing(subterm_positions))
  609    ),
  610    print_reference2(Goal, From, Why, OTerm).
  611print_reference(Goal, TermPos, Why, OTerm) :-
  612    walk_option_initialization(OTerm, Init), nonvar(Init),
  613    Init = File:Line,
  614    !,
  615    (   compound(TermPos),
  616        arg(1, TermPos, CharCount),
  617        integer(CharCount)          % test it is valid
  618    ->  From = file_term_position(File, TermPos)
  619    ;   walk_option_source(OTerm, false)
  620    ->  From = file(File, Line, -1, _)
  621    ;   From = _,
  622        throw(missing(subterm_positions))
  623    ),
  624    print_reference2(Goal, From, Why, OTerm).
  625print_reference(Goal, _, Why, OTerm) :-
  626    print_reference2(Goal, _, Why, OTerm).
  627
  628print_reference2(Goal, From, trace, OTerm) :-
  629    walk_option_on_trace(OTerm, Closure),
  630    walk_option_caller(OTerm, Caller),
  631    nonvar(Closure),
  632    call(Closure, Goal, Caller, From),
  633    !.
  634print_reference2(Goal, From, Why, _OTerm) :-
  635    make_message(Why, Goal, From, Message, Level),
  636    print_message(Level, Message).
  637
  638
  639make_message(undefined, Goal, Context,
  640             error(existence_error(procedure, PI), Context), error) :-
  641    goal_pi(Goal, PI).
  642make_message(not_callable, Goal, Context,
  643             error(type_error(callable, Goal), Context), error).
  644make_message(trace, Goal, Context,
  645             trace_call_to(PI, Context), informational) :-
  646    goal_pi(Goal, PI).
  647
  648
  649goal_pi(Goal, M:Name/Arity) :-
  650    strip_module(Goal, M, Head),
  651    callable(Head),
  652    !,
  653    functor(Head, Name, Arity).
  654goal_pi(Goal, Goal).
  655
  656:- dynamic
  657    possible_meta_predicate/2.
 register_possible_meta_clause(+ClauseRef) is det
ClausesRef contains as call to a meta-predicate. Remember to analyse this predicate. We only analyse the predicate if it is loaded from a user module. I.e., system and library modules are trusted.
  666register_possible_meta_clause(ClausesRef) :-
  667    nonvar(ClausesRef),
  668    clause_property(ClausesRef, predicate(PI)),
  669    pi_head(PI, Head, Module),
  670    module_property(Module, class(user)),
  671    \+ predicate_property(Module:Head, meta_predicate(_)),
  672    \+ inferred_meta_predicate(Module:Head, _),
  673    \+ possible_meta_predicate(Head, Module),
  674    !,
  675    assertz(possible_meta_predicate(Head, Module)).
  676register_possible_meta_clause(_).
  677
  678pi_head(Module:Name/Arity, Head, Module)  :-
  679    !,
  680    functor(Head, Name, Arity).
  681pi_head(_, _, _) :-
  682    assertion(fail).
 infer_new_meta_predicates(-MetaSpecs, +OTerm) is det
  686infer_new_meta_predicates([], OTerm) :-
  687    walk_option_infer_meta_predicates(OTerm, false),
  688    !.
  689infer_new_meta_predicates(MetaSpecs, OTerm) :-
  690    findall(Module:MetaSpec,
  691            ( retract(possible_meta_predicate(Head, Module)),
  692              infer_meta_predicate(Module:Head, MetaSpec),
  693              (   walk_option_infer_meta_predicates(OTerm, all)
  694              ->  true
  695              ;   calling_metaspec(MetaSpec)
  696              )
  697            ),
  698            MetaSpecs).
 calling_metaspec(+Head) is semidet
True if this is a meta-specification that makes a difference to the code walker.
  705calling_metaspec(Head) :-
  706    arg(_, Head, Arg),
  707    calling_metaarg(Arg),
  708    !.
  709
  710calling_metaarg(I) :- integer(I), !.
  711calling_metaarg(^).
  712calling_metaarg(//).
 walk_meta_call(+Index, +GoalHead, +MetaHead, +Module, +ArgPosList, +EndPos, +OTerm)
Walk a call to a meta-predicate. This walks all meta-arguments labeled with an integer, ^ or //.
Arguments:
EndPos- reflects the end of the term. This is used if the number of arguments in the compiled form exceeds the number of arguments in the term read.
  725walk_meta_call(I, Head, Meta, M, ArgPosList, EPos, OTerm) :-
  726    arg(I, Head, AS),
  727    !,
  728    (   ArgPosList = [ArgPos|ArgPosTail]
  729    ->  true
  730    ;   ArgPos = EPos,
  731        ArgPosTail = []
  732    ),
  733    (   integer(AS)
  734    ->  arg(I, Meta, MA),
  735        extend(MA, AS, Goal, ArgPos, ArgPosEx, OTerm),
  736        walk_called(Goal, M, ArgPosEx, OTerm)
  737    ;   AS == (^)
  738    ->  arg(I, Meta, MA),
  739        remove_quantifier(MA, Goal, ArgPos, ArgPosEx, M, MG, OTerm),
  740        walk_called(Goal, MG, ArgPosEx, OTerm)
  741    ;   AS == (//)
  742    ->  arg(I, Meta, DCG),
  743        walk_dcg_body(DCG, M, ArgPos, OTerm)
  744    ;   true
  745    ),
  746    succ(I, I2),
  747    walk_meta_call(I2, Head, Meta, M, ArgPosTail, EPos, OTerm).
  748walk_meta_call(_, _, _, _, _, _, _).
  749
  750remove_quantifier(Goal, _, TermPos, TermPos, M, M, OTerm) :-
  751    var(Goal),
  752    !,
  753    undecided(Goal, TermPos, OTerm).
  754remove_quantifier(_^Goal0, Goal,
  755                  term_position(_,_,_,_,[_,GPos]),
  756                  TermPos, M0, M, OTerm) :-
  757    !,
  758    remove_quantifier(Goal0, Goal, GPos, TermPos, M0, M, OTerm).
  759remove_quantifier(M1:Goal0, Goal,
  760                  term_position(_,_,_,_,[_,GPos]),
  761                  TermPos, _, M, OTerm) :-
  762    !,
  763    remove_quantifier(Goal0, Goal, GPos, TermPos, M1, M, OTerm).
  764remove_quantifier(Goal, Goal, TermPos, TermPos, M, M, _).
 walk_called_by(+Called:list, +Module, +Goal, +TermPos, +OTerm)
Walk code explicitly mentioned to be called through the hook prolog:called_by/2.
  772walk_called_by([], _, _, _, _).
  773walk_called_by([H|T], M, Goal, TermPos, OTerm) :-
  774    (   H = G0+N
  775    ->  subterm_pos(G0, M, Goal, TermPos, G, GPos),
  776        (   extend(G, N, G2, GPos, GPosEx, OTerm)
  777        ->  walk_called(G2, M, GPosEx, OTerm)
  778        ;   true
  779        )
  780    ;   subterm_pos(H, M, Goal, TermPos, G, GPos),
  781        walk_called(G, M, GPos, OTerm)
  782    ),
  783    walk_called_by(T, M, Goal, TermPos, OTerm).
  784
  785subterm_pos(Sub, _, Term, TermPos, Sub, SubTermPos) :-
  786    subterm_pos(Sub, Term, TermPos, SubTermPos),
  787    !.
  788subterm_pos(Sub, M, Term, TermPos, G, SubTermPos) :-
  789    nonvar(Sub),
  790    Sub = M:H,
  791    !,
  792    subterm_pos(H, M, Term, TermPos, G, SubTermPos).
  793subterm_pos(Sub, _, _, _, Sub, _).
  794
  795subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  796    subterm_pos(Sub, Term, same_term, TermPos, SubTermPos),
  797    !.
  798subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  799    subterm_pos(Sub, Term, ==, TermPos, SubTermPos),
  800    !.
  801subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  802    subterm_pos(Sub, Term, =@=, TermPos, SubTermPos),
  803    !.
  804subterm_pos(Sub, Term, TermPos, SubTermPos) :-
  805    subterm_pos(Sub, Term, subsumes_term, TermPos, SubTermPos),
  806    !.
 walk_dcg_body(+Body, +Module, +TermPos, +OTerm)
Walk a DCG body that is meta-called.
  812walk_dcg_body(Var, _Module, TermPos, OTerm) :-
  813    var(Var),
  814    !,
  815    undecided(Var, TermPos, OTerm).
  816walk_dcg_body([], _Module, _, _) :- !.
  817walk_dcg_body([_|_], _Module, _, _) :- !.
  818walk_dcg_body(String, _Module, _, _) :-
  819    string(String),
  820    !.
  821walk_dcg_body(!, _Module, _, _) :- !.
  822walk_dcg_body(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :-
  823    !,
  824    (   nonvar(M)
  825    ->  walk_dcg_body(G, M, Pos, OTerm)
  826    ;   undecided(M, MPos, OTerm)
  827    ).
  828walk_dcg_body((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  829    !,
  830    walk_dcg_body(A, M, PA, OTerm),
  831    walk_dcg_body(B, M, PB, OTerm).
  832walk_dcg_body((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  833    !,
  834    walk_dcg_body(A, M, PA, OTerm),
  835    walk_dcg_body(B, M, PB, OTerm).
  836walk_dcg_body((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  837    !,
  838    walk_dcg_body(A, M, PA, OTerm),
  839    walk_dcg_body(B, M, PB, OTerm).
  840walk_dcg_body((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  841    !,
  842    (   walk_dcg_body(A, M, PA, OTerm)
  843    ;   walk_dcg_body(B, M, PB, OTerm)
  844    ).
  845walk_dcg_body((A|B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :-
  846    !,
  847    (   walk_dcg_body(A, M, PA, OTerm)
  848    ;   walk_dcg_body(B, M, PB, OTerm)
  849    ).
  850walk_dcg_body({G}, M, brace_term_position(_,_,PG), OTerm) :-
  851    !,
  852    walk_called(G, M, PG, OTerm).
  853walk_dcg_body(G, M, TermPos, OTerm) :-
  854    extend(G, 2, G2, TermPos, TermPosEx, OTerm),
  855    walk_called(G2, M, TermPosEx, OTerm).
 subterm_pos(+SubTerm, +Term, :Cmp, +TermPosition, -SubTermPos) is nondet
True when SubTerm is a sub term of Term, compared using Cmp, TermPosition describes the term layout of Term and SubTermPos describes the term layout of SubTerm. Cmp is typically one of same_term, ==, =@= or subsumes_term
  866:- meta_predicate
  867    subterm_pos(+, +, 2, +, -),
  868    sublist_pos(+, +, +, +, 2, -).  869
  870subterm_pos(_, _, _, Pos, _) :-
  871    var(Pos), !, fail.
  872subterm_pos(Sub, Term, Cmp, Pos, Pos) :-
  873    call(Cmp, Sub, Term),
  874    !.
  875subterm_pos(Sub, Term, Cmp, term_position(_,_,_,_,ArgPosList), Pos) :-
  876    is_list(ArgPosList),
  877    compound(Term),
  878    nth1(I, ArgPosList, ArgPos),
  879    arg(I, Term, Arg),
  880    subterm_pos(Sub, Arg, Cmp, ArgPos, Pos).
  881subterm_pos(Sub, Term, Cmp, list_position(_,_,ElemPosList,TailPos), Pos) :-
  882    sublist_pos(ElemPosList, TailPos, Sub, Term, Cmp, Pos).
  883subterm_pos(Sub, {Arg}, Cmp, brace_term_position(_,_,ArgPos), Pos) :-
  884    subterm_pos(Sub, Arg, Cmp, ArgPos, Pos).
  885
  886sublist_pos([EP|TP], TailPos, Sub, [H|T], Cmp, Pos) :-
  887    (   subterm_pos(Sub, H, Cmp, EP, Pos)
  888    ;   sublist_pos(TP, TailPos, Sub, T, Cmp, Pos)
  889    ).
  890sublist_pos([], TailPos, Sub, Tail, Cmp, Pos) :-
  891    TailPos \== none,
  892    subterm_pos(Sub, Tail, Cmp, TailPos, Pos).
 extend(+Goal, +ExtraArgs, +TermPosIn, -TermPosOut, +OTerm)
bug
- :
  898extend(Goal, 0, Goal, TermPos, TermPos, _) :- !.
  899extend(Goal, _, _, TermPos, TermPos, OTerm) :-
  900    var(Goal),
  901    !,
  902    undecided(Goal, TermPos, OTerm).
  903extend(M:Goal, N, M:GoalEx,
  904       term_position(F,T,FT,TT,[MPos,GPosIn]),
  905       term_position(F,T,FT,TT,[MPos,GPosOut]), OTerm) :-
  906    !,
  907    (   var(M)
  908    ->  undecided(N, MPos, OTerm)
  909    ;   true
  910    ),
  911    extend(Goal, N, GoalEx, GPosIn, GPosOut, OTerm).
  912extend(Goal, N, GoalEx, TermPosIn, TermPosOut, _) :-
  913    callable(Goal),
  914    !,
  915    Goal =.. List,
  916    length(Extra, N),
  917    extend_term_pos(TermPosIn, N, TermPosOut),
  918    append(List, Extra, ListEx),
  919    GoalEx =.. ListEx.
  920extend(Closure, N, M:GoalEx, TermPosIn, TermPosOut, OTerm) :-
  921    blob(Closure, closure),             % call(Closure, A1, ...)
  922    !,
  923    '$closure_predicate'(Closure, M:Name/Arity),
  924    length(Extra, N),
  925    extend_term_pos(TermPosIn, N, TermPosOut),
  926    GoalEx =.. [Name|Extra],
  927    (   N =:= Arity
  928    ->  true
  929    ;   print_reference(Closure, TermPosIn, closure_arity_mismatch, OTerm)
  930    ).
  931extend(Goal, _, _, TermPos, _, OTerm) :-
  932    print_reference(Goal, TermPos, not_callable, OTerm).
  933
  934extend_term_pos(Var, _, _) :-
  935    var(Var),
  936    !.
  937extend_term_pos(term_position(F,T,FT,TT,ArgPosIn),
  938                N,
  939                term_position(F,T,FT,TT,ArgPosOut)) :-
  940    !,
  941    length(Extra, N),
  942    maplist(=(0-0), Extra),
  943    append(ArgPosIn, Extra, ArgPosOut).
  944extend_term_pos(F-T, N, term_position(F,T,F,T,Extra)) :-
  945    length(Extra, N),
  946    maplist(=(0-0), Extra).
 variants(+SortedList, -Variants) is det
  951variants([], []).
  952variants([H|T], List) :-
  953    variants(T, H, List).
  954
  955variants([], H, [H]).
  956variants([H|T], V, List) :-
  957    (   H =@= V
  958    ->  variants(T, V, List)
  959    ;   List = [V|List2],
  960        variants(T, H, List2)
  961    ).
 predicate_in_module(+Module, ?PI) is nondet
True if PI is a predicate locally defined in Module.
  967predicate_in_module(Module, PI) :-
  968    current_predicate(Module:PI),
  969    PI = Name/Arity,
  970    functor(Head, Name, Arity),
  971    \+ predicate_property(Module:Head, imported_from(_)).
  972
  973
  974                 /*******************************
  975                 *      ENUMERATE CLAUSES       *
  976                 *******************************/
 prolog_program_clause(-ClauseRef, +Options) is nondet
True when ClauseRef is a reference for clause in the program. Options is a subset of the options processed by prolog_walk_code/1. The logic for deciding on which clauses to enumerate is shared with prolog_walk_code/1.
  988prolog_program_clause(ClauseRef, Options) :-
  989    make_walk_option(Options, OTerm, _),
  990    setup_call_cleanup(
  991        true,
  992        (   current_module(Module),
  993            scan_module(Module, OTerm),
  994            module_clause(Module, ClauseRef, OTerm)
  995        ;   retract(multifile_predicate(Name, Arity, MM)),
  996            multifile_clause(ClauseRef, MM:Name/Arity, OTerm)
  997        ;   initialization_clause(ClauseRef, OTerm)
  998        ),
  999        retractall(multifile_predicate(_,_,_))).
 1000
 1001
 1002module_clause(Module, ClauseRef, _OTerm) :-
 1003    predicate_in_module(Module, Name/Arity),
 1004    \+ multifile_predicate(Name, Arity, Module),
 1005    functor(Head, Name, Arity),
 1006    (   predicate_property(Module:Head, multifile)
 1007    ->  assertz(multifile_predicate(Name, Arity, Module)),
 1008        fail
 1009    ;   predicate_property(Module:Head, Property),
 1010        no_enum_property(Property)
 1011    ->  fail
 1012    ;   catch(nth_clause(Module:Head, _, ClauseRef), _, fail)
 1013    ).
 1014
 1015no_enum_property(foreign).
 1016
 1017multifile_clause(ClauseRef, M:Name/Arity, OTerm) :-
 1018    functor(Head, Name, Arity),
 1019    catch(clauseref_not_from_development(M:Head, ClauseRef, OTerm),
 1020          _, fail).
 1021
 1022clauseref_not_from_development(Module:Head, Ref, OTerm) :-
 1023    nth_clause(Module:Head, _N, Ref),
 1024    \+ ( clause_property(Ref, file(File)),
 1025         module_property(LoadModule, file(File)),
 1026         \+ scan_module(LoadModule, OTerm)
 1027       ).
 1028
 1029initialization_clause(ClauseRef, OTerm) :-
 1030    catch(clause(system:'$init_goal'(_File, M:_Goal, SourceLocation),
 1031                 true, ClauseRef),
 1032          _, fail),
 1033    walk_option_initialization(OTerm, SourceLocation),
 1034    scan_module(M, OTerm).
 1035
 1036
 1037                 /*******************************
 1038                 *            MESSAGES          *
 1039                 *******************************/
 1040
 1041:- multifile
 1042    prolog:message//1,
 1043    prolog:message_location//1. 1044
 1045prolog:message(trace_call_to(PI, Context)) -->
 1046    [ 'Call to ~q at '-[PI] ],
 1047    prolog:message_location(Context).
 1048
 1049prolog:message_location(clause_term_position(ClauseRef, TermPos)) -->
 1050    { clause_property(ClauseRef, file(File)) },
 1051    message_location_file_term_position(File, TermPos).
 1052prolog:message_location(clause(ClauseRef)) -->
 1053    { clause_property(ClauseRef, file(File)),
 1054      clause_property(ClauseRef, line_count(Line))
 1055    },
 1056    !,
 1057    [ '~w:~d: '-[File, Line] ].
 1058prolog:message_location(clause(ClauseRef)) -->
 1059    { clause_name(ClauseRef, Name) },
 1060    [ '~w: '-[Name] ].
 1061prolog:message_location(file_term_position(Path, TermPos)) -->
 1062    message_location_file_term_position(Path, TermPos).
 1063prolog:message(codewalk(reiterate(New, Iteration, CPU))) -->
 1064    [ 'Found new meta-predicates in iteration ~w (~3f sec)'-
 1065      [Iteration, CPU], nl ],
 1066    meta_decls(New),
 1067    [ 'Restarting analysis ...'-[], nl ].
 1068
 1069meta_decls([]) --> [].
 1070meta_decls([H|T]) -->
 1071    [ ':- meta_predicate ~q.'-[H], nl ],
 1072    meta_decls(T).
 1073
 1074message_location_file_term_position(File, TermPos) -->
 1075    { arg(1, TermPos, CharCount),
 1076      filepos_line(File, CharCount, Line, LinePos)
 1077    },
 1078    [ '~w:~d:~d: '-[File, Line, LinePos] ].
 filepos_line(+File, +CharPos, -Line, -Column) is det
Arguments:
CharPos- is 0-based character offset in the file.
Column- is the current column, counting tabs as 8 spaces.
 1085filepos_line(File, CharPos, Line, LinePos) :-
 1086    setup_call_cleanup(
 1087        ( open(File, read, In),
 1088          open_null_stream(Out)
 1089        ),
 1090        ( copy_stream_data(In, Out, CharPos),
 1091          stream_property(In, position(Pos)),
 1092          stream_position_data(line_count, Pos, Line),
 1093          stream_position_data(line_position, Pos, LinePos)
 1094        ),
 1095        ( close(Out),
 1096          close(In)
 1097        ))