1/* 2 * Copyright (c) 2021-2023 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#include "agent/debugger_impl.h" 17 18#include "tooling/base/pt_base64.h" 19#include "tooling/base/pt_events.h" 20#include "tooling/base/pt_params.h" 21#include "tooling/base/pt_returns.h" 22#include "tooling/base/pt_types.h" 23#include "backend/debugger_executor.h" 24#include "dispatcher.h" 25#include "protocol_handler.h" 26 27#include "ecmascript/jspandafile/js_pandafile_manager.h" 28#include "ecmascript/napi/jsnapi_helper.h" 29#include "ecmascript/tagged_array-inl.h" 30 31namespace panda::ecmascript::tooling { 32using namespace std::placeholders; 33 34using ObjectType = RemoteObject::TypeName; 35using ObjectSubType = RemoteObject::SubTypeName; 36using ObjectClassName = RemoteObject::ClassName; 37using StepperType = SingleStepper::Type; 38 39#ifdef OHOS_UNIT_TEST 40const std::string DATA_APP_PATH = "/"; 41#else 42const std::string DATA_APP_PATH = "/data/"; 43#endif 44 45static std::atomic<uint32_t> g_scriptId {0}; 46 47DebuggerImpl::DebuggerImpl(const EcmaVM *vm, ProtocolChannel *channel, RuntimeImpl *runtime) 48 : vm_(vm), frontend_(channel), runtime_(runtime) 49{ 50 hooks_ = std::make_unique<JSPtHooks>(this); 51 52 jsDebugger_ = DebuggerApi::CreateJSDebugger(vm_); 53 DebuggerApi::RegisterHooks(jsDebugger_, hooks_.get()); 54 55 updaterFunc_ = std::bind(&DebuggerImpl::UpdateScopeObject, this, _1, _2, _3, _4); 56 stepperFunc_ = std::bind(&DebuggerImpl::ClearSingleStepper, this); 57 returnNative_ = std::bind(&DebuggerImpl::NotifyReturnNative, this); 58 vm_->GetJsDebuggerManager()->SetLocalScopeUpdater(&updaterFunc_); 59 vm_->GetJsDebuggerManager()->SetStepperFunc(&stepperFunc_); 60 vm_->GetJsDebuggerManager()->SetJSReturnNativeFunc(&returnNative_); 61} 62 63DebuggerImpl::~DebuggerImpl() 64{ 65 // in worker thread, it will ~DebuggerImpl before release worker thread 66 // after ~DebuggerImpl, it maybe call these methods 67 vm_->GetJsDebuggerManager()->SetLocalScopeUpdater(nullptr); 68 vm_->GetJsDebuggerManager()->SetStepperFunc(nullptr); 69 vm_->GetJsDebuggerManager()->SetJSReturnNativeFunc(nullptr); 70 DebuggerApi::DestroyJSDebugger(jsDebugger_); 71} 72 73bool DebuggerImpl::NotifyScriptParsed(const std::string &fileName, std::string_view entryPoint) 74{ 75 if (!CheckScriptParsed(fileName)) { 76 return false; 77 } 78 79 const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(fileName.c_str()).get(); 80 if (jsPandaFile == nullptr) { 81 LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: unknown file: " << fileName; 82 return false; 83 } 84 85 DebugInfoExtractor *extractor = GetExtractor(jsPandaFile); 86 if (extractor == nullptr) { 87 LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: Unsupported file: " << fileName; 88 return false; 89 } 90 91 const char *recordName = entryPoint.data(); 92 auto mainMethodIndex = panda_file::File::EntityId(jsPandaFile->GetMainMethodIndex(recordName)); 93 const std::string &source = extractor->GetSourceCode(mainMethodIndex); 94 const std::string &url = extractor->GetSourceFile(mainMethodIndex); 95 if (source.empty()) { 96 LOG_DEBUGGER(WARN) << "NotifyScriptParsed: invalid debugger file: " << fileName; 97 return false; 98 } 99 100 recordNames_[url].insert(recordName); 101 102 // if load module, it needs to check whether clear singlestepper_ 103 ClearSingleStepper(); 104 if (MatchUrlAndFileName(url, fileName)) { 105 return false; 106 } 107 urlFileNameMap_[url].insert(fileName); 108 109 // Notify script parsed event 110 std::unique_ptr<PtScript> script = std::make_unique<PtScript>(g_scriptId++, fileName, url, source); 111 112 frontend_.ScriptParsed(vm_, *script); 113 114 // Store parsed script in map 115 scripts_[script->GetScriptId()] = std::move(script); 116 return true; 117} 118 119bool DebuggerImpl::SendableScriptParsed(const std::string &fileName, const std::string &url, 120 const std::string &source, const std::string &recordName) 121{ 122 if (!CheckScriptParsed(fileName)) { 123 return false; 124 } 125 126 recordNames_[url].insert(recordName); 127 128 // if load module, it needs to check whether clear singlestepper_ 129 ClearSingleStepper(); 130 131 urlFileNameMap_[url].insert(fileName); 132 // Notify script parsed event 133 std::unique_ptr<PtScript> script = std::make_unique<PtScript>(g_scriptId++, fileName, url, source); 134 135 frontend_.ScriptParsed(vm_, *script); 136 137 // Store parsed script in map 138 scripts_[script->GetScriptId()] = std::move(script); 139 return true; 140} 141 142bool DebuggerImpl::CheckScriptParsed([[maybe_unused]] const std::string &fileName) 143{ 144#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) \ 145 && !defined(PANDA_TARGET_ANDROID) && !defined(PANDA_TARGET_IOS) \ 146 && !defined(PANDA_TARGET_LINUX) 147 if (fileName.substr(0, DATA_APP_PATH.length()) != DATA_APP_PATH) { 148 LOG_DEBUGGER(DEBUG) << "SendableScriptParsed: unsupport file: " << fileName; 149 return false; 150 } 151#endif 152 153 // The release application does not require scriptParsed 154 if (!vm_->GetJsDebuggerManager()->IsDebugApp()) { 155 return false; 156 } 157 158 return true; 159} 160 161bool DebuggerImpl::SendableMethodEntry(JSHandle<Method> method) 162{ 163 const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); 164 if (jsPandaFile == nullptr) { 165 LOG_DEBUGGER(ERROR) << "JSPandaFile is nullptr"; 166 return false; 167 } 168 DebugInfoExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); 169 if (extractor == nullptr) { 170 LOG_DEBUGGER(ERROR) << "extractor is nullptr"; 171 return false; 172 } 173 auto methodId = method->GetMethodId(); 174 const std::string &url = extractor->GetSourceFile(methodId); 175 const std::string &fileName = std::string(jsPandaFile->GetJSPandaFileDesc()); 176 if (!MatchUrlAndFileName(url, fileName)) { 177 // scriptParsed 178 const std::string &source = extractor->GetSourceCode(methodId); 179 const std::string &recordName = std::string(method->GetRecordNameStr()); 180 SendableScriptParsed(fileName, url, source, recordName); 181 return true; 182 } 183 return false; 184} 185 186bool DebuggerImpl::MatchUrlAndFileName(const std::string &url, const std::string &fileName) 187{ 188 auto urlFileNameIter = urlFileNameMap_.find(url); 189 if (urlFileNameIter != urlFileNameMap_.end()) { 190 if (urlFileNameIter->second.find(fileName) != urlFileNameIter->second.end()) { 191 LOG_DEBUGGER(WARN) << "MatchUrlAndFileName: already loaded: " << url; 192 return true; 193 } 194 } 195 return false; 196} 197 198bool DebuggerImpl::NotifyNativeOut() 199{ 200 if (nativeOutPause_) { 201 nativeOutPause_ = false; 202 return true; 203 } 204 return false; 205} 206 207bool DebuggerImpl::NotifySingleStep(const JSPtLocation &location) 208{ 209 if (UNLIKELY(pauseOnNextByteCode_)) { 210 if (IsSkipLine(location)) { 211 return false; 212 } 213 pauseOnNextByteCode_ = false; 214 LOG_DEBUGGER(INFO) << "StepComplete: pause on next bytecode"; 215 return true; 216 } 217 218 if (LIKELY(singleStepper_ == nullptr)) { 219 return false; 220 } 221 222 // step not complete 223 if (!singleStepper_->StepComplete(location.GetBytecodeOffset())) { 224 return false; 225 } 226 227 // skip unknown file or special line -1 228 if (IsSkipLine(location)) { 229 return false; 230 } 231 232 singleStepper_.reset(); 233 LOG_DEBUGGER(INFO) << "StepComplete: pause on current byte_code"; 234 if (!DebuggerApi::GetSingleStepStatus(jsDebugger_)) { 235 DebuggerApi::SetSingleStepStatus(jsDebugger_, true); 236 } 237 return true; 238} 239 240bool DebuggerImpl::IsSkipLine(const JSPtLocation &location) 241{ 242 DebugInfoExtractor *extractor = nullptr; 243 const auto *jsPandaFile = location.GetJsPandaFile(); 244 auto scriptFunc = [this, &extractor, jsPandaFile](PtScript *) -> bool { 245 extractor = GetExtractor(jsPandaFile); 246 return true; 247 }; 248 249 // In hot reload scenario, use the base js panda file instead 250 const auto &fileName = DebuggerApi::GetBaseJSPandaFile(vm_, jsPandaFile)->GetJSPandaFileDesc(); 251 if (!MatchScripts(scriptFunc, fileName.c_str(), ScriptMatchType::FILE_NAME) || extractor == nullptr) { 252 LOG_DEBUGGER(INFO) << "StepComplete: skip unknown file " << fileName.c_str(); 253 return true; 254 } 255 256 auto callbackFunc = [](int32_t line) -> bool { 257 return line == DebugInfoExtractor::SPECIAL_LINE_MARK; 258 }; 259 panda_file::File::EntityId methodId = location.GetMethodId(); 260 uint32_t offset = location.GetBytecodeOffset(); 261 if (extractor->MatchLineWithOffset(callbackFunc, methodId, offset)) { 262 LOG_DEBUGGER(INFO) << "StepComplete: skip -1"; 263 return true; 264 } 265 266 return false; 267} 268 269bool DebuggerImpl::CheckPauseOnException() 270{ 271 if (pauseOnException_ == PauseOnExceptionsState::NONE) { 272 return false; 273 } 274 if (pauseOnException_ == PauseOnExceptionsState::UNCAUGHT) { 275 if (DebuggerApi::IsExceptionCaught(vm_)) { 276 return false; 277 } 278 } 279 return true; 280} 281 282void DebuggerImpl::NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason) 283{ 284 if (skipAllPausess_) { 285 return; 286 } 287 288 if (location.has_value() && !breakpointsState_) { 289 return; 290 } 291 292 if (reason == EXCEPTION && !CheckPauseOnException()) { 293 return; 294 } 295 296 Local<JSValueRef> exception = DebuggerApi::GetAndClearException(vm_); 297 298 std::vector<std::string> hitBreakpoints; 299 if (location.has_value()) { 300 BreakpointDetails detail; 301 DebugInfoExtractor *extractor = nullptr; 302 auto scriptFunc = [this, &location, &detail, &extractor](PtScript *script) -> bool { 303 detail.url_ = script->GetUrl(); 304 extractor = GetExtractor(location->GetJsPandaFile()); 305 return true; 306 }; 307 auto callbackLineFunc = [&detail](int32_t line) -> bool { 308 detail.line_ = line; 309 return true; 310 }; 311 auto callbackColumnFunc = [&detail](int32_t column) -> bool { 312 detail.column_ = column; 313 return true; 314 }; 315 panda_file::File::EntityId methodId = location->GetMethodId(); 316 uint32_t offset = location->GetBytecodeOffset(); 317 // In merge abc scenario, need to use the source file to match to get right url 318 if (!MatchScripts(scriptFunc, location->GetSourceFile(), ScriptMatchType::URL) || 319 extractor == nullptr || !extractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || 320 !extractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { 321 LOG_DEBUGGER(ERROR) << "NotifyPaused: unknown file " << location->GetSourceFile(); 322 return; 323 } 324 hitBreakpoints.emplace_back(BreakpointDetails::ToString(detail)); 325 } 326 327 // Do something cleaning on paused 328 CleanUpOnPaused(); 329 GeneratePausedInfo(reason, hitBreakpoints, exception); 330} 331 332void DebuggerImpl::GeneratePausedInfo(PauseReason reason, 333 std::vector<std::string> &hitBreakpoints, 334 const Local<JSValueRef> &exception) 335{ 336 // Notify paused event 337 std::vector<std::unique_ptr<CallFrame>> callFrames; 338 if (!GenerateCallFrames(&callFrames, true)) { 339 LOG_DEBUGGER(ERROR) << "NotifyPaused: GenerateCallFrames failed"; 340 return; 341 } 342 tooling::Paused paused; 343 if (reason == DEBUGGERSTMT) { 344 BreakpointDetails detail; 345 hitBreakpoints.emplace_back(BreakpointDetails::ToString(detail)); 346 paused.SetCallFrames(std::move(callFrames)) 347 .SetReason(PauseReason::OTHER) 348 .SetHitBreakpoints(std::move(hitBreakpoints)); 349 } else { 350 paused.SetCallFrames(std::move(callFrames)).SetReason(reason).SetHitBreakpoints(std::move(hitBreakpoints)); 351 } 352 if (reason == EXCEPTION && exception->IsError(vm_)) { 353 std::unique_ptr<RemoteObject> tmpException = RemoteObject::FromTagged(vm_, exception); 354 paused.SetData(std::move(tmpException)); 355 } 356 frontend_.Paused(vm_, paused); 357 if (reason != BREAK_ON_START && reason != NATIVE_OUT) { 358 singleStepper_.reset(); 359 } 360 nativeOutPause_ = false; 361 debuggerState_ = DebuggerState::PAUSED; 362 frontend_.WaitForDebugger(vm_); 363 DebuggerApi::SetException(vm_, exception); 364} 365 366bool DebuggerImpl::IsUserCode(const void *nativeAddress) 367{ 368 uint64_t nativeEntry = reinterpret_cast<uint64_t>(nativeAddress); 369 for (const auto &nativeRange : nativeRanges_) { 370 if (nativeEntry >= nativeRange.GetStart() && nativeEntry <= nativeRange.GetEnd()) { 371 return true; 372 } 373 } 374 return false; 375} 376 377void DebuggerImpl::NotifyNativeCalling(const void *nativeAddress) 378{ 379 // native calling only after step into should be reported 380 if (singleStepper_ != nullptr && 381 singleStepper_->GetStepperType() == StepperType::STEP_INTO) { 382 tooling::NativeCalling nativeCalling; 383 nativeCalling.SetIntoStatus(true); 384 nativeCalling.SetNativeAddress(nativeAddress); 385 frontend_.NativeCalling(vm_, nativeCalling); 386 frontend_.WaitForDebugger(vm_); 387 } 388 389 if (mixStackEnabled_ && IsUserCode(nativeAddress)) { 390 tooling::MixedStack mixedStack; 391 nativePointer_ = DebuggerApi::GetNativePointer(vm_); 392 mixedStack.SetNativePointers(nativePointer_); 393 std::vector<std::unique_ptr<CallFrame>> callFrames; 394 if (GenerateCallFrames(&callFrames, false)) { 395 mixedStack.SetCallFrames(std::move(callFrames)); 396 } 397 frontend_.MixedStack(vm_, mixedStack); 398 } 399} 400 401void DebuggerImpl::NotifyNativeReturn(const void *nativeAddress) 402{ 403 if (mixStackEnabled_ && IsUserCode(nativeAddress)) { 404 nativeOutPause_ = true; 405 } 406} 407 408void DebuggerImpl::NotifyReturnNative() 409{ 410 if (mixStackEnabled_) { 411 tooling::MixedStack mixedStack; 412 nativePointer_ = DebuggerApi::GetNativePointer(vm_); 413 if (nativePointer_.empty()) { 414 return; 415 } 416 mixedStack.SetNativePointers(nativePointer_); 417 std::vector<std::unique_ptr<CallFrame>> callFrames; 418 if (GenerateCallFrames(&callFrames, false)) { 419 mixedStack.SetCallFrames(std::move(callFrames)); 420 } 421 frontend_.MixedStack(vm_, mixedStack); 422 } 423} 424 425// only use for test case 426void DebuggerImpl::SetDebuggerState(DebuggerState debuggerState) 427{ 428 debuggerState_ = debuggerState; 429} 430 431// only use for test case 432void DebuggerImpl::SetNativeOutPause(bool nativeOutPause) 433{ 434 nativeOutPause_ = nativeOutPause; 435} 436 437void DebuggerImpl::NotifyHandleProtocolCommand() 438{ 439 auto *handler = vm_->GetJsDebuggerManager()->GetDebuggerHandler(); 440 handler->ProcessCommand(); 441} 442 443// Whenever adding a new protocol which is not a standard CDP protocol, 444// must add its methodName to the debuggerProtocolsList 445void DebuggerImpl::InitializeExtendedProtocolsList() 446{ 447 std::vector<std::string> debuggerProtocolList { 448 "removeBreakpointsByUrl", 449 "setMixedDebugEnabled", 450 "replyNativeCalling", 451 "getPossibleAndSetBreakpointByUrl", 452 "dropFrame", 453 "setNativeRange", 454 "resetSingleStepper", 455 "callFunctionOn", 456 "smartStepInto" 457 }; 458 debuggerExtendedProtocols_ = std::move(debuggerProtocolList); 459} 460 461void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) 462{ 463 Method method = GetMethodEnum(request.GetMethod()); 464 LOG_DEBUGGER(DEBUG) << "dispatch [" << request.GetMethod() << "] to DebuggerImpl"; 465 switch (method) { 466 case Method::CONTINUE_TO_LOCATION: 467 ContinueToLocation(request); 468 break; 469 case Method::ENABLE: 470 Enable(request); 471 break; 472 case Method::DISABLE: 473 Disable(request); 474 break; 475 case Method::EVALUATE_ON_CALL_FRAME: 476 EvaluateOnCallFrame(request); 477 break; 478 case Method::GET_POSSIBLE_BREAKPOINTS: 479 GetPossibleBreakpoints(request); 480 break; 481 case Method::GET_SCRIPT_SOURCE: 482 GetScriptSource(request); 483 break; 484 case Method::PAUSE: 485 Pause(request); 486 break; 487 case Method::REMOVE_BREAKPOINT: 488 RemoveBreakpoint(request); 489 break; 490 case Method::REMOVE_BREAKPOINTS_BY_URL: 491 RemoveBreakpointsByUrl(request); 492 break; 493 case Method::RESUME: 494 Resume(request); 495 break; 496 case Method::SET_ASYNC_CALL_STACK_DEPTH: 497 SetAsyncCallStackDepth(request); 498 break; 499 case Method::SET_BREAKPOINT_BY_URL: 500 SetBreakpointByUrl(request); 501 break; 502 case Method::SET_BREAKPOINTS_ACTIVE: 503 SetBreakpointsActive(request); 504 break; 505 case Method::SET_PAUSE_ON_EXCEPTIONS: 506 SetPauseOnExceptions(request); 507 break; 508 case Method::SET_SKIP_ALL_PAUSES: 509 SetSkipAllPauses(request); 510 break; 511 case Method::STEP_INTO: 512 StepInto(request); 513 break; 514 case Method::SMART_STEP_INTO: 515 SmartStepInto(request); 516 break; 517 case Method::STEP_OUT: 518 StepOut(request); 519 break; 520 case Method::STEP_OVER: 521 StepOver(request); 522 break; 523 case Method::SET_MIXED_DEBUG_ENABLED: 524 SetMixedDebugEnabled(request); 525 break; 526 case Method::SET_BLACKBOX_PATTERNS: 527 SetBlackboxPatterns(request); 528 break; 529 case Method::REPLY_NATIVE_CALLING: 530 ReplyNativeCalling(request); 531 break; 532 case Method::GET_POSSIBLE_AND_SET_BREAKPOINT_BY_URL: 533 GetPossibleAndSetBreakpointByUrl(request); 534 break; 535 case Method::DROP_FRAME: 536 DropFrame(request); 537 break; 538 case Method::SET_NATIVE_RANGE: 539 SetNativeRange(request); 540 break; 541 case Method::RESET_SINGLE_STEPPER: 542 ResetSingleStepper(request); 543 break; 544 case Method::CLIENT_DISCONNECT: 545 ClientDisconnect(request); 546 break; 547 case Method::CALL_FUNCTION_ON: 548 CallFunctionOn(request); 549 break; 550 default: 551 SendResponse(request, DispatchResponse::Fail("Unknown method: " + request.GetMethod())); 552 break; 553 } 554} 555 556DebuggerImpl::DispatcherImpl::Method DebuggerImpl::DispatcherImpl::GetMethodEnum(const std::string& method) 557{ 558 if (method == "continueToLocation") { 559 return Method::CONTINUE_TO_LOCATION; 560 } else if (method == "enable") { 561 return Method::ENABLE; 562 } else if (method == "disable") { 563 return Method::DISABLE; 564 } else if (method == "evaluateOnCallFrame") { 565 return Method::EVALUATE_ON_CALL_FRAME; 566 } else if (method == "getPossibleBreakpoints") { 567 return Method::GET_POSSIBLE_BREAKPOINTS; 568 } else if (method == "getScriptSource") { 569 return Method::GET_SCRIPT_SOURCE; 570 } else if (method == "pause") { 571 return Method::PAUSE; 572 } else if (method == "removeBreakpoint") { 573 return Method::REMOVE_BREAKPOINT; 574 } else if (method == "removeBreakpointsByUrl") { 575 return Method::REMOVE_BREAKPOINTS_BY_URL; 576 } else if (method == "resume") { 577 return Method::RESUME; 578 } else if (method == "setAsyncCallStackDepth") { 579 return Method::SET_ASYNC_CALL_STACK_DEPTH; 580 } else if (method == "setBreakpointByUrl") { 581 return Method::SET_BREAKPOINT_BY_URL; 582 } else if (method == "setBreakpointsActive") { 583 return Method::SET_BREAKPOINTS_ACTIVE; 584 } else if (method == "setPauseOnExceptions") { 585 return Method::SET_PAUSE_ON_EXCEPTIONS; 586 } else if (method == "setSkipAllPauses") { 587 return Method::SET_SKIP_ALL_PAUSES; 588 } else if (method == "stepInto") { 589 return Method::STEP_INTO; 590 } else if (method == "smartStepInto") { 591 return Method::SMART_STEP_INTO; 592 } else if (method == "stepOut") { 593 return Method::STEP_OUT; 594 } else if (method == "stepOver") { 595 return Method::STEP_OVER; 596 } else if (method == "setMixedDebugEnabled") { 597 return Method::SET_MIXED_DEBUG_ENABLED; 598 } else if (method == "setBlackboxPatterns") { 599 return Method::SET_BLACKBOX_PATTERNS; 600 } else if (method == "replyNativeCalling") { 601 return Method::REPLY_NATIVE_CALLING; 602 } else if (method == "getPossibleAndSetBreakpointByUrl") { 603 return Method::GET_POSSIBLE_AND_SET_BREAKPOINT_BY_URL; 604 } else if (method == "dropFrame") { 605 return Method::DROP_FRAME; 606 } else if (method == "setNativeRange") { 607 return Method::SET_NATIVE_RANGE; 608 } else if (method == "resetSingleStepper") { 609 return Method::RESET_SINGLE_STEPPER; 610 } else if (method == "clientDisconnect") { 611 return Method::CLIENT_DISCONNECT; 612 } else if (method == "callFunctionOn") { 613 return Method::CALL_FUNCTION_ON; 614 } else { 615 return Method::UNKNOWN; 616 } 617} 618 619void DebuggerImpl::DispatcherImpl::ContinueToLocation(const DispatchRequest &request) 620{ 621 std::unique_ptr<ContinueToLocationParams> params = ContinueToLocationParams::Create(request.GetParams()); 622 if (params == nullptr) { 623 SendResponse(request, DispatchResponse::Fail("wrong params")); 624 return; 625 } 626 627 DispatchResponse response = debugger_->ContinueToLocation(*params); 628 SendResponse(request, response); 629} 630 631void DebuggerImpl::DispatcherImpl::Enable(const DispatchRequest &request) 632{ 633 std::unique_ptr<EnableParams> params = EnableParams::Create(request.GetParams()); 634 if (params == nullptr) { 635 SendResponse(request, DispatchResponse::Fail("wrong params")); 636 return; 637 } 638 639 UniqueDebuggerId id; 640 DispatchResponse response = debugger_->Enable(*params, &id); 641 642 debugger_->InitializeExtendedProtocolsList(); 643 DebuggerEnableReturns result(id, debugger_->debuggerExtendedProtocols_); 644 SendResponse(request, response, result); 645} 646 647void DebuggerImpl::DispatcherImpl::Disable(const DispatchRequest &request) 648{ 649 DispatchResponse response = debugger_->Disable(); 650 SendResponse(request, response); 651} 652 653void DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame(const DispatchRequest &request) 654{ 655 std::unique_ptr<EvaluateOnCallFrameParams> params = EvaluateOnCallFrameParams::Create(request.GetParams()); 656 if (params == nullptr) { 657 SendResponse(request, DispatchResponse::Fail("wrong params")); 658 return; 659 } 660 std::unique_ptr<RemoteObject> result1; 661 DispatchResponse response = debugger_->EvaluateOnCallFrame(*params, &result1); 662 if (result1 == nullptr) { 663 SendResponse(request, response); 664 return; 665 } 666 667 EvaluateOnCallFrameReturns result(std::move(result1)); 668 SendResponse(request, response, result); 669} 670 671void DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints(const DispatchRequest &request) 672{ 673 std::unique_ptr<GetPossibleBreakpointsParams> params = GetPossibleBreakpointsParams::Create(request.GetParams()); 674 if (params == nullptr) { 675 SendResponse(request, DispatchResponse::Fail("wrong params")); 676 return; 677 } 678 std::vector<std::unique_ptr<BreakLocation>> locations; 679 DispatchResponse response = debugger_->GetPossibleBreakpoints(*params, &locations); 680 GetPossibleBreakpointsReturns result(std::move(locations)); 681 SendResponse(request, response, result); 682} 683 684void DebuggerImpl::DispatcherImpl::GetScriptSource(const DispatchRequest &request) 685{ 686 std::unique_ptr<GetScriptSourceParams> params = GetScriptSourceParams::Create(request.GetParams()); 687 if (params == nullptr) { 688 SendResponse(request, DispatchResponse::Fail("wrong params")); 689 return; 690 } 691 std::string source; 692 DispatchResponse response = debugger_->GetScriptSource(*params, &source); 693 GetScriptSourceReturns result(source); 694 SendResponse(request, response, result); 695} 696 697void DebuggerImpl::DispatcherImpl::Pause(const DispatchRequest &request) 698{ 699 DispatchResponse response = debugger_->Pause(); 700 SendResponse(request, response); 701} 702 703void DebuggerImpl::DispatcherImpl::RemoveBreakpoint(const DispatchRequest &request) 704{ 705 std::unique_ptr<RemoveBreakpointParams> params = RemoveBreakpointParams::Create(request.GetParams()); 706 if (params == nullptr) { 707 SendResponse(request, DispatchResponse::Fail("wrong params")); 708 return; 709 } 710 DispatchResponse response = debugger_->RemoveBreakpoint(*params); 711 SendResponse(request, response); 712} 713 714void DebuggerImpl::DispatcherImpl::RemoveBreakpointsByUrl(const DispatchRequest &request) 715{ 716 std::unique_ptr<RemoveBreakpointsByUrlParams> params = RemoveBreakpointsByUrlParams::Create(request.GetParams()); 717 if (params == nullptr) { 718 SendResponse(request, DispatchResponse::Fail("wrong params")); 719 return; 720 } 721 DispatchResponse response = debugger_->RemoveBreakpointsByUrl(*params); 722 SendResponse(request, response); 723} 724 725void DebuggerImpl::DispatcherImpl::Resume(const DispatchRequest &request) 726{ 727 std::unique_ptr<ResumeParams> params = ResumeParams::Create(request.GetParams()); 728 if (params == nullptr) { 729 SendResponse(request, DispatchResponse::Fail("wrong params")); 730 return; 731 } 732 DispatchResponse response = debugger_->Resume(*params); 733 SendResponse(request, response); 734} 735 736void DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth(const DispatchRequest &request) 737{ 738 DispatchResponse response = debugger_->SetAsyncCallStackDepth(); 739 SendResponse(request, response); 740} 741 742void DebuggerImpl::DispatcherImpl::SetBreakpointByUrl(const DispatchRequest &request) 743{ 744 std::unique_ptr<SetBreakpointByUrlParams> params = SetBreakpointByUrlParams::Create(request.GetParams()); 745 if (params == nullptr) { 746 SendResponse(request, DispatchResponse::Fail("wrong params")); 747 return; 748 } 749 750 std::string outId; 751 std::vector<std::unique_ptr<Location>> outLocations; 752 DispatchResponse response = debugger_->SetBreakpointByUrl(*params, &outId, &outLocations); 753 SetBreakpointByUrlReturns result(outId, std::move(outLocations)); 754 SendResponse(request, response, result); 755} 756 757void DebuggerImpl::DispatcherImpl::SetBreakpointsActive(const DispatchRequest &request) 758{ 759 std::unique_ptr<SetBreakpointsActiveParams> params = SetBreakpointsActiveParams::Create(request.GetParams()); 760 if (params == nullptr) { 761 SendResponse(request, DispatchResponse::Fail("wrong params")); 762 return; 763 } 764 765 DispatchResponse response = debugger_->SetBreakpointsActive(*params); 766 SendResponse(request, response); 767} 768 769void DebuggerImpl::DispatcherImpl::GetPossibleAndSetBreakpointByUrl(const DispatchRequest &request) 770{ 771 std::unique_ptr<GetPossibleAndSetBreakpointParams> params = 772 GetPossibleAndSetBreakpointParams::Create(request.GetParams()); 773 if (params == nullptr) { 774 SendResponse(request, DispatchResponse::Fail("wrong params")); 775 return; 776 } 777 778 std::vector<std::unique_ptr<BreakpointReturnInfo>> outLocation; 779 DispatchResponse response = debugger_->GetPossibleAndSetBreakpointByUrl(*params, outLocation); 780 GetPossibleAndSetBreakpointByUrlReturns result(std::move(outLocation)); 781 SendResponse(request, response, result); 782} 783 784void DebuggerImpl::DispatcherImpl::SetPauseOnExceptions(const DispatchRequest &request) 785{ 786 std::unique_ptr<SetPauseOnExceptionsParams> params = SetPauseOnExceptionsParams::Create(request.GetParams()); 787 if (params == nullptr) { 788 SendResponse(request, DispatchResponse::Fail("wrong params")); 789 return; 790 } 791 792 DispatchResponse response = debugger_->SetPauseOnExceptions(*params); 793 SendResponse(request, response); 794} 795 796void DebuggerImpl::DispatcherImpl::SetSkipAllPauses(const DispatchRequest &request) 797{ 798 std::unique_ptr<SetSkipAllPausesParams> params = SetSkipAllPausesParams::Create(request.GetParams()); 799 if (params == nullptr) { 800 SendResponse(request, DispatchResponse::Fail("wrong params")); 801 return; 802 } 803 804 DispatchResponse response = debugger_->SetSkipAllPauses(*params); 805 SendResponse(request, response); 806} 807 808void DebuggerImpl::DispatcherImpl::SetNativeRange(const DispatchRequest &request) 809{ 810 std::unique_ptr<SetNativeRangeParams> params = SetNativeRangeParams::Create(request.GetParams()); 811 if (params == nullptr) { 812 SendResponse(request, DispatchResponse::Fail("wrong params")); 813 return; 814 } 815 DispatchResponse response = debugger_->SetNativeRange(*params); 816 SendResponse(request, response); 817} 818 819void DebuggerImpl::DispatcherImpl::ResetSingleStepper(const DispatchRequest &request) 820{ 821 std::unique_ptr<ResetSingleStepperParams> params = ResetSingleStepperParams::Create(request.GetParams()); 822 if (params == nullptr) { 823 SendResponse(request, DispatchResponse::Fail("wrong params")); 824 return; 825 } 826 DispatchResponse response = debugger_->ResetSingleStepper(*params); 827 SendResponse(request, response); 828} 829 830void DebuggerImpl::DispatcherImpl::StepInto(const DispatchRequest &request) 831{ 832 std::unique_ptr<StepIntoParams> params = StepIntoParams::Create(request.GetParams()); 833 if (params == nullptr) { 834 SendResponse(request, DispatchResponse::Fail("wrong params")); 835 return; 836 } 837 DispatchResponse response = debugger_->StepInto(*params); 838 SendResponse(request, response); 839} 840 841void DebuggerImpl::DispatcherImpl::SmartStepInto(const DispatchRequest &request) 842{ 843 std::unique_ptr<SmartStepIntoParams> params = SmartStepIntoParams::Create(request.GetParams()); 844 if (params == nullptr) { 845 SendResponse(request, DispatchResponse::Fail("wrong params")); 846 return; 847 } 848 DispatchResponse response = debugger_->SmartStepInto(*params); 849 SendResponse(request, response); 850} 851 852void DebuggerImpl::DispatcherImpl::StepOut(const DispatchRequest &request) 853{ 854 DispatchResponse response = debugger_->StepOut(); 855 SendResponse(request, response); 856} 857 858void DebuggerImpl::DispatcherImpl::StepOver(const DispatchRequest &request) 859{ 860 std::unique_ptr<StepOverParams> params = StepOverParams::Create(request.GetParams()); 861 if (params == nullptr) { 862 SendResponse(request, DispatchResponse::Fail("wrong params")); 863 return; 864 } 865 DispatchResponse response = debugger_->StepOver(*params); 866 SendResponse(request, response); 867} 868 869void DebuggerImpl::DispatcherImpl::SetMixedDebugEnabled(const DispatchRequest &request) 870{ 871 std::unique_ptr<SetMixedDebugParams> params = SetMixedDebugParams::Create(request.GetParams()); 872 if (params == nullptr) { 873 SendResponse(request, DispatchResponse::Fail("wrong params")); 874 return; 875 } 876 DispatchResponse response = debugger_->SetMixedDebugEnabled(*params); 877 SendResponse(request, response); 878} 879 880void DebuggerImpl::DispatcherImpl::ReplyNativeCalling(const DispatchRequest &request) 881{ 882 std::unique_ptr<ReplyNativeCallingParams> params = ReplyNativeCallingParams::Create(request.GetParams()); 883 if (params == nullptr) { 884 SendResponse(request, DispatchResponse::Fail("wrong params")); 885 return; 886 } 887 DispatchResponse response = debugger_->ReplyNativeCalling(*params); 888 SendResponse(request, response); 889} 890 891void DebuggerImpl::DispatcherImpl::SetBlackboxPatterns(const DispatchRequest &request) 892{ 893 DispatchResponse response = debugger_->SetBlackboxPatterns(); 894 SendResponse(request, response); 895} 896 897void DebuggerImpl::DispatcherImpl::DropFrame(const DispatchRequest &request) 898{ 899 std::unique_ptr<DropFrameParams> params = DropFrameParams::Create(request.GetParams()); 900 if (params == nullptr) { 901 SendResponse(request, DispatchResponse::Fail("wrong params")); 902 return; 903 } 904 DispatchResponse response = debugger_->DropFrame(*params); 905 SendResponse(request, response); 906} 907 908// inner message, not SendResponse to outer 909void DebuggerImpl::DispatcherImpl::ClientDisconnect([[maybe_unused]] const DispatchRequest &request) 910{ 911 debugger_->ClientDisconnect(); 912} 913 914void DebuggerImpl::DispatcherImpl::CallFunctionOn(const DispatchRequest &request) 915{ 916 std::unique_ptr<CallFunctionOnParams> params = CallFunctionOnParams::Create(request.GetParams()); 917 if (params == nullptr) { 918 SendResponse(request, DispatchResponse::Fail("wrong params")); 919 return; 920 } 921 922 std::unique_ptr<RemoteObject> outRemoteObject; 923 std::optional<std::unique_ptr<ExceptionDetails>> outExceptionDetails; 924 DispatchResponse response = debugger_->CallFunctionOn(*params, &outRemoteObject, &outExceptionDetails); 925 if (outExceptionDetails) { 926 ASSERT(outExceptionDetails.value() != nullptr); 927 LOG_DEBUGGER(WARN) << "CallFunctionOn thrown an exception"; 928 } 929 if (outRemoteObject == nullptr) { 930 SendResponse(request, response); 931 return; 932 } 933 934 CallFunctionOnReturns result(std::move(outRemoteObject), std::move(outExceptionDetails)); 935 SendResponse(request, response, result); 936} 937 938bool DebuggerImpl::Frontend::AllowNotify(const EcmaVM *vm) const 939{ 940 return vm->GetJsDebuggerManager()->IsDebugMode() && channel_ != nullptr; 941} 942 943void DebuggerImpl::Frontend::BreakpointResolved(const EcmaVM *vm) 944{ 945 if (!AllowNotify(vm)) { 946 return; 947 } 948 949 tooling::BreakpointResolved breakpointResolved; 950 channel_->SendNotification(breakpointResolved); 951} 952 953void DebuggerImpl::Frontend::Paused(const EcmaVM *vm, const tooling::Paused &paused) 954{ 955 if (!AllowNotify(vm)) { 956 return; 957 } 958 959 channel_->SendNotification(paused); 960} 961 962void DebuggerImpl::Frontend::NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling) 963{ 964 if (!AllowNotify(vm)) { 965 return; 966 } 967 968 channel_->SendNotification(nativeCalling); 969} 970 971void DebuggerImpl::Frontend::MixedStack(const EcmaVM *vm, const tooling::MixedStack &mixedStack) 972{ 973 if (!AllowNotify(vm)) { 974 return; 975 } 976 977 channel_->SendNotification(mixedStack); 978} 979 980void DebuggerImpl::Frontend::Resumed(const EcmaVM *vm) 981{ 982 if (!AllowNotify(vm)) { 983 return; 984 } 985 986 channel_->RunIfWaitingForDebugger(); 987 tooling::Resumed resumed; 988 channel_->SendNotification(resumed); 989} 990 991void DebuggerImpl::Frontend::ScriptFailedToParse(const EcmaVM *vm) 992{ 993 if (!AllowNotify(vm)) { 994 return; 995 } 996 997 tooling::ScriptFailedToParse scriptFailedToParse; 998 channel_->SendNotification(scriptFailedToParse); 999} 1000 1001void DebuggerImpl::Frontend::ScriptParsed(const EcmaVM *vm, const PtScript &script) 1002{ 1003 if (!AllowNotify(vm)) { 1004 return; 1005 } 1006 1007 tooling::ScriptParsed scriptParsed; 1008 scriptParsed.SetScriptId(script.GetScriptId()) 1009 .SetUrl(script.GetUrl()) 1010 .SetStartLine(0) 1011 .SetStartColumn(0) 1012 .SetEndLine(script.GetEndLine()) 1013 .SetEndColumn(0) 1014 .SetExecutionContextId(0) 1015 .SetHash(script.GetHash()); 1016 1017 channel_->SendNotification(scriptParsed); 1018} 1019 1020void DebuggerImpl::Frontend::WaitForDebugger(const EcmaVM *vm) 1021{ 1022 if (!AllowNotify(vm)) { 1023 return; 1024 } 1025 1026 channel_->WaitForDebugger(); 1027} 1028 1029void DebuggerImpl::Frontend::RunIfWaitingForDebugger([[maybe_unused]] const EcmaVM *vm) 1030{ 1031 // Because release hap can WaitForDebugger, need RunIfWaitingForDebugger to run continue. 1032 // But release hap debugMode is false, so not check debugMode. 1033 if (channel_ == nullptr) { 1034 return; 1035 } 1036 1037 channel_->RunIfWaitingForDebugger(); 1038} 1039 1040DispatchResponse DebuggerImpl::ContinueToLocation(const ContinueToLocationParams ¶ms) 1041{ 1042 location_ = *params.GetLocation(); 1043 return DispatchResponse::Ok(); 1044} 1045 1046DispatchResponse DebuggerImpl::Enable([[maybe_unused]] const EnableParams ¶ms, UniqueDebuggerId *id) 1047{ 1048 DebuggerExecutor::Initialize(vm_); 1049 ASSERT(id != nullptr); 1050 *id = 0; 1051 vm_->GetJsDebuggerManager()->SetDebugMode(true); 1052 for (auto &script : scripts_) { 1053 frontend_.ScriptParsed(vm_, *script.second); 1054 } 1055 debuggerState_ = DebuggerState::ENABLED; 1056 return DispatchResponse::Ok(); 1057} 1058 1059DispatchResponse DebuggerImpl::Disable() 1060{ 1061 DebuggerApi::RemoveAllBreakpoints(jsDebugger_); 1062 frontend_.RunIfWaitingForDebugger(vm_); 1063 frontend_.Resumed(vm_); 1064 vm_->GetJsDebuggerManager()->SetDebugMode(false); 1065 debuggerState_ = DebuggerState::DISABLED; 1066 return DispatchResponse::Ok(); 1067} 1068 1069DispatchResponse DebuggerImpl::EvaluateOnCallFrame(const EvaluateOnCallFrameParams ¶ms, 1070 std::unique_ptr<RemoteObject> *result) 1071{ 1072 CallFrameId callFrameId = params.GetCallFrameId(); 1073 const std::string &expression = params.GetExpression(); 1074 if (callFrameId < 0 || callFrameId >= static_cast<CallFrameId>(callFrameHandlers_.size())) { 1075 return DispatchResponse::Fail("Invalid callFrameId."); 1076 } 1077 1078 std::vector<uint8_t> dest; 1079 if (!DecodeAndCheckBase64(expression, dest)) { 1080 LOG_DEBUGGER(ERROR) << "EvaluateValue: base64 decode failed"; 1081 auto ret = CmptEvaluateValue(callFrameId, expression, result); 1082 if (ret.has_value()) { 1083 LOG_DEBUGGER(ERROR) << "Evaluate fail, expression: " << expression; 1084 } 1085 return DispatchResponse::Create(ret); 1086 } 1087 1088 auto funcRef = DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(), 1089 JSPandaFile::ENTRY_FUNCTION_NAME); 1090 auto res = DebuggerApi::EvaluateViaFuncCall(const_cast<EcmaVM *>(vm_), funcRef, 1091 callFrameHandlers_[callFrameId]); 1092 if (vm_->GetJSThread()->HasPendingException()) { 1093 LOG_DEBUGGER(ERROR) << "EvaluateValue: has pending exception"; 1094 std::string msg; 1095 DebuggerApi::HandleUncaughtException(vm_, msg); 1096 *result = RemoteObject::FromTagged(vm_, 1097 Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, msg.data()))); 1098 return DispatchResponse::Fail(msg); 1099 } 1100 1101 *result = RemoteObject::FromTagged(vm_, res); 1102 runtime_->CacheObjectIfNeeded(res, (*result).get()); 1103 return DispatchResponse::Ok(); 1104} 1105 1106DispatchResponse DebuggerImpl::GetPossibleBreakpoints(const GetPossibleBreakpointsParams ¶ms, 1107 std::vector<std::unique_ptr<BreakLocation>> *locations) 1108{ 1109 Location *start = params.GetStart(); 1110 auto iter = scripts_.find(start->GetScriptId()); 1111 if (iter == scripts_.end()) { 1112 return DispatchResponse::Fail("Unknown file name."); 1113 } 1114 const std::string &url = iter->second->GetUrl(); 1115 std::vector<DebugInfoExtractor *> extractors = GetExtractors(url); 1116 for (auto extractor : extractors) { 1117 if (extractor == nullptr) { 1118 LOG_DEBUGGER(DEBUG) << "GetPossibleBreakpoints: extractor is null"; 1119 continue; 1120 } 1121 1122 int32_t line = start->GetLine(); 1123 int32_t column = start->GetColumn(); 1124 auto callbackFunc = [](const JSPtLocation &) -> bool { 1125 return true; 1126 }; 1127 if (extractor->MatchWithLocation(callbackFunc, line, column, url, GetRecordName(url))) { 1128 std::unique_ptr<BreakLocation> location = std::make_unique<BreakLocation>(); 1129 location->SetScriptId(start->GetScriptId()).SetLine(line).SetColumn(column); 1130 locations->emplace_back(std::move(location)); 1131 break; 1132 } 1133 } 1134 return DispatchResponse::Ok(); 1135} 1136 1137DispatchResponse DebuggerImpl::GetScriptSource(const GetScriptSourceParams ¶ms, std::string *source) 1138{ 1139 ScriptId scriptId = params.GetScriptId(); 1140 auto iter = scripts_.find(scriptId); 1141 if (iter == scripts_.end()) { 1142 *source = ""; 1143 return DispatchResponse::Fail("unknown script id: " + std::to_string(scriptId)); 1144 } 1145 *source = iter->second->GetScriptSource(); 1146 1147 return DispatchResponse::Ok(); 1148} 1149 1150DispatchResponse DebuggerImpl::Pause() 1151{ 1152 if (debuggerState_ == DebuggerState::PAUSED) { 1153 return DispatchResponse::Fail("Can only perform operation while running"); 1154 } 1155 pauseOnNextByteCode_ = true; 1156 return DispatchResponse::Ok(); 1157} 1158 1159DispatchResponse DebuggerImpl::RemoveBreakpoint(const RemoveBreakpointParams ¶ms) 1160{ 1161 std::string id = params.GetBreakpointId(); 1162 LOG_DEBUGGER(INFO) << "RemoveBreakpoint: " << id; 1163 BreakpointDetails metaData{}; 1164 if (!BreakpointDetails::ParseBreakpointId(id, &metaData)) { 1165 return DispatchResponse::Fail("Parse breakpoint id failed"); 1166 } 1167 1168 auto scriptFunc = [](PtScript *) -> bool { 1169 return true; 1170 }; 1171 if (!MatchScripts(scriptFunc, metaData.url_, ScriptMatchType::URL)) { 1172 LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Unknown url: " << metaData.url_; 1173 return DispatchResponse::Fail("Unknown file name."); 1174 } 1175 1176 std::vector<DebugInfoExtractor *> extractors = GetExtractors(metaData.url_); 1177 for (auto extractor : extractors) { 1178 if (extractor == nullptr) { 1179 LOG_DEBUGGER(DEBUG) << "RemoveBreakpoint: extractor is null"; 1180 continue; 1181 } 1182 1183 auto callbackFunc = [this](const JSPtLocation &location) -> bool { 1184 LOG_DEBUGGER(INFO) << "remove breakpoint location: " << location.ToString(); 1185 return DebuggerApi::RemoveBreakpoint(jsDebugger_, location); 1186 }; 1187 if (!extractor->MatchWithLocation(callbackFunc, metaData.line_, metaData.column_, 1188 metaData.url_, GetRecordName(metaData.url_))) { 1189 LOG_DEBUGGER(ERROR) << "failed to remove breakpoint location number: " 1190 << metaData.line_ << ":" << metaData.column_; 1191 } 1192 } 1193 1194 LOG_DEBUGGER(INFO) << "remove breakpoint line number:" << metaData.line_; 1195 return DispatchResponse::Ok(); 1196} 1197 1198DispatchResponse DebuggerImpl::RemoveBreakpointsByUrl(const RemoveBreakpointsByUrlParams ¶ms) 1199{ 1200 std::string url = params.GetUrl(); 1201 auto scriptMatchCallback = [](PtScript *) -> bool { 1202 return true; 1203 }; 1204 if (!MatchScripts(scriptMatchCallback, url, ScriptMatchType::URL)) { 1205 LOG_DEBUGGER(ERROR) << "RemoveBreakpointByUrl: Unknown url: " << url; 1206 return DispatchResponse::Fail("Unknown url"); 1207 } 1208 if (!DebuggerApi::RemoveBreakpointsByUrl(jsDebugger_, url)) { 1209 return DispatchResponse::Fail("RemoveBreakpointByUrl failed"); 1210 } 1211 1212 LOG_DEBUGGER(INFO) << "All breakpoints on " << url << " are removed"; 1213 return DispatchResponse::Ok(); 1214} 1215 1216DispatchResponse DebuggerImpl::Resume([[maybe_unused]] const ResumeParams ¶ms) 1217{ 1218 if (debuggerState_ != DebuggerState::PAUSED) { 1219 return DispatchResponse::Fail("Can only perform operation while paused"); 1220 } 1221 frontend_.Resumed(vm_); 1222 debuggerState_ = DebuggerState::ENABLED; 1223 return DispatchResponse::Ok(); 1224} 1225 1226DispatchResponse DebuggerImpl::SetAsyncCallStackDepth() 1227{ 1228 return DispatchResponse::Fail("SetAsyncCallStackDepth not support now"); 1229} 1230 1231void DebuggerImpl::AddBreakpointDetail(const std::string &url, 1232 int32_t lineNumber, 1233 std::string *outId, 1234 std::vector<std::unique_ptr<Location>> *outLocations) 1235{ 1236 std::vector<PtScript *> ptScripts = MatchAllScripts(url); 1237 for (auto ptScript : ptScripts) { 1238 ScriptId scriptId = ptScript->GetScriptId(); 1239 std::unique_ptr<Location> location = std::make_unique<Location>(); 1240 location->SetScriptId(scriptId).SetLine(lineNumber).SetColumn(0); 1241 outLocations->emplace_back(std::move(location)); 1242 } 1243 BreakpointDetails metaData{lineNumber, 0, url}; 1244 *outId = BreakpointDetails::ToString(metaData); 1245} 1246 1247 1248DispatchResponse DebuggerImpl::SetBreakpointByUrl(const SetBreakpointByUrlParams ¶ms, 1249 std::string *outId, 1250 std::vector<std::unique_ptr<Location>> *outLocations, 1251 bool isSmartBreakpoint) 1252{ 1253 if (!vm_->GetJsDebuggerManager()->IsDebugMode()) { 1254 return DispatchResponse::Fail("SetBreakpointByUrl: debugger agent is not enabled"); 1255 } 1256 const std::string &url = params.GetUrl(); 1257 int32_t lineNumber = params.GetLine(); 1258 // it is not support column breakpoint now, so columnNumber is not useful 1259 int32_t columnNumber = -1; 1260 auto condition = params.HasCondition() ? params.GetCondition() : std::optional<std::string> {}; 1261 *outLocations = std::vector<std::unique_ptr<Location>>(); 1262 1263 auto scriptFunc = [](PtScript *) -> bool { 1264 return true; 1265 }; 1266 if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) { 1267 LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: Unknown url: " << url; 1268 return DispatchResponse::Fail("Unknown file name."); 1269 } 1270 1271 std::vector<DebugInfoExtractor *> extractors = GetExtractors(url); 1272 for (auto extractor : extractors) { 1273 if (extractor == nullptr) { 1274 LOG_DEBUGGER(DEBUG) << "SetBreakpointByUrl: extractor is null"; 1275 continue; 1276 } 1277 1278 auto callbackFunc = [this, &condition, &isSmartBreakpoint](const JSPtLocation &location) -> bool { 1279 LOG_DEBUGGER(INFO) << "set breakpoint location: " << location.ToString(); 1280 Local<FunctionRef> condFuncRef = FunctionRef::Undefined(vm_); 1281 if (condition.has_value() && !condition.value().empty()) { 1282 condFuncRef = CheckAndGenerateCondFunc(condition); 1283 if (condFuncRef->IsUndefined()) { 1284 LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: generate function failed"; 1285 return false; 1286 } 1287 } 1288 return DebuggerApi::SetBreakpoint(jsDebugger_, location, condFuncRef, isSmartBreakpoint); 1289 }; 1290 if (!extractor->MatchWithLocation(callbackFunc, lineNumber, columnNumber, url, GetRecordName(url))) { 1291 LOG_DEBUGGER(ERROR) << "failed to set breakpoint location number: " 1292 << lineNumber << ":" << columnNumber; 1293 return DispatchResponse::Fail("Breakpoint not found."); 1294 } 1295 } 1296 AddBreakpointDetail(url, lineNumber, outId, outLocations); 1297 return DispatchResponse::Ok(); 1298} 1299 1300DispatchResponse DebuggerImpl::SetBreakpointsActive(const SetBreakpointsActiveParams ¶ms) 1301{ 1302 breakpointsState_ = params.GetBreakpointsState(); 1303 return DispatchResponse::Ok(); 1304} 1305 1306DispatchResponse DebuggerImpl::GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams ¶ms, 1307 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations) 1308{ 1309 if (!vm_->GetJsDebuggerManager()->IsDebugMode()) { 1310 return DispatchResponse::Fail("GetPossibleAndSetBreakpointByUrl: debugger agent is not enabled"); 1311 } 1312 if (!params.HasBreakpointsList()) { 1313 return DispatchResponse::Fail("GetPossibleAndSetBreakpointByUrl: no pennding breakpoint exists"); 1314 } 1315 auto breakpointList = params.GetBreakpointsList(); 1316 for (const auto &breakpoint : *breakpointList) { 1317 if (!ProcessSingleBreakpoint(*breakpoint, outLocations)) { 1318 std::string invalidBpId = "invalid"; 1319 std::unique_ptr<BreakpointReturnInfo> bpInfo = std::make_unique<BreakpointReturnInfo>(); 1320 bpInfo->SetId(invalidBpId) 1321 .SetLineNumber(breakpoint->GetLineNumber()) 1322 .SetColumnNumber(breakpoint->GetColumnNumber()); 1323 outLocations.emplace_back(std::move(bpInfo)); 1324 } 1325 } 1326 return DispatchResponse::Ok(); 1327} 1328 1329bool DebuggerImpl::ProcessSingleBreakpoint(const BreakpointInfo &breakpoint, 1330 std::vector<std::unique_ptr<BreakpointReturnInfo>> &outLocations) 1331{ 1332 const std::string &url = breakpoint.GetUrl(); 1333 int32_t lineNumber = breakpoint.GetLineNumber(); 1334 // it is not support column breakpoint now, so columnNumber is not useful 1335 int32_t columnNumber = -1; 1336 auto condition = breakpoint.HasCondition() ? breakpoint.GetCondition() : std::optional<std::string> {}; 1337 1338 ScriptId scriptId; 1339 auto scriptFunc = [&scriptId](PtScript *script) -> bool { 1340 scriptId = script->GetScriptId(); 1341 return true; 1342 }; 1343 if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) { 1344 LOG_DEBUGGER(ERROR) << "GetPossibleAndSetBreakpointByUrl: Unknown url: " << url; 1345 return false; 1346 } 1347 1348 std::vector<DebugInfoExtractor *> extractors = GetExtractors(url); 1349 for (auto extractor : extractors) { 1350 if (extractor == nullptr) { 1351 LOG_DEBUGGER(DEBUG) << "GetPossibleAndSetBreakpointByUrl: extractor is null"; 1352 continue; 1353 } 1354 // decode and convert condition to function before doing matchWithLocation 1355 Local<FunctionRef> funcRef = FunctionRef::Undefined(vm_); 1356 if (condition.has_value() && !condition.value().empty()) { 1357 funcRef = CheckAndGenerateCondFunc(condition); 1358 if (funcRef->IsUndefined()) { 1359 LOG_DEBUGGER(ERROR) << "GetPossibleAndSetBreakpointByUrl: generate function failed"; 1360 return false; 1361 } 1362 } 1363 auto matchLocationCbFunc = [this, &funcRef](const JSPtLocation &location) -> bool { 1364 return DebuggerApi::SetBreakpoint(jsDebugger_, location, funcRef); 1365 }; 1366 if (!extractor->MatchWithLocation(matchLocationCbFunc, lineNumber, columnNumber, url, GetRecordName(url))) { 1367 LOG_DEBUGGER(ERROR) << "failed to set breakpoint location number: " << lineNumber << ":" << columnNumber; 1368 return false; 1369 } 1370 } 1371 1372 BreakpointDetails bpMetaData {lineNumber, 0, url}; 1373 std::string outId = BreakpointDetails::ToString(bpMetaData); 1374 std::unique_ptr<BreakpointReturnInfo> bpInfo = std::make_unique<BreakpointReturnInfo>(); 1375 bpInfo->SetScriptId(scriptId).SetLineNumber(lineNumber).SetColumnNumber(0).SetId(outId); 1376 outLocations.emplace_back(std::move(bpInfo)); 1377 1378 return true; 1379} 1380 1381DispatchResponse DebuggerImpl::SetNativeRange(const SetNativeRangeParams ¶ms) 1382{ 1383 nativeRanges_ = params.GetNativeRange(); 1384 return DispatchResponse::Ok(); 1385} 1386 1387DispatchResponse DebuggerImpl::ResetSingleStepper(const ResetSingleStepperParams ¶ms) 1388{ 1389 // if JS to C++ and C++ has breakpoint; it need to clear singleStepper_ 1390 if (params.GetResetSingleStepper()) { 1391 singleStepper_.reset(); 1392 } 1393 return DispatchResponse::Ok(); 1394} 1395 1396DispatchResponse DebuggerImpl::SetPauseOnExceptions(const SetPauseOnExceptionsParams ¶ms) 1397{ 1398 pauseOnException_ = params.GetState(); 1399 return DispatchResponse::Ok(); 1400} 1401 1402DispatchResponse DebuggerImpl::SetSkipAllPauses(const SetSkipAllPausesParams ¶ms) 1403{ 1404 skipAllPausess_ = params.GetSkipAllPausesState(); 1405 return DispatchResponse::Ok(); 1406} 1407 1408DispatchResponse DebuggerImpl::StepInto([[maybe_unused]] const StepIntoParams ¶ms) 1409{ 1410 if (debuggerState_ != DebuggerState::PAUSED) { 1411 return DispatchResponse::Fail("Can only perform operation while paused"); 1412 } 1413 singleStepper_ = SingleStepper::GetStepIntoStepper(vm_); 1414 if (singleStepper_ == nullptr) { 1415 LOG_DEBUGGER(ERROR) << "StepInto: singleStepper is null"; 1416 return DispatchResponse::Fail("Failed to StepInto"); 1417 } 1418 frontend_.Resumed(vm_); 1419 debuggerState_ = DebuggerState::ENABLED; 1420 return DispatchResponse::Ok(); 1421} 1422 1423DispatchResponse DebuggerImpl::SmartStepInto(const SmartStepIntoParams ¶ms) 1424{ 1425 if (debuggerState_ != DebuggerState::PAUSED) { 1426 return DispatchResponse::Fail("Can only perform operation while paused"); 1427 } 1428 [[maybe_unused]] std::string outId; 1429 [[maybe_unused]] std::vector<std::unique_ptr<Location>> outLocations; 1430 return SetBreakpointByUrl(*(params.GetSetBreakpointByUrlParams()), &outId, &outLocations, true); 1431} 1432 1433DispatchResponse DebuggerImpl::StepOut() 1434{ 1435 if (debuggerState_ != DebuggerState::PAUSED) { 1436 return DispatchResponse::Fail("Can only perform operation while paused"); 1437 } 1438 singleStepper_ = SingleStepper::GetStepOutStepper(vm_); 1439 if (singleStepper_ == nullptr) { 1440 LOG_DEBUGGER(ERROR) << "StepOut: singleStepper is null"; 1441 return DispatchResponse::Fail("Failed to StepOut"); 1442 } 1443 frontend_.Resumed(vm_); 1444 debuggerState_ = DebuggerState::ENABLED; 1445 return DispatchResponse::Ok(); 1446} 1447 1448DispatchResponse DebuggerImpl::StepOver([[maybe_unused]] const StepOverParams ¶ms) 1449{ 1450 if (debuggerState_ != DebuggerState::PAUSED) { 1451 return DispatchResponse::Fail("Can only perform operation while paused"); 1452 } 1453 singleStepper_ = SingleStepper::GetStepOverStepper(vm_); 1454 if (singleStepper_ == nullptr) { 1455 LOG_DEBUGGER(ERROR) << "StepOver: singleStepper is null"; 1456 return DispatchResponse::Fail("Failed to StepOver"); 1457 } 1458 frontend_.Resumed(vm_); 1459 debuggerState_ = DebuggerState::ENABLED; 1460 return DispatchResponse::Ok(); 1461} 1462 1463DispatchResponse DebuggerImpl::SetBlackboxPatterns() 1464{ 1465 return DispatchResponse::Fail("SetBlackboxPatterns not support now"); 1466} 1467 1468DispatchResponse DebuggerImpl::SetMixedDebugEnabled([[maybe_unused]] const SetMixedDebugParams ¶ms) 1469{ 1470 vm_->GetJsDebuggerManager()->SetMixedDebugEnabled(params.GetEnabled()); 1471 vm_->GetJsDebuggerManager()->SetMixedStackEnabled(params.GetMixedStackEnabled()); 1472 mixStackEnabled_ = params.GetMixedStackEnabled(); 1473 return DispatchResponse::Ok(); 1474} 1475 1476DispatchResponse DebuggerImpl::ReplyNativeCalling([[maybe_unused]] const ReplyNativeCallingParams ¶ms) 1477{ 1478 frontend_.Resumed(vm_); 1479 if (params.GetUserCode()) { 1480 singleStepper_.reset(); 1481 } 1482 return DispatchResponse::Ok(); 1483} 1484 1485DispatchResponse DebuggerImpl::DropFrame(const DropFrameParams ¶ms) 1486{ 1487 if (debuggerState_ != DebuggerState::PAUSED) { 1488 return DispatchResponse::Fail("Can only perform operation while paused"); 1489 } 1490 uint32_t droppedDepth = 1; 1491 if (params.HasDroppedDepth()) { 1492 droppedDepth = params.GetDroppedDepth(); 1493 if (droppedDepth == 0) { 1494 return DispatchResponse::Ok(); 1495 } 1496 if (droppedDepth > 1) { 1497 return DispatchResponse::Fail("Not yet support dropping multiple frames"); 1498 } 1499 } 1500 uint32_t stackDepth = DebuggerApi::GetStackDepth(vm_); 1501 if (droppedDepth > stackDepth) { 1502 return DispatchResponse::Fail("The input depth exceeds stackDepth"); 1503 } 1504 if (droppedDepth == stackDepth) { 1505 return DispatchResponse::Fail("The bottom frame cannot be dropped"); 1506 } 1507 uint32_t stackDepthOverBuiltin = DebuggerApi::GetStackDepthOverBuiltin(vm_); 1508 if (droppedDepth >= stackDepthOverBuiltin) { 1509 return DispatchResponse::Fail("Frames to be dropped contain builtin frame"); 1510 } 1511 if (DebuggerApi::CheckIsSendableMethod(vm_)) { 1512 return DispatchResponse::Fail("Not yet support sendable method"); 1513 } 1514 if (!DebuggerApi::CheckPromiseQueueSize(vm_)) { 1515 return DispatchResponse::Fail("Detect promise enqueued in current frame"); 1516 } 1517 for (uint32_t i = 0; i < droppedDepth; i++) { 1518 DebuggerApi::DropLastFrame(vm_); 1519 } 1520 pauseOnNextByteCode_ = true; 1521 frontend_.RunIfWaitingForDebugger(vm_); 1522 debuggerState_ = DebuggerState::ENABLED; 1523 return DispatchResponse::Ok(); 1524} 1525 1526DispatchResponse DebuggerImpl::ClientDisconnect() 1527{ 1528 DeviceDisconnectCallback cb = vm_->GetDeviceDisconnectCallback(); 1529 if (cb == nullptr) { 1530 LOG_DEBUGGER(DEBUG) << "DebuggerImpl::ClientDisconnect callback is nullptr"; 1531 } else { 1532 cb(); 1533 } 1534 return DispatchResponse::Ok(); 1535} 1536 1537DispatchResponse DebuggerImpl::CallFunctionOn([[maybe_unused]] const CallFunctionOnParams ¶ms, 1538 std::unique_ptr<RemoteObject> *outRemoteObject, 1539 [[maybe_unused]] std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails) 1540{ 1541 // get callFrameId 1542 CallFrameId callFrameId = params.GetCallFrameId(); 1543 if (callFrameId < 0 || callFrameId >= static_cast<CallFrameId>(callFrameHandlers_.size())) { 1544 return DispatchResponse::Fail("Invalid callFrameId."); 1545 } 1546 // get function declaration 1547 std::string functionDeclaration = params.GetFunctionDeclaration(); 1548 std::vector<uint8_t> dest; 1549 if (!DecodeAndCheckBase64(functionDeclaration, dest)) { 1550 LOG_DEBUGGER(ERROR) << "CallFunctionOn: base64 decode failed"; 1551 return DispatchResponse::Fail("base64 decode failed, functionDeclaration: " + 1552 functionDeclaration); 1553 } 1554 auto funcRef = DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(), 1555 JSPandaFile::ENTRY_FUNCTION_NAME); 1556 // call function 1557 auto res = DebuggerApi::CallFunctionOnCall(const_cast<EcmaVM *>(vm_), funcRef, 1558 callFrameHandlers_[callFrameId]); 1559 if (vm_->GetJSThread()->HasPendingException()) { 1560 LOG_DEBUGGER(ERROR) << "CallFunctionOn: has pending exception"; 1561 std::string msg; 1562 DebuggerApi::HandleUncaughtException(vm_, msg); 1563 *outRemoteObject = RemoteObject::FromTagged(vm_, 1564 Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, msg.data()))); 1565 return DispatchResponse::Fail(msg); 1566 } 1567 1568 *outRemoteObject = RemoteObject::FromTagged(vm_, res); 1569 runtime_->CacheObjectIfNeeded(res, (*outRemoteObject).get()); 1570 return DispatchResponse::Ok(); 1571} 1572 1573void DebuggerImpl::CleanUpOnPaused() 1574{ 1575 CleanUpRuntimeProperties(); 1576 callFrameHandlers_.clear(); 1577 scopeObjects_.clear(); 1578} 1579 1580void DebuggerImpl::CleanUpRuntimeProperties() 1581{ 1582 LOG_DEBUGGER(INFO) << "CleanUpRuntimeProperties OnPaused"; 1583 if (runtime_->properties_.empty()) { 1584 return; 1585 } 1586 RemoteObjectId validObjId = runtime_->curObjectId_ - 1; 1587 for (; validObjId >= 0; validObjId--) { 1588 runtime_->properties_[validObjId].FreeGlobalHandleAddr(); 1589 } 1590 runtime_->curObjectId_ = 0; 1591 runtime_->properties_.clear(); 1592} 1593 1594std::string DebuggerImpl::Trim(const std::string &str) 1595{ 1596 std::string ret = str; 1597 // If ret has only ' ', remove all charactors. 1598 ret.erase(ret.find_last_not_of(' ') + 1); 1599 // If ret has only ' ', remove all charactors. 1600 ret.erase(0, ret.find_first_not_of(' ')); 1601 return ret; 1602} 1603 1604DebugInfoExtractor *DebuggerImpl::GetExtractor(const JSPandaFile *jsPandaFile) 1605{ 1606 return JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); 1607} 1608 1609// mainly used for breakpoints to match location 1610std::vector<DebugInfoExtractor *> DebuggerImpl::GetExtractors(const std::string &url) 1611{ 1612 std::vector<DebugInfoExtractor *> extractors; 1613 // match patch file first if it contains diff for the url, and currently only support the file 1614 // specified by the url change as a whole 1615 extractors = DebuggerApi::GetPatchExtractors(vm_, url); 1616 if (!extractors.empty()) { 1617 return extractors; 1618 } 1619 1620 std::vector<PtScript *> ptScripts = MatchAllScripts(url); 1621 for (auto ptScript : ptScripts) { 1622 std::string fileName = ptScript->GetFileName(); 1623 const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(fileName.c_str()).get(); 1624 if (jsPandaFile == nullptr) { 1625 continue; 1626 } 1627 DebugInfoExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); 1628 if (extractor == nullptr) { 1629 LOG_DEBUGGER(DEBUG) << "GetPossibleBreakpoints: extractor is null"; 1630 continue; 1631 } 1632 extractors.emplace_back(extractor); 1633 } 1634 return extractors; 1635} 1636 1637bool DebuggerImpl::GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames, bool getScope) 1638{ 1639 CallFrameId callFrameId = 0; 1640 auto walkerFunc = [this, &callFrameId, &callFrames, &getScope](const FrameHandler *frameHandler) -> StackState { 1641 if (DebuggerApi::IsNativeMethod(frameHandler)) { 1642 LOG_DEBUGGER(INFO) << "GenerateCallFrames: Skip CFrame and Native method"; 1643 return StackState::CONTINUE; 1644 } 1645 std::unique_ptr<CallFrame> callFrame = std::make_unique<CallFrame>(); 1646 if (!GenerateCallFrame(callFrame.get(), frameHandler, callFrameId, getScope)) { 1647 if (callFrameId == 0) { 1648 return StackState::FAILED; 1649 } 1650 } else { 1651 SaveCallFrameHandler(frameHandler); 1652 callFrames->emplace_back(std::move(callFrame)); 1653 callFrameId++; 1654 } 1655 return StackState::CONTINUE; 1656 }; 1657 return DebuggerApi::StackWalker(vm_, walkerFunc); 1658} 1659 1660void DebuggerImpl::SaveCallFrameHandler(const FrameHandler *frameHandler) 1661{ 1662 auto handlerPtr = DebuggerApi::NewFrameHandler(vm_); 1663 *handlerPtr = *frameHandler; 1664 callFrameHandlers_.emplace_back(handlerPtr); 1665} 1666 1667bool DebuggerImpl::GenerateCallFrame(CallFrame *callFrame, const FrameHandler *frameHandler, 1668 CallFrameId callFrameId, bool getScope) 1669{ 1670 if (!frameHandler->HasFrame()) { 1671 return false; 1672 } 1673 Method *method = DebuggerApi::GetMethod(frameHandler); 1674 auto methodId = method->GetMethodId(); 1675 const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); 1676 DebugInfoExtractor *extractor = GetExtractor(jsPandaFile); 1677 if (extractor == nullptr) { 1678 LOG_DEBUGGER(DEBUG) << "GenerateCallFrame: extractor is null"; 1679 return false; 1680 } 1681 1682 // functionName 1683 std::string functionName = method->ParseFunctionName(); 1684 1685 // location 1686 std::unique_ptr<Location> location = std::make_unique<Location>(); 1687 std::string url = extractor->GetSourceFile(methodId); 1688 auto scriptFunc = [&location](PtScript *script) -> bool { 1689 location->SetScriptId(script->GetScriptId()); 1690 return true; 1691 }; 1692 if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) { 1693 LOG_DEBUGGER(ERROR) << "GenerateCallFrame: Unknown url: " << url; 1694 return false; 1695 } 1696 auto callbackLineFunc = [&location](int32_t line) -> bool { 1697 location->SetLine(line); 1698 return true; 1699 }; 1700 auto callbackColumnFunc = [&location](int32_t column) -> bool { 1701 location->SetColumn(column); 1702 return true; 1703 }; 1704 if (!extractor->MatchLineWithOffset(callbackLineFunc, methodId, DebuggerApi::GetBytecodeOffset(frameHandler)) || 1705 !extractor->MatchColumnWithOffset(callbackColumnFunc, methodId, DebuggerApi::GetBytecodeOffset(frameHandler))) { 1706 LOG_DEBUGGER(ERROR) << "GenerateCallFrame: unknown offset: " << DebuggerApi::GetBytecodeOffset(frameHandler); 1707 return false; 1708 } 1709 std::unique_ptr<RemoteObject> thisObj = std::make_unique<RemoteObject>(); 1710 std::vector<std::unique_ptr<Scope>> scopeChain; 1711 DebuggerImpl::GenerateScopeChains(getScope, frameHandler, jsPandaFile, scopeChain, thisObj); 1712 callFrame->SetCallFrameId(callFrameId) 1713 .SetFunctionName(functionName) 1714 .SetLocation(std::move(location)) 1715 .SetUrl(url) 1716 .SetScopeChain(std::move(scopeChain)) 1717 .SetThis(std::move(thisObj)); 1718 return true; 1719} 1720 1721void DebuggerImpl::GenerateScopeChains(bool getScope, 1722 const FrameHandler *frameHandler, 1723 const JSPandaFile *jsPandaFile, 1724 std::vector<std::unique_ptr<Scope>> &scopeChain, 1725 std::unique_ptr<RemoteObject> &thisObj) 1726{ 1727 // scopeChain & this 1728 1729 thisObj->SetType(ObjectType::Undefined); 1730 JSThread *thread = vm_->GetJSThread(); 1731 if (getScope) { 1732 scopeChain.emplace_back(GetLocalScopeChain(frameHandler, &thisObj)); 1733 // generate closure scopes 1734 auto closureScopeChains = GetClosureScopeChains(frameHandler, &thisObj); 1735 for (auto &scope : closureScopeChains) { 1736 scopeChain.emplace_back(std::move(scope)); 1737 } 1738 if (jsPandaFile != nullptr && !jsPandaFile->IsBundlePack() && jsPandaFile->IsNewVersion()) { 1739 JSHandle<JSTaggedValue> currentModule(thread, DebuggerApi::GetCurrentModule(vm_)); 1740 if (currentModule->IsSourceTextModule()) { // CJS module is string 1741 scopeChain.emplace_back(GetModuleScopeChain(frameHandler)); 1742 } 1743 } 1744 scopeChain.emplace_back(GetGlobalScopeChain(frameHandler)); 1745 } 1746} 1747 1748std::unique_ptr<Scope> DebuggerImpl::GetLocalScopeChain(const FrameHandler *frameHandler, 1749 std::unique_ptr<RemoteObject> *thisObj) 1750{ 1751 auto localScope = std::make_unique<Scope>(); 1752 1753 Method *method = DebuggerApi::GetMethod(frameHandler); 1754 auto methodId = method->GetMethodId(); 1755 const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); 1756 DebugInfoExtractor *extractor = GetExtractor(jsPandaFile); 1757 if (extractor == nullptr) { 1758 LOG_DEBUGGER(ERROR) << "GetScopeChain: extractor is null"; 1759 return localScope; 1760 } 1761 1762 std::unique_ptr<RemoteObject> local = std::make_unique<RemoteObject>(); 1763 Local<ObjectRef> localObj = ObjectRef::New(vm_); 1764 local->SetType(ObjectType::Object) 1765 .SetObjectId(runtime_->curObjectId_) 1766 .SetClassName(ObjectClassName::Object) 1767 .SetDescription(RemoteObject::ObjectDescription); 1768 auto *sp = DebuggerApi::GetSp(frameHandler); 1769 scopeObjects_[sp][Scope::Type::Local()].push_back(runtime_->curObjectId_); 1770 DebuggerApi::AddInternalProperties(vm_, localObj, ArkInternalValueType::Scope, runtime_->internalObjects_); 1771 runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, localObj); 1772 1773 Local<JSValueRef> thisVal = JSNApiHelper::ToLocal<JSValueRef>( 1774 JSHandle<JSTaggedValue>(vm_->GetJSThread(), JSTaggedValue::Hole())); 1775 GetLocalVariables(frameHandler, methodId, jsPandaFile, thisVal, localObj); 1776 *thisObj = RemoteObject::FromTagged(vm_, thisVal); 1777 runtime_->CacheObjectIfNeeded(thisVal, (*thisObj).get()); 1778 1779 const LineNumberTable &lines = extractor->GetLineNumberTable(methodId); 1780 std::unique_ptr<Location> startLoc = std::make_unique<Location>(); 1781 std::unique_ptr<Location> endLoc = std::make_unique<Location>(); 1782 auto scriptFunc = [&startLoc, &endLoc, lines](PtScript *script) -> bool { 1783 startLoc->SetScriptId(script->GetScriptId()) 1784 .SetLine(lines.front().line) 1785 .SetColumn(0); 1786 endLoc->SetScriptId(script->GetScriptId()) 1787 .SetLine(lines.back().line + 1) 1788 .SetColumn(0); 1789 return true; 1790 }; 1791 if (MatchScripts(scriptFunc, extractor->GetSourceFile(methodId), ScriptMatchType::URL)) { 1792 localScope->SetType(Scope::Type::Local()) 1793 .SetObject(std::move(local)) 1794 .SetStartLocation(std::move(startLoc)) 1795 .SetEndLocation(std::move(endLoc)); 1796 } 1797 1798 return localScope; 1799} 1800 1801std::vector<std::unique_ptr<Scope>> DebuggerImpl::GetClosureScopeChains(const FrameHandler *frameHandler, 1802 std::unique_ptr<RemoteObject> *thisObj) 1803{ 1804 std::vector<std::unique_ptr<Scope>> closureScopes; 1805 Method *method = DebuggerApi::GetMethod(frameHandler); 1806 EntityId methodId = method->GetMethodId(); 1807 const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); 1808 DebugInfoExtractor *extractor = GetExtractor(jsPandaFile); 1809 JSThread *thread = vm_->GetJSThread(); 1810 1811 if (extractor == nullptr) { 1812 LOG_DEBUGGER(ERROR) << "GetClosureScopeChains: extractor is null"; 1813 return closureScopes; 1814 } 1815 1816 JSMutableHandle<JSTaggedValue> envHandle = JSMutableHandle<JSTaggedValue>( 1817 thread, DebuggerApi::GetEnv(frameHandler)); 1818 JSMutableHandle<JSTaggedValue> valueHandle = JSMutableHandle<JSTaggedValue>(thread, JSTaggedValue::Hole()); 1819 JSTaggedValue currentEnv = envHandle.GetTaggedValue(); 1820 if (!currentEnv.IsTaggedArray()) { 1821 LOG_DEBUGGER(DEBUG) << "GetClosureScopeChains: currentEnv is invalid"; 1822 return closureScopes; 1823 } 1824 // check if GetLocalScopeChain has already found and set 'this' value 1825 bool thisFound = (*thisObj)->HasValue(); 1826 bool closureVarFound = false; 1827 // currentEnv = currentEnv->parent until currentEnv becomes undefined 1828 for (; currentEnv.IsTaggedArray(); currentEnv = LexicalEnv::Cast(currentEnv.GetTaggedObject())->GetParentEnv()) { 1829 LexicalEnv *lexicalEnv = LexicalEnv::Cast(currentEnv.GetTaggedObject()); 1830 envHandle.Update(currentEnv); 1831 if (lexicalEnv->GetScopeInfo().IsHole()) { 1832 continue; 1833 } 1834 auto closureScope = std::make_unique<Scope>(); 1835 auto result = JSNativePointer::Cast(lexicalEnv->GetScopeInfo().GetTaggedObject())->GetExternalPointer(); 1836 ScopeDebugInfo *scopeDebugInfo = reinterpret_cast<ScopeDebugInfo *>(result); 1837 std::unique_ptr<RemoteObject> closure = std::make_unique<RemoteObject>(); 1838 Local<ObjectRef> closureScopeObj = ObjectRef::New(vm_); 1839 1840 for (const auto &[name, slot] : scopeDebugInfo->scopeInfo) { 1841 if (IsVariableSkipped(name.c_str())) { 1842 continue; 1843 } 1844 currentEnv = envHandle.GetTaggedValue(); 1845 lexicalEnv = LexicalEnv::Cast(currentEnv.GetTaggedObject()); 1846 valueHandle.Update(lexicalEnv->GetProperties(slot)); 1847 Local<JSValueRef> value = JSNApiHelper::ToLocal<JSValueRef>(valueHandle); 1848 Local<JSValueRef> varName = StringRef::NewFromUtf8(vm_, name.c_str()); 1849 // found 'this' and 'this' is not set in GetLocalScopechain 1850 if (!thisFound && name == "this") { 1851 *thisObj = RemoteObject::FromTagged(vm_, value); 1852 // cache 'this' object 1853 runtime_->CacheObjectIfNeeded(value, (*thisObj).get()); 1854 thisFound = true; 1855 continue; 1856 } 1857 // found closure variable in current lexenv 1858 closureVarFound = true; 1859 // if value is hole, should manually set it to undefined 1860 // otherwise after DefineProperty, corresponding varName 1861 // will become undefined 1862 if (value->IsHole()) { 1863 valueHandle.Update(JSTaggedValue::Undefined()); 1864 value = JSNApiHelper::ToLocal<JSValueRef>(valueHandle); 1865 } 1866 PropertyAttribute descriptor(value, true, true, true); 1867 closureScopeObj->DefineProperty(vm_, varName, descriptor); 1868 } 1869 // at least one closure variable has been found 1870 if (closureVarFound) { 1871 closure->SetType(ObjectType::Object) 1872 .SetObjectId(runtime_->curObjectId_) 1873 .SetClassName(ObjectClassName::Object) 1874 .SetDescription(RemoteObject::ObjectDescription); 1875 1876 auto scriptFunc = []([[maybe_unused]] PtScript *script) -> bool { 1877 return true; 1878 }; 1879 if (MatchScripts(scriptFunc, extractor->GetSourceFile(methodId), ScriptMatchType::URL)) { 1880 closureScope->SetType(Scope::Type::Closure()).SetObject(std::move(closure)); 1881 DebuggerApi::AddInternalProperties( 1882 vm_, closureScopeObj, ArkInternalValueType::Scope, runtime_->internalObjects_); 1883 auto *sp = DebuggerApi::GetSp(frameHandler); 1884 scopeObjects_[sp][Scope::Type::Closure()].push_back(runtime_->curObjectId_); 1885 runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, closureScopeObj); 1886 closureScopes.emplace_back(std::move(closureScope)); 1887 } 1888 } 1889 currentEnv = envHandle.GetTaggedValue(); 1890 closureVarFound = false; 1891 } 1892 return closureScopes; 1893} 1894 1895std::unique_ptr<Scope> DebuggerImpl::GetModuleScopeChain(const FrameHandler *frameHandler) 1896{ 1897 auto moduleScope = std::make_unique<Scope>(); 1898 1899 std::unique_ptr<RemoteObject> module = std::make_unique<RemoteObject>(); 1900 Local<ObjectRef> moduleObj = ObjectRef::New(vm_); 1901 module->SetType(ObjectType::Object) 1902 .SetObjectId(runtime_->curObjectId_) 1903 .SetClassName(ObjectClassName::Object) 1904 .SetDescription(RemoteObject::ObjectDescription); 1905 moduleScope->SetType(Scope::Type::Module()).SetObject(std::move(module)); 1906 DebuggerApi::AddInternalProperties(vm_, moduleObj, ArkInternalValueType::Scope, runtime_->internalObjects_); 1907 auto *sp = DebuggerApi::GetSp(frameHandler); 1908 scopeObjects_[sp][Scope::Type::Module()].push_back(runtime_->curObjectId_); 1909 runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, moduleObj); 1910 JSThread *thread = vm_->GetJSThread(); 1911 JSHandle<JSTaggedValue> currentModule(thread, DebuggerApi::GetCurrentModule(vm_)); 1912 DebuggerApi::GetLocalExportVariables(vm_, moduleObj, currentModule, false); 1913 DebuggerApi::GetIndirectExportVariables(vm_, moduleObj, currentModule); 1914 DebuggerApi::GetImportVariables(vm_, moduleObj, currentModule); 1915 return moduleScope; 1916} 1917 1918void DebuggerImpl::GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId, 1919 const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj) 1920{ 1921 auto *extractor = GetExtractor(jsPandaFile); 1922 Local<JSValueRef> value = JSValueRef::Undefined(vm_); 1923 // in case of arrow function, which doesn't have this in local variable table 1924 for (const auto &localVariableInfo : extractor->GetLocalVariableTable(methodId)) { 1925 std::string varName = localVariableInfo.name; 1926 int32_t regIndex = localVariableInfo.regNumber; 1927 uint32_t bcOffset = DebuggerApi::GetBytecodeOffset(frameHandler); 1928 // if the bytecodeOffset is not in the range of the variable's scope, 1929 // which is indicated as [start_offset, end_offset), ignore it. 1930 if (!IsWithinVariableScope(localVariableInfo, bcOffset)) { 1931 continue; 1932 } 1933 1934 if (IsVariableSkipped(varName)) { 1935 continue; 1936 } 1937 1938 value = DebuggerApi::GetVRegValue(vm_, frameHandler, regIndex); 1939 if (varName == "this") { 1940 LOG_DEBUGGER(INFO) << "find 'this' in local variable table"; 1941 thisVal = value; 1942 continue; 1943 } 1944 Local<JSValueRef> name = JSValueRef::Undefined(vm_); 1945 if (varName == "4funcObj") { 1946 if (value->IsFunction(vm_)) { 1947 auto funcName = Local<FunctionRef>(value)->GetName(vm_)->ToString(vm_); 1948 name = StringRef::NewFromUtf8(vm_, funcName.c_str()); 1949 } else { 1950 continue; 1951 } 1952 } else { 1953 name = StringRef::NewFromUtf8(vm_, varName.c_str()); 1954 } 1955 PropertyAttribute descriptor(value, true, true, true); 1956 localObj->DefineProperty(vm_, name, descriptor); 1957 } 1958} 1959 1960bool DebuggerImpl::IsWithinVariableScope(const LocalVariableInfo &localVariableInfo, uint32_t bcOffset) 1961{ 1962 return bcOffset >= localVariableInfo.startOffset && bcOffset < localVariableInfo.endOffset; 1963} 1964 1965bool DebuggerImpl::IsVariableSkipped(const std::string &varName) 1966{ 1967 return varName == "4newTarget" || varName == "0this" || varName == "0newTarget" || varName == "0funcObj"; 1968} 1969 1970std::unique_ptr<Scope> DebuggerImpl::GetGlobalScopeChain(const FrameHandler *frameHandler) 1971{ 1972 auto globalScope = std::make_unique<Scope>(); 1973 1974 std::unique_ptr<RemoteObject> global = std::make_unique<RemoteObject>(); 1975 Local<ObjectRef> globalObj = ObjectRef::New(vm_); 1976 global->SetType(ObjectType::Object) 1977 .SetObjectId(runtime_->curObjectId_) 1978 .SetClassName(ObjectClassName::Global) 1979 .SetDescription(RemoteObject::GlobalDescription); 1980 globalScope->SetType(Scope::Type::Global()).SetObject(std::move(global)); 1981 globalObj = JSNApi::GetGlobalObject(vm_); 1982 DebuggerApi::AddInternalProperties(vm_, globalObj, ArkInternalValueType::Scope, runtime_->internalObjects_); 1983 auto *sp = DebuggerApi::GetSp(frameHandler); 1984 scopeObjects_[sp][Scope::Type::Global()].push_back(runtime_->curObjectId_); 1985 runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, globalObj); 1986 return globalScope; 1987} 1988 1989void DebuggerImpl::UpdateScopeObject(const FrameHandler *frameHandler, 1990 std::string_view varName, Local<JSValueRef> newVal, const std::string& scope) 1991{ 1992 auto *sp = DebuggerApi::GetSp(frameHandler); 1993 auto iter = scopeObjects_.find(sp); 1994 if (iter == scopeObjects_.end()) { 1995 LOG_DEBUGGER(ERROR) << "UpdateScopeObject: object not found"; 1996 return; 1997 } 1998 1999 for (auto objectId : scopeObjects_[sp][scope]) { 2000 Local<ObjectRef> localObj = runtime_->properties_[objectId].ToLocal(vm_); 2001 Local<JSValueRef> name = StringRef::NewFromUtf8(vm_, varName.data()); 2002 if (localObj->Has(vm_, name)) { 2003 LOG_DEBUGGER(DEBUG) << "UpdateScopeObject: set new value"; 2004 PropertyAttribute descriptor(newVal, true, true, true); 2005 localObj->DefineProperty(vm_, name, descriptor); 2006 return; 2007 } 2008 } 2009 LOG_DEBUGGER(ERROR) << "UpdateScopeObject: not found " << varName; 2010} 2011 2012void DebuggerImpl::ClearSingleStepper() 2013{ 2014 // if current depth is 0, then it is safe to reset 2015 if (singleStepper_ != nullptr && DebuggerApi::GetStackDepth(vm_) == 0) { 2016 singleStepper_.reset(); 2017 } 2018} 2019 2020std::optional<std::string> DebuggerImpl::CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression, 2021 std::unique_ptr<RemoteObject> *result) 2022{ 2023 if (DebuggerApi::IsNativeMethod(vm_)) { 2024 *result = RemoteObject::FromTagged(vm_, 2025 Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Native Frame not support."))); 2026 return "Native Frame not support."; 2027 } 2028 DebugInfoExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_)); 2029 if (extractor == nullptr) { 2030 *result = RemoteObject::FromTagged(vm_, 2031 Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Internal error."))); 2032 return "Internal error."; 2033 } 2034 std::string varName = expression; 2035 std::string varValue; 2036 std::string::size_type indexEqual = expression.find_first_of('=', 0); 2037 if (indexEqual != std::string::npos) { 2038 varName = Trim(expression.substr(0, indexEqual)); 2039 varValue = Trim(expression.substr(indexEqual + 1, expression.length())); 2040 } 2041 2042 Local<StringRef> name = StringRef::NewFromUtf8(vm_, varName.c_str()); 2043 FrameHandler *frameHandler = callFrameHandlers_[callFrameId].get(); 2044 if (varValue.empty()) { 2045 Local<JSValueRef> ret = DebuggerExecutor::GetValue(vm_, frameHandler, name); 2046 if (!ret.IsEmpty()) { 2047 *result = RemoteObject::FromTagged(vm_, ret); 2048 runtime_->CacheObjectIfNeeded(ret, (*result).get()); 2049 return {}; 2050 } 2051 } else { 2052 Local<JSValueRef> value = ConvertToLocal(varValue); 2053 if (value.IsEmpty()) { 2054 return "Unsupported expression."; 2055 } 2056 JsDebuggerManager *mgr = vm_->GetJsDebuggerManager(); 2057 mgr->SetEvalFrameHandler(callFrameHandlers_[callFrameId]); 2058 bool ret = DebuggerExecutor::SetValue(vm_, frameHandler, name, value); 2059 mgr->SetEvalFrameHandler(nullptr); 2060 if (ret) { 2061 *result = RemoteObject::FromTagged(vm_, value); 2062 return {}; 2063 } 2064 } 2065 2066 *result = RemoteObject::FromTagged(vm_, 2067 Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Unsupported expression."))); 2068 return "Unsupported expression."; 2069} 2070 2071Local<JSValueRef> DebuggerImpl::ConvertToLocal(const std::string &varValue) 2072{ 2073 Local<JSValueRef> taggedValue; 2074 if (varValue.empty()) { 2075 taggedValue = NumberRef::New(vm_, 0); 2076 } else if (varValue == "false") { 2077 taggedValue = JSValueRef::False(vm_); 2078 } else if (varValue == "true") { 2079 taggedValue = JSValueRef::True(vm_); 2080 } else if (varValue == "undefined") { 2081 taggedValue = JSValueRef::Undefined(vm_); 2082 } else if (varValue[0] == '\"' && varValue[varValue.length() - 1] == '\"') { 2083 // 2 : 2 means length 2084 taggedValue = StringRef::NewFromUtf8(vm_, varValue.substr(1, varValue.length() - 2).c_str()); 2085 } else { 2086 auto begin = reinterpret_cast<const uint8_t *>((varValue.c_str())); 2087 auto end = begin + varValue.length(); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) 2088 double d = DebuggerApi::StringToDouble(begin, end, 0); 2089 if (!std::isnan(d)) { 2090 taggedValue = NumberRef::New(vm_, d); 2091 } 2092 } 2093 return taggedValue; 2094} 2095 2096bool DebuggerImpl::DecodeAndCheckBase64(const std::string &src, std::vector<uint8_t> &dest) 2097{ 2098 dest.resize(PtBase64::DecodedSize(src.size())); 2099 auto [numOctets, done] = PtBase64::Decode(dest.data(), src.data(), src.size()); 2100 dest.resize(numOctets); 2101 if ((done && numOctets > panda_file::File::MAGIC_SIZE) && 2102 memcmp(dest.data(), panda_file::File::MAGIC.data(), panda_file::File::MAGIC_SIZE) == 0) { 2103 return true; 2104 } 2105 return false; 2106} 2107 2108Local<FunctionRef> DebuggerImpl::CheckAndGenerateCondFunc(const std::optional<std::string> &condition) 2109{ 2110 std::vector<uint8_t> dest; 2111 if (DecodeAndCheckBase64(condition.value(), dest)) { 2112 Local<FunctionRef> funcRef = 2113 DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(), JSPandaFile::ENTRY_FUNCTION_NAME); 2114 if (!funcRef->IsUndefined()) { 2115 return funcRef; 2116 } 2117 } 2118 return FunctionRef::Undefined(vm_); 2119} 2120} // namespace panda::ecmascript::tooling 2121