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