thrift/compiler/generate/t_mstch_cpp2_generator.cc (2,265 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <algorithm> #include <array> #include <cassert> #include <memory> #include <queue> #include <set> #include <vector> #include <boost/algorithm/string/replace.hpp> #include <thrift/compiler/gen/cpp/type_resolver.h> #include <thrift/compiler/generate/t_mstch_generator.h> #include <thrift/compiler/generate/t_mstch_objects.h> #include <thrift/compiler/lib/cpp2/util.h> #include <thrift/compiler/util.h> #include <thrift/compiler/validator/validator.h> namespace apache { namespace thrift { namespace compiler { namespace { std::string const& get_cpp_template(const t_type* type) { return type->get_annotation({"cpp.template", "cpp2.template"}); } bool is_annotation_blacklisted_in_fatal(const std::string& key) { const static std::set<std::string> black_list{ "cpp.methods", "cpp.name", "cpp.ref", "cpp.ref_type", "cpp.template", "cpp.type", "cpp2.methods", "cpp2.ref", "cpp2.ref_type", "cpp2.template", "cpp2.type", "cpp.internal.deprecated._data.method", }; return black_list.find(key) != black_list.end(); } bool same_types(const t_type* a, const t_type* b) { if (!a || !b) { return false; } if (get_cpp_template(a) != get_cpp_template(b) || cpp2::get_type(a) != cpp2::get_type(b)) { return false; } const auto* resolved_a = a->get_true_type(); const auto* resolved_b = b->get_true_type(); if (resolved_a->get_type_value() != resolved_b->get_type_value()) { return false; } switch (resolved_a->get_type_value()) { case t_type::type::t_list: { const auto* list_a = static_cast<const t_list*>(resolved_a); const auto* list_b = static_cast<const t_list*>(resolved_b); return same_types(list_a->get_elem_type(), list_b->get_elem_type()); } case t_type::type::t_set: { const auto* set_a = static_cast<const t_set*>(resolved_a); const auto* set_b = static_cast<const t_set*>(resolved_b); return same_types(set_a->get_elem_type(), set_b->get_elem_type()); } case t_type::type::t_map: { const auto* map_a = static_cast<const t_map*>(resolved_a); const auto* map_b = static_cast<const t_map*>(resolved_b); return same_types(map_a->get_key_type(), map_b->get_key_type()) && same_types(map_a->get_val_type(), map_b->get_val_type()); } default:; } return true; } std::vector<t_annotation> get_fatal_annotations( std::map<std::string, annotation_value> annotations) { std::vector<t_annotation> fatal_annotations; for (const auto& iter : annotations) { if (is_annotation_blacklisted_in_fatal(iter.first)) { continue; } fatal_annotations.push_back({iter.first, iter.second}); } return fatal_annotations; } std::string get_fatal_string_short_id(const std::string& key) { return boost::algorithm::replace_all_copy(key, ".", "_"); } std::string get_fatal_namespace_name_short_id( const std::string& lang, const std::string& ns) { std::string replacement = lang == "cpp" || lang == "cpp2" ? "__" : "_"; std::string result = boost::algorithm::replace_all_copy(ns, ".", replacement); if (lang == "php_path") { return boost::algorithm::replace_all_copy(ns, "/", "_"); } return result; } std::string get_fatal_namespace( const std::string& lang, const std::string& ns) { if (lang == "cpp" || lang == "cpp2") { return boost::algorithm::replace_all_copy(ns, ".", "::"); } else if (lang == "php") { return boost::algorithm::replace_all_copy(ns, ".", "_"); } return ns; } std::string render_fatal_string(const std::string& normal_string) { const static std::map<char, std::string> substition{ {'\0', "\\0"}, {'\n', "\\n"}, {'\r', "\\r"}, {'\t', "\\t"}, {'\'', "\\\'"}, {'\\', "\\\\"}, }; std::ostringstream res; res << "::fatal::sequence<char"; for (const char& c : normal_string) { res << ", '"; auto found = substition.find(c); if (found != substition.end()) { res << found->second; } else { res << c; } res << "'"; } res << ">"; return res.str(); } std::string get_out_dir_base( const std::map<std::string, std::string>& options) { return options.find("py3cpp") != options.end() ? "gen-py3cpp" : "gen-cpp2"; } std::string mangle_field_name(const std::string& name) { return "__fbthrift_field_" + name; } } // namespace class cpp2_generator_context { public: static cpp2_generator_context create() { return cpp2_generator_context(); } cpp2_generator_context(cpp2_generator_context&&) = default; cpp2_generator_context& operator=(cpp2_generator_context&&) = default; bool is_orderable(t_type const& type) { std::unordered_set<t_type const*> seen; auto& memo = is_orderable_memo_; return cpp2::is_orderable(seen, memo, type); } gen::cpp::type_resolver& resolver() { return resolver_; } private: cpp2_generator_context() = default; std::unordered_map<t_type const*, bool> is_orderable_memo_; gen::cpp::type_resolver resolver_; }; class t_mstch_cpp2_generator : public t_mstch_generator { public: t_mstch_cpp2_generator( t_program* program, t_generation_context context, const std::map<std::string, std::string>& parsed_options, const std::string& /*option_string*/); void generate_program() override; void fill_validator_list(validator_list&) const override; static std::string get_cpp2_namespace(t_program const* program); static std::string get_cpp2_unprefixed_namespace(t_program const* program); static mstch::array get_namespace_array(t_program const* program); static mstch::node cpp_includes(t_program const* program); static mstch::node include_prefix( t_program const* program, std::map<std::string, std::string>& options); static std::string get_service_qualified_name(t_service const* service); private: void set_mstch_generators(); void generate_sinit(t_program const* program); void generate_reflection(t_program const* program); void generate_visitation(t_program const* program); void generate_constants(t_program const* program); void generate_metadata(t_program const* program); void generate_structs(t_program const* program); void generate_service(t_service const* service); std::shared_ptr<cpp2_generator_context> context_; std::unordered_map<std::string, int32_t> client_name_to_split_count_; }; class mstch_cpp2_enum : public mstch_enum { public: mstch_cpp2_enum( t_enum const* enm, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos) : mstch_enum(enm, std::move(generators), std::move(cache), pos) { register_methods( this, { {"enum:empty?", &mstch_cpp2_enum::is_empty}, {"enum:size", &mstch_cpp2_enum::size}, {"enum:min", &mstch_cpp2_enum::min}, {"enum:max", &mstch_cpp2_enum::max}, {"enum:cpp_is_unscoped", &mstch_cpp2_enum::cpp_is_unscoped}, {"enum:cpp_name", &mstch_cpp2_enum::cpp_name}, {"enum:cpp_enum_type", &mstch_cpp2_enum::cpp_enum_type}, {"enum:cpp_declare_bitwise_ops", &mstch_cpp2_enum::cpp_declare_bitwise_ops}, {"enum:has_zero", &mstch_cpp2_enum::has_zero}, {"enum:fatal_annotations?", &mstch_cpp2_enum::has_fatal_annotations}, {"enum:fatal_annotations", &mstch_cpp2_enum::fatal_annotations}, {"enum:legacy_type_id", &mstch_cpp2_enum::get_legacy_type_id}, }); } mstch::node is_empty() { return enm_->get_enum_values().empty(); } mstch::node size() { return std::to_string(enm_->get_enum_values().size()); } mstch::node min() { if (!enm_->get_enum_values().empty()) { auto e_min = std::min_element( enm_->get_enum_values().begin(), enm_->get_enum_values().end(), [](t_enum_value* a, t_enum_value* b) { return a->get_value() < b->get_value(); }); return cpp2::get_name(*e_min); } return mstch::node(); } mstch::node max() { if (!enm_->get_enum_values().empty()) { auto e_max = std::max_element( enm_->get_enum_values().begin(), enm_->get_enum_values().end(), [](t_enum_value* a, t_enum_value* b) { return a->get_value() < b->get_value(); }); return cpp2::get_name(*e_max); } return mstch::node(); } std::string const& cpp_is_unscoped_() { return enm_->get_annotation( {"cpp2.deprecated_enum_unscoped", "cpp.deprecated_enum_unscoped"}); } mstch::node cpp_is_unscoped() { return cpp_is_unscoped_(); } mstch::node cpp_name() { return cpp2::get_name(enm_); } mstch::node cpp_enum_type() { static std::string kInt = "int"; return enm_->get_annotation( {"cpp.enum_type", "cpp2.enum_type"}, cpp_is_unscoped_().empty() ? nullptr : &kInt); } mstch::node cpp_declare_bitwise_ops() { return enm_->get_annotation( {"cpp.declare_bitwise_ops", "cpp2.declare_bitwise_ops"}); } mstch::node has_zero() { auto* enm_value = enm_->find_value(0); if (enm_value != nullptr) { return generators_->enum_value_generator_->generate( enm_value, generators_, cache_, pos_); } return mstch::node(); } mstch::node has_fatal_annotations() { return get_fatal_annotations(enm_->annotations()).size() > 0; } mstch::node fatal_annotations() { return generate_annotations(get_fatal_annotations(enm_->annotations())); } mstch::node get_legacy_type_id() { return std::to_string(enm_->get_type_id()); } }; class mstch_cpp2_enum_value : public mstch_enum_value { public: mstch_cpp2_enum_value( t_enum_value const* enm_value, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos) : mstch_enum_value( enm_value, std::move(generators), std::move(cache), pos) { register_methods( this, { {"enum_value:name_hash", &mstch_cpp2_enum_value::name_hash}, {"enum_value:cpp_name", &mstch_cpp2_enum_value::cpp_name}, {"enum_value:fatal_annotations?", &mstch_cpp2_enum_value::has_fatal_annotations}, {"enum_value:fatal_annotations", &mstch_cpp2_enum_value::fatal_annotations}, }); } mstch::node name_hash() { return "__fbthrift_hash_" + cpp2::sha256_hex(enm_value_->get_name()); } mstch::node cpp_name() { return cpp2::get_name(enm_value_); } mstch::node has_fatal_annotations() { return get_fatal_annotations(enm_value_->annotations()).size() > 0; } mstch::node fatal_annotations() { return generate_annotations( get_fatal_annotations(enm_value_->annotations())); } }; class mstch_cpp2_const_value : public mstch_const_value { public: mstch_cpp2_const_value( t_const_value const* const_value, t_const const* current_const, t_type const* expected_type, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t index) : mstch_const_value( const_value, current_const, expected_type, std::move(generators), std::move(cache), pos, index) {} private: bool same_type_as_expected() const override { return const_value_->get_owner() && same_types(expected_type_, const_value_->get_owner()->get_type()); } }; class mstch_cpp2_type : public mstch_type { public: mstch_cpp2_type( t_type const* type, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos, std::shared_ptr<cpp2_generator_context> context) : mstch_type(type, std::move(generators), std::move(cache), pos), context_(std::move(context)) { register_methods( this, { {"type:resolves_to_base?", &mstch_cpp2_type::resolves_to_base}, {"type:resolves_to_integral?", &mstch_cpp2_type::resolves_to_integral}, {"type:resolves_to_base_or_enum?", &mstch_cpp2_type::resolves_to_base_or_enum}, {"type:resolves_to_container?", &mstch_cpp2_type::resolves_to_container}, {"type:resolves_to_container_or_struct?", &mstch_cpp2_type::resolves_to_container_or_struct}, {"type:resolves_to_container_or_enum?", &mstch_cpp2_type::resolves_to_container_or_enum}, {"type:resolves_to_complex_return?", &mstch_cpp2_type::resolves_to_complex_return}, {"type:resolves_to_fixed_size?", &mstch_cpp2_type::resolves_to_fixed_size}, {"type:resolves_to_enum?", &mstch_cpp2_type::resolves_to_enum}, {"type:transitively_refers_to_struct?", &mstch_cpp2_type::transitively_refers_to_struct}, {"type:cpp_name", &mstch_cpp2_type::cpp_name}, {"type:cpp_fullname", &mstch_cpp2_type::cpp_fullname}, {"type:cpp_type", &mstch_cpp2_type::cpp_type}, {"type:cpp_standard_type", &mstch_cpp2_type::cpp_standard_type}, {"type:cpp_adapter", &mstch_cpp2_type::cpp_adapter}, {"type:raw_binary?", &mstch_cpp2_type::raw_binary}, {"type:raw_string_or_binary?", &mstch_cpp2_type::raw_string_or_binary}, {"type:string_or_binary?", &mstch_cpp2_type::is_string_or_binary}, {"type:resolved_cpp_type", &mstch_cpp2_type::resolved_cpp_type}, {"type:cpp_template", &mstch_cpp2_type::cpp_template}, {"type:cpp_indirection?", &mstch_cpp2_type::cpp_indirection}, {"type:non_empty_struct?", &mstch_cpp2_type::is_non_empty_struct}, {"type:namespace_cpp2", &mstch_cpp2_type::namespace_cpp2}, {"type:cpp_declare_hash", &mstch_cpp2_type::cpp_declare_hash}, {"type:cpp_declare_equal_to", &mstch_cpp2_type::cpp_declare_equal_to}, {"type:type_class", &mstch_cpp2_type::type_class}, {"type:type_tag", &mstch_cpp2_type::type_tag}, {"type:type_class_with_indirection", &mstch_cpp2_type::type_class_with_indirection}, {"type:program_name", &mstch_cpp2_type::program_name}, {"type:cpp_use_allocator?", &mstch_cpp2_type::cpp_use_allocator}, }); register_has_option( "type:sync_methods_return_try?", "sync_methods_return_try"); } std::string get_type_namespace(t_program const* program) override { return cpp2::get_gen_namespace(*program); } mstch::node resolves_to_base() { return resolved_type_->is_base_type(); } mstch::node resolves_to_integral() { return resolved_type_->is_byte() || resolved_type_->is_any_int(); } mstch::node resolves_to_base_or_enum() { return resolved_type_->is_base_type() || resolved_type_->is_enum(); } mstch::node resolves_to_container() { return resolved_type_->is_container(); } mstch::node resolves_to_container_or_struct() { return resolved_type_->is_container() || resolved_type_->is_struct() || resolved_type_->is_xception(); } mstch::node resolves_to_container_or_enum() { return resolved_type_->is_container() || resolved_type_->is_enum(); } mstch::node resolves_to_complex_return() { return is_complex_return(resolved_type_) && !resolved_type_->is_service(); } static bool is_complex_return(const t_type* type) { return type->is_container() || type->is_string_or_binary() || type->is_struct() || type->is_xception(); } mstch::node resolves_to_fixed_size() { return resolved_type_->is_bool() || resolved_type_->is_byte() || resolved_type_->is_any_int() || resolved_type_->is_enum() || resolved_type_->is_floating_point(); } mstch::node resolves_to_enum() { return resolved_type_->is_enum(); } mstch::node transitively_refers_to_struct() { // fast path is unnecessary but may avoid allocations if (resolved_type_->is_struct()) { return true; } if (!resolved_type_->is_container()) { return false; } // type is a container: traverse (breadthwise, but could be depthwise) std::queue<t_type const*> queue; queue.push(resolved_type_); while (!queue.empty()) { auto next = queue.front(); queue.pop(); if (next->is_struct()) { return true; } if (!next->is_container()) { continue; } if (false) { } else if (next->is_list()) { queue.push(static_cast<t_list const*>(next)->get_elem_type()); } else if (next->is_set()) { queue.push(static_cast<t_set const*>(next)->get_elem_type()); } else if (next->is_map()) { queue.push(static_cast<t_map const*>(next)->get_key_type()); queue.push(static_cast<t_map const*>(next)->get_val_type()); } else { assert(false); } } return false; } mstch::node cpp_name() { return cpp2::get_name(type_); } mstch::node cpp_fullname() { return context_->resolver().get_namespaced_name( *type_->get_program(), *type_); } mstch::node cpp_type() { return context_->resolver().get_type_name(*type_); } mstch::node cpp_standard_type() { return context_->resolver().get_standard_type_name(*type_); } mstch::node cpp_adapter() { if (const auto* adapter = gen::cpp::type_resolver::find_first_adapter(*type_)) { return *adapter; } return {}; } mstch::node raw_binary() { return resolved_type_->is_binary() && !is_adapted(); } mstch::node raw_string_or_binary() { return resolved_type_->is_string_or_binary() && !is_adapted(); } mstch::node resolved_cpp_type() { return cpp2::get_type(resolved_type_); } mstch::node is_string_or_binary() { return resolved_type_->is_string_or_binary(); } mstch::node cpp_template() { return get_cpp_template(type_); } mstch::node cpp_indirection() { return resolved_type_->has_annotation("cpp.indirection"); } mstch::node cpp_declare_hash() { return resolved_type_->has_annotation( {"cpp.declare_hash", "cpp2.declare_hash"}); } mstch::node cpp_declare_equal_to() { return resolved_type_->has_annotation( {"cpp.declare_equal_to", "cpp2.declare_equal_to"}); } mstch::node cpp_use_allocator() { return resolved_type_->has_annotation("cpp.use_allocator") || type_->has_annotation("cpp.use_allocator"); } mstch::node is_non_empty_struct() { auto as_struct = dynamic_cast<t_struct const*>(resolved_type_); return as_struct && as_struct->has_fields(); } mstch::node namespace_cpp2() { return t_mstch_cpp2_generator::get_namespace_array(type_->program()); } mstch::node type_class() { return cpp2::get_gen_type_class(*resolved_type_); } mstch::node type_tag() { return context_->resolver().get_type_tag(*type_); } mstch::node type_class_with_indirection() { return cpp2::get_gen_type_class_with_indirection(*resolved_type_); } mstch::node program_name() { std::string name; if (auto prog = type_->program()) { name = prog->name(); } return name; } private: std::shared_ptr<cpp2_generator_context> context_; bool is_adapted() const { return gen::cpp::type_resolver::find_first_adapter(*type_) != nullptr; } }; class mstch_cpp2_field : public mstch_field { public: mstch_cpp2_field( t_field const* field, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos, int32_t index, field_generator_context const* field_context, std::shared_ptr<cpp2_generator_context> context) : mstch_field( field, std::move(generators), std::move(cache), pos, index, field_context), context_(std::move(context)) { register_methods( this, { {"field:name_hash", &mstch_cpp2_field::name_hash}, {"field:index_plus_one", &mstch_cpp2_field::index_plus_one}, {"field:has_isset?", &mstch_cpp2_field::has_isset}, {"field:isset_index", &mstch_cpp2_field::isset_index}, {"field:cpp_name", &mstch_cpp2_field::cpp_name}, {"field:cpp_type", &mstch_cpp2_field::cpp_type}, {"field:cpp_storage_name", &mstch_cpp2_field::cpp_storage_name}, {"field:cpp_storage_type", &mstch_cpp2_field::cpp_storage_type}, {"field:cpp_deprecated_accessor_type", &mstch_cpp2_field::cpp_deprecated_accessor_type}, {"field:has_deprecated_accessors?", &mstch_cpp2_field::has_deprecated_accessors}, {"field:serialization_next_field_key", &mstch_cpp2_field::serialization_next_field_key}, {"field:serialization_prev_field_key", &mstch_cpp2_field::serialization_prev_field_key}, {"field:serialization_next_field_type", &mstch_cpp2_field::serialization_next_field_type}, {"field:non_opt_cpp_ref?", &mstch_cpp2_field::non_opt_cpp_ref}, {"field:opt_cpp_ref?", &mstch_cpp2_field::opt_cpp_ref}, {"field:cpp_ref?", &mstch_cpp2_field::cpp_ref}, {"field:cpp_ref_unique?", &mstch_cpp2_field::cpp_ref_unique}, {"field:cpp_ref_shared?", &mstch_cpp2_field::cpp_ref_shared}, {"field:cpp_ref_shared_const?", &mstch_cpp2_field::cpp_ref_shared_const}, {"field:cpp_adapter", &mstch_cpp2_field::cpp_adapter}, {"field:zero_copy_arg", &mstch_cpp2_field::zero_copy_arg}, {"field:cpp_noncopyable?", &mstch_cpp2_field::cpp_noncopyable}, {"field:enum_has_value", &mstch_cpp2_field::enum_has_value}, {"field:deprecated_terse_writes?", &mstch_cpp2_field::deprecated_terse_writes}, {"field:terse_write?", &mstch_cpp2_field::terse_write}, {"field:fatal_annotations?", &mstch_cpp2_field::has_fatal_annotations}, {"field:fatal_annotations", &mstch_cpp2_field::fatal_annotations}, {"field:fatal_required_qualifier", &mstch_cpp2_field::fatal_required_qualifier}, {"field:visibility", &mstch_cpp2_field::visibility}, {"field:metadata_name", &mstch_cpp2_field::metadata_name}, {"field:lazy?", &mstch_cpp2_field::lazy}, {"field:lazy_ref?", &mstch_cpp2_field::lazy_ref}, {"field:boxed_ref?", &mstch_cpp2_field::boxed_ref}, {"field:use_field_ref?", &mstch_cpp2_field::use_field_ref}, {"field:field_ref_type", &mstch_cpp2_field::field_ref_type}, {"field:transitively_refers_to_unique?", &mstch_cpp2_field::transitively_refers_to_unique}, {"field:eligible_for_storage_name_mangling?", &mstch_cpp2_field::eligible_for_storage_name_mangling}, }); register_has_option("field:deprecated_clear?", "deprecated_clear"); } mstch::node name_hash() { return "__fbthrift_hash_" + cpp2::sha256_hex(field_->get_name()); } mstch::node index_plus_one() { return std::to_string(index_ + 1); } mstch::node isset_index() { assert(field_context_); return field_context_->isset_index; } mstch::node cpp_name() { return cpp2::get_name(field_); } mstch::node cpp_type() { assert(field_context_->strct); return context_->resolver().get_type_name(*field_, *field_context_->strct); } mstch::node cpp_storage_name() { if (!is_eligible_for_storage_name_mangling()) { return cpp2::get_name(field_); } return mangle_field_name(cpp2::get_name(field_)); } mstch::node cpp_storage_type() { assert(field_context_->strct); return context_->resolver().get_storage_type_name( *field_, *field_context_->strct); } mstch::node eligible_for_storage_name_mangling() { return is_eligible_for_storage_name_mangling(); } mstch::node cpp_deprecated_accessor_type() { // The type to use for pre-field_ref backwards compatiblity functions. // These leaked the internal storage type directly. // // TODO(afuller): Remove this once all non-field_ref based accessors have // been removed. assert(field_context_->strct); return context_->resolver().get_storage_type_name( *field_, *field_context_->strct); } mstch::node has_deprecated_accessors() { return !cpp2::is_explicit_ref(field_) && !cpp2::is_lazy(field_) && !gen::cpp::type_resolver::find_first_adapter(*field_) && !has_option("no_getters_setters"); } mstch::node cpp_ref() { return cpp2::is_explicit_ref(field_); } mstch::node opt_cpp_ref() { return cpp2::is_explicit_ref(field_) && field_->get_req() == t_field::e_req::optional; } mstch::node non_opt_cpp_ref() { return cpp2::is_explicit_ref(field_) && field_->get_req() != t_field::e_req::optional; } mstch::node lazy() { return cpp2::is_lazy(field_); } mstch::node lazy_ref() { return cpp2::is_lazy_ref(field_); } mstch::node boxed_ref() { return gen::cpp::find_ref_type(*field_) == gen::cpp::reference_type::boxed; } mstch::node use_field_ref() { return !cpp2::is_explicit_ref(field_) || gen::cpp::find_ref_type(*field_) == gen::cpp::reference_type::boxed; } mstch::node field_ref_type() { static const std::string ns = "::apache::thrift::"; if (gen::cpp::find_ref_type(*field_) == gen::cpp::reference_type::boxed) { return ns + "optional_boxed_field_ref"; } switch (field_->get_req()) { case t_field::e_req::required: return ns + "required_field_ref"; case t_field::e_req::optional: return ns + "optional_field_ref"; case t_field::e_req::opt_in_req_out: return ns + "field_ref"; case t_field::e_req::terse: return ns + "terse_field_ref"; default: throw std::runtime_error("unknown qualifier"); } } mstch::node transitively_refers_to_unique() { return cpp2::field_transitively_refers_to_unique(field_); } mstch::node cpp_ref_unique() { return cpp2::is_unique_ref(field_); } mstch::node cpp_ref_shared() { return gen::cpp::find_ref_type(*field_) == gen::cpp::reference_type::shared_mutable; } mstch::node cpp_ref_shared_const() { return gen::cpp::find_ref_type(*field_) == gen::cpp::reference_type::shared_const; } mstch::node cpp_adapter() { if (const std::string* adapter = gen::cpp::type_resolver::find_first_adapter(*field_)) { return *adapter; } return {}; } mstch::node cpp_noncopyable() { return field_->get_type()->has_annotation( {"cpp.noncopyable", "cpp2.noncopyable"}); } mstch::node enum_has_value() { if (auto enm = dynamic_cast<t_enum const*>(field_->get_type())) { auto const* const_value = field_->get_value(); using cv = t_const_value::t_const_value_type; if (const_value->get_type() == cv::CV_INTEGER) { auto* enm_value = enm->find_value(const_value->get_integer()); if (enm_value != nullptr) { return generators_->enum_value_generator_->generate( enm_value, generators_, cache_, pos_); } } } return mstch::node(); } mstch::node serialization_prev_field_key() { assert(field_context_ && field_context_->serialization_prev); return field_context_->serialization_prev->get_key(); } mstch::node serialization_next_field_key() { assert(field_context_ && field_context_->serialization_next); return field_context_->serialization_next->get_key(); } mstch::node serialization_next_field_type() { assert(field_context_ && field_context_->serialization_next); return field_context_->serialization_next ? generators_->type_generator_->generate( field_context_->serialization_next->get_type(), generators_, cache_, pos_) : mstch::node(""); } mstch::node deprecated_terse_writes() { return has_option("terse_writes") && cpp2::deprecated_terse_writes(field_); } mstch::node terse_write() { return field_->get_req() == t_field::e_req::terse; } mstch::node zero_copy_arg() { switch (field_->get_type()->get_type_value()) { case t_type::type::t_binary: case t_type::type::t_struct: return std::string("true"); default: return std::string("false"); } } mstch::node has_fatal_annotations() { return get_fatal_annotations(field_->annotations()).size() > 0; } mstch::node has_isset() { return cpp2::field_has_isset(field_); } mstch::node fatal_annotations() { return generate_annotations(get_fatal_annotations(field_->annotations())); } mstch::node fatal_required_qualifier() { switch (field_->get_req()) { case t_field::e_req::required: return std::string("required"); case t_field::e_req::optional: return std::string("optional"); case t_field::e_req::opt_in_req_out: return std::string("required_of_writer"); default: throw std::runtime_error("unknown required qualifier"); } } mstch::node visibility() { return std::string(is_private() ? "private" : "public"); } mstch::node metadata_name() { auto key = field_->get_key(); auto suffix = key >= 0 ? std::to_string(key) : "_" + std::to_string(-key); return field_->get_name() + "_" + suffix; } private: bool is_private() const { auto req = field_->get_req(); bool isPrivate = true; if (cpp2::is_lazy(field_)) { // Lazy field has to be private. } else if (cpp2::is_ref(field_)) { if (gen::cpp::find_ref_type(*field_) != gen::cpp::reference_type::boxed) { isPrivate = has_option("deprecated_private_fields_for_cpp_ref"); } } else if (req == t_field::e_req::required) { isPrivate = !has_option("deprecated_public_required_fields"); } return isPrivate; } bool is_eligible_for_storage_name_mangling() const { const auto* strct = field_context_->strct; if (strct->is_union() || strct->is_exception()) { return false; } if (strct->has_annotation({"cpp.methods", "cpp2.methods"})) { return false; } return is_private(); } std::shared_ptr<cpp2_generator_context> context_; }; class mstch_cpp2_struct : public mstch_struct { public: mstch_cpp2_struct( t_struct const* strct, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos, std::shared_ptr<cpp2_generator_context> context) : mstch_struct(strct, std::move(generators), std::move(cache), pos), context_(std::move(context)) { register_methods( this, { {"struct:fields_size", &mstch_cpp2_struct::fields_size}, {"struct:explicitly_constructed_fields", &mstch_cpp2_struct::explicitly_constructed_fields}, {"struct:fields_in_key_order", &mstch_cpp2_struct::fields_in_key_order}, {"struct:fields_in_layout_order", &mstch_cpp2_struct::fields_in_layout_order}, {"struct:is_struct_orderable?", &mstch_cpp2_struct::is_struct_orderable}, {"struct:nondefault_copy_ctor_and_assignment?", &mstch_cpp2_struct::nondefault_copy_ctor_and_assignment}, {"struct:cpp_name", &mstch_cpp2_struct::cpp_name}, {"struct:cpp_fullname", &mstch_cpp2_struct::cpp_fullname}, {"struct:cpp_methods", &mstch_cpp2_struct::cpp_methods}, {"struct:cpp_declare_hash", &mstch_cpp2_struct::cpp_declare_hash}, {"struct:cpp_declare_equal_to", &mstch_cpp2_struct::cpp_declare_equal_to}, {"struct:cpp_noncopyable", &mstch_cpp2_struct::cpp_noncopyable}, {"struct:cpp_noncomparable", &mstch_cpp2_struct::cpp_noncomparable}, {"struct:is_eligible_for_constexpr?", &mstch_cpp2_struct::is_eligible_for_constexpr}, {"struct:virtual", &mstch_cpp2_struct::cpp_virtual}, {"struct:message", &mstch_cpp2_struct::message}, {"struct:isset_fields?", &mstch_cpp2_struct::has_isset_fields}, {"struct:isset_fields", &mstch_cpp2_struct::isset_fields}, {"struct:isset_fields_size", &mstch_cpp2_struct::isset_fields_size}, {"struct:isset_bitset_option", &mstch_cpp2_struct::isset_bitset_option}, {"struct:lazy_fields?", &mstch_cpp2_struct::has_lazy_fields}, {"struct:indexing?", &mstch_cpp2_struct::indexing}, {"struct:write_lazy_field_checksum", &mstch_cpp2_struct::write_lazy_field_checksum}, {"struct:is_large?", &mstch_cpp2_struct::is_large}, {"struct:fatal_annotations?", &mstch_cpp2_struct::has_fatal_annotations}, {"struct:fatal_annotations", &mstch_cpp2_struct::fatal_annotations}, {"struct:legacy_type_id", &mstch_cpp2_struct::get_legacy_type_id}, {"struct:metadata_name", &mstch_cpp2_struct::metadata_name}, {"struct:mixin_fields", &mstch_cpp2_struct::mixin_fields}, {"struct:num_union_members", &mstch_cpp2_struct::get_num_union_members}, {"struct:cpp_allocator", &mstch_cpp2_struct::cpp_allocator}, {"struct:cpp_allocator_via", &mstch_cpp2_struct::cpp_allocator_via}, {"struct:cpp_data_method?", &mstch_cpp2_struct::cpp_data_method}, {"struct:cpp_frozen2_exclude?", &mstch_cpp2_struct::cpp_frozen2_exclude}, {"struct:has_non_optional_and_non_terse_field?", &mstch_cpp2_struct::has_non_optional_and_non_terse_field}, }); register_has_option( "struct:deprecated_tag_incompatible?", "deprecated_tag_incompatible"); } mstch::node fields_size() { return std::to_string(strct_->fields().size()); } mstch::node explicitly_constructed_fields() { // Filter fields according to the following criteria: // Get all enums // Get all base_types but empty strings // Get all non-empty structs and containers // Get all non-optional references with basetypes, enums, // non-empty structs, and containers std::vector<t_field const*> filtered_fields; for (auto const* field : get_members_in_layout_order()) { const t_type* type = field->get_type()->get_true_type(); // Filter out all optional references. if (cpp2::is_explicit_ref(field) && field->get_req() == t_field::e_req::optional) { continue; } if (type->is_enum() || (type->is_base_type() && !type->is_string_or_binary()) || (type->is_string_or_binary() && field->get_value() != nullptr) || (type->is_container() && field->get_value() != nullptr && !field->get_value()->is_empty()) || (type->is_struct() && (strct_ != dynamic_cast<t_struct const*>(type)) && ((field->get_value() && !field->get_value()->is_empty()) || (cpp2::is_explicit_ref(field) && field->get_req() != t_field::e_req::optional))) || (type->is_container() && cpp2::is_explicit_ref(field) && field->get_req() != t_field::e_req::optional) || (type->is_base_type() && cpp2::is_explicit_ref(field) && field->get_req() != t_field::e_req::optional)) { filtered_fields.push_back(field); } } return generate_fields(filtered_fields); } mstch::node mixin_fields() { mstch::array fields; for (auto i : cpp2::get_mixins_and_members(*strct_)) { fields.push_back(mstch::map{ {"mixin:name", i.mixin->get_name()}, {"mixin:field_name", i.member->get_name()}}); } return fields; } mstch::node is_struct_orderable() { return context_->is_orderable(*strct_) && !strct_->has_annotation("no_default_comparators"); } mstch::node nondefault_copy_ctor_and_assignment() { for (auto const& f : strct_->fields()) { if (cpp2::field_transitively_refers_to_unique(&f) || cpp2::is_lazy(&f) || gen::cpp::type_resolver::find_first_adapter(f)) { return true; } } return false; } mstch::node cpp_name() { return cpp2::get_name(strct_); } mstch::node cpp_fullname() { return context_->resolver().get_namespaced_name( *strct_->get_program(), *strct_); } mstch::node cpp_methods() { return strct_->get_annotation({"cpp.methods", "cpp2.methods"}); } mstch::node cpp_declare_hash() { return strct_->has_annotation({"cpp.declare_hash", "cpp2.declare_hash"}); } mstch::node cpp_declare_equal_to() { return strct_->has_annotation( {"cpp.declare_equal_to", "cpp2.declare_equal_to"}); } mstch::node cpp_noncopyable() { if (strct_->has_annotation({"cpp.noncopyable", "cpp2.noncopyable"})) { return true; } bool result = false; cpp2::for_each_transitive_field(strct_, [&result](const t_field* field) { if (!field->get_type()->has_annotation( {"cpp.noncopyable", "cpp2.noncopyable"})) { return true; } switch (gen::cpp::find_ref_type(*field)) { case gen::cpp::reference_type::shared_const: case gen::cpp::reference_type::shared_mutable: { return true; } case gen::cpp::reference_type::boxed: case gen::cpp::reference_type::none: case gen::cpp::reference_type::unique: case gen::cpp::reference_type::unrecognized: { break; } } result = true; return false; }); return result; } mstch::node cpp_noncomparable() { return strct_->has_annotation({"cpp.noncomparable", "cpp2.noncomparable"}); } mstch::node is_eligible_for_constexpr() { return is_eligible_for_constexpr_(strct_) || strct_->has_annotation({"cpp.methods", "cpp2.methods"}); } mstch::node cpp_virtual() { return strct_->has_annotation({"cpp.virtual", "cpp2.virtual"}); } mstch::node message() { return strct_->is_exception() ? strct_->get_annotation("message") : mstch::node(); } mstch::node cpp_allocator() { return strct_->get_annotation("cpp.allocator"); } mstch::node cpp_data_method() { return strct_->has_annotation("cpp.internal.deprecated._data.method"); } mstch::node cpp_frozen2_exclude() { return strct_->has_annotation("cpp.frozen2_exclude"); } mstch::node cpp_allocator_via() { if (const auto* name = strct_->find_annotation_or_null("cpp.allocator_via")) { for (const auto& field : strct_->fields()) { if (cpp2::get_name(&field) == *name) { return mangle_field_name(*name); } } throw std::runtime_error("No cpp.allocator_via field \"" + *name + "\""); } return std::string(); } mstch::node has_lazy_fields() { for (const auto& field : strct_->get_members()) { if (cpp2::is_lazy(field)) { return true; } } return false; } mstch::node indexing() { return has_lazy_fields(); } mstch::node write_lazy_field_checksum() { if (strct_->find_structured_annotation_or_null( "facebook.com/thrift/annotation/cpp/DisableLazyChecksum")) { return std::string("false"); } return std::string("true"); } mstch::node has_isset_fields() { for (const auto& field : strct_->fields()) { if (cpp2::field_has_isset(&field)) { return true; } } return false; } mstch::node isset_fields() { std::vector<t_field const*> fields; for (const auto& field : strct_->fields()) { if (cpp2::field_has_isset(&field)) { fields.push_back(&field); } } if (fields.empty()) { return mstch::node(); } return generate_fields(fields); } mstch::node isset_fields_size() { std::size_t size = 0; for (const auto& field : strct_->fields()) { if (cpp2::field_has_isset(&field)) { size++; } } return std::to_string(size); } mstch::node isset_bitset_option() { static const std::string kPrefix = "apache::thrift::detail::IssetBitsetOption::"; if (const auto* anno = cpp2::packed_isset(*strct_)) { for (const auto& kv : anno->value()->get_map()) { if (kv.first->get_string() == "atomic") { if (!kv.second->get_bool()) { return kPrefix + "Packed"; } } } return kPrefix + "PackedWithAtomic"; } return kPrefix + "Unpacked"; } mstch::node is_large() { // Outline constructors and destructors if the struct has // enough members and at least one has a non-trivial destructor // (involving at least a branch and a likely deallocation). // TODO(ott): Support unions. if (strct_->is_exception()) { return true; } constexpr size_t kLargeStructThreshold = 4; if (strct_->fields().size() <= kLargeStructThreshold) { return false; } for (auto const& field : strct_->fields()) { auto const* resolved_typedef = field.type()->get_true_type(); if (cpp2::is_ref(&field) || resolved_typedef->is_string_or_binary() || resolved_typedef->is_container()) { return true; } } return false; } mstch::node has_fatal_annotations() { return get_fatal_annotations(strct_->annotations()).size() > 0; } mstch::node fatal_annotations() { return generate_annotations(get_fatal_annotations(strct_->annotations())); } mstch::node get_legacy_type_id() { return std::to_string(strct_->get_type_id()); } mstch::node metadata_name() { return strct_->program()->name() + "_" + strct_->get_name(); } mstch::node get_num_union_members() { if (!strct_->is_union()) { throw std::runtime_error("not a union struct"); } return std::to_string(strct_->fields().size()); } mstch::node has_non_optional_and_non_terse_field() { const auto& fields = strct_->fields(); return std::any_of( fields.begin(), fields.end(), [enabled_terse_write = has_option("terse_writes")](auto& field) { return (!enabled_terse_write || !cpp2::deprecated_terse_writes(&field)) && field.get_req() != t_field::e_req::optional && field.get_req() != t_field::e_req::terse; }); } protected: // Computes the alignment of field on the target platform. // Returns 0 if cannot compute the alignment. static size_t compute_alignment( t_field const* field, std::unordered_map<t_field const*, size_t>& memo) { auto find = memo.emplace(field, 0); auto& ret = find.first->second; if (!find.second) { return ret; } if (cpp2::is_ref(field)) { return ret = 8; } t_type const* type = field->get_type(); if (cpp2::is_custom_type(*type)) { return ret = 0; } switch (type->get_type_value()) { case t_type::type::t_bool: case t_type::type::t_byte: return ret = 1; case t_type::type::t_i16: return ret = 2; case t_type::type::t_i32: case t_type::type::t_float: case t_type::type::t_enum: return ret = 4; case t_type::type::t_i64: case t_type::type::t_double: case t_type::type::t_string: case t_type::type::t_binary: case t_type::type::t_list: case t_type::type::t_set: case t_type::type::t_map: return ret = 8; case t_type::type::t_struct: { size_t align = 1; const size_t kMaxAlign = 8; t_struct const* strct = dynamic_cast<t_struct const*>(type->get_true_type()); assert(strct); for (auto const& field : strct->fields()) { size_t field_align = compute_alignment(&field, memo); if (field_align == 0) { // Unknown alignment, bail out. return ret = 0; } align = std::max(align, field_align); if (align == kMaxAlign) { // No need to continue because the struct already has the maximum // alignment. return ret = align; } } // The __isset member that is generated in the presence of non-required // fields doesn't affect the alignment, because, having only bool // fields, it has the alignments of 1. return ret = align; } default: return ret = 0; } } // Returns the struct members reordered to minimize padding if the // cpp.minimize_padding annotation is specified. const std::vector<const t_field*>& get_members_in_layout_order() { if (strct_->fields().size() == fields_in_layout_order_.size()) { // Already reordered. return fields_in_layout_order_; } if (!strct_->has_annotation("cpp.minimize_padding") && !strct_->find_structured_annotation_or_null( "facebook.com/thrift/annotation/cpp/MinimizePadding")) { return fields_in_layout_order_ = strct_->fields().copy(); } // Compute field alignments. struct FieldAlign { const t_field* field = nullptr; size_t align = 0; }; std::vector<FieldAlign> field_alignments; field_alignments.reserve(strct_->fields().size()); std::unordered_map<t_field const*, size_t> memo; for (const auto& field : strct_->fields()) { auto align = compute_alignment(&field, memo); if (align == 0) { // Unknown alignment, don't reorder anything. return fields_in_layout_order_ = strct_->fields().copy(); } field_alignments.push_back(FieldAlign{&field, align}); } // Sort by decreasing alignment using stable sort to avoid unnecessary // reordering. std::stable_sort( field_alignments.begin(), field_alignments.end(), [](auto const& lhs, auto const& rhs) { return lhs.align > rhs.align; }); // Construct the reordered field vector. fields_in_layout_order_.reserve(strct_->fields().size()); std::transform( field_alignments.begin(), field_alignments.end(), std::back_inserter(fields_in_layout_order_), [](FieldAlign const& fa) { return fa.field; }); return fields_in_layout_order_; } mstch::node fields_in_layout_order() { return generate_fields(get_members_in_layout_order()); } mstch::node fields_in_key_order() { return generate_fields(get_members_in_key_order()); } std::shared_ptr<cpp2_generator_context> context_; std::vector<const t_field*> fields_in_layout_order_; cpp2::is_eligible_for_constexpr is_eligible_for_constexpr_; }; class mstch_cpp2_function : public mstch_function { public: mstch_cpp2_function( t_function const* function, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos) : mstch_function(function, std::move(generators), std::move(cache), pos) { register_methods( this, { {"function:coroutine?", &mstch_cpp2_function::coroutine}, {"function:eb", &mstch_cpp2_function::event_based}, {"function:cpp_name", &mstch_cpp2_function::cpp_name}, {"function:stack_arguments?", &mstch_cpp2_function::stack_arguments}, {"function:created_interaction", &mstch_cpp2_function::created_interaction}, {"function:creates_interaction?", &mstch_cpp2_function::creates_interaction}, {"function:in_or_creates_interaction?", &mstch_cpp2_function::in_or_creates_interaction}, {"function:void?", &mstch_cpp2_function::is_void}, {"function:sync_returns_by_outparam?", &mstch_cpp2_function::sync_returns_by_outparam}, }); } mstch::node coroutine() { return function_->has_annotation("cpp.coroutine") || function_->returned_interaction().is_initialized() || function_->is_interaction_member(); } mstch::node event_based() { return function_->get_annotation("thread") == "eb"; } mstch::node cpp_name() { return cpp2::get_name(function_); } mstch::node stack_arguments() { return cpp2::is_stack_arguments(cache_->parsed_options_, *function_); } mstch::node creates_interaction() { return function_->returned_interaction().is_initialized(); } mstch::node created_interaction() { return cpp2::get_name(function_->returned_interaction()->get_type()); } mstch::node in_or_creates_interaction() { return function_->returned_interaction().is_initialized() || function_->is_interaction_member(); } mstch::node is_void() { return function_->return_type().deref().is_void() && !function_->returned_interaction(); } mstch::node sync_returns_by_outparam() { return mstch_cpp2_type::is_complex_return( function_->return_type().deref().get_true_type()) && !function_->returned_interaction(); } }; class mstch_cpp2_service : public mstch_service { public: mstch_cpp2_service( t_service const* service, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos, int32_t split_id = 0, int32_t split_count = 1) : mstch_service(service, std::move(generators), std::move(cache), pos) { register_methods( this, { {"service:program_name", &mstch_cpp2_service::program_name}, {"service:program_path", &mstch_cpp2_service::program_path}, {"service:include_prefix", &mstch_cpp2_service::include_prefix}, {"service:thrift_includes", &mstch_cpp2_service::thrift_includes}, {"service:namespace_cpp2", &mstch_cpp2_service::namespace_cpp2}, {"service:oneway_functions", &mstch_cpp2_service::oneway_functions}, {"service:oneways?", &mstch_cpp2_service::has_oneway}, {"service:cpp_includes", &mstch_cpp2_service::cpp_includes}, {"service:metadata_name", &mstch_cpp2_service::metadata_name}, {"service:cpp_name", &mstch_cpp2_service::cpp_name}, {"service:qualified_name", &mstch_cpp2_service::qualified_name}, {"service:parent_service_name", &mstch_cpp2_service::parent_service_name}, {"service:parent_service_cpp_name", &mstch_cpp2_service::parent_service_cpp_name}, {"service:parent_service_qualified_name", &mstch_cpp2_service::parent_service_qualified_name}, {"service:reduced_client?", &mstch_service::is_interaction}, }); const auto all_functions = mstch_service::get_functions(); for (size_t id = split_id; id < all_functions.size(); id += split_count) { functions_.push_back(all_functions[id]); } } std::string get_service_namespace(t_program const* program) override { return t_mstch_cpp2_generator::get_cpp2_namespace(program); } mstch::node program_name() { return service_->program()->name(); } mstch::node program_path() { return service_->program()->path(); } mstch::node cpp_includes() { return t_mstch_cpp2_generator::cpp_includes(service_->program()); } mstch::node include_prefix() { return t_mstch_cpp2_generator::include_prefix( service_->program(), cache_->parsed_options_); } mstch::node thrift_includes() { mstch::array a{}; for (auto const* program : service_->program()->get_included_programs()) { a.push_back(generators_->program_generator_->generate_cached( program, generators_, cache_)); } return a; } mstch::node namespace_cpp2() { return t_mstch_cpp2_generator::get_namespace_array(service_->program()); } mstch::node oneway_functions() { std::vector<t_function const*> oneway_functions; for (auto const* function : get_functions()) { if (function->qualifier() == t_function_qualifier::one_way) { oneway_functions.push_back(function); } } return generate_functions(oneway_functions); } mstch::node has_oneway() { for (auto const* function : get_functions()) { if (function->qualifier() == t_function_qualifier::one_way) { return true; } } return false; } mstch::node metadata_name() { return service_->program()->name() + "_" + service_->get_name(); } mstch::node cpp_name() { return service_->is_interaction() ? service_->name() : cpp2::get_name(service_); } mstch::node qualified_name() { return t_mstch_cpp2_generator::get_service_qualified_name(service_); } mstch::node parent_service_name() { return cache_->parsed_options_.at("parent_service_name"); } mstch::node parent_service_cpp_name() { return cache_->parsed_options_.at("parent_service_cpp_name"); } mstch::node parent_service_qualified_name() { return cache_->parsed_options_.at("parent_service_qualified_name"); } private: const std::vector<t_function*>& get_functions() const override { return functions_; } std::vector<t_function*> functions_; }; class mstch_cpp2_annotation : public mstch_annotation { public: mstch_cpp2_annotation( const std::string& key, annotation_value val, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t index) : mstch_annotation( key, val, std::move(generators), std::move(cache), pos, index) { register_methods( this, { {"annotation:safe_key", &mstch_cpp2_annotation::safe_key}, {"annotation:fatal_string", &mstch_cpp2_annotation::fatal_string}, }); } mstch::node safe_key() { return get_fatal_string_short_id(key_); } mstch::node fatal_string() { return render_fatal_string(val_.value); } }; class mstch_cpp2_const : public mstch_const { public: mstch_cpp2_const( t_const const* cnst, t_const const* current_const, t_type const* expected_type, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos, int32_t index, t_field const* field) : mstch_const( cnst, current_const, expected_type, std::move(generators), std::move(cache), pos, index, field) { register_methods( this, { {"constant:enum_value", &mstch_cpp2_const::enum_value}, {"constant:cpp_name", &mstch_cpp2_const::cpp_name}, }); } mstch::node enum_value() { if (cnst_->get_type()->is_enum()) { auto const* enm = static_cast<t_enum const*>(cnst_->get_type()); auto const* enm_val = enm->find_value(cnst_->get_value()->get_integer()); if (enm_val) { return enm_val->get_name(); } else { return std::to_string(cnst_->get_value()->get_integer()); } } return mstch::node(); } mstch::node cpp_name() { return cpp2::get_name(field_); } }; class mstch_cpp2_program : public mstch_program { public: mstch_cpp2_program( t_program const* program, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION const pos, boost::optional<int32_t> split_id = boost::none) : mstch_program(program, std::move(generators), std::move(cache), pos), split_id_(split_id) { register_methods( this, { {"program:cpp_includes", &mstch_cpp2_program::cpp_includes}, {"program:namespace_cpp2", &mstch_cpp2_program::namespace_cpp2}, {"program:include_prefix", &mstch_cpp2_program::include_prefix}, {"program:cpp_declare_hash?", &mstch_cpp2_program::cpp_declare_hash}, {"program:thrift_includes", &mstch_cpp2_program::thrift_includes}, {"program:frozen_packed?", &mstch_cpp2_program::frozen_packed}, {"program:fatal_languages", &mstch_cpp2_program::fatal_languages}, {"program:fatal_enums", &mstch_cpp2_program::fatal_enums}, {"program:fatal_unions", &mstch_cpp2_program::fatal_unions}, {"program:fatal_structs", &mstch_cpp2_program::fatal_structs}, {"program:fatal_constants", &mstch_cpp2_program::fatal_constants}, {"program:fatal_services", &mstch_cpp2_program::fatal_services}, {"program:fatal_identifiers", &mstch_cpp2_program::fatal_identifiers}, {"program:fatal_data_member", &mstch_cpp2_program::fatal_data_member}, }); register_has_option("program:tablebased?", "tablebased"); register_has_option("program:no_metadata?", "no_metadata"); register_has_option( "program:enforce_required?", "deprecated_enforce_required"); register_has_option( "program:deprecated_tag_incompatible?", "deprecated_tag_incompatible"); } std::string get_program_namespace(t_program const* program) override { return t_mstch_cpp2_generator::get_cpp2_namespace(program); } std::vector<const t_typedef*> alias_to_struct() { std::vector<const t_typedef*> result; for (const t_typedef* i : program_->typedefs()) { const t_type* alias = i->get_type(); if (alias->is_typedef() && alias->has_annotation("cpp.type")) { const t_type* ttype = i->get_type()->get_true_type(); if (ttype->is_struct() || ttype->is_xception()) { result.push_back(i); } } } return result; } template <typename Node> void collect_fatal_string_annotated( std::map<std::string, std::string>& fatal_strings, const Node* node) { // TODO: extra copy auto cpp_name = cpp2::get_name(node); fatal_strings.emplace(get_fatal_string_short_id(cpp_name), cpp_name); auto hash = cpp2::sha256_hex(node->get_name()); fatal_strings.emplace("__fbthrift_hash_" + hash, node->get_name()); for (const auto& a : node->annotations()) { if (!is_annotation_blacklisted_in_fatal(a.first)) { fatal_strings.emplace(get_fatal_string_short_id(a.first), a.first); } } } std::vector<std::string> get_fatal_enum_names() { std::vector<std::string> result; for (const auto* enm : program_->enums()) { result.push_back(get_fatal_string_short_id(enm->get_name())); } return result; } std::vector<std::string> get_fatal_union_names() { std::vector<std::string> result; for (const auto* obj : program_->objects()) { if (obj->is_union()) { result.push_back(get_fatal_string_short_id(obj->get_name())); } } return result; } std::vector<std::string> get_fatal_struct_names() { std::vector<std::string> result; for (const auto* obj : program_->objects()) { if (!obj->is_union()) { result.push_back(get_fatal_string_short_id(obj->get_name())); } } // typedefs resolve to struct for (const t_typedef* i : alias_to_struct()) { result.push_back(get_fatal_string_short_id(i->get_name())); } return result; } std::vector<std::string> get_fatal_constant_names() { std::vector<std::string> result; for (const auto* cnst : program_->consts()) { result.push_back(get_fatal_string_short_id(cnst->get_name())); } return result; } std::vector<std::string> get_fatal_service_names() { std::vector<std::string> result; for (const auto* service : program_->services()) { result.push_back(get_fatal_string_short_id(service->get_name())); } return result; } mstch::node to_fatal_string_array(const std::vector<std::string>&& vec) { mstch::array a; for (size_t i = 0; i < vec.size(); i++) { a.push_back(mstch::map{ {"fatal_string:name", vec.at(i)}, {"last?", i == vec.size() - 1}, }); } return mstch::map{{"fatal_strings:items", a}}; } mstch::node namespace_cpp2() { return t_mstch_cpp2_generator::get_namespace_array(program_); } mstch::node cpp_includes() { return t_mstch_cpp2_generator::cpp_includes(program_); } mstch::node include_prefix() { return t_mstch_cpp2_generator::include_prefix( program_, cache_->parsed_options_); } mstch::node cpp_declare_hash() { bool cpp_declare_in_structs = std::any_of( program_->structs().begin(), program_->structs().end(), [](const auto* strct) { return strct->has_annotation( {"cpp.declare_hash", "cpp2.declare_hash"}); }); bool cpp_declare_in_typedefs = std::any_of( program_->typedefs().begin(), program_->typedefs().end(), [](const auto* typedf) { return typedf->get_type()->has_annotation( {"cpp.declare_hash", "cpp2.declare_hash"}); }); return cpp_declare_in_structs || cpp_declare_in_typedefs; } mstch::node thrift_includes() { mstch::array a{}; for (auto const* program : program_->get_included_programs()) { a.push_back(generators_->program_generator_->generate_cached( program, generators_, cache_)); } return a; } mstch::node frozen_packed() { return get_option("frozen") == "packed"; } mstch::node fatal_languages() { mstch::array a; size_t size = program_->namespaces().size(); size_t idx = 0; for (const auto& pair : program_->namespaces()) { a.push_back(mstch::map{ {"language:safe_name", get_fatal_string_short_id(pair.first)}, {"language:safe_namespace", get_fatal_namespace_name_short_id(pair.first, pair.second)}, {"last?", idx == size - 1}, }); ++idx; } return mstch::map{{"fatal_languages:items", a}}; } mstch::node fatal_enums() { return to_fatal_string_array(get_fatal_enum_names()); } mstch::node fatal_unions() { return to_fatal_string_array(get_fatal_union_names()); } mstch::node fatal_structs() { return to_fatal_string_array(get_fatal_struct_names()); } mstch::node fatal_constants() { return to_fatal_string_array(get_fatal_constant_names()); } mstch::node fatal_services() { return to_fatal_string_array(get_fatal_service_names()); } mstch::node fatal_identifiers() { std::map<std::string, std::string> unique_names; unique_names.emplace( get_fatal_string_short_id(program_->name()), program_->name()); // languages and namespaces for (const auto& pair : program_->namespaces()) { unique_names.emplace(get_fatal_string_short_id(pair.first), pair.first); unique_names.emplace( get_fatal_namespace_name_short_id(pair.first, pair.second), get_fatal_namespace(pair.first, pair.second)); } // enums for (const auto* enm : program_->enums()) { collect_fatal_string_annotated(unique_names, enm); unique_names.emplace( get_fatal_string_short_id(enm->get_name()), enm->get_name()); for (const auto& i : enm->get_enum_values()) { collect_fatal_string_annotated(unique_names, i); } } // structs, unions and exceptions for (const auto* obj : program_->objects()) { if (obj->is_union()) { // When generating <program_name>_fatal_union.h, we will generate // <union_name>_Type_enum_traits unique_names.emplace("Type", "Type"); } collect_fatal_string_annotated(unique_names, obj); for (const auto& m : obj->fields()) { collect_fatal_string_annotated(unique_names, &m); } } // consts for (const auto* cnst : program_->consts()) { unique_names.emplace( get_fatal_string_short_id(cnst->get_name()), cnst->get_name()); } // services for (const auto* service : program_->services()) { // function annotations are not currently included. unique_names.emplace( get_fatal_string_short_id(service->get_name()), service->get_name()); for (const auto* f : service->get_functions()) { unique_names.emplace( get_fatal_string_short_id(f->get_name()), f->get_name()); for (const auto& p : f->get_paramlist()->fields()) { unique_names.emplace(get_fatal_string_short_id(p.name()), p.name()); } } } // typedefs resolve to struct for (const t_typedef* i : alias_to_struct()) { unique_names.emplace( get_fatal_string_short_id(i->get_name()), i->get_name()); } mstch::array a; for (const auto& name : unique_names) { a.push_back(mstch::map{ {"identifier:name", name.first}, {"identifier:fatal_string", render_fatal_string(name.second)}, }); } return a; } mstch::node fatal_data_member() { std::unordered_set<std::string> fields; std::vector<const std::string*> ordered_fields; for (const t_struct* s : program_->objects()) { if (!s->is_union()) { for (const t_field& f : s->fields()) { auto result = fields.insert(cpp2::get_name(&f)); if (result.second) { ordered_fields.push_back(&*result.first); } } } } mstch::array a; for (const auto& f : ordered_fields) { a.push_back(*f); } return a; } private: boost::optional<std::vector<t_struct*>> objects_; boost::optional<std::vector<t_enum*>> enums_; const boost::optional<int32_t> split_id_; const std::vector<t_enum*>& get_program_enums() override { if (!enums_) { init_objects_enums(); } return *enums_; } const std::vector<t_struct*>& get_program_objects() override { if (!objects_) { init_objects_enums(); } return *objects_; } void init_objects_enums() { const auto& prog_objects = program_->objects(); const auto& prog_enums = program_->enums(); const bool sort_objects_with_map_dependency = has_option("sort_objects_with_map_dependency"); if (!split_id_) { auto edges = cpp2::gen_dependency_graph( program_, prog_objects, sort_objects_with_map_dependency); objects_ = topological_sort<t_struct*>( prog_objects.begin(), prog_objects.end(), edges); enums_ = prog_enums; return; } int32_t split_count = std::max(cpp2::get_split_count(cache_->parsed_options_), 1); objects_.emplace(); enums_.emplace(); const size_t cnt = prog_objects.size() + prog_enums.size(); for (size_t i = split_id_.value_or(0); i < cnt; i += split_count) { if (i < prog_objects.size()) { objects_->push_back(prog_objects[i]); } else { enums_->push_back(prog_enums[i - prog_objects.size()]); } } } }; class enum_cpp2_generator : public enum_generator { public: enum_cpp2_generator() = default; ~enum_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_enum const* enm, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_enum>( enm, std::move(generators), std::move(cache), pos); } }; class enum_value_cpp2_generator : public enum_value_generator { public: std::shared_ptr<mstch_base> generate( t_enum_value const* enm_value, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_enum_value>( enm_value, std::move(generators), std::move(cache), pos); } }; class type_cpp2_generator : public type_generator { public: explicit type_cpp2_generator( std::shared_ptr<cpp2_generator_context> context) noexcept : context_(std::move(context)) {} std::shared_ptr<mstch_base> generate( t_type const* type, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_type>( type, std::move(generators), std::move(cache), pos, context_); } private: std::shared_ptr<cpp2_generator_context> context_; }; class field_cpp2_generator : public field_generator { public: explicit field_cpp2_generator( std::shared_ptr<cpp2_generator_context> context) noexcept : context_(std::move(context)) {} std::shared_ptr<mstch_base> generate( t_field const* field, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t index, field_generator_context const* field_context) const override { return std::make_shared<mstch_cpp2_field>( field, std::move(generators), std::move(cache), pos, index, field_context, context_); } private: std::shared_ptr<cpp2_generator_context> context_; }; class function_cpp2_generator : public function_generator { public: function_cpp2_generator() = default; ~function_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_function const* function, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_function>( function, std::move(generators), std::move(cache), pos); } }; class struct_cpp2_generator : public struct_generator { public: explicit struct_cpp2_generator( std::shared_ptr<cpp2_generator_context> context) : context_(std::move(context)) {} ~struct_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_struct const* strct, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_struct>( strct, std::move(generators), std::move(cache), pos, context_); } private: std::shared_ptr<cpp2_generator_context> context_; }; class service_cpp2_generator : public service_generator { public: service_cpp2_generator() = default; ~service_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_service const* service, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_service>( service, std::move(generators), std::move(cache), pos); } std::shared_ptr<mstch_base> generate_with_split_id( t_service const* service, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, int32_t split_id, int32_t split_count) const { return std::make_shared<mstch_cpp2_service>( service, generators, cache, ELEMENT_POSITION::NONE, split_id, split_count); } }; class annotation_cpp2_generator : public annotation_generator { public: annotation_cpp2_generator() = default; ~annotation_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_annotation const& keyval, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t index) const override { return std::make_shared<mstch_cpp2_annotation>( keyval.first, keyval.second, std::move(generators), std::move(cache), pos, index); } }; class const_cpp2_generator : public const_generator { public: const_cpp2_generator() = default; ~const_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_const const* cnst, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t index, t_const const* current_const, t_type const* expected_type, t_field const* field) const override { return std::make_shared<mstch_cpp2_const>( cnst, current_const, expected_type, std::move(generators), std::move(cache), pos, index, field); } }; class const_value_cpp2_generator : public const_value_generator { public: const_value_cpp2_generator() = default; ~const_value_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_const_value const* const_value, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t index, t_const const* current_const, t_type const* expected_type) const override { return std::make_shared<mstch_cpp2_const_value>( const_value, current_const, expected_type, std::move(generators), std::move(cache), pos, index); } }; class program_cpp2_generator : public program_generator { public: program_cpp2_generator() = default; ~program_cpp2_generator() override = default; std::shared_ptr<mstch_base> generate( t_program const* program, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, ELEMENT_POSITION pos, int32_t /*index*/) const override { return std::make_shared<mstch_cpp2_program>( program, std::move(generators), std::move(cache), pos); } std::shared_ptr<mstch_base> generate_with_split_id( t_program const* program, std::shared_ptr<mstch_generators const> generators, std::shared_ptr<mstch_cache> cache, int32_t split_id) const { return std::make_shared<mstch_cpp2_program>( program, std::move(generators), std::move(cache), ELEMENT_POSITION::NONE, split_id); } }; t_mstch_cpp2_generator::t_mstch_cpp2_generator( t_program* program, t_generation_context context, const std::map<std::string, std::string>& parsed_options, const std::string& /*option_string*/) : t_mstch_generator( program, std::move(context), "cpp2", parsed_options, true), context_(std::make_shared<cpp2_generator_context>( cpp2_generator_context::create())), client_name_to_split_count_( cpp2::get_client_name_to_split_count(parsed_options)) { out_dir_base_ = get_out_dir_base(parsed_options); } void t_mstch_cpp2_generator::generate_program() { auto const* program = get_program(); set_mstch_generators(); if (has_option("any")) { generate_sinit(program); } if (has_option("reflection")) { generate_reflection(program); } generate_structs(program); generate_constants(program); for (const auto* service : program->services()) { generate_service(service); } generate_metadata(program); generate_visitation(program); } void t_mstch_cpp2_generator::set_mstch_generators() { generators_->set_enum_generator(std::make_unique<enum_cpp2_generator>()); generators_->set_enum_value_generator( std::make_unique<enum_value_cpp2_generator>()); generators_->set_type_generator( std::make_unique<type_cpp2_generator>(context_)); generators_->set_field_generator( std::make_unique<field_cpp2_generator>(context_)); generators_->set_function_generator( std::make_unique<function_cpp2_generator>()); generators_->set_struct_generator( std::make_unique<struct_cpp2_generator>(context_)); generators_->set_service_generator( std::make_unique<service_cpp2_generator>()); generators_->set_const_generator(std::make_unique<const_cpp2_generator>()); generators_->set_const_value_generator( std::make_unique<const_value_cpp2_generator>()); generators_->set_annotation_generator( std::make_unique<annotation_cpp2_generator>()); generators_->set_program_generator( std::make_unique<program_cpp2_generator>()); } void t_mstch_cpp2_generator::generate_constants(t_program const* program) { const auto& name = program->name(); const auto& prog = cached_program(program); render_to_file(prog, "module_constants.h", name + "_constants.h"); render_to_file(prog, "module_constants.cpp", name + "_constants.cpp"); } void t_mstch_cpp2_generator::generate_metadata(const t_program* program) { const auto& name = program->name(); const auto& prog = cached_program(program); render_to_file(prog, "module_metadata.h", name + "_metadata.h"); if (!has_option("no_metadata")) { render_to_file(prog, "module_metadata.cpp", name + "_metadata.cpp"); } } void t_mstch_cpp2_generator::generate_sinit(t_program const* program) { const auto& name = program->name(); const auto& prog = cached_program(program); render_to_file(prog, "module_sinit.cpp", name + "_sinit.cpp"); } void t_mstch_cpp2_generator::generate_reflection(t_program const* program) { const auto& name = program->name(); const auto& prog = cached_program(program); // Combo include: all render_to_file(prog, "module_fatal_all.h", name + "_fatal_all.h"); // Combo include: types render_to_file(prog, "module_fatal_types.h", name + "_fatal_types.h"); // Unique Compile-time Strings, Metadata tags and Metadata registration render_to_file(prog, "module_fatal.h", name + "_fatal.h"); render_to_file(prog, "module_fatal_enum.h", name + "_fatal_enum.h"); render_to_file(prog, "module_fatal_union.h", name + "_fatal_union.h"); render_to_file(prog, "module_fatal_struct.h", name + "_fatal_struct.h"); render_to_file(prog, "module_fatal_constant.h", name + "_fatal_constant.h"); render_to_file(prog, "module_fatal_service.h", name + "_fatal_service.h"); } void t_mstch_cpp2_generator::generate_visitation(const t_program* program) { const auto& name = program->name(); const auto& prog = cached_program(program); render_to_file(prog, "module_visitation.h", name + "_visitation.h"); render_to_file(prog, "module_for_each_field.h", name + "_for_each_field.h"); render_to_file(prog, "module_visit_union.h", name + "_visit_union.h"); render_to_file( prog, "module_visit_by_thrift_field_metadata.h", name + "_visit_by_thrift_field_metadata.h"); } void t_mstch_cpp2_generator::generate_structs(t_program const* program) { const auto& name = program->name(); const auto& prog = cached_program(program); render_to_file(prog, "module_data.h", name + "_data.h"); render_to_file(prog, "module_data.cpp", name + "_data.cpp"); render_to_file(prog, "module_types.h", name + "_types.h"); render_to_file(prog, "module_types.tcc", name + "_types.tcc"); if (auto split_count = cpp2::get_split_count(parsed_options_)) { auto digit = std::to_string(split_count - 1).size(); for (int split_id = 0; split_id < split_count; ++split_id) { auto s = std::to_string(split_id); s = std::string(digit - s.size(), '0') + s; render_to_file( program_cpp2_generator{}.generate_with_split_id( program, generators_, cache_, split_id), "module_types.cpp", name + "_types." + s + ".split.cpp"); } } else { render_to_file(prog, "module_types.cpp", name + "_types.cpp"); } render_to_file( prog, "module_types_custom_protocol.h", name + "_types_custom_protocol.h"); if (has_option("frozen2")) { render_to_file(prog, "module_layouts.h", name + "_layouts.h"); render_to_file(prog, "module_layouts.cpp", name + "_layouts.cpp"); } } void t_mstch_cpp2_generator::generate_service(t_service const* service) { const auto& name = service->get_name(); // for interactions cache_->parsed_options_["parent_service_name"] = name; cache_->parsed_options_["parent_service_cpp_name"] = cpp2::get_name(service); cache_->parsed_options_["parent_service_qualified_name"] = t_mstch_cpp2_generator::get_service_qualified_name(service); auto serv = generators_->service_generator_->generate_cached( get_program(), service, generators_, cache_); render_to_file(serv, "ServiceAsyncClient.h", name + "AsyncClient.h"); render_to_file(serv, "service.cpp", name + ".cpp"); render_to_file(serv, "service.h", name + ".h"); render_to_file(serv, "service.tcc", name + ".tcc"); render_to_file(serv, "types_custom_protocol.h", name + "_custom_protocol.h"); auto iter = client_name_to_split_count_.find(name); if (iter != client_name_to_split_count_.end()) { auto split_count = iter->second; auto digit = std::to_string(split_count - 1).size(); for (int split_id = 0; split_id < split_count; ++split_id) { auto s = std::to_string(split_id); s = std::string(digit - s.size(), '0') + s; auto split_service = service_cpp2_generator{}.generate_with_split_id( service, generators_, cache_, split_id, split_count); render_to_file( split_service, "ServiceAsyncClient.cpp", name + "." + s + ".async_client_split.cpp"); } } else { render_to_file(serv, "ServiceAsyncClient.cpp", name + "AsyncClient.cpp"); } std::vector<std::array<std::string, 3>> protocols = { {{"binary", "BinaryProtocol", "T_BINARY_PROTOCOL"}}, {{"compact", "CompactProtocol", "T_COMPACT_PROTOCOL"}}, }; for (const auto& protocol : protocols) { render_to_file( serv, "service_processmap_protocol.cpp", name + "_processmap_" + protocol.at(0) + ".cpp"); } cache_->parsed_options_.erase("parent_service_name"); cache_->parsed_options_.erase("parent_service_cpp_name"); cache_->parsed_options_.erase("parent_service_qualified_name"); } std::string t_mstch_cpp2_generator::get_cpp2_namespace( t_program const* program) { return cpp2::get_gen_namespace(*program); } /* static */ std::string t_mstch_cpp2_generator::get_cpp2_unprefixed_namespace( t_program const* program) { return cpp2::get_gen_unprefixed_namespace(*program); } /* static */ std::string t_mstch_cpp2_generator::get_service_qualified_name( t_service const* service) { return get_cpp2_namespace(service->program()) + "::" + cpp2::get_name(service); } mstch::array t_mstch_cpp2_generator::get_namespace_array( t_program const* program) { auto const v = cpp2::get_gen_namespace_components(*program); mstch::array a; for (auto it = v.begin(); it != v.end(); ++it) { mstch::map m; m.emplace("namespace:name", *it); a.push_back(m); } for (auto itr = a.begin(); itr != a.end(); ++itr) { boost::get<mstch::map>(*itr).emplace("first?", itr == a.begin()); boost::get<mstch::map>(*itr).emplace("last?", std::next(itr) == a.end()); } return a; } mstch::node t_mstch_cpp2_generator::cpp_includes(t_program const* program) { mstch::array a{}; for (auto include : program->cpp_includes()) { mstch::map cpp_include; if (include.at(0) != '<') { include = "\"" + include + "\""; } cpp_include.emplace("cpp_include", std::string(include)); a.push_back(cpp_include); } return a; } mstch::node t_mstch_cpp2_generator::include_prefix( t_program const* program, std::map<std::string, std::string>& options) { auto prefix = program->include_prefix(); auto include_prefix = options["include_prefix"]; auto out_dir_base = get_out_dir_base(options); if (prefix.empty()) { if (include_prefix.empty()) { return prefix; } else { return include_prefix + "/" + out_dir_base + "/"; } } if (boost::filesystem::path(prefix).has_root_directory()) { return include_prefix + "/" + out_dir_base + "/"; } return prefix + out_dir_base + "/"; } namespace { class annotation_validator : public validator { public: explicit annotation_validator( std::map<std::string, std::string> const& options) : options_(options) {} using validator::visit; /** * Make sure there is no incompatible annotation. */ bool visit(t_struct* s) override; private: const std::map<std::string, std::string>& options_; }; bool annotation_validator::visit(t_struct* s) { if (cpp2::packed_isset(*s)) { if (options_.count("tablebased") != 0) { add_error( s->lineno(), "Tablebased serialization is incompatible with isset bitpacking for struct `" + s->get_name() + "`"); } } for (const auto& field : s->fields()) { if (cpp2::is_mixin(field)) { // Mixins cannot be refs if (cpp2::is_explicit_ref(&field)) { add_error( field.lineno(), "Mixin field `" + field.name() + "` can not be a ref in cpp."); } } } return true; } class service_method_validator : public validator { public: explicit service_method_validator( std::map<std::string, std::string> const& options) : options_(options) {} using validator::visit; /** * Make sure there is no 'cpp.coroutine' annotation set when * 'stack_arguments' is turned on. */ bool visit(t_service* service) override; private: const std::map<std::string, std::string>& options_; }; bool service_method_validator::visit(t_service* service) { auto suppress_key = "cpp.coroutine_stack_arguments_broken_suppress_error"; for (const auto& func : service->functions()) { if (!func.has_annotation(suppress_key) && func.has_annotation("cpp.coroutine") && cpp2::is_stack_arguments(options_, func)) { // when cpp.coroutine and stack_arguments are both on, return failure if // this function has complex types (including string and binary). const auto& params = func.get_paramlist()->fields(); bool ok = std::all_of(params.begin(), params.end(), [](const auto& param) { auto type = param.type()->get_true_type(); return type->is_base_type() && !type->is_string_or_binary(); }); if (!ok) { add_error( func.lineno(), "`" + service->name() + "." + func.name() + "` use of cpp.coroutine and stack_arguments together is " "disallowed."); } } } return true; } class splits_validator : public validator { public: explicit splits_validator(int split_count) : split_count_(split_count) {} using validator::visit; bool visit(t_program* program) override { set_program(program); const int32_t object_count = program->objects().size() + program->enums().size(); if (split_count_ != 0 && split_count_ > object_count) { add_error( boost::none, "`types_cpp_splits=" + std::to_string(split_count_) + "` is misconfigured: it can not be greater than number of object, which is " + std::to_string(object_count) + "."); } return true; } private: int32_t split_count_; }; class lazy_field_validator : public validator { public: using validator::visit; bool visit(t_field* field) override { if (cpp2::is_lazy(field)) { auto t = field->get_type()->get_true_type(); boost::optional<std::string> field_type; if (t->is_any_int() || t->is_bool() || t->is_byte()) { field_type = "Integral field"; } if (t->is_floating_point()) { field_type = "Floating point field"; } if (field_type) { add_error( field->get_lineno(), *field_type + " `" + field->get_name() + "` can not be marked as lazy, " "since doing so won't bring any benefit."); } } return true; } }; } // namespace void t_mstch_cpp2_generator::fill_validator_list(validator_list& l) const { l.add<annotation_validator>(this->parsed_options_); l.add<service_method_validator>(this->parsed_options_); l.add<splits_validator>(cpp2::get_split_count(parsed_options_)); l.add<lazy_field_validator>(); } THRIFT_REGISTER_GENERATOR(mstch_cpp2, "cpp2", ""); } // namespace compiler } // namespace thrift } // namespace apache