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/domain/debugger_client.h"
17
18#include <map>
19
20#include "common/log_wrapper.h"
21#include "tooling/client/manager/breakpoint_manager.h"
22#include "tooling/client/manager/source_manager.h"
23#include "tooling/client/manager/stack_manager.h"
24#include "tooling/base/pt_json.h"
25#include "tooling/client/session/session.h"
26
27using PtJson = panda::ecmascript::tooling::PtJson;
28namespace OHOS::ArkCompiler::Toolchain {
29bool DebuggerClient::DispatcherCmd(const std::string &cmd)
30{
31    std::map<std::string, std::function<int()>> dispatcherTable {
32        { "break", std::bind(&DebuggerClient::BreakCommand, this)},
33        { "backtrack", std::bind(&DebuggerClient::BacktrackCommand, this)},
34        { "continue", std::bind(&DebuggerClient::ResumeCommand, this)},
35        { "delete", std::bind(&DebuggerClient::DeleteCommand, this)},
36        { "jump", std::bind(&DebuggerClient::JumpCommand, this)},
37        { "disable", std::bind(&DebuggerClient::DisableCommand, this)},
38        { "display", std::bind(&DebuggerClient::DisplayCommand, this)},
39        { "enable", std::bind(&DebuggerClient::EnableCommand, this)},
40        { "finish", std::bind(&DebuggerClient::FinishCommand, this)},
41        { "frame", std::bind(&DebuggerClient::FrameCommand, this)},
42        { "ignore", std::bind(&DebuggerClient::IgnoreCommand, this)},
43        { "infobreakpoints", std::bind(&DebuggerClient::InfobreakpointsCommand, this)},
44        { "infosource", std::bind(&DebuggerClient::InfosourceCommand, this)},
45        { "list", std::bind(&DebuggerClient::ListCommand, this)},
46        { "next", std::bind(&DebuggerClient::NextCommand, this)},
47        { "ptype", std::bind(&DebuggerClient::PtypeCommand, this)},
48        { "run", std::bind(&DebuggerClient::RunCommand, this)},
49        { "setvar", std::bind(&DebuggerClient::SetvarCommand, this)},
50        { "step", std::bind(&DebuggerClient::StepCommand, this)},
51        { "undisplay", std::bind(&DebuggerClient::UndisplayCommand, this)},
52        { "watch", std::bind(&DebuggerClient::WatchCommand, this)},
53        { "resume", std::bind(&DebuggerClient::ResumeCommand, this)},
54        { "step-into", std::bind(&DebuggerClient::StepIntoCommand, this)},
55        { "step-out", std::bind(&DebuggerClient::StepOutCommand, this)},
56        { "step-over", std::bind(&DebuggerClient::StepOverCommand, this)},
57    };
58
59    auto entry = dispatcherTable.find(cmd);
60    if (entry != dispatcherTable.end()) {
61        entry->second();
62        LOGI("DebuggerClient DispatcherCmd cmd: %{public}s", cmd.c_str());
63        return true;
64    }
65
66    LOGI("unknown command: %{public}s", cmd.c_str());
67    return false;
68}
69
70int DebuggerClient::BreakCommand()
71{
72    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
73    uint32_t id = session->GetMessageId();
74
75    std::unique_ptr<PtJson> request = PtJson::CreateObject();
76    request->Add("id", id);
77    request->Add("method", "Debugger.setBreakpointByUrl");
78
79    std::unique_ptr<PtJson> params = PtJson::CreateObject();
80    params->Add("columnNumber", breakPointInfoList_.back().columnNumber);
81    params->Add("lineNumber", breakPointInfoList_.back().lineNumber);
82    params->Add("url", breakPointInfoList_.back().url.c_str());
83    request->Add("params", params);
84
85    std::string message = request->Stringify();
86    if (session->ClientSendReq(message)) {
87        session->GetDomainManager().SetDomainById(id, "Debugger");
88    }
89    return 0;
90}
91
92int DebuggerClient::BacktrackCommand()
93{
94    return 0;
95}
96
97int DebuggerClient::DeleteCommand()
98{
99    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
100    uint32_t id = session->GetMessageId();
101
102    std::unique_ptr<PtJson> request = PtJson::CreateObject();
103    request->Add("id", id);
104    request->Add("method", "Debugger.removeBreakpoint");
105
106    std::unique_ptr<PtJson> params = PtJson::CreateObject();
107    std::string breakpointId = breakPointInfoList_.back().url;
108    params->Add("breakpointId", breakpointId.c_str());
109    request->Add("params", params);
110
111    std::string message = request->Stringify();
112    if (session->ClientSendReq(message)) {
113        session->GetDomainManager().SetDomainById(id, "Debugger");
114    }
115    return 0;
116}
117
118int DebuggerClient::DisableCommand()
119{
120    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
121    uint32_t id = session->GetMessageId();
122
123    std::unique_ptr<PtJson> request = PtJson::CreateObject();
124    request->Add("id", id);
125    request->Add("method", "Debugger.disable");
126
127    std::unique_ptr<PtJson> params = PtJson::CreateObject();
128    request->Add("params", params);
129
130    std::string message = request->Stringify();
131    if (session->ClientSendReq(message)) {
132        session->GetDomainManager().SetDomainById(id, "Debugger");
133    }
134    return 0;
135}
136
137int DebuggerClient::DisplayCommand()
138{
139    return 0;
140}
141
142int DebuggerClient::EnableCommand()
143{
144    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
145    uint32_t id = session->GetMessageId();
146
147    std::unique_ptr<PtJson> request = PtJson::CreateObject();
148    request->Add("id", id);
149    request->Add("method", "Debugger.enable");
150
151    std::unique_ptr<PtJson> params = PtJson::CreateObject();
152    request->Add("params", params);
153
154    std::string message = request->Stringify();
155    if (session->ClientSendReq(message)) {
156        session->GetDomainManager().SetDomainById(id, "Debugger");
157    }
158    return 0;
159}
160
161int DebuggerClient::FinishCommand()
162{
163    return 0;
164}
165
166int DebuggerClient::FrameCommand()
167{
168    return 0;
169}
170
171int DebuggerClient::IgnoreCommand()
172{
173    return 0;
174}
175
176int DebuggerClient::InfobreakpointsCommand()
177{
178    return 0;
179}
180
181int DebuggerClient::InfosourceCommand()
182{
183    return 0;
184}
185
186int DebuggerClient::JumpCommand()
187{
188    return 0;
189}
190
191int DebuggerClient::NextCommand()
192{
193    return 0;
194}
195
196int DebuggerClient::ListCommand()
197{
198    return 0;
199}
200
201int DebuggerClient::PtypeCommand()
202{
203    return 0;
204}
205
206int DebuggerClient::RunCommand()
207{
208    return 0;
209}
210
211int DebuggerClient::SetvarCommand()
212{
213    return 0;
214}
215
216int DebuggerClient::StepCommand()
217{
218    return 0;
219}
220
221int DebuggerClient::UndisplayCommand()
222{
223    return 0;
224}
225
226int DebuggerClient::WatchCommand()
227{
228    return 0;
229}
230
231int DebuggerClient::ResumeCommand()
232{
233    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
234    uint32_t id = session->GetMessageId();
235
236    std::unique_ptr<PtJson> request = PtJson::CreateObject();
237    request->Add("id", id);
238    request->Add("method", "Debugger.resume");
239
240    std::unique_ptr<PtJson> params = PtJson::CreateObject();
241    request->Add("params", params);
242
243    std::string message = request->Stringify();
244    if (session->ClientSendReq(message)) {
245        session->GetDomainManager().SetDomainById(id, "Debugger");
246    }
247    return 0;
248}
249
250int DebuggerClient::StepIntoCommand()
251{
252    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
253    uint32_t id = session->GetMessageId();
254
255    std::unique_ptr<PtJson> request = PtJson::CreateObject();
256    request->Add("id", id);
257    request->Add("method", "Debugger.stepInto");
258
259    std::unique_ptr<PtJson> params = PtJson::CreateObject();
260    request->Add("params", params);
261
262    std::string message = request->Stringify();
263    if (session->ClientSendReq(message)) {
264        session->GetDomainManager().SetDomainById(id, "Debugger");
265    }
266    return 0;
267}
268
269int DebuggerClient::StepOutCommand()
270{
271    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
272    uint32_t id = session->GetMessageId();
273
274    std::unique_ptr<PtJson> request = PtJson::CreateObject();
275    request->Add("id", id);
276    request->Add("method", "Debugger.stepOut");
277
278    std::unique_ptr<PtJson> params = PtJson::CreateObject();
279    request->Add("params", params);
280
281    std::string message = request->Stringify();
282    if (session->ClientSendReq(message)) {
283        session->GetDomainManager().SetDomainById(id, "Debugger");
284    }
285    return 0;
286}
287
288int DebuggerClient::StepOverCommand()
289{
290    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
291    uint32_t id = session->GetMessageId();
292
293    std::unique_ptr<PtJson> request = PtJson::CreateObject();
294    request->Add("id", id);
295    request->Add("method", "Debugger.stepOver");
296
297    std::unique_ptr<PtJson> params = PtJson::CreateObject();
298    request->Add("params", params);
299
300    std::string message = request->Stringify();
301    if (session->ClientSendReq(message)) {
302        session->GetDomainManager().SetDomainById(id, "Debugger");
303    }
304    return 0;
305}
306
307void DebuggerClient::AddBreakPointInfo(const std::string& url, const int& lineNumber, const int& columnNumber)
308{
309    BreakPointInfo breakPointInfo;
310    breakPointInfo.url = url;
311    breakPointInfo.lineNumber = lineNumber - 1;
312    breakPointInfo.columnNumber = columnNumber;
313    breakPointInfoList_.emplace_back(breakPointInfo);
314}
315
316void DebuggerClient::RecvReply(std::unique_ptr<PtJson> json)
317{
318    if (json == nullptr) {
319        LOGE("arkdb: json parse error");
320        return;
321    }
322
323    if (!json->IsObject()) {
324        LOGE("arkdb: json parse format error");
325        json->ReleaseRoot();
326        return;
327    }
328
329    std::string wholeMethod;
330    std::string method;
331    Result ret = json->GetString("method", &wholeMethod);
332    if (ret != Result::SUCCESS) {
333        LOGE("arkdb: find method error");
334    }
335
336    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
337    SourceManager &sourceManager = session->GetSourceManager();
338    WatchManager &watchManager = session->GetWatchManager();
339
340    std::string::size_type length = wholeMethod.length();
341    std::string::size_type indexPoint = 0;
342    indexPoint = wholeMethod.find_first_of('.', 0);
343    method = wholeMethod.substr(indexPoint + 1, length);
344    if (method == "paused") {
345        PausedReply(std::move(json));
346        return;
347    } else if (method == "scriptParsed") {
348        sourceManager.EnableReply(std::move(json));
349        return;
350    } else if (method == "resumed") {
351        watchManager.DebugFalseState();
352        return;
353    } else {
354        LOGI("arkdb: Debugger reply is: %{public}s", json->Stringify().c_str());
355    }
356    handleResponse(std::move(json));
357}
358
359void DebuggerClient::PausedReply(const std::unique_ptr<PtJson> json)
360{
361    if (json == nullptr) {
362        LOGE("arkdb: json parse error");
363        return;
364    }
365
366    if (!json->IsObject()) {
367        LOGE("arkdb: json parse format error");
368        json->ReleaseRoot();
369        return;
370    }
371
372    std::unique_ptr<PtJson> params;
373    Result ret = json->GetObject("params", &params);
374    if (ret != Result::SUCCESS) {
375        LOGE("arkdb: find params error");
376        return;
377    }
378
379    std::unique_ptr<PtJson> callFrames;
380    ret = params->GetArray("callFrames", &callFrames);
381    if (ret != Result::SUCCESS) {
382        LOGE("arkdb: find callFrames error");
383        return;
384    }
385
386    std::map<int32_t, std::unique_ptr<CallFrame>> data;
387    for (int32_t i = 0; i < callFrames->GetSize(); i++) {
388        std::unique_ptr<CallFrame> callFrameInfo = CallFrame::Create(*(callFrames->Get(i)));
389        data.emplace(i + 1, std::move(callFrameInfo));
390    }
391
392    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
393    StackManager& stackManager = session->GetStackManager();
394    SourceManager &sourceManager = session->GetSourceManager();
395    WatchManager &watchManager = session->GetWatchManager();
396    stackManager.ClearCallFrame();
397    stackManager.SetCallFrames(std::move(data));
398    sourceManager.GetDebugSources(callFrames->Get(0));
399    watchManager.RequestWatchInfo(callFrames->Get(0));
400    watchManager.DebugTrueState();
401}
402
403void DebuggerClient::handleResponse(std::unique_ptr<PtJson> json)
404{
405    Session *session = SessionManager::getInstance().GetSessionById(sessionId_);
406    SourceManager &sourceManager = session->GetSourceManager();
407    WatchManager &watchManager = session->GetWatchManager();
408    BreakPointManager& breakpoint = session->GetBreakPointManager();
409    std::unique_ptr<PtJson> result;
410    Result ret = json->GetObject("result", &result);
411    if (ret != Result::SUCCESS) {
412        LOGE("arkdb: find result error");
413        return;
414    }
415    int32_t id;
416    ret = json->GetInt("id", &id);
417    if (ret == Result::SUCCESS) {
418        std::string scriptSource;
419        ret = result->GetString("scriptSource", &scriptSource);
420        if (ret == Result::SUCCESS) {
421            sourceManager.SetFileSource(id, scriptSource);
422            return;
423        }
424    }
425    std::string breakpointId;
426    ret = result->GetString("breakpointId", &breakpointId);
427    if (ret == Result::SUCCESS) {
428        breakpoint.Createbreaklocation(std::move(json));
429        return;
430    }
431    if (watchManager.HandleWatchResult(std::move(json), id)) {
432        return;
433    }
434    return;
435}
436} // OHOS::ArkCompiler::Toolchain