Revision control
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #ifndef QNAPI_P_H
++ #define QNAPI_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ #include <QtCore/private/qohoslogger_p.h>
++ #include <algorithm>
++ #include <functional>
++ #include <initializer_list>
++ #include <iterator>
++ #include <memory>
++ #include <napi.h>
++ #include <optional>
++ #include <string>
++ #include <tuple>
++ #include <type_traits>
++ #include <utility>
++ #include <vector>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QNapi {
++
++ template<typename... ExtraArgs>
++ class ExtendedCallbackFuncWrapper
++ {
++ public:
++ template<typename Func>
++ ExtendedCallbackFuncWrapper(Func &&callbackFunc, std::enable_if_t<std::is_void<std::invoke_result_t<Func, const Napi::CallbackInfo &, ExtraArgs...>>::value, char *> = nullptr);
++
++ template<typename Func>
++ ExtendedCallbackFuncWrapper(Func &&callbackFunc, std::enable_if_t<std::is_base_of<Napi::Value, std::invoke_result_t<Func, const Napi::CallbackInfo &, ExtraArgs...>>::value, short *> = nullptr);
++
++ template<typename Func>
++ ExtendedCallbackFuncWrapper(Func &&callbackFunc, std::enable_if_t<std::is_void<std::invoke_result_t<Func, ExtraArgs...>>::value, int *> = nullptr);
++
++ template<typename Func>
++ ExtendedCallbackFuncWrapper(Func &&callbackFunc, std::enable_if_t<std::is_base_of<Napi::Value, std::invoke_result_t<Func, ExtraArgs...>>::value, long *> = nullptr);
++
++ std::function<Napi::Value(const Napi::CallbackInfo &, ExtraArgs...)> &callbackFunc();
++
++ private:
++ std::function<Napi::Value(const Napi::CallbackInfo &, ExtraArgs...)> m_callbackFunc;
++ };
++
++ using CallbackFuncWrapper = ExtendedCallbackFuncWrapper<>;
++
++ using Value = Napi::Value;
++
++ using Boolean = Napi::Boolean;
++
++ using Number = Napi::Number;
++
++ using BigInt = Napi::BigInt;
++
++ using Date = Napi::Date;
++
++ using String = Napi::String;
++
++ using Symbol = Napi::Symbol;
++
++ using ArrayBuffer = Napi::ArrayBuffer;
++
++ using TypedArray = Napi::TypedArray;
++
++ using Function = Napi::Function;
++
++ using DataView = Napi::DataView;
++
++ template<typename BufferT>
++ using Buffer = Napi::Buffer<BufferT>;
++
++ template<typename ExternalT>
++ using External = Napi::External<ExternalT>;
++
++ template<typename T>
++ using TypedArrayOf = Napi::TypedArrayOf<T>;
++
++ class Promise;
++
++ class ValueWrapper
++ {
++ public:
++ template<typename T, std::enable_if_t<!std::is_same<std::decay_t<T>, ValueWrapper>::value, int> = 0>
++ ValueWrapper(T &&inputValue);
++
++ Napi::Value mapToValue(napi_env env) const;
++
++ private:
++ std::function<Napi::Value(napi_env)> m_valueFactory;
++ };
++
++ class Array : public Napi::Array
++ {
++ public:
++ using Napi::Array::Array;
++
++ static Array New(napi_env env, std::size_t length = 0);
++
++ Array(const Napi::Array &other);
++ Array &operator=(const Napi::Array &other);
++
++ void fill(const ValueWrapper &value);
++ };
++
++ class Object : public Napi::Object
++ {
++ public:
++ using Napi::Object::Object;
++
++ static Object New(napi_env env);
++
++ Object(const Napi::Object &other);
++ Object &operator=(const Napi::Object &other);
++
++ template<typename T = Value>
++ T get(const std::string &expr) const;
++
++ template<typename T = Value>
++ T get(const Napi::Name &name) const;
++
++ template<typename T = Value>
++ T eval(const std::string &expr, const std::vector<ValueWrapper> &exprArgs = {}) const;
++
++ QNapi::Promise evalToPromiseOrRejectOnThrow(const std::string &expr, const std::vector<ValueWrapper> &exprArgs = {}) const;
++
++ void set(const std::string &name, const ValueWrapper &value);
++
++ void set(const Napi::Name &name, const ValueWrapper &value);
++
++ void set(const std::vector<std::pair<std::string, ValueWrapper>> &namedValues);
++
++ template<typename Result = Value>
++ Q_DECL_DEPRECATED_X("Use eval() with a methodName() or methodName(*) expression instead.")
++ Result call(const std::string &methodName, const std::vector<ValueWrapper> &args = {}) const;
++
++ // Returns the string keys selected by keyMode and keyFilter, numeric keys as strings; symbol
++ // keys are skipped. Use it instead of GetPropertyNames() or iterating over the object, as both
++ // rely on napi_get_property_names(), which has been seen to return non-enumerable and symbol
++ // keys as well on HarmonyOS devices.
++ std::vector<std::string> getFilteredStringKeys(napi_key_collection_mode keyMode, napi_key_filter keyFilter) const;
++ };
++
++ template<typename Context>
++ class PromiseWithContext;
++
++ class Promise : public Napi::Promise
++ {
++ public:
++ using Napi::Promise::Promise;
++
++ Promise();
++ Promise(const Napi::Promise &other);
++ Promise &operator=(const Napi::Promise &other);
++
++ Promise onThen(CallbackFuncWrapper &&onFulfilledFunc);
++ Promise onThen(CallbackFuncWrapper &&onFulfilledFunc, CallbackFuncWrapper &&onRejectedFunc);
++ Promise onCatch(CallbackFuncWrapper &&onRejectedFunc);
++ Promise onFinally(CallbackFuncWrapper &&onFinallyFunc);
++ Promise onThenAndFinally(
++ CallbackFuncWrapper &&onFulfilledFunc, CallbackFuncWrapper &&onRejectedFunc,
++ CallbackFuncWrapper &&onFinallyFunc);
++
++ template<typename Context>
++ PromiseWithContext<Context> withContext(Context &&context);
++ };
++
++ template<typename Context>
++ class PromiseWithContext : public Promise
++ {
++ public:
++ PromiseWithContext(const Promise &promise, std::shared_ptr<Context> context);
++
++ PromiseWithContext onThenWithContext(ExtendedCallbackFuncWrapper<Context &> &&onFulfilledFunc);
++ PromiseWithContext onThenWithContext(
++ ExtendedCallbackFuncWrapper<Context &> &&onFulfilledFunc, CallbackFuncWrapper &&onRejectedFunc);
++ PromiseWithContext onCatchWithContext(ExtendedCallbackFuncWrapper<Context &> &&onRejectedFunc);
++ PromiseWithContext onFinallyWithContext(ExtendedCallbackFuncWrapper<Context &> &&onFinallyFunc);
++
++ private:
++ std::shared_ptr<Context> m_context;
++ };
++
++ template<typename T = void>
++ class Reference : public Napi::Reference<T>
++ {
++ public:
++ using Napi::Reference<T>::Reference;
++
++ Reference(Reference<T> &&other);
++ Reference<T> &operator=(Reference<T> &&other);
++
++ Reference(const Reference<T> &other) = delete;
++ Reference<T> &operator=(const Reference<T> &other) = delete;
++
++ Reference(Napi::Reference<T> &&other);
++ Reference<T> &operator=(Napi::Reference<T> &&other);
++
++ static Reference<T> makePersistentFrom(const T &value);
++
++ static Reference<T> makeEmpty();
++ };
++
++ template<>
++ class Reference<Object> : public Napi::Reference<Object>
++ {
++ public:
++ using Napi::Reference<Object>::Reference;
++
++ Reference(Reference<Object> &&other);
++ Reference<Object> &operator=(Reference<Object> &&other);
++
++ Reference(const Reference<Object> &other) = delete;
++ Reference<Object> &operator=(const Reference<Object> &other) = delete;
++
++ Reference(Napi::Reference<Object> &&other);
++ Reference<Object> &operator=(Napi::Reference<Object> &&other);
++
++ template<typename T = ::QNapi::Value>
++ T eval(const std::string &expr, const std::vector<ValueWrapper> &exprArgs = {}) const;
++
++ QNapi::Promise evalToPromiseOrRejectOnThrow(const std::string &expr, const std::vector<ValueWrapper> &exprArgs = {}) const;
++
++ void set(const std::string &name, const ValueWrapper &value);
++
++ void set(const std::vector<std::pair<std::string, ValueWrapper>> &namedValues);
++
++ template<typename Result = ::QNapi::Value>
++ Q_DECL_DEPRECATED_X("Use eval() with a methodName() or methodName(*) expression instead.")
++ Result call(const std::string &methodName, const std::vector<ValueWrapper> &args = {}) const;
++
++ static Reference<Object> makePersistentFrom(const Object &value);
++
++ static Reference<Object> makeEmpty();
++ };
++
++ template<>
++ class Reference<void>
++ {
++ public:
++ Reference() = delete;
++
++ template<typename T>
++ static std::enable_if_t<std::is_base_of<Napi::Value, T>::value, Reference<T>> makePersistentFrom(const T &value);
++
++ template<typename T = Value>
++ static Reference<T> makeEmpty();
++ };
++
++ class CallbackInfo
++ {
++ public:
++ CallbackInfo(const Napi::CallbackInfo &cbInfo);
++
++ CallbackInfo(const CallbackInfo &other) = delete;
++ CallbackInfo &operator=(const CallbackInfo &other) = delete;
++
++ Napi::Env Env() const;
++ std::size_t Length() const;
++
++ template<typename... Args>
++ void getLeadingArgs(const std::string &funcName, Args &...args) const;
++
++ template<typename Arg>
++ Arg getFirstArg(const std::string &funcName) const;
++
++ operator const Napi::CallbackInfo &() const;
++
++ private:
++ const Napi::CallbackInfo &m_cbInfo;
++ };
++
++ std::vector<napi_value> unwrapValues(napi_env env, const std::vector<ValueWrapper> &wrappedValues);
++
++ Napi::Error makeLoggedException(napi_env env, const std::string &msg);
++
++ template<typename T>
++ bool valueTypeMatches(const Napi::Value &value);
++
++ template<typename Element>
++ bool arrayElementTypesMatch(const Napi::Value &value);
++
++ std::string getValueTypeString(const Napi::Value &value);
++
++ template<typename T, typename ValueDescriptionSupplier>
++ T checkedCast(const Napi::Value &value, ValueDescriptionSupplier &&valueDescSupplier);
++
++ template<typename T>
++ T checkedCast(const Napi::Value &value);
++
++ template<typename OutputContainer, typename Element, typename TransFunc>
++ OutputContainer getArrayElements(const Napi::Array &inputArray, TransFunc &&transFunc);
++
++ template<typename OutputContainer, typename Element>
++ OutputContainer getArrayElements(const Napi::Array &inputArray);
++
++ template<typename OutputContainer, typename Element, typename TransFilterFunc>
++ OutputContainer getFilteredArrayElements(const Napi::Array &inputArray, TransFilterFunc &&transFilterFunc);
++
++ Object makeNewInstance(const Napi::Function &type, const std::vector<ValueWrapper> &args = {});
++
++ Object makeNewInstance(const Napi::Object &baseObj, const std::string &typePath, const std::vector<ValueWrapper> &args = {});
++
++ Napi::Value getPropOrUndefined(const Napi::Value &obj, const std::string &propName);
++
++ template<typename T>
++ Napi::Value getPropOrUndefined(const Napi::Reference<T> &objRef, const std::string &propName);
++
++ template<typename T>
++ std::enable_if_t<std::is_base_of<Napi::Value, T>::value, T>
++ getOptionalPropOrEmpty(const Napi::Object &optObj, const std::string &propName, const std::string &objDesc = {});
++
++ template<typename T>
++ std::enable_if_t<std::is_base_of<Napi::Value, T>::value, T>
++ getOptionalPropOrEmpty(const Napi::Object &optObj, const Napi::Name &propName, const std::string &objDesc = {});
++
++ Object makeObject(napi_env env, const std::vector<std::pair<std::string, ValueWrapper>> &namedValues = {});
++
++ Array makeArray(napi_env env, std::initializer_list<ValueWrapper> values = {});
++
++ template<typename InputContainer, typename TransFunc>
++ Array makeArray(napi_env env, InputContainer &&inputContainer, TransFunc &&transFunc);
++
++ template<typename InputContainer>
++ Array makeArray(napi_env env, InputContainer &&inputContainer);
++
++ template<typename Result = Napi::Value, typename F>
++ Result runEscapingHandleScope(napi_env env, F &&func);
++
++ std::string toJsonString(const Napi::Value &value);
++
++ namespace details_qnapi_p_h {
++
++ template<typename T>
++ struct ValueTypeTraits
++ {
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Boolean>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsBoolean;
++ static constexpr const char *typeName = "Boolean";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Number>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsNumber;
++ static constexpr const char *typeName = "Number";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::BigInt>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsBigInt;
++ static constexpr const char *typeName = "BigInt";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Date>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsDate;
++ static constexpr const char *typeName = "Date";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::String>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsString;
++ static constexpr const char *typeName = "String";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Symbol>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsSymbol;
++ static constexpr const char *typeName = "Symbol";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Array>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsArray;
++ static constexpr const char *typeName = "Array";
++ };
++
++ template<>
++ struct ValueTypeTraits<Array> : public ValueTypeTraits<Napi::Array>
++ {
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::ArrayBuffer>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsArrayBuffer;
++ static constexpr const char *typeName = "ArrayBuffer";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::TypedArray>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsTypedArray;
++ static constexpr const char *typeName = "TypedArray";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Object>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsObject;
++ static constexpr const char *typeName = "Object";
++ };
++
++ template<>
++ struct ValueTypeTraits<Object> : public ValueTypeTraits<Napi::Object>
++ {
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Function>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsFunction;
++ static constexpr const char *typeName = "Function";
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::Promise>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsPromise;
++ static constexpr const char *typeName = "Promise";
++ };
++
++ template<>
++ struct ValueTypeTraits<Promise> : public ValueTypeTraits<Napi::Promise>
++ {
++ };
++
++ template<>
++ struct ValueTypeTraits<Napi::DataView>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsDataView;
++ static constexpr const char *typeName = "DataView";
++ };
++
++ template<typename BufferT>
++ struct ValueTypeTraits<Napi::Buffer<BufferT>>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsBuffer;
++ static constexpr const char *typeName = "Buffer";
++ };
++
++ template<typename ExternalT>
++ struct ValueTypeTraits<Napi::External<ExternalT>>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsExternal;
++ static constexpr const char *typeName = "External";
++ };
++
++ template<typename T>
++ struct ValueTypeTraits<Napi::TypedArrayOf<T>>
++ {
++ static constexpr auto typeCheckMemFun = &Napi::Value::IsTypedArray;
++ static constexpr const char *typeName = "TypedArray";
++ };
++
++ template<typename T>
++ constexpr bool isCallbackFuncType()
++ {
++ return std::is_constructible<CallbackFuncWrapper, T>::value;
++ }
++
++ template<typename T>
++ inline std::enable_if_t<isCallbackFuncType<T>(), std::function<Napi::Value(napi_env)>> makeValueFactory(T &&inputValue)
++ {
++ return [callbackFunc = std::move(CallbackFuncWrapper(std::forward<T>(inputValue)).callbackFunc())](napi_env env) {
++ return Napi::Function::New(env, callbackFunc);
++ };
++ }
++
++ template<typename T>
++ inline std::enable_if_t<!isCallbackFuncType<T>(), std::function<Napi::Value(napi_env)>> makeValueFactory(T &&inputValue)
++ {
++ return [inputValue = std::forward<T>(inputValue)](napi_env env) {
++ return Napi::Value::From(env, inputValue);
++ };
++ }
++
++ inline Napi::Error makeLoggedExceptionImpl(napi_env env, const std::string &msg)
++ {
++ qOhosPrintfError("QNapi: exception: %s", msg.c_str());
++ return Napi::Error::New(env, msg);
++ }
++
++ template<typename T>
++ std::enable_if_t<std::is_same<T, Napi::Value>::value, bool> valueTypeMatchesImpl(const Napi::Value &)
++ {
++ return true;
++ }
++
++ template<typename T>
++ std::enable_if_t<!std::is_same<T, Napi::Value>::value, bool> valueTypeMatchesImpl(const Napi::Value &value)
++ {
++ return (value.*ValueTypeTraits<T>::typeCheckMemFun)();
++ }
++
++ std::string getArrayElementValueTypeString(const Napi::Array &arrayValue);
++
++ inline std::string getValueTypeStringImpl(const Napi::Value &value)
++ {
++ using namespace std::string_literals;
++
++ if (value.IsArray()) {
++ auto arrayElemTypeString = getArrayElementValueTypeString(value.As<Napi::Array>());
++ return "Array<"s + arrayElemTypeString + ">"s;
++ }
++
++ // put subtypes of the Napi::Object at the beginning, so that we find most specific type name
++ const std::pair<bool (Napi::Value::*)() const, const char *> typesChecksAndNames[] = {
++ {&Napi::Value::IsEmpty, "<empty>"},
++ {&Napi::Value::IsNull, "Null"},
++ {&Napi::Value::IsUndefined, "Undefined"},
++ {&Napi::Value::IsArray, "Array"},
++ {&Napi::Value::IsArrayBuffer, "ArrayBuffer"},
++ {&Napi::Value::IsBuffer, "Buffer"},
++ {&Napi::Value::IsTypedArray, "TypedArray"},
++ {&Napi::Value::IsDataView, "DataView"},
++ {&Napi::Value::IsFunction, "Function"},
++ {&Napi::Value::IsPromise, "Promise"},
++ {&Napi::Value::IsBigInt, "BigInt"},
++ {&Napi::Value::IsBoolean, "Boolean"},
++ {&Napi::Value::IsDate, "Date"},
++ {&Napi::Value::IsExternal, "External"},
++ {&Napi::Value::IsNumber, "Number"},
++ {&Napi::Value::IsObject, "Object"},
++ {&Napi::Value::IsString, "String"},
++ {&Napi::Value::IsSymbol, "Symbol"},
++ };
++
++ const char *foundTypeName = nullptr;
++ for (const auto &typeCheckEntry : typesChecksAndNames) {
++ if ((value.*(typeCheckEntry.first))()) {
++ foundTypeName = typeCheckEntry.second;
++ break;
++ }
++ }
++
++ return foundTypeName != nullptr ? foundTypeName : "?";
++ }
++
++ inline std::string getArrayElementValueTypeString(const Napi::Array &arrayValue)
++ {
++ constexpr std::size_t maxArrayElementsForTypeCheck = 10;
++
++ std::size_t arrayLength = arrayValue.Length();
++ auto checkRangeSize = std::min(arrayLength, maxArrayElementsForTypeCheck);
++
++ std::string commonElemTypeString;
++ for (std::size_t i = 0; i < checkRangeSize; ++i) {
++ auto elemTypeString = getValueTypeStringImpl(arrayValue.Get(i));
++ if (commonElemTypeString.empty()) {
++ commonElemTypeString = elemTypeString;
++ } else if (commonElemTypeString != elemTypeString) {
++ commonElemTypeString = "";
++ break;
++ }
++ }
++
++ return !commonElemTypeString.empty()
++ ? checkRangeSize == arrayLength
++ ? commonElemTypeString
++ : commonElemTypeString + "?"
++ : "?";
++ }
++
++ template<typename T, typename ValueDescriptionSupplier>
++ std::enable_if_t<std::is_same<T, Napi::Value>::value, T> checkedCastImpl(
++ const Napi::Value &value, ValueDescriptionSupplier &&)
++ {
++ return value;
++ }
++
++ template<typename T, typename ValueDescriptionSupplier>
++ std::enable_if_t<!std::is_same<T, Napi::Value>::value, T> checkedCastImpl(
++ const Napi::Value &value, ValueDescriptionSupplier &&valueDescSupplier)
++ {
++ using namespace std::string_literals;
++
++ if (!valueTypeMatchesImpl<T>(value)) {
++ constexpr const char *expectedTypeName = ValueTypeTraits<T>::typeName;
++ auto valueTypeStr = getValueTypeStringImpl(value);
++ std::string valueDesc = valueDescSupplier();
++ auto baseErrorMsg =
++ "wrong type (expected '"s + expectedTypeName + "', got '"s + valueTypeStr + "') of Napi value"s;
++ throw makeLoggedExceptionImpl(
++ value.Env(), valueDesc.empty() ? baseErrorMsg : baseErrorMsg + ": "s + valueDesc);
++ }
++
++ return T(value.Env(), value);
++ }
++
++ template<typename Result, typename F>
++ Result runEscapingHandleScopeImpl(napi_env env, F &&func)
++ {
++ Napi::EscapableHandleScope scope(env);
++ Result result = std::forward<F>(func)();
++ return checkedCastImpl<Result>(
++ scope.Escape(result),
++ [&]() {
++ return "value escaping from HandleScope";
++ });
++ }
++
++ inline std::vector<napi_value> expandEvalCallArgs(
++ napi_env env, const std::string &callArgsSubExpr, const std::vector<napi_value> &exprArgs)
++ {
++ using namespace std::string_literals;
++
++ if (callArgsSubExpr.empty()) {
++ return {};
++ } else if (callArgsSubExpr == "*") {
++ return exprArgs;
++ } else {
++ throw makeLoggedException(
++ env, "illegal call arguments in expression: '"s + callArgsSubExpr + "'"s);
++ }
++ }
++
++ template<typename T>
++ std::pair<Napi::Object, T> evalWithContextImpl(
++ Napi::Object obj, const std::string &expr, const std::vector<napi_value> &exprArgs = {})
++ {
++ using namespace details_qnapi_p_h;
++ using namespace std::string_literals;
++
++ static const std::string newCallTag = "<new>";
++
++ std::pair<Napi::Object, Napi::Value> result;
++
++ auto lastDotPos = expr.rfind('.');
++
++ if (!expr.empty() && expr.back() == ')') {
++ auto openingBracketPos = expr.rfind('(');
++ if (openingBracketPos == std::string::npos)
++ throw makeLoggedExceptionImpl(obj.Env(), "missing opening bracket in '"s + expr + "'"s);
++ bool newInstanceCall =
++ openingBracketPos > newCallTag.size()
++ && expr.compare(openingBracketPos - newCallTag.size(), newCallTag.size(), newCallTag) == 0;
++ auto subFuncArgsExpr = expr.substr(
++ openingBracketPos + 1, expr.size() - 1 - (openingBracketPos + 1));
++ auto subFuncArgs = expandEvalCallArgs(obj.Env(), subFuncArgsExpr, exprArgs);
++ auto subFuncExpr = expr.substr(
++ 0, newInstanceCall ? openingBracketPos - newCallTag.size() : openingBracketPos);
++ auto subFuncWithCtx = evalWithContextImpl<Napi::Function>(obj, subFuncExpr, exprArgs);
++ Napi::Value funcCallResult;
++ try {
++ funcCallResult =
++ newInstanceCall
++ ? subFuncWithCtx.second.New(subFuncArgs)
++ : !subFuncWithCtx.first.IsEmpty()
++ ? subFuncWithCtx.second.Call(subFuncWithCtx.first, subFuncArgs)
++ : subFuncWithCtx.second.Call(subFuncArgs);
++ } catch (const Napi::Error &error) {
++ auto message = "QNapi: got exception from call '"s + expr + "': "s + error.what();
++ qOhosPrintfError("%s", message.c_str());
++ throw;
++ }
++
++ result = std::make_pair(Napi::Object(), funcCallResult);
++ } else if (lastDotPos != std::string::npos) {
++ auto subObjExpr = expr.substr(0, lastDotPos);
++ auto propName = expr.substr(lastDotPos + 1);
++
++ auto subObj = evalWithContextImpl<Napi::Object>(obj, subObjExpr, exprArgs).second;
++ if (!subObj.Has(propName)) {
++ throw makeLoggedExceptionImpl(
++ obj.Env(), "object '"s + subObjExpr + "' has no property named '"s + propName + "'"s);
++ }
++
++ result = std::make_pair(subObj, subObj.Get(propName));
++ } else {
++ if (!obj.Has(expr)) {
++ throw makeLoggedExceptionImpl(
++ obj.Env(), "object has no property named '"s + expr + "'"s);
++ }
++
++ result = std::make_pair(obj, obj.Get(expr));
++ }
++
++ return std::make_pair(result.first, checkedCastImpl<T>(result.second, [&]() { return expr; }));
++ }
++
++ inline Napi::Value callMethodWithValueResultImpl(
++ const Napi::Object &obj, const std::string &methodName, const std::vector<napi_value> &args)
++ {
++ using namespace std::string_literals;
++ return runEscapingHandleScopeImpl<Napi::Value>(
++ obj.Env(),
++ [&]() {
++ auto funcWithCtx = evalWithContextImpl<Napi::Function>(obj, methodName);
++ Napi::Value funcResult;
++ try {
++ funcResult = funcWithCtx.second.Call(funcWithCtx.first, args);
++ } catch (const Napi::Error &error) {
++ auto message = "QNapi: got exception from method call '"s + methodName + "': "s + error.what();
++ qOhosPrintfError("%s", message.c_str());
++ throw;
++ }
++
++ return funcResult;
++ });
++ }
++
++ template<typename Result>
++ Result callMethodImpl(const Napi::Object &obj, const std::string &methodName, const std::vector<napi_value> &args)
++ {
++ using namespace std::string_literals;
++
++ return checkedCastImpl<Result>(
++ callMethodWithValueResultImpl(obj, methodName, args),
++ [&]() {
++ return "result of '"s + methodName + "' method call"s;
++ });
++ }
++
++ template<typename Result = Value>
++ Result callMethod(const Napi::Object &obj, const std::string &methodName, const std::vector<ValueWrapper> &args)
++ {
++ return runEscapingHandleScopeImpl<Result>(
++ obj.Env(),
++ [&]() {
++ return callMethodImpl<Result>(obj, methodName, unwrapValues(obj.Env(), args));
++ });
++ }
++
++ template<typename Arg>
++ void getArgImpl(const std::string &funcName, const Napi::CallbackInfo &cbInfo, Arg &arg, std::size_t argIndex)
++ {
++ using namespace std::string_literals;
++
++ arg = checkedCastImpl<Arg>(
++ cbInfo[argIndex],
++ [&]() {
++ return "arg #"s + std::to_string(argIndex) + " of '"s + funcName + "' func call"s;
++ });
++ }
++
++ template<typename... Args, std::size_t... Is>
++ void getLeadingArgsImpl(
++ const std::string &funcName, const Napi::CallbackInfo &cbInfo, std::tuple<Args...> args, std::index_sequence<Is...>)
++ {
++ using namespace std::string_literals;
++
++ if (cbInfo.Length() < sizeof...(Args)) {
++ throw makeLoggedExceptionImpl(
++ cbInfo.Env(),
++ "getArgs: func '"s + funcName + "' received less args than expected minimum: "s
++ + std::to_string(cbInfo.Length()) + " vs "s + std::to_string(sizeof...(Args)));
++ }
++
++ auto unused = {(getArgImpl(funcName, cbInfo, std::get<Is>(args), Is), 0)..., 0};
++ (void) unused;
++ }
++
++ template<typename T>
++ std::enable_if_t<std::is_base_of<Napi::Value, T>::value, T>
++ getOptionalPropOrEmptyImpl(const Napi::Object &optObj, const Napi::Name &propName, const std::string &objDesc)
++ {
++ using namespace std::string_literals;
++
++ if (optObj.IsEmpty() || !optObj.Has(propName))
++ return T();
++
++ auto propValue = optObj.Get(propName);
++
++ return !propValue.IsUndefined()
++ ? QNapi::checkedCast<T>(
++ propValue,
++ [&]() {
++ auto baseDesc = "property '"s + propName.ToString().Utf8Value() + "' of object"s;
++ return !objDesc.empty()
++ ? baseDesc + ": "s + objDesc
++ : baseDesc;
++ })
++ : T();
++ }
++
++ template<typename Container, typename Element>
++ constexpr decltype(auto) forwardContainerElement(Element &element) noexcept
++ {
++ using Result = std::conditional_t<std::is_lvalue_reference<Container>::value, Element &, Element &&>;
++ return static_cast<Result>(element);
++ }
++
++ }
++
++ template<typename T, std::enable_if_t<!std::is_same<std::decay_t<T>, ValueWrapper>::value, int>>
++ ValueWrapper::ValueWrapper(T &&inputValue)
++ : m_valueFactory(details_qnapi_p_h::makeValueFactory(std::forward<T>(inputValue)))
++ {
++ }
++
++ inline Napi::Value ValueWrapper::mapToValue(napi_env env) const
++ {
++ return m_valueFactory(env);
++ }
++
++ template<typename... ExtraArgs>
++ template<typename Func>
++ ExtendedCallbackFuncWrapper<ExtraArgs...>::ExtendedCallbackFuncWrapper(
++ Func &&callbackFunc, std::enable_if_t<std::is_void<std::invoke_result_t<Func, const Napi::CallbackInfo &, ExtraArgs...>>::value, char *>)
++ : m_callbackFunc(
++ [callbackFunc = std::make_shared<std::decay_t<Func>>(std::move(callbackFunc))](const Napi::CallbackInfo &cbInfo, ExtraArgs... extraArgs) {
++ (*callbackFunc)(cbInfo, extraArgs...);
++ return Napi::Value();
++ })
++ {
++ static_assert(!std::is_lvalue_reference<Func>::value, "callback functor must be passed as an rvalue");
++ }
++
++ template<typename... ExtraArgs>
++ template<typename Func>
++ ExtendedCallbackFuncWrapper<ExtraArgs...>::ExtendedCallbackFuncWrapper(
++ Func &&callbackFunc, std::enable_if_t<std::is_base_of<Napi::Value, std::invoke_result_t<Func, const Napi::CallbackInfo &, ExtraArgs...>>::value, short *>)
++ : m_callbackFunc(
++ [callbackFunc = std::make_shared<std::decay_t<Func>>(std::move(callbackFunc))](const Napi::CallbackInfo &cbInfo, ExtraArgs... extraArgs) {
++ return (*callbackFunc)(cbInfo, extraArgs...);
++ })
++ {
++ static_assert(!std::is_lvalue_reference<Func>::value, "callback functor must be passed as an rvalue");
++ }
++
++ template<typename... ExtraArgs>
++ template<typename Func>
++ ExtendedCallbackFuncWrapper<ExtraArgs...>::ExtendedCallbackFuncWrapper(Func &&callbackFunc, std::enable_if_t<std::is_void<std::invoke_result_t<Func, ExtraArgs...>>::value, int *>)
++ : m_callbackFunc(
++ [callbackFunc = std::make_shared<std::decay_t<Func>>(std::move(callbackFunc))](const Napi::CallbackInfo &, ExtraArgs... extraArgs) {
++ (*callbackFunc)(extraArgs...);
++ return Napi::Value();
++ })
++ {
++ static_assert(!std::is_lvalue_reference<Func>::value, "callback functor must be passed as an rvalue");
++ }
++
++ template<typename... ExtraArgs>
++ template<typename Func>
++ ExtendedCallbackFuncWrapper<ExtraArgs...>::ExtendedCallbackFuncWrapper(
++ Func &&callbackFunc, std::enable_if_t<std::is_base_of<Napi::Value, std::invoke_result_t<Func, ExtraArgs...>>::value, long *>)
++ : m_callbackFunc(
++ [callbackFunc = std::make_shared<std::decay_t<Func>>(std::move(callbackFunc))](const Napi::CallbackInfo &, ExtraArgs... extraArgs) {
++ return (*callbackFunc)(extraArgs...);
++ })
++ {
++ static_assert(!std::is_lvalue_reference<Func>::value, "callback functor must be passed as an rvalue");
++ }
++
++ template<typename... ExtraArgs>
++ std::function<Napi::Value(const Napi::CallbackInfo &, ExtraArgs... extraArgs)> &ExtendedCallbackFuncWrapper<ExtraArgs...>::callbackFunc()
++ {
++ return m_callbackFunc;
++ }
++
++ inline Object Object::New(napi_env env)
++ {
++ return Napi::Object::New(env);
++ }
++
++ inline Array Array::New(napi_env env, std::size_t length)
++ {
++ return length != 0
++ ? Napi::Array::New(env, length)
++ : Napi::Array::New(env);
++ }
++
++ inline Array::Array(const Napi::Array &other)
++ : Napi::Array(other)
++ {
++ }
++
++ inline Array &Array::operator=(const Napi::Array &other)
++ {
++ Napi::Array::operator=(other);
++ return *this;
++ }
++
++ inline void Array::fill(const ValueWrapper &value)
++ {
++ details_qnapi_p_h::callMethod(*this, "fill", {value});
++ }
++
++ inline Object::Object(const Napi::Object &other)
++ : Napi::Object(other)
++ {
++ }
++
++ inline Object &Object::operator=(const Napi::Object &other)
++ {
++ Napi::Object::operator=(other);
++ return *this;
++ }
++
++ template<typename T>
++ T Object::get(const std::string &expr) const
++ {
++ return details_qnapi_p_h::runEscapingHandleScopeImpl<T>(
++ Env(),
++ [&]() {
++ return details_qnapi_p_h::evalWithContextImpl<T>(*this, expr).second;
++ });
++ }
++
++ template<typename T>
++ T Object::get(const Napi::Name &name) const
++ {
++ using namespace std::string_literals;
++
++ if (!Has(name)) {
++ throw makeLoggedException(
++ Env(), "object has no property '"s + name.ToString().Utf8Value() + "'"s);
++ }
++
++ return checkedCast<T>(
++ Get(name),
++ [&]() {
++ return "property '"s + name.ToString().Utf8Value() + "'"s;
++ });
++ }
++
++ template<typename T>
++ T Object::eval(const std::string &expr, const std::vector<ValueWrapper> &exprArgs) const
++ {
++ using namespace details_qnapi_p_h;
++
++ return runEscapingHandleScope<T>(
++ Env(),
++ [&]() {
++ return evalWithContextImpl<T>(*this, expr, unwrapValues(Env(), exprArgs)).second;
++ });
++ }
++
++ inline QNapi::Promise Object::evalToPromiseOrRejectOnThrow(
++ const std::string &expr, const std::vector<ValueWrapper> &exprArgs) const
++ {
++ return runEscapingHandleScope<QNapi::Promise>(
++ Env(),
++ [&]() {
++ try {
++ return eval<QNapi::Promise>(expr, exprArgs);
++ } catch (const Napi::Error &error) {
++ auto deferred = Napi::Promise::Deferred::New(Env());
++ deferred.Reject(error.Value());
++ return QNapi::Promise(deferred.Promise());
++ }
++ });
++ }
++
++ inline void Object::set(const std::string &name, const ValueWrapper &value)
++ {
++ Napi::HandleScope setPropScope(Env());
++ Set(name, value.mapToValue(Env()));
++ }
++
++ inline void Object::set(const Napi::Name &name, const ValueWrapper &value)
++ {
++ Napi::HandleScope setPropScope(Env());
++ Set(name, value.mapToValue(Env()));
++ }
++
++ inline void Object::set(const std::vector<std::pair<std::string, ValueWrapper>> &namedValues)
++ {
++ Napi::HandleScope setPropsScope(Env());
++ for (const auto &namedValue : namedValues)
++ Set(namedValue.first, namedValue.second.mapToValue(Env()));
++ }
++
++ template<typename Result>
++ Result Object::call(const std::string &methodName, const std::vector<ValueWrapper> &args) const
++ {
++ return details_qnapi_p_h::callMethod<Result>(*this, methodName, args);
++ }
++
++ inline std::vector<std::string> Object::getFilteredStringKeys(
++ napi_key_collection_mode keyMode, napi_key_filter keyFilter) const
++ {
++ napi_value keys = nullptr;
++ const napi_status status = napi_get_all_property_names(
++ Env(), *this, keyMode, static_cast<napi_key_filter>(keyFilter | napi_key_skip_symbols),
++ napi_key_numbers_to_strings, &keys);
++ if (status != napi_ok)
++ throw Napi::Error::New(Env());
++
++ return getArrayElements<std::vector<std::string>, String>(Array(Env(), keys));
++ }
++
++ inline Promise::Promise()
++ : Napi::Promise(nullptr, nullptr)
++ {
++ }
++
++ inline Promise::Promise(const Napi::Promise &other)
++ : Napi::Promise(other)
++ {
++ }
++
++ inline Promise &Promise::operator=(const Napi::Promise &other)
++ {
++ Napi::Promise::operator=(other);
++ return *this;
++ }
++
++ inline Promise Promise::onThen(CallbackFuncWrapper &&onFulfilledFunc)
++ {
++ return details_qnapi_p_h::callMethod<Promise>(
++ *this, "then", {std::move(onFulfilledFunc.callbackFunc())});
++ }
++
++ inline Promise Promise::onThen(CallbackFuncWrapper &&onFulfilledFunc, CallbackFuncWrapper &&onRejectedFunc)
++ {
++ return details_qnapi_p_h::callMethod<Promise>(
++ *this, "then", {std::move(onFulfilledFunc.callbackFunc()), std::move(onRejectedFunc.callbackFunc())});
++ }
++
++ inline Promise Promise::onCatch(CallbackFuncWrapper &&onRejectedFunc)
++ {
++ return details_qnapi_p_h::callMethod<Promise>(
++ *this, "catch", {std::move(onRejectedFunc.callbackFunc())});
++ }
++
++ inline Promise Promise::onFinally(CallbackFuncWrapper &&onFinallyFunc)
++ {
++ return details_qnapi_p_h::callMethod<Promise>(
++ *this, "finally", {std::move(onFinallyFunc.callbackFunc())});
++ }
++
++ inline Promise Promise::onThenAndFinally(
++ CallbackFuncWrapper &&onFulfilledFunc, CallbackFuncWrapper &&onRejectedFunc,
++ CallbackFuncWrapper &&onFinallyFunc)
++ {
++ return onThen(std::move(onFulfilledFunc), std::move(onRejectedFunc)).onFinally(std::move(onFinallyFunc));
++ }
++
++ template<typename Context>
++ PromiseWithContext<Context> Promise::withContext(Context &&context)
++ {
++ static_assert(!std::is_lvalue_reference<Context>::value, "withContext() supports only rvalue contexts");
++ return PromiseWithContext<Context>(*this, std::make_shared<Context>(std::forward<Context>(context)));
++ }
++
++ template<typename Context>
++ PromiseWithContext<Context>::PromiseWithContext(const Promise &promise, std::shared_ptr<Context> context)
++ : Promise(promise)
++ , m_context(context)
++ {
++ }
++
++ template<typename Context>
++ PromiseWithContext<Context> PromiseWithContext<Context>::onThenWithContext(
++ ExtendedCallbackFuncWrapper<Context &> &&onFulfilledFunc)
++ {
++ return PromiseWithContext(
++ Promise::onThen(
++ [context = m_context, onFulfilledFunc = std::move(onFulfilledFunc.callbackFunc())](const Napi::CallbackInfo &cbInfo) {
++ return onFulfilledFunc(cbInfo, *context);
++ }),
++ m_context);
++ }
++
++ template<typename Context>
++ PromiseWithContext<Context> PromiseWithContext<Context>::onThenWithContext(
++ ExtendedCallbackFuncWrapper<Context &> &&onFulfilledFunc, CallbackFuncWrapper &&onRejectedFunc)
++ {
++ return PromiseWithContext(
++ Promise::onThen(
++ [context = m_context, onFulfilledFunc = std::move(onFulfilledFunc.callbackFunc())](const Napi::CallbackInfo &cbInfo) {
++ return onFulfilledFunc(cbInfo, *context);
++ },
++ [context = m_context, onRejectedFunc = std::move(onRejectedFunc.callbackFunc())](const Napi::CallbackInfo &cbInfo) {
++ return onRejectedFunc(cbInfo, *context);
++ }),
++ m_context);
++ }
++
++ template<typename Context>
++ PromiseWithContext<Context> PromiseWithContext<Context>::onCatchWithContext(
++ ExtendedCallbackFuncWrapper<Context &> &&onRejectedFunc)
++ {
++ return PromiseWithContext(
++ Promise::onCatch(
++ [context = m_context, onRejectedFunc = std::move(onRejectedFunc.callbackFunc())](const Napi::CallbackInfo &cbInfo) {
++ return onRejectedFunc(cbInfo, *context);
++ }),
++ m_context);
++ }
++
++ template<typename Context>
++ PromiseWithContext<Context> PromiseWithContext<Context>::onFinallyWithContext(
++ ExtendedCallbackFuncWrapper<Context &> &&onFinallyFunc)
++ {
++ return PromiseWithContext(
++ Promise::onFinally(
++ [context = m_context, onFinallyFunc = std::move(onFinallyFunc.callbackFunc())](const Napi::CallbackInfo &cbInfo) {
++ return onFinallyFunc(cbInfo, *context);
++ }),
++ m_context);
++ }
++
++ template<typename T>
++ Reference<T>::Reference(Reference<T> &&other)
++ : Napi::Reference<T>(std::move(other))
++ {
++ }
++
++ template<typename T>
++ Reference<T> &Reference<T>::operator=(Reference<T> &&other)
++ {
++ Napi::Reference<T>::operator=(std::move(other));
++ return *this;
++ }
++
++ template<typename T>
++ Reference<T>::Reference(Napi::Reference<T> &&other)
++ : Napi::Reference<T>(std::move(other))
++ {
++ }
++
++ template<typename T>
++ Reference<T> &Reference<T>::operator=(Napi::Reference<T> &&other)
++ {
++ Napi::Reference<T>::operator=(std::move(other));
++ return *this;
++ }
++
++ template<typename T>
++ Reference<T> Reference<T>::makePersistentFrom(const T &value)
++ {
++ return Napi::Persistent(value);
++ }
++
++ template<typename T>
++ Reference<T> Reference<T>::makeEmpty()
++ {
++ return Napi::Reference<T>();
++ }
++
++ inline Reference<Object>::Reference(Reference<Object> &&other)
++ : Napi::Reference<Object>(std::move(other))
++ {
++ }
++
++ inline Reference<Object> &Reference<Object>::operator=(Reference<Object> &&other)
++ {
++ Napi::Reference<Object>::operator=(std::move(other));
++ return *this;
++ }
++
++ inline Reference<Object>::Reference(Napi::Reference<Object> &&other)
++ : Napi::Reference<Object>(std::move(other))
++ {
++ }
++
++ inline Reference<Object> &Reference<Object>::operator=(Napi::Reference<Object> &&other)
++ {
++ Napi::Reference<Object>::operator=(std::move(other));
++ return *this;
++ }
++
++ template<typename T>
++ T Reference<Object>::eval(const std::string &expr, const std::vector<ValueWrapper> &exprArgs) const
++ {
++ return runEscapingHandleScope<T>(
++ Env(),
++ [&]() {
++ return Value().eval<T>(expr, exprArgs);
++ });
++ }
++
++ inline QNapi::Promise Reference<Object>::evalToPromiseOrRejectOnThrow(
++ const std::string &expr, const std::vector<ValueWrapper> &exprArgs) const
++ {
++ return runEscapingHandleScope<QNapi::Promise>(
++ Env(),
++ [&]() {
++ return Value().evalToPromiseOrRejectOnThrow(expr, exprArgs);
++ });
++ }
++
++ inline void Reference<Object>::set(const std::string &name, const ValueWrapper &value)
++ {
++ Napi::HandleScope scope(Env());
++ Value().set(name, value);
++ }
++
++ inline void Reference<Object>::set(const std::vector<std::pair<std::string, ValueWrapper>> &namedValues)
++ {
++ Napi::HandleScope scope(Env());
++ Value().set(namedValues);
++ }
++
++ template<typename Result>
++ Result Reference<Object>::call(const std::string &methodName, const std::vector<ValueWrapper> &args) const
++ {
++ return runEscapingHandleScope<Result>(
++ Env(),
++ [&]() {
++ return Value().call<Result>(methodName, args);
++ });
++ }
++
++ inline Reference<Object> Reference<Object>::makePersistentFrom(const Object &value)
++ {
++ return Napi::Persistent(value);
++ }
++
++ inline Reference<Object> Reference<Object>::makeEmpty()
++ {
++ return Napi::Reference<Object>();
++ }
++
++ template<typename T>
++ std::enable_if_t<std::is_base_of<Napi::Value, T>::value, Reference<T>>
++ Reference<void>::makePersistentFrom(const T &value)
++ {
++ return Napi::Persistent(value);
++ }
++
++ template<typename T>
++ Reference<T> Reference<void>::makeEmpty()
++ {
++ return Napi::Reference<T>();
++ }
++
++ inline CallbackInfo::CallbackInfo(const Napi::CallbackInfo &cbInfo)
++ : m_cbInfo(cbInfo)
++ {
++ }
++
++ inline Napi::Env CallbackInfo::Env() const
++ {
++ return m_cbInfo.Env();
++ }
++
++ inline std::size_t CallbackInfo::Length() const
++ {
++ return m_cbInfo.Length();
++ }
++
++ template<typename... Args>
++ void CallbackInfo::getLeadingArgs(const std::string &funcName, Args &...args) const
++ {
++ details_qnapi_p_h::getLeadingArgsImpl(
++ funcName, m_cbInfo, std::tie(args...), std::make_index_sequence<sizeof...(Args)>());
++ }
++
++ template<typename Arg>
++ Arg CallbackInfo::getFirstArg(const std::string &funcName) const
++ {
++ Arg arg;
++ getLeadingArgs(funcName, arg);
++ return arg;
++ }
++
++ inline CallbackInfo::operator const Napi::CallbackInfo &() const
++ {
++ return m_cbInfo;
++ }
++
++ inline std::vector<napi_value> unwrapValues(napi_env env, const std::vector<ValueWrapper> &wrappedValues)
++ {
++ std::vector<napi_value> unwrappedValues;
++ std::transform(
++ wrappedValues.begin(), wrappedValues.end(),
++ std::back_inserter(unwrappedValues),
++ [&](const ValueWrapper &arg) {
++ return arg.mapToValue(env);
++ });
++ return unwrappedValues;
++ }
++
++ inline Napi::Error makeLoggedException(napi_env env, const std::string &msg)
++ {
++ return details_qnapi_p_h::makeLoggedExceptionImpl(env, msg);
++ }
++
++ template<typename T>
++ bool valueTypeMatches(const Napi::Value &value)
++ {
++ return details_qnapi_p_h::valueTypeMatchesImpl<T>(value);
++ }
++
++ template<typename Element>
++ bool arrayElementTypesMatch(const Napi::Value &value)
++ {
++ if (!valueTypeMatches<Napi::Array>(value))
++ return false;
++
++ auto arrayValue = checkedCast<Napi::Array>(value);
++ auto arrayLength = arrayValue.Length();
++
++ bool allElementsMatch = true;
++ for (std::size_t i = 0; i < arrayLength; ++i) {
++ if (!valueTypeMatches<Element>(arrayValue.Get(i))) {
++ allElementsMatch = false;
++ break;
++ }
++ }
++
++ return allElementsMatch;
++ }
++
++ inline std::string getValueTypeString(const Napi::Value &value)
++ {
++ return details_qnapi_p_h::getValueTypeStringImpl(value);
++ }
++
++ template<typename T, typename ValueDescriptionSupplier>
++ T checkedCast(const Napi::Value &value, ValueDescriptionSupplier &&valueDescSupplier)
++ {
++ return details_qnapi_p_h::checkedCastImpl<T>(
++ value, std::forward<ValueDescriptionSupplier>(valueDescSupplier));
++ }
++
++ template<typename T>
++ T checkedCast(const Napi::Value &value)
++ {
++ return details_qnapi_p_h::checkedCastImpl<T>(
++ value,
++ []() {
++ return std::string();
++ });
++ }
++
++ template<typename OutputContainer, typename Element, typename TransFunc>
++ OutputContainer getArrayElements(const Napi::Array &inputArray, TransFunc &&transFunc)
++ {
++ return getFilteredArrayElements<OutputContainer, Element>(
++ inputArray,
++ [&](Element &&elem) {
++ return std::optional(transFunc(std::forward<Element>(elem)));
++ });
++ }
++
++ template<typename OutputContainer, typename Element>
++ OutputContainer getArrayElements(const Napi::Array &inputArray)
++ {
++ return getArrayElements<OutputContainer, Element>(
++ inputArray,
++ [](Element &&elem) {
++ return std::forward<Element>(elem);
++ });
++ }
++
++ template<typename OutputContainer, typename Element, typename TransFilterFunc>
++ OutputContainer getFilteredArrayElements(const Napi::Array &inputArray, TransFilterFunc &&transFilterFunc)
++ {
++ using namespace std::string_literals;
++
++ OutputContainer result;
++ auto arrayLength = inputArray.Length();
++ for (std::size_t i = 0; i < arrayLength; ++i) {
++ auto arg = inputArray.Get(i);
++ if (!valueTypeMatches<Element>(arg)) {
++ constexpr const char *expectedTypeName = details_qnapi_p_h::ValueTypeTraits<Element>::typeName;
++ auto argTypeStr = getValueTypeString(arg);
++ throw makeLoggedException(
++ inputArray.Env(),
++ "wrong type of Napi array element #"s + std::to_string(i)
++ + ", expected '"s + expectedTypeName + "', got '"s + argTypeStr + "'"s);
++ }
++ if (auto optTransformed = transFilterFunc(checkedCast<Element>(arg)))
++ result.insert(result.end(), std::move(*optTransformed));
++ }
++
++ return result;
++ }
++
++ inline Object makeNewInstance(const Napi::Function &type, const std::vector<ValueWrapper> &args)
++ {
++ return type.New(unwrapValues(type.Env(), args));
++ }
++
++ inline Object makeNewInstance(const Napi::Object &baseObj, const std::string &typePath, const std::vector<ValueWrapper> &args)
++ {
++ return makeNewInstance(Object(baseObj).get<Napi::Function>(typePath), args);
++ }
++
++ inline Napi::Value getPropOrUndefined(const Napi::Value &obj, const std::string &propName)
++ {
++ if (obj.IsEmpty())
++ return Napi::Value();
++
++ if (!obj.IsObject())
++ return obj.Env().Undefined();
++
++ auto typedObj = checkedCast<Napi::Object>(obj);
++
++ return typedObj.Has(propName)
++ ? typedObj.Get(propName)
++ : typedObj.Env().Undefined();
++ }
++
++ template<typename T>
++ Napi::Value getPropOrUndefined(const Napi::Reference<T> &objRef, const std::string &propName)
++ {
++ if (objRef.IsEmpty())
++ return Napi::Value();
++
++ return details_qnapi_p_h::runEscapingHandleScopeImpl<Napi::Value>(
++ objRef.Env(),
++ [&]() {
++ return getPropOrUndefined(objRef.Value(), propName);
++ });
++ }
++
++ template<typename T>
++ std::enable_if_t<std::is_base_of<Napi::Value, T>::value, T>
++ getOptionalPropOrEmpty(const Napi::Object &optObj, const std::string &propName, const std::string &objDesc)
++ {
++ return !optObj.IsEmpty()
++ ? details_qnapi_p_h::getOptionalPropOrEmptyImpl<T>(
++ optObj, Napi::String::New(optObj.Env(), propName), objDesc)
++ : T();
++ }
++
++ template<typename T>
++ std::enable_if_t<std::is_base_of<Napi::Value, T>::value, T>
++ getOptionalPropOrEmpty(const Napi::Object &optObj, const Napi::Name &propName, const std::string &objDesc)
++ {
++ return details_qnapi_p_h::getOptionalPropOrEmptyImpl<T>(optObj, propName, objDesc);
++ }
++
++ inline Object makeObject(napi_env env, const std::vector<std::pair<std::string, ValueWrapper>> &namedValues)
++ {
++ auto obj = QNapi::Object::New(env);
++ obj.set(namedValues);
++ return obj;
++ }
++
++ inline Array makeArray(napi_env env, std::initializer_list<ValueWrapper> values)
++ {
++ return makeArray<std::initializer_list<ValueWrapper> &>(env, values);
++ }
++
++ template<typename InputContainer, typename TransFunc>
++ Array makeArray(napi_env env, InputContainer &&inputContainer, TransFunc &&transFunc)
++ {
++ using namespace details_qnapi_p_h;
++
++ return runEscapingHandleScopeImpl<Napi::Array>(
++ env,
++ [&]() {
++ auto containerSize = static_cast<std::size_t>(
++ std::distance(std::begin(inputContainer), std::end(inputContainer)));
++ auto array = Napi::Array::New(env, containerSize);
++ std::size_t i = 0;
++ for (auto &element : inputContainer) {
++ array.Set(i, ValueWrapper(transFunc(forwardContainerElement<InputContainer>(element))).mapToValue(env));
++ ++i;
++ }
++ return array;
++ });
++ }
++
++ template<typename InputContainer>
++ Array makeArray(napi_env env, InputContainer &&inputContainer)
++ {
++ return makeArray(
++ env, std::forward<InputContainer>(inputContainer),
++ [](auto &&element) -> decltype(auto) {
++ return std::forward<decltype(element)>(element);
++ });
++ }
++
++ template<typename Result, typename F>
++ Result runEscapingHandleScope(napi_env env, F &&func)
++ {
++ return details_qnapi_p_h::runEscapingHandleScopeImpl<Result>(env, std::forward<F>(func));
++ }
++
++ inline std::string toJsonString(const Napi::Value &value)
++ {
++ std::string jsonString;
++ try {
++ Napi::HandleScope stringifyScope(value.Env());
++ jsonString = details_qnapi_p_h::callMethod<String>(value.Env().Global(), "JSON.stringify", {value});
++ } catch (...) {
++ jsonString = "<stringify-error>";
++ }
++ return jsonString;
++ }
++
++ }
++
++ QT_END_NAMESPACE
++
++ #endif // QNAPI_P_H