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