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/jspandafile/js_pandafile_executor.h"
17
18 #include "ecmascript/base/path_helper.h"
19 #include "ecmascript/compiler/aot_file/an_file_data_manager.h"
20 #include "ecmascript/compiler/aot_file/aot_file_manager.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/jspandafile/js_pandafile_manager.h"
23 #include "ecmascript/jspandafile/program_object.h"
24 #include "ecmascript/mem/c_string.h"
25 #include "ecmascript/mem/c_containers.h"
26 #include "ecmascript/module/js_module_manager.h"
27 #include "ecmascript/module/module_path_helper.h"
28 #include "ecmascript/patch/quick_fix_manager.h"
29
30 namespace panda::ecmascript {
31 using PathHelper = base::PathHelper;
ExecuteFromFile(JSThread * thread,const CString & filename,std::string_view entryPoint,bool needUpdate,bool excuteFromJob)32 Expected<JSTaggedValue, bool> JSPandaFileExecutor::ExecuteFromFile(JSThread *thread, const CString &filename,
33 std::string_view entryPoint, bool needUpdate, bool excuteFromJob)
34 {
35 LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteFromFile filename " << filename;
36 CString entry;
37 CString name;
38 EcmaVM *vm = thread->GetEcmaVM();
39 if (!vm->IsBundlePack() && !excuteFromJob) {
40 #if defined(PANDA_TARGET_LINUX) || defined(OHOS_UNIT_TEST)
41 name = filename;
42 entry = entryPoint.data();
43 #else
44 CString normalName = PathHelper::NormalizePath(filename);
45 ModulePathHelper::ParseOhmUrl(vm, normalName, name, entry);
46 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS)
47 if (name.empty()) {
48 name = vm->GetAssetPath();
49 }
50 #elif defined(PANDA_TARGET_WINDOWS)
51 CString assetPath = vm->GetAssetPath();
52 name = assetPath + "\\" + JSPandaFile::MERGE_ABC_NAME;
53 #else
54 CString assetPath = vm->GetAssetPath();
55 name = assetPath + "/" + JSPandaFile::MERGE_ABC_NAME;
56 #endif
57 #endif
58 } else {
59 name = filename;
60 entry = entryPoint.data();
61 }
62
63 std::shared_ptr<JSPandaFile> jsPandaFile =
64 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, name, entry, needUpdate);
65 if (jsPandaFile == nullptr) {
66 CString msg = "Load file with filename '" + name + "' failed, recordName '" + entry + "'";
67 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
68 }
69 LoadAOTFilesForFile(vm, jsPandaFile.get());
70
71 // realEntry is used to record the original record, which is easy to throw when there are exceptions
72 const CString realEntry = entry;
73 // If it is an old record, delete the bundleName and moduleName
74 if (!jsPandaFile->IsBundlePack() && !excuteFromJob && !vm->GetBundleName().empty()) {
75 jsPandaFile->CheckIsRecordWithBundleName(entry);
76 if (!jsPandaFile->IsRecordWithBundleName()) {
77 PathHelper::AdaptOldIsaRecord(entry);
78 }
79 }
80
81 JSRecordInfo recordInfo;
82 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
83 if (!hasRecord) {
84 LOG_FULL(ERROR) << "cannot find record '" << realEntry <<"' in baseFileName " << name << ".";
85 CString msg = "cannot find record '" + realEntry + "', please check the request path.";
86 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
87 }
88 if (jsPandaFile->IsModule(recordInfo)) {
89 [[maybe_unused]] EcmaHandleScope scope(thread);
90 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
91 JSHandle<JSTaggedValue> moduleRecord(thread->GlobalConstants()->GetHandledUndefined());
92 if (jsPandaFile->IsBundlePack()) {
93 moduleRecord = moduleManager->HostResolveImportedModule(name, excuteFromJob);
94 } else {
95 moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(name, entry, excuteFromJob);
96 }
97 SourceTextModule::Instantiate(thread, moduleRecord, excuteFromJob);
98 if (thread->HasPendingException()) {
99 if (!excuteFromJob) {
100 thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException());
101 }
102 return Unexpected(false);
103 }
104 JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
105 module->SetStatus(ModuleStatus::INSTANTIATED);
106 SourceTextModule::Evaluate(thread, module, nullptr, 0, excuteFromJob);
107 return JSTaggedValue::Undefined();
108 }
109 return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entry.c_str(), excuteFromJob);
110 }
111
112 // The security interface needs to be modified accordingly.
ExecuteFromBuffer(JSThread * thread,const void * buffer,size_t size,std::string_view entryPoint,const CString & filename,bool needUpdate)113 Expected<JSTaggedValue, bool> JSPandaFileExecutor::ExecuteFromBuffer(JSThread *thread,
114 const void *buffer, size_t size, std::string_view entryPoint, const CString &filename, bool needUpdate)
115 {
116 LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteFromBuffer filename " << filename;
117 CString normalName = PathHelper::NormalizePath(filename);
118 std::shared_ptr<JSPandaFile> jsPandaFile =
119 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, normalName, entryPoint, buffer, size, needUpdate);
120 if (jsPandaFile == nullptr) {
121 CString msg = "Load file with filename '" + normalName + "' failed, recordName '" + entryPoint.data() + "'";
122 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
123 }
124 auto vm = thread->GetEcmaVM();
125 LoadAOTFilesForFile(vm, jsPandaFile.get());
126
127 CString entry = entryPoint.data();
128 JSRecordInfo recordInfo;
129 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
130 if (!hasRecord) {
131 LOG_FULL(ERROR) << "cannot find record '" << entry <<"' in baseFileName " << normalName << ".";
132 CString msg = "cannot find record '" + entry + "', please check the request path.";
133 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
134 }
135 if (jsPandaFile->IsModule(recordInfo)) {
136 bool isBundle = jsPandaFile->IsBundlePack();
137 return CommonExecuteBuffer(thread, isBundle, normalName, entry, buffer, size);
138 }
139 return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entry);
140 }
141
142 // The security interface needs to be modified accordingly.
ExecuteModuleBuffer(JSThread * thread,const void * buffer,size_t size,const CString & filename,bool needUpdate)143 Expected<JSTaggedValue, bool> JSPandaFileExecutor::ExecuteModuleBuffer(
144 JSThread *thread, const void *buffer, size_t size, const CString &filename, bool needUpdate)
145 {
146 LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteModuleBuffer filename " << filename;
147 CString name;
148 EcmaVM *vm = thread->GetEcmaVM();
149 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS)
150 name = vm->GetAssetPath();
151 #elif defined(PANDA_TARGET_WINDOWS)
152 CString assetPath = vm->GetAssetPath();
153 name = assetPath + "\\" + JSPandaFile::MERGE_ABC_NAME;
154 #else
155 CString assetPath = vm->GetAssetPath();
156 name = assetPath + "/" + JSPandaFile::MERGE_ABC_NAME;
157 #endif
158 CString normalName = PathHelper::NormalizePath(filename);
159 CString entry;
160 ModulePathHelper::ParseOhmUrl(vm, normalName, name, entry);
161 std::shared_ptr<JSPandaFile> jsPandaFile =
162 JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, name, entry, buffer, size, needUpdate);
163 if (jsPandaFile == nullptr) {
164 CString msg = "Load file with filename '" + name + "' failed, recordName '" + entry + "'";
165 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
166 }
167 LoadAOTFilesForFile(vm, jsPandaFile.get());
168
169 bool isBundle = jsPandaFile->IsBundlePack();
170
171 // realEntry is used to record the original record, which is easy to throw when there are exceptions
172 const CString realEntry = entry;
173 if (!isBundle) {
174 jsPandaFile->CheckIsRecordWithBundleName(entry);
175 if (!jsPandaFile->IsRecordWithBundleName()) {
176 PathHelper::AdaptOldIsaRecord(entry);
177 }
178 }
179 JSRecordInfo recordInfo;
180 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
181 if (!hasRecord) {
182 LOG_FULL(ERROR) << "cannot find record '" << realEntry <<"' in baseFileName " << name << ".";
183 CString msg = "cannot find record '" + realEntry + "', please check the request path.";
184 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
185 }
186 if (!jsPandaFile->IsModule(recordInfo)) {
187 LOG_ECMA(FATAL) << "Input file is not esmodule";
188 }
189 return CommonExecuteBuffer(thread, isBundle, name, entry, buffer, size);
190 }
191
192 // The security interface needs to be modified accordingly.
CommonExecuteBuffer(JSThread * thread,bool isBundle,const CString & filename,const CString & entry,const void * buffer,size_t size)193 Expected<JSTaggedValue, bool> JSPandaFileExecutor::CommonExecuteBuffer(JSThread *thread,
194 bool isBundle, const CString &filename, const CString &entry, const void *buffer, size_t size)
195 {
196 [[maybe_unused]] EcmaHandleScope scope(thread);
197 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
198 moduleManager->SetExecuteMode(true);
199 JSMutableHandle<JSTaggedValue> moduleRecord(thread, thread->GlobalConstants()->GetUndefined());
200 if (isBundle) {
201 moduleRecord.Update(moduleManager->HostResolveImportedModule(buffer, size, filename));
202 } else {
203 moduleRecord.Update(moduleManager->HostResolveImportedModuleWithMerge(filename, entry));
204 }
205
206 SourceTextModule::Instantiate(thread, moduleRecord);
207 if (thread->HasPendingException()) {
208 thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException());
209 return Unexpected(false);
210 }
211
212 JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
213 module->SetStatus(ModuleStatus::INSTANTIATED);
214 SourceTextModule::Evaluate(thread, module, buffer, size);
215 return JSTaggedValue::Undefined();
216 }
217
Execute(JSThread * thread,const JSPandaFile * jsPandaFile,std::string_view entryPoint,bool excuteFromJob)218 Expected<JSTaggedValue, bool> JSPandaFileExecutor::Execute(JSThread *thread, const JSPandaFile *jsPandaFile,
219 std::string_view entryPoint, bool excuteFromJob)
220 {
221 // For Ark application startup
222 EcmaContext *context = thread->GetCurrentEcmaContext();
223
224 QuickFixManager *quickFixManager = thread->GetEcmaVM()->GetQuickFixManager();
225 quickFixManager->LoadPatchIfNeeded(thread, jsPandaFile);
226
227 Expected<JSTaggedValue, bool> result = context->InvokeEcmaEntrypoint(jsPandaFile, entryPoint, excuteFromJob);
228 return result;
229 }
230
LoadAOTFilesForFile(EcmaVM * vm,JSPandaFile * jsPandaFile)231 void JSPandaFileExecutor::LoadAOTFilesForFile(EcmaVM *vm, JSPandaFile *jsPandaFile)
232 {
233 if (vm->GetJSOptions().GetEnableAsmInterpreter()) {
234 auto aotFM = vm->GetJSThread()->GetCurrentEcmaContext()->GetAOTFileManager();
235 if (vm->GetJSOptions().WasAOTOutputFileSet()) {
236 AnFileDataManager::GetInstance()->SetEnable(true);
237 std::string aotFilename = vm->GetJSOptions().GetAOTOutputFile();
238 vm->GetJSThread()->GetCurrentEcmaContext()->LoadAOTFiles(aotFilename);
239 }
240 if (aotFM->IsLoad(jsPandaFile)) {
241 uint32_t index = aotFM->GetAnFileIndex(jsPandaFile);
242 jsPandaFile->SetAOTFileInfoIndex(index);
243 }
244 }
245 }
246
ExecuteFromBufferSecure(JSThread * thread,uint8_t * buffer,size_t size,std::string_view entryPoint,const CString & filename,bool needUpdate)247 Expected<JSTaggedValue, bool> JSPandaFileExecutor::ExecuteFromBufferSecure(JSThread *thread, uint8_t *buffer,
248 size_t size, std::string_view entryPoint, const CString &filename, bool needUpdate)
249 {
250 LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteFromBufferSecure with secure buffer filename " << filename;
251 CString normalName = PathHelper::NormalizePath(filename);
252 std::shared_ptr<JSPandaFile> jsPandaFile = JSPandaFileManager::GetInstance()->
253 LoadJSPandaFileSecure(thread, normalName, entryPoint, buffer, size, needUpdate);
254 if (jsPandaFile == nullptr) {
255 CString msg = "Load file with filename '" + normalName + "' failed, recordName '" + entryPoint.data() + "'";
256 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
257 }
258 auto vm = thread->GetEcmaVM();
259 LoadAOTFilesForFile(vm, jsPandaFile.get());
260
261 CString entry = entryPoint.data();
262 JSRecordInfo recordInfo;
263 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
264 if (!hasRecord) {
265 LOG_FULL(ERROR) << "cannot find record '" << entry <<"' in baseFileName " << normalName << ".";
266 CString msg = "cannot find record '" + entry + "', please check the request path.";
267 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
268 }
269 if (jsPandaFile->IsModule(recordInfo)) {
270 return CommonExecuteBuffer(thread, normalName, entry, jsPandaFile.get());
271 }
272 return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entry);
273 }
274
CommonExecuteBuffer(JSThread * thread,const CString & filename,const CString & entry,const JSPandaFile * jsPandaFile)275 Expected<JSTaggedValue, bool> JSPandaFileExecutor::CommonExecuteBuffer(JSThread *thread, const CString &filename,
276 const CString &entry, const JSPandaFile *jsPandaFile)
277 {
278 [[maybe_unused]] EcmaHandleScope scope(thread);
279 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
280 moduleManager->SetExecuteMode(true);
281 JSMutableHandle<JSTaggedValue> moduleRecord(thread, thread->GlobalConstants()->GetUndefined());
282 if (jsPandaFile->IsBundlePack()) {
283 moduleRecord.Update(moduleManager->HostResolveImportedModule(jsPandaFile, filename));
284 } else {
285 moduleRecord.Update(moduleManager->HostResolveImportedModuleWithMerge(filename, entry));
286 }
287
288 SourceTextModule::Instantiate(thread, moduleRecord);
289 if (thread->HasPendingException()) {
290 thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException());
291 return Unexpected(false);
292 }
293
294 JSHandle<SourceTextModule> module = JSHandle<SourceTextModule>::Cast(moduleRecord);
295 module->SetStatus(ModuleStatus::INSTANTIATED);
296 SourceTextModule::Evaluate(thread, module, nullptr, 0);
297 return JSTaggedValue::Undefined();
298 }
299
ExecuteModuleBufferSecure(JSThread * thread,uint8_t * buffer,size_t size,const CString & filename,bool needUpdate)300 Expected<JSTaggedValue, bool> JSPandaFileExecutor::ExecuteModuleBufferSecure(JSThread *thread, uint8_t *buffer,
301 size_t size, const CString &filename, bool needUpdate)
302 {
303 LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteModuleBufferSecure with secure buffer filename " << filename;
304 CString name;
305 EcmaVM *vm = thread->GetEcmaVM();
306 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS)
307 name = vm->GetAssetPath();
308 #elif defined(PANDA_TARGET_WINDOWS)
309 CString assetPath = vm->GetAssetPath();
310 name = assetPath + "\\" + JSPandaFile::MERGE_ABC_NAME;
311 #else
312 CString assetPath = vm->GetAssetPath();
313 name = assetPath + "/" + JSPandaFile::MERGE_ABC_NAME;
314 #endif
315 CString normalName = PathHelper::NormalizePath(filename);
316 CString entry;
317 ModulePathHelper::ParseOhmUrl(vm, normalName, name, entry);
318 std::shared_ptr<JSPandaFile> jsPandaFile = JSPandaFileManager::GetInstance()->
319 LoadJSPandaFileSecure(thread, name, entry, buffer, size, needUpdate);
320 if (jsPandaFile == nullptr) {
321 CString msg = "Load file with filename '" + name + "' failed, recordName '" + entry + "'";
322 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
323 }
324 LoadAOTFilesForFile(vm, jsPandaFile.get());
325
326 // realEntry is used to record the original record, which is easy to throw when there are exceptions
327 const CString realEntry = entry;
328 if (!jsPandaFile->IsBundlePack()) {
329 jsPandaFile->CheckIsRecordWithBundleName(entry);
330 if (!jsPandaFile->IsRecordWithBundleName()) {
331 PathHelper::AdaptOldIsaRecord(entry);
332 }
333 }
334
335 // will be refactored, temporarily use the function IsModule to verify realEntry
336 JSRecordInfo recordInfo;
337 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
338 if (!hasRecord) {
339 LOG_FULL(ERROR) << "cannot find record '" << realEntry <<"' in baseFileName " << name << ".";
340 CString msg = "cannot find record '" + realEntry + "', please check the request path.";
341 THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false));
342 }
343 if (!jsPandaFile->IsModule(recordInfo)) {
344 LOG_ECMA(FATAL) << "Input file is not esmodule";
345 }
346 return CommonExecuteBuffer(thread, name, entry, jsPandaFile.get());
347 }
348 } // namespace panda::ecmascript
349