ArKanjo 0.5.1
A tool for find code duplicated functions in codebases
ast_method.cpp
Go to the documentation of this file.
8
9#include <thread>
10
12
13ZSNode ASTMethod::from_tsnode(TSNode node) {
14 ZSNode out;
15 out.symbol = ts_node_symbol(node);
16
17 uint32_t count = ts_node_named_child_count(node);
18
19 for (uint32_t i = 0; i < count; i++) {
20 out.children.push_back(from_tsnode(ts_node_named_child(node, i)));
21 }
22
23 return out;
24}
25
26int build_postorder(const ZSNode& node, PostOrderTree& tree) {
27 int leftmost = -1;
28
29 for (const auto& child : node.children) {
30 int child_lmd = build_postorder(child, tree);
31
32 if (leftmost == -1) {
33 leftmost = child_lmd;
34 }
35 }
36
37 // node type
38 int id = tree.labels.size();
39
40 tree.labels.push_back(node.symbol);
41
42 if (leftmost == -1) {
43 leftmost = id;
44 }
45
46 tree.lmd.push_back(leftmost);
47
48 return leftmost;
49}
50
51int ASTMethod::tree_distance(const PostOrderTree& a, const PostOrderTree& b) {
52 size_t n = a.labels.size();
53 size_t m = b.labels.size();
54
55 std::vector<std::vector<int>> dp(n + 1, std::vector<int>(m + 1));
56
57 for (size_t i = 0; i <= n; i++) {
58 dp[i][0] = i;
59 }
60
61 for (size_t j = 0; j <= m; j++) {
62 dp[0][j] = j;
63 }
64
65 for (size_t i = 1; i <= n; i++) {
66 for (size_t j = 1; j <= m; j++) {
67
68 int rename_cost = a.labels[i - 1] == b.labels[j - 1] ? 0 : 1;
69
70 dp[i][j] = std::min({
71 dp[i - 1][j] + 1,
72 dp[i][j - 1] + 1,
73 dp[i - 1][j - 1] + rename_cost
74 });
75 }
76 }
77
78 return dp[n][m];
79}
80
81double ASTMethod::similarity_score(const PostOrderTree& a, const PostOrderTree& b) {
82 int dist = tree_distance(a, b);
83
84 int max_size = std::max(a.labels.size(), b.labels.size());
85
86 return 1.0 - (static_cast<double>(dist) / max_size);
87}
88
89ASTMethod::ASTMethod(const fs::path& base_path_, double similarity_) {
90 base_path = base_path_;
91 similarity = similarity_;
92
93 if (similarity < 0) {
94 std::cerr << "SIMILARITY SHOULD BE GREATER OR EQUAL 0 TO USE DUPLICATION FINDER BY AST COMMAND";
95 }
96}
97
98void ASTMethod::save_duplications(std::vector<DuplicationEntry>& file_duplication_pairs) {
99 for (const auto& [similarity, path1, path2] : file_duplication_pairs) {
100 output_file << path1 << ' ' << path2 << ' ';
101 output_file << std::fixed << std::setprecision(2) << similarity << '\n';
102 }
103}
104
105void ASTMethod::header_duplications() {
106 fs::path output_file_path = base_path / "output_parsed.tmp";
107
108 output_file.open(
109 output_file_path,
110 std::ios::out | std::ios::trunc
111 );
112}
113
114void ASTMethod::flush_duplications(std::vector<DuplicationEntry>& local) {
115 if (local.empty())
116 return;
117
118 std::lock_guard<std::mutex> lock(output_mutex);
119
120 save_duplications(local);
121
122 total_matches.fetch_add(local.size(), std::memory_order_relaxed);
123
124 local.clear();
125}
126
127void ASTMethod::compare_range(
128 const std::vector<PostOrderTree>& processed,
129 size_t begin, size_t end,
130 std::vector<DuplicationEntry>& local
131) {
132 for (size_t i = begin; i < end; i++) {
133 for (size_t j = i + 1; j < processed.size(); j++) {
134 double sim = similarity_score(processed[i], processed[j]) * 100.0;
135
136 if (sim >= similarity)
137 local.push_back({sim, processed[i].path.string(), processed[j].path.string()});
138 }
139 }
140}
141
143 auto ast = fd.get_feature<ASTFeature>();
144 if (!ast)
145 return;
146
147 auto tree = from_tsnode(ast->root);
148
150 build_postorder(tree, p);
151
152 fs::path path{fd.path};
153 std::string filename = fd.function_name + path.extension().string();
154
155 p.path = path / filename;
156
157 {
158 std::lock_guard<std::mutex> lock(processed_mutex);
159 processed.push_back(p);
160 }
161}
162
164 unsigned int thread_count = std::thread::hardware_concurrency();
165 const size_t threads = std::max<size_t>(1, thread_count);
166
167 size_t n = processed.size();
168 size_t chunk = (n + threads - 1) / threads;
169
170 constexpr size_t block_size = 4;
171 constexpr size_t flush_size = 10000;
172 std::atomic<size_t> next{0};
173 std::vector<std::thread> workers;
174
175 header_duplications();
176
177 for (size_t t = 0; t < threads; t++) {
178 workers.emplace_back([&]() {
179 std::vector<DuplicationEntry> local;
180 local.reserve(flush_size);
181
182 while (true) {
183 size_t begin = next.fetch_add(block_size);
184 if (begin >= processed.size()) break;
185
186 size_t end = std::min(begin + block_size, processed.size());
187
188 compare_range(processed, begin, end, local);
189
190 if (local.size() >= flush_size)
191 flush_duplications(local);
192 }
193
194 flush_duplications(local);
195 });
196 }
197
198 for (auto& w : workers)
199 w.join();
200
201 output_file.flush();
202 output_file.close();
203
204 std::ifstream tmp(base_path / "output_parsed.tmp");
205 std::ofstream final(FunctionStorage{}.build_similarity_path());
206
207 final << total_matches.load() << '\n';
208 final << tmp.rdbuf();
209
210 tmp.close();
211 final.close();
212
213 fs::remove(base_path / "output_parsed.tmp");
214}
int build_postorder(const ZSNode &node, PostOrderTree &tree)
Definition: ast_method.cpp:26
ASTMethod(const fs::path &base_path_, double similarity_)
Constructs preprocessor with configuration.
Definition: ast_method.cpp:89
void on_function(const FunctionData &fd) override
Definition: ast_method.cpp:142
void execute() override
Executes the preprocessing pipeline.
Definition: ast_method.cpp:163
std::string function_name
Name of the function.
std::string path
std::shared_ptr< T > get_feature() const
fs::path build_similarity_path() const
Builds similarity file path.
Configuration management interface.
Provides access to function storage locations.
fs::path path
Definition: ast_method.hpp:39
std::vector< int > lmd
Left-most descendant index for each node.
Definition: ast_method.hpp:37
std::vector< TSSymbol > labels
Tree-sitter grammar symbol for each node.
Definition: ast_method.hpp:27
std::vector< ZSNode > children
Definition: ast_method.hpp:20
TSSymbol symbol
Definition: ast_method.hpp:19
Defines utility functions used across all files.