[M120 Migration][NaCl][PPFWK] Change plugin process name
[platform/framework/web/chromium-efl.git] / ppapi / generate_ppapi_size_checks.py
1 #!/usr/bin/env python
2 # Copyright 2012 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """This script should be run manually on occasion to make sure all PPAPI types
7 have appropriate size checking.
8 """
9
10 from __future__ import print_function
11
12 import optparse
13 import os
14 import subprocess
15 import sys
16
17
18 # The string that the PrintNamesAndSizes plugin uses to indicate a type is
19 # expected to have architecture-dependent size.
20 ARCH_DEPENDENT_STRING = "ArchDependentSize"
21
22
23 COPYRIGHT_STRING_C = (
24 """/* Copyright %s The Chromium Authors
25  * Use of this source code is governed by a BSD-style license that can be
26  * found in the LICENSE file.
27  *
28  * This file has compile assertions for the sizes of types that are dependent
29  * on the architecture for which they are compiled (i.e., 32-bit vs 64-bit).
30  */
31
32 """) % datetime.date.today().year
33
34
35 class SourceLocation(object):
36   """A class representing the source location of a definiton."""
37
38   def __init__(self, filename="", start_line=-1, end_line=-1):
39     self.filename = os.path.normpath(filename)
40     self.start_line = start_line
41     self.end_line = end_line
42
43
44 class TypeInfo(object):
45   """A class representing information about a C++ type.  It contains the
46   following fields:
47    - kind:  The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
48    - name:  The unmangled string name of the type.
49    - size:  The size in bytes of the type.
50    - arch_dependent:  True if the type may have architecture dependent size
51                       according to PrintNamesAndSizes.  False otherwise.  Types
52                       which are considered architecture-dependent from 32-bit
53                       to 64-bit are pointers, longs, unsigned longs, and any
54                       type that contains an architecture-dependent type.
55    - source_location:  A SourceLocation describing where the type is defined.
56    - target:  The target Clang was compiling when it found the type definition.
57               This is used only for diagnostic output.
58    - parsed_line:  The line which Clang output which was used to create this
59                    TypeInfo (as the info_string parameter to __init__).  This is
60                    used only for diagnostic output.
61   """
62
63   def __init__(self, info_string, target):
64     """Create a TypeInfo from a given info_string.  Also store the name of the
65     target for which the TypeInfo was first created just so we can print useful
66     error information.
67     info_string is a comma-delimited string of the following form:
68     kind,name,size,arch_dependent,source_file,start_line,end_line
69     Where:
70    - kind:  The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
71    - name:  The unmangled string name of the type.
72    - size:  The size in bytes of the type.
73    - arch_dependent:  'ArchDependentSize' if the type has architecture-dependent
74                       size, NotArchDependentSize otherwise.
75    - source_file:  The source file in which the type is defined.
76    - first_line:  The first line of the definition (counting from 0).
77    - last_line:  The last line of the definition (counting from 0).
78    This should match the output of the PrintNamesAndSizes plugin.
79    """
80     [self.kind, self.name, self.size, arch_dependent_string, source_file,
81         start_line, end_line] = info_string.split(',')
82     self.target = target
83     self.parsed_line = info_string
84     # Note that Clang counts line numbers from 1, but we want to count from 0.
85     self.source_location = SourceLocation(source_file,
86                                           int(start_line)-1,
87                                           int(end_line)-1)
88     self.arch_dependent = (arch_dependent_string == ARCH_DEPENDENT_STRING)
89
90
91 class FilePatch(object):
92   """A class representing a set of line-by-line changes to a particular file.
93   None of the changes are applied until Apply is called.  All line numbers are
94   counted from 0.
95   """
96
97   def __init__(self, filename):
98     self.filename = filename
99     self.linenums_to_delete = set()
100     # A dictionary from line number to an array of strings to be inserted at
101     # that line number.
102     self.lines_to_add = {}
103
104   def Delete(self, start_line, end_line):
105     """Make the patch delete the lines starting with |start_line| up to but not
106     including |end_line|.
107     """
108     self.linenums_to_delete |= set(range(start_line, end_line))
109
110   def Add(self, text, line_number):
111     """Add the given text before the text on the given line number."""
112     if line_number in self.lines_to_add:
113       self.lines_to_add[line_number].append(text)
114     else:
115       self.lines_to_add[line_number] = [text]
116
117   def Apply(self):
118     """Apply the patch by writing it to self.filename."""
119     # Read the lines of the existing file in to a list.
120     sourcefile = open(self.filename, "r")
121     file_lines = sourcefile.readlines()
122     sourcefile.close()
123     # Now apply the patch.  Our strategy is to keep the array at the same size,
124     # and just edit strings in the file_lines list as necessary.  When we delete
125     # lines, we just blank the line and keep it in the list.  When we add lines,
126     # we just prepend the added source code to the start of the existing line at
127     # that line number.  This way, all the line numbers we cached from calls to
128     # Add and Delete remain valid list indices, and we don't have to worry about
129     # maintaining any offsets.  Each element of file_lines at the end may
130     # contain any number of lines (0 or more) delimited by carriage returns.
131     for linenum_to_delete in self.linenums_to_delete:
132       file_lines[linenum_to_delete] = "";
133     for linenum, sourcelines in self.lines_to_add.items():
134       # Sort the lines we're adding so we get relatively consistent results.
135       sourcelines.sort()
136       # Prepend the new lines.  When we output
137       file_lines[linenum] = "".join(sourcelines) + file_lines[linenum]
138     newsource = open(self.filename, "w")
139     for line in file_lines:
140       newsource.write(line)
141     newsource.close()
142
143
144 def CheckAndInsert(typeinfo, typeinfo_map):
145   """Check if a TypeInfo exists already in the given map with the same name.  If
146   so, make sure the size is consistent.
147   - If the name exists but the sizes do not match, print a message and
148     exit with non-zero exit code.
149   - If the name exists and the sizes match, do nothing.
150   - If the name does not exist, insert the typeinfo in to the map.
151
152   """
153   # If the type is unnamed, ignore it.
154   if typeinfo.name == "":
155     return
156   # If the size is 0, ignore it.
157   elif int(typeinfo.size) == 0:
158     return
159   # If the type is not defined under ppapi, ignore it.
160   elif typeinfo.source_location.filename.find("ppapi") == -1:
161     return
162   # If the type is defined under GLES2, ignore it.
163   elif typeinfo.source_location.filename.find("GLES2") > -1:
164     return
165   # If the type is an interface (by convention, starts with PPP_ or PPB_),
166   # ignore it.
167   elif (typeinfo.name[:4] == "PPP_") or (typeinfo.name[:4] == "PPB_"):
168     return
169   elif typeinfo.name in typeinfo_map:
170     if typeinfo.size != typeinfo_map[typeinfo.name].size:
171       print("Error: '" + typeinfo.name + "' is", \
172           typeinfo_map[typeinfo.name].size, \
173           "bytes on target '" + typeinfo_map[typeinfo.name].target + \
174           "', but", typeinfo.size, "on target '" + typeinfo.target + "'")
175       print(typeinfo_map[typeinfo.name].parsed_line)
176       print(typeinfo.parsed_line)
177       sys.exit(1)
178     else:
179       # It's already in the map and the sizes match.
180       pass
181   else:
182     typeinfo_map[typeinfo.name] = typeinfo
183
184
185 def ProcessTarget(clang_command, target, types):
186   """Run clang using the given clang_command for the given target string.  Parse
187   the output to create TypeInfos for each discovered type.  Insert each type in
188   to the 'types' dictionary.  If the type already exists in the types
189   dictionary, make sure that the size matches what's already in the map.  If
190   not, exit with an error message.
191   """
192   p = subprocess.Popen(clang_command + " -triple " + target,
193                        shell=True,
194                        stdout=subprocess.PIPE)
195   lines = p.communicate()[0].split()
196   for line in lines:
197     typeinfo = TypeInfo(line, target)
198     CheckAndInsert(typeinfo, types)
199
200
201 def ToAssertionCode(typeinfo):
202   """Convert the TypeInfo to an appropriate C compile assertion.
203   If it's a struct (Record in Clang terminology), we want a line like this:
204     PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(<name>, <size>);\n
205   Enums:
206     PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(<name>, <size>);\n
207   Typedefs:
208     PP_COMPILE_ASSERT_SIZE_IN_BYTES(<name>, <size>);\n
209
210   """
211   line = "PP_COMPILE_ASSERT_"
212   if typeinfo.kind == "Enum":
213     line += "ENUM_"
214   elif typeinfo.kind == "Record":
215     line += "STRUCT_"
216   line += "SIZE_IN_BYTES("
217   line += typeinfo.name
218   line += ", "
219   line += typeinfo.size
220   line += ");\n"
221   return line
222
223
224 def IsMacroDefinedName(typename):
225   """Return true iff the given typename came from a PPAPI compile assertion."""
226   return typename.find("PP_Dummy_Struct_For_") == 0
227
228
229 def WriteArchSpecificCode(types, root, filename):
230   """Write a header file that contains a compile-time assertion for the size of
231      each of the given typeinfos, in to a file named filename rooted at root.
232   """
233   assertion_lines = [ToAssertionCode(typeinfo) for typeinfo in types]
234   assertion_lines.sort()
235   outfile = open(os.path.join(root, filename), "w")
236   header_guard = "PPAPI_TESTS_" + filename.upper().replace(".", "_") + "_"
237   outfile.write(COPYRIGHT_STRING_C)
238   outfile.write('#ifndef ' + header_guard + '\n')
239   outfile.write('#define ' + header_guard + '\n\n')
240   outfile.write('#include "ppapi/tests/test_struct_sizes.c"\n\n')
241   for line in assertion_lines:
242     outfile.write(line)
243   outfile.write('\n#endif  /* ' + header_guard + ' */\n')
244
245
246 def main(argv):
247   # See README file for example command-line invocation.  This script runs the
248   # PrintNamesAndSizes Clang plugin with 'test_struct_sizes.c' as input, which
249   # should include all C headers and all existing size checks.  It runs the
250   # plugin multiple times;  once for each of a set of targets, some 32-bit and
251   # some 64-bit.  It verifies that wherever possible, types have a consistent
252   # size on both platforms.  Types that can't easily have consistent size (e.g.
253   # ones that contain a pointer) are checked to make sure they are consistent
254   # for all 32-bit platforms and consistent on all 64-bit platforms, but the
255   # sizes on 32 vs 64 are allowed to differ.
256   #
257   # Then, if all the types have consistent size as expected, compile assertions
258   # are added to the source code.  Types whose size is independent of
259   # architectureacross have their compile assertions placed immediately after
260   # their definition in the C API header.  Types whose size differs on 32-bit
261   # vs 64-bit have a compile assertion placed in each of:
262   # ppapi/tests/arch_dependent_sizes_32.h and
263   # ppapi/tests/arch_dependent_sizes_64.h.
264   #
265   # Note that you should always check the results of the tool to make sure
266   # they are sane.
267   parser = optparse.OptionParser()
268   parser.add_option(
269       '-c', '--clang-path', dest='clang_path',
270       default=(''),
271       help='the path to the clang binary (default is to get it from your path)')
272   parser.add_option(
273       '-p', '--plugin', dest='plugin',
274       default='tests/clang/libPrintNamesAndSizes.so',
275       help='The path to the PrintNamesAndSizes plugin library.')
276   parser.add_option(
277       '--targets32', dest='targets32',
278       default='i386-pc-linux,arm-pc-linux,i386-pc-win32',
279       help='Which 32-bit target triples to provide to clang.')
280   parser.add_option(
281       '--targets64', dest='targets64',
282       default='x86_64-pc-linux,x86_64-pc-win',
283       help='Which 32-bit target triples to provide to clang.')
284   parser.add_option(
285       '-r', '--ppapi-root', dest='ppapi_root',
286       default='.',
287       help='The root directory of ppapi.')
288   options, args = parser.parse_args(argv)
289   if args:
290     parser.print_help()
291     print('ERROR: invalid argument')
292     sys.exit(1)
293
294   clang_executable = os.path.join(options.clang_path, 'clang')
295   clang_command = clang_executable + " -cc1" \
296       + " -load " + options.plugin \
297       + " -plugin PrintNamesAndSizes" \
298       + " -I" + os.path.join(options.ppapi_root, "..") \
299       + " " \
300       + os.path.join(options.ppapi_root, "tests", "test_struct_sizes.c")
301
302   # Dictionaries mapping type names to TypeInfo objects.
303   # Types that have size dependent on architecture, for 32-bit
304   types32 = {}
305   # Types that have size dependent on architecture, for 64-bit
306   types64 = {}
307   # Note that types32 and types64 should contain the same types, but with
308   # different sizes.
309
310   # Types whose size should be consistent regardless of architecture.
311   types_independent = {}
312
313   # Now run clang for each target.  Along the way, make sure architecture-
314   # dependent types are consistent sizes on all 32-bit platforms and consistent
315   # on all 64-bit platforms.
316   targets32 = options.targets32.split(',');
317   for target in targets32:
318     # For each 32-bit target, run the PrintNamesAndSizes Clang plugin to get
319     # information about all types in the translation unit, and add a TypeInfo
320     # for each of them to types32.  If any size mismatches are found,
321     # ProcessTarget will spit out an error and exit.
322     ProcessTarget(clang_command, target, types32)
323   targets64 = options.targets64.split(',');
324   for target in targets64:
325     # Do the same as above for each 64-bit target;  put all types in types64.
326     ProcessTarget(clang_command, target, types64)
327
328   # Now for each dictionary, find types whose size are consistent regardless of
329   # architecture, and move those in to types_independent.  Anywhere sizes
330   # differ, make sure they are expected to be architecture-dependent based on
331   # their structure.  If we find types which could easily be consistent but
332   # aren't, spit out an error and exit.
333   types_independent = {}
334   for typename, typeinfo32 in types32.items():
335     if (typename in types64):
336       typeinfo64 = types64[typename]
337       if (typeinfo64.size == typeinfo32.size):
338         # The types are the same size, so we can treat it as arch-independent.
339         types_independent[typename] = typeinfo32
340         del types32[typename]
341         del types64[typename]
342       elif (typeinfo32.arch_dependent or typeinfo64.arch_dependent):
343         # The type is defined in such a way that it would be difficult to make
344         # its size consistent.  E.g., it has pointers.  We'll leave it in the
345         # arch-dependent maps so that we can put arch-dependent size checks in
346         # test code.
347         pass
348       else:
349         # The sizes don't match, but there's no reason they couldn't.  It's
350         # probably due to an alignment mismatch between Win32/NaCl vs Linux32/
351         # Mac32.
352         print("Error: '" + typename + "' is", typeinfo32.size, \
353             "bytes on target '" + typeinfo32.target + \
354             "', but", typeinfo64.size, "on target '" + typeinfo64.target + "'")
355         print(typeinfo32.parsed_line)
356         print(typeinfo64.parsed_line)
357         sys.exit(1)
358     else:
359       print("WARNING:  Type '", typename, "' was defined for target '")
360       print(typeinfo32.target, ", but not for any 64-bit targets.")
361
362   # Now we have all the information we need to generate our static assertions.
363   # Types that have consistent size across architectures will have the static
364   # assertion placed immediately after their definition.  Types whose size
365   # depends on 32-bit vs 64-bit architecture will have checks placed in
366   # tests/arch_dependent_sizes_32/64.h.
367
368   # This dictionary maps file names to FilePatch objects.  We will add items
369   # to it as needed.  Each FilePatch represents a set of changes to make to the
370   # associated file (additions and deletions).
371   file_patches = {}
372
373   # Find locations of existing macros, and just delete them all.  Note that
374   # normally, only things in 'types_independent' need to be deleted, as arch-
375   # dependent checks exist in tests/arch_dependent_sizes_32/64.h, which are
376   # always completely over-ridden.  However, it's possible that a type that used
377   # to be arch-independent has changed to now be arch-dependent (e.g., because
378   # a pointer was added), and we want to delete the old check in that case.
379   for name, typeinfo in \
380       types_independent.items() + types32.items() + types64.items():
381     if IsMacroDefinedName(name):
382       sourcefile = typeinfo.source_location.filename
383       if sourcefile not in file_patches:
384         file_patches[sourcefile] = FilePatch(sourcefile)
385       file_patches[sourcefile].Delete(typeinfo.source_location.start_line,
386                                       typeinfo.source_location.end_line+1)
387
388   # Add a compile-time assertion for each type whose size is independent of
389   # architecture.  These assertions go immediately after the class definition.
390   for name, typeinfo in types_independent.items():
391     # Ignore dummy types that were defined by macros and also ignore types that
392     # are 0 bytes (i.e., typedefs to void).
393     if not IsMacroDefinedName(name) and typeinfo.size > 0:
394       sourcefile = typeinfo.source_location.filename
395       if sourcefile not in file_patches:
396         file_patches[sourcefile] = FilePatch(sourcefile)
397       # Add the assertion code just after the definition of the type.
398       # E.g.:
399       # struct Foo {
400       #   int32_t x;
401       # };
402       # PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(Foo, 4); <---Add this line
403       file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
404                                    typeinfo.source_location.end_line+1)
405
406   # Apply our patches.  This actually edits the files containing the definitions
407   # for the types in types_independent.
408   for filename, patch in file_patches.items():
409     patch.Apply()
410
411   # Write out a file of checks for 32-bit architectures and a separate file for
412   # 64-bit architectures.  These only have checks for types that are
413   # architecture-dependent.
414   c_source_root = os.path.join(options.ppapi_root, "tests")
415   WriteArchSpecificCode(types32.values(),
416                         c_source_root,
417                         "arch_dependent_sizes_32.h")
418   WriteArchSpecificCode(types64.values(),
419                         c_source_root,
420                         "arch_dependent_sizes_64.h")
421
422   return 0
423
424
425 if __name__ == '__main__':
426   sys.exit(main(sys.argv[1:]))