from collections import OrderedDict
from ccrawl import formatters
from ccrawl.utils import struct_letters, c_type, cxx_type
from ccrawl.db import where
[docs]class ccore(object):
"""
Generic class dedicated to a collected object,
used as parent class for all C/C++ items collected
by ccrawl:
- typedef
- struct
- union
- enum
- macro
- func
- class
- template
- namespace
Attributes:
formatter (function): a function used to print the object
in various formats.
_cache_ (dict): a global (parent class level) dict of all types
that have been fetched from the database so far.
"""
_is_typedef = False
_is_struct = False
_is_union = False
_is_enum = False
_is_macro = False
_is_func = False
_is_class = False
_is_template = False
_is_namespace = False
formatter = None
_cache_ = {}
[docs] def show(self, db=None, r=None, form=None):
"""
Generic method that possibly defines and
ultimately calls the internal formatter function.
Attributes:
db [opt] (Proxy): database used in recursive mode
"""
if (not self.formatter) or form:
self.set_formatter(form)
return self.formatter(db, r)
[docs] def unfold(self, db, limit=None):
"""
Generic method that fetches recursively from the given database
all other types on which this type depends. The method is recursive
in the sense that all subtypes are unfolded as well until they only
depend on primitive types (int, short, float, etc.)
"""
self.subtypes = OrderedDict()
return self
[docs] def build(self, db):
"""
Generic method for building a ctypes instance for this type.
Basically just a wrapper for the :mod:`ctypes_`.build function.
Parameters:
db (Proxy): database used to get any other type on which
this type depends.
"""
self.unfold(db)
from ccrawl.ext import ctypes_
return ctypes_.build(self, db)
[docs] def add_subtype(self, db, elt, limit=None):
"""
Generic method that fetches item 'elt' from the database and
adds it to the subtypes of this type before unfolding it.
"""
x = ccore._cache_.get(elt, None)
if x is None:
data = db.get(where("id") == elt)
if data:
x = ccore.from_db(data)
ccore._cache_[elt] = x
else:
self.subtypes[elt] = None
return
self.subtypes[elt] = x.unfold(db, limit)
[docs] def graph(self,db,V=None,g=None):
"""
Generic method that returns the types-dependency graph
associated with this type.
Basically just a wrapper for the graphs.build function.
Parameters:
db (Proxy): database used to get any other type on which
this type depends.
"""
from ccrawl.graphs import build
return build(self,db,V,g)
@staticmethod
def getcls(name):
if name == "cTypedef":
return cTypedef
if name == "cStruct":
return cStruct
if name == "cUnion":
return cUnion
if name == "cEnum":
return cEnum
if name == "cMacro":
return cMacro
if name == "cFunc":
return cFunc
if name == "cClass":
return cClass
if name == "cTemplate":
return cTemplate
if name == "cNamespace":
return cNamespace
[docs] def to_db(self, identifier, tag, src):
"""
Generic method that returns a list of database-insertable "documents"
for the current item.
"""
doc = {
"id": identifier,
"val": self,
"cls": self.__class__.__name__,
"src": src,
}
if tag:
doc["tag"] = tag
data = [doc]
if hasattr(self, "local"):
for i, x in iter(self.local.items()):
if x:
data.extend(x.to_db(i, tag, identifier))
return data
[docs] @staticmethod
def from_db(data):
"""
Generic method that returns a specialized ccore instance from a given
document data usually obtained from the database.
Parameters:
data (dict): must have "id", "cls" and "val" keys where cls is
one of the below ccore specialized class name.
"""
identifier = data["id"]
val = ccore.getcls(data["cls"])(data["val"])
val.identifier = identifier
val.subtypes = None
return val
# ------------------------------------------------------------------------------
[docs]class cTypedef(str, ccore):
"""
Specialized ccore class that is also a 'str' representing a C/C++ typedef.
Attributes:
identifier: the new typename associated to this typedef.
"""
_is_typedef = True
[docs] def unfold(self, db, limit=None, ctx=None):
"""
Unfolding a typedef simply adds its underlying type definition to subtypes.
"""
if self.subtypes is None:
self.subtypes = OrderedDict()
ctype = c_type(self)
if limit != None:
if limit <= 0 and ctype.is_ptr:
return self
elt = ctype.lbase
if elt not in struct_letters:
if limit:
limit -= 1
self.add_subtype(db, elt, limit)
return self
def __eq__(self, other):
return str(self) == str(other)
# ------------------------------------------------------------------------------
[docs]class cStruct(list, ccore):
"""
Specialized ccore class that is also a 'list' representing a C struct.
Attributes:
identifier: the typename associated to this C struct.
Items of the list represent fields of the structure and are formatted as
triplet of the form (t,n,c) where
- t is a string that represents the type the field
- n is a string that represents the name of the field
- c is a string that represents a comment for the field
"""
_is_struct = True
[docs] def unfold(self, db, limit=None):
"""
Unfolding a struct adds all its fields' types to subtypes.
"""
if self.subtypes is None:
self.subtypes = OrderedDict()
T = list(struct_letters.keys())
T.append(self.identifier)
for (t, n, c) in self:
ctype = c_type(t)
if limit != None:
if limit <= 0 and ctype.is_ptr:
continue
elt = ctype.lbase
if elt not in T:
T.append(elt)
if limit:
limit -= 1
self.add_subtype(db, elt, limit)
return self
def index_of(self,n):
i=0
for f in self:
if f[1]==n:
return i
i += 1
return None
def __eq__(self, other):
return list(self) == list(other)
# ------------------------------------------------------------------------------
[docs]class cClass(list, ccore):
"""
Specialized ccore class that is also a 'list' representing a C++ class.
Attributes:
identifier: the name associated to this C++ class.
Items of the list represent attributes of the class and are formatted as
triplet of the form (x,y,z) where
- x is a tuple (q,t) where q is a "parent", "using" or "virtual" keyword
and t is "virtual" or a type name,
- y is a tuple (mn,n) where mn is the mangled name and n is the full name
of the class attribute,
- z is a tuple (p,c) where p is a "public"/"protected"/"private"/"friend"
indicator and c is a string that represents a comment.
"""
_is_class = True
[docs] def unfold(self, db, limit=None):
if self.subtypes is None:
self.subtypes = OrderedDict()
T = list(struct_letters.keys())
T.append(self.identifier)
for (x, y, _) in self:
qal, t = x
mn, n = y
if qal == "parent":
elt = [n]
elif qal == "using":
elt = t
else:
if mn or ("virtual" in qal):
continue
elt = cxx_type(t)
elt = elt.show_base(kw=True, ns=True)
elt = [elt]
for e in elt:
if e not in T:
T.append(e)
self.add_subtype(db, e, limit)
return self
[docs] def build(self, db):
from ccrawl.ext import ctypes_
x = self.as_cStruct(db)
x.unfold(db)
return ctypes_.build(x, db)
[docs] def cStruct_build_info(self, db):
"""Defines the structure layout for this class,
according to the gcc cxx ABI for virtual classes.
The returned value is a triplet, (vptr, M, V) where
- vptr is a virtual indicator,
- M is the list of non-virtual fields,
- V is the ordered dict of virtual fields.
This triplet is used to create a cStruct instance
that correspond to an instance of this C++ class in memory.
"""
self.unfold(db)
M, V = [], OrderedDict()
vptr = 0
# iterate over classes' fields
for (x, y, _) in self:
qal, t = x
mn, n = y
# we don't care about scope & comments
# we start by handling parent classes:
if qal == "parent":
n = cxx_type(n)
nn = n.show_base()
name = n.show_base(True, True)
x = ccore._cache_.get(name, None)
try:
if x._is_typedef:
x = ccore._cache_.get(x, None)
except Exception:
pass
if x is None:
raise TypeError("unkown type '%s'" % n)
assert x._is_class
# get layout of the parent class:
vtbl, m, v = x.cStruct_build_info(db)
if t == "virtual":
vptr = 2
if nn not in V:
V[nn] = (vtbl, m)
else:
if vtbl:
vptr += vtbl
if len(m) > 0:
if not m[0][1].startswith("__vptr"):
t = cxx_type("void *")
M.append((t, "__vptr$%s" % nn))
M.extend(m)
V.update(v)
elif qal == "using":
continue
elif "virtual" in qal:
vptr = 1
else:
t = cxx_type(t)
if not t.is_method:
M.append((t, n))
return (vptr, M, V)
[docs] def as_cStruct(self, db):
"""
Creates a cStruct instance that correspond to this C++ class,
according the gcc cxx ABI for virtual classes.
"""
if self.identifier.startswith("union "):
x = cUnion()
else:
x = cStruct()
name = cxx_type(self.identifier)
x.identifier = "struct __layout$%s"%(name.show_base(kw=False,ns=True))
# now get the structure information for this class:
x.subtypes = None
vptr, M, V = self.cStruct_build_info(db)
if len(M) > 0 and vptr:
if not M[0][1].startswith("__vptr"):
n = cxx_type(self.identifier)
x.append(("void *", "__vptr$%s" % n.show_base(), ""))
for t, n in M:
x.append((t.show(), n, ""))
for nn, v in V.items():
vptr, m = v
if vptr:
x.append(("void *", "__vptr$%s" % nn, ""))
for t, n in m:
x.append((t.show(), n, ""))
return x
[docs] def has_virtual_members(self):
"""
Returns True if the C++ class has virtual members.
"""
for x, _, _ in self:
qal, t = x
if "virtual" in qal:
return True
return False
[docs] def base_specifier_list(self):
"""
Returns the list of C++ parent (possibly virtual) classes.
"""
spe = []
for x, y, z in self:
qal, t = x
if "parent" in qal:
mn, n = y
n = cxx_type(n)
p, _ = z
s = ""
if t:
s += " virtual"
s += " %s %s" % (p.lower(), n.show_base())
spe.append(s)
if len(spe) > 0:
return " :" + (",".join(spe))
else:
return ""
def __eq__(self, other):
return list(self) == list(other)
# ------------------------------------------------------------------------------
[docs]class cUnion(list, ccore):
"""
Specialized ccore class that is also a 'list' representing a C union.
"""
_is_union = True
[docs] def unfold(self, db, limit=None):
if self.subtypes is None:
self.subtypes = OrderedDict()
T = list(struct_letters.keys())
T.append(self.identifier)
for (t, n, c) in self:
ctype = c_type(t)
if limit != None:
if limit <= 0 and ctype.is_ptr:
continue
elt = ctype.lbase
if elt not in T:
T.append(elt)
if limit:
limit -= 1
self.add_subtype(db, elt, limit)
return self
def index_of(self,n):
i=0
for f in self:
if f[1]==n:
return i
i += 1
return None
def __eq__(self, other):
return list(self) == list(other)
# ------------------------------------------------------------------------------
[docs]class cEnum(dict, ccore):
"""
Specialized ccore class that is also a 'dict' representing a C enum.
"""
_is_enum = True
# ------------------------------------------------------------------------------
[docs]class cMacro(str, ccore):
"""
Specialized ccore class that is also a 'str' representing a C macro.
"""
_is_macro = True
# ------------------------------------------------------------------------------
[docs]class cFunc(dict, ccore):
"""
Specialized ccore class that is also a 'dict' representing a C/C++ function.
"""
_is_func = True
def restype(self):
t = c_type(self["prototype"])
if len(t.pstack)>0:
t.pstack.pop()
return t.show()
def argtypes(self):
t = c_type(self["prototype"])
if len(t.pstack)>0:
return t.pstack[-1].args
return []
[docs] def unfold(self, db, limit=None):
if self.subtypes is None:
self.subtypes = OrderedDict()
T = list(struct_letters.keys())
rett = self.restype()
args = self.argtypes()
args.insert(0, rett)
for t in args:
elt = c_type(t).lbase
if elt not in T:
T.append(elt)
self.add_subtype(db, elt)
return self
def __eq__(self, other):
return str(self) == str(other)
# ------------------------------------------------------------------------------
[docs]class cTemplate(dict, ccore):
"""
Specialized ccore class that is also a 'dict' representing a C++ template.
"""
_is_template = True
def get_basename(self):
if self.get("partial_specialization", False):
return self.identifier
i = self.identifier.rfind("<")
assert i > 0
return self.identifier[:i]
def get_template(self):
return "<%s>" % (",".join(self["params"]))
# ------------------------------------------------------------------------------
[docs]class cNamespace(list, ccore):
"""
Specialized ccore class that is also a 'list' representing a C++ namespace.
"""
_is_namespace = True
[docs] def unfold(self, db, limit=None):
if self.subtypes is None:
self.subtypes = OrderedDict()
T = list(struct_letters.keys())
T.append(self.identifier)
for elt in self:
self.add_subtype(db, elt)
return self
def __eq__(self, other):
return list(self) == list(other)