| OLD | NEW |
| (Empty) | |
| 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 |
| OLD | NEW |