1/* 2 * Copyright (c) 2022 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 16#include "ecmascript/require/js_cjs_module.h" 17 18#include "ecmascript/interpreter/interpreter-inl.h" 19#include "ecmascript/jspandafile/js_pandafile_executor.h" 20#include "ecmascript/module/module_data_extractor.h" 21#include "ecmascript/module/module_path_helper.h" 22#include "ecmascript/require/js_require_manager.h" 23 24namespace panda::ecmascript { 25 26void CjsModule::InitializeModule(JSThread *thread, JSHandle<CjsModule> &module, 27 JSHandle<JSTaggedValue> &filename, JSHandle<JSTaggedValue> &dirname) 28{ 29 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 30 31 JSHandle<JSTaggedValue> dirKey(factory->NewFromASCII("path")); 32 SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), dirKey.GetTaggedValue(), 33 dirname.GetTaggedValue()); 34 JSHandle<JSTaggedValue> filenameKey(factory->NewFromASCII("filename")); 35 SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), filenameKey.GetTaggedValue(), 36 filename.GetTaggedValue()); 37 module->SetFilename(thread, filename.GetTaggedValue()); 38 module->SetPath(thread, dirname.GetTaggedValue()); 39 return; 40} 41 42JSHandle<JSTaggedValue> CjsModule::SearchFromModuleCache(JSThread *thread, JSHandle<JSTaggedValue> &filename) 43{ 44 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 45 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 46 47 JSHandle<JSTaggedValue> moduleObj(env->GetCjsModuleFunction()); 48 JSHandle<JSTaggedValue> cacheName = globalConst->GetHandledCjsCacheString(); 49 JSTaggedValue modCache = SlowRuntimeStub::LdObjByName(thread, moduleObj.GetTaggedValue(), 50 cacheName.GetTaggedValue(), 51 false, 52 JSTaggedValue::Undefined()); 53 JSHandle<CjsModuleCache> moduleCache = JSHandle<CjsModuleCache>(thread, modCache); 54 if (moduleCache->ContainsModule(filename.GetTaggedValue())) { 55 JSHandle<CjsModule> cachedModule = JSHandle<CjsModule>(thread, 56 moduleCache->GetModule(filename.GetTaggedValue())); 57 JSHandle<JSTaggedValue> exportsName = globalConst->GetHandledCjsExportsString(); 58 JSTaggedValue cachedExports = SlowRuntimeStub::LdObjByName(thread, cachedModule.GetTaggedValue(), 59 exportsName.GetTaggedValue(), 60 false, 61 JSTaggedValue::Undefined()); 62 63 return JSHandle<JSTaggedValue>(thread, cachedExports); 64 } 65 return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Hole()); 66} 67 68void CjsModule::PutIntoCache(JSThread *thread, JSHandle<CjsModule> &module, JSHandle<JSTaggedValue> &filename) 69{ 70 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 71 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 72 73 JSHandle<JSTaggedValue> moduleObj(env->GetCjsModuleFunction()); 74 JSHandle<JSTaggedValue> cacheName = globalConst->GetHandledCjsCacheString(); 75 JSTaggedValue modCache = SlowRuntimeStub::LdObjByName(thread, moduleObj.GetTaggedValue(), 76 cacheName.GetTaggedValue(), 77 false, 78 JSTaggedValue::Undefined()); 79 JSHandle<CjsModuleCache> moduleCache = JSHandle<CjsModuleCache>(thread, modCache); 80 JSHandle<JSTaggedValue> moduleHandle = JSHandle<JSTaggedValue>::Cast(module); 81 JSHandle<CjsModuleCache> newCache = CjsModuleCache::PutIfAbsentAndReset(thread, moduleCache, filename, 82 moduleHandle); 83 SlowRuntimeStub::StObjByName(thread, moduleObj.GetTaggedValue(), cacheName.GetTaggedValue(), 84 newCache.GetTaggedValue()); 85} 86 87JSHandle<JSTaggedValue> CjsModule::Load(JSThread *thread, JSHandle<EcmaString> &request) 88{ 89 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 90 // Get local jsPandaFile's dirPath 91 const JSPandaFile *jsPandaFile = EcmaInterpreter::GetNativeCallPandafile(thread); 92 if (jsPandaFile == nullptr) { 93 LOG_ECMA(FATAL) << "CJS REQUIRE FAIL : jsPandaFile is null"; 94 UNREACHABLE(); 95 } 96 CString filename = jsPandaFile->GetJSPandaFileDesc(); 97 CString requestEntryPoint = JSPandaFile::ENTRY_MAIN_FUNCTION; 98 CString requestStr = ModulePathHelper::Utf8ConvertToString(request.GetTaggedValue()); 99 CString parent; 100 CString dirname; 101 CString recordName; 102 if (jsPandaFile->IsBundlePack()) { 103 ModulePathHelper::ResolveCurrentPath(parent, dirname, jsPandaFile); 104 recordName = ResolveFilenameFromNative(thread, dirname, requestStr); 105 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 106 filename = recordName; 107 } else { 108 CString currentEntryPoint = EcmaInterpreter::GetCurrentEntryPoint(thread).first; 109 requestEntryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, jsPandaFile, filename, 110 currentEntryPoint, requestStr); 111 recordName = requestEntryPoint; 112 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); 113 } 114 115 // Search from Module.cache 116 JSHandle<JSTaggedValue> recordNameHdl = JSHandle<JSTaggedValue>::Cast(factory->NewFromUtf8(recordName)); 117 JSHandle<JSTaggedValue> maybeCachedExports = SearchFromModuleCache(thread, recordNameHdl); 118 if (!maybeCachedExports->IsHole()) { 119 return maybeCachedExports; 120 } 121 122 // Don't get required exports from cache, execute required JSPandaFile. 123 // module = new Module(), which belongs to required JSPandaFile. 124 JSHandle<CjsModule> module = factory->NewCjsModule(); 125 dirname = PathHelper::ResolveDirPath(recordName); 126 JSHandle<JSTaggedValue> dirnameHdl = JSHandle<JSTaggedValue>::Cast(factory->NewFromUtf8(dirname)); 127 InitializeModule(thread, module, recordNameHdl, dirnameHdl); 128 PutIntoCache(thread, module, recordNameHdl); 129 130 JSRecordInfo *recordInfo = nullptr; 131 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(requestEntryPoint, &recordInfo); 132 if (!hasRecord) { 133 JSHandle<JSTaggedValue> exp(thread, JSTaggedValue::Exception()); 134 THROW_MODULE_NOT_FOUND_ERROR_WITH_RETURN_VALUE(thread, requestStr, recordName, exp); 135 } 136 if (jsPandaFile->IsJson(recordInfo)) { 137 JSHandle<JSTaggedValue> result = JSHandle<JSTaggedValue>(thread, 138 ModuleDataExtractor::JsonParse(thread, jsPandaFile, requestEntryPoint)); 139 // Set module.exports ---> exports 140 JSHandle<JSTaggedValue> exportsKey = thread->GlobalConstants()->GetHandledCjsExportsString(); 141 SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), exportsKey.GetTaggedValue(), 142 result.GetTaggedValue()); 143 return result; 144 } 145 // Execute required JSPandaFile 146 RequireExecution(thread, filename, requestEntryPoint); 147 if (thread->HasPendingException()) { 148 thread->GetCurrentEcmaContext()->HandleUncaughtException(); 149 return thread->GlobalConstants()->GetHandledUndefined(); 150 } 151 // Search from Module.cache after execution. 152 JSHandle<JSTaggedValue> cachedExports = SearchFromModuleCache(thread, recordNameHdl); 153 if (cachedExports->IsHole()) { 154 LOG_ECMA(FATAL) << "CJS REQUIRE FAIL : Can not obtain module, after executing required jsPandaFile"; 155 UNREACHABLE(); 156 } 157 return cachedExports; 158} 159 160void CjsModule::RequireExecution(JSThread *thread, const CString &mergedFilename, const CString &requestEntryPoint) 161{ 162 std::shared_ptr<JSPandaFile> jsPandaFile = 163 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, mergedFilename, requestEntryPoint); 164 if (jsPandaFile == nullptr) { 165 LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << mergedFilename; 166 } 167 JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), requestEntryPoint); 168} 169 170JSTaggedValue CjsModule::Require(JSThread *thread, JSHandle<EcmaString> &request, 171 [[maybe_unused]] JSHandle<CjsModule> &parent, 172 [[maybe_unused]] bool isMain) 173{ 174 Load(thread, request); 175 return JSTaggedValue::Undefined(); 176} 177} // namespace panda::ecmascript