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