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
19 #include <atomic>
20 #include <unordered_map>
21 #include <vector>
22
23 using namespace panda;
24 using namespace panda::ecmascript;
25
26 namespace {
27 class __attribute__((capability("mutex"))) SpinLock final {
28 public:
Acquire()29 void Acquire() __attribute__((acquire_capability()))
30 {
31 bool locked = false;
32 while (!isLocked_.compare_exchange_strong(locked, true)) {
33 locked = false;
34 }
35 }
36
Release()37 void Release() __attribute__((release_capability()))
38 {
39 isLocked_ = false;
40 }
41 private:
42 std::atomic<bool> isLocked_ {false};
43 };
44
45 class GlobalManager {
46 public:
47 static void Dispose(EcmaVM* vm, uintptr_t handle);
48 static void AsyncDisposer(ARKTS_Env env, void* data);
49
50 explicit GlobalManager(EcmaVM* vm);
51
52 private:
53 static SpinLock managersMutex_;
54 static std::unordered_map<EcmaVM*, GlobalManager> managers_ __attribute__((guarded_by(managersMutex_)));
55
56 private:
57 EcmaVM* vm_;
58 bool onSchedule_;
59 SpinLock handleMutex_ {};
60 std::vector<uintptr_t> handlesToDispose_ __attribute__((guarded_by(handleMutex_))) {};
61 };
62
63 std::unordered_map<EcmaVM*, GlobalManager> GlobalManager::managers_;
64 SpinLock GlobalManager::managersMutex_;
65
GlobalManager(EcmaVM * vm)66 GlobalManager::GlobalManager(EcmaVM* vm)
67 {
68 onSchedule_ = false;
69 vm_ = vm;
70 }
71
AsyncDisposer(ARKTS_Env env,void * data)72 void GlobalManager::AsyncDisposer(ARKTS_Env env, void* data)
73 {
74 auto manager = (GlobalManager*)data;
75 std::vector<uintptr_t> toDispose;
76 manager->handleMutex_.Acquire();
77 manager->onSchedule_ = false;
78 std::swap(toDispose, manager->handlesToDispose_);
79 manager->handleMutex_.Release();
80
81 for (auto handle : toDispose) {
82 auto global = P_CAST(handle, Global<JSValueRef>*);
83 delete global;
84 }
85 }
86
Dispose(EcmaVM * vm,uintptr_t handle)87 void GlobalManager::Dispose(EcmaVM* vm, uintptr_t handle)
88 {
89 GlobalManager* manager;
90 managersMutex_.Acquire();
91 manager = &managers_.try_emplace(vm, vm).first->second;
92 managersMutex_.Release();
93 bool needSchedule = false;
94 manager->handleMutex_.Acquire();
95 manager->handlesToDispose_.push_back(handle);
96 if (!manager->onSchedule_) {
97 manager->onSchedule_ = needSchedule = true;
98 }
99 manager->handleMutex_.Release();
100 if (needSchedule) {
101 ARKTSInner_CreateAsyncTask(P_CAST(vm, ARKTS_Env), AsyncDisposer, manager);
102 }
103 }
104 }
105
106 // assume value is object
ARKTS_CreateGlobal(ARKTS_Env env,ARKTS_Value value)107 ARKTS_Global ARKTS_CreateGlobal(ARKTS_Env env, ARKTS_Value value)
108 {
109 ARKTS_ASSERT_P(env, "env is null");
110 ARKTS_ASSERT_P(ARKTS_IsHeapObject(value), "value is not heap object");
111
112 auto vm = P_CAST(env, EcmaVM*);
113 auto handle = BIT_CAST(value, Local<JSValueRef>);
114 auto result = new Global<JSValueRef>(vm, handle);
115
116 return P_CAST(result, ARKTS_Global);
117 }
118
119 /**
120 * no actual action about this convert, the data is in the same form with JSHandle,
121 * it does not mean global is equal to local, the memory they are allocated are controlled by different system,
122 * have no idea the consequence of NativeScope escape a global variable
123 */
ARKTS_GetGlobalValue(ARKTS_Global global)124 ARKTS_Value ARKTS_GetGlobalValue(ARKTS_Global global)
125 {
126 ARKTS_ASSERT_P(global, "global is null");
127
128 auto result = *P_CAST(global, Local<JSValueRef>*);
129
130 return ARKTS_FromHandle(result);
131 }
132
133 /**
134 * cj destructor called in isolate thread, may crash js GC.
135 * store the handle to a table, do dispose at js thread on idle
136 */
ARKTS_DisposeGlobal(ARKTS_Env env,ARKTS_Global global)137 void ARKTS_DisposeGlobal(ARKTS_Env env, ARKTS_Global global)
138 {
139 ARKTS_ASSERT_V(env, "env is null");
140 ARKTS_ASSERT_V(global, "global is null");
141 GlobalManager::Dispose(P_CAST(env, EcmaVM*), P_CAST(global, uintptr_t));
142 }