1 /* 2 * Copyright (C) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef NAPI_API_FUNCTION_CONTEXT_H 17 #define NAPI_API_FUNCTION_CONTEXT_H 18 19 #ifdef __OHOS_PLATFORM__ 20 #include "napi/native_api.h" 21 #else 22 #include <node_api.h> 23 #endif 24 25 #include <base/containers/string.h> 26 27 #include "env.h" 28 #include "object.h" 29 #include "utils.h" 30 #include "value.h" 31 32 namespace NapiApi { 33 34 // Should the requested arg count match the provided count or do we allow partial requests. 35 enum class ArgCount { EXACT, PARTIAL }; 36 37 template<typename... RequestedArgs> 38 class FunctionContext { 39 public: 40 FunctionContext(const napi_env env, const napi_callback_info info, ArgCount argMode = ArgCount::EXACT) 41 { 42 if ((!env) || (!info)) { 43 return; 44 } 45 napi_status status { napi_ok }; 46 status = napi_get_cb_info(env, info, &providedArgCount_, nullptr, &jsThis_, &data_); 47 if (status != napi_ok) { 48 return; 49 } 50 env_ = NapiApi::Env(env); 51 info_ = info; 52 if constexpr (sizeof...(RequestedArgs) > 0) { 53 // validate arg count first 54 const bool exactOk = argMode == ArgCount::EXACT && requestedArgCount_ == providedArgCount_; 55 const bool partialOk = argMode == ArgCount::PARTIAL && requestedArgCount_ <= providedArgCount_; 56 if (!exactOk && !partialOk) { 57 jsThis_ = nullptr; 58 data_ = nullptr; 59 info_ = nullptr; 60 return; 61 } 62 63 // get the arguments 64 auto getThisManyArgs = requestedArgCount_; // The napi call needs a non-const. 65 status = napi_get_cb_info(env, info, &getThisManyArgs, args, nullptr, nullptr); 66 if (!validate<RequestedArgs...>(0)) { 67 // non matching types in context! 68 jsThis_ = nullptr; 69 data_ = nullptr; 70 info_ = nullptr; 71 return; 72 } 73 } 74 } 75 76 template<typename... Other> FunctionContext(const FunctionContext<Other...> & other)77 FunctionContext(const FunctionContext<Other...>& other) : FunctionContext(other.GetEnv(), other.GetInfo()) 78 {} 79 80 operator bool() const 81 { 82 return (env_ && info_); 83 } 84 GetData()85 void* GetData() const 86 { 87 return data_; 88 } 89 Env()90 NapiApi::Env Env() const 91 { 92 return env_; 93 } 94 GetEnv()95 napi_env GetEnv() const 96 { 97 return env_; 98 } 99 GetInfo()100 napi_callback_info GetInfo() const 101 { 102 return info_; 103 } 104 This()105 NapiApi::Object This() 106 { 107 return { env_, jsThis_ }; 108 } 109 value(size_t index)110 napi_value value(size_t index) 111 { 112 if (index < requestedArgCount_) { 113 return args[index]; 114 } 115 return nullptr; 116 } 117 118 template<size_t I, typename T, typename... TypesI> 119 struct GetTypeImpl { 120 using type = typename GetTypeImpl<I - 1, TypesI...>::type; 121 }; 122 123 template<typename T, typename... TypesI> 124 struct GetTypeImpl<0, T, TypesI...> { 125 using type = T; 126 }; 127 128 template<size_t index> 129 auto Arg() 130 { 131 if constexpr (sizeof...(RequestedArgs) > 0) { 132 if constexpr (index < sizeof...(RequestedArgs)) { 133 return NapiApi::Value<typename GetTypeImpl<index, RequestedArgs...>::type> { env_, args[index] }; 134 } 135 if constexpr (index >= sizeof...(RequestedArgs)) { 136 static_assert(index < sizeof...(RequestedArgs), "Index out of range !"); 137 return NapiApi::Value<void*>((napi_env) nullptr, (void*)nullptr); 138 } 139 } 140 if constexpr (sizeof...(RequestedArgs) == 0) { 141 return; 142 } 143 } 144 145 size_t ArgCount() const 146 { 147 return providedArgCount_; 148 } 149 150 napi_value GetUndefined() 151 { 152 return env_.GetUndefined(); 153 } 154 155 napi_value GetNull() 156 { 157 return env_.GetNull(); 158 } 159 160 napi_value GetBoolean(bool value) 161 { 162 return env_.GetBoolean(value); 163 } 164 165 napi_value GetNumber(int32_t value) 166 { 167 return env_.GetNumber(value); 168 } 169 170 napi_value GetNumber(uint32_t value) 171 { 172 return env_.GetNumber(value); 173 } 174 175 napi_value GetNumber(float value) 176 { 177 return env_.GetNumber(value); 178 } 179 180 napi_value GetNumber(double value) 181 { 182 return env_.GetNumber(value); 183 } 184 185 napi_value GetString(const BASE_NS::string_view value) 186 { 187 return env_.GetString(value); 188 } 189 190 private: 191 template<typename First, typename... Rest> 192 inline bool validate(size_t index) 193 { 194 napi_valuetype jstype; 195 napi_status status = napi_invalid_arg; 196 status = napi_typeof(env_, args[index], &jstype); 197 bool isArray = false; 198 napi_is_array(env_, args[index], &isArray); 199 200 bool ret = NapiApi::ValidateType<First>(jstype, isArray); 201 if (ret) { 202 if constexpr (sizeof...(Rest) == 0) { 203 return true; 204 } 205 if constexpr (sizeof...(Rest) > 0) { 206 return validate<Rest...>(index + 1); 207 } 208 } 209 return false; 210 } 211 212 napi_value jsThis_ { nullptr }; 213 void* data_ { nullptr }; 214 // How many args we want to access from the JS function call. 215 const size_t requestedArgCount_ { sizeof...(RequestedArgs) }; 216 napi_value args[sizeof...(RequestedArgs) + 1] {}; 217 NapiApi::Env env_ { nullptr }; 218 napi_callback_info info_ { nullptr }; 219 // How many args actually were in the JS function call. 220 size_t providedArgCount_ { 0 }; 221 }; 222 223 } // namespace NapiApi 224 225 #endif 226