Source code for chc.app.CGlobalDeclarations

# ------------------------------------------------------------------------------
# CodeHawk C Analyzer
# Author: Henny Sipma
# ------------------------------------------------------------------------------
# The MIT License (MIT)
#
# Copyright (c) 2017-2020 Kestrel Technology LLC
# Copyright (c) 2020-2022 Henny B. Sipma
# Copyright (c) 2023-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.
# ------------------------------------------------------------------------------
"""Declarations of global entities across files."""

from dataclasses import dataclass

from typing import (
    Any, Callable, cast, Dict, List, Optional, Set, Tuple, TYPE_CHECKING)
import xml.etree.ElementTree as ET

import chc.util.fileutil as UF
import chc.util.IndexedTable as IT
import chc.app.CInitInfo as CI

from chc.app.CCompInfo import CCompInfo
from chc.app.CDeclarations import CDeclarations
from chc.app.CFieldInfo import CFieldInfo
from chc.app.CGlobalDictionary import CGlobalDictionary
from chc.app.CInitInfo import CInitInfo, COffsetInitInfo
from chc.app.IndexManager import VarReference, CKeyReference
from chc.app.CVarInfo import CVarInfo

from chc.util.IndexedTable import IndexedTable, IndexedTableValue
from chc.util.loggingutil import chklogger

if TYPE_CHECKING:
    from chc.api.CGlobalContract import CGlobalContract
    from chc.app.CApplication import CApplication
    from chc.app.CFile import CFile
    from chc.app.CInitInfo import CSingleInitInfo, CCompoundInitInfo


[docs]class ConjectureFailure(Exception): def __init__(self, ckey: int, gckey: int) -> None: self.ckey = ckey self.gckey = gckey def __str__(self) -> str: return ( f"Compinfo {self.ckey} is not compatible with global compinfo {self.gckey}")
[docs]class CGlobalDeclarations(CDeclarations): """Dictionary that indexes global vars and struct definitions from all files. The indexing of struct definitions may involve backtracking in the case of structs that contain pointer references to itself, or circular references that involve multiple structs. The backtracking is performed per file. When a struct (represented by a compinfo) is indexed its status is set to pending. When a request for a TComp ckey conversion for the same compinfo is encountered a new global key is conjectured as follows: - gckey that has already been reserved for this ckey - gckey that has already been conjectured for this ckey - gckey for an existing global compinfo that has the same fields, if (ckey,gckey) is not in the list of incompatibles - reserve a new key from the indexed table and set its status to reserved, and remove its pending status When the compinfo for ckey has been constructed the state is updated as follows: - if ckey had a reserved key the reserved key is now committed - if ckey had a conjectured key and the conjectured key is the same as the returned gckey, nothing needs to be done - if ckey had a conjectured key but the conjectured key is not the same as the returned gckey, add (ckey,gckey) to the list of incompatibles, reset the indexed table to the file checkpoint, and re-index all compinfos in the file. """ def __init__(self, capp: "CApplication", xnode: Optional[ET.Element]) -> None: self._capp = capp # Global definitions and declarations dictionary self.fieldinfo_table = IndexedTable("fieldinfo-table") self.compinfo_table = IndexedTable("compinfo-table") self.varinfo_table = IT.IndexedTable("varinfo-table") self.initinfo_table = IT.IndexedTable("initinfo-table") self.offset_init_table = IT.IndexedTable("offset-init-table") self.tables = [ self.fieldinfo_table, self.compinfo_table, self.initinfo_table, self.varinfo_table, self.offset_init_table ] # Collect names for compinfo equivalence classes self.compinfo_names: Dict[int, Set[str]] = {} # gckey -> string set # Collect storage classes for varinfo equivalence classes self._varinfo_storage_classes: Dict[int, Set[str]] = {} # gvid -> string # Support data structures for linker self._ckey2gckey: Dict[int, Dict[int, int]] = {} # fid -> ckey -> gckey self._vid2gvid: Dict[int, Dict[int, int]] = {} # fid -> vid -> gvid # string of joined fields -> gckey list self._fieldstrings: Dict[str, List[int]] = {} self.pending: List[int] = [] self.conjectured: Dict[int, int] = {} # ckey -> gckey self.reserved: Dict[int, int] = {} # ckey -> gckey self.incompatibles: Dict[int, Set[int]] = {} # ckey -> gckey set # (fid,varinfo) list self.default_function_prototypes: List[Tuple[int, CVarInfo]] = [] self._initialize(xnode) if self.compinfo_table.size() == 0: self.index_opaque_struct() @property def cfile(self) -> "CFile": raise UF.CHCError("No access to individual file via global dictionary") @property def capp(self) -> "CApplication": return self._capp @property def globalcontract(self) -> "CGlobalContract": return self.capp.globalcontract @property def dictionary(self) -> "CGlobalDictionary": return self.capp.dictionary @property def varinfo_storage_classes(self) -> Dict[int, Set[str]]: return self._varinfo_storage_classes @property def fieldstrings(self) -> Dict[str, List[int]]: """Returns a map from a conjoined fieldstring to global struct keys fieldstrings -> gckey """ return self._fieldstrings @property def ckey2gckey(self) -> Dict[int, Dict[int, int]]: """Returns the global compinfo key for a given file and compinfo key fid -> ckey -> gckey """ return self._ckey2gckey
[docs] def get_gckey(self, ckeyref: CKeyReference) -> Optional[int]: if ckeyref.fid is None: # this already is a global key return ckeyref.ckey if ckeyref.fid in self.ckey2gckey: if ckeyref.ckey in self.ckey2gckey[ckeyref.fid]: return self.ckey2gckey[ckeyref.fid][ckeyref.ckey] return None
[docs] def register_gcompinfo( self, ckeyref: CKeyReference, gcompinfo: CCompInfo) -> None: if ckeyref.fid is None: chklogger.logger.warning( "register_gcompinfo is called with a global ckeyref: %s", gcompinfo.name) return gckey = gcompinfo.ckey fields = gcompinfo.field_strings self._fieldstrings.setdefault(fields, []) if gckey not in self.fieldstrings[fields]: self._fieldstrings[fields].append(gckey) self._ckey2gckey.setdefault(ckeyref.fid, {}) self._ckey2gckey[ckeyref.fid][ckeyref.ckey] = gckey
@property def vid2gvid(self) -> Dict[int, Dict[int, int]]: """Returns the global vid for a given file and varinfo vid. fid -> vid -> gvid. """ return self._vid2gvid
[docs] def get_gvid(self, varref: VarReference) -> Optional[int]: if varref.fid is None: chklogger.logger.warning( "get_gvid is called with a global vid; returning global vid") return varref.vid if varref.fid in self.vid2gvid: if varref.vid in self.vid2gvid[varref.fid]: return self.vid2gvid[varref.fid][varref.vid] return None
[docs] def is_hidden_field(self, compname: str, fieldname: str) -> bool: return self.globalcontract.is_hidden_field(compname, fieldname)
[docs] def is_hidden_struct(self, filename: str, compname: str) -> bool: return self.globalcontract.is_hidden_struct(filename, compname)
# ---------------------- Retrieve items from definitions dictionary ------
[docs] def get_fieldinfo(self, ix: int) -> CFieldInfo: itv = self.fieldinfo_table.retrieve(ix) return CFieldInfo(self, itv)
[docs] def get_varinfo(self, ix: int) -> CVarInfo: itv = self.varinfo_table.retrieve(ix) return CVarInfo(self, itv)
[docs] def get_compinfo(self, ix: int) -> CCompInfo: itv = self.compinfo_table.retrieve(ix) return CCompInfo(self, itv)
[docs] def get_initinfo(self, ix: int) -> CInitInfo: itv = self.initinfo_table.retrieve(ix) return CInitInfo(self, itv)
[docs] def get_offset_init(self, ix: int) -> COffsetInitInfo: itv = self.offset_init_table.retrieve(ix) return COffsetInitInfo(self, itv)
# --------------------- Retrieve derived items ---------------------------
[docs] def varinfo_values(self) -> List[CVarInfo]: return [CVarInfo(self, itv) for itv in self.varinfo_table.values()]
[docs] def has_varinfo_by_name(self, name: str) -> bool: return any([v.vname == name for v in self.varinfo_values()])
[docs] def get_varinfo_by_name(self, name: str) -> CVarInfo: for v in self.varinfo_values(): if v.vname == name: return v raise Exception("No varinfo with name \"" + name + "\"")
[docs] def get_compinfo_by_ckey(self, ckey: int) -> CCompInfo: raise Exception("get_compinfo_by_ckey: not yet implemented")
[docs] def get_structname(self, ckey: int) -> str: if ckey in self.compinfo_names: return list(self.compinfo_names[ckey])[0] chklogger.logger.warning("Compinfo name for %s not found", str(ckey)) return "struct " + str(ckey)
[docs] def get_gcompinfo(self, ckeyref: CKeyReference) -> Optional[CCompInfo]: """Returns the global compinfo associated with a file compinfo.""" if ckeyref.fid is None: chklogger.logger.warning( "get_gcompinfo is called with a global ckeyref") return None gckey = self.get_gckey(ckeyref) if gckey is not None: return self.get_compinfo(gckey) return None
[docs] def convert_ckey(self, ckeyref: CKeyReference) -> Optional[int]: if ckeyref.fid is None: # this already is a global key return ckeyref.ckey return self.get_gckey(ckeyref)
[docs] def list_compinfos(self) -> str: lines: List[str] = [] for gckey in self.compinfo_names: names = ",".join(list(self.compinfo_names[gckey])) lines.append(names) return "\n".join(sorted(lines))
[docs] def show_compinfos(self, name: str) -> List[Tuple[int, CCompInfo]]: result: List[Tuple[int, CCompInfo]] = [] gckeys = [ x for x in self.compinfo_names.keys() if name in self.compinfo_names[x]] for k in gckeys: result.append((k, self.get_compinfo(k))) return result
# -------------------- Linker support services ---------------------------
[docs] def reset_conjectures(self) -> None: self.pending = [] self.conjectured = {} self.reserved = {}
[docs] def cleanup(self, checkpoint: int, ckey: int, gckey: int) -> None: if ckey not in self.incompatibles: self.incompatibles[ckey] = set([]) self.incompatibles[ckey].add(gckey) self.reset_conjectures() for k in self.compinfo_names.keys(): if k >= checkpoint: self.compinfo_names.pop(k) for fs in self.fieldstrings.keys(): for gckey in self.fieldstrings[fs]: if gckey >= checkpoint: self.fieldstrings[fs].remove(gckey)
[docs] def get_state(self) -> str: lines = [] lines.append("Pending : " + str(self.pending)) lines.append("Conjectured: " + str(self.conjectured)) lines.append("Reserved : " + str(self.reserved)) return "\n".join(lines)
[docs] def get_field_strings_conjecture( self, cname: str, fields: str, ckey: int) -> Optional[int]: if fields in self.fieldstrings: for gckey in self.fieldstrings[fields]: conjecturedkey = gckey if ( ckey in self.incompatibles and conjecturedkey in self.incompatibles[ckey] ): pass else: break else: return None return conjecturedkey return None
[docs] def conjecture_key(self, fid: int, compinfo: CCompInfo) -> int: ckey = compinfo.ckey if ckey in self.reserved: return self.reserved[ckey] if ckey in self.conjectured: return self.conjectured[ckey] conjecturedkey = self.get_field_strings_conjecture( compinfo.name, compinfo.field_strings, ckey ) if conjecturedkey is None: reservedkey = self.compinfo_table.reserve() self.reserved[ckey] = reservedkey self.pending.remove(ckey) return reservedkey else: self.conjectured[ckey] = conjecturedkey self.pending.remove(ckey) return conjecturedkey
# ------------------- Indexing compinfos ---------------------------------
[docs] def index_fieldinfo(self, fieldinfo: CFieldInfo, compinfoname: str) -> int: def f(index: int, tags: List[str], args: List[int]) -> CFieldInfo: itv = IndexedTableValue(index, tags, args) return CFieldInfo(self, itv) tags = [fieldinfo.fname] if self.is_hidden_field(compinfoname, fieldinfo.fname): gftype = self.index_opaque_struct_pointer() else: gftype = self.dictionary.index_typ( fieldinfo.ftype.expand().strip_attributes()) args = [-1, gftype, fieldinfo.bitfield, -1, -1] return self.fieldinfo_table.add_tags_args(tags, args, f)
# only compinfo's from files should be indexed
[docs] def index_compinfo_key(self, compinfo: CCompInfo, fid: int) -> int: """Returns the global ckey for the given compinfo and file id.""" chklogger.logger.info( "Index compinfo key for %s and fid %d", compinfo.name, fid) ckey = compinfo.ckey ckeyref = CKeyReference(fid, ckey) gckey = self.get_gckey(ckeyref) if gckey is not None: return gckey if ckey in self.conjectured: chklogger.logger.info( "Compinfo %s (fid.ckey: %s.%s) conjectured key: %s", compinfo.name, str(fid), str(ckey), str(self.conjectured[ckey])) return self.conjectured[ckey] if ckey in self.reserved: chklogger.logger.info( "Compinfo %s (fid.ckey: %s.%s) reserved key: %s", compinfo.name, str(fid), str(ckey), str(self.reserved[ckey])) return self.reserved[ckey] if ckey in self.pending: pendingkey = self.conjecture_key(fid, compinfo) chklogger.logger.info( "Compinfo %s (fid.ckey: %s.%s) new pending key: %s", compinfo.name, str(fid), str(ckey), str(pendingkey)) return pendingkey # this compinfo is new return self.make_global_compinfo(fid, compinfo).ckey
[docs] def index_opaque_struct(self) -> int: tags = ["?"] args = [-1, 1, -1] def f(index: int, tags: List[str], args: List[int]) -> CCompInfo: self.compinfo_names.setdefault(index, set([])) self.compinfo_names[index].add("opaque-struct") itv = IT.IndexedTableValue(index, tags, args) return CCompInfo(self, itv) return self.compinfo_table.add_tags_args(tags, args, f)
[docs] def get_opaque_struct(self) -> CCompInfo: itv = self.compinfo_table.retrieve(1) return CCompInfo(self, itv)
[docs] def index_opaque_struct_pointer(self) -> int: tags = ["tcomp"] args = [1] comptypix = self.dictionary.mk_typ_index(tags, args) tags = ["tptr"] args = [comptypix] return self.dictionary.mk_typ_index(tags, args)
[docs] def make_global_compinfo(self, fid: int, compinfo: CCompInfo) -> CCompInfo: chklogger.logger.info( "Indexing compinfo %s for fid: %d", compinfo.name, fid) filename = self.capp.get_file_by_index(fid).name ckey = compinfo.ckey cname = compinfo.name if self.is_hidden_struct(filename, cname): chklogger.logger.info("Hide struct %s in file %", cname, filename) return self.get_opaque_struct() keyref = CKeyReference(fid, ckey) gcompinfo = self.get_gcompinfo(keyref) if gcompinfo is not None: return gcompinfo chklogger.logger.info("Make compinfo pending: %s", compinfo.name) self.pending.append(compinfo.ckey) tags = ["?"] # we don't have a name yet fieldixs = [self.index_fieldinfo(f, cname) for f in compinfo.fields] # create a key with args: ckey = -1; is_struct; iattributes args = [-1, 1 if compinfo.is_struct else 0, -1] + fieldixs key = (",".join(tags), ",".join([str(x) for x in args])) if ckey in self.reserved: gckey = self.reserved[ckey] itv = IT.IndexedTableValue(gckey, tags, args) gcompinfo = CCompInfo(self, itv) self.compinfo_table.commit_reserved(gckey, key, gcompinfo) self.reserved.pop(ckey) self.compinfo_names.setdefault(gckey, set([])) self.compinfo_names[gckey].add(compinfo.name) self.register_gcompinfo(keyref, gcompinfo) return gcompinfo # use tags and args to obtain an index from the comp-info table def f(index: int, tags: List[str], args: List[int]) -> CCompInfo: self.compinfo_names.setdefault(index, set([])) self.compinfo_names[index].add(compinfo.name) itv = IT.IndexedTableValue(index, tags, args) return CCompInfo(self, itv) compinfoindex = self.compinfo_table.add_tags_args(tags, args, f) # check if this new index corresponds to an earlier conjectured key # if so return the global compinfo, otherwise fail and backtrack gcompinfo = self.get_compinfo(compinfoindex) if ckey in self.conjectured: conjecturedkey = self.conjectured[ckey] gckey = gcompinfo.ckey if gckey == conjecturedkey: self.ckey2gckey[fid][ckey] = gcompinfo.ckey self.conjectured.pop(ckey) return gcompinfo else: chklogger.logger.info( "Conjecture failure for %s (fid:%s, ckey:%s, gckey:%s, " + "conjectured key: %s", compinfo.name, str(fid), str(ckey), str(gckey), str(conjecturedkey)) raise ConjectureFailure(ckey, conjecturedkey) else: # connect the global compinfo to the file compinfo and clean up keyref = CKeyReference(fid, ckey) self.register_gcompinfo(keyref, gcompinfo) self.pending.remove(compinfo.ckey) return gcompinfo
[docs] def index_file_compinfos(self, fid: int, compinfos: List[CCompInfo]) -> None: """Indexes and connects the compinfos from the individual c files.""" if len(compinfos) > 0: chklogger.logger.info("Index %d compinfos", len(compinfos)) while 1: self.compinfo_table.set_checkpoint() self._ckey2gckey[fid] = {} try: for c in compinfos: chklogger.logger.info( "Index compinfo (fid: %d): %s", fid, c.name) self.make_global_compinfo(fid, c) except ConjectureFailure as e: checkpoint = self.compinfo_table.reset_to_checkpoint() self.cleanup(checkpoint, e.ckey, e.gckey) else: self.compinfo_table.remove_checkpoint() self.incompatibles = {} break
# -------------------- Indexing varinfos ---------------------------------
[docs] def mk_single_init_index(self, tags: List[str], args: List[int]) -> int: def f(index: int, tags: List[str], args: List[int]) -> CI.CInitInfo: itv = IndexedTableValue(index, tags, args) return CI.CSingleInitInfo(self, itv) return self.initinfo_table.add_tags_args(tags, args, f)
[docs] def mk_compound_init_index(self, tags: List[str], args: List[int]) -> int: def f(index: int, tags: List[str], args: List[int]) -> CI.CInitInfo: itv = IndexedTableValue(index, tags, args) return CI.CCompoundInitInfo(self, itv) return self.initinfo_table.add_tags_args(tags, args, f)
[docs] def mk_offset_init_index(self, tags: List[str], args: List[int]) -> int: def f(index: int, tags: List[str], args: List[int]) -> CI.COffsetInitInfo: itv = IndexedTableValue(index, tags, args) return CI.COffsetInitInfo(self, itv) return self.offset_init_table.add_tags_args(tags, args, f)
[docs] def index_init(self, init: CInitInfo, fid: int = -1) -> int: args: List[int] if init.is_single: init = cast("CSingleInitInfo", init) args = [self.dictionary.index_exp(init.exp)] return self.mk_single_init_index(init.tags, args) if init.is_compound: init = cast("CCompoundInitInfo", init) gtype = self.dictionary.index_typ(init.typ) oinits: List[int] = [ self.index_offset_init(x) for x in init.offset_initializers] args = [gtype] + oinits return self.mk_compound_init_index(init.tags, args) else: raise Exception("InitInfo not recognized")
[docs] def index_offset_init(self, oinit: COffsetInitInfo, fid: int = -1) -> int: args: List[int] = [ self.dictionary.index_offset(oinit.offset), self.index_init(oinit.initializer)] return self.mk_offset_init_index(oinit.tags, args)
[docs] def index_varinfo_vid(self, varref: VarReference) -> Optional[int]: if varref.fid is None: # this already is a global vid return varref.vid return self.get_gvid(varref)
[docs] def make_global_varinfo(self, fid: int, varinfo: CVarInfo) -> None: """Returns the global varinfo that corresponds to a file varinfo.""" if varinfo.vtype.is_default_function_prototype: # a function declaration without arguments self.default_function_prototypes.append((fid, varinfo)) return vid = varinfo.vid if varinfo.vstorage == "s": # create a file-specific name for static variables vname = varinfo.vname + "__file__" + str(fid) + "__" else: vname = varinfo.vname vtype = varinfo.vtype.expand().strip_attributes() vtypeix = self.dictionary.index_typ(vtype) vtype = self.dictionary.get_typ(vtypeix) if varinfo.has_initializer(): vinit = varinfo.initializer gvinit = [self.index_init(vinit, fid=fid)] else: gvinit = [] tags = [vname] vargs = varinfo.args vaddrof = 1 if vtype.is_function else vargs[6] args = [ -1, # vid: use the CVarInfo index as the gvid vtypeix, # vtype index, as indexed in the global dictionary -1, # vattr index: drop the attributes vargs[3], # vglob: same as file varinfo vargs[4], # vinline: same as file varinfo -1, # vdecl: location is not kept at the global level vaddrof, # true for functions, otherwise same as file varinfo vargs[7]] + gvinit # global initializer as converted, or absent def f(index: int, tags: List[str], args: List[int]) -> CVarInfo: itv = IndexedTableValue(index, tags, args) return CVarInfo(self, itv) gvarinfoindex = self.varinfo_table.add_tags_args(tags, args, f) gvarinfo = self.get_varinfo(gvarinfoindex) gvid = gvarinfo.vid if gvid not in self.varinfo_storage_classes: self._varinfo_storage_classes[gvid] = set([""]) vstorageclass = varinfo.tags[1] if vstorageclass not in self.varinfo_storage_classes[gvid]: self._varinfo_storage_classes[gvid].add(vstorageclass) self.vid2gvid[fid][vid] = gvarinfo.vid chklogger.logger.debug( "Fid: %s, vid:%s, gvid:%s: %s", str(fid), str(vid), str(gvarinfo.vid), gvarinfo.vname)
[docs] def index_file_varinfos(self, fid: int, varinfos: List[CVarInfo]) -> None: if len(varinfos) > 0: self.vid2gvid[fid] = {} for v in varinfos: self.make_global_varinfo(fid, v)
[docs] def resolve_default_function_prototypes(self) -> None: chklogger.logger.info( "Resolving %d function prototypes", len(self.default_function_prototypes)) for (fid, varinfo) in self.default_function_prototypes: def f(key: Tuple[str, str]) -> bool: return key[0].startswith(varinfo.vname) itvcandidates = self.varinfo_table.retrieve_by_key(f) candidates = [ (itv[0], CVarInfo(self, itv[1])) for itv in itvcandidates] if len(candidates) == 1: self.vid2gvid[fid][varinfo.vid] = candidates[0][1].vid chklogger.logger.info( "Resolved prototype for %s", varinfo.vname) else: pcandidates = ",".join([c[1].vname for c in candidates]) for (_, c) in candidates: if c.vname == varinfo.vname: self.vid2gvid[fid][varinfo.vid] = c.vid chklogger.logger.warning( "Selected prototype %s for %s from multiple " + "candidates: %s", c.vname, varinfo.vname, pcandidates) break else: chklogger.logger.warning( "Unable to resolve prototype for %s: %s: %s", varinfo.vname, str(len(candidates)), pcandidates)
# -------------------- Writing xml ---------------------------------------
[docs] def write_xml(self, node: ET.Element) -> None: dnode = ET.Element("dictionary") self.dictionary.write_xml(dnode) node.append(dnode) def f(n: ET.Element, r: IndexedTableValue) -> None: r.write_xml(n) for t in self.tables: tnode = ET.Element(t.name) t.write_xml(tnode, f) node.append(tnode) cnode = ET.Element("compinfo-names") for ckey in sorted(self.compinfo_names): nnode = ET.Element("n") nnode.set("ckey", str(ckey)) nnode.set("names", ",".join(sorted(self.compinfo_names[ckey]))) cnode.append(nnode) node.append(cnode) vsnode = ET.Element("varinfo-storage-classes") for vid in sorted(self.varinfo_storage_classes): if "n" in self.varinfo_storage_classes[vid]: continue if len(self.varinfo_storage_classes[vid]) > 1: chklogger.logger.warning( "Multiple storage classes for variable %d", vid) continue else: storageclass = list(self.varinfo_storage_classes[vid])[0] vnode = ET.Element("n") vnode.set("vid", str(vid)) vnode.set("s", storageclass) vsnode.append(vnode) node.append(vsnode)
def __str__(self) -> str: lines = [] lines.append(str(self.dictionary)) for t in self.tables: if t.size() > 0: lines.append(str(t)) return "\n".join(lines) def _initialize(self, xnode: Optional[ET.Element]) -> None: # Initialize global declarations from globaldefinitions file if # available if xnode is None: return for t in self.tables: xtable = xnode.find(t.name) if xtable is None: raise Exception("Missing element `" + t.name + "`") t.reset() t.read_xml(xtable, "n") xml_compinfo_names = xnode.find("compinfo-names") if xml_compinfo_names is None: raise Exception("Missing element `compinfo-names`") xml_compinfo_names_n = xml_compinfo_names.find("n") if xml_compinfo_names_n is None: raise Exception("Missing element `n`") for n in xml_compinfo_names_n: xml_ckey = n.get("ckey") if xml_ckey is None: raise Exception("Missing element `ckey`") xml_names = n.get("names") if xml_names is None: raise Exception("Missing element `names`") self.compinfo_names[int(xml_ckey)] = set(xml_names.split(",")) xml_varinfo_storage_classes = xnode.find("varinfo-storage-classes") if xml_varinfo_storage_classes is None: raise Exception("Missing element `varinfo-storage-classes`") for n in xml_varinfo_storage_classes: xml_vid = n.get("vid") if xml_vid is None: raise Exception("Missing element `vid`") xml_s = n.get("s") if xml_s is None: raise Exception("Missing element `s`") self.varinfo_storage_classes[int(xml_vid)] = set([xml_s]) def _read_xml_varinfo_storage_classes(self, xnode: ET.Element) -> None: for n in xnode.findall("n"): xml_vid = n.get("vid") if xml_vid is None: raise Exception("Missing `vid` element") vid = int(xml_vid) self.varinfo_storage_classes[vid] = set([]) xml_s = n.get("s") if xml_s is None: raise Exception("Missing `s` element") for c in xml_s: self.varinfo_storage_classes[vid].add(c) def _read_xml_compinfo_names(self, xnode: ET.Element) -> None: for n in xnode.findall("n"): xml_ckey = n.get("ckey") if xml_ckey is None: raise Exception("Missing `ckey` element") ckey = int(xml_ckey) xml_names = n.get("names") if xml_names is None: raise Exception("Missing `names` element") names = xml_names.split(",") self.compinfo_names[ckey] = set(names)