//===--- MemoryReader.h - Abstract access to remote memory ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file declares an abstract interface for working with the memory
//  of a remote process.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_REMOTE_MEMORYREADER_H
#define SWIFT_REMOTE_MEMORYREADER_H

#include "swift/Remote/RemoteAddress.h"
#include "swift/SwiftRemoteMirror/MemoryReaderInterface.h"

#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <tuple>

namespace swift {
namespace remote {

/// An abstract interface for reading memory.
///
/// This abstraction presents memory as if it were a read-only
/// representation of the address space of a remote process.
class MemoryReader {
public:
  /// A convenient name for the return type from readBytes.
  using ReadBytesResult =
      std::unique_ptr<const void, std::function<void(const void *)>>;

  virtual bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
                               void *outBuffer) = 0;

  /// Look up the given public symbol name in the remote process.
  virtual RemoteAddress getSymbolAddress(const std::string &name) = 0;

  /// Attempts to read a C string from the given address in the remote
  /// process.
  ///
  /// Returns false if the operation failed.
  virtual bool readString(RemoteAddress address, std::string &dest) = 0;
  
  /// Attempts to read an integer from the given address in the remote
  /// process.
  ///
  /// Returns false if the operation failed.
  template <typename IntegerType>
  bool readInteger(RemoteAddress address, IntegerType *dest) {
    return readBytes(address, reinterpret_cast<uint8_t*>(dest),
                     sizeof(IntegerType));
  }

  /// Attempts to read an integer of the specified size from the given
  /// address in the remote process.  Following `storeEnumElement`
  /// in EnumImpl.h, this reads arbitrary-size integers by ignoring
  /// high-order bits that are outside the range of `IntegerType`.
  ///
  /// Returns false if the operation failed.
  template <typename IntegerType>
  bool readInteger(RemoteAddress address, size_t bytes, IntegerType *dest) {
    *dest = 0;
    size_t readSize = std::min(bytes, sizeof(IntegerType));
    // FIXME: Assumes host and target have the same endianness.
    // TODO: Query DLQ for endianness of target, compare to endianness of host.
#if defined(__BIG_ENDIAN__)
    // Read low-order bits of source ...
    if (!readBytes(address + (bytes - readSize), (uint8_t *)dest, readSize)) {
      return false;
    }
    // ... align result to low-order bits of *dest
    *dest >>= 8 * (sizeof(IntegerType) - readSize);
#else
    // Read from low-order bytes of integer
    if (!readBytes(address, (uint8_t *)dest, readSize)) {
      return false;
    }
#endif
    return true;
  }

  /// Attempts to read 'size' bytes from the given address in the remote process.
  ///
  /// Returns a pointer to the requested data and a function that must be called to
  /// free that data when done. The pointer will be NULL if the operation failed.
  ///
  /// NOTE: subclasses MUST override at least one of the readBytes functions. The default
  /// implementation calls through to the other one.
  virtual ReadBytesResult
  readBytes(RemoteAddress address, uint64_t size) {
    auto *Buf = malloc(size);
    if (!Buf)
      return ReadBytesResult{};
    ReadBytesResult Result(Buf, [](const void *ptr) {
      free(const_cast<void *>(ptr));
    });
    bool success = readBytes(address, reinterpret_cast<uint8_t *>(Buf), size);
    if (!success) {
      Result.reset();
    }
    return Result;
  }

  /// Attempts to read 'size' bytes from the given address in the
  /// remote process.
  ///
  /// Returns false if the operation failed.
  ///
  /// NOTE: subclasses MUST override at least one of the readBytes functions. The default
  /// implementation calls through to the other one.
  virtual bool readBytes(RemoteAddress address, uint8_t *dest, uint64_t size) {
    auto Ptr = readBytes(address, size);
    if (!Ptr)
      return false;
    
    memcpy(dest, Ptr.get(), size);
    return true;
  }
  
  /// Attempts to resolve a pointer value read from the given remote address.
  virtual RemoteAbsolutePointer resolvePointer(RemoteAddress address,
                                               uint64_t readValue) {
    // Default implementation returns the read value as is.
    return RemoteAbsolutePointer("", readValue);
  }
  
  /// Attempt to read and resolve a pointer value at the given remote address.
  llvm::Optional<RemoteAbsolutePointer> readPointer(RemoteAddress address,
                                                    unsigned pointerSize) {
    auto result = readBytes(address, pointerSize);
    if (!result)
      return llvm::None;
    
    uint64_t pointerData;
    if (pointerSize == 4) {
      uint32_t theData;
      memcpy(&theData, result.get(), 4);
      pointerData = theData;
    } else if (pointerSize == 8) {
      memcpy(&pointerData, result.get(), 8);
    } else {
      return llvm::None;
    }
    
    return resolvePointer(address, pointerData);
  }

 // Parse extra inhabitants stored in a pointer.
  // Sets *extraInhabitant to -1 if the pointer at this address
  // is actually a valid pointer.
  // Otherwise, it sets *extraInhabitant to the inhabitant
  // index (counting from 0).
  bool readHeapObjectExtraInhabitantIndex(RemoteAddress address,
                                          int *extraInhabitantIndex) {
    uint8_t PointerSize;
    if (!queryDataLayout(DataLayoutQueryType::DLQ_GetPointerSize,
                         nullptr, &PointerSize)) {
      return false;
    }
    uint64_t LeastValidPointerValue;
    if (!queryDataLayout(DataLayoutQueryType::DLQ_GetLeastValidPointerValue,
                         nullptr, &LeastValidPointerValue)) {
      return false;
    }
    uint8_t ObjCReservedLowBits;
    if (!queryDataLayout(DataLayoutQueryType::DLQ_GetObjCReservedLowBits,
                         nullptr, &ObjCReservedLowBits)) {
      return false;
    }
    uint64_t RawPointerValue;
    if (!readInteger(address, PointerSize, &RawPointerValue)) {
      return false;
    }
    if (RawPointerValue >= LeastValidPointerValue) {
      *extraInhabitantIndex = -1; // Valid value, not an XI
    } else {
      *extraInhabitantIndex = (RawPointerValue >> ObjCReservedLowBits);
    }
    return true;
  }

  bool readFunctionPointerExtraInhabitantIndex(RemoteAddress address,
                                               int *extraInhabitantIndex) {
    uint8_t PointerSize;
    if (!queryDataLayout(DataLayoutQueryType::DLQ_GetPointerSize,
                         nullptr, &PointerSize)) {
      return false;
    }
    uint64_t LeastValidPointerValue;
    if (!queryDataLayout(DataLayoutQueryType::DLQ_GetLeastValidPointerValue,
                         nullptr, &LeastValidPointerValue)) {
      return false;
    }
    uint64_t RawPointerValue;
    if (!readInteger(address, PointerSize, &RawPointerValue)) {
      return false;
    }
    if (RawPointerValue >= LeastValidPointerValue) {
      *extraInhabitantIndex = -1; // Valid value, not an XI
    } else {
      *extraInhabitantIndex = RawPointerValue;
    }
    return true;
  }

  virtual ~MemoryReader() = default;
};

} // end namespace reflection
} // end namespace swift

#endif // SWIFT_REFLECTION_READER_H

