|  | #!/usr/bin/env python | 
|  | # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """ | 
|  | IDLRelease for PPAPI | 
|  |  | 
|  | This file defines the behavior of the AST namespace which allows for resolving | 
|  | a symbol as one or more AST nodes given a Release or range of Releases. | 
|  | """ | 
|  |  | 
|  | import sys | 
|  |  | 
|  | from idl_log import ErrOut, InfoOut, WarnOut | 
|  | from idl_option import GetOption, Option, ParseOptions | 
|  |  | 
|  | Option('release_debug', 'Debug Release data') | 
|  | Option('wgap', 'Ignore Release gap warning') | 
|  |  | 
|  |  | 
|  | # | 
|  | # Module level functions and data used for testing. | 
|  | # | 
|  | error = None | 
|  | warning = None | 
|  | def ReportReleaseError(msg): | 
|  | global error | 
|  | error = msg | 
|  |  | 
|  | def ReportReleaseWarning(msg): | 
|  | global warning | 
|  | warning = msg | 
|  |  | 
|  | def ReportClear(): | 
|  | global error, warning | 
|  | error = None | 
|  | warning = None | 
|  |  | 
|  | # | 
|  | # IDLRelease | 
|  | # | 
|  | # IDLRelease is an object which stores the association of a given symbol | 
|  | # name, with an AST node for a range of Releases for that object. | 
|  | # | 
|  | # A vmin value of None indicates that the object begins at the earliest | 
|  | # available Release number.  The value of vmin is always inclusive. | 
|  |  | 
|  | # A vmax value of None indicates that the object is never deprecated, so | 
|  | # it exists until it is overloaded or until the latest available Release. | 
|  | # The value of vmax is always exclusive, representing the first Release | 
|  | # on which the object is no longer valid. | 
|  | class IDLRelease(object): | 
|  | def __init__(self, rmin, rmax): | 
|  | self.rmin = rmin | 
|  | self.rmax = rmax | 
|  |  | 
|  | def __str__(self): | 
|  | if not self.rmin: | 
|  | rmin = '0' | 
|  | else: | 
|  | rmin = str(self.rmin) | 
|  | if not self.rmax: | 
|  | rmax = '+oo' | 
|  | else: | 
|  | rmax = str(self.rmax) | 
|  | return '[%s,%s)' % (rmin, rmax) | 
|  |  | 
|  | def SetReleaseRange(self, rmin, rmax): | 
|  | self.rmin = rmin | 
|  | self.rmax = rmax | 
|  |  | 
|  | # True, if Release falls within the interval [self.vmin, self.vmax) | 
|  | def IsRelease(self, release): | 
|  | if self.rmax and self.rmax <= release: | 
|  | return False | 
|  | if self.rmin and self.rmin > release: | 
|  | return False | 
|  | if GetOption('release_debug'): | 
|  | InfoOut.Log('%f is in %s' % (release, self)) | 
|  | return True | 
|  |  | 
|  | # True, if Release falls within the interval [self.vmin, self.vmax) | 
|  | def InReleases(self, releases): | 
|  | if not releases: return False | 
|  |  | 
|  | # Check last release first, since InRange does not match last item | 
|  | if self.IsRelease(releases[-1]): return True | 
|  | if len(releases) > 1: | 
|  | return self.InRange(releases[0], releases[-1]) | 
|  | return False | 
|  |  | 
|  | # True, if interval [vmin, vmax) overlaps interval [self.vmin, self.vmax) | 
|  | def InRange(self, rmin, rmax): | 
|  | assert (rmin == None) or rmin < rmax | 
|  |  | 
|  | # An min of None always passes a min bound test | 
|  | # An max of None always passes a max bound test | 
|  | if rmin is not None and self.rmax is not None: | 
|  | if self.rmax <= rmin: | 
|  | return False | 
|  | if rmax is not None and self.rmin is not None: | 
|  | if self.rmin >= rmax: | 
|  | return False | 
|  |  | 
|  | if GetOption('release_debug'): | 
|  | InfoOut.Log('%f to %f is in %s' % (rmin, rmax, self)) | 
|  | return True | 
|  |  | 
|  | def GetMinMax(self, releases = None): | 
|  | if not releases: | 
|  | return self.rmin, self.rmax | 
|  |  | 
|  | if not self.rmin: | 
|  | rmin = releases[0] | 
|  | else: | 
|  | rmin = str(self.rmin) | 
|  | if not self.rmax: | 
|  | rmax = releases[-1] | 
|  | else: | 
|  | rmax = str(self.rmax) | 
|  | return (rmin, rmax) | 
|  |  | 
|  | def SetMin(self, release): | 
|  | assert not self.rmin | 
|  | self.rmin = release | 
|  |  | 
|  | def Error(self, msg): | 
|  | ReportReleaseError(msg) | 
|  |  | 
|  | def Warn(self, msg): | 
|  | ReportReleaseWarning(msg) | 
|  |  | 
|  |  | 
|  | # | 
|  | # IDLReleaseList | 
|  | # | 
|  | # IDLReleaseList is a list based container for holding IDLRelease | 
|  | # objects in order.  The IDLReleaseList can be added to, and searched by | 
|  | # range.  Objects are stored in order, and must be added in order. | 
|  | # | 
|  | class IDLReleaseList(object): | 
|  | def __init__(self): | 
|  | self._nodes = [] | 
|  |  | 
|  | def GetReleases(self): | 
|  | return self._nodes | 
|  |  | 
|  | def FindRelease(self, release): | 
|  | for node in self._nodes: | 
|  | if node.IsRelease(release): | 
|  | return node | 
|  | return None | 
|  |  | 
|  | def FindRange(self, rmin, rmax): | 
|  | assert (rmin == None) or rmin != rmax | 
|  |  | 
|  | out = [] | 
|  | for node in self._nodes: | 
|  | if node.InRange(rmin, rmax): | 
|  | out.append(node) | 
|  | return out | 
|  |  | 
|  | def AddNode(self, node): | 
|  | if GetOption('release_debug'): | 
|  | InfoOut.Log('\nAdding %s %s' % (node.Location(), node)) | 
|  | last = None | 
|  |  | 
|  | # Check current releases in that namespace | 
|  | for cver in self._nodes: | 
|  | if GetOption('release_debug'): InfoOut.Log('  Checking %s' % cver) | 
|  |  | 
|  | # We should only be missing a 'release' tag for the first item. | 
|  | if not node.rmin: | 
|  | node.Error('Missing release on overload of previous %s.' % | 
|  | cver.Location()) | 
|  | return False | 
|  |  | 
|  | # If the node has no max, then set it to this one | 
|  | if not cver.rmax: | 
|  | cver.rmax = node.rmin | 
|  | if GetOption('release_debug'): InfoOut.Log('  Update %s' % cver) | 
|  |  | 
|  | # if the max and min overlap, than's an error | 
|  | if cver.rmax > node.rmin: | 
|  | if node.rmax and cver.rmin >= node.rmax: | 
|  | node.Error('Declarations out of order.') | 
|  | else: | 
|  | node.Error('Overlap in releases: %s vs %s when adding %s' % | 
|  | (cver.rmax, node.rmin, node)) | 
|  | return False | 
|  | last = cver | 
|  |  | 
|  | # Otherwise, the previous max and current min should match | 
|  | # unless this is the unlikely case of something being only | 
|  | # temporarily deprecated. | 
|  | if last and last.rmax != node.rmin: | 
|  | node.Warn('Gap in release numbers.') | 
|  |  | 
|  | # If we made it here, this new node must be the 'newest' | 
|  | # and does not overlap with anything previously added, so | 
|  | # we can add it to the end of the list. | 
|  | if GetOption('release_debug'): InfoOut.Log('Done %s' % node) | 
|  | self._nodes.append(node) | 
|  | return True | 
|  |  | 
|  | # | 
|  | # IDLReleaseMap | 
|  | # | 
|  | # A release map, can map from an float interface release, to a global | 
|  | # release string. | 
|  | # | 
|  | class IDLReleaseMap(object): | 
|  | def __init__(self, release_info): | 
|  | self.version_to_release = {} | 
|  | self.release_to_version = {} | 
|  | self.release_to_channel = {} | 
|  | for release, version, channel in release_info: | 
|  | self.version_to_release[version] = release | 
|  | self.release_to_version[release] = version | 
|  | self.release_to_channel[release] = channel | 
|  | self.releases = sorted(self.release_to_version.keys()) | 
|  | self.versions = sorted(self.version_to_release.keys()) | 
|  |  | 
|  | def GetVersion(self, release): | 
|  | return self.release_to_version.get(release, None) | 
|  |  | 
|  | def GetVersions(self): | 
|  | return self.versions | 
|  |  | 
|  | def GetRelease(self, version): | 
|  | return self.version_to_release.get(version, None) | 
|  |  | 
|  | def GetReleases(self): | 
|  | return self.releases | 
|  |  | 
|  | def GetReleaseRange(self): | 
|  | return (self.releases[0], self.releases[-1]) | 
|  |  | 
|  | def GetVersionRange(self): | 
|  | return (self.versions[0], self.version[-1]) | 
|  |  | 
|  | def GetChannel(self, release): | 
|  | return self.release_to_channel.get(release, None) | 
|  |  | 
|  | # | 
|  | # Test Code | 
|  | # | 
|  | def TestReleaseNode(): | 
|  | FooXX = IDLRelease(None, None) | 
|  | Foo1X = IDLRelease('M14', None) | 
|  | Foo23 = IDLRelease('M15', 'M16') | 
|  |  | 
|  | assert FooXX.IsRelease('M13') | 
|  | assert FooXX.IsRelease('M14') | 
|  | assert FooXX.InRange('M13', 'M13A') | 
|  | assert FooXX.InRange('M14','M15') | 
|  |  | 
|  | assert not Foo1X.IsRelease('M13') | 
|  | assert Foo1X.IsRelease('M14') | 
|  | assert Foo1X.IsRelease('M15') | 
|  |  | 
|  | assert not Foo1X.InRange('M13', 'M14') | 
|  | assert not Foo1X.InRange('M13A', 'M14') | 
|  | assert Foo1X.InRange('M14', 'M15') | 
|  | assert Foo1X.InRange('M15', 'M16') | 
|  |  | 
|  | assert not Foo23.InRange('M13', 'M14') | 
|  | assert not Foo23.InRange('M13A', 'M14') | 
|  | assert not Foo23.InRange('M14', 'M15') | 
|  | assert Foo23.InRange('M15', 'M16') | 
|  | assert Foo23.InRange('M14', 'M15A') | 
|  | assert Foo23.InRange('M15B', 'M17') | 
|  | assert not Foo23.InRange('M16', 'M17') | 
|  | print "TestReleaseNode - Passed" | 
|  |  | 
|  |  | 
|  | def TestReleaseListWarning(): | 
|  | FooXX = IDLRelease(None, None) | 
|  | Foo1X = IDLRelease('M14', None) | 
|  | Foo23 = IDLRelease('M15', 'M16') | 
|  | Foo45 = IDLRelease('M17', 'M18') | 
|  |  | 
|  | # Add nodes out of order should fail | 
|  | ReportClear() | 
|  | releases = IDLReleaseList() | 
|  | assert releases.AddNode(Foo23) | 
|  | assert releases.AddNode(Foo45) | 
|  | assert warning | 
|  | print "TestReleaseListWarning - Passed" | 
|  |  | 
|  |  | 
|  | def TestReleaseListError(): | 
|  | FooXX = IDLRelease(None, None) | 
|  | Foo1X = IDLRelease('M14', None) | 
|  | Foo23 = IDLRelease('M15', 'M16') | 
|  | Foo45 = IDLRelease('M17', 'M18') | 
|  |  | 
|  | # Add nodes out of order should fail | 
|  | ReportClear() | 
|  | releases = IDLReleaseList() | 
|  | assert releases.AddNode(FooXX) | 
|  | assert releases.AddNode(Foo23) | 
|  | assert not releases.AddNode(Foo1X) | 
|  | assert error | 
|  | print "TestReleaseListError - Passed" | 
|  |  | 
|  |  | 
|  | def TestReleaseListOK(): | 
|  | FooXX = IDLRelease(None, None) | 
|  | Foo1X = IDLRelease('M14', None) | 
|  | Foo23 = IDLRelease('M15', 'M16') | 
|  | Foo45 = IDLRelease('M17', 'M18') | 
|  |  | 
|  | # Add nodes in order should work | 
|  | ReportClear() | 
|  | releases = IDLReleaseList() | 
|  | assert releases.AddNode(FooXX) | 
|  | assert releases.AddNode(Foo1X) | 
|  | assert releases.AddNode(Foo23) | 
|  | assert not error and not warning | 
|  | assert releases.AddNode(Foo45) | 
|  | assert warning | 
|  |  | 
|  | assert releases.FindRelease('M13') == FooXX | 
|  | assert releases.FindRelease('M14') == Foo1X | 
|  | assert releases.FindRelease('M15') == Foo23 | 
|  | assert releases.FindRelease('M16') == None | 
|  | assert releases.FindRelease('M17') == Foo45 | 
|  | assert releases.FindRelease('M18') == None | 
|  |  | 
|  | assert releases.FindRange('M13','M14') == [FooXX] | 
|  | assert releases.FindRange('M13','M17') == [FooXX, Foo1X, Foo23] | 
|  | assert releases.FindRange('M16','M17') == [] | 
|  | assert releases.FindRange(None, None) == [FooXX, Foo1X, Foo23, Foo45] | 
|  |  | 
|  | # Verify we can find the correct versions | 
|  | print "TestReleaseListOK - Passed" | 
|  |  | 
|  |  | 
|  | def TestReleaseMap(): | 
|  | print "TestReleaseMap- Passed" | 
|  |  | 
|  |  | 
|  | def Main(args): | 
|  | TestReleaseNode() | 
|  | TestReleaseListWarning() | 
|  | TestReleaseListError() | 
|  | TestReleaseListOK() | 
|  | print "Passed" | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(Main(sys.argv[1:])) | 
|  |  |