# HG changeset patch
# User Joshua Cranmer <Pidgeot18@gmail.com>
# Date 1325721797 21600
# Node ID 2a0d8841a4908b33604f9e97cd12ef9cca7eb5c3
# Parent 42e1cdd661d6b51305c6d81ef1c64dad337c3368
[mq]: clang-plugin

diff --git a/include/clang/Basic/Plugin.h b/include/clang/Basic/Plugin.h
new file mode 100644
--- /dev/null
+++ b/include/clang/Basic/Plugin.h
@@ -0,0 +1,67 @@
+//===--- Plugin.h -----------------------------------------------*- C++ -*_===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_PLUGIN_H
+#define LLVM_CLANG_BASIC_PLUGIN_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+  class StringRef;
+}
+
+namespace clang {
+
+  class ASTConsumer;
+  class PPCallbacks;
+  class DiagnosticConsumer;
+
+namespace plugin {
+
+struct PluginArg {
+  const char *name;
+  const char *value;
+};
+
+struct Version {
+  unsigned major;
+  unsigned minor;
+  unsigned subminor;
+};
+
+// XXX: What else I need:
+// -> IR codegen
+// -> setting diagnostics
+struct PluginFileCallbacks {
+  ASTConsumer *astConsumer;
+  PPCallbacks *ppCallback;
+  DiagnosticConsumer *diagnostics;
+};
+
+extern "C" {
+
+/**
+ * Callback to initialize the plugin.
+ *
+ * \return Whether or not the plugin initialization succeeded. If initialization
+ *         fails, the compilation process is terminated.
+ */
+extern bool clang_plugin_init(int argc, PluginArg *argv, Version *version);
+
+// Create ASTConsumer?
+extern void clang_plugin_begin_file(llvm::StringRef fileName,
+  PluginFileCallbacks *callbacks);
+extern void clang_plugin_end_file(llvm::StringRef fileName);
+extern void clang_plugin_destroy();
+}
+
+} // namespace plugin
+} // namespace clang
+
+#endif
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -341,16 +341,20 @@ def load : Separate<"-load">, MetaVarNam
   HelpText<"Load the named plugin (dynamic shared object)">;
 def plugin : Separate<"-plugin">, MetaVarName<"<name>">,
   HelpText<"Use the named plugin action instead of the default action (use \"help\" to list available options)">;
 def plugin_arg : JoinedAndSeparate<"-plugin-arg-">,
     MetaVarName<"<name> <arg>">,
     HelpText<"Pass <arg> to plugin <name>">;
 def add_plugin : Separate<"-add-plugin">, MetaVarName<"<name>">,
   HelpText<"Use the named plugin action in addition to the default action">;
+def fplugin : Joined<"-fplugin=">,
+  HelpText<"Load the named compiler plugin">;
+def fplugin_arg : Joined<"-fplugin-arg-">,
+    HelpText<"Pass <arg> to plugin <name>">;
 def resource_dir : Separate<"-resource-dir">,
   HelpText<"The directory which holds the compiler resource files">;
 def version : Flag<"-version">,
   HelpText<"Print the compiler version">;
 def _version : Flag<"--version">, Alias<version>;
 
 def Action_Group : OptionGroup<"<action group>">;
 let Group = Action_Group in {
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -480,16 +480,18 @@ def foutput_class_dir_EQ : Joined<"-fout
 def fpack_struct : Flag<"-fpack-struct">, Group<f_Group>;
 def fno_pack_struct : Flag<"-fno-pack-struct">, Group<f_Group>;
 def fpack_struct_EQ : Joined<"-fpack-struct=">, Group<f_Group>;
 def fpascal_strings : Flag<"-fpascal-strings">, Group<f_Group>;
 def fpch_preprocess : Flag<"-fpch-preprocess">, Group<f_Group>;
 def fpic : Flag<"-fpic">, Group<f_Group>;
 def fpie : Flag<"-fpie">, Group<f_Group>, Flags<[NoArgumentUnused]>;
 def fno_pie : Flag<"-fno-pie">, Group<f_Group>, Flags<[NoArgumentUnused]>;
+def fplugin : Joined<"-fplugin=">, Group<f_Group>;
+def fplugin_arg : Joined<"-fplugin-arg-">, Group<f_Group>;
 def fprofile_arcs : Flag<"-fprofile-arcs">, Group<f_Group>;
 def fprofile_generate : Flag<"-fprofile-generate">, Group<f_Group>;
 def framework : Separate<"-framework">, Flags<[LinkerInput]>;
 def frandom_seed_EQ : Joined<"-frandom-seed=">, Group<clang_ignored_f_Group>;
 def frtti : Flag<"-frtti">, Group<f_Group>;
 def fsched_interblock : Flag<"-fsched-interblock">, Group<clang_ignored_f_Group>;
 def fshort_enums : Flag<"-fshort-enums">, Group<f_Group>;
 def freorder_blocks : Flag<"-freorder-blocks">, Group<clang_ignored_f_Group>;
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -110,16 +110,23 @@ public:
   std::vector<std::string> AddPluginActions;
 
   /// Args to pass to the additional plugins
   std::vector<std::vector<std::string> > AddPluginArgs;
 
   /// The list of plugins to load.
   std::vector<std::string> Plugins;
 
+  /// The list of compiler plugins to load.
+  std::vector<std::string> CompilerPlugins;
+
+  /// Argments for the above compiler plugins
+  std::vector<std::vector<std::pair<std::string, std::string> > >
+    CompilerPluginArgs;
+
   /// \brief The list of AST files to merge.
   std::vector<std::string> ASTMergeFiles;
 
   /// \brief A list of arguments to forward to LLVM's option processing; this
   /// should only be used for debugging and experimental features.
   std::vector<std::string> LLVMArgs;
 
 public:
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1372,16 +1372,20 @@ void Clang::ConstructJob(Compilation &C,
     CmdArgs.push_back("-w");
 
     // Add -Xanalyzer arguments when running as analyzer.
     Args.AddAllArgValues(CmdArgs, options::OPT_Xanalyzer);
   }
 
   CheckCodeGenerationOptions(D, Args);
 
+  // Pass through plugins
+  Args.AddAllArgs(CmdArgs, options::OPT_fplugin);
+  Args.AddAllArgs(CmdArgs, options::OPT_fplugin_arg);
+
   // Perform argument translation for LLVM backend. This
   // takes some care in reconciling with llvm-gcc. The
   // issue is that llvm-gcc translates these options based on
   // the values in cc1, whereas we are processing based on
   // the driver arguments.
 
   // This comes from the default translation the driver + cc1
   // would do to enable flag_pic.
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1366,16 +1366,55 @@ static InputKind ParseFrontendArgs(Front
   for (int i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) {
     for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg),
            end = Args.filtered_end(); it != end; ++it) {
       if ((*it)->getValue(Args, 0) == Opts.AddPluginActions[i])
         Opts.AddPluginArgs[i].push_back((*it)->getValue(Args, 1));
     }
   }
 
+  Opts.CompilerPlugins = Args.getAllArgValues(OPT_fplugin);
+  Opts.CompilerPluginArgs.resize(Opts.CompilerPlugins.size());
+
+  // Parse the plugin arguments
+  std::vector<std::pair<std::string, std::string> > ParsedPluginArgs;
+  for (arg_iterator it = Args.filtered_begin(OPT_fplugin_arg),
+      end = Args.filtered_end(); it != end; ++it) {
+    std::string value = (*it)->getValue(Args);
+    size_t boundary = value.find('-');
+    if (boundary == std::string::npos) {
+      Diags.Report(diag::err_drv_invalid_value) << value;
+      continue;
+    }
+    ParsedPluginArgs.push_back(std::pair<std::string, std::string>(
+      value.substr(0, boundary), value.substr(boundary + 1)));
+  }
+
+  // Add the plugin arguments to a parsedPluginArgs
+  for (int i = 0, e = Opts.CompilerPlugins.size(); i != e; ++i) {
+    std::string base = llvm::sys::path::stem(Opts.CompilerPlugins[i]);
+    for (std::vector<std::pair<std::string, std::string> >::iterator
+           it = ParsedPluginArgs.begin(), end = ParsedPluginArgs.end();
+           it != end; ++it) {
+      if (it->first == base) {
+        std::string argpair = it->second;
+        size_t boundary = argpair.find("=");
+        if (boundary == std::string::npos) {
+          Diags.Report(diag::err_drv_invalid_value) << argpair;
+          continue;
+       }
+        Opts.CompilerPluginArgs[i].push_back(
+          std::pair<std::string, std::string>(argpair.substr(0, boundary),
+            argpair.substr(boundary + 1)));
+
+      }
+    }
+  }
+
+
   if (const Arg *A = Args.getLastArg(OPT_code_completion_at)) {
     Opts.CodeCompletionAt =
       ParsedSourceLocation::FromString(A->getValue(Args));
     if (Opts.CodeCompletionAt.FileName.empty())
       Diags.Report(diag::err_drv_invalid_value)
         << A->getAsString(Args) << A->getValue(Args);
   }
   Opts.DisableFree = Args.hasArg(OPT_disable_free);
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -10,29 +10,137 @@
 // This file holds ExecuteCompilerInvocation(). It is split into its own file to
 // minimize the impact of pulling in essentially everything else in Clang.
 //
 //===----------------------------------------------------------------------===//
 
 #include "clang/FrontendTool/Utils.h"
 #include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
 #include "clang/ARCMigrate/ARCMTActions.h"
+#include "clang/Basic/Plugin.h"
+#include "clang/Basic/Version.inc"
 #include "clang/CodeGen/CodeGenAction.h"
 #include "clang/Driver/CC1Options.h"
 #include "clang/Driver/OptTable.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Frontend/ChainedDiagnosticConsumer.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Rewrite/FrontendActions.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/DynamicLibrary.h"
 using namespace clang;
 
+namespace {
+  class Plugin {
+    llvm::sys::DynamicLibrary PluginLibrary;
+    clang::plugin::PluginFileCallbacks callbacks;
+  public:
+    Plugin(llvm::sys::DynamicLibrary library) : PluginLibrary(library) {}
+    clang::plugin::PluginFileCallbacks *getCallbacks() { return &callbacks; }
+    intptr_t getFunctionCall(const char *name) {
+      return (intptr_t)PluginLibrary.getAddressOfSymbol(name);
+    }
+
+    typedef void (*BeginFileType)(StringRef, clang::plugin::PluginFileCallbacks*);
+    typedef void (*EndFileFnTy)(StringRef);
+    typedef void (*DestroyTy)();
+  };
+  class PluginWrapperAction : public WrapperFrontendAction {
+    std::vector<Plugin> plugins;
+    std::string CurrentFile;
+  public:
+    PluginWrapperAction(FrontendAction *WrappedAction,
+      std::vector<Plugin> &plugins)
+    : WrapperFrontendAction(WrappedAction), plugins(plugins) {}
+
+    virtual bool BeginSourceFileAction(CompilerInstance &CI,
+                                       StringRef Filename);
+    virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
+                                           StringRef InFile);
+    virtual void EndSourceFileAction();
+
+    virtual ~PluginWrapperAction();
+  };
+}
+
+bool PluginWrapperAction::BeginSourceFileAction(CompilerInstance &CI,
+    StringRef Filename) {
+  if (!WrapperFrontendAction::BeginSourceFileAction(CI, Filename))
+    return false;
+
+  CurrentFile = Filename;
+  for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+      ++it) {
+    // Retrieve the callbacks, and clear all of the values to NULL
+    clang::plugin::PluginFileCallbacks *callbacks = it->getCallbacks();
+    memset(callbacks, 0, sizeof(clang::plugin::PluginFileCallbacks));
+
+    // Get the callbacks from the plugin
+    Plugin::BeginFileType beginFunc = (Plugin::BeginFileType)it->getFunctionCall(
+      "clang_plugin_begin_file");
+    if (!beginFunc)
+      continue;
+    beginFunc(Filename, callbacks);
+
+    // ASTConsumer is handled by GetASTConsumer; others are handled now
+    if (callbacks->ppCallback)
+      CI.getPreprocessor().addPPCallbacks(callbacks->ppCallback);
+    if (callbacks->diagnostics)
+    {
+      // The front end has already run this, so let's do this now.
+      callbacks->diagnostics->BeginSourceFile(CI.getLangOpts(),
+        &CI.getPreprocessor());
+      CI.getDiagnostics().setClient(new ChainedDiagnosticConsumer(
+        &CI.getDiagnosticClient(), callbacks->diagnostics));
+    }
+  }
+
+  return true;
+}
+
+ASTConsumer *PluginWrapperAction::CreateASTConsumer(CompilerInstance &CI,
+                                                    StringRef InFile) {
+  std::vector<ASTConsumer*> Consumers(1,
+    WrapperFrontendAction::CreateASTConsumer(CI, InFile));
+  for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+      ++it) {
+    if (ASTConsumer *consumer = it->getCallbacks()->astConsumer) {
+      Consumers.push_back(consumer);
+    }
+  }
+  return new MultiplexConsumer(Consumers);
+}
+
+void PluginWrapperAction::EndSourceFileAction()
+{
+  WrapperFrontendAction::EndSourceFileAction();
+  for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+      ++it) {
+    Plugin::EndFileFnTy EndFileFunc =
+      (Plugin::EndFileFnTy)it->getFunctionCall("clang_plugin_end_file");
+    if (EndFileFunc)
+      EndFileFunc(CurrentFile);
+  }
+}
+
+PluginWrapperAction::~PluginWrapperAction() {
+  for (std::vector<Plugin>::iterator it = plugins.begin(); it != plugins.end();
+      ++it) {
+    Plugin::DestroyTy destroyFunc = (Plugin::DestroyTy)
+      it->getFunctionCall("clang_plugin_destroy");
+    if (destroyFunc)
+      destroyFunc();
+  }
+}
+
 static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) {
   using namespace clang::frontend;
 
   switch (CI.getFrontendOpts().ProgramAction) {
   default:
     llvm_unreachable("Invalid program action!");
 
   case ASTDump:                return new ASTDumpAction();
@@ -138,16 +246,65 @@ bool clang::ExecuteCompilerInvocation(Co
          e = Clang->getFrontendOpts().Plugins.size(); i != e; ++i) {
     const std::string &Path = Clang->getFrontendOpts().Plugins[i];
     std::string Error;
     if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(Path.c_str(), &Error))
       Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
         << Path << Error;
   }
 
+  std::vector<Plugin> plugins;
+  for (unsigned i = 0,
+        e = Clang->getFrontendOpts().CompilerPlugins.size(); i != e; ++i) {
+    const std::string &Path = Clang->getFrontendOpts().CompilerPlugins[i];
+
+    // Try loading the library
+    std::string Error;
+    llvm::sys::DynamicLibrary plugin =
+      llvm::sys::DynamicLibrary::getPermanentLibrary(Path.c_str(), &Error);
+    if (!plugin.isValid()) {
+      Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+        << Path << Error;
+      continue;
+    }
+
+    // If the library has been loaded, it's time to run plugin_init.
+    // Construct the version
+    clang::plugin::Version pluginVersion = {
+      CLANG_VERSION_MAJOR,
+      CLANG_VERSION_MINOR,
+#ifdef CLANG_VERSION_PATCHLEVEL
+      CLANG_VERSION_PATCHLEVEL
+#else
+      0
+#endif
+    };
+
+    // Construct the arguments
+    std::vector<std::pair<std::string, std::string> > &pluginArgs =
+      Clang->getFrontendOpts().CompilerPluginArgs[i];
+    plugin::PluginArg *args = new plugin::PluginArg[pluginArgs.size()];
+    for (unsigned i = 0; i < pluginArgs.size(); i++) {
+      args[i].name = pluginArgs[i].first.c_str();
+      args[i].value = pluginArgs[i].second.c_str();
+    }
+    typedef bool (*init_func)(int argc, clang::plugin::PluginArg *argv,
+        clang::plugin::Version *version);
+    init_func plugin_init = (init_func)(intptr_t)
+      plugin.getAddressOfSymbol("clang_plugin_init");
+    if (!plugin_init || !plugin_init(pluginArgs.size(), args, &pluginVersion)) {
+      Clang->getDiagnostics().Report(diag::err_fe_unable_to_load_plugin)
+        << Path << "Plugin initialization failed";
+    } else {
+      plugins.push_back(Plugin(plugin));
+    }
+
+    delete [] args;
+  }
+
   // Honor -mllvm.
   //
   // FIXME: Remove this, one day.
   // This should happen AFTER plugins have been loaded!
   if (!Clang->getFrontendOpts().LLVMArgs.empty()) {
     unsigned NumArgs = Clang->getFrontendOpts().LLVMArgs.size();
     const char **Args = new const char*[NumArgs + 2];
     Args[0] = "clang (LLVM option parsing)";
@@ -163,17 +320,18 @@ bool clang::ExecuteCompilerInvocation(Co
     ento::printCheckerHelp(llvm::outs(), Clang->getFrontendOpts().Plugins);
     return 0;
   }
 
   // If there were errors in processing arguments, don't do anything else.
   bool Success = false;
   if (!Clang->getDiagnostics().hasErrorOccurred()) {
     // Create and execute the frontend action.
-    llvm::OwningPtr<FrontendAction> Act(CreateFrontendAction(*Clang));
+    llvm::OwningPtr<FrontendAction> Act(
+      new PluginWrapperAction(CreateFrontendAction(*Clang), plugins));
     if (Act) {
       Success = Clang->ExecuteAction(*Act);
       if (Clang->getFrontendOpts().DisableFree)
         Act.take();
     }
   }
 
   return Success;
