• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         JSNApi::DebugOption debugOption = { libPath_.c_str(), isDebugApp ? isDebugMode_ : false };
183 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
184         ConnectServerManager::Get().AddInstance(gettid(), language_);
185         ret = JSNApi::NotifyDebugMode(gettid(), vm_, debugOption, gettid(), debuggerPostTask_, isDebugApp);
186 #elif defined(ANDROID_PLATFORM)
187         ret = JSNApi::StartDebugger(vm_, debugOption, instanceId_, debuggerPostTask_);
188 #endif
189     }
190 #if defined(IOS_PLATFORM)
191     JSNApi::DebugOption debugOption = { nullptr, isDebugMode_ };
192     ret = JSNApi::StartDebugger(vm_, debugOption, instanceId_, debuggerPostTask_);
193 #endif
194 #endif
195     return ret;
196 }
197 
IsExecuteModuleInAbcFile(const std::string & bundleName,const std::string & moduleName,const std::string & ohmurl)198 bool ArkJSRuntime::IsExecuteModuleInAbcFile(
199     const std::string &bundleName, const std::string &moduleName, const std::string &ohmurl)
200 {
201     JSExecutionScope executionScope(vm_);
202     LocalScope scope(vm_);
203     panda::TryCatch trycatch(vm_);
204     bool ret = JSNApi::IsExecuteModuleInAbcFile(vm_, bundleName, moduleName, ohmurl);
205     HandleUncaughtException(trycatch);
206     return ret;
207 }
208 
ExecuteModuleBuffer(const uint8_t * data,int32_t size,const std::string & filename,bool needUpdate)209 bool ArkJSRuntime::ExecuteModuleBuffer(const uint8_t* data, int32_t size, const std::string& filename, bool needUpdate)
210 {
211     JSExecutionScope executionScope(vm_);
212     LocalScope scope(vm_);
213     panda::TryCatch trycatch(vm_);
214     bool ret = JSNApi::ExecuteModuleBuffer(vm_, data, size, filename, needUpdate);
215     HandleUncaughtException(trycatch);
216     return ret;
217 }
218 
EvaluateJsCode(const std::string & src)219 shared_ptr<JsValue> ArkJSRuntime::EvaluateJsCode([[maybe_unused]] const std::string& src)
220 {
221     return NewUndefined();
222 }
223 
EvaluateJsCode(const uint8_t * buffer,int32_t size,const std::string & filePath,bool needUpdate)224 bool ArkJSRuntime::EvaluateJsCode(const uint8_t* buffer, int32_t size, const std::string& filePath, bool needUpdate)
225 {
226     JSExecutionScope executionScope(vm_);
227     LocalScope scope(vm_);
228     panda::TryCatch trycatch(vm_);
229     bool ret = JSNApi::Execute(vm_, buffer, size, PANDA_MAIN_FUNCTION, filePath, needUpdate);
230     HandleUncaughtException(trycatch);
231     return ret;
232 }
233 
ExecuteJsBin(const std::string & fileName,const std::function<void (const std::string &,int32_t)> & errorCallback)234 bool ArkJSRuntime::ExecuteJsBin(const std::string& fileName,
235     const std::function<void(const std::string&, int32_t)>& errorCallback)
236 {
237     JSExecutionScope executionScope(vm_);
238     LocalScope scope(vm_);
239     panda::TryCatch trycatch(vm_);
240     bool ret = JSNApi::Execute(vm_, fileName, PANDA_MAIN_FUNCTION);
241     HandleUncaughtException(trycatch, errorCallback);
242     return ret;
243 }
244 
ExecuteJsBinForAOT(const std::string & fileName,const std::function<void (const std::string &,int32_t)> & errorCallback)245 bool ArkJSRuntime::ExecuteJsBinForAOT(const std::string& fileName,
246     const std::function<void(const std::string&, int32_t)>& errorCallback)
247 {
248     JSExecutionScope executionScope(vm_);
249     LocalScope scope(vm_);
250     panda::TryCatch trycatch(vm_);
251     bool ret = JSNApi::ExecuteForAbsolutePath(vm_, fileName, PANDA_MAIN_FUNCTION);
252     HandleUncaughtException(trycatch, errorCallback);
253     return ret;
254 }
255 
GetGlobal()256 shared_ptr<JsValue> ArkJSRuntime::GetGlobal()
257 {
258     LocalScope scope(vm_);
259     return std::make_shared<ArkJSValue>(shared_from_this(), JSNApi::GetGlobalObject(vm_));
260 }
261 
RunGC()262 void ArkJSRuntime::RunGC()
263 {
264     JSExecutionScope executionScope(vm_);
265     LocalScope scope(vm_);
266     JSNApi::TriggerGC(vm_, panda::ecmascript::GCReason::TRIGGER_BY_ARKUI, JSNApi::TRIGGER_GC_TYPE::SEMI_GC);
267 }
268 
RunFullGC()269 void ArkJSRuntime::RunFullGC()
270 {
271     JSExecutionScope executionScope(vm_);
272     LocalScope scope(vm_);
273     JSNApi::TriggerGC(vm_, panda::ecmascript::GCReason::TRIGGER_BY_ARKUI, JSNApi::TRIGGER_GC_TYPE::FULL_GC);
274 }
275 
NewInt32(int32_t value)276 shared_ptr<JsValue> ArkJSRuntime::NewInt32(int32_t value)
277 {
278     LocalScope scope(vm_);
279     return std::make_shared<ArkJSValue>(shared_from_this(), IntegerRef::New(vm_, value));
280 }
281 
NewBoolean(bool value)282 shared_ptr<JsValue> ArkJSRuntime::NewBoolean(bool value)
283 {
284     LocalScope scope(vm_);
285     return std::make_shared<ArkJSValue>(shared_from_this(), BooleanRef::New(vm_, value));
286 }
287 
NewNumber(double d)288 shared_ptr<JsValue> ArkJSRuntime::NewNumber(double d)
289 {
290     LocalScope scope(vm_);
291     return std::make_shared<ArkJSValue>(shared_from_this(), NumberRef::New(vm_, d));
292 }
293 
NewNull()294 shared_ptr<JsValue> ArkJSRuntime::NewNull()
295 {
296     LocalScope scope(vm_);
297     return std::make_shared<ArkJSValue>(shared_from_this(), JSValueRef::Null(vm_));
298 }
299 
NewUndefined()300 shared_ptr<JsValue> ArkJSRuntime::NewUndefined()
301 {
302     LocalScope scope(vm_);
303     return std::make_shared<ArkJSValue>(shared_from_this(), JSValueRef::Undefined(vm_));
304 }
305 
NewString(const std::string & str)306 shared_ptr<JsValue> ArkJSRuntime::NewString(const std::string& str)
307 {
308     LocalScope scope(vm_);
309     return std::make_shared<ArkJSValue>(shared_from_this(), StringRef::NewFromUtf8(vm_, str.c_str()));
310 }
311 
ParseJson(const std::string & str)312 shared_ptr<JsValue> ArkJSRuntime::ParseJson(const std::string& str)
313 {
314     LocalScope scope(vm_);
315     Local<StringRef> string = StringRef::NewFromUtf8(vm_, str.c_str());
316     return std::make_shared<ArkJSValue>(shared_from_this(), JSON::Parse(vm_, string));
317 }
318 
NewObject()319 shared_ptr<JsValue> ArkJSRuntime::NewObject()
320 {
321     LocalScope scope(vm_);
322     return std::make_shared<ArkJSValue>(shared_from_this(), ObjectRef::New(vm_));
323 }
324 
NewArray()325 shared_ptr<JsValue> ArkJSRuntime::NewArray()
326 {
327     LocalScope scope(vm_);
328     return std::make_shared<ArkJSValue>(shared_from_this(), ArrayRef::New(vm_));
329 }
330 
NewFunction(RegisterFunctionType func)331 shared_ptr<JsValue> ArkJSRuntime::NewFunction(RegisterFunctionType func)
332 {
333     LocalScope scope(vm_);
334     auto data = new PandaFunctionData(shared_from_this(), func);
335     return std::make_shared<ArkJSValue>(shared_from_this(),
336         FunctionRef::NewConcurrent(vm_, FunctionCallback, FunctionDeleter, data));
337 }
338 
NewNativePointer(void * ptr)339 shared_ptr<JsValue> ArkJSRuntime::NewNativePointer(void* ptr)
340 {
341     LocalScope scope(vm_);
342     return std::make_shared<ArkJSValue>(shared_from_this(), NativePointerRef::New(vm_, ptr));
343 }
344 
ThrowError(const std::string & msg,int32_t code)345 void ArkJSRuntime::ThrowError(const std::string& msg, int32_t code)
346 {
347     auto errorVal = panda::Exception::Error(vm_, panda::StringRef::NewFromUtf8(vm_, msg.c_str()));
348     auto codeVal = panda::Exception::Error(vm_, panda::StringRef::NewFromUtf8(vm_, std::to_string(code).c_str()));
349     Local<StringRef> codeKey = StringRef::NewFromUtf8(vm_, "code");
350     Local<ObjectRef> errorObj(errorVal);
351     errorObj->Set(vm_, codeKey, codeVal);
352     panda::JSNApi::ThrowException(vm_, errorObj);
353 }
354 
RegisterUncaughtExceptionHandler(UncaughtExceptionCallback callback)355 void ArkJSRuntime::RegisterUncaughtExceptionHandler(UncaughtExceptionCallback callback)
356 {
357     JSNApi::EnableUserUncaughtErrorHandler(vm_);
358     std::weak_ptr<ArkJSRuntime> weakThis = shared_from_this();
359     JSNApi::RegisterUncatchableErrorHandler(vm_,
360         [weakThis](auto& tryCatch) {
361             auto sharedThis = weakThis.lock();
362             if (sharedThis) {
363                 sharedThis->HandleUncaughtExceptionWithoutNativeEngine(tryCatch, nullptr);
364             } else {
365                 LOGE("ArkJSRuntime has been destructed.");
366             }
367         });
368     uncaughtErrorHandler_ = callback;
369 }
370 
HasPendingException()371 bool ArkJSRuntime::HasPendingException()
372 {
373     return JSNApi::HasPendingException(vm_);
374 }
375 
HandleUncaughtException(panda::TryCatch & trycatch,const std::function<void (const std::string &,int32_t)> & errorCallback)376 void ArkJSRuntime::HandleUncaughtException(panda::TryCatch& trycatch,
377     const std::function<void(const std::string&, int32_t)>& errorCallback)
378 {
379     if (errorCallback != nullptr) {
380         Local<ObjectRef> exception = trycatch.GetAndClearException();
381         if (!exception.IsEmpty() && !exception->IsHole()) {
382             errorCallback("loading js file has crash or the uri of router is not exist.",
383                 ERROR_CODE_URI_ERROR);
384             return;
385         }
386     }
387 
388     // Handle the uncaught exception by native engine created by ability runtime in the stage model project.
389     if (nativeEngine_) {
390         nativeEngine_->HandleUncaughtException();
391         return;
392     }
393 
394     // Handle the uncaught exception without native engine, such as oom error
395     HandleUncaughtExceptionWithoutNativeEngine(trycatch, errorCallback);
396 }
397 
HandleUncaughtExceptionWithoutNativeEngine(panda::TryCatch & trycatch,const std::function<void (const std::string &,int32_t)> & errorCallback)398 void ArkJSRuntime::HandleUncaughtExceptionWithoutNativeEngine(panda::TryCatch& trycatch,
399     const std::function<void(const std::string&, int32_t)>& errorCallback)
400 {
401     if (uncaughtErrorHandler_ == nullptr) {
402         return;
403     }
404 
405     Local<ObjectRef> exception = trycatch.GetAndClearException();
406     if (!exception.IsEmpty() && !exception->IsHole()) {
407         shared_ptr<JsValue> errorPtr =
408             std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(shared_from_this(), exception));
409         uncaughtErrorHandler_(errorPtr, shared_from_this());
410     }
411 }
412 
ExecutePendingJob()413 void ArkJSRuntime::ExecutePendingJob()
414 {
415     LocalScope scope(vm_);
416     JSNApi::ExecutePendingJob(vm_);
417 }
418 
NotifyUIIdle()419 void ArkJSRuntime::NotifyUIIdle()
420 {
421     LocalScope scope(vm_);
422     panda::JSNApi::NotifyUIIdle(vm_, 0);
423 }
424 
425 #if !defined(PREVIEW) && !defined(IOS_PLATFORM)
DumpHeapSnapshot(bool isPrivate)426 void ArkJSRuntime::DumpHeapSnapshot(bool isPrivate)
427 {
428     panda::ecmascript::DumpSnapShotOption dumpOption;
429     dumpOption.dumpFormat = panda::ecmascript::DumpFormat::JSON;
430     dumpOption.isVmMode = true;
431     dumpOption.isPrivate = isPrivate;
432     dumpOption.isSync = false;
433     LocalScope scope(vm_);
434     panda::DFXJSNApi::DumpHeapSnapshot(vm_, dumpOption);
435 }
436 #else
DumpHeapSnapshot(bool isPrivate)437 void ArkJSRuntime::DumpHeapSnapshot(bool isPrivate)
438 {
439     LOGE("Do not support Ark DumpHeapSnapshot on Windows or MacOS");
440 }
441 #endif
442 
443 #if !defined(PREVIEW) && !defined(IOS_PLATFORM)
DestroyHeapProfiler()444 void ArkJSRuntime::DestroyHeapProfiler()
445 {
446     LocalScope scope(vm_);
447     panda::DFXJSNApi::DestroyHeapProfiler(vm_);
448 }
449 #else
DestroyHeapProfiler()450 void ArkJSRuntime::DestroyHeapProfiler()
451 {
452     LOGE("Do not support Ark DestroyHeapProfiler on Windows or MacOS");
453 }
454 #endif
455 
Callback(panda::JsiRuntimeCallInfo * info) const456 Local<JSValueRef> PandaFunctionData::Callback(panda::JsiRuntimeCallInfo* info) const
457 {
458     auto runtime = runtime_.lock();
459     if (runtime == nullptr) {
460         return Local<JSValueRef>();
461     }
462     EscapeLocalScope scope(runtime->GetEcmaVm());
463     shared_ptr<JsValue> thisPtr =
464         std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(runtime, info->GetThisRef()));
465 
466     std::vector<shared_ptr<JsValue>> argv;
467     uint32_t length = info->GetArgsNumber();
468     argv.reserve(length);
469     for (uint32_t i = 0; i < length; ++i) {
470         argv.emplace_back(
471             std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(runtime, info->GetCallArgRef(i))));
472     }
473     shared_ptr<JsValue> result = func_(runtime, thisPtr, argv, length);
474     return scope.Escape(std::static_pointer_cast<ArkJSValue>(result)->GetValue(runtime));
475 }
476 
LoadDestinationFile(const std::string & bundleName,const std::string & moduleName,const std::string & pageSourceFile,bool isSingleton)477 int32_t ArkJSRuntime::LoadDestinationFile(const std::string& bundleName, const std::string& moduleName,
478     const std::string& pageSourceFile, bool isSingleton)
479 {
480     JSExecutionScope executionScope(vm_);
481     LocalScope scope(vm_);
482     panda::TryCatch trycatch(vm_);
483     std::string module = moduleName;
484     int ret = JSNApi::ExecuteWithSingletonPatternFlag(vm_, bundleName, module, pageSourceFile, isSingleton);
485     HandleUncaughtException(trycatch);
486     if (ret != 0) {
487         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "load pageSourceFile failed code is: %{public}d", ret);
488     }
489     return ret;
490 }
491 } // namespace OHOS::Ace::Framework
492