14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#ifndef ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
174514f5e3Sopenharmony_ci#define ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
184514f5e3Sopenharmony_ci
194514f5e3Sopenharmony_ci#include <mutex>
204514f5e3Sopenharmony_ci
214514f5e3Sopenharmony_ci#include "ecmascript/common.h"
224514f5e3Sopenharmony_ci#include "ecmascript/debugger/js_pt_location.h"
234514f5e3Sopenharmony_ci#include "ecmascript/jspandafile/js_pandafile.h"
244514f5e3Sopenharmony_ci#include "ecmascript/mem/c_containers.h"
254514f5e3Sopenharmony_ci#include "ecmascript/mem/c_string.h"
264514f5e3Sopenharmony_ci
274514f5e3Sopenharmony_ci#include "libpandafile/class_data_accessor-inl.h"
284514f5e3Sopenharmony_ci#include "libpandafile/file.h"
294514f5e3Sopenharmony_ci#include "libpandabase/utils/utf.h"
304514f5e3Sopenharmony_ci
314514f5e3Sopenharmony_cinamespace panda::ecmascript {
324514f5e3Sopenharmony_ciclass JSPandaFile;
334514f5e3Sopenharmony_ci
344514f5e3Sopenharmony_cistruct LineTableEntry {
354514f5e3Sopenharmony_ci    uint32_t offset;
364514f5e3Sopenharmony_ci    int32_t line;
374514f5e3Sopenharmony_ci
384514f5e3Sopenharmony_ci    bool operator<(const LineTableEntry &other) const
394514f5e3Sopenharmony_ci    {
404514f5e3Sopenharmony_ci        return offset < other.offset;
414514f5e3Sopenharmony_ci    }
424514f5e3Sopenharmony_ci};
434514f5e3Sopenharmony_ci
444514f5e3Sopenharmony_cistruct ColumnTableEntry {
454514f5e3Sopenharmony_ci    uint32_t offset;
464514f5e3Sopenharmony_ci    int32_t column;
474514f5e3Sopenharmony_ci
484514f5e3Sopenharmony_ci    bool operator<(const ColumnTableEntry &other) const
494514f5e3Sopenharmony_ci    {
504514f5e3Sopenharmony_ci        return offset < other.offset;
514514f5e3Sopenharmony_ci    }
524514f5e3Sopenharmony_ci};
534514f5e3Sopenharmony_ci
544514f5e3Sopenharmony_ciusing LineNumberTable = CVector<LineTableEntry>;
554514f5e3Sopenharmony_ciusing ColumnNumberTable = CVector<ColumnTableEntry>;
564514f5e3Sopenharmony_ciusing JSPtLocation = tooling::JSPtLocation;
574514f5e3Sopenharmony_ci
584514f5e3Sopenharmony_ci/*
594514f5e3Sopenharmony_ci * Full version of LocalVariableInfo is defined in frontend,
604514f5e3Sopenharmony_ci * here only using name, reg_number, start_offset, and end_offset:
614514f5e3Sopenharmony_ci *   std::string name
624514f5e3Sopenharmony_ci *   std::string type
634514f5e3Sopenharmony_ci *   std::string typeSignature
644514f5e3Sopenharmony_ci *   int32_t regNumber
654514f5e3Sopenharmony_ci *   uint32_t startOffset
664514f5e3Sopenharmony_ci *   uint32_t endOffset
674514f5e3Sopenharmony_ci */
684514f5e3Sopenharmony_cistruct LocalVariableInfo {
694514f5e3Sopenharmony_ci    std::string name;
704514f5e3Sopenharmony_ci    int32_t regNumber;
714514f5e3Sopenharmony_ci    uint32_t startOffset;
724514f5e3Sopenharmony_ci    uint32_t endOffset;
734514f5e3Sopenharmony_ci};
744514f5e3Sopenharmony_ciusing LocalVariableTable = CVector<LocalVariableInfo>;
754514f5e3Sopenharmony_ci
764514f5e3Sopenharmony_ci// public for debugger
774514f5e3Sopenharmony_ciclass PUBLIC_API DebugInfoExtractor {
784514f5e3Sopenharmony_cipublic:
794514f5e3Sopenharmony_ci    explicit DebugInfoExtractor(const JSPandaFile *jsPandaFile) : jsPandaFile_(jsPandaFile)
804514f5e3Sopenharmony_ci    {}
814514f5e3Sopenharmony_ci
824514f5e3Sopenharmony_ci    ~DebugInfoExtractor() = default;
834514f5e3Sopenharmony_ci
844514f5e3Sopenharmony_ci    const LineNumberTable &GetLineNumberTable(const panda_file::File::EntityId methodId);
854514f5e3Sopenharmony_ci
864514f5e3Sopenharmony_ci    const ColumnNumberTable &GetColumnNumberTable(const panda_file::File::EntityId methodId);
874514f5e3Sopenharmony_ci
884514f5e3Sopenharmony_ci    const LocalVariableTable &GetLocalVariableTable(const panda_file::File::EntityId methodId);
894514f5e3Sopenharmony_ci
904514f5e3Sopenharmony_ci    const std::string &GetSourceFile(const panda_file::File::EntityId methodId);
914514f5e3Sopenharmony_ci
924514f5e3Sopenharmony_ci    const std::string &GetSourceCode(const panda_file::File::EntityId methodId);
934514f5e3Sopenharmony_ci
944514f5e3Sopenharmony_ci    template<class Callback>
954514f5e3Sopenharmony_ci    bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column,
964514f5e3Sopenharmony_ci        const std::string &url, const std::unordered_set<std::string> &debugRecordName)
974514f5e3Sopenharmony_ci    {
984514f5e3Sopenharmony_ci        if (line == SPECIAL_LINE_MARK) {
994514f5e3Sopenharmony_ci            return false;
1004514f5e3Sopenharmony_ci        }
1014514f5e3Sopenharmony_ci        auto &pandaFile = *jsPandaFile_->GetPandaFile();
1024514f5e3Sopenharmony_ci        auto classes = jsPandaFile_->GetClasses();
1034514f5e3Sopenharmony_ci        for (size_t i = 0; i < classes.Size(); i++) {
1044514f5e3Sopenharmony_ci            panda_file::File::EntityId id(classes[i]);
1054514f5e3Sopenharmony_ci            if (jsPandaFile_->IsExternal(id)) {
1064514f5e3Sopenharmony_ci                continue;
1074514f5e3Sopenharmony_ci            }
1084514f5e3Sopenharmony_ci
1094514f5e3Sopenharmony_ci            CVector<panda_file::File::EntityId> methodIds;
1104514f5e3Sopenharmony_ci            panda_file::ClassDataAccessor cda(pandaFile, id);
1114514f5e3Sopenharmony_ci            CString recordName = JSPandaFile::ParseEntryPoint(utf::Mutf8AsCString(cda.GetDescriptor()));
1124514f5e3Sopenharmony_ci            // Check record name in stage mode
1134514f5e3Sopenharmony_ci            if (!jsPandaFile_->IsBundlePack()) {
1144514f5e3Sopenharmony_ci                // the recordName for testcases is empty
1154514f5e3Sopenharmony_ci                if (!debugRecordName.empty()) {
1164514f5e3Sopenharmony_ci                    auto iter = debugRecordName.find(std::string(recordName.c_str()));
1174514f5e3Sopenharmony_ci                    if (iter == debugRecordName.end()) {
1184514f5e3Sopenharmony_ci                        continue;
1194514f5e3Sopenharmony_ci                    }
1204514f5e3Sopenharmony_ci                }
1214514f5e3Sopenharmony_ci            }
1224514f5e3Sopenharmony_ci            cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
1234514f5e3Sopenharmony_ci                methodIds.push_back(mda.GetMethodId());
1244514f5e3Sopenharmony_ci            });
1254514f5e3Sopenharmony_ci
1264514f5e3Sopenharmony_ci            int32_t minColumn = INT32_MAX;
1274514f5e3Sopenharmony_ci            uint32_t currentOffset = UINT32_MAX;
1284514f5e3Sopenharmony_ci            uint32_t minColumnOffset = UINT32_MAX;
1294514f5e3Sopenharmony_ci            EntityId currentMethodId;
1304514f5e3Sopenharmony_ci            EntityId minColumnMethodId;
1314514f5e3Sopenharmony_ci            for (auto &methodId : methodIds) {
1324514f5e3Sopenharmony_ci                const std::string &sourceFile = GetSourceFile(methodId);
1334514f5e3Sopenharmony_ci                // the url for testcases is empty
1344514f5e3Sopenharmony_ci                if (!url.empty() && sourceFile != url) {
1354514f5e3Sopenharmony_ci                    continue;
1364514f5e3Sopenharmony_ci                }
1374514f5e3Sopenharmony_ci                const LineNumberTable &lineTable = GetLineNumberTable(methodId);
1384514f5e3Sopenharmony_ci                const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId);
1394514f5e3Sopenharmony_ci                for (uint32_t j = 0; j < lineTable.size(); j++) {
1404514f5e3Sopenharmony_ci                    if (lineTable[j].line != line) {
1414514f5e3Sopenharmony_ci                        continue;
1424514f5e3Sopenharmony_ci                    }
1434514f5e3Sopenharmony_ci                    currentMethodId = methodId;
1444514f5e3Sopenharmony_ci                    currentOffset = lineTable[j].offset;
1454514f5e3Sopenharmony_ci                    uint32_t nextOffset = ((j == lineTable.size() - 1) ? UINT32_MAX : lineTable[j + 1].offset);
1464514f5e3Sopenharmony_ci                    for (const auto &pair : columnTable) {
1474514f5e3Sopenharmony_ci                        if (pair.offset >= currentOffset && pair.offset < nextOffset) {
1484514f5e3Sopenharmony_ci                            if (pair.column == column) {
1494514f5e3Sopenharmony_ci                                return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url));
1504514f5e3Sopenharmony_ci                            } else if (pair.column < minColumn && currentOffset < minColumnOffset) {
1514514f5e3Sopenharmony_ci                                minColumn = pair.column;
1524514f5e3Sopenharmony_ci                                minColumnOffset = currentOffset;
1534514f5e3Sopenharmony_ci                                minColumnMethodId = currentMethodId;
1544514f5e3Sopenharmony_ci                            }
1554514f5e3Sopenharmony_ci                        }
1564514f5e3Sopenharmony_ci                    }
1574514f5e3Sopenharmony_ci                }
1584514f5e3Sopenharmony_ci            }
1594514f5e3Sopenharmony_ci            if (minColumn != INT32_MAX) { // find the smallest column for the corresponding row
1604514f5e3Sopenharmony_ci                return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, url));
1614514f5e3Sopenharmony_ci            }
1624514f5e3Sopenharmony_ci            if (currentOffset != UINT32_MAX) { // find corresponding row, but not find corresponding column
1634514f5e3Sopenharmony_ci                return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, url));
1644514f5e3Sopenharmony_ci            }
1654514f5e3Sopenharmony_ci        }
1664514f5e3Sopenharmony_ci        return false;
1674514f5e3Sopenharmony_ci    }
1684514f5e3Sopenharmony_ci
1694514f5e3Sopenharmony_ci    template<class Callback>
1704514f5e3Sopenharmony_ci    bool MatchLineWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset)
1714514f5e3Sopenharmony_ci    {
1724514f5e3Sopenharmony_ci        int32_t line = 0;
1734514f5e3Sopenharmony_ci        const LineNumberTable &lineTable = GetLineNumberTable(methodId);
1744514f5e3Sopenharmony_ci        auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0});
1754514f5e3Sopenharmony_ci        if (iter != lineTable.begin()) {
1764514f5e3Sopenharmony_ci            line = (iter - 1)->line;
1774514f5e3Sopenharmony_ci        }
1784514f5e3Sopenharmony_ci        return cb(line);
1794514f5e3Sopenharmony_ci    }
1804514f5e3Sopenharmony_ci
1814514f5e3Sopenharmony_ci    template<class Callback>
1824514f5e3Sopenharmony_ci    bool MatchColumnWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset)
1834514f5e3Sopenharmony_ci    {
1844514f5e3Sopenharmony_ci        int32_t column = 0;
1854514f5e3Sopenharmony_ci        const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId);
1864514f5e3Sopenharmony_ci        auto iter = std::upper_bound(columnTable.begin(), columnTable.end(), ColumnTableEntry {offset, 0});
1874514f5e3Sopenharmony_ci        if (iter != columnTable.begin()) {
1884514f5e3Sopenharmony_ci            column = (iter - 1)->column;
1894514f5e3Sopenharmony_ci        }
1904514f5e3Sopenharmony_ci        return cb(column);
1914514f5e3Sopenharmony_ci    }
1924514f5e3Sopenharmony_ci
1934514f5e3Sopenharmony_ci    int32_t GetFristLine(panda_file::File::EntityId methodId)
1944514f5e3Sopenharmony_ci    {
1954514f5e3Sopenharmony_ci        const LineNumberTable &lineTable = GetLineNumberTable(methodId);
1964514f5e3Sopenharmony_ci        auto tableSize = lineTable.size();
1974514f5e3Sopenharmony_ci        if (tableSize == 0) {
1984514f5e3Sopenharmony_ci            return 0;
1994514f5e3Sopenharmony_ci        }
2004514f5e3Sopenharmony_ci        if (tableSize == 1) {
2014514f5e3Sopenharmony_ci            return lineTable[0].line + 1;
2024514f5e3Sopenharmony_ci        }
2034514f5e3Sopenharmony_ci        int firstLineIndex = ((lineTable[0].line == SPECIAL_LINE_MARK) ? 1 : 0);
2044514f5e3Sopenharmony_ci        return lineTable[firstLineIndex].line + 1;
2054514f5e3Sopenharmony_ci    }
2064514f5e3Sopenharmony_ci
2074514f5e3Sopenharmony_ci    int32_t GetFristColumn(panda_file::File::EntityId methodId)
2084514f5e3Sopenharmony_ci    {
2094514f5e3Sopenharmony_ci        const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId);
2104514f5e3Sopenharmony_ci        auto tableSize = columnTable.size();
2114514f5e3Sopenharmony_ci        if (tableSize == 0) {
2124514f5e3Sopenharmony_ci            return 0;
2134514f5e3Sopenharmony_ci        }
2144514f5e3Sopenharmony_ci        if (tableSize == 1) {
2154514f5e3Sopenharmony_ci            return columnTable[0].column + 1;
2164514f5e3Sopenharmony_ci        }
2174514f5e3Sopenharmony_ci        int firstColumnIndex = ((columnTable[0].column == SPECIAL_LINE_MARK) ? 1 : 0);
2184514f5e3Sopenharmony_ci        return columnTable[firstColumnIndex].column + 1;
2194514f5e3Sopenharmony_ci    }
2204514f5e3Sopenharmony_ci
2214514f5e3Sopenharmony_ci    void Extract();
2224514f5e3Sopenharmony_ci
2234514f5e3Sopenharmony_ci    constexpr static int32_t SPECIAL_LINE_MARK = -1;
2244514f5e3Sopenharmony_ci
2254514f5e3Sopenharmony_ciprivate:
2264514f5e3Sopenharmony_ci    bool ExtractorMethodDebugInfo(const panda_file::File::EntityId methodId);
2274514f5e3Sopenharmony_ci    void ExtractorMethodDebugInfo(const panda_file::File &pandaFile,
2284514f5e3Sopenharmony_ci                                  const std::optional<panda_file::File::EntityId> sourceFileId,
2294514f5e3Sopenharmony_ci                                  const std::optional<panda_file::File::EntityId> debugInfoId,
2304514f5e3Sopenharmony_ci                                  uint32_t offset);
2314514f5e3Sopenharmony_ci    struct MethodDebugInfo {
2324514f5e3Sopenharmony_ci        std::string sourceFile;
2334514f5e3Sopenharmony_ci        std::string sourceCode;
2344514f5e3Sopenharmony_ci        LineNumberTable lineNumberTable;
2354514f5e3Sopenharmony_ci        ColumnNumberTable columnNumberTable;
2364514f5e3Sopenharmony_ci        LocalVariableTable localVariableTable;
2374514f5e3Sopenharmony_ci    };
2384514f5e3Sopenharmony_ci
2394514f5e3Sopenharmony_ci    std::recursive_mutex mutex_;
2404514f5e3Sopenharmony_ci    CUnorderedMap<uint32_t, MethodDebugInfo> methods_;
2414514f5e3Sopenharmony_ci    const JSPandaFile *jsPandaFile_ {nullptr};
2424514f5e3Sopenharmony_ci};
2434514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
2444514f5e3Sopenharmony_ci
2454514f5e3Sopenharmony_ci#endif  // ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H
246