• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #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