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