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