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