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