def _test_ast_equivalent(self, a: ASTNode, b: ASTNode) -> None: assert a.attr_names == b.attr_names for name in a.attr_names: assert getattr(a, name) == getattr(b, name) for (name_a, child_a), (name_b, child_b) in zip(a.children(), b.children()): assert name_a == name_b self._test_ast_equivalent(child_a, child_b)
def AddExplicitCasts(node: c_ast.Node, _parent: c_ast.Node, meta_info, skip_constants): def constant_check(node: c_ast.Node): return not skip_constants or not isinstance(node, c_ast.Constant) for c in node: AddExplicitCasts(c, node, meta_info, skip_constants) if isinstance(node, c_ast.BinaryOp): tl = meta_info.type_links[node.left] tr = meta_info.type_links[node.right] if not isinstance(tl, c_ast.IdentifierType) or not isinstance(tr, c_ast.IdentifierType): return cmp = common.TypeCompare(tl, tr) if cmp == "<" and constant_check(node.left): node.left = MakeCast(tr, node.left) meta_info.type_links[node.left] = tr elif cmp == ">" and constant_check(node.right): node.right = MakeCast(tl, node.right) meta_info.type_links[node.right] = tl elif (isinstance(node, c_ast.Decl) and node.init is not None and not isinstance(node.init, c_ast.InitList)): left = node.type.type right = meta_info.type_links[node.init] if not isinstance(left, c_ast.IdentifierType) or not isinstance(right, c_ast.IdentifierType): return if constant_check(node.init): node.init = MakeCast(left, node.init) meta_info.type_links[node.init] = left elif isinstance(node, c_ast.Assignment): left = meta_info.type_links[node.lvalue] right = meta_info.type_links[node.rvalue] if not isinstance(left, c_ast.IdentifierType) or not isinstance(right, c_ast.IdentifierType): return if left is not right and constant_check(node.rvalue): node.rvalue = MakeCast(left, node.rvalue) meta_info.type_links[node.rvalue] = left # if not p: return # right = CanonicalScalarType(ttab[node.rvalue]) # if right != p: # node.rvalue = MakeCast(p, node.rvalue) # # elif isinstance(node, c_ast.FuncCall): # pass elif isinstance(node, c_ast.Return): if node.expr and constant_check(node.expr): src = meta_info.type_links[node.expr] dst = meta_info.type_links[node] if not isinstance(src, c_ast.IdentifierType) or not isinstance(dst, c_ast.IdentifierType): return if src is not dst: node.expr = MakeCast(dst, node.expr) meta_info.type_links[node.expr] = dst
def resolve_typedefs(self, declaration: c_ast.Node, typedefs: set): """ Resolves any typedefs present in the given declaration for the given typedef list. Works in-place on the AST and applies itself recursively until no more typedefs are found. :param declaration: The declaration in which to resolve typedefs in. Type is one of c_ast.TypeDecl, c_ast.PtrDecl or c_ast.ArrayDecl. :param typedefs: A set of typedefs to check against. :return: A set of the typedefs that was applied, hence a subset of the given typedefs set. :rtype: set """ # Three cases to be distinguished: Type, array or pointer declaration. For arrays, pointers, the type # resolving is just applied recursively. For a type declaration, all typedefs are first resolved, and in case we # have a aggregate type (struct or union), the resolving is again applied recursively to their members. # Keeps track of non-resolved typedefs for recursive application of this function. We do not want already # resolved typedefs to be re-applied again. applied_typedefs = set() if type(declaration) == c_ast.TypeDecl: for typedef in typedefs: if type(typedef) is c_ast.Typedef: if type(declaration.type) == c_ast.Struct or type( declaration.type) == c_ast.Union: if typedef.name == declaration.type.name: declaration.type = copy.deepcopy(typedef.type.type) declaration.quals = copy.deepcopy(typedef.quals) applied_typedefs.add(typedef) elif type(declaration.type) == c_ast.IdentifierType: if typedef.name in declaration.type.names: applied_typedefs = applied_typedefs.union( self.resolve_typedefs(typedef.type, typedefs)) declaration.type = copy.deepcopy(typedef.type.type) declaration.quals = copy.deepcopy(typedef.quals) applied_typedefs.add(typedef) elif type(typedef) is c_ast.Struct and type( declaration.type) == c_ast.Struct: if typedef.name == declaration.type.name: declaration.type = copy.deepcopy(typedef) applied_typedefs.add(typedef) elif type(declaration) == c_ast.ArrayDecl or type( declaration) == c_ast.PtrDecl: applied_typedefs = applied_typedefs.union( self.resolve_typedefs(declaration.type, typedefs)) if type(declaration.type) == c_ast.Struct or type( declaration.type) == c_ast.Union: if declaration.type.decls: for member in declaration.type.decls: applied_typedefs = applied_typedefs.union( self.resolve_typedefs(member.type, typedefs)) return applied_typedefs
def traverse(node: ASTNode, depth: int = 0) -> JSONNode: klass = node.__class__ result = {} # Node type result[NODE_TYPE_ATTR] = klass.__name__ # Local node attributes for attr in klass.attr_names: result[attr] = getattr(node, attr) # Token position if tokens is not None: if node.coord is not None and node.coord.line > 0: # some nodes have invalid coordinate (0, 1) coord: Coord = node.coord pos = find_token(coord.line, coord.column) result[TOKEN_POS_ATTR] = pos else: result[TOKEN_POS_ATTR] = None # node_name = (" " * (2 * depth) + klass.__name__).ljust(35) # if node.coord is not None: # coord: Coord = node.coord # pos = result['coord'] # print(node_name, coord.line, coord.column, pos, (tokens[pos] if pos else None), sep='\t') # else: # print(node_name) # Children nodes children: Dict[str, Optional[MaybeList[JSONNode]]] = {} for child_name, child in node.children(): child_dict = traverse(child, depth + 1) # Child strings are either simple (e.g. 'value') or arrays (e.g. 'block_items[1]') match = RE_CHILD_ARRAY.match(child_name) if match: array_name, array_index = match.groups() array_index = int(array_index) # arrays come in order, so we verify and append. array: List[JSONNode] = children.setdefault(array_name, []) # type: ignore if array_index != len(array): raise ValueError(f"Internal ast error. Array {array_name} out of order. " f"Expected index {len(array)}, got {array_index}") array.append(child_dict) else: children[child_name] = child_dict # Missing children are filled with `None` values in the dictionary. for child_attr in child_attrs_of(klass): if child_attr not in children: children[child_attr] = None result[CHILDREN_ATTR] = children return result
def replace_while_with_body(codeast: c_ast.Node): if type(codeast) == c_ast.Compound: new_block_items = copy.deepcopy(codeast.block_items) for i in range(0, len(codeast.block_items)): if type(codeast.block_items[i]) == c_ast.While: # insert list in position i while_content = copy.deepcopy( codeast.block_items[i].stmt.block_items) del new_block_items[i] new_block_items[i:i] = while_content codeast.block_items = new_block_items
def ForwardGotosAndRemoveUnusedLabels(node: c_ast.Node, forwards: Mapping[str, str]): def IsGotoOrLabel(node, _): return isinstance(node, (c_ast.Goto, c_ast.Label)) candidates = common.FindMatchingNodesPostOrder(node, node, IsGotoOrLabel) for node, parent in candidates: if isinstance(node, c_ast.Goto): while node.name in forwards: node.name = forwards[node.name] elif isinstance(node, c_ast.Label) and node.name in forwards: stmts = common.GetStatementList(parent) assert stmts, parent stmts.remove(node)
def try_handle_block(block: ca.Node, where: Optional[Loc]) -> None: if not isinstance(block, ca.Compound) or not block.block_items: return pragma = block.block_items[0] if not isinstance(pragma, ca.Pragma) or pragma.string != wanted_pragma: return args: List[Statement] = block.block_items[1:] stmts = perm.eval_statement_ast(args, seed) if where: where[0][where[1]:where[1] + 1] = stmts else: block.block_items = stmts raise _Done
def dfs(u: c_ast.Node): nonlocal node_parent for (v_name, v) in u.children(): node_parent[v] = u dfs(v)
"visit_dict", "JSONNode", "NODE_TYPE_ATTR", "CHILDREN_ATTR", "TOKEN_POS_ATTR", ] T = TypeVar('T') K = TypeVar('K') MaybeList = Union[T, List[T]] JSONNode = Dict[str, Any] RE_CHILD_ARRAY = re.compile(r'(.*)\[(.*)\]') RE_INTERNAL_ATTR = re.compile('__.*__') AVAILABLE_NODES: Dict[str, Type[ASTNode]] = {klass.__name__: klass for klass in ASTNode.__subclasses__()} NODE_TYPE_ATTR = "_t" CHILDREN_ATTR = "_c" TOKEN_POS_ATTR = "_p" @functools.lru_cache() def child_attrs_of(klass: Type[ASTNode]): r"""Given a Node class, get a set of child attrs. Memoized to avoid highly repetitive string manipulation """ non_child_attrs = set(klass.attr_names) all_attrs = set([i for i in klass.__slots__ if not RE_INTERNAL_ATTR.match(i)]) all_attrs -= {"coord"} return all_attrs - non_child_attrs
def create_havoc_assignment(self, declaration: c_ast.Decl, parent: c_ast.Node = None): """ Creates a havoc assignment block for the variable declared in the given declaration. :param declaration: The declaration of the variable to havoc. :return: First entry: A set of strings containing the employed non-deterministic assignment SV comp function names, e.g. "__VERIFIER_nondet_int". Second entry: A block containing all havoc assignments for that variable. :parent: A parent node for aggregates to allow for access of the children. Either c_ast.StructRef, c_ast.ArrayRef or c_ast.UnaryOp with op="*". :rtype: set of str, c_ast.Compound """ # Here be dragons. Most likely contains some bugs. # TODO Should be tested thoroughly. body_items = [] svcomp_havoc_functions = set() # First, registers itself into the parent struct, if there is one. if type(parent) == c_ast.StructRef and parent.field is None: parent.field = c_ast.ID(declaration.name) # Checks for five main cases: We have a basic identifier, a struct, a union, an array or a pointer. # If a compound type is encountered, this function is called recursively on the child declaration(s). if type(declaration.type) == c_ast.TypeDecl: # CASE STRUCT if type(declaration.type.type) == c_ast.Struct: # Iterates over every struct member and creates a havoc block for this. Useful for nested structs. if declaration.type.type.decls: for member in declaration.type.type.decls: if parent is None: new_parent = c_ast.StructRef( c_ast.ID(declaration.name), ".", None) else: new_parent = c_ast.StructRef(parent, ".", None) rec_svcomp_havoc_funcs, rec_havoc_block = self.create_havoc_assignment( member, new_parent) body_items.append(rec_havoc_block) svcomp_havoc_functions = svcomp_havoc_functions.union( rec_svcomp_havoc_funcs) # CASE UNION elif type(declaration.type.type) == c_ast.Union and len( declaration.type.type.decls) > 0: # For a union, we just havoc the very first member. if parent is None: new_parent = c_ast.StructRef(c_ast.ID(declaration.name), ".", None) else: new_parent = c_ast.StructRef(parent, ".", None) rec_svcomp_havoc_funcs, rec_havoc_block = self.create_havoc_assignment( declaration.type.type.decls[0], new_parent) body_items.append(rec_havoc_block) svcomp_havoc_functions = svcomp_havoc_functions.union( rec_svcomp_havoc_funcs) # CASE BASIC IDENTIFIER elif type(declaration.type.type) == c_ast.IdentifierType: # Base case of the recursion. havoc_function = VERIFIER_NONDET_FUNCTION_NAME + self.get_svcomp_type( declaration.type.type.names) rvalue = self.create_function_call(havoc_function) if parent is None: lvalue = c_ast.ID(declaration.name) else: lvalue = parent havoc_variable = c_ast.Assignment("=", lvalue, rvalue) body_items.append(havoc_variable) svcomp_havoc_functions.add(havoc_function) # CASE ARRAY elif type(declaration.type) == c_ast.ArrayDecl: modified_declaration = copy.deepcopy(declaration) modified_declaration.type = modified_declaration.type.type if type(declaration.type.dim ) == c_ast.Constant and declaration.type.dim.type == "int": # Iterates over every member of the array (Thus, the size has to be constant). for i in range(int(declaration.type.dim.value)): if parent is None: new_parent = c_ast.ID(declaration.name) else: new_parent = parent rec_svcomp_havoc_funcs, rec_havoc_block = self.create_havoc_assignment( modified_declaration, c_ast.ArrayRef(new_parent, c_ast.Constant("int", str(i)))) body_items.append(rec_havoc_block) svcomp_havoc_functions = svcomp_havoc_functions.union( rec_svcomp_havoc_funcs) else: sys.stderr.write( "WARNING: Non-constant array encountered!") # TODO # CASE POINTER elif type(declaration.type) == c_ast.PtrDecl: if type(declaration.type.type) == c_ast.TypeDecl and \ type(declaration.type.type.type) == c_ast.IdentifierType and \ ("const" not in declaration.type.quals or "void" in declaration.type.type.type.names): # Base case of the recursion. Only entered if we can not dereference the pointer due to either an # unknown type (void pointer) or a constant memory location behind the pointer. havoc_function = VERIFIER_NONDET_FUNCTION_NAME + "pointer" svcomp_havoc_functions.add(havoc_function) rvalue = self.create_function_call(havoc_function) if parent is None: lvalue = c_ast.ID(declaration.name) else: lvalue = parent havoc_variable = c_ast.Assignment("=", lvalue, rvalue) body_items.append(havoc_variable) else: # We can dereference the pointer: Does so and creates a havoc statement for the type behind the pointer. modified_declaration = copy.deepcopy(declaration) modified_declaration.type = modified_declaration.type.type if parent is None: new_parent = c_ast.ID(declaration.name) else: new_parent = parent rec_svcomp_havoc_funcs, rec_havoc_block = self.create_havoc_assignment( modified_declaration, c_ast.UnaryOp("*", new_parent)) body_items.append(rec_havoc_block) svcomp_havoc_functions = svcomp_havoc_functions.union( rec_svcomp_havoc_funcs) # Bundles the havoc assignments into one compound statement. if len(body_items) == 0: sys.stderr.write( "WARNING: Could not havoc variable of declaration " + GnuCGenerator().visit(declaration) + "\n") return svcomp_havoc_functions, c_ast.Compound(body_items)
def CanonicalizeBaseTypes(node: c_ast.Node): if isinstance(node, c_ast.IdentifierType): node.names = common.CanonicalizeIdentifierType(node.names) for c in node: CanonicalizeBaseTypes(c)
def RemoveVoidParam(node: c_ast.Node): if isinstance(node, c_ast.ParamList) and len(node.params) == 1: if IsVoidArg(node.params[0]): node.params = [] for c in node: RemoveVoidParam(c)