Skip to content

Commit 8e0579c

Browse files
committed
CMD: add command for scanning potential output parameters
1 parent 55c7beb commit 8e0579c

File tree

6 files changed

+413
-7
lines changed

6 files changed

+413
-7
lines changed

chc/app/CPrettyPrinter.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,12 @@ def visit_funarg(self, funarg: CT.CFunArg) -> None:
310310
self.funtypptr_with_name(ptx, funarg.name)
311311
return
312312

313-
if (
314-
argtyp.attributes.length > 0
315-
and argtyp.attributes.attributes[0].name == "arraylen"):
316-
self.ptrarg_with_attribute_length(
317-
ptargtyp, argtyp.attributes.attributes[0], funarg.name)
318-
return
313+
# if (
314+
# argtyp.attributes.length > 0
315+
# and argtyp.attributes.attributes[0].name == "arraylen"):
316+
# self.ptrarg_with_attribute_length(
317+
# ptargtyp, argtyp.attributes.attributes[0], funarg.name)
318+
# return
319319

320320
funarg.typ.accept(self)
321321
self.ccode.write(" ")

chc/app/CTyp.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ def is_pointer(self) -> bool:
184184
def is_struct(self) -> bool:
185185
return False
186186

187+
@property
188+
def is_union(self) -> bool:
189+
return False
190+
187191
@property
188192
def is_void(self) -> bool:
189193
return False
@@ -388,6 +392,10 @@ def name(self) -> str:
388392
def is_struct(self) -> bool:
389393
return self.compinfo.is_struct
390394

395+
@property
396+
def is_union(self) -> bool:
397+
return not self.compinfo.is_struct
398+
391399
def accept(self, visitor: "CVisitor") -> None:
392400
visitor.visit_comptyp(self)
393401

chc/cmdline/c_file/cfileutil.py

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@
5656
import subprocess
5757
import sys
5858

59-
from typing import Callable, List, NoReturn, Optional, TYPE_CHECKING
59+
from typing import (
60+
Any, Callable, cast, Dict, List, NoReturn, Optional, TYPE_CHECKING)
6061

6162
from chc.cmdline.AnalysisManager import AnalysisManager
6263
from chc.cmdline.ParseManager import ParseManager
6364
import chc.cmdline.jsonresultutil as JU
6465

6566
from chc.app.CApplication import CApplication
67+
from chc.app.CPrettyPrinter import CPrettyPrinter
6668

6769
import chc.reporting.ProofObligations as RP
6870

@@ -71,9 +73,13 @@
7173
from chc.util.loggingutil import chklogger, LogLevel
7274

7375
if TYPE_CHECKING:
76+
from chc.app.CAttributes import CAttributes
77+
from chc.app.CTyp import (
78+
CTypComp, CTypFloat, CTypFun, CTypInt, CTypPtr)
7479
from chc.proof.CFunctionPO import CFunctionPO
7580

7681

82+
7783
def print_error(m: str) -> None:
7884
sys.stderr.write(("*" * 80) + "\n")
7985
sys.stderr.write(m + "\n")
@@ -174,6 +180,149 @@ def cfile_parse_file(args: argparse.Namespace) -> NoReturn:
174180
exit(0)
175181

176182

183+
def cfile_scan_op(args: argparse.Namespace) -> NoReturn:
184+
185+
# arguments
186+
xcfilename: str = args.filename
187+
jsonoutput: bool = args.json
188+
outputfilename: Optional[str] = args.output
189+
loglevel: str = args.loglevel
190+
logfilename: Optional[str] = args.logfilename
191+
logfilemode: str = args.logfilemode
192+
193+
projectpath = os.path.dirname(os.path.abspath(xcfilename))
194+
targetpath = projectpath
195+
targetpath = os.path.abspath(targetpath)
196+
cfilename_ext = os.path.basename(xcfilename)
197+
cfilename = cfilename_ext[:-2]
198+
projectname = cfilename
199+
200+
set_logging(
201+
loglevel,
202+
targetpath,
203+
logfilename=logfilename,
204+
mode=logfilemode,
205+
msg="Command cfile scan-output-parameters invoked")
206+
207+
chklogger.logger.info(
208+
"Project path: %s; target path: %s", projectpath, targetpath)
209+
210+
parsearchive = UF.get_parse_archive(targetpath, projectname)
211+
212+
if not os.path.isfile(parsearchive):
213+
print_error("Please run parser first on c file")
214+
exit(1)
215+
216+
if os.path.isfile(parsearchive):
217+
os.chdir(targetpath)
218+
tarname = os.path.basename(parsearchive)
219+
cmd = ["tar", "xfz", os.path.basename(tarname)]
220+
result = subprocess.call(cmd, cwd=targetpath, stderr=subprocess.STDOUT)
221+
if result != 0:
222+
print_error("Error in extracting " + tarname)
223+
exit(1)
224+
225+
def has_const_attribute(attrs: "CAttributes") -> bool:
226+
for attr in attrs.attributes:
227+
if "const" in attr.name:
228+
return True
229+
return False
230+
231+
contractpath = os.path.join(targetpath, "chc_contracts")
232+
233+
capp = CApplication(
234+
projectpath, projectname, targetpath, contractpath, singlefile=True)
235+
236+
capp.initialize_single_file(cfilename)
237+
cfile = capp.get_cfile()
238+
239+
ptrcandidates: List[Dict[str, Any]] = []
240+
opcandidates: List[Dict[str, Any]] = []
241+
242+
for cfun in cfile.gfunctions.values():
243+
if cfun.varinfo.vname == "main":
244+
continue
245+
pp = CPrettyPrinter()
246+
fndecl = pp.function_declaration_str(cfun.varinfo).strip()
247+
cfuntyp = cfun.varinfo.vtype
248+
if not cfuntyp.is_function:
249+
continue
250+
cfuntyp = cast("CTypFun", cfuntyp)
251+
cfunargs = cfuntyp.funargs
252+
if cfunargs is None:
253+
continue
254+
cfunarguments = cfunargs.arguments
255+
if not any(cfunarg.typ.is_pointer for cfunarg in cfunarguments):
256+
continue
257+
candidate: Dict[str, Any] = {}
258+
candidate["name"] = cfun.vname
259+
candidate["signature"] = fndecl
260+
ptrargs = candidate["ptrargs"] = []
261+
for (index, cfunarg) in enumerate(cfunarguments):
262+
argtyp = cfunarg.typ.expand()
263+
if argtyp.is_pointer:
264+
disqualifiers: List[str] = []
265+
argtgttyp = cast("CTypPtr", argtyp).pointedto_type.expand()
266+
ptrarg: Dict[str, Any] = {}
267+
ptrarg["index"] = index
268+
ptrarg["name"] = cfunarg.name
269+
ptrarg["target-type"] = str(argtgttyp)
270+
if argtgttyp.attributes.length > 0:
271+
ptrarg["attributes"] = (
272+
[str(attr) for attr in argtgttyp.attributes.attributes])
273+
if has_const_attribute(argtgttyp.attributes):
274+
disqualifiers.append("const")
275+
if argtgttyp.is_int:
276+
ptrarg["kind"] = ["int", cast("CTypInt", argtgttyp).ikind]
277+
if "ichar" in ptrarg["kind"]:
278+
disqualifiers.append("ichar")
279+
elif "iuchar" in ptrarg["kind"]:
280+
disqualifiers.append("iuchar")
281+
elif argtgttyp.is_float:
282+
ptrarg["kind"] = ["float", cast("CTypFloat", argtgttyp).fkind]
283+
elif argtgttyp.is_void:
284+
ptrarg["kind"] = ["void"]
285+
disqualifiers.append("void")
286+
elif argtgttyp.is_pointer:
287+
ptrarg["kind"] = ["pointer"]
288+
disqualifiers.append("pointer")
289+
elif argtgttyp.is_struct:
290+
ptrarg["kind"] = ["struct", cast("CTypComp", argtgttyp).name]
291+
elif argtgttyp.is_union:
292+
ptrarg["kind"] = ["union", cast("CTypComp", argtgttyp).name]
293+
disqualifiers.append("union")
294+
else:
295+
ptrarg["kind"] = ["other"]
296+
if len(disqualifiers) > 0:
297+
ptrarg["disqualifiers"] = disqualifiers
298+
else:
299+
opcandidate: Dict[str, Any] = {}
300+
opcandidate["function"] = cfun.varinfo.vname
301+
opcandidate["arg-index"] = index
302+
opcandidate["arg-name"] = cfunarg.name
303+
opcandidate["arg-target-type"] = str(argtgttyp)
304+
opcandidates.append(opcandidate)
305+
ptrargs.append(ptrarg)
306+
ptrcandidates.append(candidate)
307+
308+
results: Dict[str, Any] = {}
309+
results["function-count"] = len(cfile.gfunctions.keys())
310+
results["ptr-candidates"] = ptrcandidates
311+
results["op-candidates"] = opcandidates
312+
313+
if jsonoutput:
314+
jsonresult = JU.cfile_output_parameters_to_json_result(cfilename, results)
315+
jsonok = JU.jsonok("scan_output_parameters", jsonresult.content)
316+
if outputfilename is not None:
317+
with open(outputfilename, "w") as fp:
318+
json.dump(jsonok, fp, indent=2)
319+
else:
320+
resultstring = json.dumps(jsonok, indent=2)
321+
print(resultstring)
322+
323+
exit(0)
324+
325+
177326
def cfile_mk_headerfile(args: argparse.Namespace) -> NoReturn:
178327
"""Produces a .c file with prototype definitions of functions and global variables.
179328

0 commit comments

Comments
 (0)