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     for release, version in release_info:
    218       self.version_to_release[version] = release
    219       self.release_to_version[release] = version
    220     self.releases = sorted(self.release_to_version.keys())
    221     self.versions = sorted(self.version_to_release.keys())
    222 
    223   def GetVersion(self, release):
    224     return self.release_to_version.get(release, None)
    225 
    226   def GetVersions(self):
    227     return self.versions
    228 
    229   def GetRelease(self, version):
    230     return self.version_to_release.get(version, None)
    231 
    232   def GetReleases(self):
    233     return self.releases
    234 
    235   def GetReleaseRange(self):
    236     return (self.releases[0], self.releases[-1])
    237 
    238   def GetVersionRange(self):
    239     return (self.versions[0], self.version[-1])
    240 
    241 #
    242 # Test Code
    243 #
    244 def TestReleaseNode():
    245   FooXX = IDLRelease(None, None)
    246   Foo1X = IDLRelease('M14', None)
    247   Foo23 = IDLRelease('M15', 'M16')
    248 
    249   assert FooXX.IsRelease('M13')
    250   assert FooXX.IsRelease('M14')
    251   assert FooXX.InRange('M13', 'M13A')
    252   assert FooXX.InRange('M14','M15')
    253 
    254   assert not Foo1X.IsRelease('M13')
    255   assert Foo1X.IsRelease('M14')
    256   assert Foo1X.IsRelease('M15')
    257 
    258   assert not Foo1X.InRange('M13', 'M14')
    259   assert not Foo1X.InRange('M13A', 'M14')
    260   assert Foo1X.InRange('M14', 'M15')
    261   assert Foo1X.InRange('M15', 'M16')
    262 
    263   assert not Foo23.InRange('M13', 'M14')
    264   assert not Foo23.InRange('M13A', 'M14')
    265   assert not Foo23.InRange('M14', 'M15')
    266   assert Foo23.InRange('M15', 'M16')
    267   assert Foo23.InRange('M14', 'M15A')
    268   assert Foo23.InRange('M15B', 'M17')
    269   assert not Foo23.InRange('M16', 'M17')
    270   print "TestReleaseNode - Passed"
    271 
    272 
    273 def TestReleaseListWarning():
    274   FooXX = IDLRelease(None, None)
    275   Foo1X = IDLRelease('M14', None)
    276   Foo23 = IDLRelease('M15', 'M16')
    277   Foo45 = IDLRelease('M17', 'M18')
    278 
    279   # Add nodes out of order should fail
    280   ReportClear()
    281   releases = IDLReleaseList()
    282   assert releases.AddNode(Foo23)
    283   assert releases.AddNode(Foo45)
    284   assert warning
    285   print "TestReleaseListWarning - Passed"
    286 
    287 
    288 def TestReleaseListError():
    289   FooXX = IDLRelease(None, None)
    290   Foo1X = IDLRelease('M14', None)
    291   Foo23 = IDLRelease('M15', 'M16')
    292   Foo45 = IDLRelease('M17', 'M18')
    293 
    294   # Add nodes out of order should fail
    295   ReportClear()
    296   releases = IDLReleaseList()
    297   assert releases.AddNode(FooXX)
    298   assert releases.AddNode(Foo23)
    299   assert not releases.AddNode(Foo1X)
    300   assert error
    301   print "TestReleaseListError - Passed"
    302 
    303 
    304 def TestReleaseListOK():
    305   FooXX = IDLRelease(None, None)
    306   Foo1X = IDLRelease('M14', None)
    307   Foo23 = IDLRelease('M15', 'M16')
    308   Foo45 = IDLRelease('M17', 'M18')
    309 
    310   # Add nodes in order should work
    311   ReportClear()
    312   releases = IDLReleaseList()
    313   assert releases.AddNode(FooXX)
    314   assert releases.AddNode(Foo1X)
    315   assert releases.AddNode(Foo23)
    316   assert not error and not warning
    317   assert releases.AddNode(Foo45)
    318   assert warning
    319 
    320   assert releases.FindRelease('M13') == FooXX
    321   assert releases.FindRelease('M14') == Foo1X
    322   assert releases.FindRelease('M15') == Foo23
    323   assert releases.FindRelease('M16') == None
    324   assert releases.FindRelease('M17') == Foo45
    325   assert releases.FindRelease('M18') == None
    326 
    327   assert releases.FindRange('M13','M14') == [FooXX]
    328   assert releases.FindRange('M13','M17') == [FooXX, Foo1X, Foo23]
    329   assert releases.FindRange('M16','M17') == []
    330   assert releases.FindRange(None, None) == [FooXX, Foo1X, Foo23, Foo45]
    331 
    332   # Verify we can find the correct versions
    333   print "TestReleaseListOK - Passed"
    334 
    335 
    336 def TestReleaseMap():
    337   print "TestReleaseMap- Passed"
    338 
    339 
    340 def Main(args):
    341   TestReleaseNode()
    342   TestReleaseListWarning()
    343   TestReleaseListError()
    344   TestReleaseListOK()
    345   print "Passed"
    346   return 0
    347 
    348 
    349 if __name__ == '__main__':
    350   sys.exit(Main(sys.argv[1:]))
    351 
    352