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
22namespace panda::ecmascript::tooling {
23void 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
33Local<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
64Local<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
89Local<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
112bool 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
131void 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
138Local<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
152bool 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
166Local<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
180bool 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
194Local<JSValueRef> DebuggerExecutor::GetGlobalValue(const EcmaVM *vm, Local<StringRef> name)
195{
196    return DebuggerApi::GetGlobalValue(vm, name);
197}
198
199bool 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
206Local<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
224bool 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