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