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