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