1 /*
2 * Copyright (c) 2021 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 #include <cinttypes>
17
18 #include "ark_native_reference.h"
19
20 #include "native_engine/native_api_internal.h"
21 #include "native_engine/native_utils.h"
22
ArkNativeReference(ArkNativeEngine * engine,napi_value value,uint32_t initialRefcount,bool deleteSelf,NapiNativeFinalize napiCallback,void * data,void * hint,bool isAsyncCall,size_t nativeBindingSize)23 ArkNativeReference::ArkNativeReference(ArkNativeEngine* engine,
24 napi_value value,
25 uint32_t initialRefcount,
26 bool deleteSelf,
27 NapiNativeFinalize napiCallback,
28 void* data,
29 void* hint,
30 bool isAsyncCall,
31 size_t nativeBindingSize)
32 : engine_(engine),
33 value_(engine->GetEcmaVm(), LocalValueFromJsValue(value)),
34 refCount_(initialRefcount),
35 ownership_(deleteSelf ? ReferenceOwnerShip::RUNTIME : ReferenceOwnerShip::USER),
36 napiCallback_(napiCallback),
37 data_(data),
38 hint_(hint),
39 nativeBindingSize_(nativeBindingSize)
40 {
41 InitProperties(deleteSelf, isAsyncCall);
42 ArkNativeReferenceConstructor();
43 }
44
ArkNativeReference(ArkNativeEngine * engine,Local<JSValueRef> value,uint32_t initialRefcount,bool deleteSelf,NapiNativeFinalize napiCallback,void * data,void * hint,bool isAsyncCall,size_t nativeBindingSize)45 ArkNativeReference::ArkNativeReference(ArkNativeEngine* engine,
46 Local<JSValueRef> value,
47 uint32_t initialRefcount,
48 bool deleteSelf,
49 NapiNativeFinalize napiCallback,
50 void* data,
51 void* hint,
52 bool isAsyncCall,
53 size_t nativeBindingSize)
54 : engine_(engine),
55 value_(engine->GetEcmaVm(), value),
56 refCount_(initialRefcount),
57 ownership_(deleteSelf ? ReferenceOwnerShip::RUNTIME : ReferenceOwnerShip::USER),
58 napiCallback_(napiCallback),
59 data_(data),
60 hint_(hint),
61 nativeBindingSize_(nativeBindingSize)
62 {
63 InitProperties(deleteSelf, isAsyncCall);
64 ArkNativeReferenceConstructor();
65 }
66
ArkNativeReferenceConstructor()67 void ArkNativeReference::ArkNativeReferenceConstructor()
68 {
69 if (napiCallback_ != nullptr) {
70 // Async callback will redirect to root engine, no monitoring needed.
71 if (!IsAsyncCall()) {
72 engine_->IncreaseCallbackbleRefCounter();
73 // Non-callback runtime owned napi_ref will free when env teardown.
74 if (ownership_ == ReferenceOwnerShip::RUNTIME && !engine_->IsMainEnvContext()) {
75 engine_->IncreaseRuntimeOwnedRefCounter();
76 }
77 }
78 } else {
79 engine_->IncreaseNonCallbackRefCounter();
80 }
81
82 if (refCount_ == 0) {
83 value_.SetWeakCallback(reinterpret_cast<void*>(this), FreeGlobalCallBack, NativeFinalizeCallBack);
84 }
85
86 if (ownership_ == ReferenceOwnerShip::RUNTIME) {
87 NativeReferenceManager* referenceManager = engine_->GetReferenceManager();
88 if (referenceManager != nullptr) {
89 referenceManager->CreateHandler(this);
90 }
91 }
92
93 engineId_ = engine_->GetId();
94 }
95
InitProperties(bool deleteSelf,bool isAsyncCall)96 inline void ArkNativeReference::InitProperties(bool deleteSelf, bool isAsyncCall)
97 {
98 if (deleteSelf) {
99 SetDeleteSelf();
100 }
101 if (isAsyncCall) {
102 properties_ |= ReferencePropertiesMask::IS_ASYNC_CALL_MASK;
103 }
104 }
105
106 // Do not use pure virtual function of NativeEngine,
107 // it may cause crash if ArkNativeEngine is deconstructed.
108 // Which would occur when EcmaVM is destroying in CleanEnv.
~ArkNativeReference()109 ArkNativeReference::~ArkNativeReference()
110 {
111 VALID_ENGINE_CHECK(engine_, engine_, engineId_);
112
113 if (!napiCallback_) {
114 engine_->DecreaseNonCallbackRefCounter();
115 }
116
117 NativeReferenceManager* refMgr = engine_->GetReferenceManager();
118 if (ownership_ == ReferenceOwnerShip::RUNTIME && refMgr != nullptr) {
119 refMgr->ReleaseHandler(this);
120 prev_ = nullptr;
121 next_ = nullptr;
122 }
123 if (value_.IsEmpty()) {
124 return;
125 }
126 SetHasDelete();
127 value_.FreeGlobalHandleAddr();
128 FinalizeCallback(FinalizerState::DESTRUCTION);
129 }
130
Ref()131 uint32_t ArkNativeReference::Ref()
132 {
133 ++refCount_;
134 if (refCount_ == 1) {
135 value_.ClearWeak();
136 }
137 return refCount_;
138 }
139
Unref()140 uint32_t ArkNativeReference::Unref()
141 {
142 if (refCount_ == 0) {
143 return refCount_;
144 }
145 --refCount_;
146 if (value_.IsEmpty()) {
147 return refCount_;
148 }
149 if (refCount_ == 0) {
150 value_.SetWeakCallback(reinterpret_cast<void*>(this), FreeGlobalCallBack, NativeFinalizeCallBack);
151 }
152 return refCount_;
153 }
154
Get(NativeEngine * engine)155 napi_value ArkNativeReference::Get(NativeEngine* engine)
156 {
157 if (value_.IsEmpty()) {
158 return nullptr;
159 }
160 VALID_ENGINE_CHECK(engine, engine_, engineId_);
161 Local<JSValueRef> value = value_.ToLocal(engine->GetEcmaVm());
162 return JsValueFromLocalValue(value);
163 }
164
Get()165 napi_value ArkNativeReference::Get()
166 {
167 return Get(engine_);
168 }
169
operator napi_value()170 ArkNativeReference::operator napi_value()
171 {
172 return Get(engine_);
173 }
174
GetData()175 void* ArkNativeReference::GetData()
176 {
177 return data_;
178 }
179
180 struct NapiSecondCallback {
181 NapiNativeFinalize callback_;
182 void* data_;
183 ReferenceOwnerShip ownership_;
InvokeNapiSecondCallback184 void Invoke(napi_env env, void* hint)
185 {
186 callback_(env, data_, hint);
187 }
188 // proxy of user callback
SecondFinalizeCallbackNapiSecondCallback189 static void SecondFinalizeCallback(napi_env env, void* data, void* hint)
190 {
191 NapiSecondCallback* callbackWrap = reinterpret_cast<NapiSecondCallback*>(data);
192 callbackWrap->Invoke(env, hint);
193 ArkNativeEngine* engine = reinterpret_cast<ArkNativeEngine*>(env);
194 engine->DecreaseCallbackbleRefCounter();
195
196 if (callbackWrap->ownership_ == ReferenceOwnerShip::RUNTIME) {
197 engine->DecreaseRuntimeOwnedRefCounter();
198 }
199
200 delete callbackWrap;
201 if (engine->IsReadyToDelete()) {
202 engine->Delete();
203 }
204 }
205 };
206
EnqueueAsyncTask()207 void ArkNativeReference::EnqueueAsyncTask()
208 {
209 std::pair<void*, void*> pair = std::make_pair(data_, hint_);
210 RefAsyncFinalizer asyncFinalizer = std::make_pair(napiCallback_, pair);
211 // Async callback doesn't require the current engine. Use the root engine if a context is passed.
212 if (engine_->IsMainEnvContext()) {
213 engine_->GetPendingAsyncFinalizers().emplace_back(asyncFinalizer);
214 } else {
215 const_cast<ArkNativeEngine*>(engine_->GetParent())
216 ->GetPendingAsyncFinalizers()
217 .emplace_back(asyncFinalizer);
218 }
219 }
220
EnqueueDeferredTask()221 void ArkNativeReference::EnqueueDeferredTask()
222 {
223 if (engine_->IsMainEnvContext()) {
224 std::tuple<NativeEngine*, void*, void*> tuple = std::make_tuple(engine_, data_, hint_);
225 RefFinalizer finalizer = std::make_pair(napiCallback_, tuple);
226 engine_->GetArkFinalizersPack().AddFinalizer(finalizer, nativeBindingSize_);
227 } else {
228 std::tuple<NativeEngine*, void*, void*> tuple =
229 std::make_tuple(engine_, new NapiSecondCallback { napiCallback_, data_, ownership_ }, hint_);
230 RefFinalizer finalizer = std::make_pair(NapiSecondCallback::SecondFinalizeCallback, tuple);
231 // callbackble ref counter will decrease until callback is invoked
232 const_cast<ArkNativeEngine*>(engine_->GetParent())
233 ->GetArkFinalizersPack()
234 .AddFinalizer(finalizer, nativeBindingSize_);
235 }
236 }
237
DispatchFinalizeCallback()238 void ArkNativeReference::DispatchFinalizeCallback()
239 {
240 if (IsAsyncCall()) {
241 EnqueueAsyncTask();
242 } else {
243 EnqueueDeferredTask();
244 }
245 }
246
FinalizeCallback(FinalizerState state)247 void ArkNativeReference::FinalizeCallback(FinalizerState state)
248 {
249 // Invoke the callback only if it is callbackble and has not already been invoked.
250 if (!GetFinalRun() && napiCallback_ && !engine_->IsInDestructor()) {
251 if (state == FinalizerState::COLLECTION) {
252 DispatchFinalizeCallback();
253 } else {
254 if (!IsAsyncCall()) {
255 engine_->DecreaseCallbackbleRefCounter();
256 }
257 if (ownership_ == ReferenceOwnerShip::RUNTIME) {
258 engine_->DecreaseRuntimeOwnedRefCounter();
259 }
260 napiCallback_(reinterpret_cast<napi_env>(engine_), data_, hint_);
261 }
262 }
263 data_ = nullptr;
264 hint_ = nullptr;
265 SetFinalRan();
266
267 if (GetDeleteSelf() && !HasDelete()) {
268 delete this;
269 }
270 }
271
FreeGlobalCallBack(void * ref)272 void ArkNativeReference::FreeGlobalCallBack(void* ref)
273 {
274 auto that = reinterpret_cast<ArkNativeReference*>(ref);
275 that->value_.FreeGlobalHandleAddr();
276 }
277
NativeFinalizeCallBack(void * ref)278 void ArkNativeReference::NativeFinalizeCallBack(void* ref)
279 {
280 auto that = reinterpret_cast<ArkNativeReference*>(ref);
281 that->FinalizeCallback(FinalizerState::COLLECTION);
282 }
283
SetDeleteSelf()284 void ArkNativeReference::SetDeleteSelf()
285 {
286 properties_ |= ReferencePropertiesMask::DELETE_SELF_MASK;
287 }
288
GetDeleteSelf() const289 bool ArkNativeReference::GetDeleteSelf() const
290 {
291 return (properties_ & ReferencePropertiesMask::DELETE_SELF_MASK) != 0;
292 }
293
GetRefCount()294 uint32_t ArkNativeReference::GetRefCount()
295 {
296 return refCount_;
297 }
298
GetFinalRun()299 bool ArkNativeReference::GetFinalRun()
300 {
301 return (properties_ & ReferencePropertiesMask::FINAL_RAN_MASK) != 0;
302 }
303
GetNapiValue()304 napi_value ArkNativeReference::GetNapiValue()
305 {
306 return Get();
307 }
308
ResetFinalizer()309 void ArkNativeReference::ResetFinalizer()
310 {
311 napiCallback_ = nullptr;
312 data_ = nullptr;
313 hint_ = nullptr;
314 }
315
IsAsyncCall() const316 bool ArkNativeReference::IsAsyncCall() const
317 {
318 return (properties_ & ReferencePropertiesMask::IS_ASYNC_CALL_MASK) != 0;
319 }
320
HasDelete() const321 inline bool ArkNativeReference::HasDelete() const
322 {
323 return (properties_ & ReferencePropertiesMask::HAS_DELETE_MASK) != 0;
324 }
325
SetHasDelete()326 inline void ArkNativeReference::SetHasDelete()
327 {
328 properties_ |= ReferencePropertiesMask::HAS_DELETE_MASK;
329 }
330
SetFinalRan()331 inline void ArkNativeReference::SetFinalRan()
332 {
333 properties_ |= ReferencePropertiesMask::FINAL_RAN_MASK;
334 }
335
336 #ifdef PANDA_JS_ETS_HYBRID_MODE
MarkFromObject()337 void ArkNativeReference::MarkFromObject()
338 {
339 value_.MarkFromObject();
340 }
341
IsObjectAlive()342 bool ArkNativeReference::IsObjectAlive()
343 {
344 return value_.IsObjectAlive();
345 }
346
IsValidHeapObject()347 bool ArkNativeReference::IsValidHeapObject()
348 {
349 return value_.IsValidHeapObject();
350 }
351 #endif // PANDA_JS_ETS_HYBRID_MODE
352