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