• 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 <mutex>
20 #include <vector>
21 
22 #include "jsvm_dfx.h"
23 #include "jsvm_inspector_agent.h"
24 #include "jsvm_reference.h"
25 #include "jsvm_types.h"
26 #include "jsvm_util.h"
27 #include "type_conversion.h"
28 #include "v8.h"
29 
30 inline JSVM_Status ClearLastError(JSVM_Env env);
31 
32 struct JSVM_Env__ final {
33 public:
JSVM_Env__final34     explicit JSVM_Env__(v8::Local<v8::Context> context, int32_t apiVersion)
35         : isolate(context->GetIsolate()), contextPersistent(isolate, context), apiVersion(apiVersion)
36     {
37         ClearLastError(this);
38     }
39 
40     // Constructor for creating partial env.
41     explicit JSVM_Env__(v8::Isolate* isolate, int32_t apiVersion);
42 
GetVersionfinal43     int32_t GetVersion()
44     {
45         return apiVersion;
46     }
47 
48     using Callback = std::function<void(JSVM_Env)>;
49 
RequestInterruptfinal50     inline void RequestInterrupt(Callback cb)
51     {
52         {
53             const std::lock_guard<std::mutex> lock(messageQueueMutex);
54             messageQueue.emplace_back(std::move(cb));
55         }
56         isolate->RequestInterrupt(
57             [](v8::Isolate* isolate, void* data) { static_cast<JSVM_Env__*>(data)->RunAndClearInterrupts(); }, this);
58     }
59 
60     void RunAndClearInterrupts();
61 
GetInspectorAgentfinal62     jsvm::InspectorAgent* GetInspectorAgent()
63     {
64         return inspectorAgent;
65     }
66 
67     v8::Platform* platform();
68 
contextfinal69     inline v8::Local<v8::Context> context() const
70     {
71         return v8impl::PersistentToLocal::Strong(contextPersistent);
72     }
73 
CanCallIntoJSfinal74     bool CanCallIntoJS() const
75     {
76         return true;
77     }
78 
HandleThrowfinal79     static inline void HandleThrow(JSVM_Env env, v8::Local<v8::Value> value)
80     {
81         if (env->IsTerminatedOrTerminating()) {
82             return;
83         }
84         env->isolate->ThrowException(value);
85     }
86 
87     // i.e. whether v8 exited or is about to exit
IsTerminatedOrTerminatingfinal88     inline bool IsTerminatedOrTerminating()
89     {
90         return this->isolate->IsExecutionTerminating() || !CanCallIntoJS();
91     }
92 
93     // v8 uses a special exception to indicate termination, the
94     // `handle_exception` callback should identify such case using
95     // IsTerminatedOrTerminating() before actually handle the exception
96     template<typename T, typename U = decltype(HandleThrow)>
97     inline void CallIntoModule(T&& call, U&& handle_exception = HandleThrow)
98     {
99         int openHandleScopesBefore = openHandleScopes;
100         int openCallbackScopesBefore = openCallbackScopes;
101         ClearLastError(this);
102         call(this);
103         CHECK_EQ(openHandleScopes, openHandleScopesBefore);
104         CHECK_EQ(openCallbackScopes, openCallbackScopesBefore);
105         if (!lastException.IsEmpty()) {
106             handle_exception(this, lastException.Get(this->isolate));
107             lastException.Reset();
108         }
109     }
110 
111     // Call finalizer immediately.
CallFinalizerfinal112     void CallFinalizer(JSVM_Finalize cb, void* data, void* hint)
113     {
114         v8::HandleScope handleScope(isolate);
115         CallIntoModule([&](JSVM_Env env) { cb(env, data, hint); });
116     }
117 
118     void DeleteMe();
119 
CheckGCAccessfinal120     void CheckGCAccess()
121     {
122         if (inGcFinalizer) {
123             jsvm::OnFatalError(nullptr, "Finalizer is calling a function that may affect GC state.\n"
124                                         "The finalizers are run directly from GC and must not affect GC "
125                                         "state.\n"
126                                         "Use `node_api_post_finalizer` from inside of the finalizer to work "
127                                         "around this issue.\n"
128                                         "It schedules the call as a new task in the event loop.");
129         }
130     }
131 
132     template<typename T>
133     JSVM_Script_Data__* NewJsvmData(T srcPtr, JSVM_Script_Data__::DataType type = JSVM_Script_Data__::kJsvmScript)
134     {
135         if (dataStack.empty() || openHandleScopes != dataStack.top().first) {
136             dataStack.emplace(openHandleScopes, std::vector<JSVM_Script_Data__*>());
137         }
138         auto newData = new JSVM_Script_Data__(srcPtr, false, type);
139         dataStack.top().second.push_back(newData);
140         return newData;
141     }
142 
ReleaseJsvmDatafinal143     void ReleaseJsvmData()
144     {
145         if (dataStack.empty() || openHandleScopes != dataStack.top().first) {
146             return;
147         }
148         for (auto data : dataStack.top().second) {
149             if (!data->isGlobal) {
150                 delete data;
151             }
152         }
153         dataStack.pop();
154     }
155 
156     // Shortcut for context()->GetIsolate()
157     v8::Isolate* const isolate;
158     v8impl::Persistent<v8::Context> contextPersistent;
159 
160     // Error info and execption
161     JSVM_ExtendedErrorInfo lastError;
162     v8impl::Persistent<v8::Value> lastException;
163 
164     // We store references in two different lists, depending on whether they have
165     // `JSVM_Finalizer` callbacks, because we must first finalize the ones that
166     // have such a callback. See `~JSVM_Env__()` above for details.
167     v8impl::RefList userReferenceList;
168     v8impl::RefList finalizerList;
169 
170     // Store v8::Data
171     std::stack<std::pair<int, std::vector<JSVM_Script_Data__*>>> dataStack;
172 
173     // Store external instance data
174     void* instanceData = nullptr;
175 
176     // Store v8::Locker
177     v8::Locker* locker = nullptr;
178 
179     int32_t apiVersion;
180 
181     int openHandleScopes = 0;
182     int openCallbackScopes = 0;
183     bool inGcFinalizer = false;
184 
185 private:
186     // Used for inspector
187     jsvm::InspectorAgent* inspectorAgent;
188     std::mutex messageQueueMutex;
189     std::vector<Callback> messageQueue;
190 
191 protected:
192     // Should not be deleted directly. Delete with `JSVM_Env__::DeleteMe()`
193     // instead.
194     ~JSVM_Env__() = default;
195 };
196 
ClearLastError(JSVM_Env env)197 inline JSVM_Status ClearLastError(JSVM_Env env)
198 {
199     env->lastError.errorCode = JSVM_OK;
200     env->lastError.engineErrorCode = 0;
201     env->lastError.engineReserved = nullptr;
202     env->lastError.errorMessage = nullptr;
203     return JSVM_OK;
204 }
205 
206 #endif