Home | History | Annotate | Download | only in generators
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 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 IDLRelease for PPAPI
      8 
      9 This file defines the behavior of the AST namespace which allows for resolving
     10 a symbol as one or more AST nodes given a Release or range of Releases.
     11 """
     12 
     13 import sys
     14 
     15 from idl_log import ErrOut, InfoOut, WarnOut
     16 from idl_option import GetOption, Option, ParseOptions
     17 
     18 Option('release_debug', 'Debug Release data')
     19 Option('wgap', 'Ignore Release gap warning')
     20 
     21 
     22 #
     23 # Module level functions and data used for testing.
     24 #
     25 error = None
     26 warning = None
     27 def ReportReleaseError(msg):
     28   global error
     29   error = msg
     30 
     31 def ReportReleaseWarning(msg):
     32   global warning
     33   warning = msg
     34 
     35 def ReportClear():
     36   global error, warning
     37   error = None
     38   warning = None
     39 
     40 #
     41 # IDLRelease
     42 #
     43 # IDLRelease is an object which stores the association of a given symbol
     44 # name, with an AST node for a range of Releases for that object.
     45 #
     46 # A vmin value of None indicates that the object begins at the earliest
     47 # available Release number.  The value of vmin is always inclusive.
     48 
     49 # A vmax value of None indicates that the object is never deprecated, so
     50 # it exists until it is overloaded or until the latest available Release.
     51 # The value of vmax is always exclusive, representing the first Release
     52 # on which the object is no longer valid.
     53 class IDLRelease(object):
     54   def __init__(self, rmin, rmax):
     55     self.rmin = rmin
     56     self.rmax = rmax
     57 
     58   def __str__(self):
     59     if not self.rmin:
     60       rmin = '0'
     61     else:
     62       rmin = str(self.rmin)
     63     if not self.rmax:
     64       rmax = '+oo'
     65     else:
     66       rmax = str(self.rmax)
     67     return '[%s,%s)' % (rmin, rmax)
     68 
     69   def SetReleaseRange(self, rmin, rmax):
     70     self.rmin = rmin
     71     self.rmax = rmax
     72 
     73   # True, if Release falls within the interval [self.vmin, self.vmax)
     74   def IsRelease(self, release):
     75     if self.rmax and self.rmax <= release:
     76       return False
     77     if self.rmin and self.rmin > release:
     78       return False
     79     if GetOption('release_debug'):
     80       InfoOut.Log('%f is in %s' % (release, self))
     81     return True
     82 
     83   # True, if Release falls within the interval [self.vmin, self.vmax)
     84   def InReleases(self, releases):
     85     if not releases: return False
     86 
     87     # Check last release first, since InRange does not match last item
     88     if self.IsRelease(releases[-1]): return True
     89     if len(releases) > 1:
     90       return self.InRange(releases[0], releases[-1])
     91     return False
     92 
     93   # True, if interval [vmin, vmax) overlaps interval [self.vmin, self.vmax)
     94   def InRange(self, rmin, rmax):
     95     assert (rmin == None) or rmin < rmax
     96 
     97     # An min of None always passes a min bound test
     98     # An max of None always passes a max bound test
     99     if rmin is not None and self.rmax is not None:
    100       if self.rmax <= rmin:
    101         return False
    102     if rmax is not None and self.rmin is not None:
    103       if self.rmin >= rmax:
    104         return False
    105 
    106     if GetOption('release_debug'):
    107       InfoOut.Log('%f to %f is in %s' % (rmin, rmax, self))
    108     return True
    109 
    110   def GetMinMax(self, releases = None):
    111     if not releases:
    112       return self.rmin, self.rmax
    113 
    114     if not self.rmin:
    115       rmin = releases[0]
    116     else:
    117       rmin = str(self.rmin)
    118     if not self.rmax:
    119       rmax = releases[-1]
    120     else:
    121       rmax = str(self.rmax)
    122     return (rmin, rmax)
    123 
    124   def SetMin(self, release):
    125     assert not self.rmin
    126     self.rmin = release
    127 
    128   def Error(self, msg):
    129     ReportReleaseError(msg)
    130 
    131   def Warn(self, msg):
    132     ReportReleaseWarning(msg)
    133 
    134 
    135 #
    136 # IDLReleaseList
    137 #
    138 # IDLReleaseList is a list based container for holding IDLRelease
    139 # objects in order.  The IDLReleaseList can be added to, and searched by
    140 # range.  Objects are stored in order, and must be added in order.
    141 #
    142 class IDLReleaseList(object):
    143   def __init__(self):
    144     self._nodes = []
    145 
    146   def GetReleases(self):
    147     return self._nodes
    148 
    149   def FindRelease(self, release):
    150     for node in self._nodes:
    151       if node.IsRelease(release):
    152         return node
    153     return None
    154 
    155   def FindRange(self, rmin, rmax):
    156     assert (rmin == None) or rmin != rmax
    157 
    158     out = []
    159     for node in self._nodes:
    160       if node.InRange(rmin, rmax):
    161         out.append(node)
    162     return out
    163 
    164   def AddNode(self, node):
    165     if GetOption('release_debug'):
    166       InfoOut.Log('\nAdding %s %s' % (node.Location(), node))
    167     last = None
    168 
    169     # Check current releases in that namespace
    170     for cver in self._nodes:
    171       if GetOption('release_debug'): InfoOut.Log('  Checking %s' % cver)
    172 
    173       # We should only be missing a 'release' tag for the first item.
    174       if not node.rmin:
    175         node.Error('Missing release on overload of previous %s.' %
    176                    cver.Location())
    177         return False
    178 
    179       # If the node has no max, then set it to this one
    180       if not cver.rmax:
    181         cver.rmax = node.rmin
    182         if GetOption('release_debug'): InfoOut.Log('  Update %s' % cver)
    183 
    184       # if the max and min overlap, than's an error
    185       if cver.rmax > node.rmin:
    186         if node.rmax and cver.rmin >= node.rmax:
    187           node.Error('Declarations out of order.')
    188         else:
    189           node.Error('Overlap in releases: %s vs %s when adding %s' %
    190                      (cver.rmax, node.rmin, node))
    191         return False
    192       last = cver
    193 
    194     # Otherwise, the previous max and current min should match
    195     # unless this is the unlikely case of something being only
    196     # temporarily deprecated.
    197     if last and last.rmax != node.rmin:
    198       node.Warn('Gap in release numbers.')
    199 
    200     # If we made it here, this new node must be the 'newest'
    201     # and does not overlap with anything previously added, so
    202     # we can add it to the end of the list.
    203     if GetOption('release_debug'): InfoOut.Log('Done %s' % node)
    204     self._nodes.append(node)
    205     return True
    206 
    207 #
    208 # IDLReleaseMap
    209 #
    210 # A release map, can map from an float interface release, to a global
    211 # release string.
    212 #
    213 class IDLReleaseMap(object):
    214   def __init__(self, release_info):
    215     self.version_to_release = {}
    216     self.release_to_version = {}
    217     self.release_to_channel = {}
    218     for release, version, channel in release_info:
    219       self.version_to_release[version] = release
    220       self.release_to_version[release] = version
    221       self.release_to_channel[release] = channel
    222     self.releases = sorted(self.release_to_version.keys())
    223     self.versions = sorted(self.version_to_release.keys())
    224 
    225   def GetVersion(self, release):
    226     return self.release_to_version.get(release, None)
    227 
    228   def GetVersions(self):
    229     return self.versions
    230 
    231   def GetRelease(self, version):
    232     return self.version_to_release.get(version, None)
    233 
    234   def GetReleases(self):
    235     return self.releases
    236 
    237   def GetReleaseRange(self):
    238     return (self.releases[0], self.releases[-1])
    239 
    240   def GetVersionRange(self):
    241     return (self.versions[0], self.version[-1])
    242 
    243   def GetChannel(self, release):
    244     return self.release_to_channel.get(release, None)
    245 
    246 #
    247 # Test Code
    248 #
    249 def TestReleaseNode():
    250   FooXX = IDLRelease(None, None)
    251   Foo1X = IDLRelease('M14', None)
    252   Foo23 = IDLRelease('M15', 'M16')
    253 
    254   assert FooXX.IsRelease('M13')
    255   assert FooXX.IsRelease('M14')
    256   assert FooXX.InRange('M13', 'M13A')
    257   assert FooXX.InRange('M14','M15')
    258 
    259   assert not Foo1X.IsRelease('M13')
    260   assert Foo1X.IsRelease('M14')
    261   assert Foo1X.IsRelease('M15')
    262 
    263   assert not Foo1X.InRange('M13', 'M14')
    264   assert not Foo1X.InRange('M13A', 'M14')
    265   assert Foo1X.InRange('M14', 'M15')
    266   assert Foo1X.InRange('M15', 'M16')
    267 
    268   assert not Foo23.InRange('M13', 'M14')
    269   assert not Foo23.InRange('M13A', 'M14')
    270   assert not Foo23.InRange('M14', 'M15')
    271   assert Foo23.InRange('M15', 'M16')
    272   assert Foo23.InRange('M14', 'M15A')
    273   assert Foo23.InRange('M15B', 'M17')
    274   assert not Foo23.InRange('M16', 'M17')
    275   print "TestReleaseNode - Passed"
    276 
    277 
    278 def TestReleaseListWarning():
    279   FooXX = IDLRelease(None, None)
    280   Foo1X = IDLRelease('M14', None)
    281   Foo23 = IDLRelease('M15', 'M16')
    282   Foo45 = IDLRelease('M17', 'M18')
    283 
    284   # Add nodes out of order should fail
    285   ReportClear()
    286   releases = IDLReleaseList()
    287   assert releases.AddNode(Foo23)
    288   assert releases.AddNode(Foo45)
    289   assert warning
    290   print "TestReleaseListWarning - Passed"
    291 
    292 
    293 def TestReleaseListError():
    294   FooXX = IDLRelease(None, None)
    295   Foo1X = IDLRelease('M14', None)
    296   Foo23 = IDLRelease('M15', 'M16')
    297   Foo45 = IDLRelease('M17', 'M18')
    298 
    299   # Add nodes out of order should fail
    300   ReportClear()
    301   releases = IDLReleaseList()
    302   assert releases.AddNode(FooXX)
    303   assert releases.AddNode(Foo23)
    304   assert not releases.AddNode(Foo1X)
    305   assert error
    306   print "TestReleaseListError - Passed"
    307 
    308 
    309 def TestReleaseListOK():
    310   FooXX = IDLRelease(None, None)
    311   Foo1X = IDLRelease('M14', None)
    312   Foo23 = IDLRelease('M15', 'M16')
    313   Foo45 = IDLRelease('M17', 'M18')
    314 
    315   # Add nodes in order should work
    316   ReportClear()
    317   releases = IDLReleaseList()
    318   assert releases.AddNode(FooXX)
    319   assert releases.AddNode(Foo1X)
    320   assert releases.AddNode(Foo23)
    321   assert not error and not warning
    322   assert releases.AddNode(Foo45)
    323   assert warning
    324 
    325   assert releases.FindRelease('M13') == FooXX
    326   assert releases.FindRelease('M14') == Foo1X
    327   assert releases.FindRelease('M15') == Foo23
    328   assert releases.FindRelease('M16') == None
    329   assert releases.FindRelease('M17') == Foo45
    330   assert releases.FindRelease('M18') == None
    331 
    332   assert releases.FindRange('M13','M14') == [FooXX]
    333   assert releases.FindRange('M13','M17') == [FooXX, Foo1X, Foo23]
    334   assert releases.FindRange('M16','M17') == []
    335   assert releases.FindRange(None, None) == [FooXX, Foo1X, Foo23, Foo45]
    336 
    337   # Verify we can find the correct versions
    338   print "TestReleaseListOK - Passed"
    339 
    340 
    341 def TestReleaseMap():
    342   print "TestReleaseMap- Passed"
    343 
    344 
    345 def Main(args):
    346   TestReleaseNode()
    347   TestReleaseListWarning()
    348   TestReleaseListError()
    349   TestReleaseListOK()
    350   print "Passed"
    351   return 0
    352 
    353 
    354 if __name__ == '__main__':
    355   sys.exit(Main(sys.argv[1:]))
    356 
    357