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