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