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