1 /*
2 * Copyright (c) 2021 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 "js_runtime.h"
17
18 #include <cerrno>
19 #include <climits>
20 #include <cstdlib>
21 #include <fstream>
22
23 #include "native_engine/impl/ark/ark_native_engine.h"
24 #include "core/common/container_scope.h"
25 #include "declarative_module_preloader.h"
26
27 #include "event_handler.h"
28 #include "hilog_wrapper.h"
29 #include "js_runtime_utils.h"
30
31 #include "systemcapability.h"
32 #include "parameters.h"
33
34 namespace OHOS {
35 namespace AbilityRuntime {
36 namespace {
37 constexpr uint8_t SYSCAP_MAX_SIZE = 64;
38 constexpr int64_t DEFAULT_GC_POOL_SIZE = 0x10000000; // 256MB
39 #if defined(_ARM64_)
40 constexpr char ARK_DEBUGGER_LIB_PATH[] = "/system/lib64/libark_debugger.z.so";
41 #else
42 constexpr char ARK_DEBUGGER_LIB_PATH[] = "/system/lib/libark_debugger.z.so";
43 #endif
44
45 class ArkJsRuntime : public JsRuntime {
46 public:
ArkJsRuntime()47 ArkJsRuntime()
48 {
49 isArkEngine_ = true;
50 }
51
~ArkJsRuntime()52 ~ArkJsRuntime() override
53 {
54 Deinitialize();
55 if (vm_ != nullptr) {
56 panda::JSNApi::DestroyJSVM(vm_);
57 vm_ = nullptr;
58 }
59 }
60
StartDebugMode(bool needBreakPoint,int32_t instanceId)61 void StartDebugMode(bool needBreakPoint, int32_t instanceId) override
62 {
63 if (!debugMode_) {
64 HILOG_INFO("Ark VM is starting debug mode [%{public}s]", needBreakPoint ? "break" : "normal");
65 auto&& debuggerPostTask = [eventHandler = eventHandler_](std::function<void()>&& task) {
66 eventHandler->PostTask(task);
67 };
68 panda::JSNApi::StartDebugger(ARK_DEBUGGER_LIB_PATH, vm_, needBreakPoint, instanceId,
69 std::move(debuggerPostTask));
70 debugMode_ = true;
71 }
72 }
73
RunScript(const std::string & path)74 bool RunScript(const std::string& path) override
75 {
76 static const char PANDA_MAIN_FUNCTION[] = "_GLOBAL::func_main_0";
77 return vm_ != nullptr ? panda::JSNApi::Execute(vm_, path.c_str(), PANDA_MAIN_FUNCTION) : false;
78 }
79
80 private:
PrintVmLog(int32_t id,int32_t level,const char * tag,const char * fmt,const char * message)81 static int32_t PrintVmLog(int32_t id, int32_t level, const char* tag, const char* fmt, const char* message)
82 {
83 HILOG_INFO("ArkLog: %{public}s", message);
84 return 0;
85 }
86
Initialize(const Runtime::Options & options)87 bool Initialize(const Runtime::Options& options) override
88 {
89 panda::RuntimeOption pandaOption;
90 int arkProperties = OHOS::system::GetIntParameter<int>("persist.ark.properties", -1);
91 pandaOption.SetArkProperties(arkProperties);
92 HILOG_INFO("ArkJSRuntime::Initialize ark properties = %{public}d", arkProperties);
93 pandaOption.SetGcType(panda::RuntimeOption::GC_TYPE::GEN_GC);
94 pandaOption.SetGcPoolSize(DEFAULT_GC_POOL_SIZE);
95 pandaOption.SetLogLevel(panda::RuntimeOption::LOG_LEVEL::ERROR);
96 pandaOption.SetLogBufPrint(PrintVmLog);
97 vm_ = panda::JSNApi::CreateJSVM(pandaOption);
98 if (vm_ == nullptr) {
99 return false;
100 }
101
102 nativeEngine_ = std::make_unique<ArkNativeEngine>(vm_, static_cast<JsRuntime*>(this));
103 return JsRuntime::Initialize(options);
104 }
105
106 panda::ecmascript::EcmaVM* vm_ = nullptr;
107 };
108
GetLogContent(NativeCallbackInfo & info)109 std::string GetLogContent(NativeCallbackInfo& info)
110 {
111 std::string content;
112
113 for (size_t i = 0; i < info.argc; i++) {
114 NativeValue* value = info.argv[i];
115 if (value->TypeOf() != NATIVE_STRING) {
116 value = value->ToString();
117 }
118
119 NativeString* str = ConvertNativeValueTo<NativeString>(value);
120 if (str == nullptr) {
121 continue;
122 }
123
124 size_t bufferLen = str->GetLength();
125 auto buffer = std::make_unique<char[]>(bufferLen + 1);
126 if (buffer == nullptr) {
127 break;
128 }
129
130 size_t strLen = 0;
131 str->GetCString(buffer.get(), bufferLen + 1, &strLen);
132 if (!content.empty()) {
133 content.append(" ");
134 }
135 content.append(buffer.get());
136 }
137
138 return content;
139 }
140
141 template<LogLevel LEVEL>
ConsoleLog(NativeEngine * engine,NativeCallbackInfo * info)142 NativeValue* ConsoleLog(NativeEngine* engine, NativeCallbackInfo* info)
143 {
144 if (engine == nullptr || info == nullptr) {
145 HILOG_ERROR("engine or callback info is nullptr");
146 return nullptr;
147 }
148
149 std::string content = GetLogContent(*info);
150 HiLogPrint(LOG_APP, LEVEL, AMS_LOG_DOMAIN, "JsApp", "%{public}s", content.c_str());
151
152 return engine->CreateUndefined();
153 }
154
InitConsoleLogModule(NativeEngine & engine,NativeObject & globalObject)155 void InitConsoleLogModule(NativeEngine& engine, NativeObject& globalObject)
156 {
157 NativeValue* consoleValue = engine.CreateObject();
158 NativeObject* consoleObj = ConvertNativeValueTo<NativeObject>(consoleValue);
159 if (consoleObj == nullptr) {
160 HILOG_ERROR("Failed to create console object");
161 return;
162 }
163
164 BindNativeFunction(engine, *consoleObj, "log", ConsoleLog<LOG_INFO>);
165 BindNativeFunction(engine, *consoleObj, "debug", ConsoleLog<LOG_DEBUG>);
166 BindNativeFunction(engine, *consoleObj, "info", ConsoleLog<LOG_INFO>);
167 BindNativeFunction(engine, *consoleObj, "warn", ConsoleLog<LOG_WARN>);
168 BindNativeFunction(engine, *consoleObj, "error", ConsoleLog<LOG_ERROR>);
169 BindNativeFunction(engine, *consoleObj, "fatal", ConsoleLog<LOG_FATAL>);
170
171 globalObject.SetProperty("console", consoleValue);
172 }
173
SetTimeout(NativeEngine * engine,NativeCallbackInfo * info)174 NativeValue* SetTimeout(NativeEngine* engine, NativeCallbackInfo* info)
175 {
176 if (engine == nullptr || info == nullptr) {
177 HILOG_ERROR("Set timeout failed with engine or callback info is nullptr.");
178 return nullptr;
179 }
180
181 JsRuntime& jsRuntime = *reinterpret_cast<JsRuntime*>(engine->GetJsEngine());
182 return jsRuntime.SetCallbackTimer(*engine, *info, false);
183 }
184
CanIUse(NativeEngine * engine,NativeCallbackInfo * info)185 NativeValue* CanIUse(NativeEngine* engine, NativeCallbackInfo* info)
186 {
187 if (engine == nullptr || info == nullptr) {
188 HILOG_ERROR("get syscap failed since engine or callback info is nullptr.");
189 return nullptr;
190 }
191
192 if (info->argc != 1 || info->argv[0]->TypeOf() != NATIVE_STRING) {
193 HILOG_ERROR("Get syscap failed with invalid parameter.");
194 return engine->CreateUndefined();
195 }
196
197 char syscap[SYSCAP_MAX_SIZE] = { 0 };
198
199 NativeString* str = ConvertNativeValueTo<NativeString>(info->argv[0]);
200 if (str == nullptr) {
201 HILOG_ERROR("Convert to NativeString failed.");
202 return engine->CreateUndefined();
203 }
204 size_t bufferLen = str->GetLength();
205 size_t strLen = 0;
206 str->GetCString(syscap, bufferLen + 1, &strLen);
207
208 bool ret = HasSystemCapability(syscap);
209 return engine->CreateBoolean(ret);
210 }
211
SetInterval(NativeEngine * engine,NativeCallbackInfo * info)212 NativeValue* SetInterval(NativeEngine* engine, NativeCallbackInfo* info)
213 {
214 if (engine == nullptr || info == nullptr) {
215 HILOG_ERROR("Set interval failed with engine or callback info is nullptr.");
216 return nullptr;
217 }
218
219 JsRuntime& jsRuntime = *reinterpret_cast<JsRuntime*>(engine->GetJsEngine());
220 return jsRuntime.SetCallbackTimer(*engine, *info, true);
221 }
222
ClearTimeoutOrInterval(NativeEngine * engine,NativeCallbackInfo * info)223 NativeValue* ClearTimeoutOrInterval(NativeEngine* engine, NativeCallbackInfo* info)
224 {
225 if (engine == nullptr || info == nullptr) {
226 HILOG_ERROR("Clear timer failed with engine or callback info is nullptr.");
227 return nullptr;
228 }
229
230 JsRuntime& jsRuntime = *reinterpret_cast<JsRuntime*>(engine->GetJsEngine());
231 return jsRuntime.ClearCallbackTimer(*engine, *info);
232 }
233
InitTimerModule(NativeEngine & engine,NativeObject & globalObject)234 void InitTimerModule(NativeEngine& engine, NativeObject& globalObject)
235 {
236 BindNativeFunction(engine, globalObject, "setTimeout", SetTimeout);
237 BindNativeFunction(engine, globalObject, "setInterval", SetInterval);
238 BindNativeFunction(engine, globalObject, "clearTimeout", ClearTimeoutOrInterval);
239 BindNativeFunction(engine, globalObject, "clearInterval", ClearTimeoutOrInterval);
240 }
241
InitSyscapModule(NativeEngine & engine,NativeObject & globalObject)242 void InitSyscapModule(NativeEngine& engine, NativeObject& globalObject)
243 {
244 BindNativeFunction(engine, globalObject, "canIUse", CanIUse);
245 }
246
MakeFilePath(const std::string & codePath,const std::string & modulePath,std::string & fileName)247 bool MakeFilePath(const std::string& codePath, const std::string& modulePath, std::string& fileName)
248 {
249 std::string path(codePath);
250 path.append("/").append(modulePath);
251 if (path.length() > PATH_MAX) {
252 HILOG_ERROR("Path length(%{public}d) longer than MAX(%{public}d)", (int32_t)path.length(), PATH_MAX);
253 return false;
254 }
255 char resolvedPath[PATH_MAX + 1] = { 0 };
256 if (realpath(path.c_str(), resolvedPath) != nullptr) {
257 fileName = resolvedPath;
258 return true;
259 }
260
261 auto start = path.find_last_of('/');
262 auto end = path.find_last_of('.');
263 if (end == std::string::npos || end == 0) {
264 HILOG_ERROR("No secondary file path");
265 return false;
266 }
267
268 auto pos = path.find_last_of('.', end - 1);
269 if (pos == std::string::npos) {
270 HILOG_ERROR("No secondary file path");
271 return false;
272 }
273
274 path.erase(start + 1, pos - start);
275 HILOG_INFO("Try using secondary file path: %{public}s", path.c_str());
276
277 if (realpath(path.c_str(), resolvedPath) == nullptr) {
278 HILOG_ERROR("Failed to call realpath, errno = %{public}d", errno);
279 return false;
280 }
281
282 fileName = resolvedPath;
283 return true;
284 }
285 } // namespace
286
Create(const Runtime::Options & options)287 std::unique_ptr<Runtime> JsRuntime::Create(const Runtime::Options& options)
288 {
289 std::unique_ptr<JsRuntime> instance = std::make_unique<ArkJsRuntime>();
290 if (!instance->Initialize(options)) {
291 return std::unique_ptr<Runtime>();
292 }
293 return instance;
294 }
295
Initialize(const Options & options)296 bool JsRuntime::Initialize(const Options& options)
297 {
298 // Create event handler for runtime
299 eventHandler_ = std::make_shared<AppExecFwk::EventHandler>(options.eventRunner);
300 nativeEngine_->SetPostTask([this](bool needSync) {
301 eventHandler_->PostTask(
302 [this, needSync]() {
303 nativeEngine_->Loop(LOOP_NOWAIT, needSync);
304 },
305 "idleTask");
306 });
307 nativeEngine_->CheckUVLoop();
308
309 HandleScope handleScope(*this);
310
311 NativeObject* globalObj = ConvertNativeValueTo<NativeObject>(nativeEngine_->GetGlobal());
312 if (globalObj == nullptr) {
313 HILOG_ERROR("Failed to get global object");
314 return false;
315 }
316
317 InitConsoleLogModule(*nativeEngine_, *globalObj);
318 InitTimerModule(*nativeEngine_, *globalObj);
319 InitSyscapModule(*nativeEngine_, *globalObj);
320
321 // Simple hook function 'isSystemplugin'
322 BindNativeFunction(*nativeEngine_, *globalObj, "isSystemplugin",
323 [](NativeEngine* engine, NativeCallbackInfo* info) -> NativeValue* {
324 return engine->CreateUndefined();
325 });
326
327 methodRequireNapiRef_.reset(nativeEngine_->CreateReference(globalObj->GetProperty("requireNapi"), 1));
328 if (!methodRequireNapiRef_) {
329 HILOG_ERROR("Failed to create reference for global.requireNapi");
330 return false;
331 }
332
333 if (options.loadAce) {
334 OHOS::Ace::DeclarativeModulePreloader::Preload(*nativeEngine_);
335 }
336 codePath_ = options.codePath;
337
338 auto moduleManager = NativeModuleManager::GetInstance();
339 std::string packagePath = options.packagePath;
340 if (moduleManager && !packagePath.empty()) {
341 moduleManager->SetAppLibPath(packagePath.c_str());
342 }
343
344 return true;
345 }
346
Deinitialize()347 void JsRuntime::Deinitialize()
348 {
349 for (auto it = modules_.begin(); it != modules_.end(); it = modules_.erase(it)) {
350 delete it->second;
351 it->second = nullptr;
352 }
353
354 methodRequireNapiRef_.reset();
355 nativeEngine_->CancelCheckUVLoop();
356 RemoveTask("idleTask");
357 nativeEngine_.reset();
358 }
359
LoadModule(const std::string & moduleName,const std::string & modulePath)360 std::unique_ptr<NativeReference> JsRuntime::LoadModule(const std::string& moduleName, const std::string& modulePath)
361 {
362 HILOG_INFO("JsRuntime::LoadModule(%{public}s, %{public}s)", moduleName.c_str(), modulePath.c_str());
363
364 HandleScope handleScope(*this);
365
366 NativeValue* classValue = nullptr;
367
368 auto it = modules_.find(modulePath);
369 if (it != modules_.end()) {
370 classValue = it->second->Get();
371 } else {
372 std::string fileName;
373 if (!MakeFilePath(codePath_, modulePath, fileName)) {
374 HILOG_ERROR("Failed to make module file path: %{private}s", modulePath.c_str());
375 return std::unique_ptr<NativeReference>();
376 }
377
378 NativeObject* globalObj = ConvertNativeValueTo<NativeObject>(nativeEngine_->GetGlobal());
379 NativeValue* exports = nativeEngine_->CreateObject();
380 globalObj->SetProperty("exports", exports);
381
382 if (!RunScript(fileName)) {
383 HILOG_ERROR("Failed to run script: %{public}s", fileName.c_str());
384 return std::unique_ptr<NativeReference>();
385 }
386
387 NativeObject* exportsObj = ConvertNativeValueTo<NativeObject>(globalObj->GetProperty("exports"));
388 if (exportsObj == nullptr) {
389 HILOG_ERROR("Failed to get exports objcect: %{public}s", modulePath.c_str());
390 return std::unique_ptr<NativeReference>();
391 }
392
393 classValue = exportsObj->GetProperty("default");
394 if (classValue == nullptr) {
395 HILOG_ERROR("Failed to get default objcect: %{public}s", modulePath.c_str());
396 return std::unique_ptr<NativeReference>();
397 }
398
399 modules_.emplace(modulePath, nativeEngine_->CreateReference(classValue, 1));
400 }
401
402 NativeValue* instanceValue = nativeEngine_->CreateInstance(classValue, nullptr, 0);
403 if (instanceValue == nullptr) {
404 HILOG_ERROR("Failed to create object instance");
405 return std::unique_ptr<NativeReference>();
406 }
407
408 return std::unique_ptr<NativeReference>(nativeEngine_->CreateReference(instanceValue, 1));
409 }
410
LoadSystemModule(const std::string & moduleName,NativeValue * const * argv,size_t argc)411 std::unique_ptr<NativeReference> JsRuntime::LoadSystemModule(
412 const std::string& moduleName, NativeValue* const* argv, size_t argc)
413 {
414 HILOG_INFO("JsRuntime::LoadSystemModule(%{public}s)", moduleName.c_str());
415
416 HandleScope handleScope(*this);
417
418 NativeValue* className = nativeEngine_->CreateString(moduleName.c_str(), moduleName.length());
419 NativeValue* classValue =
420 nativeEngine_->CallFunction(nativeEngine_->GetGlobal(), methodRequireNapiRef_->Get(), &className, 1);
421 NativeValue* instanceValue = nativeEngine_->CreateInstance(classValue, argv, argc);
422 if (instanceValue == nullptr) {
423 HILOG_ERROR("Failed to create object instance");
424 return std::unique_ptr<NativeReference>();
425 }
426
427 return std::unique_ptr<NativeReference>(nativeEngine_->CreateReference(instanceValue, 1));
428 }
429
RunScript(const std::string & path)430 bool JsRuntime::RunScript(const std::string& path)
431 {
432 return nativeEngine_->RunScript(path.c_str()) != nullptr;
433 }
434
RunSandboxScript(const std::string & path)435 bool JsRuntime::RunSandboxScript(const std::string& path)
436 {
437 std::string fileName;
438 if (!MakeFilePath(codePath_, path, fileName)) {
439 HILOG_ERROR("Failed to make module file path: %{private}s", path.c_str());
440 return false;
441 }
442
443 if (!RunScript(fileName)) {
444 HILOG_ERROR("Failed to run script: %{public}s", fileName.c_str());
445 return false;
446 }
447 return true;
448 }
449
450 using OHOS::Ace::ContainerScope;
451 class TimerTask final {
452 public:
TimerTask(JsRuntime & jsRuntime,std::shared_ptr<NativeReference> jsFunction,const std::string & name,int64_t interval)453 TimerTask(
454 JsRuntime& jsRuntime, std::shared_ptr<NativeReference> jsFunction, const std::string &name, int64_t interval)
455 : jsRuntime_(jsRuntime), jsFunction_(jsFunction), name_(name), interval_(interval)
456 {
457 containerScopeId_ = ContainerScope::CurrentId();
458 }
459
460 ~TimerTask() = default;
461
operator ()()462 void operator()()
463 {
464 if (interval_ > 0) {
465 jsRuntime_.PostTask(*this, name_, interval_);
466 }
467
468 // call js function
469 ContainerScope containerScope(containerScopeId_);
470 HandleScope handleScope(jsRuntime_);
471
472 std::vector<NativeValue*> args_;
473 args_.reserve(jsArgs_.size());
474 for (auto arg : jsArgs_) {
475 args_.emplace_back(arg->Get());
476 }
477
478 NativeEngine& engine = jsRuntime_.GetNativeEngine();
479 engine.CallFunction(engine.CreateUndefined(), jsFunction_->Get(), args_.data(), args_.size());
480 }
481
PushArgs(std::shared_ptr<NativeReference> ref)482 void PushArgs(std::shared_ptr<NativeReference> ref)
483 {
484 jsArgs_.emplace_back(ref);
485 }
486
487 private:
488 JsRuntime& jsRuntime_;
489 std::shared_ptr<NativeReference> jsFunction_;
490 std::vector<std::shared_ptr<NativeReference>> jsArgs_;
491 std::string name_;
492 int64_t interval_ = 0;
493 int32_t containerScopeId_ = 0;
494 };
495
PostTask(const TimerTask & task,const std::string & name,int64_t delayTime)496 void JsRuntime::PostTask(const TimerTask& task, const std::string& name, int64_t delayTime)
497 {
498 eventHandler_->PostTask(task, name, delayTime);
499 }
500
RemoveTask(const std::string & name)501 void JsRuntime::RemoveTask(const std::string& name)
502 {
503 eventHandler_->RemoveTask(name);
504 }
505
SetCallbackTimer(NativeEngine & engine,NativeCallbackInfo & info,bool isInterval)506 NativeValue* JsRuntime::SetCallbackTimer(NativeEngine& engine, NativeCallbackInfo& info, bool isInterval)
507 {
508 // parameter check, must have at least 2 params
509 if (info.argc < 2 || info.argv[0]->TypeOf() != NATIVE_FUNCTION || info.argv[1]->TypeOf() != NATIVE_NUMBER) {
510 HILOG_ERROR("Set callback timer failed with invalid parameter.");
511 return engine.CreateUndefined();
512 }
513
514 // parse parameter
515 std::shared_ptr<NativeReference> jsFunction(engine.CreateReference(info.argv[0], 1));
516 int64_t delayTime = *ConvertNativeValueTo<NativeNumber>(info.argv[1]);
517 uint32_t callbackId = callbackId_++;
518 std::string name = "JsRuntimeTimer_";
519 name.append(std::to_string(callbackId));
520
521 // create timer task
522 TimerTask task(*this, jsFunction, name, isInterval ? delayTime : 0);
523 for (size_t index = 2; index < info.argc; ++index) {
524 task.PushArgs(std::shared_ptr<NativeReference>(engine.CreateReference(info.argv[index], 1)));
525 }
526
527 JsRuntime::PostTask(task, name, delayTime);
528 return engine.CreateNumber(callbackId);
529 }
530
ClearCallbackTimer(NativeEngine & engine,NativeCallbackInfo & info)531 NativeValue* JsRuntime::ClearCallbackTimer(NativeEngine& engine, NativeCallbackInfo& info)
532 {
533 // parameter check, must have at least 1 param
534 if (info.argc < 1 || info.argv[0]->TypeOf() != NATIVE_NUMBER) {
535 HILOG_ERROR("Clear callback timer failed with invalid parameter.");
536 return engine.CreateUndefined();
537 }
538
539 uint32_t callbackId = *ConvertNativeValueTo<NativeNumber>(info.argv[0]);
540 std::string name = "JsRuntimeTimer_";
541 name.append(std::to_string(callbackId));
542
543 // event should be cancelable before executed
544 JsRuntime::RemoveTask(name);
545 return engine.CreateUndefined();
546 }
547
BuildNativeAndJsBackStackTrace()548 std::string JsRuntime::BuildNativeAndJsBackStackTrace()
549 {
550 std::string straceStr = "";
551 // [[maybe_unused]]bool temp = nativeEngine_->BuildNativeAndJsBackStackTrace(straceStr)
552 return straceStr;
553 }
554 } // namespace AbilityRuntime
555 } // namespace OHOS
556