1 /*
2  * Copyright (c) 2021-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 "frameworks/bridge/js_frontend/engine/jsi/ark_js_runtime.h"
17 
18 #include <unistd.h>
19 
20 #include "ecmascript/napi/include/dfx_jsnapi.h"
21 #include "frameworks/bridge/common/utils/utils.h"
22 #include "frameworks/bridge/js_frontend/engine/jsi/ark_js_value.h"
23 #include "frameworks/core/common/ace_application_info.h"
24 #include "frameworks/core/common/connect_server_manager.h"
25 #include "frameworks/core/common/hdc_register.h"
26 
27 // NOLINTNEXTLINE(readability-identifier-naming)
28 namespace OHOS::Ace::Framework {
29 // NOLINTNEXTLINE(readability-identifier-naming)
30 static constexpr auto PANDA_MAIN_FUNCTION = "_GLOBAL::func_main_0";
31 #if !defined(PREVIEW)
32 constexpr auto DEBUGGER = "@Debugger";
33 #endif
34 
FunctionCallback(panda::JsiRuntimeCallInfo * info)35 Local<JSValueRef> FunctionCallback(panda::JsiRuntimeCallInfo* info)
36 {
37     auto package = reinterpret_cast<PandaFunctionData*>(info->GetData());
38     if (package == nullptr) {
39         return JSValueRef::Undefined(info->GetVM());
40     }
41     return package->Callback(info);
42 }
43 
FunctionDeleter(void * env,void * nativePointer,void * data)44 void FunctionDeleter(void *env, void *nativePointer, void *data)
45 {
46     auto info = reinterpret_cast<PandaFunctionData*>(data);
47     if (info != nullptr) {
48         delete info;
49     }
50 }
51 
52 thread_local EcmaVM* ArkJSRuntime::threadVm_ = nullptr;
53 
Initialize(const std::string & libraryPath,bool isDebugMode,int32_t instanceId)54 bool ArkJSRuntime::Initialize(const std::string& libraryPath, bool isDebugMode, int32_t instanceId)
55 {
56     RuntimeOption option;
57     option.SetGcType(RuntimeOption::GC_TYPE::GEN_GC);
58 #ifdef OHOS_PLATFORM
59     option.SetArkProperties(SystemProperties::GetArkProperties());
60     option.SetArkBundleName(SystemProperties::GetArkBundleName());
61     option.SetMemConfigProperty(SystemProperties::GetMemConfigProperty());
62     option.SetGcThreadNum(SystemProperties::GetGcThreadNum());
63     option.SetLongPauseTime(SystemProperties::GetLongPauseTime());
64     option.SetEnableAsmInterpreter(SystemProperties::GetAsmInterpreterEnabled());
65     option.SetAsmOpcodeDisableRange(SystemProperties::GetAsmOpcodeDisableRange());
66     LOGI("Initialize ark properties = %{public}d, bundlename = %{public}s", SystemProperties::GetArkProperties(),
67         SystemProperties::GetArkBundleName().c_str());
68 #endif
69     const int64_t poolSize = 0x10000000; // 256M
70     option.SetGcPoolSize(poolSize);
71     option.SetLogLevel(RuntimeOption::LOG_LEVEL::FOLLOW);
72     option.SetLogBufPrint(print_);
73     option.SetDebuggerLibraryPath(libraryPath);
74     libPath_ = libraryPath;
75     isDebugMode_ = isDebugMode;
76     instanceId_ = instanceId;
77 
78     vm_ = JSNApi::CreateJSVM(option);
79 #if defined(PREVIEW)
80     if (!pkgNameMap_.empty()) {
81         JSNApi::SetPkgNameList(vm_, pkgNameMap_);
82     }
83     if (!pkgAliasMap_.empty()) {
84         JSNApi::SetPkgAliasList(vm_, pkgAliasMap_);
85     }
86     if (!pkgContextInfoMap_.empty()) {
87         JSNApi::SetpkgContextInfoList(vm_, pkgContextInfoMap_);
88     }
89 #endif
90     return vm_ != nullptr;
91 }
92 
InitializeFromExistVM(EcmaVM * vm)93 bool ArkJSRuntime::InitializeFromExistVM(EcmaVM* vm)
94 {
95     vm_ = vm;
96     usingExistVM_ = true;
97     return vm_ != nullptr;
98 }
99 
Reset()100 void ArkJSRuntime::Reset()
101 {
102     if (vm_ != nullptr) {
103         if (!usingExistVM_) {
104 #if !defined(PREVIEW)
105 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
106             HdcRegister::Get().StopHdcRegister(instanceId_);
107             ConnectServerManager::Get().RemoveInstance(instanceId_);
108 #endif
109             JSNApi::StopDebugger(vm_);
110 #endif
111             JSNApi::DestroyJSVM(vm_);
112             vm_ = nullptr;
113         }
114     }
115 #if defined(PREVIEW)
116     previewComponents_.clear();
117 #endif
118 }
119 
SetLogPrint(LOG_PRINT out)120 void ArkJSRuntime::SetLogPrint(LOG_PRINT out)
121 {
122     print_ = out;
123 }
124 
125 #if !defined(PREVIEW) && !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
ParseHdcRegisterOption(std::string & option)126 static int ParseHdcRegisterOption(std::string& option)
127 {
128     LOGI("hdc option is %{public}s ", option.c_str());
129     if (option.find(":") == std::string::npos) {
130         return -1;
131     }
132     std::size_t pos = option.find_first_of(":");
133     std::string idStr = option.substr(pos + 1);
134     if (idStr.find(DEBUGGER) == std::string::npos) {
135         return -1;
136     }
137     pos = idStr.find(DEBUGGER);
138     idStr = idStr.substr(0, pos);
139     if (idStr.find("@") != std::string::npos) {
140         pos = idStr.find("@");
141         idStr = idStr.substr(pos + 1);
142     }
143     return static_cast<uint32_t>(std::atol(idStr.c_str()));
144 }
145 
StartDebuggerForSocketPair(std::string & option,uint32_t socketFd)146 void ArkJSRuntime::StartDebuggerForSocketPair(std::string& option, uint32_t socketFd)
147 {
148     CHECK_NULL_VOID(vm_);
149     int identifierId = ParseHdcRegisterOption(option);
150     panda::JSNApi::StartDebuggerForSocketPair(identifierId, socketFd);
151 }
152 #endif
153 
StartDebugger()154 bool ArkJSRuntime::StartDebugger()
155 {
156     bool ret = false;
157 #if !defined(PREVIEW)
158     if (!libPath_.empty()) {
159         bool isDebugApp = AceApplicationInfo::GetInstance().IsDebugVersion();
160 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
161         auto callback = [instanceId = instanceId_,
162                             weak = weak_from_this(), isDebugApp](int socketFd, std::string option) {
163             LOGI("HdcRegister callback socket %{public}d, option %{public}s.", socketFd, option.c_str());
164             if (option.find(DEBUGGER) == std::string::npos) {
165                 if (isDebugApp) {
166                     ConnectServerManager::Get().StopConnectServer();
167                 }
168                 ConnectServerManager::Get().StartConnectServerWithSocketPair(socketFd);
169             } else {
170                 auto runtime = weak.lock();
171                 CHECK_NULL_VOID(runtime);
172                 if (isDebugApp) {
173                     JSNApi::StopDebugger(ParseHdcRegisterOption(option));
174                 }
175                 runtime->StartDebuggerForSocketPair(option, socketFd);
176             }
177         };
178 
179         HdcRegister::Get().StartHdcRegister(instanceId_, callback);
180         ConnectServerManager::Get().SetDebugMode();
181 #endif
182         //FA:true port:-1
183         JSNApi::DebugOption debugOption = { libPath_.c_str(), isDebugApp ? isDebugMode_ : false, -1, true };
184 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
185         ConnectServerManager::Get().AddInstance(gettid(), language_);
186         ret = JSNApi::NotifyDebugMode(gettid(), vm_, debugOption, gettid(), debuggerPostTask_, isDebugApp);
187 #elif defined(ANDROID_PLATFORM)
188         ret = JSNApi::StartDebugger(vm_, debugOption, instanceId_, debuggerPostTask_);
189 #endif
190     }
191 #if defined(IOS_PLATFORM)
192     JSNApi::DebugOption debugOption = { nullptr, isDebugMode_, -1, true }; //FA:true port:-1
193     ret = JSNApi::StartDebugger(vm_, debugOption, instanceId_, debuggerPostTask_);
194 #endif
195 #endif
196     return ret;
197 }
198 
IsExecuteModuleInAbcFile(const std::string & bundleName,const std::string & moduleName,const std::string & ohmurl)199 bool ArkJSRuntime::IsExecuteModuleInAbcFile(
200     const std::string &bundleName, const std::string &moduleName, const std::string &ohmurl)
201 {
202     JSExecutionScope executionScope(vm_);
203     LocalScope scope(vm_);
204     panda::TryCatch trycatch(vm_);
205     bool ret = JSNApi::IsExecuteModuleInAbcFile(vm_, bundleName, moduleName, ohmurl);
206     HandleUncaughtException(trycatch);
207     return ret;
208 }
209 
ExecuteModuleBuffer(const uint8_t * data,int32_t size,const std::string & filename,bool needUpdate)210 bool ArkJSRuntime::ExecuteModuleBuffer(const uint8_t* data, int32_t size, const std::string& filename, bool needUpdate)
211 {
212     JSExecutionScope executionScope(vm_);
213     LocalScope scope(vm_);
214     panda::TryCatch trycatch(vm_);
215     bool ret = JSNApi::ExecuteModuleBuffer(vm_, data, size, filename, needUpdate);
216     HandleUncaughtException(trycatch);
217     return ret;
218 }
219 
220 shared_ptr<JsValue> ArkJSRuntime::EvaluateJsCode([[maybe_unused]] const std::string& src)
221 {
222     return NewUndefined();
223 }
224 
EvaluateJsCode(const uint8_t * buffer,int32_t size,const std::string & filePath,bool needUpdate)225 bool ArkJSRuntime::EvaluateJsCode(const uint8_t* buffer, int32_t size, const std::string& filePath, bool needUpdate)
226 {
227     JSExecutionScope executionScope(vm_);
228     LocalScope scope(vm_);
229     panda::TryCatch trycatch(vm_);
230     bool ret = JSNApi::Execute(vm_, buffer, size, PANDA_MAIN_FUNCTION, filePath, needUpdate);
231     HandleUncaughtException(trycatch);
232     return ret;
233 }
234 
ExecuteJsBin(const std::string & fileName,const std::function<void (const std::string &,int32_t)> & errorCallback)235 bool ArkJSRuntime::ExecuteJsBin(const std::string& fileName,
236     const std::function<void(const std::string&, int32_t)>& errorCallback)
237 {
238     JSExecutionScope executionScope(vm_);
239     LocalScope scope(vm_);
240     panda::TryCatch trycatch(vm_);
241     bool ret = JSNApi::Execute(vm_, fileName, PANDA_MAIN_FUNCTION);
242     HandleUncaughtException(trycatch, errorCallback);
243     return ret;
244 }
245 
ExecuteJsBinForAOT(const std::string & fileName,const std::function<void (const std::string &,int32_t)> & errorCallback)246 bool ArkJSRuntime::ExecuteJsBinForAOT(const std::string& fileName,
247     const std::function<void(const std::string&, int32_t)>& errorCallback)
248 {
249     JSExecutionScope executionScope(vm_);
250     LocalScope scope(vm_);
251     panda::TryCatch trycatch(vm_);
252     bool ret = JSNApi::ExecuteForAbsolutePath(vm_, fileName, PANDA_MAIN_FUNCTION);
253     HandleUncaughtException(trycatch, errorCallback);
254     return ret;
255 }
256 
GetGlobal()257 shared_ptr<JsValue> ArkJSRuntime::GetGlobal()
258 {
259     LocalScope scope(vm_);
260     return std::make_shared<ArkJSValue>(shared_from_this(), JSNApi::GetGlobalObject(vm_));
261 }
262 
RunGC()263 void ArkJSRuntime::RunGC()
264 {
265     JSExecutionScope executionScope(vm_);
266     LocalScope scope(vm_);
267     JSNApi::TriggerGC(vm_, panda::ecmascript::GCReason::TRIGGER_BY_ARKUI, JSNApi::TRIGGER_GC_TYPE::SEMI_GC);
268 }
269 
RunFullGC()270 void ArkJSRuntime::RunFullGC()
271 {
272     JSExecutionScope executionScope(vm_);
273     LocalScope scope(vm_);
274     JSNApi::TriggerGC(vm_, panda::ecmascript::GCReason::TRIGGER_BY_ARKUI, JSNApi::TRIGGER_GC_TYPE::FULL_GC);
275 }
276 
NewInt32(int32_t value)277 shared_ptr<JsValue> ArkJSRuntime::NewInt32(int32_t value)
278 {
279     LocalScope scope(vm_);
280     return std::make_shared<ArkJSValue>(shared_from_this(), IntegerRef::New(vm_, value));
281 }
282 
NewBoolean(bool value)283 shared_ptr<JsValue> ArkJSRuntime::NewBoolean(bool value)
284 {
285     LocalScope scope(vm_);
286     return std::make_shared<ArkJSValue>(shared_from_this(), BooleanRef::New(vm_, value));
287 }
288 
NewNumber(double d)289 shared_ptr<JsValue> ArkJSRuntime::NewNumber(double d)
290 {
291     LocalScope scope(vm_);
292     return std::make_shared<ArkJSValue>(shared_from_this(), NumberRef::New(vm_, d));
293 }
294 
NewNull()295 shared_ptr<JsValue> ArkJSRuntime::NewNull()
296 {
297     LocalScope scope(vm_);
298     return std::make_shared<ArkJSValue>(shared_from_this(), JSValueRef::Null(vm_));
299 }
300 
NewUndefined()301 shared_ptr<JsValue> ArkJSRuntime::NewUndefined()
302 {
303     LocalScope scope(vm_);
304     return std::make_shared<ArkJSValue>(shared_from_this(), JSValueRef::Undefined(vm_));
305 }
306 
NewString(const std::string & str)307 shared_ptr<JsValue> ArkJSRuntime::NewString(const std::string& str)
308 {
309     LocalScope scope(vm_);
310     return std::make_shared<ArkJSValue>(shared_from_this(), StringRef::NewFromUtf8(vm_, str.c_str()));
311 }
312 
ParseJson(const std::string & str)313 shared_ptr<JsValue> ArkJSRuntime::ParseJson(const std::string& str)
314 {
315     LocalScope scope(vm_);
316     Local<StringRef> string = StringRef::NewFromUtf8(vm_, str.c_str());
317     return std::make_shared<ArkJSValue>(shared_from_this(), JSON::Parse(vm_, string));
318 }
319 
NewObject()320 shared_ptr<JsValue> ArkJSRuntime::NewObject()
321 {
322     LocalScope scope(vm_);
323     return std::make_shared<ArkJSValue>(shared_from_this(), ObjectRef::New(vm_));
324 }
325 
NewArray()326 shared_ptr<JsValue> ArkJSRuntime::NewArray()
327 {
328     LocalScope scope(vm_);
329     return std::make_shared<ArkJSValue>(shared_from_this(), ArrayRef::New(vm_));
330 }
331 
NewFunction(RegisterFunctionType func)332 shared_ptr<JsValue> ArkJSRuntime::NewFunction(RegisterFunctionType func)
333 {
334     LocalScope scope(vm_);
335     auto data = new PandaFunctionData(shared_from_this(), func);
336     return std::make_shared<ArkJSValue>(shared_from_this(),
337         FunctionRef::NewConcurrent(vm_, FunctionCallback, FunctionDeleter, data));
338 }
339 
NewNativePointer(void * ptr)340 shared_ptr<JsValue> ArkJSRuntime::NewNativePointer(void* ptr)
341 {
342     LocalScope scope(vm_);
343     return std::make_shared<ArkJSValue>(shared_from_this(), NativePointerRef::New(vm_, ptr));
344 }
345 
ThrowError(const std::string & msg,int32_t code)346 void ArkJSRuntime::ThrowError(const std::string& msg, int32_t code)
347 {
348     auto errorVal = panda::Exception::Error(vm_, panda::StringRef::NewFromUtf8(vm_, msg.c_str()));
349     auto codeVal = panda::Exception::Error(vm_, panda::StringRef::NewFromUtf8(vm_, std::to_string(code).c_str()));
350     Local<StringRef> codeKey = StringRef::NewFromUtf8(vm_, "code");
351     Local<ObjectRef> errorObj(errorVal);
352     errorObj->Set(vm_, codeKey, codeVal);
353     panda::JSNApi::ThrowException(vm_, errorObj);
354 }
355 
RegisterUncaughtExceptionHandler(UncaughtExceptionCallback callback)356 void ArkJSRuntime::RegisterUncaughtExceptionHandler(UncaughtExceptionCallback callback)
357 {
358     JSNApi::EnableUserUncaughtErrorHandler(vm_);
359     std::weak_ptr<ArkJSRuntime> weakThis = shared_from_this();
360     JSNApi::RegisterUncatchableErrorHandler(vm_,
361         [weakThis](auto& tryCatch) {
362             auto sharedThis = weakThis.lock();
363             if (sharedThis) {
364                 sharedThis->HandleUncaughtExceptionWithoutNativeEngine(tryCatch, nullptr);
365             } else {
366                 LOGE("ArkJSRuntime has been destructed.");
367             }
368         });
369     uncaughtErrorHandler_ = callback;
370 }
371 
HasPendingException()372 bool ArkJSRuntime::HasPendingException()
373 {
374     return JSNApi::HasPendingException(vm_);
375 }
376 
HandleUncaughtException(panda::TryCatch & trycatch,const std::function<void (const std::string &,int32_t)> & errorCallback)377 void ArkJSRuntime::HandleUncaughtException(panda::TryCatch& trycatch,
378     const std::function<void(const std::string&, int32_t)>& errorCallback)
379 {
380     if (errorCallback != nullptr) {
381         Local<ObjectRef> exception = trycatch.GetAndClearException();
382         if (!exception.IsEmpty() && !exception->IsHole()) {
383             errorCallback("loading js file has crash or the uri of router is not exist.",
384                 ERROR_CODE_URI_ERROR);
385             return;
386         }
387     }
388 
389     // Handle the uncaught exception by native engine created by ability runtime in the stage model project.
390     if (nativeEngine_) {
391         nativeEngine_->HandleUncaughtException();
392         return;
393     }
394 
395     // Handle the uncaught exception without native engine, such as oom error
396     HandleUncaughtExceptionWithoutNativeEngine(trycatch, errorCallback);
397 }
398 
HandleUncaughtExceptionWithoutNativeEngine(panda::TryCatch & trycatch,const std::function<void (const std::string &,int32_t)> & errorCallback)399 void ArkJSRuntime::HandleUncaughtExceptionWithoutNativeEngine(panda::TryCatch& trycatch,
400     const std::function<void(const std::string&, int32_t)>& errorCallback)
401 {
402     if (uncaughtErrorHandler_ == nullptr) {
403         return;
404     }
405 
406     Local<ObjectRef> exception = trycatch.GetAndClearException();
407     if (!exception.IsEmpty() && !exception->IsHole()) {
408         shared_ptr<JsValue> errorPtr =
409             std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(shared_from_this(), exception));
410         uncaughtErrorHandler_(errorPtr, shared_from_this());
411     }
412 }
413 
ExecutePendingJob()414 void ArkJSRuntime::ExecutePendingJob()
415 {
416     LocalScope scope(vm_);
417     JSNApi::ExecutePendingJob(vm_);
418 }
419 
NotifyUIIdle()420 void ArkJSRuntime::NotifyUIIdle()
421 {
422     LocalScope scope(vm_);
423     panda::JSNApi::NotifyUIIdle(vm_, 0);
424 }
425 
426 #if !defined(PREVIEW) && !defined(IOS_PLATFORM)
DumpHeapSnapshot(bool isPrivate)427 void ArkJSRuntime::DumpHeapSnapshot(bool isPrivate)
428 {
429     panda::ecmascript::DumpSnapShotOption dumpOption;
430     dumpOption.dumpFormat = panda::ecmascript::DumpFormat::JSON;
431     dumpOption.isVmMode = true;
432     dumpOption.isPrivate = isPrivate;
433     dumpOption.isSync = false;
434     LocalScope scope(vm_);
435     panda::DFXJSNApi::DumpHeapSnapshot(vm_, dumpOption);
436 }
437 #else
DumpHeapSnapshot(bool isPrivate)438 void ArkJSRuntime::DumpHeapSnapshot(bool isPrivate)
439 {
440     LOGE("Do not support Ark DumpHeapSnapshot on Windows or MacOS");
441 }
442 #endif
443 
444 #if !defined(PREVIEW) && !defined(IOS_PLATFORM)
DestroyHeapProfiler()445 void ArkJSRuntime::DestroyHeapProfiler()
446 {
447     LocalScope scope(vm_);
448     panda::DFXJSNApi::DestroyHeapProfiler(vm_);
449 }
450 #else
DestroyHeapProfiler()451 void ArkJSRuntime::DestroyHeapProfiler()
452 {
453     LOGE("Do not support Ark DestroyHeapProfiler on Windows or MacOS");
454 }
455 #endif
456 
Callback(panda::JsiRuntimeCallInfo * info) const457 Local<JSValueRef> PandaFunctionData::Callback(panda::JsiRuntimeCallInfo* info) const
458 {
459     auto runtime = runtime_.lock();
460     if (runtime == nullptr) {
461         return Local<JSValueRef>();
462     }
463     EscapeLocalScope scope(runtime->GetEcmaVm());
464     shared_ptr<JsValue> thisPtr =
465         std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(runtime, info->GetThisRef()));
466 
467     std::vector<shared_ptr<JsValue>> argv;
468     uint32_t length = info->GetArgsNumber();
469     argv.reserve(length);
470     for (uint32_t i = 0; i < length; ++i) {
471         argv.emplace_back(
472             std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(runtime, info->GetCallArgRef(i))));
473     }
474     shared_ptr<JsValue> result = func_(runtime, thisPtr, argv, length);
475     return scope.Escape(std::static_pointer_cast<ArkJSValue>(result)->GetValue(runtime));
476 }
477 
LoadDestinationFile(const std::string & bundleName,const std::string & moduleName,const std::string & pageSourceFile,bool isSingleton)478 int32_t ArkJSRuntime::LoadDestinationFile(const std::string& bundleName, const std::string& moduleName,
479     const std::string& pageSourceFile, bool isSingleton)
480 {
481     JSExecutionScope executionScope(vm_);
482     LocalScope scope(vm_);
483     panda::TryCatch trycatch(vm_);
484     std::string module = moduleName;
485     int ret = JSNApi::ExecuteWithSingletonPatternFlag(vm_, bundleName, module, pageSourceFile, isSingleton);
486     HandleUncaughtException(trycatch);
487     if (ret != 0) {
488         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "load pageSourceFile failed code is: %{public}d", ret);
489     }
490     return ret;
491 }
492 } // namespace OHOS::Ace::Framework
493