C++程序  |  524行  |  17.52 KB

/*
 * Copyright (C) 2017 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.
 */

#ifndef ART_RUNTIME_SUBTYPE_CHECK_INFO_H_
#define ART_RUNTIME_SUBTYPE_CHECK_INFO_H_

#include "base/bit_string.h"
#include "subtype_check_bits.h"

// Forward-declare for testing purposes.
struct SubtypeCheckInfoTest;

namespace art {

/**
 * SubtypeCheckInfo is a logical label for the class SubtypeCheck data, which is necessary to
 * perform efficient O(1) subtype comparison checks. See also subtype_check.h
 * for the more general explanation of how the labels are used overall.
 *
 * For convenience, we also store the class depth within an SubtypeCheckInfo, since nearly all
 * calculations are dependent on knowing the depth of the class.
 *
 * A SubtypeCheckInfo logically has:
 *          * Depth - How many levels up to the root (j.l.Object)?
 *          * PathToRoot - Possibly truncated BitString that encodes path to root
 *          * Next - The value a newly inserted Child would get appended to its path.
 *          * Overflow - If this path can never become a full path.
 *
 * Depending on the values of the above, it can be in one of these logical states,
 * which are introduced in subtype_check.h:
 *
 *               Transient States                         Terminal States
 *
 *  +-----------------+     +--------------------+     +-------------------+
 *  |                 |     |                    |     |                   |
 *  |  Uninitialized  | +--->    Initialized     | +--->     Assigned      |
 *  |                 |     |                    |     |                   |
 *  +--------+--------+     +---------+----------+     +-------------------+
 *           |                        |
 *           |                        |
 *           |                        |                +-------------------+
 *           |                        +---------------->                   |
 *           |                                         |     Overflowed    |
 *           +----------------------------------------->                   |
 *                                                     +-------------------+
 *
 * Invariants:
 *
 *   Initialized      => Parent >= Initialized
 *
 *   Assigned         => Parent == Assigned
 *
 *   Overflowed       => Parent == Overflowed || Parent.Next == Overflowed
 *
 * Thread-safety invariants:
 *
 *   Initialized      => Parent == Assigned
 *   // For a class that has an Initialized bitstring, its superclass needs to have an
 *   // Assigned bitstring since if its super class's bitstring is not Assigned yet,
 *   // once it becomes Assigned, we cannot update its children's bitstrings to maintain
 *   // all the tree invariants (below) atomically.
 *
 * --------------------------------------------------------------------------------------------
 * Knowing these transitions above, we can more closely define the various terms and operations:
 *
 * Definitions:
 *   (see also base/bit_string.h definitions).
 *
 *           Depth :=  Distance(Root, Class)
 *     Safe(Depth) :=  Min(Depth, MaxBitstringLen)
 *      PathToRoot :=  Bitstring[0..Safe(Depth))
 *           Next  :=  Bitstring[Depth]
 *           OF    ∈   {False, True}
 *    TruncPath(D) :=  PathToRoot[0..D)
 *
 * Local Invariants:
 *
 *   Uninitialized <=> StrLen(PathToRoot) == 0
 *                     Next == 0
 *                     OF == False
 *   Initialized   <=> StrLen(PathToRoot) < Depth
 *                     Next == 1
 *                     OF == False
 *   Assigned      <=> StrLen(PathToRoot) == Depth
 *                     Next >= 1
 *                     OF == False
 *   Overflowed    <=> OF == True
 *
 * Tree Invariants:
 *
 *   Uninitialized =>
 *     forall child ∈ Children(Class):
 *       child.State == Uninitialized
 *
 *   Assigned       =>
 *     forall child ∈ Children(Class):
 *       Next > Child.PathToRoot[Child.Depth-1]
 *
 *   ! Uninitialized =>
 *     forall ancestor ∈ Ancestors(Class):
 *       TruncPath(ancestor.Depth) == ancestor.PathToRoot
 *     forall unrelated ∈ (Classes - Ancestors(Class))
 *         s.t. unrelated.State == Assigned:
 *       TruncPath(unrelated.Depth) != unrelated.PathToRoot
 *
 * Thread-safety invariants:
 *
 *   Initialized   <=> StrLen(PathToRoot) == Safe(Depth - 1)
 *   // Initialized State corresponds to exactly 1 bitstring.
 *   // Cannot transition from Initialized to Initialized.
 */
struct SubtypeCheckInfo {
  // See above documentation for possible state transitions.
  enum State {
    kUninitialized,
    kInitialized,
    kAssigned,
    kOverflowed
  };

  // The result of a "src IsSubType target" check:
  enum Result {
    kUnknownSubtypeOf,  // Not enough data. Operand states weren't high enough.
    kNotSubtypeOf,      // Enough data. src is not a subchild of the target.
    kSubtypeOf          // Enough data. src is a subchild of the target.
  };

  // Get the raw depth.
  size_t GetDepth() const {
    return depth_;
  }

  // Chop off the depth, returning only the bitstring+of state.
  // (Used to store into memory, since storing the depth would be redundant.)
  SubtypeCheckBits GetSubtypeCheckBits() const {
    return bitstring_and_of_;
  }

  // Create from the depth and the bitstring+of state.
  // This is done for convenience to avoid passing in "depth" everywhere,
  // since our current state is almost always a function of depth.
  static SubtypeCheckInfo Create(SubtypeCheckBits compressed_value, size_t depth) {
    SubtypeCheckInfo io;
    io.depth_ = depth;
    io.bitstring_and_of_ = compressed_value;
    io.DcheckInvariants();
    return io;
  }

  // Is this a subtype of the target?
  //
  // The current state must be at least Initialized, and the target state
  // must be Assigned, otherwise the result will return kUnknownSubtypeOf.
  //
  // Normally, return kSubtypeOf or kNotSubtypeOf.
  Result IsSubtypeOf(const SubtypeCheckInfo& target) {
    if (target.GetState() != SubtypeCheckInfo::kAssigned) {
      return Result::kUnknownSubtypeOf;
    } else if (GetState() == SubtypeCheckInfo::kUninitialized) {
      return Result::kUnknownSubtypeOf;
    }

    BitString::StorageType source_value = GetEncodedPathToRoot();
    BitString::StorageType target_value = target.GetEncodedPathToRoot();
    BitString::StorageType target_mask = target.GetEncodedPathToRootMask();

    bool result = (source_value & target_mask) == (target_value);
    if (result) {
      DCHECK_EQ(GetPathToRoot().Truncate(target.GetSafeDepth()), target.GetPathToRoot())
          << "Source: " << *this << ", Target: " << target;
    } else {
      DCHECK_NE(GetPathToRoot().Truncate(target.GetSafeDepth()), target.GetPathToRoot())
          << "Source: " << *this << ", Target: " << target;
    }

    // Note: We could've also used shifts here, as described in subtype_check_bits.h,
    // but it doesn't make much of a difference in the Runtime since we aren't trying to optimize
    // for code size.

    return result ? Result::kSubtypeOf : Result::kNotSubtypeOf;
  }

  // Returns a new root SubtypeCheckInfo with a blank PathToRoot.
  // Post-condition: The return valued has an Assigned state.
  static SubtypeCheckInfo CreateRoot() {
    SubtypeCheckInfo io{};
    io.depth_ = 0u;
    io.SetNext(io.GetNext() + 1u);

    // The root is always considered assigned once it is no longer Initialized.
    DCHECK_EQ(SubtypeCheckInfo::kAssigned, io.GetState());
    return io;
  }

  // Copies the current PathToRoot into the child.
  //
  // If assign_next is true, then also assign a new SubtypeCheckInfo for a child by
  // assigning the current Next value to its PathToRoot[Depth] component.
  // Updates the current Next value as a side effect.
  //
  // Preconditions: State is either Assigned or Overflowed.
  // Returns: A new child >= Initialized state.
  SubtypeCheckInfo CreateChild(bool assign_next) {
    SubtypeCheckInfo child = *this;  // Copy everything (path, next, of).
    child.depth_ = depth_ + 1u;

    // Must be Assigned or Overflowed in order to create a subchild.
    DCHECK(GetState() == kAssigned || GetState() == kOverflowed)
        << "Unexpected bitstring state: " << GetState();

    // Begin transition to >= Initialized.

    // Always attempt to re-initialize Child's Next value.
    // Next must be non-0 to disambiguate it from Uninitialized.
    child.MaybeInitNext();

    // Always clear the inherited Parent's next Value, i.e. the child's last path entry.
    OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{});

    // The state is now Initialized | Overflowed.
    DCHECK_NE(kAssigned, child.GetState()) << child.GetBitString();
    DCHECK_NE(kUninitialized, child.GetState()) << child.GetBitString();

    if (assign_next == false) {
      child.DcheckInvariants();
      return child;
    }

    // Begin transition to >= Assigned.

    // Assign attempt.
    if (HasNext() && !bitstring_and_of_.overflow_) {
      BitStringChar next = GetNext();
      if (next != next.MaximumValue()) {
        // The parent's "next" value is now the child's latest path element.
        OverwriteNextValueFromParent(/*inout*/&child, next);
        // Update self next value, so that future CreateChild calls
        // do not get the same path value.
        SetNext(next + 1u);
      } else {
        child.MarkOverflowed();  // Too wide.
      }
    } else {
      child.MarkOverflowed();  // Too deep, or parent was already overflowed.
    }

    // The state is now Assigned | Overflowed.
    DCHECK(child.GetState() == kAssigned || child.GetState() == kOverflowed);

    child.DcheckInvariants();
    return child;
  }

  // Get the current state (Uninitialized, Initialized, Assigned, or Overflowed).
  // See the "SubtypeCheckInfo" documentation above which explains how a state is determined.
  State GetState() const {
    if (bitstring_and_of_.overflow_) {
      // Overflowed if and only if the OF bit was set.
      return kOverflowed;
    }

    if (GetBitString().IsEmpty()) {
      // Empty bitstring (all 0s) -> uninitialized.
      return kUninitialized;
    }

    // Either Assigned or Initialized.
    BitString path_to_root = GetPathToRoot();

    DCHECK(!HasNext() || GetNext() != 0u)
        << "Expected (Assigned|Initialized) state to have >0 Next value: "
        << GetNext() << " path: " << path_to_root;

    if (path_to_root.Length() == depth_) {
      return kAssigned;
    }

    return kInitialized;
  }

  // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
  // be used by a fast check "encoded_src & mask_target == encoded_target".
  BitString::StorageType GetEncodedPathToRoot() const {
    BitString::StorageType data = static_cast<BitString::StorageType>(GetPathToRoot());
    // Bit strings are logically in the least-significant memory.
    return data;
  }

  // Retrieve the path to root bitstring mask as a plain uintN_t that is amenable to
  // be used by a fast check "encoded_src & mask_target == encoded_target".
  BitString::StorageType GetEncodedPathToRootMask() const {
    size_t num_bitchars = GetSafeDepth();
    size_t bitlength = BitString::GetBitLengthTotalAtPosition(num_bitchars);
    return MaskLeastSignificant<BitString::StorageType>(bitlength);
  }

  // Get the "Next" bitchar, assuming that there is one to get.
  BitStringChar GetNext() const {
    DCHECK(HasNext());
    return GetBitString()[depth_];
  }

  // Try to get the Next value, if there is one.
  // Returns: Whether or not there was a Next value.
  bool MaybeGetNext(/*out*/BitStringChar* next) const {
    DCHECK(next != nullptr);

    if (HasNext()) {
      *next = GetBitString()[depth_];
      return true;
    }
    return false;
  }

 private:
  // Constructor intended for testing. Runs all invariant checks.
  SubtypeCheckInfo(BitString path_to_root, BitStringChar next, bool overflow, size_t depth) {
    SubtypeCheckBits iod;
    iod.bitstring_ = path_to_root;
    iod.overflow_ = overflow;

    bitstring_and_of_ = iod;
    depth_ = depth;

    // Len(Path-to-root) <= Depth.
    DCHECK_GE(depth_, path_to_root.Length())
        << "Path was too long for the depth, path: " << path_to_root;

    bool did_overlap = false;
    if (HasNext()) {
      if (kIsDebugBuild) {
        did_overlap = (GetNext() != 0u);
      }

      SetNext(next);

      DCHECK_EQ(next, GetNext());
    }
    // "Next" must be set before we can check the invariants.
    DcheckInvariants();
    DCHECK(!did_overlap)
          << "Path to root overlapped with Next value, path: " << path_to_root;
    DCHECK_EQ(path_to_root, GetPathToRoot());
  }

  // Factory intended for testing. Skips DcheckInvariants.
  static SubtypeCheckInfo MakeUnchecked(BitString bitstring, bool overflow, size_t depth) {
    SubtypeCheckBits iod;
    iod.bitstring_ = bitstring;
    iod.overflow_ = overflow;

    SubtypeCheckInfo io;
    io.depth_ = depth;
    io.bitstring_and_of_ = iod;

    return io;
  }

  void SetNext(BitStringChar next) {
    DCHECK(HasNext());
    BitString bs = GetBitString();
    bs.SetAt(depth_, next);
    SetBitString(bs);
  }

  void SetNextUnchecked(BitStringChar next) {
    BitString bs = GetBitString();
    bs.SetAt(depth_, next);
    SetBitStringUnchecked(bs);
  }

  // If there is a next field, set it to 1.
  void MaybeInitNext() {
    if (HasNext()) {
      // Clearing out the "Next" value like this
      // is often an intermediate operation which temporarily
      // violates the invariants. Do not do the extra dchecks.
      SetNextUnchecked(BitStringChar{});
      SetNextUnchecked(GetNext()+1u);
    }
  }

  BitString GetPathToRoot() const {
    size_t end = GetSafeDepth();
    return GetBitString().Truncate(end);
  }

  bool HasNext() const {
    return depth_ < BitString::kCapacity;
  }

  void MarkOverflowed() {
    bitstring_and_of_.overflow_ = true;
  }

  static constexpr bool HasBitStringCharStorage(size_t idx) {
    return idx < BitString::kCapacity;
  }

  size_t GetSafeDepth() const {
    return GetSafeDepth(depth_);
  }

  // Get a "safe" depth, one that is truncated to the bitstring max capacity.
  // Using a value larger than this will cause undefined behavior.
  static size_t GetSafeDepth(size_t depth) {
    return std::min(depth, BitString::kCapacity);
  }

  BitString GetBitString() const {
    return bitstring_and_of_.bitstring_;
  }

  void SetBitString(const BitString& val) {
    SetBitStringUnchecked(val);
    DcheckInvariants();
  }

  void SetBitStringUnchecked(const BitString& val) {
    bitstring_and_of_.bitstring_ = val;
  }

  void OverwriteNextValueFromParent(/*inout*/SubtypeCheckInfo* child, BitStringChar value) const {
    // Helper function for CreateChild.
    if (HasNext()) {
      // When we copied the "Next" value, it is now our
      // last path component in the child.
      // Always overwrite it with either a cleared value or the parent's Next value.
      BitString bs = child->GetBitString();

      // Safe write. This.Next always occupies same slot as Child[Depth_].
      DCHECK(child->HasBitStringCharStorage(depth_));

      bs.SetAt(depth_, value);

      // The child is temporarily in a bad state until it is fixed up further.
      // Do not do the normal dchecks which do not allow transient badness.
      child->SetBitStringUnchecked(bs);
    }
  }

  void DcheckInvariants() const {
    if (kIsDebugBuild) {
      CHECK_GE(GetSafeDepth(depth_ + 1u), GetBitString().Length())
          << "Bitstring too long for depth, bitstring: " << GetBitString() << ", depth: " << depth_;

      BitString path_to_root = GetPathToRoot();

      // A 'null' (\0) character in path-to-root must be followed only
      // by other null characters.
      size_t i;
      for (i = 0; i < BitString::kCapacity; ++i) {
        BitStringChar bc = path_to_root[i];
        if (bc == 0u) {
          break;
        }
      }

      // All characters following a 0 must also be 0.
      for (; i < BitString::kCapacity; ++i) {
        BitStringChar bc = path_to_root[i];
        if (bc != 0u) {
          LOG(FATAL) << "Path to root had non-0s following 0s: " << path_to_root;
        }
      }

       // Trigger any dchecks in GetState.
      (void)GetState();
    }
  }

  SubtypeCheckInfo() = default;
  size_t depth_;
  SubtypeCheckBits bitstring_and_of_;

  friend struct ::SubtypeCheckInfoTest;
  friend std::ostream& operator<<(std::ostream& os, const SubtypeCheckInfo& io);
};

// Prints the SubtypeCheckInfo::State, e.g. "kUnitialized".
inline std::ostream& operator<<(std::ostream& os, const SubtypeCheckInfo::State& state) {
  switch (state) {
    case SubtypeCheckInfo::kUninitialized:
      os << "kUninitialized";
      break;
    case SubtypeCheckInfo::kInitialized:
      os << "kInitialized";
      break;
    case SubtypeCheckInfo::kAssigned:
      os << "kAssigned";
      break;
    case SubtypeCheckInfo::kOverflowed:
      os << "kOverflowed";
      break;
    default:
      os << "(Invalid SubtypeCheckInfo::State " << static_cast<int>(state) << ")";
  }
  return os;
}

// Prints e.g. "SubtypeCheckInfo{BitString[1,2,3], depth: 3, of:1}"
inline std::ostream& operator<<(std::ostream& os, const SubtypeCheckInfo& io) {
  os << "SubtypeCheckInfo{" << io.GetBitString() << ", "
     << "depth: " << io.depth_ << ", of:" << io.bitstring_and_of_.overflow_ << "}";
  return os;
}

}  // namespace art

#endif  // ART_RUNTIME_SUBTYPE_CHECK_INFO_H_