//===- llvm/unittest/DebugInfo/DWARFDebugArangeSetTest.cpp-----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"

using namespace llvm;

namespace {

struct WarningHandler {
  ~WarningHandler() { EXPECT_THAT_ERROR(std::move(Err), Succeeded()); }

  void operator()(Error E) { Err = joinErrors(std::move(Err), std::move(E)); }

  Error getWarning() { return std::move(Err); }

  Error Err = Error::success();
};

template <size_t SecSize>
void ExpectExtractError(const char (&SecDataRaw)[SecSize],
                        const char *ErrorMessage) {
  DWARFDataExtractor Extractor(StringRef(SecDataRaw, SecSize - 1),
                               /* IsLittleEndian = */ true,
                               /* AddressSize = */ 4);
  DWARFDebugArangeSet Set;
  uint64_t Offset = 0;
  WarningHandler Warnings;
  Error E = Set.extract(Extractor, &Offset, Warnings);
  ASSERT_TRUE(E.operator bool());
  EXPECT_STREQ(ErrorMessage, toString(std::move(E)).c_str());
}

TEST(DWARFDebugArangeSet, LengthExceedsSectionSize) {
  static const char DebugArangesSecRaw[] =
      "\x15\x00\x00\x00" // The length exceeds the section boundaries
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      "\x00\x00\x00\x00" // Termination tuple
      "\x00\x00\x00\x00";
  ExpectExtractError(
      DebugArangesSecRaw,
      "the length of address range table at offset 0x0 exceeds section size");
}

TEST(DWARFDebugArangeSet, LengthExceedsSectionSizeDWARF64) {
  static const char DebugArangesSecRaw[] =
      "\xff\xff\xff\xff"                 // DWARF64 mark
      "\x15\x00\x00\x00\x00\x00\x00\x00" // The length exceeds the section
                                         // boundaries
      "\x02\x00"                         // Version
      "\x00\x00\x00\x00\x00\x00\x00\x00" // Debug Info Offset
      "\x04"                             // Address Size
      "\x00"                             // Segment Selector Size
                                         // No padding
      "\x00\x00\x00\x00"                 // Termination tuple
      "\x00\x00\x00\x00";
  ExpectExtractError(
      DebugArangesSecRaw,
      "the length of address range table at offset 0x0 exceeds section size");
}

TEST(DWARFDebugArangeSet, UnsupportedAddressSize) {
  static const char DebugArangesSecRaw[] =
      "\x0c\x00\x00\x00"  // Length
      "\x02\x00"          // Version
      "\x00\x00\x00\x00"  // Debug Info Offset
      "\x03"              // Address Size (not supported)
      "\x00"              // Segment Selector Size
                          // No padding
      "\x00\x00\x00\x00"; // Termination tuple
  ExpectExtractError(
      DebugArangesSecRaw,
      "address range table at offset 0x0 has unsupported address size: 3 "
      "(supported are 2, 4, 8)");
}

TEST(DWARFDebugArangeSet, UnsupportedSegmentSelectorSize) {
  static const char DebugArangesSecRaw[] =
      "\x14\x00\x00\x00" // Length
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x04"             // Segment Selector Size (not supported)
                         // No padding
      "\x00\x00\x00\x00" // Termination tuple
      "\x00\x00\x00\x00"
      "\x00\x00\x00\x00";
  ExpectExtractError(
      DebugArangesSecRaw,
      "non-zero segment selector size in address range table at offset 0x0 "
      "is not supported");
}

TEST(DWARFDebugArangeSet, NoTerminationEntry) {
  static const char DebugArangesSecRaw[] =
      "\x14\x00\x00\x00" // Length
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      "\x00\x00\x00\x00" // Entry: Address
      "\x01\x00\x00\x00" //        Length
      ;                  // No termination tuple
  ExpectExtractError(
      DebugArangesSecRaw,
      "address range table at offset 0x0 is not terminated by null entry");
}

TEST(DWARFDebugArangeSet, ReservedUnitLength) {
  // Note: 12 is the minimum length to pass the basic check for the size of
  // the section. 1 will be automatically subtracted in ExpectExtractError().
  static const char DebugArangesSecRaw[12 + 1] =
      "\xf0\xff\xff\xff"; // Reserved unit length value
  ExpectExtractError(DebugArangesSecRaw,
                     "parsing address ranges table at offset 0x0: unsupported "
                     "reserved unit length of value 0xfffffff0");
}

TEST(DWARFDebugArangeSet, SectionTooShort) {
  // Note: 1 will be automatically subtracted in ExpectExtractError().
  static const char DebugArangesSecRaw[11 + 1] = {0};
  ExpectExtractError(DebugArangesSecRaw,
                     "parsing address ranges table at offset 0x0: unexpected "
                     "end of data at offset 0xb while reading [0xb, 0xc)");
}

TEST(DWARFDebugArangeSet, SectionTooShortDWARF64) {
  // Note: 1 will be automatically subtracted in ExpectExtractError().
  static const char DebugArangesSecRaw[23 + 1] =
      "\xff\xff\xff\xff"; // DWARF64 mark
  ExpectExtractError(DebugArangesSecRaw,
                     "parsing address ranges table at offset 0x0: unexpected "
                     "end of data at offset 0x17 while reading [0x17, 0x18)");
}

TEST(DWARFDebugArangeSet, NoSpaceForEntries) {
  static const char DebugArangesSecRaw[] =
      "\x0c\x00\x00\x00" // Length
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      ;                  // No entries
  ExpectExtractError(
      DebugArangesSecRaw,
      "address range table at offset 0x0 has an insufficient length "
      "to contain any entries");
}

TEST(DWARFDebugArangeSet, UnevenLength) {
  static const char DebugArangesSecRaw[] =
      "\x1b\x00\x00\x00" // Length (not a multiple of tuple size)
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      "\x00\x00\x00\x00" // Entry: Address
      "\x01\x00\x00\x00" //        Length
      "\x00\x00\x00\x00" // Termination tuple
      "\x00\x00\x00\x00";
  ExpectExtractError(
      DebugArangesSecRaw,
      "address range table at offset 0x0 has length that is not a multiple "
      "of the tuple size");
}

TEST(DWARFDebugArangeSet, ZeroAddressEntry) {
  static const char DebugArangesSecRaw[] =
      "\x1c\x00\x00\x00" // Length
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      "\x00\x00\x00\x00" // Entry1: Address
      "\x01\x00\x00\x00" //         Length
      "\x00\x00\x00\x00" // Termination tuple
      "\x00\x00\x00\x00";
  DWARFDataExtractor Extractor(
      StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1),
      /*IsLittleEndian=*/true,
      /*AddressSize=*/4);
  DWARFDebugArangeSet Set;
  uint64_t Offset = 0;
  ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, WarningHandler()),
                    Succeeded());
  auto Range = Set.descriptors();
  auto Iter = Range.begin();
  ASSERT_EQ(std::distance(Iter, Range.end()), 1);
  EXPECT_EQ(Iter->Address, 0u);
  EXPECT_EQ(Iter->Length, 1u);
}

TEST(DWARFDebugArangeSet, ZeroLengthEntry) {
  static const char DebugArangesSecRaw[] =
      "\x1c\x00\x00\x00" // Length
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      "\x01\x00\x00\x00" // Entry1: Address
      "\x00\x00\x00\x00" //         Length
      "\x00\x00\x00\x00" // Termination tuple
      "\x00\x00\x00\x00";
  DWARFDataExtractor Extractor(
      StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1),
      /*IsLittleEndian=*/true,
      /*AddressSize=*/4);
  DWARFDebugArangeSet Set;
  uint64_t Offset = 0;
  ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, WarningHandler()),
                    Succeeded());
  auto Range = Set.descriptors();
  auto Iter = Range.begin();
  ASSERT_EQ(std::distance(Iter, Range.end()), 1);
  EXPECT_EQ(Iter->Address, 1u);
  EXPECT_EQ(Iter->Length, 0u);
}

TEST(DWARFDebugArangesSet, PrematureTerminator) {
  static const char DebugArangesSecRaw[] =
      "\x24\x00\x00\x00" // Length
      "\x02\x00"         // Version
      "\x00\x00\x00\x00" // Debug Info Offset
      "\x04"             // Address Size
      "\x00"             // Segment Selector Size
      "\x00\x00\x00\x00" // Padding
      "\x00\x00\x00\x00" // Entry1: Premature
      "\x00\x00\x00\x00" //         terminator
      "\x01\x00\x00\x00" // Entry2: Address
      "\x01\x00\x00\x00" //         Length
      "\x00\x00\x00\x00" // Termination tuple
      "\x00\x00\x00\x00";
  DWARFDataExtractor Extractor(
      StringRef(DebugArangesSecRaw, sizeof(DebugArangesSecRaw) - 1),
      /*IsLittleEndian=*/true,
      /*AddressSize=*/4);
  DWARFDebugArangeSet Set;
  uint64_t Offset = 0;
  WarningHandler Warnings;
  ASSERT_THAT_ERROR(Set.extract(Extractor, &Offset, Warnings), Succeeded());
  auto Range = Set.descriptors();
  auto Iter = Range.begin();
  ASSERT_EQ(std::distance(Iter, Range.end()), 2);
  EXPECT_EQ(Iter->Address, 0u);
  EXPECT_EQ(Iter->Length, 0u);
  ++Iter;
  EXPECT_EQ(Iter->Address, 1u);
  EXPECT_EQ(Iter->Length, 1u);
  EXPECT_THAT_ERROR(
      Warnings.getWarning(),
      FailedWithMessage("address range table at offset 0x0 has a premature "
                        "terminator entry at offset 0x10"));
}

} // end anonymous namespace
