1/*
2 * Copyright (c) 2021 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 "dispatcher.h"
17
18#include "agent/debugger_impl.h"
19#include "agent/runtime_impl.h"
20#ifdef ECMASCRIPT_SUPPORT_HEAPPROFILER
21#include "agent/heapprofiler_impl.h"
22#endif
23#ifdef ECMASCRIPT_SUPPORT_CPUPROFILER
24#include "agent/profiler_impl.h"
25#endif
26#include "agent/tracing_impl.h"
27#include "agent/css_impl.h"
28#include "agent/dom_impl.h"
29#include "agent/overlay_impl.h"
30#include "agent/page_impl.h"
31#include "agent/target_impl.h"
32#include "protocol_channel.h"
33
34namespace panda::ecmascript::tooling {
35DispatchRequest::DispatchRequest(const std::string &message)
36{
37    std::unique_ptr<PtJson> json = PtJson::Parse(message);
38    if (json == nullptr) {
39        JsonParseError();
40        return;
41    }
42    if (!json->IsObject()) {
43        JsonFormatError(json);
44        return;
45    }
46
47    Result ret;
48    int32_t callId;
49    ret = json->GetInt("id", &callId);
50    if (ret != Result::SUCCESS) {
51        code_ = RequestCode::PARSE_ID_ERROR;
52        LOG_DEBUGGER(ERROR) << "parse id error";
53        return;
54    }
55    callId_ = callId;
56
57    std::string wholeMethod;
58    ret = json->GetString("method", &wholeMethod);
59    if (ret != Result::SUCCESS || wholeMethod.empty()) {
60        code_ = RequestCode::PARSE_METHOD_ERROR;
61        LOG_DEBUGGER(ERROR) << "parse method error";
62        return;
63    }
64    std::string::size_type length = wholeMethod.length();
65    std::string::size_type indexPoint = wholeMethod.find_first_of('.', 0);
66    if (indexPoint == std::string::npos || indexPoint == 0 || indexPoint == length - 1) {
67        code_ = RequestCode::METHOD_FORMAT_ERROR;
68        LOG_DEBUGGER(ERROR) << "method format error: " << wholeMethod;
69        return;
70    }
71    domain_ = wholeMethod.substr(0, indexPoint);
72    method_ = wholeMethod.substr(indexPoint + 1, length);
73
74    LOG_DEBUGGER(DEBUG) << "id: " << callId_ << ", domain: " << domain_ << ", method: " << method_;
75
76    std::unique_ptr<PtJson> params;
77    ret = json->GetObject("params", &params);
78    if (ret == Result::NOT_EXIST) {
79        return;
80    }
81    if (ret == Result::TYPE_ERROR) {
82        code_ = RequestCode::PARAMS_FORMAT_ERROR;
83        LOG_DEBUGGER(ERROR) << "params format error";
84        return;
85    }
86    params_ = std::move(params);
87}
88
89DispatchRequest::~DispatchRequest()
90{
91    params_->ReleaseRoot();
92}
93
94DispatchResponse DispatchResponse::Create(ResponseCode code, const std::string &msg)
95{
96    DispatchResponse response;
97    response.code_ = code;
98    response.errorMsg_ = msg;
99    return response;
100}
101
102DispatchResponse DispatchResponse::Create(std::optional<std::string> error)
103{
104    DispatchResponse response;
105    if (error.has_value()) {
106        response.code_ = ResponseCode::NOK;
107        response.errorMsg_ = error.value();
108    }
109    return response;
110}
111
112DispatchResponse DispatchResponse::Ok()
113{
114    return DispatchResponse();
115}
116
117DispatchResponse DispatchResponse::Fail(const std::string &message)
118{
119    DispatchResponse response;
120    response.code_ = ResponseCode::NOK;
121    response.errorMsg_ = message;
122    return response;
123}
124
125void DispatcherBase::SendResponse(const DispatchRequest &request, const DispatchResponse &response,
126                                  const PtBaseReturns &result)
127{
128    if (channel_ != nullptr) {
129        channel_->SendResponse(request, response, result);
130    }
131}
132
133Dispatcher::Dispatcher(const EcmaVM *vm, ProtocolChannel *channel)
134{
135    // profiler
136#ifdef ECMASCRIPT_SUPPORT_CPUPROFILER
137    auto profiler = std::make_unique<ProfilerImpl>(vm, channel);
138    dispatchers_["Profiler"] =
139        std::make_unique<ProfilerImpl::DispatcherImpl>(channel, std::move(profiler));
140#endif
141#ifdef ECMASCRIPT_SUPPORT_HEAPPROFILER
142    auto heapProfiler = std::make_unique<HeapProfilerImpl>(vm, channel);
143    dispatchers_["HeapProfiler"] =
144        std::make_unique<HeapProfilerImpl::DispatcherImpl>(channel, std::move(heapProfiler));
145#endif
146#ifdef ECMASCRIPT_SUPPORT_TRACING
147    auto tracing = std::make_unique<TracingImpl>(vm, channel);
148    dispatchers_["Tracing"] =
149        std::make_unique<TracingImpl::DispatcherImpl>(channel, std::move(tracing));
150#endif
151
152    // debugger
153    auto runtime = std::make_unique<RuntimeImpl>(vm, channel);
154    auto debugger = std::make_unique<DebuggerImpl>(vm, channel, runtime.get());
155    dispatchers_["Runtime"] =
156        std::make_unique<RuntimeImpl::DispatcherImpl>(channel, std::move(runtime));
157    dispatchers_["Debugger"] =
158        std::make_unique<DebuggerImpl::DispatcherImpl>(channel, std::move(debugger));
159
160    auto dom = std::make_unique<DomImpl>();
161    dispatchers_["DOM"] =
162        std::make_unique<DomImpl::DispatcherImpl>(channel, std::move(dom));
163
164    auto css = std::make_unique<CssImpl>();
165    dispatchers_["CSS"] =
166        std::make_unique<CssImpl::DispatcherImpl>(channel, std::move(css));
167
168    auto overlay = std::make_unique<OverlayImpl>();
169    dispatchers_["Overlay"] =
170        std::make_unique<OverlayImpl::DispatcherImpl>(channel, std::move(overlay));
171
172    auto target = std::make_unique<TargetImpl>();
173    dispatchers_["Target"] =
174        std::make_unique<TargetImpl::DispatcherImpl>(channel, std::move(target));
175
176    auto page = std::make_unique<PageImpl>();
177    dispatchers_["Page"] =
178        std::make_unique<PageImpl::DispatcherImpl>(channel, std::move(page));
179}
180
181void Dispatcher::Dispatch(const DispatchRequest &request)
182{
183    if (!request.IsValid()) {
184        LOG_DEBUGGER(ERROR) << "Unknown request";
185        return;
186    }
187    const std::string &domain = request.GetDomain();
188    auto dispatcher = dispatchers_.find(domain);
189    if (dispatcher != dispatchers_.end()) {
190        dispatcher->second->Dispatch(request);
191    } else {
192        if (domain == "Test") {
193            if (request.GetMethod() == "fail") {
194                LOG_DEBUGGER(FATAL) << "Test fail";
195                UNREACHABLE();
196            }
197            LOG_DEBUGGER(INFO) << "Test success";
198        } else {
199            LOG_DEBUGGER(ERROR) << "unknown domain: " << domain;
200        }
201    }
202}
203}  // namespace panda::ecmascript::tooling
204