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