1 /** 2 * Copyright (c) 2023-2024 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 PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JSVALUE_H_ 17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JSVALUE_H_ 18 19 #include "plugins/ets/runtime/ets_coroutine.h" 20 #include "plugins/ets/runtime/interop_js/interop_common.h" 21 #include "plugins/ets/runtime/interop_js/interop_context.h" 22 #include "plugins/ets/runtime/types/ets_object.h" 23 #include "runtime/include/coretypes/class.h" 24 #include "utils/small_vector.h" 25 #include <node_api.h> 26 27 namespace ark::ets::interop::js { 28 29 namespace testing { 30 class JSValueOffsets; 31 } // namespace testing 32 33 struct JSValueMemberOffsets; 34 35 class JSValue : private EtsObject { 36 public: FromEtsObject(EtsObject * etsObject)37 static JSValue *FromEtsObject(EtsObject *etsObject) 38 { 39 ASSERT(etsObject->GetClass() == EtsClass::FromRuntimeClass(InteropCtx::Current()->GetJSValueClass())); 40 return static_cast<JSValue *>(etsObject); 41 } 42 FromCoreType(ObjectHeader * object)43 static JSValue *FromCoreType(ObjectHeader *object) 44 { 45 return FromEtsObject(EtsObject::FromCoreType(object)); 46 } 47 AsObject()48 EtsObject *AsObject() 49 { 50 return reinterpret_cast<EtsObject *>(this); 51 } 52 AsObject()53 const EtsObject *AsObject() const 54 { 55 return reinterpret_cast<const EtsObject *>(this); 56 } 57 GetCoreType()58 ObjectHeader *GetCoreType() const 59 { 60 return EtsObject::GetCoreType(); 61 } 62 GetType()63 napi_valuetype GetType() const 64 { 65 return bit_cast<napi_valuetype>(type_); 66 } 67 68 static JSValue *Create(EtsCoroutine *coro, InteropCtx *ctx, napi_value nvalue); 69 CreateUndefined(EtsCoroutine * coro,InteropCtx * ctx)70 static JSValue *CreateUndefined(EtsCoroutine *coro, InteropCtx *ctx) 71 { 72 return AllocUndefined(coro, ctx); 73 } 74 CreateNull(EtsCoroutine * coro,InteropCtx * ctx)75 static JSValue *CreateNull(EtsCoroutine *coro, InteropCtx *ctx) 76 { 77 auto jsvalue = AllocUndefined(coro, ctx); 78 if (UNLIKELY(jsvalue == nullptr)) { 79 return nullptr; 80 } 81 jsvalue->SetNull(); 82 return jsvalue; 83 } 84 CreateBoolean(EtsCoroutine * coro,InteropCtx * ctx,bool value)85 static JSValue *CreateBoolean(EtsCoroutine *coro, InteropCtx *ctx, bool value) 86 { 87 auto jsvalue = AllocUndefined(coro, ctx); 88 if (UNLIKELY(jsvalue == nullptr)) { 89 return nullptr; 90 } 91 jsvalue->SetBoolean(value); 92 return jsvalue; 93 } 94 CreateNumber(EtsCoroutine * coro,InteropCtx * ctx,double value)95 static JSValue *CreateNumber(EtsCoroutine *coro, InteropCtx *ctx, double value) 96 { 97 auto jsvalue = AllocUndefined(coro, ctx); 98 if (UNLIKELY(jsvalue == nullptr)) { 99 return nullptr; 100 } 101 jsvalue->SetNumber(value); 102 return jsvalue; 103 } 104 CreateString(EtsCoroutine * coro,InteropCtx * ctx,std::string && value)105 static JSValue *CreateString(EtsCoroutine *coro, InteropCtx *ctx, std::string &&value) 106 { 107 auto jsvalue = AllocUndefined(coro, ctx); 108 if (UNLIKELY(jsvalue == nullptr)) { 109 return nullptr; 110 } 111 jsvalue->SetString(ctx->GetStringStor()->Get(std::move(value))); 112 return JSValue::AttachFinalizer(EtsCoroutine::GetCurrent(), jsvalue); 113 } 114 CreateRefValue(EtsCoroutine * coro,InteropCtx * ctx,napi_value value,napi_valuetype type)115 static JSValue *CreateRefValue(EtsCoroutine *coro, InteropCtx *ctx, napi_value value, napi_valuetype type) 116 { 117 auto jsvalue = AllocUndefined(coro, ctx); 118 if (UNLIKELY(jsvalue == nullptr)) { 119 return nullptr; 120 } 121 jsvalue->SetRefValue(ctx->GetJSEnv(), value, type); 122 return JSValue::AttachFinalizer(EtsCoroutine::GetCurrent(), jsvalue); 123 } 124 125 napi_value GetNapiValue(napi_env env); 126 GetBoolean()127 bool GetBoolean() const 128 { 129 ASSERT(GetType() == napi_boolean); 130 return GetData<bool>(); 131 } 132 GetNumber()133 double GetNumber() const 134 { 135 ASSERT(GetType() == napi_number); 136 return GetData<double>(); 137 } 138 GetBigInt()139 std::pair<SmallVector<uint64_t, 4U>, int> *GetBigInt() const 140 { 141 ASSERT(GetType() == napi_bigint); 142 return GetData<std::pair<SmallVector<uint64_t, 4U>, int> *>(); 143 } 144 GetString()145 JSValueStringStorage::CachedEntry GetString() const 146 { 147 ASSERT(GetType() == napi_string); 148 return JSValueStringStorage::CachedEntry(GetData<std::string const *>()); 149 } 150 GetRefValue(napi_env env)151 napi_value GetRefValue(napi_env env) 152 { 153 napi_value value; 154 NAPI_ASSERT_OK(napi_get_reference_value(env, GetNapiRef(), &value)); 155 return value; 156 } 157 158 static void FinalizeETSWeak(InteropCtx *ctx, EtsObject *cbarg); 159 GetTypeOffset()160 static constexpr uint32_t GetTypeOffset() 161 { 162 return MEMBER_OFFSET(JSValue, type_); 163 } 164 165 JSValue() = delete; 166 167 private: 168 static JSValue *CreateByType(InteropCtx *ctx, napi_env env, napi_value nvalue, napi_valuetype jsType, 169 JSValue *jsvalue); 170 IsRefType(napi_valuetype type)171 static constexpr bool IsRefType(napi_valuetype type) 172 { 173 return type == napi_object || type == napi_function || type == napi_symbol; 174 } 175 IsFinalizableType(napi_valuetype type)176 static constexpr bool IsFinalizableType(napi_valuetype type) 177 { 178 return type == napi_string || type == napi_bigint || IsRefType(type); 179 } 180 SetType(napi_valuetype type)181 void SetType(napi_valuetype type) 182 { 183 type_ = bit_cast<decltype(type_)>(type); 184 } 185 186 template <typename T> SetData(T val)187 std::enable_if_t<std::is_trivially_copyable_v<T>> SetData(T val) 188 { 189 static_assert(sizeof(data_) >= sizeof(T)); 190 std::copy_n(reinterpret_cast<uint8_t *>(&val), sizeof(T), reinterpret_cast<uint8_t *>(&data_)); 191 } 192 193 template <typename T> GetData()194 std::enable_if_t<std::is_trivially_copyable_v<T> && std::is_trivially_constructible_v<T>, T> GetData() const 195 { 196 static_assert(sizeof(data_) >= sizeof(T)); 197 T val; 198 std::copy_n(reinterpret_cast<const uint8_t *>(&data_), sizeof(T), reinterpret_cast<uint8_t *>(&val)); 199 return val; 200 } 201 AllocUndefined(EtsCoroutine * coro,InteropCtx * ctx)202 static JSValue *AllocUndefined(EtsCoroutine *coro, InteropCtx *ctx) 203 { 204 JSValue *jsValue; 205 { 206 auto obj = ObjectHeader::Create(coro, ctx->GetJSValueClass()); 207 if (UNLIKELY(!obj)) { 208 return nullptr; 209 } 210 jsValue = FromCoreType(obj); 211 } 212 static_assert(napi_undefined == 0); // zero-initialized 213 ASSERT(jsValue->GetType() == napi_undefined); 214 return jsValue; 215 } 216 217 // Returns moved jsValue 218 [[nodiscard]] static JSValue *AttachFinalizer(EtsCoroutine *coro, JSValue *jsValue); 219 SetNapiRef(napi_ref ref,napi_valuetype type)220 void SetNapiRef(napi_ref ref, napi_valuetype type) 221 { 222 ASSERT(IsRefType(type)); 223 SetType(type); 224 SetData(ref); 225 } 226 GetNapiRef()227 napi_ref GetNapiRef() const 228 { 229 ASSERT(IsRefType(GetType())); 230 return GetData<napi_ref>(); 231 } 232 SetUndefined()233 void SetUndefined() 234 { 235 SetType(napi_undefined); 236 } 237 SetNull()238 void SetNull() 239 { 240 SetType(napi_null); 241 } 242 SetBoolean(bool value)243 void SetBoolean(bool value) 244 { 245 SetType(napi_boolean); 246 SetData(value); 247 } 248 SetNumber(double value)249 void SetNumber(double value) 250 { 251 SetType(napi_number); 252 SetData(value); 253 } 254 SetString(JSValueStringStorage::CachedEntry value)255 void SetString(JSValueStringStorage::CachedEntry value) 256 { 257 SetType(napi_string); 258 SetData(value); 259 } 260 SetBigInt(std::pair<SmallVector<uint64_t,4U>,int> && value)261 void SetBigInt(std::pair<SmallVector<uint64_t, 4U>, int> &&value) 262 { 263 SetType(napi_bigint); 264 SetData(new std::pair<SmallVector<uint64_t, 4U>, int>(std::move(value))); 265 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) 266 } 267 SetRefValue(napi_env env,napi_value jsValue,napi_valuetype type)268 void SetRefValue(napi_env env, napi_value jsValue, napi_valuetype type) 269 { 270 ASSERT(GetValueType(env, jsValue) == type); 271 napi_ref jsRef; 272 NAPI_ASSERT_OK(napi_create_reference(env, jsValue, 1, &jsRef)); 273 SetNapiRef(jsRef, type); 274 } 275 276 FIELD_UNUSED uint32_t type_; 277 FIELD_UNUSED uint32_t padding_; 278 FIELD_UNUSED uint64_t data_; 279 280 friend class testing::JSValueOffsets; 281 }; 282 283 static_assert(JSValue::GetTypeOffset() == sizeof(ObjectHeader)); 284 285 } // namespace ark::ets::interop::js 286 287 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JSVALUE_H_ 288