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  * Contains some contributions under the Thrift Software License.
20  * Please see doc/old-thrift-license.txt in the Thrift distribution for
21  * details.
22  */
23 
24 #include <cassert>
25 
26 #include <fstream>
27 #include <iostream>
28 #include <set>
29 #include <sstream>
30 #include <string>
31 #include <vector>
32 
33 #include <sys/stat.h>
34 
35 #include "thrift/platform.h"
36 #include "thrift/generate/t_oop_generator.h"
37 
38 using std::map;
39 using std::ofstream;
40 using std::ostream;
41 using std::ostringstream;
42 using std::set;
43 using std::string;
44 using std::vector;
45 
46 static const string endl = "\n"; // avoid ostream << std::endl flushes
47 
48 /**
49  * D code generator.
50  *
51  * generate_*() functions are called by the base class to emit code for the
52  * given entity, print_*() functions write a piece of code to the passed
53  * stream, and render_*() return a string containing the D representation of
54  * the passed entity.
55  */
56 class t_d_generator : public t_oop_generator {
57 public:
t_d_generator(t_program * program,const std::map<string,string> & parsed_options,const string & option_string)58   t_d_generator(t_program* program,
59                 const std::map<string, string>& parsed_options,
60                 const string& option_string)
61     : t_oop_generator(program) {
62     (void)option_string;
63     std::map<std::string, std::string>::const_iterator iter;
64 
65     /* no options yet */
66     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
67       throw "unknown option d:" + iter->first;
68     }
69 
70     out_dir_base_ = "gen-d";
71   }
72 
73 protected:
74 
75   // D reserved words are suffixed with an underscore
suffix_if_reserved(const string & name)76   static string suffix_if_reserved(const string& name) {
77 	const bool isIn = std::binary_search(std::begin(d_reserved_words), std::end(d_reserved_words), name);
78 	string ret = isIn ? name + "_" : name;
79 	return ret;
80   }
81 
init_generator()82   void init_generator() override {
83     // Make output directory
84     MKDIR(get_out_dir().c_str());
85 
86     string dir = program_->get_namespace("d");
87     string subdir = get_out_dir();
88     string::size_type loc;
89     while ((loc = dir.find(".")) != string::npos) {
90       subdir = subdir + "/" + dir.substr(0, loc);
91       MKDIR(subdir.c_str());
92       dir = dir.substr(loc + 1);
93     }
94     if (!dir.empty()) {
95       subdir = subdir + "/" + dir;
96       MKDIR(subdir.c_str());
97     }
98 
99     package_dir_ = subdir + "/";
100 
101     // Make output file
102     string f_types_name = package_dir_ + program_name_ + "_types.d";
103     f_types_.open(f_types_name.c_str());
104 
105     // Print header
106     f_types_ << autogen_comment() << "module " << render_package(*program_) << program_name_
107              << "_types;" << endl << endl;
108 
109     print_default_imports(f_types_);
110 
111     // Include type modules from other imported programs.
112     const vector<t_program*>& includes = program_->get_includes();
113     for (auto include : includes) {
114       f_types_ << "public import " << render_package(*include) << include->get_name()
115                << "_types;" << endl;
116     }
117     if (!includes.empty())
118       f_types_ << endl;
119   }
120 
close_generator()121   void close_generator() override {
122     // Close output file
123     f_types_.close();
124   }
125 
generate_consts(std::vector<t_const * > consts)126   void generate_consts(std::vector<t_const*> consts) override {
127     if (!consts.empty()) {
128       string f_consts_name = package_dir_ + program_name_ + "_constants.d";
129       ofstream_with_content_based_conditional_update f_consts;
130       f_consts.open(f_consts_name.c_str());
131 
132       f_consts << autogen_comment() << "module " << render_package(*program_) << program_name_
133                << "_constants;" << endl << endl;
134 
135       print_default_imports(f_consts);
136 
137       f_consts << "import " << render_package(*get_program()) << program_name_ << "_types;" << endl
138                << endl;
139 
140       vector<t_const*>::iterator c_iter;
141       for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
142         this->emit_doc(*c_iter, f_consts);
143         string name = suffix_if_reserved((*c_iter)->get_name());
144         t_type* type = (*c_iter)->get_type();
145         indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << endl;
146       }
147 
148       f_consts << endl << "shared static this() {" << endl;
149       indent_up();
150 
151       bool first = true;
152       for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
153         if (first) {
154           first = false;
155         } else {
156           f_consts << endl;
157         }
158         t_type* type = (*c_iter)->get_type();
159         indent(f_consts) << suffix_if_reserved((*c_iter)->get_name()) << " = ";
160         if (!is_immutable_type(type)) {
161           f_consts << "cast(immutable(" << render_type_name(type) << ")) ";
162         }
163         f_consts << render_const_value(type, (*c_iter)->get_value()) << ";" << endl;
164       }
165       indent_down();
166       indent(f_consts) << "}" << endl;
167     }
168   }
169 
generate_typedef(t_typedef * ttypedef)170   void generate_typedef(t_typedef* ttypedef) override {
171     this->emit_doc(ttypedef, f_types_);
172     f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " "
173              << ttypedef->get_symbolic() << ";" << endl << endl;
174   }
175 
generate_enum(t_enum * tenum)176   void generate_enum(t_enum* tenum) override {
177     vector<t_enum_value*> constants = tenum->get_constants();
178 
179     this->emit_doc(tenum, f_types_);
180     string enum_name = suffix_if_reserved(tenum->get_name());
181     f_types_ << indent() << "enum " << enum_name << " {" << endl;
182 
183     indent_up();
184 
185     vector<t_enum_value*>::const_iterator c_iter;
186     for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
187       this->emit_doc(*c_iter, f_types_);
188       indent(f_types_) << suffix_if_reserved((*c_iter)->get_name());
189       f_types_ << " = " << (*c_iter)->get_value() << ",";
190     }
191 
192     f_types_ << endl;
193     indent_down();
194     indent(f_types_) << "}" << endl;
195 
196     f_types_ << endl;
197   }
198 
generate_struct(t_struct * tstruct)199   void generate_struct(t_struct* tstruct) override {
200     print_struct_definition(f_types_, tstruct, false);
201   }
202 
generate_xception(t_struct * txception)203   void generate_xception(t_struct* txception) override {
204     print_struct_definition(f_types_, txception, true);
205   }
206 
generate_service(t_service * tservice)207   void generate_service(t_service* tservice) override {
208     string svc_name = suffix_if_reserved(tservice->get_name());
209 
210     // Service implementation file includes
211     string f_servicename = package_dir_ + svc_name + ".d";
212     ofstream_with_content_based_conditional_update f_service;
213     f_service.open(f_servicename.c_str());
214     f_service << autogen_comment() << "module " << suffix_if_reserved(render_package(*program_)) << svc_name << ";"
215               << endl << endl;
216 
217     print_default_imports(f_service);
218 
219     f_service << "import " << suffix_if_reserved(render_package(*get_program())) << program_name_ << "_types;" << endl;
220 
221     t_service* extends_service = tservice->get_extends();
222     if (extends_service != NULL) {
223       f_service << "import " << suffix_if_reserved(render_package(*(extends_service->get_program())))
224                 << suffix_if_reserved(extends_service->get_name()) << ";" << endl;
225     }
226 
227     f_service << endl;
228 
229     string extends = "";
230     if (tservice->get_extends() != NULL) {
231       extends = " : " + suffix_if_reserved(render_type_name(tservice->get_extends()));
232     }
233 
234     this->emit_doc(tservice, f_service);
235     f_service << indent() << "interface " << svc_name << extends << " {" << endl;
236     indent_up();
237 
238     // Collect all the exception types service methods can throw so we can
239     // emit the necessary aliases later.
240     set<t_type*> exception_types;
241 
242     // Print the method signatures.
243     vector<t_function*> functions = tservice->get_functions();
244     vector<t_function*>::iterator fn_iter;
245     for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
246       this->emit_doc(*fn_iter, f_service);
247       f_service << indent();
248       print_function_signature(f_service, *fn_iter);
249       f_service << ";" << endl;
250 
251       const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members();
252       vector<t_field*>::const_iterator ex_iter;
253       for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) {
254         exception_types.insert((*ex_iter)->get_type());
255       }
256     }
257 
258     // Alias the exception types into the current scope.
259     if (!exception_types.empty())
260       f_service << endl;
261     set<t_type*>::const_iterator et_iter;
262     for (et_iter = exception_types.begin(); et_iter != exception_types.end(); ++et_iter) {
263       indent(f_service) << "alias " << render_package(*(*et_iter)->get_program())
264                         << (*et_iter)->get_program()->get_name() << "_types"
265                         << "." << (*et_iter)->get_name() << " " << (*et_iter)->get_name() << ";"
266                         << endl;
267     }
268 
269     // Write the method metadata.
270     ostringstream meta;
271     indent_up();
272     bool first = true;
273     for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
274       if ((*fn_iter)->get_arglist()->get_members().empty()
275           && (*fn_iter)->get_xceptions()->get_members().empty() && !(*fn_iter)->is_oneway()) {
276         continue;
277       }
278 
279       if (first) {
280         first = false;
281       } else {
282         meta << ",";
283       }
284 
285       meta << endl << indent() << "TMethodMeta(`" << suffix_if_reserved((*fn_iter)->get_name()) << "`, " << endl;
286       indent_up();
287       indent(meta) << "[";
288 
289       bool first = true;
290       const vector<t_field*>& params = (*fn_iter)->get_arglist()->get_members();
291       vector<t_field*>::const_iterator p_iter;
292       for (p_iter = params.begin(); p_iter != params.end(); ++p_iter) {
293         if (first) {
294           first = false;
295         } else {
296           meta << ", ";
297         }
298 
299         meta << "TParamMeta(`" << suffix_if_reserved((*p_iter)->get_name()) << "`, " << (*p_iter)->get_key();
300 
301         t_const_value* cv = (*p_iter)->get_value();
302         if (cv != NULL) {
303           meta << ", q{" << render_const_value((*p_iter)->get_type(), cv) << "}";
304         }
305         meta << ")";
306       }
307 
308       meta << "]";
309 
310       if (!(*fn_iter)->get_xceptions()->get_members().empty() || (*fn_iter)->is_oneway()) {
311         meta << "," << endl << indent() << "[";
312 
313         bool first = true;
314         const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members();
315         vector<t_field*>::const_iterator ex_iter;
316         for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) {
317           if (first) {
318             first = false;
319           } else {
320             meta << ", ";
321           }
322 
323           meta << "TExceptionMeta(`" << suffix_if_reserved((*ex_iter)->get_name()) << "`, "
324                << (*ex_iter)->get_key() << ", `" << (*ex_iter)->get_type()->get_name() << "`)";
325         }
326 
327         meta << "]";
328       }
329 
330       if ((*fn_iter)->is_oneway()) {
331         meta << "," << endl << indent() << "TMethodType.ONEWAY";
332       }
333 
334       indent_down();
335       meta << endl << indent() << ")";
336     }
337     indent_down();
338 
339     string meta_str(meta.str());
340     if (!meta_str.empty()) {
341       f_service << endl << indent() << "enum methodMeta = [" << meta_str << endl << indent() << "];"
342                 << endl;
343     }
344 
345     indent_down();
346     indent(f_service) << "}" << endl;
347 
348     // Server skeleton generation.
349     string f_skeletonname = package_dir_ + svc_name + "_server.skeleton.d";
350     ofstream_with_content_based_conditional_update f_skeleton;
351     f_skeleton.open(f_skeletonname.c_str());
352     print_server_skeleton(f_skeleton, tservice);
353     f_skeleton.close();
354   }
355 
emit_doc(t_doc * doc,std::ostream & out)356   void emit_doc(t_doc *doc, std::ostream& out) {
357     if (!doc->has_doc()) {
358       return;
359     }
360     indent(out) << "/**" << std::endl;
361     indent_up();
362     // No endl -- comments reliably have a newline at the end.
363     // This is true even for stuff like:
364     //     /** method infos */ void foo(/** huh?*/ 1: i64 stuff)
365     indent(out) << doc->get_doc();
366     indent_down();
367     indent(out) << "*/" << std::endl;
368   }
369 
370 private:
371   /**
372    * Writes a server skeleton for the passed service to out.
373    */
print_server_skeleton(ostream & out,t_service * tservice)374   void print_server_skeleton(ostream& out, t_service* tservice) {
375     string svc_name = suffix_if_reserved(tservice->get_name());
376 
377     out << "/*" << endl
378         << " * This auto-generated skeleton file illustrates how to build a server. If you" << endl
379         << " * intend to customize it, you should edit a copy with another file name to " << endl
380         << " * avoid overwriting it when running the generator again." << endl << " */" << endl
381         << "module " << render_package(*tservice->get_program()) << svc_name << "_server;" << endl
382         << endl << "import std.stdio;" << endl << "import thrift.codegen.processor;" << endl
383         << "import thrift.protocol.binary;" << endl << "import thrift.server.simple;" << endl
384         << "import thrift.server.transport.socket;" << endl << "import thrift.transport.buffered;"
385         << endl << "import thrift.util.hashset;" << endl << endl << "import "
386         << render_package(*tservice->get_program()) << svc_name << ";" << endl << "import "
387         << render_package(*get_program()) << program_name_ << "_types;" << endl << endl << endl
388         << "class " << svc_name << "Handler : " << svc_name << " {" << endl;
389 
390     indent_up();
391     out << indent() << "this() {" << endl << indent() << "  // Your initialization goes here."
392         << endl << indent() << "}" << endl << endl;
393 
394     vector<t_function*> functions = tservice->get_functions();
395     vector<t_function*>::iterator f_iter;
396     for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
397       out << indent();
398       print_function_signature(out, *f_iter);
399       out << " {" << endl;
400 
401       indent_up();
402 
403       out << indent() << "// Your implementation goes here." << endl << indent() << "writeln(\""
404           << suffix_if_reserved((*f_iter)->get_name()) << " called\");" << endl;
405 
406 	  t_type* rt = (*f_iter)->get_returntype();
407 	  if (!rt->is_void()) {
408         indent(out) << "return typeof(return).init;" << endl;
409       }
410 
411       indent_down();
412 
413       out << indent() << "}" << endl << endl;
414     }
415 
416     indent_down();
417     out << "}" << endl << endl;
418 
419     out << indent() << "void main() {" << endl;
420     indent_up();
421     out << indent() << "auto protocolFactory = new TBinaryProtocolFactory!();" << endl << indent()
422         << "auto processor = new TServiceProcessor!" << svc_name << "(new " << svc_name
423         << "Handler);" << endl << indent() << "auto serverTransport = new TServerSocket(9090);"
424         << endl << indent() << "auto transportFactory = new TBufferedTransportFactory;" << endl
425         << indent() << "auto server = new TSimpleServer(" << endl << indent()
426         << "  processor, serverTransport, transportFactory, protocolFactory);" << endl << indent()
427         << "server.serve();" << endl;
428     indent_down();
429     out << "}" << endl;
430   }
431 
432   /**
433    * Writes the definition of a struct or an exception type to out.
434    */
print_struct_definition(ostream & out,t_struct * tstruct,bool is_exception)435   void print_struct_definition(ostream& out, t_struct* tstruct, bool is_exception) {
436     const vector<t_field*>& members = tstruct->get_members();
437 
438     if (is_exception) {
439       indent(out) << "class " << suffix_if_reserved(tstruct->get_name()) << " : TException {" << endl;
440     } else {
441       indent(out) << "struct " << suffix_if_reserved(tstruct->get_name()) << " {" << endl;
442     }
443     indent_up();
444 
445     // Declare all fields.
446     vector<t_field*>::const_iterator m_iter;
447     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
448       indent(out) << render_type_name((*m_iter)->get_type()) << " " << suffix_if_reserved((*m_iter)->get_name()) << ";"
449                   << endl;
450     }
451 
452     if (!members.empty())
453       indent(out) << endl;
454     indent(out) << "mixin TStructHelpers!(";
455 
456     if (!members.empty()) {
457       // If there are any fields, construct the TFieldMeta array to pass to
458       // TStructHelpers. We can't just pass an empty array if not because []
459       // doesn't pass the TFieldMeta[] constraint.
460       out << "[";
461       indent_up();
462 
463       bool first = true;
464       vector<t_field*>::const_iterator m_iter;
465       for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
466         if (first) {
467           first = false;
468         } else {
469           out << ",";
470         }
471         out << endl;
472 
473         indent(out) << "TFieldMeta(`" << suffix_if_reserved((*m_iter)->get_name()) << "`, " << (*m_iter)->get_key();
474 
475         t_const_value* cv = (*m_iter)->get_value();
476         t_field::e_req req = (*m_iter)->get_req();
477         out << ", " << render_req(req);
478         if (cv != NULL) {
479           out << ", q{" << render_const_value((*m_iter)->get_type(), cv) << "}";
480         }
481         out << ")";
482       }
483 
484       indent_down();
485       out << endl << indent() << "]";
486     }
487 
488     out << ");" << endl;
489 
490     indent_down();
491     indent(out) << "}" << endl << endl;
492   }
493 
494   /**
495    * Prints the D function signature (including return type) for the given
496    * method.
497    */
print_function_signature(ostream & out,t_function * fn)498   void print_function_signature(ostream& out, t_function* fn) {
499     out << render_type_name(fn->get_returntype()) << " " << suffix_if_reserved(fn->get_name()) << "(";
500 
501     const vector<t_field*>& fields = fn->get_arglist()->get_members();
502     vector<t_field*>::const_iterator f_iter;
503     bool first = true;
504     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
505       if (first) {
506         first = false;
507       } else {
508         out << ", ";
509       }
510       out << render_type_name((*f_iter)->get_type(), true) << " " << suffix_if_reserved((*f_iter)->get_name());
511     }
512 
513     out << ")";
514   }
515 
516   /**
517    * Returns the D representation of value. The result is guaranteed to be a
518    * single expression; for complex types, immediately called delegate
519    * literals are used to achieve this.
520    */
render_const_value(t_type * type,t_const_value * value)521   string render_const_value(t_type* type, t_const_value* value) {
522     // Resolve any typedefs.
523     type = get_true_type(type);
524 
525     ostringstream out;
526     if (type->is_base_type()) {
527       t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
528       switch (tbase) {
529       case t_base_type::TYPE_STRING:
530         out << '"' << get_escaped_string(value) << '"';
531         break;
532       case t_base_type::TYPE_BOOL:
533         out << ((value->get_integer() > 0) ? "true" : "false");
534         break;
535       case t_base_type::TYPE_I8:
536       case t_base_type::TYPE_I16:
537         out << "cast(" << render_type_name(type) << ")" << value->get_integer();
538         break;
539       case t_base_type::TYPE_I32:
540         out << value->get_integer();
541         break;
542       case t_base_type::TYPE_I64:
543         out << value->get_integer() << "L";
544         break;
545       case t_base_type::TYPE_DOUBLE:
546         if (value->get_type() == t_const_value::CV_INTEGER) {
547           out << value->get_integer();
548         } else {
549           out << value->get_double();
550         }
551         break;
552       default:
553         throw "Compiler error: No const of base type " + t_base_type::t_base_name(tbase);
554       }
555     } else if (type->is_enum()) {
556       out << "cast(" << render_type_name(type) << ")" << value->get_integer();
557     } else {
558       out << "{" << endl;
559       indent_up();
560 
561       indent(out) << render_type_name(type) << " v;" << endl;
562       if (type->is_struct() || type->is_xception()) {
563         indent(out) << "v = " << (type->is_xception() ? "new " : "") << render_type_name(type)
564                     << "();" << endl;
565 
566         const vector<t_field*>& fields = ((t_struct*)type)->get_members();
567         vector<t_field*>::const_iterator f_iter;
568         const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
569         map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
570         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
571           t_type* field_type = NULL;
572           for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
573             if ((*f_iter)->get_name() == v_iter->first->get_string()) {
574               field_type = (*f_iter)->get_type();
575             }
576           }
577           if (field_type == NULL) {
578             throw "Type error: " + type->get_name() + " has no field "
579                 + v_iter->first->get_string();
580           }
581           string val = render_const_value(field_type, v_iter->second);
582           indent(out) << "v.set!`" << v_iter->first->get_string() << "`(" << val << ");" << endl;
583         }
584       } else if (type->is_map()) {
585         t_type* ktype = ((t_map*)type)->get_key_type();
586         t_type* vtype = ((t_map*)type)->get_val_type();
587         const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
588         map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
589         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
590           string key = render_const_value(ktype, v_iter->first);
591           string val = render_const_value(vtype, v_iter->second);
592           indent(out) << "v[";
593           if (!is_immutable_type(ktype)) {
594             out << "cast(immutable(" << render_type_name(ktype) << "))";
595           }
596           out << key << "] = " << val << ";" << endl;
597         }
598       } else if (type->is_list()) {
599         t_type* etype = ((t_list*)type)->get_elem_type();
600         const vector<t_const_value*>& val = value->get_list();
601         vector<t_const_value*>::const_iterator v_iter;
602         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
603           string val = render_const_value(etype, *v_iter);
604           indent(out) << "v ~= " << val << ";" << endl;
605         }
606       } else if (type->is_set()) {
607         t_type* etype = ((t_set*)type)->get_elem_type();
608         const vector<t_const_value*>& val = value->get_list();
609         vector<t_const_value*>::const_iterator v_iter;
610         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
611           string val = render_const_value(etype, *v_iter);
612           indent(out) << "v ~= " << val << ";" << endl;
613         }
614       } else {
615         throw "Compiler error: Invalid type in render_const_value: " + type->get_name();
616       }
617       indent(out) << "return v;" << endl;
618 
619       indent_down();
620       indent(out) << "}()";
621     }
622 
623     return out.str();
624   }
625 
626   /**
627    * Returns the D package to which modules for program are written (with a
628    * trailing dot, if not empty).
629    */
render_package(const t_program & program) const630   string render_package(const t_program& program) const {
631     string package = program.get_namespace("d");
632     if (package.size() == 0)
633       return "";
634     return package + ".";
635   }
636 
637   /**
638    * Returns the name of the D repesentation of ttype.
639    *
640    * If isArg is true, a const reference to the type will be returned for
641    * structs.
642    */
render_type_name(const t_type * ttype,bool isArg=false) const643   string render_type_name(const t_type* ttype, bool isArg = false) const {
644     if (ttype->is_base_type()) {
645       t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
646       switch (tbase) {
647       case t_base_type::TYPE_VOID:
648         return "void";
649       case t_base_type::TYPE_STRING:
650         return "string";
651       case t_base_type::TYPE_BOOL:
652         return "bool";
653       case t_base_type::TYPE_I8:
654         return "byte";
655       case t_base_type::TYPE_I16:
656         return "short";
657       case t_base_type::TYPE_I32:
658         return "int";
659       case t_base_type::TYPE_I64:
660         return "long";
661       case t_base_type::TYPE_DOUBLE:
662         return "double";
663       default:
664         throw "Compiler error: No D type name for base type " + t_base_type::t_base_name(tbase);
665       }
666     }
667 
668     if (ttype->is_container()) {
669       t_container* tcontainer = (t_container*)ttype;
670       if (tcontainer->has_cpp_name()) {
671         return tcontainer->get_cpp_name();
672       } else if (ttype->is_map()) {
673         t_map* tmap = (t_map*)ttype;
674         t_type* ktype = tmap->get_key_type();
675 
676         string name = render_type_name(tmap->get_val_type()) + "[";
677         if (!is_immutable_type(ktype)) {
678           name += "immutable(";
679         }
680         name += render_type_name(ktype);
681         if (!is_immutable_type(ktype)) {
682           name += ")";
683         }
684         name += "]";
685         return name;
686       } else if (ttype->is_set()) {
687         t_set* tset = (t_set*)ttype;
688         return "HashSet!(" + render_type_name(tset->get_elem_type()) + ")";
689       } else if (ttype->is_list()) {
690         t_list* tlist = (t_list*)ttype;
691         return render_type_name(tlist->get_elem_type()) + "[]";
692       }
693     }
694 
695     if (ttype->is_struct() && isArg) {
696       return "ref const(" + ttype->get_name() + ")";
697     } else {
698       return ttype->get_name();
699     }
700   }
701 
702   /**
703    * Returns the D TReq enum member corresponding to req.
704    */
render_req(t_field::e_req req) const705   string render_req(t_field::e_req req) const {
706     switch (req) {
707     case t_field::T_OPT_IN_REQ_OUT:
708       return "TReq.OPT_IN_REQ_OUT";
709     case t_field::T_OPTIONAL:
710       return "TReq.OPTIONAL";
711     case t_field::T_REQUIRED:
712       return "TReq.REQUIRED";
713     default: {
714       std::stringstream ss;
715       ss << "Compiler error: Invalid requirement level " << req;
716       throw ss.str();
717     }
718     }
719   }
720 
721   /**
722    * Writes the default list of imports (which are written to every generated
723    * module) to f.
724    */
print_default_imports(ostream & out)725   void print_default_imports(ostream& out) {
726     indent(out) << "import thrift.base;" << endl << "import thrift.codegen.base;" << endl
727                 << "import thrift.util.hashset;" << endl << endl;
728   }
729 
730   /**
731    * Returns whether type is »intrinsically immutable«, in the sense that
732    * a value of that type is implicitly castable to immutable(type), and it is
733    * allowed for AA keys without an immutable() qualifier.
734    */
is_immutable_type(t_type * type) const735   bool is_immutable_type(t_type* type) const {
736     t_type* ttype = get_true_type(type);
737     return ttype->is_base_type() || ttype->is_enum();
738   }
739 
740   /*
741    * File streams, stored here to avoid passing them as parameters to every
742    * function.
743    */
744   ofstream_with_content_based_conditional_update f_types_;
745   ofstream_with_content_based_conditional_update f_header_;
746 
747   string package_dir_;
748 
749   protected:
750    static vector<string> d_reserved_words;
751 
752 };
753 
754 vector<string> t_d_generator::d_reserved_words = {
755     // The keywords are extracted from https://dlang.org/spec/lex.html
756     // and sorted for use with std::binary_search
757     "__FILE_FULL_PATH__", "__FILE__", "__FUNCTION__", "__LINE__", "__MODULE__",
758     "__PRETTY_FUNCTION__", "__gshared", "__parameters", "__traits", "__vector",
759     "abstract", "alias", "align", "asm", "assert", "auto", "body", "bool",
760     "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat",
761     "char", "class", "const", "continue", "creal", "dchar", "debug", "default",
762     "delegate", "delete", "deprecated", "do", "double", "else", "enum",
763     "export", "extern", "false", "final", "finally", "float", "for", "foreach",
764     "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable",
765     "import", "in", "inout", "int", "interface", "invariant", "ireal", "is",
766     "lazy", "long", "macro ", "mixin", "module", "new", "nothrow", "null", "out",
767     "override", "package", "pragma", "private", "protected", "public", "pure",
768     "real", "ref", "return", "scope", "shared", "short", "static", "struct",
769     "super", "switch", "synchronized", "template", "this", "throw", "true", "try",
770     "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", "union", "unittest",
771     "ushort", "version", "void", "wchar", "while", "with"
772 };
773 
774 THRIFT_REGISTER_GENERATOR(d, "D", "")
775