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