3636
3737import ast
3838from pathlib import Path
39- from typing import Dict , List , Optional , Set , Tuple
39+ from typing import Callable , Dict , List , Optional , Set , Tuple
4040
4141from codeanalyzer .dataflow .access_paths import _PathExtractor , _calls_in
4242from codeanalyzer .dataflow .alias import TypeBasedAliasOracle
@@ -127,14 +127,24 @@ def _match_args(
127127 return tuple (pairs )
128128
129129
130- def build_program_graphs (
130+ def build_function_pdgs (
131131 app : PyApplication ,
132132 k : int = DEFAULT_K_LIMIT ,
133- ) -> ProgramGraphsIR :
134- """Build CFG/PDG per callable and the whole-program SDG."""
135- class_idx = _class_index (app )
136- callable_idx = _callable_index (app )
137-
133+ * ,
134+ oracle_factory : Callable [[PyCallable ], object ],
135+ ) -> Tuple [Dict [str , FunctionInfo ], Dict [str , ast .AST ]]:
136+ """Intraprocedural phase only: one ``FunctionInfo`` (CFG → PDG) per
137+ callable, keyed by signature, with no SDG/summary/callsite work.
138+
139+ ``oracle_factory(pycallable)`` supplies the may-alias oracle per callable —
140+ ``TypeBasedAliasOracle`` for the L4 path, ``SyntacticOracle`` for L3.
141+
142+ Returns ``(infos, func_asts)`` rather than bare PDGs so that the L4
143+ orchestrator (:func:`build_program_graphs`) still has both the
144+ ``FunctionInfo`` records its callsite/summary/SDG phases mutate and the
145+ matched def nodes its Phase 2 reads. L3 callers just read ``info.pdg`` per
146+ signature and ignore ``func_asts``.
147+ """
138148 infos : Dict [str , FunctionInfo ] = {}
139149 func_asts : Dict [str , ast .AST ] = {}
140150
@@ -166,7 +176,7 @@ def build_program_graphs(
166176 if enclosing_ast is not None :
167177 enclosing_locals |= _locals_of (enclosing_ast )
168178
169- oracle = TypeBasedAliasOracle ( _base_types ( pycallable ) )
179+ oracle = oracle_factory ( pycallable )
170180 pdg = build_pdg (
171181 func ,
172182 enclosing_locals = enclosing_locals ,
@@ -179,6 +189,21 @@ def build_program_graphs(
179189 )
180190 func_asts [pycallable .signature ] = func
181191
192+ return infos , func_asts
193+
194+
195+ def build_program_graphs (
196+ app : PyApplication ,
197+ k : int = DEFAULT_K_LIMIT ,
198+ ) -> ProgramGraphsIR :
199+ """Build CFG/PDG per callable and the whole-program SDG."""
200+ class_idx = _class_index (app )
201+ callable_idx = _callable_index (app )
202+
203+ infos , func_asts = build_function_pdgs (
204+ app , k , oracle_factory = lambda c : TypeBasedAliasOracle (_base_types (c ))
205+ )
206+
182207 # Callsites and nested defs, now that every signature is known.
183208 for sig , info in infos .items ():
184209 pycallable = callable_idx [sig ]
0 commit comments