| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """ |
| 7 Systematically search the Chrome codebase for dialog invocations. |
| 8 """ |
| 9 |
| 10 import subprocess |
| 11 import sys |
| 12 import re |
| 13 import mmap |
| 14 import os |
| 15 import csv |
| 16 |
| 17 CLASS_UNKNOWN = '<class unknown>' |
| 18 CLASS_UNKNOWN = '?' |
| 19 VERBOSE = False |
| 20 |
| 21 BLACKLIST = { |
| 22 'ViewEventTestBase', # Has lots of uninteresting descendants in test files. |
| 23 'TestDelegate', # Causes infinite recursion. |
| 24 'UI' # Used in mash/webtest/webtest.cc - confuses regex. |
| 25 } |
| 26 |
| 27 def InheritsRe(klass): |
| 28 return r'public.*[: ]{}[, ]'.format(klass) |
| 29 |
| 30 def ChildRe(klass): |
| 31 return r'class ([\w :]+)\s*: [^{]*' + klass + r'[^{]*[{]' |
| 32 |
| 33 def FindFilesInheritingFromClass(klass): |
| 34 process = subprocess.Popen(['git', 'grep', '-l', InheritsRe(klass), '--', |
| 35 '*.h', '*.mm', '*.cc'], |
| 36 stdout=subprocess.PIPE) |
| 37 return process.communicate()[0].splitlines() |
| 38 |
| 39 def ToLine(lines, offset): |
| 40 line = 0 |
| 41 pos = 0 |
| 42 while pos < offset[0]: |
| 43 pos += len(lines[line]) |
| 44 line += 1 |
| 45 return line |
| 46 |
| 47 def FindClassesInFilesInheritingFromClass(files, klass): |
| 48 children = [] |
| 49 child_re = re.compile(ChildRe(klass), re.MULTILINE) |
| 50 for filename in files: |
| 51 with open(filename, 'r+') as f: |
| 52 lines = f.readlines() |
| 53 text = " ".join([l[:-1] for l in lines]) |
| 54 found = child_re.finditer(text) |
| 55 before = len(children) |
| 56 for child in found: |
| 57 child_klass = child.group(1).split()[-1] |
| 58 children.append((child_klass, filename, ToLine(lines, child.span()), |
| 59 re.sub(' +', ' ', child.group(0)), klass)) |
| 60 if len(children) == before: |
| 61 sys.stderr.write( |
| 62 "ERROR: Couldn't find a child of {} in {}\n".format(klass, |
| 63 filename)) |
| 64 return children |
| 65 |
| 66 def HuntForDecendants(result, parent, klasses, depth=''): |
| 67 for klass in klasses: |
| 68 if klass[0] in BLACKLIST: |
| 69 sys.stderr.write('{}Skipping (blacklisted): {}\n'.format(depth, klass[0])) |
| 70 continue |
| 71 |
| 72 result.append(klass) |
| 73 sys.stderr.write('{}{}'.format(depth, klass[0])) |
| 74 files = FindFilesInheritingFromClass(klass[0]) |
| 75 children = FindClassesInFilesInheritingFromClass(files, klass[0]) |
| 76 sys.stderr.write( ' has {} children\n'.format(len(children))) |
| 77 HuntForDecendants(result, klass, children, depth + ' ') |
| 78 |
| 79 def DefaultHunt(): |
| 80 seed = 'WidgetDelegate' |
| 81 #$seed = 'DialogDelegate' |
| 82 #seed = 'DialogDelegateView' |
| 83 #seed = 'LocationBarBubbleDelegateView' |
| 84 result = [] |
| 85 HuntForDecendants(result, 'Seed', [(seed, '', 0, '', 'Seed')]) |
| 86 return result |
| 87 |
| 88 def ToUrl(filename, klass, line): |
| 89 return 'https://cs.chromium.org/chromium/src/{}?q={}&l={}'.format( |
| 90 filename, klass, line) |
| 91 |
| 92 def Hyperlink(klass, url): |
| 93 return '=HYPERLINK("{}", "{}")'.format(url, klass) |
| 94 |
| 95 def Process(data): |
| 96 cells = eval("".join(open('tools/ui/cells.cache').readlines())) |
| 97 newcells = [] |
| 98 cellmap = {} |
| 99 for row in range(len(cells)): |
| 100 ux_name = cells[row][1] |
| 101 klass = cells[row][0] |
| 102 if klass == '' and ux_name != '': |
| 103 newcells.append([CLASS_UNKNOWN, ux_name]) |
| 104 print "Unmatched: {} {}".format(row, ux_name) |
| 105 else: |
| 106 cellmap.setdefault(klass, []).append(row) |
| 107 newcells.append([klass, ux_name]) |
| 108 print "" |
| 109 ignored = [] |
| 110 parents = {} |
| 111 missing = {} |
| 112 dupe_check = {} |
| 113 for (klass, filename, line, match, parent) in data: |
| 114 # Ignore test files |
| 115 if filename.find('test.') > 0: |
| 116 ignored.append((klass, filename)) |
| 117 continue |
| 118 |
| 119 dupe_check.setdefault(klass, []).append(filename) |
| 120 if len(dupe_check[klass]) > 1: |
| 121 print "WARNING: {} found in {}".format(klass, dupe_check[klass]) |
| 122 |
| 123 # Track classes that have children and ignore them. |
| 124 parents.setdefault(parent, [('f', 'c', 0, 'p'), []])[1].append(klass) |
| 125 if parent in missing: |
| 126 parents[parent][0] = missing[parent] |
| 127 del missing[parent] |
| 128 |
| 129 url = ToUrl(filename, klass, line) |
| 130 if klass in cellmap: |
| 131 for row in cellmap[klass]: |
| 132 newcells[row][0] = Hyperlink(klass, url) |
| 133 newcells[row].append(filename) |
| 134 newcells[row].append(parent) |
| 135 if VERBOSE: |
| 136 print 'Matched: {}'.format(klass) |
| 137 continue |
| 138 |
| 139 missing[klass] = (filename, klass, line, parent) |
| 140 |
| 141 sys.stderr.write('\nIgnored (Tests):') |
| 142 for ignore in ignored: |
| 143 sys.stderr.write(' {}({})'.format(ignore[0], os.path.basename(ignore[1]))) |
| 144 sys.stderr.write('\n') |
| 145 |
| 146 sys.stderr.write('\nIgnored (Parents):') |
| 147 for pk in parents: |
| 148 parent = parents[pk] |
| 149 filename, klass, line, p = parent[0] |
| 150 children = " ".join(parent[1]) |
| 151 basename = os.path.basename(filename) |
| 152 sys.stderr.write('\n\t {}({}): {}'.format(pk, basename, children)) |
| 153 sys.stderr.write('\n') |
| 154 |
| 155 # Sort by filename. |
| 156 missing_list = missing.values() |
| 157 missing_list.sort() |
| 158 |
| 159 # Write out CSV |
| 160 with open('tools/ui/sheet.csv', 'wb') as csvfile: |
| 161 writer = csv.writer(csvfile, dialect='excel-tab') |
| 162 for row in range(len(cells)): |
| 163 if row == 0: |
| 164 writer.writerow(['DialogClass', 'Description', 'File', 'ParentClass', |
| 165 'Children']) |
| 166 continue |
| 167 writer.writerow(newcells[row]) |
| 168 |
| 169 for m in missing_list: |
| 170 filename, klass, line, parent = m |
| 171 url = ToUrl(filename, klass, line) |
| 172 missing_text = 'What is this?' |
| 173 writer.writerow([Hyperlink(klass, url), missing_text, filename, parent]) |
| 174 print 'Missing: {:40} from {}'.format(klass, url) |
| 175 |
| 176 for pk in parents: |
| 177 filename, klass, line, parent = parents[pk][0] |
| 178 url = ToUrl(filename, klass, line) |
| 179 num_children = len(parents[pk][1]) |
| 180 parent_text = 'Has {} Child{}'.format(num_children, |
| 181 num_children != 1 and "ren" or "") |
| 182 writer.writerow([Hyperlink(klass, url), parent_text, filename, parent, |
| 183 ",".join(parents[pk][1])]) |
| 184 for ignore in ignored: |
| 185 writer.writerow([ignore[0], '<test>', ignore[1]]) |
| 186 |
| 187 def main(): |
| 188 try: |
| 189 data = eval(open('tools/ui/dialog_audit.cache').readline()) |
| 190 Process(data) |
| 191 except IOError as e: |
| 192 data = DefaultHunt() |
| 193 print data |
| 194 sys.stderr.write(""" |
| 195 Try `tools/ui/dialog_audit.py > tools/ui/dialog_audit.cache.new && \ |
| 196 mv tools/ui/dialog_audit.cache.new tools/ui/dialog_audit.cache` |
| 197 """) |
| 198 |
| 199 if __name__ == '__main__': |
| 200 sys.exit(main()) |
| OLD | NEW |