blob: 1ea8e031bd30a99be5ce8c4ad4dba4e07f8fb78d [file] [log] [blame]
[email protected]38c0f7e2011-06-02 01:16:301#!/usr/bin/python
2#
3# Copyright (c) 2011 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7""" Generator for C style prototypes and definitions """
8
9import glob
10import os
11import sys
12
13from idl_log import ErrOut, InfoOut, WarnOut
[email protected]5eef2882011-07-19 00:08:5414from idl_node import IDLNode
15from idl_ast import IDLAst
[email protected]38c0f7e2011-06-02 01:16:3016from idl_option import GetOption, Option, ParseOptions
[email protected]5eef2882011-07-19 00:08:5417from idl_parser import ParseFiles
18
19Option('cgen_debug', 'Debug generate.')
20
21class CGenError(Exception):
22 def __init__(self, msg):
23 self.value = value
24
25 def __str__(self):
26 return repr(self.value)
[email protected]38c0f7e2011-06-02 01:16:3027
28
[email protected]f7314732011-08-24 23:03:0629def CommentLines(lines, tabs=0):
30 # Generate a C style comment block by prepending the block with '<tab>/*'
31 # and adding a '<tab> *' per line.
32 tab = ' ' * tabs
33
34 out = '%s/*' % tab + ('\n%s *' % tab).join(lines)
35
36 # Add a terminating ' */' unless the last line is blank which would mean it
37 # already has ' *'
38 if not lines[-1]:
39 out += '/\n'
40 else:
41 out += ' */\n'
42 return out
43
44def Comment(node, prefix=None, tabs=0):
45 # Generate a comment block from the provided Comment node.
46 comment = node.GetName()
47 lines = comment.split('\n')
48
49 # If an option prefix is provided, then prepend that to the comment
50 # for this node.
51 if prefix:
52 prefix_lines = prefix.split('\n')
53 # If both the prefix and comment start with a blank line ('*') remove
54 # the extra one.
55 if prefix_lines[0] == '*' and lines[0] == '*':
56 lines = prefix_lines + lines[1:]
57 else:
58 lines = prefix_lines + lines;
59 return CommentLines(lines, tabs)
60
61def GetNodeComments(node, prefix=None, tabs=0):
62 # Generate a comment block joining all comment nodes which are children of
63 # the provided node.
64 comment_txt = ''
65 for doc in node.GetListOf('Comment'):
66 comment_txt += Comment(doc, tabs=tabs)
67 return comment_txt
68
69
[email protected]5eef2882011-07-19 00:08:5470class CGen(object):
71 # TypeMap
72 #
73 # TypeMap modifies how an object is stored or passed, for example pointers
74 # are passed as 'const' if they are 'in' parameters, and structures are
75 # preceeded by the keyword 'struct' as well as using a pointer.
76 #
77 TypeMap = {
78 'Array': {
79 'in': 'const %s',
80 'inout': '%s*',
81 'out': '%s*',
82 'store': '%s',
83 'return': '%s'
84 },
85 'Callspec': {
86 'in': '%s',
87 'inout': '%s',
88 'out': '%s',
89 'store': '%s',
90 'return': '%s'
91 },
92 'Enum': {
93 'in': '%s',
94 'inout': '%s*',
95 'out': '%s*',
96 'store': '%s',
97 'return': '%s'
98 },
99 'Struct': {
100 'in': 'const %s*',
101 'inout': '%s*',
102 'out': '%s*',
103 'return': ' %s*',
104 'store': '%s'
105 },
106 'mem_t': {
107 'in': 'const %s',
108 'inout': '%s',
109 'out': '%s',
110 'return': '%s',
111 'store': '%s'
112 },
113 'str_t': {
114 'in': 'const %s',
115 'inout': '%s',
116 'out': '%s',
117 'return': 'const %s',
118 'store': '%s'
119 },
120 'TypeValue': {
121 'in': '%s',
122 'inout': '%s*',
123 'out': '%s*',
124 'return': '%s',
125 'store': '%s'
126 },
[email protected]38c0f7e2011-06-02 01:16:30127 }
128
[email protected]38c0f7e2011-06-02 01:16:30129
[email protected]5eef2882011-07-19 00:08:54130 #
131 # RemapName
132 #
133 # A diction array of PPAPI types that are converted to language specific
134 # types before being returned by by the C generator
135 #
136 RemapName = {
137 'float_t': 'float',
138 'double_t': 'double',
139 'handle_t': 'int',
140 'mem_t': 'void*',
141 'str_t': 'char*',
142 'interface_t' : 'const void*'
143 }
[email protected]38c0f7e2011-06-02 01:16:30144
[email protected]5eef2882011-07-19 00:08:54145 def __init__(self):
146 self.dbg_depth = 0
[email protected]5eef2882011-07-19 00:08:54147
148 #
149 # Debug Logging functions
150 #
151 def Log(self, txt):
152 if not GetOption('cgen_debug'): return
153 tabs = ''
154 for tab in range(self.dbg_depth): tabs += ' '
155 print '%s%s' % (tabs, txt)
156
157 def LogEnter(self, txt):
158 if txt: self.Log(txt)
159 self.dbg_depth += 1
160
161 def LogExit(self, txt):
162 self.dbg_depth -= 1
163 if txt: self.Log(txt)
164
165 #
166 # Return the array specification of the object.
167 #
168 def GetArraySpec(self, node):
169 assert(node.cls == 'Array')
170 out = ''
171 fixed = node.GetProperty('FIXED')
172 if fixed:
173 return '[%s]' % fixed
174 else:
175 return '[]'
176
177 #
178 # GetTypeName
179 #
180 # For any valid 'typed' object such as Member or Typedef
181 # the typenode object contains the typename
182 #
183 # For a given node return the type name by passing mode.
184 #
[email protected]f7314732011-08-24 23:03:06185 def GetTypeName(self, node, release, prefix=''):
186 self.LogEnter('GetTypeName of %s rel=%s' % (node, release))
[email protected]5eef2882011-07-19 00:08:54187
[email protected]f7314732011-08-24 23:03:06188 # For Members, Params, and Typedefs get the type it refers to otherwise
189 # the node in question is it's own type (struct, union etc...)
[email protected]5eef2882011-07-19 00:08:54190 if node.IsA('Member', 'Param', 'Typedef'):
[email protected]f7314732011-08-24 23:03:06191 typeref = node.GetType(release)
[email protected]5eef2882011-07-19 00:08:54192 else:
193 typeref = node
194
195 if typeref is None:
196 raise CGenError('No type for %s' % node)
197
198 # If the type is a (BuiltIn) Type then return it's name
199 # remapping as needed
200 if typeref.IsA('Type'):
201 name = CGen.RemapName.get(typeref.GetName(), None)
202 if name is None: name = typeref.GetName()
203 name = '%s%s' % (prefix, name)
204
205 # For structures, preceed with 'struct' or 'union' as appropriate
206 elif typeref.IsA('Interface', 'Struct'):
207 if typeref.GetProperty('union'):
208 name = 'union %s%s' % (prefix, typeref.GetName())
209 else:
210 name = 'struct %s%s' % (prefix, typeref.GetName())
211
212 # If it's an enum, or typedef then return the Enum's name
213 elif typeref.IsA('Enum', 'Typedef'):
214 name = '%s%s' % (prefix, typeref.GetName())
215
216 else:
217 raise RuntimeError('Getting name of non-type %s.' % node)
218 self.LogExit('GetTypeName %s is %s' % (node, name))
219 return name
220
221
222 #
223 # GetRootType
224 #
225 # For a given node return basic type of that object. This is
226 # either a 'Type', 'Callspec', or 'Array'
227 #
[email protected]f7314732011-08-24 23:03:06228 def GetRootTypeMode(self, node, release, mode):
[email protected]5eef2882011-07-19 00:08:54229 self.LogEnter('GetRootType of %s' % node)
230 # If it has an array spec, then treat it as an array regardless of type
231 if node.GetOneOf('Array'):
232 rootType = 'Array'
233 # Or if it has a callspec, treat it as a function
234 elif node.GetOneOf('Callspec'):
[email protected]f7314732011-08-24 23:03:06235 rootType, mode = self.GetRootTypeMode(node.GetType(release), release,
[email protected]5eef2882011-07-19 00:08:54236 'return')
237
238 # If it's a plain typedef, try that object's root type
239 elif node.IsA('Member', 'Param', 'Typedef'):
[email protected]f7314732011-08-24 23:03:06240 rootType, mode = self.GetRootTypeMode(node.GetType(release),
241 release, mode)
[email protected]5eef2882011-07-19 00:08:54242
243 # If it's an Enum, then it's normal passing rules
244 elif node.IsA('Enum'):
245 rootType = node.cls
246
247 # If it's an Interface or Struct, we may be passing by value
248 elif node.IsA('Interface', 'Struct'):
249 if mode == 'return':
250 if node.GetProperty('returnByValue'):
251 rootType = 'TypeValue'
252 else:
253 rootType = node.cls
254 else:
255 if node.GetProperty('passByValue'):
256 rootType = 'TypeValue'
257 else:
258 rootType = node.cls
259
260 # If it's an Basic Type, check if it's a special type
261 elif node.IsA('Type'):
262 if node.GetName() in CGen.TypeMap:
263 rootType = node.GetName()
264 else:
265 rootType = 'TypeValue'
266 else:
267 raise RuntimeError('Getting root type of non-type %s.' % node)
268 self.LogExit('RootType is "%s"' % rootType)
269 return rootType, mode
270
271
[email protected]f7314732011-08-24 23:03:06272 def GetTypeByMode(self, node, release, mode):
273 self.LogEnter('GetTypeByMode of %s mode=%s release=%s' %
274 (node, mode, release))
275 name = self.GetTypeName(node, release)
276 ntype, mode = self.GetRootTypeMode(node, release, mode)
[email protected]ec5af272011-07-19 01:21:53277 out = CGen.TypeMap[ntype][mode] % name
[email protected]5eef2882011-07-19 00:08:54278 self.LogExit('GetTypeByMode %s = %s' % (node, out))
279 return out
280
281
282 # Get the passing mode of the object (in, out, inout).
283 def GetParamMode(self, node):
284 self.Log('GetParamMode for %s' % node)
285 if node.GetProperty('in'): return 'in'
286 if node.GetProperty('out'): return 'out'
287 if node.GetProperty('inout'): return 'inout'
288 return 'return'
289
290 #
291 # GetComponents
292 #
293 # Returns the signature components of an object as a tuple of
294 # (rtype, name, arrays, callspec) where:
295 # rtype - The store or return type of the object.
296 # name - The name of the object.
297 # arrays - A list of array dimensions as [] or [<fixed_num>].
298 # args - None of not a function, otherwise a list of parameters.
299 #
[email protected]f7314732011-08-24 23:03:06300 def GetComponents(self, node, release, mode):
301 self.LogEnter('GetComponents mode %s for %s %s' % (mode, node, release))
[email protected]5eef2882011-07-19 00:08:54302
303 # Generate passing type by modifying root type
[email protected]f7314732011-08-24 23:03:06304 rtype = self.GetTypeByMode(node, release, mode)
[email protected]5eef2882011-07-19 00:08:54305 if node.IsA('Enum', 'Interface', 'Struct'):
306 rname = node.GetName()
307 else:
[email protected]f7314732011-08-24 23:03:06308 rname = node.GetType(release).GetName()
[email protected]5eef2882011-07-19 00:08:54309
310 if rname in CGen.RemapName:
311 rname = CGen.RemapName[rname]
312 if '%' in rtype:
313 rtype = rtype % rname
314 name = node.GetName()
315 arrayspec = [self.GetArraySpec(array) for array in node.GetListOf('Array')]
316 callnode = node.GetOneOf('Callspec')
317 if callnode:
318 callspec = []
319 for param in callnode.GetListOf('Param'):
320 mode = self.GetParamMode(param)
[email protected]f7314732011-08-24 23:03:06321 ptype, pname, parray, pspec = self.GetComponents(param, release, mode)
[email protected]5eef2882011-07-19 00:08:54322 callspec.append((ptype, pname, parray, pspec))
323 else:
324 callspec = None
325
326 self.LogExit('GetComponents: %s, %s, %s, %s' %
327 (rtype, name, arrayspec, callspec))
328 return (rtype, name, arrayspec, callspec)
329
330
331 def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr):
332 self.LogEnter('Compose: %s %s' % (rtype, name))
333 arrayspec = ''.join(arrayspec)
334 name = '%s%s%s' % (prefix, name, arrayspec)
335 if callspec is None:
336 out = '%s %s' % (rtype, name)
337 else:
338 params = []
339 for ptype, pname, parray, pspec in callspec:
340 params.append(self.Compose(ptype, pname, parray, pspec, '', True))
341 if func_as_ptr: name = '(*%s)' % name
342 out = '%s %s(%s)' % (rtype, name, ', '.join(params))
343 self.LogExit('Exit Compose: %s' % out)
344 return out
345
346 #
347 # GetSignature
348 #
349 # Returns the 'C' style signature of the object
350 # prefix - A prefix for the object's name
351 # func_as_ptr - Formats a function as a function pointer
352 #
[email protected]f7314732011-08-24 23:03:06353 def GetSignature(self, node, release, mode, prefix='', func_as_ptr=True):
[email protected]5eef2882011-07-19 00:08:54354 self.LogEnter('GetSignature %s %s as func=%s' % (node, mode, func_as_ptr))
[email protected]f7314732011-08-24 23:03:06355 rtype, name, arrayspec, callspec = self.GetComponents(node, release, mode)
[email protected]5eef2882011-07-19 00:08:54356 out = self.Compose(rtype, name, arrayspec, callspec, prefix, func_as_ptr)
357 self.LogExit('Exit GetSignature: %s' % out)
358 return out
359
[email protected]f7314732011-08-24 23:03:06360 # Define a Typedef.
361 def DefineTypedef(self, node, releases, prefix='', comment=False):
362 release = releases[0]
363 out = 'typedef %s;\n' % self.GetSignature(node, release, 'return',
364 prefix, True)
[email protected]5eef2882011-07-19 00:08:54365 self.Log('DefineTypedef: %s' % out)
366 return out
367
368 # Define an Enum.
[email protected]f7314732011-08-24 23:03:06369 def DefineEnum(self, node, releases, prefix='', comment=False):
[email protected]5eef2882011-07-19 00:08:54370 self.LogEnter('DefineEnum %s' % node)
371 unnamed = node.GetProperty('unnamed')
372 if unnamed:
373 out = 'enum {'
374 else:
375 out = 'typedef enum {'
376 name = '%s%s' % (prefix, node.GetName())
377 enumlist = []
378 for child in node.GetListOf('EnumItem'):
379 value = child.GetProperty('VALUE')
[email protected]f7314732011-08-24 23:03:06380 comment_txt = GetNodeComments(child, tabs=1)
[email protected]5eef2882011-07-19 00:08:54381 if value:
382 item_txt = '%s%s = %s' % (prefix, child.GetName(), value)
383 else:
384 item_txt = '%s%s' % (prefix, child.GetName())
385 enumlist.append('%s %s' % (comment_txt, item_txt))
386 self.LogExit('Exit DefineEnum')
387
388 if unnamed:
389 out = '%s\n%s\n};\n' % (out, ',\n'.join(enumlist))
390 else:
391 out = '%s\n%s\n} %s;\n' % (out, ',\n'.join(enumlist), name)
392 return out
393
[email protected]f7314732011-08-24 23:03:06394 def DefineMember(self, node, releases, prefix='', comment=False):
395 release = releases[0]
[email protected]5eef2882011-07-19 00:08:54396 self.LogEnter('DefineMember %s' % node)
[email protected]f7314732011-08-24 23:03:06397 out = '%s;' % self.GetSignature(node, release, 'store', '', True)
[email protected]5eef2882011-07-19 00:08:54398 self.LogExit('Exit DefineMember')
399 return out
400
[email protected]e8a83feb2011-08-25 21:04:26401 def DefineStructInternals(self, node, release, suffix='', comment=True):
[email protected]5eef2882011-07-19 00:08:54402 out = ''
[email protected]5eef2882011-07-19 00:08:54403 if node.GetProperty('union'):
[email protected]e8a83feb2011-08-25 21:04:26404 out += 'union %s%s {\n' % (node.GetName(), suffix)
[email protected]5eef2882011-07-19 00:08:54405 else:
[email protected]e8a83feb2011-08-25 21:04:26406 out += 'struct %s%s {\n' % (node.GetName(), suffix)
[email protected]5eef2882011-07-19 00:08:54407
408 # Generate Member Functions
409 members = []
410 for child in node.GetListOf('Member'):
[email protected]e8a83feb2011-08-25 21:04:26411 member = self.Define(child, [release], tabs=1, comment=comment)
[email protected]5eef2882011-07-19 00:08:54412 if not member:
413 continue
414 members.append(member)
415 out += '%s\n};\n' % '\n'.join(members)
[email protected]e8a83feb2011-08-25 21:04:26416 return out
417
418
419 def DefineStruct(self, node, releases, prefix='', comment=False):
420 self.LogEnter('DefineStruct %s' % node)
421 out = ''
422 build_list = node.GetUniqueReleases(releases)
423
424 # Build the most recent one with comments
425 out = self.DefineStructInternals(node, build_list[-1], comment=True)
426
427 # Build the rest without comments and with the version number appended
428 for rel in build_list[0:-1]:
429 ver_num = node.GetVersion(rel)
430 ver = ("_%s" % ver_num).replace('.', '_')
431 out += '\n' + self.DefineStructInternals(node, rel, suffix=ver,
432 comment=False)
433
[email protected]5eef2882011-07-19 00:08:54434 self.LogExit('Exit DefineStruct')
435 return out
436
[email protected]e8a83feb2011-08-25 21:04:26437
[email protected]5eef2882011-07-19 00:08:54438 #
439 # Copyright and Comment
440 #
441 # Generate a comment or copyright block
442 #
443 def Copyright(self, node, tabs=0):
444 lines = node.GetName().split('\n')
[email protected]f7314732011-08-24 23:03:06445 return CommentLines(lines, tabs)
[email protected]5eef2882011-07-19 00:08:54446
447
448 # Define a top level object.
[email protected]f7314732011-08-24 23:03:06449 def Define(self, node, releases, tabs=0, prefix='', comment=False):
450 if not node.InReleases(releases):
[email protected]5eef2882011-07-19 00:08:54451 return ''
[email protected]38c0f7e2011-06-02 01:16:30452
[email protected]f7314732011-08-24 23:03:06453 self.LogEnter('Define %s tab=%d prefix="%s"' % (node,tabs,prefix))
454 declmap = {
455 'Enum' : CGen.DefineEnum,
456 'Function' : CGen.DefineMember,
457 'Interface' : CGen.DefineStruct,
458 'Member' : CGen.DefineMember,
459 'Struct' : CGen.DefineStruct,
460 'Typedef' : CGen.DefineTypedef,
461 }
462
463 out = ''
464 func = declmap.get(node.cls)
465 if not func:
466 ErrOut.Log('Failed to define %s named %s' % (node.cls, node.GetName()))
467 define_txt = func(self, node, releases, prefix=prefix, comment=comment)
468
469 comment_txt = GetNodeComments(node, tabs=0)
470 if comment_txt and comment:
471 out += comment_txt
472 out += define_txt
473
474 tab = ' ' * tabs
475 lines = []
476 for line in out.split('\n'):
477 # Add indentation
478 line = tab + line
479 if len(line) > 80:
480 left = line.rfind('(') + 1
481 args = line[left:].split(',')
482 line_max = 0
483 for arg in args:
484 if len(arg) > line_max: line_max = len(arg)
485
486 if left + line_max >= 80:
487 space = '%s ' % tab
488 args = (',\n%s' % space).join([arg.strip() for arg in args])
489 lines.append('%s\n%s%s' % (line[:left], space, args))
490 else:
491 space = ' ' * (left - 1)
492 args = (',\n%s' % space).join(args)
493 lines.append('%s%s' % (line[:left], args))
494 else:
495 lines.append(line.rstrip())
496 self.LogExit('Exit Define')
497 return '\n'.join(lines)
498
[email protected]38c0f7e2011-06-02 01:16:30499# Clean a string representing an object definition and return then string
500# as a single space delimited set of tokens.
501def CleanString(instr):
502 instr = instr.strip()
503 instr = instr.split()
504 return ' '.join(instr)
505
506
507# Test a file, by comparing all it's objects, with their comments.
508def TestFile(filenode):
[email protected]5eef2882011-07-19 00:08:54509 cgen = CGen()
510
[email protected]38c0f7e2011-06-02 01:16:30511 errors = 0
[email protected]5eef2882011-07-19 00:08:54512 for node in filenode.GetChildren()[2:]:
[email protected]38c0f7e2011-06-02 01:16:30513 instr = node.GetOneOf('Comment')
[email protected]5eef2882011-07-19 00:08:54514 if not instr: continue
515 instr.Dump()
516 instr = CleanString(instr.GetName())
[email protected]38c0f7e2011-06-02 01:16:30517
[email protected]f7314732011-08-24 23:03:06518 outstr = cgen.Define(node, releases=['M14'])
[email protected]5eef2882011-07-19 00:08:54519 if GetOption('verbose'):
520 print outstr + '\n'
[email protected]38c0f7e2011-06-02 01:16:30521 outstr = CleanString(outstr)
522
523 if instr != outstr:
524 ErrOut.Log('Failed match of\n>>%s<<\n>>%s<<\nto:' % (instr, outstr))
525 node.Dump(1, comments=True)
526 errors += 1
527 return errors
528
529
[email protected]38c0f7e2011-06-02 01:16:30530# Build and resolve the AST and compare each file individual.
531def TestFiles(filenames):
532 if not filenames:
533 idldir = os.path.split(sys.argv[0])[0]
534 idldir = os.path.join(idldir, 'test_cgen', '*.idl')
535 filenames = glob.glob(idldir)
536
537 filenames = sorted(filenames)
[email protected]5eef2882011-07-19 00:08:54538 ast = ParseFiles(filenames)
[email protected]38c0f7e2011-06-02 01:16:30539
540 total_errs = 0
[email protected]5eef2882011-07-19 00:08:54541 for filenode in ast.GetListOf('File'):
[email protected]38c0f7e2011-06-02 01:16:30542 errs = TestFile(filenode)
543 if errs:
[email protected]5eef2882011-07-19 00:08:54544 ErrOut.Log('%s test failed with %d error(s).' %
545 (filenode.GetName(), errs))
[email protected]38c0f7e2011-06-02 01:16:30546 total_errs += errs
547
548 if total_errs:
[email protected]f0dd202f2011-06-07 01:36:01549 ErrOut.Log('Failed generator test.')
[email protected]38c0f7e2011-06-02 01:16:30550 else:
[email protected]f0dd202f2011-06-07 01:36:01551 InfoOut.Log('Passed generator test.')
[email protected]38c0f7e2011-06-02 01:16:30552 return total_errs
553
[email protected]38c0f7e2011-06-02 01:16:30554def Main(args):
555 filenames = ParseOptions(args)
[email protected]5eef2882011-07-19 00:08:54556 if GetOption('test'):
557 return TestFiles(filenames)
558 ast = ParseFiles(filenames)
559 for f in ast.GetListOf('File'):
560 if f.GetProperty('ERRORS') > 0:
561 print 'Skipping %s' % f.GetName()
562 continue
563 print DefineDepends(node)
564 for node in f.GetChildren()[2:]:
565 print Define(node, comment=True, prefix='tst_')
566
[email protected]38c0f7e2011-06-02 01:16:30567
568if __name__ == '__main__':
569 sys.exit(Main(sys.argv[1:]))
570