diff options
Diffstat (limited to 'gcc/rust/metadata/rust-export-metadata.cc')
-rw-r--r-- | gcc/rust/metadata/rust-export-metadata.cc | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/gcc/rust/metadata/rust-export-metadata.cc b/gcc/rust/metadata/rust-export-metadata.cc new file mode 100644 index 0000000..4856bc2 --- /dev/null +++ b/gcc/rust/metadata/rust-export-metadata.cc @@ -0,0 +1,385 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "rust-export-metadata.h" +#include "rust-hir-visitor.h" +#include "rust-hir-full.h" +#include "rust-hir-map.h" +#include "rust-ast-dump.h" +#include "rust-abi.h" +#include "rust-object-export.h" + +#include "md5.h" + +namespace Rust { +namespace Metadata { + +static const std::string extension_path = ".rox"; + +ExportContext::ExportContext () : mappings (Analysis::Mappings::get ()) {} + +ExportContext::~ExportContext () {} + +void +ExportContext::push_module_scope (const HIR::Module &module) +{ + module_stack.push_back (module); +} + +const HIR::Module & +ExportContext::pop_module_scope () +{ + rust_assert (!module_stack.empty ()); + const HIR::Module &poped = module_stack.back (); + module_stack.pop_back (); + return poped; +} + +void +ExportContext::emit_trait (const HIR::Trait &trait) +{ + // lookup the AST node for this + AST::Item *item = nullptr; + bool ok + = mappings->lookup_ast_item (trait.get_mappings ().get_nodeid (), &item); + rust_assert (ok); + + std::stringstream oss; + AST::Dump dumper (oss); + dumper.go (*item); + + public_interface_buffer += oss.str (); +} + +void +ExportContext::emit_function (const HIR::Function &fn) +{ + // lookup the AST node for this + AST::Item *item = nullptr; + bool ok = mappings->lookup_ast_item (fn.get_mappings ().get_nodeid (), &item); + rust_assert (ok); + + // is this a CFG macro or not + if (item->is_marked_for_strip ()) + return; + + // FIXME add assertion that item must be a vis_item; + AST::VisItem &vis_item = static_cast<AST::VisItem &> (*item); + + // if its a generic function we need to output the full declaration + // otherwise we can let people link against this + + std::stringstream oss; + AST::Dump dumper (oss); + if (!fn.has_generics ()) + { + // FIXME assert that this is actually an AST::Function + AST::Function &function = static_cast<AST::Function &> (vis_item); + + // we can emit an extern block with abi of "rust" + Identifier item_name = function.get_function_name (); + + // always empty for extern linkage + AST::WhereClause where_clause = AST::WhereClause::create_empty (); + std::vector<std::unique_ptr<AST::GenericParam>> generic_params; + + AST::Visibility vis = function.get_visibility (); + std::unique_ptr<AST::Type> return_type + = std::unique_ptr<AST::Type> (nullptr); + if (function.has_return_type ()) + { + return_type = function.get_return_type ()->clone_type (); + } + + std::vector<AST::NamedFunctionParam> function_params; + for (AST::FunctionParam ¶m : function.get_function_params ()) + { + std::string name = param.get_pattern ()->as_string (); + std::unique_ptr<AST::Type> param_type + = param.get_type ()->clone_type (); + + AST::NamedFunctionParam p (name, std::move (param_type), {}, + param.get_locus ()); + function_params.push_back (std::move (p)); + } + + AST::ExternalItem *external_item = new AST::ExternalFunctionItem ( + item_name, {} /* generic_params */, std::move (return_type), + where_clause, std::move (function_params), false /* has_variadics */, + {} /* variadic_outer_attrs */, vis, function.get_outer_attrs (), + function.get_locus ()); + + std::vector<std::unique_ptr<AST::ExternalItem>> external_items; + external_items.push_back ( + std::unique_ptr<AST::ExternalItem> (external_item)); + + AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST), + std::move (external_items), + vis_item.get_visibility (), {}, {}, + fn.get_locus ()); + + dumper.go (extern_block); + } + else + { + dumper.go (*item); + } + + // store the dump + public_interface_buffer += oss.str (); +} + +const std::string & +ExportContext::get_interface_buffer () const +{ + return public_interface_buffer; +} + +// implicitly by using HIR nodes we know that these have passed CFG expansion +// and they exist in the compilation unit +class ExportVisItems : public HIR::HIRVisItemVisitor +{ +public: + ExportVisItems (ExportContext &context) : ctx (context) {} + + void visit (HIR::Module &module) override {} + void visit (HIR::ExternCrate &crate) override {} + void visit (HIR::UseDeclaration &use_decl) override {} + void visit (HIR::TypeAlias &type_alias) override {} + void visit (HIR::StructStruct &struct_item) override {} + void visit (HIR::TupleStruct &tuple_struct) override {} + void visit (HIR::Enum &enum_item) override {} + void visit (HIR::Union &union_item) override {} + void visit (HIR::ConstantItem &const_item) override {} + void visit (HIR::StaticItem &static_item) override {} + void visit (HIR::ImplBlock &impl) override {} + void visit (HIR::ExternBlock &block) override {} + + void visit (HIR::Trait &trait) override { ctx.emit_trait (trait); } + + void visit (HIR::Function &function) override + { + ctx.emit_function (function); + } + +private: + ExportContext &ctx; +}; + +PublicInterface::PublicInterface (HIR::Crate &crate) + : crate (crate), mappings (*Analysis::Mappings::get ()), context () +{} + +void +PublicInterface::Export (HIR::Crate &crate) +{ + PublicInterface interface (crate); + interface.gather_export_data (); + interface.write_to_object_file (); +} + +void +PublicInterface::ExportTo (HIR::Crate &crate, const std::string &output_path) +{ + PublicInterface interface (crate); + interface.gather_export_data (); + interface.write_to_path (output_path); +} + +void +PublicInterface::gather_export_data () +{ + ExportVisItems visitor (context); + for (auto &item : crate.items) + { + bool is_vis_item = item->get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM; + if (!is_vis_item) + continue; + + HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (*item.get ()); + if (is_crate_public (vis_item)) + vis_item.accept_vis (visitor); + } +} + +void +PublicInterface::write_to_object_file () const +{ + // done + const auto &buf = context.get_interface_buffer (); + std::string size_buffer = std::to_string (buf.size ()); + + // md5 this + struct md5_ctx chksm; + unsigned char checksum[16]; + + md5_init_ctx (&chksm); + md5_process_bytes (buf.c_str (), buf.size (), &chksm); + md5_finish_ctx (&chksm, checksum); + + // MAGIC MD5 DLIM DLIM buffer-size DELIM contents + const std::string current_crate_name = mappings.get_current_crate_name (); + + // extern void + rust_write_export_data (kMagicHeader, sizeof (kMagicHeader)); + rust_write_export_data ((const char *) checksum, sizeof (checksum)); + rust_write_export_data (kSzDelim, sizeof (kSzDelim)); + rust_write_export_data (current_crate_name.c_str (), + current_crate_name.size ()); + rust_write_export_data (kSzDelim, sizeof (kSzDelim)); + rust_write_export_data (size_buffer.c_str (), size_buffer.size ()); + rust_write_export_data (kSzDelim, sizeof (kSzDelim)); + rust_write_export_data (buf.c_str (), buf.size ()); +} + +void +PublicInterface::write_to_path (const std::string &path) const +{ + // validate path contains correct extension + const std::string expected_file_name = expected_metadata_filename (); + const char *path_base_name = basename (path.c_str ()); + if (strcmp (path_base_name, expected_file_name.c_str ()) != 0) + { + rust_error_at (Location (), + "expected metadata-output path to have base file name of: " + "%<%s%> got %<%s%>", + expected_file_name.c_str (), path_base_name); + return; + } + + // done + const auto &buf = context.get_interface_buffer (); + std::string size_buffer = std::to_string (buf.size ()); + + // md5 this + struct md5_ctx chksm; + unsigned char checksum[16]; + + md5_init_ctx (&chksm); + md5_process_bytes (buf.c_str (), buf.size (), &chksm); + md5_finish_ctx (&chksm, checksum); + + // MAGIC MD5 DLIM DLIM buffer-size DELIM contents + const std::string current_crate_name = mappings.get_current_crate_name (); + + // write to path + FILE *nfd = fopen (path.c_str (), "wb"); + if (nfd == NULL) + { + rust_error_at (Location (), "failed to open file %<%s%> for writing: %s", + path.c_str (), xstrerror (errno)); + return; + } + + // write data + if (fwrite (kMagicHeader, sizeof (kMagicHeader), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (fwrite (checksum, sizeof (checksum), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (fwrite (current_crate_name.c_str (), current_crate_name.size (), 1, nfd) + < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (fwrite (size_buffer.c_str (), size_buffer.size (), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + if (!buf.empty ()) + if (fwrite (buf.c_str (), buf.size (), 1, nfd) < 1) + { + rust_error_at (Location (), "failed to write to file %<%s%>: %s", + path.c_str (), xstrerror (errno)); + fclose (nfd); + return; + } + + // done + fclose (nfd); +} + +bool +PublicInterface::is_crate_public (const HIR::VisItem &item) +{ + const HIR::Visibility &visibility = item.get_visibility (); + + bool is_public + = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC; + bool has_path = !visibility.get_path ().is_error (); + + // FIXME this might be pub(crate) + // Arthur magic required here + + return is_public && !has_path; +} + +std::string +PublicInterface::expected_metadata_filename () +{ + auto mappings = Analysis::Mappings::get (); + + const std::string current_crate_name = mappings->get_current_crate_name (); + return current_crate_name + extension_path; +} + +} // namespace Metadata +} // namespace Rust |