idl_ast.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Nodes for PPAPI IDL AST."""
6
7from idl_namespace import IDLNamespace
8from idl_node import IDLAttribute, IDLFile, IDLNode
9from idl_option import GetOption
10from idl_visitor import IDLVisitor
11from idl_release import IDLReleaseList, IDLReleaseMap
12
13#
14# IDL Predefined types
15#
16BuiltIn = set(['int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t',
17               'uint16_t', 'uint32_t', 'uint64_t', 'double_t', 'float_t',
18               'handle_t', 'interface_t', 'char', 'mem_t', 'str_t', 'void'])
19
20
21#
22# IDLLabelResolver
23#
24# A specialized visitor which traverses the AST, building a mapping of
25# Release names to Versions numbers and calculating a min version.
26# The mapping is applied to the File nodes within the AST.
27#
28class IDLLabelResolver(IDLVisitor):
29  def Arrive(self, node, ignore):
30    # If we are entering a File, clear the visitor local mapping
31    if node.IsA('File'):
32      self.release_map = None
33      self.filenode = node
34    # For any non AST node, the filenode is the last known file
35    if not node.IsA('AST'):
36      node.filenode = self.filenode
37    return ignore
38
39  def Depart(self, node, ignore, childdata):
40    # Build list of Release=Version
41    if node.IsA('LabelItem'):
42      return (node.GetName(), node.GetProperty('VALUE'))
43
44    # On completion of the Label, apply to the parent File if the
45    # name of the label matches the generation label.
46    if node.IsA('Label') and node.GetName() == GetOption('label'):
47      try:
48        self.release_map = IDLReleaseMap(childdata)
49        node.parent.release_map = self.release_map
50      except Exception as err:
51        node.Error('Unable to build release map: %s' % str(err))
52
53    # For File objects, set the minimum version
54    if node.IsA('File'):
55      file_min, file_max = node.release_map.GetReleaseRange()
56      node.SetMin(file_min)
57
58    return None
59
60
61#
62# IDLNamespaceVersionResolver
63#
64# A specialized visitor which traverses the AST, building a namespace tree
65# as it goes.  The namespace tree is mapping from a name to a version list.
66# Labels must already be resolved to use.
67#
68class IDLNamespaceVersionResolver(IDLVisitor):
69  NamespaceSet = set(['AST', 'Callspec', 'Interface', 'Member', 'Struct'])
70  #
71  # When we arrive at a node we must assign it a namespace and if the
72  # node is named, then place it in the appropriate namespace.
73  #
74  def Arrive(self, node, parent_namespace):
75    # If we are a File, grab the Min version and replease mapping
76    if node.IsA('File'):
77      self.rmin = node.GetMinMax()[0]
78      self.release_map = node.release_map
79
80    # Set the min version on any non Label within the File
81    if not node.IsA('AST', 'File', 'Label', 'LabelItem'):
82      my_min, my_max = node.GetMinMax()
83      if not my_min:
84        node.SetMin(self.rmin)
85
86    # If this object is not a namespace aware object, use the parent's one
87    if node.cls not in self.NamespaceSet:
88      node.namespace = parent_namespace
89    else:
90    # otherwise create one.
91      node.namespace = IDLNamespace(parent_namespace, node.GetName())
92
93    # If this node is named, place it in its parent's namespace
94    if parent_namespace and node.cls in IDLNode.NamedSet:
95      # Set version min and max based on properties
96      if self.release_map:
97        vmin = node.GetProperty('version')
98        vmax = node.GetProperty('deprecate')
99        # If no min is available, the use the parent File's min
100        if vmin == None:
101          rmin = self.rmin
102        else:
103          rmin = self.release_map.GetRelease(vmin)
104        rmax = self.release_map.GetRelease(vmax)
105        node.SetReleaseRange(rmin, rmax)
106      parent_namespace.AddNode(node)
107
108    # Pass this namespace to each child in case they inherit it
109    return node.namespace
110
111
112#
113# IDLFileTypeRessolver
114#
115# A specialized visitor which traverses the AST and sets a FILE property
116# on all file nodes.  In addition, searches the namespace resolving all
117# type references.  The namespace tree must already have been populated
118# before this visitor is used.
119#
120class IDLFileTypeResolver(IDLVisitor):
121  def VisitFilter(self, node, data):
122    return not node.IsA('Comment', 'Copyright')
123
124  def Arrive(self, node, filenode):
125    # Track the file node to update errors
126    if node.IsA('File'):
127      node.SetProperty('FILE', node)
128      filenode = node
129
130    if not node.IsA('AST'):
131      file_min, file_max = filenode.release_map.GetReleaseRange()
132      if not file_min:
133        print 'Resetting min on %s to %s' % (node, file_min)
134        node.SetMinRange(file_min)
135
136    # If this node has a TYPEREF, resolve it to a version list
137    typeref = node.property_node.GetPropertyLocal('TYPEREF')
138    if typeref:
139      node.typelist = node.parent.namespace.FindList(typeref)
140      if not node.typelist:
141        node.Error('Could not resolve %s.' % typeref)
142    else:
143      node.typelist = None
144    return filenode
145
146#
147# IDLReleaseResolver
148#
149# A specialized visitor which will traverse the AST, and generate a mapping
150# from any release to the first release in which that version of the object
151# was generated.  Types must already be resolved to use.
152#
153class IDLReleaseResolver(IDLVisitor):
154  def Arrive(self, node, releases):
155    node.BuildReleaseMap(releases)
156    return releases
157
158
159#
160# IDLAst
161#
162# A specialized version of the IDLNode for containing the whole of the
163# AST.  Construction of the AST object will cause resolution of the
164# tree including versions, types, etc...  Errors counts will be collected
165# both per file, and on the AST itself.
166#
167class IDLAst(IDLNode):
168  def __init__(self, children):
169    IDLNode.__init__(self, 'AST', 'BuiltIn', 1, 0, children)
170    self.Resolve()
171
172  def Resolve(self):
173    # Set the appropriate Release=Version mapping for each File
174    IDLLabelResolver().Visit(self, None)
175
176    # Generate the Namesapce Tree
177    self.namespace = IDLNamespace(None, 'AST')
178    IDLNamespaceVersionResolver().Visit(self, self.namespace)
179
180    # Using the namespace, resolve type references
181    IDLFileTypeResolver().Visit(self, None)
182
183    # Build an ordered list of all releases
184    releases = set()
185    for filenode in self.GetListOf('File'):
186      releases |= set(filenode.release_map.GetReleases())
187
188    # Generate a per node list of releases and release mapping
189    IDLReleaseResolver().Visit(self, sorted(releases))
190
191    for filenode in self.GetListOf('File'):
192      self.errors += int(filenode.GetProperty('ERRORS', 0))
193
194
195