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