Skip to content

File traits.h

File List > jac > machine > traits.h

Go to the documentation of this file

#pragma once

#include <type_traits>
#include <vector>

#include "context.h"
#include "stringView.h"
#include "values.h"


namespace jac {


template<typename T, typename En = T>
struct ConvTraits {
    static void from(ContextRef, ValueWeak) {
        static_assert(sizeof(T) == 0, "No conversion defined");
    }

    static void to(ContextRef, T) {
        static_assert(sizeof(T) == 0, "No conversion defined");
    }
};

template<>
struct ConvTraits<bool> {
    static bool from(ContextRef ctx, ValueWeak val) {
        return JS_ToBool(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, bool val) {
        return Value(ctx, JS_NewBool(ctx, val));
    }
};

namespace detail {

    template<typename T>
    constexpr bool is_leq_i32 = (std::is_signed_v<T> && sizeof(T) <= sizeof(int32_t))
                             || (std::is_unsigned_v<T> && sizeof(T) < sizeof(int32_t));

} // namespace detail


template<typename T>
struct ConvTraits<T, std::enable_if_t<
       std::is_integral_v<T>
    && detail::is_leq_i32<T>
, T>> {
    static T from(ContextRef ctx, ValueWeak val) {
        int32_t res;
        int ex = JS_ToInt32(ctx, &res, val.getVal());
        if (ex < 0) {
            throw Exception::create(Exception::Type::TypeError, "Failed to convert to int");
        }
        return res;
    }

    static Value to(ContextRef ctx, T val) {
        return Value(ctx, JS_NewInt32(ctx, val));
    }
};

template<typename T>
struct ConvTraits<T, std::enable_if_t<
       std::is_floating_point_v<T>
    && sizeof(T) <= sizeof(double)
, T>> {
    static T from(ContextRef ctx, ValueWeak val) {
        double res;
        int ex = JS_ToFloat64(ctx, &res, val.getVal());
        if (ex < 0) {
            throw Exception::create(Exception::Type::TypeError, "Failed to convert to double");
        }
        return res;
    }

    static Value to(ContextRef ctx, T val) {
        return Value(ctx, JS_NewFloat64(ctx, val));
    }
};

template<typename T>
struct ConvTraits<T, std::enable_if_t<
       std::is_integral_v<T>
    && sizeof(T) <= sizeof(int64_t)
    && !detail::is_leq_i32<T>
, T>> {
    static T from(ContextRef ctx, ValueWeak val) {
        int64_t res = 0;
        int ex = JS_ToInt64(ctx, &res, val.getVal());
        if (ex < 0) {
            throw Exception::create(Exception::Type::TypeError, "Failed to convert to int");
        }
        return res;
    }

    static Value to(ContextRef ctx, T val) {
        return Value(ctx, JS_NewInt64(ctx, val));
    }
};

template<>
struct ConvTraits<const char*> {
    static Value to(ContextRef ctx, const char* val) {
        return Value(ctx, JS_NewString(ctx, val));
    }
};

template<>
struct ConvTraits<char*> : public ConvTraits<const char*> {};

template<>
struct ConvTraits<StringView> {
    static StringView from(ContextRef ctx, ValueWeak val) {
        const char* str = JS_ToCString(ctx, val.getVal());
        if (!str) {
            throw Exception::create(Exception::Type::TypeError, "Failed to convert to string");
        }
        return StringView(ctx, str);
    }

    static Value to(ContextRef ctx, StringView val) {
        return ConvTraits<const char*>::to(ctx, val.c_str());
    }
};

template<>
struct ConvTraits<std::string> {
    static std::string from(ContextRef ctx, ValueWeak val) {
        return std::string(ConvTraits<StringView>::from(ctx, val));
    }

    static Value to(ContextRef ctx, const std::string& val) {
        return ConvTraits<const char*>::to(ctx, val.c_str());
    }
};

template<>
struct ConvTraits<ValueWeak> {
    static ValueWeak from(ContextRef, ValueWeak val) {
        return val;
    }

    static Value to(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<>
struct ConvTraits<Value> {
    static Value from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }

    static Value to(ContextRef, Value val) {
        return val;
    }
};

template<>
struct ConvTraits<ObjectWeak> {
    static ObjectWeak from(ContextRef ctx, ValueWeak val) {
        return ObjectWeak(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, ObjectWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<>
struct ConvTraits<Object> {
    static Object from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Object(ctx, val.getVal());
    }

    static Value to(ContextRef, Object val) {
        return static_cast<Value>(val);
    }
};

template<>
struct ConvTraits<FunctionWeak> {
    static FunctionWeak from(ContextRef ctx, ValueWeak val) {
        return FunctionWeak(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, FunctionWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<>
struct ConvTraits<Function> {
    static Function from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Function(ctx, val.getVal());
    }

    static Value to(ContextRef, Function val) {
        return static_cast<Value>(val);
    }
};

template<>
struct ConvTraits<ArrayWeak> {
    static ArrayWeak from(ContextRef ctx, ValueWeak val) {
        return ArrayWeak(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, ArrayWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<>
struct ConvTraits<Array> {
    static Array from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Array(ctx, val.getVal());
    }

    static Value to(ContextRef, Array val) {
        return static_cast<Value>(val);
    }
};

template<>
struct ConvTraits<PromiseWeak> {
    static PromiseWeak from(ContextRef ctx, ValueWeak val) {
        return PromiseWeak(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, PromiseWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<>
struct ConvTraits<Promise> {
    static Promise from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Promise(ctx, val.getVal());
    }

    static Value to(ContextRef, Promise val) {
        return static_cast<Value>(val);
    }
};

template<>
struct ConvTraits<ExceptionWeak> {
    static ExceptionWeak from(ContextRef ctx, ValueWeak val) {
        return ExceptionWeak(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, ExceptionWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<>
struct ConvTraits<Exception> {
    static Exception from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Exception(ctx, val.getVal());
    }

    static Value to(ContextRef, Exception val) {
        return static_cast<Value>(val);
    }
};

template<>
struct ConvTraits<ArrayBuffer> {
    static ArrayBuffer from(ContextRef ctx, ValueWeak val) {
        JS_DupValue(ctx, val.getVal());
        return ArrayBuffer(ctx, val.getVal());
    }

    static Value to(ContextRef, ArrayBuffer val) {
        return static_cast<Value>(val);
    }
};

template<>
struct ConvTraits<ArrayBufferWeak> {
    static ArrayBufferWeak from(ContextRef ctx, ValueWeak val) {
        return ArrayBufferWeak(ctx, val.getVal());
    }

    static Value to(ContextRef ctx, ArrayBufferWeak val) {
        JS_DupValue(ctx, val.getVal());
        return Value(ctx, val.getVal());
    }
};

template<typename T>
struct ConvTraits<std::vector<T>> {
    static std::vector<T> from(ContextRef, ValueWeak val) {
        auto arr = val.to<Array>();
        std::vector<T> res;
        for (int i = 0; i < arr.length(); i++) {
            try {
                res.push_back(arr.get(i).to<T>());
            }
            catch (Exception& e) {
                throw Exception::create(Exception::Type::TypeError, "Failed to convert array element");
            }
        }
        return res;
    }

    static Value to(ContextRef ctx, const std::vector<T>& val) {
        Array arr = Array::create(ctx);
        for (size_t i = 0; i < val.size(); i++) {
            arr.set(i, val[i]);
        }
        return arr;
    }
};

template<typename... Args>
struct ConvTraits<std::tuple<Args...>> {
    template<std::size_t... Is>
    static std::tuple<Args...> unwrapHelper(ContextRef, ValueWeak val, std::index_sequence<Is...>) {
        auto arr = val.to<Array>();
        if (arr.length() < static_cast<int>(sizeof...(Args))) {
            throw Exception::create(Exception::Type::TypeError, "Tuple size mismatch");
        }
        return std::make_tuple(arr.get<Args>(Is)...);
    }

    static std::tuple<Args...> from(ContextRef ctx, ValueWeak val) {
        return unwrapHelper(ctx, val, std::index_sequence_for<Args...>{});
    }

    template<std::size_t... Is>
    static Value wrapHelper(ContextRef ctx, const std::tuple<Args...>& val, std::index_sequence<Is...>) {
        Array arr = Array::create(ctx);
        (arr.set(Is, std::get<Is>(val)), ...);
        return arr;
    }

    static Value to(ContextRef ctx, std::tuple<Args...> val) {
        return wrapHelper(ctx, val, std::index_sequence_for<Args...>{});
    }
};


} // namespace jac