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