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