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 IDLNamespace 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_option import GetOption, Option, ParseOptions
     16 from idl_log import ErrOut, InfoOut, WarnOut
     17 from idl_release import IDLRelease, IDLReleaseList
     18 
     19 Option('label', 'Use the specifed label blocks.', default='Chrome')
     20 Option('namespace_debug', 'Use the specified release')
     21 
     22 
     23 #
     24 # IDLNamespace
     25 #
     26 # IDLNamespace provides a mapping between a symbol name and an IDLReleaseList
     27 # which contains IDLRelease objects.  It provides an interface for fetching
     28 # one or more IDLNodes based on a release or range of releases.
     29 #
     30 class IDLNamespace(object):
     31   def __init__(self, parent, name):
     32     self._name_to_releases = {}
     33     self._parent = parent
     34     self._name = name
     35 
     36   def Dump(self):
     37     for name in self._name_to_releases:
     38       InfoOut.Log('NAME=%s' % name)
     39       for cver in self._name_to_releases[name].GetReleases():
     40         InfoOut.Log('  %s' % cver)
     41       InfoOut.Log('')
     42 
     43   def FindRelease(self, name, release):
     44     verlist = self._name_to_releases.get(name, None)
     45     if verlist == None:
     46       if self._parent:
     47         return self._parent.FindRelease(name, release)
     48       else:
     49         return None
     50     return verlist.FindRelease(release)
     51 
     52   def FindRange(self, name, rmin, rmax):
     53     verlist = self._name_to_releases.get(name, None)
     54     if verlist == None:
     55       if self._parent:
     56         return self._parent.FindRange(name, rmin, rmax)
     57       else:
     58         return []
     59     return verlist.FindRange(rmin, rmax)
     60 
     61   def FindList(self, name):
     62     verlist = self._name_to_releases.get(name, None)
     63     if verlist == None:
     64       if self._parent:
     65         return self._parent.FindList(name)
     66     return verlist
     67 
     68   def AddNode(self, node):
     69     name = node.GetName()
     70     verlist = self._name_to_releases.setdefault(name,IDLReleaseList())
     71     if GetOption('namespace_debug'):
     72         print "Adding to namespace: %s" % node
     73     return verlist.AddNode(node)
     74 
     75 
     76 
     77 #
     78 # Testing Code
     79 #
     80 
     81 #
     82 # MockNode
     83 #
     84 # Mocks the IDLNode to support error, warning handling, and string functions.
     85 #
     86 class MockNode(IDLRelease):
     87   def __init__(self, name, rmin, rmax):
     88     self.name = name
     89     self.rmin = rmin
     90     self.rmax = rmax
     91     self.errors = []
     92     self.warns = []
     93     self.properties = {
     94         'NAME': name,
     95         'release': rmin,
     96         'deprecate' : rmax
     97         }
     98 
     99   def __str__(self):
    100     return '%s (%s : %s)' % (self.name, self.rmin, self.rmax)
    101 
    102   def GetName(self):
    103     return self.name
    104 
    105   def Error(self, msg):
    106     if GetOption('release_debug'): print 'Error: %s' % msg
    107     self.errors.append(msg)
    108 
    109   def Warn(self, msg):
    110     if GetOption('release_debug'): print 'Warn: %s' % msg
    111     self.warns.append(msg)
    112 
    113   def GetProperty(self, name):
    114     return self.properties.get(name, None)
    115 
    116 errors = 0
    117 #
    118 # DumpFailure
    119 #
    120 # Dumps all the information relevant  to an add failure.
    121 def DumpFailure(namespace, node, msg):
    122   global errors
    123   print '\n******************************'
    124   print 'Failure: %s %s' % (node, msg)
    125   for warn in node.warns:
    126     print '  WARN: %s' % warn
    127   for err in node.errors:
    128     print '  ERROR: %s' % err
    129   print '\n'
    130   namespace.Dump()
    131   print '******************************\n'
    132   errors += 1
    133 
    134 # Add expecting no errors or warnings
    135 def AddOkay(namespace, node):
    136   okay = namespace.AddNode(node)
    137   if not okay or node.errors or node.warns:
    138     DumpFailure(namespace, node, 'Expected success')
    139 
    140 # Add expecting a specific warning
    141 def AddWarn(namespace, node, msg):
    142   okay = namespace.AddNode(node)
    143   if not okay or node.errors or not node.warns:
    144     DumpFailure(namespace, node, 'Expected warnings')
    145   if msg not in node.warns:
    146     DumpFailure(namespace, node, 'Expected warning: %s' % msg)
    147 
    148 # Add expecting a specific error any any number of warnings
    149 def AddError(namespace, node, msg):
    150   okay = namespace.AddNode(node)
    151   if okay or not node.errors:
    152     DumpFailure(namespace, node, 'Expected errors')
    153   if msg not in node.errors:
    154     DumpFailure(namespace, node, 'Expected error: %s' % msg)
    155     print ">>%s<<\n>>%s<<\n" % (node.errors[0], msg)
    156 
    157 # Verify that a FindRelease call on the namespace returns the expected node.
    158 def VerifyFindOne(namespace, name, release, node):
    159   global errors
    160   if (namespace.FindRelease(name, release) != node):
    161     print "Failed to find %s as release %f of %s" % (node, release, name)
    162     namespace.Dump()
    163     print "\n"
    164     errors += 1
    165 
    166 # Verify that a FindRage call on the namespace returns a set of expected nodes.
    167 def VerifyFindAll(namespace, name, rmin, rmax, nodes):
    168   global errors
    169   out = namespace.FindRange(name, rmin, rmax)
    170   if (out != nodes):
    171     print "Found [%s] instead of[%s] for releases %f to %f of %s" % (
    172         ' '.join([str(x) for x in out]),
    173         ' '.join([str(x) for x in nodes]),
    174         rmin,
    175         rmax,
    176         name)
    177     namespace.Dump()
    178     print "\n"
    179     errors += 1
    180 
    181 def Main(args):
    182   global errors
    183   ParseOptions(args)
    184 
    185   InfoOut.SetConsole(True)
    186 
    187   namespace = IDLNamespace(None)
    188 
    189   FooXX = MockNode('foo', None, None)
    190   Foo1X = MockNode('foo', 1.0, None)
    191   Foo2X = MockNode('foo', 2.0, None)
    192   Foo3X = MockNode('foo', 3.0, None)
    193 
    194   # Verify we succeed with undeprecated adds
    195   AddOkay(namespace, FooXX)
    196   AddOkay(namespace, Foo1X)
    197   AddOkay(namespace, Foo3X)
    198   # Verify we fail to add a node between undeprecated releases
    199   AddError(namespace, Foo2X,
    200            'Overlap in releases: 3.0 vs 2.0 when adding foo (2.0 : None)')
    201 
    202   BarXX = MockNode('bar', None, None)
    203   Bar12 = MockNode('bar', 1.0, 2.0)
    204   Bar23 = MockNode('bar', 2.0, 3.0)
    205   Bar34 = MockNode('bar', 3.0, 4.0)
    206 
    207 
    208   # Verify we succeed with fully qualified releases
    209   namespace = IDLNamespace(namespace)
    210   AddOkay(namespace, BarXX)
    211   AddOkay(namespace, Bar12)
    212   # Verify we warn when detecting a gap
    213   AddWarn(namespace, Bar34, 'Gap in release numbers.')
    214   # Verify we fail when inserting into this gap
    215   # (NOTE: while this could be legal, it is sloppy so we disallow it)
    216   AddError(namespace, Bar23, 'Declarations out of order.')
    217 
    218   # Verify local namespace
    219   VerifyFindOne(namespace, 'bar', 0.0, BarXX)
    220   VerifyFindAll(namespace, 'bar', 0.5, 1.5, [BarXX, Bar12])
    221 
    222   # Verify the correct release of the object is found recursively
    223   VerifyFindOne(namespace, 'foo', 0.0, FooXX)
    224   VerifyFindOne(namespace, 'foo', 0.5, FooXX)
    225   VerifyFindOne(namespace, 'foo', 1.0, Foo1X)
    226   VerifyFindOne(namespace, 'foo', 1.5, Foo1X)
    227   VerifyFindOne(namespace, 'foo', 3.0, Foo3X)
    228   VerifyFindOne(namespace, 'foo', 100.0, Foo3X)
    229 
    230   # Verify the correct range of objects is found
    231   VerifyFindAll(namespace, 'foo', 0.0, 1.0, [FooXX])
    232   VerifyFindAll(namespace, 'foo', 0.5, 1.0, [FooXX])
    233   VerifyFindAll(namespace, 'foo', 1.0, 1.1, [Foo1X])
    234   VerifyFindAll(namespace, 'foo', 0.5, 1.5, [FooXX, Foo1X])
    235   VerifyFindAll(namespace, 'foo', 0.0, 3.0, [FooXX, Foo1X])
    236   VerifyFindAll(namespace, 'foo', 3.0, 100.0, [Foo3X])
    237 
    238   FooBar = MockNode('foobar', 1.0, 2.0)
    239   namespace = IDLNamespace(namespace)
    240   AddOkay(namespace, FooBar)
    241 
    242   if errors:
    243     print 'Test failed with %d errors.' % errors
    244   else:
    245     print 'Passed.'
    246   return errors
    247 
    248 
    249 if __name__ == '__main__':
    250   sys.exit(Main(sys.argv[1:]))
    251 
    252