blob: b4b98227df4cad4697f4c0acbdc76f205cd15b76 [file] [log] [blame]
[email protected]4a41db52012-01-13 00:02:561#!/usr/bin/env python
[email protected]dbf21cc2012-01-04 21:57:042# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]f0dd202f2011-06-07 01:36:013# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6""" Generator for C style prototypes and definitions """
7
8import glob
9import os
[email protected]532d461a2012-05-11 18:21:2010import re
[email protected]f0dd202f2011-06-07 01:36:0111import sys
12
13from idl_log import ErrOut, InfoOut, WarnOut
[email protected]5eef2882011-07-19 00:08:5414from idl_node import IDLAttribute, IDLNode
15from idl_ast import IDLAst
[email protected]f0dd202f2011-06-07 01:36:0116from idl_option import GetOption, Option, ParseOptions
17from idl_outfile import IDLOutFile
18from idl_parser import ParseFiles
[email protected]f7314732011-08-24 23:03:0619from idl_c_proto import CGen, GetNodeComments, CommentLines, Comment
20from idl_generator import Generator, GeneratorByFile
[email protected]76641b5e2012-12-13 19:47:0821from idl_visitor import IDLVisitor
[email protected]f0dd202f2011-06-07 01:36:0122
[email protected]f93616a2011-10-04 22:54:2223Option('dstroot', 'Base directory of output', default=os.path.join('..', 'c'))
24Option('guard', 'Include guard prefix', default=os.path.join('ppapi', 'c'))
[email protected]5eef2882011-07-19 00:08:5425
[email protected]406793d2011-11-14 22:26:5426
[email protected]76641b5e2012-12-13 19:47:0827#
28# PrototypeResolver
29#
30# A specialized visitor which traverses the AST, building a mapping of
31# Release names to Versions numbers and calculating a min version.
32# The mapping is applied to the File nodes within the AST.
33#
34class ProtoResolver(IDLVisitor):
35 def __init__(self):
36 IDLVisitor.__init__(self)
37 self.struct_map = {}
38 self.interface_map = {}
39
40 def Arrive(self, node, ignore):
41 if node.IsA('Member') and node.GetProperty('ref'):
42 typeref = node.typelist.GetReleases()[0]
43 if typeref.IsA('Struct'):
44 nodelist = self.struct_map.get(typeref.GetName(), [])
45 nodelist.append(node)
46 self.struct_map[typeref.GetName()] = nodelist
47
48 if node.IsA('Param'):
49 typeref = node.typelist.GetReleases()[0]
50 if typeref.IsA('Interface'):
51 nodelist = self.struct_map.get(typeref.GetName(), [])
52 nodelist.append(node)
53 self.interface_map[typeref.GetName()] = nodelist
54
55 return None
56
57
[email protected]6faeb202012-12-06 15:43:0558def GetPathFromNode(filenode, relpath=None, ext=None):
[email protected]5eef2882011-07-19 00:08:5459 path, name = os.path.split(filenode.GetProperty('NAME'))
[email protected]6faeb202012-12-06 15:43:0560 if ext: name = os.path.splitext(name)[0] + ext
[email protected]5eef2882011-07-19 00:08:5461 if path: name = os.path.join(path, name)
62 if relpath: name = os.path.join(relpath, name)
[email protected]6faeb202012-12-06 15:43:0563 name = os.path.normpath(name)
[email protected]5eef2882011-07-19 00:08:5464 return name
65
[email protected]406793d2011-11-14 22:26:5466
[email protected]6faeb202012-12-06 15:43:0567def GetHeaderFromNode(filenode, relpath=None):
68 return GetPathFromNode(filenode, relpath, ext='.h')
69
70
[email protected]f7314732011-08-24 23:03:0671def WriteGroupMarker(out, node, last_group):
72 # If we are part of a group comment marker...
73 if last_group and last_group != node.cls:
74 pre = CommentLines(['*',' @}', '']) + '\n'
75 else:
76 pre = '\n'
[email protected]5eef2882011-07-19 00:08:5477
[email protected]f7314732011-08-24 23:03:0678 if node.cls in ['Typedef', 'Interface', 'Struct', 'Enum']:
79 if last_group != node.cls:
80 pre += CommentLines(['*',' @addtogroup %ss' % node.cls, ' @{', ''])
81 last_group = node.cls
82 else:
83 last_group = None
84 out.Write(pre)
85 return last_group
[email protected]5eef2882011-07-19 00:08:5486
[email protected]5eef2882011-07-19 00:08:5487
[email protected]f7314732011-08-24 23:03:0688def GenerateHeader(out, filenode, releases):
[email protected]f7314732011-08-24 23:03:0689 cgen = CGen()
90 pref = ''
91 do_comments = True
[email protected]5eef2882011-07-19 00:08:5492
93 # Generate definitions.
94 last_group = None
[email protected]f7314732011-08-24 23:03:0695 top_types = ['Typedef', 'Interface', 'Struct', 'Enum', 'Inline']
96 for node in filenode.GetListOf(*top_types):
97 # Skip if this node is not in this release
98 if not node.InReleases(releases):
99 print "Skiping %s" % node
100 continue
[email protected]5eef2882011-07-19 00:08:54101
[email protected]f7314732011-08-24 23:03:06102 # End/Start group marker
103 if do_comments:
104 last_group = WriteGroupMarker(out, node, last_group)
[email protected]5eef2882011-07-19 00:08:54105
[email protected]f7314732011-08-24 23:03:06106 if node.IsA('Inline'):
107 item = node.GetProperty('VALUE')
108 # If 'C++' use __cplusplus wrapper
[email protected]5eef2882011-07-19 00:08:54109 if node.GetName() == 'cc':
[email protected]8450e432012-11-06 03:33:46110 item = '#ifdef __cplusplus\n%s\n#endif /* __cplusplus */\n\n' % item
[email protected]f7314732011-08-24 23:03:06111 # If not C++ or C, then skip it
112 elif not node.GetName() == 'c':
[email protected]5eef2882011-07-19 00:08:54113 continue
[email protected]f7314732011-08-24 23:03:06114 if item: out.Write(item)
115 continue
[email protected]5eef2882011-07-19 00:08:54116
[email protected]f7314732011-08-24 23:03:06117 #
118 # Otherwise we are defining a file level object, so generate the
119 # correct document notation.
120 #
121 item = cgen.Define(node, releases, prefix=pref, comment=True)
122 if not item: continue
123 asize = node.GetProperty('assert_size()')
124 if asize:
125 name = '%s%s' % (pref, node.GetName())
126 if node.IsA('Struct'):
127 form = 'PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(%s, %s);\n'
[email protected]03f41ec2011-11-18 22:53:04128 elif node.IsA('Enum'):
129 if node.GetProperty('notypedef'):
130 form = 'PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(%s, %s);\n'
131 else:
132 form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n'
[email protected]f7314732011-08-24 23:03:06133 else:
134 form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n'
135 item += form % (name, asize[0])
136
137 if item: out.Write(item)
[email protected]5eef2882011-07-19 00:08:54138 if last_group:
[email protected]f7314732011-08-24 23:03:06139 out.Write(CommentLines(['*',' @}', '']) + '\n')
[email protected]5eef2882011-07-19 00:08:54140
[email protected]5eef2882011-07-19 00:08:54141
[email protected]590a06e32013-04-24 16:52:27142def CheckTypedefs(filenode, releases):
143 """Checks that typedefs don't specify callbacks that take some structs.
144
145 See http://crbug.com/233439 for details.
146 """
147 cgen = CGen()
[email protected]590a06e32013-04-24 16:52:27148 for node in filenode.GetListOf('Typedef'):
[email protected]590a06e32013-04-24 16:52:27149 build_list = node.GetUniqueReleases(releases)
150 callnode = node.GetOneOf('Callspec')
151 if callnode:
152 for param in callnode.GetListOf('Param'):
153 if param.GetListOf('Array'):
154 continue
155 if cgen.GetParamMode(param) != 'in':
156 continue
157 t = param.GetType(build_list[0])
158 while t.IsA('Typedef'):
159 t = t.GetType(build_list[0])
[email protected]63e56c832013-12-11 04:38:11160 if t.IsA('Struct') and t.GetProperty('passByValue'):
[email protected]590a06e32013-04-24 16:52:27161 raise Exception('%s is a struct in callback %s. '
162 'See http://crbug.com/233439' %
163 (t.GetName(), node.GetName()))
164
[email protected]25cdabd2013-05-15 16:38:10165
166def CheckPassByValue(filenode, releases):
167 """Checks that new pass-by-value structs are not introduced.
168
169 See http://crbug.com/233439 for details.
170 """
171 cgen = CGen()
172 # DO NOT add any more entries to this whitelist.
173 # http://crbug.com/233439
174 type_whitelist = ['PP_ArrayOutput', 'PP_CompletionCallback',
175 'PP_Ext_EventListener', 'PP_FloatPoint',
[email protected]914f5262013-06-01 00:17:22176 'PP_Point', 'PP_TouchPoint', 'PP_Var']
[email protected]25cdabd2013-05-15 16:38:10177 nodes_to_check = filenode.GetListOf('Struct')
178 nodes_to_check.extend(filenode.GetListOf('Union'))
179 for node in nodes_to_check:
180 if node.GetName() in type_whitelist:
181 continue
182 build_list = node.GetUniqueReleases(releases)
183 if node.GetProperty('passByValue'):
184 raise Exception('%s is a new passByValue struct or union. '
185 'See http://crbug.com/233439' % node.GetName())
186 if node.GetProperty('returnByValue'):
187 raise Exception('%s is a new returnByValue struct or union. '
188 'See http://crbug.com/233439' % node.GetName())
189
190
[email protected]f7314732011-08-24 23:03:06191class HGen(GeneratorByFile):
[email protected]acc3d8e2011-08-22 22:17:40192 def __init__(self):
193 Generator.__init__(self, 'C Header', 'cgen', 'Generate the C headers.')
[email protected]5eef2882011-07-19 00:08:54194
[email protected]406793d2011-11-14 22:26:54195 def GenerateFile(self, filenode, releases, options):
[email protected]590a06e32013-04-24 16:52:27196 CheckTypedefs(filenode, releases)
[email protected]25cdabd2013-05-15 16:38:10197 CheckPassByValue(filenode, releases)
[email protected]6faeb202012-12-06 15:43:05198 savename = GetHeaderFromNode(filenode, GetOption('dstroot'))
[email protected]98cfdfc2012-10-23 18:10:02199 my_min, my_max = filenode.GetMinMax(releases)
200 if my_min > releases[-1] or my_max < releases[0]:
[email protected]55e1a702012-01-10 17:55:17201 if os.path.isfile(savename):
202 print "Removing stale %s for this range." % filenode.GetName()
203 os.remove(os.path.realpath(savename))
204 return False
205
[email protected]406793d2011-11-14 22:26:54206 out = IDLOutFile(savename)
207 self.GenerateHead(out, filenode, releases, options)
208 self.GenerateBody(out, filenode, releases, options)
209 self.GenerateTail(out, filenode, releases, options)
210 return out.Close()
[email protected]f0dd202f2011-06-07 01:36:01211
[email protected]f7314732011-08-24 23:03:06212 def GenerateHead(self, out, filenode, releases, options):
[email protected]406793d2011-11-14 22:26:54213 __pychecker__ = 'unusednames=options'
[email protected]76641b5e2012-12-13 19:47:08214
215 proto = ProtoResolver()
216 proto.Visit(filenode, None)
217
[email protected]f7314732011-08-24 23:03:06218 cgen = CGen()
219 gpath = GetOption('guard')
[email protected]6faeb202012-12-06 15:43:05220 def_guard = GetHeaderFromNode(filenode, relpath=gpath)
[email protected]f93616a2011-10-04 22:54:22221 def_guard = def_guard.replace(os.sep,'_').replace('.','_').upper() + '_'
[email protected]5eef2882011-07-19 00:08:54222
[email protected]f7314732011-08-24 23:03:06223 cright_node = filenode.GetChildren()[0]
224 assert(cright_node.IsA('Copyright'))
225 fileinfo = filenode.GetChildren()[1]
226 assert(fileinfo.IsA('Comment'))
[email protected]5eef2882011-07-19 00:08:54227
[email protected]f7314732011-08-24 23:03:06228 out.Write('%s\n' % cgen.Copyright(cright_node))
[email protected]dbf21cc2012-01-04 21:57:04229
230 # Wrap the From ... modified ... comment if it would be >80 characters.
[email protected]4d0dd162012-12-19 17:32:28231 from_text = 'From %s' % GetPathFromNode(filenode).replace(os.sep, '/')
[email protected]dbf21cc2012-01-04 21:57:04232 modified_text = 'modified %s.' % (
233 filenode.GetProperty('DATETIME'))
234 if len(from_text) + len(modified_text) < 74:
235 out.Write('/* %s %s */\n\n' % (from_text, modified_text))
236 else:
237 out.Write('/* %s,\n * %s\n */\n\n' % (from_text, modified_text))
238
[email protected]f7314732011-08-24 23:03:06239 out.Write('#ifndef %s\n#define %s\n\n' % (def_guard, def_guard))
240 # Generate set of includes
241
242 deps = set()
243 for release in releases:
244 deps |= filenode.GetDeps(release)
245
246 includes = set([])
247 for dep in deps:
248 depfile = dep.GetProperty('FILE')
249 if depfile:
250 includes.add(depfile)
[email protected]6faeb202012-12-06 15:43:05251 includes = [GetHeaderFromNode(
[email protected]f93616a2011-10-04 22:54:22252 include, relpath=gpath).replace(os.sep, '/') for include in includes]
[email protected]f7314732011-08-24 23:03:06253 includes.append('ppapi/c/pp_macros.h')
254
255 # Assume we need stdint if we "include" C or C++ code
256 if filenode.GetListOf('Include'):
257 includes.append('ppapi/c/pp_stdint.h')
258
259 includes = sorted(set(includes))
[email protected]6faeb202012-12-06 15:43:05260 cur_include = GetHeaderFromNode(filenode,
261 relpath=gpath).replace(os.sep, '/')
[email protected]f7314732011-08-24 23:03:06262 for include in includes:
263 if include == cur_include: continue
264 out.Write('#include "%s"\n' % include)
265
[email protected]76641b5e2012-12-13 19:47:08266 # Generate Prototypes
267 if proto.struct_map:
268 out.Write('\n/* Struct prototypes */\n')
269 for struct in proto.struct_map:
270 out.Write('struct %s;\n' % struct)
271
[email protected]15a86acd2013-01-11 16:27:47272 # Create a macro for the highest available release number.
[email protected]532d461a2012-05-11 18:21:20273 if filenode.GetProperty('NAME').endswith('pp_macros.idl'):
[email protected]15a86acd2013-01-11 16:27:47274 releasestr = ' '.join(releases)
[email protected]532d461a2012-05-11 18:21:20275 if releasestr:
[email protected]15a86acd2013-01-11 16:27:47276 release_numbers = re.findall('[\d\_]+', releasestr)
277 release = re.findall('\d+', release_numbers[-1])[0]
278 if release:
279 out.Write('\n#define PPAPI_RELEASE %s\n' % release)
[email protected]532d461a2012-05-11 18:21:20280
[email protected]f7314732011-08-24 23:03:06281 # Generate all interface defines
282 out.Write('\n')
283 for node in filenode.GetListOf('Interface'):
284 idefs = ''
[email protected]406793d2011-11-14 22:26:54285 macro = cgen.GetInterfaceMacro(node)
[email protected]55e1a702012-01-10 17:55:17286 unique = node.GetUniqueReleases(releases)
287
288 # Skip this interface if there are no matching versions
289 if not unique: continue
290
[email protected]6a3acf12014-05-19 18:16:10291 # Skip this interface if it should have no interface string.
292 if node.GetProperty('no_interface_string'): continue
293
[email protected]67676f212013-12-17 17:10:19294 last_stable_ver = None
295 last_dev_rel = None
296 for rel in unique:
297 channel = node.GetProperty('FILE').release_map.GetChannel(rel)
298 if channel == 'dev':
299 last_dev_rel = rel
300
[email protected]55e1a702012-01-10 17:55:17301 for rel in unique:
[email protected]e8a83feb2011-08-25 21:04:26302 version = node.GetVersion(rel)
[email protected]406793d2011-11-14 22:26:54303 name = cgen.GetInterfaceString(node, version)
[email protected]e8a83feb2011-08-25 21:04:26304 strver = str(version).replace('.', '_')
[email protected]67676f212013-12-17 17:10:19305 channel = node.GetProperty('FILE').release_map.GetChannel(rel)
306 if channel == 'dev':
307 # Skip dev channel interface versions that are
308 # Not the newest version, and
309 # Don't have an equivalent stable version.
310 if rel != last_dev_rel and not node.DevInterfaceMatchesStable(rel):
311 continue
312 value_string = '"%s" /* dev */' % name
313 else:
314 value_string = '"%s"' % name
315 last_stable_ver = strver
316 idefs += cgen.GetDefine('%s_%s' % (macro, strver), value_string)
317 if last_stable_ver:
318 idefs += cgen.GetDefine(macro, '%s_%s' % (macro, last_stable_ver))
319 idefs += '\n'
320
[email protected]f7314732011-08-24 23:03:06321 out.Write(idefs)
322
323 # Generate the @file comment
324 out.Write('%s\n' % Comment(fileinfo, prefix='*\n @file'))
325
326 def GenerateBody(self, out, filenode, releases, options):
[email protected]406793d2011-11-14 22:26:54327 __pychecker__ = 'unusednames=options'
[email protected]f7314732011-08-24 23:03:06328 GenerateHeader(out, filenode, releases)
329
330 def GenerateTail(self, out, filenode, releases, options):
[email protected]406793d2011-11-14 22:26:54331 __pychecker__ = 'unusednames=options,releases'
[email protected]f7314732011-08-24 23:03:06332 gpath = GetOption('guard')
[email protected]6faeb202012-12-06 15:43:05333 def_guard = GetPathFromNode(filenode, relpath=gpath, ext='.h')
[email protected]f93616a2011-10-04 22:54:22334 def_guard = def_guard.replace(os.sep,'_').replace('.','_').upper() + '_'
[email protected]f7314732011-08-24 23:03:06335 out.Write('#endif /* %s */\n\n' % def_guard)
[email protected]5eef2882011-07-19 00:08:54336
[email protected]5eef2882011-07-19 00:08:54337
[email protected]acc3d8e2011-08-22 22:17:40338hgen = HGen()
339
[email protected]6faeb202012-12-06 15:43:05340def main(args):
[email protected]acc3d8e2011-08-22 22:17:40341 # Default invocation will verify the golden files are unchanged.
[email protected]f7314732011-08-24 23:03:06342 failed = 0
[email protected]acc3d8e2011-08-22 22:17:40343 if not args:
344 args = ['--wnone', '--diff', '--test', '--dstroot=.']
345
346 ParseOptions(args)
[email protected]f7314732011-08-24 23:03:06347
[email protected]acc3d8e2011-08-22 22:17:40348 idldir = os.path.split(sys.argv[0])[0]
349 idldir = os.path.join(idldir, 'test_cgen', '*.idl')
350 filenames = glob.glob(idldir)
[email protected]f7314732011-08-24 23:03:06351 ast = ParseFiles(filenames)
352 if hgen.GenerateRelease(ast, 'M14', {}):
353 print "Golden file for M14 failed."
354 failed = 1
355 else:
356 print "Golden file for M14 passed."
357
358
359 idldir = os.path.split(sys.argv[0])[0]
360 idldir = os.path.join(idldir, 'test_cgen_range', '*.idl')
361 filenames = glob.glob(idldir)
[email protected]acc3d8e2011-08-22 22:17:40362
363 ast = ParseFiles(filenames)
[email protected]67676f212013-12-17 17:10:19364 if hgen.GenerateRange(ast, ['M13', 'M14', 'M15', 'M16', 'M17'], {}):
365 print "Golden file for M13-M17 failed."
[email protected]f7314732011-08-24 23:03:06366 failed =1
367 else:
[email protected]67676f212013-12-17 17:10:19368 print "Golden file for M13-M17 passed."
[email protected]f7314732011-08-24 23:03:06369
370 return failed
[email protected]f0dd202f2011-06-07 01:36:01371
372if __name__ == '__main__':
[email protected]6faeb202012-12-06 15:43:05373 sys.exit(main(sys.argv[1:]))
[email protected]55e1a702012-01-10 17:55:17374