• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 #include "ark_interop_internal.h"
17 #include "ark_interop_napi.h"
18 #include "ark_interop_log.h"
19 
20 #include <atomic>
21 #include <unordered_map>
22 #include <vector>
23 
24 using namespace panda;
25 using namespace panda::ecmascript;
26 
27 struct ARKTS_Global_ {
ARKTS_Global_ARKTS_Global_28     ARKTS_Global_(EcmaVM *vm, const Local<JSValueRef>& value): ref(vm, value), isDisposed(false) {}
29 
~ARKTS_Global_ARKTS_Global_30     ~ARKTS_Global_()
31     {
32         if (!isDisposed) {
33             ref.FreeGlobalHandleAddr();
34         }
35     }
36 
SetWeakARKTS_Global_37     void SetWeak()
38     {
39         if (isDisposed) {
40             return;
41         }
42         if (ref.IsWeak()) {
43             return;
44         }
45         ref.SetWeakCallback(this, [](void* handle) {
46             auto global = P_CAST(handle, ARKTS_Global);
47             if (!global) {
48                 return;
49             }
50             global->isDisposed = true;
51             global->ref.FreeGlobalHandleAddr();
52         }, [](void*) {});
53     }
54 
ClearWeakARKTS_Global_55     void ClearWeak()
56     {
57         if (isDisposed) {
58             return;
59         }
60         if (ref.IsEmpty() || !ref.IsWeak()) {
61             return;
62         }
63         ref.ClearWeak();
64     }
65 
GetValueARKTS_Global_66     ARKTS_Value GetValue() const
67     {
68         if (isDisposed) {
69             return ARKTS_CreateUndefined();
70         }
71         auto result = BIT_CAST(ref, const Local<JSValueRef>);
72         return ARKTS_FromHandle(result);
73     }
74 
IsAliveARKTS_Global_75     bool IsAlive() const
76     {
77         return !isDisposed && !ref.IsEmpty();
78     }
79 
SetDisposedARKTS_Global_80     void SetDisposed()
81     {
82         isDisposed = true;
83     }
84 
85 private:
86     Global<JSValueRef> ref;
87     bool isDisposed;
88 };
89 
90 namespace {
91 class __attribute__((capability("mutex"))) SpinLock final {
92 public:
Acquire()93     void Acquire() __attribute__((acquire_capability()))
94     {
95         bool locked = false;
96         while (!isLocked_.compare_exchange_strong(locked, true)) {
97             locked = false;
98         }
99     }
100 
Release()101     void Release() __attribute__((release_capability()))
102     {
103         isLocked_ = false;
104     }
105 private:
106     std::atomic<bool> isLocked_ {false};
107 };
108 
109 class GlobalManager {
110 public:
111     static void Dispose(EcmaVM* vm, uintptr_t handle);
112     static void AsyncDisposer(ARKTS_Env env, void* data);
113     static void AddManager(ARKTS_Env env);
114     static void RemoveManager(ARKTS_Env env);
115 
116     explicit GlobalManager(EcmaVM* vm);
117     ~GlobalManager();
118 
119 private:
120     static SpinLock managersMutex_;
121     static std::unordered_map<EcmaVM*, GlobalManager*> managers_ __attribute__((guarded_by(managersMutex_)));
122 
123 private:
124     EcmaVM* vm_;
125     bool onSchedule_;
126     SpinLock handleMutex_ {};
127     std::vector<uintptr_t> handlesToDispose_ __attribute__((guarded_by(handleMutex_))) {};
128 };
129 
130 std::unordered_map<EcmaVM*, GlobalManager*> GlobalManager::managers_;
131 SpinLock GlobalManager::managersMutex_;
132 
GlobalManager(EcmaVM * vm)133 GlobalManager::GlobalManager(EcmaVM* vm)
134 {
135     onSchedule_ = false;
136     vm_ = vm;
137 }
138 
AsyncDisposer(ARKTS_Env env,void * data)139 void GlobalManager::AsyncDisposer(ARKTS_Env env, void* data)
140 {
141     GlobalManager* current = nullptr;
142     managersMutex_.Acquire();
143     auto exist = managers_.find(reinterpret_cast<EcmaVM*>(data));
144     if (exist != managers_.end()) {
145         current = exist->second;
146     }
147     managersMutex_.Release();
148     if (!current || data != current) {
149         return;
150     }
151     auto manager = (GlobalManager*)data;
152     std::vector<uintptr_t> toDispose;
153     manager->handleMutex_.Acquire();
154     manager->onSchedule_ = false;
155     std::swap(toDispose, manager->handlesToDispose_);
156     manager->handleMutex_.Release();
157 
158     for (auto handle : toDispose) {
159         auto global = P_CAST(handle, ARKTS_Global);
160         delete global;
161     }
162 }
163 
Dispose(EcmaVM * vm,uintptr_t handle)164 void GlobalManager::Dispose(EcmaVM* vm, uintptr_t handle)
165 {
166     GlobalManager* manager = nullptr;
167     managersMutex_.Acquire();
168     auto itor = managers_.find(vm);
169     if (itor != managers_.end()) {
170         manager = itor->second;
171     }
172     managersMutex_.Release();
173     if (!manager) {
174         // JSRuntime is disposed, still need to delete c++ object.
175         auto global = reinterpret_cast<ARKTS_Global_*>(handle);
176         // JSRuntime is disposed, can not FreeGlobal by now, set disposed to skip FreeGlobal.
177         global->SetDisposed();
178         delete global;
179         return;
180     }
181     bool needSchedule = false;
182     manager->handleMutex_.Acquire();
183     manager->handlesToDispose_.push_back(handle);
184     if (!manager->onSchedule_) {
185         manager->onSchedule_ = needSchedule = true;
186     }
187     manager->handleMutex_.Release();
188     if (needSchedule) {
189         ARKTSInner_CreateAsyncTask(P_CAST(vm, ARKTS_Env), AsyncDisposer, manager);
190     }
191 }
192 
AddManager(ARKTS_Env env)193 void GlobalManager::AddManager(ARKTS_Env env)
194 {
195     auto vm = reinterpret_cast<EcmaVM*>(env);
196     auto manager = new GlobalManager(vm);
197     managersMutex_.Acquire();
198     auto result = managers_.try_emplace(vm, manager);
199     managersMutex_.Release();
200     if (!result.second) {
201         delete manager;
202     }
203 }
204 
RemoveManager(ARKTS_Env env)205 void GlobalManager::RemoveManager(ARKTS_Env env)
206 {
207     auto vm = reinterpret_cast<EcmaVM*>(env);
208     GlobalManager* current = nullptr;
209     managersMutex_.Acquire();
210     auto itor = managers_.find(vm);
211     if (itor != managers_.end()) {
212         current = itor->second;
213         managers_.erase(itor);
214     }
215     managersMutex_.Release();
216     if (current) {
217         delete current;
218     }
219 }
220 
~GlobalManager()221 GlobalManager::~GlobalManager()
222 {
223     std::vector<uintptr_t> toDispose;
224     handleMutex_.Acquire();
225     std::swap(toDispose, handlesToDispose_);
226     handleMutex_.Release();
227     for (auto p : toDispose) {
228         auto global = reinterpret_cast<ARKTS_Global_*>(p);
229         global->SetDisposed();
230     }
231 }
232 }
233 
234 // assume value is object
ARKTS_CreateGlobal(ARKTS_Env env,ARKTS_Value value)235 ARKTS_Global ARKTS_CreateGlobal(ARKTS_Env env, ARKTS_Value value)
236 {
237     ARKTS_ASSERT_P(env, "env is null");
238     ARKTS_ASSERT_P(ARKTS_IsHeapObject(value), "value is not heap object");
239 
240     auto vm = P_CAST(env, EcmaVM*);
241     auto handle = BIT_CAST(value, Local<JSValueRef>);
242     auto result = new ARKTS_Global_(vm, handle);
243 
244     return P_CAST(result, ARKTS_Global);
245 }
246 
ARKTS_GetGlobalValue(ARKTS_Global global)247 ARKTS_Value ARKTS_GetGlobalValue(ARKTS_Global global)
248 {
249     ARKTS_ASSERT_P(global, "global is null");
250 
251     return global->GetValue();
252 }
253 
254 /**
255  * cj destructor called in isolate thread, may crash js GC.
256  * store the handle to a table, do dispose at js thread on idle
257  */
ARKTS_DisposeGlobal(ARKTS_Env env,ARKTS_Global global)258 void ARKTS_DisposeGlobal(ARKTS_Env env, ARKTS_Global global)
259 {
260     ARKTS_ASSERT_V(env, "env is null");
261     ARKTS_ASSERT_V(global, "global is null");
262     GlobalManager::Dispose(P_CAST(env, EcmaVM*), P_CAST(global, uintptr_t));
263 }
264 
ARKTS_DisposeGlobalSync(ARKTS_Env env,ARKTS_Global global)265 void ARKTS_DisposeGlobalSync(ARKTS_Env env, ARKTS_Global global)
266 {
267     ARKTS_ASSERT_V(env, "env is null");
268     ARKTS_ASSERT_V(global, "handle is null");
269 
270     delete global;
271 }
272 
ARKTS_GlobalSetWeak(ARKTS_Env env,ARKTS_Global global)273 void ARKTS_GlobalSetWeak(ARKTS_Env env, ARKTS_Global global)
274 {
275     ARKTS_ASSERT_V(env, "env is null");
276     ARKTS_ASSERT_V(global, "global is null");
277     global->SetWeak();
278 }
279 
ARKTS_GlobalClearWeak(ARKTS_Env env,ARKTS_Global global)280 void ARKTS_GlobalClearWeak(ARKTS_Env env, ARKTS_Global global)
281 {
282     ARKTS_ASSERT_V(env, "env is null");
283     ARKTS_ASSERT_V(global, "global is null");
284     global->ClearWeak();
285 }
286 
287 constexpr uint64_t GLOBAL_TAG = 0b11111ULL << 48;
288 constexpr uint64_t GLOBAL_MASK = 0x0000'FFFF'FFFF'FFFF;
289 
ARKTS_GlobalToValue(ARKTS_Env env,ARKTS_Global global)290 ARKTS_Value ARKTS_GlobalToValue(ARKTS_Env env, ARKTS_Global global)
291 {
292     ARKTS_ASSERT_P(env, "env is null");
293     ARKTS_ASSERT_P(global, "global is null");
294 
295     auto value = reinterpret_cast<uint64_t>(global) & GLOBAL_MASK;
296     value = value | GLOBAL_TAG;
297     auto dValue = static_cast<double>(value);
298     return ARKTS_CreateF64(dValue);
299 }
300 
ARKTS_GlobalFromValue(ARKTS_Env env,ARKTS_Value value)301 ARKTS_Global ARKTS_GlobalFromValue(ARKTS_Env env, ARKTS_Value value)
302 {
303     ARKTS_ASSERT_P(env, "env is null");
304     ARKTS_ASSERT_P(ARKTS_IsNumber(value), "value is a number");
305 
306     auto dValue = ARKTS_GetValueNumber(value);
307     auto iValue = static_cast<uint64_t>(dValue);
308     ARKTS_ASSERT_P((iValue & GLOBAL_TAG) == GLOBAL_TAG, "invalid tag value");
309     iValue = iValue & GLOBAL_MASK;
310     return BIT_CAST(iValue, ARKTS_Global);
311 }
312 
ARKTS_GlobalIsAlive(ARKTS_Env env,ARKTS_Global global)313 bool ARKTS_GlobalIsAlive(ARKTS_Env env, ARKTS_Global global)
314 {
315     ARKTS_ASSERT_F(env, "env is null");
316     ARKTS_ASSERT_F(global, "global is null");
317 
318     return global->IsAlive();
319 }
320 
ARKTS_InitGlobalManager(ARKTS_Env env)321 void ARKTS_InitGlobalManager(ARKTS_Env env)
322 {
323     GlobalManager::AddManager(env);
324 }
325 
ARKTS_RemoveGlobalManager(ARKTS_Env env)326 void ARKTS_RemoveGlobalManager(ARKTS_Env env)
327 {
328     GlobalManager::RemoveManager(env);
329 }