1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "ecmascript/module/js_module_manager.h"
16
17 #include "ecmascript/interpreter/frame_handler.h"
18 #include "ecmascript/jspandafile/js_pandafile_executor.h"
19 #include "ecmascript/module/js_shared_module_manager.h"
20 #include "ecmascript/module/module_data_extractor.h"
21 #include "ecmascript/module/module_path_helper.h"
22 #include "ecmascript/module/module_resolver.h"
23 #include "ecmascript/object_factory-inl.h"
24 #include "ecmascript/require/js_cjs_module.h"
25
26 namespace panda::ecmascript {
27 using StringHelper = base::StringHelper;
28 using JSPandaFile = ecmascript::JSPandaFile;
29 using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo;
30
ModuleManager(EcmaVM * vm)31 ModuleManager::ModuleManager(EcmaVM *vm) : vm_(vm) {}
32
GenerateSendableFuncModule(const JSHandle<JSTaggedValue> & module)33 JSHandle<JSTaggedValue> ModuleManager::GenerateSendableFuncModule(const JSHandle<JSTaggedValue> &module)
34 {
35 // Clone isolate module at shared-heap to mark sendable class.
36 return SendableClassModule::GenerateSendableFuncModule(vm_->GetJSThread(), module);
37 }
38
CheckModuleValueOutterResolved(JSThread * thread,int32_t index,JSFunction * jsFunc)39 bool ModuleManager::CheckModuleValueOutterResolved(JSThread *thread, int32_t index, JSFunction *jsFunc)
40 {
41 // check module resolved, if resolved, load var from module directly for jit compiled code.
42 ASSERT(jsFunc != nullptr);
43 JSTaggedValue currentModule = jsFunc->GetModule(thread);
44 if (!currentModule.IsSourceTextModule()) {
45 return false;
46 }
47 if (SourceTextModule::IsSendableFunctionModule(currentModule)) {
48 return false;
49 }
50 JSTaggedValue moduleEnv =
51 reinterpret_cast<SourceTextModule*>(currentModule.GetTaggedObject())->GetEnvironment(thread);
52 if (!moduleEnv.IsTaggedArray()) {
53 return false;
54 }
55 JSTaggedValue resolvedBinding = TaggedArray::Cast(moduleEnv.GetTaggedObject())->Get(thread, index);
56 if (!resolvedBinding.IsResolvedIndexBinding()) {
57 return false;
58 }
59
60 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
61 SourceTextModule *resolvedModule = reinterpret_cast<SourceTextModule*>(
62 binding->GetModule(thread).GetTaggedObject());
63 ModuleTypes moduleType = resolvedModule->GetTypes();
64 if (moduleType != ModuleTypes::ECMA_MODULE) {
65 return false;
66 }
67
68 JSTaggedValue resolvedModuleDict = resolvedModule->GetNameDictionary(thread);
69 if (!resolvedModuleDict.IsTaggedArray()) {
70 return false;
71 }
72 return true;
73 }
74
GetExternalModuleVarFastPathForJIT(JSThread * thread,int32_t index,JSFunction * jsFunc)75 JSTaggedValue ModuleManager::GetExternalModuleVarFastPathForJIT(JSThread *thread, int32_t index, JSFunction *jsFunc)
76 {
77 // fast path for jit load ecma module, check resolved in compiled
78 // with CheckModuleValueOutterResolved, avoid redundancy check in runtime.
79 ASSERT(jsFunc != nullptr);
80 ASSERT(thread != nullptr);
81 if (thread->GetStageOfHotReload() == StageOfHotReload::LOAD_END_EXECUTE_PATCHMAIN) {
82 return JSTaggedValue::Hole();
83 }
84
85 JSTaggedValue currentModule = jsFunc->GetModule(thread);
86 ASSERT(currentModule.IsSourceTextModule());
87 JSTaggedValue moduleEnvironment =
88 reinterpret_cast<SourceTextModule*>(currentModule.GetTaggedObject())->GetEnvironment(thread);
89 ASSERT(moduleEnvironment.IsTaggedArray());
90 JSTaggedValue resolvedBinding = TaggedArray::Cast(moduleEnvironment.GetTaggedObject())->Get(thread, index);
91 ASSERT(resolvedBinding.IsResolvedIndexBinding());
92 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
93 JSTaggedValue resolvedModule = binding->GetModule(thread);
94 int32_t bindingIndex = binding->GetIndex();
95 ASSERT(resolvedModule.IsSourceTextModule());
96 JSTaggedValue dictionary =
97 reinterpret_cast<SourceTextModule*>(resolvedModule.GetTaggedObject())->GetNameDictionary(thread);
98 TaggedArray *array = TaggedArray::Cast(dictionary.GetTaggedObject());
99 return array->Get(thread, bindingIndex);
100 }
101
GetImportedModule(const CString & referencing)102 JSHandle<SourceTextModule> ModuleManager::GetImportedModule(const CString &referencing)
103 {
104 auto thread = vm_->GetJSThread();
105 if (!IsLocalModuleLoaded(referencing)) {
106 return SharedModuleManager::GetInstance()->GetSModule(thread, referencing);
107 } else {
108 return HostGetImportedModule(referencing);
109 }
110 }
111
HostGetImportedModule(const CString & referencing)112 JSHandle<SourceTextModule> ModuleManager::HostGetImportedModule(const CString &referencing)
113 {
114 auto entry = resolvedModules_.Find(referencing);
115 if (!entry) { // LCOV_EXCL_BR_LINE
116 LOG_ECMA(FATAL) << "Can not get module: " << referencing;
117 }
118 return JSHandle<SourceTextModule>(vm_->GetJSThread(), entry.value());
119 }
120
HostGetImportedModule(void * src)121 JSTaggedValue ModuleManager::HostGetImportedModule(void *src)
122 {
123 const char *str = reinterpret_cast<char *>(src);
124 CString referencing(str, strlen(str));
125 LOG_FULL(INFO) << "current str during module deregister process : " << referencing;
126 auto entry = resolvedModules_.Find(referencing);
127 if (!entry) { // LCOV_EXCL_BR_LINE
128 LOG_FULL(INFO) << "The module has been unloaded, " << referencing;
129 return JSTaggedValue::Undefined();
130 }
131 return entry.value();
132 }
133
IsLocalModuleLoaded(const CString & referencing)134 bool ModuleManager::IsLocalModuleLoaded(const CString& referencing)
135 {
136 auto entry = resolvedModules_.Find(referencing);
137 if (!entry) {
138 return false;
139 }
140 return true;
141 }
142
IsSharedModuleLoaded(const CString & referencing)143 bool ModuleManager::IsSharedModuleLoaded(const CString &referencing)
144 {
145 SharedModuleManager* sharedModuleManager = SharedModuleManager::GetInstance();
146 return sharedModuleManager->SearchInSModuleManager(vm_->GetJSThread(), referencing);
147 }
148
IsModuleLoaded(const CString & referencing)149 bool ModuleManager::IsModuleLoaded(const CString &referencing)
150 {
151 if (IsLocalModuleLoaded(referencing)) {
152 return true;
153 }
154 SharedModuleManager* sharedModuleManager = SharedModuleManager::GetInstance();
155 return sharedModuleManager->SearchInSModuleManager(vm_->GetJSThread(), referencing);
156 }
157
IsEvaluatedModule(const CString & referencing)158 bool ModuleManager::IsEvaluatedModule(const CString &referencing)
159 {
160 auto entry = resolvedModules_.Find(referencing);
161 if (!entry) {
162 return false;
163 }
164 JSTaggedValue result = entry.value();
165 // ModuleStatus == (EVALUATED || ERRORED).
166 if (SourceTextModule::Cast(result.GetTaggedObject())->GetStatus() >= ModuleStatus::EVALUATED) {
167 return true;
168 }
169 return false;
170 }
171
172 // This function assumes that referencing already existed in resolvedModules_/resolvedSharedModules_.
IsInstantiatedModule(const CString & referencing)173 bool ModuleManager::IsInstantiatedModule(const CString &referencing)
174 {
175 JSHandle<SourceTextModule> module = GetImportedModule(referencing);
176 return module->GetStatus() == ModuleStatus::INSTANTIATED;
177 }
178
IsLocalModuleInstantiated(const CString & referencing)179 bool ModuleManager::IsLocalModuleInstantiated(const CString &referencing)
180 {
181 JSHandle<SourceTextModule> module = HostGetImportedModule(referencing);
182 return module->GetStatus() == ModuleStatus::INSTANTIATED;
183 }
184
NeedExecuteModule(const CString & referencing)185 bool ModuleManager::NeedExecuteModule(const CString &referencing)
186 {
187 if (IsModuleLoaded(referencing)) {
188 JSHandle<SourceTextModule> module = GetImportedModule(referencing);
189 return module->GetStatus() == ModuleStatus::INSTANTIATED;
190 }
191 return true;
192 }
193
Iterate(RootVisitor & v)194 void ModuleManager::Iterate(RootVisitor &v)
195 {
196 resolvedModules_.ForEach(
197 [&v](auto iter) { iter->second.VisitRoot([&v](ObjectSlot slot) { v.VisitRoot(Root::ROOT_VM, slot); }); });
198 }
199
GetRecordName(const JSThread * thread,JSTaggedValue module)200 CString ModuleManager::GetRecordName(const JSThread *thread, JSTaggedValue module)
201 {
202 CString entry = "";
203 if (module.IsString()) {
204 entry = ModulePathHelper::Utf8ConvertToString(const_cast<JSThread*>(thread), module);
205 }
206 if (module.IsSourceTextModule()) {
207 SourceTextModule *sourceTextModule = SourceTextModule::Cast(module.GetTaggedObject());
208 CString recordName = sourceTextModule->GetEcmaModuleRecordNameString();
209 if (!recordName.empty()) {
210 return recordName;
211 }
212 }
213 return entry;
214 }
215
GetExportObjectIndex(EcmaVM * vm,JSHandle<SourceTextModule> ecmaModule,const CString & key)216 int ModuleManager::GetExportObjectIndex(EcmaVM *vm, JSHandle<SourceTextModule> ecmaModule,
217 const CString &key)
218 {
219 JSThread *thread = vm->GetJSThread();
220 if (ecmaModule->GetLocalExportEntries(thread).IsUndefined()) {
221 CString msg = "No export named '" + key;
222 if (!ecmaModule->GetEcmaModuleRecordNameString().empty()) {
223 msg += "' which exported by '" + ecmaModule->GetEcmaModuleRecordNameString() + "'";
224 } else {
225 msg += "' which exported by '" + ecmaModule->GetEcmaModuleFilenameString() + "'";
226 }
227 ObjectFactory *factory = vm->GetFactory();
228 JSTaggedValue error = factory->GetJSError(ErrorType::SYNTAX_ERROR, msg.c_str(),
229 StackCheck::NO).GetTaggedValue();
230 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, 0);
231 }
232 JSHandle<TaggedArray> localExportEntries(thread, ecmaModule->GetLocalExportEntries(thread));
233 size_t exportEntriesLen = localExportEntries->GetLength();
234 // 0: There's only one export value "default"
235 int index = 0;
236 JSMutableHandle<LocalExportEntry> ee(thread, thread->GlobalConstants()->GetUndefined());
237 if (exportEntriesLen > 1) { // 1: The number of export objects exceeds 1
238 for (size_t idx = 0; idx < exportEntriesLen; idx++) {
239 ee.Update(localExportEntries->Get(thread, idx));
240 if (EcmaStringAccessor(ee->GetExportName(thread)).Utf8ConvertToString(thread) == key) {
241 ASSERT(idx <= static_cast<size_t>(INT_MAX));
242 index = static_cast<int>(ee->GetLocalIndex());
243 break;
244 }
245 }
246 }
247 return index;
248 }
249
LoadNativeModule(JSThread * thread,const CString & key)250 JSHandle<JSTaggedValue> ModuleManager::LoadNativeModule(JSThread *thread, const CString &key)
251 {
252 JSHandle<SourceTextModule> ecmaModule = JSHandle<SourceTextModule>::Cast(ExecuteNativeModule(thread, key));
253 ASSERT(ecmaModule->GetIsNewBcVersion());
254 int index = GetExportObjectIndex(vm_, ecmaModule, key);
255 JSTaggedValue result = ecmaModule->GetModuleValue(thread, index, false);
256 return JSHandle<JSTaggedValue>(thread, result);
257 }
258
ExecuteNativeModuleMayThrowError(JSThread * thread,const CString & recordName)259 JSHandle<JSTaggedValue> ModuleManager::ExecuteNativeModuleMayThrowError(JSThread *thread, const CString &recordName)
260 {
261 JSMutableHandle<JSTaggedValue> requiredModule(thread, thread->GlobalConstants()->GetUndefined());
262 if (IsEvaluatedModule(recordName)) {
263 JSHandle<SourceTextModule> moduleRecord = HostGetImportedModule(recordName);
264 return JSHandle<JSTaggedValue>(thread, moduleRecord->GetModuleValue(thread, 0, false));
265 }
266
267 ModuleTypes moduleType = SourceTextModule::GetNativeModuleType(recordName);
268 JSHandle<JSTaggedValue> moduleRecord = ModuleDataExtractor::ParseNativeModule(thread,
269 recordName, "", moduleType);
270 JSHandle<SourceTextModule> nativeModule =
271 JSHandle<SourceTextModule>::Cast(moduleRecord);
272 auto exportObject = SourceTextModule::LoadNativeModuleMayThrowError(thread, nativeModule, moduleType);
273 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
274 JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
275 SourceTextModule::RecordEvaluatedOrError(thread, nativeModule);
276 nativeModule->SetLoadingTypes(LoadingTypes::STABLE_MODULE);
277 SourceTextModule::StoreModuleValue(thread, nativeModule, 0, exportObject);
278 AddResolveImportedModule(recordName, moduleRecord.GetTaggedValue());
279 return exportObject;
280 }
281
ExecuteNativeModule(JSThread * thread,const CString & recordName)282 JSHandle<JSTaggedValue> ModuleManager::ExecuteNativeModule(JSThread *thread, const CString &recordName)
283 {
284 JSMutableHandle<JSTaggedValue> requiredModule(thread, thread->GlobalConstants()->GetUndefined());
285 if (IsEvaluatedModule(recordName)) {
286 JSHandle<SourceTextModule> moduleRecord = HostGetImportedModule(recordName);
287 requiredModule.Update(moduleRecord);
288 } else if (IsLocalModuleLoaded(recordName)) {
289 JSHandle<SourceTextModule> nativeModule = HostGetImportedModule(recordName);
290 SourceTextModule::EvaluateNativeModule(thread, nativeModule, nativeModule->GetTypes());
291 nativeModule->SetLoadingTypes(LoadingTypes::STABLE_MODULE);
292 requiredModule.Update(nativeModule);
293 } else {
294 ModuleTypes moduleType = SourceTextModule::GetNativeModuleType(recordName);
295 JSHandle<JSTaggedValue> nativeModuleHandle =
296 ModuleResolver::ResolveNativeModule(thread, recordName, "", moduleType);
297 JSHandle<SourceTextModule> nativeModule =
298 JSHandle<SourceTextModule>::Cast(nativeModuleHandle);
299 SourceTextModule::EvaluateNativeModule(thread, nativeModule, moduleType);
300 nativeModule->SetLoadingTypes(LoadingTypes::STABLE_MODULE);
301 requiredModule.Update(nativeModule);
302 }
303 AddResolveImportedModule(recordName, requiredModule.GetTaggedValue());
304 return requiredModule;
305 }
306
ExecuteJsonModule(JSThread * thread,const CString & recordName,const CString & filename,const JSPandaFile * jsPandaFile)307 JSHandle<JSTaggedValue> ModuleManager::ExecuteJsonModule(JSThread *thread, const CString &recordName,
308 const CString &filename, const JSPandaFile *jsPandaFile)
309 {
310 JSMutableHandle<JSTaggedValue> requiredModule(thread, thread->GlobalConstants()->GetUndefined());
311 if (IsEvaluatedModule(recordName)) {
312 JSHandle<SourceTextModule> moduleRecord = HostGetImportedModule(recordName);
313 requiredModule.Update(moduleRecord);
314 } else {
315 JSHandle<SourceTextModule> moduleRecord =
316 JSHandle<SourceTextModule>::Cast(ModuleDataExtractor::ParseJsonModule(thread, jsPandaFile, filename,
317 recordName));
318 SourceTextModule::RecordEvaluatedOrError(thread, moduleRecord);
319 requiredModule.Update(moduleRecord);
320 UpdateResolveImportedModule(recordName, moduleRecord.GetTaggedValue());
321 }
322 return requiredModule;
323 }
324
ExecuteCjsModule(JSThread * thread,const CString & recordName,const JSPandaFile * jsPandaFile)325 JSHandle<JSTaggedValue> ModuleManager::ExecuteCjsModule(JSThread *thread, const CString &recordName,
326 const JSPandaFile *jsPandaFile)
327 {
328 CString entryPoint = JSPandaFile::ENTRY_FUNCTION_NAME;
329 CString moduleRecord = jsPandaFile->GetJSPandaFileDesc();
330 if (!jsPandaFile->IsBundlePack()) {
331 entryPoint = recordName;
332 moduleRecord = recordName;
333 }
334
335 JSMutableHandle<JSTaggedValue> requiredModule(thread, thread->GlobalConstants()->GetUndefined());
336 if (IsEvaluatedModule(moduleRecord)) {
337 requiredModule.Update(HostGetImportedModule(moduleRecord));
338 } else {
339 JSHandle<SourceTextModule> module =
340 JSHandle<SourceTextModule>::Cast(ModuleDataExtractor::ParseCjsModule(thread, jsPandaFile));
341 module->SetEcmaModuleRecordNameString(moduleRecord);
342 requiredModule.Update(module);
343 AddResolveImportedModule(recordName, module.GetTaggedValue());
344 JSPandaFileExecutor::Execute(thread, jsPandaFile, entryPoint);
345 SourceTextModule::RecordEvaluatedOrError(thread, JSHandle<SourceTextModule>::Cast(requiredModule));
346 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, requiredModule);
347 UpdateResolveImportedModule(recordName, module.GetTaggedValue());
348 }
349 return requiredModule;
350 }
351
TryGetImportedModule(const CString & referencing)352 JSHandle<JSTaggedValue> ModuleManager::TryGetImportedModule(const CString& referencing)
353 {
354 JSThread *thread = vm_->GetJSThread();
355 auto entry = resolvedModules_.Find(referencing);
356 if (!entry) {
357 return thread->GlobalConstants()->GetHandledUndefined();
358 }
359 return JSHandle<JSTaggedValue>(thread, entry.value());
360 }
361
RemoveModuleFromCache(const CString & recordName)362 void ModuleManager::RemoveModuleFromCache(const CString& recordName)
363 {
364 auto entry = resolvedModules_.Find(recordName);
365 if (!entry) { // LCOV_EXCL_BR_LINE
366 LOG_ECMA(FATAL) << "Can not get module: " << recordName <<
367 ", when try to remove the module";
368 }
369 JSTaggedValue result = entry.value();
370 SourceTextModule::Cast(result)->DestoryLazyImportArray();
371 SourceTextModule::Cast(result)->DestoryEcmaModuleFilenameString();
372 SourceTextModule::Cast(result)->DestoryEcmaModuleRecordNameString();
373 resolvedModules_.Erase(recordName);
374 }
375
376 // this function only remove module's name from resolvedModules List, it's content still needed by sharedmodule
RemoveModuleNameFromList(const CString & recordName)377 void ModuleManager::RemoveModuleNameFromList(const CString& recordName)
378 {
379 resolvedModules_.Erase(recordName);
380 }
381
CreateModuleManagerNativePointer(JSThread * thread)382 JSTaggedValue ModuleManager::CreateModuleManagerNativePointer(JSThread *thread)
383 {
384 EcmaVM *vm = thread->GetEcmaVM();
385 ModuleManager *moduleManager = new ModuleManager(vm);
386 moduleManager->SyncModuleExecuteMode(thread);
387 auto factory = vm->GetFactory();
388 JSHandle<JSNativePointer> nativePointer = factory->NewJSNativePointer(reinterpret_cast<void *>(moduleManager),
389 nullptr, nullptr, true);
390 vm->AddModuleManager(moduleManager);
391 return nativePointer.GetTaggedValue();
392 }
393
SyncModuleExecuteMode(JSThread * thread)394 void ModuleManager::SyncModuleExecuteMode(JSThread *thread)
395 {
396 JSHandle<GlobalEnv> globalEnv = thread->GetGlobalEnv();
397 if (!globalEnv.GetTaggedValue().IsHole()) {
398 JSHandle<JSNativePointer> nativePointer(globalEnv->GetModuleManagerNativePointer());
399 ModuleManager *moduleManager = reinterpret_cast<ModuleManager *>(nativePointer->GetExternalPointer());
400 if (moduleManager == nullptr) {
401 return;
402 }
403 SetExecuteMode(moduleManager->GetExecuteMode());
404 }
405 }
406 } // namespace panda::ecmascript
407