2022-03-24 08:43:08 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021 Huawei Device Co., Ltd.
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
|
|
|
|
#define ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
|
|
|
|
|
2023-04-26 09:30:51 +00:00
|
|
|
#include <mutex>
|
|
|
|
|
2022-09-27 14:47:24 +00:00
|
|
|
#include "ecmascript/common.h"
|
|
|
|
#include "ecmascript/debugger/js_pt_location.h"
|
|
|
|
#include "ecmascript/jspandafile/js_pandafile.h"
|
2022-03-31 15:38:07 +00:00
|
|
|
#include "ecmascript/mem/c_containers.h"
|
|
|
|
#include "ecmascript/mem/c_string.h"
|
2022-07-23 10:33:33 +00:00
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
#include "libpandafile/class_data_accessor-inl.h"
|
2022-03-24 08:43:08 +00:00
|
|
|
#include "libpandafile/file.h"
|
2023-03-30 06:18:48 +00:00
|
|
|
#include "libpandabase/utils/utf.h"
|
2022-03-24 08:43:08 +00:00
|
|
|
|
|
|
|
namespace panda::ecmascript {
|
2022-03-31 15:38:07 +00:00
|
|
|
class JSPandaFile;
|
|
|
|
|
2022-03-24 08:43:08 +00:00
|
|
|
struct LineTableEntry {
|
|
|
|
uint32_t offset;
|
2022-03-31 15:38:07 +00:00
|
|
|
int32_t line;
|
2022-05-14 14:15:39 +00:00
|
|
|
|
|
|
|
bool operator<(const LineTableEntry &other) const
|
|
|
|
{
|
|
|
|
return offset < other.offset;
|
|
|
|
}
|
2022-03-24 08:43:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ColumnTableEntry {
|
|
|
|
uint32_t offset;
|
2022-03-31 15:38:07 +00:00
|
|
|
int32_t column;
|
2022-05-14 14:15:39 +00:00
|
|
|
|
|
|
|
bool operator<(const ColumnTableEntry &other) const
|
|
|
|
{
|
|
|
|
return offset < other.offset;
|
|
|
|
}
|
2022-03-24 08:43:08 +00:00
|
|
|
};
|
|
|
|
|
2022-03-31 15:38:07 +00:00
|
|
|
using LineNumberTable = CVector<LineTableEntry>;
|
|
|
|
using ColumnNumberTable = CVector<ColumnTableEntry>;
|
2022-09-27 14:47:24 +00:00
|
|
|
using JSPtLocation = tooling::JSPtLocation;
|
2022-03-24 08:43:08 +00:00
|
|
|
|
|
|
|
/*
|
2023-06-27 07:47:45 +00:00
|
|
|
* Full version of LocalVariableInfo is defined in frontend,
|
|
|
|
* here only using name, reg_number, start_offset, and end_offset:
|
2022-06-06 15:34:43 +00:00
|
|
|
* std::string name
|
|
|
|
* std::string type
|
|
|
|
* std::string typeSignature
|
2022-03-24 08:43:08 +00:00
|
|
|
* int32_t regNumber
|
|
|
|
* uint32_t startOffset
|
|
|
|
* uint32_t endOffset
|
|
|
|
*/
|
2023-06-27 07:47:45 +00:00
|
|
|
struct LocalVariableInfo {
|
2023-06-29 01:39:13 +00:00
|
|
|
std::string name;
|
2023-07-11 09:35:47 +00:00
|
|
|
int32_t regNumber;
|
|
|
|
uint32_t startOffset;
|
|
|
|
uint32_t endOffset;
|
2023-06-27 07:47:45 +00:00
|
|
|
};
|
|
|
|
using LocalVariableTable = CVector<LocalVariableInfo>;
|
2022-03-24 08:43:08 +00:00
|
|
|
|
|
|
|
// public for debugger
|
|
|
|
class PUBLIC_API DebugInfoExtractor {
|
|
|
|
public:
|
2023-03-30 06:18:48 +00:00
|
|
|
explicit DebugInfoExtractor(const JSPandaFile *jsPandaFile) : jsPandaFile_(jsPandaFile)
|
|
|
|
{}
|
2022-03-24 08:43:08 +00:00
|
|
|
|
|
|
|
~DebugInfoExtractor() = default;
|
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
const LineNumberTable &GetLineNumberTable(const panda_file::File::EntityId methodId);
|
2022-03-24 08:43:08 +00:00
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
const ColumnNumberTable &GetColumnNumberTable(const panda_file::File::EntityId methodId);
|
2022-03-24 08:43:08 +00:00
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
const LocalVariableTable &GetLocalVariableTable(const panda_file::File::EntityId methodId);
|
2022-03-24 08:43:08 +00:00
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
const std::string &GetSourceFile(const panda_file::File::EntityId methodId);
|
2022-03-24 08:43:08 +00:00
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
const std::string &GetSourceCode(const panda_file::File::EntityId methodId);
|
2022-03-24 08:43:08 +00:00
|
|
|
|
2022-09-20 11:25:15 +00:00
|
|
|
template<class Callback>
|
2023-03-30 06:18:48 +00:00
|
|
|
bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column,
|
2024-08-21 08:18:32 +00:00
|
|
|
const std::string &url, const std::unordered_set<std::string> &debugRecordName)
|
2022-09-20 11:25:15 +00:00
|
|
|
{
|
|
|
|
if (line == SPECIAL_LINE_MARK) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-30 06:18:48 +00:00
|
|
|
auto &pandaFile = *jsPandaFile_->GetPandaFile();
|
|
|
|
auto classes = jsPandaFile_->GetClasses();
|
|
|
|
for (size_t i = 0; i < classes.Size(); i++) {
|
|
|
|
panda_file::File::EntityId id(classes[i]);
|
|
|
|
if (jsPandaFile_->IsExternal(id)) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-09-20 11:25:15 +00:00
|
|
|
|
2023-03-30 06:18:48 +00:00
|
|
|
CVector<panda_file::File::EntityId> methodIds;
|
|
|
|
panda_file::ClassDataAccessor cda(pandaFile, id);
|
|
|
|
CString recordName = JSPandaFile::ParseEntryPoint(utf::Mutf8AsCString(cda.GetDescriptor()));
|
2023-10-20 02:30:57 +00:00
|
|
|
// Check record name in stage mode
|
2023-10-20 13:17:35 +00:00
|
|
|
if (!jsPandaFile_->IsBundlePack()) {
|
2023-10-20 02:30:57 +00:00
|
|
|
// the recordName for testcases is empty
|
2023-10-20 13:17:35 +00:00
|
|
|
if (!debugRecordName.empty()) {
|
2023-10-20 02:30:57 +00:00
|
|
|
auto iter = debugRecordName.find(std::string(recordName.c_str()));
|
2023-10-20 13:17:35 +00:00
|
|
|
if (iter == debugRecordName.end()) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-10-11 07:03:05 +00:00
|
|
|
}
|
2022-09-25 13:52:00 +00:00
|
|
|
}
|
2023-03-30 06:18:48 +00:00
|
|
|
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
|
|
|
|
methodIds.push_back(mda.GetMethodId());
|
|
|
|
});
|
|
|
|
|
2023-06-25 12:04:34 +00:00
|
|
|
int32_t minColumn = INT32_MAX;
|
|
|
|
uint32_t currentOffset = UINT32_MAX;
|
|
|
|
uint32_t minColumnOffset = UINT32_MAX;
|
|
|
|
EntityId currentMethodId;
|
|
|
|
EntityId minColumnMethodId;
|
2023-03-30 06:18:48 +00:00
|
|
|
for (auto &methodId : methodIds) {
|
|
|
|
const std::string &sourceFile = GetSourceFile(methodId);
|
|
|
|
// the url for testcases is empty
|
|
|
|
if (!url.empty() && sourceFile != url) {
|
2022-09-20 11:25:15 +00:00
|
|
|
continue;
|
|
|
|
}
|
2023-03-30 06:18:48 +00:00
|
|
|
const LineNumberTable &lineTable = GetLineNumberTable(methodId);
|
|
|
|
const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId);
|
|
|
|
for (uint32_t j = 0; j < lineTable.size(); j++) {
|
|
|
|
if (lineTable[j].line != line) {
|
|
|
|
continue;
|
2022-09-20 11:25:15 +00:00
|
|
|
}
|
2023-06-25 12:04:34 +00:00
|
|
|
currentMethodId = methodId;
|
|
|
|
currentOffset = lineTable[j].offset;
|
2023-03-30 06:18:48 +00:00
|
|
|
uint32_t nextOffset = ((j == lineTable.size() - 1) ? UINT32_MAX : lineTable[j + 1].offset);
|
|
|
|
for (const auto &pair : columnTable) {
|
2023-06-25 12:04:34 +00:00
|
|
|
if (pair.offset >= currentOffset && pair.offset < nextOffset) {
|
|
|
|
if (pair.column == column) {
|
2024-08-21 08:18:32 +00:00
|
|
|
return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url));
|
2024-07-19 06:34:16 +00:00
|
|
|
} else if (pair.column < minColumn && currentOffset < minColumnOffset) {
|
2023-06-25 12:04:34 +00:00
|
|
|
minColumn = pair.column;
|
|
|
|
minColumnOffset = currentOffset;
|
|
|
|
minColumnMethodId = currentMethodId;
|
|
|
|
}
|
2023-03-30 06:18:48 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-20 11:25:15 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-25 12:04:34 +00:00
|
|
|
if (minColumn != INT32_MAX) { // find the smallest column for the corresponding row
|
2024-08-21 08:18:32 +00:00
|
|
|
return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, url));
|
2023-06-25 12:04:34 +00:00
|
|
|
}
|
|
|
|
if (currentOffset != UINT32_MAX) { // find corresponding row, but not find corresponding column
|
2024-08-21 08:18:32 +00:00
|
|
|
return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, url));
|
2023-06-25 12:04:34 +00:00
|
|
|
}
|
2022-09-20 11:25:15 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class Callback>
|
|
|
|
bool MatchLineWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset)
|
|
|
|
{
|
|
|
|
int32_t line = 0;
|
|
|
|
const LineNumberTable &lineTable = GetLineNumberTable(methodId);
|
|
|
|
auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0});
|
|
|
|
if (iter != lineTable.begin()) {
|
|
|
|
line = (iter - 1)->line;
|
|
|
|
}
|
|
|
|
return cb(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class Callback>
|
|
|
|
bool MatchColumnWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset)
|
|
|
|
{
|
|
|
|
int32_t column = 0;
|
|
|
|
const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId);
|
|
|
|
auto iter = std::upper_bound(columnTable.begin(), columnTable.end(), ColumnTableEntry {offset, 0});
|
|
|
|
if (iter != columnTable.begin()) {
|
|
|
|
column = (iter - 1)->column;
|
|
|
|
}
|
|
|
|
return cb(column);
|
|
|
|
}
|
|
|
|
|
2022-10-24 04:46:06 +00:00
|
|
|
int32_t GetFristLine(panda_file::File::EntityId methodId)
|
|
|
|
{
|
|
|
|
const LineNumberTable &lineTable = GetLineNumberTable(methodId);
|
2023-07-03 07:25:27 +00:00
|
|
|
auto tableSize = lineTable.size();
|
|
|
|
if (tableSize == 0) {
|
2022-10-24 04:46:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2023-07-03 07:25:27 +00:00
|
|
|
if (tableSize == 1) {
|
|
|
|
return lineTable[0].line + 1;
|
|
|
|
}
|
|
|
|
int firstLineIndex = ((lineTable[0].line == SPECIAL_LINE_MARK) ? 1 : 0);
|
|
|
|
return lineTable[firstLineIndex].line + 1;
|
2022-10-24 04:46:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t GetFristColumn(panda_file::File::EntityId methodId)
|
|
|
|
{
|
|
|
|
const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId);
|
2023-07-03 07:25:27 +00:00
|
|
|
auto tableSize = columnTable.size();
|
|
|
|
if (tableSize == 0) {
|
2022-10-24 04:46:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2023-07-03 07:25:27 +00:00
|
|
|
if (tableSize == 1) {
|
|
|
|
return columnTable[0].column + 1;
|
|
|
|
}
|
|
|
|
int firstColumnIndex = ((columnTable[0].column == SPECIAL_LINE_MARK) ? 1 : 0);
|
|
|
|
return columnTable[firstColumnIndex].column + 1;
|
2022-10-24 04:46:06 +00:00
|
|
|
}
|
|
|
|
|
2023-05-19 01:26:18 +00:00
|
|
|
void Extract();
|
|
|
|
|
2022-09-20 11:25:15 +00:00
|
|
|
constexpr static int32_t SPECIAL_LINE_MARK = -1;
|
|
|
|
|
2022-03-24 08:43:08 +00:00
|
|
|
private:
|
2023-04-26 09:30:51 +00:00
|
|
|
bool ExtractorMethodDebugInfo(const panda_file::File::EntityId methodId);
|
2023-05-19 01:26:18 +00:00
|
|
|
void ExtractorMethodDebugInfo(const panda_file::File &pandaFile,
|
|
|
|
const std::optional<panda_file::File::EntityId> sourceFileId,
|
|
|
|
const std::optional<panda_file::File::EntityId> debugInfoId,
|
|
|
|
uint32_t offset);
|
2022-03-24 08:43:08 +00:00
|
|
|
struct MethodDebugInfo {
|
2022-06-06 15:34:43 +00:00
|
|
|
std::string sourceFile;
|
|
|
|
std::string sourceCode;
|
2022-03-24 08:43:08 +00:00
|
|
|
LineNumberTable lineNumberTable;
|
|
|
|
ColumnNumberTable columnNumberTable;
|
|
|
|
LocalVariableTable localVariableTable;
|
|
|
|
};
|
|
|
|
|
2023-05-19 01:26:18 +00:00
|
|
|
std::recursive_mutex mutex_;
|
2022-03-31 15:38:07 +00:00
|
|
|
CUnorderedMap<uint32_t, MethodDebugInfo> methods_;
|
2022-09-27 14:47:24 +00:00
|
|
|
const JSPandaFile *jsPandaFile_ {nullptr};
|
2022-03-24 08:43:08 +00:00
|
|
|
};
|
|
|
|
} // namespace panda::ecmascript
|
|
|
|
|
2024-08-21 08:18:32 +00:00
|
|
|
#endif // ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
|