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