1 /*
2 * Copyright (c) 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 #include "ecmascript/module/js_shared_module_manager.h"
16
17 #include "ecmascript/jspandafile/js_pandafile_executor.h"
18 #include "ecmascript/jspandafile/js_pandafile_manager.h"
19 #include "ecmascript/module/module_manager_helper.h"
20 #include "ecmascript/module/module_path_helper.h"
21 #include "ecmascript/runtime_lock.h"
22
23 namespace panda::ecmascript {
24 using StringHelper = base::StringHelper;
25 using JSPandaFile = ecmascript::JSPandaFile;
26 using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo;
27
GetInstance()28 SharedModuleManager* SharedModuleManager::GetInstance()
29 {
30 static SharedModuleManager* instance = new SharedModuleManager();
31 return instance;
32 }
33
Iterate(const RootVisitor & v)34 void SharedModuleManager::Iterate(const RootVisitor &v)
35 {
36 for (auto &it : resolvedSharedModules_) {
37 ObjectSlot slot(reinterpret_cast<uintptr_t>(&it.second));
38 v(Root::ROOT_VM, slot);
39 ASSERT(slot.GetTaggedValue() == it.second);
40 }
41 }
42
GetSendableModuleValue(JSThread * thread,int32_t index,JSTaggedValue jsFunc)43 JSTaggedValue SharedModuleManager::GetSendableModuleValue(JSThread *thread, int32_t index, JSTaggedValue jsFunc)
44 {
45 JSTaggedValue currentModule = JSFunction::Cast(jsFunc.GetTaggedObject())->GetModule();
46 return GetSendableModuleValueImpl(thread, index, currentModule);
47 }
48
GetSendableModuleValueImpl(JSThread * thread,int32_t index,JSTaggedValue currentModule) const49 JSTaggedValue SharedModuleManager::GetSendableModuleValueImpl(
50 JSThread *thread, int32_t index, JSTaggedValue currentModule) const
51 {
52 if (currentModule.IsUndefined()) { // LCOV_EXCL_BR_LINE
53 LOG_FULL(FATAL) << "GetModuleValueOutter currentModule failed";
54 UNREACHABLE();
55 }
56
57 JSHandle<SourceTextModule> module(thread, currentModule.GetTaggedObject());
58 JSTaggedValue moduleEnvironment = module->GetEnvironment();
59 if (moduleEnvironment.IsUndefined()) {
60 return thread->GlobalConstants()->GetUndefined();
61 }
62 ASSERT(moduleEnvironment.IsTaggedArray());
63 JSTaggedValue resolvedBinding = TaggedArray::Cast(moduleEnvironment.GetTaggedObject())->Get(index);
64 if (resolvedBinding.IsResolvedRecordIndexBinding()) {
65 return ModuleManagerHelper::GetModuleValueFromIndexBinding(thread, module, resolvedBinding);
66 } else if (resolvedBinding.IsResolvedIndexBinding()) {
67 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
68 JSHandle<SourceTextModule> resolvedModule(thread, binding->GetModule().GetTaggedObject());
69 return ModuleManagerHelper::GetModuleValue(thread, resolvedModule, binding->GetIndex());
70 } else if (resolvedBinding.IsResolvedRecordBinding()) {
71 return ModuleManagerHelper::GetModuleValueFromRecordBinding(thread, module, resolvedBinding);
72 }
73 LOG_ECMA(FATAL) << "Unexpect binding";
74 UNREACHABLE();
75 }
76
GetLazySendableModuleValue(JSThread * thread,int32_t index,JSTaggedValue jsFunc)77 JSTaggedValue SharedModuleManager::GetLazySendableModuleValue(JSThread *thread, int32_t index, JSTaggedValue jsFunc)
78 {
79 JSTaggedValue currentModule = JSFunction::Cast(jsFunc.GetTaggedObject())->GetModule();
80 return GetLazySendableModuleValueImpl(thread, index, currentModule);
81 }
82
GetLazySendableModuleValueImpl(JSThread * thread,int32_t index,JSTaggedValue currentModule) const83 JSTaggedValue SharedModuleManager::GetLazySendableModuleValueImpl(
84 JSThread *thread, int32_t index, JSTaggedValue currentModule) const
85 {
86 if (currentModule.IsUndefined()) { // LCOV_EXCL_BR_LINE
87 LOG_FULL(FATAL) << "GetModuleValueOutter currentModule failed";
88 UNREACHABLE();
89 }
90
91 JSHandle<SourceTextModule> module(thread, currentModule.GetTaggedObject());
92 JSTaggedValue moduleEnvironment = module->GetEnvironment();
93 if (moduleEnvironment.IsUndefined()) {
94 return thread->GlobalConstants()->GetUndefined();
95 }
96 ASSERT(moduleEnvironment.IsTaggedArray());
97 JSTaggedValue resolvedBinding = TaggedArray::Cast(moduleEnvironment.GetTaggedObject())->Get(index);
98 if (resolvedBinding.IsResolvedRecordIndexBinding()) {
99 return ModuleManagerHelper::GetLazyModuleValueFromIndexBinding(thread, module, resolvedBinding);
100 } else if (resolvedBinding.IsResolvedIndexBinding()) {
101 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
102 JSHandle<SourceTextModule> resolvedModule(thread, binding->GetModule().GetTaggedObject());
103 SourceTextModule::Evaluate(thread, resolvedModule, nullptr);
104 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
105 return ModuleManagerHelper::GetModuleValue(thread, resolvedModule, binding->GetIndex());
106 } else if (resolvedBinding.IsResolvedRecordBinding()) {
107 return ModuleManagerHelper::GetLazyModuleValueFromRecordBinding(thread, module, resolvedBinding);
108 }
109 LOG_ECMA(FATAL) << "Unexpect binding";
110 UNREACHABLE();
111 }
112
ResolveImportedModule(JSThread * thread,const CString & fileName,bool executeFromJob)113 JSHandle<JSTaggedValue> SharedModuleManager::ResolveImportedModule(JSThread *thread, const CString &fileName,
114 bool executeFromJob)
115 {
116 std::shared_ptr<JSPandaFile> jsPandaFile =
117 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, fileName, JSPandaFile::ENTRY_MAIN_FUNCTION);
118 if (jsPandaFile == nullptr) { // LCOV_EXCL_BR_LINE
119 LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << fileName;
120 }
121 JSRecordInfo *recordInfo = nullptr;
122 [[maybe_unused]] bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(fileName, &recordInfo);
123 ASSERT(hasRecord && !jsPandaFile->IsSharedModule(recordInfo));
124 // loading unshared module though current context's module manager
125 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
126 return moduleManager->HostResolveImportedModule(fileName, executeFromJob);
127 }
128
ResolveImportedModuleWithMerge(JSThread * thread,const CString & fileName,const CString & recordName,bool executeFromJob)129 JSHandle<JSTaggedValue> SharedModuleManager::ResolveImportedModuleWithMerge(JSThread *thread,
130 const CString &fileName, const CString &recordName, bool executeFromJob)
131 {
132 std::shared_ptr<JSPandaFile> jsPandaFile =
133 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, fileName, recordName, false);
134 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
135 if (jsPandaFile == nullptr) {
136 CString msg = "Load file with filename '" + fileName + "' failed, recordName '" + recordName + "'";
137 THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str());
138 }
139 JSRecordInfo *recordInfo = nullptr;
140 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(recordName, &recordInfo);
141 if (!hasRecord) {
142 CString msg = "cannot find record '" + recordName + "', please check the request path.'"
143 + fileName + "'.";
144 THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str());
145 }
146
147 if (jsPandaFile->IsSharedModule(recordInfo)) {
148 return ResolveSharedImportedModuleWithMerge(thread, fileName, recordName, jsPandaFile.get(),
149 recordInfo);
150 }
151 // loading unshared module though current context's module manager
152 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
153 return moduleManager->HostResolveImportedModuleWithMerge(fileName, recordName, executeFromJob);
154 }
155
ResolveSharedImportedModuleWithMerge(JSThread * thread,const CString & fileName,const CString & recordName,const JSPandaFile * jsPandaFile,JSRecordInfo * recordInfo)156 JSHandle<JSTaggedValue> SharedModuleManager::ResolveSharedImportedModuleWithMerge(JSThread *thread,
157 const CString &fileName, const CString &recordName, const JSPandaFile *jsPandaFile,
158 [[maybe_unused]] JSRecordInfo *recordInfo)
159 {
160 if (SearchInSModuleManager(thread, recordName)) {
161 return JSHandle<JSTaggedValue>(GetSModule(thread, recordName));
162 }
163 // before resolving module completely, shared-module put into isolate -thread resolvedModules_ temporarily.
164 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
165 JSHandle<JSTaggedValue> module = moduleManager->TryGetImportedModule(recordName);
166 if (!module->IsUndefined()) {
167 return module;
168 }
169
170 ASSERT(jsPandaFile->IsModule(recordInfo));
171 JSHandle<JSTaggedValue> moduleRecord = SharedModuleHelper::ParseSharedModule(thread, jsPandaFile, recordName,
172 fileName, recordInfo);
173 JSHandle<SourceTextModule>::Cast(moduleRecord)->SetEcmaModuleRecordNameString(recordName);
174 moduleManager->AddResolveImportedModule(recordName, moduleRecord.GetTaggedValue());
175 moduleManager->AddToInstantiatingSModuleList(recordName);
176 return moduleRecord;
177 }
178
SearchInSModuleManagerUnsafe(const CString & recordName)179 bool SharedModuleManager::SearchInSModuleManagerUnsafe(const CString &recordName)
180 {
181 auto entry = resolvedSharedModules_.find(recordName);
182 if (entry != resolvedSharedModules_.end()) {
183 return true;
184 }
185 return false;
186 }
187
GetSModuleUnsafe(JSThread * thread,const CString & recordName)188 JSHandle<SourceTextModule> SharedModuleManager::GetSModuleUnsafe(JSThread *thread, const CString &recordName)
189 {
190 auto entry = resolvedSharedModules_.find(recordName);
191 if (entry == resolvedSharedModules_.end()) {
192 return JSHandle<SourceTextModule>::Cast(thread->GlobalConstants()->GetHandledUndefined());
193 }
194 JSHandle<JSTaggedValue> module(thread, entry->second);
195 return JSHandle<SourceTextModule>::Cast(module);
196 }
197
GetSModule(JSThread * thread,const CString & recordName)198 JSHandle<SourceTextModule> SharedModuleManager::GetSModule(JSThread *thread, const CString &recordName)
199 {
200 RuntimeLockHolder locker(thread, mutex_);
201 return GetSModuleUnsafe(thread, recordName);
202 }
203
SearchInSModuleManager(JSThread * thread,const CString & recordName)204 bool SharedModuleManager::SearchInSModuleManager(JSThread *thread, const CString &recordName)
205 {
206 RuntimeLockHolder locker(thread, mutex_);
207 return SearchInSModuleManagerUnsafe(recordName);
208 }
InsertInSModuleManager(JSThread * thread,const CString & recordName,JSHandle<SourceTextModule> & moduleRecord)209 void SharedModuleManager::InsertInSModuleManager(JSThread *thread, const CString &recordName,
210 JSHandle<SourceTextModule> &moduleRecord)
211 {
212 RuntimeLockHolder locker(thread, mutex_);
213 JSHandle<JSTaggedValue> module = JSHandle<JSTaggedValue>::Cast(moduleRecord);
214 if (!SearchInSModuleManagerUnsafe(recordName)) {
215 AddResolveImportedSModule(recordName, module.GetTaggedValue());
216 StateVisit stateVisit;
217 sharedModuleMutex_.emplace(recordName, std::move(stateVisit));
218 }
219 }
220
TransferSModule(JSThread * thread)221 void SharedModuleManager::TransferSModule(JSThread *thread)
222 {
223 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
224 CVector<CString> instantiatingSModuleList = moduleManager->GetInstantiatingSModuleList();
225 for (auto s:instantiatingSModuleList) {
226 JSHandle<SourceTextModule> module = moduleManager->HostGetImportedModule(s);
227 ASSERT(module->GetSharedType() == SharedTypes::SHARED_MODULE);
228 InsertInSModuleManager(thread, s, module);
229 moduleManager->RemoveModuleNameFromList(s);
230 }
231 moduleManager->ClearInstantiatingSModuleList();
232 }
233
findModuleMutexWithLock(JSThread * thread,const JSHandle<SourceTextModule> & module)234 StateVisit &SharedModuleManager::findModuleMutexWithLock(JSThread *thread, const JSHandle<SourceTextModule> &module)
235 {
236 RuntimeLockHolder locker(thread, mutex_);
237 CString moduleName = SourceTextModule::GetModuleName(module.GetTaggedValue());
238 auto it = sharedModuleMutex_.find(moduleName);
239 if (it == sharedModuleMutex_.end()) { // LCOV_EXCL_BR_LINE
240 LOG_ECMA(FATAL) << " Get shared module mutex failed";
241 }
242 return it->second;
243 }
244
IsInstaniatedSModule(JSThread * thread,const JSHandle<SourceTextModule> & module)245 bool SharedModuleManager::IsInstaniatedSModule(JSThread *thread, const JSHandle<SourceTextModule> &module)
246 {
247 RuntimeLockHolder locker(thread, mutex_);
248 return (module->GetStatus() >= ModuleStatus::INSTANTIATED);
249 }
250
GenerateFuncModule(JSThread * thread,const JSPandaFile * jsPandaFile,const CString & entryPoint,ClassKind classKind)251 JSHandle<JSTaggedValue> SharedModuleManager::GenerateFuncModule(JSThread *thread, const JSPandaFile *jsPandaFile,
252 const CString &entryPoint, ClassKind classKind)
253 {
254 CString recordName = jsPandaFile->GetRecordName(entryPoint);
255 auto vm = thread->GetEcmaVM();
256 JSRecordInfo *recordInfo = nullptr;
257 [[maybe_unused]] bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(recordName, &recordInfo);
258 ASSERT(hasRecord);
259 if (jsPandaFile->IsModule(recordInfo)) {
260 JSHandle<SourceTextModule> module;
261 if (jsPandaFile->IsSharedModule(recordInfo)) {
262 return JSHandle<JSTaggedValue>(GetSModule(thread, entryPoint));
263 } else {
264 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
265 module = moduleManager->HostGetImportedModule(recordName);
266 }
267
268 if (classKind == ClassKind::NON_SENDABLE) {
269 return JSHandle<JSTaggedValue>(module);
270 } else {
271 // Clone isolate module at shared-heap to mark sendable class.
272 return SendableClassModule::GenerateSendableFuncModule(thread, JSHandle<JSTaggedValue>(module));
273 }
274 }
275 return JSHandle<JSTaggedValue>(vm->GetFactory()->NewFromUtf8(recordName));
276 }
277
SModuleNamespaceCreate(JSThread * thread,const JSHandle<JSTaggedValue> & module,const JSHandle<TaggedArray> & exports)278 JSHandle<ModuleNamespace> SharedModuleManager::SModuleNamespaceCreate(JSThread *thread,
279 const JSHandle<JSTaggedValue> &module, const JSHandle<TaggedArray> &exports)
280 {
281 RuntimeLockHolder locker(thread, mutex_);
282 return JSSharedModule::SModuleNamespaceCreate(thread, module, exports);
283 }
284
SharedNativeObjDestory()285 void SharedModuleManager::SharedNativeObjDestory()
286 {
287 for (auto it = resolvedSharedModules_.begin(); it != resolvedSharedModules_.end(); it++) {
288 CString key = it->first;
289 ASSERT(!key.empty());
290 JSTaggedValue module = it->second;
291 SourceTextModule::Cast(module)->DestoryLazyImportArray();
292 SourceTextModule::Cast(module)->DestoryEcmaModuleFilenameString();
293 SourceTextModule::Cast(module)->DestoryEcmaModuleRecordNameString();
294 }
295 }
296 } // namespace panda::ecmascript
297