• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 
35 namespace ark::es2panda::evaluate {
36 
37 namespace {
38 
GetVarDeclSourceCode(std::string_view varName,DebugInfoDeserializer::RegisterNumber regNumber,const std::string & typeSignature,panda_file::Type::TypeId typeId,checker::GlobalTypesHolder * globalTypes)39 std::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     ES2PANDA_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 
GetVarUpdateSourceCode(std::string_view varName,DebugInfoDeserializer::RegisterNumber regNumber,panda_file::Type::TypeId typeId)56 std::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 
DebugInfoDeserializer(ScopedDebugInfoPlugin & debugInfoPlugin)67 DebugInfoDeserializer::DebugInfoDeserializer(ScopedDebugInfoPlugin &debugInfoPlugin) : debugInfoPlugin_(debugInfoPlugin)
68 {
69 }
70 
CreateIrClass(panda_file::File::EntityId classId,parser::Program * program,util::StringView pathToSource,util::StringView classDeclName)71 varbinder::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     ES2PANDA_ASSERT(program);
76 
77     const auto *pf = debugInfoPlugin_.GetDebugInfoStorage()->GetPandaFile(pathToSource.Utf8());
78     ES2PANDA_ASSERT(pf != nullptr);
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 
CreateClassDeclaration(util::StringView identName,panda_file::ClassDataAccessor & cda,ir::ETSTypeReference * superClass,parser::Program * program)87 ir::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 
CreateIrLocalVariable(ir::Identifier * ident,const panda_file::LocalVariableTable & localVariableTable,size_t bytecodeOffset)110 varbinder::Variable *DebugInfoDeserializer::CreateIrLocalVariable(
111     ir::Identifier *ident, const panda_file::LocalVariableTable &localVariableTable, size_t bytecodeOffset)
112 {
113     ES2PANDA_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 
CreateIrGlobalVariable(parser::Program * program,util::StringView pathToSource,util::StringView varDeclName)133 varbinder::Variable *DebugInfoDeserializer::CreateIrGlobalVariable(parser::Program *program,
134                                                                    util::StringView pathToSource,
135                                                                    util::StringView varDeclName)
136 {
137     ES2PANDA_ASSERT(program);
138 
139     auto *debugInfoStorage = debugInfoPlugin_.GetDebugInfoStorage();
140     auto *checkHelper = debugInfoPlugin_.GetIrCheckHelper();
141 
142     const auto *pf = debugInfoStorage->GetPandaFile(pathToSource.Utf8());
143     ES2PANDA_ASSERT(pf);
144 
145     varbinder::Variable *var = nullptr;
146 
147     auto *cda = debugInfoStorage->GetGlobalClassAccessor(pathToSource.Utf8());
148     ES2PANDA_ASSERT(cda != nullptr);
149     cda->EnumerateFields([program, varDeclName, pf, &var, checkHelper](panda_file::FieldDataAccessor &fda) {
150         // All ETSGLOBAL fields must be static.
151         ES2PANDA_ASSERT(fda.IsStatic());
152 
153         const char *name = utf::Mutf8AsCString(pf->GetStringData(fda.GetNameId()).data);
154         if (!varDeclName.Is(name)) {
155             return;
156         }
157         // Must be unique within global variables.
158         ES2PANDA_ASSERT(var == nullptr);
159 
160         auto *typeNode = helpers::PandaTypeToTypeNode(*pf, fda, checkHelper->GetChecker());
161         ES2PANDA_ASSERT(typeNode);
162 
163         // Global variable is found - add it into source module's global class properties.
164         auto modFlags = helpers::GetModifierFlags(fda, true) | ir::ModifierFlags::EXPORT;
165         auto *field = helpers::CreateClassProperty(checkHelper->GetChecker(), name, typeNode, modFlags);
166         // Fields parent will be set in `AddProperties`.
167         program->GlobalClass()->AddProperties(
168             ArenaVector<ir::AstNode *>(1, field, checkHelper->GetChecker()->Allocator()->Adapter()));
169 
170         checkHelper->CheckGlobalEntity(program, field);
171         var = field->Key()->AsIdentifier()->Variable();
172     });
173 
174     return var;
175 }
176 
CreateIrGlobalMethods(ArenaVector<ir::AstNode * > & createdMethods,parser::Program * program,util::StringView pathToSource,util::StringView methodDeclName)177 varbinder::Variable *DebugInfoDeserializer::CreateIrGlobalMethods(ArenaVector<ir::AstNode *> &createdMethods,
178                                                                   parser::Program *program,
179                                                                   util::StringView pathToSource,
180                                                                   util::StringView methodDeclName)
181 {
182     ES2PANDA_ASSERT(program);
183 
184     varbinder::Variable *var = nullptr;
185 
186     auto *cda = debugInfoPlugin_.GetDebugInfoStorage()->GetGlobalClassAccessor(pathToSource.Utf8());
187     ES2PANDA_ASSERT(cda != nullptr);
188     cda->EnumerateMethods([this, &var, &createdMethods, program, methodDeclName,
189                            &cda](panda_file::MethodDataAccessor &mda) {
190         if (!methodDeclName.Is(mda.GetFullName())) {
191             return;
192         }
193 
194         MethodBuilder builder(debugInfoPlugin_.GetIrCheckHelper()->GetChecker(), mda, helpers::GetModifierFlags(*cda));
195         auto *method = std::move(builder).Build();
196 
197         method->SetParent(program->GlobalClass());
198         createdMethods.emplace_back(method);
199 
200         // Postpone Checker until the whole overload set is created.
201         debugInfoPlugin_.GetIrCheckHelper()->CheckGlobalEntity(program, method, false);
202 
203         // Sanity checks.
204         auto *methodVar = method->AsClassElement()->Value()->AsFunctionExpression()->Function()->Id()->Variable();
205         ES2PANDA_ASSERT(methodVar != nullptr);
206         ES2PANDA_ASSERT(var == nullptr || var == methodVar);
207         var = methodVar;
208     });
209 
210     return var;
211 }
212 
CreateLocalVarDecl(ir::Identifier * ident,RegisterNumber regNumber,const std::string & typeSignature)213 varbinder::Variable *DebugInfoDeserializer::CreateLocalVarDecl(ir::Identifier *ident, RegisterNumber regNumber,
214                                                                const std::string &typeSignature)
215 {
216     ES2PANDA_ASSERT(ident);
217 
218     auto *checkHelper = debugInfoPlugin_.GetIrCheckHelper();
219     auto *varBinder = debugInfoPlugin_.GetETSBinder();
220     auto identName = ident->Name().Utf8();
221 
222     auto typeId = helpers::GetTypeId(typeSignature);
223     auto *checker = checkHelper->GetChecker();
224     auto varDeclSource =
225         GetVarDeclSourceCode(identName, regNumber, typeSignature, typeId, checker->GetGlobalTypesHolder());
226 
227     // Set up correct scope before parsing statements.
228     auto *topStatement = helpers::GetEnclosingBlock(ident);
229     checker::ScopeContext ctx(checker, topStatement->Scope());
230     auto statementScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, topStatement->Scope());
231 
232     parser::Program p(checker->Allocator(), varBinder);
233     auto parser = parser::ETSParser(&p, *varBinder->GetContext()->config->options,
234                                     *varBinder->GetContext()->diagnosticEngine, parser::ParserStatus::NO_OPTS);
235 
236     auto *varDecl = parser.CreateFormattedStatement(varDeclSource, parser::ParserContext::DEFAULT_SOURCE_FILE);
237 
238     // NOTE(kaskov): #23399 It is temporary solution, we clear SourcePosition in generated code
239     compiler::SetSourceRangesRecursively(varDecl, lexer::SourceRange());
240 
241     ES2PANDA_ASSERT(varDecl != nullptr);
242     varDecl->SetParent(topStatement);
243     // Declaration will be placed at start of current scope.
244     // Can't insert right away until block's statements iteration ends.
245     debugInfoPlugin_.RegisterPrologueEpilogue<true>(topStatement, varDecl);
246     checkHelper->CheckLocalEntity(varDecl);
247 
248     // Yet don't track whether the value was modified, so store result unconditionally in the end of the scope.
249     auto varUpdateSource = GetVarUpdateSourceCode(identName, regNumber, typeId);
250 
251     auto *varUpdate = parser.CreateFormattedStatement(varUpdateSource, parser::ParserContext::DEFAULT_SOURCE_FILE);
252 
253     // NOTE(kaskov): #23399 It is temporary solution, we clear SourcePosition in generated code
254     compiler::SetSourceRangesRecursively(varUpdate, lexer::SourceRange());
255 
256     ES2PANDA_ASSERT(varUpdate != nullptr);
257     varUpdate->SetParent(topStatement);
258     // Can't insert right away until block's statements iteration ends.
259     debugInfoPlugin_.RegisterPrologueEpilogue<false>(topStatement, varUpdate);
260     checkHelper->CheckLocalEntity(varUpdate);
261 
262     // Local variables are not registered, as they can be found in local scope.
263     ES2PANDA_ASSERT(varDecl->AsVariableDeclaration()->Declarators().size() == 1);
264     return varDecl->AsVariableDeclaration()->Declarators()[0]->Id()->Variable();
265 }
266 
267 }  // namespace ark::es2panda::evaluate
268