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