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