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

Side by Side Diff: ppapi/generate_ppapi_size_checks.py

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

Powered by Google App Engine
This is Rietveld 408576698