• 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 bool JSPandaFile::loadedFirstPandaFile = false;
JSPandaFile(const panda_file::File * pf,const CString & descriptor)24 JSPandaFile::JSPandaFile(const panda_file::File *pf, const CString &descriptor)
25     : pf_(pf), desc_(descriptor)
26 {
27     ASSERT(pf_ != nullptr);
28     CheckIsBundlePack();
29     if (isBundlePack_) {
30         InitializeUnMergedPF();
31     } else {
32         InitializeMergedPF();
33     }
34     checksum_ = pf->GetHeader()->checksum;
35     isNewVersion_ = pf_->GetHeader()->version > OLD_VERSION;
36     if (!loadedFirstPandaFile && !isBundlePack_) {
37         // Tag the first merged abc to use constant string. The lifetime of this first panda file is the same
38         // as the vm. And make sure the first pandafile is the same at the compile time and runtime.
39         isFirstPandafile_ = true;
40         loadedFirstPandaFile = true;
41     }
42 }
43 
CheckIsBundlePack()44 void JSPandaFile::CheckIsBundlePack()
45 {
46     Span<const uint32_t> classIndexes = pf_->GetClasses();
47     for (const uint32_t index : classIndexes) {
48         panda_file::File::EntityId classId(index);
49         if (pf_->IsExternal(classId)) {
50             continue;
51         }
52         panda_file::ClassDataAccessor cda(*pf_, classId);
53         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
54             panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
55             panda_file::File::StringData sd = GetStringData(fieldNameId);
56             const char *fieldName = utf::Mutf8AsCString(sd.data);
57             if (std::strcmp(IS_COMMON_JS, fieldName) == 0 || std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) {
58                 isBundlePack_ = false;
59             }
60         });
61         if (!isBundlePack_) {
62             return;
63         }
64     }
65 }
66 
CheckIsRecordWithBundleName(const CString & entry)67 void JSPandaFile::CheckIsRecordWithBundleName(const CString &entry)
68 {
69     size_t pos = entry.find('/');
70     if (pos == CString::npos) {
71         LOG_FULL(FATAL) << "CheckIsRecordWithBundleName Invalid parameter entry";
72     }
73 
74     CString bundleName = entry.substr(0, pos);
75     size_t bundleNameLen = bundleName.length();
76     for (auto info : jsRecordInfo_) {
77         if (info.first.find(PACKAGE_PATH_SEGMENT) != CString::npos ||
78             info.first.find(NPM_PATH_SEGMENT) != CString::npos) {
79             continue;
80         }
81         CString recordName = info.first;
82         // Confirm whether the current record is new or old by judging whether the recordName has a bundleName
83         if (!(recordName.length() > bundleNameLen && (recordName.compare(0, bundleNameLen, bundleName) == 0))) {
84             isRecordWithBundleName_ = false;
85         }
86         return;
87     }
88 }
89 
~JSPandaFile()90 JSPandaFile::~JSPandaFile()
91 {
92     if (pf_ != nullptr) {
93         delete pf_;
94         pf_ = nullptr;
95     }
96 
97     constpoolMap_.clear();
98     jsRecordInfo_.clear();
99     methodLiteralMap_.clear();
100 
101     if (methodLiterals_ != nullptr) {
102         JSPandaFileManager::FreeBuffer(methodLiterals_);
103         methodLiterals_ = nullptr;
104     }
105 }
106 
GetOrInsertConstantPool(ConstPoolType type,uint32_t offset,const CUnorderedMap<uint32_t,uint64_t> * constpoolMap)107 uint32_t JSPandaFile::GetOrInsertConstantPool(ConstPoolType type, uint32_t offset,
108                                               const CUnorderedMap<uint32_t, uint64_t> *constpoolMap)
109 {
110     CUnorderedMap<uint32_t, uint64_t> *map = nullptr;
111     if (constpoolMap != nullptr && !IsBundlePack()) {
112         map = const_cast<CUnorderedMap<uint32_t, uint64_t> *>(constpoolMap);
113     } else {
114         map = &constpoolMap_;
115     }
116     auto it = map->find(offset);
117     if (it != map->cend()) {
118         ConstPoolValue value(it->second);
119         return value.GetConstpoolIndex();
120     }
121     ASSERT(constpoolIndex_ != UINT32_MAX);
122     uint32_t index = constpoolIndex_++;
123     ConstPoolValue value(type, index);
124     map->emplace(offset, value.GetValue());
125     return index;
126 }
127 
InitializeUnMergedPF()128 void JSPandaFile::InitializeUnMergedPF()
129 {
130     Span<const uint32_t> classIndexes = pf_->GetClasses();
131     JSRecordInfo info;
132     for (const uint32_t index : classIndexes) {
133         panda_file::File::EntityId classId(index);
134         if (pf_->IsExternal(classId)) {
135             continue;
136         }
137         panda_file::ClassDataAccessor cda(*pf_, classId);
138         numMethods_ += cda.GetMethodsNumber();
139         const char *desc = utf::Mutf8AsCString(cda.GetDescriptor());
140         if (info.moduleRecordIdx == -1 && std::strcmp(MODULE_CLASS, desc) == 0) {
141             cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
142                 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
143                 panda_file::File::StringData sd = GetStringData(fieldNameId);
144                 CString fieldName = utf::Mutf8AsCString(sd.data);
145                 if (fieldName != desc_) {
146                     info.moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value();
147                     return;
148                 }
149             });
150         }
151         if (!info.isCjs && std::strcmp(COMMONJS_CLASS, desc) == 0) {
152             info.isCjs = true;
153         }
154     }
155     jsRecordInfo_.insert({JSPandaFile::ENTRY_FUNCTION_NAME, info});
156     methodLiterals_ =
157         static_cast<MethodLiteral *>(JSPandaFileManager::AllocateBuffer(sizeof(MethodLiteral) * numMethods_));
158 }
159 
InitializeMergedPF()160 void JSPandaFile::InitializeMergedPF()
161 {
162     Span<const uint32_t> classIndexes = pf_->GetClasses();
163     for (const uint32_t index : classIndexes) {
164         panda_file::File::EntityId classId(index);
165         if (pf_->IsExternal(classId)) {
166             continue;
167         }
168         panda_file::ClassDataAccessor cda(*pf_, classId);
169         numMethods_ += cda.GetMethodsNumber();
170         // get record info
171         JSRecordInfo info;
172         bool hasCjsFiled = false;
173         bool hasJsonFiled = false;
174         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
175             panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
176             panda_file::File::StringData sd = GetStringData(fieldNameId);
177             const char *fieldName = utf::Mutf8AsCString(sd.data);
178             if (std::strcmp(IS_COMMON_JS, fieldName) == 0) {
179                 hasCjsFiled = true;
180                 info.isCjs = fieldAccessor.GetValue<bool>().value();
181             } else if (std::strcmp(IS_JSON_CONTENT, fieldName) == 0) {
182                 hasJsonFiled = true;
183                 info.isJson = true;
184                 info.jsonStringId = fieldAccessor.GetValue<uint32_t>().value();
185             } else if (std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) {
186                 info.moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value();
187             } else if (std::strcmp(TYPE_FLAG, fieldName) == 0) {
188                 info.hasTSTypes = fieldAccessor.GetValue<uint8_t>().value() != 0;
189             } else if (std::strcmp(TYPE_SUMMARY_OFFSET, fieldName) == 0) {
190                 info.typeSummaryOffset = fieldAccessor.GetValue<uint32_t>().value();
191             } else if (std::strlen(fieldName) > PACKAGE_NAME_LEN &&
192                        std::strncmp(fieldName, PACKAGE_NAME, PACKAGE_NAME_LEN) == 0) {
193                 info.npmPackageName = fieldName + PACKAGE_NAME_LEN;
194             }
195         });
196         if (hasCjsFiled || hasJsonFiled) {
197             CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
198             jsRecordInfo_.insert({ParseEntryPoint(desc), info});
199         }
200     }
201     methodLiterals_ =
202         static_cast<MethodLiteral *>(JSPandaFileManager::AllocateBuffer(sizeof(MethodLiteral) * numMethods_));
203 }
204 
FindMethodLiteral(uint32_t offset) const205 MethodLiteral *JSPandaFile::FindMethodLiteral(uint32_t offset) const
206 {
207     auto iter = methodLiteralMap_.find(offset);
208     if (iter == methodLiteralMap_.end()) {
209         return nullptr;
210     }
211     return iter->second;
212 }
213 
IsFirstMergedAbc() const214 bool JSPandaFile::IsFirstMergedAbc() const
215 {
216     if (isFirstPandafile_ && !IsBundlePack()) {
217         return true;
218     }
219     return false;
220 }
221 
CheckAndGetRecordInfo(const CString & recordName,JSRecordInfo & recordInfo) const222 bool JSPandaFile::CheckAndGetRecordInfo(const CString &recordName, JSRecordInfo &recordInfo) const
223 {
224     if (IsBundlePack()) {
225         recordInfo = jsRecordInfo_.begin()->second;
226         return true;
227     }
228     auto info = jsRecordInfo_.find(recordName);
229     if (info != jsRecordInfo_.end()) {
230         recordInfo = info->second;
231         return true;
232     }
233     return false;
234 }
235 
GetJsonStringId(const JSRecordInfo & jsRecordInfo) const236 CString JSPandaFile::GetJsonStringId(const JSRecordInfo &jsRecordInfo) const
237 {
238     StringData sd = GetStringData(EntityId(jsRecordInfo.jsonStringId));
239     return utf::Mutf8AsCString(sd.data);
240 }
241 
GetEntryPoint(const CString & recordName) const242 CString JSPandaFile::GetEntryPoint(const CString &recordName) const
243 {
244     CString entryPoint = GetNpmEntries(recordName);
245     if (HasRecord(entryPoint)) {
246         return entryPoint;
247     }
248     return CString();
249 }
250 
GetNpmEntries(const CString & recordName) const251 CString JSPandaFile::GetNpmEntries(const CString &recordName) const
252 {
253     Span<const uint32_t> classIndexes = pf_->GetClasses();
254     CString npmEntrie;
255     for (const uint32_t index : classIndexes) {
256         panda_file::File::EntityId classId(index);
257         if (pf_->IsExternal(classId)) {
258             continue;
259         }
260         panda_file::ClassDataAccessor cda(*pf_, classId);
261         CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
262         if (recordName == ParseEntryPoint(desc)) {
263             cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void {
264                 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId();
265                 panda_file::File::StringData sd = GetStringData(fieldNameId);
266                 CString fieldName = utf::Mutf8AsCString(sd.data);
267                 npmEntrie = fieldName;
268             });
269         }
270         if (!npmEntrie.empty()) {
271             return npmEntrie;
272         }
273     }
274     return npmEntrie;
275 }
276 
GetFunctionKind(panda_file::FunctionKind funcKind)277 FunctionKind JSPandaFile::GetFunctionKind(panda_file::FunctionKind funcKind)
278 {
279     FunctionKind kind;
280     switch (funcKind) {
281         case panda_file::FunctionKind::NONE:
282         case panda_file::FunctionKind::FUNCTION:
283             kind = FunctionKind::BASE_CONSTRUCTOR;
284             break;
285         case panda_file::FunctionKind::NC_FUNCTION:
286             kind = FunctionKind::ARROW_FUNCTION;
287             break;
288         case panda_file::FunctionKind::GENERATOR_FUNCTION:
289             kind = FunctionKind::GENERATOR_FUNCTION;
290             break;
291         case panda_file::FunctionKind::ASYNC_FUNCTION:
292             kind = FunctionKind::ASYNC_FUNCTION;
293             break;
294         case panda_file::FunctionKind::ASYNC_GENERATOR_FUNCTION:
295             kind = FunctionKind::ASYNC_GENERATOR_FUNCTION;
296             break;
297         case panda_file::FunctionKind::ASYNC_NC_FUNCTION:
298             kind = FunctionKind::ASYNC_ARROW_FUNCTION;
299             break;
300         case panda_file::FunctionKind::CONCURRENT_FUNCTION:
301             kind = FunctionKind::CONCURRENT_FUNCTION;
302             break;
303         default:
304             LOG_ECMA(FATAL) << "this branch is unreachable";
305             UNREACHABLE();
306     }
307     return kind;
308 }
309 
GetFunctionKind(ConstPoolType type)310 FunctionKind JSPandaFile::GetFunctionKind(ConstPoolType type)
311 {
312     FunctionKind kind;
313     switch (type) {
314         case ConstPoolType::BASE_FUNCTION:
315             kind = FunctionKind::BASE_CONSTRUCTOR;
316             break;
317         case ConstPoolType::NC_FUNCTION:
318             kind = FunctionKind::ARROW_FUNCTION;
319             break;
320         case ConstPoolType::GENERATOR_FUNCTION:
321             kind = FunctionKind::GENERATOR_FUNCTION;
322             break;
323         case ConstPoolType::ASYNC_FUNCTION:
324             kind = FunctionKind::ASYNC_FUNCTION;
325             break;
326         case ConstPoolType::CLASS_FUNCTION:
327             kind = FunctionKind::CLASS_CONSTRUCTOR;
328             break;
329         case ConstPoolType::METHOD:
330             kind = FunctionKind::NORMAL_FUNCTION;
331             break;
332         case ConstPoolType::ASYNC_GENERATOR_FUNCTION:
333             kind = FunctionKind::ASYNC_GENERATOR_FUNCTION;
334             break;
335         default:
336             LOG_ECMA(FATAL) << "this branch is unreachable";
337             UNREACHABLE();
338     }
339     return kind;
340 }
341 }  // namespace panda::ecmascript
342