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