• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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