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