C++程序  |  323行  |  12.05 KB

// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "abi_diff_wrappers.h"

#include<header_abi_util.h>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wnested-anon-types"
#include "proto/abi_dump.pb.h"
#include "proto/abi_diff.pb.h"
#pragma clang diagnostic pop

#include <llvm/Support/raw_ostream.h>

using abi_diff::RecordDeclDiff;
using abi_diff::RecordFieldDeclDiff;
using abi_diff::CXXBaseSpecifierDiff;
using abi_diff::CXXVTableDiff;
using abi_diff::EnumDeclDiff;
using abi_diff::ReturnTypeDiff;
using abi_diff::ParamDeclDiff;
using abi_diff::FunctionDeclDiff;
using abi_diff::EnumDeclDiff;
using abi_diff::EnumFieldDeclDiff;
using abi_diff::GlobalVarDeclDiff;
using abi_dump::RecordDecl;
using abi_dump::RecordFieldDecl;
using abi_dump::EnumDecl;
using abi_dump::EnumFieldDecl;
using abi_dump::FunctionDecl;
using abi_dump::ParamDecl;
using abi_dump::VTableComponent;
using abi_dump::CXXBaseSpecifier;
using abi_dump::GlobalVarDecl;
using abi_dump::BasicNamedAndTypedDecl;

namespace abi_diff_wrappers {

static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
                               abi_dump::AccessSpecifier new_access) {
  bool access_downgraded = false;
  switch (old_access) {
    case abi_dump::AccessSpecifier::protected_access:
      if (new_access == abi_dump::AccessSpecifier::private_access) {
        access_downgraded = true;
      }
      break;
    case abi_dump::AccessSpecifier::public_access:
      if (new_access != abi_dump::AccessSpecifier::public_access) {
        access_downgraded = true;
      }
      break;
    default:
      break;
  }
  return access_downgraded;
}

static std::string CpptoCAdjustment(const std::string &type) {
  std::string adjusted_type_name =
      abi_util::FindAndReplace(type, "\\bstruct ", "");

  return adjusted_type_name;
}

static bool CompareTypeNames(const abi_dump::BasicTypeAbi &old_abi,
                             const abi_dump::BasicTypeAbi &new_abi) {
  // Strip of leading 'struct' keyword from type names
  std::string old_type = old_abi.name();
  std::string new_type = new_abi.name();
  old_type = CpptoCAdjustment(old_type);
  new_type = CpptoCAdjustment(new_type);
  // TODO: Add checks for C++ built-in types vs C corresponding types.
  return old_type != new_type;
}

static bool DiffBasicTypeAbi(const abi_dump::BasicTypeAbi &old_abi,
                             const abi_dump::BasicTypeAbi &new_abi) {
  // We need to add a layer of indirection to account for issues when C and C++
  // are mixed. For example some types like wchar_t are in-built types for C++
  // but not for C. Another example would be clang reporting C structures
  // without the leading "struct" keyword when headers defining them are
  // included in C++ files.
  bool name_comparison = CompareTypeNames(old_abi, new_abi);
  bool size_comparison = (old_abi.size() != new_abi.size());
  bool alignment_comparison = (old_abi.alignment() != new_abi.alignment());
  return name_comparison || size_comparison || alignment_comparison;
}

template <typename T>
static bool Diff(const T &old_element, const T &new_element) {
  // Can be specialized for future changes in the format.
  return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
                          new_element.basic_abi().type_abi()) ||
      (old_element.basic_abi().name() != new_element.basic_abi().name()) ||
      IsAccessDownGraded(old_element.basic_abi().access(),
                         new_element.basic_abi().access());
}

template <>
bool Diff<EnumFieldDecl>(const EnumFieldDecl &old_element,
                         const EnumFieldDecl &new_element) {
  // Can be specialized for future changes in the format.
  return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
                          new_element.basic_abi().type_abi()) ||
      (old_element.enum_field_value() != new_element.enum_field_value()) ||
      (old_element.basic_abi().name() != new_element.basic_abi().name());
}

template <>
bool Diff<ParamDecl>(const ParamDecl &old_element,
                     const ParamDecl &new_element) {
  // Can be specialized for future changes in the format.
  return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
                          new_element.basic_abi().type_abi());
}

template <>
bool Diff<CXXBaseSpecifier>(const CXXBaseSpecifier &old_element,
                            const CXXBaseSpecifier &new_element) {
  // Can be specialized for future changes in the format.
  return (DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
                           new_element.basic_abi().type_abi()) ||
      old_element.basic_abi().access() != new_element.basic_abi().access() ||
      old_element.is_virtual() != new_element.is_virtual());
}

template <>
bool Diff<VTableComponent>(const VTableComponent &old_element,
                           const VTableComponent &new_element) {
  bool kind_comparison = old_element.kind() != new_element.kind();
  bool mangled_name_comparison = old_element.mangled_component_name() !=
      new_element.mangled_component_name();
  bool value_comparison = old_element.value() != new_element.value();
  return kind_comparison || mangled_name_comparison || value_comparison;
}

// This function fills in a *Diff Message's repeated field. For eg:
// RecordDeclDiff's CXXBaseSpecifierDiff fields and well as FieldDeclDiff
// fields.
template <typename T, typename TDiff>
template <typename Element, typename ElementDiff>
bool DiffWrapperBase<T, TDiff>::GetElementDiffs(
    google::protobuf::RepeatedPtrField<ElementDiff> *dst,
    const google::protobuf::RepeatedPtrField<Element> &old_elements,
    const google::protobuf::RepeatedPtrField<Element> &new_elements) {
  bool differs = false;
  assert(dst != nullptr);
  int i = 0;
  int j = 0;
  while (i < old_elements.size() && j < new_elements.size()) {
    const Element &old_element = old_elements.Get(i);
    const Element &new_element = new_elements.Get(i);

    if (Diff(old_element, new_element)) {
      ElementDiff *diff = dst->Add();
      Element *old_elementp = nullptr;
      Element *new_elementp = nullptr;
      if (!diff || !(old_elementp = diff->mutable_old()) ||
          !(new_elementp = diff->mutable_new_())) {
        llvm::errs() << "Failed to add diff element\n";
        ::exit(1);
      }
      *old_elementp = old_element;
      *new_elementp = new_element;
      diff->set_index(i);
      differs = true;
    }
    i++;
    j++;
  }
  if (old_elements.size() != new_elements.size()) {
    GetExtraElementDiffs(dst, i, j, old_elements, new_elements);
    differs = true;
  }
  return differs;
}

template <typename T, typename TDiff>
template <typename Element, typename ElementDiff>
void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs(
    google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
    const google::protobuf::RepeatedPtrField<Element> &old_elements,
    const google::protobuf::RepeatedPtrField<Element> &new_elements) {
 assert(dst != nullptr);
 while (i < old_elements.size()) {
    const Element &old_element = old_elements.Get(i);
    ElementDiff *diff = dst->Add();
    Element *old_elementp = nullptr;
    if (!diff || !(old_elementp = diff->mutable_old())) {
      llvm::errs() << "Failed to add diff element\n";
      ::exit(1);
    }
    *old_elementp = old_element;
    diff->set_index(i);
    i++;
 }
 while (j < new_elements.size()) {
    const Element &new_element = new_elements.Get(j);
    ElementDiff *diff = dst->Add();
    Element *new_elementp = nullptr;
    if (!diff || !(new_elementp = diff->mutable_new_())) {
      llvm::errs() << "Failed to add diff element\n";
      ::exit(1);
    }
    *new_elementp = new_element;
    diff->set_index(j);
    j++;
 }
}

static bool DiffBasicNamedAndTypedDecl(BasicNamedAndTypedDecl *type_diff_old,
                                       BasicNamedAndTypedDecl *type_diff_new,
                                       const BasicNamedAndTypedDecl &old,
                                       const BasicNamedAndTypedDecl &new_) {
  assert(type_diff_old != nullptr);
  assert(type_diff_new != nullptr);
  if (DiffBasicTypeAbi(old.type_abi(), new_.type_abi()) ||
      IsAccessDownGraded(old.access(), new_.access())) {
    *(type_diff_old) = old;
    *(type_diff_new) = new_;
    return true;
  }
  return false;
}

template <>
std::unique_ptr<RecordDeclDiff>
DiffWrapper<RecordDecl, RecordDeclDiff>::Get() {
  std::unique_ptr<RecordDeclDiff> record_diff(new RecordDeclDiff());
  assert(oldp_->mangled_record_name() ==
         newp_->mangled_record_name());
  record_diff->set_name(oldp_->basic_abi().name());
  google::protobuf::RepeatedPtrField<RecordFieldDeclDiff> *fdiffs =
      record_diff->mutable_field_diffs();
  google::protobuf::RepeatedPtrField<CXXBaseSpecifierDiff> *bdiffs =
      record_diff->mutable_base_diffs();
  google::protobuf::RepeatedPtrField<CXXVTableDiff> *vtdiffs =
      record_diff->mutable_vtable_diffs();
  assert(fdiffs != nullptr && bdiffs != nullptr);
  // Template Information isn't diffed since the linker_set_key includes the
  // mangled name which includes template information.
  if (GetElementDiffs(fdiffs, oldp_->fields(), newp_->fields()) ||
      GetElementDiffs(bdiffs, oldp_->base_specifiers(),
                      newp_->base_specifiers()) ||
      GetElementDiffs(vtdiffs, oldp_->vtable_layout().vtable_components(),
                      newp_->vtable_layout().vtable_components()) ||
      DiffBasicNamedAndTypedDecl(
          record_diff->mutable_type_diff()->mutable_old(),
          record_diff->mutable_type_diff()->mutable_new_(),
          oldp_->basic_abi(), newp_->basic_abi())) {
    return record_diff;
  }
  return nullptr;
}

template <>
std::unique_ptr<EnumDeclDiff>
DiffWrapper<EnumDecl, EnumDeclDiff>::Get() {
  std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff());
  assert(oldp_->basic_abi().linker_set_key() ==
         newp_->basic_abi().linker_set_key());
  google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *fdiffs =
      enum_diff->mutable_field_diffs();
  assert(fdiffs != nullptr);
  enum_diff->set_name(oldp_->basic_abi().name());
  if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) ||
      DiffBasicNamedAndTypedDecl(
          enum_diff->mutable_type_diff()->mutable_old(),
          enum_diff->mutable_type_diff()->mutable_new_(),
          oldp_->basic_abi(), newp_->basic_abi())) {
    return enum_diff;
  }
  return nullptr;
}

template <>
std::unique_ptr<FunctionDeclDiff>
DiffWrapper<FunctionDecl, FunctionDeclDiff>::Get() {
  std::unique_ptr<FunctionDeclDiff> func_diff(new FunctionDeclDiff());
  google::protobuf::RepeatedPtrField<ParamDeclDiff> *pdiffs =
      func_diff->mutable_param_diffs();
  assert(func_diff->mutable_return_type_diffs() != nullptr);
  func_diff->set_name(oldp_->basic_abi().linker_set_key());
  if (DiffBasicNamedAndTypedDecl(
          func_diff->mutable_return_type_diffs()->mutable_old(),
          func_diff->mutable_return_type_diffs()->mutable_new_(),
          oldp_->basic_abi(), newp_->basic_abi()) ||
      GetElementDiffs(pdiffs, oldp_->parameters(), newp_->parameters())) {
    return func_diff;
  }
  return nullptr;
}

template <>
std::unique_ptr<GlobalVarDeclDiff>
DiffWrapper<GlobalVarDecl, GlobalVarDeclDiff>::Get() {
  std::unique_ptr<GlobalVarDeclDiff> global_var_diff(new GlobalVarDeclDiff());
  assert(global_var_diff->mutable_type_diff() != nullptr);
  if (DiffBasicNamedAndTypedDecl(
          global_var_diff->mutable_type_diff()->mutable_old(),
          global_var_diff->mutable_type_diff()->mutable_new_(),
          oldp_->basic_abi(), newp_->basic_abi())) {
    return global_var_diff;
  }
  return nullptr;
}

} // abi_diff_wrappers