text: // Copyright 2016 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef LIB_CONVERTER_TONIC_DART_CONVERTER_H_
  #define LIB_CONVERTER_TONIC_DART_CONVERTER_H_
  
  #include <string>
  #include <vector>
  
  #include "third_party/dart/runtime/include/dart_api.h"
  #include "tonic/common/macros.h"
  
  namespace tonic {
  
  // DartConvert converts types back and forth from Sky to Dart. The template
  // parameter |T| determines what kind of type conversion to perform.
  template <typename T, typename Enable = void>
  struct DartConverter {};
  
  // This is to work around the fact that typedefs do not create new types. If you
  // have a typedef, and want it to use a different converter, specialize this
  // template and override the types here.
  // Ex:
  //   typedef int ColorType;  // Want to use a different converter.
  //   class ColorConverterType {};  // Dummy type.
  //   template<> struct DartConvertType<ColorConverterType> {
  //     using ConverterType = ColorConverterType;
  //     using ValueType = ColorType;
  //   };
  template <typename T>
  struct DartConverterTypes {
    using ConverterType = T;
    using ValueType = T;
  };
  
  ////////////////////////////////////////////////////////////////////////////////
  // Boolean
  
  template <>
  struct DartConverter<bool> {
    static Dart_Handle ToDart(bool val) { return Dart_NewBoolean(val); }
  
    static void SetReturnValue(Dart_NativeArguments args, bool val) {
      Dart_SetBooleanReturnValue(args, val);
    }
  
    static bool FromDart(Dart_Handle handle) {
      bool result = 0;
      Dart_BooleanValue(handle, &result);
      return result;
    }
  
    static bool FromArguments(Dart_NativeArguments args,
                              int index,
                              Dart_Handle& exception) {
      bool result = false;
      Dart_GetNativeBooleanArgument(args, index, &result);
      return result;
    }
  };
  
  ////////////////////////////////////////////////////////////////////////////////
  // Numbers
  
  template <typename T>
  struct DartConverterInteger {
    static Dart_Handle ToDart(T val) { return Dart_NewInteger(val); }
  
    static void SetReturnValue(Dart_NativeArguments args, T val) {
      Dart_SetIntegerReturnValue(args, val);
    }
  
    static T FromDart(Dart_Handle handle) {
      int64_t result = 0;
      Dart_IntegerToInt64(handle, &result);
      return static_cast<T>(result);
    }
  
    static T FromArguments(Dart_NativeArguments args,
                           int index,
                           Dart_Handle& exception) {
      int64_t result = 0;
      Dart_GetNativeIntegerArgument(args, index, &result);
      return static_cast<T>(result);
    }
  };
  
  template <>
  struct DartConverter<int> : public DartConverterInteger<int> {};
  
  template <>
  struct DartConverter<long int> : public DartConverterInteger<long int> {};
  
  template <>
  struct DartConverter<unsigned> : public DartConverterInteger<unsigned> {};
  
  template <>
  struct DartConverter<long long> : public DartConverterInteger<long long> {};
  
  template <>
  struct DartConverter<unsigned long>
      : public DartConverterInteger<unsigned long> {};
  
  template <>
  struct DartConverter<unsigned long long> {
    // TODO(abarth): The Dart VM API doesn't yet have an entry-point for
    // an unsigned 64-bit type. We will need to add a Dart API for
    // constructing an integer from uint64_t.
    //
    // (In the meantime, we have asserts below to check that we're never
    // converting values that have the 64th bit set.)
  
    static Dart_Handle ToDart(unsigned long long val) {
      TONIC_DCHECK(val <= 0x7fffffffffffffffLL);
      return Dart_NewInteger(static_cast<int64_t>(val));
    }
  
    static void SetReturnValue(Dart_NativeArguments args,
                               unsigned long long val) {
      TONIC_DCHECK(val <= 0x7fffffffffffffffLL);
      Dart_SetIntegerReturnValue(args, val);
    }
  
    static unsigned long long FromDart(Dart_Handle handle) {
      int64_t result = 0;
      Dart_IntegerToInt64(handle, &result);
      return result;
    }
  
    static unsigned long long FromArguments(Dart_NativeArguments args,
                                            int index,
                                            Dart_Handle& exception) {
      int64_t result = 0;
      Dart_GetNativeIntegerArgument(args, index, &result);
      return result;
    }
  };
  
  template <typename T>
  struct DartConverterFloatingPoint {
    static Dart_Handle ToDart(T val) { return Dart_NewDouble(val); }
  
    static void SetReturnValue(Dart_NativeArguments args, T val) {
      Dart_SetDoubleReturnValue(args, val);
    }
  
    static T FromDart(Dart_Handle handle) {
      double result = 0;
      Dart_DoubleValue(handle, &result);
      return result;
    }
  
    static T FromArguments(Dart_NativeArguments args,
                           int index,
                           Dart_Handle& exception) {
      double result = 0;
      Dart_GetNativeDoubleArgument(args, index, &result);
      return result;
    }
  };
  
  template <>
  struct DartConverter<float> : public DartConverterFloatingPoint<float> {};
  
  template <>
  struct DartConverter<double> : public DartConverterFloatingPoint<double> {};
  
  ////////////////////////////////////////////////////////////////////////////////
  // Enum Classes
  
  template <typename T>
  struct DartConverter<T, typename std::enable_if<std::is_enum<T>::value>::type> {
    static Dart_Handle ToDart(T val) {
      return Dart_NewInteger(
          static_cast<typename std::underlying_type<T>::type>(val));
    }
  
    static void SetReturnValue(Dart_NativeArguments args, T val) {
      Dart_SetIntegerReturnValue(
          args, static_cast<typename std::underlying_type<T>::type>(val));
    }
  
    static T FromDart(Dart_Handle handle) {
      int64_t result = 0;
      Dart_IntegerToInt64(handle, &result);
      return static_cast<T>(result);
    }
  
    static T FromArguments(Dart_NativeArguments args,
                           int index,
                           Dart_Handle& exception) {
      int64_t result = 0;
      Dart_GetNativeIntegerArgument(args, index, &result);
      return static_cast<T>(result);
    }
  };
  
  ////////////////////////////////////////////////////////////////////////////////
  // Strings
  
  template <>
  struct DartConverter<std::string> {
    static Dart_Handle ToDart(const std::string& val) {
      return Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(val.data()),
                                    val.length());
    }
  
    static void SetReturnValue(Dart_NativeArguments args,
                               const std::string& val) {
      Dart_SetReturnValue(args, ToDart(val));
    }
  
    static std::string FromDart(Dart_Handle handle) {
      uint8_t* data = nullptr;
      intptr_t length = 0;
      if (Dart_IsError(Dart_StringToUTF8(handle, &data, &length)))
        return std::string();
      return std::string(reinterpret_cast<char*>(data), length);
    }
  
    static std::string FromArguments(Dart_NativeArguments args,
                                     int index,
                                     Dart_Handle& exception) {
      return FromDart(Dart_GetNativeArgument(args, index));
    }
  };
  
  template <>
  struct DartConverter<std::u16string> {
    static Dart_Handle ToDart(const std::u16string& val) {
      return Dart_NewStringFromUTF16(
          reinterpret_cast<const uint16_t*>(val.data()), val.length());
    }
  
    static void SetReturnValue(Dart_NativeArguments args,
                               const std::u16string& val) {
      Dart_SetReturnValue(args, ToDart(val));
    }
  
    static std::u16string FromDart(Dart_Handle handle) {
      intptr_t length = 0;
      Dart_StringLength(handle, &length);
      std::vector<uint16_t> data(length);
      Dart_StringToUTF16(handle, data.data(), &length);
      return std::u16string(reinterpret_cast<char16_t*>(data.data()), length);
    }
  
    static std::u16string FromArguments(Dart_NativeArguments args,
                                        int index,
                                        Dart_Handle& exception) {
      return FromDart(Dart_GetNativeArgument(args, index));
    }
  };
  
  template <>
  struct DartConverter<const char*> {
    static Dart_Handle ToDart(const char* val) {
      return Dart_NewStringFromCString(val);
    }
  
    static void SetReturnValue(Dart_NativeArguments args, const char* val) {
      Dart_SetReturnValue(args, ToDart(val));
    }
  
    static const char* FromDart(Dart_Handle handle) {
      const char* result = nullptr;
      Dart_StringToCString(handle, &result);
      return result;
    }
  
    static const char* FromArguments(Dart_NativeArguments args,
                                     int index,
                                     Dart_Handle& exception) {
      return FromDart(Dart_GetNativeArgument(args, index));
    }
  };
  
  ////////////////////////////////////////////////////////////////////////////////
  // Collections
  
  template <typename T, typename Enable = void>
  struct DartListFactory {
    static Dart_Handle NewList(intptr_t length) { return Dart_NewList(length); }
  };
  
  template <typename T>
  struct DartConverter<std::vector<T>> {
    using ValueType = typename DartConverterTypes<T>::ValueType;
    using ConverterType = typename DartConverterTypes<T>::ConverterType;
  
    static Dart_Handle ToDart(const std::vector<ValueType>& val) {
      Dart_Handle list = DartListFactory<ValueType>::NewList(val.size());
      if (Dart_IsError(list))
        return list;
      for (size_t i = 0; i < val.size(); i++) {
        Dart_Handle result =
            Dart_ListSetAt(list, i, DartConverter<ConverterType>::ToDart(val[i]));
        if (Dart_IsError(result))
          return result;
      }
      return list;
    }
  
    static void SetReturnValue(Dart_NativeArguments args,
                               const std::vector<ValueType>& val) {
      Dart_SetReturnValue(args, ToDart(val));
    }
  
    static std::vector<ValueType> FromDart(Dart_Handle handle) {
      std::vector<ValueType> result;
  
      if (!Dart_IsList(handle))
        return result;
  
      intptr_t length = 0;
      Dart_ListLength(handle, &length);
  
      if (length == 0)
        return result;
  
      result.reserve(length);
  
      std::vector<Dart_Handle> items(length);
      Dart_Handle items_result =
          Dart_ListGetRange(handle, 0, length, items.data());
      TONIC_DCHECK(!Dart_IsError(items_result));
  
      for (intptr_t i = 0; i < length; ++i) {
        TONIC_DCHECK(items[i]);
        result.push_back(DartConverter<ConverterType>::FromDart(items[i]));
      }
      return result;
    }
  
    static std::vector<ValueType> FromArguments(Dart_NativeArguments args,
                                                int index,
                                                Dart_Handle& exception) {
      return FromDart(Dart_GetNativeArgument(args, index));
    }
  };
  
  ////////////////////////////////////////////////////////////////////////////////
  // Dart_Handle
  
  template <>
  struct DartConverter<Dart_Handle> {
    static Dart_Handle ToDart(Dart_Handle val) { return val; }
  
    static void SetReturnValue(Dart_NativeArguments args, Dart_Handle val) {
      Dart_SetReturnValue(args, val);
    }
  
    static Dart_Handle FromDart(Dart_Handle handle) { return handle; }
  
    static Dart_Handle FromArguments(Dart_NativeArguments args,
                                     int index,
                                     Dart_Handle& exception) {
      return Dart_GetNativeArgument(args, index);
    }
  };
  
  ////////////////////////////////////////////////////////////////////////////////
  // Convience wrappers using type inference
  
  template <typename T>
  Dart_Handle ToDart(const T& object) {
    return DartConverter<T>::ToDart(object);
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  // std::string support
  
  inline Dart_Handle StdStringToDart(const std::string& val) {
    return DartConverter<std::string>::ToDart(val);
  }
  
  inline std::string StdStringFromDart(Dart_Handle handle) {
    return DartConverter<std::string>::FromDart(handle);
  }
  
  // Alias Dart_NewStringFromCString for less typing.
  inline Dart_Handle ToDart(const char* val) {
    return Dart_NewStringFromCString(val);
  }
  
  }  // namespace tonic
  
  #endif  // LIB_CONVERTER_TONIC_DART_CONVERTER_H_
  