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