# ------------------------------------------------------------------------------
# CodeHawk C Analyzer
# Author: Henny Sipma
# ------------------------------------------------------------------------------
# The MIT License (MIT)
#
# Copyright (c) 2017-2020 Kestrel Technology LLC
# Copyright (c) 2020-2023 Henny B. Sipma
# Copyright (c) 2024 Aarno Labs LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ------------------------------------------------------------------------------
"""Global definitions in a c-file.
These definitions are obtained from the file <filename>_cfile.xml.
"""
from dataclasses import dataclass
import xml.etree.ElementTree as ET
from typing import Dict, List, Optional, TYPE_CHECKING
import chc.util.fileutil as UF
from chc.util.loggingutil import chklogger
if TYPE_CHECKING:
from chc.app.CCompInfo import CCompInfo
from chc.app.CEnumInfo import CEnumInfo
from chc.app.CFile import CFile
from chc.app.CFileDeclarations import CFileDeclarations
from chc.app.CInitInfo import CInitInfo
from chc.app.CLocation import CLocation
from chc.app.CTyp import CTyp
from chc.app.CTypeInfo import CTypeInfo
from chc.app.CVarInfo import CVarInfo
[docs]@dataclass
class CGCompTag:
"""Definition of a struct."""
location: "CLocation"
compinfo: "CCompInfo"
@property
def name(self) -> str:
return self.compinfo.name
@property
def ckey(self) -> int:
return self.compinfo.ckey
@property
def is_struct(self) -> bool:
return self.compinfo.is_struct
def __str__(self) -> str:
if self.is_struct:
return f"struct {self.name} (ckey: {self.ckey})"
else:
return f"union {self.name} (ckey: {self.ckey})"
[docs]@dataclass
class CGEnumTag:
"""Definition of an enum."""
location: "CLocation"
enuminfo: "CEnumInfo"
[docs]@dataclass
class CGFunction:
"""Function declaration."""
location: "CLocation"
varinfo: "CVarInfo"
@property
def vname(self) -> str:
return self.varinfo.vname
@property
def vtype(self) -> "CTyp":
return self.varinfo.vtype
@property
def line(self) -> int:
return self.varinfo.line
def __str__(self) -> str:
return f"{self.vname}: {self.vtype}"
[docs]@dataclass
class CGType:
"""Type definition that associates a name with a type."""
location: "CLocation"
typeinfo: "CTypeInfo"
[docs]@dataclass
class CGVarDecl:
"""Global variable declaration."""
location: "CLocation"
varinfo: "CVarInfo"
@property
def vname(self) -> str:
return self.varinfo.vname
[docs]@dataclass
class CGVarDef:
"""Global variable definition (in this file)."""
location: "CLocation"
varinfo: "CVarInfo"
initializer: Optional["CInitInfo"]
@property
def vname(self) -> str:
return self.varinfo.vname
[docs] def has_initializer(self) -> bool:
return self.initializer is not None
def __str__(self) -> str:
if self.initializer is not None:
return f"{self.varinfo} = {self.initializer}"
else:
return f"{self.varinfo}"
[docs]class CFileGlobals:
def __init__(self, cfile: "CFile", xnode: ET.Element) -> None:
self._cfile = cfile
self._xnode = xnode
self._gcomptagdefs: Optional[Dict[int, CGCompTag]] = None
self._gcomptagdecls: Optional[Dict[int, CGCompTag]] = None
self._genumtagdecls: Optional[Dict[str, CGEnumTag]] = None
self._genumtagdefs: Optional[Dict[str, CGEnumTag]] = None
self._gfunctions: Optional[Dict[int, CGFunction]] = None
self._gtypes: Optional[Dict[str, CGType]] = None
self._gvardecls: Optional[Dict[int, CGVarDecl]] = None
self._gvardefs: Optional[Dict[int, CGVarDef]] = None
self._globalcompinfockeys: Optional[Dict[int, "CCompInfo"]] = None
self._globalvarinfonames: Optional[Dict[str, "CVarInfo"]] = None
self._globalvarinfovids: Optional[Dict[int, "CVarInfo"]] = None
@property
def cfile(self) -> "CFile":
return self._cfile
@property
def declarations(self) -> "CFileDeclarations":
return self.cfile.declarations
[docs] def expand(self, name: str) -> "CTyp":
if name in self.gtypes:
return self.gtypes[name].typeinfo.type.expand()
else:
raise UF.CHCError(f"No type definition found for {name}")
@property
def functioncount(self) -> int:
return len(self.gfunctions)
@property
def gcomptagdecls(self) -> Dict[int, CGCompTag]:
if self._gcomptagdecls is None:
self._gcomptagdecls = {}
xgc = self._xnode.find("global-comptag-declarations")
if xgc is not None:
for xc in xgc.findall("gcomptagdecl"):
xicinfo = xc.get("icinfo")
xiloc = xc.get("iloc")
if xicinfo is not None and xiloc is not None:
cinfo = self.declarations.get_compinfo(int(xicinfo))
loc = self.declarations.get_location(int(xiloc))
gcomp = CGCompTag(loc, cinfo)
self._gcomptagdecls[cinfo.ckey] = gcomp
else:
chklogger.logger.error(
"Invalid comptag definition record in %s",
self.cfile.name)
return self._gcomptagdecls
@property
def gcomptagdefs(self) -> Dict[int, CGCompTag]:
if self._gcomptagdefs is None:
self._gcomptagdefs = {}
xgc = self._xnode.find("global-comptag-definitions")
if xgc is not None:
for xc in xgc.findall("gcomptag"):
xicinfo = xc.get("icinfo")
xiloc = xc.get("iloc")
if xicinfo is not None and xiloc is not None:
cinfo = self.declarations.get_compinfo(int(xicinfo))
loc = self.declarations.get_location(int(xiloc))
gcomp = CGCompTag(loc, cinfo)
self._gcomptagdefs[cinfo.ckey] = gcomp
else:
chklogger.logger.error(
"Invalid comptag definition record in %s",
self.cfile.name)
return self._gcomptagdefs
@property
def genumtagdecls(self) -> Dict[str, CGEnumTag]:
if self._genumtagdecls is None:
self._genumtagdecls = {}
xge = self._xnode.find("global-enumtag-declarations")
if xge is not None:
for xe in xge.findall("genumtagdecl"):
xieinfo = xe.get("ieinfo")
xiloc = xe.get("iloc")
if xieinfo is not None and xiloc is not None:
einfo = self.declarations.get_enuminfo(int(xieinfo))
loc = self.declarations.get_location(int(xiloc))
genum = CGEnumTag(loc, einfo)
self._genumtagdecls[einfo.ename] = genum
else:
chklogger.logger.error(
"Invalid enum declaration record in %s",
self.cfile.name)
return self._genumtagdecls
@property
def genumtagdefs(self) -> Dict[str, CGEnumTag]:
if self._genumtagdefs is None:
self._genumtagdefs = {}
xge = self._xnode.find("global-enumtag-definitions")
if xge is not None:
for xe in xge.findall("genumtag"):
xieinfo = xe.get("ieinfo")
xiloc = xe.get("iloc")
if xieinfo is not None and xiloc is not None:
einfo = self.declarations.get_enuminfo(int(xieinfo))
loc = self.declarations.get_location(int(xiloc))
genum = CGEnumTag(loc, einfo)
self._genumtagdefs[einfo.ename] = genum
else:
chklogger.logger.error(
"Invalid enum declaration record in %s",
self.cfile.name)
return self._genumtagdefs
@property
def gfunctions(self) -> Dict[int, CGFunction]:
if self._gfunctions is None:
self._gfunctions = {}
xgf = self._xnode.find("functions")
if xgf is not None:
for xf in xgf.findall("gfun"):
xivinfo = xf.get("ivinfo")
xiloc = xf.get("iloc")
if xivinfo is not None and xiloc is not None:
vinfo = self.declarations.get_varinfo(int(xivinfo))
loc = self.declarations.get_location(int(xiloc))
gfun = CGFunction(loc, vinfo)
self._gfunctions[vinfo.vid] = gfun
else:
chklogger.logger.error(
"Invalid function declaration record in %s",
self.cfile.name)
return self._gfunctions
@property
def gtypes(self) -> Dict[str, CGType]:
if self._gtypes is None:
self._gtypes = {}
xgt = self._xnode.find("global-type-definitions")
if xgt is not None:
for xt in xgt.findall("gtype"):
xitinfo = xt.get("itinfo")
xiloc = xt.get("iloc")
if xitinfo is not None and xiloc is not None:
tinfo = self.declarations.get_typeinfo(int(xitinfo))
loc = self.declarations.get_location(int(xiloc))
gtype = CGType(loc, tinfo)
self._gtypes[tinfo.name] = gtype
else:
chklogger.logger.error(
"Invalid type definition record in %s",
self.cfile.name)
return self._gtypes
@property
def gvardecls(self) -> Dict[int, CGVarDecl]:
if self._gvardecls is None:
self._gvardecls = {}
xgv = self._xnode.find("global-var-declarations")
if xgv is not None:
for xv in xgv.findall("gvardecl"):
xivinfo = xv.get("ivinfo")
xiloc = xv.get("iloc")
if xivinfo is not None and xiloc is not None:
vinfo = self.declarations.get_varinfo(int(xivinfo))
loc = self.declarations.get_location(int(xiloc))
gvardecl = CGVarDecl(loc, vinfo)
self._gvardecls[vinfo.vid] = gvardecl
else:
chklogger.logger.error(
"Invalid variable declaration record in %s",
self.cfile.name)
return self._gvardecls
@property
def gvardefs(self) -> Dict[int, CGVarDef]:
if self._gvardefs is None:
self._gvardefs = {}
xgv = self._xnode.find("global-var-definitions")
if xgv is not None:
for xv in xgv.findall("gvar"):
xivinfo = xv.get("ivinfo")
xiloc = xv.get("iloc")
xiinit = xv.get("iinit")
if (
xivinfo is not None
and xiloc is not None):
vinfo = self.declarations.get_varinfo(int(xivinfo))
loc = self.declarations.get_location(int(xiloc))
vinit: Optional["CInitInfo"] = None
if xiinit is not None:
vinit = self.declarations.get_initinfo(int(xiinit))
gvar = CGVarDef(loc, vinfo, vinit)
self._gvardefs[vinfo.vid] = gvar
else:
chklogger.logger.error(
"Invalid variable definition record in %s",
self.cfile.name)
return self._gvardefs
@property
def global_varinfo_names(self) -> Dict[str, "CVarInfo"]:
if self._globalvarinfonames is None:
self._globalvarinfonames = {}
for vardef in self.gvardefs.values():
self._globalvarinfonames[vardef.vname] = vardef.varinfo
for vardecl in self.gvardecls.values():
self._globalvarinfonames[vardecl.vname] = vardecl.varinfo
for gfun in self.gfunctions.values():
self._globalvarinfonames[gfun.vname] = gfun.varinfo
return self._globalvarinfonames
@property
def global_varinfo_vids(self) -> Dict[int, "CVarInfo"]:
if self._globalvarinfovids is None:
self._globalvarinfovids = {}
for (vid, vardef) in self.gvardefs.items():
self._globalvarinfovids[vid] = vardef.varinfo
for (vid, vardecl) in self.gvardecls.items():
self._globalvarinfovids[vid] = vardecl.varinfo
for (vid, gfun) in self.gfunctions.items():
self._globalvarinfovids[vid] = gfun.varinfo
return self._globalvarinfovids
[docs] def get_global_varinfos(self) -> List["CVarInfo"]:
return list(self.global_varinfo_vids.values())
@property
def global_compinfo_ckeys(self) -> Dict[int, "CCompInfo"]:
if self._globalcompinfockeys is None:
self._globalcompinfockeys = {}
for (ckey, comptag) in self.gcomptagdefs.items():
self._globalcompinfockeys[ckey] = comptag.compinfo
for (ckey, comptag) in self.gcomptagdecls.items():
self._globalcompinfockeys[ckey] = comptag.compinfo
return self._globalcompinfockeys
[docs] def get_compinfos(self) -> List["CCompInfo"]:
result: List["CCompInfo"] = []
for gcdef in self.gcomptagdefs.values():
result.append(gcdef.compinfo)
for gcdecl in self.gcomptagdecls.values():
result.append(gcdecl.compinfo)
return result