1/*
2 * Copyright (c) 2024 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 "evaluate/debugInfoStorage.h"
17#include "evaluate/helpers.h"
18#include "evaluate/entityDeclarator.h"
19#include "evaluate/scopedDebugInfoPlugin-inl.h"
20#include "evaluate/debugInfoDeserialization/debugInfoDeserializer.h"
21#include "evaluate/debugInfoDeserialization/classBuilder.h"
22#include "evaluate/debugInfoDeserialization/methodBuilder.h"
23
24#include "public/public.h"
25#include "varbinder/variable.h"
26#include "parser/ETSparser.h"
27#include "parser/program/program.h"
28#include "parser/context/parserContext.h"
29#include "compiler/lowering/scopesInit/scopesInitPhase.h"
30#include "compiler/lowering/util.h"
31
32#include "libpandafile/class_data_accessor-inl.h"
33#include "libpandafile/file-inl.h"
34
35namespace ark::es2panda::evaluate {
36
37namespace {
38
39std::string GetVarDeclSourceCode(std::string_view varName, DebugInfoDeserializer::RegisterNumber regNumber,
40                                 const std::string &typeSignature, panda_file::Type::TypeId typeId,
41                                 checker::GlobalTypesHolder *globalTypes)
42{
43    auto returnType = helpers::ToTypeName(typeSignature, globalTypes);
44    ASSERT(returnType.has_value());
45
46    std::stringstream sstream;
47    sstream << "let " << varName << ':' << *returnType << '=' << helpers::DEBUGGER_API_CLASS_NAME << '.'
48            << helpers::CreateGetterName(typeId) << '(' << regNumber << ')';
49    // Must add cast from Object.
50    if (typeId == panda_file::Type::TypeId::REFERENCE) {
51        sstream << " as " << *returnType;
52    }
53    return sstream.str();
54}
55
56std::string GetVarUpdateSourceCode(std::string_view varName, DebugInfoDeserializer::RegisterNumber regNumber,
57                                   panda_file::Type::TypeId typeId)
58{
59    std::stringstream sstream;
60    sstream << helpers::DEBUGGER_API_CLASS_NAME << '.' << helpers::CreateSetterName(typeId) << '(' << regNumber << ','
61            << varName << ')';
62    return sstream.str();
63}
64
65}  // namespace
66
67DebugInfoDeserializer::DebugInfoDeserializer(ScopedDebugInfoPlugin &debugInfoPlugin) : debugInfoPlugin_(debugInfoPlugin)
68{
69}
70
71varbinder::Variable *DebugInfoDeserializer::CreateIrClass(panda_file::File::EntityId classId, parser::Program *program,
72                                                          util::StringView pathToSource, util::StringView classDeclName)
73{
74    LOG(DEBUG, ES2PANDA) << "DebugInfoDeserializer::CreateIrClass " << classDeclName << " in " << pathToSource;
75    ASSERT(program);
76
77    const auto *pf = debugInfoPlugin_.GetDebugInfoStorage()->GetPandaFile(pathToSource.Utf8());
78    ASSERT(pf);
79
80    // NOTE: may cache the created `ClassDataAccessor`.
81    auto cda = panda_file::ClassDataAccessor(*pf, classId);
82
83    auto *classDecl = CreateClassDeclaration(classDeclName, cda, GetSuperClass(cda), program);
84    return classDecl->Definition()->Ident()->Variable();
85}
86
87ir::ClassDeclaration *DebugInfoDeserializer::CreateClassDeclaration(util::StringView identName,
88                                                                    panda_file::ClassDataAccessor &cda,
89                                                                    ir::ETSTypeReference *superClass,
90                                                                    parser::Program *program)
91{
92    LOG(DEBUG, ES2PANDA) << "Create class declaration from debug info: " << identName;
93
94    auto *checkHelper = debugInfoPlugin_.GetIrCheckHelper();
95    auto *checker = checkHelper->GetChecker();
96    auto *varBinder = debugInfoPlugin_.GetETSBinder();
97
98    auto *classDecl = ClassBuilder(checker, identName, cda, superClass).Build(program);
99
100    helpers::DoScopedAction(checker, varBinder, program, nullptr, nullptr, [varBinder, classDecl]() {
101        compiler::InitScopesPhaseETS::RunExternalNode(classDecl, varBinder);
102        varBinder->ResolveReferencesForScope(classDecl, compiler::NearestScope(classDecl));
103    });
104    // Run checker to assign types to all entities.
105    checkHelper->CheckNewNode(classDecl, program->GlobalScope(), nullptr, program);
106
107    return classDecl;
108}
109
110varbinder::Variable *DebugInfoDeserializer::CreateIrLocalVariable(
111    ir::Identifier *ident, const panda_file::LocalVariableTable &localVariableTable, size_t bytecodeOffset)
112{
113    ASSERT(ident);
114
115    auto typedVarIter = localVariableTable.end();
116    uint32_t startOffset = 0;
117
118    const auto &identName = ident->Name();
119    for (auto iter = localVariableTable.begin(); iter != localVariableTable.end(); ++iter) {
120        const auto &var = *iter;
121        if (identName.Is(var.name) && var.IsAccessibleAt(bytecodeOffset) && startOffset <= var.startOffset) {
122            typedVarIter = iter;
123            startOffset = var.startOffset;
124        }
125    }
126    if (typedVarIter != localVariableTable.end()) {
127        return CreateLocalVarDecl(ident, typedVarIter->regNumber, typedVarIter->typeSignature);
128    }
129
130    return nullptr;
131}
132
133varbinder::Variable *DebugInfoDeserializer::CreateIrGlobalVariable(parser::Program *program,
134                                                                   util::StringView pathToSource,
135                                                                   util::StringView varDeclName)
136{
137    ASSERT(program);
138
139    auto *debugInfoStorage = debugInfoPlugin_.GetDebugInfoStorage();
140    auto *checkHelper = debugInfoPlugin_.GetIrCheckHelper();
141
142    const auto *pf = debugInfoStorage->GetPandaFile(pathToSource.Utf8());
143    ASSERT(pf);
144
145    varbinder::Variable *var = nullptr;
146
147    auto *cda = debugInfoStorage->GetGlobalClassAccessor(pathToSource.Utf8());
148    cda->EnumerateFields([program, varDeclName, pf, &var, checkHelper](panda_file::FieldDataAccessor &fda) {
149        // All ETSGLOBAL fields must be static.
150        ASSERT(fda.IsStatic());
151
152        const char *name = utf::Mutf8AsCString(pf->GetStringData(fda.GetNameId()).data);
153        if (!varDeclName.Is(name)) {
154            return;
155        }
156        // Must be unique within global variables.
157        ASSERT(var == nullptr);
158
159        auto *typeNode = helpers::PandaTypeToTypeNode(*pf, fda, checkHelper->GetChecker());
160        ASSERT(typeNode);
161
162        // Global variable is found - add it into source module's global class properties.
163        auto modFlags = helpers::GetModifierFlags(fda, true) | ir::ModifierFlags::EXPORT;
164        auto *field = helpers::CreateClassProperty(checkHelper->GetChecker(), name, typeNode, modFlags);
165        // Fields parent will be set in `AddProperties`.
166        program->GlobalClass()->AddProperties(
167            ArenaVector<ir::AstNode *>(1, field, checkHelper->GetChecker()->Allocator()->Adapter()));
168
169        checkHelper->CheckGlobalEntity(program, field);
170        var = field->Key()->AsIdentifier()->Variable();
171    });
172
173    return var;
174}
175
176varbinder::Variable *DebugInfoDeserializer::CreateIrGlobalMethods(ArenaVector<ir::AstNode *> &createdMethods,
177                                                                  parser::Program *program,
178                                                                  util::StringView pathToSource,
179                                                                  util::StringView methodDeclName)
180{
181    ASSERT(program);
182
183    varbinder::Variable *var = nullptr;
184
185    auto *cda = debugInfoPlugin_.GetDebugInfoStorage()->GetGlobalClassAccessor(pathToSource.Utf8());
186    cda->EnumerateMethods([this, &var, &createdMethods, program, methodDeclName,
187                           &cda](panda_file::MethodDataAccessor &mda) {
188        if (!methodDeclName.Is(mda.GetFullName())) {
189            return;
190        }
191
192        MethodBuilder builder(debugInfoPlugin_.GetIrCheckHelper()->GetChecker(), mda, helpers::GetModifierFlags(*cda));
193        auto *method = std::move(builder).Build();
194
195        method->SetParent(program->GlobalClass());
196        createdMethods.emplace_back(method);
197
198        // Postpone Checker until the whole overload set is created.
199        debugInfoPlugin_.GetIrCheckHelper()->CheckGlobalEntity(program, method, false);
200
201        // Sanity checks.
202        auto *methodVar = method->AsClassElement()->Value()->AsFunctionExpression()->Function()->Id()->Variable();
203        ASSERT(methodVar != nullptr);
204        ASSERT(var == nullptr || var == methodVar);
205        var = methodVar;
206    });
207
208    return var;
209}
210
211varbinder::Variable *DebugInfoDeserializer::CreateLocalVarDecl(ir::Identifier *ident, RegisterNumber regNumber,
212                                                               const std::string &typeSignature)
213{
214    ASSERT(ident);
215
216    auto *checkHelper = debugInfoPlugin_.GetIrCheckHelper();
217    auto *varBinder = debugInfoPlugin_.GetETSBinder();
218    auto identName = ident->Name().Utf8();
219
220    auto typeId = helpers::GetTypeId(typeSignature);
221    auto *checker = checkHelper->GetChecker();
222    auto varDeclSource =
223        GetVarDeclSourceCode(identName, regNumber, typeSignature, typeId, checker->GetGlobalTypesHolder());
224
225    // Set up correct scope before parsing statements.
226    auto *topStatement = helpers::GetEnclosingBlock(ident);
227    checker::ScopeContext ctx(checker, topStatement->Scope());
228    auto statementScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, topStatement->Scope());
229
230    parser::Program p(checker->Allocator(), varBinder);
231    auto parser = parser::ETSParser(&p, varBinder->GetContext()->config->options->CompilerOptions(),
232                                    parser::ParserStatus::NO_OPTS);
233
234    auto *varDecl = parser.CreateFormattedStatement(varDeclSource, parser::ParserContext::DEFAULT_SOURCE_FILE);
235    ASSERT(varDecl != nullptr);
236    varDecl->SetParent(topStatement);
237    // Declaration will be placed at start of current scope.
238    // Can't insert right away until block's statements iteration ends.
239    debugInfoPlugin_.RegisterPrologueEpilogue<true>(topStatement, varDecl);
240    checkHelper->CheckLocalEntity(varDecl);
241
242    // Yet don't track whether the value was modified, so store result unconditionally in the end of the scope.
243    auto varUpdateSource = GetVarUpdateSourceCode(identName, regNumber, typeId);
244
245    auto *varUpdate = parser.CreateFormattedStatement(varUpdateSource, parser::ParserContext::DEFAULT_SOURCE_FILE);
246    ASSERT(varUpdate != nullptr);
247    varUpdate->SetParent(topStatement);
248    // Can't insert right away until block's statements iteration ends.
249    debugInfoPlugin_.RegisterPrologueEpilogue<false>(topStatement, varUpdate);
250    checkHelper->CheckLocalEntity(varUpdate);
251
252    // Local variables are not registered, as they can be found in local scope.
253    ASSERT(varDecl->AsVariableDeclaration()->Declarators().size() == 1);
254    return varDecl->AsVariableDeclaration()->Declarators()[0]->Id()->Variable();
255}
256
257}  // namespace ark::es2panda::evaluate
258