#!/usr/bin/env python """Feed this script the concatenated output of gcobject.js from a bunch of files and it will produce a patch causing the relevant objects to inherit from XPCOMGCFinalizedObject.""" import sys import re lineSplitter = re.compile(r'(?<=\n)') def writePatchHunk(oldText, newText, file, lineno): """Write a hunk in unified diff format (with no context)""" oldLines = oldText.splitlines(True) newLines = newText.splitlines(True) print "--- %s" % file print "+++ %s" % file print "@@ -%(lineno)s,%(oldcount)s +%(lineno)s,%(newcount)s @@" % \ {'lineno': lineno, 'oldcount': len(oldLines), 'newcount': len(newLines) } for line in oldLines: print "-%s" % line, for line in newLines: print "+%s" % line, replacer = re.compile(r""" (?P\s*class\s+(?:NS_COM|NS_COM_GLUE|NS_GFX)?\s*(?P\w+)\s*:\s*) (?P(?:(?:public|private|protected)\s*\w+\s*,\s*)* (?:public|private|protected)?\s*\w+\s*,?\s*)$ """, re.X) baseSplitter = re.compile(r'(\s*,\s*)') visFinder = re.compile(r'private|public|protected') commentFinder = re.compile(r'#if|\s*//') def writePatch(classname, decl): file, lineno = decl.split(':') fd = open(file, 'r') file_lines = fd.readlines() fd.close() # CPP line numbers are 1-based startLine = lineno = int(lineno) - 1 declText = '' while True: line = file_lines[lineno] if commentFinder.match(line): declExtra = '' break # Special-case stupidity if line.startswith('NS_DECL_EDITOR_COMMAND') or line.startswith('NS_DECL_COMPOSER_COMMAND'): return bracketPos = line.find('{') if bracketPos != -1: declText += line[:bracketPos] # Save the rest of the line to create a useful patch below declExtra = line[bracketPos:] break else: declText += line lineno += 1 m = replacer.match(declText) if m is None: raise "File %s line %s didn't match:\n* %s" % (file, lineno, declText) decl, foundClassname, bases = \ m.group('decl', 'classname', 'bases') if classname != foundClassname: raise "At %s found class %s expected %s" % (decl, foundClassname, classname) # this will split into a list base1, split1, base2, split2... baseList = baseSplitter.split(bases) # if the visibility of the first base was not specified, # specify it as "private" which is the default if not visFinder.match(baseList[0]): baseList[0] = 'private ' + baseList[0] if len(baseList) == 1: newSplit = ', ' else: newSplit = baseList[1] baseList[0:0] = ['public XPCOMGCFinalizedObject', newSplit] writePatchHunk(declText + declExtra, decl + ''.join(baseList) + declExtra, file, startLine) # First, we read all the lines and parse them into a class map. Classes # which inherit from other classes are removed before processing. classes = {} def set_class(name, decl, bases): if name in classes: if classes[name]['decl'] != decl: print >>sys.stderr, "Class '%s' has different declarations:\n* %s\n* %s" % \ (name, decl, classes[name]['decl']) return if classes[name]['bases'] != bases: raise "Class '%s' has different bases:\n* %s\n* %s" % \ (name, bases, classes[name]['bases']) else: classes[name] = {'decl': decl, 'bases': bases} mountRemover = re.compile(r'^/mnt/hgfs') for line in sys.stdin: c, decl, baselist = line.split() bases = baselist.split(',') decl = mountRemover.sub('', decl, 1) set_class(c, decl, bases) for c in classes: process = True for b in classes[c]['bases']: if b in classes: print >>sys.stderr, "Not processing %s: inherits from %s" % (c, b) process = False break if process: writePatch(c, classes[c]['decl'])