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