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 }