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