Line data Source code
1 : // Copyright (C) 2020-2026 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 4028 : ExportContext::ExportContext () : mappings (Analysis::Mappings::get ()) {}
38 :
39 4028 : 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 1915 : ExportContext::emit_trait (const HIR::Trait &trait)
58 : {
59 : // lookup the AST node for this
60 1915 : AST::Item *item
61 1915 : = mappings.lookup_ast_item (trait.get_mappings ().get_nodeid ()).value ();
62 :
63 1915 : std::stringstream oss;
64 1915 : AST::Dump dumper (oss);
65 1915 : dumper.go (*item);
66 :
67 3830 : public_interface_buffer += oss.str ();
68 1915 : }
69 :
70 : void
71 707 : ExportContext::emit_function (const HIR::Function &fn)
72 : {
73 : // lookup the AST node for this
74 707 : AST::Item *item
75 707 : = mappings.lookup_ast_item (fn.get_mappings ().get_nodeid ()).value ();
76 :
77 : // is this a CFG macro or not
78 707 : if (item->is_marked_for_strip ())
79 0 : return;
80 :
81 : // FIXME add assertion that item must be a vis_item;
82 707 : 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 707 : std::stringstream oss;
88 707 : AST::Dump dumper (oss);
89 707 : if (!fn.has_generics ())
90 : {
91 : // FIXME assert that this is actually an AST::Function
92 626 : AST::Function &function = static_cast<AST::Function &> (vis_item);
93 :
94 626 : std::vector<std::unique_ptr<AST::ExternalItem>> external_items;
95 626 : external_items.emplace_back (
96 626 : static_cast<AST::ExternalItem *> (&function));
97 :
98 626 : AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST),
99 : std::move (external_items),
100 626 : vis_item.get_visibility (), {}, {},
101 626 : fn.get_locus ());
102 :
103 626 : dumper.go (extern_block);
104 626 : }
105 : else
106 : {
107 81 : dumper.go (*item);
108 : }
109 :
110 : // store the dump
111 1414 : public_interface_buffer += oss.str ();
112 707 : }
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 4028 : ExportContext::get_interface_buffer () const
127 : {
128 4028 : 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 4028 : ExportVisItems (ExportContext &context) : ctx (context) {}
137 :
138 49 : 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 168 : void visit (HIR::StructStruct &) override {}
143 57 : 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 1915 : void visit (HIR::Trait &trait) override { ctx.emit_trait (trait); }
152 :
153 707 : void visit (HIR::Function &function) override
154 : {
155 707 : ctx.emit_function (function);
156 707 : }
157 :
158 : private:
159 : ExportContext &ctx;
160 : };
161 :
162 4028 : PublicInterface::PublicInterface (HIR::Crate &crate)
163 4028 : : crate (crate), mappings (Analysis::Mappings::get ()), context ()
164 4028 : {}
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 4028 : PublicInterface::ExportTo (HIR::Crate &crate, const std::string &output_path)
176 : {
177 4028 : PublicInterface interface (crate);
178 4028 : interface.gather_export_data ();
179 4028 : interface.write_to_path (output_path);
180 4028 : }
181 :
182 : void
183 4028 : PublicInterface::gather_export_data ()
184 : {
185 4028 : ExportVisItems visitor (context);
186 20761 : for (auto &item : crate.get_items ())
187 : {
188 16733 : bool is_vis_item = item->get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM;
189 16733 : if (!is_vis_item)
190 0 : continue;
191 :
192 16733 : HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (*item.get ());
193 16733 : if (is_crate_public (vis_item))
194 3004 : vis_item.accept_vis (visitor);
195 : }
196 :
197 4030 : for (auto ¯o : mappings.get_exported_macros ())
198 4030 : context.emit_macro (macro);
199 4028 : }
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 4028 : PublicInterface::write_to_path (const std::string &path) const
233 : {
234 : // validate path contains correct extension
235 4028 : const std::string expected_file_name = expected_metadata_filename ();
236 4028 : const char *path_base_name = lbasename (path.c_str ());
237 4028 : 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 4028 : const auto &buf = context.get_interface_buffer ();
248 4028 : std::string size_buffer = std::to_string (buf.size ());
249 :
250 : // md5 this
251 4028 : struct md5_ctx chksm;
252 4028 : unsigned char checksum[16];
253 :
254 4028 : md5_init_ctx (&chksm);
255 4028 : md5_process_bytes (buf.c_str (), buf.size (), &chksm);
256 4028 : md5_finish_ctx (&chksm, checksum);
257 :
258 : // MAGIC MD5 DLIM DLIM buffer-size DELIM contents
259 4028 : const std::string current_crate_name = mappings.get_current_crate_name ();
260 :
261 : // write to path
262 4028 : FILE *nfd = fopen (path.c_str (), "wb");
263 4028 : 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 4028 : 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 4028 : 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 4028 : 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 4028 : 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 4028 : 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 4028 : 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 4028 : 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 4028 : if (!buf.empty ())
329 1607 : 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 4028 : fclose (nfd);
339 4028 : }
340 :
341 : bool
342 16733 : PublicInterface::is_crate_public (const HIR::VisItem &item)
343 : {
344 16733 : const HIR::Visibility &visibility = item.get_visibility ();
345 :
346 16733 : bool is_public
347 16733 : = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
348 16733 : bool has_path = !visibility.get_path ().is_error ();
349 :
350 : // FIXME this might be pub(crate)
351 : // Arthur magic required here
352 :
353 16733 : return is_public && !has_path;
354 : }
355 :
356 : std::string
357 8056 : PublicInterface::expected_metadata_filename ()
358 : {
359 8056 : auto &mappings = Analysis::Mappings::get ();
360 :
361 8056 : const std::string current_crate_name = mappings.get_current_crate_name ();
362 8056 : return current_crate_name + extension_path;
363 8056 : }
364 :
365 : } // namespace Metadata
366 : } // namespace Rust
|