Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Side by Side Diff: ppapi/generate_ppapi_size_checks.py

Issue 5340003: Make a new test to enforce the sizes of all structs and enums in the C API.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ppapi/generate_ppapi_include_tests.py ('k') | ppapi/ppapi.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 # Copyright (c) 2010 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 """This script should be run manually on occasion to make sure all PPAPI types
8 have appropriate size checking.
9
10 """
11
12 import re
13 import optparse
14 import os
15 import posixpath
16 import subprocess
17 import sys
18
19
20 HAS_POINTER_STRING = "HasPointer"
21 ARCH_DEPENDENT_TYPES = set(["khronos_intptr_t",
22 "khronos_uintptr_t",
23 "khronos_ssize_t",
24 "khronos_usize_t",
25 "GLintptr",
26 "GLsizeiptr"
27 ])
28
29
30
31 class SourceLocation:
32
33 """A class representing the source location of a definiton."""
34
35 def __init__(self, filename="", start_line=-1, end_line=-1):
36 self.filename = os.path.normpath(filename)
37 self.start_line = start_line
38 self.end_line = end_line
39
40
41
42 class TypeInfo:
43
44 """A class representing information about a C++ type."""
45
46 def __init__(self, info_string, target):
47 [self.kind, self.name, self.size, has_pointer_string, source_file,
48 start_line, end_line] = info_string.split(',')
49 self.target = target
50 self.parsed_line = info_string
51 # Note that Clang counts line numbers from 1, but we want to count from 0.
52 self.source_location = SourceLocation(source_file,
53 int(start_line)-1,
54 int(end_line)-1)
55 # If the type is one of our known special cases, mark it as architecture-
56 # dependent.
57 if (self.name in ARCH_DEPENDENT_TYPES):
58 self.arch_dependent = True
59 # Otherwise, its size can be architecture dependent if it contains one or
60 # more pointers (or is a pointer).
61 else:
62 self.arch_dependent = (has_pointer_string == HAS_POINTER_STRING)
63 self.target = target
64 self.parsed_line = info_string
65
66
67
68 class FilePatch:
69
70 """A class representing a set of line-by-line changes to a particular file.
71 None
72 of the changes are applied until Apply is called. All line numbers are
73 counted from 0.
74 """
75
76 def __init__(self, filename):
77 self.filename = filename
78 self.linenums_to_delete = set()
79 # A dictionary from line number to an array of strings to be inserted at
80 # that line number.
81 self.lines_to_add = {}
82
83 def Delete(self, start_line, end_line):
84 """Make the patch delete the lines [start_line, end_line)."""
85 self.linenums_to_delete |= set(range(start_line, end_line))
86
87 def Add(self, text, line_number):
88 """Add the given text before the text on the given line number."""
89 if line_number in self.lines_to_add:
90 self.lines_to_add[line_number].append(text)
91 else:
92 self.lines_to_add[line_number] = [text]
93
94 def Apply(self):
95 """Apply the patch by writing it to self.filename"""
96 # Read the lines of the existing file in to a list.
97 sourcefile = open(self.filename, "r")
98 file_lines = sourcefile.readlines()
99 sourcefile.close()
100 # Now apply the patch. Our strategy is to keep the array at the same size,
101 # and just edit strings in the file_lines list as necessary. When we delete
102 # lines, we just blank the line and keep it in the list. When we add lines,
103 # we just prepend the added source code to the start of the existing line at
104 # that line number. This way, all the line numbers we cached from calls to
105 # Add and Delete remain valid list indices, and we don't have to worry about
106 # maintaining any offsets. Each element of file_lines at the end may
107 # contain any number of lines (0 or more) delimited by carriage returns.
108 for linenum_to_delete in self.linenums_to_delete:
109 file_lines[linenum_to_delete] = "";
110 for linenum, sourcelines in self.lines_to_add.items():
111 # Sort the lines we're adding so we get relatively consistent results.
112 sourcelines.sort()
113 # Prepend the new lines. When we output
114 file_lines[linenum] = "".join(sourcelines) + file_lines[linenum]
115 newsource = open(self.filename, "w")
116 for line in file_lines:
117 newsource.write(line)
118 newsource.close()
119
120
121 def CheckAndInsert(typeinfo, typeinfo_map):
122 """Check if a TypeInfo exists already in the given map with the same name. If
123 so, make sure the size is consistent.
124 - If the name exists but the sizes do not match, print a message and
125 exit with non-zero exit code.
126 - If the name exists and the sizes match, do nothing.
127 - If the name does not exist, insert the typeinfo in to the map.
128
129 """
130 # If the type is unnamed, ignore it.
131 if (typeinfo.name == ""):
132 return
133 # If the size is 0, ignore it.
134 elif (int(typeinfo.size) == 0):
135 return
136 # If the type is not defined under ppapi, ignore it.
137 elif (typeinfo.source_location.filename.find("ppapi") == -1):
138 return
139 # If the type is defined under GLES2, ignore it.
140 elif (typeinfo.source_location.filename.find("GLES2") > -1):
141 return
142 elif typeinfo.name in typeinfo_map:
143 if typeinfo.size != typeinfo_map[typeinfo.name].size:
144 print "Error: '" + typeinfo.name + "' is", \
145 typeinfo_map[typeinfo.name].size, \
146 "bytes on target '" + typeinfo_map[typeinfo.name].target + \
147 "', but", typeinfo.size, "on target '" + typeinfo.target + "'"
148 print typeinfo_map[typeinfo.name].parsed_line
149 print typeinfo.parsed_line
150 sys.exit(1)
151 else:
152 # It's already in the map and the sizes match.
153 pass
154 else:
155 typeinfo_map[typeinfo.name] = typeinfo
156
157
158 def ProcessTarget(clang_command, target, arch_types, ind_types):
159 """Run clang using the given clang_command for the given target string. Parse
160 the output to create TypeInfos for each discovered type. Insert each type in
161 to the appropriate dictionary. For each type that has architecture-dependent
162 size, insert it in to arch_types. Types with independent size go in to
163 ind_types. If the type already exists in the appropriate map, make sure that
164 the size matches what's already in the map. If not, the script terminates
165 with an error message.
166 """
167 p = subprocess.Popen(clang_command + " -triple " + target,
168 shell=True,
169 stdout=subprocess.PIPE)
170 lines = p.communicate()[0].split()
171 for line in lines:
172 typeinfo = TypeInfo(line, target)
173 # Put types which have architecture-specific size in to arch_types. All
174 # other types are 'architecture-independent' and get put in ind_types.
175 # in the appropraite dictionary.
176 if (typeinfo.arch_dependent):
177 CheckAndInsert(typeinfo, arch_types)
178 else:
179 CheckAndInsert(typeinfo, ind_types)
180
181
182 def ToAssertionCode(typeinfo):
183 """Convert the TypeInfo to an appropriate C compile assertion.
184 If it's a struct (Record in Clang terminology), we want a line like this:
185 PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(<name>, <size>);\n
186 Enums:
187 PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(<name>, <size>);\n
188 Typedefs:
189 PP_COMPILE_ASSERT_SIZE_IN_BYTES(<name>, <size>);\n
190
191 """
192 line = "PP_COMPILE_ASSERT_"
193 if typeinfo.kind == "Enum":
194 line += "ENUM_"
195 elif typeinfo.kind == "Record":
196 line += "STRUCT_"
197 line += "SIZE_IN_BYTES("
198 line += typeinfo.name
199 line += ", "
200 line += typeinfo.size
201 line += ");\n"
202 return line
203
204
205 def IsMacroDefinedName(typename):
206 """Return true iff the given typename came from a PPAPI compile assertion."""
207 return typename.find("PP_Dummy_Struct_For_") == 0
208
209
210 COPYRIGHT_STRING_C = \
211 """/* Copyright (c) 2010 The Chromium Authors. All rights reserved.
212 * Use of this source code is governed by a BSD-style license that can be
213 * found in the LICENSE file.
214 *
215 * This file has compile assertions for the sizes of types that are dependent
216 * on the architecture for which they are compiled (i.e., 32-bit vs 64-bit).
217 */
218
219 """
220
221
222 def WriteArchSpecificCode(types, root, filename):
223 """Write a header file that contains a compile-time assertion for the size of
224 each of the given typeinfos, in to a file named filename rooted at root.
225 """
226 assertion_lines = [ToAssertionCode(typeinfo) for typeinfo in types]
227 assertion_lines.sort()
228 outfile = open(os.path.join(root, filename), "w")
229 header_guard = "PPAPI_TESTS_" + filename.upper().replace(".", "_") + "_"
230 outfile.write(COPYRIGHT_STRING_C)
231 outfile.write('#ifndef ' + header_guard + '\n')
232 outfile.write('#define ' + header_guard + '\n\n')
233 outfile.write('#include "ppapi/tests/all_c_includes.h"\n\n')
234 for line in assertion_lines:
235 outfile.write(line)
236 outfile.write('\n#endif /* ' + header_guard + ' */\n')
237
238
239 def main(argv):
240 parser = optparse.OptionParser()
241 parser.add_option(
242 '-c', '--clang-path', dest='clang_path',
243 default=(''),
244 help='the path to the clang binary (default is to get it from your path)')
245 parser.add_option(
246 '-p', '--plugin', dest='plugin',
247 default='tests/clang/libPrintNamesAndSizes.so',
248 help='The path to the PrintNamesAndSizes plugin library.')
249 parser.add_option(
250 '--targets32', dest='targets32',
251 default='i386-pc-linux,arm-pc-linux,i386-pc-win32',
252 help='Which 32-bit target triples to provide to clang.')
253 parser.add_option(
254 '--targets64', dest='targets64',
255 default='x86_64-pc-linux,x86_64-pc-win',
256 help='Which 32-bit target triples to provide to clang.')
257 parser.add_option(
258 '-r', '--ppapi-root', dest='ppapi_root',
259 default='.',
260 help='The root directory of ppapi.')
261 options, args = parser.parse_args(argv)
262 if args:
263 parser.print_help()
264 print 'ERROR: invalid argument'
265 sys.exit(1)
266
267 clang_executable = os.path.join(options.clang_path, 'clang')
268 clang_command = clang_executable + " -cc1" \
269 + " -load " + options.plugin \
270 + " -plugin PrintNamesAndSizes" \
271 + " -I" + os.path.join(options.ppapi_root, "..") \
272 + " " \
273 + os.path.join(options.ppapi_root, "tests", "all_c_includes.h")
274
275 # Dictionaries mapping type names to TypeInfo objects.
276 # Types that have size dependent on architecture, for 32-bit
277 types32 = {}
278 # Types that have size dependent on architecture, for 64-bit
279 types64 = {}
280 # Note that types32 and types64 should contain the same types, but with
281 # different sizes.
282
283 # Types whose size should be consistent regardless of architecture.
284 types_independent = {}
285
286 # Now run clang for each target. Along the way, make sure architecture-
287 # dependent types are consistent sizes on all 32-bit platforms and consistent
288 # on all 64-bit platforms. Any types in 'types_independent' are checked for
289 # all targets to make sure their size is consistent across all of them.
290 targets32 = options.targets32.split(',');
291 for target in targets32:
292 ProcessTarget(clang_command, target, types32, types_independent)
293 targets64 = options.targets64.split(',');
294 for target in targets64:
295 ProcessTarget(clang_command, target, types64, types_independent)
296
297 # This dictionary maps file names to FilePatch objects.
298 file_patches = {}
299
300 # Find locations of existing macros, and just delete them all.
301 source_locations_to_delete = []
302 for name, typeinfo in types_independent.items():
303 if IsMacroDefinedName(name):
304 sourcefile = typeinfo.source_location.filename
305 if (sourcefile not in file_patches):
306 file_patches[sourcefile] = FilePatch(sourcefile)
307 file_patches[sourcefile].Delete(typeinfo.source_location.start_line,
308 typeinfo.source_location.end_line+1)
309
310 # Add a compile-time assertion for each type whose size is independent of
311 # architecture. These assertions go immediately after the class definition.
312 for name, typeinfo in types_independent.items():
313 # Ignore macros and types that are 0 bytes (i.e., typedefs to void)
314 if not IsMacroDefinedName(name) and typeinfo.size > 0:
315 sourcefile = typeinfo.source_location.filename
316 if (sourcefile not in file_patches):
317 file_patches[sourcefile] = FilePatch(sourcefile)
318 # Add the assertion code just after the definition of the type.
319 file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
320 typeinfo.source_location.end_line+1)
321
322 for filename, patch in file_patches.items():
323 patch.Apply()
324
325 c_source_root = os.path.join(options.ppapi_root, "tests")
326 WriteArchSpecificCode(types32.values(), c_source_root, "all_sizes_32.h")
327 WriteArchSpecificCode(types64.values(), c_source_root, "all_sizes_64.h")
328
329 return 0
330
331
332 if __name__ == '__main__':
333 sys.exit(main(sys.argv[1:]))
334
OLDNEW
« no previous file with comments | « ppapi/generate_ppapi_include_tests.py ('k') | ppapi/ppapi.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698