Best Python code snippet using slash
cleanup_check.py
Source:cleanup_check.py
1# Copyright 2013-2018 Free Software Foundation, Inc.2#3# This is free software: you can redistribute it and/or modify it4# under the terms of the GNU General Public License as published by5# the Free Software Foundation, either version 3 of the License, or6# (at your option) any later version.7#8# This program is distributed in the hope that it will be useful, but9# WITHOUT ANY WARRANTY; without even the implied warranty of10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11# General Public License for more details.12#13# You should have received a copy of the GNU General Public License14# along with this program. If not, see15# <http://www.gnu.org/licenses/>.16import gcc17import gccutils18import sys19want_raii_info = False20logging = False21show_cfg = False22def log(msg, indent=0):23 global logging24 if logging:25 sys.stderr.write('%s%s\n' % (' ' * indent, msg))26 sys.stderr.flush()27def is_cleanup_type(return_type):28 if not isinstance(return_type, gcc.PointerType):29 return False30 if not isinstance(return_type.dereference, gcc.RecordType):31 return False32 if str(return_type.dereference.name) == 'cleanup':33 return True34 return False35def is_constructor(decl):36 "Return True if the function DECL is a cleanup constructor; False otherwise"37 return is_cleanup_type(decl.type.type) and (not decl.name or str(decl.name) != 'make_final_cleanup')38destructor_names = set(['do_cleanups', 'discard_cleanups'])39def is_destructor(decl):40 return decl.name in destructor_names41# This list is just much too long... we should probably have an42# attribute instead.43special_names = set(['do_final_cleanups', 'discard_final_cleanups',44 'save_cleanups', 'save_final_cleanups',45 'restore_cleanups', 'restore_final_cleanups',46 'exceptions_state_mc_init',47 'make_my_cleanup2', 'make_final_cleanup', 'all_cleanups',48 'save_my_cleanups', 'quit_target'])49def needs_special_treatment(decl):50 return decl.name in special_names51# Sometimes we need a new placeholder object that isn't the same as52# anything else.53class Dummy(object):54 def __init__(self, location):55 self.location = location56# A wrapper for a cleanup which has been assigned to a variable.57# This holds the variable and the location.58class Cleanup(object):59 def __init__(self, var, location):60 self.var = var61 self.location = location62# A class representing a master cleanup. This holds a stack of63# cleanup objects and supports a merging operation.64class MasterCleanup(object):65 # Create a new MasterCleanup object. OTHER, if given, is a66 # MasterCleanup object to copy.67 def __init__(self, other = None):68 # 'cleanups' is a list of cleanups. Each element is either a69 # Dummy, for an anonymous cleanup, or a Cleanup, for a cleanup70 # which was assigned to a variable.71 if other is None:72 self.cleanups = []73 self.aliases = {}74 else:75 self.cleanups = other.cleanups[:]76 self.aliases = dict(other.aliases)77 def compare_vars(self, definition, argument):78 if definition == argument:79 return True80 if argument in self.aliases:81 argument = self.aliases[argument]82 if definition in self.aliases:83 definition = self.aliases[definition]84 return definition == argument85 def note_assignment(self, lhs, rhs):86 log('noting assignment %s = %s' % (lhs, rhs), 4)87 self.aliases[lhs] = rhs88 # Merge with another MasterCleanup.89 # Returns True if this resulted in a change to our state.90 def merge(self, other):91 # We do explicit iteration like this so we can easily92 # update the list after the loop.93 counter = -194 found_named = False95 for counter in range(len(self.cleanups) - 1, -1, -1):96 var = self.cleanups[counter]97 log('merge checking %s' % var, 4)98 # Only interested in named cleanups.99 if isinstance(var, Dummy):100 log('=> merge dummy', 5)101 continue102 # Now see if VAR is found in OTHER.103 if other._find_var(var.var) >= 0:104 log ('=> merge found', 5)105 break106 log('=>merge not found', 5)107 found_named = True108 if found_named and counter < len(self.cleanups) - 1:109 log ('merging to %d' % counter, 4)110 if counter < 0:111 self.cleanups = []112 else:113 self.cleanups = self.cleanups[0:counter]114 return True115 # If SELF is empty but OTHER has some cleanups, then consider116 # that a change as well.117 if len(self.cleanups) == 0 and len(other.cleanups) > 0:118 log('merging non-empty other', 4)119 self.cleanups = other.cleanups[:]120 return True121 return False122 # Push a new constructor onto our stack. LHS is the123 # left-hand-side of the GimpleCall statement. It may be None,124 # meaning that this constructor's value wasn't used.125 def push(self, location, lhs):126 if lhs is None:127 obj = Dummy(location)128 else:129 obj = Cleanup(lhs, location)130 log('pushing %s' % lhs, 4)131 idx = self._find_var(lhs)132 if idx >= 0:133 gcc.permerror(location, 'reassigning to known cleanup')134 gcc.inform(self.cleanups[idx].location,135 'previous assignment is here')136 self.cleanups.append(obj)137 # A helper for merge and pop that finds BACK_TO in self.cleanups,138 # and returns the index, or -1 if not found.139 def _find_var(self, back_to):140 for i in range(len(self.cleanups) - 1, -1, -1):141 if isinstance(self.cleanups[i], Dummy):142 continue143 if self.compare_vars(self.cleanups[i].var, back_to):144 return i145 return -1146 # Pop constructors until we find one matching BACK_TO.147 # This is invoked when we see a do_cleanups call.148 def pop(self, location, back_to):149 log('pop:', 4)150 i = self._find_var(back_to)151 if i >= 0:152 self.cleanups = self.cleanups[0:i]153 else:154 gcc.permerror(location, 'destructor call with unknown argument')155 # Check whether ARG is the current master cleanup. Return True if156 # all is well.157 def verify(self, location, arg):158 log('verify %s' % arg, 4)159 return (len(self.cleanups) > 0160 and not isinstance(self.cleanups[0], Dummy)161 and self.compare_vars(self.cleanups[0].var, arg))162 # Check whether SELF is empty.163 def isempty(self):164 log('isempty: len = %d' % len(self.cleanups), 4)165 return len(self.cleanups) == 0166 # Emit informational warnings about the cleanup stack.167 def inform(self):168 for item in reversed(self.cleanups):169 gcc.inform(item.location, 'leaked cleanup')170class CleanupChecker:171 def __init__(self, fun):172 self.fun = fun173 self.seen_edges = set()174 self.bad_returns = set()175 # This maps BB indices to a list of master cleanups for the176 # BB.177 self.master_cleanups = {}178 # Pick a reasonable location for the basic block BB.179 def guess_bb_location(self, bb):180 if isinstance(bb.gimple, list):181 for stmt in bb.gimple:182 if stmt.loc:183 return stmt.loc184 return self.fun.end185 # Compute the master cleanup list for BB.186 # Modifies MASTER_CLEANUP in place.187 def compute_master(self, bb, bb_from, master_cleanup):188 if not isinstance(bb.gimple, list):189 return190 curloc = self.fun.end191 for stmt in bb.gimple:192 if stmt.loc:193 curloc = stmt.loc194 if isinstance(stmt, gcc.GimpleCall) and stmt.fndecl:195 if is_constructor(stmt.fndecl):196 log('saw constructor %s in bb=%d' % (str(stmt.fndecl), bb.index), 2)197 self.cleanup_aware = True198 master_cleanup.push(curloc, stmt.lhs)199 elif is_destructor(stmt.fndecl):200 if str(stmt.fndecl.name) != 'do_cleanups':201 self.only_do_cleanups_seen = False202 log('saw destructor %s in bb=%d, bb_from=%d, argument=%s'203 % (str(stmt.fndecl.name), bb.index, bb_from, str(stmt.args[0])),204 2)205 master_cleanup.pop(curloc, stmt.args[0])206 elif needs_special_treatment(stmt.fndecl):207 pass208 # gcc.permerror(curloc, 'function needs special treatment')209 elif isinstance(stmt, gcc.GimpleAssign):210 if isinstance(stmt.lhs, gcc.VarDecl) and isinstance(stmt.rhs[0], gcc.VarDecl):211 master_cleanup.note_assignment(stmt.lhs, stmt.rhs[0])212 elif isinstance(stmt, gcc.GimpleReturn):213 if self.is_constructor:214 if not master_cleanup.verify(curloc, stmt.retval):215 gcc.permerror(curloc,216 'constructor does not return master cleanup')217 elif not self.is_special_constructor:218 if not master_cleanup.isempty():219 if curloc not in self.bad_returns:220 gcc.permerror(curloc, 'cleanup stack is not empty at return')221 self.bad_returns.add(curloc)222 master_cleanup.inform()223 # Traverse a basic block, updating the master cleanup information224 # and propagating to other blocks.225 def traverse_bbs(self, edge, bb, bb_from, entry_master):226 log('traverse_bbs %d from %d' % (bb.index, bb_from), 1)227 # Propagate the entry MasterCleanup though this block.228 master_cleanup = MasterCleanup(entry_master)229 self.compute_master(bb, bb_from, master_cleanup)230 modified = False231 if bb.index in self.master_cleanups:232 # Merge the newly-computed MasterCleanup into the one we233 # have already computed. If this resulted in a234 # significant change, then we need to re-propagate.235 modified = self.master_cleanups[bb.index].merge(master_cleanup)236 else:237 self.master_cleanups[bb.index] = master_cleanup238 modified = True239 # EDGE is None for the entry BB.240 if edge is not None:241 # If merging cleanups caused a change, check to see if we242 # have a bad loop.243 if edge in self.seen_edges:244 # This error doesn't really help.245 # if modified:246 # gcc.permerror(self.guess_bb_location(bb),247 # 'invalid cleanup use in loop')248 return249 self.seen_edges.add(edge)250 if not modified:251 return252 # Now propagate to successor nodes.253 for edge in bb.succs:254 self.traverse_bbs(edge, edge.dest, bb.index, master_cleanup)255 def check_cleanups(self):256 if not self.fun.cfg or not self.fun.decl:257 return 'ignored'258 if is_destructor(self.fun.decl):259 return 'destructor'260 if needs_special_treatment(self.fun.decl):261 return 'special'262 self.is_constructor = is_constructor(self.fun.decl)263 self.is_special_constructor = not self.is_constructor and str(self.fun.decl.name).find('with_cleanup') > -1264 # Yuck.265 if str(self.fun.decl.name) == 'gdb_xml_create_parser_and_cleanup_1':266 self.is_special_constructor = True267 if self.is_special_constructor:268 gcc.inform(self.fun.start, 'function %s is a special constructor' % (self.fun.decl.name))269 # If we only see do_cleanups calls, and this function is not270 # itself a constructor, then we can convert it easily to RAII.271 self.only_do_cleanups_seen = not self.is_constructor272 # If we ever call a constructor, then we are "cleanup-aware".273 self.cleanup_aware = False274 entry_bb = self.fun.cfg.entry275 master_cleanup = MasterCleanup()276 self.traverse_bbs(None, entry_bb, -1, master_cleanup)277 if want_raii_info and self.only_do_cleanups_seen and self.cleanup_aware:278 gcc.inform(self.fun.decl.location,279 'function %s could be converted to RAII' % (self.fun.decl.name))280 if self.is_constructor:281 return 'constructor'282 return 'OK'283class CheckerPass(gcc.GimplePass):284 def execute(self, fun):285 if fun.decl:286 log("Starting " + fun.decl.name)287 if show_cfg:288 dot = gccutils.cfg_to_dot(fun.cfg, fun.decl.name)289 gccutils.invoke_dot(dot, name=fun.decl.name)290 checker = CleanupChecker(fun)291 what = checker.check_cleanups()292 if fun.decl:293 log(fun.decl.name + ': ' + what, 2)294ps = CheckerPass(name = 'check-cleanups')295# We need the cfg, but we want a relatively high-level Gimple....
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!