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