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 &params)
1041{
1042    location_ = *params.GetLocation();
1043    return DispatchResponse::Ok();
1044}
1045
1046DispatchResponse DebuggerImpl::Enable([[maybe_unused]] const EnableParams &params, 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 &params,
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 &params,
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 &params, 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 &params)
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 &params)
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 &params)
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 &params,
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 &params)
1301{
1302    breakpointsState_ = params.GetBreakpointsState();
1303    return DispatchResponse::Ok();
1304}
1305
1306DispatchResponse DebuggerImpl::GetPossibleAndSetBreakpointByUrl(const GetPossibleAndSetBreakpointParams &params,
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 &params)
1382{
1383    nativeRanges_ = params.GetNativeRange();
1384    return DispatchResponse::Ok();
1385}
1386
1387DispatchResponse DebuggerImpl::ResetSingleStepper(const ResetSingleStepperParams &params)
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 &params)
1397{
1398    pauseOnException_ = params.GetState();
1399    return DispatchResponse::Ok();
1400}
1401
1402DispatchResponse DebuggerImpl::SetSkipAllPauses(const SetSkipAllPausesParams &params)
1403{
1404    skipAllPausess_ = params.GetSkipAllPausesState();
1405    return DispatchResponse::Ok();
1406}
1407
1408DispatchResponse DebuggerImpl::StepInto([[maybe_unused]] const StepIntoParams &params)
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 &params)
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 &params)
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 &params)
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 &params)
1477{
1478    frontend_.Resumed(vm_);
1479    if (params.GetUserCode()) {
1480        singleStepper_.reset();
1481    }
1482    return DispatchResponse::Ok();
1483}
1484
1485DispatchResponse DebuggerImpl::DropFrame(const DropFrameParams &params)
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 &params,
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