• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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