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/backend/debugger_api.h"
17
18 #include "ecmascript/base/number_helper.h"
19 #include "ecmascript/interpreter/frame_handler.h"
20 #include "ecmascript/interpreter/slow_runtime_stub.h"
21 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
22 #include "ecmascript/jspandafile/program_object.h"
23 #include "ecmascript/js_handle.h"
24 #include "ecmascript/js_method.h"
25 #include "ecmascript/jspandafile/js_pandafile_manager.h"
26 #include "ecmascript/napi/jsnapi_helper-inl.h"
27 #include "ecmascript/tooling/backend/js_debugger.h"
28
29 namespace panda::ecmascript::tooling {
30 using panda::ecmascript::base::ALLOW_BINARY;
31 using panda::ecmascript::base::ALLOW_HEX;
32 using panda::ecmascript::base::ALLOW_OCTAL;
33 using panda::ecmascript::base::NumberHelper;
34
35 // InterpretedFrameHandler
GetStackDepth(const EcmaVM * ecmaVm)36 uint32_t DebuggerApi::GetStackDepth(const EcmaVM *ecmaVm)
37 {
38 uint32_t count = 0;
39 InterpretedFrameHandler frameHandler(ecmaVm->GetJSThread());
40 for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
41 if (frameHandler.IsBreakFrame()) {
42 continue;
43 }
44 ++count;
45 }
46 return count;
47 }
48
NewFrameHandler(const EcmaVM * ecmaVm)49 std::shared_ptr<InterpretedFrameHandler> DebuggerApi::NewFrameHandler(const EcmaVM *ecmaVm)
50 {
51 return std::make_shared<InterpretedFrameHandler>(ecmaVm->GetJSThread());
52 }
53
StackWalker(const EcmaVM * ecmaVm,std::function<StackState (const InterpretedFrameHandler *)> func)54 bool DebuggerApi::StackWalker(const EcmaVM *ecmaVm, std::function<StackState(const InterpretedFrameHandler *)> func)
55 {
56 InterpretedFrameHandler frameHandler(ecmaVm->GetJSThread());
57 for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
58 if (frameHandler.IsBreakFrame()) {
59 continue;
60 }
61 StackState state = func(&frameHandler);
62 if (state == StackState::CONTINUE) {
63 continue;
64 }
65 if (state == StackState::FAILED) {
66 return false;
67 }
68 return true;
69 }
70 return true;
71 }
72
GetBytecodeOffset(const EcmaVM * ecmaVm)73 uint32_t DebuggerApi::GetBytecodeOffset(const EcmaVM *ecmaVm)
74 {
75 return InterpretedFrameHandler(ecmaVm->GetJSThread()).GetBytecodeOffset();
76 }
77
GetMethod(const EcmaVM * ecmaVm)78 JSMethod *DebuggerApi::GetMethod(const EcmaVM *ecmaVm)
79 {
80 return InterpretedFrameHandler(ecmaVm->GetJSThread()).GetMethod();
81 }
82
SetVRegValue(InterpretedFrameHandler * frameHandler,size_t index,Local<JSValueRef> value)83 void DebuggerApi::SetVRegValue(InterpretedFrameHandler *frameHandler, size_t index, Local<JSValueRef> value)
84 {
85 return frameHandler->SetVRegValue(index, JSNApiHelper::ToJSTaggedValue(*value));
86 }
87
GetBytecodeOffset(const InterpretedFrameHandler * frameHandler)88 uint32_t DebuggerApi::GetBytecodeOffset(const InterpretedFrameHandler *frameHandler)
89 {
90 return frameHandler->GetBytecodeOffset();
91 }
92
GetMethod(const InterpretedFrameHandler * frameHandler)93 JSMethod *DebuggerApi::GetMethod(const InterpretedFrameHandler *frameHandler)
94 {
95 return frameHandler->GetMethod();
96 }
97
GetEnv(const InterpretedFrameHandler * frameHandler)98 JSTaggedValue DebuggerApi::GetEnv(const InterpretedFrameHandler *frameHandler)
99 {
100 return frameHandler->GetEnv();
101 }
102
GetSp(const InterpretedFrameHandler * frameHandler)103 JSTaggedType *DebuggerApi::GetSp(const InterpretedFrameHandler *frameHandler)
104 {
105 return frameHandler->GetSp();
106 }
107
GetVregIndex(const InterpretedFrameHandler * frameHandler,std::string_view name)108 int32_t DebuggerApi::GetVregIndex(const InterpretedFrameHandler *frameHandler, std::string_view name)
109 {
110 JSMethod *method = frameHandler->GetMethod();
111 if (method->IsNativeWithCallField()) {
112 LOG(ERROR, DEBUGGER) << "GetVregIndex: native frame not support";
113 return -1;
114 }
115 JSPtExtractor *extractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
116 if (extractor == nullptr) {
117 LOG(ERROR, DEBUGGER) << "GetVregIndex: extractor is null";
118 return -1;
119 }
120 auto table = extractor->GetLocalVariableTable(method->GetMethodId());
121 auto iter = table.find(name.data());
122 if (iter == table.end()) {
123 return -1;
124 }
125 return iter->second;
126 }
127
GetVRegValue(const EcmaVM * ecmaVm,const InterpretedFrameHandler * frameHandler,size_t index)128 Local<JSValueRef> DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm,
129 const InterpretedFrameHandler *frameHandler, size_t index)
130 {
131 auto value = frameHandler->GetVRegValue(index);
132 JSHandle<JSTaggedValue> handledValue(ecmaVm->GetJSThread(), value);
133 return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
134 }
135
136 // JSThread
GetAndClearException(const EcmaVM * ecmaVm)137 Local<JSValueRef> DebuggerApi::GetAndClearException(const EcmaVM *ecmaVm)
138 {
139 auto exception = ecmaVm->GetJSThread()->GetException();
140 JSHandle<JSTaggedValue> handledException(ecmaVm->GetJSThread(), exception);
141 ecmaVm->GetJSThread()->ClearException();
142 return JSNApiHelper::ToLocal<JSValueRef>(handledException);
143 }
144
SetException(const EcmaVM * ecmaVm,Local<JSValueRef> exception)145 void DebuggerApi::SetException(const EcmaVM *ecmaVm, Local<JSValueRef> exception)
146 {
147 ecmaVm->GetJSThread()->SetException(JSNApiHelper::ToJSTaggedValue(*exception));
148 }
149
ClearException(const EcmaVM * ecmaVm)150 void DebuggerApi::ClearException(const EcmaVM *ecmaVm)
151 {
152 return ecmaVm->GetJSThread()->ClearException();
153 }
154
155 // NumberHelper
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix)156 double DebuggerApi::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix)
157 {
158 return NumberHelper::StringToDouble(start, end, radix, ALLOW_BINARY | ALLOW_HEX | ALLOW_OCTAL);
159 }
160
161 // JSDebugger
CreateJSDebugger(const EcmaVM * ecmaVm)162 JSDebugger *DebuggerApi::CreateJSDebugger(const EcmaVM *ecmaVm)
163 {
164 return new JSDebugger(ecmaVm);
165 }
166
DestroyJSDebugger(JSDebugger * debugger)167 void DebuggerApi::DestroyJSDebugger(JSDebugger *debugger)
168 {
169 delete debugger;
170 }
171
RegisterHooks(JSDebugger * debugger,PtHooks * hooks)172 void DebuggerApi::RegisterHooks(JSDebugger *debugger, PtHooks *hooks)
173 {
174 debugger->RegisterHooks(hooks);
175 }
176
SetBreakpoint(JSDebugger * debugger,const JSPtLocation & location,Local<FunctionRef> condFuncRef)177 bool DebuggerApi::SetBreakpoint(JSDebugger *debugger, const JSPtLocation &location,
178 Local<FunctionRef> condFuncRef)
179 {
180 return debugger->SetBreakpoint(location, condFuncRef);
181 }
182
RemoveBreakpoint(JSDebugger * debugger,const JSPtLocation & location)183 bool DebuggerApi::RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location)
184 {
185 return debugger->RemoveBreakpoint(location);
186 }
187
188 // JSMethod
ParseFunctionName(const JSMethod * method)189 std::string DebuggerApi::ParseFunctionName(const JSMethod *method)
190 {
191 return method->ParseFunctionName();
192 }
193
194 // ScopeInfo
GetProperties(const EcmaVM * vm,const InterpretedFrameHandler * frameHandler,int32_t level,uint32_t slot)195 Local<JSValueRef> DebuggerApi::GetProperties(const EcmaVM *vm, const InterpretedFrameHandler *frameHandler,
196 int32_t level, uint32_t slot)
197 {
198 JSTaggedValue env = frameHandler->GetEnv();
199 for (int i = 0; i < level; i++) {
200 JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv();
201 ASSERT(!taggedParentEnv.IsUndefined());
202 env = taggedParentEnv;
203 }
204 JSTaggedValue value = LexicalEnv::Cast(env.GetTaggedObject())->GetProperties(slot);
205 JSHandle<JSTaggedValue> handledValue(vm->GetJSThread(), value);
206 return JSNApiHelper::ToLocal<JSValueRef>(handledValue);
207 }
208
SetProperties(const EcmaVM * vm,const InterpretedFrameHandler * frameHandler,int32_t level,uint32_t slot,Local<JSValueRef> value)209 void DebuggerApi::SetProperties(const EcmaVM *vm, const InterpretedFrameHandler *frameHandler,
210 int32_t level, uint32_t slot, Local<JSValueRef> value)
211 {
212 JSTaggedValue env = frameHandler->GetEnv();
213 for (int i = 0; i < level; i++) {
214 JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv();
215 ASSERT(!taggedParentEnv.IsUndefined());
216 env = taggedParentEnv;
217 }
218 JSTaggedValue target = JSNApiHelper::ToJSHandle(value).GetTaggedValue();
219 LexicalEnv::Cast(env.GetTaggedObject())->SetProperties(vm->GetJSThread(), slot, target);
220 }
221
GetLevelSlot(const InterpretedFrameHandler * frameHandler,std::string_view name)222 std::pair<int32_t, uint32_t> DebuggerApi::GetLevelSlot(const InterpretedFrameHandler *frameHandler,
223 std::string_view name)
224 {
225 int32_t level = 0;
226 JSTaggedValue curEnv = frameHandler->GetEnv();
227 for (; curEnv.IsTaggedArray(); curEnv = LexicalEnv::Cast(curEnv.GetTaggedObject())->GetParentEnv(), level++) {
228 LexicalEnv *lexicalEnv = LexicalEnv::Cast(curEnv.GetTaggedObject());
229 if (lexicalEnv->GetScopeInfo().IsHole()) {
230 continue;
231 }
232 auto result = JSNativePointer::Cast(lexicalEnv->GetScopeInfo().GetTaggedObject())->GetExternalPointer();
233 ScopeDebugInfo *scopeDebugInfo = reinterpret_cast<ScopeDebugInfo *>(result);
234 for (const auto &info : scopeDebugInfo->scopeInfo) {
235 if (info.name == name.data()) {
236 return std::make_pair(level, info.slot);
237 }
238 }
239 }
240 return std::make_pair(-1, 0);
241 }
242
GetGlobalValue(const EcmaVM * vm,Local<StringRef> name)243 Local<JSValueRef> DebuggerApi::GetGlobalValue(const EcmaVM *vm, Local<StringRef> name)
244 {
245 JSTaggedValue result;
246 JSTaggedValue globalObj = vm->GetGlobalEnv()->GetGlobalObject();
247 JSThread *thread = vm->GetJSThread();
248
249 JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name);
250 JSTaggedValue globalRec = SlowRuntimeStub::LdGlobalRecord(thread, key);
251 if (!globalRec.IsUndefined()) {
252 ASSERT(globalRec.IsPropertyBox());
253 result = PropertyBox::Cast(globalRec.GetTaggedObject())->GetValue();
254 return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, result));
255 }
256
257 JSTaggedValue globalVar = FastRuntimeStub::GetGlobalOwnProperty(thread, globalObj, key);
258 if (!globalVar.IsHole()) {
259 return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, globalVar));
260 } else {
261 result = SlowRuntimeStub::TryLdGlobalByName(thread, globalObj, key);
262 return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, result));
263 }
264
265 return JSValueRef::Exception(vm);
266 }
267
SetGlobalValue(const EcmaVM * vm,Local<StringRef> name,Local<JSValueRef> value)268 bool DebuggerApi::SetGlobalValue(const EcmaVM *vm, Local<StringRef> name, Local<JSValueRef> value)
269 {
270 JSTaggedValue result;
271 JSTaggedValue globalObj = vm->GetGlobalEnv()->GetGlobalObject();
272 JSThread *thread = vm->GetJSThread();
273
274 JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name);
275 JSTaggedValue newVal = JSNApiHelper::ToJSTaggedValue(*value);
276 JSTaggedValue globalRec = SlowRuntimeStub::LdGlobalRecord(thread, key);
277 if (!globalRec.IsUndefined()) {
278 result = SlowRuntimeStub::TryUpdateGlobalRecord(thread, key, newVal);
279 return !result.IsException();
280 }
281
282 JSTaggedValue globalVar = FastRuntimeStub::GetGlobalOwnProperty(thread, globalObj, key);
283 if (!globalVar.IsHole()) {
284 result = SlowRuntimeStub::StGlobalVar(thread, key, newVal);
285 return !result.IsException();
286 }
287
288 return false;
289 }
290
HandleUncaughtException(const EcmaVM * ecmaVm,std::string & message)291 void DebuggerApi::HandleUncaughtException(const EcmaVM *ecmaVm, std::string &message)
292 {
293 JSThread *thread = ecmaVm->GetJSThread();
294 [[maybe_unused]] EcmaHandleScope handleScope(thread);
295 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
296
297 JSHandle<JSTaggedValue> exHandle(thread, thread->GetException());
298 if (exHandle->IsJSError()) {
299 JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
300 JSHandle<EcmaString> name(JSObject::GetProperty(thread, exHandle, nameKey).GetValue());
301 JSHandle<JSTaggedValue> msgKey = globalConst->GetHandledMessageString();
302 JSHandle<EcmaString> msg(JSObject::GetProperty(thread, exHandle, msgKey).GetValue());
303 message = ConvertToString(*name) + ": " + ConvertToString(*msg);
304 } else {
305 JSHandle<EcmaString> ecmaStr = JSTaggedValue::ToString(thread, exHandle);
306 message = ConvertToString(*ecmaStr);
307 }
308 thread->ClearException();
309 }
310
GenerateFuncFromBuffer(const EcmaVM * ecmaVm,const void * buffer,size_t size)311 Local<FunctionRef> DebuggerApi::GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, size_t size)
312 {
313 JSPandaFileManager *mgr = JSPandaFileManager::GetInstance();
314 const auto *jsPandaFile = mgr->LoadBufferAbc("", buffer, size);
315 if (jsPandaFile == nullptr) {
316 return JSValueRef::Undefined(ecmaVm);
317 }
318
319 JSHandle<Program> program = mgr->GenerateProgram(const_cast<EcmaVM *>(ecmaVm), jsPandaFile);
320 JSTaggedValue func = program->GetMainFunction();
321 return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(ecmaVm->GetJSThread(), func));
322 }
323
EvaluateViaFuncCall(EcmaVM * ecmaVm,Local<FunctionRef> funcRef,std::shared_ptr<InterpretedFrameHandler> & frameHandler)324 Local<JSValueRef> DebuggerApi::EvaluateViaFuncCall(EcmaVM *ecmaVm, Local<FunctionRef> funcRef,
325 std::shared_ptr<InterpretedFrameHandler> &frameHandler)
326 {
327 JSNApi::EnableUserUncaughtErrorHandler(ecmaVm);
328
329 JsDebuggerManager *mgr = ecmaVm->GetJsDebuggerManager();
330 bool prevDebugMode = mgr->IsDebugMode();
331 mgr->SetEvalFrameHandler(frameHandler);
332 mgr->SetDebugMode(false); // in order to catch exception
333 std::vector<Local<JSValueRef>> args;
334 auto result = funcRef->Call(ecmaVm, JSValueRef::Undefined(ecmaVm), args.data(), args.size());
335 mgr->SetDebugMode(prevDebugMode);
336 mgr->SetEvalFrameHandler(nullptr);
337
338 return result;
339 }
340 } // namespace panda::ecmascript::tooling
341