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 panda::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 // Specific for experimental ts2ets_tstype CreateTSTypeDerived(Class * klass,napi_env env,napi_value jsVal)152 static JSValue *CreateTSTypeDerived([[maybe_unused]] Class *klass, [[maybe_unused]] napi_env env, 153 [[maybe_unused]] napi_value jsVal) 154 { 155 UNREACHABLE(); 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: IsRefType(napi_valuetype type)168 static constexpr bool IsRefType(napi_valuetype type) 169 { 170 return type == napi_object || type == napi_function || type == napi_symbol; 171 } 172 IsFinalizableType(napi_valuetype type)173 static constexpr bool IsFinalizableType(napi_valuetype type) 174 { 175 return type == napi_string || IsRefType(type); 176 } 177 SetType(napi_valuetype type)178 void SetType(napi_valuetype type) 179 { 180 type_ = bit_cast<decltype(type_)>(type); 181 } 182 183 template <typename T> SetData(T val)184 std::enable_if_t<std::is_trivially_copyable_v<T>> SetData(T val) 185 { 186 static_assert(sizeof(data_) >= sizeof(T)); 187 std::copy_n(reinterpret_cast<uint8_t *>(&val), sizeof(T), reinterpret_cast<uint8_t *>(&data_)); 188 } 189 190 template <typename T> GetData()191 std::enable_if_t<std::is_trivially_copyable_v<T> && std::is_trivially_constructible_v<T>, T> GetData() const 192 { 193 static_assert(sizeof(data_) >= sizeof(T)); 194 T val; 195 std::copy_n(reinterpret_cast<const uint8_t *>(&data_), sizeof(T), reinterpret_cast<uint8_t *>(&val)); 196 return val; 197 } 198 AllocUndefined(EtsCoroutine * coro,InteropCtx * ctx)199 static JSValue *AllocUndefined(EtsCoroutine *coro, InteropCtx *ctx) 200 { 201 JSValue *jsValue; 202 { 203 auto obj = ObjectHeader::Create(coro, ctx->GetJSValueClass()); 204 if (UNLIKELY(!obj)) { 205 return nullptr; 206 } 207 jsValue = FromCoreType(obj); 208 } 209 static_assert(napi_undefined == 0); // zero-initialized 210 ASSERT(jsValue->GetType() == napi_undefined); 211 return jsValue; 212 } 213 214 // Returns moved jsValue 215 [[nodiscard]] static JSValue *AttachFinalizer(EtsCoroutine *coro, JSValue *jsValue); 216 SetNapiRef(napi_ref ref,napi_valuetype type)217 void SetNapiRef(napi_ref ref, napi_valuetype type) 218 { 219 ASSERT(IsRefType(type)); 220 SetType(type); 221 SetData(ref); 222 } 223 GetNapiRef()224 napi_ref GetNapiRef() const 225 { 226 ASSERT(IsRefType(GetType())); 227 return GetData<napi_ref>(); 228 } 229 SetUndefined()230 void SetUndefined() 231 { 232 SetType(napi_undefined); 233 } 234 SetNull()235 void SetNull() 236 { 237 SetType(napi_null); 238 } 239 SetBoolean(bool value)240 void SetBoolean(bool value) 241 { 242 SetType(napi_boolean); 243 SetData(value); 244 } 245 SetNumber(double value)246 void SetNumber(double value) 247 { 248 SetType(napi_number); 249 SetData(value); 250 } 251 SetString(JSValueStringStorage::CachedEntry value)252 void SetString(JSValueStringStorage::CachedEntry value) 253 { 254 SetType(napi_string); 255 SetData(value); 256 } 257 SetRefValue(napi_env env,napi_value jsValue,napi_valuetype type)258 void SetRefValue(napi_env env, napi_value jsValue, napi_valuetype type) 259 { 260 ASSERT(GetValueType(env, jsValue) == type); 261 napi_ref jsRef; 262 NAPI_ASSERT_OK(napi_create_reference(env, jsValue, 1, &jsRef)); 263 SetNapiRef(jsRef, type); 264 } 265 266 FIELD_UNUSED uint32_t type_; 267 FIELD_UNUSED uint32_t padding_; 268 FIELD_UNUSED uint64_t data_; 269 270 friend class testing::JSValueOffsets; 271 }; 272 273 static_assert(JSValue::GetTypeOffset() == sizeof(ObjectHeader)); 274 275 } // namespace panda::ets::interop::js 276 277 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JSVALUE_H 278