| Index: ppapi/generate_ppapi_size_checks.py
|
| ===================================================================
|
| --- ppapi/generate_ppapi_size_checks.py (revision 69746)
|
| +++ ppapi/generate_ppapi_size_checks.py (working copy)
|
| @@ -15,20 +15,9 @@
|
| import sys
|
|
|
|
|
| -# The string that the PrintNamesAndSizes plugin uses to indicate a type is or
|
| -# contains a pointer.
|
| -HAS_POINTER_STRING = "HasPointer"
|
| -# These are types that don't include a pointer but nonetheless have
|
| -# architecture-dependent size. They all are ultimately typedefs to 'long' or
|
| -# 'unsigned long'. If there get to be too many types that use 'long' or
|
| -# 'unsigned long', we can add detection of them to the plugin to automate this.
|
| -ARCH_DEPENDENT_TYPES = set(["khronos_intptr_t",
|
| - "khronos_uintptr_t",
|
| - "khronos_ssize_t",
|
| - "khronos_usize_t",
|
| - "GLintptr",
|
| - "GLsizeiptr"
|
| - ])
|
| +# The string that the PrintNamesAndSizes plugin uses to indicate a type is
|
| +# expected to have architecture-dependent size.
|
| +ARCH_DEPENDENT_STRING = "ArchDependentSize"
|
|
|
|
|
|
|
| @@ -45,10 +34,42 @@
|
|
|
| class TypeInfo:
|
|
|
| - """A class representing information about a C++ type."""
|
| + """A class representing information about a C++ type. It contains the
|
| + following fields:
|
| + - kind: The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
|
| + - name: The unmangled string name of the type.
|
| + - size: The size in bytes of the type.
|
| + - arch_dependent: True if the type may have architecture dependent size
|
| + according to PrintNamesAndSizes. False otherwise. Types
|
| + which are considered architecture-dependent from 32-bit
|
| + to 64-bit are pointers, longs, unsigned longs, and any
|
| + type that contains an architecture-dependent type.
|
| + - source_location: A SourceLocation describing where the type is defined.
|
| + - target: The target Clang was compiling when it found the type definition.
|
| + This is used only for diagnostic output.
|
| + - parsed_line: The line which Clang output which was used to create this
|
| + TypeInfo (as the info_string parameter to __init__). This is
|
| + used only for diagnostic output.
|
| + """
|
|
|
| def __init__(self, info_string, target):
|
| - [self.kind, self.name, self.size, has_pointer_string, source_file,
|
| + """Create a TypeInfo from a given info_string. Also store the name of the
|
| + target for which the TypeInfo was first created just so we can print useful
|
| + error information.
|
| + info_string is a comma-delimited string of the following form:
|
| + kind,name,size,arch_dependent,source_file,start_line,end_line
|
| + Where:
|
| + - kind: The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
|
| + - name: The unmangled string name of the type.
|
| + - size: The size in bytes of the type.
|
| + - arch_dependent: 'ArchDependentSize' if the type has architecture-dependent
|
| + size, NotArchDependentSize otherwise.
|
| + - source_file: The source file in which the type is defined.
|
| + - first_line: The first line of the definition (counting from 0).
|
| + - last_line: The last line of the definition (counting from 0).
|
| + This should match the output of the PrintNamesAndSizes plugin.
|
| + """
|
| + [self.kind, self.name, self.size, arch_dependent_string, source_file,
|
| start_line, end_line] = info_string.split(',')
|
| self.target = target
|
| self.parsed_line = info_string
|
| @@ -56,24 +77,13 @@
|
| self.source_location = SourceLocation(source_file,
|
| int(start_line)-1,
|
| int(end_line)-1)
|
| - # If the type is one of our known special cases, mark it as architecture-
|
| - # dependent.
|
| - if self.name in ARCH_DEPENDENT_TYPES:
|
| - self.arch_dependent = True
|
| - # Otherwise, its size can be architecture dependent if it contains one or
|
| - # more pointers (or is a pointer).
|
| - else:
|
| - self.arch_dependent = (has_pointer_string == HAS_POINTER_STRING)
|
| - self.target = target
|
| - self.parsed_line = info_string
|
| + self.arch_dependent = (arch_dependent_string == ARCH_DEPENDENT_STRING)
|
|
|
|
|
| -
|
| class FilePatch:
|
|
|
| """A class representing a set of line-by-line changes to a particular file.
|
| - None
|
| - of the changes are applied until Apply is called. All line numbers are
|
| + None of the changes are applied until Apply is called. All line numbers are
|
| counted from 0.
|
| """
|
|
|
| @@ -85,7 +95,9 @@
|
| self.lines_to_add = {}
|
|
|
| def Delete(self, start_line, end_line):
|
| - """Make the patch delete the lines [start_line, end_line)."""
|
| + """Make the patch delete the lines starting with |start_line| up to but not
|
| + including |end_line|.
|
| + """
|
| self.linenums_to_delete |= set(range(start_line, end_line))
|
|
|
| def Add(self, text, line_number):
|
| @@ -96,7 +108,7 @@
|
| self.lines_to_add[line_number] = [text]
|
|
|
| def Apply(self):
|
| - """Apply the patch by writing it to self.filename"""
|
| + """Apply the patch by writing it to self.filename."""
|
| # Read the lines of the existing file in to a list.
|
| sourcefile = open(self.filename, "r")
|
| file_lines = sourcefile.readlines()
|
| @@ -163,14 +175,12 @@
|
| typeinfo_map[typeinfo.name] = typeinfo
|
|
|
|
|
| -def ProcessTarget(clang_command, target, arch_types, ind_types):
|
| +def ProcessTarget(clang_command, target, types):
|
| """Run clang using the given clang_command for the given target string. Parse
|
| the output to create TypeInfos for each discovered type. Insert each type in
|
| - to the appropriate dictionary. For each type that has architecture-dependent
|
| - size, insert it in to arch_types. Types with independent size go in to
|
| - ind_types. If the type already exists in the appropriate map, make sure that
|
| - the size matches what's already in the map. If not, the script terminates
|
| - with an error message.
|
| + to the 'types' dictionary. If the type already exists in the types
|
| + dictionary, make sure that the size matches what's already in the map. If
|
| + not, exit with an error message.
|
| """
|
| p = subprocess.Popen(clang_command + " -triple " + target,
|
| shell=True,
|
| @@ -178,13 +188,7 @@
|
| lines = p.communicate()[0].split()
|
| for line in lines:
|
| typeinfo = TypeInfo(line, target)
|
| - # Put types which have architecture-specific size in to arch_types. All
|
| - # other types are 'architecture-independent' and get put in ind_types.
|
| - # in the appropraite dictionary.
|
| - if typeinfo.arch_dependent:
|
| - CheckAndInsert(typeinfo, arch_types)
|
| - else:
|
| - CheckAndInsert(typeinfo, ind_types)
|
| + CheckAndInsert(typeinfo, types)
|
|
|
|
|
| def ToAssertionCode(typeinfo):
|
| @@ -245,6 +249,26 @@
|
|
|
|
|
| def main(argv):
|
| + # See README file for example command-line invocation. This script runs the
|
| + # PrintNamesAndSizes Clang plugin with 'test_struct_sizes.c' as input, which
|
| + # should include all C headers and all existing size checks. It runs the
|
| + # plugin multiple times; once for each of a set of targets, some 32-bit and
|
| + # some 64-bit. It verifies that wherever possible, types have a consistent
|
| + # size on both platforms. Types that can't easily have consistent size (e.g.
|
| + # ones that contain a pointer) are checked to make sure they are consistent
|
| + # for all 32-bit platforms and consistent on all 64-bit platforms, but the
|
| + # sizes on 32 vs 64 are allowed to differ.
|
| + #
|
| + # Then, if all the types have consistent size as expected, compile assertions
|
| + # are added to the source code. Types whose size is independent of
|
| + # architectureacross have their compile assertions placed immediately after
|
| + # their definition in the C API header. Types whose size differs on 32-bit
|
| + # vs 64-bit have a compile assertion placed in each of:
|
| + # ppapi/tests/arch_dependent_sizes_32.h and
|
| + # ppapi/tests/arch_dependent_sizes_64.h.
|
| + #
|
| + # Note that you should always check the results of the tool to make sure
|
| + # they are sane.
|
| parser = optparse.OptionParser()
|
| parser.add_option(
|
| '-c', '--clang-path', dest='clang_path',
|
| @@ -293,19 +317,70 @@
|
|
|
| # Now run clang for each target. Along the way, make sure architecture-
|
| # dependent types are consistent sizes on all 32-bit platforms and consistent
|
| - # on all 64-bit platforms. Any types in 'types_independent' are checked for
|
| - # all targets to make sure their size is consistent across all of them.
|
| + # on all 64-bit platforms.
|
| targets32 = options.targets32.split(',');
|
| for target in targets32:
|
| - ProcessTarget(clang_command, target, types32, types_independent)
|
| + # For each 32-bit target, run the PrintNamesAndSizes Clang plugin to get
|
| + # information about all types in the translation unit, and add a TypeInfo
|
| + # for each of them to types32. If any size mismatches are found,
|
| + # ProcessTarget will spit out an error and exit.
|
| + ProcessTarget(clang_command, target, types32)
|
| targets64 = options.targets64.split(',');
|
| for target in targets64:
|
| - ProcessTarget(clang_command, target, types64, types_independent)
|
| + # Do the same as above for each 64-bit target; put all types in types64.
|
| + ProcessTarget(clang_command, target, types64)
|
|
|
| - # This dictionary maps file names to FilePatch objects.
|
| + # Now for each dictionary, find types whose size are consistent regardless of
|
| + # architecture, and move those in to types_independent. Anywhere sizes
|
| + # differ, make sure they are expected to be architecture-dependent based on
|
| + # their structure. If we find types which could easily be consistent but
|
| + # aren't, spit out an error and exit.
|
| + types_independent = {}
|
| + for typename, typeinfo32 in types32.items():
|
| + if (typename in types64):
|
| + typeinfo64 = types64[typename]
|
| + if (typeinfo64.size == typeinfo32.size):
|
| + # The types are the same size, so we can treat it as arch-independent.
|
| + types_independent[typename] = typeinfo32
|
| + del types32[typename]
|
| + del types64[typename]
|
| + elif (typeinfo32.arch_dependent or typeinfo64.arch_dependent):
|
| + # The type is defined in such a way that it would be difficult to make
|
| + # its size consistent. E.g., it has pointers. We'll leave it in the
|
| + # arch-dependent maps so that we can put arch-dependent size checks in
|
| + # test code.
|
| + pass
|
| + else:
|
| + # The sizes don't match, but there's no reason they couldn't. It's
|
| + # probably due to an alignment mismatch between Win32/NaCl vs Linux32/
|
| + # Mac32.
|
| + print "Error: '" + typename + "' is", typeinfo32.size, \
|
| + "bytes on target '" + typeinfo32.target + \
|
| + "', but", typeinfo64.size, "on target '" + typeinfo64.target + "'"
|
| + print typeinfo32.parsed_line
|
| + print typeinfo64.parsed_line
|
| + sys.exit(1)
|
| + else:
|
| + print "WARNING: Type '", typename, "' was defined for target '",
|
| + print typeinfo32.target, ", but not for any 64-bit targets."
|
| +
|
| + # Now we have all the information we need to generate our static assertions.
|
| + # Types that have consistent size across architectures will have the static
|
| + # assertion placed immediately after their definition. Types whose size
|
| + # depends on 32-bit vs 64-bit architecture will have checks placed in
|
| + # tests/arch_dependent_sizes_32/64.h.
|
| +
|
| + # This dictionary maps file names to FilePatch objects. We will add items
|
| + # to it as needed. Each FilePatch represents a set of changes to make to the
|
| + # associated file (additions and deletions).
|
| file_patches = {}
|
|
|
| - # Find locations of existing macros, and just delete them all.
|
| + # Find locations of existing macros, and just delete them all. Note that
|
| + # normally, only things in 'types_independent' need to be deleted, as arch-
|
| + # dependent checks exist in tests/arch_dependent_sizes_32/64.h, which are
|
| + # always completely over-ridden. However, it's possible that a type that used
|
| + # to be arch-independent has changed to now be arch-dependent (e.g., because
|
| + # a pointer was added), and we want to delete the old check in that case.
|
| for name, typeinfo in \
|
| types_independent.items() + types32.items() + types64.items():
|
| if IsMacroDefinedName(name):
|
| @@ -318,18 +393,29 @@
|
| # Add a compile-time assertion for each type whose size is independent of
|
| # architecture. These assertions go immediately after the class definition.
|
| for name, typeinfo in types_independent.items():
|
| - # Ignore macros and types that are 0 bytes (i.e., typedefs to void)
|
| + # Ignore dummy types that were defined by macros and also ignore types that
|
| + # are 0 bytes (i.e., typedefs to void).
|
| if not IsMacroDefinedName(name) and typeinfo.size > 0:
|
| sourcefile = typeinfo.source_location.filename
|
| if sourcefile not in file_patches:
|
| file_patches[sourcefile] = FilePatch(sourcefile)
|
| # Add the assertion code just after the definition of the type.
|
| + # E.g.:
|
| + # struct Foo {
|
| + # int32_t x;
|
| + # };
|
| + # PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(Foo, 4); <---Add this line
|
| file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
|
| typeinfo.source_location.end_line+1)
|
|
|
| + # Apply our patches. This actually edits the files containing the definitions
|
| + # for the types in types_independent.
|
| for filename, patch in file_patches.items():
|
| patch.Apply()
|
|
|
| + # Write out a file of checks for 32-bit architectures and a separate file for
|
| + # 64-bit architectures. These only have checks for types that are
|
| + # architecture-dependent.
|
| c_source_root = os.path.join(options.ppapi_root, "tests")
|
| WriteArchSpecificCode(types32.values(),
|
| c_source_root,
|
|
|