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