1 /*
2 * Copyright (c) 2023 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 "js_environment.h"
17
18 #include <chrono>
19
20 #include "console.h"
21 #include "ffrt.h"
22 #include "hilog_tag_wrapper.h"
23 #include "js_environment_impl.h"
24 #include "native_engine/impl/ark/ark_native_engine.h"
25 #include "uncaught_exception_callback.h"
26
27 namespace OHOS {
28 namespace JsEnv {
29 namespace {
30 static const std::string DEBUGGER = "@Debugger";
31 static const std::string NOT_INIT = "SourceMap is not initialized yet \n";
32 }
33
ConvertProfilerType(JsEnvironment::PROFILERTYPE type)34 static panda::DFXJSNApi::ProfilerType ConvertProfilerType(JsEnvironment::PROFILERTYPE type)
35 {
36 if (type == JsEnvironment::PROFILERTYPE::PROFILERTYPE_CPU) {
37 return panda::DFXJSNApi::ProfilerType::CPU_PROFILER;
38 } else {
39 return panda::DFXJSNApi::ProfilerType::HEAP_PROFILER;
40 }
41 }
42
JsEnvironment(std::unique_ptr<JsEnvironmentImpl> impl)43 JsEnvironment::JsEnvironment(std::unique_ptr<JsEnvironmentImpl> impl) : impl_(std::move(impl))
44 {}
45
~JsEnvironment()46 JsEnvironment::~JsEnvironment()
47 {
48 TAG_LOGD(AAFwkTag::JSENV, "called");
49
50 if (engine_ != nullptr) {
51 delete engine_;
52 engine_ = nullptr;
53 }
54
55 if (vm_ != nullptr) {
56 panda::JSNApi::DestroyJSVM(vm_);
57 vm_ = nullptr;
58 }
59 }
60
Initialize(const panda::RuntimeOption & pandaOption,void * jsEngine)61 bool JsEnvironment::Initialize(const panda::RuntimeOption& pandaOption, void* jsEngine)
62 {
63 TAG_LOGD(AAFwkTag::JSENV, "Js environment initialize");
64 vm_ = panda::JSNApi::CreateJSVM(pandaOption);
65 if (vm_ == nullptr) {
66 TAG_LOGE(AAFwkTag::JSENV, "Create vm failed");
67 return false;
68 }
69
70 engine_ = new ArkNativeEngine(vm_, jsEngine);
71 return true;
72 }
73
InitTimerModule()74 void JsEnvironment::InitTimerModule()
75 {
76 if (engine_ == nullptr) {
77 TAG_LOGE(AAFwkTag::JSENV, "Invalid native engine");
78 return;
79 }
80
81 if (impl_ != nullptr) {
82 impl_->InitTimerModule(engine_);
83 }
84 }
85
InitWorkerModule(std::shared_ptr<WorkerInfo> workerInfo)86 void JsEnvironment::InitWorkerModule(std::shared_ptr<WorkerInfo> workerInfo)
87 {
88 if (engine_ == nullptr) {
89 TAG_LOGE(AAFwkTag::JSENV, "Invalid native engine");
90 return;
91 }
92
93 if (impl_ != nullptr) {
94 impl_->InitWorkerModule(engine_, workerInfo);
95 }
96 }
97
InitSyscapModule()98 void JsEnvironment::InitSyscapModule()
99 {
100 if (impl_ != nullptr) {
101 impl_->InitSyscapModule();
102 }
103 }
104
PostTask(const std::function<void ()> & task,const std::string & name,int64_t delayTime)105 void JsEnvironment::PostTask(const std::function<void()>& task, const std::string& name, int64_t delayTime)
106 {
107 if (impl_ != nullptr) {
108 impl_->PostTask(task, name, delayTime);
109 }
110 }
111
PostSyncTask(const std::function<void ()> & task,const std::string & name)112 void JsEnvironment::PostSyncTask(const std::function<void()>& task, const std::string& name)
113 {
114 if (impl_ != nullptr) {
115 impl_->PostSyncTask(task, name);
116 }
117 }
118
RemoveTask(const std::string & name)119 void JsEnvironment::RemoveTask(const std::string& name)
120 {
121 if (impl_ != nullptr) {
122 impl_->RemoveTask(name);
123 }
124 }
125
InitSourceMap(const std::shared_ptr<JsEnv::SourceMapOperator> operatorObj)126 void JsEnvironment::InitSourceMap(const std::shared_ptr<JsEnv::SourceMapOperator> operatorObj)
127 {
128 sourceMapOperator_ = operatorObj;
129 if (engine_ == nullptr) {
130 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
131 return;
132 }
133
134 if (sourceMapOperator_ != nullptr && sourceMapOperator_->GetHasFile()) {
135 sourceMapOperator_->InitSourceMap();
136 } else {
137 sourceMapOperator_->SetInitStatus(InitStatus::EXECUTED_SUCCESSFULLY);
138 }
139
140 auto translateBySourceMapFunc = [&](const std::string& rawStack) -> std::string {
141 if (sourceMapOperator_ != nullptr && sourceMapOperator_->GetInitStatus()) {
142 return sourceMapOperator_->TranslateBySourceMap(rawStack);
143 } else {
144 return NOT_INIT + rawStack;
145 }
146 };
147 engine_->RegisterTranslateBySourceMap(translateBySourceMapFunc);
148
149 auto translateUrlBySourceMapFunc = [&](std::string& url, int& line, int& column, std::string& packageName) -> bool {
150 if (sourceMapOperator_ != nullptr && sourceMapOperator_->GetInitStatus()) {
151 return sourceMapOperator_->TranslateUrlPositionBySourceMap(url, line, column, packageName);
152 }
153 return false;
154 };
155 engine_->RegisterSourceMapTranslateCallback(translateUrlBySourceMapFunc);
156 }
157
RegisterUncaughtExceptionHandler(const JsEnv::UncaughtExceptionInfo & uncaughtExceptionInfo)158 void JsEnvironment::RegisterUncaughtExceptionHandler(const JsEnv::UncaughtExceptionInfo& uncaughtExceptionInfo)
159 {
160 if (engine_ == nullptr) {
161 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
162 return;
163 }
164
165 engine_->RegisterNapiUncaughtExceptionHandler(NapiUncaughtExceptionCallback(uncaughtExceptionInfo.uncaughtTask,
166 sourceMapOperator_, reinterpret_cast<napi_env>(engine_)));
167 }
168
LoadScript(const std::string & path,std::vector<uint8_t> * buffer,bool isBundle)169 bool JsEnvironment::LoadScript(const std::string& path, std::vector<uint8_t>* buffer, bool isBundle)
170 {
171 if (engine_ == nullptr) {
172 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
173 return false;
174 }
175
176 if (buffer == nullptr) {
177 return engine_->RunScriptPath(path.c_str());
178 }
179 auto start = std::chrono::high_resolution_clock::now();
180 bool ret = engine_->RunScriptBuffer(path.c_str(), *buffer, isBundle) != nullptr;
181 auto end = std::chrono::high_resolution_clock::now();
182 auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
183 TAG_LOGI(AAFwkTag::JSENV, "timing: %{public}lld", duration_ms);
184 return ret;
185 }
186
StartDebugger(std::string & option,uint32_t socketFd,bool isDebugApp)187 bool JsEnvironment::StartDebugger(
188 std::string& option, uint32_t socketFd, bool isDebugApp)
189 {
190 TAG_LOGD(AAFwkTag::JSENV, "call");
191 if (vm_ == nullptr) {
192 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
193 return false;
194 }
195 int32_t identifierId = ParseHdcRegisterOption(option);
196 if (identifierId == -1) {
197 TAG_LOGE(AAFwkTag::JSENV, "Abnormal parsing of tid results");
198 return false;
199 }
200 debugMode_ = panda::JSNApi::StartDebuggerForSocketPair(identifierId, socketFd);
201 return debugMode_;
202 }
203
StopDebugger()204 void JsEnvironment::StopDebugger()
205 {
206 if (vm_ == nullptr) {
207 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
208 return;
209 }
210
211 (void)panda::JSNApi::StopDebugger(vm_);
212 }
213
StopDebugger(std::string & option)214 void JsEnvironment::StopDebugger(std::string& option)
215 {
216 int32_t identifierId = ParseHdcRegisterOption(option);
217 if (identifierId == -1) {
218 TAG_LOGE(AAFwkTag::JSENV, "Abnormal parsing of tid results");
219 return;
220 }
221 panda::JSNApi::StopDebugger(identifierId);
222 }
223
InitConsoleModule()224 void JsEnvironment::InitConsoleModule()
225 {
226 if (engine_ == nullptr) {
227 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
228 return;
229 }
230
231 if (impl_ != nullptr) {
232 impl_->InitConsoleModule(engine_);
233 }
234 }
235
InitLoop(bool isStage)236 bool JsEnvironment::InitLoop(bool isStage)
237 {
238 if (engine_ == nullptr) {
239 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
240 return false;
241 }
242
243 if (impl_ != nullptr) {
244 impl_->InitLoop(engine_, isStage);
245 }
246 return true;
247 }
248
DeInitLoop()249 void JsEnvironment::DeInitLoop()
250 {
251 if (engine_ == nullptr) {
252 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
253 return;
254 }
255
256 if (impl_ != nullptr) {
257 impl_->DeInitLoop(engine_);
258 }
259 }
260
LoadScript(const std::string & path,uint8_t * buffer,size_t len,bool isBundle)261 bool JsEnvironment::LoadScript(const std::string& path, uint8_t* buffer, size_t len, bool isBundle)
262 {
263 if (engine_ == nullptr) {
264 TAG_LOGE(AAFwkTag::JSENV, "Invalid Native Engine");
265 return false;
266 }
267
268 auto start = std::chrono::high_resolution_clock::now();
269 bool ret = engine_->RunScriptBuffer(path, buffer, len, isBundle);
270 auto end = std::chrono::high_resolution_clock::now();
271 auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
272 TAG_LOGI(AAFwkTag::JSENV, "timing: %{public}lld", duration_ms);
273
274 return ret;
275 }
276
StartProfiler(const char * libraryPath,uint32_t instanceId,PROFILERTYPE profiler,int32_t interval,int tid,bool isDebugApp)277 void JsEnvironment::StartProfiler(const char* libraryPath, uint32_t instanceId, PROFILERTYPE profiler,
278 int32_t interval, int tid, bool isDebugApp)
279 {
280 if (vm_ == nullptr) {
281 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
282 return;
283 }
284
285 auto debuggerPostTask = [weak = weak_from_this()](std::function<void()>&& task) {
286 auto jsEnv = weak.lock();
287 if (jsEnv == nullptr) {
288 TAG_LOGE(AAFwkTag::JSENV, "JsEnv is invalid");
289 return;
290 }
291 jsEnv->PostTask(task, "JsEnvironment::StartProfiler");
292 };
293
294 panda::DFXJSNApi::ProfilerOption option;
295 option.libraryPath = libraryPath;
296 option.profilerType = ConvertProfilerType(profiler);
297 option.interval = interval;
298
299 panda::DFXJSNApi::StartProfiler(vm_, option, tid, instanceId, debuggerPostTask, isDebugApp);
300 }
301
DestroyHeapProfiler()302 void JsEnvironment::DestroyHeapProfiler()
303 {
304 if (vm_ == nullptr) {
305 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
306 return;
307 }
308 panda::DFXJSNApi::DestroyHeapProfiler(vm_);
309 }
310
GetHeapPrepare()311 void JsEnvironment::GetHeapPrepare()
312 {
313 if (vm_ == nullptr) {
314 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
315 return;
316 }
317 panda::DFXJSNApi::GetHeapPrepare(vm_);
318 }
319
SetModuleLoadChecker(const std::shared_ptr<ModuleCheckerDelegate> moduleCheckerDelegate)320 void JsEnvironment::SetModuleLoadChecker(const std::shared_ptr<ModuleCheckerDelegate> moduleCheckerDelegate)
321 {
322 if (engine_ == nullptr) {
323 TAG_LOGE(AAFwkTag::JSENV, "Invalid native engine");
324 return;
325 }
326
327 engine_->SetModuleLoadChecker(moduleCheckerDelegate);
328 }
329
ReInitJsEnvImpl(std::unique_ptr<JsEnvironmentImpl> impl)330 void JsEnvironment::ReInitJsEnvImpl(std::unique_ptr<JsEnvironmentImpl> impl)
331 {
332 TAG_LOGD(AAFwkTag::JSENV, "ReInit jsenv impl.");
333 impl_ = std::move(impl);
334 }
335
SetRequestAotCallback(const RequestAotCallback & cb)336 void JsEnvironment::SetRequestAotCallback(const RequestAotCallback& cb)
337 {
338 if (vm_ == nullptr) {
339 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
340 return;
341 }
342
343 panda::JSNApi::SetRequestAotCallback(vm_, cb);
344 }
345
SetDeviceDisconnectCallback(const std::function<bool ()> & cb)346 void JsEnvironment::SetDeviceDisconnectCallback(const std::function<bool()> &cb)
347 {
348 panda::JSNApi::SetDeviceDisconnectCallback(vm_, std::move(cb));
349 }
350
GetDebuggerPostTask()351 DebuggerPostTask JsEnvironment::GetDebuggerPostTask()
352 {
353 auto debuggerPostTask = [weak = weak_from_this()](std::function<void()>&& task) {
354 auto jsEnv = weak.lock();
355 if (jsEnv == nullptr) {
356 TAG_LOGE(AAFwkTag::JSENV, "JsEnv is invalid");
357 return;
358 }
359 jsEnv->PostTask(task, "JsEnvironment:GetDebuggerPostTask");
360 };
361 return debuggerPostTask;
362 }
363
NotifyDebugMode(int tid,const char * libraryPath,uint32_t instanceId,bool debug,bool debugMode)364 void JsEnvironment::NotifyDebugMode(
365 int tid, const char* libraryPath, uint32_t instanceId, bool debug, bool debugMode)
366 {
367 if (vm_ == nullptr) {
368 TAG_LOGE(AAFwkTag::JSENV, "Invalid vm");
369 return;
370 }
371 panda::JSNApi::DebugOption debugOption = {libraryPath, debug ? debugMode : false};
372 auto debuggerPostTask = [weak = weak_from_this()](std::function<void()>&& task) {
373 auto jsEnv = weak.lock();
374 if (jsEnv == nullptr) {
375 TAG_LOGE(AAFwkTag::JSENV, "JsEnv is invalid");
376 return;
377 }
378 jsEnv->PostTask(task, "JsEnvironment:NotifyDebugMode");
379 };
380 panda::JSNApi::NotifyDebugMode(tid, vm_, debugOption, instanceId, debuggerPostTask, debug);
381 }
382
ParseHdcRegisterOption(std::string & option)383 int32_t JsEnvironment::ParseHdcRegisterOption(std::string& option)
384 {
385 TAG_LOGD(AAFwkTag::JSENV, "Start");
386 std::size_t pos = option.find_first_of(":");
387 if (pos == std::string::npos) {
388 return -1;
389 }
390 std::string idStr = option.substr(pos + 1);
391 pos = idStr.find(DEBUGGER);
392 if (pos == std::string::npos) {
393 return -1;
394 }
395 idStr = idStr.substr(0, pos);
396 pos = idStr.find("@");
397 if (pos != std::string::npos) {
398 idStr = idStr.substr(pos + 1);
399 }
400 return std::atoi(idStr.c_str());
401 }
402
GetDebugMode() const403 bool JsEnvironment::GetDebugMode() const
404 {
405 return debugMode_;
406 }
407 } // namespace JsEnv
408 } // namespace OHOS
409