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 "symbolTable.h"
17
18#include <fstream>
19#include <iostream>
20#include <util/helpers.h>
21
22namespace panda::es2panda::util {
23const std::string SymbolTable::FIRST_LEVEL_SEPERATOR = "|";
24const std::string SymbolTable::SECOND_LEVEL_SEPERATOR = ";";
25const size_t FUNCTION_ITEM_NUMBER = 3;
26const size_t MODULE_ITEM_NUMBER = 1;
27
28bool SymbolTable::Initialize(int targetApiVersion, std::string targetApiSubVersion)
29{
30    targetApiVersion_ = targetApiVersion;
31    targetApiSubVersion_ = targetApiSubVersion;
32    if (!symbolTable_.empty() && !ReadSymbolTable(symbolTable_)) {
33        std::cerr << "Failed to open the symbol table file'" << std::endl;
34        return false;
35    }
36
37    if (!dumpSymbolTable_.empty()) {
38        std::fstream fs;
39        fs.open(panda::os::file::File::GetExtendedFilePath(dumpSymbolTable_),
40            std::ios_base::out | std::ios_base::trunc);
41        if (!fs.is_open()) {
42            std::cerr << "Failed to create or open the output symbol table file '"
43                      << dumpSymbolTable_ << "' during symbol table initialization." << std::endl
44                      << "This error could be due to invalid file path, lack of write permissions, "
45                      << "or the file being in use by another process." << std::endl;
46            return false;
47        }
48        fs.close();
49    }
50
51    return true;
52}
53
54void SymbolTable::ReadRecordHashFunctionNames(const std::string &recordName, const std::string &funcInternalName,
55                                              const std::string &specialFuncIndex)
56{
57    auto recordHashFunctionNames = originRecordHashFunctionNames_.find(recordName);
58    if (specialFuncIndex == "0") {
59        // 0: not anonymous, special or duplicate function
60        if (recordHashFunctionNames == originRecordHashFunctionNames_.end())  {
61            std::unordered_map<std::string, std::string> functionIndexNameMap {};
62            originRecordHashFunctionNames_.insert({recordName, functionIndexNameMap});
63        }
64    } else {
65        if (recordHashFunctionNames != originRecordHashFunctionNames_.end()) {
66            recordHashFunctionNames->second.insert({specialFuncIndex, funcInternalName});
67        } else {
68            std::unordered_map<std::string, std::string> functionIndexNameMap {{specialFuncIndex, funcInternalName}};
69            originRecordHashFunctionNames_.insert({recordName, functionIndexNameMap});
70        }
71    }
72}
73
74bool SymbolTable::ReadSymbolTable(const std::string &symbolTable)
75{
76    std::ifstream ifs;
77    std::string line;
78    ifs.open(panda::os::file::File::GetExtendedFilePath(symbolTable));
79    if (!ifs.is_open()) {
80        std::cerr << "Failed to open the symbol table file '"
81                  << symbolTable << "' during symbol table reading." << std::endl
82                  << "Please check if the file exists, the path is correct, "
83                  << "and your program has the necessary permissions to access the file." << std::endl;
84        return false;
85    }
86
87    while (std::getline(ifs, line)) {
88        auto itemList = GetStringItems(line, FIRST_LEVEL_SEPERATOR);
89
90        if (itemList.size() == FUNCTION_ITEM_NUMBER) {
91            // read function info
92            struct OriginFunctionInfo info(&allocator_);
93            auto funcItems = GetStringItems(itemList[0], SECOND_LEVEL_SEPERATOR);
94            auto classItems = GetStringItems(itemList[1], SECOND_LEVEL_SEPERATOR);
95            auto lexItems = GetStringItems(itemList[2], SECOND_LEVEL_SEPERATOR);
96
97            info.recordName = funcItems[0].substr(0, funcItems[0].find_last_of("."));
98            info.funcInternalName = funcItems[1];
99            // 2: use the third element of the function as the hash of the function
100            info.funcHash = funcItems[2];
101
102            // 2 is to process each class name and its corresponding hash value
103            for (size_t i = 0; i < classItems.size(); i = i + 2) {
104                info.classHash.insert(std::pair<std::string, std::string>(classItems[i], classItems[i + 1]));
105            }
106            // 3 is to process a complete lexical environment entry
107            for (size_t i = 0; i < lexItems.size(); i = i + 3) {
108                auto name = std::string(lexItems[i]);
109                auto slot = std::atoi(std::string(lexItems[i + 1]).c_str());
110                auto type = std::atoi(std::string(lexItems[i + 2]).c_str());
111                info.lexenv.insert({slot, std::pair<std::string, int>(name, type)});
112            }
113
114            originFunctionInfo_.insert(std::pair<std::string, OriginFunctionInfo>(info.funcInternalName, info));
115            if (util::Helpers::IsDefaultApiVersion(targetApiVersion_, targetApiSubVersion_)) {
116                // index of function in its record's special function array
117                std::string specialFuncIndex{funcItems[3]};
118                ReadRecordHashFunctionNames(info.recordName, info.funcInternalName, specialFuncIndex);
119            }
120        } else if (itemList.size() == MODULE_ITEM_NUMBER) {
121            // read module info
122            auto moduleItems = GetStringItems(itemList[0], SECOND_LEVEL_SEPERATOR);
123            originModuleInfo_.insert(std::pair<std::string, std::string>(moduleItems[0], moduleItems[1]));
124        } else {
125            std::cerr << "Failed to read the symbol table line: '" << line
126                      << "' from the symbol table file '" << symbolTable << "' due to unrecognized format." << std::endl
127                      << "Please verify the format of the symbol table." << std::endl;
128        }
129    }
130    return true;
131}
132
133void SymbolTable::FillSymbolTable(const std::stringstream &content)
134{
135    std::lock_guard<std::mutex> lock(m_);
136    symbolTableContent_ << content.rdbuf();
137}
138
139void SymbolTable::WriteSymbolTable()
140{
141    std::fstream fs;
142    fs.open(panda::os::file::File::GetExtendedFilePath(dumpSymbolTable_),
143        std::ios_base::app | std::ios_base::in);
144    if (fs.is_open()) {
145        fs << symbolTableContent_.str();
146        fs.close();
147    }
148}
149
150std::vector<std::string_view> SymbolTable::GetStringItems(std::string_view input, const std::string &separator)
151{
152    std::vector<std::string_view> items;
153    size_t curPos = 0;
154    size_t lastPos = 0;
155
156    while ((curPos = input.find(separator, lastPos)) != std::string_view::npos) {
157        auto token = input.substr(lastPos, curPos - lastPos);
158        if (!token.empty()) {
159            items.push_back(token);
160        }
161        lastPos = curPos + separator.size();
162    }
163
164    auto tail = input.substr(lastPos);
165    if (!tail.empty()) {
166        items.push_back(tail);
167    }
168
169    return items;
170}
171}  // namespace panda::es2panda::util