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