Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65d74c77 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "AAPT2: Produce Conditional Proguard Keep Rules"

parents c98b189f dc21dea9
Loading
Loading
Loading
Loading
+11 −6
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ struct LinkOptions {
  Maybe<std::string> generate_text_symbols_path;
  Maybe<std::string> generate_proguard_rules_path;
  Maybe<std::string> generate_main_dex_proguard_rules_path;
  bool generate_conditional_proguard_rules = false;
  bool generate_non_final_ids = false;
  std::vector<std::string> javadoc_annotations;
  Maybe<std::string> private_symbols;
@@ -499,7 +500,7 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
    return {};
  }

  if (options_.update_proguard_spec && !proguard::CollectProguardRules(src, doc, keep_set_)) {
  if (options_.update_proguard_spec && !proguard::CollectProguardRules(doc, keep_set_)) {
    return {};
  }

@@ -538,6 +539,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
  bool error = false;
  std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;

  proguard::CollectResourceReferences(context_, table, keep_set_);

  for (auto& pkg : table->packages) {
    CHECK(!pkg->name.empty()) << "Packages must have names when being linked";

@@ -1719,7 +1722,8 @@ class LinkCommand {
      }
    }

    proguard::KeepSet proguard_keep_set;
    proguard::KeepSet proguard_keep_set =
        proguard::KeepSet(options_.generate_conditional_proguard_rules);
    proguard::KeepSet proguard_main_dex_keep_set;

    if (context_->GetPackageType() == PackageType::kStaticLib) {
@@ -1795,14 +1799,12 @@ class LinkCommand {
      XmlReferenceLinker manifest_linker;
      if (manifest_linker.Consume(context_, manifest_xml.get())) {
        if (options_.generate_proguard_rules_path &&
            !proguard::CollectProguardRulesForManifest(Source(options_.manifest_path),
                                                       manifest_xml.get(), &proguard_keep_set)) {
            !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
          error = true;
        }

        if (options_.generate_main_dex_proguard_rules_path &&
            !proguard::CollectProguardRulesForManifest(Source(options_.manifest_path),
                                                       manifest_xml.get(),
            !proguard::CollectProguardRulesForManifest(manifest_xml.get(),
                                                       &proguard_main_dex_keep_set, true)) {
          error = true;
        }
@@ -1919,6 +1921,9 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
          .OptionalFlag("--proguard-main-dex",
                        "Output file for generated Proguard rules for the main dex.",
                        &options.generate_main_dex_proguard_rules_path)
          .OptionalSwitch("--proguard-conditional-keep-rules",
                          "Generate conditional Proguard keep rules.",
                          &options.generate_conditional_proguard_rules)
          .OptionalSwitch("--no-auto-version",
                          "Disables automatic style and layout SDK versioning.",
                          &options.no_auto_version)
+3 −3
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ static bool IsValidSymbol(const StringPiece& symbol) {

// Java symbols can not contain . or -, but those are valid in a resource name.
// Replace those with '_'.
static std::string TransformToFieldName(const StringPiece& symbol) {
std::string JavaClassGenerator::TransformToFieldName(const StringPiece& symbol) {
  std::string output = symbol.to_string();
  for (char& c : output) {
    if (c == '.' || c == '-') {
@@ -89,9 +89,9 @@ static std::string TransformNestedAttr(const ResourceNameRef& attr_name,
  // the package.
  if (!attr_name.package.empty() &&
      package_name_to_generate != attr_name.package) {
    output += "_" + TransformToFieldName(attr_name.package);
    output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.package);
  }
  output += "_" + TransformToFieldName(attr_name.entry);
  output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.entry);
  return output;
}

+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@

#include "ResourceTable.h"
#include "ResourceValues.h"
#include "androidfw/StringPiece.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"

@@ -78,6 +79,8 @@ class JavaClassGenerator {

  const std::string& getError() const;

  static std::string TransformToFieldName(const android::StringPiece& symbol);

 private:
  bool SkipSymbol(SymbolState state);
  bool SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol);
+147 −27
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@

#include "android-base/macros.h"

#include "JavaClassGenerator.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "androidfw/StringPiece.h"
#include "util/Util.h"
#include "xml/XmlDom.h"

@@ -31,7 +35,7 @@ class BaseVisitor : public xml::Visitor {
 public:
  using xml::Visitor::Visit;

  BaseVisitor(const Source& source, KeepSet* keep_set) : source_(source), keep_set_(keep_set) {
  BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : file_(file), keep_set_(keep_set) {
  }

  void Visit(xml::Element* node) override {
@@ -52,27 +56,47 @@ class BaseVisitor : public xml::Visitor {
    for (const auto& child : node->children) {
      child->Accept(this);
    }

    for (const auto& attr : node->attributes) {
      if (attr.compiled_value) {
        auto ref = ValueCast<Reference>(attr.compiled_value.get());
        if (ref) {
          AddReference(node->line_number, ref);
        }
      }
    }
  }

 protected:
  void AddClass(size_t line_number, const std::string& class_name) {
    keep_set_->AddClass(Source(source_.path, line_number), class_name);
  ResourceFile file_;
  KeepSet* keep_set_;

  virtual void AddClass(size_t line_number, const std::string& class_name) {
    keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, class_name);
  }

  void AddMethod(size_t line_number, const std::string& method_name) {
    keep_set_->AddMethod(Source(source_.path, line_number), method_name);
    keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)}, method_name);
  }

  void AddReference(size_t line_number, Reference* ref) {
    if (ref && ref->name) {
      ResourceName ref_name = ref->name.value();
      if (ref_name.package.empty()) {
        ref_name = ResourceName(file_.name.package, ref_name.type, ref_name.entry);
      }
      keep_set_->AddReference({file_.name, file_.source.WithLine(line_number)}, ref_name);
    }
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(BaseVisitor);

  Source source_;
  KeepSet* keep_set_;
};

class LayoutVisitor : public BaseVisitor {
 public:
  LayoutVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
  LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
  }

  void Visit(xml::Element* node) override {
@@ -110,7 +134,7 @@ class LayoutVisitor : public BaseVisitor {

class MenuVisitor : public BaseVisitor {
 public:
  MenuVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
  MenuVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
  }

  void Visit(xml::Element* node) override {
@@ -136,7 +160,7 @@ class MenuVisitor : public BaseVisitor {

class XmlResourceVisitor : public BaseVisitor {
 public:
  XmlResourceVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
  XmlResourceVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
  }

  void Visit(xml::Element* node) override {
@@ -163,7 +187,7 @@ class XmlResourceVisitor : public BaseVisitor {

class TransitionVisitor : public BaseVisitor {
 public:
  TransitionVisitor(const Source& source, KeepSet* keep_set) : BaseVisitor(source, keep_set) {
  TransitionVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
  }

  void Visit(xml::Element* node) override {
@@ -185,8 +209,9 @@ class TransitionVisitor : public BaseVisitor {

class ManifestVisitor : public BaseVisitor {
 public:
  ManifestVisitor(const Source& source, KeepSet* keep_set, bool main_dex_only)
      : BaseVisitor(source, keep_set), main_dex_only_(main_dex_only) {}
  ManifestVisitor(const ResourceFile& file, KeepSet* keep_set, bool main_dex_only)
      : BaseVisitor(file, keep_set), main_dex_only_(main_dex_only) {
  }

  void Visit(xml::Element* node) override {
    if (node->namespace_uri.empty()) {
@@ -241,6 +266,10 @@ class ManifestVisitor : public BaseVisitor {
    BaseVisitor::Visit(node);
  }

  virtual void AddClass(size_t line_number, const std::string& class_name) override {
    keep_set_->AddManifestClass({file_.name, file_.source.WithLine(line_number)}, class_name);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(ManifestVisitor);

@@ -249,9 +278,8 @@ class ManifestVisitor : public BaseVisitor {
  std::string default_process_;
};

bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keep_set,
                                     bool main_dex_only) {
  ManifestVisitor visitor(source, keep_set, main_dex_only);
bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set, bool main_dex_only) {
  ManifestVisitor visitor(res->file, keep_set, main_dex_only);
  if (res->root) {
    res->root->Accept(&visitor);
    return true;
@@ -259,58 +287,150 @@ bool CollectProguardRulesForManifest(const Source& source, xml::XmlResource* res
  return false;
}

bool CollectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keep_set) {
bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) {
  if (!res->root) {
    return false;
  }

  switch (res->file.name.type) {
    case ResourceType::kLayout: {
      LayoutVisitor visitor(source, keep_set);
      LayoutVisitor visitor(res->file, keep_set);
      res->root->Accept(&visitor);
      break;
    }

    case ResourceType::kXml: {
      XmlResourceVisitor visitor(source, keep_set);
      XmlResourceVisitor visitor(res->file, keep_set);
      res->root->Accept(&visitor);
      break;
    }

    case ResourceType::kTransition: {
      TransitionVisitor visitor(source, keep_set);
      TransitionVisitor visitor(res->file, keep_set);
      res->root->Accept(&visitor);
      break;
    }

    case ResourceType::kMenu: {
      MenuVisitor visitor(source, keep_set);
      MenuVisitor visitor(res->file, keep_set);
      res->root->Accept(&visitor);
      break;
    }

    default:
    default: {
      BaseVisitor visitor(res->file, keep_set);
      res->root->Accept(&visitor);
      break;
    }
  }
  return true;
}

bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
  for (const auto& entry : keep_set.keep_set_) {
    for (const Source& source : entry.second) {
      *out << "# Referenced at " << source << "\n";
  for (const auto& entry : keep_set.manifest_class_set_) {
    for (const UsageLocation& location : entry.second) {
      *out << "# Referenced at " << location.source << "\n";
    }
    *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
  }

  for (const auto& entry : keep_set.keep_method_set_) {
    for (const Source& source : entry.second) {
      *out << "# Referenced at " << source << "\n";
  for (const auto& entry : keep_set.conditional_class_set_) {
    std::set<UsageLocation> locations;
    bool can_be_conditional = true;
    for (const UsageLocation& location : entry.second) {
      can_be_conditional &= CollectLocations(location, keep_set, &locations);
    }

    for (const UsageLocation& location : entry.second) {
      *out << "# Referenced at " << location.source << "\n";
    }
    if (keep_set.conditional_keep_rules_ && can_be_conditional) {
      *out << "-keep class " << entry.first << " {\n  ifused class **.R$layout {\n";
      for (const UsageLocation& location : locations) {
        auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
        *out << "    int " << transformed_name << ";\n";
      }
      *out << "  };\n  <init>(...);\n}\n" << std::endl;
    } else {
      *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
    }
  }

  for (const auto& entry : keep_set.method_set_) {
    for (const UsageLocation& location : entry.second) {
      *out << "# Referenced at " << location.source << "\n";
    }
    *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
  }
  return true;
}

bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                      std::set<UsageLocation>* locations) {
  locations->insert(location);

  // TODO: allow for more reference types if we can determine its safe.
  if (location.name.type != ResourceType::kLayout) {
    return false;
  }

  for (const auto& entry : keep_set.reference_set_) {
    if (entry.first == location.name) {
      for (auto& refLocation : entry.second) {
        // Don't get stuck in loops
        if (locations->find(refLocation) != locations->end()) {
          return false;
        }
        if (!CollectLocations(refLocation, keep_set, locations)) {
          return false;
        }
      }
    }
  }

  return true;
}

class ReferenceVisitor : public ValueVisitor {
 public:
  using ValueVisitor::Visit;

  ReferenceVisitor(aapt::IAaptContext* context, ResourceName from, KeepSet* keep_set)
      : context_(context), from_(from), keep_set_(keep_set) {
  }

  void Visit(Reference* reference) override {
    if (reference->name) {
      ResourceName reference_name = reference->name.value();
      if (reference_name.package.empty()) {
        reference_name = ResourceName(context_->GetCompilationPackage(), reference_name.type,
                                      reference_name.entry);
      }
      keep_set_->AddReference({from_, reference->GetSource()}, reference_name);
    }
  }

 private:
  aapt::IAaptContext* context_;
  ResourceName from_;
  KeepSet* keep_set_;
};

bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
                               KeepSet* keep_set) {
  for (auto& pkg : table->packages) {
    for (auto& type : pkg->types) {
      for (auto& entry : type->entries) {
        for (auto& config_value : entry->values) {
          ResourceName from(pkg->name, type->type, entry->name);
          ReferenceVisitor visitor(context, from, keep_set);
          config_value->value->Accept(&visitor);
        }
      }
    }
  }
  return true;
}

}  // namespace proguard
}  // namespace aapt
+53 −10
Original line number Diff line number Diff line
@@ -23,37 +23,80 @@
#include <string>

#include "Resource.h"
#include "ResourceTable.h"
#include "Source.h"
#include "ValueVisitor.h"
#include "androidfw/StringPiece.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"

namespace aapt {
namespace proguard {

struct UsageLocation {
  ResourceName name;
  Source source;
};

class KeepSet {
 public:
  inline void AddClass(const Source& source, const std::string& class_name) {
    keep_set_[class_name].insert(source);
  KeepSet() = default;

  KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) {
  }

  inline void AddManifestClass(const UsageLocation& file, const std::string& class_name) {
    manifest_class_set_[class_name].insert(file);
  }

  inline void AddConditionalClass(const UsageLocation& file, const std::string& class_name) {
    conditional_class_set_[class_name].insert(file);
  }

  inline void AddMethod(const UsageLocation& file, const std::string& method_name) {
    method_set_[method_name].insert(file);
  }

  inline void AddMethod(const Source& source, const std::string& method_name) {
    keep_method_set_[method_name].insert(source);
  inline void AddReference(const UsageLocation& file, const ResourceName& resource_name) {
    reference_set_[resource_name].insert(file);
  }

 private:
  friend bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);

  std::map<std::string, std::set<Source>> keep_set_;
  std::map<std::string, std::set<Source>> keep_method_set_;
  friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                               std::set<UsageLocation>* locations);

  bool conditional_keep_rules_ = false;
  std::map<std::string, std::set<UsageLocation>> manifest_class_set_;
  std::map<std::string, std::set<UsageLocation>> method_set_;
  std::map<std::string, std::set<UsageLocation>> conditional_class_set_;
  std::map<ResourceName, std::set<UsageLocation>> reference_set_;
};

bool CollectProguardRulesForManifest(const Source& source,
                                     xml::XmlResource* res, KeepSet* keep_set,
bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set,
                                     bool main_dex_only = false);
bool CollectProguardRules(const Source& source, xml::XmlResource* res,
bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
                               KeepSet* keep_set);

bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);

bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                      std::set<UsageLocation>* locations);

//
// UsageLocation implementation.
//

inline bool operator==(const UsageLocation& lhs, const UsageLocation& rhs) {
  return lhs.name == rhs.name;
}

inline int operator<(const UsageLocation& lhs, const UsageLocation& rhs) {
  return lhs.name.compare(rhs.name);
}

}  // namespace proguard
}  // namespace aapt

Loading