• 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/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