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