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#ifndef ECMASCRIPT_TOOLING_AGENT_DEBUGGER_IMPL_H
17#define ECMASCRIPT_TOOLING_AGENT_DEBUGGER_IMPL_H
18
19#include "agent/runtime_impl.h"
20#include "backend/js_pt_hooks.h"
21#include "tooling/base/pt_params.h"
22#include "backend/js_single_stepper.h"
23#include "dispatcher.h"
24
25#include "ecmascript/debugger/js_debugger_manager.h"
26#include "ecmascript/debugger/js_pt_method.h"
27#include "libpandabase/macros.h"
28
29namespace panda::ecmascript::tooling {
30namespace test {
31class TestHooks;
32}  // namespace test
33
34enum class DebuggerState { DISABLED, ENABLED, PAUSED };
35class DebuggerImpl final {
36public:
37    DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime);
38    ~DebuggerImpl();
39
40    // event
41    bool NotifyScriptParsed(const std::string &fileName,
42                            std::string_view entryPoint = "func_main_0");
43    bool SendableScriptParsed(const std::string &fileName, const std::string &url,
44                              const std::string &source, const std::string &recordName);
45    bool CheckScriptParsed(const std::string &fileName);
46    bool SendableMethodEntry(JSHandle<Method> method);
47    bool MatchUrlAndFileName(const std::string &url, const std::string &fileName);
48    bool NotifySingleStep(const JSPtLocation &location);
49    void NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason);
50    void GeneratePausedInfo(PauseReason reason,
51                           std::vector<std::string> &hitBreakpoints,
52                           const Local<JSValueRef> &exception);
53    bool NotifyNativeOut();
54    void NotifyHandleProtocolCommand();
55    void NotifyNativeCalling(const void *nativeAddress);
56    void NotifyNativeReturn(const void *nativeAddress);
57    void NotifyReturnNative();
58    bool IsUserCode(const void *nativeAddress);
59    void SetDebuggerState(DebuggerState debuggerState);
60    void SetNativeOutPause(bool nativeOutPause);
61    void AddBreakpointDetail(const std::string &url, int32_t lineNumber,
62        std::string *outId, std::vector<std::unique_ptr<Location>> *outLocations);
63
64    DispatchResponse ContinueToLocation(const ContinueToLocationParams &params);
65    DispatchResponse Enable(const EnableParams &params, UniqueDebuggerId *id);
66    DispatchResponse Disable();
67    DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams &params,
68                                         std::unique_ptr<RemoteObject> *result);
69    DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams &params,
70                                            std::vector<std::unique_ptr<BreakLocation>> *outLocations);
71    DispatchResponse GetScriptSource(const GetScriptSourceParams &params, std::string *source);
72    DispatchResponse Pause();
73    DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams &params);
74    DispatchResponse RemoveBreakpointsByUrl(const RemoveBreakpointsByUrlParams &params);
75    DispatchResponse Resume(const ResumeParams &params);
76    DispatchResponse SetAsyncCallStackDepth();
77    DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams &params, std::string *outId,
78                                        std::vector<std::unique_ptr<Location>> *outLocations,
79                                        bool isSmartBreakpoint = false);
80    DispatchResponse SetBreakpointsActive(const SetBreakpointsActiveParams &params);
81    DispatchResponse GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams &params,
82        std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations);
83    DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams &params);
84    DispatchResponse SetSkipAllPauses(const SetSkipAllPausesParams &params);
85    DispatchResponse SetNativeRange(const SetNativeRangeParams &params);
86    DispatchResponse ResetSingleStepper(const ResetSingleStepperParams &params);
87    DispatchResponse StepInto(const StepIntoParams &params);
88    DispatchResponse SmartStepInto(const SmartStepIntoParams &params);
89    DispatchResponse StepOut();
90    DispatchResponse StepOver(const StepOverParams &params);
91    DispatchResponse SetBlackboxPatterns();
92    DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams &params);
93    DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams &params);
94    DispatchResponse DropFrame(const DropFrameParams &params);
95    DispatchResponse ClientDisconnect();
96    DispatchResponse CallFunctionOn(
97            const CallFunctionOnParams &params,
98            std::unique_ptr<RemoteObject> *outRemoteObject,
99            std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails);
100
101    /**
102     * @brief: match first script and callback
103     *
104     * @return: true means matched and callback execute success
105     */
106    template<class Callback>
107    bool MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const
108    {
109        for (const auto &script : scripts_) {
110            std::string value;
111            switch (type) {
112                case ScriptMatchType::URL: {
113                    value = script.second->GetUrl();
114                    break;
115                }
116                case ScriptMatchType::FILE_NAME: {
117                    value = script.second->GetFileName();
118                    break;
119                }
120                case ScriptMatchType::HASH: {
121                    value = script.second->GetHash();
122                    break;
123                }
124                default: {
125                    return false;
126                }
127            }
128            if (matchStr == value) {
129                return cb(script.second.get());
130            }
131        }
132        return false;
133    }
134
135    std::vector<PtScript *> MatchAllScripts(const std::string &url) const
136    {
137        std::vector<PtScript *> result;
138        for (const auto &script : scripts_) {
139            if (url == script.second->GetUrl()) {
140                result.push_back(script.second.get());
141            }
142        }
143        return result;
144    }
145    bool GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames, bool getScope);
146
147    class DispatcherImpl final : public DispatcherBase {
148    public:
149        DispatcherImpl(ProtocolChannel *channel, std::unique_ptr<DebuggerImpl> debugger)
150            : DispatcherBase(channel), debugger_(std::move(debugger)) {}
151        ~DispatcherImpl() override = default;
152
153        void ContinueToLocation(const DispatchRequest &request);
154        void Dispatch(const DispatchRequest &request) override;
155        void Enable(const DispatchRequest &request);
156        void Disable(const DispatchRequest &request);
157        void EvaluateOnCallFrame(const DispatchRequest &request);
158        void GetPossibleBreakpoints(const DispatchRequest &request);
159        void GetScriptSource(const DispatchRequest &request);
160        void Pause(const DispatchRequest &request);
161        void RemoveBreakpoint(const DispatchRequest &request);
162        void RemoveBreakpointsByUrl(const DispatchRequest &request);
163        void Resume(const DispatchRequest &request);
164        void SetAsyncCallStackDepth(const DispatchRequest &request);
165        void SetBreakpointByUrl(const DispatchRequest &request);
166        void SetBreakpointsActive(const DispatchRequest &request);
167        void SetPauseOnExceptions(const DispatchRequest &request);
168        void SetSkipAllPauses(const DispatchRequest &request);
169        void SetNativeRange(const DispatchRequest &request);
170        void ResetSingleStepper(const DispatchRequest &request);
171        void StepInto(const DispatchRequest &request);
172        void SmartStepInto(const DispatchRequest &request);
173        void StepOut(const DispatchRequest &request);
174        void StepOver(const DispatchRequest &request);
175        void SetMixedDebugEnabled(const DispatchRequest &request);
176        void SetBlackboxPatterns(const DispatchRequest &request);
177        void ReplyNativeCalling(const DispatchRequest &request);
178        void GetPossibleAndSetBreakpointByUrl(const DispatchRequest &request);
179        void DropFrame(const DispatchRequest &request);
180        void ClientDisconnect(const DispatchRequest &request);
181        void CallFunctionOn(const DispatchRequest &request);
182
183        enum class Method {
184            CONTINUE_TO_LOCATION,
185            ENABLE,
186            DISABLE,
187            EVALUATE_ON_CALL_FRAME,
188            GET_POSSIBLE_BREAKPOINTS,
189            GET_SCRIPT_SOURCE,
190            PAUSE,
191            REMOVE_BREAKPOINT,
192            REMOVE_BREAKPOINTS_BY_URL,
193            RESUME,
194            SET_ASYNC_CALL_STACK_DEPTH,
195            SET_BREAKPOINT_BY_URL,
196            SET_BREAKPOINTS_ACTIVE,
197            SET_PAUSE_ON_EXCEPTIONS,
198            SET_SKIP_ALL_PAUSES,
199            STEP_INTO,
200            SMART_STEP_INTO,
201            STEP_OUT,
202            STEP_OVER,
203            SET_MIXED_DEBUG_ENABLED,
204            SET_BLACKBOX_PATTERNS,
205            REPLY_NATIVE_CALLING,
206            GET_POSSIBLE_AND_SET_BREAKPOINT_BY_URL,
207            DROP_FRAME,
208            SET_NATIVE_RANGE,
209            RESET_SINGLE_STEPPER,
210            CLIENT_DISCONNECT,
211            CALL_FUNCTION_ON,
212            UNKNOWN
213        };
214        Method GetMethodEnum(const std::string& method);
215
216    private:
217        NO_COPY_SEMANTIC(DispatcherImpl);
218        NO_MOVE_SEMANTIC(DispatcherImpl);
219
220        std::unique_ptr<DebuggerImpl> debugger_ {};
221    };
222
223private:
224    NO_COPY_SEMANTIC(DebuggerImpl);
225    NO_MOVE_SEMANTIC(DebuggerImpl);
226
227    std::string Trim(const std::string &str);
228    DebugInfoExtractor *GetExtractor(const JSPandaFile *jsPandaFile);
229    std::vector<DebugInfoExtractor *> GetExtractors(const std::string &url);
230    std::optional<std::string> CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression,
231        std::unique_ptr<RemoteObject> *result);
232    bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId, bool getScope);
233    void GenerateScopeChains(bool getScope, const FrameHandler *frameHandler, const JSPandaFile *jsPandaFile,
234        std::vector<std::unique_ptr<Scope>> &scopeChain, std::unique_ptr<RemoteObject> &thisObj);
235    void SaveCallFrameHandler(const FrameHandler *frameHandler);
236    std::unique_ptr<Scope> GetLocalScopeChain(const FrameHandler *frameHandler,
237        std::unique_ptr<RemoteObject> *thisObj);
238    std::unique_ptr<Scope> GetModuleScopeChain(const FrameHandler *frameHandler);
239    std::unique_ptr<Scope> GetGlobalScopeChain(const FrameHandler *frameHandler);
240    std::vector<std::unique_ptr<Scope>> GetClosureScopeChains(const FrameHandler *frameHandler,
241        std::unique_ptr<RemoteObject> *thisObj);
242    void GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId,
243        const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj);
244    void CleanUpOnPaused();
245    void CleanUpRuntimeProperties();
246    void UpdateScopeObject(const FrameHandler *frameHandler, std::string_view varName,
247        Local<JSValueRef> newVal, const std::string& scope);
248    void ClearSingleStepper();
249    Local<JSValueRef> ConvertToLocal(const std::string &varValue);
250    bool DecodeAndCheckBase64(const std::string &src, std::vector<uint8_t> &dest);
251    bool IsSkipLine(const JSPtLocation &location);
252    bool CheckPauseOnException();
253    bool IsWithinVariableScope(const LocalVariableInfo &localVariableInfo, uint32_t bcOffset);
254    bool ProcessSingleBreakpoint(const BreakpointInfo &breakpoint,
255        std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations);
256    bool IsVariableSkipped(const std::string &varName);
257    Local<FunctionRef> CheckAndGenerateCondFunc(const std::optional<std::string> &condition);
258    void InitializeExtendedProtocolsList();
259
260    const std::unordered_set<std::string> &GetRecordName(const std::string &url)
261    {
262        static const std::unordered_set<std::string> recordName;
263        auto iter = recordNames_.find(url);
264        if (iter != recordNames_.end()) {
265            return iter->second;
266        }
267        return recordName;
268    }
269
270    class Frontend {
271    public:
272        explicit Frontend(ProtocolChannel *channel) : channel_(channel) {}
273        ~Frontend() = default;
274
275        void BreakpointResolved(const EcmaVM *vm);
276        void Paused(const EcmaVM *vm, const tooling::Paused &paused);
277        void Resumed(const EcmaVM *vm);
278        void NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling);
279        void MixedStack(const EcmaVM *vm, const tooling::MixedStack &mixedStack);
280        void ScriptFailedToParse(const EcmaVM *vm);
281        void ScriptParsed(const EcmaVM *vm, const PtScript &script);
282        void WaitForDebugger(const EcmaVM *vm);
283        void RunIfWaitingForDebugger(const EcmaVM *vm);
284
285    private:
286        bool AllowNotify(const EcmaVM *vm) const;
287
288        ProtocolChannel *channel_ {nullptr};
289    };
290
291    const EcmaVM *vm_ {nullptr};
292    Frontend frontend_;
293
294    RuntimeImpl *runtime_ {nullptr};
295    std::unique_ptr<JSPtHooks> hooks_ {nullptr};
296    JSDebugger *jsDebugger_ {nullptr};
297
298    std::unordered_map<std::string, std::unordered_set<std::string>> recordNames_ {};
299    std::unordered_map<std::string, std::unordered_set<std::string>> urlFileNameMap_ {};
300    std::unordered_map<ScriptId, std::unique_ptr<PtScript>> scripts_ {};
301    PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE};
302    DebuggerState debuggerState_ {DebuggerState::ENABLED};
303    bool pauseOnNextByteCode_ {false};
304    bool breakpointsState_ {true};
305    bool skipAllPausess_ {false};
306    bool mixStackEnabled_ {false};
307    std::unique_ptr<SingleStepper> singleStepper_ {nullptr};
308    Location location_ {};
309
310    std::unique_ptr<SingleStepper> nativeOut_ {nullptr};
311    std::vector<void *>  nativePointer_;
312
313    bool nativeOutPause_ {false};
314    std::vector<NativeRange> nativeRanges_ {};
315    std::unordered_map<JSTaggedType *, std::unordered_map<std::string,
316        std::vector<RemoteObjectId>>> scopeObjects_ {};
317    std::vector<std::shared_ptr<FrameHandler>> callFrameHandlers_;
318    JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr};
319    JsDebuggerManager::SingleStepperFunc stepperFunc_ {nullptr};
320    JsDebuggerManager::ReturnNativeFunc returnNative_ {nullptr};
321    std::vector<std::string> debuggerExtendedProtocols_ {};
322
323    friend class JSPtHooks;
324    friend class test::TestHooks;
325    friend class DebuggerImplFriendTest;
326};
327}  // namespace panda::ecmascript::tooling
328#endif