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