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!!
