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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
|
#!/usr/bin/python2.4
# Copyright 2009, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""O3D interface class generator.
This module generates the O3D interface classes.
"""
import os
import syntax_tree
import cpp_utils
import naming
class CircularDefinition(Exception):
"""Raised when a circular type definition is found."""
def __init__(self, type_defn):
Exception.__init__(self)
self.type = type_defn
class BadForwardDeclaration(Exception):
"""Raised when an impossible forward declaration is required."""
def ForwardDecl(section, type_defn):
"""Emits the forward declaration of a type, if possible.
Inner types (declared inside a class) cannot be forward-declared.
Only classes can be forward-declared.
Args:
section: the section to emit to.
type_defn: the Definition for the type to forward-declare.
Raises:
BadForwardDeclaration: an inner type or a non-class was passed as an
argument.
"""
# inner types cannot be forward-declared
if type_defn.parent.defn_type != 'Namespace':
raise BadForwardDeclaration
stack = type_defn.GetParentScopeStack()
if type_defn.defn_type == 'Class':
for scope in stack:
if scope.name:
section.PushNamespace(scope.name)
section.EmitCode('class %s;' % type_defn.name)
for scope in stack[::-1]:
if scope.name:
section.PopNamespace()
else:
raise BadForwardDeclaration
class O3DInterfaceGenerator(object):
"""Header generator class.
This class takes care of the details of generating a C++ header file
containing all the definitions from a syntax tree.
It contains a list of functions named after each of the Definition classes in
syntax_tree, with a common signature. The appropriate function will be called
for each definition, to generate the code for that definition.
"""
class GenerationContext(object):
def __init__(self, header_scope, cpp_scope, header_section, cpp_section):
self.header_scope = header_scope
self.header_section = header_section
self.cpp_scope = cpp_scope
self.cpp_section = cpp_section
self.needed_decl = set()
self.needed_defn = set()
self.emitted_defn = set()
def Fork(self, header_scope, cpp_scope, header_section, cpp_section):
new_context = type(self)(header_scope, cpp_scope, header_section,
cpp_section)
new_context.needed_decl = self.needed_decl
new_context.needed_defn = self.needed_defn
new_context.emitted_defn = self.emitted_defn
return new_context
def CheckType(self, need_defn, type_defn):
"""Checks for the definition or declaration of a type.
This function helps keeping track of which types are needed to be defined
or declared in the C++ file before other definitions can happen. If the
definition is needed (and is not in this C++ header file), the proper
include will be generated. If the type only needs to be forward-declared,
the forward declaration will be output (if the type is not otherwise
defined).
Args:
need_defn: a boolean, True if the C++ definition of the type is needed,
False if only the declaration is needed.
type_defn: the Definition of the type to check.
"""
while type_defn.defn_type == 'Array':
# arrays are implicitly defined with their data type
type_defn = type_defn.data_type
if need_defn:
if type_defn not in self.emitted_defn:
self.needed_defn.add(type_defn)
else:
if type_defn in self.emitted_defn:
return
if type_defn.parent and type_defn.parent.defn_type != 'Namespace':
# inner type: need the definition of the parent.
self.CheckType(True, type_defn.parent)
else:
# only forward-declare classes.
# typedefs could be forward-declared by emitting the definition again,
# but this necessitates the source type to be forward-declared before.
# TODO: see if it is possible to find a proper ordering that let
# us forward-declare typedefs instead of needing to include the
# definition.
if type_defn.defn_type == 'Class':
self.needed_decl.add(type_defn)
else:
self.needed_defn.add(type_defn)
def __init__(self, output_dir, namespace):
self._output_dir = output_dir
self._void_type = namespace.LookupTypeRecursive('void')
def GetHeaderFile(self, idl_file):
return idl_file.source.split('.')[0] + '.h'
def GetCppFile(self, idl_file):
return idl_file.source.split('.')[0] + '.cc'
def GetInterfaceInclude(self, type_defn):
if self.NeedsGlue(type_defn):
return self.GetHeaderFile(type_defn.source.file)
else:
return type_defn.GetDefinitionInclude()
def GetImplementationInclude(self, type_defn):
return type_defn.GetDefinitionInclude()
def IsVoid(self, type_defn):
return type_defn.GetFinalType() == self._void_type
def NeedsGlue(self, obj):
return obj.LookupBindingModel() == 'o3d' or 'glue_iface' in obj.attributes
def GetSectionFromAttributes(self, parent_section, defn):
"""Gets the code section appropriate for a given definition.
Classes have 3 definition sections: private, protected and public. This
function will pick one of the sections, based on the attributes of the
definition, if its parent is a class. For other scopes (namespaces) it will
return the parent scope main section.
Args:
parent_section: the main section for the parent scope.
defn: the definition.
Returns:
the appropriate section.
"""
if defn.parent and defn.parent.defn_type == 'Class':
if 'private' in defn.attributes:
return parent_section.GetSection('private:') or parent_section
elif 'protected' in defn.attributes:
return parent_section.GetSection('protected:') or parent_section
else:
return parent_section.GetSection('public:') or parent_section
else:
return parent_section
def Verbatim(self, context, obj):
"""Generates the code for a Verbatim definition.
Verbatim definitions being written for a particular type of output file,
this function will check the 'verbatim' attribute, and only emit the
verbatim code if it is 'cpp_header'.
Args:
parent_section: the main section of the parent scope.
obj: the Verbatim definition.
"""
try:
verbatim_attr = obj.attributes['verbatim']
except KeyError:
source = obj.source
print ('%s:%d ignoring verbatim with no verbatim attribute' %
(source.file.source, source.line))
return
if verbatim_attr == 'o3d_iface_header':
section = self.GetSectionFromAttributes(context.header_section, obj)
section.EmitCode(obj.text)
elif verbatim_attr == 'o3d_iface_cpp':
context.cpp_section.EmitCode(obj.text)
def Typedef(self, context, obj):
"""Generates the code for a Typedef definition.
Args:
parent_section: the main section of the parent scope.
obj: the Typedef definition.
Returns:
a list of (boolean, Definition) pairs, of all the types that need
to be declared (boolean is False) or defined (boolean is True) before
this definition.
"""
section = self.GetSectionFromAttributes(context.header_section, obj)
bm = obj.type.binding_model
type_string, unused_need_defn = bm.CppTypedefString(context.header_scope,
obj.type)
context.CheckType(True, obj.type)
section.EmitCode('typedef %s %s;' % (type_string, obj.name))
def Variable(self, context, obj):
"""Generates the code for a Variable definition.
This function will generate the member/global variable declaration, as well
as the setter/getter functions if specified in the attributes.
Args:
parent_section: the main section of the parent scope.
obj: the Variable definition.
"""
bm = obj.type.binding_model
type_string, need_defn = bm.CppMemberString(context.header_scope, obj.type)
context.CheckType(need_defn, obj.type)
need_glue = self.NeedsGlue(obj) or (obj.parent.is_type and
self.NeedsGlue(obj.parent));
getter_attributes = {}
if 'static' in obj.attributes:
getter_attributes['static'] = obj.attributes['static']
static = 'static '
else:
static = ''
for attr in ['public', 'protected', 'private']:
if attr in obj.attributes:
getter_attributes[attr] = obj.attributes[attr]
if not need_glue:
if obj.parent.defn_type == 'Class':
if 'field_access' in obj.attributes:
member_section = context.header_section.GetSection(
obj.attributes['field_access'] + ':')
else:
member_section = context.header_section.GetSection('private:')
else:
member_section = context.header_section
field_name = naming.Normalize(obj.name, naming.LowerTrailing)
member_section.EmitCode('%s%s %s;' % (static, type_string, field_name))
if 'getter' in obj.attributes:
func = obj.MakeGetter(getter_attributes, cpp_utils.GetGetterName(obj))
if need_glue:
self.FunctionGlue(context, func)
impl = None
else:
impl = ' { return %s; }' % field_name
self.FunctionDecl(context, func, impl)
if 'setter' in obj.attributes:
func = obj.MakeSetter(getter_attributes, cpp_utils.GetSetterName(obj))
if need_glue:
self.FunctionGlue(context, func)
impl = None
else:
impl = ' { %s = %s; }' % (field_name, obj.name)
self.FunctionDecl(context, func, impl)
def GetParamsDecls(self, scope, obj, context=None):
param_strings = []
for p in obj.params:
bm = p.type.binding_model
if p.mutable:
text, need_defn = bm.CppMutableParameterString(scope, p.type)
else:
text, need_defn = bm.CppParameterString(scope, p.type)
if context:
context.CheckType(need_defn, p.type)
param_strings += ['%s %s' % (text, p.name)]
return ', '.join(param_strings)
def FunctionDecl(self, context, obj, impl_string=None):
section = self.GetSectionFromAttributes(context.header_section, obj)
if not impl_string:
impl_string = ';'
params_string = self.GetParamsDecls(context.header_scope, obj, context)
prefix_strings = []
suffix_strings = []
for attrib in ['static', 'virtual', 'inline']:
if attrib in obj.attributes:
prefix_strings.append(attrib)
if prefix_strings:
prefix_strings.append('')
if 'const' in obj.attributes:
suffix_strings.append('const')
if 'pure' in obj.attributes:
suffix_strings.append('= 0')
if suffix_strings:
suffix_strings.insert(0, '')
prefix = ' '.join(prefix_strings)
suffix = ' '.join(suffix_strings)
if obj.type:
bm = obj.type.binding_model
return_type, need_defn = bm.CppReturnValueString(context.header_scope,
obj.type)
context.CheckType(need_defn, obj.type)
section.EmitCode('%s%s %s(%s)%s%s' % (prefix, return_type, obj.name,
params_string, suffix, impl_string))
else:
section.EmitCode('%s%s(%s)%s%s' % (prefix, obj.name, params_string,
suffix, impl_string))
def FunctionGlue(self, context, obj):
if not obj.type:
# TODO autogen a factory
return
if 'pure' in obj.attributes:
return
if obj.parent.is_type:
func_name = '%s::%s' % (obj.parent.name, obj.name)
if 'static' in obj.attributes:
call_prefix = 'impl::' + func_name
else:
# this_call
if self.NeedsGlue(obj.parent):
call_prefix = 'GetImpl()->'
else:
call_prefix = ''
else:
call_prefix = ''
func_name = obj.name
params_string = self.GetParamsDecls(context.cpp_scope, obj)
param_exprs = []
for p in obj.params:
if self.NeedsGlue(p.type):
param_exprs.append('%s->GetImpl()' % p.name)
else:
param_exprs.append(p.name)
if not self.IsVoid(obj.type):
return_prefix = 'return '
if self.NeedsGlue(obj.type):
return_suffix = '->GetIface()'
else:
return_suffix = ''
else:
return_prefix = ''
return_suffix = ''
bm = obj.type.binding_model
return_type, unused = bm.CppReturnValueString(context.header_scope,
obj.type)
if 'const' in obj.attributes:
func_suffix = ' const'
else:
func_suffix = ''
section = context.cpp_section
section.EmitCode('%s %s(%s)%s {' % (return_type, func_name, params_string,
func_suffix))
section.EmitCode('%s%s%s(%s)%s;' % (return_prefix, call_prefix, obj.name,
', '.join(param_exprs), return_suffix))
section.EmitCode('}')
def Function(self, context, obj):
"""Generates the code for a Function definition.
Args:
parent_section: the main section of the parent scope.
obj: the Function definition.
"""
self.FunctionDecl(context, obj)
if self.NeedsGlue(obj) or (obj.parent.is_type and
self.NeedsGlue(obj.parent)):
self.FunctionGlue(context, obj)
def Class(self, context, obj):
"""Generates the code for a Class definition.
This function will recursively generate the code for all the definitions
inside the class. These definitions will be output into one of 3 sections
(private, protected, public), depending on their attributes. These
individual sections will only be output if they are not empty.
Args:
parent_section: the main section of the parent scope.
obj: the Class definition.
"""
h_section = self.GetSectionFromAttributes(context.header_section,
obj).CreateSection(obj.name)
c_section = context.cpp_section
need_glue = self.NeedsGlue(obj)
if need_glue:
h_section.PushNamespace('impl')
h_section.EmitCode('class %s;' % obj.name)
h_section.PopNamespace()
h_section.EmitCode('')
if obj.base_type:
bm = obj.base_type.binding_model
h_section.EmitCode('class %s : public %s {' %
(obj.name, bm.CppBaseClassString(context.header_scope,
obj.base_type)))
context.CheckType(True, obj.base_type)
else:
h_section.EmitCode('class %s {' % obj.name)
public_section = h_section.CreateSection('public:')
protected_section = h_section.CreateSection('protected:')
private_section = h_section.CreateSection('private:')
new_context = context.Fork(obj, context.cpp_scope, h_section, c_section)
self.DefinitionList(new_context, obj.defn_list)
if need_glue:
public_section.EmitCode('impl::%s *GetImpl();' % obj.name)
c_section.EmitCode('impl::%s *%s::GetImpl() {' % (obj.name, obj.name))
c_section.EmitCode('return static_cast<impl::%s *>(impl_);' % obj.name)
c_section.EmitCode('}')
if not public_section.IsEmpty():
public_section.AddPrefix('public:')
if not protected_section.IsEmpty():
protected_section.AddPrefix('protected:')
if not private_section.IsEmpty():
private_section.AddPrefix('private:')
h_section.EmitCode('};')
def Namespace(self, context, obj):
"""Generates the code for a Namespace definition.
This function will recursively generate the code for all the definitions
inside the namespace.
Args:
parent_section: the main section of the parent scope.
obj: the Namespace definition.
"""
context.header_section.PushNamespace(obj.name)
context.cpp_section.PushNamespace(obj.name)
new_context = context.Fork(obj, obj, context.header_section,
context.cpp_section)
self.DefinitionList(new_context, obj.defn_list)
context.header_section.PopNamespace()
context.cpp_section.PopNamespace()
def Typename(self, context, obj):
"""Generates the code for a Typename definition.
Since typenames (undefined types) cannot be expressed in C++, this function
will not output code. The definition may be output with a verbatim section.
Args:
parent_section: the main section of the parent scope.
scope: the parent scope.
obj: the Typename definition.
"""
def Enum(self, context, obj):
"""Generates the code for an Enum definition.
Args:
parent_section: the main section of the parent scope.
scope: the parent scope.
obj: the Enum definition.
"""
section = self.GetSectionFromAttributes(context.header_section, obj)
section.EmitCode('enum %s {' % obj.name)
for value in obj.values:
if value.value is None:
section.EmitCode('%s,' % value.name)
else:
section.EmitCode('%s = %s,' % (value.name, value.value))
section.EmitCode('};')
def DefinitionList(self, context, defn_list):
"""Generates the code for all the definitions in a list.
Args:
parent_section: the main section of the parent scope.
scope: the parent scope.
defn_list: the list of definitions.
"""
for obj in defn_list:
context.emitted_defn.add(obj)
# array types are implicitly defined
for k in obj.array_defns:
context.emitted_defn.add(obj.array_defns[k])
getattr(self, obj.defn_type)(context, obj)
def Generate(self, idl_file, namespace, defn_list):
"""Generates the header file.
Args:
idl_file: the source IDL file containing the definitions, as a
idl_parser.File instance.
namespace: a Definition for the global namespace.
defn_list: the list of top-level definitions.
Returns:
a cpp_utils.CppFileWriter that contains the C++ header file code.
Raises:
CircularDefinition: circular definitions were found in the file.
"""
all_defn = syntax_tree.GetObjectsRecursive(defn_list)
need_glue = False
for defn in all_defn:
if self.NeedsGlue(defn):
need_glue = True
break
if not need_glue:
return []
header_writer = cpp_utils.CppFileWriter(
'%s/%s' % (self._output_dir, self.GetHeaderFile(idl_file)), True)
cpp_writer = cpp_utils.CppFileWriter(
'%s/%s' % (self._output_dir, self.GetCppFile(idl_file)), True)
h_decl_section = header_writer.CreateSection('decls')
h_code_section = header_writer.CreateSection('defns')
c_code_section = cpp_writer.CreateSection('glue')
context = self.GenerationContext(namespace, namespace, h_code_section,
c_code_section)
self.DefinitionList(context, defn_list)
context.needed_decl -= context.needed_defn
if context.needed_decl:
for type_defn in context.needed_decl:
# TODO: sort by namespace so that we don't open and close them
# more than necessary.
ForwardDecl(h_decl_section, type_defn)
h_decl_section.EmitCode('')
for type_defn in context.needed_defn:
if type_defn.source.file == idl_file:
raise CircularDefinition(type_defn)
h_includes = set(self.GetInterfaceInclude(type_defn)
for type_defn in context.needed_defn)
c_includes = set(self.GetImplementationInclude(type_defn)
for type_defn in context.emitted_defn
if self.NeedsGlue(type_defn))
c_includes.add(self.GetHeaderFile(idl_file))
for include_file in h_includes:
if include_file is not None:
header_writer.AddInclude(include_file)
for include_file in c_includes:
if include_file is not None:
cpp_writer.AddInclude(include_file)
return [header_writer, cpp_writer]
def ProcessFiles(output_dir, pairs, namespace):
"""Generates the headers for all input files.
Args:
output_dir: the output directory.
pairs: a list of (idl_parser.File, syntax_tree.Definition list) describing
the list of top-level definitions in each source file.
namespace: a syntax_tree.Namespace for the global namespace.
Returns:
a list of cpp_utils.CppFileWriter, one for each output file.
"""
output_dir = output_dir + '/iface'
if not os.access(output_dir + '/', os.F_OK):
os.makedirs(output_dir)
generator = O3DInterfaceGenerator(output_dir, namespace)
writer_list = []
for (f, defn) in pairs:
writer_list += generator.Generate(f, namespace, defn)
return writer_list
def main():
pass
if __name__ == '__main__':
main()
|