summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
authordmichael@google.com <dmichael@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-16 23:23:03 +0000
committerdmichael@google.com <dmichael@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-16 23:23:03 +0000
commit8b3adbb7f1208c72fd2e3da4d47897c5935e64c6 (patch)
tree51b9f9ec655845ff030a9e3e902a3ef9624d5265 /ppapi
parente4be9ca2ca1a8b36271c015f39fe606d81087e3e (diff)
downloadchromium_src-8b3adbb7f1208c72fd2e3da4d47897c5935e64c6.zip
chromium_src-8b3adbb7f1208c72fd2e3da4d47897c5935e64c6.tar.gz
chromium_src-8b3adbb7f1208c72fd2e3da4d47897c5935e64c6.tar.bz2
Another attempt at landing:
http://codereview.chromium.org/5730003/ Previously r69187 Check-in was almost clean, but checkdeps.py flagged it as having invalid includes (because the Clang plugins aren't meant to be built as part of Chrome). This adds the magic incantation (a DEPS file... who knew?) to make checkdeps.py ignore it. Tested by running checkdeps.py in my gclient before and after. BUG=61004,62983 TEST=compile asserts and ppapi/tests/test_struct_sizes.c Review URL: http://codereview.chromium.org/5927004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69483 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
-rw-r--r--ppapi/generate_ppapi_size_checks.py346
-rw-r--r--ppapi/tests/DEPS5
-rw-r--r--ppapi/tests/clang/Makefile67
-rw-r--r--ppapi/tests/clang/README32
-rw-r--r--ppapi/tests/clang/find_affected_interfaces.cc154
-rw-r--r--ppapi/tests/clang/print_names_and_sizes.cc150
6 files changed, 754 insertions, 0 deletions
diff --git a/ppapi/generate_ppapi_size_checks.py b/ppapi/generate_ppapi_size_checks.py
new file mode 100644
index 0000000..ba55602e
--- /dev/null
+++ b/ppapi/generate_ppapi_size_checks.py
@@ -0,0 +1,346 @@
+#!/usr/bin/python
+
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This script should be run manually on occasion to make sure all PPAPI types
+have appropriate size checking.
+
+"""
+
+import optparse
+import os
+import subprocess
+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"
+ ])
+
+
+
+class SourceLocation:
+
+ """A class representing the source location of a definiton."""
+
+ def __init__(self, filename="", start_line=-1, end_line=-1):
+ self.filename = os.path.normpath(filename)
+ self.start_line = start_line
+ self.end_line = end_line
+
+
+
+class TypeInfo:
+
+ """A class representing information about a C++ type."""
+
+ def __init__(self, info_string, target):
+ [self.kind, self.name, self.size, has_pointer_string, source_file,
+ start_line, end_line] = info_string.split(',')
+ self.target = target
+ self.parsed_line = info_string
+ # Note that Clang counts line numbers from 1, but we want to count from 0.
+ 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
+
+
+
+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
+ counted from 0.
+ """
+
+ def __init__(self, filename):
+ self.filename = filename
+ self.linenums_to_delete = set()
+ # A dictionary from line number to an array of strings to be inserted at
+ # that line number.
+ self.lines_to_add = {}
+
+ def Delete(self, start_line, end_line):
+ """Make the patch delete the lines [start_line, end_line)."""
+ self.linenums_to_delete |= set(range(start_line, end_line))
+
+ def Add(self, text, line_number):
+ """Add the given text before the text on the given line number."""
+ if line_number in self.lines_to_add:
+ self.lines_to_add[line_number].append(text)
+ else:
+ self.lines_to_add[line_number] = [text]
+
+ def Apply(self):
+ """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()
+ sourcefile.close()
+ # Now apply the patch. Our strategy is to keep the array at the same size,
+ # and just edit strings in the file_lines list as necessary. When we delete
+ # lines, we just blank the line and keep it in the list. When we add lines,
+ # we just prepend the added source code to the start of the existing line at
+ # that line number. This way, all the line numbers we cached from calls to
+ # Add and Delete remain valid list indices, and we don't have to worry about
+ # maintaining any offsets. Each element of file_lines at the end may
+ # contain any number of lines (0 or more) delimited by carriage returns.
+ for linenum_to_delete in self.linenums_to_delete:
+ file_lines[linenum_to_delete] = "";
+ for linenum, sourcelines in self.lines_to_add.items():
+ # Sort the lines we're adding so we get relatively consistent results.
+ sourcelines.sort()
+ # Prepend the new lines. When we output
+ file_lines[linenum] = "".join(sourcelines) + file_lines[linenum]
+ newsource = open(self.filename, "w")
+ for line in file_lines:
+ newsource.write(line)
+ newsource.close()
+
+
+def CheckAndInsert(typeinfo, typeinfo_map):
+ """Check if a TypeInfo exists already in the given map with the same name. If
+ so, make sure the size is consistent.
+ - If the name exists but the sizes do not match, print a message and
+ exit with non-zero exit code.
+ - If the name exists and the sizes match, do nothing.
+ - If the name does not exist, insert the typeinfo in to the map.
+
+ """
+ # If the type is unnamed, ignore it.
+ if typeinfo.name == "":
+ return
+ # If the size is 0, ignore it.
+ elif int(typeinfo.size) == 0:
+ return
+ # If the type is not defined under ppapi, ignore it.
+ elif typeinfo.source_location.filename.find("ppapi") == -1:
+ return
+ # If the type is defined under GLES2, ignore it.
+ elif typeinfo.source_location.filename.find("GLES2") > -1:
+ return
+ # If the type is an interface (by convention, starts with PPP_ or PPB_),
+ # ignore it.
+ elif (typeinfo.name[:4] == "PPP_") or (typeinfo.name[:4] == "PPB_"):
+ return
+ elif typeinfo.name in typeinfo_map:
+ if typeinfo.size != typeinfo_map[typeinfo.name].size:
+ print "Error: '" + typeinfo.name + "' is", \
+ typeinfo_map[typeinfo.name].size, \
+ "bytes on target '" + typeinfo_map[typeinfo.name].target + \
+ "', but", typeinfo.size, "on target '" + typeinfo.target + "'"
+ print typeinfo_map[typeinfo.name].parsed_line
+ print typeinfo.parsed_line
+ sys.exit(1)
+ else:
+ # It's already in the map and the sizes match.
+ pass
+ else:
+ typeinfo_map[typeinfo.name] = typeinfo
+
+
+def ProcessTarget(clang_command, target, arch_types, ind_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.
+ """
+ p = subprocess.Popen(clang_command + " -triple " + target,
+ shell=True,
+ stdout=subprocess.PIPE)
+ 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)
+
+
+def ToAssertionCode(typeinfo):
+ """Convert the TypeInfo to an appropriate C compile assertion.
+ If it's a struct (Record in Clang terminology), we want a line like this:
+ PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(<name>, <size>);\n
+ Enums:
+ PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(<name>, <size>);\n
+ Typedefs:
+ PP_COMPILE_ASSERT_SIZE_IN_BYTES(<name>, <size>);\n
+
+ """
+ line = "PP_COMPILE_ASSERT_"
+ if typeinfo.kind == "Enum":
+ line += "ENUM_"
+ elif typeinfo.kind == "Record":
+ line += "STRUCT_"
+ line += "SIZE_IN_BYTES("
+ line += typeinfo.name
+ line += ", "
+ line += typeinfo.size
+ line += ");\n"
+ return line
+
+
+def IsMacroDefinedName(typename):
+ """Return true iff the given typename came from a PPAPI compile assertion."""
+ return typename.find("PP_Dummy_Struct_For_") == 0
+
+
+COPYRIGHT_STRING_C = \
+"""/* Copyright (c) 2010 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This file has compile assertions for the sizes of types that are dependent
+ * on the architecture for which they are compiled (i.e., 32-bit vs 64-bit).
+ */
+
+"""
+
+
+def WriteArchSpecificCode(types, root, filename):
+ """Write a header file that contains a compile-time assertion for the size of
+ each of the given typeinfos, in to a file named filename rooted at root.
+ """
+ assertion_lines = [ToAssertionCode(typeinfo) for typeinfo in types]
+ assertion_lines.sort()
+ outfile = open(os.path.join(root, filename), "w")
+ header_guard = "PPAPI_TESTS_" + filename.upper().replace(".", "_") + "_"
+ outfile.write(COPYRIGHT_STRING_C)
+ outfile.write('#ifndef ' + header_guard + '\n')
+ outfile.write('#define ' + header_guard + '\n\n')
+ outfile.write('#include "ppapi/tests/test_struct_sizes.c"\n\n')
+ for line in assertion_lines:
+ outfile.write(line)
+ outfile.write('\n#endif /* ' + header_guard + ' */\n')
+
+
+def main(argv):
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '-c', '--clang-path', dest='clang_path',
+ default=(''),
+ help='the path to the clang binary (default is to get it from your path)')
+ parser.add_option(
+ '-p', '--plugin', dest='plugin',
+ default='tests/clang/libPrintNamesAndSizes.so',
+ help='The path to the PrintNamesAndSizes plugin library.')
+ parser.add_option(
+ '--targets32', dest='targets32',
+ default='i386-pc-linux,arm-pc-linux,i386-pc-win32',
+ help='Which 32-bit target triples to provide to clang.')
+ parser.add_option(
+ '--targets64', dest='targets64',
+ default='x86_64-pc-linux,x86_64-pc-win',
+ help='Which 32-bit target triples to provide to clang.')
+ parser.add_option(
+ '-r', '--ppapi-root', dest='ppapi_root',
+ default='.',
+ help='The root directory of ppapi.')
+ options, args = parser.parse_args(argv)
+ if args:
+ parser.print_help()
+ print 'ERROR: invalid argument'
+ sys.exit(1)
+
+ clang_executable = os.path.join(options.clang_path, 'clang')
+ clang_command = clang_executable + " -cc1" \
+ + " -load " + options.plugin \
+ + " -plugin PrintNamesAndSizes" \
+ + " -I" + os.path.join(options.ppapi_root, "..") \
+ + " " \
+ + os.path.join(options.ppapi_root, "tests", "test_struct_sizes.c")
+
+ # Dictionaries mapping type names to TypeInfo objects.
+ # Types that have size dependent on architecture, for 32-bit
+ types32 = {}
+ # Types that have size dependent on architecture, for 64-bit
+ types64 = {}
+ # Note that types32 and types64 should contain the same types, but with
+ # different sizes.
+
+ # Types whose size should be consistent regardless of architecture.
+ types_independent = {}
+
+ # 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.
+ targets32 = options.targets32.split(',');
+ for target in targets32:
+ ProcessTarget(clang_command, target, types32, types_independent)
+ targets64 = options.targets64.split(',');
+ for target in targets64:
+ ProcessTarget(clang_command, target, types64, types_independent)
+
+ # This dictionary maps file names to FilePatch objects.
+ file_patches = {}
+
+ # Find locations of existing macros, and just delete them all.
+ for name, typeinfo in \
+ types_independent.items() + types32.items() + types64.items():
+ if IsMacroDefinedName(name):
+ sourcefile = typeinfo.source_location.filename
+ if sourcefile not in file_patches:
+ file_patches[sourcefile] = FilePatch(sourcefile)
+ file_patches[sourcefile].Delete(typeinfo.source_location.start_line,
+ typeinfo.source_location.end_line+1)
+
+ # 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)
+ 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.
+ file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
+ typeinfo.source_location.end_line+1)
+
+ for filename, patch in file_patches.items():
+ patch.Apply()
+
+ c_source_root = os.path.join(options.ppapi_root, "tests")
+ WriteArchSpecificCode(types32.values(),
+ c_source_root,
+ "arch_dependent_sizes_32.h")
+ WriteArchSpecificCode(types64.values(),
+ c_source_root,
+ "arch_dependent_sizes_64.h")
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
+
diff --git a/ppapi/tests/DEPS b/ppapi/tests/DEPS
new file mode 100644
index 0000000..557b432
--- /dev/null
+++ b/ppapi/tests/DEPS
@@ -0,0 +1,5 @@
+# checkdeps.py shouldn't check include paths for files in clang, which aren't
+# part of the chrome build.
+skip_child_includes = [
+ "clang",
+]
diff --git a/ppapi/tests/clang/Makefile b/ppapi/tests/clang/Makefile
new file mode 100644
index 0000000..db594fb
--- /dev/null
+++ b/ppapi/tests/clang/Makefile
@@ -0,0 +1,67 @@
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Makefile to build the PPAPI Clang plugins
+
+.PHONY: all clean
+
+PLUGINS = PrintNamesAndSizes FindAffectedInterfaces
+
+all: $(foreach plugin,$(PLUGINS),$(plugin))
+
+clean: $(foreach plugin,$(PLUGINS),clean_$(plugin))
+
+# This makefile assumes that you checked out llvm in a subdirectory of your
+# home directory called 'llvm'. The source of llvm itself should be one level
+# deeper at ~/llvm/llvm. It also assumes that you also have the clang component
+# of llvm installed.
+# It further assumes that clang has been built in Debug mode following the
+# usual instructions, with a build directory parallel to the llvm directory.
+# See instructions here: http://clang.llvm.org/get_started.html
+# If you didn't configure Clang this way, you can set environment variables
+# appropriately to work with your configuration of Clang and LLVM.
+LLVM_ROOT ?= ~/llvm
+LLVM_SOURCE_ROOT ?= $(LLVM_ROOT)/llvm
+LLVM_BUILD_ROOT?=$(LLVM_ROOT)/build
+LLVM_INCLUDES?=-I$(LLVM_BUILD_ROOT)/include -I$(LLVM_SOURCE_ROOT)/include
+LLVM_LIB_PATHS?=-L$(LLVM_BUILD_ROOT)/Debug+Asserts/lib
+
+CLANG_ROOT=$(LLVM_SOURCE_ROOT)/tools/clang
+CLANG_BUILD_ROOT=$(LLVM_BUILD_ROOT)/tools/clang
+CLANG_INCLUDES=-I$(CLANG_ROOT)/include -I$(CLANG_BUILD_ROOT)/include
+
+INCLUDES=$(LLVM_INCLUDES) $(CLANG_INCLUDES) -I.
+
+CLANG_DEFINITIONS=-D_DEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS\
+ -D__STDC_CONSTANT_MACROS
+
+COMMON_CCFLAGS=-fno-exceptions -fno-rtti -fPIC -Woverloaded-virtual -Wcast-qual\
+ -fno-strict-aliasing -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter\
+ -Wwrite-strings
+
+CCFLAGS=$(INCLUDES) $(CLANG_DEFINITIONS) $(COMMON_CCFLAGS) -O2
+LDFLAGS = -Wl,-R $(LLVM_LIB_PATHS) -lpthread -ldl -lm -shared
+
+CPP=g++
+
+%.o: %.cc
+ $(CPP) $(CCFLAGS) -c -o $@ $<
+
+PRINT_NAMES_AND_SIZES_OBJECTS=print_names_and_sizes.o
+
+PrintNamesAndSizes: $(PRINT_NAMES_AND_SIZES_OBJECTS)
+ $(CPP) $^ $(LDFLAGS) -o libPrintNamesAndSizes.so
+
+clean_PrintNamesAndSizes:
+ rm -f $(PRINT_NAMES_AND_SIZES_OBJECTS) libPrintNamesAndSizes.so
+
+FIND_AFFECTED_INTERFACES_OBJECTS=find_affected_interfaces.o
+
+FindAffectedInterfaces: $(FIND_AFFECTED_INTERFACES_OBJECTS)
+ $(CPP) $^ $(LDFLAGS) -o libFindAffectedInterfaces.so
+
+clean_FindAffectedInterfaces:
+ rm -f $(FIND_AFFECTED_INTERFACES_OBJECTS) libFindAffectedInterfaces.so
+
+
diff --git a/ppapi/tests/clang/README b/ppapi/tests/clang/README
new file mode 100644
index 0000000..17abb83
--- /dev/null
+++ b/ppapi/tests/clang/README
@@ -0,0 +1,32 @@
+This is a directory for Clang plugins that are designed to do analysis and/or manipulation of PPAPI code. Clang is an open-source C front-end that allows you to parse C, C++, or Objective-C code in to an abstract syntax tree (or AST) for processing.
+
+To use these plugins, you will need to get Clang. Clang is rapidly changing, so you may want to download and build it yourself. See the instructions here:
+- http://clang.llvm.org/get_started.html
+
+To build the plugins, use the Makefile in this directory. If you want the provided Makefile to work out-of-the-box, in step 2 of the instructions at the above URL, you should do the following:
+> mkdir ~/llvm
+> cd ~/llvm
+Now continue with the svn co command to check out llvm in ~/llvm/llvm.
+
+To run a plugin, use clang with the -cc1 -load and -plugin flags and an otherwise normal build line. For example, to run liBPrintNamesAndSizes.so, if you currently build like this:
+g++ (build_options)
+Run this from the command-line instead:
+clang -cc1 -load ppapi/tests/clang/libPrintNamesAndSizes.so -plugin PrintNamesAndSizes (build_options)
+
+Plugins:
+PrintNamesAndSizes : print_names_and_sizes.cc
+Print information about all top-level type definitions. You probably won't need to run it by itself; instead see generate_ppapi_size_checks.py, which uses the plugin.
+
+Example command-line:
+ python generate_ppapi_size_checks.py --ppapi-root=/usr/local/google/chrome_build/src/ppapi
+ python generate_ppapi_size_checks.py --help
+
+
+FindAffectedInterfaces : find_affected_interfaces.cc
+Given typenames as parameters, print out all types that are affected (including function pointer types and structs containing affected function pointer types) if the given type(s) change. This is meant to be used for determining what interfaces are affected by a change to a struct.
+
+Example command-line:
+ clang -cc1 -load ppapi/tests/clang/libFindAffectedInterfaces.so -plugin FindAffectedInterfaces -I. ppapi/tests/all_includes.h -plugin-arg-FindAffectedInterfaces "struct PP_VideoCompressedDataBuffer_Dev"
+ clang -cc1 -load tests/clang/libFindAffectedInterfaces.so -plugin FindAffectedInterfaces -I../ tests/all_c_includes.h -plugin-arg-FindAffectedInterfaces "struct PP_VideoCompressedDataBuffer_Dev,struct PP_Var"
+
+(This assumes that clang is in your path and you are running the plugin from the ppapi subdirectory in a chrome checkout).
diff --git a/ppapi/tests/clang/find_affected_interfaces.cc b/ppapi/tests/clang/find_affected_interfaces.cc
new file mode 100644
index 0000000..4d053e7
--- /dev/null
+++ b/ppapi/tests/clang/find_affected_interfaces.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Clang plugin which finds types that are affected if the types listed in the
+// plugin parameters are changed. This is for use in determining what PPAPI
+// C-level interfaces are affected if one or more PPAPI C structs are changed.
+
+#include <algorithm>
+#include <cstdio>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+
+namespace {
+
+typedef std::vector<std::string> StringVec;
+
+class FindAffectedInterfacesConsumer : public clang::ASTConsumer {
+ public:
+ explicit FindAffectedInterfacesConsumer(const StringVec& changed_classes) {
+ StringVec::const_iterator iter(changed_classes.begin());
+ StringVec::const_iterator the_end(changed_classes.end());
+ for (; iter != the_end; ++iter) {
+ class_is_affected_map_[*iter] = true;
+ }
+ }
+
+ private:
+ typedef std::map<std::string, bool> StringBoolMap;
+ StringBoolMap class_is_affected_map_;
+
+ bool IsAffected(const clang::Type& type_to_check) {
+ std::string type_string(
+ type_to_check.getCanonicalTypeInternal().getAsString());
+ std::pair<StringBoolMap::iterator, bool> iter_success_pair =
+ class_is_affected_map_.insert(
+ StringBoolMap::value_type(type_string, false));
+ StringBoolMap::iterator iter(iter_success_pair.first);
+ bool successfully_inserted(iter_success_pair.second);
+ // If we were able to insert, then we haven't yet seen this type. Compute
+ // IsAffected and put it at the newly inserted location in the map.
+ if (successfully_inserted) {
+ if (type_to_check.isPointerType()) {
+ const clang::PointerType* pointer_type =
+ dyn_cast<clang::PointerType>(&type_to_check);
+ // Recurse to the pointee type.
+ iter->second = IsAffected(*pointer_type->getPointeeType().getTypePtr());
+ } else if (type_to_check.isFunctionProtoType()) {
+ const clang::FunctionProtoType* func_type =
+ dyn_cast<clang::FunctionProtoType>(&type_to_check);
+ // Recurse to the return type and parameter types.
+ iter->second = IsAffected(*func_type->getResultType().getTypePtr());
+ if (!iter->second) {
+ clang::FunctionProtoType::arg_type_iterator arg_iter =
+ func_type->arg_type_begin();
+ clang::FunctionProtoType::arg_type_iterator arg_end =
+ func_type->arg_type_end();
+ for (; (arg_iter != arg_end) && (!iter->second); ++arg_iter) {
+ iter->second = IsAffected(*(arg_iter->getTypePtr()));
+ }
+ }
+ } else if (type_to_check.isRecordType()) {
+ // For records (unions, structs), recurse to the fields.
+ const clang::RecordType* record =
+ dyn_cast<clang::RecordType>(&type_to_check);
+ const clang::RecordDecl* decl = record->getDecl();
+ clang::RecordDecl::field_iterator field_iter(decl->field_begin());
+ clang::RecordDecl::field_iterator field_end(decl->field_end());
+ for (; (field_iter != field_end) && (!iter->second); ++field_iter) {
+ iter->second = IsAffected(*(field_iter->getType().getTypePtr()));
+ }
+ }
+ }
+ // By this point, the bool in the map at the location referenced by iter has
+ // the correct value. Either it was cached, or we computed & inserted it.
+ return iter->second;
+ }
+
+ // Virtual function to consume top-level declarations. For each one, we check
+ // to see if it is a type definition. If it is, we print information about
+ // it.
+ virtual void HandleTopLevelDecl(clang::DeclGroupRef decl_group) {
+ clang::DeclGroupRef::iterator iter(decl_group.begin());
+ clang::DeclGroupRef::iterator the_end(decl_group.end());
+ for (; iter != the_end; ++iter) {
+ const clang::Decl *decl = *iter;
+ if (const clang::TypeDecl* type_decl = dyn_cast<clang::TypeDecl>(decl)) {
+ std::string name(type_decl->getNameAsString());
+ // TagDecl covers structs, enums, unions, and classes.
+ if (const clang::TagDecl* tag = dyn_cast<clang::TagDecl>(type_decl)) {
+ // Only print out info when we find the definition; ignore forward
+ // references.
+ if (tag->isDefinition()) {
+ clang::Type* type = type_decl->getTypeForDecl();
+ if (IsAffected(*type)) {
+ std::printf("%s\n", name.c_str());
+ }
+ }
+ } else if (const clang::TypedefDecl* td =
+ dyn_cast<clang::TypedefDecl>(type_decl)) {
+ clang::Type* type = td->getUnderlyingType().getTypePtr();
+ if (IsAffected(*type)) {
+ std::printf("%s\n", name.c_str());
+ }
+ }
+ }
+ }
+ }
+};
+
+class FindAffectedInterfacesAction : public clang::PluginASTAction {
+ public:
+ FindAffectedInterfacesAction() {}
+
+ private:
+ StringVec types_;
+
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &instance, llvm::StringRef /*input_file*/) {
+ return new FindAffectedInterfacesConsumer(types_);
+ }
+
+ virtual bool ParseArgs(const clang::CompilerInstance& /*instance*/,
+ const std::vector<std::string>& args) {
+ // Every argument is interpreted as a comma-delimited list of names of types
+ // that have been changed.
+ StringVec::const_iterator iter(args.begin()), end(args.end());
+ for (; iter != end; ++iter) {
+ std::stringstream stream(*iter);
+ std::string type_name;
+ while (std::getline(stream, type_name, ',')) {
+ types_.push_back(type_name);
+ }
+ }
+ return true;
+ }
+};
+
+} // namespace
+
+static clang::FrontendPluginRegistry::Add<FindAffectedInterfacesAction>
+X("FindAffectedInterfaces",
+ "Find interfaces affected by changes to the passes classes.");
+
diff --git a/ppapi/tests/clang/print_names_and_sizes.cc b/ppapi/tests/clang/print_names_and_sizes.cc
new file mode 100644
index 0000000..18aee47
--- /dev/null
+++ b/ppapi/tests/clang/print_names_and_sizes.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Clang plugin which prints the names and sizes of all the top-level
+// structs, enums, and typedefs in the input file.
+
+#include <cstdio>
+#include <string>
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+
+namespace {
+
+const char* const kTypedefName = "Typedef";
+const char* const kDelim = ",";
+const char* const kHasPointer = "HasPointer";
+const char* const kNoPointer = "NoPointer";
+
+// This class consumes a Clang-parsed AST and prints out information about types
+// defined in the global namespace. Specifically, for each type definition
+// encountered, it prints:
+// "kind,name,size,has_pointer,source_file,first_line,last_line\n"
+// 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.
+// - has_pointer: 'HasPointer' if the type is or has a pointer. Otherwise,
+// 'NoPointer'.
+// - 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).
+class PrintNamesAndSizesConsumer : public clang::ASTConsumer {
+ public:
+ explicit PrintNamesAndSizesConsumer(clang::SourceManager* source_mgr)
+ : source_manager_(source_mgr) {}
+
+ private:
+ // SourceManager has information about source code locations, to help us know
+ // where definitions appear. We do not own this pointer, so we must not
+ // delete it.
+ clang::SourceManager* source_manager_;
+
+ // Return true iff the type is a pointer or contains a pointer. This is
+ // important because these types may be different sizes on 32-bit vs 64-bit
+ // platforms. Structs, enums, and unions that do NOT contain pointers are
+ // crafted to be the same size on 32-bit and 64-bit platforms by convention.
+ bool HasPointer(const clang::Type& type) {
+ if (type.isPointerType()) {
+ return true;
+ } else if (const clang::RecordType* record =
+ dyn_cast<clang::RecordType>(&type)) {
+ // If it's a struct or union, iterate through the fields. If any of them
+ // contain is or has a pointer, then we have one too.
+ const clang::RecordDecl* decl = record->getDecl();
+ clang::RecordDecl::field_iterator iter(decl->field_begin());
+ clang::RecordDecl::field_iterator end(decl->field_end());
+ for (; iter != end; ++iter) {
+ if (HasPointer(*(iter->getType().getTypePtr()))) {
+ return true;
+ }
+ }
+ // It's a struct or union, but contains no pointers.
+ return false;
+ }
+ // It's not a pointer, a struct, or a union.
+ return false;
+ }
+
+ void PrintTypeInfo(const std::string& name, const clang::Type& type,
+ const std::string& kind, const clang::CharUnits& size,
+ const clang::SourceLocation& begin_loc,
+ const clang::SourceLocation& end_loc) {
+ clang::PresumedLoc presumed_begin(
+ source_manager_->getPresumedLoc(begin_loc));
+ clang::PresumedLoc presumed_end(source_manager_->getPresumedLoc(end_loc));
+ std::printf("%s,%s,%lu,%s,%s,%u,%u\n",
+ kind.c_str(),
+ name.c_str(),
+ size.getQuantity(),
+ HasPointer(type) ? kHasPointer : kNoPointer,
+ presumed_begin.getFilename(),
+ presumed_begin.getLine(),
+ presumed_end.getLine());
+ }
+
+ // Virtual function to consume top-level declarations. For each one, we check
+ // to see if it is a type definition. If it is, we print information about
+ // it.
+ virtual void HandleTopLevelDecl(clang::DeclGroupRef decl_group) {
+ clang::DeclGroupRef::iterator iter(decl_group.begin());
+ clang::DeclGroupRef::iterator the_end(decl_group.end());
+ for (; iter != the_end; ++iter) {
+ const clang::Decl *decl = *iter;
+ if (const clang::TypeDecl* type_decl = dyn_cast<clang::TypeDecl>(decl)) {
+ std::string name(type_decl->getNameAsString());
+ clang::SourceLocation start_loc = type_decl->getLocStart();
+ clang::SourceLocation end_loc = type_decl->getLocEnd();
+ // TagDecl covers structs, enums, unions, and classes.
+ if (const clang::TagDecl* tag = dyn_cast<clang::TagDecl>(type_decl)) {
+ // Only print out info when we find the definition; ignore forward
+ // references.
+ if (tag->isDefinition()) {
+ clang::Type* type = type_decl->getTypeForDecl();
+ clang::CharUnits size =
+ tag->getASTContext().getTypeSizeInChars(type);
+ PrintTypeInfo(name, *type, type->getTypeClassName(), size,
+ start_loc, end_loc);
+ }
+ } else if (const clang::TypedefDecl* td =
+ dyn_cast<clang::TypedefDecl>(type_decl)) {
+ clang::Type* type = td->getUnderlyingType().getTypePtr();
+ clang::CharUnits size = td->getASTContext().getTypeSizeInChars(type);
+ PrintTypeInfo(name, *type, kTypedefName, size, start_loc, end_loc);
+ }
+ }
+ }
+ }
+};
+
+class PrintNamesAndSizesAction : public clang::PluginASTAction {
+ public:
+ PrintNamesAndSizesAction() {}
+
+ private:
+ virtual clang::ASTConsumer *CreateASTConsumer(
+ clang::CompilerInstance &instance, llvm::StringRef /*input_file*/) {
+ return new PrintNamesAndSizesConsumer(
+ &(instance.getDiagnostics().getSourceManager()));
+ }
+
+ // We don't accept any arguments, but ParseArgs is pure-virtual.
+ virtual bool ParseArgs(const clang::CompilerInstance& /*instance*/,
+ const std::vector<std::string>& /*args*/) {
+ return true;
+ }
+};
+
+} // namespace
+
+static clang::FrontendPluginRegistry::Add<PrintNamesAndSizesAction>
+X("PrintNamesAndSizes",
+ "Print the names and sizes of classes, enums, and typedefs.");
+