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