1/*
2 * Copyright (c) 2023 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 "tooling/client/manager/source_manager.h"
17
18#include "common/log_wrapper.h"
19#include "tooling/client/session/session.h"
20
21using PtJson = panda::ecmascript::tooling::PtJson;
22using Result = panda::ecmascript::tooling::Result;
23namespace OHOS::ArkCompiler::Toolchain {
24void SourceManager::SendRequeSource(int scriptId)
25{
26    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
27    uint32_t id = session->GetMessageId();
28    scriptIdMap_.emplace(std::make_pair(id, scriptId));
29
30    std::unique_ptr<PtJson> request = PtJson::CreateObject();
31    request->Add("id", id);
32    request->Add("method", "Debugger.getScriptSource");
33
34    std::unique_ptr<PtJson> params = PtJson::CreateObject();
35    params->Add("scriptId", std::to_string(scriptId).c_str());
36    request->Add("params", params);
37
38    std::string message = request->Stringify();
39    if (session->ClientSendReq(message)) {
40        session->GetDomainManager().SetDomainById(id, "Debugger");
41    }
42    return;
43}
44
45void SourceManager::EnableReply(const std::unique_ptr<PtJson> json)
46{
47    if (json == nullptr) {
48        LOGE("arkdb: json parse error");
49        return;
50    }
51
52    if (!json->IsObject()) {
53        LOGE("arkdb: json parse format error");
54        json->ReleaseRoot();
55        return;
56    }
57
58    std::unique_ptr<PtJson> params;
59    Result ret = json->GetObject("params", &params);
60    if (ret != Result::SUCCESS) {
61        LOGE("arkdb: find params error");
62        return;
63    }
64
65    std::string scriptIdStr;
66    ret = params->GetString("scriptId", &scriptIdStr);
67    if (ret != Result::SUCCESS) {
68        LOGE("arkdb: find scriptId error");
69        return;
70    }
71
72    std::string fileName;
73    ret = params->GetString("url", &fileName);
74    if (ret != Result::SUCCESS) {
75        LOGE("arkdb: find fileName error");
76        return;
77    }
78    int scriptId = std::atoi(scriptIdStr.c_str());
79    SetFileName(scriptId, fileName);
80    SendRequeSource(scriptId);
81    return;
82}
83
84void SourceManager::SetFileName(int scriptId, const std::string& fileName)
85{
86    fileSource_.insert(std::make_pair(scriptId, std::make_pair(fileName, std::vector<std::string> {})));
87    return;
88}
89
90void SourceManager::GetFileName()
91{
92    for (auto it = fileSource_.begin(); it != fileSource_.end(); it++) {
93        std::cout << "scriptID : " << it->first;
94        std::cout << " fileName : " << it->second.first <<std::endl;
95    }
96    return;
97}
98
99void SourceManager::SetFileSource(int scriptIdIndex, const std::string& fileSource)
100{
101    const int IGNOR_NEWLINE_FLAG = 2;
102    auto scriptIdIt = scriptIdMap_.find(scriptIdIndex);
103    if (scriptIdIt == scriptIdMap_.end()) {
104        return;
105    }
106    int scriptId = scriptIdIt->second;
107
108    auto it = fileSource_.find(scriptId);
109    if (it != fileSource_.end() && it->second.second.empty()) {
110        std::string::size_type startPos = 0;
111        std::string::size_type endPos = fileSource.find("\r\n");
112        while (endPos != std::string::npos) {
113            std::string line = fileSource.substr(startPos, endPos - startPos);
114            it->second.second.push_back(line);
115            startPos = endPos + IGNOR_NEWLINE_FLAG;  // ignore "\r\n"
116            endPos = fileSource.find("\r\n", startPos);
117        }
118        it->second.second.push_back(fileSource.substr(startPos));
119    }
120    return;
121}
122
123std::vector<std::string> SourceManager::GetFileSource(int scriptId)
124{
125    int linNum = 0;
126    auto it = fileSource_.find(scriptId);
127    if (it != fileSource_.end()) {
128        std::cout << "fileSource : " <<std::endl;
129        for (const std::string& value : it->second.second) {
130            std::cout << ++linNum << "    " << value << std::endl;
131        }
132        return it->second.second;
133    }
134    return std::vector<std::string> {};
135}
136
137void SourceManager::GetDebugSources(const std::unique_ptr<PtJson> json)
138{
139    std::string funcName;
140    Result ret = json->GetString("functionName", &funcName);
141    if (ret != Result::SUCCESS) {
142        LOGE("arkdb: get functionName error");
143        return;
144    }
145
146    std::unique_ptr<PtJson> location;
147    ret = json->GetObject("location", &location);
148    if (ret != Result::SUCCESS) {
149        LOGE("arkdb: get location error");
150        return;
151    }
152
153    std::string scriptIdStr;
154    ret = location->GetString("scriptId", &scriptIdStr);
155    if (ret != Result::SUCCESS) {
156        LOGE("arkdb: get scriptId error");
157        return;
158    }
159    scriptId_ = std::atoi(scriptIdStr.c_str());
160
161    ret = location->GetInt("lineNumber", &debugLineNum_);
162    if (ret != Result::SUCCESS) {
163        LOGE("arkdb: get lineNumber error");
164        return;
165    }
166
167    LOGE("arkdb: callFrames : funcName %{public}s, scriptid %{public}s, lineNum %{public}d",
168        funcName.c_str(), scriptIdStr.c_str(), debugLineNum_);
169    auto it = fileSource_.find(scriptId_);
170    if (it != fileSource_.end()) {
171        std::cout << (debugLineNum_ + 1) << "    " << it->second.second[debugLineNum_] << std::endl;
172        std::cout << ">>> ";
173        fflush(stdout);
174    }
175    return;
176}
177
178void SourceManager::ListSourceCodeWithParameters(int startLine, int endLine)
179{
180    const int BLANK_LINE = std::numeric_limits<int>::max();
181    const int STATR_LINE_OFFSET = 6;
182    const int END_LINE_OFFSET = 4;
183    const int END_LINE = 10;
184    if (startLine != BLANK_LINE && endLine == BLANK_LINE) {
185        auto it = fileSource_.find(scriptId_);
186        if (it == fileSource_.end()) {
187            LOGE("arkdb: get file source error");
188            return;
189        }
190        if (startLine >= static_cast<int>(it->second.second.size()) + STATR_LINE_OFFSET ||
191            startLine < 0) {
192            std::cout << "Line number out of range, this file has " <<
193            static_cast<int>(it->second.second.size()) << " lines" << std::endl;
194            return;
195        }
196        int showLine = startLine - STATR_LINE_OFFSET;
197        if (showLine < 0) {
198            showLine = 0;
199            endLine = END_LINE;
200        } else {
201            endLine = startLine + END_LINE_OFFSET;
202        }
203        if (endLine > static_cast<int>(it->second.second.size())) {
204            endLine = static_cast<int>(it->second.second.size());
205        }
206        for (; showLine < endLine; showLine++) {
207            std::cout << (showLine + 1) << "    " << it->second.second[showLine] << std::endl;
208        }
209    } else if (startLine != BLANK_LINE && endLine != BLANK_LINE) {
210        auto it = fileSource_.find(scriptId_);
211        if (it == fileSource_.end()) {
212            LOGE("arkdb: get file source error");
213            return;
214        }
215        if (startLine > static_cast<int>(it->second.second.size()) ||
216            endLine > static_cast<int>(it->second.second.size()) || startLine < 1) {
217            std::cout << "Line number out of range, this file has " <<
218            static_cast<int>(it->second.second.size()) << " lines" << std::endl;
219            return;
220        }
221        if (endLine > static_cast<int>(it->second.second.size())) {
222            endLine = static_cast<int>(it->second.second.size());
223        }
224        for (int showLine = startLine - 1; showLine < endLine; showLine++) {
225            std::cout << (showLine + 1) << "    " << it->second.second[showLine] << std::endl;
226        }
227    }
228}
229
230void SourceManager::ListSource(int startLine, int endLine)
231{
232    const int BLANK_LINE = std::numeric_limits<int>::max();
233    const int STATR_LINE_OFFSET = 6;
234    const int END_LINE_OFFSET = 4;
235    const int END_LINE = 10;
236    if (startLine == BLANK_LINE && endLine == BLANK_LINE) {
237        auto it = fileSource_.find(scriptId_);
238        if (it == fileSource_.end()) {
239            LOGE("arkdb: get file source error");
240            return;
241        }
242        int showLine = (debugLineNum_ + 1) - STATR_LINE_OFFSET;
243        if (showLine < 0) {
244            showLine = 0;
245            endLine = END_LINE;
246        } else {
247            endLine = debugLineNum_ + END_LINE_OFFSET;
248        }
249        if (endLine > static_cast<int>(it->second.second.size())) {
250            endLine = static_cast<int>(it->second.second.size());
251        }
252        for (; showLine <= endLine; showLine++) {
253            std::cout << (showLine + 1) << "    " << it->second.second[showLine] << std::endl;
254        }
255    } else {
256        ListSourceCodeWithParameters(startLine, endLine);
257    }
258}
259
260void SourceManager::GetListSource(std::string startLine, std::string endLine)
261{
262    const int BLANK_LINE = std::numeric_limits<int>::max();
263    int startline = startLine.empty() ? BLANK_LINE : std::stoi(startLine);
264    int endline = endLine.empty() ? BLANK_LINE : std::stoi(endLine);
265    ListSource(startline, endline);
266    return;
267}
268}