1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 #ifndef T_GENERATOR_H 21 #define T_GENERATOR_H 22 #define MSC_2015_VER 1900 23 24 #include <cstring> 25 #include <string> 26 #include <iomanip> 27 #include <iostream> 28 #include <fstream> 29 #include <limits> 30 #include <sstream> 31 #include "thrift/common.h" 32 #include "thrift/logging.h" 33 #include "thrift/version.h" 34 #include "thrift/generate/t_generator_registry.h" 35 #include "thrift/parse/t_program.h" 36 37 /** 38 * Base class for a thrift code generator. This class defines the basic 39 * routines for code generation and contains the top level method that 40 * dispatches code generation across various components. 41 * 42 */ 43 class t_generator { 44 public: t_generator(t_program * program)45 t_generator(t_program* program) { 46 update_keywords(); 47 48 tmp_ = 0; 49 indent_ = 0; 50 program_ = program; 51 program_name_ = get_program_name(program); 52 escape_['\n'] = "\\n"; 53 escape_['\r'] = "\\r"; 54 escape_['\t'] = "\\t"; 55 escape_['"'] = "\\\""; 56 escape_['\\'] = "\\\\"; 57 } 58 ~t_generator()59 virtual ~t_generator() {} 60 61 /** 62 * Framework generator method that iterates over all the parts of a program 63 * and performs general actions. This is implemented by the base class and 64 * should not normally be overwritten in the subclasses. 65 */ 66 virtual void generate_program(); 67 get_program()68 const t_program* get_program() const { return program_; } 69 70 void generate_docstring_comment(std::ostream& out, 71 const std::string& comment_start, 72 const std::string& line_prefix, 73 const std::string& contents, 74 const std::string& comment_end); 75 76 static void parse_options(const std::string& options, std::string& language, 77 std::map<std::string, std::string>& parsed_options); 78 79 /** 80 * check whether sub-namespace declaraction is used by generator. 81 * e.g. allow 82 * namespace py.twisted bar 83 * to specify namespace to use when -gen py:twisted is specified. 84 * Will be called with subnamespace, i.e. is_valid_namespace("twisted") 85 * will be called for the above example. 86 */ is_valid_namespace(const std::string & sub_namespace)87 static bool is_valid_namespace(const std::string& sub_namespace) { 88 (void)sub_namespace; 89 return false; 90 } 91 92 /** 93 * Escape string to use one in generated sources. 94 */ 95 virtual std::string escape_string(const std::string& in) const; 96 get_escaped_string(t_const_value * constval)97 std::string get_escaped_string(t_const_value* constval) { 98 return escape_string(constval->get_string()); 99 } 100 101 /** 102 * Check if all identifiers are valid for the target language 103 * See update_keywords() 104 */ 105 virtual void validate_input() const; 106 107 protected: 108 virtual std::set<std::string> lang_keywords() const; 109 110 /** 111 * Call this from constructor if you implement lang_keywords() 112 */ update_keywords()113 void update_keywords() { 114 keywords_ = lang_keywords(); 115 } 116 117 /** 118 * A list of reserved words that cannot be used as identifiers. 119 */ 120 std::set<std::string> keywords_; 121 122 virtual void validate_id(const std::string& id) const; 123 124 virtual void validate(t_enum const* en) const; 125 virtual void validate(t_enum_value const* en_val) const; 126 virtual void validate(t_typedef const* td) const; 127 virtual void validate(t_const const* c) const; 128 virtual void validate(t_service const* s) const; 129 virtual void validate(t_struct const* c) const; 130 virtual void validate(t_field const* f) const; 131 virtual void validate(t_function const* f) const; 132 133 template <typename T> 134 void validate(const std::vector<T>& list) const; 135 136 /** 137 * Optional methods that may be implemented by subclasses to take necessary 138 * steps at the beginning or end of code generation. 139 */ 140 init_generator()141 virtual void init_generator() {} close_generator()142 virtual void close_generator() {} 143 144 virtual void generate_consts(std::vector<t_const*> consts); 145 146 /** 147 * Pure virtual methods implemented by the generator subclasses. 148 */ 149 150 virtual void generate_typedef(t_typedef* ttypedef) = 0; 151 virtual void generate_enum(t_enum* tenum) = 0; generate_const(t_const * tconst)152 virtual void generate_const(t_const* tconst) { (void)tconst; } 153 virtual void generate_struct(t_struct* tstruct) = 0; 154 virtual void generate_service(t_service* tservice) = 0; generate_forward_declaration(t_struct *)155 virtual void generate_forward_declaration(t_struct*) {} generate_xception(t_struct * txception)156 virtual void generate_xception(t_struct* txception) { 157 // By default exceptions are the same as structs 158 generate_struct(txception); 159 } 160 161 /** 162 * Method to get the program name, may be overridden 163 */ get_program_name(t_program * tprogram)164 virtual std::string get_program_name(t_program* tprogram) { return tprogram->get_name(); } 165 166 /** 167 * Method to get the service name, may be overridden 168 */ get_service_name(t_service * tservice)169 virtual std::string get_service_name(t_service* tservice) { return tservice->get_name(); } 170 171 /** 172 * Get the current output directory 173 */ get_out_dir()174 virtual std::string get_out_dir() const { 175 if (program_->is_out_path_absolute()) { 176 return program_->get_out_path() + "/"; 177 } 178 179 return program_->get_out_path() + out_dir_base_ + "/"; 180 } 181 182 /** 183 * Creates a unique temporary variable name, which is just "name" with a 184 * number appended to it (i.e. name35) 185 */ tmp(std::string name)186 std::string tmp(std::string name) { 187 std::ostringstream out; 188 out << name << tmp_++; 189 return out.str(); 190 } 191 192 /** 193 * Generates a comment about this code being autogenerated, using C++ style 194 * comments, which are also fair game in Java / PHP, yay! 195 * 196 * @return C-style comment mentioning that this file is autogenerated. 197 */ autogen_comment()198 virtual std::string autogen_comment() { 199 return std::string("/**\n") + " * " + autogen_summary() + "\n" + " *\n" 200 + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" 201 + " * @generated\n" + " */\n"; 202 } 203 autogen_summary()204 virtual std::string autogen_summary() { 205 return std::string("Autogenerated by Thrift Compiler (") + THRIFT_VERSION + ")"; 206 } 207 208 /** 209 * Indentation level modifiers 210 */ 211 indent_up()212 void indent_up() { ++indent_; } 213 indent_down()214 void indent_down() { --indent_; } 215 216 /** 217 * Indentation validation helper 218 */ indent_count()219 int indent_count() { return indent_; } 220 indent_validate(int expected,const char * func_name)221 void indent_validate( int expected, const char * func_name) { 222 if (indent_ != expected) { 223 pverbose("Wrong indent count in %s: difference = %i \n", func_name, (expected - indent_)); 224 } 225 } 226 227 /** 228 * Indentation print function 229 */ indent()230 std::string indent() { 231 std::string ind = ""; 232 int i; 233 for (i = 0; i < indent_; ++i) { 234 ind += indent_str(); 235 } 236 return ind; 237 } 238 239 /** 240 * Indentation utility wrapper 241 */ indent(std::ostream & os)242 std::ostream& indent(std::ostream& os) { return os << indent(); } 243 244 /** 245 * Capitalization helpers 246 */ capitalize(std::string in)247 std::string capitalize(std::string in) { 248 in[0] = toupper(in[0]); 249 return in; 250 } decapitalize(std::string in)251 std::string decapitalize(std::string in) { 252 in[0] = tolower(in[0]); 253 return in; 254 } lowercase(std::string in)255 static std::string lowercase(std::string in) { 256 for (size_t i = 0; i < in.size(); ++i) { 257 in[i] = tolower(in[i]); 258 } 259 return in; 260 } uppercase(std::string in)261 static std::string uppercase(std::string in) { 262 for (size_t i = 0; i < in.size(); ++i) { 263 in[i] = toupper(in[i]); 264 } 265 return in; 266 } 267 268 /** 269 * Transforms a camel case string to an equivalent one separated by underscores 270 * e.g. aMultiWord -> a_multi_word 271 * someName -> some_name 272 * CamelCase -> camel_case 273 * name -> name 274 * Name -> name 275 */ underscore(std::string in)276 std::string underscore(std::string in) { 277 in[0] = tolower(in[0]); 278 for (size_t i = 1; i < in.size(); ++i) { 279 if (isupper(in[i])) { 280 in[i] = tolower(in[i]); 281 in.insert(i, "_"); 282 } 283 } 284 return in; 285 } 286 287 /** 288 * Transforms a string with words separated by underscores to a camel case equivalent 289 * e.g. a_multi_word -> aMultiWord 290 * some_name -> someName 291 * name -> name 292 */ camelcase(std::string in)293 std::string camelcase(std::string in) { 294 std::ostringstream out; 295 bool underscore = false; 296 297 for (size_t i = 0; i < in.size(); i++) { 298 if (in[i] == '_') { 299 underscore = true; 300 continue; 301 } 302 if (underscore) { 303 out << (char)toupper(in[i]); 304 underscore = false; 305 continue; 306 } 307 out << in[i]; 308 } 309 310 return out.str(); 311 } 312 emit_double_as_string(const double value)313 const std::string emit_double_as_string(const double value) { 314 std::stringstream double_output_stream; 315 // sets the maximum precision: http://en.cppreference.com/w/cpp/io/manip/setprecision 316 // sets the output format to fixed: http://en.cppreference.com/w/cpp/io/manip/fixed (not in scientific notation) 317 double_output_stream << std::setprecision(std::numeric_limits<double>::digits10 + 1); 318 319 #ifdef _MSC_VER 320 // strtod is broken in MSVC compilers older than 2015, so std::fixed fails to format a double literal. 321 // more details: https://blogs.msdn.microsoft.com/vcblog/2014/06/18/ 322 // c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/ 323 // and 324 // http://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/ 325 #if _MSC_VER >= MSC_2015_VER 326 double_output_stream << std::fixed; 327 #endif 328 #else 329 double_output_stream << std::fixed; 330 #endif 331 332 double_output_stream << value; 333 334 return double_output_stream.str(); 335 } 336 337 public: 338 /** 339 * Get the true type behind a series of typedefs. 340 */ get_true_type(const t_type * type)341 static const t_type* get_true_type(const t_type* type) { return type->get_true_type(); } get_true_type(t_type * type)342 static t_type* get_true_type(t_type* type) { return type->get_true_type(); } 343 344 protected: 345 /** 346 * The program being generated 347 */ 348 t_program* program_; 349 350 /** 351 * Quick accessor for formatted program name that is currently being 352 * generated. 353 */ 354 std::string program_name_; 355 356 /** 357 * Quick accessor for formatted service name that is currently being 358 * generated. 359 */ 360 std::string service_name_; 361 362 /** 363 * Output type-specifc directory name ("gen-*") 364 */ 365 std::string out_dir_base_; 366 367 /** 368 * Map of characters to escape in string literals. 369 */ 370 std::map<char, std::string> escape_; 371 indent_str()372 virtual std::string indent_str() const { 373 return " "; 374 } 375 376 private: 377 /** 378 * Current code indentation level 379 */ 380 int indent_; 381 382 /** 383 * Temporary variable counter, for making unique variable names 384 */ 385 int tmp_; 386 }; 387 388 template<typename _CharT, typename _Traits = std::char_traits<_CharT> > 389 class template_ofstream_with_content_based_conditional_update : public std::ostringstream { 390 public: template_ofstream_with_content_based_conditional_update()391 template_ofstream_with_content_based_conditional_update(): contents_written(false) {} 392 template_ofstream_with_content_based_conditional_update(std::string const & output_file_path_)393 template_ofstream_with_content_based_conditional_update(std::string const& output_file_path_) 394 : output_file_path(output_file_path_), contents_written(false) {} 395 ~template_ofstream_with_content_based_conditional_update()396 ~template_ofstream_with_content_based_conditional_update() { 397 if (!contents_written) { 398 close(); 399 } 400 } 401 open(std::string const & output_file_path_)402 void open(std::string const& output_file_path_) { 403 output_file_path = output_file_path_; 404 clear_buf(); 405 contents_written = false; 406 } 407 close()408 void close() { 409 if (contents_written || output_file_path == "") 410 return; 411 412 if (!is_readable(output_file_path)) { 413 dump(); 414 return; 415 } 416 417 std::ifstream old_file; 418 old_file.exceptions(old_file.exceptions() | std::ifstream::badbit | std::ifstream::failbit); 419 old_file.open(output_file_path.c_str(), std::ios::in); 420 421 if (old_file) { 422 std::string const old_file_contents(static_cast<std::ostringstream const&>(std::ostringstream() << old_file.rdbuf()).str()); 423 old_file.close(); 424 425 if (old_file_contents != str()) { 426 dump(); 427 } 428 } 429 } 430 431 protected: dump()432 void dump() { 433 std::ofstream out_file; 434 out_file.exceptions(out_file.exceptions() | std::ofstream::badbit | std::ofstream::failbit); 435 try { 436 out_file.open(output_file_path.c_str(), std::ios::out); 437 } 438 catch (const std::ios_base::failure& e) { 439 ::failure("failed to write the output to the file '%s', details: '%s'", output_file_path.c_str(), e.what()); 440 } 441 out_file << str(); 442 out_file.close(); 443 clear_buf(); 444 contents_written = true; 445 } 446 clear_buf()447 void clear_buf() { 448 str(std::string()); 449 } 450 is_readable(std::string const & file_name)451 static bool is_readable(std::string const& file_name) { 452 return static_cast<bool>(std::ifstream(file_name.c_str())); 453 } 454 455 private: 456 std::string output_file_path; 457 bool contents_written; 458 }; 459 typedef template_ofstream_with_content_based_conditional_update<char> ofstream_with_content_based_conditional_update; 460 461 #endif 462