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