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 "backend/debugger_executor.h"
17 
18 #include "ecmascript/debugger/debugger_api.h"
19 #include "ecmascript/debugger/js_debugger_manager.h"
20 #include "tooling/base/pt_types.h"
21 
22 namespace panda::ecmascript::tooling {
Initialize(const EcmaVM *vm)23 void DebuggerExecutor::Initialize(const EcmaVM *vm)
24 {
25     [[maybe_unused]] EcmaHandleScope handleScope(vm->GetJSThread());
26     Local<ObjectRef> globalObj = JSNApi::GetGlobalObject(vm);
27     globalObj->Set(vm, StringRef::NewFromUtf8(vm, "debuggerSetValue"), FunctionRef::New(
28         const_cast<panda::EcmaVM*>(vm), DebuggerExecutor::DebuggerSetValue));
29     globalObj->Set(vm, StringRef::NewFromUtf8(vm, "debuggerGetValue"), FunctionRef::New(
30         const_cast<panda::EcmaVM*>(vm), DebuggerExecutor::DebuggerGetValue));
31 }
32 
DebuggerGetValue(JsiRuntimeCallInfo *runtimeCallInfo)33 Local<JSValueRef> DebuggerExecutor::DebuggerGetValue(JsiRuntimeCallInfo *runtimeCallInfo)
34 {
35     EcmaVM *vm = runtimeCallInfo->GetVM();
36     uint32_t argc = runtimeCallInfo->GetArgsNumber();
37     if (argc != NUM_ARGS) {
38         return JSValueRef::Undefined(vm);
39     }
40     Local<JSValueRef> name = runtimeCallInfo->GetCallArgRef(0);
41     if (!name->IsString(vm)) {
42         return JSValueRef::Undefined(vm);
43     }
44     Local<JSValueRef> isThrow = runtimeCallInfo->GetCallArgRef(1);
45 
46     auto &frameHandler = vm->GetJsDebuggerManager()->GetEvalFrameHandler();
47     ASSERT(frameHandler);
48 
49     Local<JSValueRef> value = GetValue(vm, frameHandler.get(), Local<StringRef>(name));
50     if (!value.IsEmpty()) {
51         return value;
52     }
53 
54     if (!isThrow->ToBoolean(vm)->Value()) {
55         DebuggerApi::ClearException(vm);
56         return JSValueRef::Undefined(vm);
57     }
58 
59     std::string varName = Local<StringRef>(name)->ToString(vm);
60     ThrowException(vm, varName + " is not defined");
61     return Local<JSValueRef>();
62 }
63 
DebuggerSetValue(JsiRuntimeCallInfo *runtimeCallInfo)64 Local<JSValueRef> DebuggerExecutor::DebuggerSetValue(JsiRuntimeCallInfo *runtimeCallInfo)
65 {
66     EcmaVM *vm = runtimeCallInfo->GetVM();
67     uint32_t argc = runtimeCallInfo->GetArgsNumber();
68     if (argc != NUM_ARGS) {
69         return JSValueRef::Undefined(vm);
70     }
71     Local<JSValueRef> name = runtimeCallInfo->GetCallArgRef(0);
72     if (!name->IsString(vm)) {
73         return JSValueRef::Undefined(vm);
74     }
75     Local<JSValueRef> value = runtimeCallInfo->GetCallArgRef(1);
76 
77     auto &frameHandler = vm->GetJsDebuggerManager()->GetEvalFrameHandler();
78     ASSERT(frameHandler);
79 
80     if (SetValue(vm, frameHandler.get(), Local<StringRef>(name), value)) {
81         return value;
82     }
83 
84     std::string varName = StringRef::Cast(*name)->ToString(vm);
85     ThrowException(vm, varName + " is not defined");
86     return Local<JSValueRef>();
87 }
88 
GetValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name)89 Local<JSValueRef> DebuggerExecutor::GetValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name)
90 {
91     Local<JSValueRef> value;
92     value = GetLocalValue(vm, frameHandler, name);
93     if (!value.IsEmpty()) {
94         return value;
95     }
96     value = GetLexicalValue(vm, frameHandler, name);
97     if (!value.IsEmpty()) {
98         return value;
99     }
100     value = GetModuleValue(vm, frameHandler, name);
101     if (!value.IsEmpty()) {
102         return value;
103     }
104     value = GetGlobalValue(vm, name);
105     if (!value.IsEmpty()) {
106         return value;
107     }
108 
109     return Local<JSValueRef>();
110 }
111 
SetValue(const EcmaVM *vm, FrameHandler *frameHandler, Local<StringRef> name, Local<JSValueRef> value)112 bool DebuggerExecutor::SetValue(const EcmaVM *vm, FrameHandler *frameHandler,
113                                 Local<StringRef> name, Local<JSValueRef> value)
114 {
115     if (SetLocalValue(vm, frameHandler, name, value)) {
116         return true;
117     }
118     if (SetLexicalValue(vm, frameHandler, name, value)) {
119         return true;
120     }
121     if (SetModuleValue(vm, frameHandler, name, value)) {
122         return true;
123     }
124     if (SetGlobalValue(vm, name, value)) {
125         return true;
126     }
127 
128     return false;
129 }
130 
ThrowException(const EcmaVM *vm, const std::string &error)131 void DebuggerExecutor::ThrowException(const EcmaVM *vm, const std::string &error)
132 {
133     Local<StringRef> msg = StringRef::NewFromUtf8(vm, error.c_str());
134     Local<JSValueRef> exception = Exception::ReferenceError(vm, msg);
135     JSNApi::ThrowException(vm, exception);
136 }
137 
GetLocalValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name)138 Local<JSValueRef> DebuggerExecutor::GetLocalValue(const EcmaVM *vm, const FrameHandler *frameHandler,
139                                                   Local<StringRef> name)
140 {
141     Local<JSValueRef> result;
142 
143     int32_t index = DebuggerApi::GetVregIndex(frameHandler, name->ToString(vm));
144     if (index == -1) {
145         return result;
146     }
147 
148     result = DebuggerApi::GetVRegValue(vm, frameHandler, index);
149     return result;
150 }
151 
SetLocalValue(const EcmaVM *vm, FrameHandler *frameHandler, Local<StringRef> name, Local<JSValueRef> value)152 bool DebuggerExecutor::SetLocalValue(const EcmaVM *vm, FrameHandler *frameHandler,
153                                      Local<StringRef> name, Local<JSValueRef> value)
154 {
155     std::string varName = name->ToString(vm);
156     int32_t index = DebuggerApi::GetVregIndex(frameHandler, varName);
157     if (index == -1) {
158         return false;
159     }
160 
161     DebuggerApi::SetVRegValue(frameHandler, index, value);
162     vm->GetJsDebuggerManager()->NotifyScopeUpdated(varName, value, Scope::Type::Local());
163     return true;
164 }
165 
GetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name)166 Local<JSValueRef> DebuggerExecutor::GetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler,
167                                                     Local<StringRef> name)
168 {
169     Local<JSValueRef> result;
170 
171     auto [level, slot] = DebuggerApi::GetLevelSlot(frameHandler, name->ToString(vm));
172     if (level == -1) {
173         return result;
174     }
175 
176     result = DebuggerApi::GetProperties(vm, frameHandler, level, slot);
177     return result;
178 }
179 
SetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name, Local<JSValueRef> value)180 bool DebuggerExecutor::SetLexicalValue(const EcmaVM *vm, const FrameHandler *frameHandler,
181                                        Local<StringRef> name, Local<JSValueRef> value)
182 {
183     std::string varName = name->ToString(vm);
184     auto [level, slot] = DebuggerApi::GetLevelSlot(frameHandler, varName);
185     if (level == -1) {
186         return false;
187     }
188 
189     DebuggerApi::SetProperties(vm, frameHandler, level, slot, value);
190     vm->GetJsDebuggerManager()->NotifyScopeUpdated(varName, value, Scope::Type::Closure());
191     return true;
192 }
193 
GetGlobalValue(const EcmaVM *vm, Local<StringRef> name)194 Local<JSValueRef> DebuggerExecutor::GetGlobalValue(const EcmaVM *vm, Local<StringRef> name)
195 {
196     return DebuggerApi::GetGlobalValue(vm, name);
197 }
198 
SetGlobalValue(const EcmaVM *vm, Local<StringRef> name, Local<JSValueRef> value)199 bool DebuggerExecutor::SetGlobalValue(const EcmaVM *vm, Local<StringRef> name, Local<JSValueRef> value)
200 {
201     std::string varName = name->ToString(vm);
202     vm->GetJsDebuggerManager()->NotifyScopeUpdated(varName, value, Scope::Type::Global());
203     return DebuggerApi::SetGlobalValue(vm, name, value);
204 }
205 
GetModuleValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name)206 Local<JSValueRef> DebuggerExecutor::GetModuleValue(const EcmaVM *vm, const FrameHandler *frameHandler,
207                                                    Local<StringRef> name)
208 {
209     Local<JSValueRef> result;
210     std::string varName = name->ToString(vm);
211     Method *method = DebuggerApi::GetMethod(frameHandler);
212     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
213     if (jsPandaFile != nullptr && (jsPandaFile->IsBundlePack() || !jsPandaFile->IsNewVersion())) {
214         return result;
215     }
216     JSThread *thread = vm->GetJSThread();
217     JSHandle<JSTaggedValue> currentModule(thread, DebuggerApi::GetCurrentModule(vm));
218     if (currentModule->IsSourceTextModule()) {
219         result = DebuggerApi::GetModuleValue(vm, currentModule, varName);
220     }
221     return result;
222 }
223 
SetModuleValue(const EcmaVM *vm, const FrameHandler *frameHandler, Local<StringRef> name, Local<JSValueRef> value)224 bool DebuggerExecutor::SetModuleValue(const EcmaVM *vm, const FrameHandler *frameHandler,
225                                       Local<StringRef> name, Local<JSValueRef> value)
226 {
227     std::string varName = name->ToString(vm);
228     Method *method = DebuggerApi::GetMethod(frameHandler);
229     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
230     if (jsPandaFile != nullptr && (jsPandaFile->IsBundlePack() || !jsPandaFile->IsNewVersion())) {
231         return false;
232     }
233     JSThread *thread = vm->GetJSThread();
234     JSHandle<JSTaggedValue> currentModule(thread, DebuggerApi::GetCurrentModule(vm));
235     if (currentModule->IsSourceTextModule()) {
236         DebuggerApi::SetModuleValue(vm, currentModule, varName, value);
237         vm->GetJsDebuggerManager()->NotifyScopeUpdated(varName, value, Scope::Type::Module());
238     }
239     return true;
240 }
241 }  // namespace panda::ecmascript::tooling
242