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 ¶ms, 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 ¶ms,
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 ¶ms,
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 ¶ms, 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 ¶ms)
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 ¶ms)
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 ¶ms,
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 ¶ms)
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 ¶ms)
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 ¶ms)
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