• 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.h"
17 
18 #include "ecmascript/jspandafile/js_pandafile_manager.h"
19 #include "ecmascript/jspandafile/program_object.h"
20 #include "libpandafile/class_data_accessor-inl.h"
21 
22 namespace panda::ecmascript {
23 namespace {
24 const CString OHOS_PKG_ABC_PATH_ROOT = "/ets/";  // abc file always under /ets/ dir in HAP/HSP
25 }  // namespace
26 bool JSPandaFile::loadedFirstPandaFile = false;
JSPandaFile(const panda_file::File * pf,const CString & descriptor)27 JSPandaFile::JSPandaFile(const panda_file::File *pf, const CString &descriptor)
28     : pf_(pf), desc_(descriptor)
29 {
30     ASSERT(pf_ != nullptr);
31     CheckIsBundlePack();
32     if (isBundlePack_) {
33         InitializeUnMergedPF();
34     } else {
35         InitializeMergedPF();
36     }
37     checksum_ = pf->GetHeader()->checksum;
38     isNewVersion_ = pf_->GetHeader()->version > OLD_VERSION;
39     if (!loadedFirstPandaFile && !isBundlePack_) {
40         // Tag the first merged abc to use constant string. The lifetime of this first panda file is the same
41         // as the vm. And make sure the first pandafile is the same at the compile time and runtime.
42         isFirstPandafile_ = false;
43         loadedFirstPandaFile = true;
44     }
45 }
46 
CheckIsBundlePack()47 void JSPandaFile::CheckIsBundlePack()
48 {
49     Span<const uint32_t> classIndexes = pf_->GetClasses();
50     for (const uint32_t index : classIndexes) {
51         panda_file::File::EntityId classId(index);
52         if (pf_->IsExternal(classId)) {
53             continue;
54         }
55         panda_file::ClassDataAccessor cda(*pf_, classId);
56         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
57             panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
58             panda_file::File::StringData sd = GetStringData(fieldNameId);
59             const char *fieldName = utf::Mutf8AsCString(sd.data);
60             if (std::strcmp(IS_COMMON_JS, fieldName) == 0 || std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) {
61                 isBundlePack_ = false;
62             }
63         });
64         if (!isBundlePack_) {
65             return;
66         }
67     }
68 }
69 
CheckIsRecordWithBundleName(const CString & entry)70 void JSPandaFile::CheckIsRecordWithBundleName(const CString &entry)
71 {
72     size_t pos = entry.find('/');
73     if (pos == CString::npos) {
74         LOG_FULL(FATAL) << "CheckIsRecordWithBundleName Invalid parameter entry";
75     }
76 
77     CString bundleName = entry.substr(0, pos);
78     size_t bundleNameLen = bundleName.length();
79     for (auto info : jsRecordInfo_) {
80         if (info.first.find(PACKAGE_PATH_SEGMENT) != CString::npos ||
81             info.first.find(NPM_PATH_SEGMENT) != CString::npos) {
82             continue;
83         }
84         CString recordName = info.first;
85         // Confirm whether the current record is new or old by judging whether the recordName has a bundleName
86         if (!(recordName.length() > bundleNameLen && (recordName.compare(0, bundleNameLen, bundleName) == 0))) {
87             isRecordWithBundleName_ = false;
88         }
89         return;
90     }
91 }
92 
~JSPandaFile()93 JSPandaFile::~JSPandaFile()
94 {
95     if (pf_ != nullptr) {
96         delete pf_;
97         pf_ = nullptr;
98     }
99 
100     constpoolMap_.clear();
101     jsRecordInfo_.clear();
102     methodLiteralMap_.clear();
103     ClearNameMap();
104     if (methodLiterals_ != nullptr) {
105         JSPandaFileManager::FreeBuffer(methodLiterals_);
106         methodLiterals_ = nullptr;
107     }
108 }
109 
GetOrInsertConstantPool(ConstPoolType type,uint32_t offset,const CUnorderedMap<uint32_t,uint64_t> * constpoolMap)110 uint32_t JSPandaFile::GetOrInsertConstantPool(ConstPoolType type, uint32_t offset,
111                                               const CUnorderedMap<uint32_t, uint64_t> *constpoolMap)
112 {
113     CUnorderedMap<uint32_t, uint64_t> *map = nullptr;
114     if (constpoolMap != nullptr && !IsBundlePack()) {
115         map = const_cast<CUnorderedMap<uint32_t, uint64_t> *>(constpoolMap);
116     } else {
117         map = &constpoolMap_;
118     }
119     auto it = map->find(offset);
120     if (it != map->cend()) {
121         ConstPoolValue value(it->second);
122         return value.GetConstpoolIndex();
123     }
124     ASSERT(constpoolIndex_ != UINT32_MAX);
125     uint32_t index = constpoolIndex_++;
126     ConstPoolValue value(type, index);
127     map->emplace(offset, value.GetValue());
128     return index;
129 }
130 
InitializeUnMergedPF()131 void JSPandaFile::InitializeUnMergedPF()
132 {
133     Span<const uint32_t> classIndexes = pf_->GetClasses();
134     JSRecordInfo info;
135     for (const uint32_t index : classIndexes) {
136         panda_file::File::EntityId classId(index);
137         if (pf_->IsExternal(classId)) {
138             continue;
139         }
140         panda_file::ClassDataAccessor cda(*pf_, classId);
141         numMethods_ += cda.GetMethodsNumber();
142         const char *desc = utf::Mutf8AsCString(cda.GetDescriptor());
143         if (info.moduleRecordIdx == -1 && std::strcmp(MODULE_CLASS, desc) == 0) {
144             cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
145                 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
146                 panda_file::File::StringData sd = GetStringData(fieldNameId);
147                 CString fieldName = utf::Mutf8AsCString(sd.data);
148                 if (fieldName != desc_) {
149                     info.moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value();
150                     info.classId = index;
151                     return;
152                 }
153             });
154         }
155         if (!info.isCjs && std::strcmp(COMMONJS_CLASS, desc) == 0) {
156             info.classId = index;
157             info.isCjs = true;
158         }
159         if (!info.hasTopLevelAwait && std::strcmp(HASTLA_CLASS, desc) == 0) {
160             info.hasTopLevelAwait = true;
161         }
162     }
163     jsRecordInfo_.insert({JSPandaFile::ENTRY_FUNCTION_NAME, info});
164     methodLiterals_ =
165         static_cast<MethodLiteral *>(JSPandaFileManager::AllocateBuffer(sizeof(MethodLiteral) * numMethods_));
166 }
167 
InitializeMergedPF()168 void JSPandaFile::InitializeMergedPF()
169 {
170     Span<const uint32_t> classIndexes = pf_->GetClasses();
171     for (const uint32_t index : classIndexes) {
172         panda_file::File::EntityId classId(index);
173         if (pf_->IsExternal(classId)) {
174             continue;
175         }
176         panda_file::ClassDataAccessor cda(*pf_, classId);
177         numMethods_ += cda.GetMethodsNumber();
178         // get record info
179         JSRecordInfo info;
180         info.classId = index;
181         bool hasCjsFiled = false;
182         bool hasJsonFiled = false;
183         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
184             panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
185             panda_file::File::StringData sd = GetStringData(fieldNameId);
186             const char *fieldName = utf::Mutf8AsCString(sd.data);
187             if (std::strcmp(IS_COMMON_JS, fieldName) == 0) {
188                 hasCjsFiled = true;
189                 info.isCjs = fieldAccessor.GetValue<bool>().value();
190             } else if (std::strcmp(IS_JSON_CONTENT, fieldName) == 0) {
191                 hasJsonFiled = true;
192                 info.isJson = true;
193                 info.jsonStringId = fieldAccessor.GetValue<uint32_t>().value();
194             } else if (std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) {
195                 info.moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value();
196             } else if (std::strcmp(HAS_TOP_LEVEL_AWAIT, fieldName) == 0) {
197                 info.hasTopLevelAwait = fieldAccessor.GetValue<bool>().value();
198             } else if (std::strcmp(TYPE_FLAG, fieldName) == 0) {
199                 info.hasTSTypes = fieldAccessor.GetValue<uint8_t>().value() != 0;
200             } else if (std::strcmp(TYPE_SUMMARY_OFFSET, fieldName) == 0) {
201                 info.typeSummaryOffset = fieldAccessor.GetValue<uint32_t>().value();
202             } else if (std::strlen(fieldName) > PACKAGE_NAME_LEN &&
203                        std::strncmp(fieldName, PACKAGE_NAME, PACKAGE_NAME_LEN) == 0) {
204                 info.npmPackageName = fieldName + PACKAGE_NAME_LEN;
205             }
206         });
207         if (hasCjsFiled || hasJsonFiled) {
208             CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
209             jsRecordInfo_.insert({ParseEntryPoint(desc), info});
210         }
211     }
212     methodLiterals_ =
213         static_cast<MethodLiteral *>(JSPandaFileManager::AllocateBuffer(sizeof(MethodLiteral) * numMethods_));
214 }
215 
FindMethodLiteral(uint32_t offset) const216 MethodLiteral *JSPandaFile::FindMethodLiteral(uint32_t offset) const
217 {
218     auto iter = methodLiteralMap_.find(offset);
219     if (iter == methodLiteralMap_.end()) {
220         return nullptr;
221     }
222     return iter->second;
223 }
224 
IsFirstMergedAbc() const225 bool JSPandaFile::IsFirstMergedAbc() const
226 {
227     if (isFirstPandafile_ && !IsBundlePack()) {
228         return true;
229     }
230     return false;
231 }
232 
CheckAndGetRecordInfo(const CString & recordName,JSRecordInfo & recordInfo) const233 bool JSPandaFile::CheckAndGetRecordInfo(const CString &recordName, JSRecordInfo &recordInfo) const
234 {
235     if (IsBundlePack()) {
236         recordInfo = jsRecordInfo_.begin()->second;
237         return true;
238     }
239     auto info = jsRecordInfo_.find(recordName);
240     if (info != jsRecordInfo_.end()) {
241         recordInfo = info->second;
242         return true;
243     }
244     return false;
245 }
246 
GetEntryPoint(const CString & recordName) const247 CString JSPandaFile::GetEntryPoint(const CString &recordName) const
248 {
249     CString entryPoint;
250     if (FindOhmUrlInPF(recordName, entryPoint) && HasRecord(entryPoint)) {
251         return entryPoint;
252     }
253     return CString();
254 }
255 
FindOhmUrlInPF(const CString & recordName,CString & entryPoint) const256 bool JSPandaFile::FindOhmUrlInPF(const CString &recordName, CString &entryPoint) const
257 {
258     Span<const uint32_t> classIndexes = pf_->GetClasses();
259     for (const uint32_t index : classIndexes) {
260         panda_file::File::EntityId classId(index);
261         if (pf_->IsExternal(classId)) {
262             continue;
263         }
264         panda_file::ClassDataAccessor cda(*pf_, classId);
265         CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
266         if (recordName == ParseEntryPoint(desc)) {
267             cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
268                 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
269                 panda_file::File::StringData sd = GetStringData(fieldNameId);
270                 CString fieldName = utf::Mutf8AsCString(sd.data);
271                 entryPoint = fieldName;
272             });
273         }
274         if (!entryPoint.empty()) {
275             return true;
276         }
277     }
278     return false;
279 }
280 
GetFunctionKind(panda_file::FunctionKind funcKind)281 FunctionKind JSPandaFile::GetFunctionKind(panda_file::FunctionKind funcKind)
282 {
283     FunctionKind kind;
284     switch (funcKind) {
285         case panda_file::FunctionKind::NONE:
286             kind = FunctionKind::NONE_FUNCTION;
287             break;
288         case panda_file::FunctionKind::FUNCTION:
289             kind = FunctionKind::BASE_CONSTRUCTOR;
290             break;
291         case panda_file::FunctionKind::NC_FUNCTION:
292             kind = FunctionKind::ARROW_FUNCTION;
293             break;
294         case panda_file::FunctionKind::GENERATOR_FUNCTION:
295             kind = FunctionKind::GENERATOR_FUNCTION;
296             break;
297         case panda_file::FunctionKind::ASYNC_FUNCTION:
298             kind = FunctionKind::ASYNC_FUNCTION;
299             break;
300         case panda_file::FunctionKind::ASYNC_GENERATOR_FUNCTION:
301             kind = FunctionKind::ASYNC_GENERATOR_FUNCTION;
302             break;
303         case panda_file::FunctionKind::ASYNC_NC_FUNCTION:
304             kind = FunctionKind::ASYNC_ARROW_FUNCTION;
305             break;
306         case panda_file::FunctionKind::CONCURRENT_FUNCTION:
307             kind = FunctionKind::CONCURRENT_FUNCTION;
308             break;
309         default:
310             LOG_ECMA(FATAL) << "this branch is unreachable";
311             UNREACHABLE();
312     }
313     return kind;
314 }
315 
GetFunctionKind(ConstPoolType type)316 FunctionKind JSPandaFile::GetFunctionKind(ConstPoolType type)
317 {
318     FunctionKind kind;
319     switch (type) {
320         case ConstPoolType::BASE_FUNCTION:
321             kind = FunctionKind::BASE_CONSTRUCTOR;
322             break;
323         case ConstPoolType::NC_FUNCTION:
324             kind = FunctionKind::ARROW_FUNCTION;
325             break;
326         case ConstPoolType::GENERATOR_FUNCTION:
327             kind = FunctionKind::GENERATOR_FUNCTION;
328             break;
329         case ConstPoolType::ASYNC_FUNCTION:
330             kind = FunctionKind::ASYNC_FUNCTION;
331             break;
332         case ConstPoolType::CLASS_FUNCTION:
333             kind = FunctionKind::CLASS_CONSTRUCTOR;
334             break;
335         case ConstPoolType::METHOD:
336             kind = FunctionKind::NORMAL_FUNCTION;
337             break;
338         case ConstPoolType::ASYNC_GENERATOR_FUNCTION:
339             kind = FunctionKind::ASYNC_GENERATOR_FUNCTION;
340             break;
341         default:
342             LOG_ECMA(FATAL) << "this branch is unreachable";
343             UNREACHABLE();
344     }
345     return kind;
346 }
347 
348 /*
349  handle desc like: case1: /data/storage/el1/bundle/entry/ets/modules.abc -> entry/ets/modules.abc
350  case2: /data/storage/el1/bundle/entry/ets/widgets.abc -> entry/ets/widgets.abc
351  case3: /data/app/el1/bundle/public/com.xx.xx/entry/ets/modules.abc -> entry/ets/modules.abc
352  case4: /data/app/el1/bundle/public/com.xx.xx/entry/ets/widgets.abc -> entry/ets/widgets.abc
353 */
GetNormalizedFileDesc(const CString & desc)354 CString JSPandaFile::GetNormalizedFileDesc(const CString &desc)
355 {
356     auto etsTokenPos = desc.rfind(OHOS_PKG_ABC_PATH_ROOT);
357     if (etsTokenPos == std::string::npos) {
358         // file not in OHOS package.
359         return desc;
360     }
361     auto ohosModulePos = desc.rfind('/', etsTokenPos - 1);
362     if (ohosModulePos == std::string::npos) {
363         LOG_ECMA(ERROR) << "Get abcPath from desc failed. desc: " << desc;
364         return desc;
365     }
366     // substring likes {ohosModuleName}/ets/modules.abc or {ohosModuleName}/ets/widgets.abc
367     return desc.substr(ohosModulePos + 1);
368 }
369 
GetNormalizedFileDesc() const370 CString JSPandaFile::GetNormalizedFileDesc() const
371 {
372     return GetNormalizedFileDesc(desc_);
373 }
374 
GetMethodName(EntityId methodId)375 const char *JSPandaFile::GetMethodName(EntityId methodId)
376 {
377     LockHolder lock(methodNameMapMutex_);
378     uint32_t id = methodId.GetOffset();
379     auto iter = methodNameMap_.find(id);
380     if (iter != methodNameMap_.end()) {
381         return iter->second;
382     }
383 
384     panda_file::MethodDataAccessor mda(*pf_, methodId);
385     auto sd = GetStringData(mda.GetNameId());
386     auto name = utf::Mutf8AsCString(sd.data);
387     methodNameMap_.emplace(id, name);
388 
389     return name;
390 }
391 
GetCpuProfilerMethodName(EntityId methodId)392 const char *JSPandaFile::GetCpuProfilerMethodName(EntityId methodId)
393 {
394     panda_file::MethodDataAccessor mda(*pf_, methodId);
395     auto sd = GetStringData(mda.GetNameId());
396     auto name = utf::Mutf8AsCString(sd.data);
397     return name;
398 }
399 
GetRecordName(EntityId methodId)400 CString JSPandaFile::GetRecordName(EntityId methodId)
401 {
402     LockHolder lock(recordNameMapMutex_);
403     uint32_t id = methodId.GetOffset();
404     auto iter = recordNameMap_.find(id);
405     if (iter != recordNameMap_.end()) {
406         return iter->second;
407     }
408 
409     panda_file::MethodDataAccessor mda(*pf_, methodId);
410     panda_file::ClassDataAccessor cda(*pf_, mda.GetClassId());
411     CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
412     auto name =  JSPandaFile::ParseEntryPoint(desc);
413     recordNameMap_.emplace(id, name);
414     return name;
415 }
416 
ClearNameMap()417 void JSPandaFile::ClearNameMap()
418 {
419     {
420         LockHolder lock(methodNameMapMutex_);
421         methodNameMap_.clear();
422     }
423     {
424         LockHolder lock(recordNameMapMutex_);
425         recordNameMap_.clear();
426     }
427 }
428 }  // namespace panda::ecmascript
429