1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <algorithm>
18 #include <array>
19 #include <cassert>
20 #include <memory>
21 #include <queue>
22 #include <set>
23 #include <vector>
24 
25 #include <boost/algorithm/string/replace.hpp>
26 
27 #include <thrift/compiler/gen/cpp/type_resolver.h>
28 #include <thrift/compiler/generate/t_mstch_generator.h>
29 #include <thrift/compiler/generate/t_mstch_objects.h>
30 #include <thrift/compiler/lib/cpp2/util.h>
31 #include <thrift/compiler/util.h>
32 #include <thrift/compiler/validator/validator.h>
33 
34 namespace apache {
35 namespace thrift {
36 namespace compiler {
37 
38 namespace {
39 
get_cpp_template(const t_type * type)40 std::string const& get_cpp_template(const t_type* type) {
41   return type->get_annotation({"cpp.template", "cpp2.template"});
42 }
43 
is_cpp_ref_unique_either(const t_field * f)44 bool is_cpp_ref_unique_either(const t_field* f) {
45   return cpp2::is_unique_ref(f) || cpp2::is_implicit_ref(f->get_type());
46 }
47 
is_annotation_blacklisted_in_fatal(const std::string & key)48 bool is_annotation_blacklisted_in_fatal(const std::string& key) {
49   const static std::set<std::string> black_list{
50       "cpp.methods",
51       "cpp.name",
52       "cpp.ref",
53       "cpp.ref_type",
54       "cpp.template",
55       "cpp.type",
56       "cpp2.methods",
57       "cpp2.ref",
58       "cpp2.ref_type",
59       "cpp2.template",
60       "cpp2.type",
61       "cpp.internal.deprecated._data.method",
62   };
63   return black_list.find(key) != black_list.end();
64 }
65 
same_types(const t_type * a,const t_type * b)66 bool same_types(const t_type* a, const t_type* b) {
67   if (!a || !b) {
68     return false;
69   }
70 
71   if (get_cpp_template(a) != get_cpp_template(b) ||
72       cpp2::get_type(a) != cpp2::get_type(b)) {
73     return false;
74   }
75 
76   const auto* resolved_a = a->get_true_type();
77   const auto* resolved_b = b->get_true_type();
78 
79   if (resolved_a->get_type_value() != resolved_b->get_type_value()) {
80     return false;
81   }
82 
83   switch (resolved_a->get_type_value()) {
84     case t_type::type::t_list: {
85       const auto* list_a = static_cast<const t_list*>(resolved_a);
86       const auto* list_b = static_cast<const t_list*>(resolved_b);
87       return same_types(list_a->get_elem_type(), list_b->get_elem_type());
88     }
89     case t_type::type::t_set: {
90       const auto* set_a = static_cast<const t_set*>(resolved_a);
91       const auto* set_b = static_cast<const t_set*>(resolved_b);
92       return same_types(set_a->get_elem_type(), set_b->get_elem_type());
93     }
94     case t_type::type::t_map: {
95       const auto* map_a = static_cast<const t_map*>(resolved_a);
96       const auto* map_b = static_cast<const t_map*>(resolved_b);
97       return same_types(map_a->get_key_type(), map_b->get_key_type()) &&
98           same_types(map_a->get_val_type(), map_b->get_val_type());
99     }
100     default:;
101   }
102   return true;
103 }
104 
get_fatal_annotations(std::map<std::string,annotation_value> annotations)105 std::vector<t_annotation> get_fatal_annotations(
106     std::map<std::string, annotation_value> annotations) {
107   std::vector<t_annotation> fatal_annotations;
108   for (const auto& iter : annotations) {
109     if (is_annotation_blacklisted_in_fatal(iter.first)) {
110       continue;
111     }
112     fatal_annotations.push_back({iter.first, iter.second});
113   }
114 
115   return fatal_annotations;
116 }
117 
get_fatal_string_short_id(const std::string & key)118 std::string get_fatal_string_short_id(const std::string& key) {
119   return boost::algorithm::replace_all_copy(key, ".", "_");
120 }
121 
get_fatal_namesoace_name_short_id(const std::string & lang,const std::string & ns)122 std::string get_fatal_namesoace_name_short_id(
123     const std::string& lang, const std::string& ns) {
124   std::string replacement = lang == "cpp" || lang == "cpp2" ? "__" : "_";
125   std::string result = boost::algorithm::replace_all_copy(ns, ".", replacement);
126   if (lang == "php_path") {
127     return boost::algorithm::replace_all_copy(ns, "/", "_");
128   }
129   return result;
130 }
131 
get_fatal_namesoace(const std::string & lang,const std::string & ns)132 std::string get_fatal_namesoace(
133     const std::string& lang, const std::string& ns) {
134   if (lang == "cpp" || lang == "cpp2") {
135     return boost::algorithm::replace_all_copy(ns, ".", "::");
136   } else if (lang == "php") {
137     return boost::algorithm::replace_all_copy(ns, ".", "_");
138   }
139   return ns;
140 }
141 
render_fatal_string(const std::string & normal_string)142 std::string render_fatal_string(const std::string& normal_string) {
143   const static std::map<char, std::string> substition{
144       {'\0', "\\0"},
145       {'\n', "\\n"},
146       {'\r', "\\r"},
147       {'\t', "\\t"},
148       {'\'', "\\\'"},
149       {'\\', "\\\\"},
150   };
151   std::ostringstream res;
152   res << "::fatal::sequence<char";
153   for (const char& c : normal_string) {
154     res << ", '";
155     auto found = substition.find(c);
156     if (found != substition.end()) {
157       res << found->second;
158     } else {
159       res << c;
160     }
161     res << "'";
162   }
163   res << ">";
164   return res.str();
165 }
166 
get_out_dir_base(const std::map<std::string,std::string> & options)167 std::string get_out_dir_base(
168     const std::map<std::string, std::string>& options) {
169   return options.find("py3cpp") != options.end() ? "gen-py3cpp" : "gen-cpp2";
170 }
171 
mangle_field_name(const std::string & name)172 std::string mangle_field_name(const std::string& name) {
173   return "__fbthrift_field_" + name;
174 }
175 
176 } // namespace
177 
178 class cpp2_generator_context {
179  public:
create()180   static cpp2_generator_context create() { return cpp2_generator_context(); }
181 
182   cpp2_generator_context(cpp2_generator_context&&) = default;
183   cpp2_generator_context& operator=(cpp2_generator_context&&) = default;
184 
is_orderable(t_type const & type)185   bool is_orderable(t_type const& type) {
186     std::unordered_set<t_type const*> seen;
187     auto& memo = is_orderable_memo_;
188     return cpp2::is_orderable(seen, memo, type);
189   }
190 
resolver()191   gen::cpp::type_resolver& resolver() { return resolver_; }
192 
193  private:
194   cpp2_generator_context() = default;
195 
196   std::unordered_map<t_type const*, bool> is_orderable_memo_;
197   gen::cpp::type_resolver resolver_;
198 };
199 
200 class t_mstch_cpp2_generator : public t_mstch_generator {
201  public:
202   t_mstch_cpp2_generator(
203       t_program* program,
204       t_generation_context context,
205       const std::map<std::string, std::string>& parsed_options,
206       const std::string& /*option_string*/);
207 
208   void generate_program() override;
209   void fill_validator_list(validator_list&) const override;
210   static std::string get_cpp2_namespace(t_program const* program);
211   static mstch::array get_namespace_array(t_program const* program);
212   static mstch::node cpp_includes(t_program const* program);
213   static mstch::node include_prefix(
214       t_program const* program, std::map<std::string, std::string>& options);
215 
216  private:
217   void set_mstch_generators();
218   void generate_sinit(t_program const* program);
219   void generate_reflection(t_program const* program);
220   void generate_visitation(t_program const* program);
221   void generate_constants(t_program const* program);
222   void generate_metadata(t_program const* program);
223   void generate_structs(t_program const* program);
224   void generate_service(t_service const* service);
225 
226   std::shared_ptr<cpp2_generator_context> context_;
227   std::unordered_map<std::string, int32_t> client_name_to_split_count_;
228 };
229 
230 class mstch_cpp2_enum : public mstch_enum {
231  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)232   mstch_cpp2_enum(
233       t_enum const* enm,
234       std::shared_ptr<mstch_generators const> generators,
235       std::shared_ptr<mstch_cache> cache,
236       ELEMENT_POSITION const pos)
237       : mstch_enum(enm, std::move(generators), std::move(cache), pos) {
238     register_methods(
239         this,
240         {
241             {"enum:empty?", &mstch_cpp2_enum::is_empty},
242             {"enum:size", &mstch_cpp2_enum::size},
243             {"enum:min", &mstch_cpp2_enum::min},
244             {"enum:max", &mstch_cpp2_enum::max},
245             {"enum:cpp_is_unscoped", &mstch_cpp2_enum::cpp_is_unscoped},
246             {"enum:cpp_name", &mstch_cpp2_enum::cpp_name},
247             {"enum:cpp_enum_type", &mstch_cpp2_enum::cpp_enum_type},
248             {"enum:cpp_declare_bitwise_ops",
249              &mstch_cpp2_enum::cpp_declare_bitwise_ops},
250             {"enum:has_zero", &mstch_cpp2_enum::has_zero},
251             {"enum:fatal_annotations?",
252              &mstch_cpp2_enum::has_fatal_annotations},
253             {"enum:fatal_annotations", &mstch_cpp2_enum::fatal_annotations},
254             {"enum:legacy_type_id", &mstch_cpp2_enum::get_legacy_type_id},
255         });
256   }
is_empty()257   mstch::node is_empty() { return enm_->get_enum_values().empty(); }
size()258   mstch::node size() { return std::to_string(enm_->get_enum_values().size()); }
min()259   mstch::node min() {
260     if (!enm_->get_enum_values().empty()) {
261       auto e_min = std::min_element(
262           enm_->get_enum_values().begin(),
263           enm_->get_enum_values().end(),
264           [](t_enum_value* a, t_enum_value* b) {
265             return a->get_value() < b->get_value();
266           });
267       return cpp2::get_name(*e_min);
268     }
269     return mstch::node();
270   }
max()271   mstch::node max() {
272     if (!enm_->get_enum_values().empty()) {
273       auto e_max = std::max_element(
274           enm_->get_enum_values().begin(),
275           enm_->get_enum_values().end(),
276           [](t_enum_value* a, t_enum_value* b) {
277             return a->get_value() < b->get_value();
278           });
279       return cpp2::get_name(*e_max);
280     }
281     return mstch::node();
282   }
cpp_is_unscoped_()283   std::string const& cpp_is_unscoped_() {
284     return enm_->get_annotation(
285         {"cpp2.deprecated_enum_unscoped", "cpp.deprecated_enum_unscoped"});
286   }
cpp_is_unscoped()287   mstch::node cpp_is_unscoped() { return cpp_is_unscoped_(); }
cpp_name()288   mstch::node cpp_name() { return cpp2::get_name(enm_); }
cpp_enum_type()289   mstch::node cpp_enum_type() {
290     static std::string kInt = "int";
291     return enm_->get_annotation(
292         {"cpp.enum_type", "cpp2.enum_type"},
293         cpp_is_unscoped_().empty() ? nullptr : &kInt);
294   }
cpp_declare_bitwise_ops()295   mstch::node cpp_declare_bitwise_ops() {
296     return enm_->get_annotation(
297         {"cpp.declare_bitwise_ops", "cpp2.declare_bitwise_ops"});
298   }
has_zero()299   mstch::node has_zero() {
300     auto* enm_value = enm_->find_value(0);
301     if (enm_value != nullptr) {
302       return generators_->enum_value_generator_->generate(
303           enm_value, generators_, cache_, pos_);
304     }
305     return mstch::node();
306   }
has_fatal_annotations()307   mstch::node has_fatal_annotations() {
308     return get_fatal_annotations(enm_->annotations()).size() > 0;
309   }
fatal_annotations()310   mstch::node fatal_annotations() {
311     return generate_annotations(get_fatal_annotations(enm_->annotations()));
312   }
get_legacy_type_id()313   mstch::node get_legacy_type_id() {
314     return std::to_string(enm_->get_type_id());
315   }
316 };
317 
318 class mstch_cpp2_enum_value : public mstch_enum_value {
319  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)320   mstch_cpp2_enum_value(
321       t_enum_value const* enm_value,
322       std::shared_ptr<mstch_generators const> generators,
323       std::shared_ptr<mstch_cache> cache,
324       ELEMENT_POSITION const pos)
325       : mstch_enum_value(
326             enm_value, std::move(generators), std::move(cache), pos) {
327     register_methods(
328         this,
329         {
330             {"enum_value:name_hash", &mstch_cpp2_enum_value::name_hash},
331             {"enum_value:cpp_name", &mstch_cpp2_enum_value::cpp_name},
332             {"enum_value:fatal_annotations?",
333              &mstch_cpp2_enum_value::has_fatal_annotations},
334             {"enum_value:fatal_annotations",
335              &mstch_cpp2_enum_value::fatal_annotations},
336         });
337   }
name_hash()338   mstch::node name_hash() {
339     return "__fbthrift_hash_" + cpp2::sha256_hex(enm_value_->get_name());
340   }
cpp_name()341   mstch::node cpp_name() { return cpp2::get_name(enm_value_); }
has_fatal_annotations()342   mstch::node has_fatal_annotations() {
343     return get_fatal_annotations(enm_value_->annotations()).size() > 0;
344   }
fatal_annotations()345   mstch::node fatal_annotations() {
346     return generate_annotations(
347         get_fatal_annotations(enm_value_->annotations()));
348   }
349 };
350 
351 class mstch_cpp2_const_value : public mstch_const_value {
352  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)353   mstch_cpp2_const_value(
354       t_const_value const* const_value,
355       t_const const* current_const,
356       t_type const* expected_type,
357       std::shared_ptr<mstch_generators const> generators,
358       std::shared_ptr<mstch_cache> cache,
359       ELEMENT_POSITION pos,
360       int32_t index)
361       : mstch_const_value(
362             const_value,
363             current_const,
364             expected_type,
365             std::move(generators),
366             std::move(cache),
367             pos,
368             index) {}
369 
370  private:
same_type_as_expected() const371   bool same_type_as_expected() const override {
372     return const_value_->get_owner() &&
373         same_types(expected_type_, const_value_->get_owner()->get_type());
374   }
375 };
376 
377 class mstch_cpp2_type : public mstch_type {
378  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)379   mstch_cpp2_type(
380       t_type const* type,
381       std::shared_ptr<mstch_generators const> generators,
382       std::shared_ptr<mstch_cache> cache,
383       ELEMENT_POSITION const pos,
384       std::shared_ptr<cpp2_generator_context> context)
385       : mstch_type(type, std::move(generators), std::move(cache), pos),
386         context_(std::move(context)) {
387     register_methods(
388         this,
389         {
390             {"type:resolves_to_base?", &mstch_cpp2_type::resolves_to_base},
391             {"type:resolves_to_integral?",
392              &mstch_cpp2_type::resolves_to_integral},
393             {"type:resolves_to_base_or_enum?",
394              &mstch_cpp2_type::resolves_to_base_or_enum},
395             {"type:resolves_to_container?",
396              &mstch_cpp2_type::resolves_to_container},
397             {"type:resolves_to_container_or_struct?",
398              &mstch_cpp2_type::resolves_to_container_or_struct},
399             {"type:resolves_to_container_or_enum?",
400              &mstch_cpp2_type::resolves_to_container_or_enum},
401             {"type:resolves_to_complex_return?",
402              &mstch_cpp2_type::resolves_to_complex_return},
403             {"type:resolves_to_fixed_size?",
404              &mstch_cpp2_type::resolves_to_fixed_size},
405             {"type:resolves_to_enum?", &mstch_cpp2_type::resolves_to_enum},
406             {"type:transitively_refers_to_struct?",
407              &mstch_cpp2_type::transitively_refers_to_struct},
408             {"type:cpp_name", &mstch_cpp2_type::cpp_name},
409             {"type:cpp_type", &mstch_cpp2_type::cpp_type},
410             {"type:cpp_standard_type", &mstch_cpp2_type::cpp_standard_type},
411             {"type:cpp_adapter", &mstch_cpp2_type::cpp_adapter},
412             {"type:raw_binary?", &mstch_cpp2_type::raw_binary},
413             {"type:raw_string_or_binary?",
414              &mstch_cpp2_type::raw_string_or_binary},
415             {"type:string_or_binary?", &mstch_cpp2_type::is_string_or_binary},
416             {"type:resolved_cpp_type", &mstch_cpp2_type::resolved_cpp_type},
417             {"type:cpp_template", &mstch_cpp2_type::cpp_template},
418             {"type:cpp_indirection?", &mstch_cpp2_type::cpp_indirection},
419             {"type:non_empty_struct?", &mstch_cpp2_type::is_non_empty_struct},
420             {"type:namespace_cpp2", &mstch_cpp2_type::namespace_cpp2},
421             {"type:cpp_declare_hash", &mstch_cpp2_type::cpp_declare_hash},
422             {"type:cpp_declare_equal_to",
423              &mstch_cpp2_type::cpp_declare_equal_to},
424             {"type:type_class", &mstch_cpp2_type::type_class},
425             {"type:type_class_with_indirection",
426              &mstch_cpp2_type::type_class_with_indirection},
427             {"type:program_name", &mstch_cpp2_type::program_name},
428             {"type:cpp_use_allocator?", &mstch_cpp2_type::cpp_use_allocator},
429         });
430     register_has_option(
431         "type:sync_methods_return_try?", "sync_methods_return_try");
432   }
get_type_namespace(t_program const * program)433   std::string get_type_namespace(t_program const* program) override {
434     return cpp2::get_gen_namespace(*program);
435   }
resolves_to_base()436   mstch::node resolves_to_base() { return resolved_type_->is_base_type(); }
resolves_to_integral()437   mstch::node resolves_to_integral() {
438     return resolved_type_->is_byte() || resolved_type_->is_any_int();
439   }
resolves_to_base_or_enum()440   mstch::node resolves_to_base_or_enum() {
441     return resolved_type_->is_base_type() || resolved_type_->is_enum();
442   }
resolves_to_container()443   mstch::node resolves_to_container() { return resolved_type_->is_container(); }
resolves_to_container_or_struct()444   mstch::node resolves_to_container_or_struct() {
445     return resolved_type_->is_container() || resolved_type_->is_struct() ||
446         resolved_type_->is_xception();
447   }
resolves_to_container_or_enum()448   mstch::node resolves_to_container_or_enum() {
449     return resolved_type_->is_container() || resolved_type_->is_enum();
450   }
resolves_to_complex_return()451   mstch::node resolves_to_complex_return() {
452     if (resolved_type_->is_service()) {
453       return false;
454     }
455     return resolved_type_->is_container() ||
456         resolved_type_->is_string_or_binary() || resolved_type_->is_struct() ||
457         resolved_type_->is_xception();
458   }
resolves_to_fixed_size()459   mstch::node resolves_to_fixed_size() {
460     return resolved_type_->is_bool() || resolved_type_->is_byte() ||
461         resolved_type_->is_any_int() || resolved_type_->is_enum() ||
462         resolved_type_->is_floating_point();
463   }
resolves_to_enum()464   mstch::node resolves_to_enum() { return resolved_type_->is_enum(); }
transitively_refers_to_struct()465   mstch::node transitively_refers_to_struct() {
466     // fast path is unnecessary but may avoid allocations
467     if (resolved_type_->is_struct()) {
468       return true;
469     }
470     if (!resolved_type_->is_container()) {
471       return false;
472     }
473     // type is a container: traverse (breadthwise, but could be depthwise)
474     std::queue<t_type const*> queue;
475     queue.push(resolved_type_);
476     while (!queue.empty()) {
477       auto next = queue.front();
478       queue.pop();
479       if (next->is_struct()) {
480         return true;
481       }
482       if (!next->is_container()) {
483         continue;
484       }
485       if (false) {
486       } else if (next->is_list()) {
487         queue.push(static_cast<t_list const*>(next)->get_elem_type());
488       } else if (next->is_set()) {
489         queue.push(static_cast<t_set const*>(next)->get_elem_type());
490       } else if (next->is_map()) {
491         queue.push(static_cast<t_map const*>(next)->get_key_type());
492         queue.push(static_cast<t_map const*>(next)->get_val_type());
493       } else {
494         assert(false);
495       }
496     }
497     return false;
498   }
cpp_name()499   mstch::node cpp_name() { return cpp2::get_name(type_); }
cpp_type()500   mstch::node cpp_type() { return context_->resolver().get_type_name(type_); }
cpp_standard_type()501   mstch::node cpp_standard_type() {
502     return context_->resolver().get_standard_type_name(type_);
503   }
cpp_adapter()504   mstch::node cpp_adapter() {
505     if (const auto* adapter =
506             gen::cpp::type_resolver::find_first_adapter(type_)) {
507       return *adapter;
508     }
509     return {};
510   }
raw_binary()511   mstch::node raw_binary() {
512     return resolved_type_->is_binary() && !is_adapted();
513   }
raw_string_or_binary()514   mstch::node raw_string_or_binary() {
515     return resolved_type_->is_string_or_binary() && !is_adapted();
516   }
resolved_cpp_type()517   mstch::node resolved_cpp_type() { return cpp2::get_type(resolved_type_); }
is_string_or_binary()518   mstch::node is_string_or_binary() {
519     return resolved_type_->is_string_or_binary();
520   }
cpp_template()521   mstch::node cpp_template() { return get_cpp_template(type_); }
cpp_indirection()522   mstch::node cpp_indirection() {
523     return resolved_type_->has_annotation("cpp.indirection");
524   }
cpp_declare_hash()525   mstch::node cpp_declare_hash() {
526     return resolved_type_->has_annotation(
527         {"cpp.declare_hash", "cpp2.declare_hash"});
528   }
cpp_declare_equal_to()529   mstch::node cpp_declare_equal_to() {
530     return resolved_type_->has_annotation(
531         {"cpp.declare_equal_to", "cpp2.declare_equal_to"});
532   }
cpp_use_allocator()533   mstch::node cpp_use_allocator() {
534     return resolved_type_->has_annotation("cpp.use_allocator") ||
535         type_->has_annotation("cpp.use_allocator");
536   }
is_non_empty_struct()537   mstch::node is_non_empty_struct() {
538     auto as_struct = dynamic_cast<t_struct const*>(resolved_type_);
539     return as_struct && as_struct->has_fields();
540   }
namespace_cpp2()541   mstch::node namespace_cpp2() {
542     return t_mstch_cpp2_generator::get_namespace_array(type_->program());
543   }
type_class()544   mstch::node type_class() { return cpp2::get_gen_type_class(*resolved_type_); }
type_class_with_indirection()545   mstch::node type_class_with_indirection() {
546     return cpp2::get_gen_type_class_with_indirection(*resolved_type_);
547   }
program_name()548   mstch::node program_name() {
549     std::string name;
550     if (auto prog = type_->program()) {
551       name = prog->name();
552     }
553     return name;
554   }
555 
556  private:
557   std::shared_ptr<cpp2_generator_context> context_;
558 
is_adapted() const559   bool is_adapted() const {
560     return gen::cpp::type_resolver::find_first_adapter(type_) != nullptr;
561   }
562 };
563 
564 class mstch_cpp2_field : public mstch_field {
565  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)566   mstch_cpp2_field(
567       t_field const* field,
568       std::shared_ptr<mstch_generators const> generators,
569       std::shared_ptr<mstch_cache> cache,
570       ELEMENT_POSITION const pos,
571       int32_t index,
572       field_generator_context const* field_context,
573       std::shared_ptr<cpp2_generator_context> context)
574       : mstch_field(
575             field,
576             std::move(generators),
577             std::move(cache),
578             pos,
579             index,
580             field_context),
581         context_(std::move(context)) {
582     register_methods(
583         this,
584         {
585             {"field:name_hash", &mstch_cpp2_field::name_hash},
586             {"field:index_plus_one", &mstch_cpp2_field::index_plus_one},
587             {"field:has_isset?", &mstch_cpp2_field::has_isset},
588             {"field:isset_index", &mstch_cpp2_field::isset_index},
589             {"field:cpp_name", &mstch_cpp2_field::cpp_name},
590             {"field:cpp_storage_name", &mstch_cpp2_field::cpp_storage_name},
591             {"field:cpp_storage_type", &mstch_cpp2_field::cpp_storage_type},
592             {"field:cpp_deprecated_accessor_type",
593              &mstch_cpp2_field::cpp_deprecated_accessor_type},
594             {"field:has_deprecated_accessors?",
595              &mstch_cpp2_field::has_deprecated_accessors},
596             {"field:next_field_key", &mstch_cpp2_field::next_field_key},
597             {"field:prev_field_key", &mstch_cpp2_field::prev_field_key},
598             {"field:next_field_type", &mstch_cpp2_field::next_field_type},
599             {"field:non_opt_cpp_ref?", &mstch_cpp2_field::non_opt_cpp_ref},
600             {"field:cpp_ref?", &mstch_cpp2_field::cpp_ref},
601             {"field:cpp_ref_unique?", &mstch_cpp2_field::cpp_ref_unique},
602             {"field:cpp_ref_shared?", &mstch_cpp2_field::cpp_ref_shared},
603             {"field:cpp_ref_shared_const?",
604              &mstch_cpp2_field::cpp_ref_shared_const},
605             {"field:cpp_adapter", &mstch_cpp2_field::cpp_adapter},
606             {"field:zero_copy_arg", &mstch_cpp2_field::zero_copy_arg},
607             {"field:cpp_noncopyable?", &mstch_cpp2_field::cpp_noncopyable},
608             {"field:enum_has_value", &mstch_cpp2_field::enum_has_value},
609             {"field:terse_writes?", &mstch_cpp2_field::terse_writes},
610             {"field:fatal_annotations?",
611              &mstch_cpp2_field::has_fatal_annotations},
612             {"field:fatal_annotations", &mstch_cpp2_field::fatal_annotations},
613             {"field:fatal_required_qualifier",
614              &mstch_cpp2_field::fatal_required_qualifier},
615             {"field:visibility", &mstch_cpp2_field::visibility},
616             {"field:metadata_name", &mstch_cpp2_field::metadata_name},
617             {"field:lazy?", &mstch_cpp2_field::lazy},
618             {"field:lazy_ref?", &mstch_cpp2_field::lazy_ref},
619             {"field:boxed_ref?", &mstch_cpp2_field::boxed_ref},
620             {"field:transitively_refers_to_unique?",
621              &mstch_cpp2_field::transitively_refers_to_unique},
622             {"field:eligible_for_storage_name_mangling?",
623              &mstch_cpp2_field::eligible_for_storage_name_mangling},
624         });
625   }
name_hash()626   mstch::node name_hash() {
627     return "__fbthrift_hash_" + cpp2::sha256_hex(field_->get_name());
628   }
index_plus_one()629   mstch::node index_plus_one() { return std::to_string(index_ + 1); }
isset_index()630   mstch::node isset_index() {
631     assert(field_context_);
632     return field_context_->isset_index;
633   }
cpp_name()634   mstch::node cpp_name() { return cpp2::get_name(field_); }
cpp_storage_name()635   mstch::node cpp_storage_name() {
636     if (!is_eligible_for_storage_name_mangling()) {
637       return cpp2::get_name(field_);
638     }
639 
640     return mangle_field_name(cpp2::get_name(field_));
641   }
cpp_storage_type()642   mstch::node cpp_storage_type() {
643     return context_->resolver().get_storage_type_name(field_);
644   }
eligible_for_storage_name_mangling()645   mstch::node eligible_for_storage_name_mangling() {
646     return is_eligible_for_storage_name_mangling();
647   }
cpp_deprecated_accessor_type()648   mstch::node cpp_deprecated_accessor_type() {
649     // The type to use for pre-field_ref backwards compatiblity functions.
650     // These leaked the internal storage type directly.
651     //
652     // TODO(afuller): Remove this once all non-field_ref based accessors have
653     // been removed.
654     return context_->resolver().get_storage_type_name(field_);
655   }
has_deprecated_accessors()656   mstch::node has_deprecated_accessors() {
657     return !cpp2::is_explicit_ref(field_) && !cpp2::is_lazy(field_) &&
658         !gen::cpp::type_resolver::find_first_adapter(field_) &&
659         !has_option("no_getters_setters");
660   }
cpp_ref()661   mstch::node cpp_ref() { return cpp2::is_explicit_ref(field_); }
non_opt_cpp_ref()662   mstch::node non_opt_cpp_ref() {
663     return cpp2::is_explicit_ref(field_) &&
664         field_->get_req() != t_field::e_req::optional;
665   }
lazy()666   mstch::node lazy() { return cpp2::is_lazy(field_); }
lazy_ref()667   mstch::node lazy_ref() { return cpp2::is_lazy_ref(field_); }
boxed_ref()668   mstch::node boxed_ref() {
669     return gen::cpp::find_ref_type(*field_) == gen::cpp::reference_type::boxed;
670   }
transitively_refers_to_unique()671   mstch::node transitively_refers_to_unique() {
672     return cpp2::field_transitively_refers_to_unique(field_);
673   }
cpp_ref_unique()674   mstch::node cpp_ref_unique() { return cpp2::is_unique_ref(field_); }
cpp_ref_shared()675   mstch::node cpp_ref_shared() {
676     return gen::cpp::find_ref_type(*field_) ==
677         gen::cpp::reference_type::shared_mutable;
678   }
cpp_ref_shared_const()679   mstch::node cpp_ref_shared_const() {
680     return gen::cpp::find_ref_type(*field_) ==
681         gen::cpp::reference_type::shared_const;
682   }
cpp_adapter()683   mstch::node cpp_adapter() {
684     if (const std::string* adapter =
685             gen::cpp::type_resolver::find_first_adapter(field_)) {
686       return *adapter;
687     }
688     return {};
689   }
cpp_noncopyable()690   mstch::node cpp_noncopyable() {
691     return field_->get_type()->has_annotation(
692         {"cpp.noncopyable", "cpp2.noncopyable"});
693   }
enum_has_value()694   mstch::node enum_has_value() {
695     if (auto enm = dynamic_cast<t_enum const*>(field_->get_type())) {
696       auto const* const_value = field_->get_value();
697       using cv = t_const_value::t_const_value_type;
698       if (const_value->get_type() == cv::CV_INTEGER) {
699         auto* enm_value = enm->find_value(const_value->get_integer());
700         if (enm_value != nullptr) {
701           return generators_->enum_value_generator_->generate(
702               enm_value, generators_, cache_, pos_);
703         }
704       }
705     }
706     return mstch::node();
707   }
prev_field_key()708   mstch::node prev_field_key() {
709     assert(field_context_ && field_context_->prev);
710     return field_context_->prev->get_key();
711   }
next_field_key()712   mstch::node next_field_key() {
713     assert(field_context_ && field_context_->next);
714     return field_context_->next->get_key();
715   }
next_field_type()716   mstch::node next_field_type() {
717     assert(field_context_ && field_context_->next);
718     return field_context_->next
719         ? generators_->type_generator_->generate(
720               field_context_->next->get_type(), generators_, cache_, pos_)
721         : mstch::node("");
722   }
terse_writes()723   mstch::node terse_writes() {
724     // Add terse writes for unqualified fields when comparison is cheap:
725     // (e.g. i32/i64, empty strings/list/map)
726     auto t = field_->get_type()->get_true_type();
727     return has_option("terse_writes") &&
728         field_->get_req() != t_field::e_req::optional &&
729         field_->get_req() != t_field::e_req::required &&
730         (is_cpp_ref_unique_either(field_) ||
731          (!t->is_struct() && !t->is_xception()));
732   }
zero_copy_arg()733   mstch::node zero_copy_arg() {
734     switch (field_->get_type()->get_type_value()) {
735       case t_type::type::t_binary:
736       case t_type::type::t_struct:
737         return std::string("true");
738       default:
739         return std::string("false");
740     }
741   }
has_fatal_annotations()742   mstch::node has_fatal_annotations() {
743     return get_fatal_annotations(field_->annotations()).size() > 0;
744   }
has_isset()745   mstch::node has_isset() { return cpp2::field_has_isset(field_); }
fatal_annotations()746   mstch::node fatal_annotations() {
747     return generate_annotations(get_fatal_annotations(field_->annotations()));
748   }
fatal_required_qualifier()749   mstch::node fatal_required_qualifier() {
750     switch (field_->get_req()) {
751       case t_field::e_req::required:
752         return std::string("required");
753       case t_field::e_req::optional:
754         return std::string("optional");
755       case t_field::e_req::opt_in_req_out:
756         return std::string("required_of_writer");
757       default:
758         throw std::runtime_error("unknown required qualifier");
759     }
760   }
761 
visibility()762   mstch::node visibility() {
763     return std::string(is_private() ? "private" : "public");
764   }
765 
metadata_name()766   mstch::node metadata_name() {
767     auto key = field_->get_key();
768     auto suffix = key >= 0 ? std::to_string(key) : "_" + std::to_string(-key);
769     return field_->get_name() + "_" + suffix;
770   }
771 
772  private:
is_private() const773   bool is_private() const {
774     auto req = field_->get_req();
775     bool isPrivate = true;
776     if (cpp2::is_lazy(field_)) {
777       // Lazy field has to be private.
778     } else if (cpp2::is_ref(field_)) {
779       if (gen::cpp::find_ref_type(*field_) != gen::cpp::reference_type::boxed) {
780         isPrivate = has_option("deprecated_private_fields_for_cpp_ref");
781       }
782     } else if (req == t_field::e_req::required) {
783       isPrivate = has_option("deprecated_private_required_fields");
784     } else if (req == t_field::e_req::optional) {
785       // Optional fields are always private.
786     } else if (req == t_field::e_req::opt_in_req_out) {
787       isPrivate = !has_option("deprecated_public_fields");
788     }
789     return isPrivate;
790   }
791 
is_eligible_for_storage_name_mangling() const792   bool is_eligible_for_storage_name_mangling() const {
793     const auto* strct = field_context_->strct;
794 
795     if (strct->is_union() || strct->is_exception()) {
796       return false;
797     }
798 
799     if (strct->has_annotation({"cpp.methods", "cpp2.methods"})) {
800       return false;
801     }
802 
803     return is_private();
804   }
805 
806   std::shared_ptr<cpp2_generator_context> context_;
807 };
808 
809 class mstch_cpp2_struct : public mstch_struct {
810  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)811   mstch_cpp2_struct(
812       t_struct const* strct,
813       std::shared_ptr<mstch_generators const> generators,
814       std::shared_ptr<mstch_cache> cache,
815       ELEMENT_POSITION const pos,
816       std::shared_ptr<cpp2_generator_context> context)
817       : mstch_struct(strct, std::move(generators), std::move(cache), pos),
818         context_(std::move(context)) {
819     register_methods(
820         this,
821         {
822             {"struct:fields_size", &mstch_cpp2_struct::fields_size},
823             {"struct:explicitly_constructed_fields",
824              &mstch_cpp2_struct::explicitly_constructed_fields},
825             {"struct:fields_in_key_order",
826              &mstch_cpp2_struct::fields_in_key_order},
827             {"struct:fields_in_layout_order",
828              &mstch_cpp2_struct::fields_in_layout_order},
829             {"struct:is_struct_orderable?",
830              &mstch_cpp2_struct::is_struct_orderable},
831             {"struct:nondefault_copy_ctor_and_assignment?",
832              &mstch_cpp2_struct::nondefault_copy_ctor_and_assignment},
833             {"struct:cpp_methods", &mstch_cpp2_struct::cpp_methods},
834             {"struct:cpp_declare_hash", &mstch_cpp2_struct::cpp_declare_hash},
835             {"struct:cpp_declare_equal_to",
836              &mstch_cpp2_struct::cpp_declare_equal_to},
837             {"struct:cpp_noncopyable", &mstch_cpp2_struct::cpp_noncopyable},
838             {"struct:cpp_noncomparable", &mstch_cpp2_struct::cpp_noncomparable},
839             {"struct:is_eligible_for_constexpr?",
840              &mstch_cpp2_struct::is_eligible_for_constexpr},
841             {"struct:virtual", &mstch_cpp2_struct::cpp_virtual},
842             {"struct:message", &mstch_cpp2_struct::message},
843             {"struct:isset_fields?", &mstch_cpp2_struct::has_isset_fields},
844             {"struct:isset_fields", &mstch_cpp2_struct::isset_fields},
845             {"struct:isset_fields_size", &mstch_cpp2_struct::isset_fields_size},
846             {"struct:packed_isset", &mstch_cpp2_struct::packed_isset},
847             {"struct:lazy_fields?", &mstch_cpp2_struct::has_lazy_fields},
848             {"struct:indexing?", &mstch_cpp2_struct::indexing},
849             {"struct:write_lazy_field_checksum",
850              &mstch_cpp2_struct::write_lazy_field_checksum},
851             {"struct:is_large?", &mstch_cpp2_struct::is_large},
852             {"struct:fatal_annotations?",
853              &mstch_cpp2_struct::has_fatal_annotations},
854             {"struct:fatal_annotations", &mstch_cpp2_struct::fatal_annotations},
855             {"struct:legacy_type_id", &mstch_cpp2_struct::get_legacy_type_id},
856             {"struct:metadata_name", &mstch_cpp2_struct::metadata_name},
857             {"struct:mixin_fields", &mstch_cpp2_struct::mixin_fields},
858             {"struct:num_union_members",
859              &mstch_cpp2_struct::get_num_union_members},
860             {"struct:cpp_allocator", &mstch_cpp2_struct::cpp_allocator},
861             {"struct:cpp_allocator_via", &mstch_cpp2_struct::cpp_allocator_via},
862             {"struct:cpp_data_method?", &mstch_cpp2_struct::cpp_data_method},
863             {"struct:cpp_frozen2_exclude?",
864              &mstch_cpp2_struct::cpp_frozen2_exclude},
865         });
866   }
fields_size()867   mstch::node fields_size() { return std::to_string(strct_->fields().size()); }
explicitly_constructed_fields()868   mstch::node explicitly_constructed_fields() {
869     // Filter fields according to the following criteria:
870     // Get all enums
871     // Get all base_types but empty strings
872     // Get all non-empty structs and containers
873     // Get all non-optional references with basetypes, enums,
874     // non-empty structs, and containers
875     std::vector<t_field const*> filtered_fields;
876     for (auto const* field : get_members_in_layout_order()) {
877       const t_type* type = field->get_type()->get_true_type();
878       // Filter out all optional references.
879       if (cpp2::is_explicit_ref(field) &&
880           field->get_req() == t_field::e_req::optional) {
881         continue;
882       }
883       if (type->is_enum() ||
884           (type->is_base_type() && !type->is_string_or_binary()) ||
885           (type->is_string_or_binary() && field->get_value() != nullptr) ||
886           (type->is_container() && field->get_value() != nullptr &&
887            !field->get_value()->is_empty()) ||
888           (type->is_struct() &&
889            (strct_ != dynamic_cast<t_struct const*>(type)) &&
890            ((field->get_value() && !field->get_value()->is_empty()) ||
891             (cpp2::is_explicit_ref(field) &&
892              field->get_req() != t_field::e_req::optional))) ||
893           (type->is_container() && cpp2::is_explicit_ref(field) &&
894            field->get_req() != t_field::e_req::optional) ||
895           (type->is_base_type() && cpp2::is_explicit_ref(field) &&
896            field->get_req() != t_field::e_req::optional)) {
897         filtered_fields.push_back(field);
898       }
899     }
900     return generate_fields(filtered_fields);
901   }
902 
mixin_fields()903   mstch::node mixin_fields() {
904     mstch::array fields;
905     for (auto i : cpp2::get_mixins_and_members(*strct_)) {
906       fields.push_back(mstch::map{
907           {"mixin:name", i.mixin->get_name()},
908           {"mixin:field_name", i.member->get_name()}});
909     }
910     return fields;
911   }
912 
is_struct_orderable()913   mstch::node is_struct_orderable() {
914     return context_->is_orderable(*strct_) &&
915         !strct_->has_annotation("no_default_comparators");
916   }
nondefault_copy_ctor_and_assignment()917   mstch::node nondefault_copy_ctor_and_assignment() {
918     for (auto const& f : strct_->fields()) {
919       if (cpp2::field_transitively_refers_to_unique(&f) || cpp2::is_lazy(&f) ||
920           gen::cpp::type_resolver::find_first_adapter(&f)) {
921         return true;
922       }
923     }
924     return false;
925   }
cpp_methods()926   mstch::node cpp_methods() {
927     return strct_->get_annotation({"cpp.methods", "cpp2.methods"});
928   }
cpp_declare_hash()929   mstch::node cpp_declare_hash() {
930     return strct_->has_annotation({"cpp.declare_hash", "cpp2.declare_hash"});
931   }
cpp_declare_equal_to()932   mstch::node cpp_declare_equal_to() {
933     return strct_->has_annotation(
934         {"cpp.declare_equal_to", "cpp2.declare_equal_to"});
935   }
cpp_noncopyable()936   mstch::node cpp_noncopyable() {
937     if (strct_->has_annotation({"cpp.noncopyable", "cpp2.noncopyable"})) {
938       return true;
939     }
940     bool result = false;
941     cpp2::for_each_transitive_field(strct_, [&result](const t_field* field) {
942       if (!field->get_type()->has_annotation(
943               {"cpp.noncopyable", "cpp2.noncopyable"})) {
944         return true;
945       }
946       switch (gen::cpp::find_ref_type(*field)) {
947         case gen::cpp::reference_type::shared_const:
948         case gen::cpp::reference_type::shared_mutable: {
949           return true;
950         }
951         case gen::cpp::reference_type::boxed:
952         case gen::cpp::reference_type::none:
953         case gen::cpp::reference_type::unique:
954         case gen::cpp::reference_type::unrecognized: {
955           break;
956         }
957       }
958       result = true;
959       return false;
960     });
961     return result;
962   }
cpp_noncomparable()963   mstch::node cpp_noncomparable() {
964     return strct_->has_annotation({"cpp.noncomparable", "cpp2.noncomparable"});
965   }
is_eligible_for_constexpr()966   mstch::node is_eligible_for_constexpr() {
967     return is_eligible_for_constexpr_(strct_) ||
968         strct_->has_annotation({"cpp.methods", "cpp2.methods"});
969   }
cpp_virtual()970   mstch::node cpp_virtual() {
971     return strct_->has_annotation({"cpp.virtual", "cpp2.virtual"});
972   }
message()973   mstch::node message() {
974     return strct_->is_exception() ? strct_->get_annotation("message")
975                                   : mstch::node();
976   }
cpp_allocator()977   mstch::node cpp_allocator() {
978     return strct_->get_annotation("cpp.allocator");
979   }
cpp_data_method()980   mstch::node cpp_data_method() {
981     return strct_->has_annotation("cpp.internal.deprecated._data.method");
982   }
cpp_frozen2_exclude()983   mstch::node cpp_frozen2_exclude() {
984     return strct_->has_annotation("cpp.frozen2_exclude");
985   }
cpp_allocator_via()986   mstch::node cpp_allocator_via() {
987     if (const auto* name =
988             strct_->find_annotation_or_null("cpp.allocator_via")) {
989       for (const auto& field : strct_->fields()) {
990         if (cpp2::get_name(&field) == *name) {
991           return mangle_field_name(*name);
992         }
993       }
994       throw std::runtime_error("No cpp.allocator_via field \"" + *name + "\"");
995     }
996     return std::string();
997   }
has_lazy_fields()998   mstch::node has_lazy_fields() {
999     for (const auto& field : strct_->get_members()) {
1000       if (cpp2::is_lazy(field)) {
1001         return true;
1002       }
1003     }
1004     return false;
1005   }
indexing()1006   mstch::node indexing() { return has_lazy_fields(); }
write_lazy_field_checksum()1007   mstch::node write_lazy_field_checksum() {
1008     if (strct_->find_structured_annotation_or_null(
1009             "facebook.com/thrift/annotation/cpp/DisableLazyChecksum")) {
1010       return std::string("false");
1011     }
1012 
1013     return std::string("true");
1014   }
has_isset_fields()1015   mstch::node has_isset_fields() {
1016     for (const auto& field : strct_->fields()) {
1017       if (cpp2::field_has_isset(&field)) {
1018         return true;
1019       }
1020     }
1021     return false;
1022   }
isset_fields()1023   mstch::node isset_fields() {
1024     std::vector<t_field const*> fields;
1025     for (const auto& field : strct_->fields()) {
1026       if (cpp2::field_has_isset(&field)) {
1027         fields.push_back(&field);
1028       }
1029     }
1030     if (fields.empty()) {
1031       return mstch::node();
1032     }
1033     return generate_fields(fields);
1034   }
isset_fields_size()1035   mstch::node isset_fields_size() {
1036     std::size_t size = 0;
1037     for (const auto& field : strct_->fields()) {
1038       if (cpp2::field_has_isset(&field)) {
1039         size++;
1040       }
1041     }
1042     return std::to_string(size);
1043   }
packed_isset()1044   mstch::node packed_isset() { return cpp2::packed_isset(*strct_) != nullptr; }
1045 
is_large()1046   mstch::node is_large() {
1047     // Outline constructors and destructors if the struct has
1048     // enough members and at least one has a non-trivial destructor
1049     // (involving at least a branch and a likely deallocation).
1050     // TODO(ott): Support unions.
1051     if (strct_->is_exception()) {
1052       return true;
1053     }
1054     constexpr size_t kLargeStructThreshold = 4;
1055     if (strct_->fields().size() <= kLargeStructThreshold) {
1056       return false;
1057     }
1058     for (auto const& field : strct_->fields()) {
1059       auto const* resolved_typedef = field.type()->get_true_type();
1060       if (cpp2::is_ref(&field) || resolved_typedef->is_string_or_binary() ||
1061           resolved_typedef->is_container()) {
1062         return true;
1063       }
1064     }
1065     return false;
1066   }
has_fatal_annotations()1067   mstch::node has_fatal_annotations() {
1068     return get_fatal_annotations(strct_->annotations()).size() > 0;
1069   }
fatal_annotations()1070   mstch::node fatal_annotations() {
1071     return generate_annotations(get_fatal_annotations(strct_->annotations()));
1072   }
get_legacy_type_id()1073   mstch::node get_legacy_type_id() {
1074     return std::to_string(strct_->get_type_id());
1075   }
metadata_name()1076   mstch::node metadata_name() {
1077     return strct_->program()->name() + "_" + strct_->get_name();
1078   }
1079 
get_num_union_members()1080   mstch::node get_num_union_members() {
1081     if (!strct_->is_union()) {
1082       throw std::runtime_error("not a union struct");
1083     }
1084     return std::to_string(strct_->fields().size());
1085   }
1086 
1087  protected:
1088   // Computes the alignment of field on the target platform.
1089   // Returns 0 if cannot compute the alignment.
compute_alignment(t_field const * field)1090   static size_t compute_alignment(t_field const* field) {
1091     if (cpp2::is_ref(field)) {
1092       return 8;
1093     }
1094     t_type const* type = field->get_type();
1095     switch (type->get_type_value()) {
1096       case t_type::type::t_bool:
1097       case t_type::type::t_byte:
1098         return 1;
1099       case t_type::type::t_i16:
1100         return 2;
1101       case t_type::type::t_i32:
1102       case t_type::type::t_float:
1103       case t_type::type::t_enum:
1104         return 4;
1105       case t_type::type::t_i64:
1106       case t_type::type::t_double:
1107       case t_type::type::t_string:
1108       case t_type::type::t_binary:
1109       case t_type::type::t_list:
1110       case t_type::type::t_set:
1111       case t_type::type::t_map:
1112         return 8;
1113       case t_type::type::t_struct: {
1114         size_t align = 1;
1115         const size_t kMaxAlign = 8;
1116         t_struct const* strct = dynamic_cast<t_struct const*>(type);
1117         if (!strct) {
1118           throw std::runtime_error(
1119               "cpp.minimize_padding requires struct definitions to be "
1120               "topologically sorted. Move definition of `" +
1121               type->get_name() + "` before its use in field `" +
1122               field->get_name() + "`.");
1123         }
1124         for (auto const& field : strct->fields()) {
1125           size_t field_align = compute_alignment(&field);
1126           if (field_align == 0) {
1127             // Unknown alignment, bail out.
1128             return 0;
1129           }
1130           align = std::max(align, field_align);
1131           if (align == kMaxAlign) {
1132             // No need to continue because the struct already has the maximum
1133             // alignment.
1134             return align;
1135           }
1136         }
1137         // The __isset member that is generated in the presence of non-required
1138         // fields doesn't affect the alignment, because, having only bool
1139         // fields, it has the alignments of 1.
1140         return align;
1141       }
1142       default:
1143         return 0;
1144     }
1145   }
1146 
1147   // Returns the struct members reordered to minimize padding if the
1148   // cpp.minimize_padding annotation is specified.
get_members_in_layout_order()1149   const std::vector<const t_field*>& get_members_in_layout_order() {
1150     if (strct_->fields().size() == fields_in_layout_order_.size()) {
1151       // Already reordered.
1152       return fields_in_layout_order_;
1153     }
1154 
1155     if (!strct_->has_annotation("cpp.minimize_padding")) {
1156       return fields_in_layout_order_ = strct_->fields().copy();
1157     }
1158 
1159     // Compute field alignments.
1160     struct FieldAlign {
1161       const t_field* field = nullptr;
1162       size_t align = 0;
1163     };
1164     std::vector<FieldAlign> field_alignments;
1165     field_alignments.reserve(strct_->fields().size());
1166     for (const auto& field : strct_->fields()) {
1167       auto align = compute_alignment(&field);
1168       if (align == 0) {
1169         // Unknown alignment, don't reorder anything.
1170         return fields_in_layout_order_ = strct_->fields().copy();
1171       }
1172       field_alignments.push_back(FieldAlign{&field, align});
1173     }
1174 
1175     // Sort by decreasing alignment using stable sort to avoid unnecessary
1176     // reordering.
1177     std::stable_sort(
1178         field_alignments.begin(),
1179         field_alignments.end(),
1180         [](auto const& lhs, auto const& rhs) { return lhs.align > rhs.align; });
1181 
1182     // Construct the reordered field vector.
1183     fields_in_layout_order_.reserve(strct_->fields().size());
1184     std::transform(
1185         field_alignments.begin(),
1186         field_alignments.end(),
1187         std::back_inserter(fields_in_layout_order_),
1188         [](FieldAlign const& fa) { return fa.field; });
1189     return fields_in_layout_order_;
1190   }
1191 
fields_in_layout_order()1192   mstch::node fields_in_layout_order() {
1193     return generate_fields(get_members_in_layout_order());
1194   }
1195 
1196   // Returns the struct members ordered by the key.
get_members_in_key_order()1197   const std::vector<const t_field*>& get_members_in_key_order() {
1198     if (strct_->fields().size() == fields_in_key_order_.size()) {
1199       // Already reordered.
1200       return fields_in_key_order_;
1201     }
1202 
1203     fields_in_key_order_ = strct_->fields().copy();
1204     // Sort by increasing key.
1205     std::sort(
1206         fields_in_key_order_.begin(),
1207         fields_in_key_order_.end(),
1208         [](const auto* lhs, const auto* rhs) {
1209           return lhs->get_key() < rhs->get_key();
1210         });
1211 
1212     return fields_in_key_order_;
1213   }
1214 
fields_in_key_order()1215   mstch::node fields_in_key_order() {
1216     return generate_fields(get_members_in_key_order());
1217   }
1218 
1219   std::shared_ptr<cpp2_generator_context> context_;
1220 
1221   std::vector<const t_field*> fields_in_key_order_;
1222   std::vector<const t_field*> fields_in_layout_order_;
1223   cpp2::is_eligible_for_constexpr is_eligible_for_constexpr_;
1224 };
1225 
1226 class mstch_cpp2_function : public mstch_function {
1227  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)1228   mstch_cpp2_function(
1229       t_function const* function,
1230       std::shared_ptr<mstch_generators const> generators,
1231       std::shared_ptr<mstch_cache> cache,
1232       ELEMENT_POSITION const pos)
1233       : mstch_function(function, std::move(generators), std::move(cache), pos) {
1234     register_methods(
1235         this,
1236         {
1237             {"function:coroutine?", &mstch_cpp2_function::coroutine},
1238             {"function:eb", &mstch_cpp2_function::event_based},
1239             {"function:cpp_name", &mstch_cpp2_function::cpp_name},
1240             {"function:stack_arguments?",
1241              &mstch_cpp2_function::stack_arguments},
1242         });
1243   }
coroutine()1244   mstch::node coroutine() {
1245     return function_->has_annotation("cpp.coroutine") ||
1246         function_->is_interaction_member();
1247   }
event_based()1248   mstch::node event_based() {
1249     return function_->get_annotation("thread") == "eb";
1250   }
cpp_name()1251   mstch::node cpp_name() { return cpp2::get_name(function_); }
stack_arguments()1252   mstch::node stack_arguments() {
1253     return cpp2::is_stack_arguments(cache_->parsed_options_, *function_);
1254   }
1255 };
1256 
1257 class mstch_cpp2_service : public mstch_service {
1258  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)1259   mstch_cpp2_service(
1260       t_service const* service,
1261       std::shared_ptr<mstch_generators const> generators,
1262       std::shared_ptr<mstch_cache> cache,
1263       ELEMENT_POSITION const pos,
1264       int32_t split_id = 0,
1265       int32_t split_count = 1)
1266       : mstch_service(service, std::move(generators), std::move(cache), pos) {
1267     register_methods(
1268         this,
1269         {
1270             {"service:program_name", &mstch_cpp2_service::program_name},
1271             {"service:program_path", &mstch_cpp2_service::program_path},
1272             {"service:include_prefix", &mstch_cpp2_service::include_prefix},
1273             {"service:thrift_includes", &mstch_cpp2_service::thrift_includes},
1274             {"service:namespace_cpp2", &mstch_cpp2_service::namespace_cpp2},
1275             {"service:oneway_functions", &mstch_cpp2_service::oneway_functions},
1276             {"service:oneways?", &mstch_cpp2_service::has_oneway},
1277             {"service:cpp_includes", &mstch_cpp2_service::cpp_includes},
1278             {"service:metadata_name", &mstch_cpp2_service::metadata_name},
1279             {"service:parent_service_name",
1280              &mstch_cpp2_service::parent_service_name},
1281             {"service:reduced_client?", &mstch_service::is_interaction},
1282         });
1283 
1284     const auto all_functions = mstch_service::get_functions();
1285     for (size_t id = split_id; id < all_functions.size(); id += split_count) {
1286       functions_.push_back(all_functions[id]);
1287     }
1288   }
get_service_namespace(t_program const * program)1289   std::string get_service_namespace(t_program const* program) override {
1290     return t_mstch_cpp2_generator::get_cpp2_namespace(program);
1291   }
program_name()1292   mstch::node program_name() { return service_->program()->name(); }
program_path()1293   mstch::node program_path() { return service_->program()->path(); }
cpp_includes()1294   mstch::node cpp_includes() {
1295     return t_mstch_cpp2_generator::cpp_includes(service_->program());
1296   }
include_prefix()1297   mstch::node include_prefix() {
1298     return t_mstch_cpp2_generator::include_prefix(
1299         service_->program(), cache_->parsed_options_);
1300   }
thrift_includes()1301   mstch::node thrift_includes() {
1302     mstch::array a{};
1303     for (auto const* program : service_->program()->get_included_programs()) {
1304       a.push_back(generators_->program_generator_->generate_cached(
1305           program, generators_, cache_));
1306     }
1307     return a;
1308   }
namespace_cpp2()1309   mstch::node namespace_cpp2() {
1310     return t_mstch_cpp2_generator::get_namespace_array(service_->program());
1311   }
oneway_functions()1312   mstch::node oneway_functions() {
1313     std::vector<t_function const*> oneway_functions;
1314     for (auto const* function : get_functions()) {
1315       if (function->qualifier() == t_function_qualifier::one_way) {
1316         oneway_functions.push_back(function);
1317       }
1318     }
1319     return generate_functions(oneway_functions);
1320   }
has_oneway()1321   mstch::node has_oneway() {
1322     for (auto const* function : get_functions()) {
1323       if (function->qualifier() == t_function_qualifier::one_way) {
1324         return true;
1325       }
1326     }
1327     return false;
1328   }
metadata_name()1329   mstch::node metadata_name() {
1330     return service_->program()->name() + "_" + service_->get_name();
1331   }
parent_service_name()1332   mstch::node parent_service_name() {
1333     return cache_->parsed_options_.at("parent_service_name");
1334   }
1335 
1336  private:
get_functions() const1337   const std::vector<t_function*>& get_functions() const override {
1338     return functions_;
1339   }
1340 
1341   std::vector<t_function*> functions_;
1342 };
1343 
1344 class mstch_cpp2_annotation : public mstch_annotation {
1345  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)1346   mstch_cpp2_annotation(
1347       const std::string& key,
1348       annotation_value val,
1349       std::shared_ptr<mstch_generators const> generators,
1350       std::shared_ptr<mstch_cache> cache,
1351       ELEMENT_POSITION pos,
1352       int32_t index)
1353       : mstch_annotation(
1354             key, val, std::move(generators), std::move(cache), pos, index) {
1355     register_methods(
1356         this,
1357         {
1358             {"annotation:safe_key", &mstch_cpp2_annotation::safe_key},
1359             {"annotation:fatal_string", &mstch_cpp2_annotation::fatal_string},
1360         });
1361   }
safe_key()1362   mstch::node safe_key() { return get_fatal_string_short_id(key_); }
fatal_string()1363   mstch::node fatal_string() { return render_fatal_string(val_.value); }
1364 };
1365 
1366 class mstch_cpp2_const : public mstch_const {
1367  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)1368   mstch_cpp2_const(
1369       t_const const* cnst,
1370       t_const const* current_const,
1371       t_type const* expected_type,
1372       std::shared_ptr<mstch_generators const> generators,
1373       std::shared_ptr<mstch_cache> cache,
1374       ELEMENT_POSITION const pos,
1375       int32_t index,
1376       t_field const* field)
1377       : mstch_const(
1378             cnst,
1379             current_const,
1380             expected_type,
1381             std::move(generators),
1382             std::move(cache),
1383             pos,
1384             index,
1385             field) {
1386     register_methods(
1387         this,
1388         {
1389             {"constant:enum_value", &mstch_cpp2_const::enum_value},
1390             {"constant:cpp_name", &mstch_cpp2_const::cpp_name},
1391         });
1392   }
enum_value()1393   mstch::node enum_value() {
1394     if (cnst_->get_type()->is_enum()) {
1395       auto const* enm = static_cast<t_enum const*>(cnst_->get_type());
1396       auto const* enm_val = enm->find_value(cnst_->get_value()->get_integer());
1397       if (enm_val) {
1398         return enm_val->get_name();
1399       } else {
1400         return std::to_string(cnst_->get_value()->get_integer());
1401       }
1402     }
1403     return mstch::node();
1404   }
cpp_name()1405   mstch::node cpp_name() { return cpp2::get_name(field_); }
1406 };
1407 
1408 class mstch_cpp2_program : public mstch_program {
1409  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)1410   mstch_cpp2_program(
1411       t_program const* program,
1412       std::shared_ptr<mstch_generators const> generators,
1413       std::shared_ptr<mstch_cache> cache,
1414       ELEMENT_POSITION const pos,
1415       boost::optional<int32_t> split_id = boost::none)
1416       : mstch_program(program, std::move(generators), std::move(cache), pos),
1417         split_id_(split_id) {
1418     register_methods(
1419         this,
1420         {
1421             {"program:cpp_includes", &mstch_cpp2_program::cpp_includes},
1422             {"program:namespace_cpp2", &mstch_cpp2_program::namespace_cpp2},
1423             {"program:include_prefix", &mstch_cpp2_program::include_prefix},
1424             {"program:cpp_declare_hash?",
1425              &mstch_cpp2_program::cpp_declare_hash},
1426             {"program:thrift_includes", &mstch_cpp2_program::thrift_includes},
1427             {"program:frozen_packed?", &mstch_cpp2_program::frozen_packed},
1428             {"program:fatal_languages", &mstch_cpp2_program::fatal_languages},
1429             {"program:fatal_enums", &mstch_cpp2_program::fatal_enums},
1430             {"program:fatal_unions", &mstch_cpp2_program::fatal_unions},
1431             {"program:fatal_structs", &mstch_cpp2_program::fatal_structs},
1432             {"program:fatal_constants", &mstch_cpp2_program::fatal_constants},
1433             {"program:fatal_services", &mstch_cpp2_program::fatal_services},
1434             {"program:fatal_identifiers",
1435              &mstch_cpp2_program::fatal_identifiers},
1436             {"program:fatal_data_member",
1437              &mstch_cpp2_program::fatal_data_member},
1438         });
1439     register_has_option("program:tablebased?", "tablebased");
1440     register_has_option("program:no_metadata?", "no_metadata");
1441     register_has_option(
1442         "program:enforce_required?", "deprecated_enforce_required");
1443   }
get_program_namespace(t_program const * program)1444   std::string get_program_namespace(t_program const* program) override {
1445     return t_mstch_cpp2_generator::get_cpp2_namespace(program);
1446   }
1447 
alias_to_struct()1448   std::vector<const t_typedef*> alias_to_struct() {
1449     std::vector<const t_typedef*> result;
1450     for (const t_typedef* i : program_->typedefs()) {
1451       const t_type* alias = i->get_type();
1452       if (alias->is_typedef() && alias->has_annotation("cpp.type")) {
1453         const t_type* ttype = i->get_type()->get_true_type();
1454         if (ttype->is_struct() || ttype->is_xception()) {
1455           result.push_back(i);
1456         }
1457       }
1458     }
1459     return result;
1460   }
1461   template <typename Node>
collect_fatal_string_annotated(std::map<std::string,std::string> & fatal_strings,const Node * node)1462   void collect_fatal_string_annotated(
1463       std::map<std::string, std::string>& fatal_strings, const Node* node) {
1464     // TODO: extra copy
1465     auto cpp_name = cpp2::get_name(node);
1466     fatal_strings.emplace(get_fatal_string_short_id(cpp_name), cpp_name);
1467     auto hash = cpp2::sha256_hex(node->get_name());
1468     fatal_strings.emplace("__fbthrift_hash_" + hash, node->get_name());
1469     for (const auto& a : node->annotations()) {
1470       if (!is_annotation_blacklisted_in_fatal(a.first)) {
1471         fatal_strings.emplace(get_fatal_string_short_id(a.first), a.first);
1472       }
1473     }
1474   }
get_fatal_enum_names()1475   std::vector<std::string> get_fatal_enum_names() {
1476     std::vector<std::string> result;
1477     for (const auto* enm : program_->enums()) {
1478       result.push_back(get_fatal_string_short_id(enm->get_name()));
1479     }
1480     return result;
1481   }
get_fatal_union_names()1482   std::vector<std::string> get_fatal_union_names() {
1483     std::vector<std::string> result;
1484     for (const auto* obj : program_->objects()) {
1485       if (obj->is_union()) {
1486         result.push_back(get_fatal_string_short_id(obj->get_name()));
1487       }
1488     }
1489     return result;
1490   }
get_fatal_struct_names()1491   std::vector<std::string> get_fatal_struct_names() {
1492     std::vector<std::string> result;
1493     for (const auto* obj : program_->objects()) {
1494       if (!obj->is_union()) {
1495         result.push_back(get_fatal_string_short_id(obj->get_name()));
1496       }
1497     }
1498     // typedefs resolve to struct
1499     for (const t_typedef* i : alias_to_struct()) {
1500       result.push_back(get_fatal_string_short_id(i->get_name()));
1501     }
1502     return result;
1503   }
get_fatal_constant_names()1504   std::vector<std::string> get_fatal_constant_names() {
1505     std::vector<std::string> result;
1506     for (const auto* cnst : program_->consts()) {
1507       result.push_back(get_fatal_string_short_id(cnst->get_name()));
1508     }
1509     return result;
1510   }
get_fatal_service_names()1511   std::vector<std::string> get_fatal_service_names() {
1512     std::vector<std::string> result;
1513     for (const auto* service : program_->services()) {
1514       result.push_back(get_fatal_string_short_id(service->get_name()));
1515     }
1516     return result;
1517   }
to_fatal_string_array(const std::vector<std::string> && vec)1518   mstch::node to_fatal_string_array(const std::vector<std::string>&& vec) {
1519     mstch::array a;
1520     for (size_t i = 0; i < vec.size(); i++) {
1521       a.push_back(mstch::map{
1522           {"fatal_string:name", vec.at(i)},
1523           {"last?", i == vec.size() - 1},
1524       });
1525     }
1526     return mstch::map{{"fatal_strings:items", a}};
1527   }
1528 
namespace_cpp2()1529   mstch::node namespace_cpp2() {
1530     return t_mstch_cpp2_generator::get_namespace_array(program_);
1531   }
cpp_includes()1532   mstch::node cpp_includes() {
1533     return t_mstch_cpp2_generator::cpp_includes(program_);
1534   }
include_prefix()1535   mstch::node include_prefix() {
1536     return t_mstch_cpp2_generator::include_prefix(
1537         program_, cache_->parsed_options_);
1538   }
cpp_declare_hash()1539   mstch::node cpp_declare_hash() {
1540     bool cpp_declare_in_structs = std::any_of(
1541         program_->structs().begin(),
1542         program_->structs().end(),
1543         [](const auto* strct) {
1544           return strct->has_annotation(
1545               {"cpp.declare_hash", "cpp2.declare_hash"});
1546         });
1547     bool cpp_declare_in_typedefs = std::any_of(
1548         program_->typedefs().begin(),
1549         program_->typedefs().end(),
1550         [](const auto* typedf) {
1551           return typedf->get_type()->has_annotation(
1552               {"cpp.declare_hash", "cpp2.declare_hash"});
1553         });
1554     return cpp_declare_in_structs || cpp_declare_in_typedefs;
1555   }
thrift_includes()1556   mstch::node thrift_includes() {
1557     mstch::array a{};
1558     for (auto const* program : program_->get_included_programs()) {
1559       a.push_back(generators_->program_generator_->generate_cached(
1560           program, generators_, cache_));
1561     }
1562     return a;
1563   }
frozen_packed()1564   mstch::node frozen_packed() { return get_option("frozen") == "packed"; }
fatal_languages()1565   mstch::node fatal_languages() {
1566     mstch::array a;
1567     size_t size = program_->namespaces().size();
1568     size_t idx = 0;
1569     for (const auto& pair : program_->namespaces()) {
1570       a.push_back(mstch::map{
1571           {"language:safe_name", get_fatal_string_short_id(pair.first)},
1572           {"language:safe_namespace",
1573            get_fatal_namesoace_name_short_id(pair.first, pair.second)},
1574           {"last?", idx == size - 1},
1575       });
1576       ++idx;
1577     }
1578     return mstch::map{{"fatal_languages:items", a}};
1579   }
fatal_enums()1580   mstch::node fatal_enums() {
1581     return to_fatal_string_array(get_fatal_enum_names());
1582   }
fatal_unions()1583   mstch::node fatal_unions() {
1584     return to_fatal_string_array(get_fatal_union_names());
1585   }
fatal_structs()1586   mstch::node fatal_structs() {
1587     return to_fatal_string_array(get_fatal_struct_names());
1588   }
fatal_constants()1589   mstch::node fatal_constants() {
1590     return to_fatal_string_array(get_fatal_constant_names());
1591   }
fatal_services()1592   mstch::node fatal_services() {
1593     return to_fatal_string_array(get_fatal_service_names());
1594   }
fatal_identifiers()1595   mstch::node fatal_identifiers() {
1596     std::map<std::string, std::string> unique_names;
1597     unique_names.emplace(
1598         get_fatal_string_short_id(program_->name()), program_->name());
1599     // languages and namespaces
1600     for (const auto& pair : program_->namespaces()) {
1601       unique_names.emplace(get_fatal_string_short_id(pair.first), pair.first);
1602       unique_names.emplace(
1603           get_fatal_namesoace_name_short_id(pair.first, pair.second),
1604           get_fatal_namesoace(pair.first, pair.second));
1605     }
1606     // enums
1607     for (const auto* enm : program_->enums()) {
1608       collect_fatal_string_annotated(unique_names, enm);
1609       unique_names.emplace(
1610           get_fatal_string_short_id(enm->get_name()), enm->get_name());
1611       for (const auto& i : enm->get_enum_values()) {
1612         collect_fatal_string_annotated(unique_names, i);
1613       }
1614     }
1615     // structs, unions and exceptions
1616     for (const auto* obj : program_->objects()) {
1617       if (obj->is_union()) {
1618         // When generating <program_name>_fatal_union.h, we will generate
1619         // <union_name>_Type_enum_traits
1620         unique_names.emplace("Type", "Type");
1621       }
1622       collect_fatal_string_annotated(unique_names, obj);
1623       for (const auto& m : obj->fields()) {
1624         collect_fatal_string_annotated(unique_names, &m);
1625       }
1626     }
1627     // consts
1628     for (const auto* cnst : program_->consts()) {
1629       unique_names.emplace(
1630           get_fatal_string_short_id(cnst->get_name()), cnst->get_name());
1631     }
1632     // services
1633     for (const auto* service : program_->services()) {
1634       // function annotations are not currently included.
1635       unique_names.emplace(
1636           get_fatal_string_short_id(service->get_name()), service->get_name());
1637       for (const auto* f : service->get_functions()) {
1638         unique_names.emplace(
1639             get_fatal_string_short_id(f->get_name()), f->get_name());
1640         for (const auto& p : f->get_paramlist()->fields()) {
1641           unique_names.emplace(get_fatal_string_short_id(p.name()), p.name());
1642         }
1643       }
1644     }
1645     // typedefs resolve to struct
1646     for (const t_typedef* i : alias_to_struct()) {
1647       unique_names.emplace(
1648           get_fatal_string_short_id(i->get_name()), i->get_name());
1649     }
1650 
1651     mstch::array a;
1652     for (const auto& name : unique_names) {
1653       a.push_back(mstch::map{
1654           {"identifier:name", name.first},
1655           {"identifier:fatal_string", render_fatal_string(name.second)},
1656       });
1657     }
1658     return a;
1659   }
fatal_data_member()1660   mstch::node fatal_data_member() {
1661     std::unordered_set<std::string> fields;
1662     std::vector<const std::string*> ordered_fields;
1663     for (const t_struct* s : program_->objects()) {
1664       if (!s->is_union()) {
1665         for (const t_field& f : s->fields()) {
1666           auto result = fields.insert(cpp2::get_name(&f));
1667           if (result.second) {
1668             ordered_fields.push_back(&*result.first);
1669           }
1670         }
1671       }
1672     }
1673     mstch::array a;
1674     for (const auto& f : ordered_fields) {
1675       a.push_back(*f);
1676     }
1677     return a;
1678   }
1679 
1680  private:
1681   boost::optional<std::vector<t_struct*>> objects_;
1682   boost::optional<std::vector<t_enum*>> enums_;
1683   const boost::optional<int32_t> split_id_;
1684 
get_program_enums()1685   const std::vector<t_enum*>& get_program_enums() override {
1686     if (!enums_) {
1687       init_objects_enums();
1688     }
1689 
1690     return *enums_;
1691   }
1692 
get_program_objects()1693   const std::vector<t_struct*>& get_program_objects() override {
1694     if (!objects_) {
1695       init_objects_enums();
1696     }
1697 
1698     return *objects_;
1699   }
1700 
init_objects_enums()1701   void init_objects_enums() {
1702     const auto& prog_objects = program_->objects();
1703     const auto& prog_enums = program_->enums();
1704 
1705     if (!split_id_) {
1706       objects_ = gen_sorted_objects(program_, prog_objects);
1707       enums_ = prog_enums;
1708       return;
1709     }
1710 
1711     int32_t split_count =
1712         std::max(cpp2::get_split_count(cache_->parsed_options_), 1);
1713 
1714     objects_.emplace();
1715     enums_.emplace();
1716 
1717     const size_t cnt = prog_objects.size() + prog_enums.size();
1718     for (size_t i = split_id_.value_or(0); i < cnt; i += split_count) {
1719       if (i < prog_objects.size()) {
1720         objects_->push_back(prog_objects[i]);
1721       } else {
1722         enums_->push_back(prog_enums[i - prog_objects.size()]);
1723       }
1724     }
1725   }
1726 
gen_sorted_objects(const t_program * program,const std::vector<t_struct * > & objects)1727   static std::vector<t_struct*> gen_sorted_objects(
1728       const t_program* program, const std::vector<t_struct*>& objects) {
1729     auto edges = [program](t_struct* obj) {
1730       std::vector<t_struct*> deps;
1731       for (auto& f : obj->fields()) {
1732         // Ignore ref fields.
1733         if (cpp2::is_explicit_ref(&f)) {
1734           continue;
1735         }
1736 
1737         auto add_dependency = [&](const t_type* type) {
1738           if (auto strct = dynamic_cast<const t_struct*>(type)) {
1739             // We're only interested in types defined in the current program.
1740             if (!strct->is_exception() && strct->program() == program) {
1741               // TODO(afuller): Remove const cast, once the return type also has
1742               // const elements.
1743               deps.emplace_back(const_cast<t_struct*>(strct));
1744             }
1745           }
1746         };
1747 
1748         auto t = f.type()->get_true_type();
1749         if (auto map = dynamic_cast<t_map const*>(t)) {
1750           add_dependency(map->get_key_type());
1751           add_dependency(map->get_val_type());
1752         } else {
1753           add_dependency(t);
1754         }
1755       }
1756 
1757       // Order all deps in the order they are defined in.
1758       std::sort(
1759           deps.begin(), deps.end(), [](const t_struct* a, const t_struct* b) {
1760             return a->get_lineno() < b->get_lineno();
1761           });
1762 
1763       return deps;
1764     };
1765     return topological_sort<t_struct*>(objects.begin(), objects.end(), edges);
1766   }
1767 };
1768 
1769 class enum_cpp2_generator : public enum_generator {
1770  public:
1771   enum_cpp2_generator() = default;
1772   ~enum_cpp2_generator() override = default;
generate(t_enum const * enm,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1773   std::shared_ptr<mstch_base> generate(
1774       t_enum const* enm,
1775       std::shared_ptr<mstch_generators const> generators,
1776       std::shared_ptr<mstch_cache> cache,
1777       ELEMENT_POSITION pos,
1778       int32_t /*index*/) const override {
1779     return std::make_shared<mstch_cpp2_enum>(
1780         enm, std::move(generators), std::move(cache), pos);
1781   }
1782 };
1783 
1784 class enum_value_cpp2_generator : public enum_value_generator {
1785  public:
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) const1786   std::shared_ptr<mstch_base> generate(
1787       t_enum_value const* enm_value,
1788       std::shared_ptr<mstch_generators const> generators,
1789       std::shared_ptr<mstch_cache> cache,
1790       ELEMENT_POSITION pos,
1791       int32_t /*index*/) const override {
1792     return std::make_shared<mstch_cpp2_enum_value>(
1793         enm_value, std::move(generators), std::move(cache), pos);
1794   }
1795 };
1796 
1797 class type_cpp2_generator : public type_generator {
1798  public:
type_cpp2_generator(std::shared_ptr<cpp2_generator_context> context)1799   explicit type_cpp2_generator(
1800       std::shared_ptr<cpp2_generator_context> context) noexcept
1801       : context_(std::move(context)) {}
1802 
generate(t_type const * type,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1803   std::shared_ptr<mstch_base> generate(
1804       t_type const* type,
1805       std::shared_ptr<mstch_generators const> generators,
1806       std::shared_ptr<mstch_cache> cache,
1807       ELEMENT_POSITION pos,
1808       int32_t /*index*/) const override {
1809     return std::make_shared<mstch_cpp2_type>(
1810         type, std::move(generators), std::move(cache), pos, context_);
1811   }
1812 
1813  private:
1814   std::shared_ptr<cpp2_generator_context> context_;
1815 };
1816 
1817 class field_cpp2_generator : public field_generator {
1818  public:
field_cpp2_generator(std::shared_ptr<cpp2_generator_context> context)1819   explicit field_cpp2_generator(
1820       std::shared_ptr<cpp2_generator_context> context) noexcept
1821       : context_(std::move(context)) {}
1822 
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) const1823   std::shared_ptr<mstch_base> generate(
1824       t_field const* field,
1825       std::shared_ptr<mstch_generators const> generators,
1826       std::shared_ptr<mstch_cache> cache,
1827       ELEMENT_POSITION pos,
1828       int32_t index,
1829       field_generator_context const* field_context) const override {
1830     return std::make_shared<mstch_cpp2_field>(
1831         field,
1832         std::move(generators),
1833         std::move(cache),
1834         pos,
1835         index,
1836         field_context,
1837         context_);
1838   }
1839 
1840  private:
1841   std::shared_ptr<cpp2_generator_context> context_;
1842 };
1843 
1844 class function_cpp2_generator : public function_generator {
1845  public:
1846   function_cpp2_generator() = default;
1847   ~function_cpp2_generator() override = default;
generate(t_function const * function,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1848   std::shared_ptr<mstch_base> generate(
1849       t_function const* function,
1850       std::shared_ptr<mstch_generators const> generators,
1851       std::shared_ptr<mstch_cache> cache,
1852       ELEMENT_POSITION pos,
1853       int32_t /*index*/) const override {
1854     return std::make_shared<mstch_cpp2_function>(
1855         function, std::move(generators), std::move(cache), pos);
1856   }
1857 };
1858 
1859 class struct_cpp2_generator : public struct_generator {
1860  public:
struct_cpp2_generator(std::shared_ptr<cpp2_generator_context> context)1861   explicit struct_cpp2_generator(
1862       std::shared_ptr<cpp2_generator_context> context)
1863       : context_(std::move(context)) {}
1864   ~struct_cpp2_generator() override = default;
generate(t_struct const * strct,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1865   std::shared_ptr<mstch_base> generate(
1866       t_struct const* strct,
1867       std::shared_ptr<mstch_generators const> generators,
1868       std::shared_ptr<mstch_cache> cache,
1869       ELEMENT_POSITION pos,
1870       int32_t /*index*/) const override {
1871     return std::make_shared<mstch_cpp2_struct>(
1872         strct, std::move(generators), std::move(cache), pos, context_);
1873   }
1874 
1875  private:
1876   std::shared_ptr<cpp2_generator_context> context_;
1877 };
1878 
1879 class service_cpp2_generator : public service_generator {
1880  public:
1881   service_cpp2_generator() = default;
1882   ~service_cpp2_generator() override = default;
generate(t_service const * service,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1883   std::shared_ptr<mstch_base> generate(
1884       t_service const* service,
1885       std::shared_ptr<mstch_generators const> generators,
1886       std::shared_ptr<mstch_cache> cache,
1887       ELEMENT_POSITION pos,
1888       int32_t /*index*/) const override {
1889     return std::make_shared<mstch_cpp2_service>(
1890         service, std::move(generators), std::move(cache), pos);
1891   }
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) const1892   std::shared_ptr<mstch_base> generate_with_split_id(
1893       t_service const* service,
1894       std::shared_ptr<mstch_generators const> generators,
1895       std::shared_ptr<mstch_cache> cache,
1896       int32_t split_id,
1897       int32_t split_count) const {
1898     return std::make_shared<mstch_cpp2_service>(
1899         service,
1900         generators,
1901         cache,
1902         ELEMENT_POSITION::NONE,
1903         split_id,
1904         split_count);
1905   }
1906 };
1907 
1908 class annotation_cpp2_generator : public annotation_generator {
1909  public:
1910   annotation_cpp2_generator() = default;
1911   ~annotation_cpp2_generator() override = default;
generate(t_annotation const & keyval,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t index) const1912   std::shared_ptr<mstch_base> generate(
1913       t_annotation const& keyval,
1914       std::shared_ptr<mstch_generators const> generators,
1915       std::shared_ptr<mstch_cache> cache,
1916       ELEMENT_POSITION pos,
1917       int32_t index) const override {
1918     return std::make_shared<mstch_cpp2_annotation>(
1919         keyval.first,
1920         keyval.second,
1921         std::move(generators),
1922         std::move(cache),
1923         pos,
1924         index);
1925   }
1926 };
1927 
1928 class const_cpp2_generator : public const_generator {
1929  public:
1930   const_cpp2_generator() = default;
1931   ~const_cpp2_generator() override = default;
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) const1932   std::shared_ptr<mstch_base> generate(
1933       t_const const* cnst,
1934       std::shared_ptr<mstch_generators const> generators,
1935       std::shared_ptr<mstch_cache> cache,
1936       ELEMENT_POSITION pos,
1937       int32_t index,
1938       t_const const* current_const,
1939       t_type const* expected_type,
1940       t_field const* field) const override {
1941     return std::make_shared<mstch_cpp2_const>(
1942         cnst,
1943         current_const,
1944         expected_type,
1945         std::move(generators),
1946         std::move(cache),
1947         pos,
1948         index,
1949         field);
1950   }
1951 };
1952 
1953 class const_value_cpp2_generator : public const_value_generator {
1954  public:
1955   const_value_cpp2_generator() = default;
1956   ~const_value_cpp2_generator() override = default;
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) const1957   std::shared_ptr<mstch_base> generate(
1958       t_const_value const* const_value,
1959       std::shared_ptr<mstch_generators const> generators,
1960       std::shared_ptr<mstch_cache> cache,
1961       ELEMENT_POSITION pos,
1962       int32_t index,
1963       t_const const* current_const,
1964       t_type const* expected_type) const override {
1965     return std::make_shared<mstch_cpp2_const_value>(
1966         const_value,
1967         current_const,
1968         expected_type,
1969         std::move(generators),
1970         std::move(cache),
1971         pos,
1972         index);
1973   }
1974 };
1975 
1976 class program_cpp2_generator : public program_generator {
1977  public:
1978   program_cpp2_generator() = default;
1979   ~program_cpp2_generator() override = default;
generate(t_program const * program,std::shared_ptr<mstch_generators const> generators,std::shared_ptr<mstch_cache> cache,ELEMENT_POSITION pos,int32_t) const1980   std::shared_ptr<mstch_base> generate(
1981       t_program const* program,
1982       std::shared_ptr<mstch_generators const> generators,
1983       std::shared_ptr<mstch_cache> cache,
1984       ELEMENT_POSITION pos,
1985       int32_t /*index*/) const override {
1986     return std::make_shared<mstch_cpp2_program>(
1987         program, std::move(generators), std::move(cache), pos);
1988   }
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) const1989   std::shared_ptr<mstch_base> generate_with_split_id(
1990       t_program const* program,
1991       std::shared_ptr<mstch_generators const> generators,
1992       std::shared_ptr<mstch_cache> cache,
1993       int32_t split_id) const {
1994     return std::make_shared<mstch_cpp2_program>(
1995         program,
1996         std::move(generators),
1997         std::move(cache),
1998         ELEMENT_POSITION::NONE,
1999         split_id);
2000   }
2001 };
2002 
t_mstch_cpp2_generator(t_program * program,t_generation_context context,const std::map<std::string,std::string> & parsed_options,const std::string &)2003 t_mstch_cpp2_generator::t_mstch_cpp2_generator(
2004     t_program* program,
2005     t_generation_context context,
2006     const std::map<std::string, std::string>& parsed_options,
2007     const std::string& /*option_string*/)
2008     : t_mstch_generator(
2009           program, std::move(context), "cpp2", parsed_options, true),
2010       context_(std::make_shared<cpp2_generator_context>(
2011           cpp2_generator_context::create())),
2012       client_name_to_split_count_(
2013           cpp2::get_client_name_to_split_count(parsed_options)) {
2014   out_dir_base_ = get_out_dir_base(parsed_options);
2015 }
2016 
generate_program()2017 void t_mstch_cpp2_generator::generate_program() {
2018   auto const* program = get_program();
2019   set_mstch_generators();
2020 
2021   if (has_option("any")) {
2022     generate_sinit(program);
2023   }
2024   if (has_option("reflection")) {
2025     generate_reflection(program);
2026   }
2027   generate_structs(program);
2028   generate_constants(program);
2029   for (const auto* service : program->services()) {
2030     generate_service(service);
2031   }
2032   generate_metadata(program);
2033   generate_visitation(program);
2034 }
2035 
set_mstch_generators()2036 void t_mstch_cpp2_generator::set_mstch_generators() {
2037   generators_->set_enum_generator(std::make_unique<enum_cpp2_generator>());
2038   generators_->set_enum_value_generator(
2039       std::make_unique<enum_value_cpp2_generator>());
2040   generators_->set_type_generator(
2041       std::make_unique<type_cpp2_generator>(context_));
2042   generators_->set_field_generator(
2043       std::make_unique<field_cpp2_generator>(context_));
2044   generators_->set_function_generator(
2045       std::make_unique<function_cpp2_generator>());
2046   generators_->set_struct_generator(
2047       std::make_unique<struct_cpp2_generator>(context_));
2048   generators_->set_service_generator(
2049       std::make_unique<service_cpp2_generator>());
2050   generators_->set_const_generator(std::make_unique<const_cpp2_generator>());
2051   generators_->set_const_value_generator(
2052       std::make_unique<const_value_cpp2_generator>());
2053   generators_->set_annotation_generator(
2054       std::make_unique<annotation_cpp2_generator>());
2055   generators_->set_program_generator(
2056       std::make_unique<program_cpp2_generator>());
2057 }
2058 
generate_constants(t_program const * program)2059 void t_mstch_cpp2_generator::generate_constants(t_program const* program) {
2060   const auto& name = program->name();
2061   const auto& prog = cached_program(program);
2062 
2063   render_to_file(prog, "module_constants.h", name + "_constants.h");
2064   render_to_file(prog, "module_constants.cpp", name + "_constants.cpp");
2065 }
2066 
generate_metadata(const t_program * program)2067 void t_mstch_cpp2_generator::generate_metadata(const t_program* program) {
2068   const auto& name = program->name();
2069   const auto& prog = cached_program(program);
2070 
2071   render_to_file(prog, "module_metadata.h", name + "_metadata.h");
2072   if (!has_option("no_metadata")) {
2073     render_to_file(prog, "module_metadata.cpp", name + "_metadata.cpp");
2074   }
2075 }
2076 
generate_sinit(t_program const * program)2077 void t_mstch_cpp2_generator::generate_sinit(t_program const* program) {
2078   const auto& name = program->name();
2079   const auto& prog = cached_program(program);
2080 
2081   render_to_file(prog, "module_sinit.cpp", name + "_sinit.cpp");
2082 }
2083 
generate_reflection(t_program const * program)2084 void t_mstch_cpp2_generator::generate_reflection(t_program const* program) {
2085   const auto& name = program->name();
2086   const auto& prog = cached_program(program);
2087 
2088   // Combo include: all
2089   render_to_file(prog, "module_fatal_all.h", name + "_fatal_all.h");
2090   // Combo include: types
2091   render_to_file(prog, "module_fatal_types.h", name + "_fatal_types.h");
2092   // Unique Compile-time Strings, Metadata tags and Metadata registration
2093   render_to_file(prog, "module_fatal.h", name + "_fatal.h");
2094 
2095   render_to_file(prog, "module_fatal_enum.h", name + "_fatal_enum.h");
2096   render_to_file(prog, "module_fatal_union.h", name + "_fatal_union.h");
2097   render_to_file(prog, "module_fatal_struct.h", name + "_fatal_struct.h");
2098   render_to_file(prog, "module_fatal_constant.h", name + "_fatal_constant.h");
2099   render_to_file(prog, "module_fatal_service.h", name + "_fatal_service.h");
2100 }
2101 
generate_visitation(const t_program * program)2102 void t_mstch_cpp2_generator::generate_visitation(const t_program* program) {
2103   const auto& name = program->name();
2104   const auto& prog = cached_program(program);
2105 
2106   render_to_file(prog, "module_visitation.h", name + "_visitation.h");
2107   render_to_file(prog, "module_for_each_field.h", name + "_for_each_field.h");
2108   render_to_file(prog, "module_visit_union.h", name + "_visit_union.h");
2109   render_to_file(
2110       prog,
2111       "module_visit_by_thrift_field_metadata.h",
2112       name + "_visit_by_thrift_field_metadata.h");
2113 }
2114 
generate_structs(t_program const * program)2115 void t_mstch_cpp2_generator::generate_structs(t_program const* program) {
2116   const auto& name = program->name();
2117   const auto& prog = cached_program(program);
2118 
2119   render_to_file(prog, "module_data.h", name + "_data.h");
2120   render_to_file(prog, "module_data.cpp", name + "_data.cpp");
2121   render_to_file(prog, "module_types.h", name + "_types.h");
2122   render_to_file(prog, "module_types.tcc", name + "_types.tcc");
2123 
2124   if (auto split_count = cpp2::get_split_count(parsed_options_)) {
2125     auto digit = std::to_string(split_count - 1).size();
2126     for (int split_id = 0; split_id < split_count; ++split_id) {
2127       auto s = std::to_string(split_id);
2128       s = std::string(digit - s.size(), '0') + s;
2129       render_to_file(
2130           program_cpp2_generator{}.generate_with_split_id(
2131               program, generators_, cache_, split_id),
2132           "module_types.cpp",
2133           name + "_types." + s + ".split.cpp");
2134     }
2135   } else {
2136     render_to_file(prog, "module_types.cpp", name + "_types.cpp");
2137   }
2138 
2139   render_to_file(
2140       prog,
2141       "module_types_custom_protocol.h",
2142       name + "_types_custom_protocol.h");
2143   if (has_option("frozen2")) {
2144     render_to_file(prog, "module_layouts.h", name + "_layouts.h");
2145     render_to_file(prog, "module_layouts.cpp", name + "_layouts.cpp");
2146   }
2147 }
2148 
generate_service(t_service const * service)2149 void t_mstch_cpp2_generator::generate_service(t_service const* service) {
2150   const auto& name = service->get_name();
2151   cache_->parsed_options_["parent_service_name"] = name; // for interactions
2152   auto serv = generators_->service_generator_->generate_cached(
2153       get_program(), service, generators_, cache_);
2154 
2155   render_to_file(serv, "ServiceAsyncClient.h", name + "AsyncClient.h");
2156   render_to_file(serv, "service.cpp", name + ".cpp");
2157   render_to_file(serv, "service.h", name + ".h");
2158   render_to_file(serv, "service.tcc", name + ".tcc");
2159   render_to_file(serv, "types_custom_protocol.h", name + "_custom_protocol.h");
2160 
2161   auto iter = client_name_to_split_count_.find(name);
2162   if (iter != client_name_to_split_count_.end()) {
2163     auto split_count = iter->second;
2164     auto digit = std::to_string(split_count - 1).size();
2165     for (int split_id = 0; split_id < split_count; ++split_id) {
2166       auto s = std::to_string(split_id);
2167       s = std::string(digit - s.size(), '0') + s;
2168       auto split_service = service_cpp2_generator{}.generate_with_split_id(
2169           service, generators_, cache_, split_id, split_count);
2170       render_to_file(
2171           split_service,
2172           "ServiceAsyncClient.cpp",
2173           name + "." + s + ".async_client_split.cpp");
2174     }
2175   } else {
2176     render_to_file(serv, "ServiceAsyncClient.cpp", name + "AsyncClient.cpp");
2177   }
2178 
2179   std::vector<std::array<std::string, 3>> protocols = {
2180       {{"binary", "BinaryProtocol", "T_BINARY_PROTOCOL"}},
2181       {{"compact", "CompactProtocol", "T_COMPACT_PROTOCOL"}},
2182   };
2183   for (const auto& protocol : protocols) {
2184     render_to_file(
2185         serv,
2186         "service_processmap_protocol.cpp",
2187         name + "_processmap_" + protocol.at(0) + ".cpp");
2188   }
2189   cache_->parsed_options_.erase("parent_service_name");
2190 }
2191 
get_cpp2_namespace(t_program const * program)2192 std::string t_mstch_cpp2_generator::get_cpp2_namespace(
2193     t_program const* program) {
2194   return cpp2::get_gen_namespace(*program);
2195 }
2196 
get_namespace_array(t_program const * program)2197 mstch::array t_mstch_cpp2_generator::get_namespace_array(
2198     t_program const* program) {
2199   auto const v = cpp2::get_gen_namespace_components(*program);
2200   mstch::array a;
2201   for (auto it = v.begin(); it != v.end(); ++it) {
2202     mstch::map m;
2203     m.emplace("namespace:name", *it);
2204     a.push_back(m);
2205   }
2206   for (auto itr = a.begin(); itr != a.end(); ++itr) {
2207     boost::get<mstch::map>(*itr).emplace("first?", itr == a.begin());
2208     boost::get<mstch::map>(*itr).emplace("last?", std::next(itr) == a.end());
2209   }
2210   return a;
2211 }
2212 
cpp_includes(t_program const * program)2213 mstch::node t_mstch_cpp2_generator::cpp_includes(t_program const* program) {
2214   mstch::array a{};
2215   for (auto include : program->cpp_includes()) {
2216     mstch::map cpp_include;
2217     if (include.at(0) != '<') {
2218       include = "\"" + include + "\"";
2219     }
2220     cpp_include.emplace("cpp_include", std::string(include));
2221     a.push_back(cpp_include);
2222   }
2223   return a;
2224 }
2225 
include_prefix(t_program const * program,std::map<std::string,std::string> & options)2226 mstch::node t_mstch_cpp2_generator::include_prefix(
2227     t_program const* program, std::map<std::string, std::string>& options) {
2228   auto prefix = program->include_prefix();
2229   auto include_prefix = options["include_prefix"];
2230   auto out_dir_base = get_out_dir_base(options);
2231   if (prefix.empty()) {
2232     if (include_prefix.empty()) {
2233       return prefix;
2234     } else {
2235       return include_prefix + "/" + out_dir_base + "/";
2236     }
2237   }
2238   if (boost::filesystem::path(prefix).has_root_directory()) {
2239     return include_prefix + "/" + out_dir_base + "/";
2240   }
2241   return prefix + out_dir_base + "/";
2242 }
2243 
2244 namespace {
2245 class annotation_validator : public validator {
2246  public:
annotation_validator(std::map<std::string,std::string> const & options)2247   explicit annotation_validator(
2248       std::map<std::string, std::string> const& options)
2249       : options_(options) {}
2250   using validator::visit;
2251 
2252   /**
2253    * Make sure there is no incompatible annotation.
2254    */
2255   bool visit(t_struct* s) override;
2256 
2257  private:
2258   const std::map<std::string, std::string>& options_;
2259 };
2260 
visit(t_struct * s)2261 bool annotation_validator::visit(t_struct* s) {
2262   if (cpp2::packed_isset(*s)) {
2263     if (options_.count("tablebased") != 0) {
2264       add_error(
2265           s->lineno(),
2266           "Tablebased serialization is incompatible with isset bitpacking for struct `" +
2267               s->get_name() + "`");
2268     }
2269   }
2270 
2271   for (const auto& field : s->fields()) {
2272     if (cpp2::is_mixin(field)) {
2273       // Mixins cannot be refs
2274       if (cpp2::is_explicit_ref(&field)) {
2275         add_error(
2276             field.lineno(),
2277             "Mixin field `" + field.name() + "` can not be a ref in cpp.");
2278       }
2279     }
2280   }
2281   return true;
2282 }
2283 
2284 class service_method_validator : public validator {
2285  public:
service_method_validator(std::map<std::string,std::string> const & options)2286   explicit service_method_validator(
2287       std::map<std::string, std::string> const& options)
2288       : options_(options) {}
2289 
2290   using validator::visit;
2291   /**
2292    * Make sure there is no 'cpp.coroutine' annotation set when
2293    * 'stack_arguments' is turned on.
2294    */
2295   bool visit(t_service* service) override;
2296 
2297  private:
2298   const std::map<std::string, std::string>& options_;
2299 };
2300 
visit(t_service * service)2301 bool service_method_validator::visit(t_service* service) {
2302   auto suppress_key = "cpp.coroutine_stack_arguments_broken_suppress_error";
2303   for (const auto& func : service->functions()) {
2304     if (!func.has_annotation(suppress_key) &&
2305         func.has_annotation("cpp.coroutine") &&
2306         cpp2::is_stack_arguments(options_, func)) {
2307       // when cpp.coroutine and stack_arguments are both on, return failure if
2308       // this function has complex types (including string and binary).
2309       const auto& params = func.get_paramlist()->fields();
2310       bool ok =
2311           std::all_of(params.begin(), params.end(), [](const auto& param) {
2312             auto type = param.type()->get_true_type();
2313             return type->is_base_type() && !type->is_string_or_binary();
2314           });
2315 
2316       if (!ok) {
2317         add_error(
2318             func.lineno(),
2319             "`" + service->name() + "." + func.name() +
2320                 "` use of cpp.coroutine and stack_arguments together is "
2321                 "disallowed.");
2322       }
2323     }
2324   }
2325   return true;
2326 }
2327 
2328 class splits_validator : public validator {
2329  public:
splits_validator(int split_count)2330   explicit splits_validator(int split_count) : split_count_(split_count) {}
2331 
2332   using validator::visit;
visit(t_program * program)2333   bool visit(t_program* program) override {
2334     set_program(program);
2335     const int32_t object_count =
2336         program->objects().size() + program->enums().size();
2337     if (split_count_ != 0 && split_count_ > object_count) {
2338       add_error(
2339           boost::none,
2340           "`types_cpp_splits=" + std::to_string(split_count_) +
2341               "` is misconfigured: it can not be greater than number of object, which is " +
2342               std::to_string(object_count) + ".");
2343     }
2344     return true;
2345   }
2346 
2347  private:
2348   int32_t split_count_;
2349 };
2350 
2351 class lazy_field_validator : public validator {
2352  public:
2353   using validator::visit;
visit(t_field * field)2354   bool visit(t_field* field) override {
2355     if (cpp2::is_lazy(field)) {
2356       auto t = field->get_type()->get_true_type();
2357       boost::optional<std::string> field_type;
2358       if (t->is_any_int() || t->is_bool() || t->is_byte()) {
2359         field_type = "Integral field";
2360       }
2361       if (t->is_floating_point()) {
2362         field_type = "Floating point field";
2363       }
2364       if (field_type) {
2365         add_error(
2366             field->get_lineno(),
2367             *field_type + " `" + field->get_name() +
2368                 "` can not be marked as lazy, "
2369                 "since doing so won't bring any benefit.");
2370       }
2371     }
2372     return true;
2373   }
2374 };
2375 } // namespace
2376 
fill_validator_list(validator_list & l) const2377 void t_mstch_cpp2_generator::fill_validator_list(validator_list& l) const {
2378   l.add<annotation_validator>(this->parsed_options_);
2379   l.add<service_method_validator>(this->parsed_options_);
2380   l.add<splits_validator>(cpp2::get_split_count(parsed_options_));
2381   l.add<lazy_field_validator>();
2382 }
2383 
2384 THRIFT_REGISTER_GENERATOR(mstch_cpp2, "cpp2", "");
2385 
2386 } // namespace compiler
2387 } // namespace thrift
2388 } // namespace apache
2389