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