• 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 #ifndef JSVM_ENV_H
17 #define JSVM_ENV_H
18 #include <functional>
19 #include <list>
20 #include <mutex>
21 #include <vector>
22 
23 #include "jsvm_dfx.h"
24 #include "jsvm_inspector_agent.h"
25 #include "jsvm_reference.h"
26 #include "jsvm_types.h"
27 #include "jsvm_util.h"
28 #include "memory_manager.h"
29 #include "type_conversion.h"
30 #include "v8.h"
31 
32 inline JSVM_Status ClearLastError(JSVM_Env env);
33 
34 struct JSVM_Env__ final {
35 public:
JSVM_Env__final36     explicit JSVM_Env__(v8::Local<v8::Context> context, int32_t apiVersion)
37         : isolate(context->GetIsolate()), contextPersistent(isolate, context), apiVersion(apiVersion)
38     {
39         ClearLastError(this);
40     }
41 
42     // Constructor for creating partial env.
43     explicit JSVM_Env__(v8::Isolate* isolate, int32_t apiVersion);
44 
GetVersionfinal45     int32_t GetVersion()
46     {
47         return apiVersion;
48     }
49 
50     using Callback = std::function<void(JSVM_Env)>;
51 
RequestInterruptfinal52     inline void RequestInterrupt(Callback cb)
53     {
54         {
55             const std::lock_guard<std::mutex> lock(messageQueueMutex);
56             messageQueue.emplace_back(std::move(cb));
57         }
58         isolate->RequestInterrupt(
59             [](v8::Isolate* isolate, void* data) { static_cast<JSVM_Env__*>(data)->RunAndClearInterrupts(); }, this);
60     }
61 
62     void RunAndClearInterrupts();
63 
GetInspectorAgentfinal64     jsvm::InspectorAgent* GetInspectorAgent()
65     {
66         return inspectorAgent;
67     }
68 
69     v8::Platform* platform();
70 
contextfinal71     inline v8::Local<v8::Context> context() const
72     {
73         return v8impl::PersistentToLocal::Strong(contextPersistent);
74     }
75 
CanCallIntoJSfinal76     bool CanCallIntoJS() const
77     {
78         return true;
79     }
80 
HandleThrowfinal81     static inline void HandleThrow(JSVM_Env env, v8::Local<v8::Value> value)
82     {
83         if (env->IsTerminatedOrTerminating()) {
84             return;
85         }
86         env->isolate->ThrowException(value);
87     }
88 
89     // i.e. whether v8 exited or is about to exit
IsTerminatedOrTerminatingfinal90     inline bool IsTerminatedOrTerminating()
91     {
92         return this->isolate->IsExecutionTerminating() || !CanCallIntoJS();
93     }
94 
95     // v8 uses a special exception to indicate termination, the
96     // `handle_exception` callback should identify such case using
97     // IsTerminatedOrTerminating() before actually handle the exception
98     template<typename T, typename U = decltype(HandleThrow)>
99     inline void CallIntoModule(T&& call, U&& handle_exception = HandleThrow)
100     {
101         int openHandleScopesBefore = openHandleScopes;
102         int openCallbackScopesBefore = openCallbackScopes;
103         ClearLastError(this);
104         call(this);
105         CHECK_EQ(openHandleScopes, openHandleScopesBefore);
106         CHECK_EQ(openCallbackScopes, openCallbackScopesBefore);
107         if (!lastException.IsEmpty()) {
108             handle_exception(this, lastException.Get(this->isolate));
109             lastException.Reset();
110         }
111     }
112 
113     // Call finalizer immediately.
CallFinalizerfinal114     void CallFinalizer(JSVM_Finalize cb, void* data, void* hint)
115     {
116         v8::HandleScope handleScope(isolate);
117         CallIntoModule([&](JSVM_Env env) { cb(env, data, hint); });
118     }
119 
120     void DeleteMe();
121 
CheckGCAccessfinal122     void CheckGCAccess()
123     {
124         if (inGcFinalizer) {
125             jsvm::OnFatalError(nullptr, "Finalizer is calling a function that may affect GC state.\n"
126                                         "The finalizers are run directly from GC and must not affect GC "
127                                         "state.\n"
128                                         "Use `node_api_post_finalizer` from inside of the finalizer to work "
129                                         "around this issue.\n"
130                                         "It schedules the call as a new task in the event loop.");
131         }
132     }
133 
134     template<typename T>
135     JSVM_Script_Data__* NewJsvmData(T srcPtr, JSVM_Script_Data__::DataType type = JSVM_Script_Data__::kJsvmScript)
136     {
137         if (dataStack.size() == 0 || openHandleScopes != dataStack.back().first) {
138             dataStack.emplace_back(openHandleScopes, std::list<JSVM_Script_Data__*>());
139         }
140         auto newData = new JSVM_Script_Data__(srcPtr, false, type);
141         dataStack.back().second.push_back(newData);
142         return newData;
143     }
144 
ReleaseJsvmDatafinal145     void ReleaseJsvmData()
146     {
147         if (dataStack.size() == 0 || openHandleScopes != dataStack.back().first) {
148             return;
149         }
150         for (auto data : dataStack.back().second) {
151             if (!data->isGlobal) {
152                 delete data;
153             }
154         }
155         dataStack.pop_back();
156     }
157 
RetainJsvmDatafinal158     void RetainJsvmData(JSVM_Script_Data__* data)
159     {
160         for (auto iter = dataStack.rbegin(); iter != dataStack.rend(); ++iter) {
161             auto& current = iter->second;
162             auto globalData = std::find(current.begin(), current.end(), data);
163             if (globalData != current.end()) {
164                 current.erase(globalData);
165                 break;
166             }
167         }
168     }
169 
CreateScopeTrackerfinal170     void CreateScopeTracker()
171     {
172         scopeTracker = new jsvm::ScopeLifecycleTracker();
173     }
174 
GetScopeTrackerfinal175     jsvm::ScopeLifecycleTracker* GetScopeTracker()
176     {
177         if (scopeTracker == nullptr) {
178             CreateScopeTracker();
179         }
180         return scopeTracker;
181     }
182 
183     // Shortcut for context()->GetIsolate()
184     v8::Isolate* const isolate;
185     v8impl::Persistent<v8::Context> contextPersistent;
186 
187     // Error info and execption
188     v8impl::Persistent<v8::Value> lastException;
189 
190     // We store references in two different lists, depending on whether they have
191     // `JSVM_Finalizer` callbacks, because we must first finalize the ones that
192     // have such a callback. See `~JSVM_Env__()` above for details.
193     v8impl::RefList userReferenceList;
194     v8impl::RefList finalizerList;
195 
196     JSVM_ExtendedErrorInfo lastError;
197 
198     // Store v8::Data
199     std::vector<std::pair<int, std::list<JSVM_Script_Data__*>>> dataStack;
200 
201     // Store external instance data
202     void* instanceData = nullptr;
203 
204     // Store v8::Locker
205     v8::Locker* locker = nullptr;
206 
207     using ScopeMemoryManager = MemoryChunkList<
208         jsvm::MaxSize<v8impl::EscapableHandleScopeWrapper, v8impl::HandleScopeWrapper, v8::Context::Scope>()>;
209     ScopeMemoryManager scopeMemoryManager;
210 
211     int32_t apiVersion;
212 
213     int openHandleScopes = 0;
214     int openCallbackScopes = 0;
215     bool inGcFinalizer = false;
216     uint32_t debugFlags = 0;
217 
218 private:
219     // Used for inspector
220     jsvm::InspectorAgent* inspectorAgent;
221     std::mutex messageQueueMutex;
222     std::vector<Callback> messageQueue;
223     // Used for scopeInfo
224     jsvm::ScopeLifecycleTracker* scopeTracker = nullptr;
225 
226 protected:
227     // Should not be deleted directly. Delete with `JSVM_Env__::DeleteMe()`
228     // instead.
229     ~JSVM_Env__() = default;
230 };
231 
ClearLastError(JSVM_Env env)232 inline JSVM_Status ClearLastError(JSVM_Env env)
233 {
234     env->lastError.errorCode = JSVM_OK;
235     env->lastError.engineErrorCode = 0;
236     env->lastError.engineReserved = nullptr;
237     env->lastError.errorMessage = nullptr;
238     return JSVM_OK;
239 }
240 
241 #endif