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