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 
29 namespace panda::ecmascript::tooling {
30 namespace test {
31 class TestHooks;
32 }  // namespace test
33 
34 enum class DebuggerState { DISABLED, ENABLED, PAUSED };
35 class DebuggerImpl final {
36 public:
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>
MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const107     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 
MatchAllScripts(const std::string &url) const135     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:
DispatcherImpl(ProtocolChannel *channel, std::unique_ptr<DebuggerImpl> debugger)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 
223 private:
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 
GetRecordName(const std::string &url)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:
Frontend(ProtocolChannel *channel)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