summaryrefslogtreecommitdiffstats
path: root/ppapi/generators/idl_ast.py
blob: 515dbb0c128dee5bf89e2b7a0692fe16310b90b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Nodes for PPAPI IDL AST."""

from idl_namespace import IDLNamespace
from idl_node import IDLNode
from idl_option import GetOption
from idl_visitor import IDLVisitor
from idl_release import IDLReleaseMap

#
# IDLLabelResolver
#
# A specialized visitor which traverses the AST, building a mapping of
# Release names to Versions numbers and calculating a min version.
# The mapping is applied to the File nodes within the AST.
#
class IDLLabelResolver(IDLVisitor):
  def Depart(self, node, ignore, childdata):
    # Build list of Release=Version
    if node.IsA('LabelItem'):
      channel = node.GetProperty('channel')
      if not channel:
        channel = 'stable'
      return (node.GetName(), node.GetProperty('VALUE'), channel)

    # On completion of the Label, apply to the parent File if the
    # name of the label matches the generation label.
    if node.IsA('Label') and node.GetName() == GetOption('label'):
      try:
        node.parent.release_map = IDLReleaseMap(childdata)
      except Exception as err:
        node.Error('Unable to build release map: %s' % str(err))

    # For File objects, set the minimum version
    if node.IsA('File'):
      file_min, _ = node.release_map.GetReleaseRange()
      node.SetMin(file_min)

    return None


#
# IDLNamespaceVersionResolver
#
# A specialized visitor which traverses the AST, building a namespace tree
# as it goes.  The namespace tree is mapping from a name to a version list.
# Labels must already be resolved to use.
#
class IDLNamespaceVersionResolver(IDLVisitor):
  NamespaceSet = set(['AST', 'Callspec', 'Interface', 'Member', 'Struct'])
  #
  # When we arrive at a node we must assign it a namespace and if the
  # node is named, then place it in the appropriate namespace.
  #
  def Arrive(self, node, parent_namespace):
    # If we are a File, grab the Min version and replease mapping
    if node.IsA('File'):
      self.rmin = node.GetMinMax()[0]
      self.release_map = node.release_map

    # Set the min version on any non Label within the File
    if not node.IsA('AST', 'File', 'Label', 'LabelItem'):
      my_min, _ = node.GetMinMax()
      if not my_min:
        node.SetMin(self.rmin)

    # If this object is not a namespace aware object, use the parent's one
    if node.cls not in self.NamespaceSet:
      node.namespace = parent_namespace
    else:
    # otherwise create one.
      node.namespace = IDLNamespace(parent_namespace)

    # If this node is named, place it in its parent's namespace
    if parent_namespace and node.cls in IDLNode.NamedSet:
      # Set version min and max based on properties
      if self.release_map:
        vmin = node.GetProperty('dev_version')
        if vmin == None:
          vmin = node.GetProperty('version')
        vmax = node.GetProperty('deprecate')
        # If no min is available, the use the parent File's min
        if vmin == None:
          rmin = self.rmin
        else:
          rmin = self.release_map.GetRelease(vmin)
        rmax = self.release_map.GetRelease(vmax)
        node.SetReleaseRange(rmin, rmax)
      parent_namespace.AddNode(node)

    # Pass this namespace to each child in case they inherit it
    return node.namespace


#
# IDLFileTypeRessolver
#
# A specialized visitor which traverses the AST and sets a FILE property
# on all file nodes.  In addition, searches the namespace resolving all
# type references.  The namespace tree must already have been populated
# before this visitor is used.
#
class IDLFileTypeResolver(IDLVisitor):
  def VisitFilter(self, node, data):
    return not node.IsA('Comment', 'Copyright')

  def Arrive(self, node, filenode):
    # Track the file node to update errors
    if node.IsA('File'):
      node.SetProperty('FILE', node)
      filenode = node

    if not node.IsA('AST'):
      file_min, _ = filenode.release_map.GetReleaseRange()
      if not file_min:
        print 'Resetting min on %s to %s' % (node, file_min)
        node.SetMinRange(file_min)

    # If this node has a TYPEREF, resolve it to a version list
    typeref = node.GetPropertyLocal('TYPEREF')
    if typeref:
      node.typelist = node.parent.namespace.FindList(typeref)
      if not node.typelist:
        node.Error('Could not resolve %s.' % typeref)
    else:
      node.typelist = None
    return filenode

#
# IDLReleaseResolver
#
# A specialized visitor which will traverse the AST, and generate a mapping
# from any release to the first release in which that version of the object
# was generated.  Types must already be resolved to use.
#
class IDLReleaseResolver(IDLVisitor):
  def Arrive(self, node, releases):
    node.BuildReleaseMap(releases)
    return releases


#
# IDLAst
#
# A specialized version of the IDLNode for containing the whole of the
# AST.  Construction of the AST object will cause resolution of the
# tree including versions, types, etc...  Errors counts will be collected
# both per file, and on the AST itself.
#
class IDLAst(IDLNode):
  def __init__(self, children):
    IDLNode.__init__(self, 'AST', 'BuiltIn', 1, 0, children)
    self.Resolve()

  def Resolve(self):
    # Set the appropriate Release=Version mapping for each File
    IDLLabelResolver().Visit(self, None)

    # Generate the Namesapce Tree
    self.namespace = IDLNamespace(None)
    IDLNamespaceVersionResolver().Visit(self, self.namespace)

    # Using the namespace, resolve type references
    IDLFileTypeResolver().Visit(self, None)

    # Build an ordered list of all releases
    releases = set()
    for filenode in self.GetListOf('File'):
      releases |= set(filenode.release_map.GetReleases())

    # Generate a per node list of releases and release mapping
    IDLReleaseResolver().Visit(self, sorted(releases))

    for filenode in self.GetListOf('File'):
      errors = filenode.GetProperty('ERRORS')
      if errors:
        self.errors += errors