/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/global_env.h" #include "ecmascript/base/string_helper.h" #include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/module_data_extractor.h" #include "ecmascript/linked_hash_table.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/module/js_module_namespace.h" #include "ecmascript/tagged_dictionary.h" namespace panda::ecmascript { CVector SourceTextModule::GetExportedNames(JSThread *thread, const JSHandle &module, const JSHandle &exportStarSet) { CVector exportedNames; // 1. Let module be this Source Text Module Record. // 2. If exportStarSet contains module, then if (exportStarSet->GetIdx(module.GetTaggedValue()) != TaggedArray::MAX_ARRAY_INDEX) { // a. Assert: We've reached the starting point of an import * circularity. // b. Return a new empty List. return exportedNames; } // 3. Append module to exportStarSet. size_t len = exportStarSet->GetLength(); JSHandle newExportStarSet = TaggedArray::SetCapacity(thread, exportStarSet, len + 1); newExportStarSet->Set(thread, len, module.GetTaggedValue()); // 5. For each ExportEntry Record e in module.[[LocalExportEntries]], do JSTaggedValue entryValue = module->GetLocalExportEntries(); auto globalConstants = thread->GlobalConstants(); if (!entryValue.IsUndefined()) { JSHandle localExportEntries(thread, entryValue); size_t localExportEntriesLen = localExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < localExportEntriesLen; idx++) { ee.Update(localExportEntries->Get(idx)); // a. Assert: module provides the direct binding for this export. // b. Append e.[[ExportName]] to exportedNames. std::string exportName = base::StringHelper::ToStdString(EcmaString::Cast(ee->GetExportName().GetHeapObject())); exportedNames.emplace_back(exportName); } } // 6. For each ExportEntry Record e in module.[[IndirectExportEntries]], do entryValue = module->GetIndirectExportEntries(); if (!entryValue.IsUndefined()) { JSHandle indirectExportEntries(thread, entryValue); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(idx)); // a. Assert: module imports a specific binding for this export. // b. Append e.[[ExportName]] to exportedNames. std::string exportName = base::StringHelper::ToStdString(EcmaString::Cast(ee->GetExportName().GetHeapObject())); exportedNames.emplace_back(exportName); } } // 7. For each ExportEntry Record e in module.[[StarExportEntries]], do entryValue = module->GetStarExportEntries(); if (!entryValue.IsUndefined()) { JSHandle starExportEntries(thread, entryValue); size_t starExportEntriesLen = starExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle moduleRequest(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < starExportEntriesLen; idx++) { ee.Update(starExportEntries->Get(idx)); // a. Let requestedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]). moduleRequest.Update(ee->GetModuleRequest()); JSHandle requestedModule = SourceTextModule::HostResolveImportedModule(thread, module, moduleRequest); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exportedNames); // b. Let starNames be ? requestedModule.GetExportedNames(exportStarSet). CVector starNames = SourceTextModule::GetExportedNames(thread, requestedModule, newExportStarSet); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exportedNames); // c. For each element n of starNames, do for (std::string &nn : starNames) { // i. If SameValue(n, "default") is false, then if (nn != "default" && std::find(exportedNames.begin(), exportedNames.end(), nn) == exportedNames.end()) { // 1. If n is not an element of exportedNames, then // a. Append n to exportedNames. exportedNames.emplace_back(nn); } } } } return exportedNames; } JSHandle SourceTextModule::HostResolveImportedModule(JSThread *thread, const JSHandle &module, const JSHandle &moduleRequest) { CString moduleFilename = ConvertToString(EcmaString::Cast(moduleRequest->GetHeapObject())); ASSERT(module->GetEcmaModuleFilename().IsHeapObject()); CString baseFilename = ConvertToString(EcmaString::Cast(module->GetEcmaModuleFilename().GetHeapObject())); int suffixEnd = moduleFilename.find_last_of('.'); if (suffixEnd == -1) { RETURN_HANDLE_IF_ABRUPT_COMPLETION(SourceTextModule, thread); } CString moduleFullname; if (moduleFilename[0] == '/') { // absoluteFilePath moduleFullname = moduleFilename.substr(0, suffixEnd) + ".abc"; } else { int pos = baseFilename.find_last_of('/'); if (pos == -1) { RETURN_HANDLE_IF_ABRUPT_COMPLETION(SourceTextModule, thread); } moduleFullname = baseFilename.substr(0, pos + 1) + moduleFilename.substr(0, suffixEnd) + ".abc"; } return thread->GetEcmaVM()->GetModuleManager()->HostResolveImportedModule(moduleFullname); } JSHandle SourceTextModule::ResolveExport(JSThread *thread, const JSHandle &module, const JSHandle &exportName, CVector, JSHandle>> &resolveSet) { // 1. Let module be this Source Text Module Record. // 2. For each Record { [[Module]], [[ExportName]] } r in resolveSet, do auto globalConstants = thread->GlobalConstants(); for (auto rr : resolveSet) { // a. If module and r.[[Module]] are the same Module Record and // SameValue(exportName, r.[[ExportName]]) is true, then if (JSTaggedValue::SameValue(rr.first.GetTaggedValue(), module.GetTaggedValue()) && JSTaggedValue::SameValue(rr.second, exportName)) { // i. Assert: This is a circular import request. // ii. Return null. return globalConstants->GetHandledNull(); } } // 3. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolveSet. resolveSet.emplace_back(std::make_pair(module, exportName)); // 4. For each ExportEntry Record e in module.[[LocalExportEntries]], do JSTaggedValue localExportEntriesTv = module->GetLocalExportEntries(); if (!localExportEntriesTv.IsUndefined()) { JSHandle localExportEntries(thread, localExportEntriesTv); size_t localExportEntriesLen = localExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle localName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < localExportEntriesLen; idx++) { ee.Update(localExportEntries->Get(idx)); // a. If SameValue(exportName, e.[[ExportName]]) is true, then if (JSTaggedValue::SameValue(ee->GetExportName(), exportName.GetTaggedValue())) { // i. Assert: module provides the direct binding for this export. // ii. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }. ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); localName.Update(ee->GetLocalName()); return JSHandle::Cast(factory->NewResolvedBindingRecord(module, localName)); } } } // 5. For each ExportEntry Record e in module.[[IndirectExportEntries]], do JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(); if (!indirectExportEntriesTv.IsUndefined()) { JSHandle indirectExportEntries(thread, indirectExportEntriesTv); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle moduleRequest(thread, globalConstants->GetUndefined()); JSMutableHandle importName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(idx)); // a. If SameValue(exportName, e.[[ExportName]]) is true, then if (JSTaggedValue::SameValue(exportName.GetTaggedValue(), ee->GetExportName())) { // i. Assert: module imports a specific binding for this export. // ii. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]). moduleRequest.Update(ee->GetModuleRequest()); JSHandle requestedModule = SourceTextModule::HostResolveImportedModule(thread, module, moduleRequest); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // iii. Return importedModule.ResolveExport(e.[[ImportName]], resolveSet). importName.Update(ee->GetImportName()); return SourceTextModule::ResolveExport(thread, requestedModule, importName, resolveSet); } } } // 6. If SameValue(exportName, "default") is true, then JSHandle defaultString = globalConstants->GetHandledDefaultString(); if (JSTaggedValue::SameValue(exportName, defaultString)) { // a. Assert: A default export was not explicitly defined by this module. // b. Return null. // c. NOTE: A default export cannot be provided by an export *. return globalConstants->GetHandledNull(); } // 7. Let starResolution be null. JSMutableHandle starResolution(thread, globalConstants->GetNull()); // 8. For each ExportEntry Record e in module.[[StarExportEntries]], do JSTaggedValue starExportEntriesTv = module->GetStarExportEntries(); if (starExportEntriesTv.IsUndefined()) { return starResolution; } JSHandle starExportEntries(thread, starExportEntriesTv); size_t starExportEntriesLen = starExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle moduleRequest(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < starExportEntriesLen; idx++) { ee.Update(starExportEntries->Get(idx)); // a. Let importedModule be ? HostResolveImportedModule(module, e.[[ModuleRequest]]). moduleRequest.Update(ee->GetModuleRequest()); JSHandle importedModule = SourceTextModule::HostResolveImportedModule(thread, module, moduleRequest); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // b. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet). JSHandle resolution = SourceTextModule::ResolveExport(thread, importedModule, exportName, resolveSet); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // c. If resolution is "ambiguous", return "ambiguous". if (resolution->IsString()) { // if resolution is string, resolution must be "ambiguous" return globalConstants->GetHandledAmbiguousString(); } // d. If resolution is not null, then if (resolution->IsNull()) { continue; } // i. Assert: resolution is a ResolvedBinding Record. ASSERT(resolution->IsResolvedBinding()); // ii. If starResolution is null, set starResolution to resolution. if (starResolution->IsNull()) { starResolution.Update(resolution.GetTaggedValue()); } else { // 1. Assert: There is more than one * import that includes the requested name. // 2. If resolution.[[Module]] and starResolution.[[Module]] are not the same Module Record or // SameValue( // resolution.[[BindingName]], starResolution.[[BindingName]]) is false, return "ambiguous". JSHandle resolutionBd = JSHandle::Cast(resolution); JSHandle starResolutionBd = JSHandle::Cast(starResolution); if ((!JSTaggedValue::SameValue(resolutionBd->GetModule(), starResolutionBd->GetModule())) || (!JSTaggedValue::SameValue( resolutionBd->GetBindingName(), starResolutionBd->GetBindingName()))) { return globalConstants->GetHandledAmbiguousString(); } } } // 9. Return starResolution. return starResolution; } int SourceTextModule::Instantiate(JSThread *thread, const JSHandle &module) { // 1. Let module be this Source Text Module Record. // 2. Assert: module.[[Status]] is not "instantiating" or "evaluating". ModuleStatus status = module->GetStatus(); ASSERT(status != ModuleStatus::INSTANTIATING && status != ModuleStatus::EVALUATING); // 3. Let stack be a new empty List. CVector> stack; // 4. Let result be InnerModuleInstantiation(module, stack, 0). JSHandle moduleRecord = JSHandle::Cast(module); int result = SourceTextModule::InnerModuleInstantiation(thread, moduleRecord, stack, 0); // 5. If result is an abrupt completion, then if (thread->HasPendingException()) { // a. For each module m in stack, do for (auto mm : stack) { // i. Assert: m.[[Status]] is "instantiating". [[maybe_unused]] ModuleStatus mmStatus = mm->GetStatus(); ASSERT(mmStatus == ModuleStatus::INSTANTIATING); // ii. Set m.[[Status]] to "uninstantiated". mm->SetStatus(ModuleStatus::UNINSTANTIATED); // iii. Set m.[[Environment]] to undefined. // iv. Set m.[[DFSIndex]] to undefined. mm->SetDFSIndex(SourceTextModule::UNDEFINED_INDEX); // v. Set m.[[DFSAncestorIndex]] to undefined. mm->SetDFSAncestorIndex(SourceTextModule::UNDEFINED_INDEX); } // b. Assert: module.[[Status]] is "uninstantiated". status = module->GetStatus(); ASSERT(status == ModuleStatus::UNINSTANTIATED); // c. return result return result; } // 6. Assert: module.[[Status]] is "instantiated" or "evaluated". status = module->GetStatus(); ASSERT(status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED); // 7. Assert: stack is empty. ASSERT(stack.empty()); // 8. Return undefined. return SourceTextModule::UNDEFINED_INDEX; } int SourceTextModule::InnerModuleInstantiation(JSThread *thread, const JSHandle &moduleRecord, CVector> &stack, int index) { // 1. If module is not a Source Text Module Record, then if (!moduleRecord.GetTaggedValue().IsSourceTextModule()) { // a. Perform ? module.Instantiate(). ModuleRecord::Instantiate(thread, JSHandle::Cast(moduleRecord)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // b. Return index. return index; } JSHandle module = JSHandle::Cast(moduleRecord); // 2. If module.[[Status]] is "instantiating", "instantiated", or "evaluated", then Return index. ModuleStatus status = module->GetStatus(); if (status == ModuleStatus::INSTANTIATING || status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED) { return index; } // 3. Assert: module.[[Status]] is "uninstantiated". ASSERT(status == ModuleStatus::UNINSTANTIATED); // 4. Set module.[[Status]] to "instantiating". module->SetStatus(ModuleStatus::INSTANTIATING); // 5. Set module.[[DFSIndex]] to index. module->SetDFSIndex(index); // 6. Set module.[[DFSAncestorIndex]] to index. module->SetDFSAncestorIndex(index); // 7. Set index to index + 1. index++; // 8. Append module to stack. stack.emplace_back(module); // 9. For each String required that is an element of module.[[RequestedModules]], do JSHandle requestedModules(thread, module->GetRequestedModules()); size_t requestedModulesLen = requestedModules->GetLength(); JSMutableHandle required(thread, thread->GlobalConstants()->GetUndefined()); for (size_t idx = 0; idx < requestedModulesLen; idx++) { required.Update(requestedModules->Get(idx)); // a. Let requiredModule be ? HostResolveImportedModule(module, required). JSHandle requiredModule = SourceTextModule::HostResolveImportedModule(thread, module, required); // b. Set index to ? InnerModuleInstantiation(requiredModule, stack, index). JSHandle requiredModuleRecord = JSHandle::Cast(requiredModule); index = SourceTextModule::InnerModuleInstantiation(thread, requiredModuleRecord, stack, index); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // c. Assert: requiredModule.[[Status]] is either "instantiating", "instantiated", or "evaluated". ModuleStatus requiredModuleStatus = requiredModule->GetStatus(); ASSERT((requiredModuleStatus == ModuleStatus::INSTANTIATING || requiredModuleStatus == ModuleStatus::INSTANTIATED || requiredModuleStatus == ModuleStatus::EVALUATED)); // d. Assert: requiredModule.[[Status]] is "instantiating" if and only if requiredModule is in stack. // e. If requiredModule.[[Status]] is "instantiating", then if (requiredModuleStatus == ModuleStatus::INSTANTIATING) { // d. Assert: requiredModule.[[Status]] is "instantiating" if and only if requiredModule is in stack. ASSERT(std::find(stack.begin(), stack.end(), requiredModule) != stack.end()); // i. Assert: requiredModule is a Source Text Module Record. // ii. Set module.[[DFSAncestorIndex]] to min( // module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]). int dfsAncIdx = std::min(module->GetDFSAncestorIndex(), requiredModule->GetDFSAncestorIndex()); module->SetDFSAncestorIndex(dfsAncIdx); } } // 10. Perform ? ModuleDeclarationEnvironmentSetup(module). SourceTextModule::ModuleDeclarationEnvironmentSetup(thread, module); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // 11. Assert: module occurs exactly once in stack. // 12. Assert: module.[[DFSAncestorIndex]] is less than or equal to module.[[DFSIndex]]. int dfsAncIdx = module->GetDFSAncestorIndex(); int dfsIdx = module->GetDFSIndex(); ASSERT(dfsAncIdx <= dfsIdx); // 13. If module.[[DFSAncestorIndex]] equals module.[[DFSIndex]], then if (dfsAncIdx == dfsIdx) { // a. Let done be false. bool done = false; // b. Repeat, while done is false, while (!done) { // i. Let requiredModule be the last element in stack. JSHandle requiredModule = stack.back(); // ii. Remove the last element of stack. stack.pop_back(); // iii. Set requiredModule.[[Status]] to "instantiated". requiredModule->SetStatus(ModuleStatus::INSTANTIATED); // iv. If requiredModule and module are the same Module Record, set done to true. if (JSTaggedValue::SameValue(module.GetTaggedValue(), requiredModule.GetTaggedValue())) { done = true; } } } return index; } void SourceTextModule::ModuleDeclarationEnvironmentSetup(JSThread *thread, const JSHandle &module) { auto globalConstants = thread->GlobalConstants(); // 1. For each ExportEntry Record e in module.[[IndirectExportEntries]], do JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(); if (!indirectExportEntriesTv.IsUndefined()) { JSHandle indirectExportEntries(thread, indirectExportEntriesTv); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle exportName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(idx)); // a. Let resolution be ? module.ResolveExport(e.[[ExportName]], « »). exportName.Update(ee->GetExportName()); CVector, JSHandle>> resolveSet; JSHandle resolution = SourceTextModule::ResolveExport(thread, module, exportName, resolveSet); // b. If resolution is null or "ambiguous", throw a SyntaxError exception. if (resolution->IsNull() || resolution->IsString()) { THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, ""); } // c. Assert: resolution is a ResolvedBinding Record. ASSERT(resolution->IsResolvedBinding()); } } // 2. Assert: All named exports from module are resolvable. // 3. Let realm be module.[[Realm]]. // 4. Assert: realm is not undefined. // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). JSHandle map = LinkedHashMap::Create(thread); // 6. Set module.[[Environment]] to env. module->SetEnvironment(thread, map); // 7. Let envRec be env's EnvironmentRecord. JSMutableHandle envRec(thread, module->GetEnvironment()); ASSERT(!envRec->IsUndefined()); // 8. For each ImportEntry Record in in module.[[ImportEntries]], do JSTaggedValue importEntriesTv = module->GetImportEntries(); if (importEntriesTv.IsUndefined()) { module->SetEnvironment(thread, envRec); return; } JSHandle importEntries(thread, importEntriesTv); size_t importEntriesLen = importEntries->GetLength(); JSMutableHandle in(thread, globalConstants->GetUndefined()); JSMutableHandle moduleRequest(thread, globalConstants->GetUndefined()); JSMutableHandle importName(thread, globalConstants->GetUndefined()); JSMutableHandle localName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < importEntriesLen; idx++) { in.Update(importEntries->Get(idx)); // a. Let importedModule be ! HostResolveImportedModule(module, in.[[ModuleRequest]]). moduleRequest.Update(in->GetModuleRequest()); JSHandle importedModule = SourceTextModule::HostResolveImportedModule(thread, module, moduleRequest); // c. If in.[[ImportName]] is "*", then importName.Update(in->GetImportName()); JSHandle starString = globalConstants->GetHandledStarString(); if (JSTaggedValue::SameValue(importName, starString)) { // i. Let namespace be ? GetModuleNamespace(importedModule). JSHandle moduleNamespace = SourceTextModule::GetModuleNamespace(thread, importedModule); // ii. Perform ! envRec.CreateImmutableBinding(in.[[LocalName]], true). // iii. Call envRec.InitializeBinding(in.[[LocalName]], namespace). JSHandle mapHandle = JSHandle::Cast(envRec); localName.Update(in->GetLocalName()); JSHandle newMap = LinkedHashMap::Set(thread, mapHandle, localName, moduleNamespace); envRec.Update(newMap); } else { // i. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]], « »). CVector, JSHandle>> resolveSet; JSHandle resolution = SourceTextModule::ResolveExport(thread, importedModule, importName, resolveSet); // ii. If resolution is null or "ambiguous", throw a SyntaxError exception. if (resolution->IsNull() || resolution->IsString()) { THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, ""); } // iii. Call envRec.CreateImportBinding( // in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]). JSHandle mapHandle = JSHandle::Cast(envRec); localName.Update(in->GetLocalName()); JSHandle newMap = LinkedHashMap::Set(thread, mapHandle, localName, resolution); envRec.Update(newMap); } } module->SetEnvironment(thread, envRec); } JSHandle SourceTextModule::GetModuleNamespace(JSThread *thread, const JSHandle &module) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 1. Assert: module is an instance of a concrete subclass of Module Record. // 2. Assert: module.[[Status]] is not "uninstantiated". ModuleStatus status = module->GetStatus(); ASSERT(status != ModuleStatus::UNINSTANTIATED); // 3. Assert: If module.[[Status]] is "evaluated", module.[[EvaluationError]] is undefined. if (status == ModuleStatus::EVALUATED) { ASSERT(module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX); } // 4. Let namespace be module.[[Namespace]]. JSMutableHandle moduleNamespace(thread, module->GetNamespace()); // If namespace is undefined, then if (moduleNamespace->IsUndefined()) { // a. Let exportedNames be ? module.GetExportedNames(« »). JSHandle exportStarSet = factory->EmptyArray(); CVector exportedNames = SourceTextModule::GetExportedNames(thread, module, exportStarSet); // b. Let unambiguousNames be a new empty List. JSHandle unambiguousNames = factory->NewTaggedArray(exportedNames.size()); // c. For each name that is an element of exportedNames, do size_t idx = 0; for (std::string &name : exportedNames) { // i. Let resolution be ? module.ResolveExport(name, « »). CVector, JSHandle>> resolveSet; JSHandle nameHandle = JSHandle::Cast(factory->NewFromStdString(name)); JSHandle resolution = SourceTextModule::ResolveExport(thread, module, nameHandle, resolveSet); // ii. If resolution is a ResolvedBinding Record, append name to unambiguousNames. if (resolution->IsResolvedBinding()) { unambiguousNames->Set(thread, idx, nameHandle); idx++; } } JSHandle fixUnambiguousNames = TaggedArray::SetCapacity(thread, unambiguousNames, idx); JSHandle moduleTagged = JSHandle::Cast(module); JSHandle np = ModuleNamespace::ModuleNamespaceCreate(thread, moduleTagged, fixUnambiguousNames); moduleNamespace.Update(np.GetTaggedValue()); } return moduleNamespace; } int SourceTextModule::Evaluate(JSThread *thread, const JSHandle &module) { // 1. Let module be this Source Text Module Record. // 2. Assert: module.[[Status]] is "instantiated" or "evaluated". ModuleStatus status = module->GetStatus(); ASSERT((status == ModuleStatus::INSTANTIATED || status == ModuleStatus::EVALUATED)); // 3. Let stack be a new empty List. CVector> stack; // 4. Let result be InnerModuleEvaluation(module, stack, 0) JSHandle moduleRecord = JSHandle::Cast(module); int result = SourceTextModule::InnerModuleEvaluation(thread, moduleRecord, stack, 0); // 5. If result is an abrupt completion, then if (thread->HasPendingException()) { // a. For each module m in stack, do for (auto mm : stack) { // i. Assert: m.[[Status]] is "evaluating". [[maybe_unused]] ModuleStatus mmStatus = mm->GetStatus(); ASSERT(mmStatus == ModuleStatus::EVALUATING); // ii. Set m.[[Status]] to "evaluated". mm->SetStatus(ModuleStatus::EVALUATED); // iii. Set m.[[EvaluationError]] to result. mm->SetEvaluationError(result); } // b. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is result. status = module->GetStatus(); ASSERT(status == ModuleStatus::EVALUATED && module->GetEvaluationError() == result); // c. return result return result; } // 6. Assert: module.[[Status]] is "evaluated" and module.[[EvaluationError]] is undefined. status = module->GetStatus(); ASSERT(status == ModuleStatus::EVALUATED && module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX); // 7. Assert: stack is empty. ASSERT(stack.empty()); // 8. Return undefined. return SourceTextModule::UNDEFINED_INDEX; } int SourceTextModule::InnerModuleEvaluation(JSThread *thread, const JSHandle &moduleRecord, CVector> &stack, int index) { // 1. If module is not a Source Text Module Record, then if (!moduleRecord.GetTaggedValue().IsSourceTextModule()) { // a. Perform ? module.Instantiate(). ModuleRecord::Instantiate(thread, JSHandle::Cast(moduleRecord)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // b. Return index. return index; } JSHandle module = JSHandle::Cast(moduleRecord); // 2.If module.[[Status]] is "evaluated", then ModuleStatus status = module->GetStatus(); if (status == ModuleStatus::EVALUATED) { // a. If module.[[EvaluationError]] is undefined, return index. if (module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX) { return index; } // Otherwise return module.[[EvaluationError]]. return module->GetEvaluationError(); } // 3. If module.[[Status]] is "evaluating", return index. if (status == ModuleStatus::EVALUATING) { return index; } // 4. Assert: module.[[Status]] is "instantiated". ASSERT(status == ModuleStatus::INSTANTIATED); // 5. Set module.[[Status]] to "evaluating". module->SetStatus(ModuleStatus::EVALUATING); // 6. Set module.[[DFSIndex]] to index. module->SetDFSIndex(index); // 7. Set module.[[DFSAncestorIndex]] to index. module->SetDFSAncestorIndex(index); // 8. Set index to index + 1. index++; // 9. Append module to stack. stack.emplace_back(module); // 10. For each String required that is an element of module.[[RequestedModules]], do JSHandle requestedModules(thread, module->GetRequestedModules()); size_t requestedModulesLen = requestedModules->GetLength(); JSMutableHandle required(thread, thread->GlobalConstants()->GetUndefined()); for (size_t idx = 0; idx < requestedModulesLen; idx++) { required.Update(requestedModules->Get(idx)); // a. Let requiredModule be ! HostResolveImportedModule(module, required). JSHandle requiredModule = SourceTextModule::HostResolveImportedModule(thread, module, required); // c. Set index to ? InnerModuleEvaluation(requiredModule, stack, index). JSHandle requiredModuleRecord = JSHandle::Cast(requiredModule); index = SourceTextModule::InnerModuleEvaluation(thread, requiredModuleRecord, stack, index); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // d. Assert: requiredModule.[[Status]] is either "evaluating" or "evaluated". ModuleStatus requiredModuleStatus = requiredModule->GetStatus(); ASSERT((requiredModuleStatus == ModuleStatus::EVALUATING || requiredModuleStatus == ModuleStatus::EVALUATED)); // e. Assert: requiredModule.[[Status]] is "evaluating" if and only if requiredModule is in stack. if (requiredModuleStatus == ModuleStatus::EVALUATING) { ASSERT(std::find(stack.begin(), stack.end(), requiredModule) != stack.end()); } // f. If requiredModule.[[Status]] is "evaluating", then if (requiredModuleStatus == ModuleStatus::EVALUATING) { // i. Assert: requiredModule is a Source Text Module Record. // ii. Set module.[[DFSAncestorIndex]] to min( // module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]). int dfsAncIdx = std::min(module->GetDFSAncestorIndex(), requiredModule->GetDFSAncestorIndex()); module->SetDFSAncestorIndex(dfsAncIdx); } } // 11. Perform ? ModuleExecution(module). SourceTextModule::ModuleExecution(thread, module); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // 12. Assert: module occurs exactly once in stack. // 13. Assert: module.[[DFSAncestorIndex]] is less than or equal to module.[[DFSIndex]]. int dfsAncIdx = module->GetDFSAncestorIndex(); int dfsIdx = module->GetDFSIndex(); ASSERT(dfsAncIdx <= dfsIdx); // 14. If module.[[DFSAncestorIndex]] equals module.[[DFSIndex]], then if (dfsAncIdx == dfsIdx) { // a. Let done be false. bool done = false; // b. Repeat, while done is false, while (!done) { // i. Let requiredModule be the last element in stack. JSHandle requiredModule = stack.back(); // ii. Remove the last element of stack. stack.pop_back(); // iii. Set requiredModule.[[Status]] to "evaluated". requiredModule->SetStatus(ModuleStatus::EVALUATED); // iv. If requiredModule and module are the same Module Record, set done to true. if (JSTaggedValue::SameValue(module.GetTaggedValue(), requiredModule.GetTaggedValue())) { done = true; } } } return index; } void SourceTextModule::ModuleExecution(JSThread *thread, const JSHandle &module) { JSTaggedValue moduleFileName = module->GetEcmaModuleFilename(); ASSERT(moduleFileName.IsString()); CString moduleFilenameStr = ConvertToString(EcmaString::Cast(moduleFileName.GetHeapObject())); const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(moduleFilenameStr); if (jsPandaFile == nullptr) { LOG_ECMA(ERROR) << "open jsPandaFile " << moduleFilenameStr << " error"; UNREACHABLE(); } std::vector argv; JSPandaFileExecutor::Execute(thread, jsPandaFile, ENTRY_MAIN_FUNCTION, argv); } void SourceTextModule::AddImportEntry(JSThread *thread, const JSHandle &module, const JSHandle &importEntry) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue importEntries = module->GetImportEntries(); if (importEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(1); array->Set(thread, 0, importEntry.GetTaggedValue()); module->SetImportEntries(thread, array); } else { JSHandle entries(thread, importEntries); size_t len = entries->GetLength(); JSHandle newEntries = TaggedArray::SetCapacity(thread, entries, len + 1); newEntries->Set(thread, len, importEntry.GetTaggedValue()); module->SetImportEntries(thread, newEntries); } } void SourceTextModule::AddLocalExportEntry(JSThread *thread, const JSHandle &module, const JSHandle &exportEntry) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue localExportEntries = module->GetLocalExportEntries(); if (localExportEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(1); array->Set(thread, 0, exportEntry.GetTaggedValue()); module->SetLocalExportEntries(thread, array); } else { JSHandle entries(thread, localExportEntries); size_t len = entries->GetLength(); JSHandle newEntries = TaggedArray::SetCapacity(thread, entries, len + 1); newEntries->Set(thread, len, exportEntry.GetTaggedValue()); module->SetLocalExportEntries(thread, newEntries); } } void SourceTextModule::AddIndirectExportEntry(JSThread *thread, const JSHandle &module, const JSHandle &exportEntry) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue indirectExportEntries = module->GetIndirectExportEntries(); if (indirectExportEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(1); array->Set(thread, 0, exportEntry.GetTaggedValue()); module->SetIndirectExportEntries(thread, array); } else { JSHandle entries(thread, indirectExportEntries); size_t len = entries->GetLength(); JSHandle newEntries = TaggedArray::SetCapacity(thread, entries, len + 1); newEntries->Set(thread, len, exportEntry.GetTaggedValue()); module->SetIndirectExportEntries(thread, newEntries); } } void SourceTextModule::AddStarExportEntry(JSThread *thread, const JSHandle &module, const JSHandle &exportEntry) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSTaggedValue starExportEntries = module->GetStarExportEntries(); if (starExportEntries.IsUndefined()) { JSHandle array = factory->NewTaggedArray(1); array->Set(thread, 0, exportEntry.GetTaggedValue()); module->SetStarExportEntries(thread, array); } else { JSHandle entries(thread, starExportEntries); size_t len = entries->GetLength(); JSHandle newEntries = TaggedArray::SetCapacity(thread, entries, len + 1); newEntries->Set(thread, len, exportEntry.GetTaggedValue()); module->SetStarExportEntries(thread, newEntries); } } JSTaggedValue SourceTextModule::GetModuleValue(JSThread *thread, JSTaggedValue key, bool isThrow) { JSTaggedValue dictionary = GetNameDictionary(); if (dictionary.IsUndefined()) { if (isThrow) { THROW_REFERENCE_ERROR_AND_RETURN(thread, "module environment is undefined", JSTaggedValue::Exception()); } return JSTaggedValue::Hole(); } NameDictionary *dict = NameDictionary::Cast(dictionary.GetTaggedObject()); int entry = dict->FindEntry(key); if (entry != -1) { return dict->GetValue(entry); } JSTaggedValue importEntriesTv = GetImportEntries(); if (!importEntriesTv.IsUndefined()) { TaggedArray *importEntries = TaggedArray::Cast(importEntriesTv.GetTaggedObject()); size_t importEntriesLen = importEntries->GetLength(); for (size_t idx = 0; idx < importEntriesLen; idx++) { ImportEntry *ee = ImportEntry::Cast(importEntries->Get(idx).GetTaggedObject()); if (!JSTaggedValue::SameValue(ee->GetLocalName(), key)) { continue; } JSTaggedValue importName = ee->GetImportName(); entry = dict->FindEntry(importName); if (entry != -1) { return dict->GetValue(entry); } } } JSTaggedValue exportEntriesTv = GetLocalExportEntries(); if (!exportEntriesTv.IsUndefined()) { TaggedArray *exportEntries = TaggedArray::Cast(exportEntriesTv.GetTaggedObject()); size_t exportEntriesLen = exportEntries->GetLength(); for (size_t idx = 0; idx < exportEntriesLen; idx++) { ExportEntry *ee = ExportEntry::Cast(exportEntries->Get(idx).GetTaggedObject()); if (!JSTaggedValue::SameValue(ee->GetLocalName(), key)) { continue; } JSTaggedValue exportName = ee->GetExportName(); entry = dict->FindEntry(exportName); if (entry != -1) { return dict->GetValue(entry); } } } return JSTaggedValue::Hole(); } void SourceTextModule::StoreModuleValue(JSThread *thread, const JSHandle &key, const JSHandle &value) { JSHandle module(thread, this); JSMutableHandle data(thread, module->GetNameDictionary()); if (data->IsUndefined()) { data.Update(NameDictionary::Create(thread, DEAULT_DICTIONART_CAPACITY)); } JSMutableHandle dataDict(data); JSTaggedValue localExportEntriesTv = module->GetLocalExportEntries(); auto globalConstants = thread->GlobalConstants(); if (!localExportEntriesTv.IsUndefined()) { JSHandle localExportEntries(thread, localExportEntriesTv); size_t localExportEntriesLen = localExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle exportName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < localExportEntriesLen; idx++) { ee.Update(localExportEntries->Get(idx)); if (JSTaggedValue::SameValue(ee->GetLocalName(), key.GetTaggedValue())) { exportName.Update(ee->GetExportName()); dataDict.Update(NameDictionary::Put(thread, dataDict, exportName, value, PropertyAttributes::Default())); } } } JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(); if (!indirectExportEntriesTv.IsUndefined()) { JSHandle indirectExportEntries(thread, indirectExportEntriesTv); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); JSMutableHandle exportName(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(idx)); if (JSTaggedValue::SameValue(ee->GetImportName(), key.GetTaggedValue())) { exportName.Update(ee->GetExportName()); dataDict.Update(NameDictionary::Put(thread, dataDict, exportName, value, PropertyAttributes::Default())); } } } module->SetNameDictionary(thread, dataDict); } JSTaggedValue SourceTextModule::FindExportName(JSThread *thread, const JSHandle &localName) { JSHandle module(thread, this); JSTaggedValue localExportEntriesTv = module->GetLocalExportEntries(); auto globalConstants = thread->GlobalConstants(); if (!localExportEntriesTv.IsUndefined()) { JSHandle localExportEntries(thread, localExportEntriesTv); size_t localExportEntriesLen = localExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < localExportEntriesLen; idx++) { ee.Update(localExportEntries->Get(idx)); if (JSTaggedValue::SameValue(ee->GetLocalName(), localName.GetTaggedValue())) { return ee->GetExportName(); } } } JSTaggedValue indirectExportEntriesTv = module->GetIndirectExportEntries(); if (!indirectExportEntriesTv.IsUndefined()) { JSHandle indirectExportEntries(thread, indirectExportEntriesTv); size_t indirectExportEntriesLen = indirectExportEntries->GetLength(); JSMutableHandle ee(thread, globalConstants->GetUndefined()); for (size_t idx = 0; idx < indirectExportEntriesLen; idx++) { ee.Update(indirectExportEntries->Get(idx)); if (JSTaggedValue::SameValue(ee->GetLocalName(), localName.GetTaggedValue())) { return ee->GetExportName(); } } } return globalConstants->GetUndefined(); } } // namespace panda::ecmascript