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


/*
 * WARNING: Do not include and use these directly. Use jni_macros.h instead!
 * The "detail" namespace should be a strong hint not to depend on the internals,
 * which could change at any time.
 *
 * This implements the underlying mechanism for compile-time JNI signature/ctype checking
 * and inference.
 *
 * This file provides the constexpr basic blocks such as strings, arrays, vectors
 * as well as the JNI-specific parsing functionality.
 *
 * Everything is implemented via generic-style (templates without metaprogramming)
 * wherever possible. Traditional template metaprogramming is used sparingly.
 *
 * Everything in this file except ostream<< is constexpr.
 */

#pragma once

#include <exception>
#include <iostream>     // std::ostream
#include <jni.h>        // jni typedefs, JniNativeMethod.
#include <type_traits>  // std::common_type, std::remove_cv

namespace nativehelper {
namespace detail {

// If CHECK evaluates to false then X_ASSERT will halt compilation.
//
// Asserts meant to be used only within constexpr context.
#if defined(JNI_SIGNATURE_CHECKER_DISABLE_ASSERTS)
# define X_ASSERT(CHECK) do { if ((false)) { (CHECK) ? void(0) : void(0); } } while (false)
#else
# define X_ASSERT(CHECK) \
    ( (CHECK) ? void(0) : jni_assertion_failure(#CHECK) )
#endif

// The runtime 'jni_assert' will never get called from a constexpr context;
// instead compilation will abort with a stack trace.
//
// Inspect the frame above this one to see the exact nature of the failure.
inline void jni_assertion_failure(const char* /*msg*/) __attribute__((noreturn));
inline void jni_assertion_failure(const char* /*msg*/) {
  std::terminate();
}

// An immutable constexpr string view, similar to std::string_view but for C++14.
// For a mutable string see instead ConstexprVector<char>.
//
// As it is a read-only view into a string, it is not guaranteed to be zero-terminated.
struct ConstexprStringView {
  // Implicit conversion from string literal:
  //     ConstexprStringView str = "hello_world";
  template<size_t N>
  constexpr ConstexprStringView(const char (& lit)[N])  // NOLINT: explicit.
      : _array(lit), _size(N - 1) {
    // Using an array of characters is not allowed because the inferred size would be wrong.
    // Use the other constructor instead for that.
    X_ASSERT(lit[N - 1] == '\0');
  }

  constexpr ConstexprStringView(const char* ptr, size_t size)
      : _array(ptr), _size(size) {
    // See the below constructor instead.
    X_ASSERT(ptr != nullptr);
  }

  // No-arg constructor: Create empty view.
  constexpr ConstexprStringView() : _array(""), _size(0u) {}

  constexpr size_t size() const {
    return _size;
  }

  constexpr bool empty() const {
    return size() == 0u;
  }

  constexpr char operator[](size_t i) const {
    X_ASSERT(i <= size());
    return _array[i];
  }

  // Create substring from this[start..start+len).
  constexpr ConstexprStringView substr(size_t start, size_t len) const {
    X_ASSERT(start <= size());
    X_ASSERT(len <= size() - start);

    return ConstexprStringView(&_array[start], len);
  }

  // Create maximum length substring that begins at 'start'.
  constexpr ConstexprStringView substr(size_t start) const {
    X_ASSERT(start <= size());
    return substr(start, size() - start);
  }

  using const_iterator = const char*;

  constexpr const_iterator begin() const {
    return &_array[0];
  }

  constexpr const_iterator end() const {
    return &_array[size()];
  }

 private:
  const char* _array;  // Never-null for simplicity.
  size_t _size;
};

constexpr bool
operator==(const ConstexprStringView& lhs, const ConstexprStringView& rhs) {
  if (lhs.size() != rhs.size()) {
    return false;
  }
  for (size_t i = 0; i < lhs.size(); ++i) {
    if (lhs[i] != rhs[i]) {
      return false;
    }
  }
  return true;
}

constexpr bool
operator!=(const ConstexprStringView& lhs, const ConstexprStringView& rhs) {
  return !(lhs == rhs);
}

inline std::ostream& operator<<(std::ostream& os, const ConstexprStringView& str) {
  for (char c : str) {
    os << c;
  }
  return os;
}

constexpr bool IsValidJniDescriptorStart(char shorty) {
  constexpr char kValidJniStarts[] =
      {'V', 'Z', 'B', 'C', 'S', 'I', 'J', 'F', 'D', 'L', '[', '(', ')'};

  for (char c : kValidJniStarts) {
    if (c == shorty) {
      return true;
    }
  }

  return false;
}

// A constexpr "vector" that supports storing a variable amount of Ts
// in an array-like interface.
//
// An up-front kMaxSize must be given since constexpr does not support
// dynamic allocations.
template<typename T, size_t kMaxSize>
struct ConstexprVector {
 public:
  constexpr explicit ConstexprVector() : _size(0u), _array{} {
  }

 private:
  // Custom iterator to support ptr-one-past-end into the union array without
  // undefined behavior.
  template<typename Elem>
  struct VectorIterator {
    Elem* ptr;

    constexpr VectorIterator& operator++() {
      ++ptr;
      return *this;
    }

    constexpr VectorIterator operator++(int) const {
      VectorIterator tmp(*this);
      ++tmp;
      return tmp;
    }

    constexpr /*T&*/ auto& operator*() {
      // Use 'auto' here since using 'T' is incorrect with const_iterator.
      return ptr->_value;
    }

    constexpr const /*T&*/ auto& operator*() const {
      // Use 'auto' here for consistency with above.
      return ptr->_value;
    }

    constexpr bool operator==(const VectorIterator& other) const {
      return ptr == other.ptr;
    }

    constexpr bool operator!=(const VectorIterator& other) const {
      return !(*this == other);
    }
  };

  // Do not require that T is default-constructible by using a union.
  struct MaybeElement {
    union {
      T _value;
    };
  };

 public:
  using iterator = VectorIterator<MaybeElement>;
  using const_iterator = VectorIterator<const MaybeElement>;

  constexpr iterator begin() {
    return {&_array[0]};
  }

  constexpr iterator end() {
    return {&_array[size()]};
  }

  constexpr const_iterator begin() const {
    return {&_array[0]};
  }

  constexpr const_iterator end() const {
    return {&_array[size()]};
  }

  constexpr void push_back(const T& value) {
    X_ASSERT(_size + 1 <= kMaxSize);

    _array[_size]._value = value;
    _size++;
  }

  // A pop operation could also be added since constexpr T's
  // have default destructors, it would just be _size--.
  // We do not need a pop() here though.

  constexpr const T& operator[](size_t i) const {
    return _array[i]._value;
  }

  constexpr T& operator[](size_t i) {
    return _array[i]._value;
  }

  constexpr size_t size() const {
    return _size;
  }
 private:

  size_t _size;
  MaybeElement _array[kMaxSize];
};

// Parsed and validated "long" form of a single JNI descriptor.
// e.g. one of "J", "Ljava/lang/Object;" etc.
struct JniDescriptorNode {
  ConstexprStringView longy;

  constexpr JniDescriptorNode(ConstexprStringView longy) : longy(longy) {  // NOLINT(google-explicit-constructor)
    X_ASSERT(!longy.empty());
  }
  constexpr JniDescriptorNode() : longy() {}

  constexpr char shorty() {
    // Must be initialized with the non-default constructor.
    X_ASSERT(!longy.empty());
    return longy[0];
  }
};

inline std::ostream& operator<<(std::ostream& os, const JniDescriptorNode& node) {
  os << node.longy;
  return os;
}

// Equivalent of C++17 std::optional.
//
// An optional is essentially a type safe
//    union {
//      void Nothing,
//      T    Some;
//    };
//
template<typename T>
struct ConstexprOptional {
  // Create a default optional with no value.
  constexpr ConstexprOptional() : _has_value(false), _nothing() {
  }

  // Create an optional with a value.
  constexpr ConstexprOptional(const T& value)  // NOLINT(google-explicit-constructor)
      : _has_value(true), _value(value) {
  }

  constexpr explicit operator bool() const {
    return _has_value;
  }

  constexpr bool has_value() const {
    return _has_value;
  }

  constexpr const T& value() const {
    X_ASSERT(has_value());
    return _value;
  }

  constexpr const T* operator->() const {
    return &(value());
  }

  constexpr const T& operator*() const {
    return value();
  }

 private:
  bool _has_value;
  // The "Nothing" is likely unnecessary but improves readability.
  struct Nothing {};
  union {
    Nothing _nothing;
    T _value;
  };
};

template<typename T>
constexpr bool
operator==(const ConstexprOptional<T>& lhs, const ConstexprOptional<T>& rhs) {
  if (lhs && rhs) {
    return lhs.value() == rhs.value();
  }
  return lhs.has_value() == rhs.has_value();
}

template<typename T>
constexpr bool
operator!=(const ConstexprOptional<T>& lhs, const ConstexprOptional<T>& rhs) {
  return !(lhs == rhs);
}

template<typename T>
inline std::ostream& operator<<(std::ostream& os, const ConstexprOptional<T>& val) {
  if (val) {
    os << val.value();
  }
  return os;
}

// Equivalent of std::nullopt
// Allows implicit conversion to any empty ConstexprOptional<T>.
// Mostly useful for macros that need to return an empty constexpr optional.
struct NullConstexprOptional {
  template<typename T>
  constexpr operator ConstexprOptional<T>() const {  // NOLINT(google-explicit-constructor)
    return ConstexprOptional<T>();
  }
};

inline std::ostream& operator<<(std::ostream& os, NullConstexprOptional) {
  return os;
}

#if !defined(PARSE_FAILURES_NONFATAL)
// Unfortunately we cannot have custom messages here, as it just prints a stack trace with the
// macros expanded. This is at least more flexible than static_assert which requires a string
// literal.
// NOTE: The message string literal must be on same line as the macro to be seen during a
// compilation error.
#define PARSE_FAILURE(msg) X_ASSERT(! #msg)
#define PARSE_ASSERT_MSG(cond, msg) X_ASSERT(#msg && (cond))
#define PARSE_ASSERT(cond) X_ASSERT(cond)
#else
#define PARSE_FAILURE(msg) return NullConstexprOptional{};
#define PARSE_ASSERT_MSG(cond, msg) if (!(cond)) { PARSE_FAILURE(msg); }
#define PARSE_ASSERT(cond) if (!(cond)) { PARSE_FAILURE(""); }
#endif

// This is a placeholder function and should not be called directly.
constexpr void ParseFailure(const char* msg) {
  (void) msg;  // intentionally no-op.
}

// Temporary parse data when parsing a function descriptor.
struct ParseTypeDescriptorResult {
  // A single argument descriptor, e.g. "V" or "Ljava/lang/Object;"
  ConstexprStringView token;
  // The remainder of the function descriptor yet to be parsed.
  ConstexprStringView remainder;

  constexpr bool has_token() const {
    return token.size() > 0u;
  }

  constexpr bool has_remainder() const {
    return remainder.size() > 0u;
  }

  constexpr JniDescriptorNode as_node() const {
    X_ASSERT(has_token());
    return {token};
  }
};

// Parse a single type descriptor out of a function type descriptor substring,
// and return the token and the remainder string.
//
// If parsing fails (i.e. illegal syntax), then:
//    parses are fatal -> assertion is triggered (default behavior),
//    parses are nonfatal -> returns nullopt (test behavior).
constexpr ConstexprOptional<ParseTypeDescriptorResult>
ParseSingleTypeDescriptor(ConstexprStringView single_type,
                          bool allow_void = false) {
  constexpr NullConstexprOptional kUnreachable = {};

  // Nothing else left.
  if (single_type.size() == 0) {
    return ParseTypeDescriptorResult{};
  }

  ConstexprStringView token;
  ConstexprStringView remainder = single_type.substr(/*start*/1u);

  char c = single_type[0];
  PARSE_ASSERT(IsValidJniDescriptorStart(c));

  enum State {
    kSingleCharacter,
    kArray,
    kObject
  };

  State state = kSingleCharacter;

  // Parse the first character to figure out if we should parse the rest.
  switch (c) {
    case '!': {
      constexpr bool fast_jni_is_deprecated = false;
      PARSE_ASSERT(fast_jni_is_deprecated);
      break;
    }
    case 'V':
      if (!allow_void) {
        constexpr bool void_type_descriptor_only_allowed_in_return_type = false;
        PARSE_ASSERT(void_type_descriptor_only_allowed_in_return_type);
      }
      [[clang::fallthrough]];
    case 'Z':
    case 'B':
    case 'C':
    case 'S':
    case 'I':
    case 'J':
    case 'F':
    case 'D':
      token = single_type.substr(/*start*/0u, /*len*/1u);
      break;
    case 'L':
      state = kObject;
      break;
    case '[':
      state = kArray;
      break;
    default: {
      // See JNI Chapter 3: Type Signatures.
      PARSE_FAILURE("Expected a valid type descriptor character.");
      return kUnreachable;
    }
  }

  // Possibly parse an arbitary-long remainder substring.
  switch (state) {
    case kSingleCharacter:
      return {{token, remainder}};
    case kArray: {
      // Recursively parse the array component, as it's just any non-void type descriptor.
      ConstexprOptional<ParseTypeDescriptorResult>
          maybe_res = ParseSingleTypeDescriptor(remainder, /*allow_void*/false);
      PARSE_ASSERT(maybe_res);  // Downstream parsing has asserted, bail out.

      ParseTypeDescriptorResult res = maybe_res.value();

      // Reject illegal array type descriptors such as "]".
      PARSE_ASSERT_MSG(res.has_token(), "All array types must follow by their component type (e.g. ']I', ']]Z', etc. ");

      token = single_type.substr(/*start*/0u, res.token.size() + 1u);

      return {{token, res.remainder}};
    }
    case kObject: {
      // Parse the fully qualified class, e.g. Lfoo/bar/baz;
      // Note checking that each part of the class name is a valid class identifier
      // is too complicated (JLS 3.8).
      // This simple check simply scans until the next ';'.
      bool found_semicolon = false;
      size_t semicolon_len = 0;
      for (size_t i = 0; i < single_type.size(); ++i) {
        switch (single_type[i]) {
          case ')':
          case '(':
          case '[':
            PARSE_FAILURE("Object identifiers cannot have ()[ in them.");
            break;
        }
        if (single_type[i] == ';') {
          semicolon_len = i + 1;
          found_semicolon = true;
          break;
        }
      }

      PARSE_ASSERT(found_semicolon);

      token = single_type.substr(/*start*/0u, semicolon_len);
      remainder = single_type.substr(/*start*/semicolon_len);

      bool class_name_is_empty = token.size() <= 2u;  // e.g. "L;"
      PARSE_ASSERT(!class_name_is_empty);

      return {{token, remainder}};
    }
    default:
      X_ASSERT(false);
  }

  X_ASSERT(false);
  return kUnreachable;
}

// Abstract data type to represent container for Ret(Args,...).
template<typename T, size_t kMaxSize>
struct FunctionSignatureDescriptor {
  ConstexprVector<T, kMaxSize> args;
  T ret;

  static constexpr size_t max_size = kMaxSize;
};


template<typename T, size_t kMaxSize>
inline std::ostream& operator<<(
    std::ostream& os,
    const FunctionSignatureDescriptor<T, kMaxSize>& signature) {
  size_t count = 0;
  os << "args={";
  for (auto& arg : signature.args) {
    os << arg;

    if (count != signature.args.size() - 1) {
      os << ",";
    }

    ++count;
  }
  os << "}, ret=";
  os << signature.ret;
  return os;
}

// Ret(Args...) of JniDescriptorNode.
template<size_t kMaxSize>
using JniSignatureDescriptor = FunctionSignatureDescriptor<JniDescriptorNode,
                                                           kMaxSize>;

// Parse a JNI function signature descriptor into a JniSignatureDescriptor.
//
// If parsing fails (i.e. illegal syntax), then:
//    parses are fatal -> assertion is triggered (default behavior),
//    parses are nonfatal -> returns nullopt (test behavior).
template<size_t kMaxSize>
constexpr ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
ParseSignatureAsList(ConstexprStringView signature) {
  // The list of JNI descriptors cannot possibly exceed the number of characters
  // in the JNI string literal. We leverage this to give an upper bound of the strlen.
  // This is a bit wasteful but in constexpr there *must* be a fixed upper size for data structures.
  ConstexprVector<JniDescriptorNode, kMaxSize> jni_desc_node_list;
  JniDescriptorNode return_jni_desc;

  enum State {
    kInitial = 0,
    kParsingParameters = 1,
    kParsingReturnType = 2,
    kCompleted = 3,
  };

  State state = kInitial;

  while (!signature.empty()) {
    switch (state) {
      case kInitial: {
        char c = signature[0];
        PARSE_ASSERT_MSG(c == '(',
                         "First character of a JNI signature must be a '('");
        state = kParsingParameters;
        signature = signature.substr(/*start*/1u);
        break;
      }
      case kParsingParameters: {
        char c = signature[0];
        if (c == ')') {
          state = kParsingReturnType;
          signature = signature.substr(/*start*/1u);
          break;
        }

        ConstexprOptional<ParseTypeDescriptorResult>
            res = ParseSingleTypeDescriptor(signature, /*allow_void*/false);
        PARSE_ASSERT(res);

        jni_desc_node_list.push_back(res->as_node());

        signature = res->remainder;
        break;
      }
      case kParsingReturnType: {
        ConstexprOptional<ParseTypeDescriptorResult>
            res = ParseSingleTypeDescriptor(signature, /*allow_void*/true);
        PARSE_ASSERT(res);

        return_jni_desc = res->as_node();
        signature = res->remainder;
        state = kCompleted;
        break;
      }
      default: {
        // e.g. "()VI" is illegal because the V terminates the signature.
        PARSE_FAILURE("Signature had left over tokens after parsing return type");
        break;
      }
    }
  }

  switch (state) {
    case kCompleted:
      // Everything is ok.
      break;
    case kParsingParameters:
      PARSE_FAILURE("Signature was missing ')'");
      break;
    case kParsingReturnType:
      PARSE_FAILURE("Missing return type");
    case kInitial:
      PARSE_FAILURE("Cannot have an empty signature");
    default:
      X_ASSERT(false);
  }

  return {{jni_desc_node_list, return_jni_desc}};
}

// What kind of JNI does this type belong to?
enum NativeKind {
  kNotJni,        // Illegal parameter used inside of a function type.
  kNormalJniCallingConventionParameter,
  kNormalNative,
  kFastNative,      // Also valid in normal.
  kCriticalNative,  // Also valid in fast/normal.
};

// Is this type final, i.e. it cannot be subtyped?
enum TypeFinal {
  kNotFinal,
  kFinal         // e.g. any primitive or any "final" class such as String.
};

// What position is the JNI type allowed to be in?
// Ignored when in a CriticalNative context.
enum NativePositionAllowed {
  kNotAnyPosition,
  kReturnPosition,
  kZerothPosition,
  kFirstOrLaterPosition,
  kSecondOrLaterPosition,
};

constexpr NativePositionAllowed ConvertPositionToAllowed(size_t position) {
  switch (position) {
    case 0:
      return kZerothPosition;
    case 1:
      return kFirstOrLaterPosition;
    default:
      return kSecondOrLaterPosition;
  }
}

// Type traits for a JNI parameter type. See below for specializations.
template<typename T>
struct jni_type_trait {
  static constexpr NativeKind native_kind = kNotJni;
  static constexpr const char type_descriptor[] = "(illegal)";
  static constexpr NativePositionAllowed position_allowed = kNotAnyPosition;
  static constexpr TypeFinal type_finality = kNotFinal;
  static constexpr const char type_name[] = "(illegal)";
};

// Access the jni_type_trait<T> from a non-templated constexpr function.
// Identical non-static fields to jni_type_trait, see Reify().
struct ReifiedJniTypeTrait {
  NativeKind native_kind;
  ConstexprStringView type_descriptor;
  NativePositionAllowed position_allowed;
  TypeFinal type_finality;
  ConstexprStringView type_name;

  template<typename T>
  static constexpr ReifiedJniTypeTrait Reify() {
    // This should perhaps be called 'Type Erasure' except we don't use virtuals,
    // so it's not quite the same idiom.
    using TR = jni_type_trait<T>;
    return {TR::native_kind,
            TR::type_descriptor,
            TR::position_allowed,
            TR::type_finality,
            TR::type_name};
  }

  // Find the most similar ReifiedJniTypeTrait corresponding to the type descriptor.
  //
  // Any type can be found by using the exact canonical type descriptor as listed
  // in the jni type traits definitions.
  //
  // Non-final JNI types have limited support for inexact similarity:
  //   [[* | [L* -> jobjectArray
  //   L* -> jobject
  //
  // Otherwise return a nullopt.
  static constexpr ConstexprOptional<ReifiedJniTypeTrait>
  MostSimilarTypeDescriptor(ConstexprStringView type_descriptor);
};

constexpr bool
operator==(const ReifiedJniTypeTrait& lhs, const ReifiedJniTypeTrait& rhs) {
  return lhs.native_kind == rhs.native_kind
      && rhs.type_descriptor == lhs.type_descriptor &&
      lhs.position_allowed == rhs.position_allowed
      && rhs.type_finality == lhs.type_finality &&
      lhs.type_name == rhs.type_name;
}

inline std::ostream& operator<<(std::ostream& os, const ReifiedJniTypeTrait& rjtt) {
  // os << "ReifiedJniTypeTrait<" << rjft.type_name << ">";
  os << rjtt.type_name;
  return os;
}

// Template specialization for any JNI typedefs.
#define JNI_TYPE_TRAIT(jtype, the_type_descriptor, the_native_kind, the_type_finality, the_position) \
template <>                                                                    \
struct jni_type_trait< jtype > {                                               \
  static constexpr NativeKind native_kind = the_native_kind;                   \
  static constexpr const char type_descriptor[] = the_type_descriptor;         \
  static constexpr NativePositionAllowed position_allowed = the_position;      \
  static constexpr TypeFinal type_finality = the_type_finality;                \
  static constexpr const char type_name[] = #jtype;                            \
};

#define DEFINE_JNI_TYPE_TRAIT(TYPE_TRAIT_FN)                                                                  \
TYPE_TRAIT_FN(jboolean,          "Z",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jbyte,             "B",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jchar,             "C",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jshort,            "S",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jint,              "I",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jlong,             "J",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jfloat,            "F",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jdouble,           "D",                      kCriticalNative,   kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jobject,           "Ljava/lang/Object;",     kFastNative,    kNotFinal, kFirstOrLaterPosition)  \
TYPE_TRAIT_FN(jclass,            "Ljava/lang/Class;",      kFastNative,       kFinal, kFirstOrLaterPosition)  \
TYPE_TRAIT_FN(jstring,           "Ljava/lang/String;",     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jarray,            "Ljava/lang/Object;",     kFastNative,    kNotFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jobjectArray,      "[Ljava/lang/Object;",    kFastNative,    kNotFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jbooleanArray,     "[Z",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jbyteArray,        "[B",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jcharArray,        "[C",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jshortArray,       "[S",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jintArray,         "[I",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jlongArray,        "[J",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jfloatArray,       "[F",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jdoubleArray,      "[D",                     kFastNative,       kFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(jthrowable,        "Ljava/lang/Throwable;",  kFastNative,    kNotFinal, kSecondOrLaterPosition) \
TYPE_TRAIT_FN(JNIEnv*,           "",                       kNormalJniCallingConventionParameter, kFinal, kZerothPosition) \
TYPE_TRAIT_FN(void,              "V",                      kCriticalNative,   kFinal, kReturnPosition)        \

DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT)

// See ReifiedJniTypeTrait for documentation.
constexpr ConstexprOptional<ReifiedJniTypeTrait>
ReifiedJniTypeTrait::MostSimilarTypeDescriptor(ConstexprStringView type_descriptor) {
#define MATCH_EXACT_TYPE_DESCRIPTOR_FN(type, type_desc, native_kind, ...)                      \
    if (type_descriptor == type_desc && native_kind >= kNormalNative) {                        \
      return { Reify<type>() };                                                                \
    }

  // Attempt to look up by the precise type match first.
  DEFINE_JNI_TYPE_TRAIT(MATCH_EXACT_TYPE_DESCRIPTOR_FN);

  // Otherwise, we need to do an imprecise match:
  char shorty = type_descriptor.size() >= 1 ? type_descriptor[0] : '\0';
  if (shorty == 'L') {
    // Something more specific like Ljava/lang/Throwable, string, etc
    // is already matched by the macro-expanded conditions above.
    return {Reify<jobject>()};
  } else if (type_descriptor.size() >= 2) {
    auto shorty_shorty = type_descriptor.substr(/*start*/0, /*size*/2u);
    if (shorty_shorty == "[[" || shorty_shorty == "[L") {
      // JNI arrays are covariant, so any type T[] (T!=primitive) is castable to Object[].
      return {Reify<jobjectArray>()};
    }
  }

  // To handle completely invalid values.
  return NullConstexprOptional{};
}

// Is this actual JNI position consistent with the expected position?
constexpr bool IsValidJniParameterPosition(NativeKind native_kind,
                                           NativePositionAllowed position,
                                           NativePositionAllowed expected_position) {
  X_ASSERT(expected_position != kNotAnyPosition);

  if (native_kind == kCriticalNative) {
    // CriticalNatives ignore positions since the first 2 special
    // parameters are stripped.
    return true;
  }

  // Is this a return-only position?
  if (expected_position == kReturnPosition) {
    if (position != kReturnPosition) {
      // void can only be in the return position.
      return false;
    }
    // Don't do the other non-return position checks for a return-only position.
    return true;
  }

  // JNIEnv* can only be in the first spot.
  if (position == kZerothPosition && expected_position != kZerothPosition) {
    return false;
    // jobject, jclass can be 1st or anywhere afterwards.
  } else if (position == kFirstOrLaterPosition && expected_position != kFirstOrLaterPosition) {
    return false;
    // All other parameters must be in 2nd+ spot, or in the return type.
  } else if (position == kSecondOrLaterPosition || position == kReturnPosition) {
    if (expected_position != kFirstOrLaterPosition && expected_position != kSecondOrLaterPosition) {
      return false;
    }
  }

  return true;
}

// Check if a jni parameter type is valid given its position and native_kind.
template <typename T>
constexpr bool IsValidJniParameter(NativeKind native_kind, NativePositionAllowed position) {
  // const,volatile does not affect JNI compatibility since it does not change ABI.
  using expected_trait = jni_type_trait<typename std::remove_cv<T>::type>;
  NativeKind expected_native_kind = expected_trait::native_kind;

  // Most types 'T' are not valid for JNI.
  if (expected_native_kind == NativeKind::kNotJni) {
    return false;
  }

  // The rest of the types might be valid, but it depends on the context (native_kind)
  // and also on their position within the parameters.

  // Position-check first.
  NativePositionAllowed expected_position = expected_trait::position_allowed;
  if (!IsValidJniParameterPosition(native_kind, position, expected_position)) {
    return false;
  }

  // Ensure the type appropriate is for the native kind.
  if (expected_native_kind == kNormalJniCallingConventionParameter) {
    // It's always wrong to use a JNIEnv* anywhere but the 0th spot.
    if (native_kind == kCriticalNative) {
      // CriticalNative does not allow using a JNIEnv*.
      return false;
    }

    return true;  // OK: JniEnv* used in 0th position.
  } else if (expected_native_kind == kCriticalNative) {
    // CriticalNative arguments are always valid JNI types anywhere used.
    return true;
  } else if (native_kind == kCriticalNative) {
    // The expected_native_kind was non-critical but we are in a critical context.
    // Illegal type.
    return false;
  }

  // Everything else is fine, e.g. fast/normal native + fast/normal native parameters.
  return true;
}

// Is there sufficient number of parameters given the kind of JNI that it is?
constexpr bool IsJniParameterCountValid(NativeKind native_kind, size_t count) {
  if (native_kind == kNormalNative || native_kind == kFastNative) {
    return count >= 2u;
  } else if (native_kind == kCriticalNative) {
    return true;
  }

  constexpr bool invalid_parameter = false;
  X_ASSERT(invalid_parameter);
  return false;
}

// Basic template interface. See below for partial specializations.
//
// Each instantiation will have a 'value' field that determines whether or not
// all of the Args are valid JNI arguments given their native_kind.
template<NativeKind native_kind, size_t position, typename ... Args>
struct is_valid_jni_argument_type {
  // static constexpr bool value = ?;
};

template<NativeKind native_kind, size_t position>
struct is_valid_jni_argument_type<native_kind, position> {
  static constexpr bool value = true;
};

template<NativeKind native_kind, size_t position, typename T>
struct is_valid_jni_argument_type<native_kind, position, T> {
  static constexpr bool value =
      IsValidJniParameter<T>(native_kind, ConvertPositionToAllowed(position));
};

template<NativeKind native_kind, size_t position, typename T, typename ... Args>
struct is_valid_jni_argument_type<native_kind, position, T, Args...> {
  static constexpr bool value =
      IsValidJniParameter<T>(native_kind, ConvertPositionToAllowed(position))
          && is_valid_jni_argument_type<native_kind,
                                        position + 1,
                                        Args...>::value;
};

// This helper is required to decompose the function type into a list of arg types.
template<NativeKind native_kind, typename T, T* fn>
struct is_valid_jni_function_type_helper;

template<NativeKind native_kind, typename R, typename ... Args, R (*fn)(Args...)>
struct is_valid_jni_function_type_helper<native_kind, R(Args...), fn> {
  static constexpr bool value =
      IsJniParameterCountValid(native_kind, sizeof...(Args))
          && IsValidJniParameter<R>(native_kind, kReturnPosition)
          && is_valid_jni_argument_type<native_kind, /*position*/
                                        0,
                                        Args...>::value;
};

// Is this function type 'T' a valid C++ function type given the native_kind?
template<NativeKind native_kind, typename T, T* fn>
constexpr bool IsValidJniFunctionType() {
  return is_valid_jni_function_type_helper<native_kind, T, fn>::value;
  // TODO: we could replace template metaprogramming with constexpr by
  // using FunctionTypeMetafunction.
}

// Many parts of std::array is not constexpr until C++17.
template<typename T, size_t N>
struct ConstexprArray {
  // Intentionally public to conform to std::array.
  // This means all constructors are implicit.
  // *NOT* meant to be used directly, use the below functions instead.
  //
  // The reason std::array has it is to support direct-list-initialization,
  // e.g. "ConstexprArray<T, sz>{T{...}, T{...}, T{...}, ...};"
  //
  // Note that otherwise this would need a very complicated variadic
  // argument constructor to only support list of Ts.
  T _array[N];

  constexpr size_t size() const {
    return N;
  }

  using iterator = T*;
  using const_iterator = const T*;

  constexpr iterator begin() {
    return &_array[0];
  }

  constexpr iterator end() {
    return &_array[N];
  }

  constexpr const_iterator begin() const {
    return &_array[0];
  }

  constexpr const_iterator end() const {
    return &_array[N];
  }

  constexpr T& operator[](size_t i) {
    return _array[i];
  }

  constexpr const T& operator[](size_t i) const {
    return _array[i];
  }
};

// Why do we need this?
// auto x = {1,2,3} creates an initializer_list,
//   but they can't be returned because it contains pointers to temporaries.
// auto x[] = {1,2,3} doesn't even work because auto for arrays is not supported.
//
// an alternative would be to pull up std::common_t directly into the call site
//   std::common_type_t<Args...> array[] = {1,2,3}
// but that's even more cludgier.
//
// As the other "stdlib-wannabe" functions, it's weaker than the library
// fundamentals std::make_array but good enough for our use.
template<typename... Args>
constexpr auto MakeArray(Args&& ... args) {
  return ConstexprArray<typename std::common_type<Args...>::type,
                        sizeof...(Args)>{args...};
}

// See below.
template<typename T, T* fn>
struct FunctionTypeMetafunction {
};

// Enables the "map" operation over the function component types.
template<typename R, typename ... Args, R (*fn)(Args...)>
struct FunctionTypeMetafunction<R(Args...), fn> {
  // Count how many arguments there are, and add 1 for the return type.
  static constexpr size_t
      count = sizeof...(Args) + 1u;  // args and return type.

  // Return an array where the metafunction 'Func' has been applied
  // to every argument type. The metafunction must be returning a common type.
  template<template<typename Arg> class Func>
  static constexpr auto map_args() {
    return map_args_impl<Func>(holder < Args > {}...);
  }

  // Apply the metafunction 'Func' over the return type.
  template<template<typename Ret> class Func>
  static constexpr auto map_return() {
    return Func<R>{}();
  }

 private:
  template<typename T>
  struct holder {
  };

  template<template<typename Arg> class Func, typename Arg0, typename... ArgsRest>
  static constexpr auto map_args_impl(holder<Arg0>, holder<ArgsRest>...) {
    // One does not simply call MakeArray with 0 template arguments...
    auto array = MakeArray(
        Func<Args>{}()...
    );

    return array;
  }

  template<template<typename Arg> class Func>
  static constexpr auto map_args_impl() {
    // This overload provides support for MakeArray() with 0 arguments.
    using ComponentType = decltype(Func<void>{}());

    return ConstexprArray<ComponentType, /*size*/0u>{};
  }
};

// Apply ReifiedJniTypeTrait::Reify<T> for every function component type.
template<typename T>
struct ReifyJniTypeMetafunction {
  constexpr ReifiedJniTypeTrait operator()() const {
    auto res = ReifiedJniTypeTrait::Reify<T>();
    X_ASSERT(res.native_kind != kNotJni);
    return res;
  }
};

// Ret(Args...) where every component is a ReifiedJniTypeTrait.
template<size_t kMaxSize>
using ReifiedJniSignature = FunctionSignatureDescriptor<ReifiedJniTypeTrait,
                                                        kMaxSize>;

// Attempts to convert the function type T into a list of ReifiedJniTypeTraits
// that correspond to the function components.
//
// If conversion fails (i.e. non-jni compatible types), then:
//    parses are fatal -> assertion is triggered (default behavior),
//    parses are nonfatal -> returns nullopt (test behavior).
template <NativeKind native_kind,
          typename T,
          T* fn,
          size_t kMaxSize = FunctionTypeMetafunction<T, fn>::count>
constexpr ConstexprOptional<ReifiedJniSignature<kMaxSize>>
MaybeMakeReifiedJniSignature() {
  if (!IsValidJniFunctionType<native_kind, T, fn>()) {
    PARSE_FAILURE("The function signature has one or more types incompatible with JNI.");
  }

  ReifiedJniTypeTrait return_jni_trait =
      FunctionTypeMetafunction<T,
                         fn>::template map_return<ReifyJniTypeMetafunction>();

  constexpr size_t
      kSkipArgumentPrefix = (native_kind != kCriticalNative) ? 2u : 0u;
  ConstexprVector<ReifiedJniTypeTrait, kMaxSize> args;
  auto args_list =
      FunctionTypeMetafunction<T, fn>::template map_args<ReifyJniTypeMetafunction>();
  size_t args_index = 0;
  for (auto& arg : args_list) {
    // Ignore the 'JNIEnv*, jobject' / 'JNIEnv*, jclass' prefix,
    // as its not part of the function descriptor string.
    if (args_index >= kSkipArgumentPrefix) {
      args.push_back(arg);
    }

    ++args_index;
  }

  return {{args, return_jni_trait}};
}

#define COMPARE_DESCRIPTOR_CHECK(expr) if (!(expr)) return false
#define COMPARE_DESCRIPTOR_FAILURE_MSG(msg) if ((true)) return false

// Compares a user-defined JNI descriptor (of a single argument or return value)
// to a reified jni type trait that was derived from the C++ function type.
//
// If comparison fails (i.e. non-jni compatible types), then:
//    parses are fatal -> assertion is triggered (default behavior),
//    parses are nonfatal -> returns false (test behavior).
constexpr bool
CompareJniDescriptorNodeErased(JniDescriptorNode user_defined_descriptor,
                               ReifiedJniTypeTrait derived) {

  ConstexprOptional<ReifiedJniTypeTrait> user_reified_opt =
      ReifiedJniTypeTrait::MostSimilarTypeDescriptor(user_defined_descriptor.longy);

  if (!user_reified_opt.has_value()) {
    COMPARE_DESCRIPTOR_FAILURE_MSG(
        "Could not find any JNI C++ type corresponding to the type descriptor");
  }

  char user_shorty = user_defined_descriptor.longy.size() > 0 ?
                     user_defined_descriptor.longy[0] :
                     '\0';

  ReifiedJniTypeTrait user = user_reified_opt.value();
  if (user == derived) {
    // If we had a similar match, immediately return success.
    return true;
  } else if (derived.type_name == "jthrowable") {
    if (user_shorty == 'L') {
      // Weakly allow any objects to correspond to a jthrowable.
      // We do not know the managed type system so we have to be permissive here.
      return true;
    } else {
      COMPARE_DESCRIPTOR_FAILURE_MSG(
          "jthrowable must correspond to an object type descriptor");
    }
  } else if (derived.type_name == "jarray") {
    if (user_shorty == '[') {
      // a jarray is the base type for all other array types. Allow.
      return true;
    } else {
      // Ljava/lang/Object; is the root for all array types.
      // Already handled above in 'if user == derived'.
      COMPARE_DESCRIPTOR_FAILURE_MSG(
          "jarray must correspond to array type descriptor");
    }
  }
  // Otherwise, the comparison has failed and the rest of this is only to
  // pick the most appropriate error message.
  //
  // Note: A weaker form of comparison would allow matching 'Ljava/lang/String;'
  // against 'jobject', etc. However the policy choice here is to enforce the strictest
  // comparison that we can to utilize the type system to its fullest.

  if (derived.type_finality == kFinal || user.type_finality == kFinal) {
    // Final types, e.g. "I", "Ljava/lang/String;" etc must match exactly
    // the C++ jni descriptor string ('I' -> jint, 'Ljava/lang/String;' -> jstring).
    COMPARE_DESCRIPTOR_FAILURE_MSG(
        "The JNI descriptor string must be the exact type equivalent of the "
            "C++ function signature.");
  } else if (user_shorty == '[') {
    COMPARE_DESCRIPTOR_FAILURE_MSG(
        "The array JNI descriptor must correspond to j${type}Array or jarray");
  } else if (user_shorty == 'L') {
    COMPARE_DESCRIPTOR_FAILURE_MSG(
        "The object JNI descriptor must correspond to jobject.");
  } else {
    X_ASSERT(false);  // We should never get here, but either way this means the types did not match
    COMPARE_DESCRIPTOR_FAILURE_MSG(
        "The JNI type descriptor string does not correspond to the C++ JNI type.");
  }
}

// Matches a user-defined JNI function descriptor against the C++ function type.
//
// If matches fails, then:
//    parses are fatal -> assertion is triggered (default behavior),
//    parses are nonfatal -> returns false (test behavior).
template<NativeKind native_kind, typename T, T* fn, size_t kMaxSize>
constexpr bool
MatchJniDescriptorWithFunctionType(ConstexprStringView user_function_descriptor) {
  constexpr size_t kReifiedMaxSize = FunctionTypeMetafunction<T, fn>::count;

  ConstexprOptional<ReifiedJniSignature<kReifiedMaxSize>>
      reified_signature_opt =
      MaybeMakeReifiedJniSignature<native_kind, T, fn>();
  if (!reified_signature_opt) {
    // Assertion handling done by MaybeMakeReifiedJniSignature.
    return false;
  }

  ConstexprOptional<JniSignatureDescriptor<kMaxSize>> user_jni_sig_desc_opt =
      ParseSignatureAsList<kMaxSize>(user_function_descriptor);

  if (!user_jni_sig_desc_opt) {
    // Assertion handling done by ParseSignatureAsList.
    return false;
  }

  ReifiedJniSignature<kReifiedMaxSize>
      reified_signature = reified_signature_opt.value();
  JniSignatureDescriptor<kMaxSize>
      user_jni_sig_desc = user_jni_sig_desc_opt.value();

  if (reified_signature.args.size() != user_jni_sig_desc.args.size()) {
    COMPARE_DESCRIPTOR_FAILURE_MSG(
        "Number of parameters in JNI descriptor string"
            "did not match number of parameters in C++ function type");
  } else if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.ret,
                                             reified_signature.ret)) {
    // Assertion handling done by CompareJniDescriptorNodeErased.
    return false;
  } else {
    for (size_t i = 0; i < user_jni_sig_desc.args.size(); ++i) {
      if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.args[i],
                                          reified_signature.args[i])) {
        // Assertion handling done by CompareJniDescriptorNodeErased.
        return false;
      }
    }
  }

  return true;
}

// Supports inferring the JNI function descriptor string from the C++
// function type when all type components are final.
template<NativeKind native_kind, typename T, T* fn>
struct InferJniDescriptor {
  static constexpr size_t kMaxSize = FunctionTypeMetafunction<T, fn>::count;

  // Convert the C++ function type into a JniSignatureDescriptor which holds
  // the canonical (according to jni_traits) descriptors for each component.
  // The C++ type -> JNI mapping must be nonambiguous (see jni_macros.h for exact rules).
  //
  // If conversion fails (i.e. C++ signatures is illegal for JNI, or the types are ambiguous):
  //    if parsing is fatal -> assertion failure (default behavior)
  //    if parsing is nonfatal -> returns nullopt (test behavior).
  static constexpr ConstexprOptional<JniSignatureDescriptor<kMaxSize>> FromFunctionType() {
    constexpr size_t kReifiedMaxSize = kMaxSize;
    ConstexprOptional<ReifiedJniSignature<kReifiedMaxSize>>
        reified_signature_opt =
        MaybeMakeReifiedJniSignature<native_kind, T, fn>();
    if (!reified_signature_opt) {
      // Assertion handling done by MaybeMakeReifiedJniSignature.
      return NullConstexprOptional{};
    }

    ReifiedJniSignature<kReifiedMaxSize>
        reified_signature = reified_signature_opt.value();

    JniSignatureDescriptor<kReifiedMaxSize> signature_descriptor;

    if (reified_signature.ret.type_finality != kFinal) {
      // e.g. jint, jfloatArray, jstring, jclass are ok. jobject, jthrowable, jarray are not.
      PARSE_FAILURE("Bad return type. Only unambigous (final) types can be used to infer a signature.");  // NOLINT
    }
    signature_descriptor.ret =
        JniDescriptorNode{reified_signature.ret.type_descriptor};

    for (size_t i = 0; i < reified_signature.args.size(); ++i) {
      const ReifiedJniTypeTrait& arg_trait = reified_signature.args[i];
      if (arg_trait.type_finality != kFinal) {
        PARSE_FAILURE("Bad parameter type. Only unambigous (final) types can be used to infer a signature.");  // NOLINT
      }
      signature_descriptor.args.push_back(JniDescriptorNode{
          arg_trait.type_descriptor});
    }

    return {signature_descriptor};
  }

  // Calculate the exact string size that the JNI descriptor will be
  // at runtime.
  //
  // Without this we cannot allocate enough space within static storage
  // to fit the compile-time evaluated string.
  static constexpr size_t CalculateStringSize() {
    ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
        signature_descriptor_opt =
        FromFunctionType();
    if (!signature_descriptor_opt) {
      // Assertion handling done by FromFunctionType.
      return 0u;
    }

    JniSignatureDescriptor<kMaxSize> signature_descriptor =
        signature_descriptor_opt.value();

    size_t acc_size = 1u;  // All sigs start with '('.

    // Now add every parameter.
    for (size_t j = 0; j < signature_descriptor.args.size(); ++j) {
      const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j];
      // for (const JniDescriptorNode& arg_descriptor : signature_descriptor.args) {
      acc_size += arg_descriptor.longy.size();
    }

    acc_size += 1u;   // Add space for ')'.

    // Add space for the return value.
    acc_size += signature_descriptor.ret.longy.size();

    return acc_size;
  }

  static constexpr size_t kMaxStringSize = CalculateStringSize();
  using ConstexprStringDescriptorType = ConstexprArray<char,
                                                       kMaxStringSize + 1>;

  // Convert the JniSignatureDescriptor we get in FromFunctionType()
  // into a flat constexpr char array.
  //
  // This is done by repeated string concatenation at compile-time.
  static constexpr ConstexprStringDescriptorType GetString() {
    ConstexprStringDescriptorType c_str{};

    ConstexprOptional<JniSignatureDescriptor<kMaxSize>>
        signature_descriptor_opt =
        FromFunctionType();
    if (!signature_descriptor_opt.has_value()) {
      // Assertion handling done by FromFunctionType.
      c_str[0] = '\0';
      return c_str;
    }

    JniSignatureDescriptor<kMaxSize> signature_descriptor =
        signature_descriptor_opt.value();

    size_t pos = 0u;
    c_str[pos++] = '(';

    // Copy all parameter descriptors.
    for (size_t j = 0; j < signature_descriptor.args.size(); ++j) {
      const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j];
      ConstexprStringView longy = arg_descriptor.longy;
      for (size_t i = 0; i < longy.size(); ++i) {
        c_str[pos++] = longy[i];
      }
    }

    c_str[pos++] = ')';

    // Copy return descriptor.
    ConstexprStringView longy = signature_descriptor.ret.longy;
    for (size_t i = 0; i < longy.size(); ++i) {
      c_str[pos++] = longy[i];
    }

    X_ASSERT(pos == kMaxStringSize);

    c_str[pos] = '\0';

    return c_str;
  }

  // Turn a pure constexpr string into one that can be accessed at non-constexpr
  // time. Note that the 'static constexpr' storage must be in the scope of a
  // function (prior to C++17) to avoid linking errors.
  static const char* GetStringAtRuntime() {
    static constexpr ConstexprStringDescriptorType str = GetString();
    return &str[0];
  }
};

// Expression to return JNINativeMethod, performs checking on signature+fn.
#define MAKE_CHECKED_JNI_NATIVE_METHOD(native_kind, name_, signature_, fn) \
  ([]() {                                                                \
    using namespace nativehelper::detail;  /* NOLINT(google-build-using-namespace) */ \
    static_assert(                                                       \
        MatchJniDescriptorWithFunctionType<native_kind,                  \
                                           decltype(fn),                 \
                                           fn,                           \
                                           sizeof(signature_)>(signature_),\
        "JNI signature doesn't match C++ function type.");               \
    /* Suppress implicit cast warnings by explicitly casting. */         \
    return JNINativeMethod {                                             \
        const_cast<decltype(JNINativeMethod::name)>(name_),              \
        const_cast<decltype(JNINativeMethod::signature)>(signature_),    \
        reinterpret_cast<void*>(&(fn))};                                 \
  })()

// Expression to return JNINativeMethod, infers signature from fn.
#define MAKE_INFERRED_JNI_NATIVE_METHOD(native_kind, name_, fn)          \
  ([]() {                                                                \
    using namespace nativehelper::detail;  /* NOLINT(google-build-using-namespace) */ \
    /* Suppress implicit cast warnings by explicitly casting. */         \
    return JNINativeMethod {                                             \
        const_cast<decltype(JNINativeMethod::name)>(name_),              \
        const_cast<decltype(JNINativeMethod::signature)>(                \
            InferJniDescriptor<native_kind,                              \
                               decltype(fn),                             \
                               fn>::GetStringAtRuntime()),               \
        reinterpret_cast<void*>(&(fn))};                                 \
  })()

}  // namespace detail
}  // namespace nativehelper