/* * Copyright (c) 2024 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/global_env.h" #include "ecmascript/module/js_shared_module.h" #include "ecmascript/module/module_data_extractor.h" #include "ecmascript/shared_objects/js_shared_array.h" namespace panda::ecmascript { JSHandle SendableClassModule::GenerateSendableFuncModule(JSThread *thread, const JSHandle &module) { // esm -> SourceTextModule; cjs or script -> string of recordName if (!module->IsSourceTextModule()) { ASSERT(module->IsString()); return module; } JSHandle currentModule = JSHandle::Cast(module); // Only clone module in isolate-heap. if (SourceTextModule::IsModuleInSharedHeap(currentModule)) { return module; } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle sModule = factory->NewSSourceTextModule(); JSHandle currentEnvironment(thread, currentModule->GetEnvironment()); JSHandle sendableEnvironment = SendableClassModule::CloneModuleEnvironment(thread, currentEnvironment); sModule->SetSharedType(SharedTypes::SENDABLE_FUNCTION_MODULE); sModule->SetEnvironment(thread, sendableEnvironment); sModule->SetEcmaModuleFilenameString(currentModule->GetEcmaModuleFilenameString()); sModule->SetEcmaModuleRecordNameString(currentModule->GetEcmaModuleRecordNameString()); sModule->SetSendableEnv(thread, JSTaggedValue::Undefined()); return JSHandle(sModule); } JSHandle SendableClassModule::CloneRecordIndexBinding(JSThread *thread, JSTaggedValue indexBinding) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle binding(thread, indexBinding); JSHandle resolvedModule(thread, binding->GetModule()); if (SourceTextModule::IsSharedModule((resolvedModule))) { return JSHandle::Cast( factory->NewSResolvedIndexBindingRecord(resolvedModule, binding->GetIndex())); } CString moduleName = SourceTextModule::GetModuleName(resolvedModule.GetTaggedValue()); JSHandle record = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(moduleName); JSHandle fileName = factory->NewFromUtf8(resolvedModule->GetEcmaModuleFilenameString()); int32_t index = binding->GetIndex(); return JSHandle::Cast(factory->NewSResolvedRecordIndexBindingRecord(record, fileName, index)); } JSHandle SendableClassModule::CloneRecordNameBinding(JSThread *thread, JSTaggedValue binding) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); ResolvedBinding *resolvedBinding = ResolvedBinding::Cast(binding.GetTaggedObject()); JSHandle resolvedModule(thread, resolvedBinding->GetModule()); if (SourceTextModule::IsSharedModule((resolvedModule))) { JSHandle bindingName(thread, resolvedBinding->GetBindingName()); return JSHandle::Cast( factory->NewSResolvedBindingRecord(resolvedModule, bindingName)); } CString moduleName = SourceTextModule::GetModuleName(resolvedModule.GetTaggedValue()); JSHandle record = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(moduleName); JSHandle bindingName(thread, resolvedBinding->GetBindingName()); return JSHandle::Cast(factory->NewSResolvedRecordBindingRecord(record, bindingName)); } JSHandle SendableClassModule::CloneModuleEnvironment(JSThread *thread, const JSHandle &moduleEnvironment) { if (moduleEnvironment->IsUndefined()) { return JSHandle(moduleEnvironment); } JSHandle currentEnvironment(moduleEnvironment); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); int enumKeys = static_cast(currentEnvironment->GetLength()); JSHandle sendableEnvironment = factory->NewSDictionaryArray(enumKeys); for (int idx = 0; idx < enumKeys; idx++) { JSTaggedValue key = currentEnvironment->Get(idx); if (key.IsRecord()) { JSType type = key.GetTaggedObject()->GetClass()->GetObjectType(); switch (type) { case JSType::RESOLVEDBINDING_RECORD: { JSHandle recordBinding = SendableClassModule::CloneRecordNameBinding(thread, key); sendableEnvironment->Set(thread, idx, recordBinding); break; } case JSType::RESOLVEDINDEXBINDING_RECORD: { JSHandle recordBinding = SendableClassModule::CloneRecordIndexBinding(thread, key); sendableEnvironment->Set(thread, idx, recordBinding); break; } case JSType::RESOLVEDRECORDINDEXBINDING_RECORD: break; case JSType::RESOLVEDRECORDBINDING_RECORD: break; default: LOG_FULL(FATAL) << "UNREACHABLE"; } } continue; } return sendableEnvironment; } JSHandle JSSharedModule::CloneEnvForSModule(JSThread *thread, const JSHandle &module, JSHandle &envRec) { if (!SourceTextModule::IsSharedModule(module)) { return envRec; } return SendableClassModule::CloneModuleEnvironment(thread, JSHandle(envRec)); } JSHandle SharedModuleHelper::ParseSharedModule(JSThread *thread, const JSPandaFile *jsPandaFile, const CString &descriptor, const CString &moduleFilename, JSRecordInfo *recordInfo) { int moduleIdx = jsPandaFile->GetModuleRecordIdx(descriptor); ASSERT(jsPandaFile->IsNewVersion() && (moduleIdx != -1)); // new pandafile version use new literal offset mechanism panda_file::File::EntityId moduleId = panda_file::File::EntityId(static_cast(moduleIdx)); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle moduleRecord = factory->NewSSourceTextModule(); moduleRecord->SetSharedType(SharedTypes::SHARED_MODULE); ModuleDataExtractor::ExtractModuleDatas(thread, jsPandaFile, moduleId, moduleRecord, recordInfo); bool hasTLA = jsPandaFile->GetHasTopLevelAwait(descriptor); moduleRecord->SetHasTLA(hasTLA); moduleRecord->SetEcmaModuleFilenameString(moduleFilename); moduleRecord->SetStatus(ModuleStatus::UNINSTANTIATED); moduleRecord->SetTypes(ModuleTypes::ECMA_MODULE); moduleRecord->SetIsNewBcVersion(true); return JSHandle::Cast(moduleRecord); } JSHandle JSSharedModule::GenerateSharedExports(JSThread *thread, const JSHandle &oldExports) { uint32_t newLength = oldExports->GetLength(); if (newLength == 0) { LOG_FULL(INFO) << "Empty exports in moduleNamespace"; } JSHandle newArray = thread->GetEcmaVM()->GetFactory()->NewSTaggedArray(newLength); for (uint32_t i = 0; i < newLength; i++) { JSTaggedValue value = oldExports->Get(i); ASSERT(value.IsString()); newArray->Set(thread, i, value); } return newArray; } JSHandle JSSharedModule::CreateSharedSortedExports(JSThread *thread, const JSHandle &oldExports) { auto globalConst = thread->GlobalConstants(); JSHandle exports = GenerateSharedExports(thread, oldExports); // 7. Let sortedExports be a new List containing the same values as the list exports where the values // are ordered as if an Array of the same values had been sorted using // Array.prototype.sort using undefined as comparefn. JSHandle exportsArray = JSSharedArray::CreateArrayFromList(thread, exports); JSHandle sortedExports = JSHandle::Cast(exportsArray); JSHandle fn = globalConst->GetHandledUndefined(); JSSharedArray::Sort(thread, sortedExports, fn); return sortedExports; } JSHandle JSSharedModule::SModuleNamespaceCreate(JSThread *thread, const JSHandle &module, const JSHandle &exports) { auto globalConst = thread->GlobalConstants(); // 2. Assert: module.[[Namespace]] is undefined. // 3. Assert: exports is a List of String values. // 4. Let M be a newly created object. // 5. Set M's essential internal methods to the definitions specified in 9.4.6. ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle mNp = factory->NewSModuleNamespace(); // 6. Set M.[[Module]] to module. mNp->SetModule(thread, module); JSHandle sortedExports = JSSharedModule::CreateSharedSortedExports(thread, exports); // 8. Set M.[[Exports]] to sortedExports. mNp->SetExports(thread, sortedExports); // 9. Create own properties of M corresponding to the definitions in 26.3. JSHandle toStringTag = thread->GetEcmaVM()->GetGlobalEnv()->GetToStringTagSymbol(); JSHandle moduleString = globalConst->GetHandledModuleString(); PropertyDescriptor des(thread, moduleString, false, false, false); JSHandle mNpObj = JSHandle::Cast(mNp); JSObject::DefineOwnProperty(thread, mNpObj, toStringTag, des); JSHandle moduleRecord = JSHandle::Cast(module); SourceTextModule::Cast(moduleRecord.GetTaggedValue().GetTaggedObject())->SetNamespace(thread, mNp.GetTaggedValue()); // 10. Set module.[[Namespace]] to M. mNp->GetJSHClass()->SetExtensible(false); return mNp; } } // namespace panda::ecmascript