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(ScriptId scriptId, const std::string &fileName, 42 std::string_view entryPoint = "func_main_0"); 43 bool NotifySingleStep(const JSPtLocation &location); 44 void NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason); 45 bool NotifyNativeOut(); 46 void NotifyHandleProtocolCommand(); 47 void NotifyNativeCalling(const void *nativeAddress); 48 void NotifyNativeReturn(const void *nativeAddress); 49 void NotifyReturnNative(); 50 bool IsUserCode(const void *nativeAddress); 51 void SetDebuggerState(DebuggerState debuggerState); 52 53 DispatchResponse ContinueToLocation(const ContinueToLocationParams ¶ms); 54 DispatchResponse Enable(const EnableParams ¶ms, UniqueDebuggerId *id); 55 DispatchResponse Disable(); 56 DispatchResponse EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, 57 std::unique_ptr<RemoteObject> *result); 58 DispatchResponse GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, 59 std::vector<std::unique_ptr<BreakLocation>> *outLocations); 60 DispatchResponse GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source); 61 DispatchResponse Pause(); 62 DispatchResponse RemoveBreakpoint(const RemoveBreakpointParams ¶ms); 63 DispatchResponse Resume(const ResumeParams ¶ms); 64 DispatchResponse SetAsyncCallStackDepth(); 65 DispatchResponse SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, std::string *outId, 66 std::vector<std::unique_ptr<Location>> *outLocations); 67 DispatchResponse SetBreakpointsActive(const SetBreakpointsActiveParams ¶ms); 68 DispatchResponse GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams ¶ms, 69 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations); 70 DispatchResponse SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms); 71 DispatchResponse SetSkipAllPauses(const SetSkipAllPausesParams ¶ms); 72 DispatchResponse SetNativeRange(const SetNativeRangeParams ¶ms); 73 DispatchResponse ResetSingleStepper(const ResetSingleStepperParams ¶ms); 74 DispatchResponse StepInto(const StepIntoParams ¶ms); 75 DispatchResponse StepOut(); 76 DispatchResponse StepOver(const StepOverParams ¶ms); 77 DispatchResponse SetBlackboxPatterns(); 78 DispatchResponse SetMixedDebugEnabled(const SetMixedDebugParams ¶ms); 79 DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams ¶ms); 80 DispatchResponse DropFrame(const DropFrameParams ¶ms); 81 DispatchResponse ClientDisconnect(); 82 DispatchResponse CallFunctionOn( 83 const CallFunctionOnParams ¶ms, 84 std::unique_ptr<RemoteObject> *outRemoteObject, 85 std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails); 86 87 /** 88 * @brief: match first script and callback 89 * 90 * @return: true means matched and callback execute success 91 */ 92 template<class Callback> MatchScripts(const Callback & cb,const std::string & matchStr,ScriptMatchType type)93 bool MatchScripts(const Callback &cb, const std::string &matchStr, ScriptMatchType type) const 94 { 95 for (const auto &script : scripts_) { 96 std::string value; 97 switch (type) { 98 case ScriptMatchType::URL: { 99 value = script.second->GetUrl(); 100 break; 101 } 102 case ScriptMatchType::FILE_NAME: { 103 value = script.second->GetFileName(); 104 break; 105 } 106 case ScriptMatchType::HASH: { 107 value = script.second->GetHash(); 108 break; 109 } 110 default: { 111 return false; 112 } 113 } 114 if (matchStr == value) { 115 return cb(script.second.get()); 116 } 117 } 118 return false; 119 } 120 MatchUrlAndFileName(const std::string & url,const std::string & fileName)121 bool MatchUrlAndFileName(const std::string &url, const std::string &fileName) const 122 { 123 for (const auto &script : scripts_) { 124 if (url == script.second->GetUrl() && fileName == script.second->GetFileName()) { 125 return true; 126 } 127 } 128 return false; 129 } 130 MatchAllScripts(const std::string & url)131 std::vector<PtScript *> MatchAllScripts(const std::string &url) const 132 { 133 std::vector<PtScript *> result; 134 for (const auto &script : scripts_) { 135 if (url == script.second->GetUrl()) { 136 result.push_back(script.second.get()); 137 } 138 } 139 return result; 140 } 141 bool GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames, bool getScope); 142 143 class DispatcherImpl final : public DispatcherBase { 144 public: DispatcherImpl(ProtocolChannel * channel,std::unique_ptr<DebuggerImpl> debugger)145 DispatcherImpl(ProtocolChannel *channel, std::unique_ptr<DebuggerImpl> debugger) 146 : DispatcherBase(channel), debugger_(std::move(debugger)) {} 147 ~DispatcherImpl() override = default; 148 149 void ContinueToLocation(const DispatchRequest &request); 150 void Dispatch(const DispatchRequest &request) override; 151 void Enable(const DispatchRequest &request); 152 void Disable(const DispatchRequest &request); 153 void EvaluateOnCallFrame(const DispatchRequest &request); 154 void GetPossibleBreakpoints(const DispatchRequest &request); 155 void GetScriptSource(const DispatchRequest &request); 156 void Pause(const DispatchRequest &request); 157 void RemoveBreakpoint(const DispatchRequest &request); 158 void Resume(const DispatchRequest &request); 159 void SetAsyncCallStackDepth(const DispatchRequest &request); 160 void SetBreakpointByUrl(const DispatchRequest &request); 161 void SetBreakpointsActive(const DispatchRequest &request); 162 void SetPauseOnExceptions(const DispatchRequest &request); 163 void SetSkipAllPauses(const DispatchRequest &request); 164 void SetNativeRange(const DispatchRequest &request); 165 void ResetSingleStepper(const DispatchRequest &request); 166 void StepInto(const DispatchRequest &request); 167 void StepOut(const DispatchRequest &request); 168 void StepOver(const DispatchRequest &request); 169 void SetMixedDebugEnabled(const DispatchRequest &request); 170 void SetBlackboxPatterns(const DispatchRequest &request); 171 void ReplyNativeCalling(const DispatchRequest &request); 172 void GetPossibleAndSetBreakpointByUrl(const DispatchRequest &request); 173 void DropFrame(const DispatchRequest &request); 174 void ClientDisconnect(const DispatchRequest &request); 175 void CallFunctionOn(const DispatchRequest &request); 176 177 private: 178 NO_COPY_SEMANTIC(DispatcherImpl); 179 NO_MOVE_SEMANTIC(DispatcherImpl); 180 181 using AgentHandler = void (DebuggerImpl::DispatcherImpl::*)(const DispatchRequest &request); 182 std::unique_ptr<DebuggerImpl> debugger_ {}; 183 }; 184 185 private: 186 NO_COPY_SEMANTIC(DebuggerImpl); 187 NO_MOVE_SEMANTIC(DebuggerImpl); 188 189 std::string Trim(const std::string &str); 190 DebugInfoExtractor *GetExtractor(const JSPandaFile *jsPandaFile); 191 std::vector<DebugInfoExtractor *> GetExtractors(const std::string &url); 192 std::optional<std::string> CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, 193 std::unique_ptr<RemoteObject> *result); 194 bool GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, CallFrameId frameId, bool getScope); 195 void SaveCallFrameHandler(const FrameHandler *frameHandler); 196 std::unique_ptr<Scope> GetLocalScopeChain(const FrameHandler *frameHandler, 197 std::unique_ptr<RemoteObject> *thisObj); 198 std::unique_ptr<Scope> GetModuleScopeChain(); 199 std::unique_ptr<Scope> GetGlobalScopeChain(); 200 std::vector<std::unique_ptr<Scope>> GetClosureScopeChains(const FrameHandler *frameHandler, 201 std::unique_ptr<RemoteObject> *thisObj); 202 void GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId, 203 const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj); 204 void CleanUpOnPaused(); 205 void CleanUpRuntimeProperties(); 206 void UpdateScopeObject(const FrameHandler *frameHandler, std::string_view varName, Local<JSValueRef> newVal); 207 void ClearSingleStepper(); 208 Local<JSValueRef> ConvertToLocal(const std::string &varValue); 209 bool DecodeAndCheckBase64(const std::string &src, std::vector<uint8_t> &dest); 210 bool IsSkipLine(const JSPtLocation &location); 211 bool CheckPauseOnException(); 212 bool IsWithinVariableScope(const LocalVariableInfo &localVariableInfo, uint32_t bcOffset); 213 bool ProcessSingleBreakpoint(const BreakpointInfo &breakpoint, 214 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations); 215 bool IsVariableSkipped(const std::string &varName); 216 Local<FunctionRef> CheckAndGenerateCondFunc(const std::optional<std::string> &condition); 217 GetRecordName(const std::string & url)218 const std::unordered_set<std::string> &GetRecordName(const std::string &url) 219 { 220 static const std::unordered_set<std::string> recordName; 221 auto iter = recordNames_.find(url); 222 if (iter != recordNames_.end()) { 223 return iter->second; 224 } 225 return recordName; 226 } 227 228 class Frontend { 229 public: Frontend(ProtocolChannel * channel)230 explicit Frontend(ProtocolChannel *channel) : channel_(channel) {} 231 ~Frontend() = default; 232 233 void BreakpointResolved(const EcmaVM *vm); 234 void Paused(const EcmaVM *vm, const tooling::Paused &paused); 235 void Resumed(const EcmaVM *vm); 236 void NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling); 237 void MixedStack(const EcmaVM *vm, const tooling::MixedStack &mixedStack); 238 void ScriptFailedToParse(const EcmaVM *vm); 239 void ScriptParsed(const EcmaVM *vm, const PtScript &script); 240 void WaitForDebugger(const EcmaVM *vm); 241 void RunIfWaitingForDebugger(const EcmaVM *vm); 242 243 private: 244 bool AllowNotify(const EcmaVM *vm) const; 245 246 ProtocolChannel *channel_ {nullptr}; 247 }; 248 249 const EcmaVM *vm_ {nullptr}; 250 Frontend frontend_; 251 252 RuntimeImpl *runtime_ {nullptr}; 253 std::unique_ptr<JSPtHooks> hooks_ {nullptr}; 254 JSDebugger *jsDebugger_ {nullptr}; 255 256 std::unordered_map<std::string, std::unordered_set<std::string>> recordNames_ {}; 257 std::unordered_map<ScriptId, std::unique_ptr<PtScript>> scripts_ {}; 258 PauseOnExceptionsState pauseOnException_ {PauseOnExceptionsState::NONE}; 259 DebuggerState debuggerState_ {DebuggerState::ENABLED}; 260 bool pauseOnNextByteCode_ {false}; 261 bool breakpointsState_ {true}; 262 bool skipAllPausess_ {false}; 263 bool mixStackEnabled_ {false}; 264 std::unique_ptr<SingleStepper> singleStepper_ {nullptr}; 265 Location location_ {}; 266 267 std::unique_ptr<SingleStepper> nativeOut_ {nullptr}; 268 std::vector<void *> nativePointer_; 269 270 bool nativeOutPause_ {false}; 271 std::vector<NativeRange> nativeRanges_ {}; 272 std::unordered_map<JSTaggedType *, RemoteObjectId> scopeObjects_ {}; 273 std::vector<std::shared_ptr<FrameHandler>> callFrameHandlers_; 274 JsDebuggerManager::ObjectUpdaterFunc updaterFunc_ {nullptr}; 275 JsDebuggerManager::SingleStepperFunc stepperFunc_ {nullptr}; 276 JsDebuggerManager::ReturnNativeFunc returnNative_ {nullptr}; 277 278 friend class JSPtHooks; 279 friend class test::TestHooks; 280 }; 281 } // namespace panda::ecmascript::tooling 282 #endif