1 /*
2 * Copyright (c) 2022-2024 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 "simulator.h"
17
18 #include <condition_variable>
19 #include <fstream>
20 #include <functional>
21 #include <mutex>
22 #include <thread>
23 #include <unordered_map>
24
25 #include "ability_context.h"
26 #include "ability_stage_context.h"
27 #include "bundle_container.h"
28 #include "commonlibrary/ets_utils/js_sys_module/timer/timer.h"
29 #include "commonlibrary/ets_utils/js_sys_module/console/console.h"
30 #include "hilog_tag_wrapper.h"
31 #include "js_ability_context.h"
32 #include "js_ability_stage_context.h"
33 #include "js_console_log.h"
34 #include "js_data_converter.h"
35 #include "js_module_searcher.h"
36 #include "js_runtime.h"
37 #include "js_runtime_utils.h"
38 #include "js_timer.h"
39 #include "js_window_stage.h"
40 #include "json_serializer.h"
41 #include "launch_param.h"
42 #include "native_engine/impl/ark/ark_native_engine.h"
43 #include "resource_manager.h"
44 #include "window_scene.h"
45
46 extern const char _binary_jsMockSystemPlugin_abc_start[];
47 extern const char _binary_jsMockSystemPlugin_abc_end[];
48
49 namespace OHOS {
50 namespace AbilityRuntime {
51 namespace {
52 constexpr int64_t DEFAULT_GC_POOL_SIZE = 0x10000000; // 256MB
53 constexpr int32_t DEFAULT_ARK_PROPERTIES = -1;
54 constexpr size_t DEFAULT_GC_THREAD_NUM = 7;
55 constexpr size_t DEFAULT_LONG_PAUSE_TIME = 40;
56
57 constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/";
58 const std::string PACKAGE_NAME = "packageName";
59 const std::string BUNDLE_NAME = "bundleName";
60 const std::string MODULE_NAME = "moduleName";
61 const std::string VERSION = "version";
62 const std::string ENTRY_PATH = "entryPath";
63 const std::string IS_SO = "isSO";
64 const std::string DEPENDENCY_ALIAS = "dependencyAlias";
65
66 #if defined(WINDOWS_PLATFORM)
67 constexpr char ARK_DEBUGGER_LIB_PATH[] = "libark_inspector.dll";
68 #elif defined(MAC_PLATFORM)
69 constexpr char ARK_DEBUGGER_LIB_PATH[] = "libark_inspector.dylib";
70 #else
71 #error "Unsupported platform"
72 #endif
73
PrintVmLog(int32_t,int32_t,const char *,const char *,const char * message)74 int32_t PrintVmLog(int32_t, int32_t, const char*, const char*, const char *message)
75 {
76 TAG_LOGD(AAFwkTag::ABILITY_SIM, "ArkLog:%{public}s", message);
77 return 0;
78 }
79
80 template<typename T, size_t N>
ArraySize(T (&)[N])81 inline constexpr size_t ArraySize(T (&)[N]) noexcept
82 {
83 return N;
84 }
85
86 struct DebuggerTask {
87 void OnPostTask(std::function<void()> &&task);
88
89 static void HandleTask(const uv_async_t *req);
90
91 uv_async_t onPostTaskSignal {};
92 std::function<void()> func;
93 };
94
95 class SimulatorImpl : public Simulator, public std::enable_shared_from_this<SimulatorImpl> {
96 public:
97 SimulatorImpl() = default;
98 ~SimulatorImpl();
99
100 bool Initialize(const Options &options);
101
102 int64_t StartAbility(
103 const std::string &abilitySrcPath, TerminateCallback callback, const std::string &abilityName = "") override;
104 void TerminateAbility(int64_t abilityId) override;
105 void UpdateConfiguration(const AppExecFwk::Configuration &config) override;
106 void SetMockList(const std::map<std::string, std::string> &mockList) override;
107 void SetHostResolveBufferTracker(ResolveBufferTrackerCallback cb) override;
108 private:
109 bool OnInit();
110 void Run();
111 napi_value LoadScript(const std::string &srcPath);
112 void InitResourceMgr();
113 void InitJsAbilityContext(napi_env env, napi_value instanceValue);
114 void DispatchStartLifecycle(napi_value instanceValue);
115 std::unique_ptr<NativeReference> CreateJsWindowStage(const std::shared_ptr<Rosen::WindowScene> &windowScene);
116 napi_value CreateJsWant(napi_env env);
117 bool LoadAbilityStage(uint8_t *buffer, size_t len);
118 void InitJsAbilityStageContext(napi_value instanceValue);
119 napi_value CreateJsLaunchParam(napi_env env);
120 bool ParseBundleAndModuleInfo();
121 bool ParseAbilityInfo(const std::string &abilitySrcPath, const std::string &abilityName = "");
122 bool LoadRuntimeEnv(napi_env env, napi_value globalObject);
123 static napi_value RequireNapi(napi_env env, napi_callback_info info);
124 inline void SetHostResolveBufferTracker();
125 void LoadJsMock(const std::string &fileName);
126 void ReportJsError(napi_value obj);
127 std::string GetNativeStrFromJsTaggedObj(napi_value obj, const char* key);
128
129 panda::ecmascript::EcmaVM *CreateJSVM();
130 Options options_;
131 std::string abilityPath_;
132 panda::ecmascript::EcmaVM *vm_ = nullptr;
133 DebuggerTask debuggerTask_;
134 napi_env nativeEngine_ = nullptr;
135 TerminateCallback terminateCallback_;
136
137 int64_t currentId_ = 0;
138 std::unordered_map<int64_t, std::shared_ptr<NativeReference>> abilities_;
139 std::unordered_map<int64_t, std::shared_ptr<Rosen::WindowScene>> windowScenes_;
140 std::unordered_map<int64_t, std::shared_ptr<NativeReference>> jsWindowStages_;
141 std::unordered_map<int64_t, std::shared_ptr<NativeReference>> jsContexts_;
142 std::shared_ptr<Global::Resource::ResourceManager> resourceMgr_;
143 std::shared_ptr<AbilityContext> context_;
144 std::shared_ptr<NativeReference> abilityStage_;
145 std::shared_ptr<AbilityStageContext> stageContext_;
146 std::shared_ptr<NativeReference> jsStageContext_;
147 std::shared_ptr<AppExecFwk::ApplicationInfo> appInfo_;
148 std::shared_ptr<AppExecFwk::HapModuleInfo> moduleInfo_;
149 std::shared_ptr<AppExecFwk::AbilityInfo> abilityInfo_;
150 CallbackTypePostTask postTask_ = nullptr;
151 void GetPkgContextInfoListMap(const std::map<std::string, std::string> &contextInfoMap,
152 std::map<std::string, std::vector<std::vector<std::string>>> &pkgContextInfoMap,
153 std::map<std::string, std::string> &pkgAliasMap);
154 void GetPkgContextInfoListInner(nlohmann::json &itemObject, std::vector<std::string> &items,
155 std::map<std::string, std::string> &pkgAliasMap, std::string &pkgName);
156 };
157
HandleTask(const uv_async_t * req)158 void DebuggerTask::HandleTask(const uv_async_t *req)
159 {
160 auto *debuggerTask = reinterpret_cast<DebuggerTask*>(req->data);
161 if (debuggerTask == nullptr) {
162 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null HandleTask debuggerTask");
163 return;
164 }
165 debuggerTask->func();
166 }
167
OnPostTask(std::function<void ()> && task)168 void DebuggerTask::OnPostTask(std::function<void()> &&task)
169 {
170 if (uv_is_active((uv_handle_t*)&onPostTaskSignal)) {
171 func = std::move(task);
172 onPostTaskSignal.data = static_cast<void*>(this);
173 uv_async_send(&onPostTaskSignal);
174 }
175 }
176
~SimulatorImpl()177 SimulatorImpl::~SimulatorImpl()
178 {
179 if (nativeEngine_) {
180 uv_close(reinterpret_cast<uv_handle_t*>(&debuggerTask_.onPostTaskSignal), nullptr);
181 uv_loop_t* uvLoop = nullptr;
182 napi_get_uv_event_loop(nativeEngine_, &uvLoop);
183 if (uvLoop != nullptr) {
184 uv_work_t work;
185 uv_queue_work(uvLoop, &work, [](uv_work_t*) {}, [](uv_work_t *work, int32_t status) {
186 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Simulator stop uv loop");
187 uv_stop(work->loop);
188 });
189 }
190 }
191
192 panda::JSNApi::StopDebugger(vm_);
193
194 abilities_.clear();
195 nativeEngine_ = nullptr;
196 panda::JSNApi::DestroyJSVM(vm_);
197 vm_ = nullptr;
198 }
199
Initialize(const Options & options)200 bool SimulatorImpl::Initialize(const Options &options)
201 {
202 if (nativeEngine_) {
203 TAG_LOGE(AAFwkTag::ABILITY_SIM, "initialized");
204 return true;
205 }
206
207 options_ = options;
208 postTask_ = options.postTask;
209 if (!OnInit()) {
210 return false;
211 }
212
213 uv_loop_t* uvLoop = nullptr;
214 napi_get_uv_event_loop(nativeEngine_, &uvLoop);
215 if (uvLoop == nullptr) {
216 return false;
217 }
218
219 uv_async_init(uvLoop, &debuggerTask_.onPostTaskSignal,
220 reinterpret_cast<uv_async_cb>(DebuggerTask::HandleTask));
221
222 Run();
223 return true;
224 }
225
CallObjectMethod(napi_env env,napi_value obj,const char * name,napi_value const * argv,size_t argc)226 void CallObjectMethod(napi_env env, napi_value obj, const char *name, napi_value const *argv, size_t argc)
227 {
228 if (obj == nullptr) {
229 TAG_LOGE(AAFwkTag::ABILITY_SIM, "get Ability object failed");
230 return;
231 }
232 napi_value methodOnCreate = nullptr;
233 napi_get_named_property(env, obj, name, &methodOnCreate);
234 if (methodOnCreate == nullptr) {
235 TAG_LOGE(AAFwkTag::ABILITY_SIM, "get '%{public}s' failed", name);
236 return;
237 }
238 napi_status status = napi_call_function(env, obj, methodOnCreate, argc, argv, nullptr);
239 if (status != napi_ok) {
240 TAG_LOGE(AAFwkTag::ABILITY_SIM, "napi call function failed");
241 }
242 }
243
LoadScript(const std::string & srcPath)244 napi_value SimulatorImpl::LoadScript(const std::string &srcPath)
245 {
246 panda::Local<panda::ObjectRef> objRef = panda::JSNApi::GetExportObject(vm_, srcPath, "default");
247 if (objRef->IsNull()) {
248 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Get export object failed");
249 return nullptr;
250 }
251
252 auto obj = ArkNativeEngine::ArkValueToNapiValue(nativeEngine_, objRef);
253 napi_value instanceValue = nullptr;
254 napi_new_instance(nativeEngine_, obj, 0, nullptr, &instanceValue);
255 return instanceValue;
256 }
257
ParseBundleAndModuleInfo()258 bool SimulatorImpl::ParseBundleAndModuleInfo()
259 {
260 AppExecFwk::BundleContainer::GetInstance().LoadBundleInfos(options_.moduleJsonBuffer);
261 appInfo_ = AppExecFwk::BundleContainer::GetInstance().GetApplicationInfo();
262 if (appInfo_ == nullptr) {
263 TAG_LOGE(AAFwkTag::ABILITY_SIM, "appinfo parse failed");
264 return false;
265 }
266 nlohmann::json appInfoJson;
267 to_json(appInfoJson, *appInfo_);
268 std::cout << "appinfo : " << appInfoJson.dump() << std::endl;
269
270 options_.bundleName = appInfo_->bundleName;
271 options_.compatibleVersion = appInfo_->apiCompatibleVersion;
272 options_.installationFree = (appInfo_->bundleType == AppExecFwk::BundleType::ATOMIC_SERVICE ? true : false);
273 options_.targetVersion = appInfo_->apiTargetVersion;
274 options_.releaseType = appInfo_->apiReleaseType;
275 options_.compileMode = "esmodule";
276
277 if (appInfo_->moduleInfos.empty()) {
278 TAG_LOGE(AAFwkTag::ABILITY_SIM, "module name not exist");
279 return false;
280 }
281 options_.moduleName = appInfo_->moduleInfos[0].moduleName;
282 std::cout << "module name is " << options_.moduleName << std::endl;
283
284 moduleInfo_ = AppExecFwk::BundleContainer::GetInstance().GetHapModuleInfo(options_.moduleName);
285 if (moduleInfo_ == nullptr) {
286 TAG_LOGE(AAFwkTag::ABILITY_SIM, "module info parse failed");
287 return false;
288 }
289 nlohmann::json moduleInfoJson;
290 to_json(moduleInfoJson, *moduleInfo_);
291 std::cout << "moduleInfo : " << moduleInfoJson.dump() << std::endl;
292
293 options_.pageProfile = moduleInfo_->pages;
294 options_.enablePartialUpdate = true;
295 for (auto iter : moduleInfo_->metadata) {
296 if (iter.name == "ArkTSPartialUpdate" && iter.value == "false") {
297 options_.enablePartialUpdate = false;
298 break;
299 }
300 }
301 return true;
302 }
303
ParseAbilityInfo(const std::string & abilitySrcPath,const std::string & abilityName)304 bool SimulatorImpl::ParseAbilityInfo(const std::string &abilitySrcPath, const std::string &abilityName)
305 {
306 if (!abilityName.empty()) {
307 abilityInfo_ = AppExecFwk::BundleContainer::GetInstance().GetAbilityInfo(options_.moduleName, abilityName);
308 } else {
309 auto path = abilitySrcPath;
310 path.erase(path.rfind("."));
311 auto abilityNameFromPath = path.substr(path.rfind('/') + 1, path.length());
312 abilityInfo_ = AppExecFwk::BundleContainer::GetInstance().GetAbilityInfo(
313 options_.moduleName, abilityNameFromPath);
314 }
315
316 if (abilityInfo_ == nullptr) {
317 TAG_LOGE(AAFwkTag::ABILITY_SIM, "ability info parse failed");
318 return false;
319 }
320 nlohmann::json json;
321 to_json(json, *abilityInfo_);
322 std::cout << "abilityInfo : " << json.dump() << std::endl;
323
324 options_.labelId = abilityInfo_->labelId;
325 return true;
326 }
327
StartAbility(const std::string & abilitySrcPath,TerminateCallback callback,const std::string & abilityName)328 int64_t SimulatorImpl::StartAbility(
329 const std::string &abilitySrcPath, TerminateCallback callback, const std::string &abilityName)
330 {
331 if (!ParseAbilityInfo(abilitySrcPath, abilityName)) {
332 return -1;
333 }
334
335 if (stageContext_ == nullptr) {
336 stageContext_ = std::make_shared<AbilityStageContext>();
337 stageContext_->SetOptions(options_);
338 stageContext_->SetConfiguration(options_.configuration);
339 stageContext_->SetApplicationInfo(appInfo_);
340 stageContext_->SetHapModuleInfo(moduleInfo_);
341 }
342
343 std::ifstream stream(options_.modulePath, std::ios::ate | std::ios::binary);
344 if (!stream.is_open()) {
345 TAG_LOGE(AAFwkTag::ABILITY_SIM, "open:%{public}s failed", options_.modulePath.c_str());
346 return -1;
347 }
348
349 size_t len = stream.tellg();
350 std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(len);
351 stream.seekg(0);
352 stream.read(reinterpret_cast<char*>(buffer.get()), len);
353 stream.close();
354
355 auto buf = buffer.release();
356 if (!LoadAbilityStage(buf, len)) {
357 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Load ability stage failed");
358 return -1;
359 }
360
361 abilityPath_ = BUNDLE_INSTALL_PATH + options_.moduleName + "/" + abilitySrcPath;
362 if (!reinterpret_cast<NativeEngine*>(nativeEngine_)->RunScriptBuffer(abilityPath_, buf, len, false)) {
363 TAG_LOGE(AAFwkTag::ABILITY_SIM, "run script:%{public}s failed", abilityPath_.c_str());
364 return -1;
365 }
366
367 napi_value instanceValue = LoadScript(abilityPath_);
368 if (instanceValue == nullptr) {
369 TAG_LOGE(AAFwkTag::ABILITY_SIM, "create object instance failed");
370 return -1;
371 }
372
373 ++currentId_;
374 terminateCallback_ = callback;
375 InitResourceMgr();
376 InitJsAbilityContext(nativeEngine_, instanceValue);
377 DispatchStartLifecycle(instanceValue);
378 napi_ref ref = nullptr;
379 napi_create_reference(nativeEngine_, instanceValue, 1, &ref);
380 abilities_.emplace(currentId_, std::shared_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref)));
381 return currentId_;
382 }
383
LoadAbilityStage(uint8_t * buffer,size_t len)384 bool SimulatorImpl::LoadAbilityStage(uint8_t *buffer, size_t len)
385 {
386 if (moduleInfo_ == nullptr) {
387 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null moduleInfo");
388 return false;
389 }
390
391 if (moduleInfo_->srcEntrance.empty()) {
392 TAG_LOGD(AAFwkTag::ABILITY_SIM, "module src path empty");
393 return true;
394 }
395
396 if (nativeEngine_ == nullptr) {
397 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null nativeEngine_");
398 return false;
399 }
400 std::string srcEntrance = moduleInfo_->srcEntrance;
401 srcEntrance.erase(srcEntrance.rfind("."));
402 srcEntrance.append(".abc");
403 srcEntrance = srcEntrance.substr(srcEntrance.find('/') + 1, srcEntrance.length());
404
405 auto moduleSrcPath = BUNDLE_INSTALL_PATH + options_.moduleName + "/" + srcEntrance;
406 TAG_LOGD(AAFwkTag::ABILITY_SIM, "moduleSrcPath is %{public}s", moduleSrcPath.c_str());
407 if (!reinterpret_cast<NativeEngine*>(nativeEngine_)->RunScriptBuffer(moduleSrcPath, buffer, len, false)) {
408 TAG_LOGE(AAFwkTag::ABILITY_SIM, "run ability stage script:%{public}s failed", moduleSrcPath.c_str());
409 return false;
410 }
411
412 napi_value instanceValue = LoadScript(moduleSrcPath);
413 if (instanceValue == nullptr) {
414 TAG_LOGE(AAFwkTag::ABILITY_SIM, "create ability stage instance failed");
415 return false;
416 }
417
418 InitJsAbilityStageContext(instanceValue);
419 CallObjectMethod(nativeEngine_, instanceValue, "onCreate", nullptr, 0);
420
421 napi_value wantArgv[] = {
422 CreateJsWant(nativeEngine_)
423 };
424 CallObjectMethod(nativeEngine_, instanceValue, "onAcceptWant", wantArgv, ArraySize(wantArgv));
425 napi_ref ref = nullptr;
426 napi_create_reference(nativeEngine_, instanceValue, 1, &ref);
427 abilityStage_ = std::shared_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref));
428 return true;
429 }
430
InitJsAbilityStageContext(napi_value obj)431 void SimulatorImpl::InitJsAbilityStageContext(napi_value obj)
432 {
433 napi_value contextObj = CreateJsAbilityStageContext(nativeEngine_, stageContext_);
434 if (contextObj == nullptr) {
435 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null contextObj");
436 return;
437 }
438
439 jsStageContext_ = std::shared_ptr<NativeReference>(
440 JsRuntime::LoadSystemModuleByEngine(nativeEngine_, "application.AbilityStageContext", &contextObj, 1));
441 if (jsStageContext_ == nullptr) {
442 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null get LoadSystemModuleByEngine failed");
443 return;
444 }
445
446 contextObj = jsStageContext_->GetNapiValue();
447 if (contextObj == nullptr) {
448 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null contextObj");
449 return;
450 }
451
452 if (obj == nullptr) {
453 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null obj");
454 return;
455 }
456 napi_set_named_property(nativeEngine_, obj, "context", contextObj);
457 }
458
TerminateAbility(int64_t abilityId)459 void SimulatorImpl::TerminateAbility(int64_t abilityId)
460 {
461 if (abilityId == 0 && abilities_.begin() != abilities_.end()) {
462 TerminateAbility(abilities_.begin()->first);
463 return;
464 }
465
466 auto it = abilities_.find(abilityId);
467 if (it == abilities_.end()) {
468 return;
469 }
470
471 std::shared_ptr<NativeReference> ref = it->second;
472 abilities_.erase(it);
473
474 auto instanceValue = ref->GetNapiValue();
475 if (instanceValue == nullptr) {
476 return;
477 }
478
479 CallObjectMethod(nativeEngine_, instanceValue, "onBackground", nullptr, 0);
480 CallObjectMethod(nativeEngine_, instanceValue, "onWindowStageDestroy", nullptr, 0);
481 CallObjectMethod(nativeEngine_, instanceValue, "onDestroy", nullptr, 0);
482
483 auto windowSceneIter = windowScenes_.find(abilityId);
484 if (windowSceneIter != windowScenes_.end()) {
485 windowScenes_.erase(windowSceneIter);
486 }
487
488 auto windowStageIter = jsWindowStages_.find(abilityId);
489 if (windowStageIter != jsWindowStages_.end()) {
490 jsWindowStages_.erase(windowStageIter);
491 }
492
493 auto jsContextIter = jsContexts_.find(abilityId);
494 if (jsContextIter != jsContexts_.end()) {
495 jsContexts_.erase(jsContextIter);
496 }
497 }
498
UpdateConfiguration(const AppExecFwk::Configuration & config)499 void SimulatorImpl::UpdateConfiguration(const AppExecFwk::Configuration &config)
500 {
501 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called");
502 if (abilityStage_ == nullptr) {
503 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null abilityStage_");
504 return;
505 }
506
507 auto configuration = std::make_shared<AppExecFwk::Configuration>(config);
508 if (configuration == nullptr) {
509 return;
510 }
511
512 if (stageContext_) {
513 stageContext_->SetConfiguration(configuration);
514 }
515
516 napi_value configArgv[] = {
517 CreateJsConfiguration(nativeEngine_, config)
518 };
519
520 auto abilityStage = abilityStage_->GetNapiValue();
521 if (abilityStage == nullptr) {
522 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null abilityStage");
523 return;
524 }
525 CallObjectMethod(nativeEngine_, abilityStage, "onConfigurationUpdated", configArgv, ArraySize(configArgv));
526 CallObjectMethod(nativeEngine_, abilityStage, "onConfigurationUpdate", configArgv, ArraySize(configArgv));
527 JsAbilityStageContext::ConfigurationUpdated(nativeEngine_, jsStageContext_, configuration);
528
529 for (auto iter = abilities_.begin(); iter != abilities_.end(); iter++) {
530 auto ability = iter->second->GetNapiValue();
531 if (ability == nullptr) {
532 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null ability");
533 continue;
534 }
535
536 CallObjectMethod(nativeEngine_, ability, "onConfigurationUpdated", configArgv, ArraySize(configArgv));
537 CallObjectMethod(nativeEngine_, ability, "onConfigurationUpdate", configArgv, ArraySize(configArgv));
538 JsAbilityContext::ConfigurationUpdated(nativeEngine_, iter->second, configuration);
539 }
540 }
541
SetMockList(const std::map<std::string,std::string> & mockList)542 void SimulatorImpl::SetMockList(const std::map<std::string, std::string> &mockList)
543 {
544 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called. mockList size: %{public}zu", mockList.size());
545 panda::JSNApi::SetMockModuleList(vm_, mockList);
546 }
547
InitResourceMgr()548 void SimulatorImpl::InitResourceMgr()
549 {
550 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called");
551 resourceMgr_ = std::shared_ptr<Global::Resource::ResourceManager>(Global::Resource::CreateResourceManager());
552 if (resourceMgr_ == nullptr) {
553 TAG_LOGE(AAFwkTag::ABILITY_SIM, "resourceMgr");
554 return;
555 }
556
557 if (!resourceMgr_->AddResource(options_.resourcePath.c_str())) {
558 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Add resource failed");
559 }
560 TAG_LOGD(AAFwkTag::ABILITY_SIM, "Add resource success");
561 }
562
InitJsAbilityContext(napi_env env,napi_value obj)563 void SimulatorImpl::InitJsAbilityContext(napi_env env, napi_value obj)
564 {
565 if (context_ == nullptr) {
566 context_ = std::make_shared<AbilityContext>();
567 context_->SetSimulator(static_cast<Simulator*>(this));
568 context_->SetOptions(options_);
569 context_->SetAbilityStageContext(stageContext_);
570 context_->SetResourceManager(resourceMgr_);
571 context_->SetAbilityInfo(abilityInfo_);
572 }
573 napi_value contextObj = CreateJsAbilityContext(nativeEngine_, context_);
574 auto systemModule = std::shared_ptr<NativeReference>(
575 JsRuntime::LoadSystemModuleByEngine(nativeEngine_, "application.AbilityContext", &contextObj, 1));
576 if (systemModule == nullptr) {
577 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null systemModule");
578 return;
579 }
580 contextObj = systemModule->GetNapiValue();
581 if (contextObj == nullptr) {
582 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null contextObj");
583 return;
584 }
585
586 if (obj == nullptr) {
587 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null obj");
588 return;
589 }
590 napi_set_named_property(env, obj, "context", contextObj);
591 jsContexts_.emplace(currentId_, systemModule);
592 }
593
CreateJsWant(napi_env env)594 napi_value SimulatorImpl::CreateJsWant(napi_env env)
595 {
596 napi_value objValue = nullptr;
597 napi_create_object(env, &objValue);
598 napi_set_named_property(env, objValue, "deviceId", CreateJsValue(env, std::string("")));
599 napi_set_named_property(env, objValue, "bundleName", CreateJsValue(env, options_.bundleName));
600 if (abilityInfo_) {
601 napi_set_named_property(env, objValue, "abilityName", CreateJsValue(env, abilityInfo_->name));
602 }
603 napi_set_named_property(env, objValue, "moduleName", CreateJsValue(env, options_.moduleName));
604
605 napi_set_named_property(env, objValue, "uri", CreateJsValue(env, std::string("")));
606 napi_set_named_property(env, objValue, "type", CreateJsValue(env, std::string("")));
607 napi_set_named_property(env, objValue, "flags", CreateJsValue(env, 0));
608 napi_set_named_property(env, objValue, "type", CreateJsValue(env, std::string("")));
609 napi_value object = nullptr;
610 napi_create_object(env, &object);
611 napi_set_named_property(env, objValue, "parameters", object);
612 napi_value array = nullptr;
613 napi_create_array_with_length(env, 0, &array);
614 napi_set_named_property(env, objValue, "entities", array);
615 return objValue;
616 }
617
CreateJsLaunchParam(napi_env env)618 napi_value SimulatorImpl::CreateJsLaunchParam(napi_env env)
619 {
620 napi_value objValue = nullptr;
621 napi_create_object(env, &objValue);
622 napi_set_named_property(env, objValue, "launchReason", CreateJsValue(env, AAFwk::LAUNCHREASON_UNKNOWN));
623 napi_set_named_property(env, objValue, "lastExitReason", CreateJsValue(env, AAFwk::LASTEXITREASON_UNKNOWN));
624 napi_set_named_property(env, objValue, "lastExitMessage", CreateJsValue(env, std::string("")));
625 return objValue;
626 }
627
DispatchStartLifecycle(napi_value instanceValue)628 void SimulatorImpl::DispatchStartLifecycle(napi_value instanceValue)
629 {
630 napi_value wantArgv[] = {
631 CreateJsWant(nativeEngine_),
632 CreateJsLaunchParam(nativeEngine_)
633 };
634 CallObjectMethod(nativeEngine_, instanceValue, "onCreate", wantArgv, ArraySize(wantArgv));
635 auto windowScene = std::make_shared<Rosen::WindowScene>();
636 if (windowScene == nullptr) {
637 return;
638 }
639 sptr<Rosen::IWindowLifeCycle> listener = nullptr;
640 windowScene->Init(-1, context_, listener);
641 auto jsWindowStage = CreateJsWindowStage(windowScene);
642 if (jsWindowStage == nullptr) {
643 return;
644 }
645 napi_value argv[] = { jsWindowStage->GetNapiValue() };
646 CallObjectMethod(nativeEngine_, instanceValue, "onWindowStageCreate", argv, ArraySize(argv));
647
648 CallObjectMethod(nativeEngine_, instanceValue, "onForeground", nullptr, 0);
649
650 windowScenes_.emplace(currentId_, windowScene);
651 jsWindowStages_.emplace(currentId_, std::shared_ptr<NativeReference>(jsWindowStage.release()));
652 }
653
CreateJsWindowStage(const std::shared_ptr<Rosen::WindowScene> & windowScene)654 std::unique_ptr<NativeReference> SimulatorImpl::CreateJsWindowStage(
655 const std::shared_ptr<Rosen::WindowScene> &windowScene)
656 {
657 napi_value jsWindowStage = Rosen::CreateJsWindowStage(nativeEngine_, windowScene);
658 if (jsWindowStage == nullptr) {
659 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null jsWindowSatge");
660 return nullptr;
661 }
662 return JsRuntime::LoadSystemModuleByEngine(nativeEngine_, "application.WindowStage", &jsWindowStage, 1);
663 }
664
CreateJSVM()665 panda::ecmascript::EcmaVM *SimulatorImpl::CreateJSVM()
666 {
667 panda::RuntimeOption pandaOption;
668 pandaOption.SetArkProperties(DEFAULT_ARK_PROPERTIES);
669 pandaOption.SetGcThreadNum(DEFAULT_GC_THREAD_NUM);
670 pandaOption.SetLongPauseTime(DEFAULT_LONG_PAUSE_TIME);
671 pandaOption.SetGcType(panda::RuntimeOption::GC_TYPE::GEN_GC);
672 pandaOption.SetGcPoolSize(DEFAULT_GC_POOL_SIZE);
673 pandaOption.SetLogLevel(panda::RuntimeOption::LOG_LEVEL::FOLLOW);
674 pandaOption.SetLogBufPrint(PrintVmLog);
675 pandaOption.SetEnableAsmInterpreter(true);
676 pandaOption.SetAsmOpcodeDisableRange("");
677 return panda::JSNApi::CreateJSVM(pandaOption);
678 }
679
OnInit()680 bool SimulatorImpl::OnInit()
681 {
682 if (!ParseBundleAndModuleInfo()) {
683 TAG_LOGE(AAFwkTag::ABILITY_SIM, "failed");
684 return false;
685 }
686
687 vm_ = CreateJSVM();
688 if (vm_ == nullptr) {
689 return false;
690 }
691
692 panda::JSNApi::DebugOption debugOption = {ARK_DEBUGGER_LIB_PATH, (options_.debugPort != 0), options_.debugPort};
693 panda::JSNApi::StartDebugger(vm_, debugOption, 0, [this](std::function<void()> &&arg) {
694 debuggerTask_.OnPostTask(std::move(arg));
695 });
696
697 auto nativeEngine = new (std::nothrow) ArkNativeEngine(vm_, nullptr);
698 if (nativeEngine == nullptr) {
699 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null nativeEngine");
700 return false;
701 }
702 napi_env env = reinterpret_cast<napi_env>(nativeEngine);
703 auto uncaughtTask = [weak = weak_from_this()](napi_value value) {
704 TAG_LOGE(AAFwkTag::ABILITY_SIM, "uncaught exception");
705 auto self = weak.lock();
706 if (self == nullptr) {
707 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null SimulatorImpl");
708 return;
709 }
710 self->ReportJsError(value);
711 if (self->terminateCallback_ == nullptr) {
712 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null terminateCallback");
713 return;
714 }
715 self->terminateCallback_(self->currentId_);
716 };
717 nativeEngine->RegisterNapiUncaughtExceptionHandler(uncaughtTask);
718
719 napi_value globalObj;
720 napi_get_global(env, &globalObj);
721 if (globalObj == nullptr) {
722 delete nativeEngine;
723 TAG_LOGE(AAFwkTag::ABILITY_SIM, "null global object");
724 return false;
725 }
726
727 if (!LoadRuntimeEnv(env, globalObj)) {
728 delete nativeEngine;
729 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Load runtime env failed");
730 return false;
731 }
732
733 panda::JSNApi::SetBundle(vm_, false);
734 panda::JSNApi::SetBundleName(vm_, options_.bundleName);
735 panda::JSNApi::SetModuleName(vm_, options_.moduleName);
736 panda::JSNApi::SetAssetPath(vm_, options_.modulePath);
737 std::map<std::string, std::vector<std::vector<std::string>>> pkgContextInfoMap;
738 std::map<std::string, std::string> pkgAliasMap;
739 GetPkgContextInfoListMap(options_.pkgContextInfoJsonStringMap, pkgContextInfoMap, pkgAliasMap);
740 panda::JSNApi::SetpkgContextInfoList(vm_, pkgContextInfoMap);
741 panda::JSNApi::SetPkgAliasList(vm_, pkgAliasMap);
742 panda::JSNApi::SetPkgNameList(vm_, options_.packageNameList);
743
744 nativeEngine_ = env;
745 return true;
746 }
747
RequireNapi(napi_env env,napi_callback_info info)748 napi_value SimulatorImpl::RequireNapi(napi_env env, napi_callback_info info)
749 {
750 napi_value globalObj;
751 napi_get_global(env, &globalObj);
752 napi_value requireNapi = nullptr;
753 napi_get_named_property(env, globalObj, "requireNapiPreview", &requireNapi);
754 size_t argc = ARGC_MAX_COUNT;
755 napi_value argv[ARGC_MAX_COUNT] = {nullptr};
756 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
757 napi_value result = nullptr;
758 napi_call_function(env, CreateJsUndefined(env), requireNapi, argc, argv, &result);
759 if (!CheckTypeForNapiValue(env, result, napi_undefined)) {
760 return result;
761 }
762 napi_value mockRequireNapi = nullptr;
763 napi_get_named_property(env, globalObj, "mockRequireNapi", &mockRequireNapi);
764 napi_call_function(env, CreateJsUndefined(env), mockRequireNapi, argc, argv, &result);
765 return result;
766 }
767
LoadJsMock(const std::string & fileName)768 void SimulatorImpl::LoadJsMock(const std::string &fileName)
769 {
770 std::ifstream stream(fileName, std::ios::ate | std::ios::binary);
771 if (!stream.is_open()) {
772 TAG_LOGE(AAFwkTag::ABILITY_SIM, "open: %{public}s failed", fileName.c_str());
773 return;
774 }
775 size_t len = stream.tellg();
776 std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(len);
777 stream.seekg(0);
778 stream.read(reinterpret_cast<char*>(buffer.get()), len);
779 stream.close();
780 panda::JSNApi::Execute(vm_, buffer.get(), len, "_GLOBAL::func_main_0");
781 }
782
LoadRuntimeEnv(napi_env env,napi_value globalObj)783 bool SimulatorImpl::LoadRuntimeEnv(napi_env env, napi_value globalObj)
784 {
785 JsSysModule::Console::InitConsoleModule(env);
786 auto ret = JsSysModule::Timer::RegisterTime(env);
787 if (!ret) {
788 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Register timer failed");
789 }
790 napi_value object = nullptr;
791 napi_create_object(env, &object);
792 napi_set_named_property(env, globalObj, "group", object);
793
794 uintptr_t bufferStart = reinterpret_cast<uintptr_t>(_binary_jsMockSystemPlugin_abc_start);
795 uintptr_t bufferEnd = reinterpret_cast<uintptr_t>(_binary_jsMockSystemPlugin_abc_end);
796 const uint8_t *buffer = reinterpret_cast<const uint8_t*>(bufferStart);
797 size_t size = bufferEnd - bufferStart;
798 panda::JSNApi::Execute(vm_, buffer, size, "_GLOBAL::func_main_0");
799
800 napi_value mockRequireNapi = nullptr;
801 napi_get_named_property(env, globalObj, "requireNapi", &mockRequireNapi);
802 napi_set_named_property(env, globalObj, "mockRequireNapi", mockRequireNapi);
803 auto* moduleManager = reinterpret_cast<NativeEngine*>(env)->GetModuleManager();
804 if (moduleManager != nullptr) {
805 TAG_LOGE(
806 AAFwkTag::ABILITY_SIM, "moduleManager SetPreviewSearchPath: %{public}s", options_.containerSdkPath.c_str());
807 moduleManager->SetPreviewSearchPath(options_.containerSdkPath);
808 }
809
810 std::string fileSeparator = "/";
811 auto pos = options_.containerSdkPath.find(fileSeparator);
812 if (pos == std::string::npos) {
813 fileSeparator = "\\";
814 }
815
816 std::string fileName = options_.containerSdkPath + fileSeparator + "apiMock" + fileSeparator + "jsMockHmos.abc";
817 TAG_LOGD(AAFwkTag::ABILITY_SIM, "file name:%{public}s", fileName.c_str());
818 if (!fileName.empty() && AbilityStageContext::Access(fileName)) {
819 LoadJsMock(fileName);
820 }
821
822 const char *moduleName = "SimulatorImpl";
823 BindNativeFunction(env, globalObj, "requireNapi", moduleName, SimulatorImpl::RequireNapi);
824 return true;
825 }
826
Run()827 void SimulatorImpl::Run()
828 {
829 uv_loop_t* uvLoop = nullptr;
830 napi_get_uv_event_loop(nativeEngine_, &uvLoop);
831 if (uvLoop != nullptr) {
832 uv_run(uvLoop, UV_RUN_NOWAIT);
833 }
834
835 if (postTask_ != nullptr) {
836 postTask_([this]() { Run(); }, 0);
837 }
838 }
839 }
840
Create(const Options & options)841 std::shared_ptr<Simulator> Simulator::Create(const Options &options)
842 {
843 auto simulator = std::make_shared<SimulatorImpl>();
844 if (simulator->Initialize(options)) {
845 return simulator;
846 }
847 return nullptr;
848 }
849
SetHostResolveBufferTracker(ResolveBufferTrackerCallback cb)850 void SimulatorImpl::SetHostResolveBufferTracker(ResolveBufferTrackerCallback cb)
851 {
852 if (vm_ == nullptr || cb == nullptr) {
853 TAG_LOGE(AAFwkTag::ABILITY_SIM, "Params invalid");
854 return;
855 }
856 panda::JSNApi::SetHostResolveBufferTracker(vm_, cb);
857 }
858
GetPkgContextInfoListMap(const std::map<std::string,std::string> & contextInfoMap,std::map<std::string,std::vector<std::vector<std::string>>> & pkgContextInfoMap,std::map<std::string,std::string> & pkgAliasMap)859 void SimulatorImpl::GetPkgContextInfoListMap(const std::map<std::string, std::string> &contextInfoMap,
860 std::map<std::string, std::vector<std::vector<std::string>>> &pkgContextInfoMap,
861 std::map<std::string, std::string> &pkgAliasMap)
862 {
863 for (auto it = contextInfoMap.begin(); it != contextInfoMap.end(); it++) {
864 std::vector<std::vector<std::string>> pkgContextInfoList;
865 auto jsonObject = nlohmann::json::parse(it->second);
866 if (jsonObject.is_discarded()) {
867 TAG_LOGE(AAFwkTag::JSRUNTIME, "moduleName: %{public}s parse json error", it->first.c_str());
868 continue;
869 }
870 for (nlohmann::json::iterator jsonIt = jsonObject.begin(); jsonIt != jsonObject.end(); jsonIt++) {
871 std::vector<std::string> items;
872 items.emplace_back(jsonIt.key());
873 nlohmann::json itemObject = jsonIt.value();
874 std::string pkgName = "";
875 items.emplace_back(PACKAGE_NAME);
876 if (itemObject[PACKAGE_NAME].is_null() || !itemObject[PACKAGE_NAME].is_string()) {
877 items.emplace_back(pkgName);
878 } else {
879 pkgName = itemObject[PACKAGE_NAME].get<std::string>();
880 items.emplace_back(pkgName);
881 }
882
883 items.emplace_back(BUNDLE_NAME);
884 if (itemObject[BUNDLE_NAME].is_null() || !itemObject[BUNDLE_NAME].is_string()) {
885 items.emplace_back("");
886 } else {
887 items.emplace_back(itemObject[BUNDLE_NAME].get<std::string>());
888 }
889
890 items.emplace_back(MODULE_NAME);
891 if (itemObject[MODULE_NAME].is_null() || !itemObject[MODULE_NAME].is_string()) {
892 items.emplace_back("");
893 } else {
894 items.emplace_back(itemObject[MODULE_NAME].get<std::string>());
895 }
896
897 GetPkgContextInfoListInner(itemObject, items, pkgAliasMap, pkgName);
898 pkgContextInfoList.emplace_back(items);
899 }
900 TAG_LOGI(AAFwkTag::JSRUNTIME, "moduleName: %{public}s parse json success", it->first.c_str());
901 pkgContextInfoMap[it->first] = pkgContextInfoList;
902 }
903 }
904
GetPkgContextInfoListInner(nlohmann::json & itemObject,std::vector<std::string> & items,std::map<std::string,std::string> & pkgAliasMap,std::string & pkgName)905 void SimulatorImpl::GetPkgContextInfoListInner(nlohmann::json &itemObject, std::vector<std::string> &items,
906 std::map<std::string, std::string> &pkgAliasMap, std::string &pkgName)
907 {
908 items.emplace_back(VERSION);
909 if (itemObject[VERSION].is_null() || !itemObject[VERSION].is_string()) {
910 items.emplace_back("");
911 } else {
912 items.emplace_back(itemObject[VERSION].get<std::string>());
913 }
914
915 items.emplace_back(ENTRY_PATH);
916 if (itemObject[ENTRY_PATH].is_null() || !itemObject[ENTRY_PATH].is_string()) {
917 items.emplace_back("");
918 } else {
919 items.emplace_back(itemObject[ENTRY_PATH].get<std::string>());
920 }
921
922 items.emplace_back(IS_SO);
923 if (itemObject[IS_SO].is_null() || !itemObject[IS_SO].is_boolean()) {
924 items.emplace_back("false");
925 } else {
926 bool isSo = itemObject[IS_SO].get<bool>();
927 if (isSo) {
928 items.emplace_back("true");
929 } else {
930 items.emplace_back("false");
931 }
932 }
933 if (!itemObject[DEPENDENCY_ALIAS].is_null() && itemObject[DEPENDENCY_ALIAS].is_string()) {
934 std::string pkgAlias = itemObject[DEPENDENCY_ALIAS].get<std::string>();
935 if (!pkgAlias.empty()) {
936 pkgAliasMap[pkgAlias] = pkgName;
937 }
938 }
939 }
940
GetNativeStrFromJsTaggedObj(napi_value obj,const char * key)941 std::string SimulatorImpl::GetNativeStrFromJsTaggedObj(napi_value obj, const char* key)
942 {
943 if (obj == nullptr) {
944 TAG_LOGE(AAFwkTag::ABILITY_SIM, "get value failed");
945 return "";
946 }
947
948 napi_value valueStr = nullptr;
949 napi_get_named_property(nativeEngine_, obj, key, &valueStr);
950 napi_valuetype valueType = napi_undefined;
951 napi_typeof(nativeEngine_, valueStr, &valueType);
952 if (valueType != napi_string) {
953 TAG_LOGE(AAFwkTag::ABILITY_SIM, "convert value failed");
954 return "";
955 }
956
957 size_t valueStrBufLength = 0;
958 napi_get_value_string_utf8(nativeEngine_, valueStr, nullptr, 0, &valueStrBufLength);
959 auto valueCStr = std::make_unique<char[]>(valueStrBufLength + 1);
960 size_t valueStrLength = 0;
961 napi_get_value_string_utf8(nativeEngine_, valueStr, valueCStr.get(), valueStrBufLength + 1, &valueStrLength);
962 std::string ret(valueCStr.get(), valueStrLength);
963 TAG_LOGD(AAFwkTag::ABILITY_SIM, "GetNativeStrFromJsTaggedObj Success");
964 return ret;
965 }
966
ReportJsError(napi_value obj)967 void SimulatorImpl::ReportJsError(napi_value obj)
968 {
969 std::string errorMsg = GetNativeStrFromJsTaggedObj(obj, "message");
970 std::string errorName = GetNativeStrFromJsTaggedObj(obj, "name");
971 std::string errorStack = GetNativeStrFromJsTaggedObj(obj, "stack");
972 std::string topStack = GetNativeStrFromJsTaggedObj(obj, "topstack");
973 std::string summary = "Simulator error name:" + errorName + "\n";
974 summary += "Simulator error message:" + errorMsg + "\n";
975 bool hasProperty = false;
976 napi_has_named_property(nativeEngine_, obj, "code", &hasProperty);
977 if (hasProperty) {
978 std::string errorCode = GetNativeStrFromJsTaggedObj(obj, "code");
979 summary += "Simulator error code:" + errorCode + "\n";
980 }
981 if (errorStack.empty()) {
982 TAG_LOGE(AAFwkTag::ABILITY_SIM, "errorStack empty");
983 return;
984 }
985 summary += "Stacktrace:\n" + errorStack;
986 TAG_LOGE(AAFwkTag::ABILITY_SIM, "summary:\n%{public}s", summary.c_str());
987 }
988 } // namespace AbilityRuntime
989 } // namespace OHOS
990