//===--- Value.h - Definition of interpreter value --------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Value is a lightweight struct that is used for carrying execution results in
// clang-repl. It's a special runtime that acts like a messager between compiled
// code and interpreted code. This makes it possible to exchange interesting
// information between the compiled & interpreted world.
//
// A typical usage is like the below:
//
// Value V;
// Interp.ParseAndExecute("int x = 42;");
// Interp.ParseAndExecute("x", &V);
// V.getType(); // <-- Yields a clang::QualType.
// V.getInt(); // <-- Yields 42.
//
// The current design is still highly experimental and nobody should rely on the
// API being stable because we're hopefully going to make significant changes to
// it in the relatively near future. For example, Value also intends to be used
// as an exchange token for JIT support enabling remote execution on the embed
// devices where the JIT infrastructure cannot fit. To support that we will need
// to split the memory storage in a different place and perhaps add a resource
// header is similar to intrinsics headers which have stricter performance
// constraints.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_INTERPRETER_VALUE_H
#define LLVM_CLANG_INTERPRETER_VALUE_H

#include "llvm/Config/llvm-config.h" // for LLVM_BUILD_LLVM_DYLIB, LLVM_BUILD_SHARED_LIBS
#include "llvm/Support/Compiler.h"
#include <cstdint>

// NOTE: Since the REPL itself could also include this runtime, extreme caution
// should be taken when MAKING CHANGES to this file, especially when INCLUDE NEW
// HEADERS, like <string>, <memory> and etc. (That pulls a large number of
// tokens and will impact the runtime performance of the REPL)

namespace llvm {
class raw_ostream;

} // namespace llvm

namespace clang {

class ASTContext;
class Interpreter;
class QualType;

#if defined(_WIN32)
// REPL_EXTERNAL_VISIBILITY are symbols that we need to be able to locate
// at runtime. On Windows, this requires them to be exported from any of the
// modules loaded at runtime. Marking them as dllexport achieves this; both
// for DLLs (that normally export symbols as part of their interface) and for
// EXEs (that normally don't export anything).
// For a build with libclang-cpp.dll, this doesn't make any difference - the
// functions would have been exported anyway. But for cases when these are
// statically linked into an EXE, it makes sure that they're exported.
#define REPL_EXTERNAL_VISIBILITY __declspec(dllexport)
#elif __has_attribute(visibility)
#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS)
#define REPL_EXTERNAL_VISIBILITY __attribute__((visibility("default")))
#else
#define REPL_EXTERNAL_VISIBILITY
#endif
#else
#define REPL_EXTERNAL_VISIBILITY
#endif

#define REPL_BUILTIN_TYPES                                                     \
  X(bool, Bool)                                                                \
  X(char, Char_S)                                                              \
  X(signed char, SChar)                                                        \
  X(unsigned char, Char_U)                                                     \
  X(unsigned char, UChar)                                                      \
  X(short, Short)                                                              \
  X(unsigned short, UShort)                                                    \
  X(int, Int)                                                                  \
  X(unsigned int, UInt)                                                        \
  X(long, Long)                                                                \
  X(unsigned long, ULong)                                                      \
  X(long long, LongLong)                                                       \
  X(unsigned long long, ULongLong)                                             \
  X(float, Float)                                                              \
  X(double, Double)                                                            \
  X(long double, LongDouble)

class REPL_EXTERNAL_VISIBILITY Value {
  union Storage {
#define X(type, name) type m_##name;
    REPL_BUILTIN_TYPES
#undef X
    void *m_Ptr;
  };

public:
  enum Kind {
#define X(type, name) K_##name,
    REPL_BUILTIN_TYPES
#undef X

    K_Void,
    K_PtrOrObj,
    K_Unspecified
  };

  Value() = default;
  Value(Interpreter *In, void *Ty);
  Value(const Value &RHS);
  Value(Value &&RHS) noexcept;
  Value &operator=(const Value &RHS);
  Value &operator=(Value &&RHS) noexcept;
  ~Value();

  void printType(llvm::raw_ostream &Out) const;
  void printData(llvm::raw_ostream &Out) const;
  void print(llvm::raw_ostream &Out) const;
  void dump() const;
  void clear();

  ASTContext &getASTContext();
  const ASTContext &getASTContext() const;
  Interpreter &getInterpreter();
  const Interpreter &getInterpreter() const;
  QualType getType() const;

  bool isValid() const { return ValueKind != K_Unspecified; }
  bool isVoid() const { return ValueKind == K_Void; }
  bool hasValue() const { return isValid() && !isVoid(); }
  bool isManuallyAlloc() const { return IsManuallyAlloc; }
  Kind getKind() const { return ValueKind; }
  void setKind(Kind K) { ValueKind = K; }
  void setOpaqueType(void *Ty) { OpaqueType = Ty; }

  void *getPtr() const;
  void setPtr(void *Ptr) { Data.m_Ptr = Ptr; }

#define X(type, name)                                                          \
  void set##name(type Val) { Data.m_##name = Val; }                            \
  type get##name() const { return Data.m_##name; }
  REPL_BUILTIN_TYPES
#undef X

  /// \brief Get the value with cast.
  //
  /// Get the value cast to T. This is similar to reinterpret_cast<T>(value),
  /// casting the value of builtins (except void), enums and pointers.
  /// Values referencing an object are treated as pointers to the object.
  template <typename T> T convertTo() const {
    return convertFwd<T>::cast(*this);
  }

protected:
  bool isPointerOrObjectType() const { return ValueKind == K_PtrOrObj; }

  /// \brief Get to the value with type checking casting the underlying
  /// stored value to T.
  template <typename T> T as() const {
    switch (ValueKind) {
    default:
      return T();
#define X(type, name)                                                          \
  case Value::K_##name:                                                        \
    return (T)Data.m_##name;
      REPL_BUILTIN_TYPES
#undef X
    }
  }

  // Allow convertTo to be partially specialized.
  template <typename T> struct convertFwd {
    static T cast(const Value &V) {
      if (V.isPointerOrObjectType())
        return (T)(uintptr_t)V.as<void *>();
      if (!V.isValid() || V.isVoid()) {
        return T();
      }
      return V.as<T>();
    }
  };

  template <typename T> struct convertFwd<T *> {
    static T *cast(const Value &V) {
      if (V.isPointerOrObjectType())
        return (T *)(uintptr_t)V.as<void *>();
      return nullptr;
    }
  };

  Interpreter *Interp = nullptr;
  void *OpaqueType = nullptr;
  Storage Data;
  Kind ValueKind = K_Unspecified;
  bool IsManuallyAlloc = false;
};

template <> inline void *Value::as() const {
  if (isPointerOrObjectType())
    return Data.m_Ptr;
  return (void *)as<uintptr_t>();
}

} // namespace clang
#endif
