Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
5 : : // GCC is free software; you can redistribute it and/or modify it under
6 : : // the terms of the GNU General Public License as published by the Free
7 : : // Software Foundation; either version 3, or (at your option) any later
8 : : // version.
9 : :
10 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : // for more details.
14 : :
15 : : // You should have received a copy of the GNU General Public License
16 : : // along with GCC; see the file COPYING3. If not see
17 : : // <http://www.gnu.org/licenses/>.
18 : :
19 : : #include "rust-export-metadata.h"
20 : : #include "rust-hir-visitor.h"
21 : : #include "rust-hir-full.h"
22 : : #include "rust-hir-map.h"
23 : : #include "rust-ast-dump.h"
24 : : #include "rust-abi.h"
25 : : #include "rust-item.h"
26 : : #include "rust-macro.h"
27 : : #include "rust-object-export.h"
28 : :
29 : : #include "md5.h"
30 : : #include "rust-system.h"
31 : :
32 : : namespace Rust {
33 : : namespace Metadata {
34 : :
35 : : static const std::string extension_path = ".rox";
36 : :
37 : 3990 : ExportContext::ExportContext () : mappings (Analysis::Mappings::get ()) {}
38 : :
39 : 3990 : ExportContext::~ExportContext () {}
40 : :
41 : : void
42 : 0 : ExportContext::push_module_scope (const HIR::Module &module)
43 : : {
44 : 0 : module_stack.push_back (module);
45 : 0 : }
46 : :
47 : : const HIR::Module &
48 : 0 : ExportContext::pop_module_scope ()
49 : : {
50 : 0 : rust_assert (!module_stack.empty ());
51 : 0 : const HIR::Module &poped = module_stack.back ();
52 : 0 : module_stack.pop_back ();
53 : 0 : return poped;
54 : : }
55 : :
56 : : void
57 : 1910 : ExportContext::emit_trait (const HIR::Trait &trait)
58 : : {
59 : : // lookup the AST node for this
60 : 1910 : AST::Item *item
61 : 1910 : = mappings.lookup_ast_item (trait.get_mappings ().get_nodeid ()).value ();
62 : :
63 : 1910 : std::stringstream oss;
64 : 1910 : AST::Dump dumper (oss);
65 : 1910 : dumper.go (*item);
66 : :
67 : 3820 : public_interface_buffer += oss.str ();
68 : 1910 : }
69 : :
70 : : void
71 : 684 : ExportContext::emit_function (const HIR::Function &fn)
72 : : {
73 : : // lookup the AST node for this
74 : 684 : AST::Item *item
75 : 684 : = mappings.lookup_ast_item (fn.get_mappings ().get_nodeid ()).value ();
76 : :
77 : : // is this a CFG macro or not
78 : 684 : if (item->is_marked_for_strip ())
79 : 0 : return;
80 : :
81 : : // FIXME add assertion that item must be a vis_item;
82 : 684 : AST::VisItem &vis_item = static_cast<AST::VisItem &> (*item);
83 : :
84 : : // if its a generic function we need to output the full declaration
85 : : // otherwise we can let people link against this
86 : :
87 : 684 : std::stringstream oss;
88 : 684 : AST::Dump dumper (oss);
89 : 684 : if (!fn.has_generics ())
90 : : {
91 : : // FIXME assert that this is actually an AST::Function
92 : 605 : AST::Function &function = static_cast<AST::Function &> (vis_item);
93 : :
94 : 605 : std::vector<std::unique_ptr<AST::ExternalItem>> external_items;
95 : 605 : external_items.emplace_back (
96 : 605 : static_cast<AST::ExternalItem *> (&function));
97 : :
98 : 605 : AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST),
99 : : std::move (external_items),
100 : 605 : vis_item.get_visibility (), {}, {},
101 : 605 : fn.get_locus ());
102 : :
103 : 605 : dumper.go (extern_block);
104 : 605 : }
105 : : else
106 : : {
107 : 79 : dumper.go (*item);
108 : : }
109 : :
110 : : // store the dump
111 : 1368 : public_interface_buffer += oss.str ();
112 : 684 : }
113 : :
114 : : void
115 : 2 : ExportContext::emit_macro (AST::MacroRulesDefinition ¯o)
116 : : {
117 : 2 : std::stringstream oss;
118 : 2 : AST::Dump dumper (oss);
119 : :
120 : 2 : dumper.go (macro);
121 : :
122 : 4 : public_interface_buffer += oss.str ();
123 : 2 : }
124 : :
125 : : const std::string &
126 : 3990 : ExportContext::get_interface_buffer () const
127 : : {
128 : 3990 : return public_interface_buffer;
129 : : }
130 : :
131 : : // implicitly by using HIR nodes we know that these have passed CFG expansion
132 : : // and they exist in the compilation unit
133 : : class ExportVisItems : public HIR::HIRVisItemVisitor
134 : : {
135 : : public:
136 : 3990 : ExportVisItems (ExportContext &context) : ctx (context) {}
137 : :
138 : 47 : void visit (HIR::Module &) override {}
139 : 0 : void visit (HIR::ExternCrate &) override {}
140 : 0 : void visit (HIR::UseDeclaration &) override {}
141 : 0 : void visit (HIR::TypeAlias &) override {}
142 : 161 : void visit (HIR::StructStruct &) override {}
143 : 49 : void visit (HIR::TupleStruct &) override {}
144 : 65 : void visit (HIR::Enum &) override {}
145 : 36 : void visit (HIR::Union &) override {}
146 : 4 : void visit (HIR::ConstantItem &) override {}
147 : 3 : void visit (HIR::StaticItem &) override {}
148 : 0 : void visit (HIR::ImplBlock &) override {}
149 : 0 : void visit (HIR::ExternBlock &) override {}
150 : :
151 : 1910 : void visit (HIR::Trait &trait) override { ctx.emit_trait (trait); }
152 : :
153 : 684 : void visit (HIR::Function &function) override
154 : : {
155 : 684 : ctx.emit_function (function);
156 : 684 : }
157 : :
158 : : private:
159 : : ExportContext &ctx;
160 : : };
161 : :
162 : 3990 : PublicInterface::PublicInterface (HIR::Crate &crate)
163 : 3990 : : crate (crate), mappings (Analysis::Mappings::get ()), context ()
164 : 3990 : {}
165 : :
166 : : void
167 : 0 : PublicInterface::Export (HIR::Crate &crate)
168 : : {
169 : 0 : PublicInterface interface (crate);
170 : 0 : interface.gather_export_data ();
171 : 0 : interface.write_to_object_file ();
172 : 0 : }
173 : :
174 : : void
175 : 3990 : PublicInterface::ExportTo (HIR::Crate &crate, const std::string &output_path)
176 : : {
177 : 3990 : PublicInterface interface (crate);
178 : 3990 : interface.gather_export_data ();
179 : 3990 : interface.write_to_path (output_path);
180 : 3990 : }
181 : :
182 : : void
183 : 3990 : PublicInterface::gather_export_data ()
184 : : {
185 : 3990 : ExportVisItems visitor (context);
186 : 20610 : for (auto &item : crate.get_items ())
187 : : {
188 : 16620 : bool is_vis_item = item->get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM;
189 : 16620 : if (!is_vis_item)
190 : 0 : continue;
191 : :
192 : 16620 : HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (*item.get ());
193 : 16620 : if (is_crate_public (vis_item))
194 : 2959 : vis_item.accept_vis (visitor);
195 : : }
196 : :
197 : 3992 : for (auto ¯o : mappings.get_exported_macros ())
198 : 3992 : context.emit_macro (macro);
199 : 3990 : }
200 : :
201 : : void
202 : 0 : PublicInterface::write_to_object_file () const
203 : : {
204 : : // done
205 : 0 : const auto &buf = context.get_interface_buffer ();
206 : 0 : std::string size_buffer = std::to_string (buf.size ());
207 : :
208 : : // md5 this
209 : 0 : struct md5_ctx chksm;
210 : 0 : unsigned char checksum[16];
211 : :
212 : 0 : md5_init_ctx (&chksm);
213 : 0 : md5_process_bytes (buf.c_str (), buf.size (), &chksm);
214 : 0 : md5_finish_ctx (&chksm, checksum);
215 : :
216 : : // MAGIC MD5 DLIM DLIM buffer-size DELIM contents
217 : 0 : const std::string current_crate_name = mappings.get_current_crate_name ();
218 : :
219 : : // extern void
220 : 0 : rust_write_export_data (kMagicHeader, sizeof (kMagicHeader));
221 : 0 : rust_write_export_data ((const char *) checksum, sizeof (checksum));
222 : 0 : rust_write_export_data (kSzDelim, sizeof (kSzDelim));
223 : 0 : rust_write_export_data (current_crate_name.c_str (),
224 : 0 : current_crate_name.size ());
225 : 0 : rust_write_export_data (kSzDelim, sizeof (kSzDelim));
226 : 0 : rust_write_export_data (size_buffer.c_str (), size_buffer.size ());
227 : 0 : rust_write_export_data (kSzDelim, sizeof (kSzDelim));
228 : 0 : rust_write_export_data (buf.c_str (), buf.size ());
229 : 0 : }
230 : :
231 : : void
232 : 3990 : PublicInterface::write_to_path (const std::string &path) const
233 : : {
234 : : // validate path contains correct extension
235 : 3990 : const std::string expected_file_name = expected_metadata_filename ();
236 : 3990 : const char *path_base_name = lbasename (path.c_str ());
237 : 3990 : if (strcmp (path_base_name, expected_file_name.c_str ()) != 0)
238 : : {
239 : 0 : rust_error_at (UNDEF_LOCATION,
240 : : "expected metadata-output path to have base file name of: "
241 : : "%qs got %qs",
242 : : expected_file_name.c_str (), path_base_name);
243 : 0 : return;
244 : : }
245 : :
246 : : // done
247 : 3990 : const auto &buf = context.get_interface_buffer ();
248 : 3990 : std::string size_buffer = std::to_string (buf.size ());
249 : :
250 : : // md5 this
251 : 3990 : struct md5_ctx chksm;
252 : 3990 : unsigned char checksum[16];
253 : :
254 : 3990 : md5_init_ctx (&chksm);
255 : 3990 : md5_process_bytes (buf.c_str (), buf.size (), &chksm);
256 : 3990 : md5_finish_ctx (&chksm, checksum);
257 : :
258 : : // MAGIC MD5 DLIM DLIM buffer-size DELIM contents
259 : 3990 : const std::string current_crate_name = mappings.get_current_crate_name ();
260 : :
261 : : // write to path
262 : 3990 : FILE *nfd = fopen (path.c_str (), "wb");
263 : 3990 : if (nfd == NULL)
264 : : {
265 : 0 : rust_error_at (UNDEF_LOCATION, "failed to open file %qs for writing: %s",
266 : 0 : path.c_str (), xstrerror (errno));
267 : 0 : return;
268 : : }
269 : :
270 : : // write data
271 : 3990 : if (fwrite (kMagicHeader, sizeof (kMagicHeader), 1, nfd) < 1)
272 : : {
273 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
274 : 0 : path.c_str (), xstrerror (errno));
275 : 0 : fclose (nfd);
276 : 0 : return;
277 : : }
278 : :
279 : 3990 : if (fwrite (checksum, sizeof (checksum), 1, nfd) < 1)
280 : : {
281 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
282 : 0 : path.c_str (), xstrerror (errno));
283 : 0 : fclose (nfd);
284 : 0 : return;
285 : : }
286 : :
287 : 3990 : if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
288 : : {
289 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
290 : 0 : path.c_str (), xstrerror (errno));
291 : 0 : fclose (nfd);
292 : 0 : return;
293 : : }
294 : :
295 : 3990 : if (fwrite (current_crate_name.c_str (), current_crate_name.size (), 1, nfd)
296 : : < 1)
297 : : {
298 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
299 : 0 : path.c_str (), xstrerror (errno));
300 : 0 : fclose (nfd);
301 : 0 : return;
302 : : }
303 : :
304 : 3990 : if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
305 : : {
306 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
307 : 0 : path.c_str (), xstrerror (errno));
308 : 0 : fclose (nfd);
309 : 0 : return;
310 : : }
311 : :
312 : 3990 : if (fwrite (size_buffer.c_str (), size_buffer.size (), 1, nfd) < 1)
313 : : {
314 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
315 : 0 : path.c_str (), xstrerror (errno));
316 : 0 : fclose (nfd);
317 : 0 : return;
318 : : }
319 : :
320 : 3990 : if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
321 : : {
322 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
323 : 0 : path.c_str (), xstrerror (errno));
324 : 0 : fclose (nfd);
325 : 0 : return;
326 : : }
327 : :
328 : 3990 : if (!buf.empty ())
329 : 1587 : if (fwrite (buf.c_str (), buf.size (), 1, nfd) < 1)
330 : : {
331 : 0 : rust_error_at (UNDEF_LOCATION, "failed to write to file %qs: %s",
332 : 0 : path.c_str (), xstrerror (errno));
333 : 0 : fclose (nfd);
334 : 0 : return;
335 : : }
336 : :
337 : : // done
338 : 3990 : fclose (nfd);
339 : 3990 : }
340 : :
341 : : bool
342 : 16620 : PublicInterface::is_crate_public (const HIR::VisItem &item)
343 : : {
344 : 16620 : const HIR::Visibility &visibility = item.get_visibility ();
345 : :
346 : 16620 : bool is_public
347 : 16620 : = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
348 : 16620 : bool has_path = !visibility.get_path ().is_error ();
349 : :
350 : : // FIXME this might be pub(crate)
351 : : // Arthur magic required here
352 : :
353 : 16620 : return is_public && !has_path;
354 : : }
355 : :
356 : : std::string
357 : 7980 : PublicInterface::expected_metadata_filename ()
358 : : {
359 : 7980 : auto &mappings = Analysis::Mappings::get ();
360 : :
361 : 7980 : const std::string current_crate_name = mappings.get_current_crate_name ();
362 : 7980 : return current_crate_name + extension_path;
363 : 7980 : }
364 : :
365 : : } // namespace Metadata
366 : : } // namespace Rust
|