• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "base/pt_base64.h"
19 #include "base/pt_events.h"
20 #include "base/pt_params.h"
21 #include "base/pt_returns.h"
22 #include "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 DEBUGGER_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     vm_->GetJsDebuggerManager()->SetLocalScopeUpdater(&updaterFunc_);
55     vm_->GetJsDebuggerManager()->SetStepperFunc(&stepperFunc_);
56 }
57 
~DebuggerImpl()58 DebuggerImpl::~DebuggerImpl()
59 {
60     DebuggerApi::DestroyJSDebugger(jsDebugger_);
61 }
62 
NotifyScriptParsed(ScriptId scriptId,const std::string & fileName,std::string_view entryPoint)63 bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, std::string_view entryPoint)
64 {
65 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) \
66     && !defined(PANDA_TARGET_ANDROID) && !defined(PANDA_TARGET_IOS)
67     if (fileName.substr(0, DATA_APP_PATH.length()) != DATA_APP_PATH) {
68         LOG_DEBUGGER(DEBUG) << "NotifyScriptParsed: unsupport file: " << fileName;
69         return false;
70     }
71 #endif
72 
73     const JSPandaFile *jsPandaFile = nullptr;
74     JSPandaFileManager::GetInstance()->EnumerateJSPandaFiles([&jsPandaFile, &fileName](
75         const panda::ecmascript::JSPandaFile *pf) {
76         if (fileName == pf->GetJSPandaFileDesc().c_str()) {
77             jsPandaFile = pf;
78             return false;
79         }
80         return true;
81     });
82     if (jsPandaFile == nullptr) {
83         LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: unknown file: " << fileName;
84         return false;
85     }
86 
87     DebugInfoExtractor *extractor = GetExtractor(jsPandaFile);
88     if (extractor == nullptr) {
89         LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: Unsupported file: " << fileName;
90         return false;
91     }
92 
93     auto mainMethodIndex = panda_file::File::EntityId(jsPandaFile->GetMainMethodIndex(entryPoint.data()));
94     const std::string &source = extractor->GetSourceCode(mainMethodIndex);
95     const std::string &url = extractor->GetSourceFile(mainMethodIndex);
96     const uint32_t MIN_SOURCE_CODE_LENGTH = 5;  // maybe return 'ANDA' when source code is empty
97     if (source.size() < MIN_SOURCE_CODE_LENGTH) {
98         LOG_DEBUGGER(ERROR) << "NotifyScriptParsed: invalid file: " << fileName;
99         return false;
100     }
101     // store here for performance of get extractor from url
102     extractors_[url] = extractor;
103 
104     auto scriptFunc = [this](PtScript *script) -> bool {
105         frontend_.ScriptParsed(vm_, *script);
106         return true;
107     };
108     if (MatchScripts(scriptFunc, url, ScriptMatchType::URL)) {
109         LOG_DEBUGGER(WARN) << "NotifyScriptParsed: already loaded: " << url;
110         return false;
111     }
112 
113     // Notify script parsed event
114     std::unique_ptr<PtScript> script = std::make_unique<PtScript>(scriptId, fileName, url, source);
115 
116     frontend_.ScriptParsed(vm_, *script);
117 
118     // Store parsed script in map
119     scripts_[script->GetScriptId()] = std::move(script);
120     return true;
121 }
122 
NotifySingleStep(const JSPtLocation & location)123 bool DebuggerImpl::NotifySingleStep(const JSPtLocation &location)
124 {
125     if (UNLIKELY(pauseOnNextByteCode_)) {
126         if (IsSkipLine(location)) {
127             return false;
128         }
129         pauseOnNextByteCode_ = false;
130         LOG_DEBUGGER(INFO) << "StepComplete: pause on next bytecode";
131         return true;
132     }
133 
134     if (LIKELY(singleStepper_ == nullptr)) {
135         return false;
136     }
137 
138     // step not complete
139     if (!singleStepper_->StepComplete(location.GetBytecodeOffset())) {
140         return false;
141     }
142 
143     // skip unknown file or special line -1
144     if (IsSkipLine(location)) {
145         return false;
146     }
147 
148     singleStepper_.reset();
149     LOG_DEBUGGER(INFO) << "StepComplete: pause on current byte_code";
150     return true;
151 }
152 
IsSkipLine(const JSPtLocation & location)153 bool DebuggerImpl::IsSkipLine(const JSPtLocation &location)
154 {
155     DebugInfoExtractor *extractor = nullptr;
156     const auto *jsPandaFile = location.GetJsPandaFile();
157     auto scriptFunc = [this, &extractor, jsPandaFile](PtScript *) -> bool {
158         extractor = GetExtractor(jsPandaFile);
159         return true;
160     };
161 
162     // In hot reload scenario, use the base js panda file instead
163     const auto &fileName = DebuggerApi::GetBaseJSPandaFile(vm_, jsPandaFile)->GetJSPandaFileDesc();
164     if (!MatchScripts(scriptFunc, fileName.c_str(), ScriptMatchType::FILE_NAME) || extractor == nullptr) {
165         LOG_DEBUGGER(INFO) << "StepComplete: skip unknown file " << fileName.c_str();
166         return true;
167     }
168 
169     auto callbackFunc = [](int32_t line) -> bool {
170         return line == DebugInfoExtractor::SPECIAL_LINE_MARK;
171     };
172     File::EntityId methodId = location.GetMethodId();
173     uint32_t offset = location.GetBytecodeOffset();
174     if (extractor->MatchLineWithOffset(callbackFunc, methodId, offset)) {
175         LOG_DEBUGGER(INFO) << "StepComplete: skip -1";
176         return true;
177     }
178 
179     return false;
180 }
181 
CheckPauseOnException()182 bool DebuggerImpl::CheckPauseOnException()
183 {
184     if (pauseOnException_ == PauseOnExceptionsState::NONE) {
185         return false;
186     }
187     if (pauseOnException_ == PauseOnExceptionsState::UNCAUGHT) {
188         if (DebuggerApi::IsExceptionCaught(vm_)) {
189             return false;
190         }
191     }
192     return true;
193 }
194 
NotifyPaused(std::optional<JSPtLocation> location,PauseReason reason)195 void DebuggerImpl::NotifyPaused(std::optional<JSPtLocation> location, PauseReason reason)
196 {
197     if (reason == EXCEPTION && !CheckPauseOnException()) {
198         return;
199     }
200 
201     Local<JSValueRef> exception = DebuggerApi::GetAndClearException(vm_);
202 
203     std::vector<std::string> hitBreakpoints;
204     if (location.has_value()) {
205         BreakpointDetails detail;
206         DebugInfoExtractor *extractor = nullptr;
207         auto scriptFunc = [this, &location, &detail, &extractor](PtScript *script) -> bool {
208             detail.url_ = script->GetUrl();
209             extractor = GetExtractor(location->GetJsPandaFile());
210             return true;
211         };
212         auto callbackLineFunc = [&detail](int32_t line) -> bool {
213             detail.line_ = line;
214             return true;
215         };
216         auto callbackColumnFunc = [&detail](int32_t column) -> bool {
217             detail.column_ = column;
218             return true;
219         };
220         File::EntityId methodId = location->GetMethodId();
221         uint32_t offset = location->GetBytecodeOffset();
222         // In merge abc scenario, need to use the source file to match to get right url
223         if (!MatchScripts(scriptFunc, location->GetSourceFile(), ScriptMatchType::URL) ||
224             extractor == nullptr || !extractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
225             !extractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
226             LOG_DEBUGGER(ERROR) << "NotifyPaused: unknown file " << location->GetSourceFile();
227             return;
228         }
229         hitBreakpoints.emplace_back(BreakpointDetails::ToString(detail));
230     }
231 
232     // Do something cleaning on paused
233     CleanUpOnPaused();
234 
235     // Notify paused event
236     std::vector<std::unique_ptr<CallFrame>> callFrames;
237     if (!GenerateCallFrames(&callFrames)) {
238         LOG_DEBUGGER(ERROR) << "NotifyPaused: GenerateCallFrames failed";
239         return;
240     }
241     tooling::Paused paused;
242     paused.SetCallFrames(std::move(callFrames)).SetReason(reason).SetHitBreakpoints(std::move(hitBreakpoints));
243     if (reason == EXCEPTION && exception->IsError()) {
244         std::unique_ptr<RemoteObject> tmpException = RemoteObject::FromTagged(vm_, exception);
245         paused.SetData(std::move(tmpException));
246     }
247     frontend_.Paused(vm_, paused);
248 
249     frontend_.WaitForDebugger(vm_);
250     DebuggerApi::SetException(vm_, exception);
251 }
252 
NotifyNativeCalling(const void * nativeAddress)253 void DebuggerImpl::NotifyNativeCalling(const void *nativeAddress)
254 {
255     // native calling only after step into should be reported
256     if (singleStepper_ != nullptr &&
257         singleStepper_->GetStepperType() == StepperType::STEP_INTO) {
258         tooling::NativeCalling nativeCalling;
259         nativeCalling.SetNativeAddress(nativeAddress);
260         frontend_.NativeCalling(vm_, nativeCalling);
261         frontend_.WaitForDebugger(vm_);
262     }
263 }
264 
NotifyPendingJobEntry()265 void DebuggerImpl::NotifyPendingJobEntry()
266 {
267     if (singleStepper_ != nullptr) {
268         singleStepper_.reset();
269         pauseOnNextByteCode_ = true;
270     }
271 }
272 
NotifyHandleProtocolCommand()273 void DebuggerImpl::NotifyHandleProtocolCommand()
274 {
275     auto *handler = vm_->GetJsDebuggerManager()->GetDebuggerHandler();
276     handler->ProcessCommand();
277 }
278 
Dispatch(const DispatchRequest & request)279 void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request)
280 {
281     static std::unordered_map<std::string, AgentHandler> dispatcherTable {
282         { "enable", &DebuggerImpl::DispatcherImpl::Enable },
283         { "disable", &DebuggerImpl::DispatcherImpl::Disable },
284         { "evaluateOnCallFrame", &DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame },
285         { "getPossibleBreakpoints", &DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints },
286         { "getScriptSource", &DebuggerImpl::DispatcherImpl::GetScriptSource },
287         { "pause", &DebuggerImpl::DispatcherImpl::Pause },
288         { "removeBreakpoint", &DebuggerImpl::DispatcherImpl::RemoveBreakpoint },
289         { "resume", &DebuggerImpl::DispatcherImpl::Resume },
290         { "setAsyncCallStackDepth", &DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth },
291         { "setBreakpointByUrl", &DebuggerImpl::DispatcherImpl::SetBreakpointByUrl },
292         { "setPauseOnExceptions", &DebuggerImpl::DispatcherImpl::SetPauseOnExceptions },
293         { "stepInto", &DebuggerImpl::DispatcherImpl::StepInto },
294         { "stepOut", &DebuggerImpl::DispatcherImpl::StepOut },
295         { "stepOver", &DebuggerImpl::DispatcherImpl::StepOver },
296         { "setMixedDebugEnabled", &DebuggerImpl::DispatcherImpl::SetMixedDebugEnabled },
297         { "replyNativeCalling", &DebuggerImpl::DispatcherImpl::ReplyNativeCalling }
298     };
299 
300     const std::string &method = request.GetMethod();
301     LOG_DEBUGGER(DEBUG) << "dispatch [" << method << "] to DebuggerImpl";
302     auto entry = dispatcherTable.find(method);
303     if (entry != dispatcherTable.end() && entry->second != nullptr) {
304         (this->*(entry->second))(request);
305     } else {
306         SendResponse(request, DispatchResponse::Fail("Unknown method: " + method));
307     }
308 }
309 
Enable(const DispatchRequest & request)310 void DebuggerImpl::DispatcherImpl::Enable(const DispatchRequest &request)
311 {
312     std::unique_ptr<EnableParams> params = EnableParams::Create(request.GetParams());
313     if (params == nullptr) {
314         SendResponse(request, DispatchResponse::Fail("wrong params"));
315         return;
316     }
317 
318     UniqueDebuggerId id;
319     DispatchResponse response = debugger_->Enable(*params, &id);
320 
321     EnableReturns result(id);
322     SendResponse(request, response, result);
323 }
324 
Disable(const DispatchRequest & request)325 void DebuggerImpl::DispatcherImpl::Disable(const DispatchRequest &request)
326 {
327     DispatchResponse response = debugger_->Disable();
328     SendResponse(request, response);
329 }
330 
EvaluateOnCallFrame(const DispatchRequest & request)331 void DebuggerImpl::DispatcherImpl::EvaluateOnCallFrame(const DispatchRequest &request)
332 {
333     std::unique_ptr<EvaluateOnCallFrameParams> params = EvaluateOnCallFrameParams::Create(request.GetParams());
334     if (params == nullptr) {
335         SendResponse(request, DispatchResponse::Fail("wrong params"));
336         return;
337     }
338     std::unique_ptr<RemoteObject> result1;
339     DispatchResponse response = debugger_->EvaluateOnCallFrame(*params, &result1);
340     if (result1 == nullptr) {
341         SendResponse(request, response);
342         return;
343     }
344 
345     EvaluateOnCallFrameReturns result(std::move(result1));
346     SendResponse(request, response, result);
347 }
348 
GetPossibleBreakpoints(const DispatchRequest & request)349 void DebuggerImpl::DispatcherImpl::GetPossibleBreakpoints(const DispatchRequest &request)
350 {
351     std::unique_ptr<GetPossibleBreakpointsParams> params = GetPossibleBreakpointsParams::Create(request.GetParams());
352     if (params == nullptr) {
353         SendResponse(request, DispatchResponse::Fail("wrong params"));
354         return;
355     }
356     std::vector<std::unique_ptr<BreakLocation>> locations;
357     DispatchResponse response = debugger_->GetPossibleBreakpoints(*params, &locations);
358     GetPossibleBreakpointsReturns result(std::move(locations));
359     SendResponse(request, response, result);
360 }
361 
GetScriptSource(const DispatchRequest & request)362 void DebuggerImpl::DispatcherImpl::GetScriptSource(const DispatchRequest &request)
363 {
364     std::unique_ptr<GetScriptSourceParams> params = GetScriptSourceParams::Create(request.GetParams());
365     if (params == nullptr) {
366         SendResponse(request, DispatchResponse::Fail("wrong params"));
367         return;
368     }
369     std::string source;
370     DispatchResponse response = debugger_->GetScriptSource(*params, &source);
371     GetScriptSourceReturns result(source);
372     SendResponse(request, response, result);
373 }
374 
Pause(const DispatchRequest & request)375 void DebuggerImpl::DispatcherImpl::Pause(const DispatchRequest &request)
376 {
377     DispatchResponse response = debugger_->Pause();
378     SendResponse(request, response);
379 }
380 
RemoveBreakpoint(const DispatchRequest & request)381 void DebuggerImpl::DispatcherImpl::RemoveBreakpoint(const DispatchRequest &request)
382 {
383     std::unique_ptr<RemoveBreakpointParams> params = RemoveBreakpointParams::Create(request.GetParams());
384     if (params == nullptr) {
385         SendResponse(request, DispatchResponse::Fail("wrong params"));
386         return;
387     }
388     DispatchResponse response = debugger_->RemoveBreakpoint(*params);
389     SendResponse(request, response);
390 }
391 
Resume(const DispatchRequest & request)392 void DebuggerImpl::DispatcherImpl::Resume(const DispatchRequest &request)
393 {
394     std::unique_ptr<ResumeParams> params = ResumeParams::Create(request.GetParams());
395     if (params == nullptr) {
396         SendResponse(request, DispatchResponse::Fail("wrong params"));
397         return;
398     }
399     DispatchResponse response = debugger_->Resume(*params);
400     SendResponse(request, response);
401 }
402 
SetAsyncCallStackDepth(const DispatchRequest & request)403 void DebuggerImpl::DispatcherImpl::SetAsyncCallStackDepth(const DispatchRequest &request)
404 {
405     DispatchResponse response = debugger_->SetAsyncCallStackDepth();
406     SendResponse(request, response);
407 }
408 
SetBreakpointByUrl(const DispatchRequest & request)409 void DebuggerImpl::DispatcherImpl::SetBreakpointByUrl(const DispatchRequest &request)
410 {
411     std::unique_ptr<SetBreakpointByUrlParams> params = SetBreakpointByUrlParams::Create(request.GetParams());
412     if (params == nullptr) {
413         SendResponse(request, DispatchResponse::Fail("wrong params"));
414         return;
415     }
416 
417     std::string out_id;
418     std::vector<std::unique_ptr<Location>> outLocations;
419     DispatchResponse response = debugger_->SetBreakpointByUrl(*params, &out_id, &outLocations);
420     SetBreakpointByUrlReturns result(out_id, std::move(outLocations));
421     SendResponse(request, response, result);
422 }
423 
SetPauseOnExceptions(const DispatchRequest & request)424 void DebuggerImpl::DispatcherImpl::SetPauseOnExceptions(const DispatchRequest &request)
425 {
426     std::unique_ptr<SetPauseOnExceptionsParams> params = SetPauseOnExceptionsParams::Create(request.GetParams());
427     if (params == nullptr) {
428         SendResponse(request, DispatchResponse::Fail("wrong params"));
429         return;
430     }
431 
432     DispatchResponse response = debugger_->SetPauseOnExceptions(*params);
433     SendResponse(request, response);
434 }
435 
StepInto(const DispatchRequest & request)436 void DebuggerImpl::DispatcherImpl::StepInto(const DispatchRequest &request)
437 {
438     std::unique_ptr<StepIntoParams> params = StepIntoParams::Create(request.GetParams());
439     if (params == nullptr) {
440         SendResponse(request, DispatchResponse::Fail("wrong params"));
441         return;
442     }
443     DispatchResponse response = debugger_->StepInto(*params);
444     SendResponse(request, response);
445 }
446 
StepOut(const DispatchRequest & request)447 void DebuggerImpl::DispatcherImpl::StepOut(const DispatchRequest &request)
448 {
449     DispatchResponse response = debugger_->StepOut();
450     SendResponse(request, response);
451 }
452 
StepOver(const DispatchRequest & request)453 void DebuggerImpl::DispatcherImpl::StepOver(const DispatchRequest &request)
454 {
455     std::unique_ptr<StepOverParams> params = StepOverParams::Create(request.GetParams());
456     if (params == nullptr) {
457         SendResponse(request, DispatchResponse::Fail("wrong params"));
458         return;
459     }
460     DispatchResponse response = debugger_->StepOver(*params);
461     SendResponse(request, response);
462 }
463 
SetMixedDebugEnabled(const DispatchRequest & request)464 void DebuggerImpl::DispatcherImpl::SetMixedDebugEnabled(const DispatchRequest &request)
465 {
466     std::unique_ptr<SetMixedDebugParams> params = SetMixedDebugParams::Create(request.GetParams());
467     DispatchResponse response = debugger_->SetMixedDebugEnabled(*params);
468     SendResponse(request, response);
469 }
470 
ReplyNativeCalling(const DispatchRequest & request)471 void DebuggerImpl::DispatcherImpl::ReplyNativeCalling(const DispatchRequest &request)
472 {
473     std::unique_ptr<ReplyNativeCallingParams> params = ReplyNativeCallingParams::Create(request.GetParams());
474     DispatchResponse response = debugger_->ReplyNativeCalling(*params);
475     SendResponse(request, response);
476 }
477 
SetBlackboxPatterns(const DispatchRequest & request)478 void DebuggerImpl::DispatcherImpl::SetBlackboxPatterns(const DispatchRequest &request)
479 {
480     DispatchResponse response = debugger_->SetBlackboxPatterns();
481     SendResponse(request, response);
482 }
483 
AllowNotify(const EcmaVM * vm) const484 bool DebuggerImpl::Frontend::AllowNotify(const EcmaVM *vm) const
485 {
486     return vm->GetJsDebuggerManager()->IsDebugMode() && channel_ != nullptr;
487 }
488 
BreakpointResolved(const EcmaVM * vm)489 void DebuggerImpl::Frontend::BreakpointResolved(const EcmaVM *vm)
490 {
491     if (!AllowNotify(vm)) {
492         return;
493     }
494 
495     tooling::BreakpointResolved breakpointResolved;
496     channel_->SendNotification(breakpointResolved);
497 }
498 
Paused(const EcmaVM * vm,const tooling::Paused & paused)499 void DebuggerImpl::Frontend::Paused(const EcmaVM *vm, const tooling::Paused &paused)
500 {
501     if (!AllowNotify(vm)) {
502         return;
503     }
504 
505     channel_->SendNotification(paused);
506 }
507 
NativeCalling(const EcmaVM * vm,const tooling::NativeCalling & nativeCalling)508 void DebuggerImpl::Frontend::NativeCalling(const EcmaVM *vm, const tooling::NativeCalling &nativeCalling)
509 {
510     if (!AllowNotify(vm)) {
511         return;
512     }
513 
514     channel_->SendNotification(nativeCalling);
515 }
516 
Resumed(const EcmaVM * vm)517 void DebuggerImpl::Frontend::Resumed(const EcmaVM *vm)
518 {
519     if (!AllowNotify(vm)) {
520         return;
521     }
522 
523     channel_->RunIfWaitingForDebugger();
524     tooling::Resumed resumed;
525     channel_->SendNotification(resumed);
526 }
527 
ScriptFailedToParse(const EcmaVM * vm)528 void DebuggerImpl::Frontend::ScriptFailedToParse(const EcmaVM *vm)
529 {
530     if (!AllowNotify(vm)) {
531         return;
532     }
533 
534     tooling::ScriptFailedToParse scriptFailedToParse;
535     channel_->SendNotification(scriptFailedToParse);
536 }
537 
ScriptParsed(const EcmaVM * vm,const PtScript & script)538 void DebuggerImpl::Frontend::ScriptParsed(const EcmaVM *vm, const PtScript &script)
539 {
540     if (!AllowNotify(vm)) {
541         return;
542     }
543 
544     tooling::ScriptParsed scriptParsed;
545     scriptParsed.SetScriptId(script.GetScriptId())
546         .SetUrl(script.GetUrl())
547         .SetStartLine(0)
548         .SetStartColumn(0)
549         .SetEndLine(script.GetEndLine())
550         .SetEndColumn(0)
551         .SetExecutionContextId(0)
552         .SetHash(script.GetHash());
553 
554     channel_->SendNotification(scriptParsed);
555 }
556 
WaitForDebugger(const EcmaVM * vm)557 void DebuggerImpl::Frontend::WaitForDebugger(const EcmaVM *vm)
558 {
559     if (!AllowNotify(vm)) {
560         return;
561     }
562 
563     channel_->WaitForDebugger();
564 }
565 
RunIfWaitingForDebugger(const EcmaVM * vm)566 void DebuggerImpl::Frontend::RunIfWaitingForDebugger(const EcmaVM *vm)
567 {
568     if (!AllowNotify(vm)) {
569         return;
570     }
571 
572     channel_->RunIfWaitingForDebugger();
573 }
574 
Enable(const EnableParams & params,UniqueDebuggerId * id)575 DispatchResponse DebuggerImpl::Enable([[maybe_unused]] const EnableParams &params, UniqueDebuggerId *id)
576 {
577     ASSERT(id != nullptr);
578     *id = 0;
579     vm_->GetJsDebuggerManager()->SetDebugMode(true);
580     for (auto &script : scripts_) {
581         frontend_.ScriptParsed(vm_, *script.second);
582     }
583     return DispatchResponse::Ok();
584 }
585 
Disable()586 DispatchResponse DebuggerImpl::Disable()
587 {
588     DebuggerApi::RemoveAllBreakpoints(jsDebugger_);
589     frontend_.RunIfWaitingForDebugger(vm_);
590     vm_->GetJsDebuggerManager()->SetDebugMode(false);
591     return DispatchResponse::Ok();
592 }
593 
EvaluateOnCallFrame(const EvaluateOnCallFrameParams & params,std::unique_ptr<RemoteObject> * result)594 DispatchResponse DebuggerImpl::EvaluateOnCallFrame(const EvaluateOnCallFrameParams &params,
595                                                    std::unique_ptr<RemoteObject> *result)
596 {
597     CallFrameId callFrameId = params.GetCallFrameId();
598     const std::string &expression = params.GetExpression();
599     if (callFrameId < 0 || callFrameId >= static_cast<CallFrameId>(callFrameHandlers_.size())) {
600         return DispatchResponse::Fail("Invalid callFrameId.");
601     }
602 
603     std::string dest;
604     if (!DecodeAndCheckBase64(expression, dest)) {
605         LOG_DEBUGGER(ERROR) << "EvaluateValue: base64 decode failed";
606         auto ret = CmptEvaluateValue(callFrameId, expression, result);
607         if (ret.has_value()) {
608             LOG_DEBUGGER(ERROR) << "Evaluate fail, expression: " << expression;
609         }
610         return DispatchResponse::Create(ret);
611     }
612 
613     auto funcRef = DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(),
614         JSPandaFile::ENTRY_FUNCTION_NAME);
615     auto res = DebuggerApi::EvaluateViaFuncCall(const_cast<EcmaVM *>(vm_), funcRef,
616         callFrameHandlers_[callFrameId]);
617     if (vm_->GetJSThread()->HasPendingException()) {
618         LOG_DEBUGGER(ERROR) << "EvaluateValue: has pending exception";
619         std::string msg;
620         DebuggerApi::HandleUncaughtException(vm_, msg);
621         *result = RemoteObject::FromTagged(vm_,
622             Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, msg.data())));
623         return DispatchResponse::Fail(msg);
624     }
625 
626     *result = RemoteObject::FromTagged(vm_, res);
627     runtime_->CacheObjectIfNeeded(res, (*result).get());
628     return DispatchResponse::Ok();
629 }
630 
GetPossibleBreakpoints(const GetPossibleBreakpointsParams & params,std::vector<std::unique_ptr<BreakLocation>> * locations)631 DispatchResponse DebuggerImpl::GetPossibleBreakpoints(const GetPossibleBreakpointsParams &params,
632                                                       std::vector<std::unique_ptr<BreakLocation>> *locations)
633 {
634     Location *start = params.GetStart();
635     auto iter = scripts_.find(start->GetScriptId());
636     if (iter == scripts_.end()) {
637         return DispatchResponse::Fail("Unknown file name.");
638     }
639     const std::string &url = iter->second->GetUrl();
640     DebugInfoExtractor *extractor = GetExtractor(url);
641     if (extractor == nullptr) {
642         LOG_DEBUGGER(ERROR) << "GetPossibleBreakpoints: extractor is null";
643         return DispatchResponse::Fail("Unknown file name.");
644     }
645 
646     int32_t line = start->GetLine();
647     int32_t column = start->GetColumn();
648     auto callbackFunc = [](const JSPtLocation &) -> bool {
649         return true;
650     };
651     if (extractor->MatchWithLocation(callbackFunc, line, column, url)) {
652         std::unique_ptr<BreakLocation> location = std::make_unique<BreakLocation>();
653         location->SetScriptId(start->GetScriptId()).SetLine(line).SetColumn(column);
654         locations->emplace_back(std::move(location));
655     }
656     return DispatchResponse::Ok();
657 }
658 
GetScriptSource(const GetScriptSourceParams & params,std::string * source)659 DispatchResponse DebuggerImpl::GetScriptSource(const GetScriptSourceParams &params, std::string *source)
660 {
661     ScriptId scriptId = params.GetScriptId();
662     auto iter = scripts_.find(scriptId);
663     if (iter == scripts_.end()) {
664         *source = "";
665         return DispatchResponse::Fail("unknown script id: " + std::to_string(scriptId));
666     }
667     *source = iter->second->GetScriptSource();
668 
669     return DispatchResponse::Ok();
670 }
671 
Pause()672 DispatchResponse DebuggerImpl::Pause()
673 {
674     pauseOnNextByteCode_ = true;
675     return DispatchResponse::Ok();
676 }
677 
RemoveBreakpoint(const RemoveBreakpointParams & params)678 DispatchResponse DebuggerImpl::RemoveBreakpoint(const RemoveBreakpointParams &params)
679 {
680     std::string id = params.GetBreakpointId();
681     LOG_DEBUGGER(INFO) << "RemoveBreakpoint: " << id;
682     BreakpointDetails metaData{};
683     if (!BreakpointDetails::ParseBreakpointId(id, &metaData)) {
684         return DispatchResponse::Fail("Parse breakpoint id failed");
685     }
686     DebugInfoExtractor *extractor = GetExtractor(metaData.url_);
687     if (extractor == nullptr) {
688         LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: extractor is null";
689         return DispatchResponse::Fail("Unknown file name.");
690     }
691 
692     auto scriptFunc = [](PtScript *) -> bool {
693         return true;
694     };
695     if (!MatchScripts(scriptFunc, metaData.url_, ScriptMatchType::URL)) {
696         LOG_DEBUGGER(ERROR) << "RemoveBreakpoint: Unknown url: " << metaData.url_;
697         return DispatchResponse::Fail("Unknown file name.");
698     }
699 
700     auto callbackFunc = [this](const JSPtLocation &location) -> bool {
701         LOG_DEBUGGER(INFO) << "remove breakpoint location: " << location.ToString();
702         return DebuggerApi::RemoveBreakpoint(jsDebugger_, location);
703     };
704     if (!extractor->MatchWithLocation(callbackFunc, metaData.line_, metaData.column_, metaData.url_)) {
705         LOG_DEBUGGER(ERROR) << "failed to remove breakpoint location number: "
706             << metaData.line_ << ":" << metaData.column_;
707         return DispatchResponse::Fail("Breakpoint not found.");
708     }
709 
710     LOG_DEBUGGER(INFO) << "remove breakpoint line number:" << metaData.line_;
711     return DispatchResponse::Ok();
712 }
713 
Resume(const ResumeParams & params)714 DispatchResponse DebuggerImpl::Resume([[maybe_unused]] const ResumeParams &params)
715 {
716     frontend_.Resumed(vm_);
717     singleStepper_.reset();
718     return DispatchResponse::Ok();
719 }
720 
SetAsyncCallStackDepth()721 DispatchResponse DebuggerImpl::SetAsyncCallStackDepth()
722 {
723     return DispatchResponse::Fail("SetAsyncCallStackDepth not support now");
724 }
725 
SetBreakpointByUrl(const SetBreakpointByUrlParams & params,std::string * outId,std::vector<std::unique_ptr<Location>> * outLocations)726 DispatchResponse DebuggerImpl::SetBreakpointByUrl(const SetBreakpointByUrlParams &params,
727                                                   std::string *outId,
728                                                   std::vector<std::unique_ptr<Location>> *outLocations)
729 {
730     if (!vm_->GetJsDebuggerManager()->IsDebugMode()) {
731         return DispatchResponse::Fail("SetBreakpointByUrl: debugger agent is not enabled");
732     }
733     const std::string &url = params.GetUrl();
734     int32_t lineNumber = params.GetLine();
735     int32_t columnNumber = params.GetColumn();
736     auto condition = params.HasCondition() ? params.GetCondition() : std::optional<std::string> {};
737 
738     DebugInfoExtractor *extractor = GetExtractor(url);
739     if (extractor == nullptr) {
740         LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: extractor is null";
741         return DispatchResponse::Fail("Unknown file name.");
742     }
743 
744     ScriptId scriptId;
745     std::string fileName;
746     auto scriptFunc = [&scriptId, &fileName](PtScript *script) -> bool {
747         scriptId = script->GetScriptId();
748         fileName = script->GetFileName();
749         return true;
750     };
751     if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) {
752         LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: Unknown url: " << url;
753         return DispatchResponse::Fail("Unknown file name.");
754     }
755 
756     auto callbackFunc = [this, &condition](const JSPtLocation &location) -> bool {
757         LOG_DEBUGGER(INFO) << "set breakpoint location: " << location.ToString();
758         Local<FunctionRef> condFuncRef = FunctionRef::Undefined(vm_);
759         if (condition.has_value() && !condition.value().empty()) {
760             std::string dest;
761             if (!DecodeAndCheckBase64(condition.value(), dest)) {
762                 LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: base64 decode failed";
763                 return false;
764             }
765             condFuncRef = DebuggerApi::GenerateFuncFromBuffer(vm_, dest.data(), dest.size(),
766                 JSPandaFile::ENTRY_FUNCTION_NAME);
767             if (condFuncRef->IsUndefined()) {
768                 LOG_DEBUGGER(ERROR) << "SetBreakpointByUrl: generate function failed";
769                 return false;
770             }
771         }
772         return DebuggerApi::SetBreakpoint(jsDebugger_, location, condFuncRef);
773     };
774     if (!extractor->MatchWithLocation(callbackFunc, lineNumber, columnNumber, url)) {
775         LOG_DEBUGGER(ERROR) << "failed to set breakpoint location number: " << lineNumber << ":" << columnNumber;
776         return DispatchResponse::Fail("Breakpoint not found.");
777     }
778 
779     BreakpointDetails metaData{lineNumber, 0, url};
780     *outId = BreakpointDetails::ToString(metaData);
781     *outLocations = std::vector<std::unique_ptr<Location>>();
782     std::unique_ptr<Location> location = std::make_unique<Location>();
783     location->SetScriptId(scriptId).SetLine(lineNumber).SetColumn(0);
784     outLocations->emplace_back(std::move(location));
785 
786     return DispatchResponse::Ok();
787 }
788 
SetPauseOnExceptions(const SetPauseOnExceptionsParams & params)789 DispatchResponse DebuggerImpl::SetPauseOnExceptions(const SetPauseOnExceptionsParams &params)
790 {
791     pauseOnException_ = params.GetState();
792     return DispatchResponse::Ok();
793 }
794 
StepInto(const StepIntoParams & params)795 DispatchResponse DebuggerImpl::StepInto([[maybe_unused]] const StepIntoParams &params)
796 {
797     if (!vm_->GetJsDebuggerManager()->IsDebugMode()) {
798         return DispatchResponse::Fail("Can only perform operation while paused");
799     }
800     singleStepper_ = SingleStepper::GetStepIntoStepper(vm_);
801     if (singleStepper_ == nullptr) {
802         LOG_DEBUGGER(ERROR) << "StepInto: singleStepper is null";
803         return DispatchResponse::Fail("Failed to StepInto");
804     }
805     frontend_.Resumed(vm_);
806     return DispatchResponse::Ok();
807 }
808 
StepOut()809 DispatchResponse DebuggerImpl::StepOut()
810 {
811     if (!vm_->GetJsDebuggerManager()->IsDebugMode()) {
812         return DispatchResponse::Fail("Can only perform operation while paused");
813     }
814     singleStepper_ = SingleStepper::GetStepOutStepper(vm_);
815     if (singleStepper_ == nullptr) {
816         LOG_DEBUGGER(ERROR) << "StepOut: singleStepper is null";
817         return DispatchResponse::Fail("Failed to StepOut");
818     }
819     frontend_.Resumed(vm_);
820     return DispatchResponse::Ok();
821 }
822 
StepOver(const StepOverParams & params)823 DispatchResponse DebuggerImpl::StepOver([[maybe_unused]] const StepOverParams &params)
824 {
825     if (!vm_->GetJsDebuggerManager()->IsDebugMode()) {
826         return DispatchResponse::Fail("Can only perform operation while paused");
827     }
828     singleStepper_ = SingleStepper::GetStepOverStepper(vm_);
829     if (singleStepper_ == nullptr) {
830         LOG_DEBUGGER(ERROR) << "StepOver: singleStepper is null";
831         return DispatchResponse::Fail("Failed to StepOver");
832     }
833     frontend_.Resumed(vm_);
834     return DispatchResponse::Ok();
835 }
836 
SetBlackboxPatterns()837 DispatchResponse DebuggerImpl::SetBlackboxPatterns()
838 {
839     return DispatchResponse::Fail("SetBlackboxPatterns not support now");
840 }
841 
SetMixedDebugEnabled(const SetMixedDebugParams & params)842 DispatchResponse DebuggerImpl::SetMixedDebugEnabled([[maybe_unused]] const SetMixedDebugParams &params)
843 {
844     vm_->GetJsDebuggerManager()->SetMixedDebugEnabled(params.GetEnabled());
845     return DispatchResponse::Ok();
846 }
847 
ReplyNativeCalling(const ReplyNativeCallingParams & params)848 DispatchResponse DebuggerImpl::ReplyNativeCalling([[maybe_unused]] const ReplyNativeCallingParams &params)
849 {
850     frontend_.Resumed(vm_);
851     if (params.GetUserCode()) {
852         singleStepper_.reset();
853     }
854     return DispatchResponse::Ok();
855 }
856 
CleanUpOnPaused()857 void DebuggerImpl::CleanUpOnPaused()
858 {
859     runtime_->curObjectId_ = 0;
860     runtime_->properties_.clear();
861 
862     callFrameHandlers_.clear();
863     scopeObjects_.clear();
864 }
865 
Trim(const std::string & str)866 std::string DebuggerImpl::Trim(const std::string &str)
867 {
868     std::string ret = str;
869     // If ret has only ' ', remove all charactors.
870     ret.erase(ret.find_last_not_of(' ') + 1);
871     // If ret has only ' ', remove all charactors.
872     ret.erase(0, ret.find_first_not_of(' '));
873     return ret;
874 }
875 
GetExtractor(const JSPandaFile * jsPandaFile)876 DebugInfoExtractor *DebuggerImpl::GetExtractor(const JSPandaFile *jsPandaFile)
877 {
878     return JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
879 }
880 
881 // mainly used for breakpoints to match location
GetExtractor(const std::string & url)882 DebugInfoExtractor *DebuggerImpl::GetExtractor(const std::string &url)
883 {
884     // match patch file first if it contains diff for the url, and currently only support the file
885     // specified by the url change as a whole
886     auto *extractor = DebuggerApi::GetPatchExtractor(vm_, url);
887     if (extractor != nullptr) {
888         return extractor;
889     }
890 
891     auto iter = extractors_.find(url);
892     if (iter == extractors_.end()) {
893         return nullptr;
894     }
895 
896     return iter->second;
897 }
898 
GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> * callFrames)899 bool DebuggerImpl::GenerateCallFrames(std::vector<std::unique_ptr<CallFrame>> *callFrames)
900 {
901     CallFrameId callFrameId = 0;
902     auto walkerFunc = [this, &callFrameId, &callFrames](const FrameHandler *frameHandler) -> StackState {
903         if (DebuggerApi::IsNativeMethod(frameHandler)) {
904             LOG_DEBUGGER(INFO) << "GenerateCallFrames: Skip CFrame and Native method";
905             return StackState::CONTINUE;
906         }
907         std::unique_ptr<CallFrame> callFrame = std::make_unique<CallFrame>();
908         if (!GenerateCallFrame(callFrame.get(), frameHandler, callFrameId)) {
909             if (callFrameId == 0) {
910                 return StackState::FAILED;
911             }
912         } else {
913             SaveCallFrameHandler(frameHandler);
914             callFrames->emplace_back(std::move(callFrame));
915             callFrameId++;
916         }
917         return StackState::CONTINUE;
918     };
919     return DebuggerApi::StackWalker(vm_, walkerFunc);
920 }
921 
SaveCallFrameHandler(const FrameHandler * frameHandler)922 void DebuggerImpl::SaveCallFrameHandler(const FrameHandler *frameHandler)
923 {
924     auto handlerPtr = DebuggerApi::NewFrameHandler(vm_);
925     *handlerPtr = *frameHandler;
926     callFrameHandlers_.emplace_back(handlerPtr);
927 }
928 
GenerateCallFrame(CallFrame * callFrame,const FrameHandler * frameHandler,CallFrameId callFrameId)929 bool DebuggerImpl::GenerateCallFrame(CallFrame *callFrame,
930     const FrameHandler *frameHandler, CallFrameId callFrameId)
931 {
932     if (!frameHandler->HasFrame()) {
933         return false;
934     }
935     Method *method = DebuggerApi::GetMethod(frameHandler);
936     auto methodId = method->GetMethodId();
937     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
938     DebugInfoExtractor *extractor = GetExtractor(jsPandaFile);
939     if (extractor == nullptr) {
940         LOG_DEBUGGER(ERROR) << "GenerateCallFrame: extractor is null";
941         return false;
942     }
943 
944     // functionName
945     std::string functionName = method->ParseFunctionName();
946 
947     // location
948     std::unique_ptr<Location> location = std::make_unique<Location>();
949     std::string url = extractor->GetSourceFile(methodId);
950     auto scriptFunc = [&location](PtScript *script) -> bool {
951         location->SetScriptId(script->GetScriptId());
952         return true;
953     };
954     if (!MatchScripts(scriptFunc, url, ScriptMatchType::URL)) {
955         LOG_DEBUGGER(ERROR) << "GenerateCallFrame: Unknown url: " << url;
956         return false;
957     }
958     auto callbackLineFunc = [&location](int32_t line) -> bool {
959         location->SetLine(line);
960         return true;
961     };
962     auto callbackColumnFunc = [&location](int32_t column) -> bool {
963         location->SetColumn(column);
964         return true;
965     };
966     if (!extractor->MatchLineWithOffset(callbackLineFunc, methodId, DebuggerApi::GetBytecodeOffset(frameHandler)) ||
967         !extractor->MatchColumnWithOffset(callbackColumnFunc, methodId, DebuggerApi::GetBytecodeOffset(frameHandler))) {
968         LOG_DEBUGGER(ERROR) << "GenerateCallFrame: unknown offset: " << DebuggerApi::GetBytecodeOffset(frameHandler);
969         return false;
970     }
971 
972     // scopeChain & this
973     std::unique_ptr<RemoteObject> thisObj = std::make_unique<RemoteObject>();
974     thisObj->SetType(ObjectType::Undefined);
975 
976     JSThread *thread = vm_->GetJSThread();
977     std::vector<std::unique_ptr<Scope>> scopeChain;
978     scopeChain.emplace_back(GetLocalScopeChain(frameHandler, &thisObj));
979     if (jsPandaFile != nullptr && !jsPandaFile->IsBundlePack() && jsPandaFile->IsNewVersion()) {
980         JSHandle<JSTaggedValue> currentModule(thread, DebuggerApi::GetCurrentModule(vm_));
981         if (currentModule->IsSourceTextModule()) { // CJS module is string
982             scopeChain.emplace_back(GetModuleScopeChain());
983         }
984     }
985     scopeChain.emplace_back(GetGlobalScopeChain());
986 
987     callFrame->SetCallFrameId(callFrameId)
988         .SetFunctionName(functionName)
989         .SetLocation(std::move(location))
990         .SetUrl(url)
991         .SetScopeChain(std::move(scopeChain))
992         .SetThis(std::move(thisObj));
993     return true;
994 }
995 
GetLocalScopeChain(const FrameHandler * frameHandler,std::unique_ptr<RemoteObject> * thisObj)996 std::unique_ptr<Scope> DebuggerImpl::GetLocalScopeChain(const FrameHandler *frameHandler,
997     std::unique_ptr<RemoteObject> *thisObj)
998 {
999     auto localScope = std::make_unique<Scope>();
1000 
1001     Method *method = DebuggerApi::GetMethod(frameHandler);
1002     auto methodId = method->GetMethodId();
1003     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
1004     DebugInfoExtractor *extractor = GetExtractor(jsPandaFile);
1005     if (extractor == nullptr) {
1006         LOG_DEBUGGER(ERROR) << "GetScopeChain: extractor is null";
1007         return localScope;
1008     }
1009 
1010     std::unique_ptr<RemoteObject> local = std::make_unique<RemoteObject>();
1011     Local<ObjectRef> localObj = ObjectRef::New(vm_);
1012     local->SetType(ObjectType::Object)
1013         .SetObjectId(runtime_->curObjectId_)
1014         .SetClassName(ObjectClassName::Object)
1015         .SetDescription(RemoteObject::ObjectDescription);
1016     auto *sp = DebuggerApi::GetSp(frameHandler);
1017     scopeObjects_[sp] = runtime_->curObjectId_;
1018     runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, localObj);
1019 
1020     Local<JSValueRef> thisVal = JSValueRef::Undefined(vm_);
1021     GetLocalVariables(frameHandler, methodId, jsPandaFile, thisVal, localObj);
1022     *thisObj = RemoteObject::FromTagged(vm_, thisVal);
1023     runtime_->CacheObjectIfNeeded(thisVal, (*thisObj).get());
1024 
1025     const LineNumberTable &lines = extractor->GetLineNumberTable(methodId);
1026     std::unique_ptr<Location> startLoc = std::make_unique<Location>();
1027     std::unique_ptr<Location> endLoc = std::make_unique<Location>();
1028     auto scriptFunc = [&startLoc, &endLoc, lines](PtScript *script) -> bool {
1029         startLoc->SetScriptId(script->GetScriptId())
1030             .SetLine(lines.front().line)
1031             .SetColumn(0);
1032         endLoc->SetScriptId(script->GetScriptId())
1033             .SetLine(lines.back().line + 1)
1034             .SetColumn(0);
1035         return true;
1036     };
1037     if (MatchScripts(scriptFunc, extractor->GetSourceFile(methodId), ScriptMatchType::URL)) {
1038         localScope->SetType(Scope::Type::Local())
1039             .SetObject(std::move(local))
1040             .SetStartLocation(std::move(startLoc))
1041             .SetEndLocation(std::move(endLoc));
1042     }
1043 
1044     return localScope;
1045 }
1046 
GetModuleScopeChain()1047 std::unique_ptr<Scope> DebuggerImpl::GetModuleScopeChain()
1048 {
1049     auto moduleScope = std::make_unique<Scope>();
1050 
1051     std::unique_ptr<RemoteObject> module = std::make_unique<RemoteObject>();
1052     Local<ObjectRef> moduleObj = ObjectRef::New(vm_);
1053     module->SetType(ObjectType::Object)
1054         .SetObjectId(runtime_->curObjectId_)
1055         .SetClassName(ObjectClassName::Object)
1056         .SetDescription(RemoteObject::ObjectDescription);
1057     moduleScope->SetType(Scope::Type::Module()).SetObject(std::move(module));
1058     runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, moduleObj);
1059     JSThread *thread = vm_->GetJSThread();
1060     JSHandle<JSTaggedValue> currentModule(thread, DebuggerApi::GetCurrentModule(vm_));
1061     DebuggerApi::GetLocalExportVariables(vm_, moduleObj, currentModule, false);
1062     DebuggerApi::GetIndirectExportVariables(vm_, moduleObj, currentModule);
1063     DebuggerApi::GetImportVariables(vm_, moduleObj, currentModule);
1064     return moduleScope;
1065 }
1066 
GetLocalVariables(const FrameHandler * frameHandler,panda_file::File::EntityId methodId,const JSPandaFile * jsPandaFile,Local<JSValueRef> & thisVal,Local<ObjectRef> & localObj)1067 void DebuggerImpl::GetLocalVariables(const FrameHandler *frameHandler, panda_file::File::EntityId methodId,
1068     const JSPandaFile *jsPandaFile, Local<JSValueRef> &thisVal, Local<ObjectRef> &localObj)
1069 {
1070     auto *extractor = GetExtractor(jsPandaFile);
1071     Local<JSValueRef> value = JSValueRef::Undefined(vm_);
1072     // in case of arrow function, which doesn't have this in local variable table
1073     bool hasThis = false;
1074     for (const auto &[varName, regIndex] : extractor->GetLocalVariableTable(methodId)) {
1075         value = DebuggerApi::GetVRegValue(vm_, frameHandler, regIndex);
1076         if (varName == "4newTarget" || varName == "0this" || varName == "0newTarget" || varName == "0funcObj") {
1077             continue;
1078         }
1079 
1080         if (varName == "this") {
1081             thisVal = value;
1082             hasThis = true;
1083             continue;
1084         }
1085         Local<JSValueRef> name = JSValueRef::Undefined(vm_);
1086         if (varName == "4funcObj") {
1087             if (value->IsFunction()) {
1088                 auto funcName = Local<FunctionRef>(value)->GetName(vm_)->ToString();
1089                 name = StringRef::NewFromUtf8(vm_, funcName.c_str());
1090             } else {
1091                 continue;
1092             }
1093         } else {
1094             name = StringRef::NewFromUtf8(vm_, varName.c_str());
1095         }
1096         PropertyAttribute descriptor(value, true, true, true);
1097         localObj->DefineProperty(vm_, name, descriptor);
1098     }
1099 
1100     // closure variables are stored in env
1101     JSTaggedValue env = DebuggerApi::GetEnv(frameHandler);
1102     if (env.IsTaggedArray() && DebuggerApi::GetBytecodeOffset(frameHandler) != 0) {
1103         LexicalEnv *lexEnv = LexicalEnv::Cast(env.GetTaggedObject());
1104         if (lexEnv->GetScopeInfo().IsHole()) {
1105             return;
1106         }
1107         auto ptr = JSNativePointer::Cast(lexEnv->GetScopeInfo().GetTaggedObject())->GetExternalPointer();
1108         auto *scopeDebugInfo = reinterpret_cast<ScopeDebugInfo *>(ptr);
1109         JSThread *thread = vm_->GetJSThread();
1110         for (const auto &[varName, slot] : scopeDebugInfo->scopeInfo) {
1111             // skip possible duplicate variables both in local variable table and env
1112             if (varName == "4newTarget") {
1113                 continue;
1114             }
1115             value = JSNApiHelper::ToLocal<JSValueRef>(
1116                 JSHandle<JSTaggedValue>(thread, lexEnv->GetProperties(slot)));
1117             if (varName == "this") {
1118                 if (!hasThis) {
1119                     thisVal = value;
1120                 }
1121                 continue;
1122             }
1123             Local<JSValueRef> name = StringRef::NewFromUtf8(vm_, varName.c_str());
1124             PropertyAttribute descriptor(value, true, true, true);
1125             localObj->DefineProperty(vm_, name, descriptor);
1126         }
1127     }
1128 }
1129 
GetGlobalScopeChain()1130 std::unique_ptr<Scope> DebuggerImpl::GetGlobalScopeChain()
1131 {
1132     auto globalScope = std::make_unique<Scope>();
1133 
1134     std::unique_ptr<RemoteObject> global = std::make_unique<RemoteObject>();
1135     global->SetType(ObjectType::Object)
1136         .SetObjectId(runtime_->curObjectId_)
1137         .SetClassName(ObjectClassName::Global)
1138         .SetDescription(RemoteObject::GlobalDescription);
1139     globalScope->SetType(Scope::Type::Global()).SetObject(std::move(global));
1140     runtime_->properties_[runtime_->curObjectId_++] = Global<JSValueRef>(vm_, JSNApi::GetGlobalObject(vm_));
1141     return globalScope;
1142 }
1143 
UpdateScopeObject(const FrameHandler * frameHandler,std::string_view varName,Local<JSValueRef> newVal)1144 void DebuggerImpl::UpdateScopeObject(const FrameHandler *frameHandler,
1145     std::string_view varName, Local<JSValueRef> newVal)
1146 {
1147     auto *sp = DebuggerApi::GetSp(frameHandler);
1148     auto iter = scopeObjects_.find(sp);
1149     if (iter == scopeObjects_.end()) {
1150         LOG_DEBUGGER(ERROR) << "UpdateScopeObject: object not found";
1151         return;
1152     }
1153 
1154     auto objectId = iter->second;
1155     Local<ObjectRef> localObj = runtime_->properties_[objectId].ToLocal(vm_);
1156     Local<JSValueRef> name = StringRef::NewFromUtf8(vm_, varName.data());
1157     if (localObj->Has(vm_, name)) {
1158         LOG_DEBUGGER(DEBUG) << "UpdateScopeObject: set new value";
1159         PropertyAttribute descriptor(newVal, true, true, true);
1160         localObj->DefineProperty(vm_, name, descriptor);
1161     } else {
1162         LOG_DEBUGGER(ERROR) << "UpdateScopeObject: not found " << varName;
1163     }
1164 }
1165 
ClearSingleStepper()1166 void DebuggerImpl::ClearSingleStepper()
1167 {
1168     // ClearSingleStepper is originally called from Function::Call, if current depth is 0, then it is safe to reset
1169     if (singleStepper_ != nullptr && DebuggerApi::GetStackDepth(vm_) == 0) {
1170         singleStepper_.reset();
1171     }
1172 }
1173 
CmptEvaluateValue(CallFrameId callFrameId,const std::string & expression,std::unique_ptr<RemoteObject> * result)1174 std::optional<std::string> DebuggerImpl::CmptEvaluateValue(CallFrameId callFrameId, const std::string &expression,
1175     std::unique_ptr<RemoteObject> *result)
1176 {
1177     if (DebuggerApi::IsNativeMethod(vm_)) {
1178         *result = RemoteObject::FromTagged(vm_,
1179             Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Native Frame not support.")));
1180         return "Native Frame not support.";
1181     }
1182     DebugInfoExtractor *extractor = GetExtractor(DebuggerApi::GetJSPandaFile(vm_));
1183     if (extractor == nullptr) {
1184         *result = RemoteObject::FromTagged(vm_,
1185             Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Internal error.")));
1186         return "Internal error.";
1187     }
1188     std::string varName = expression;
1189     std::string varValue;
1190     std::string::size_type indexEqual = expression.find_first_of('=', 0);
1191     if (indexEqual != std::string::npos) {
1192         varName = Trim(expression.substr(0, indexEqual));
1193         varValue = Trim(expression.substr(indexEqual + 1, expression.length()));
1194     }
1195 
1196     Local<StringRef> name = StringRef::NewFromUtf8(vm_, varName.c_str());
1197     FrameHandler *frameHandler = callFrameHandlers_[callFrameId].get();
1198     if (varValue.empty()) {
1199         Local<JSValueRef> ret = DebuggerExecutor::GetValue(vm_, frameHandler, name);
1200         if (!ret.IsEmpty()) {
1201             *result = RemoteObject::FromTagged(vm_, ret);
1202             runtime_->CacheObjectIfNeeded(ret, (*result).get());
1203             return {};
1204         }
1205     } else {
1206         Local<JSValueRef> value = ConvertToLocal(varValue);
1207         if (value.IsEmpty()) {
1208             return "Unsupported expression.";
1209         }
1210         JsDebuggerManager *mgr = vm_->GetJsDebuggerManager();
1211         mgr->SetEvalFrameHandler(callFrameHandlers_[callFrameId]);
1212         bool ret = DebuggerExecutor::SetValue(vm_, frameHandler, name, value);
1213         mgr->SetEvalFrameHandler(nullptr);
1214         if (ret) {
1215             *result = RemoteObject::FromTagged(vm_, value);
1216             return {};
1217         }
1218     }
1219 
1220     *result = RemoteObject::FromTagged(vm_,
1221         Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Unsupported expression.")));
1222     return "Unsupported expression.";
1223 }
1224 
ConvertToLocal(const std::string & varValue)1225 Local<JSValueRef> DebuggerImpl::ConvertToLocal(const std::string &varValue)
1226 {
1227     Local<JSValueRef> taggedValue;
1228     if (varValue == "false") {
1229         taggedValue = JSValueRef::False(vm_);
1230     } else if (varValue == "true") {
1231         taggedValue = JSValueRef::True(vm_);
1232     } else if (varValue == "undefined") {
1233         taggedValue = JSValueRef::Undefined(vm_);
1234     } else if (varValue[0] == '\"' && varValue[varValue.length() - 1] == '\"') {
1235         // 2 : 2 means length
1236         taggedValue = StringRef::NewFromUtf8(vm_, varValue.substr(1, varValue.length() - 2).c_str());
1237     } else {
1238         auto begin = reinterpret_cast<const uint8_t *>((varValue.c_str()));
1239         auto end = begin + varValue.length();  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1240         double d = DebuggerApi::StringToDouble(begin, end, 0);
1241         if (!std::isnan(d)) {
1242             taggedValue = NumberRef::New(vm_, d);
1243         }
1244     }
1245     return taggedValue;
1246 }
1247 
DecodeAndCheckBase64(const std::string & src,std::string & dest)1248 bool DebuggerImpl::DecodeAndCheckBase64(const std::string &src, std::string &dest)
1249 {
1250     uint32_t numOctets = PtBase64::Decode(src, dest);
1251     if (numOctets > File::MAGIC_SIZE &&
1252         memcmp(dest.data(), File::MAGIC.data(), File::MAGIC_SIZE) == 0) {
1253         return true;
1254     }
1255     return false;
1256 }
1257 }  // namespace panda::ecmascript::tooling
1258