/*
 * 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 <stdint.h>

#include <ios>
#include <vector>

#include <gtest/gtest.h>

#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/Log.h>

#include "DwarfOp.h"

#include "MemoryFake.h"
#include "RegsFake.h"

namespace unwindstack {

template <typename TypeParam>
class DwarfOpTest : public ::testing::Test {
 protected:
  void SetUp() override {
    op_memory_.Clear();
    regular_memory_.Clear();
    mem_.reset(new DwarfMemory(&op_memory_));
    op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
  }

  MemoryFake op_memory_;
  MemoryFake regular_memory_;

  std::unique_ptr<DwarfMemory> mem_;
  std::unique_ptr<DwarfOp<TypeParam>> op_;
};
TYPED_TEST_SUITE_P(DwarfOpTest);

TYPED_TEST_P(DwarfOpTest, decode) {
  // Memory error.
  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
  EXPECT_EQ(0U, this->op_->LastErrorAddress());

  // No error.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
  this->mem_->set_cur_offset(0);
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
  ASSERT_EQ(0x96U, this->op_->cur_op());
  ASSERT_EQ(1U, this->mem_->cur_offset());
}

TYPED_TEST_P(DwarfOpTest, eval) {
  // Memory error.
  ASSERT_FALSE(this->op_->Eval(0, 2));
  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
  EXPECT_EQ(0U, this->op_->LastErrorAddress());

  // Register set.
  // Do this first, to verify that subsequent calls reset the value.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
  ASSERT_TRUE(this->op_->Eval(0, 1));
  ASSERT_TRUE(this->op_->is_register());
  ASSERT_EQ(1U, this->mem_->cur_offset());
  ASSERT_EQ(1U, this->op_->StackSize());

  // Multi operation opcodes.
  std::vector<uint8_t> opcode_buffer = {
      0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Eval(0, 8));
  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
  ASSERT_FALSE(this->op_->is_register());
  ASSERT_EQ(8U, this->mem_->cur_offset());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(1U, this->op_->StackAt(0));
  ASSERT_EQ(2U, this->op_->StackAt(1));
  ASSERT_EQ(3U, this->op_->StackAt(2));
  ASSERT_EQ(4U, this->op_->StackAt(3));

  // Infinite loop.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
  ASSERT_FALSE(this->op_->Eval(0, 4));
  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->LastErrorCode());
  ASSERT_FALSE(this->op_->is_register());
  ASSERT_EQ(0U, this->op_->StackSize());
}

TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
  // Fill the buffer with all of the illegal opcodes.
  std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
  for (size_t opcode = 0xa0; opcode < 256; opcode++) {
    opcode_buffer.push_back(opcode);
  }
  this->op_memory_.SetMemory(0, opcode_buffer);

  for (size_t i = 0; i < opcode_buffer.size(); i++) {
    ASSERT_FALSE(this->op_->Decode());
    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
  }
}

TYPED_TEST_P(DwarfOpTest, not_implemented) {
  std::vector<uint8_t> opcode_buffer = {
      // Push values so that any not implemented ops will return the right error.
      0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
      // xderef
      0x18,
      // fbreg
      0x91, 0x01,
      // piece
      0x93, 0x01,
      // xderef_size
      0x95, 0x01,
      // push_object_address
      0x97,
      // call2
      0x98, 0x01, 0x02,
      // call4
      0x99, 0x01, 0x02, 0x03, 0x04,
      // call_ref
      0x9a,
      // form_tls_address
      0x9b,
      // call_frame_cfa
      0x9c,
      // bit_piece
      0x9d, 0x01, 0x01,
      // implicit_value
      0x9e, 0x01,
      // stack_value
      0x9f,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  // Push the stack values.
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_TRUE(this->op_->Decode());

  while (this->mem_->cur_offset() < opcode_buffer.size()) {
    ASSERT_FALSE(this->op_->Decode());
    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->LastErrorCode());
  }
}

TYPED_TEST_P(DwarfOpTest, op_addr) {
  std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
  if (sizeof(TypeParam) == 8) {
    opcode_buffer.push_back(0x56);
    opcode_buffer.push_back(0x67);
    opcode_buffer.push_back(0x78);
    opcode_buffer.push_back(0x89);
  }
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x03, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, op_deref) {
  std::vector<uint8_t> opcode_buffer = {
      // Try a dereference with nothing on the stack.
      0x06,
      // Add an address, then dereference.
      0x0a, 0x10, 0x20, 0x06,
      // Now do another dereference that should fail in memory.
      0x06,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);
  TypeParam value = 0x12345678;
  this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x06, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(value, this->op_->StackAt(0));

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
  ASSERT_EQ(0x12345678U, this->op_->LastErrorAddress());
}

TYPED_TEST_P(DwarfOpTest, op_deref_size) {
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
  TypeParam value = 0x12345678;
  this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  // Read all byte sizes up to the sizeof the type.
  for (size_t i = 1; i < sizeof(TypeParam); i++) {
    this->op_memory_.SetMemory(
        0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
    ASSERT_TRUE(this->op_->Eval(0, 5)) << "Failed at size " << i;
    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
    ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
    TypeParam expected_value = 0;
    memcpy(&expected_value, &value, i);
    ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
  }

  // Zero byte read.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
  ASSERT_FALSE(this->op_->Eval(0, 5));
  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());

  // Read too many bytes.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
  ASSERT_FALSE(this->op_->Eval(0, 5));
  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());

  // Force bad memory read.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
  ASSERT_FALSE(this->op_->Eval(0, 5));
  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
  EXPECT_EQ(0x4010U, this->op_->LastErrorAddress());
}

TYPED_TEST_P(DwarfOpTest, const_unsigned) {
  std::vector<uint8_t> opcode_buffer = {
      // const1u
      0x08, 0x12, 0x08, 0xff,
      // const2u
      0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
      // const4u
      0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
      // const8u
      0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
      0xdc, 0xed, 0xfe,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  // const1u
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x08, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x12U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x08, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0xffU, this->op_->StackAt(0));

  // const2u
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0a, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x1245U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0a, this->op_->cur_op());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(0xff00U, this->op_->StackAt(0));

  // const4u
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0c, this->op_->cur_op());
  ASSERT_EQ(5U, this->op_->StackSize());
  ASSERT_EQ(0x45342312U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0c, this->op_->cur_op());
  ASSERT_EQ(6U, this->op_->StackSize());
  ASSERT_EQ(0xff010203U, this->op_->StackAt(0));

  // const8u
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0e, this->op_->cur_op());
  ASSERT_EQ(7U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
  }

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0e, this->op_->cur_op());
  ASSERT_EQ(8U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, const_signed) {
  std::vector<uint8_t> opcode_buffer = {
      // const1s
      0x09, 0x12, 0x09, 0xff,
      // const2s
      0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
      // const4s
      0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
      // const8s
      0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
      0xef, 0xef, 0xff,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  // const1s
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x09, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x12U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x09, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));

  // const2s
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0b, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x3221U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0b, this->op_->cur_op());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));

  // const4s
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0d, this->op_->cur_op());
  ASSERT_EQ(5U, this->op_->StackSize());
  ASSERT_EQ(0x12233445U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0d, this->op_->cur_op());
  ASSERT_EQ(6U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));

  // const8s
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0f, this->op_->cur_op());
  ASSERT_EQ(7U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
  }

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x0f, this->op_->cur_op());
  ASSERT_EQ(8U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, const_uleb) {
  std::vector<uint8_t> opcode_buffer = {
      // Single byte ULEB128
      0x10, 0x22, 0x10, 0x7f,
      // Multi byte ULEB128
      0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
      0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  // Single byte ULEB128
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x10, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x22U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x10, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x7fU, this->op_->StackAt(0));

  // Multi byte ULEB128
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x10, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x1122U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x10, this->op_->cur_op());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(0x3a22U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x10, this->op_->cur_op());
  ASSERT_EQ(5U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
  }

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x10, this->op_->cur_op());
  ASSERT_EQ(6U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, const_sleb) {
  std::vector<uint8_t> opcode_buffer = {
      // Single byte SLEB128
      0x11, 0x22, 0x11, 0x7f,
      // Multi byte SLEB128
      0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
      0x09, 0x11,
  };
  if (sizeof(TypeParam) == 4) {
    opcode_buffer.push_back(0xb8);
    opcode_buffer.push_back(0xd3);
    opcode_buffer.push_back(0x63);
  } else {
    opcode_buffer.push_back(0x81);
    opcode_buffer.push_back(0x82);
    opcode_buffer.push_back(0x83);
    opcode_buffer.push_back(0x84);
    opcode_buffer.push_back(0x85);
    opcode_buffer.push_back(0x86);
    opcode_buffer.push_back(0x87);
    opcode_buffer.push_back(0x88);
    opcode_buffer.push_back(0x79);
  }
  this->op_memory_.SetMemory(0, opcode_buffer);

  // Single byte SLEB128
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x11, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x22U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x11, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));

  // Multi byte SLEB128
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x11, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x1122U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x11, this->op_->cur_op());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x11, this->op_->cur_op());
  ASSERT_EQ(5U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
  }

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x11, this->op_->cur_op());
  ASSERT_EQ(6U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
  } else {
    ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, op_dup) {
  std::vector<uint8_t> opcode_buffer = {
      // Should fail since nothing is on the stack.
      0x12,
      // Push on a value and dup.
      0x08, 0x15, 0x12,
      // Do it again.
      0x08, 0x23, 0x12,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(0x12, this->op_->cur_op());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x12, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x15U, this->op_->StackAt(0));
  ASSERT_EQ(0x15U, this->op_->StackAt(1));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x12, this->op_->cur_op());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(0x23U, this->op_->StackAt(0));
  ASSERT_EQ(0x23U, this->op_->StackAt(1));
  ASSERT_EQ(0x15U, this->op_->StackAt(2));
  ASSERT_EQ(0x15U, this->op_->StackAt(3));
}

TYPED_TEST_P(DwarfOpTest, op_drop) {
  std::vector<uint8_t> opcode_buffer = {
      // Push a couple of values.
      0x08, 0x10, 0x08, 0x20,
      // Drop the values.
      0x13, 0x13,
      // Attempt to drop empty stack.
      0x13,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x13, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x10U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x13, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(0x13, this->op_->cur_op());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_over) {
  std::vector<uint8_t> opcode_buffer = {
      // Push a couple of values.
      0x08, 0x1a, 0x08, 0xed,
      // Copy a value.
      0x14,
      // Remove all but one element.
      0x13, 0x13,
      // Provoke a failure with this opcode.
      0x14,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x14, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x1aU, this->op_->StackAt(0));
  ASSERT_EQ(0xedU, this->op_->StackAt(1));
  ASSERT_EQ(0x1aU, this->op_->StackAt(2));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(0x14, this->op_->cur_op());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_pick) {
  std::vector<uint8_t> opcode_buffer = {
      // Push a few values.
      0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
      // Copy the value at offset 2.
      0x15, 0x01,
      // Copy the last value in the stack.
      0x15, 0x03,
      // Choose an invalid index.
      0x15, 0x10,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x15, this->op_->cur_op());
  ASSERT_EQ(4U, this->op_->StackSize());
  ASSERT_EQ(0xedU, this->op_->StackAt(0));
  ASSERT_EQ(0x34U, this->op_->StackAt(1));
  ASSERT_EQ(0xedU, this->op_->StackAt(2));
  ASSERT_EQ(0x1aU, this->op_->StackAt(3));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x15, this->op_->cur_op());
  ASSERT_EQ(5U, this->op_->StackSize());
  ASSERT_EQ(0x1aU, this->op_->StackAt(0));
  ASSERT_EQ(0xedU, this->op_->StackAt(1));
  ASSERT_EQ(0x34U, this->op_->StackAt(2));
  ASSERT_EQ(0xedU, this->op_->StackAt(3));
  ASSERT_EQ(0x1aU, this->op_->StackAt(4));

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(0x15, this->op_->cur_op());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_swap) {
  std::vector<uint8_t> opcode_buffer = {
      // Push a couple of values.
      0x08, 0x26, 0x08, 0xab,
      // Swap values.
      0x16,
      // Pop a value to cause a failure.
      0x13, 0x16,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0xabU, this->op_->StackAt(0));
  ASSERT_EQ(0x26U, this->op_->StackAt(1));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x16, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x26U, this->op_->StackAt(0));
  ASSERT_EQ(0xabU, this->op_->StackAt(1));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(0x16, this->op_->cur_op());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_rot) {
  std::vector<uint8_t> opcode_buffer = {
      // Rotate that should cause a failure.
      0x17, 0x08, 0x10,
      // Only 1 value on stack, should fail.
      0x17, 0x08, 0x20,
      // Only 2 values on stack, should fail.
      0x17, 0x08, 0x30,
      // Should rotate properly.
      0x17,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x30U, this->op_->StackAt(0));
  ASSERT_EQ(0x20U, this->op_->StackAt(1));
  ASSERT_EQ(0x10U, this->op_->StackAt(2));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x17, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(0x20U, this->op_->StackAt(0));
  ASSERT_EQ(0x10U, this->op_->StackAt(1));
  ASSERT_EQ(0x30U, this->op_->StackAt(2));
}

TYPED_TEST_P(DwarfOpTest, op_abs) {
  std::vector<uint8_t> opcode_buffer = {
      // Abs that should fail.
      0x19,
      // A value that is already positive.
      0x08, 0x10, 0x19,
      // A value that is negative.
      0x11, 0x7f, 0x19,
      // A value that is large and negative.
      0x11, 0x81, 0x80, 0x80, 0x80,
  };
  if (sizeof(TypeParam) == 4) {
    opcode_buffer.push_back(0x08);
  } else {
    opcode_buffer.push_back(0x80);
    opcode_buffer.push_back(0x80);
    opcode_buffer.push_back(0x01);
  }
  opcode_buffer.push_back(0x19);
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x10U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x19, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x10U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x19, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x1U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x19, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(2147483647U, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, op_and) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x1b,
      // Push a single value.
      0x08, 0x20,
      // One element stack, and op will fail.
      0x1b,
      // Push another value.
      0x08, 0x02, 0x1b,
      // Push on two negative values.
      0x11, 0x7c, 0x11, 0x7f, 0x1b,
      // Push one negative, one positive.
      0x11, 0x10, 0x11, 0x7c, 0x1b,
      // Divide by zero.
      0x11, 0x10, 0x11, 0x00, 0x1b,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  // Two positive values.
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1b, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x10U, this->op_->StackAt(0));

  // Two negative values.
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1b, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x04U, this->op_->StackAt(0));

  // One negative value, one positive value.
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(4U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1b, this->op_->cur_op());
  ASSERT_EQ(3U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));

  // Divide by zero.
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(4U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(5U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_div) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x1a,
      // Push a single value.
      0x08, 0x48,
      // One element stack, and op will fail.
      0x1a,
      // Push another value.
      0x08, 0xf0, 0x1a,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1a, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x40U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_minus) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x1c,
      // Push a single value.
      0x08, 0x48,
      // One element stack, and op will fail.
      0x1c,
      // Push another value.
      0x08, 0x04, 0x1c,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1c, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x44U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_mod) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x1d,
      // Push a single value.
      0x08, 0x47,
      // One element stack, and op will fail.
      0x1d,
      // Push another value.
      0x08, 0x04, 0x1d,
      // Try a mod of zero.
      0x08, 0x01, 0x08, 0x00, 0x1d,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1d, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x03U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(3U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_mul) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x1e,
      // Push a single value.
      0x08, 0x48,
      // One element stack, and op will fail.
      0x1e,
      // Push another value.
      0x08, 0x04, 0x1e,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1e, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x120U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_neg) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x1f,
      // Push a single value.
      0x08, 0x48, 0x1f,
      // Push a negative value.
      0x11, 0x7f, 0x1f,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1f, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x1f, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x01U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_not) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x20,
      // Push a single value.
      0x08, 0x4, 0x20,
      // Push a negative value.
      0x11, 0x7c, 0x20,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x20, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x20, this->op_->cur_op());
  ASSERT_EQ(2U, this->op_->StackSize());
  ASSERT_EQ(0x03U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_or) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x21,
      // Push a single value.
      0x08, 0x48,
      // One element stack, and op will fail.
      0x21,
      // Push another value.
      0x08, 0xf4, 0x21,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x21, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0xfcU, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_plus) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x22,
      // Push a single value.
      0x08, 0xff,
      // One element stack, and op will fail.
      0x22,
      // Push another value.
      0x08, 0xf2, 0x22,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x22, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x23,
      // Push a single value.
      0x08, 0x50, 0x23, 0x80, 0x51,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x23, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_shl) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x24,
      // Push a single value.
      0x08, 0x67,
      // One element stack, and op will fail.
      0x24,
      // Push another value.
      0x08, 0x03, 0x24,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x24, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x338U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_shr) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x25,
      // Push a single value.
      0x11, 0x70,
      // One element stack, and op will fail.
      0x25,
      // Push another value.
      0x08, 0x03, 0x25,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x25, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  if (sizeof(TypeParam) == 4) {
    ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
  } else {
    ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, op_shra) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x26,
      // Push a single value.
      0x11, 0x70,
      // One element stack, and op will fail.
      0x26,
      // Push another value.
      0x08, 0x03, 0x26,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x26, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_xor) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x27,
      // Push a single value.
      0x08, 0x11,
      // One element stack, and op will fail.
      0x27,
      // Push another value.
      0x08, 0x41, 0x27,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(2U, this->op_->StackSize());

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x27, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x50U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_bra) {
  std::vector<uint8_t> opcode_buffer = {
      // No stack, and op will fail.
      0x28,
      // Push on a non-zero value with a positive branch.
      0x08, 0x11, 0x28, 0x02, 0x01,
      // Push on a zero value with a positive branch.
      0x08, 0x00, 0x28, 0x05, 0x00,
      // Push on a non-zero value with a negative branch.
      0x08, 0x11, 0x28, 0xfc, 0xff,
      // Push on a zero value with a negative branch.
      0x08, 0x00, 0x28, 0xf0, 0xff,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_FALSE(this->op_->Decode());
  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

  // Push on a non-zero value with a positive branch.
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  uint64_t offset = this->mem_->cur_offset() + 3;
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x28, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
  ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());

  // Push on a zero value with a positive branch.
  this->mem_->set_cur_offset(offset);
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  offset = this->mem_->cur_offset() + 3;
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x28, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
  ASSERT_EQ(offset - 5, this->mem_->cur_offset());

  // Push on a non-zero value with a negative branch.
  this->mem_->set_cur_offset(offset);
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  offset = this->mem_->cur_offset() + 3;
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x28, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
  ASSERT_EQ(offset - 4, this->mem_->cur_offset());

  // Push on a zero value with a negative branch.
  this->mem_->set_cur_offset(offset);
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(1U, this->op_->StackSize());

  offset = this->mem_->cur_offset() + 3;
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x28, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
  ASSERT_EQ(offset + 16, this->mem_->cur_offset());
}

TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
  // All of the ops require two stack elements. Loop through all of these
  // ops with potential errors.
  std::vector<uint8_t> opcode_buffer = {
      0xff,  // Place holder for compare op.
      0x08, 0x11,
      0xff,  // Place holder for compare op.
  };

  for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
    opcode_buffer[0] = opcode;
    opcode_buffer[3] = opcode;
    this->op_memory_.SetMemory(0, opcode_buffer);

    ASSERT_FALSE(this->op_->Eval(0, 1));
    ASSERT_EQ(opcode, this->op_->cur_op());
    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());

    ASSERT_FALSE(this->op_->Eval(1, 4));
    ASSERT_EQ(opcode, this->op_->cur_op());
    ASSERT_EQ(1U, this->op_->StackSize());
    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
  }
}

TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
  // Have three different checks for each compare op:
  //   - Both values the same.
  //   - The first value larger than the second.
  //   - The second value larger than the first.
  std::vector<uint8_t> opcode_buffer = {
      // Values the same.
      0x08, 0x11, 0x08, 0x11,
      0xff,  // Placeholder.
      // First value larger.
      0x08, 0x12, 0x08, 0x10,
      0xff,  // Placeholder.
      // Second value larger.
      0x08, 0x10, 0x08, 0x12,
      0xff,  // Placeholder.
  };

  // Opcode followed by the expected values on the stack.
  std::vector<uint8_t> expected = {
      0x29, 1, 0, 0,  // eq
      0x2a, 1, 1, 0,  // ge
      0x2b, 0, 1, 0,  // gt
      0x2c, 1, 0, 1,  // le
      0x2d, 0, 0, 1,  // lt
      0x2e, 0, 1, 1,  // ne
  };
  for (size_t i = 0; i < expected.size(); i += 4) {
    opcode_buffer[4] = expected[i];
    opcode_buffer[9] = expected[i];
    opcode_buffer[14] = expected[i];
    this->op_memory_.SetMemory(0, opcode_buffer);

    ASSERT_TRUE(this->op_->Eval(0, 15))
        << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";

    ASSERT_EQ(3U, this->op_->StackSize());
    ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
    ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
    ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
  }
}

TYPED_TEST_P(DwarfOpTest, op_skip) {
  std::vector<uint8_t> opcode_buffer = {
      // Positive value.
      0x2f, 0x10, 0x20,
      // Negative value.
      0x2f, 0xfd, 0xff,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  uint64_t offset = this->mem_->cur_offset() + 3;
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x2f, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
  ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());

  this->mem_->set_cur_offset(offset);
  offset = this->mem_->cur_offset() + 3;
  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x2f, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
  ASSERT_EQ(offset - 3, this->mem_->cur_offset());
}

TYPED_TEST_P(DwarfOpTest, op_lit) {
  std::vector<uint8_t> opcode_buffer;

  // Verify every lit opcode.
  for (uint8_t op = 0x30; op <= 0x4f; op++) {
    opcode_buffer.push_back(op);
  }
  this->op_memory_.SetMemory(0, opcode_buffer);

  for (size_t i = 0; i < opcode_buffer.size(); i++) {
    uint32_t op = opcode_buffer[i];
    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op, this->op_->cur_op());
    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
  }
}

TYPED_TEST_P(DwarfOpTest, op_reg) {
  std::vector<uint8_t> opcode_buffer;

  // Verify every reg opcode.
  for (uint8_t op = 0x50; op <= 0x6f; op++) {
    opcode_buffer.push_back(op);
  }
  this->op_memory_.SetMemory(0, opcode_buffer);

  for (size_t i = 0; i < opcode_buffer.size(); i++) {
    uint32_t op = opcode_buffer[i];
    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op, this->op_->cur_op());
    ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
  }
}

TYPED_TEST_P(DwarfOpTest, op_regx) {
  std::vector<uint8_t> opcode_buffer = {
      0x90, 0x02, 0x90, 0x80, 0x15,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  ASSERT_TRUE(this->op_->Eval(0, 2));
  ASSERT_EQ(0x90, this->op_->cur_op());
  ASSERT_TRUE(this->op_->is_register());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x02U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Eval(2, 5));
  ASSERT_EQ(0x90, this->op_->cur_op());
  ASSERT_TRUE(this->op_->is_register());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0xa80U, this->op_->StackAt(0));
}

TYPED_TEST_P(DwarfOpTest, op_breg) {
  std::vector<uint8_t> opcode_buffer;

  // Verify every reg opcode.
  for (uint8_t op = 0x70; op <= 0x8f; op++) {
    // Positive value added to register.
    opcode_buffer.push_back(op);
    opcode_buffer.push_back(0x12);
    // Negative value added to register.
    opcode_buffer.push_back(op);
    opcode_buffer.push_back(0x7e);
  }
  this->op_memory_.SetMemory(0, opcode_buffer);

  RegsImplFake<TypeParam> regs(32);
  for (size_t i = 0; i < 32; i++) {
    regs[i] = i + 10;
  }
  RegsInfo<TypeParam> regs_info(&regs);
  this->op_->set_regs_info(&regs_info);

  uint64_t offset = 0;
  for (uint32_t op = 0x70; op <= 0x8f; op++) {
    // Positive value added to register.
    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op, this->op_->cur_op());
    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
    offset += 2;

    // Negative value added to register.
    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op, this->op_->cur_op());
    ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
    ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
    offset += 2;
  }
}

TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
  std::vector<uint8_t> opcode_buffer = {
      0x7f, 0x12, 0x80, 0x12,
  };
  this->op_memory_.SetMemory(0, opcode_buffer);

  RegsImplFake<TypeParam> regs(16);
  for (size_t i = 0; i < 16; i++) {
    regs[i] = i + 10;
  }
  RegsInfo<TypeParam> regs_info(&regs);
  this->op_->set_regs_info(&regs_info);

  // Should pass since this references the last regsister.
  ASSERT_TRUE(this->op_->Eval(0, 2));
  ASSERT_EQ(0x7fU, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x2bU, this->op_->StackAt(0));

  // Should fail since this references a non-existent register.
  ASSERT_FALSE(this->op_->Eval(2, 4));
  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_bregx) {
  std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
                                        0x92, 0x05, 0x20,
                                        // Negative value added to register.
                                        0x92, 0x06, 0x80, 0x7e,
                                        // Illegal register.
                                        0x92, 0x80, 0x15, 0x80, 0x02};
  this->op_memory_.SetMemory(0, opcode_buffer);

  RegsImplFake<TypeParam> regs(10);
  regs[5] = 0x45;
  regs[6] = 0x190;
  RegsInfo<TypeParam> regs_info(&regs);
  this->op_->set_regs_info(&regs_info);

  ASSERT_TRUE(this->op_->Eval(0, 3));
  ASSERT_EQ(0x92, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x65U, this->op_->StackAt(0));

  ASSERT_TRUE(this->op_->Eval(3, 7));
  ASSERT_EQ(0x92, this->op_->cur_op());
  ASSERT_EQ(1U, this->op_->StackSize());
  ASSERT_EQ(0x90U, this->op_->StackAt(0));

  ASSERT_FALSE(this->op_->Eval(7, 12));
  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
}

TYPED_TEST_P(DwarfOpTest, op_nop) {
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});

  ASSERT_TRUE(this->op_->Decode());
  ASSERT_EQ(0x96, this->op_->cur_op());
  ASSERT_EQ(0U, this->op_->StackSize());
}

TYPED_TEST_P(DwarfOpTest, is_dex_pc) {
  // Special sequence that indicates this is a dex pc.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13});

  ASSERT_TRUE(this->op_->Eval(0, 6));
  EXPECT_TRUE(this->op_->dex_pc_set());

  // Try without the last op.
  ASSERT_TRUE(this->op_->Eval(0, 5));
  EXPECT_FALSE(this->op_->dex_pc_set());

  // Change the constant.
  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '2', 0x13});
  ASSERT_TRUE(this->op_->Eval(0, 6));
  EXPECT_FALSE(this->op_->dex_pc_set());
}

REGISTER_TYPED_TEST_SUITE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
                            op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
                            const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
                            op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or,
                            op_plus, op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
                            compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
                            op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop,
                            is_dex_pc);

typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfOpTest, DwarfOpTestTypes);

}  // namespace unwindstack
