• 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 "jsvm_reference.h"
17 
18 #include "js_native_api_v8.h"
19 
20 namespace v8impl {
21 namespace {
22 // In JavaScript, weak references can be created for object types (Object,
23 // Function, and external Object) and for local symbols that are created with
24 // the `Symbol` function call. Global symbols created with the `Symbol.for`
25 // method cannot be weak references because they are never collected.
26 // Currently, V8 has no API to detect if a symbol is local or global.
27 // Until we have a V8 API for it, we consider that all symbols can be weak.
CanBeHeldWeakly(v8::Local<v8::Value> value)28 inline bool CanBeHeldWeakly(v8::Local<v8::Value> value)
29 {
30     return value->IsObject() || value->IsSymbol();
31 }
32 } // namespace
33 
34 // RefTracker
Link(RefList * list)35 inline void RefTracker::Link(RefList* list)
36 {
37     DCHECK(list != nullptr);
38     prev = list;
39     next = list->next;
40     if (next != nullptr) {
41         next->prev = this;
42     }
43     list->next = this;
44 }
45 
FinalizeAll(RefList * list)46 void RefTracker::FinalizeAll(RefList* list)
47 {
48     while (list->next != nullptr) {
49         list->next->Finalize();
50     }
51 }
52 
Finalize()53 void RefTracker::Finalize()
54 {
55     UNREACHABLE("Finalize need to be realized");
56 }
57 
58 // UserReference
New(JSVM_Env env,v8::Local<v8::Value> value,uint32_t initialRefcount)59 UserReference* UserReference::New(JSVM_Env env, v8::Local<v8::Value> value, uint32_t initialRefcount)
60 {
61     auto ref = new UserReference(env, value, true, initialRefcount);
62 
63     return ref;
64 }
65 
NewData(JSVM_Env env,v8::Local<v8::Data> value,uint32_t initialRefcount)66 UserReference* UserReference::NewData(JSVM_Env env, v8::Local<v8::Data> value, uint32_t initialRefcount)
67 {
68     auto ref = new UserReference(env, value, false, initialRefcount);
69 
70     return ref;
71 }
72 
UserReference(JSVM_Env env,v8::Local<v8::Data> value,bool isValue,uint32_t initialRefcount)73 UserReference::UserReference(JSVM_Env env, v8::Local<v8::Data> value, bool isValue, uint32_t initialRefcount)
74     : persistent(env->isolate, value), isValue(isValue), env(env), refcount(initialRefcount),
75       canBeWeak(isValue && CanBeHeldWeakly(value.As<v8::Value>()))
76 {
77     if (refcount == 0) {
78         SetWeak();
79     }
80 
81     Link(&env->userReferenceList);
82 }
83 
~UserReference()84 UserReference::~UserReference()
85 {
86     persistent.Reset();
87     Unlink();
88 }
89 
Finalize()90 void UserReference::Finalize()
91 {
92     persistent.Reset();
93     Unlink();
94 }
95 
Get()96 v8::Local<v8::Value> UserReference::Get()
97 {
98     DCHECK(isValue);
99     if (persistent.IsEmpty()) {
100         return v8::Local<v8::Value>();
101     } else {
102         return v8::Local<v8::Data>::New(env->isolate, persistent).As<v8::Value>();
103     }
104 }
105 
GetData()106 v8::Local<v8::Data> UserReference::GetData()
107 {
108     if (persistent.IsEmpty()) {
109         return v8::Local<v8::Data>();
110     } else {
111         return v8::Local<v8::Data>::New(env->isolate, persistent);
112     }
113 }
114 
SetWeak()115 void UserReference::SetWeak()
116 {
117     if (canBeWeak) {
118         persistent.SetWeak();
119     } else {
120         persistent.Reset();
121     }
122 }
123 
Ref()124 uint32_t UserReference::Ref()
125 {
126     // If persistent is cleared by GC, return 0 unconditionally.
127     if (persistent.IsEmpty()) {
128         return 0;
129     }
130 
131     if (++refcount == 1) {
132         // If persistent can not be weak, it will be cleared in SetWeak().
133         DCHECK(canBeWeak);
134         persistent.ClearWeak();
135     }
136 
137     return refcount;
138 }
139 
Unref()140 uint32_t UserReference::Unref()
141 {
142     // If persistent is cleared by GC, return 0 unconditionally.
143     if (persistent.IsEmpty() || refcount == 0) {
144         return 0;
145     }
146 
147     if (--refcount == 0) {
148         SetWeak();
149     }
150 
151     return refcount;
152 }
153 
RefCount()154 uint32_t UserReference::RefCount()
155 {
156     return refcount;
157 }
158 
159 // FinalizerTracker
New(JSVM_Env env,JSVM_Finalize cb,void * finalizeData,void * finalizeHint)160 FinalizerTracker* FinalizerTracker::New(JSVM_Env env, JSVM_Finalize cb, void* finalizeData, void* finalizeHint)
161 {
162     return new FinalizerTracker(env, cb, finalizeData, finalizeHint);
163 }
164 
FinalizerTracker(JSVM_Env env,JSVM_Finalize cb,void * data,void * hint)165 FinalizerTracker::FinalizerTracker(JSVM_Env env, JSVM_Finalize cb, void* data, void* hint)
166     : env(env), cb(cb), data(data), hint(hint)
167 {
168     Link(&env->finalizerList);
169 }
170 
~FinalizerTracker()171 FinalizerTracker::~FinalizerTracker()
172 {
173     Unlink();
174 }
175 
ResetFinalizer()176 void FinalizerTracker::ResetFinalizer()
177 {
178     cb = nullptr;
179     data = nullptr;
180     hint = nullptr;
181 }
182 
CallFinalizer()183 void FinalizerTracker::CallFinalizer()
184 {
185     if (!cb) {
186         return;
187     }
188 
189     JSVM_Finalize cbTemp = cb;
190     void* dataTemp = data;
191     void* hintTemp = hint;
192     ResetFinalizer();
193 
194     if (!env) {
195         cbTemp(env, dataTemp, hintTemp);
196     } else {
197         env->CallIntoModule([&](JSVM_Env env) { cbTemp(env, dataTemp, hintTemp); });
198     }
199 }
200 
Finalize()201 void FinalizerTracker::Finalize()
202 {
203     CallFinalizer();
204     delete this;
205 }
206 
RuntimeReference(JSVM_Env env,v8::Local<v8::Value> value,JSVM_Finalize cb,void * data,void * hint)207 RuntimeReference::RuntimeReference(JSVM_Env env, v8::Local<v8::Value> value, JSVM_Finalize cb, void* data, void* hint)
208     : FinalizerTracker(env, cb, data, hint), persistent(env->isolate, value)
209 {
210     DCHECK(CanBeHeldWeakly(value));
211 }
212 
New(JSVM_Env env,v8::Local<v8::Value> value,void * data)213 RuntimeReference* RuntimeReference::New(JSVM_Env env, v8::Local<v8::Value> value, void* data)
214 {
215     auto* ref = new RuntimeReference(env, value, nullptr, data, nullptr);
216     // Delete self in first pass callback
217     ref->SetWeak(false);
218 
219     return ref;
220 }
221 
New(JSVM_Env env,v8::Local<v8::Value> value,JSVM_Finalize cb,void * data,void * hint)222 RuntimeReference* RuntimeReference::New(JSVM_Env env,
223                                         v8::Local<v8::Value> value,
224                                         JSVM_Finalize cb,
225                                         void* data,
226                                         void* hint)
227 {
228     auto* ref = new RuntimeReference(env, value, cb, data, hint);
229     // Need second pass callback to call finalizer
230     ref->SetWeak(cb != nullptr);
231 
232     return ref;
233 }
234 
DeleteReference(RuntimeReference * ref)235 void RuntimeReference::DeleteReference(RuntimeReference* ref)
236 {
237     // If reference is not added into first pass callbacks, delete this direct.
238     if (ref->persistent.IsWeak()) {
239         delete ref;
240         return;
241     }
242 
243     // If reference is added into first pass callbacks, reset finalizer function.
244     ref->ResetFinalizer();
245 }
246 
SetWeak(bool needSecondPass)247 inline void RuntimeReference::SetWeak(bool needSecondPass)
248 {
249     if (needSecondPass) {
250         persistent.SetWeak(this, FirstPassCallback, v8::WeakCallbackType::kParameter);
251     } else {
252         persistent.SetWeak(this, FirstPassCallbackWithoutFinalizer, v8::WeakCallbackType::kParameter);
253     }
254 }
255 
FirstPassCallback(const v8::WeakCallbackInfo<RuntimeReference> & data)256 void RuntimeReference::FirstPassCallback(const v8::WeakCallbackInfo<RuntimeReference>& data)
257 {
258     RuntimeReference* reference = data.GetParameter();
259 
260     reference->persistent.Reset();
261     data.SetSecondPassCallback(RuntimeReference::SecondPassCallback);
262 }
263 
SecondPassCallback(const v8::WeakCallbackInfo<RuntimeReference> & data)264 void RuntimeReference::SecondPassCallback(const v8::WeakCallbackInfo<RuntimeReference>& data)
265 {
266     RuntimeReference* reference = data.GetParameter();
267 
268     reference->Finalize();
269 }
270 
FirstPassCallbackWithoutFinalizer(const v8::WeakCallbackInfo<RuntimeReference> & data)271 void RuntimeReference::FirstPassCallbackWithoutFinalizer(const v8::WeakCallbackInfo<RuntimeReference>& data)
272 {
273     RuntimeReference* reference = data.GetParameter();
274 
275     reference->persistent.Reset();
276     delete reference;
277 }
278 
279 } // namespace v8impl