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.
6 """Nodes for PPAPI IDL AST"""
11 # IDL Node defines the IDLAttribute and IDLNode objects which are constructed
12 # by the parser as it processes the various 'productions'. The IDLAttribute
13 # objects are assigned to the IDLNode's property dictionary instead of being
14 # applied as children of The IDLNodes, so they do not exist in the final tree.
15 # The AST of IDLNodes is the output from the parsing state and will be used
16 # as the source data by the various generators.
21 from idl_log
import ErrOut
, InfoOut
, WarnOut
22 from idl_propertynode
import IDLPropertyNode
23 from idl_release
import IDLRelease
, IDLReleaseMap
28 # A temporary object used by the parsing process to hold an Extended Attribute
29 # which will be passed as a child to a standard IDLNode.
31 class IDLAttribute(object):
32 def __init__(self
, name
, value
):
33 self
.cls
= 'ExtAttribute'
38 return '%s=%s' % (self
.name
, self
.value
)
43 # This class implements the AST tree, providing the associations between
44 # parents and children. It also contains a namepsace and propertynode to
45 # allow for look-ups. IDLNode is derived from IDLRelease, so it is
48 class IDLNode(IDLRelease
):
50 # Set of object IDLNode types which have a name and belong in the namespace.
51 NamedSet
= set(['Enum', 'EnumItem', 'File', 'Function', 'Interface',
52 'Member', 'Param', 'Struct', 'Type', 'Typedef'])
54 def __init__(self
, cls
, filename
, lineno
, pos
, children
=None):
55 # Initialize with no starting or ending Version
56 IDLRelease
.__init
__(self
, None, None)
61 self
._filename
= filename
67 self
._property
_node
= IDLPropertyNode()
68 self
._unique
_releases
= None
70 # A list of unique releases for this node
73 # A map from any release, to the first unique release
74 self
.first_release
= None
76 # self._children is a list of children ordered as defined
78 # Process the passed in list of children, placing ExtAttributes into the
79 # property dictionary, and nodes into the local child list in order. In
80 # addition, add nodes to the namespace if the class is in the NamedSet.
82 for child
in children
:
83 if child
.cls
== 'ExtAttribute':
84 self
.SetProperty(child
.name
, child
.value
)
92 return '%s(%s)' % (self
.cls
, name
)
95 """Return a file and line number for where this node was defined."""
96 return '%s(%d)' % (self
._filename
, self
.lineno
)
99 """Log an error for this object."""
101 ErrOut
.LogLine(self
._filename
, self
.lineno
, 0, ' %s %s' %
103 filenode
= self
.GetProperty('FILE')
105 errcnt
= filenode
.GetProperty('ERRORS')
108 filenode
.SetProperty('ERRORS', errcnt
+ 1)
110 def Warning(self
, msg
):
111 """Log a warning for this object."""
112 WarnOut
.LogLine(self
._filename
, self
.lineno
, 0, ' %s %s' %
116 return self
.GetProperty('NAME')
118 def Dump(self
, depth
=0, comments
=False, out
=sys
.stdout
):
119 """Dump this object and its children"""
120 if self
.cls
in ['Comment', 'Copyright']:
125 # Skip this node if it's a comment, and we are not printing comments
126 if not comments
and is_comment
:
129 tab
= ''.rjust(depth
* 2)
131 out
.write('%sComment\n' % tab
)
132 for line
in self
.GetName().split('\n'):
133 out
.write('%s "%s"\n' % (tab
, line
))
135 ver
= IDLRelease
.__str
__(self
)
137 release_list
= ': ' + ' '.join(self
.releases
)
139 release_list
= ': undefined'
140 out
.write('%s%s%s%s\n' % (tab
, self
, ver
, release_list
))
142 out
.write('%s Typelist: %s\n' % (tab
, self
.typelist
.GetReleases()[0]))
143 properties
= self
._property
_node
.GetPropertyList()
145 out
.write('%s Properties\n' % tab
)
147 if is_comment
and p
== 'NAME':
148 # Skip printing the name for comments, since we printed above already
150 out
.write('%s %s : %s\n' % (tab
, p
, self
.GetProperty(p
)))
151 for child
in self
._children
:
152 child
.Dump(depth
+1, comments
=comments
, out
=out
)
154 def IsA(self
, *typelist
):
155 """Check if node is of a given type."""
156 return self
.cls
in typelist
158 def GetListOf(self
, *keys
):
159 """Get a list of objects for the given key(s)."""
161 for child
in self
._children
:
162 if child
.cls
in keys
:
166 def GetOneOf(self
, *keys
):
167 """Get an object for the given key(s)."""
168 out
= self
.GetListOf(*keys
)
173 def SetParent(self
, parent
):
174 self
._property
_node
.AddParent(parent
)
177 def AddChild(self
, node
):
179 self
._children
.append(node
)
181 # Get a list of all children
182 def GetChildren(self
):
183 return self
._children
185 def GetType(self
, release
):
186 if not self
.typelist
:
188 return self
.typelist
.FindRelease(release
)
190 def GetDeps(self
, release
, visited
=None):
191 visited
= visited
or set()
193 # If this release is not valid for this object, then done.
194 if not self
.IsRelease(release
) or self
.IsA('Comment', 'Copyright'):
197 # If we have cached the info for this release, return the cached value
198 deps
= self
._deps
.get(release
, None)
202 # If we are already visited, then return
206 # Otherwise, build the dependency list
207 visited |
= set([self
])
211 for child
in self
.GetChildren():
212 deps |
= child
.GetDeps(release
, visited
)
216 typeref
= self
.GetType(release
)
218 deps |
= typeref
.GetDeps(release
, visited
)
220 self
._deps
[release
] = deps
223 def GetVersion(self
, release
):
224 filenode
= self
.GetProperty('FILE')
227 return filenode
.release_map
.GetVersion(release
)
229 def GetUniqueReleases(self
, releases
):
230 """Return the unique set of first releases corresponding to input
232 Since we are returning the corresponding 'first' version for a
233 release, we may return a release version prior to the one in the list."""
234 my_min
, my_max
= self
.GetMinMax(releases
)
235 if my_min
> releases
[-1] or my_max
< releases
[0]:
240 remapped
= self
.first_release
[rel
]
243 out |
= set([remapped
])
245 # Cache the most recent set of unique_releases
246 self
._unique
_releases
= sorted(out
)
247 return self
._unique
_releases
249 def LastRelease(self
, release
):
250 # Get the most recent release from the most recently generated set of
251 # cached unique releases.
252 if self
._unique
_releases
and self
._unique
_releases
[-1] > release
:
256 def GetRelease(self
, version
):
257 filenode
= self
.GetProperty('FILE')
260 return filenode
.release_map
.GetRelease(version
)
262 def _GetReleaseList(self
, releases
, visited
=None):
263 visited
= visited
or set()
264 if not self
.releases
:
265 # If we are unversionable, then return first available release
266 if self
.IsA('Comment', 'Copyright', 'Label'):
270 # Generate the first and if deprecated within this subset, the
271 # last release for this node
272 my_min
, my_max
= self
.GetMinMax(releases
)
274 if my_max
!= releases
[-1]:
275 my_releases
= set([my_min
, my_max
])
277 my_releases
= set([my_min
])
279 r
= self
.GetRelease(self
.GetProperty('version'))
280 if not r
in my_releases
:
281 my_releases |
= set([r
])
283 # Break cycle if we reference ourselves
287 visited |
= set([self
])
289 # Files inherit all their releases from items in the file
290 if self
.IsA('AST', 'File'):
294 child_releases
= set()
296 # Exclude sibling results from parent visited set
299 for child
in self
._children
:
300 child_releases |
= set(child
._GetReleaseList
(releases
, cur_visits
))
301 visited |
= set(child_releases
)
304 type_releases
= set()
306 type_list
= self
.typelist
.GetReleases()
307 for typenode
in type_list
:
308 type_releases |
= set(typenode
._GetReleaseList
(releases
, cur_visits
))
310 type_release_list
= sorted(type_releases
)
311 if my_min
< type_release_list
[0]:
312 type_node
= type_list
[0]
313 self
.Error('requires %s in %s which is undefined at %s.' % (
314 type_node
, type_node
._filename
, my_min
))
316 for rel
in child_releases | type_releases
:
317 if rel
>= my_min
and rel
<= my_max
:
318 my_releases |
= set([rel
])
320 self
.releases
= sorted(my_releases
)
323 def BuildReleaseMap(self
, releases
):
324 unique_list
= self
._GetReleaseList
(releases
)
325 _
, my_max
= self
.GetMinMax(releases
)
327 self
.first_release
= {}
330 if rel
in unique_list
:
332 self
.first_release
[rel
] = last_rel
336 def SetProperty(self
, name
, val
):
337 self
._property
_node
.SetProperty(name
, val
)
339 def GetProperty(self
, name
):
340 return self
._property
_node
.GetProperty(name
)
342 def GetPropertyLocal(self
, name
):
343 return self
._property
_node
.GetPropertyLocal(name
)
345 def NodeIsDevOnly(self
):
346 """Returns true iff a node is only in dev channel."""
347 return self
.GetProperty('dev_version') and not self
.GetProperty('version')
349 def DevInterfaceMatchesStable(self
, release
):
350 """Returns true if an interface has an equivalent stable version."""
351 assert(self
.IsA('Interface'))
352 for child
in self
.GetListOf('Member'):
353 unique
= child
.GetUniqueReleases([release
])
354 if not unique
or not child
.InReleases([release
]):
356 if child
.NodeIsDevOnly():
364 # A specialized version of IDLNode which tracks errors and warnings.
366 class IDLFile(IDLNode
):
367 def __init__(self
, name
, children
, errors
=0):
368 attrs
= [IDLAttribute('NAME', name
),
369 IDLAttribute('ERRORS', errors
)]
372 IDLNode
.__init
__(self
, 'File', name
, 1, 0, attrs
+ children
)
373 # TODO(teravest): Why do we set release map like this here? This looks
375 self
.release_map
= IDLReleaseMap([('M13', 1.0, 'stable')])
384 text_str
= 'MyNode(%s)' % name_str
385 name_node
= IDLAttribute('NAME', name_str
)
386 node
= IDLNode('MyNode', 'no file', 1, 0, [name_node
])
387 if node
.GetName() != name_str
:
388 ErrOut
.Log('GetName returned >%s< not >%s<' % (node
.GetName(), name_str
))
390 if node
.GetProperty('NAME') != name_str
:
391 ErrOut
.Log('Failed to get name property.')
393 if str(node
) != text_str
:
394 ErrOut
.Log('str() returned >%s< not >%s<' % (str(node
), text_str
))
397 InfoOut
.Log('Passed StringTest')
403 child
= IDLNode('child', 'no file', 1, 0)
404 parent
= IDLNode('parent', 'no file', 1, 0, [child
])
406 if child
.parent
!= parent
:
407 ErrOut
.Log('Failed to connect parent.')
410 if [child
] != parent
.GetChildren():
411 ErrOut
.Log('Failed GetChildren.')
414 if child
!= parent
.GetOneOf('child'):
415 ErrOut
.Log('Failed GetOneOf(child)')
418 if parent
.GetOneOf('bogus'):
419 ErrOut
.Log('Failed GetOneOf(bogus)')
422 if not parent
.IsA('parent'):
423 ErrOut
.Log('Expecting parent type')
426 parent
= IDLNode('parent', 'no file', 1, 0, [child
, child
])
427 if [child
, child
] != parent
.GetChildren():
428 ErrOut
.Log('Failed GetChildren2.')
432 InfoOut
.Log('Passed ChildTest')
437 errors
= StringTest()
438 errors
+= ChildTest()
441 ErrOut
.Log('IDLNode failed with %d errors.' % errors
)
445 if __name__
== '__main__':