• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
29 namespace panda::ecmascript::tooling {
30 namespace test {
31 class TestHooks;
32 }  // namespace test
33 class DebuggerImpl final {
34 public:
35     DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime);
36     ~DebuggerImpl();
37 
38     // event
39     bool NotifyScriptParsed(ScriptId scriptId, const std::string &fileName,
40                             std::string_view entryPoint = "func_main_0");
41     bool NotifySingleStep(const JSPtLocation &location);
42     void NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason);
43     void NotifyPendingJobEntry();
44     void NotifyHandleProtocolCommand();
45     void NotifyNativeCalling(const void *nativeAddress);
46 
47     DispatchResponse Enable(const EnableParams &params, UniqueDebuggerId *id);
48     DispatchResponse Disable();
49     DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams &params,
50                                          std::unique_ptr<RemoteObject> *result);
51     DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams &params,
52                                             std::vector<std::unique_ptr<BreakLocation>> *outLocations);
53     DispatchResponse GetScriptSource(const GetScriptSourceParams &params, std::string *source);
54     DispatchResponse Pause();
55     DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams &params);
56     DispatchResponse Resume(const ResumeParams &params);
57     DispatchResponse SetAsyncCallStackDepth();
58     DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams &params, std::string *outId,
59                                         std::vector<std::unique_ptr<Location>> *outLocations);
60     DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams &params);
61     DispatchResponse StepInto(const StepIntoParams &params);
62     DispatchResponse StepOut();
63     DispatchResponse StepOver(const StepOverParams &params);
64     DispatchResponse SetBlackboxPatterns();
65     DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams &params);
66     DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams &params);
67 
68     /**
69      * @brief: match first script and callback
70      *
71      * @return: true means matched and callback execute success
72      */
73     template<class Callback>
MatchScripts(const Callback & cb,const std::string & matchStr,ScriptMatchType type)74     bool MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const
75     {
76         for (const auto &script : scripts_) {
77             std::string value;
78             switch (type) {
79                 case ScriptMatchType::URL: {
80                     value = script.second->GetUrl();
81                     break;
82                 }
83                 case ScriptMatchType::FILE_NAME: {
84                     value = script.second->GetFileName();
85                     break;
86                 }
87                 case ScriptMatchType::HASH: {
88                     value = script.second->GetHash();
89                     break;
90                 }
91                 default: {
92                     return false;
93                 }
94             }
95             if (matchStr == value) {
96                 return cb(script.second.get());
97             }
98         }
99         return false;
100     }
101     bool GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames);
102 
103     class DispatcherImpl final : public DispatcherBase {
104     public:
DispatcherImpl(ProtocolChannel * channel,std::unique_ptr<DebuggerImpl> debugger)105         DispatcherImpl(ProtocolChannel *channel, std::unique_ptr<DebuggerImpl> debugger)
106             : DispatcherBase(channel), debugger_(std::move(debugger)) {}
107         ~DispatcherImpl() override = default;
108 
109         void Dispatch(const DispatchRequest &request) override;
110         void Enable(const DispatchRequest &request);
111         void Disable(const DispatchRequest &request);
112         void EvaluateOnCallFrame(const DispatchRequest &request);
113         void GetPossibleBreakpoints(const DispatchRequest &request);
114         void GetScriptSource(const DispatchRequest &request);
115         void Pause(const DispatchRequest &request);
116         void RemoveBreakpoint(const DispatchRequest &request);
117         void Resume(const DispatchRequest &request);
118         void SetAsyncCallStackDepth(const DispatchRequest &request);
119         void SetBreakpointByUrl(const DispatchRequest &request);
120         void SetPauseOnExceptions(const DispatchRequest &request);
121         void StepInto(const DispatchRequest &request);
122         void StepOut(const DispatchRequest &request);
123         void StepOver(const DispatchRequest &request);
124         void SetMixedDebugEnabled(const DispatchRequest &request);
125         void SetBlackboxPatterns(const DispatchRequest &request);
126         void ReplyNativeCalling(const DispatchRequest &request);
127 
128     private:
129         NO_COPY_SEMANTIC(DispatcherImpl);
130         NO_MOVE_SEMANTIC(DispatcherImpl);
131 
132         using AgentHandler = void (DebuggerImpl::DispatcherImpl::*)(const DispatchRequest &request);
133         std::unique_ptr<DebuggerImpl> debugger_ {};
134     };
135 
136 private:
137     NO_COPY_SEMANTIC(DebuggerImpl);
138     NO_MOVE_SEMANTIC(DebuggerImpl);
139 
140     std::string Trim(const std::string &str);
141     DebugInfoExtractor *GetExtractor(const JSPandaFile *jsPandaFile);
142     DebugInfoExtractor *GetExtractor(const std::string &url);
143     std::optional<std::string> CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression,
144                                              std::unique_ptr<RemoteObject> *result);
145     bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId);
146     void SaveCallFrameHandler(const FrameHandler *frameHandler);
147     std::unique_ptr<Scope> GetLocalScopeChain(const FrameHandler *frameHandler,
148         std::unique_ptr<RemoteObject> *thisObj);
149     std::unique_ptr<Scope> GetModuleScopeChain();
150     std::unique_ptr<Scope> GetGlobalScopeChain();
151     void GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId,
152         const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj);
153     void CleanUpOnPaused();
154     void UpdateScopeObject(const FrameHandler *frameHandler, std::string_view varName, Local<JSValueRef> newVal);
155     void ClearSingleStepper();
156     Local<JSValueRef> ConvertToLocal(const std::string &varValue);
157     bool DecodeAndCheckBase64(const std::string &src, std::string &dest);
158     bool IsSkipLine(const JSPtLocation &location);
159     bool CheckPauseOnException();
160 
161     class Frontend {
162     public:
Frontend(ProtocolChannel * channel)163         explicit Frontend(ProtocolChannel *channel) : channel_(channel) {}
164         ~Frontend() = default;
165 
166         void BreakpointResolved(const EcmaVM *vm);
167         void Paused(const EcmaVM *vm, const tooling::Paused &paused);
168         void Resumed(const EcmaVM *vm);
169         void NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling);
170         void ScriptFailedToParse(const EcmaVM *vm);
171         void ScriptParsed(const EcmaVM *vm, const PtScript &script);
172         void WaitForDebugger(const EcmaVM *vm);
173         void RunIfWaitingForDebugger(const EcmaVM *vm);
174 
175     private:
176         bool AllowNotify(const EcmaVM *vm) const;
177 
178         ProtocolChannel *channel_ {nullptr};
179     };
180 
181     const EcmaVM *vm_ {nullptr};
182     Frontend frontend_;
183 
184     RuntimeImpl *runtime_ {nullptr};
185     std::unique_ptr<JSPtHooks> hooks_ {nullptr};
186     JSDebugger *jsDebugger_ {nullptr};
187 
188     std::unordered_map<std::string, DebugInfoExtractor *> extractors_ {};
189     std::unordered_map<ScriptId, std::unique_ptr<PtScript>> scripts_ {};
190     PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE};
191     bool pauseOnNextByteCode_ {false};
192     std::unique_ptr<SingleStepper> singleStepper_ {nullptr};
193 
194     std::unordered_map<JSTaggedType *, RemoteObjectId> scopeObjects_ {};
195     std::vector<std::shared_ptr<FrameHandler>> callFrameHandlers_;
196     JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr};
197     JsDebuggerManager::SingleStepperFunc stepperFunc_ {nullptr};
198 
199     friend class JSPtHooks;
200     friend class test::TestHooks;
201 };
202 }  // namespace panda::ecmascript::tooling
203 #endif