• 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/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