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
22 #include "frameworks/base/log/log_wrapper.h"
23 #include "frameworks/base/utils/system_properties.h"
24 #include "frameworks/bridge/common/utils/utils.h"
25 #include "frameworks/bridge/js_frontend/engine/jsi/ark_js_value.h"
26 #include "frameworks/core/common/ace_application_info.h"
27 #include "frameworks/core/common/connect_server_manager.h"
28 #include "frameworks/core/common/hdc_register.h"
29
30 // NOLINTNEXTLINE(readability-identifier-naming)
31 namespace OHOS::Ace::Framework {
32 // NOLINTNEXTLINE(readability-identifier-naming)
33 static constexpr auto PANDA_MAIN_FUNCTION = "_GLOBAL::func_main_0";
34 #if !defined(PREVIEW)
35 constexpr int32_t FORMAT_JSON = 1;
36 constexpr auto DEBUGGER = "@Debugger";
37 #endif
38
FunctionCallback(panda::JsiRuntimeCallInfo * info)39 Local<JSValueRef> FunctionCallback(panda::JsiRuntimeCallInfo* info)
40 {
41 auto package = reinterpret_cast<PandaFunctionData*>(info->GetData());
42 if (package == nullptr) {
43 return JSValueRef::Undefined(info->GetVM());
44 }
45 return package->Callback(info);
46 }
47
FunctionDeleter(void * nativePointer,void * data)48 void FunctionDeleter(void *nativePointer, void *data)
49 {
50 auto info = reinterpret_cast<PandaFunctionData*>(data);
51 if (info != nullptr) {
52 delete info;
53 }
54 }
55
56 thread_local EcmaVM* ArkJSRuntime::threadVm_ = nullptr;
57
Initialize(const std::string & libraryPath,bool isDebugMode,int32_t instanceId)58 bool ArkJSRuntime::Initialize(const std::string& libraryPath, bool isDebugMode, int32_t instanceId)
59 {
60 RuntimeOption option;
61 option.SetGcType(RuntimeOption::GC_TYPE::GEN_GC);
62 #ifdef OHOS_PLATFORM
63 option.SetArkProperties(SystemProperties::GetArkProperties());
64 option.SetArkBundleName(SystemProperties::GetArkBundleName());
65 option.SetGcThreadNum(SystemProperties::GetGcThreadNum());
66 option.SetLongPauseTime(SystemProperties::GetLongPauseTime());
67 option.SetEnableAsmInterpreter(SystemProperties::GetAsmInterpreterEnabled());
68 option.SetAsmOpcodeDisableRange(SystemProperties::GetAsmOpcodeDisableRange());
69 LOGI("Initialize ark properties = %{public}d, bundlename = %{public}s", SystemProperties::GetArkProperties(),
70 SystemProperties::GetArkBundleName().c_str());
71 #endif
72 const int64_t poolSize = 0x10000000; // 256M
73 option.SetGcPoolSize(poolSize);
74 option.SetLogLevel(RuntimeOption::LOG_LEVEL::FOLLOW);
75 option.SetLogBufPrint(print_);
76 option.SetDebuggerLibraryPath(libraryPath);
77 libPath_ = libraryPath;
78 isDebugMode_ = isDebugMode;
79 instanceId_ = instanceId;
80
81 vm_ = JSNApi::CreateJSVM(option);
82 return vm_ != nullptr;
83 }
84
InitializeFromExistVM(EcmaVM * vm)85 bool ArkJSRuntime::InitializeFromExistVM(EcmaVM* vm)
86 {
87 vm_ = vm;
88 usingExistVM_ = true;
89 return vm_ != nullptr;
90 }
91
Reset()92 void ArkJSRuntime::Reset()
93 {
94 if (vm_ != nullptr) {
95 if (!usingExistVM_) {
96 #if !defined(PREVIEW)
97 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
98 HdcRegister::Get().StopHdcRegister(instanceId_);
99 ConnectServerManager::Get().RemoveInstance(instanceId_);
100 #endif
101 JSNApi::StopDebugger(vm_);
102 #endif
103 JSNApi::DestroyJSVM(vm_);
104 vm_ = nullptr;
105 }
106 }
107 #if defined(PREVIEW)
108 previewComponents_.clear();
109 #endif
110 }
111
SetLogPrint(LOG_PRINT out)112 void ArkJSRuntime::SetLogPrint(LOG_PRINT out)
113 {
114 print_ = out;
115 }
116
117 #if !defined(PREVIEW) && !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
ParseHdcRegisterOption(std::string & option)118 static int ParseHdcRegisterOption(std::string& option)
119 {
120 LOGI("hdc option is %{public}s ", option.c_str());
121 if (option.find(":") == std::string::npos) {
122 return -1;
123 }
124 std::size_t pos = option.find_first_of(":");
125 std::string idStr = option.substr(pos + 1);
126 if (idStr.find(DEBUGGER) == std::string::npos) {
127 return -1;
128 }
129 pos = idStr.find(DEBUGGER);
130 idStr = idStr.substr(0, pos);
131 if (idStr.find("@") != std::string::npos) {
132 pos = idStr.find("@");
133 idStr = idStr.substr(pos + 1);
134 }
135 return static_cast<uint32_t>(std::atol(idStr.c_str()));
136 }
137
StartDebuggerForSocketPair(std::string & option,uint32_t socketFd)138 void ArkJSRuntime::StartDebuggerForSocketPair(std::string& option, uint32_t socketFd)
139 {
140 CHECK_NULL_VOID(vm_);
141 int identifierId = ParseHdcRegisterOption(option);
142 panda::JSNApi::StartDebuggerForSocketPair(identifierId, socketFd);
143 }
144 #endif
145
StartDebugger()146 bool ArkJSRuntime::StartDebugger()
147 {
148 bool ret = false;
149 #if !defined(PREVIEW)
150 if (!libPath_.empty()) {
151 bool isDebugApp = AceApplicationInfo::GetInstance().IsDebugVersion();
152 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
153 auto callback = [instanceId = instanceId_,
154 weak = weak_from_this(), isDebugApp](int socketFd, std::string option) {
155 LOGI("HdcRegister callback socket %{public}d, option %{public}s.", socketFd, option.c_str());
156 if (option.find(DEBUGGER) == std::string::npos) {
157 if (isDebugApp) {
158 ConnectServerManager::Get().StopConnectServer();
159 }
160 ConnectServerManager::Get().StartConnectServerWithSocketPair(socketFd);
161 } else {
162 auto runtime = weak.lock();
163 CHECK_NULL_VOID(runtime);
164 if (isDebugApp) {
165 JSNApi::StopDebugger(ParseHdcRegisterOption(option));
166 }
167 runtime->StartDebuggerForSocketPair(option, socketFd);
168 }
169 };
170
171 HdcRegister::Get().StartHdcRegister(instanceId_, callback);
172 ConnectServerManager::Get().SetDebugMode();
173 #endif
174 JSNApi::DebugOption debugOption = { libPath_.c_str(), isDebugApp ? isDebugMode_ : false };
175 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
176 ConnectServerManager::Get().AddInstance(gettid(), language_);
177 ret = JSNApi::NotifyDebugMode(gettid(), vm_, debugOption, gettid(), debuggerPostTask_, isDebugApp);
178 #elif defined(ANDROID_PLATFORM)
179 ret = JSNApi::StartDebugger(vm_, debugOption, instanceId_, debuggerPostTask_);
180 #endif
181 }
182 #if defined(IOS_PLATFORM)
183 JSNApi::DebugOption debugOption = { nullptr, isDebugMode_ };
184 ret = JSNApi::StartDebugger(vm_, debugOption, instanceId_, debuggerPostTask_);
185 #endif
186 #endif
187 return ret;
188 }
189
ExecuteModuleBuffer(const uint8_t * data,int32_t size,const std::string & filename,bool needUpdate)190 bool ArkJSRuntime::ExecuteModuleBuffer(const uint8_t* data, int32_t size, const std::string& filename, bool needUpdate)
191 {
192 JSExecutionScope executionScope(vm_);
193 LocalScope scope(vm_);
194 panda::TryCatch trycatch(vm_);
195 bool ret = JSNApi::ExecuteModuleBuffer(vm_, data, size, filename, needUpdate);
196 HandleUncaughtException(trycatch);
197 return ret;
198 }
199
EvaluateJsCode(const std::string & src)200 shared_ptr<JsValue> ArkJSRuntime::EvaluateJsCode([[maybe_unused]] const std::string& src)
201 {
202 return NewUndefined();
203 }
204
EvaluateJsCode(const uint8_t * buffer,int32_t size,const std::string & filePath,bool needUpdate)205 bool ArkJSRuntime::EvaluateJsCode(const uint8_t* buffer, int32_t size, const std::string& filePath, bool needUpdate)
206 {
207 JSExecutionScope executionScope(vm_);
208 LocalScope scope(vm_);
209 panda::TryCatch trycatch(vm_);
210 bool ret = JSNApi::Execute(vm_, buffer, size, PANDA_MAIN_FUNCTION, filePath, needUpdate);
211 HandleUncaughtException(trycatch);
212 return ret;
213 }
214
ExecuteJsBin(const std::string & fileName,const std::function<void (const std::string &,int32_t)> & errorCallback)215 bool ArkJSRuntime::ExecuteJsBin(const std::string& fileName,
216 const std::function<void(const std::string&, int32_t)>& errorCallback)
217 {
218 JSExecutionScope executionScope(vm_);
219 LocalScope scope(vm_);
220 panda::TryCatch trycatch(vm_);
221 bool ret = JSNApi::Execute(vm_, fileName, PANDA_MAIN_FUNCTION);
222 HandleUncaughtException(trycatch, errorCallback);
223 return ret;
224 }
225
GetGlobal()226 shared_ptr<JsValue> ArkJSRuntime::GetGlobal()
227 {
228 LocalScope scope(vm_);
229 return std::make_shared<ArkJSValue>(shared_from_this(), JSNApi::GetGlobalObject(vm_));
230 }
231
RunGC()232 void ArkJSRuntime::RunGC()
233 {
234 JSExecutionScope executionScope(vm_);
235 LocalScope scope(vm_);
236 JSNApi::TriggerGC(vm_);
237 }
238
RunFullGC()239 void ArkJSRuntime::RunFullGC()
240 {
241 JSExecutionScope executionScope(vm_);
242 LocalScope scope(vm_);
243 JSNApi::TriggerGC(vm_, JSNApi::TRIGGER_GC_TYPE::FULL_GC);
244 }
245
NewInt32(int32_t value)246 shared_ptr<JsValue> ArkJSRuntime::NewInt32(int32_t value)
247 {
248 LocalScope scope(vm_);
249 return std::make_shared<ArkJSValue>(shared_from_this(), IntegerRef::New(vm_, value));
250 }
251
NewBoolean(bool value)252 shared_ptr<JsValue> ArkJSRuntime::NewBoolean(bool value)
253 {
254 LocalScope scope(vm_);
255 return std::make_shared<ArkJSValue>(shared_from_this(), BooleanRef::New(vm_, value));
256 }
257
NewNumber(double d)258 shared_ptr<JsValue> ArkJSRuntime::NewNumber(double d)
259 {
260 LocalScope scope(vm_);
261 return std::make_shared<ArkJSValue>(shared_from_this(), NumberRef::New(vm_, d));
262 }
263
NewNull()264 shared_ptr<JsValue> ArkJSRuntime::NewNull()
265 {
266 LocalScope scope(vm_);
267 return std::make_shared<ArkJSValue>(shared_from_this(), JSValueRef::Null(vm_));
268 }
269
NewUndefined()270 shared_ptr<JsValue> ArkJSRuntime::NewUndefined()
271 {
272 LocalScope scope(vm_);
273 return std::make_shared<ArkJSValue>(shared_from_this(), JSValueRef::Undefined(vm_));
274 }
275
NewString(const std::string & str)276 shared_ptr<JsValue> ArkJSRuntime::NewString(const std::string& str)
277 {
278 LocalScope scope(vm_);
279 return std::make_shared<ArkJSValue>(shared_from_this(), StringRef::NewFromUtf8(vm_, str.c_str()));
280 }
281
ParseJson(const std::string & str)282 shared_ptr<JsValue> ArkJSRuntime::ParseJson(const std::string& str)
283 {
284 LocalScope scope(vm_);
285 Local<StringRef> string = StringRef::NewFromUtf8(vm_, str.c_str());
286 return std::make_shared<ArkJSValue>(shared_from_this(), JSON::Parse(vm_, string));
287 }
288
NewObject()289 shared_ptr<JsValue> ArkJSRuntime::NewObject()
290 {
291 LocalScope scope(vm_);
292 return std::make_shared<ArkJSValue>(shared_from_this(), ObjectRef::New(vm_));
293 }
294
NewArray()295 shared_ptr<JsValue> ArkJSRuntime::NewArray()
296 {
297 LocalScope scope(vm_);
298 return std::make_shared<ArkJSValue>(shared_from_this(), ArrayRef::New(vm_));
299 }
300
NewFunction(RegisterFunctionType func)301 shared_ptr<JsValue> ArkJSRuntime::NewFunction(RegisterFunctionType func)
302 {
303 LocalScope scope(vm_);
304 auto data = new PandaFunctionData(shared_from_this(), func);
305 return std::make_shared<ArkJSValue>(shared_from_this(),
306 FunctionRef::New(vm_, FunctionCallback, FunctionDeleter, data));
307 }
308
NewNativePointer(void * ptr)309 shared_ptr<JsValue> ArkJSRuntime::NewNativePointer(void* ptr)
310 {
311 LocalScope scope(vm_);
312 return std::make_shared<ArkJSValue>(shared_from_this(), NativePointerRef::New(vm_, ptr));
313 }
314
ThrowError(const std::string & msg,int32_t code)315 void ArkJSRuntime::ThrowError(const std::string& msg, int32_t code)
316 {
317 auto errorVal = panda::Exception::Error(vm_, panda::StringRef::NewFromUtf8(vm_, msg.c_str()));
318 auto codeVal = panda::Exception::Error(vm_, panda::StringRef::NewFromUtf8(vm_, std::to_string(code).c_str()));
319 Local<StringRef> codeKey = StringRef::NewFromUtf8(vm_, "code");
320 Local<ObjectRef> errorObj(errorVal);
321 errorObj->Set(vm_, codeKey, codeVal);
322 panda::JSNApi::ThrowException(vm_, errorObj);
323 }
324
RegisterUncaughtExceptionHandler(UncaughtExceptionCallback callback)325 void ArkJSRuntime::RegisterUncaughtExceptionHandler(UncaughtExceptionCallback callback)
326 {
327 JSNApi::EnableUserUncaughtErrorHandler(vm_);
328 JSNApi::RegisterUncatchableErrorHandler(vm_,
329 std::bind(&ArkJSRuntime::HandleUncaughtExceptionWithoutNativeEngine, this, std::placeholders::_1, nullptr));
330 uncaughtErrorHandler_ = callback;
331 }
332
HasPendingException()333 bool ArkJSRuntime::HasPendingException()
334 {
335 return JSNApi::HasPendingException(vm_);
336 }
337
HandleUncaughtException(panda::TryCatch & trycatch,const std::function<void (const std::string &,int32_t)> & errorCallback)338 void ArkJSRuntime::HandleUncaughtException(panda::TryCatch& trycatch,
339 const std::function<void(const std::string&, int32_t)>& errorCallback)
340 {
341 if (errorCallback != nullptr) {
342 Local<ObjectRef> exception = trycatch.GetAndClearException();
343 if (!exception.IsEmpty() && !exception->IsHole()) {
344 errorCallback("loading js file has crash or the uri of router is not exist.",
345 ERROR_CODE_URI_ERROR);
346 return;
347 }
348 }
349
350 // Handle the uncaught exception by native engine created by ability runtime in the stage model project.
351 if (nativeEngine_) {
352 nativeEngine_->HandleUncaughtException();
353 return;
354 }
355
356 // Handle the uncaught exception without native engine, such as oom error
357 HandleUncaughtExceptionWithoutNativeEngine(trycatch, errorCallback);
358 }
359
HandleUncaughtExceptionWithoutNativeEngine(panda::TryCatch & trycatch,const std::function<void (const std::string &,int32_t)> & errorCallback)360 void ArkJSRuntime::HandleUncaughtExceptionWithoutNativeEngine(panda::TryCatch& trycatch,
361 const std::function<void(const std::string&, int32_t)>& errorCallback)
362 {
363 if (uncaughtErrorHandler_ == nullptr) {
364 return;
365 }
366
367 Local<ObjectRef> exception = trycatch.GetAndClearException();
368 if (!exception.IsEmpty() && !exception->IsHole()) {
369 shared_ptr<JsValue> errorPtr =
370 std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(shared_from_this(), exception));
371 uncaughtErrorHandler_(errorPtr, shared_from_this());
372 }
373 }
374
ExecutePendingJob()375 void ArkJSRuntime::ExecutePendingJob()
376 {
377 LocalScope scope(vm_);
378 JSNApi::ExecutePendingJob(vm_);
379 }
380
381 #if !defined(PREVIEW) && !defined(IOS_PLATFORM)
DumpHeapSnapshot(bool isPrivate)382 void ArkJSRuntime::DumpHeapSnapshot(bool isPrivate)
383 {
384 LocalScope scope(vm_);
385 panda::DFXJSNApi::DumpHeapSnapshot(vm_, FORMAT_JSON, true, isPrivate);
386 }
387 #else
DumpHeapSnapshot(bool isPrivate)388 void ArkJSRuntime::DumpHeapSnapshot(bool isPrivate)
389 {
390 LOGE("Do not support Ark DumpHeapSnapshot on Windows or MacOS");
391 }
392 #endif
393
394 #if !defined(PREVIEW) && !defined(IOS_PLATFORM)
DestroyHeapProfiler()395 void ArkJSRuntime::DestroyHeapProfiler()
396 {
397 LocalScope scope(vm_);
398 panda::DFXJSNApi::DestroyHeapProfiler(vm_);
399 }
400 #else
DestroyHeapProfiler()401 void ArkJSRuntime::DestroyHeapProfiler()
402 {
403 LOGE("Do not support Ark DestroyHeapProfiler on Windows or MacOS");
404 }
405 #endif
406
Callback(panda::JsiRuntimeCallInfo * info) const407 Local<JSValueRef> PandaFunctionData::Callback(panda::JsiRuntimeCallInfo* info) const
408 {
409 auto runtime = runtime_.lock();
410 if (runtime == nullptr) {
411 return Local<JSValueRef>();
412 }
413 EscapeLocalScope scope(runtime->GetEcmaVm());
414 shared_ptr<JsValue> thisPtr =
415 std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(runtime, info->GetThisRef()));
416
417 std::vector<shared_ptr<JsValue>> argv;
418 int32_t length = info->GetArgsNumber();
419 argv.reserve(length);
420 for (int32_t i = 0; i < length; ++i) {
421 argv.emplace_back(
422 std::static_pointer_cast<JsValue>(std::make_shared<ArkJSValue>(runtime, info->GetCallArgRef(i))));
423 }
424 shared_ptr<JsValue> result = func_(runtime, thisPtr, argv, length);
425 return scope.Escape(std::static_pointer_cast<ArkJSValue>(result)->GetValue(runtime));
426 }
427
428 } // namespace OHOS::Ace::Framework
429