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 ¶ms); 65 DispatchResponse Enable(const EnableParams ¶ms, UniqueDebuggerId *id); 66 DispatchResponse Disable(); 67 DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, 68 std::unique_ptr<RemoteObject> *result); 69 DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, 70 std::vector<std::unique_ptr<BreakLocation>> *outLocations); 71 DispatchResponse GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source); 72 DispatchResponse Pause(); 73 DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams ¶ms); 74 DispatchResponse RemoveBreakpointsByUrl(const RemoveBreakpointsByUrlParams ¶ms); 75 DispatchResponse Resume(const ResumeParams ¶ms); 76 DispatchResponse SetAsyncCallStackDepth(); 77 DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, std::string *outId, 78 std::vector<std::unique_ptr<Location>> *outLocations, 79 bool isSmartBreakpoint = false); 80 DispatchResponse SetBreakpointsActive(const SetBreakpointsActiveParams ¶ms); 81 DispatchResponse GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams ¶ms, 82 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations); 83 DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms); 84 DispatchResponse SetSkipAllPauses(const SetSkipAllPausesParams ¶ms); 85 DispatchResponse SetNativeRange(const SetNativeRangeParams ¶ms); 86 DispatchResponse ResetSingleStepper(const ResetSingleStepperParams ¶ms); 87 DispatchResponse StepInto(const StepIntoParams ¶ms); 88 DispatchResponse SmartStepInto(const SmartStepIntoParams ¶ms); 89 DispatchResponse StepOut(); 90 DispatchResponse StepOver(const StepOverParams ¶ms); 91 DispatchResponse SetBlackboxPatterns(); 92 DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams ¶ms); 93 DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams ¶ms); 94 DispatchResponse DropFrame(const DropFrameParams ¶ms); 95 DispatchResponse ClientDisconnect(); 96 DispatchResponse CallFunctionOn( 97 const CallFunctionOnParams ¶ms, 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