• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 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 <cassert>
17 #include <cstddef>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <string>
21 #include <utility>
22 
23 #include "find_references.h"
24 #include "ir/astNode.h"
25 #include "parser/program/program.h"
26 #include "public/public.h"
27 #include "public/es2panda_lib.h"
28 #include "compiler/lowering/util.h"
29 #include "internal_api.h"
30 
31 namespace {
32 
GetIdentifier(ark::es2panda::ir::AstNode * node)33 ark::es2panda::ir::AstNode *GetIdentifier(ark::es2panda::ir::AstNode *node)
34 {
35     if (node == nullptr) {
36         return nullptr;
37     }
38     if (!node->IsIdentifier()) {
39         return node->FindChild([](ark::es2panda::ir::AstNode *child) { return child->IsIdentifier(); });
40     }
41     return node;
42 }
43 
GetIdentifierName(ark::es2panda::ir::AstNode * node)44 std::string GetIdentifierName(ark::es2panda::ir::AstNode *node)
45 {
46     auto id = ::GetIdentifier(node);
47     if (id == nullptr) {
48         return "";
49     }
50     return std::string {id->AsIdentifier()->Name()};
51 }
52 
GetOwner(ark::es2panda::ir::AstNode * node)53 ark::es2panda::ir::AstNode *GetOwner(ark::es2panda::ir::AstNode *node)
54 {
55     auto id = ::GetIdentifier(node);
56     if (id == nullptr) {
57         return nullptr;
58     }
59     return GetIdentifier(ark::es2panda::compiler::DeclarationFromIdentifier(id->AsIdentifier()));
60 }
61 
62 // NOTE(muhammet): This may be wrong/inconsistent (slow for sure) for comparison, have to investigate
63 // The Type of the Node and identifier name are not enough they don't account for edge cases like
64 // functions with the same name and signature in different namespaces
65 using LocationId = std::string;
GetLocationId(ark::es2panda::ir::AstNode * node,ark::es2panda::parser::Program * program)66 LocationId GetLocationId(ark::es2panda::ir::AstNode *node, ark::es2panda::parser::Program *program)
67 {
68     if (node == nullptr) {
69         return "";
70     }
71     // Find which file the node belongs to
72     std::string absPath;
73     if (program->Ast() == node->GetTopStatement()) {
74         absPath = std::string {program->AbsoluteName()};
75     }
76     auto externals = program->DirectExternalSources();
77     auto top = node->GetTopStatement();
78     for (const auto &entry : externals) {
79         for (const auto &p : entry.second) {
80             auto programAbsPath = std::string {p->AbsoluteName()};
81             auto ast = p->Ast();
82             if (ast == top) {
83                 absPath = programAbsPath;
84                 break;
85             }
86         }
87     }
88     // Should uniquely identify a token using it's sourceFile path, start and end positions
89     auto id = absPath + ":" + std::to_string(node->Start().index) + ":" + std::to_string(node->Start().line) + ":" +
90               std::to_string(node->End().index) + ":" + std::to_string(node->End().line);
91     return id;
92 }
93 
FindReferences(const ark::es2panda::SourceFile & srcFile,LocationId tokenLocationId,std::string tokenName)94 std::set<ark::es2panda::lsp::ReferencedNode> FindReferences(const ark::es2panda::SourceFile &srcFile,
95                                                             LocationId tokenLocationId, std::string tokenName)
96 {
97     ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
98     auto filePath = std::string {srcFile.filePath};
99     auto fileContent = std::string {srcFile.source};
100     auto context = initializer.CreateContext(filePath.c_str(), ES2PANDA_STATE_CHECKED, fileContent.c_str());
101 
102     // Clear before searching each file
103     std::set<ark::es2panda::lsp::ReferencedNode> res;
104     ark::es2panda::parser::Program *pprogram = nullptr;
105     auto cb = [&tokenName, &pprogram, &filePath, &tokenLocationId, &res](ark::es2panda::ir::AstNode *node) {
106         if (!node->IsIdentifier()) {
107             return false;
108         }
109         auto nodeName = ::GetIdentifierName(node);
110         if (nodeName != tokenName) {
111             return false;
112         }
113         auto owner = GetOwner(node);
114         auto nodeOwnerLocationId = GetLocationId(owner, pprogram);
115         auto nodeLocationId = GetLocationId(node, pprogram);
116         bool isDefinition = nodeLocationId == nodeOwnerLocationId;
117         if (nodeOwnerLocationId == tokenLocationId) {
118             res.insert(ark::es2panda::lsp::ReferencedNode {filePath, node->Start().index, node->End().index,
119                                                            node->Start().line, isDefinition});
120         }
121         return false;
122     };
123 
124     // Search an ast
125     auto search = [&cb](ark::es2panda::parser::Program *program) -> void {
126         if (program == nullptr) {
127             return;
128         }
129         auto ast = program->Ast();
130         ast->FindChild(cb);
131     };
132 
133     // Search the file
134     auto pctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
135     pprogram = pctx->parserProgram;
136     search(pprogram);
137 
138     initializer.DestroyContext(context);
139     return res;
140 }
141 }  // namespace
142 
143 namespace ark::es2panda::lsp {
FindReferences(CancellationToken * tkn,const std::vector<ark::es2panda::SourceFile> & srcFiles,const ark::es2panda::SourceFile & srcFile,size_t position)144 std::set<ReferencedNode> FindReferences(CancellationToken *tkn, const std::vector<ark::es2panda::SourceFile> &srcFiles,
145                                         const ark::es2panda::SourceFile &srcFile, size_t position)
146 {
147     std::string tokenName;
148     LocationId tokenLocationId;
149     // Part 1: Determine the type of token function/variable
150     {
151         ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
152         auto filePath = std::string {srcFile.filePath};
153         auto fileContent = std::string {srcFile.source};
154         auto context = initializer.CreateContext(filePath.c_str(), ES2PANDA_STATE_CHECKED, fileContent.c_str());
155 
156         auto touchingToken = GetTouchingToken(context, position, false);
157         tokenName = ::GetIdentifierName(touchingToken);
158         auto owner = GetOwner(touchingToken);
159         tokenLocationId =
160             ::GetLocationId(owner, reinterpret_cast<ark::es2panda::public_lib::Context *>(context)->parserProgram);
161         initializer.DestroyContext(context);
162     }
163 
164     if (tokenLocationId.empty()) {
165         return {};
166     }
167 
168     std::set<ReferencedNode> res;
169     // NOTE(muhammet): The process is very wasteful, it creates a new context for each file even if they're dependent on
170     // one another
171     for (auto fl : srcFiles) {
172         // NOTE(muhammet): Need for more fine grained cancellation check but for now doing it before context creations
173         // should be good enough, thats where it's slowest
174         if (tkn->IsCancellationRequested()) {
175             return res;
176         }
177         auto refList = ::FindReferences(fl, tokenLocationId, tokenName);
178         for (const auto &entry : refList) {
179             res.insert(entry);
180         }
181     }
182 
183     return res;
184 }
185 }  // namespace ark::es2panda::lsp
186