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/aot_file_manager.h"
19 #include "ecmascript/base/path_helper.h"
20 #include "ecmascript/builtins/builtins_json.h"
21 #include "ecmascript/interpreter/interpreter-inl.h"
22 #include "ecmascript/interpreter/slow_runtime_stub.h"
23 #include "ecmascript/platform/file.h"
24 #include "ecmascript/require/js_cjs_module_cache.h"
25 #include "ecmascript/require/js_require_manager.h"
26 #include "ecmascript/jspandafile/js_pandafile.h"
27 #include "ecmascript/jspandafile/js_pandafile_executor.h"
28 #include "ecmascript/jspandafile/js_pandafile_manager.h"
29
30 namespace panda::ecmascript {
31 using BuiltinsJson = builtins::BuiltinsJson;
32 using PathHelper = base::PathHelper;
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 PathHelper::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 = PathHelper::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 dirname.Update(PathHelper::ResolveDirPath(thread, filename));
131 InitializeModule(thread, module, filename, dirname);
132 PutIntoCache(thread, module, filename);
133
134 if (jsPandaFile->IsJson(thread, requestEntryPoint)) {
135 JSHandle<JSTaggedValue> result = JSHandle<JSTaggedValue>(thread,
136 ModuleManager::JsonParse(thread, jsPandaFile, requestEntryPoint));
137 // Set module.exports ---> exports
138 JSHandle<JSTaggedValue> exportsKey = thread->GlobalConstants()->GetHandledCjsExportsString();
139 SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), exportsKey.GetTaggedValue(),
140 result.GetTaggedValue());
141 return result;
142 }
143 // Execute required JSPandaFile
144 RequireExecution(thread, mergedFilename, requestEntryPoint);
145 if (thread->HasPendingException()) {
146 thread->GetEcmaVM()->HandleUncaughtException(thread->GetException().GetTaggedObject());
147 return thread->GlobalConstants()->GetHandledUndefined();
148 }
149 // Search from Module.cache after execution.
150 JSHandle<JSTaggedValue> cachedExports = SearchFromModuleCache(thread, filename);
151 if (cachedExports->IsHole()) {
152 LOG_ECMA(ERROR) << "CJS REQUIRE FAIL : Can not obtain module, after executing required jsPandaFile";
153 UNREACHABLE();
154 }
155 return cachedExports;
156 }
157
RequireExecution(JSThread * thread,CString mergedFilename,CString requestEntryPoint)158 void CjsModule::RequireExecution(JSThread *thread, CString mergedFilename, CString requestEntryPoint)
159 {
160 const JSPandaFile *jsPandaFile =
161 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, mergedFilename, requestEntryPoint);
162 if (jsPandaFile == nullptr) {
163 CString msg = "Load file with filename '" + mergedFilename + "' failed, recordName '" + requestEntryPoint + "'";
164 THROW_ERROR(thread, ErrorType::REFERENCE_ERROR, msg.c_str());
165 }
166 JSPandaFileExecutor::Execute(thread, jsPandaFile, requestEntryPoint);
167 }
168
Require(JSThread * thread,JSHandle<EcmaString> & request,JSHandle<CjsModule> & parent,bool isMain)169 JSTaggedValue CjsModule::Require(JSThread *thread, JSHandle<EcmaString> &request,
170 [[maybe_unused]]JSHandle<CjsModule> &parent,
171 [[maybe_unused]]bool isMain)
172 {
173 Load(thread, request);
174 return JSTaggedValue::Undefined();
175 }
176 } // namespace panda::ecmascript