• 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 {
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