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 <fstream>
25 #include <iostream>
26 #include <stdexcept>
27 #include <string>
28 #include <vector>
29 
30 #include <ctype.h>
31 
32 #include "thrift/platform.h"
33 #include "thrift/generate/t_oop_generator.h"
34 
35 using std::map;
36 using std::ostream;
37 using std::ostringstream;
38 using std::string;
39 using std::stringstream;
40 using std::vector;
41 
42 static const string endl = "\n"; // avoid ostream << std::endl flushes
43 
44 /* forward declarations */
45 string initial_caps_to_underscores(string name);
46 string underscores_to_initial_caps(string name);
47 string to_upper_case(string name);
48 string to_lower_case(string name);
49 
50 /**
51  * C code generator, using glib for C typing.
52  */
53 class t_c_glib_generator : public t_oop_generator {
54 public:
55   /* constructor */
t_c_glib_generator(t_program * program,const map<string,string> & parsed_options,const string & option_string)56   t_c_glib_generator(t_program* program,
57                      const map<string, string>& parsed_options,
58                      const string& option_string)
59     : t_oop_generator(program) {
60     (void)option_string;
61     std::map<std::string, std::string>::const_iterator iter;
62 
63     /* set the output directory */
64     this->out_dir_base_ = "gen-c_glib";
65 
66     /* no options yet */
67     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
68       throw "unknown option c_glib:" + iter->first;
69     }
70 
71     /* set the namespace */
72     this->nspace = program_->get_namespace("c_glib");
73 
74     if (this->nspace.empty()) {
75       this->nspace = "";
76       this->nspace_u = "";
77       this->nspace_uc = "";
78       this->nspace_lc = "";
79     } else {
80       /* replace dots with underscores */
81       char* tmp = strdup(this->nspace.c_str());
82       for (unsigned int i = 0; i < strlen(tmp); i++) {
83         if (tmp[i] == '.') {
84           tmp[i] = '_';
85         }
86       }
87       this->nspace = string(tmp, strlen(tmp));
88       free(tmp);
89 
90       /* clean up the namespace for C.
91        * An input of 'namespace foo' should result in:
92        *  - nspace = foo       - for thrift objects and typedefs
93        *  - nspace_u = Foo     - for internal GObject prefixes
94        *  - nspace_uc = FOO_   - for macro prefixes
95        *  - nspace_lc = foo_   - for filename and method prefixes
96        * The underscores are there since uc and lc strings are used as file and
97        * variable prefixes.
98        */
99       this->nspace_u = initial_caps_to_underscores(this->nspace);
100       this->nspace_uc = to_upper_case(this->nspace_u) + "_";
101       this->nspace_lc = to_lower_case(this->nspace_u) + "_";
102     }
103   }
104 
105   /* initialization and destruction */
106   void init_generator() override;
107   void close_generator() override;
108   std::string display_name() const override;
109 
110   /* generation functions */
111   void generate_typedef(t_typedef* ttypedef) override;
112   void generate_enum(t_enum* tenum) override;
113   void generate_consts(vector<t_const*> consts) override;
114   void generate_struct(t_struct* tstruct) override;
115   void generate_service(t_service* tservice) override;
116   void generate_xception(t_struct* tstruct) override;
117 
118 private:
119   /* file streams */
120   ofstream_with_content_based_conditional_update f_types_;
121   ofstream_with_content_based_conditional_update f_types_impl_;
122   ofstream_with_content_based_conditional_update f_header_;
123   ofstream_with_content_based_conditional_update f_service_;
124 
125   /* namespace variables */
126   string nspace;
127   string nspace_u;
128   string nspace_uc;
129   string nspace_lc;
130 
131   /* helper functions */
132   bool is_complex_type(t_type* ttype);
133   bool is_numeric(t_type* ttype);
134   string type_name(t_type* ttype, bool in_typedef = false, bool is_const = false);
135   string property_type_name(t_type* ttype, bool in_typedef = false, bool is_const = false);
136   string base_type_name(t_type* type);
137   string type_to_enum(t_type* type);
138   string constant_literal(t_type* type, t_const_value* value);
139   string constant_value(string name, t_type* type, t_const_value* value);
140   string constant_value_with_storage(string name, t_type* type, t_const_value* value);
141   string function_signature(t_function* tfunction);
142   string argument_list(t_struct* tstruct);
143   string xception_list(t_struct* tstruct);
144   string declare_field(t_field* tfield,
145                        bool init = false,
146                        bool pointer = false,
147                        bool constant = false,
148                        bool reference = false);
149   void declare_local_variable(ostream& out, t_type* ttype, string& base_name, bool for_hash_table);
150   void declore_local_variable_for_write(ostream& out, t_type* ttype, string& base_name);
151 
152   /* generation functions */
153   void generate_const_initializer(string name,
154                                   t_type* type,
155                                   t_const_value* value,
156                                   bool top_level = false);
157   void generate_service_helpers(t_service* tservice);
158   void generate_service_client(t_service* tservice);
159   void generate_service_handler(t_service* tservice);
160   void generate_service_processor(t_service* tservice);
161   void generate_service_server(t_service* tservice);
162   void generate_object(t_struct* tstruct);
163   void generate_struct_writer(ostream& out,
164                               t_struct* tstruct,
165                               string this_name,
166                               string this_get = "",
167                               bool is_function = true);
168   void generate_struct_reader(ostream& out,
169                               t_struct* tstruct,
170                               string this_name,
171                               string this_get = "",
172                               bool is_function = true);
173 
174   void generate_serialize_field(ostream& out,
175                                 t_field* tfield,
176                                 string prefix,
177                                 string suffix,
178                                 int error_ret);
179   void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix, int error_ret);
180   void generate_serialize_container(ostream& out, t_type* ttype, string prefix, int error_ret);
181   void generate_serialize_map_element(ostream& out,
182                                       t_map* tmap,
183                                       string key,
184                                       string value,
185                                       int error_ret);
186   void generate_serialize_set_element(ostream& out, t_set* tset, string element, int error_ret);
187   void generate_serialize_list_element(ostream& out,
188                                        t_list* tlist,
189                                        string list,
190                                        string index,
191                                        int error_ret);
192 
193   void generate_deserialize_field(ostream& out,
194                                   t_field* tfield,
195                                   string prefix,
196                                   string suffix,
197                                   int error_ret,
198                                   bool allocate = true);
199   void generate_deserialize_struct(ostream& out,
200                                    t_struct* tstruct,
201                                    string prefix,
202                                    int error_ret,
203                                    bool allocate = true);
204   void generate_deserialize_container(ostream& out, t_type* ttype, string prefix, int error_ret);
205   void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix, int error_ret);
206   void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix, int error_ret);
207   void generate_deserialize_list_element(ostream& out,
208                                          t_list* tlist,
209                                          string prefix,
210                                          string index,
211                                          int error_ret);
212 
213   string generate_new_hash_from_type(t_type* key, t_type* value);
214   string generate_new_array_from_type(t_type* ttype);
215 
216   string generate_free_func_from_type(t_type* ttype);
217   string generate_hash_func_from_type(t_type* ttype);
218   string generate_cmp_func_from_type(t_type* ttype);
219 };
220 
221 /**
222  * Prepare for file generation by opening up the necessary file
223  * output streams.
224  */
init_generator()225 void t_c_glib_generator::init_generator() {
226   /* create output directory */
227   MKDIR(get_out_dir().c_str());
228 
229   string program_name_u = initial_caps_to_underscores(program_name_);
230   string program_name_uc = to_upper_case(program_name_u);
231   string program_name_lc = to_lower_case(program_name_u);
232 
233   /* create output files */
234   string f_types_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.h";
235   f_types_.open(f_types_name.c_str());
236   string f_types_impl_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.c";
237   f_types_impl_.open(f_types_impl_name.c_str());
238 
239   /* add thrift boilerplate headers */
240   f_types_ << autogen_comment();
241   f_types_impl_ << autogen_comment();
242 
243   /* include inclusion guard */
244   f_types_ << "#ifndef " << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << "#define "
245            << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << endl;
246 
247   /* include base types */
248   f_types_ << "/* base includes */" << endl << "#include <glib-object.h>" << endl
249            << "#include <thrift/c_glib/thrift_struct.h>" << endl
250            << "#include <thrift/c_glib/protocol/thrift_protocol.h>" << endl;
251 
252   /* include other thrift includes */
253   const vector<t_program*>& includes = program_->get_includes();
254   if (!includes.empty()) {
255     f_types_ << "/* other thrift includes */" << endl;
256 
257     for (auto include : includes) {
258       const std::string& include_nspace = include->get_namespace("c_glib");
259       std::string include_nspace_prefix =
260         include_nspace.empty() ? "" : initial_caps_to_underscores(include_nspace) + "_";
261 
262       f_types_ << "#include \"" << include_nspace_prefix
263                << initial_caps_to_underscores(include->get_name()) << "_types.h\"" << endl;
264     }
265     f_types_ << endl;
266   }
267 
268   /* include custom headers */
269   const vector<string>& c_includes = program_->get_c_includes();
270   f_types_ << "/* custom thrift includes */" << endl;
271   for (const auto & c_include : c_includes) {
272     if (c_include[0] == '<') {
273       f_types_ << "#include " << c_include << endl;
274     } else {
275       f_types_ << "#include \"" << c_include << "\"" << endl;
276     }
277   }
278   f_types_ << endl;
279 
280   /* include math.h (for "INFINITY") in the implementation file, in case we
281      encounter a struct with a member of type double */
282   f_types_impl_ << endl << "#include <math.h>" << endl;
283 
284   // include the types file
285   f_types_impl_ << endl << "#include \"" << this->nspace_lc << program_name_u << "_types.h\""
286                 << endl << "#include <thrift/c_glib/thrift.h>" << endl << endl;
287 
288   f_types_ << "/* begin types */" << endl << endl;
289 }
290 
291 /**
292  *  Finish up generation and close all file streams.
293  */
close_generator()294 void t_c_glib_generator::close_generator() {
295   string program_name_uc = to_upper_case(initial_caps_to_underscores(program_name_));
296 
297   /* end the header inclusion guard */
298   f_types_ << "#endif /* " << this->nspace_uc << program_name_uc << "_TYPES_H */" << endl;
299 
300   /* close output file */
301   f_types_.close();
302   f_types_impl_.close();
303 }
304 
305 /**
306  * Generates a Thrift typedef in C code.  For example:
307  *
308  * Thrift:
309  * typedef map<i32,i32> SomeMap
310  *
311  * C:
312  * typedef GHashTable * ThriftSomeMap;
313  */
generate_typedef(t_typedef * ttypedef)314 void t_c_glib_generator::generate_typedef(t_typedef* ttypedef) {
315   f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " << this->nspace
316            << ttypedef->get_symbolic() << ";" << endl << endl;
317 }
318 
319 /**
320  * Generates a C enumeration.  For example:
321  *
322  * Thrift:
323  * enum MyEnum {
324  *   ONE = 1,
325  *   TWO
326  * }
327  *
328  * C:
329  * enum _ThriftMyEnum {
330  *   THRIFT_MY_ENUM_ONE = 1,
331  *   THRIFT_MY_ENUM_TWO
332  * };
333  * typedef enum _ThriftMyEnum ThriftMyEnum;
334  */
generate_enum(t_enum * tenum)335 void t_c_glib_generator::generate_enum(t_enum* tenum) {
336   string name = tenum->get_name();
337   string name_uc = to_upper_case(initial_caps_to_underscores(name));
338 
339   f_types_ << indent() << "enum _" << this->nspace << name << " {" << endl;
340 
341   indent_up();
342 
343   vector<t_enum_value*> constants = tenum->get_constants();
344   vector<t_enum_value*>::iterator c_iter;
345   bool first = true;
346 
347   /* output each of the enumeration elements */
348   for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
349     if (first) {
350       first = false;
351     } else {
352       f_types_ << "," << endl;
353     }
354 
355     f_types_ << indent() << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name();
356     f_types_ << " = " << (*c_iter)->get_value();
357   }
358 
359   indent_down();
360   f_types_ << endl << "};" << endl << "typedef enum _" << this->nspace << name << " "
361            << this->nspace << name << ";" << endl << endl;
362 
363   f_types_ << "/* return the name of the constant */" << endl;
364   f_types_ << "const char *" << endl;
365   f_types_ << "toString_" << name << "(int value); " << endl << endl;
366   ;
367   f_types_impl_ << "/* return the name of the constant */" << endl;
368   f_types_impl_ << "const char *" << endl;
369   f_types_impl_ << "toString_" << name << "(int value) " << endl;
370   f_types_impl_ << "{" << endl;
371   f_types_impl_ << "  static __thread char buf[16] = {0};" << endl;
372   f_types_impl_ << "  switch(value) {" << endl;
373   std::set<int> done;
374   for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
375     int value = (*c_iter)->get_value();
376     // Skipping duplicate value
377     if (done.find(value) == done.end()) {
378       done.insert(value);
379       f_types_impl_ << "  case " << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name()
380                     << ":"
381                     << "return \"" << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name()
382                     << "\";" << endl;
383     }
384   }
385   f_types_impl_ << "  default: g_snprintf(buf, 16, \"%d\", value); return buf;" << endl;
386   f_types_impl_ << "  }" << endl;
387   f_types_impl_ << "}" << endl << endl;
388 }
389 
390 /**
391  * Generates Thrift constants in C code.
392  */
generate_consts(vector<t_const * > consts)393 void t_c_glib_generator::generate_consts(vector<t_const*> consts) {
394   f_types_ << "/* constants */" << endl;
395   f_types_impl_ << "/* constants */" << endl;
396 
397   vector<t_const*>::iterator c_iter;
398   for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
399     string name = (*c_iter)->get_name();
400     string name_uc = to_upper_case(name);
401     string name_lc = to_lower_case(name);
402     t_type* type = (*c_iter)->get_type();
403     t_const_value* value = (*c_iter)->get_value();
404 
405     if (is_complex_type(type)) {
406       f_types_ << type_name(type) << indent() << this->nspace_lc << name_lc
407                << "_constant();" << endl;
408     }
409 
410     f_types_ << indent() << "#define " << this->nspace_uc << name_uc << " "
411              << constant_value(name_lc, type, value) << endl;
412 
413     generate_const_initializer(name_lc, type, value, true);
414   }
415 
416   f_types_ << endl;
417   f_types_impl_ << endl;
418 }
419 
420 /**
421  * Generate Thrift structs in C code, as GObjects.  Example:
422  *
423  * Thrift:
424  * struct Bonk
425  * {
426  *   1: string message,
427  *   2: i32 type
428  * }
429  *
430  * C GObject instance header:
431  * struct _ThriftBonk
432  * {
433  *   GObject parent;
434  *
435  *   gchar * message;
436  *   gint32 type;
437  * };
438  * typedef struct _ThriftBonk ThriftBonk
439  * // ... additional GObject boilerplate ...
440  */
generate_struct(t_struct * tstruct)441 void t_c_glib_generator::generate_struct(t_struct* tstruct) {
442   f_types_ << "/* struct " << tstruct->get_name() << " */" << endl;
443   generate_object(tstruct);
444 }
445 
446 /**
447  * Generate C code to represent Thrift services.  Creates a new GObject
448  * which can be used to access the service.
449  */
generate_service(t_service * tservice)450 void t_c_glib_generator::generate_service(t_service* tservice) {
451   string svcname_u = initial_caps_to_underscores(tservice->get_name());
452   string svcname_uc = this->nspace_uc + to_upper_case(svcname_u);
453   string filename = this->nspace_lc + to_lower_case(svcname_u);
454 
455   // make output files
456   string f_header_name = get_out_dir() + filename + ".h";
457   f_header_.open(f_header_name.c_str());
458 
459   string program_name_u = initial_caps_to_underscores(program_name_);
460   string program_name_lc = to_lower_case(program_name_u);
461 
462   // add header file boilerplate
463   f_header_ << autogen_comment();
464 
465   // add an inclusion guard
466   f_header_ << "#ifndef " << svcname_uc << "_H" << endl << "#define " << svcname_uc << "_H" << endl
467             << endl;
468 
469   // add standard includes
470   f_header_ << "#include <thrift/c_glib/processor/thrift_dispatch_processor.h>" << endl << endl;
471   f_header_ << "#include \"" << this->nspace_lc << program_name_lc << "_types.h\"" << endl;
472 
473   // if we are inheriting from another service, include its header
474   t_service* extends_service = tservice->get_extends();
475   if (extends_service != nullptr) {
476     f_header_ << "#include \"" << this->nspace_lc
477               << to_lower_case(initial_caps_to_underscores(extends_service->get_name())) << ".h\""
478               << endl;
479   }
480   f_header_ << endl;
481 
482   // create the service implementation
483   string f_service_name = get_out_dir() + filename + ".c";
484   f_service_.open(f_service_name.c_str());
485 
486   // add the boilerplace header
487   f_service_ << autogen_comment();
488 
489   // include the headers
490   f_service_ << "#include <string.h>" << endl << "#include <thrift/c_glib/thrift.h>" << endl
491              << "#include <thrift/c_glib/thrift_application_exception.h>" << endl << "#include \""
492              << filename << ".h\"" << endl << endl;
493 
494   // generate the service-helper classes
495   generate_service_helpers(tservice);
496 
497   // generate the client objects
498   generate_service_client(tservice);
499 
500   // generate the server objects
501   generate_service_server(tservice);
502 
503   // end the header inclusion guard
504   f_header_ << "#endif /* " << svcname_uc << "_H */" << endl;
505 
506   // close the files
507   f_service_.close();
508   f_header_.close();
509 }
510 
511 /**
512  *
513  */
generate_xception(t_struct * tstruct)514 void t_c_glib_generator::generate_xception(t_struct* tstruct) {
515   string name = tstruct->get_name();
516   string name_u = initial_caps_to_underscores(name);
517   string name_lc = to_lower_case(name_u);
518   string name_uc = to_upper_case(name_u);
519 
520   generate_object(tstruct);
521 
522   f_types_ << "/* exception */" << endl
523            << "typedef enum" << endl
524            << "{" << endl;
525   indent_up();
526   f_types_ << indent() << this->nspace_uc << name_uc << "_ERROR_CODE" << endl;
527   indent_down();
528   f_types_ << "} " << this->nspace << name << "Error;" << endl
529            << endl
530            << "GQuark " << this->nspace_lc << name_lc
531            << "_error_quark (void);" << endl
532            << "#define " << this->nspace_uc << name_uc << "_ERROR ("
533            << this->nspace_lc << name_lc << "_error_quark())" << endl
534            << endl
535            << endl;
536 
537   f_types_impl_ << "/* define the GError domain for exceptions */" << endl << "#define "
538                 << this->nspace_uc << name_uc << "_ERROR_DOMAIN \"" << this->nspace_lc << name_lc
539                 << "_error_quark\"" << endl << "GQuark" << endl << this->nspace_lc << name_lc
540                 << "_error_quark (void)" << endl << "{" << endl
541                 << "  return g_quark_from_static_string (" << this->nspace_uc << name_uc
542                 << "_ERROR_DOMAIN);" << endl << "}" << endl << endl;
543 }
544 
545 /********************
546  * HELPER FUNCTIONS *
547  ********************/
548 
549 /**
550  * Returns true if ttype is not a primitive.
551  */
is_complex_type(t_type * ttype)552 bool t_c_glib_generator::is_complex_type(t_type* ttype) {
553   ttype = get_true_type(ttype);
554 
555   return ttype->is_container() || ttype->is_struct() || ttype->is_xception();
556 }
557 
is_numeric(t_type * ttype)558 bool t_c_glib_generator::is_numeric(t_type* ttype) {
559   return ttype->is_enum() || (ttype->is_base_type() && !ttype->is_string());
560 }
561 
562 /**
563  * Maps a Thrift t_type to a C type.
564  */
type_name(t_type * ttype,bool in_typedef,bool is_const)565 string t_c_glib_generator::type_name(t_type* ttype, bool in_typedef, bool is_const) {
566   if (ttype->is_base_type()) {
567     string bname = base_type_name(ttype);
568 
569     if (is_const) {
570       return "const " + bname;
571     } else {
572       return bname;
573     }
574   }
575 
576   if (ttype->is_container()) {
577     string cname;
578 
579     t_container* tcontainer = (t_container*)ttype;
580     if (tcontainer->has_cpp_name()) {
581       cname = tcontainer->get_cpp_name();
582     } else if (ttype->is_map()) {
583       cname = "GHashTable";
584     } else if (ttype->is_set()) {
585       // since a set requires unique elements, use a GHashTable, and
586       // populate the keys and values with the same data, using keys for
587       // the actual writes and reads.
588       // TODO: discuss whether or not to implement TSet, THashSet or GHashSet
589       cname = "GHashTable";
590     } else if (ttype->is_list()) {
591       t_type* etype = get_true_type(((t_list*)ttype)->get_elem_type());
592       if (etype->is_void()) {
593         throw std::runtime_error("compiler error: list element type cannot be void");
594       }
595       // TODO: investigate other implementations besides GPtrArray
596       cname = is_numeric(etype) ? "GArray" : "GPtrArray";
597     }
598 
599     /* Omit the dereference operator if we are aliasing this type within a
600        typedef, to allow the type to be used more naturally in client code;
601        otherwise, include it */
602     if (!in_typedef) {
603       cname += " *";
604     }
605 
606     if (is_const) {
607       return "const " + cname;
608     } else {
609       return cname;
610     }
611   }
612 
613   // check for a namespace
614   t_program* tprogram = ttype->get_program();
615   string pname = (tprogram ? tprogram->get_namespace("c_glib") : "") + ttype->get_name();
616 
617   if (is_complex_type(ttype)) {
618     pname += " *";
619   }
620 
621   if (is_const) {
622     return "const " + pname;
623   } else {
624     return pname;
625   }
626 }
627 
628 /**
629  * Maps a Thrift primitive to the type needed to hold its value when used as an
630  * object property.
631  *
632  * This method is needed because all integer properties of width less than 64
633  * bits map to the same type, gint, as opposed to their width-specific type
634  * (gint8, gint16 or gint32).
635  */
property_type_name(t_type * ttype,bool in_typedef,bool is_const)636 string t_c_glib_generator::property_type_name(t_type* ttype, bool in_typedef, bool is_const) {
637   string result;
638 
639   if (ttype->is_base_type()) {
640     switch (((t_base_type*)ttype)->get_base()) {
641     case t_base_type::TYPE_I8:
642     case t_base_type::TYPE_I16:
643     case t_base_type::TYPE_I32:
644       if (is_const) {
645         result = "const gint";
646       } else {
647         result = "gint";
648       }
649       break;
650 
651     default:
652       result = type_name(ttype, in_typedef, is_const);
653     }
654   } else {
655     result = type_name(ttype, in_typedef, is_const);
656   }
657 
658   return result;
659 }
660 
661 /**
662  * Maps a Thrift primitive to a C primitive.
663  */
base_type_name(t_type * type)664 string t_c_glib_generator::base_type_name(t_type* type) {
665   if (type->is_enum()) {
666     return type_name(type);
667   }
668   if (!type->is_base_type()) {
669     throw std::invalid_argument("Only base types are suppported.");
670   }
671   t_base_type* base_type = reinterpret_cast<t_base_type*>(type);
672   t_base_type::t_base tbase = base_type->get_base();
673   switch (tbase) {
674   case t_base_type::TYPE_VOID:
675     return "void";
676   case t_base_type::TYPE_STRING:
677     if (base_type->is_binary()) {
678       return "GByteArray *";
679     } else {
680       return "gchar *";
681     }
682   case t_base_type::TYPE_BOOL:
683     return "gboolean";
684   case t_base_type::TYPE_I8:
685     return "gint8";
686   case t_base_type::TYPE_I16:
687     return "gint16";
688   case t_base_type::TYPE_I32:
689     return "gint32";
690   case t_base_type::TYPE_I64:
691     return "gint64";
692   case t_base_type::TYPE_DOUBLE:
693     return "gdouble";
694   default:
695     throw std::logic_error("compiler error: no C base type name for base type "
696                            + t_base_type::t_base_name(tbase));
697   }
698 }
699 
700 /**
701  * Returns a member of the ThriftType C enumeration in thrift_protocol.h
702  * for a Thrift type.
703  */
type_to_enum(t_type * type)704 string t_c_glib_generator::type_to_enum(t_type* type) {
705   type = get_true_type(type);
706 
707   if (type->is_base_type()) {
708     t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
709 
710     switch (tbase) {
711     case t_base_type::TYPE_VOID:
712       throw "NO T_VOID CONSTRUCT";
713     case t_base_type::TYPE_STRING:
714       return "T_STRING";
715     case t_base_type::TYPE_BOOL:
716       return "T_BOOL";
717     case t_base_type::TYPE_I8:
718       return "T_BYTE";
719     case t_base_type::TYPE_I16:
720       return "T_I16";
721     case t_base_type::TYPE_I32:
722       return "T_I32";
723     case t_base_type::TYPE_I64:
724       return "T_I64";
725     case t_base_type::TYPE_DOUBLE:
726       return "T_DOUBLE";
727     default:
728       break;
729     }
730   } else if (type->is_enum()) {
731     return "T_I32";
732   } else if (type->is_struct()) {
733     return "T_STRUCT";
734   } else if (type->is_xception()) {
735     return "T_STRUCT";
736   } else if (type->is_map()) {
737     return "T_MAP";
738   } else if (type->is_set()) {
739     return "T_SET";
740   } else if (type->is_list()) {
741     return "T_LIST";
742   }
743 
744   throw "INVALID TYPE IN type_to_enum: " + type->get_name();
745 }
746 
747 /**
748  * Returns a Thrift constant formatted as a literal for inclusion in C code.
749  */
constant_literal(t_type * type,t_const_value * value)750 string t_c_glib_generator::constant_literal(t_type* type, t_const_value* value) {
751   ostringstream render;
752 
753   if (type->is_base_type()) {
754     /* primitives */
755     t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
756 
757     switch (tbase) {
758     case t_base_type::TYPE_STRING:
759       render << "\"" + value->get_string() + "\"";
760       break;
761     case t_base_type::TYPE_BOOL:
762       render << ((value->get_integer() != 0) ? "TRUE" : "FALSE");
763       break;
764     case t_base_type::TYPE_I8:
765     case t_base_type::TYPE_I16:
766     case t_base_type::TYPE_I32:
767     case t_base_type::TYPE_I64:
768       render << value->get_integer();
769       break;
770     case t_base_type::TYPE_DOUBLE:
771       render << value->get_double();
772       break;
773     default:
774       throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
775     }
776   } else {
777     t_const_value::t_const_value_type value_type = value->get_type();
778 
779     switch (value_type) {
780     case t_const_value::CV_IDENTIFIER:
781       render << value->get_integer();
782       break;
783     case t_const_value::CV_LIST:
784       render << "{ ";
785       {
786         t_type* elem_type = ((t_list*)type)->get_elem_type();
787         const vector<t_const_value*>& list = value->get_list();
788         vector<t_const_value*>::const_iterator list_iter;
789 
790         if (list.size() > 0) {
791           list_iter = list.begin();
792           render << constant_literal(elem_type, *list_iter);
793 
794           while (++list_iter != list.end()) {
795             render << ", " << constant_literal(elem_type, *list_iter);
796           }
797         }
798       }
799       render << " }";
800       break;
801     case t_const_value::CV_MAP:
802     default:
803       render << "NULL /* not supported */";
804     }
805   }
806 
807   return render.str();
808 }
809 
810 /**
811  * Returns C code that represents a Thrift constant.
812  */
constant_value(string name,t_type * type,t_const_value * value)813 string t_c_glib_generator::constant_value(string name, t_type* type, t_const_value* value) {
814   ostringstream render;
815 
816   if (type->is_base_type()) {
817     /* primitives */
818     t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
819     switch (tbase) {
820     case t_base_type::TYPE_STRING:
821       render << "g_strdup (\"" + value->get_string() + "\")";
822       break;
823     case t_base_type::TYPE_BOOL:
824       render << ((value->get_integer() != 0) ? 1 : 0);
825       break;
826     case t_base_type::TYPE_I8:
827     case t_base_type::TYPE_I16:
828     case t_base_type::TYPE_I32:
829       render << value->get_integer();
830       break;
831     case t_base_type::TYPE_I64:
832       render << "G_GINT64_CONSTANT (" << value->get_integer() << ")";
833       break;
834     case t_base_type::TYPE_DOUBLE:
835       if (value->get_type() == t_const_value::CV_INTEGER) {
836         render << value->get_integer();
837       } else {
838         render << value->get_double();
839       }
840       break;
841     default:
842       throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
843     }
844   } else if (type->is_enum()) {
845     render << "(" << type_name(type) << ")" << value->get_integer();
846   } else if (is_complex_type(type)) {
847     render << "(" << this->nspace_lc << to_lower_case(name) << "_constant())";
848   } else {
849     render << "NULL /* not supported */";
850   }
851 
852   return render.str();
853 }
854 
855 /**
856  * Renders a function signature of the form 'type name(args)'
857  *
858  * @param tfunction Function definition
859  * @return String of rendered function definition
860  */
function_signature(t_function * tfunction)861 string t_c_glib_generator::function_signature(t_function* tfunction) {
862   t_type* ttype = tfunction->get_returntype();
863   t_struct* arglist = tfunction->get_arglist();
864   t_struct* xlist = tfunction->get_xceptions();
865   string fname = initial_caps_to_underscores(tfunction->get_name());
866 
867   bool has_return = !ttype->is_void();
868   bool has_args = arglist->get_members().size() == 0;
869   bool has_xceptions = xlist->get_members().size() == 0;
870   return "gboolean " + this->nspace_lc + fname + " (" + this->nspace + service_name_ + "If * iface"
871          + (has_return ? ", " + type_name(ttype) + "* _return" : "")
872          + (has_args ? "" : (", " + argument_list(arglist)))
873          + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError ** error)";
874 }
875 
876 /**
877  * Renders a field list
878  *
879  * @param tstruct The struct definition
880  * @return Comma sepearated list of all field names in that struct
881  */
argument_list(t_struct * tstruct)882 string t_c_glib_generator::argument_list(t_struct* tstruct) {
883   string result = "";
884 
885   const vector<t_field*>& fields = tstruct->get_members();
886   vector<t_field*>::const_iterator f_iter;
887   bool first = true;
888   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
889     if (first) {
890       first = false;
891     } else {
892       result += ", ";
893     }
894     result += type_name((*f_iter)->get_type(), false, true) + " " + (*f_iter)->get_name();
895   }
896   return result;
897 }
898 
899 /**
900  * Renders mutable exception lists
901  *
902  * @param tstruct The struct definition
903  * @return Comma sepearated list of all field names in that struct
904  */
xception_list(t_struct * tstruct)905 string t_c_glib_generator::xception_list(t_struct* tstruct) {
906   string result = "";
907 
908   const vector<t_field*>& fields = tstruct->get_members();
909   vector<t_field*>::const_iterator f_iter;
910   bool first = true;
911   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
912     if (first) {
913       first = false;
914     } else {
915       result += ", ";
916     }
917     result += type_name((*f_iter)->get_type(), false, false) + "* " + (*f_iter)->get_name();
918   }
919   return result;
920 }
921 
922 /**
923  * Declares a field, including any necessary initialization.
924  */
declare_field(t_field * tfield,bool init,bool pointer,bool constant,bool reference)925 string t_c_glib_generator::declare_field(t_field* tfield,
926                                          bool init,
927                                          bool pointer,
928                                          bool constant,
929                                          bool reference) {
930   string result = "";
931   if (constant) {
932     result += "const ";
933   }
934   result += type_name(tfield->get_type());
935   if (pointer) {
936     result += "*";
937   }
938   if (reference) {
939     result += "*";
940   }
941   result += " " + tfield->get_name();
942   if (init) {
943     t_type* type = get_true_type(tfield->get_type());
944 
945     if (type->is_base_type()) {
946       t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
947       switch (tbase) {
948       case t_base_type::TYPE_VOID:
949         break;
950       case t_base_type::TYPE_BOOL:
951       case t_base_type::TYPE_I8:
952       case t_base_type::TYPE_I16:
953       case t_base_type::TYPE_I32:
954       case t_base_type::TYPE_I64:
955         result += " = 0";
956         break;
957       case t_base_type::TYPE_DOUBLE:
958         result += " = (gdouble) 0";
959         break;
960       case t_base_type::TYPE_STRING:
961         result += " = NULL";
962         break;
963       default:
964         throw "compiler error: no C intializer for base type " + t_base_type::t_base_name(tbase);
965       }
966     } else if (type->is_enum()) {
967       result += " = (" + type_name(type) + ") 0";
968     } else if (type->is_struct() || type->is_container()) {
969       result += " = NULL";
970     }
971   }
972 
973   if (!reference) {
974     result += ";";
975   }
976 
977   return result;
978 }
979 
constant_value_with_storage(string fname,t_type * etype,t_const_value * value)980 string t_c_glib_generator::constant_value_with_storage(string fname,
981                                                        t_type* etype,
982                                                        t_const_value* value) {
983   ostringstream render;
984   if (is_numeric(etype)) {
985     render << "    " << type_name(etype) << " *" << fname << " = "
986            << "g_new (" << base_type_name(etype) << ", 1);" << endl
987            << "    *" << fname << " = " << constant_value(fname, (t_type*)etype, value) << ";"
988            << endl;
989   } else {
990     render << "    " << type_name(etype) << " " << fname << " = "
991            << constant_value(fname, (t_type*)etype, value) << ";" << endl;
992   }
993   return render.str();
994 }
995 
996 /**
997  * Generates C code that initializes complex constants.
998  */
generate_const_initializer(string name,t_type * type,t_const_value * value,bool top_level)999 void t_c_glib_generator::generate_const_initializer(string name,
1000                                                     t_type* type,
1001                                                     t_const_value* value,
1002                                                     bool top_level) {
1003   string name_u = initial_caps_to_underscores(name);
1004   string name_lc = to_lower_case(name_u);
1005   string type_u = initial_caps_to_underscores(type->get_name());
1006   string type_uc = to_upper_case(type_u);
1007   string maybe_static = top_level ? "" : "static ";
1008 
1009   if (type->is_struct() || type->is_xception()) {
1010     const vector<t_field*>& fields = ((t_struct*)type)->get_members();
1011     vector<t_field*>::const_iterator f_iter;
1012     const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
1013     map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
1014     ostringstream initializers;
1015 
1016     // initialize any constants that may be referenced by this initializer
1017     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
1018       t_type* field_type = nullptr;
1019       string field_name = "";
1020 
1021       for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1022         if ((*f_iter)->get_name() == v_iter->first->get_string()) {
1023           field_type = (*f_iter)->get_type();
1024           field_name = (*f_iter)->get_name();
1025           break;
1026         }
1027       }
1028       if (field_type == nullptr) {
1029         throw "type error: " + type->get_name() + " has no field "
1030           + v_iter->first->get_string();
1031       }
1032       field_name = tmp(field_name);
1033 
1034       generate_const_initializer(name + "_constant_" + field_name,
1035                                  field_type,
1036                                  v_iter->second);
1037       initializers << "    constant->" << v_iter->first->get_string() << " = "
1038                    << constant_value(name + "_constant_" + field_name,
1039                                      field_type,
1040                                      v_iter->second) << ";" << endl
1041                    << "    constant->__isset_" << v_iter->first->get_string()
1042                    << " = TRUE;" << endl;
1043     }
1044 
1045     // implement the initializer
1046     f_types_impl_ << maybe_static << this->nspace << type->get_name() << " *"
1047                   << endl
1048                   << this->nspace_lc << name_lc << "_constant (void)" << endl;
1049     scope_up(f_types_impl_);
1050     f_types_impl_ << indent() << "static " << this->nspace << type->get_name()
1051                   << " *constant = NULL;" << endl
1052                   << indent() << "if (constant == NULL)" << endl;
1053     scope_up(f_types_impl_);
1054     f_types_impl_ << indent() << "constant = g_object_new (" << this->nspace_uc
1055                   << "TYPE_" << type_uc << ", NULL);" << endl
1056                   << initializers.str();
1057     scope_down(f_types_impl_);
1058 
1059     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
1060       t_type* field_type = nullptr;
1061       string field_name = "";
1062 
1063       for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1064         if ((*f_iter)->get_name() == v_iter->first->get_string()) {
1065           field_type = (*f_iter)->get_type();
1066           field_name = (*f_iter)->get_name();
1067           break;
1068         }
1069       }
1070       if (field_type == nullptr) {
1071         throw "type error: " + type->get_name() + " has no field "
1072           + v_iter->first->get_string();
1073       }
1074       field_name = tmp(field_name);
1075     }
1076 
1077     f_types_impl_ << indent() << "return constant;" << endl;
1078     scope_down(f_types_impl_);
1079     f_types_impl_ << endl;
1080   } else if (type->is_list()) {
1081     string list_type = "GPtrArray *";
1082     string free_func
1083         = generate_free_func_from_type(reinterpret_cast<t_list*>(type)->get_elem_type());
1084     string list_initializer = "g_ptr_array_new_with_free_func (" + free_func + ");";
1085     string list_appender = "g_ptr_array_add";
1086     bool list_variable = false;
1087 
1088     t_type* etype = ((t_list*)type)->get_elem_type();
1089     const vector<t_const_value*>& val = value->get_list();
1090     vector<t_const_value*>::const_iterator v_iter;
1091     ostringstream initializers;
1092     ostringstream appenders;
1093 
1094     list_initializer = generate_new_array_from_type(etype);
1095     if (etype->is_base_type()) {
1096       t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
1097       switch (tbase) {
1098       case t_base_type::TYPE_VOID:
1099         throw "compiler error: cannot determine array type";
1100       case t_base_type::TYPE_BOOL:
1101       case t_base_type::TYPE_I8:
1102       case t_base_type::TYPE_I16:
1103       case t_base_type::TYPE_I32:
1104       case t_base_type::TYPE_I64:
1105       case t_base_type::TYPE_DOUBLE:
1106         list_type = "GArray *";
1107         list_appender = "g_array_append_val";
1108         list_variable = true;
1109         break;
1110       case t_base_type::TYPE_STRING:
1111         break;
1112       default:
1113         throw "compiler error: no array info for type";
1114       }
1115     } else if (etype->is_enum()) {
1116       list_type = "GArray *";
1117       list_appender = "g_array_append_val";
1118       list_variable = true;
1119     }
1120 
1121     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
1122       string fname = tmp(name);
1123 
1124       generate_const_initializer(fname, etype, (*v_iter));
1125       if (list_variable) {
1126         initializers << "    " << type_name(etype) << " " << fname << " = "
1127                      << constant_value(fname, (t_type*)etype, (*v_iter)) << ";"
1128                      << endl;
1129         appenders << "    " << list_appender << "(constant, " << fname << ");"
1130                   << endl;
1131       } else {
1132         appenders << "    " << list_appender << "(constant, "
1133                   << constant_value(fname, (t_type*)etype, (*v_iter)) << ");"
1134                   << endl;
1135       }
1136     }
1137 
1138     f_types_impl_ << maybe_static << list_type << endl
1139                   << this->nspace_lc << name_lc << "_constant (void)" << endl;
1140     scope_up(f_types_impl_);
1141     f_types_impl_ << indent() << "static " << list_type << " constant = NULL;"
1142                   << endl
1143                   << indent() << "if (constant == NULL)" << endl;
1144     scope_up(f_types_impl_);
1145     if (!initializers.str().empty()) {
1146       f_types_impl_ << initializers.str()
1147                     << endl;
1148     }
1149     f_types_impl_ << indent() << "constant = " << list_initializer << endl
1150                   << appenders.str();
1151     scope_down(f_types_impl_);
1152     f_types_impl_ << indent() << "return constant;" << endl;
1153     scope_down(f_types_impl_);
1154     f_types_impl_ << endl;
1155   } else if (type->is_set()) {
1156     t_type* etype = ((t_set*)type)->get_elem_type();
1157     const vector<t_const_value*>& val = value->get_list();
1158     vector<t_const_value*>::const_iterator v_iter;
1159     ostringstream initializers;
1160     ostringstream appenders;
1161 
1162     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
1163       string fname = tmp(name);
1164       string ptr = is_numeric(etype) ? "*" : "";
1165       generate_const_initializer(fname, etype, (*v_iter));
1166       initializers << constant_value_with_storage(fname, (t_type*)etype, *v_iter);
1167       appenders << "    g_hash_table_insert (constant, " << fname << ", 0);" << endl;
1168     }
1169 
1170     f_types_impl_ << maybe_static << "GHashTable *" << endl
1171                   << this->nspace_lc << name_lc << "_constant (void)" << endl;
1172     scope_up(f_types_impl_);
1173     f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl
1174                   << indent() << "if (constant == NULL)" << endl;
1175     scope_up(f_types_impl_);
1176     f_types_impl_ << initializers.str() << endl
1177                   << indent() << "constant = " << generate_new_hash_from_type(etype, nullptr) << endl
1178                   << appenders.str();
1179     scope_down(f_types_impl_);
1180     f_types_impl_ << indent() << "return constant;" << endl;
1181     scope_down(f_types_impl_);
1182     f_types_impl_ << endl;
1183   } else if (type->is_map()) {
1184     t_type* ktype = ((t_map*)type)->get_key_type();
1185     t_type* vtype = ((t_map*)type)->get_val_type();
1186     const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
1187     map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
1188     ostringstream initializers;
1189     ostringstream appenders;
1190 
1191     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
1192       string fname = tmp(name);
1193       string kname = fname + "key";
1194       string vname = fname + "val";
1195       generate_const_initializer(kname, ktype, v_iter->first);
1196       generate_const_initializer(vname, vtype, v_iter->second);
1197 
1198       initializers << constant_value_with_storage(kname, (t_type*)ktype, v_iter->first);
1199       initializers << constant_value_with_storage(vname, (t_type*)vtype, v_iter->second);
1200       appenders << "    g_hash_table_insert (constant, " << kname << ", " << vname << ");" << endl;
1201     }
1202 
1203     f_types_impl_ << maybe_static << "GHashTable *" << endl
1204                   << this->nspace_lc << name_lc << "_constant (void)" << endl;
1205     scope_up(f_types_impl_);
1206     f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl
1207                   << indent() << "if (constant == NULL)" << endl;
1208     scope_up(f_types_impl_);
1209     f_types_impl_ << initializers.str() << endl
1210                   << indent() << "constant = " << generate_new_hash_from_type(ktype, vtype) << endl
1211                   << appenders.str();
1212     scope_down(f_types_impl_);
1213     f_types_impl_ << indent() << "return constant;" << endl;
1214     scope_down(f_types_impl_);
1215     f_types_impl_ << endl;
1216   }
1217 }
1218 
1219 /**
1220  * Generates helper classes for a service, consisting of a ThriftStruct subclass
1221  * for the arguments to and the result from each method.
1222  *
1223  * @param tservice The service for which to generate helper classes
1224  */
generate_service_helpers(t_service * tservice)1225 void t_c_glib_generator::generate_service_helpers(t_service* tservice) {
1226   vector<t_function*> functions = tservice->get_functions();
1227   vector<t_function*>::iterator function_iter;
1228 
1229   // Iterate through the service's methods
1230   for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
1231     string function_name = (*function_iter)->get_name();
1232     t_struct* arg_list = (*function_iter)->get_arglist();
1233     string arg_list_name_orig = arg_list->get_name();
1234 
1235     // Generate the arguments class
1236     arg_list->set_name(tservice->get_name() + underscores_to_initial_caps(function_name) + "Args");
1237     generate_struct(arg_list);
1238 
1239     arg_list->set_name(arg_list_name_orig);
1240 
1241     // Generate the result class
1242     if (!(*function_iter)->is_oneway()) {
1243       t_struct result(program_,
1244                       tservice->get_name() + underscores_to_initial_caps(function_name) + "Result");
1245       t_field success((*function_iter)->get_returntype(), "success", 0);
1246       success.set_req(t_field::T_OPTIONAL);
1247       if (!(*function_iter)->get_returntype()->is_void()) {
1248         result.append(&success);
1249       }
1250 
1251       t_struct* xs = (*function_iter)->get_xceptions();
1252       const vector<t_field*>& fields = xs->get_members();
1253       vector<t_field*>::const_iterator field_iter;
1254       for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) {
1255         (*field_iter)->set_req(t_field::T_OPTIONAL);
1256         result.append(*field_iter);
1257       }
1258 
1259       generate_struct(&result);
1260     }
1261   }
1262 }
1263 
1264 /**
1265  * Generates C code that represents a Thrift service client.
1266  */
generate_service_client(t_service * tservice)1267 void t_c_glib_generator::generate_service_client(t_service* tservice) {
1268   /* get some C friendly service names */
1269   string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
1270   string service_name_uc = to_upper_case(service_name_lc);
1271 
1272   string parent_service_name;
1273   string parent_service_name_lc;
1274   string parent_service_name_uc;
1275 
1276   string parent_class_name = "GObject";
1277   string parent_type_name = "G_TYPE_OBJECT";
1278 
1279   // The service this service extends, or nullptr if it extends no
1280   // service
1281   t_service* extends_service = tservice->get_extends();
1282   if (extends_service) {
1283     // The name of the parent service
1284     parent_service_name = extends_service->get_name();
1285     parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name));
1286     parent_service_name_uc = to_upper_case(parent_service_name_lc);
1287 
1288     // The names of the client class' parent class and type
1289     parent_class_name = this->nspace + parent_service_name + "Client";
1290     parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_CLIENT";
1291   }
1292 
1293   // The base service (the topmost in the "extends" hierarchy), on
1294   // whose client class the "input_protocol" and "output_protocol"
1295   // properties are defined
1296   t_service* base_service = tservice;
1297   while (base_service->get_extends()) {
1298     base_service = base_service->get_extends();
1299   }
1300 
1301   string base_service_name = base_service->get_name();
1302   string base_service_name_lc = to_lower_case(initial_caps_to_underscores(base_service_name));
1303   string base_service_name_uc = to_upper_case(base_service_name_lc);
1304 
1305   // Generate the client interface dummy object in the header.
1306   f_header_ << "/* " << service_name_ << " service interface */" << endl << "typedef struct _"
1307             << this->nspace << service_name_ << "If " << this->nspace << service_name_ << "If; "
1308             << " /* dummy object */" << endl << endl;
1309 
1310   // Generate the client interface object in the header.
1311   f_header_ << "struct _" << this->nspace << service_name_ << "IfInterface" << endl << "{" << endl
1312             << "  GTypeInterface parent;" << endl << endl;
1313 
1314   /* write out the functions for this interface */
1315   indent_up();
1316   vector<t_function*> functions = tservice->get_functions();
1317   vector<t_function*>::const_iterator f_iter;
1318   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1319     /* make the function name C friendly */
1320     string funname = initial_caps_to_underscores((*f_iter)->get_name());
1321     t_type* ttype = (*f_iter)->get_returntype();
1322     t_struct* arglist = (*f_iter)->get_arglist();
1323     t_struct* xlist = (*f_iter)->get_xceptions();
1324     bool has_return = !ttype->is_void();
1325     bool has_args = arglist->get_members().size() == 0;
1326     bool has_xceptions = xlist->get_members().size() == 0;
1327 
1328     string params = "(" + this->nspace + service_name_ + "If *iface"
1329                     + (has_return ? ", " + type_name(ttype) + "* _return" : "")
1330                     + (has_args ? "" : (", " + argument_list(arglist)))
1331                     + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)";
1332 
1333     indent(f_header_) << "gboolean (*" << funname << ") " << params << ";" << endl;
1334   }
1335   indent_down();
1336 
1337   f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "IfInterface "
1338             << this->nspace << service_name_ << "IfInterface;" << endl << endl;
1339 
1340   // generate all the interface boilerplate
1341   f_header_ << "GType " << this->nspace_lc << service_name_lc << "_if_get_type (void);" << endl
1342             << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF "
1343             << "(" << this->nspace_lc << service_name_lc << "_if_get_type())" << endl << "#define "
1344             << this->nspace_uc << service_name_uc << "_IF(obj) "
1345             << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
1346             << service_name_uc << "_IF, " << this->nspace << service_name_ << "If))" << endl
1347             << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_IF(obj) "
1348             << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
1349             << service_name_uc << "_IF))" << endl << "#define " << this->nspace_uc
1350             << service_name_uc << "_IF_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), "
1351             << this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << this->nspace
1352             << service_name_ << "IfInterface))" << endl << endl;
1353 
1354   // write out all the interface function prototypes
1355   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1356     /* make the function name C friendly */
1357     string funname = initial_caps_to_underscores((*f_iter)->get_name());
1358     t_type* ttype = (*f_iter)->get_returntype();
1359     t_struct* arglist = (*f_iter)->get_arglist();
1360     t_struct* xlist = (*f_iter)->get_xceptions();
1361     bool has_return = !ttype->is_void();
1362     bool has_args = arglist->get_members().size() == 0;
1363     bool has_xceptions = xlist->get_members().size() == 0;
1364 
1365     string params = "(" + this->nspace + service_name_ + "If *iface"
1366                     + (has_return ? ", " + type_name(ttype) + "* _return" : "")
1367                     + (has_args ? "" : (", " + argument_list(arglist)))
1368                     + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)";
1369 
1370     f_header_ << "gboolean " << this->nspace_lc << service_name_lc << "_if_" << funname << " "
1371               << params << ";" << endl;
1372   }
1373   f_header_ << endl;
1374 
1375   // Generate the client object instance definition in the header.
1376   f_header_ << "/* " << service_name_ << " service client */" << endl << "struct _" << this->nspace
1377             << service_name_ << "Client" << endl << "{" << endl << "  " << parent_class_name
1378             << " parent;" << endl;
1379   if (!extends_service) {
1380     // Define "input_protocol" and "output_protocol" properties only
1381     // for base services; child service-client classes will inherit
1382     // these
1383     f_header_ << endl << "  ThriftProtocol *input_protocol;" << endl
1384               << "  ThriftProtocol *output_protocol;" << endl;
1385   }
1386   f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "Client "
1387             << this->nspace << service_name_ << "Client;" << endl << endl;
1388 
1389   // Generate the class definition in the header.
1390   f_header_ << "struct _" << this->nspace << service_name_ << "ClientClass" << endl << "{" << endl
1391             << "  " << parent_class_name << "Class parent;" << endl << "};" << endl
1392             << "typedef struct _" << this->nspace << service_name_ << "ClientClass " << this->nspace
1393             << service_name_ << "ClientClass;" << endl << endl;
1394 
1395   // Create all the GObject boilerplate
1396   f_header_ << "GType " << this->nspace_lc << service_name_lc << "_client_get_type (void);" << endl
1397             << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT "
1398             << "(" << this->nspace_lc << service_name_lc << "_client_get_type())" << endl
1399             << "#define " << this->nspace_uc << service_name_uc << "_CLIENT(obj) "
1400             << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
1401             << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "Client))" << endl
1402             << "#define " << this->nspace_uc << service_name_uc << "_CLIENT_CLASS(c) "
1403             << "(G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
1404             << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))" << endl << "#define "
1405             << this->nspace_uc << service_name_uc << "_IS_CLIENT(obj) "
1406             << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
1407             << service_name_uc << "_CLIENT))" << endl << "#define " << this->nspace_uc
1408             << service_name_uc << "_IS_CLIENT_CLASS(c) "
1409             << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
1410             << "_CLIENT))" << endl << "#define " << this->nspace_uc << service_name_uc
1411             << "_CLIENT_GET_CLASS(obj) "
1412             << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
1413             << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))"
1414             << endl << endl;
1415 
1416   /* write out the function prototypes */
1417   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1418     /* make the function name C friendly */
1419     string funname = to_lower_case(initial_caps_to_underscores((*f_iter)->get_name()));
1420 
1421     t_function service_function((*f_iter)->get_returntype(),
1422                                 service_name_lc + string("_client_") + funname,
1423                                 (*f_iter)->get_arglist(),
1424                                 (*f_iter)->get_xceptions());
1425     indent(f_header_) << function_signature(&service_function) << ";" << endl;
1426 
1427     t_function send_function(g_type_void,
1428                              service_name_lc + string("_client_send_") + funname,
1429                              (*f_iter)->get_arglist());
1430     indent(f_header_) << function_signature(&send_function) << ";" << endl;
1431 
1432     // implement recv if not a oneway service
1433     if (!(*f_iter)->is_oneway()) {
1434       t_struct noargs(program_);
1435       t_function recv_function((*f_iter)->get_returntype(),
1436                                service_name_lc + string("_client_recv_") + funname,
1437                                &noargs,
1438                                (*f_iter)->get_xceptions());
1439       indent(f_header_) << function_signature(&recv_function) << ";" << endl;
1440     }
1441   }
1442 
1443   /* write out the get/set function prototypes */
1444   f_header_ << "void " + service_name_lc + "_client_set_property (GObject *object, guint "
1445                                            "property_id, const GValue *value, GParamSpec *pspec);"
1446             << endl;
1447   f_header_ << "void " + service_name_lc + "_client_get_property (GObject *object, guint "
1448                                            "property_id, GValue *value, GParamSpec *pspec);"
1449             << endl;
1450 
1451   f_header_ << endl;
1452   // end of header code
1453 
1454   // Generate interface method implementations
1455   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1456     /* make the function name C friendly */
1457     string funname = initial_caps_to_underscores((*f_iter)->get_name());
1458     t_type* ttype = (*f_iter)->get_returntype();
1459     t_struct* arglist = (*f_iter)->get_arglist();
1460     t_struct* xlist = (*f_iter)->get_xceptions();
1461     bool has_return = !ttype->is_void();
1462     bool has_args = arglist->get_members().size() == 0;
1463     bool has_xceptions = xlist->get_members().size() == 0;
1464 
1465     string params = "(" + this->nspace + service_name_ + "If *iface"
1466                     + (has_return ? ", " + type_name(ttype) + "* _return" : "")
1467                     + (has_args ? "" : (", " + argument_list(arglist)))
1468                     + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)";
1469 
1470     string params_without_type = string("iface, ") + (has_return ? "_return, " : "");
1471 
1472     const vector<t_field*>& fields = arglist->get_members();
1473     vector<t_field*>::const_iterator f_iter_field;
1474     for (f_iter_field = fields.begin(); f_iter_field != fields.end(); ++f_iter_field) {
1475       params_without_type += (*f_iter_field)->get_name();
1476       params_without_type += ", ";
1477     }
1478 
1479     const vector<t_field*>& xceptions = xlist->get_members();
1480     vector<t_field*>::const_iterator x_iter;
1481     for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1482       params_without_type += (*x_iter)->get_name();
1483       params_without_type += ", ";
1484     }
1485 
1486     f_service_ << "gboolean" << endl << this->nspace_lc << service_name_lc << "_if_" << funname
1487                << " " << params << endl << "{" << endl << "  return " << this->nspace_uc
1488                << service_name_uc << "_IF_GET_INTERFACE (iface)->" << funname << " ("
1489                << params_without_type << "error);" << endl << "}" << endl << endl;
1490   }
1491 
1492   // Generate interface boilerplate
1493   f_service_ << "GType" << endl << this->nspace_lc << service_name_lc << "_if_get_type (void)"
1494              << endl << "{" << endl << "  static GType type = 0;" << endl << "  if (type == 0)"
1495              << endl << "  {" << endl << "    static const GTypeInfo type_info =" << endl << "    {"
1496              << endl << "      sizeof (" << this->nspace << service_name_ << "IfInterface)," << endl
1497              << "      NULL,  /* base_init */" << endl << "      NULL,  /* base_finalize */" << endl
1498              << "      NULL,  /* class_init */" << endl << "      NULL,  /* class_finalize */"
1499              << endl << "      NULL,  /* class_data */" << endl
1500              << "      0,     /* instance_size */" << endl << "      0,     /* n_preallocs */"
1501              << endl << "      NULL,  /* instance_init */" << endl
1502              << "      NULL   /* value_table */" << endl << "    };" << endl
1503              << "    type = g_type_register_static (G_TYPE_INTERFACE," << endl
1504              << "                                   \"" << this->nspace << service_name_ << "If\","
1505              << endl << "                                   &type_info, 0);" << endl << "  }"
1506              << endl << "  return type;" << endl << "}" << endl << endl;
1507 
1508   // Generate client boilerplate
1509   f_service_ << "static void " << endl << this->nspace_lc << service_name_lc
1510              << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);"
1511              << endl << endl << "G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_
1512              << "Client, " << this->nspace_lc << service_name_lc << "_client," << endl
1513              << "                         " << parent_type_name << ", " << endl
1514              << "                         G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_"
1515              << service_name_uc << "_IF," << endl
1516              << "                                                " << this->nspace_lc
1517              << service_name_lc << "_if_interface_init))" << endl << endl;
1518 
1519   // Generate property-related code only for base services---child
1520   // service-client classes have only properties inherited from their
1521   // parent class
1522   if (!extends_service) {
1523     // Generate client properties
1524     f_service_ << "enum _" << this->nspace << service_name_ << "ClientProperties" << endl << "{"
1525                << endl << "  PROP_0," << endl << "  PROP_" << this->nspace_uc << service_name_uc
1526                << "_CLIENT_INPUT_PROTOCOL," << endl << "  PROP_" << this->nspace_uc
1527                << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL" << endl << "};" << endl << endl;
1528 
1529     // generate property setter
1530     f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_set_property ("
1531                << "GObject *object, guint property_id, const GValue *value, "
1532                << "GParamSpec *pspec)" << endl << "{" << endl << "  " << this->nspace
1533                << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc
1534                << "_CLIENT (object);" << endl << endl << "  THRIFT_UNUSED_VAR (pspec);" << endl
1535                << endl << "  switch (property_id)" << endl << "  {" << endl << "    case PROP_"
1536                << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl
1537                << "      client->input_protocol = g_value_get_object (value);" << endl
1538                << "      break;" << endl << "    case PROP_" << this->nspace_uc << service_name_uc
1539                << "_CLIENT_OUTPUT_PROTOCOL:" << endl
1540                << "      client->output_protocol = g_value_get_object (value);" << endl
1541                << "      break;" << endl << "  }" << endl << "}" << endl << endl;
1542 
1543     // generate property getter
1544     f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_get_property ("
1545                << "GObject *object, guint property_id, GValue *value, "
1546                << "GParamSpec *pspec)" << endl << "{" << endl << "  " << this->nspace
1547                << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc
1548                << "_CLIENT (object);" << endl << endl << "  THRIFT_UNUSED_VAR (pspec);" << endl
1549                << endl << "  switch (property_id)" << endl << "  {" << endl << "    case PROP_"
1550                << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl
1551                << "      g_value_set_object (value, client->input_protocol);" << endl
1552                << "      break;" << endl << "    case PROP_" << this->nspace_uc << service_name_uc
1553                << "_CLIENT_OUTPUT_PROTOCOL:" << endl
1554                << "      g_value_set_object (value, client->output_protocol);" << endl
1555                << "      break;" << endl << "  }" << endl << "}" << endl << endl;
1556   }
1557 
1558   // Generate client method implementations
1559   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1560     string name = (*f_iter)->get_name();
1561     string funname = initial_caps_to_underscores(name);
1562 
1563     // Get the struct of function call params and exceptions
1564     t_struct* arg_struct = (*f_iter)->get_arglist();
1565 
1566     // Function for sending
1567     t_function send_function(g_type_void,
1568                              service_name_lc + string("_client_send_") + funname,
1569                              (*f_iter)->get_arglist());
1570 
1571     // Open the send function
1572     indent(f_service_) << function_signature(&send_function) << endl;
1573     scope_up(f_service_);
1574 
1575     string reqType = (*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL";
1576 
1577     // Serialize the request
1578     f_service_ << indent() << "gint32 cseqid = 0;" << endl << indent()
1579                << "ThriftProtocol * protocol = " << this->nspace_uc << base_service_name_uc
1580                << "_CLIENT (iface)->output_protocol;" << endl << endl << indent()
1581                << "if (thrift_protocol_write_message_begin (protocol, \"" << name << "\", "
1582                << reqType << ", cseqid, error) < 0)" << endl << indent() << "  return FALSE;"
1583                << endl << endl;
1584 
1585     generate_struct_writer(f_service_, arg_struct, "", "", false);
1586 
1587     f_service_ << indent() << "if (thrift_protocol_write_message_end (protocol, error) < 0)" << endl
1588                << indent() << "  return FALSE;" << endl << indent()
1589                << "if (!thrift_transport_flush (protocol->transport, error))" << endl << indent()
1590                << "  return FALSE;" << endl << indent()
1591                << "if (!thrift_transport_write_end (protocol->transport, error))" << endl
1592                << indent() << "  return FALSE;" << endl << endl << indent() << "return TRUE;"
1593                << endl;
1594 
1595     scope_down(f_service_);
1596     f_service_ << endl;
1597 
1598     // Generate recv function only if not an async function
1599     if (!(*f_iter)->is_oneway()) {
1600       t_struct noargs(program_);
1601       t_function recv_function((*f_iter)->get_returntype(),
1602                                service_name_lc + string("_client_recv_") + funname,
1603                                &noargs,
1604                                (*f_iter)->get_xceptions());
1605       // Open function
1606       indent(f_service_) << function_signature(&recv_function) << endl;
1607       scope_up(f_service_);
1608 
1609       f_service_ << indent() << "gint32 rseqid;" << endl
1610                  << indent() << "gchar * fname = NULL;" << endl
1611                  << indent() << "ThriftMessageType mtype;" << endl
1612                  << indent() << "ThriftProtocol * protocol = "
1613                  << this->nspace_uc << base_service_name_uc
1614                  << "_CLIENT (iface)->input_protocol;" << endl
1615                  << indent() << "ThriftApplicationException *xception;" << endl
1616                  << endl
1617                  << indent() << "if (thrift_protocol_read_message_begin "
1618                     "(protocol, &fname, &mtype, &rseqid, error) < 0) {" << endl;
1619       indent_up();
1620       f_service_ << indent() << "if (fname) g_free (fname);" << endl
1621                  << indent() << "return FALSE;" << endl;
1622       indent_down();
1623       f_service_ << indent() << "}" << endl
1624                  << endl
1625                  << indent() << "if (mtype == T_EXCEPTION) {" << endl;
1626       indent_up();
1627       f_service_ << indent() << "if (fname) g_free (fname);" << endl
1628                  << indent() << "xception = g_object_new "
1629                     "(THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);" << endl
1630                  << indent() << "thrift_struct_read (THRIFT_STRUCT (xception), "
1631                     "protocol, NULL);" << endl
1632                  << indent() << "thrift_protocol_read_message_end "
1633                     "(protocol, NULL);" << endl
1634                  << indent() << "thrift_transport_read_end "
1635                     "(protocol->transport, NULL);" << endl
1636                  << indent() << "g_set_error (error, "
1637                     "THRIFT_APPLICATION_EXCEPTION_ERROR,xception->type, "
1638                     "\"application error: %s\", xception->message);" << endl
1639                  << indent() << "g_object_unref (xception);" << endl
1640                  << indent() << "return FALSE;" << endl;
1641       indent_down();
1642       f_service_ << indent() << "} else if (mtype != T_REPLY) {" << endl;
1643       indent_up();
1644       f_service_ << indent() << "if (fname) g_free (fname);" << endl
1645                  << indent() << "thrift_protocol_skip (protocol, T_STRUCT, "
1646                     "NULL);" << endl
1647                  << indent() << "thrift_protocol_read_message_end (protocol, "
1648                     "NULL);" << endl
1649                  << indent() << "thrift_transport_read_end ("
1650                     "protocol->transport, NULL);" << endl
1651                  << indent() << "g_set_error (error, "
1652                     "THRIFT_APPLICATION_EXCEPTION_ERROR, "
1653                     "THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, "
1654                     "\"invalid message type %d, expected T_REPLY\", mtype);"
1655                  << endl
1656                  << indent() << "return FALSE;" << endl;
1657       indent_down();
1658       f_service_ << indent() << "} else if (strncmp (fname, \"" << name
1659                  << "\", " << name.length() << ") != 0) {" << endl;
1660       indent_up();
1661       f_service_ << indent() << "thrift_protocol_skip (protocol, T_STRUCT, "
1662                     "NULL);" << endl
1663                  << indent() << "thrift_protocol_read_message_end (protocol,"
1664                     "error);" << endl
1665                  << indent() << "thrift_transport_read_end ("
1666                     "protocol->transport, error);" << endl
1667                  << indent() << "g_set_error (error, "
1668                     "THRIFT_APPLICATION_EXCEPTION_ERROR, "
1669                     "THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, "
1670                     "\"wrong method name %s, expected " << name
1671                     << "\", fname);" << endl
1672                  << indent() << "if (fname) g_free (fname);" << endl
1673                  << indent() << "return FALSE;" << endl;
1674       indent_down();
1675       f_service_ << indent() << "}" << endl
1676                  << indent() << "if (fname) g_free (fname);" << endl
1677                  << endl;
1678 
1679       t_struct* xs = (*f_iter)->get_xceptions();
1680       const std::vector<t_field*>& xceptions = xs->get_members();
1681       vector<t_field*>::const_iterator x_iter;
1682 
1683       {
1684         t_struct result(program_, tservice->get_name() + "_" + (*f_iter)->get_name() + "_result");
1685         t_field success((*f_iter)->get_returntype(), "*_return", 0);
1686         if (!(*f_iter)->get_returntype()->is_void()) {
1687           result.append(&success);
1688         }
1689 
1690         // add readers for exceptions, dereferencing the pointer.
1691         for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) {
1692           t_field* xception = new t_field((*x_iter)->get_type(),
1693                                           "*" + (*x_iter)->get_name(),
1694                                           (*x_iter)->get_key());
1695           result.append(xception);
1696         }
1697 
1698         generate_struct_reader(f_service_, &result, "", "", false);
1699       }
1700 
1701       f_service_ << indent() << "if (thrift_protocol_read_message_end (protocol, error) < 0)"
1702                  << endl << indent() << "  return FALSE;" << endl << endl << indent()
1703                  << "if (!thrift_transport_read_end (protocol->transport, error))" << endl
1704                  << indent() << "  return FALSE;" << endl << endl;
1705 
1706       // copy over any throw exceptions and return failure
1707       for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) {
1708         f_service_ << indent() << "if (*" << (*x_iter)->get_name() << " != NULL)" << endl
1709                    << indent() << "{" << endl << indent() << "    g_set_error (error, "
1710                    << this->nspace_uc
1711                    << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name()))
1712                    << "_ERROR, " << this->nspace_uc
1713                    << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name()))
1714                    << "_ERROR_CODE, \"" << (*x_iter)->get_type()->get_name() << "\");" << endl
1715                    << indent() << "    return FALSE;" << endl << indent() << "}" << endl;
1716       }
1717       // Close function
1718       indent(f_service_) << "return TRUE;" << endl;
1719       scope_down(f_service_);
1720       f_service_ << endl;
1721     }
1722 
1723     // Open function
1724     t_function service_function((*f_iter)->get_returntype(),
1725                                 service_name_lc + string("_client_") + funname,
1726                                 (*f_iter)->get_arglist(),
1727                                 (*f_iter)->get_xceptions());
1728     indent(f_service_) << function_signature(&service_function) << endl;
1729     scope_up(f_service_);
1730 
1731     // wrap each function
1732     f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_send_"
1733                << funname << " (iface";
1734 
1735     // Declare the function arguments
1736     const vector<t_field*>& fields = arg_struct->get_members();
1737     vector<t_field*>::const_iterator fld_iter;
1738     for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1739       f_service_ << ", " << (*fld_iter)->get_name();
1740     }
1741     f_service_ << ", error))" << endl << indent() << "  return FALSE;" << endl;
1742 
1743     // if not oneway, implement recv
1744     if (!(*f_iter)->is_oneway()) {
1745       string ret = (*f_iter)->get_returntype()->is_void() ? "" : "_return, ";
1746 
1747       const vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
1748       vector<t_field*>::const_iterator x_iter;
1749       for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1750         ret += (*x_iter)->get_name();
1751         ret += ", ";
1752       }
1753 
1754       f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_recv_"
1755                  << funname << " (iface, " << ret << "error))" << endl << indent()
1756                  << "  return FALSE;" << endl;
1757     }
1758 
1759     // return TRUE which means all functions were called OK
1760     indent(f_service_) << "return TRUE;" << endl;
1761     scope_down(f_service_);
1762     f_service_ << endl;
1763   }
1764 
1765   // create the interface initializer
1766   f_service_ << "static void" << endl
1767              << this->nspace_lc << service_name_lc << "_if_interface_init ("
1768              << this->nspace << service_name_ << "IfInterface *iface)" << endl;
1769   scope_up(f_service_);
1770   if (functions.size() > 0) {
1771     for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1772       /* make the function name C friendly */
1773       string funname = initial_caps_to_underscores((*f_iter)->get_name());
1774 
1775       f_service_ << indent() << "iface->" << funname << " = " << this->nspace_lc
1776                  << service_name_lc << "_client_" << funname << ";" << endl;
1777     }
1778   }
1779   else {
1780     f_service_ << indent() << "THRIFT_UNUSED_VAR (iface);" << endl;
1781   }
1782   scope_down(f_service_);
1783   f_service_ << endl;
1784 
1785   // create the client instance initializer
1786   f_service_ << "static void" << endl
1787              << this->nspace_lc << service_name_lc << "_client_init ("
1788              << this->nspace << service_name_ << "Client *client)" << endl;
1789   scope_up(f_service_);
1790   if (!extends_service) {
1791     f_service_ << indent() << "client->input_protocol = NULL;" << endl
1792                << indent() << "client->output_protocol = NULL;" << endl;
1793   }
1794   else {
1795     f_service_ << indent() << "THRIFT_UNUSED_VAR (client);" << endl;
1796   }
1797   scope_down(f_service_);
1798   f_service_ << endl;
1799 
1800   // create the client class initializer
1801   f_service_ << "static void" << endl << this->nspace_lc << service_name_lc
1802              << "_client_class_init (" << this->nspace << service_name_ << "ClientClass *cls)"
1803              << endl << "{" << endl;
1804   if (!extends_service) {
1805     f_service_ << "  GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl
1806                << "  GParamSpec *param_spec;" << endl << endl
1807                << "  gobject_class->set_property = " << this->nspace_lc << service_name_lc
1808                << "_client_set_property;" << endl
1809                << "  gobject_class->get_property = " << this->nspace_lc << service_name_lc
1810                << "_client_get_property;" << endl << endl
1811                << "  param_spec = g_param_spec_object (\"input_protocol\"," << endl
1812                << "                                    \"input protocol (construct)\"," << endl
1813                << "                                    \"Set the client input protocol\"," << endl
1814                << "                                    THRIFT_TYPE_PROTOCOL," << endl
1815                << "                                    G_PARAM_READWRITE);" << endl
1816                << "  g_object_class_install_property (gobject_class," << endl
1817                << "                                   PROP_" << this->nspace_uc << service_name_uc
1818                << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl << endl
1819                << "  param_spec = g_param_spec_object (\"output_protocol\"," << endl
1820                << "                                    \"output protocol (construct)\"," << endl
1821                << "                                    \"Set the client output protocol\"," << endl
1822                << "                                    THRIFT_TYPE_PROTOCOL," << endl
1823                << "                                    G_PARAM_READWRITE);" << endl
1824                << "  g_object_class_install_property (gobject_class," << endl
1825                << "                                   PROP_" << this->nspace_uc << service_name_uc
1826                << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl;
1827   }
1828   else {
1829     f_service_ << "  THRIFT_UNUSED_VAR (cls);" << endl;
1830   }
1831   f_service_ << "}" << endl << endl;
1832 }
1833 
1834 /**
1835  * Generates C code that represents a Thrift service handler.
1836  *
1837  * @param tservice The service for which to generate a handler.
1838  */
generate_service_handler(t_service * tservice)1839 void t_c_glib_generator::generate_service_handler(t_service* tservice) {
1840   vector<t_function*> functions = tservice->get_functions();
1841   vector<t_function*>::const_iterator function_iter;
1842 
1843   string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
1844   string service_name_uc = to_upper_case(service_name_lc);
1845 
1846   string service_handler_name = service_name_ + "Handler";
1847 
1848   string class_name = this->nspace + service_handler_name;
1849   string class_name_lc = this->nspace_lc + initial_caps_to_underscores(service_handler_name);
1850   string class_name_uc = to_upper_case(class_name_lc);
1851 
1852   string parent_class_name;
1853   string parent_type_name;
1854 
1855   string args_indent;
1856 
1857   // The service this service extends, or nullptr if it extends no service
1858   t_service* extends_service = tservice->get_extends();
1859 
1860   // Determine the name of our parent service (if any) and the handler class'
1861   // parent class name and type
1862   if (extends_service) {
1863     string parent_service_name = extends_service->get_name();
1864     string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name));
1865     string parent_service_name_uc = to_upper_case(parent_service_name_lc);
1866 
1867     parent_class_name = this->nspace + parent_service_name + "Handler";
1868     parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_HANDLER";
1869   } else {
1870     parent_class_name = "GObject";
1871     parent_type_name = "G_TYPE_OBJECT";
1872   }
1873 
1874   // Generate the handler class' definition in the header file
1875 
1876   // Generate the handler instance definition
1877   f_header_ << "/* " << service_name_ << " handler (abstract base class) */" << endl << "struct _"
1878             << class_name << endl << "{" << endl;
1879   indent_up();
1880   f_header_ << indent() << parent_class_name << " parent;" << endl;
1881   indent_down();
1882   f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl
1883             << endl;
1884 
1885   // Generate the handler class definition, including its class members
1886   // (methods)
1887   f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl;
1888   indent_up();
1889   f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl;
1890 
1891   for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
1892     string method_name = initial_caps_to_underscores((*function_iter)->get_name());
1893     t_type* return_type = (*function_iter)->get_returntype();
1894     t_struct* arg_list = (*function_iter)->get_arglist();
1895     t_struct* x_list = (*function_iter)->get_xceptions();
1896     bool has_return = !return_type->is_void();
1897     bool has_args = arg_list->get_members().size() == 0;
1898     bool has_xceptions = x_list->get_members().size() == 0;
1899 
1900     string params = "(" + this->nspace + service_name_ + "If *iface"
1901                     + (has_return ? ", " + type_name(return_type) + "* _return" : "")
1902                     + (has_args ? "" : (", " + argument_list(arg_list)))
1903                     + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)";
1904 
1905     indent(f_header_) << "gboolean (*" << method_name << ") " << params << ";" << endl;
1906   }
1907   indent_down();
1908 
1909   f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name
1910             << "Class;" << endl << endl;
1911 
1912   // Generate the remaining header boilerplate
1913   f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define "
1914             << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER "
1915             << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc
1916             << "(obj) "
1917             << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
1918             << service_name_uc << "_HANDLER, " << class_name << "))" << endl << "#define "
1919             << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER(obj) "
1920             << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
1921             << service_name_uc << "_HANDLER))" << endl << "#define " << class_name_uc
1922             << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_"
1923             << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << "#define "
1924             << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER_CLASS(c) "
1925             << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
1926             << "_HANDLER))" << endl << "#define " << this->nspace_uc << service_name_uc
1927             << "_HANDLER_GET_CLASS(obj) "
1928             << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
1929             << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << endl;
1930 
1931   // Generate the handler class' method definitions
1932   for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
1933     string method_name = initial_caps_to_underscores((*function_iter)->get_name());
1934     t_type* return_type = (*function_iter)->get_returntype();
1935     t_struct* arg_list = (*function_iter)->get_arglist();
1936     t_struct* x_list = (*function_iter)->get_xceptions();
1937     bool has_return = !return_type->is_void();
1938     bool has_args = arg_list->get_members().size() == 0;
1939     bool has_xceptions = x_list->get_members().size() == 0;
1940 
1941     string params = "(" + this->nspace + service_name_ + "If *iface"
1942                     + (has_return ? ", " + type_name(return_type) + "* _return" : "")
1943                     + (has_args ? "" : (", " + argument_list(arg_list)))
1944                     + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)";
1945 
1946     f_header_ << "gboolean " << class_name_lc << "_" << method_name << " " << params << ";" << endl;
1947   }
1948   f_header_ << endl;
1949 
1950   // Generate the handler's implementation in the implementation file
1951 
1952   // Generate the implementation boilerplate
1953   f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc
1954              << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);"
1955              << endl << endl;
1956 
1957   args_indent = string(25, ' ');
1958   f_service_ << "G_DEFINE_TYPE_WITH_CODE (" << class_name << ", " << endl << args_indent
1959              << class_name_lc << "," << endl << args_indent << parent_type_name << "," << endl
1960              << args_indent << "G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_"
1961              << service_name_uc << "_IF," << endl;
1962   args_indent += string(23, ' ');
1963   f_service_ << args_indent << class_name_lc << "_" << service_name_lc << "_if_interface_init))"
1964              << endl << endl;
1965 
1966   // Generate the handler method implementations
1967   for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
1968     string function_name = (*function_iter)->get_name();
1969     string method_name = initial_caps_to_underscores(function_name);
1970     t_type* return_type = (*function_iter)->get_returntype();
1971     t_struct* arg_list = (*function_iter)->get_arglist();
1972     t_struct* x_list = (*function_iter)->get_xceptions();
1973 
1974     const vector<t_field*>& args = arg_list->get_members();
1975     const vector<t_field*>& xceptions = x_list->get_members();
1976 
1977     vector<t_field*>::const_iterator field_iter;
1978 
1979     t_function implementing_function(return_type,
1980                                      service_name_lc + "_handler_" + method_name,
1981                                      arg_list,
1982                                      x_list,
1983                                      (*function_iter)->is_oneway());
1984 
1985     indent(f_service_) << function_signature(&implementing_function) << endl;
1986     scope_up(f_service_);
1987     f_service_ << indent() << "g_return_val_if_fail (" << this->nspace_uc << "IS_"
1988                << service_name_uc << "_HANDLER (iface), FALSE);" << endl << endl << indent()
1989                << "return " << class_name_uc << "_GET_CLASS (iface)"
1990                << "->" << method_name << " (iface, ";
1991 
1992     if (!return_type->is_void()) {
1993       f_service_ << "_return, ";
1994     }
1995     for (field_iter = args.begin(); field_iter != args.end(); ++field_iter) {
1996       f_service_ << (*field_iter)->get_name() << ", ";
1997     }
1998     for (field_iter = xceptions.begin(); field_iter != xceptions.end(); ++field_iter) {
1999       f_service_ << (*field_iter)->get_name() << ", ";
2000     }
2001     f_service_ << "error);" << endl;
2002     scope_down(f_service_);
2003     f_service_ << endl;
2004   }
2005 
2006   // Generate the handler interface initializer
2007   f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc
2008              << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface)"
2009              << endl;
2010   scope_up(f_service_);
2011   if (functions.size() > 0) {
2012     for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
2013       string method_name = initial_caps_to_underscores((*function_iter)->get_name());
2014 
2015       f_service_ << indent() << "iface->" << method_name << " = " << class_name_lc << "_"
2016                  << method_name << ";" << endl;
2017     }
2018   }
2019   else {
2020     f_service_ << "THRIFT_UNUSED_VAR (iface);" << endl;
2021   }
2022   scope_down(f_service_);
2023   f_service_ << endl;
2024 
2025   // Generate the handler instance initializer
2026   f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)"
2027              << endl;
2028   scope_up(f_service_);
2029   f_service_ << indent() << "THRIFT_UNUSED_VAR (self);" << endl;
2030   scope_down(f_service_);
2031   f_service_ << endl;
2032 
2033   // Generate the handler class initializer
2034   f_service_ << "static void" << endl
2035              << class_name_lc << "_class_init (" << class_name << "Class *cls)"
2036              << endl;
2037   scope_up(f_service_);
2038   if (functions.size() > 0) {
2039     for (function_iter = functions.begin();
2040          function_iter != functions.end();
2041          ++function_iter) {
2042       string function_name = (*function_iter)->get_name();
2043       string method_name = initial_caps_to_underscores(function_name);
2044 
2045       // All methods are pure virtual and must be implemented by subclasses
2046       f_service_ << indent() << "cls->" << method_name << " = NULL;" << endl;
2047     }
2048   }
2049   else {
2050     f_service_ << indent() << "THRIFT_UNUSED_VAR (cls);" << endl;
2051   }
2052   scope_down(f_service_);
2053   f_service_ << endl;
2054 }
2055 
2056 /**
2057  * Generates C code that represents a Thrift service processor.
2058  *
2059  * @param tservice The service for which to generate a processor
2060  */
generate_service_processor(t_service * tservice)2061 void t_c_glib_generator::generate_service_processor(t_service* tservice) {
2062   vector<t_function*> functions = tservice->get_functions();
2063   vector<t_function*>::const_iterator function_iter;
2064 
2065   string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
2066   string service_name_uc = to_upper_case(service_name_lc);
2067 
2068   string service_processor_name = service_name_ + "Processor";
2069 
2070   string class_name = this->nspace + service_processor_name;
2071   string class_name_lc = this->nspace_lc + initial_caps_to_underscores(service_processor_name);
2072   string class_name_uc = to_upper_case(class_name_lc);
2073 
2074   string parent_class_name;
2075   string parent_type_name;
2076 
2077   string handler_class_name = this->nspace + service_name_ + "Handler";
2078   string handler_class_name_lc = initial_caps_to_underscores(handler_class_name);
2079 
2080   string process_function_type_name = class_name + "ProcessFunction";
2081   string process_function_def_type_name =
2082     class_name_lc + "_process_function_def";
2083 
2084   string function_name;
2085   string args_indent;
2086 
2087   // The service this service extends, or nullptr if it extends no service
2088   t_service* extends_service = tservice->get_extends();
2089 
2090   // Determine the name of our parent service (if any) and the
2091   // processor class' parent class name and type
2092   if (extends_service) {
2093     string parent_service_name = extends_service->get_name();
2094     string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name));
2095     string parent_service_name_uc = to_upper_case(parent_service_name_lc);
2096 
2097     parent_class_name = this->nspace + parent_service_name + "Processor";
2098     parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_PROCESSOR";
2099   } else {
2100     parent_class_name = "ThriftDispatchProcessor";
2101     parent_type_name = "THRIFT_TYPE_DISPATCH_PROCESSOR";
2102   }
2103 
2104   // Generate the processor class' definition in the header file
2105 
2106   // Generate the processor instance definition
2107   f_header_ << "/* " << service_name_ << " processor */" << endl << "struct _" << class_name << endl
2108             << "{" << endl;
2109   indent_up();
2110   f_header_ << indent() << parent_class_name << " parent;" << endl << endl << indent()
2111             << "/* protected */" << endl << indent()
2112             << this->nspace + service_name_ + "Handler *handler;" << endl << indent()
2113             << "GHashTable *process_map;" << endl;
2114   indent_down();
2115   f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl
2116             << endl;
2117 
2118   // Generate the processor class definition
2119   f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl;
2120   indent_up();
2121   f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl << indent()
2122             << "/* protected */" << endl << indent()
2123             << "gboolean (*dispatch_call) (ThriftDispatchProcessor *processor," << endl;
2124   args_indent = indent() + string(27, ' ');
2125   f_header_ << args_indent << "ThriftProtocol *in," << endl << args_indent << "ThriftProtocol *out,"
2126             << endl << args_indent << "gchar *fname," << endl << args_indent << "gint32 seqid,"
2127             << endl << args_indent << "GError **error);" << endl;
2128   indent_down();
2129   f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name
2130             << "Class;" << endl << endl;
2131 
2132   // Generate the remaining header boilerplate
2133   f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define "
2134             << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR "
2135             << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc
2136             << "(obj) "
2137             << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
2138             << service_name_uc << "_PROCESSOR, " << class_name << "))" << endl << "#define "
2139             << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR(obj) "
2140             << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
2141             << service_name_uc << "_PROCESSOR))" << endl << "#define " << class_name_uc
2142             << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_"
2143             << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << "#define "
2144             << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR_CLASS(c) "
2145             << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
2146             << "_PROCESSOR))" << endl << "#define " << this->nspace_uc << service_name_uc
2147             << "_PROCESSOR_GET_CLASS(obj) "
2148             << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
2149             << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << endl;
2150 
2151   // Generate the processor's implementation in the implementation file
2152 
2153   // Generate the processor's properties enum
2154   f_service_ << "enum _" << class_name << "Properties" << endl << "{" << endl;
2155   indent_up();
2156   f_service_ << indent() << "PROP_" << class_name_uc << "_0," << endl << indent() << "PROP_"
2157              << class_name_uc << "_HANDLER" << endl;
2158   indent_down();
2159   f_service_ << "};" << endl << endl;
2160 
2161   // Generate the implementation boilerplate
2162   args_indent = string(15, ' ');
2163   f_service_ << "G_DEFINE_TYPE (" << class_name << "," << endl << args_indent << class_name_lc
2164              << "," << endl << args_indent << parent_type_name << ")" << endl << endl;
2165 
2166   // Generate the processor's processing-function type
2167   args_indent = string(process_function_type_name.length() + 23, ' ');
2168   f_service_ << "typedef gboolean (* " << process_function_type_name << ") ("
2169              << class_name << " *, " << endl
2170              << args_indent << "gint32," << endl
2171              << args_indent << "ThriftProtocol *," << endl
2172              << args_indent << "ThriftProtocol *," << endl
2173              << args_indent << "GError **);" << endl
2174              << endl;
2175 
2176   // Generate the processor's processing-function-definition type
2177   f_service_ << "typedef struct" << endl
2178              << "{" << endl;
2179   indent_up();
2180   f_service_ << indent() << "gchar *name;" << endl
2181              << indent() << process_function_type_name << " function;" << endl;
2182   indent_down();
2183   f_service_ << "} " << process_function_def_type_name << ";" << endl
2184              << endl;
2185 
2186   // Generate forward declarations of the processor's processing functions so we
2187   // can refer to them in the processing-function-definition struct below and
2188   // keep all of the processor's declarations in one place
2189   for (function_iter = functions.begin();
2190        function_iter != functions.end();
2191        ++function_iter) {
2192     function_name = class_name_lc + "_process_"
2193       + initial_caps_to_underscores((*function_iter)->get_name());
2194 
2195     args_indent = string(function_name.length() + 2, ' ');
2196     f_service_ << "static gboolean" << endl
2197                << function_name << " ("
2198                << class_name << " *," << endl
2199                << args_indent << "gint32," << endl
2200                << args_indent << "ThriftProtocol *," << endl
2201                << args_indent << "ThriftProtocol *," << endl
2202                << args_indent << "GError **);" << endl;
2203   }
2204   f_service_ << endl;
2205 
2206   // Generate the processor's processing-function definitions, if the service
2207   // defines any methods
2208   if (functions.size() > 0) {
2209     f_service_ << indent() << "static " << process_function_def_type_name
2210                << endl
2211                << indent() << class_name_lc << "_process_function_defs["
2212                << functions.size() << "] = {" << endl;
2213     indent_up();
2214     for (function_iter = functions.begin();
2215          function_iter != functions.end();
2216          ++function_iter) {
2217       string service_function_name = (*function_iter)->get_name();
2218       string process_function_name = class_name_lc + "_process_"
2219         + initial_caps_to_underscores(service_function_name);
2220 
2221       f_service_ << indent() << "{" << endl;
2222       indent_up();
2223       f_service_ << indent() << "\"" << service_function_name << "\"," << endl
2224                  << indent() << process_function_name << endl;
2225       indent_down();
2226       f_service_ << indent() << "}"
2227                  << (function_iter == --functions.end() ? "" : ",") << endl;
2228     }
2229     indent_down();
2230     f_service_ << indent() << "};" << endl
2231                << endl;
2232   }
2233 
2234   // Generate the processor's processing functions
2235   for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
2236     string service_function_name = (*function_iter)->get_name();
2237     string service_function_name_ic = underscores_to_initial_caps(service_function_name);
2238     string service_function_name_lc = initial_caps_to_underscores(service_function_name);
2239     string service_function_name_uc = to_upper_case(service_function_name_lc);
2240 
2241     t_type* return_type = (*function_iter)->get_returntype();
2242     bool has_return_value = !return_type->is_void();
2243 
2244     t_struct* arg_list = (*function_iter)->get_arglist();
2245     const vector<t_field*>& args = arg_list->get_members();
2246     vector<t_field*>::const_iterator arg_iter;
2247 
2248     const vector<t_field*>& xceptions = (*function_iter)->get_xceptions()->get_members();
2249     vector<t_field*>::const_iterator xception_iter;
2250 
2251     string args_class_name = this->nspace + service_name_ + service_function_name_ic + "Args";
2252     string args_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_"
2253                              + service_function_name_uc + "_ARGS";
2254 
2255     string result_class_name = this->nspace + service_name_ + service_function_name_ic + "Result";
2256     string result_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_"
2257                                + service_function_name_uc + "_RESULT";
2258 
2259     string handler_function_name = handler_class_name_lc + "_" + service_function_name_lc;
2260 
2261     function_name = class_name_lc + "_process_"
2262                     + initial_caps_to_underscores(service_function_name);
2263 
2264     args_indent = string(function_name.length() + 2, ' ');
2265     f_service_ << "static gboolean" << endl << function_name << " (" << class_name << " *self,"
2266                << endl << args_indent << "gint32 sequence_id," << endl << args_indent
2267                << "ThriftProtocol *input_protocol," << endl << args_indent
2268                << "ThriftProtocol *output_protocol," << endl << args_indent << "GError **error)"
2269                << endl;
2270     scope_up(f_service_);
2271     f_service_ << indent() << "gboolean result = TRUE;" << endl
2272                << indent() << "ThriftTransport * transport;" << endl
2273                << indent() << "ThriftApplicationException *xception;" << endl
2274                << indent() << args_class_name + " * args =" << endl;
2275     indent_up();
2276     f_service_ << indent() << "g_object_new (" << args_class_type << ", NULL);" << endl << endl;
2277     indent_down();
2278     if ((*function_iter)->is_oneway()) {
2279       f_service_ << indent() << "THRIFT_UNUSED_VAR (sequence_id);" << endl << indent()
2280                  << "THRIFT_UNUSED_VAR (output_protocol);" << endl << endl;
2281     }
2282     f_service_ << indent() << "g_object_get (input_protocol, \"transport\", "
2283                << "&transport, NULL);" << endl << endl;
2284 
2285     // Read the method's arguments from the caller
2286     f_service_ << indent() << "if ((thrift_struct_read (THRIFT_STRUCT (args), "
2287                << "input_protocol, error) != -1) &&" << endl << indent()
2288                << "    (thrift_protocol_read_message_end (input_protocol, "
2289                << "error) != -1) &&" << endl << indent()
2290                << "    (thrift_transport_read_end (transport, error) != FALSE))" << endl;
2291     scope_up(f_service_);
2292 
2293     for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
2294       f_service_ << indent() << property_type_name((*arg_iter)->get_type()) << " "
2295                  << (*arg_iter)->get_name() << ";" << endl;
2296     }
2297     for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) {
2298       f_service_ << indent() << type_name((*xception_iter)->get_type()) << " "
2299                  << initial_caps_to_underscores((*xception_iter)->get_name()) << " = NULL;" << endl;
2300     }
2301     if (has_return_value) {
2302       f_service_ << indent() << property_type_name(return_type) << " return_value;" << endl;
2303     }
2304     if (!(*function_iter)->is_oneway()) {
2305       f_service_ << indent() << result_class_name << " * result_struct;" << endl;
2306     }
2307     f_service_ << endl;
2308 
2309     if (args.size() > 0) {
2310       f_service_ << indent() << "g_object_get (args," << endl;
2311       args_indent = indent() + string(14, ' ');
2312       for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
2313         string arg_name = (*arg_iter)->get_name();
2314 
2315         f_service_ << args_indent << "\"" << arg_name << "\", &" << arg_name << "," << endl;
2316       }
2317       f_service_ << args_indent << "NULL);" << endl << endl;
2318     }
2319 
2320     if (!(*function_iter)->is_oneway()) {
2321       f_service_ << indent() << "g_object_unref (transport);" << endl << indent()
2322                  << "g_object_get (output_protocol, \"transport\", "
2323                  << "&transport, NULL);" << endl << endl << indent()
2324                  << "result_struct = g_object_new (" << result_class_type << ", NULL);" << endl;
2325       if (has_return_value) {
2326         f_service_ << indent() << "g_object_get (result_struct, "
2327                                   "\"success\", &return_value, NULL);" << endl;
2328       }
2329       f_service_ << endl;
2330     }
2331 
2332     // Pass the arguments to the corresponding method in the handler
2333     f_service_ << indent() << "if (" << handler_function_name << " (" << this->nspace_uc
2334                << service_name_uc << "_IF (self->handler)," << endl;
2335     args_indent = indent() + string(handler_function_name.length() + 6, ' ');
2336     if (has_return_value) {
2337       string return_type_name = type_name(return_type);
2338 
2339       f_service_ << args_indent;
2340 
2341       // Cast return_value if it was declared as a type other than the return
2342       // value's actual type---this is true for integer values 32 bits or fewer
2343       // in width, for which GLib requires a plain gint type be used when
2344       // storing or retrieving as an object property
2345       if (return_type_name != property_type_name(return_type)) {
2346         if (return_type_name[return_type_name.length() - 1] != '*') {
2347           return_type_name += ' ';
2348         }
2349         return_type_name += '*';
2350 
2351         f_service_ << "(" << return_type_name << ")";
2352       }
2353 
2354       f_service_ << "&return_value," << endl;
2355     }
2356     for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
2357       f_service_ << args_indent << (*arg_iter)->get_name() << "," << endl;
2358     }
2359     for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) {
2360       f_service_ << args_indent << "&" << initial_caps_to_underscores((*xception_iter)->get_name())
2361                  << "," << endl;
2362     }
2363     f_service_ << args_indent << "error) == TRUE)" << endl;
2364     scope_up(f_service_);
2365 
2366     // The handler reported success; return the result, if any, to the caller
2367     if (!(*function_iter)->is_oneway()) {
2368       if (has_return_value) {
2369         f_service_ << indent() << "g_object_set (result_struct, \"success\", ";
2370         if (type_name(return_type) != property_type_name(return_type)) {
2371           // Roundtrip cast to fix the position of sign bit.
2372           f_service_ << "(" << property_type_name(return_type) << ")"
2373                      << "(" << type_name(return_type) << ")";
2374         }
2375         f_service_ << "return_value, "
2376                    << "NULL);" << endl;
2377         f_service_ << endl;
2378       }
2379       f_service_ << indent() << "result =" << endl;
2380       indent_up();
2381       f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl;
2382       args_indent = indent() + string(39, ' ');
2383       f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent
2384                  << "T_REPLY," << endl << args_indent << "sequence_id," << endl << args_indent
2385                  << "error) != -1) &&" << endl << indent()
2386                  << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << endl;
2387       args_indent = indent() + string(23, ' ');
2388       f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));"
2389                  << endl;
2390       indent_down();
2391     }
2392     scope_down(f_service_);
2393     f_service_ << indent() << "else" << endl;
2394     scope_up(f_service_);
2395 
2396     // The handler reported failure; check to see if an application-defined
2397     // exception was raised and if so, return it to the caller
2398     f_service_ << indent();
2399     if (xceptions.size() > 0) {
2400       for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) {
2401         f_service_ << "if (" << initial_caps_to_underscores((*xception_iter)->get_name())
2402                    << " != NULL)" << endl;
2403         scope_up(f_service_);
2404         f_service_ << indent() << "g_object_set (result_struct," << endl;
2405         args_indent = indent() + string(14, ' ');
2406         f_service_ << args_indent << "\"" << (*xception_iter)->get_name() << "\", "
2407                    << (*xception_iter)->get_name() << "," << endl << args_indent << "NULL);" << endl
2408                    << endl;
2409         f_service_ << indent() << "g_object_unref ("<< (*xception_iter)->get_name() <<");"<< endl;
2410         f_service_ << indent() << "result =" << endl;
2411         indent_up();
2412         f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl;
2413         args_indent = indent() + string(39, ' ');
2414         f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent
2415                    << "T_REPLY," << endl << args_indent << "sequence_id," << endl << args_indent
2416                    << "error) != -1) &&" << endl << indent()
2417                    << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << endl;
2418         args_indent = indent() + string(23, ' ');
2419         f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));"
2420                    << endl;
2421         indent_down();
2422         scope_down(f_service_);
2423         f_service_ << indent() << "else" << endl;
2424       }
2425 
2426       scope_up(f_service_);
2427       f_service_ << indent();
2428     }
2429 
2430     // If the handler reported failure but raised no application-defined
2431     // exception, return a Thrift application exception with the information
2432     // returned via GLib's own error-reporting mechanism
2433     f_service_ << "if (*error == NULL)" << endl;
2434     indent_up();
2435     f_service_ << indent() << "g_warning (\"" << service_name_ << "."
2436                << (*function_iter)->get_name() << " implementation returned FALSE \"" << endl
2437                << indent() << string(11, ' ') << "\"but did not set an error\");" << endl << endl;
2438     indent_down();
2439     f_service_ << indent() << "xception =" << endl;
2440     indent_up();
2441     f_service_ << indent() << "g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION," << endl;
2442     args_indent = indent() + string(14, ' ');
2443     f_service_ << args_indent << "\"type\",    *error != NULL ? (*error)->code :" << endl
2444                << args_indent << string(11, ' ') << "THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN,"
2445                << endl << args_indent << "\"message\", *error != NULL ? (*error)->message : NULL,"
2446                << endl << args_indent << "NULL);" << endl;
2447     indent_down();
2448     f_service_ << indent() << "g_clear_error (error);" << endl << endl << indent()
2449                << "result =" << endl;
2450     indent_up();
2451     f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl;
2452     args_indent = indent() + string(39, ' ');
2453     f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent
2454                << "T_EXCEPTION," << endl << args_indent << "sequence_id," << endl << args_indent
2455                << "error) != -1) &&" << endl << indent()
2456                << " (thrift_struct_write (THRIFT_STRUCT (xception)," << endl;
2457     args_indent = indent() + string(23, ' ');
2458     f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));"
2459                << endl;
2460     indent_down();
2461     f_service_ << endl << indent() << "g_object_unref (xception);" << endl;
2462 
2463     if (xceptions.size() > 0) {
2464       scope_down(f_service_);
2465     }
2466     scope_down(f_service_);
2467     f_service_ << endl;
2468 
2469     // Dellocate or unref retrieved argument values as necessary
2470     for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
2471       string arg_name = (*arg_iter)->get_name();
2472       t_type* arg_type = get_true_type((*arg_iter)->get_type());
2473 
2474       if (arg_type->is_base_type()) {
2475         t_base_type* base_type = ((t_base_type*)arg_type);
2476 
2477         if (base_type->get_base() == t_base_type::TYPE_STRING) {
2478           f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl;
2479           indent_up();
2480           if (base_type->is_binary()) {
2481             f_service_ << indent() << "g_byte_array_unref (" << arg_name << ");" << endl;
2482           } else {
2483             f_service_ << indent() << "g_free (" << arg_name << ");" << endl;
2484           }
2485           indent_down();
2486         }
2487       } else if (arg_type->is_container()) {
2488         f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl;
2489         indent_up();
2490 
2491         if (arg_type->is_list()) {
2492           t_type* elem_type = ((t_list*)arg_type)->get_elem_type();
2493 
2494           f_service_ << indent();
2495           if (is_numeric(elem_type)) {
2496             f_service_ << "g_array_unref";
2497           } else {
2498             f_service_ << "g_ptr_array_unref";
2499           }
2500           f_service_ << " (" << arg_name << ");" << endl;
2501         } else if (arg_type->is_map() || arg_type->is_set()) {
2502           f_service_ << indent() << "g_hash_table_unref (" << arg_name << ");" << endl;
2503         }
2504 
2505         indent_down();
2506       } else if (arg_type->is_struct()) {
2507         f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl;
2508         indent_up();
2509         f_service_ << indent() << "g_object_unref (" << arg_name << ");" << endl;
2510         indent_down();
2511       }
2512     }
2513 
2514     if (!(*function_iter)->is_oneway()) {
2515       if (has_return_value) {
2516         // Deallocate (or unref) return_value
2517         return_type = get_true_type(return_type);
2518         if (return_type->is_base_type()) {
2519           t_base_type* base_type = ((t_base_type*)return_type);
2520             if (base_type->get_base() == t_base_type::TYPE_STRING) {
2521             f_service_ << indent() << "if (return_value != NULL)" << endl;
2522             indent_up();
2523             if (base_type->is_binary()) {
2524               f_service_ << indent() << "g_byte_array_unref (return_value);" << endl;
2525             } else {
2526               f_service_ << indent() << "g_free (return_value);" << endl;
2527             }
2528             indent_down();
2529           }
2530         } else if (return_type->is_container()) {
2531           f_service_ << indent() << "if (return_value != NULL)" << endl;
2532           indent_up();
2533 
2534           if (return_type->is_list()) {
2535             t_type* elem_type = ((t_list*)return_type)->get_elem_type();
2536 
2537             f_service_ << indent();
2538             if (is_numeric(elem_type)) {
2539               f_service_ << "g_array_unref";
2540             } else {
2541               f_service_ << "g_ptr_array_unref";
2542             }
2543             f_service_ << " (return_value);" << endl;
2544           } else if (return_type->is_map() || return_type->is_set()) {
2545             f_service_ << indent() << "g_hash_table_unref (return_value);" << endl;
2546           }
2547 
2548           indent_down();
2549         } else if (return_type->is_struct()) {
2550           f_service_ << indent() << "if (return_value != NULL)" << endl;
2551           indent_up();
2552           f_service_ << indent() << "g_object_unref (return_value);" << endl;
2553           indent_down();
2554         }
2555       }
2556       f_service_ << indent() << "g_object_unref (result_struct);" << endl << endl << indent()
2557                  << "if (result == TRUE)" << endl;
2558       indent_up();
2559       f_service_ << indent() << "result =" << endl;
2560       indent_up();
2561       f_service_ << indent() << "((thrift_protocol_write_message_end "
2562                  << "(output_protocol, error) != -1) &&" << endl << indent()
2563                  << " (thrift_transport_write_end (transport, error) "
2564                  << "!= FALSE) &&" << endl << indent()
2565                  << " (thrift_transport_flush (transport, error) "
2566                  << "!= FALSE));" << endl;
2567       indent_down();
2568       indent_down();
2569     }
2570     scope_down(f_service_);
2571     f_service_ << indent() << "else" << endl;
2572     indent_up();
2573     f_service_ << indent() << "result = FALSE;" << endl;
2574     indent_down();
2575 
2576     f_service_ << endl << indent() << "g_object_unref (transport);" << endl << indent()
2577                << "g_object_unref (args);" << endl << endl << indent() << "return result;" << endl;
2578     scope_down(f_service_);
2579 
2580     f_service_ << endl;
2581   }
2582 
2583   // Generate the processor's dispatch_call implementation
2584   function_name = class_name_lc + "_dispatch_call";
2585   args_indent = indent() + string(function_name.length() + 2, ' ');
2586   f_service_ << "static gboolean" << endl << function_name
2587              << " (ThriftDispatchProcessor *dispatch_processor," << endl << args_indent
2588              << "ThriftProtocol *input_protocol," << endl << args_indent
2589              << "ThriftProtocol *output_protocol," << endl << args_indent << "gchar *method_name,"
2590              << endl << args_indent << "gint32 sequence_id," << endl << args_indent
2591              << "GError **error)" << endl;
2592   scope_up(f_service_);
2593   f_service_ << indent() << class_name_lc << "_process_function_def *"
2594              << "process_function_def;" << endl;
2595   f_service_ << indent() << "gboolean dispatch_result = FALSE;" << endl << endl << indent()
2596              << class_name << " *self = " << class_name_uc << " (dispatch_processor);" << endl;
2597   f_service_ << indent() << parent_class_name << "Class "
2598                                                  "*parent_class =" << endl;
2599   indent_up();
2600   f_service_ << indent() << "g_type_class_peek_parent (" << class_name_uc << "_GET_CLASS (self));"
2601              << endl;
2602   indent_down();
2603   f_service_ << endl
2604              << indent() << "process_function_def = "
2605              << "g_hash_table_lookup (self->process_map, method_name);" << endl
2606              << indent() << "if (process_function_def != NULL)" << endl;
2607   scope_up(f_service_);
2608   args_indent = indent() + string(53, ' ');
2609   f_service_ << indent() << "g_free (method_name);" << endl
2610              << indent() << "dispatch_result = "
2611              << "(*process_function_def->function) (self," << endl
2612              << args_indent << "sequence_id," << endl
2613              << args_indent << "input_protocol," << endl
2614              << args_indent << "output_protocol," << endl
2615              << args_indent << "error);" << endl;
2616   scope_down(f_service_);
2617   f_service_ << indent() << "else" << endl;
2618   scope_up(f_service_);
2619 
2620   // Method name not recognized; chain up to our parent processor---note the
2621   // top-most implementation of this method, in ThriftDispatchProcessor itself,
2622   // will return an application exception to the caller if no class in the
2623   // hierarchy recognizes the method name
2624   f_service_ << indent() << "dispatch_result = parent_class->dispatch_call "
2625                             "(dispatch_processor," << endl;
2626   args_indent = indent() + string(47, ' ');
2627   f_service_ << args_indent << "input_protocol," << endl << args_indent << "output_protocol,"
2628              << endl << args_indent << "method_name," << endl << args_indent << "sequence_id,"
2629              << endl << args_indent << "error);" << endl;
2630   scope_down(f_service_);
2631   f_service_ << endl << indent() << "return dispatch_result;" << endl;
2632   scope_down(f_service_);
2633   f_service_ << endl;
2634 
2635   // Generate the processor's property setter
2636   function_name = class_name_lc + "_set_property";
2637   args_indent = string(function_name.length() + 2, ' ');
2638   f_service_ << "static void" << endl << function_name << " (GObject *object," << endl
2639              << args_indent << "guint property_id," << endl << args_indent << "const GValue *value,"
2640              << endl << args_indent << "GParamSpec *pspec)" << endl;
2641   scope_up(f_service_);
2642   f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
2643              << endl << indent() << "switch (property_id)" << endl;
2644   scope_up(f_service_);
2645   f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << endl;
2646   indent_up();
2647   f_service_ << indent() << "if (self->handler != NULL)" << endl;
2648   indent_up();
2649   f_service_ << indent() << "g_object_unref (self->handler);" << endl;
2650   indent_down();
2651   f_service_ << indent() << "self->handler = g_value_get_object (value);" << endl << indent()
2652              << "g_object_ref (self->handler);" << endl;
2653   if (extends_service) {
2654     // Chain up to set the handler in every superclass as well
2655     f_service_ << endl << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)->"
2656                << endl;
2657     indent_up();
2658     f_service_ << indent() << "set_property (object, property_id, value, pspec);" << endl;
2659     indent_down();
2660   }
2661   f_service_ << indent() << "break;" << endl;
2662   indent_down();
2663   f_service_ << indent() << "default:" << endl;
2664   indent_up();
2665   f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
2666              << endl << indent() << "break;" << endl;
2667   indent_down();
2668   scope_down(f_service_);
2669   scope_down(f_service_);
2670   f_service_ << endl;
2671 
2672   // Generate processor's property getter
2673   function_name = class_name_lc + "_get_property";
2674   args_indent = string(function_name.length() + 2, ' ');
2675   f_service_ << "static void" << endl << function_name << " (GObject *object," << endl
2676              << args_indent << "guint property_id," << endl << args_indent << "GValue *value,"
2677              << endl << args_indent << "GParamSpec *pspec)" << endl;
2678   scope_up(f_service_);
2679   f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
2680              << endl << indent() << "switch (property_id)" << endl;
2681   scope_up(f_service_);
2682   f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << endl;
2683   indent_up();
2684   f_service_ << indent() << "g_value_set_object (value, self->handler);" << endl << indent()
2685              << "break;" << endl;
2686   indent_down();
2687   f_service_ << indent() << "default:" << endl;
2688   indent_up();
2689   f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
2690              << endl << indent() << "break;" << endl;
2691   indent_down();
2692   scope_down(f_service_);
2693   scope_down(f_service_);
2694   f_service_ << endl;
2695 
2696   // Generator the processor's dispose function
2697   f_service_ << "static void" << endl << class_name_lc << "_dispose (GObject *gobject)" << endl;
2698   scope_up(f_service_);
2699   f_service_ << indent() << class_name << " *self = " << class_name_uc << " (gobject);" << endl
2700              << endl << indent() << "if (self->handler != NULL)" << endl;
2701   scope_up(f_service_);
2702   f_service_ << indent() << "g_object_unref (self->handler);" << endl << indent()
2703              << "self->handler = NULL;" << endl;
2704   scope_down(f_service_);
2705   f_service_ << endl << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)"
2706                                                                            "->dispose (gobject);"
2707              << endl;
2708   scope_down(f_service_);
2709   f_service_ << endl;
2710 
2711   // Generate processor finalize function
2712   f_service_ << "static void" << endl << class_name_lc << "_finalize (GObject *gobject)" << endl;
2713   scope_up(f_service_);
2714   f_service_ << indent() << this->nspace << service_name_ << "Processor *self = " << this->nspace_uc
2715              << service_name_uc << "_PROCESSOR (gobject);" << endl << endl << indent()
2716              << "thrift_safe_hash_table_destroy (self->process_map);" << endl << endl << indent()
2717              << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)"
2718                                                        "->finalize (gobject);" << endl;
2719   scope_down(f_service_);
2720   f_service_ << endl;
2721 
2722   // Generate processor instance initializer
2723   f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)"
2724              << endl;
2725   scope_up(f_service_);
2726   if (functions.size() > 0) {
2727     f_service_ << indent() << "guint index;" << endl
2728                << endl;
2729   }
2730   f_service_ << indent() << "self->handler = NULL;" << endl << indent()
2731              << "self->process_map = "
2732                 "g_hash_table_new (g_str_hash, g_str_equal);" << endl;
2733   if (functions.size() > 0) {
2734     args_indent = string(21, ' ');
2735     f_service_ << endl
2736                << indent() << "for (index = 0; index < "
2737                << functions.size() << "; index += 1)" << endl;
2738     indent_up();
2739     f_service_ << indent() << "g_hash_table_insert (self->process_map," << endl
2740                << indent() << args_indent
2741                << class_name_lc << "_process_function_defs[index].name," << endl
2742                << indent() << args_indent
2743                << "&" << class_name_lc << "_process_function_defs[index]" << ");"
2744                << endl;
2745     indent_down();
2746   }
2747   scope_down(f_service_);
2748   f_service_ << endl;
2749 
2750   // Generate processor class initializer
2751   f_service_ << "static void" << endl << class_name_lc << "_class_init (" << class_name
2752              << "Class *cls)" << endl;
2753   scope_up(f_service_);
2754   f_service_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl
2755              << indent() << "ThriftDispatchProcessorClass *dispatch_processor_class =" << endl;
2756   indent_up();
2757   f_service_ << indent() << "THRIFT_DISPATCH_PROCESSOR_CLASS (cls);" << endl;
2758   indent_down();
2759   f_service_ << indent() << "GParamSpec *param_spec;" << endl << endl << indent()
2760              << "gobject_class->dispose = " << class_name_lc << "_dispose;" << endl << indent()
2761              << "gobject_class->finalize = " << class_name_lc << "_finalize;" << endl << indent()
2762              << "gobject_class->set_property = " << class_name_lc << "_set_property;" << endl
2763              << indent() << "gobject_class->get_property = " << class_name_lc << "_get_property;"
2764              << endl << endl << indent()
2765              << "dispatch_processor_class->dispatch_call = " << class_name_lc << "_dispatch_call;"
2766              << endl << indent() << "cls->dispatch_call = " << class_name_lc << "_dispatch_call;"
2767              << endl << endl << indent() << "param_spec = g_param_spec_object (\"handler\","
2768              << endl;
2769   args_indent = indent() + string(34, ' ');
2770   f_service_ << args_indent << "\"Service handler implementation\"," << endl << args_indent
2771              << "\"The service handler implementation \"" << endl << args_indent
2772              << "\"to which method calls are dispatched.\"," << endl << args_indent
2773              << this->nspace_uc + "TYPE_" + service_name_uc + "_HANDLER," << endl << args_indent
2774              << "G_PARAM_READWRITE);" << endl;
2775   f_service_ << indent() << "g_object_class_install_property (gobject_class," << endl;
2776   args_indent = indent() + string(33, ' ');
2777   f_service_ << args_indent << "PROP_" << class_name_uc << "_HANDLER," << endl << args_indent
2778              << "param_spec);" << endl;
2779   scope_down(f_service_);
2780 }
2781 
2782 /**
2783  * Generates C code that represents a Thrift service server.
2784  */
generate_service_server(t_service * tservice)2785 void t_c_glib_generator::generate_service_server(t_service* tservice) {
2786   (void)tservice;
2787   // Generate the service's handler class
2788   generate_service_handler(tservice);
2789 
2790   // Generate the service's processor class
2791   generate_service_processor(tservice);
2792 }
2793 
2794 /**
2795  * Generates C code to represent a THrift structure as a GObject.
2796  */
generate_object(t_struct * tstruct)2797 void t_c_glib_generator::generate_object(t_struct* tstruct) {
2798   string name = tstruct->get_name();
2799   string name_u = initial_caps_to_underscores(name);
2800   string name_uc = to_upper_case(name_u);
2801 
2802   string class_name = this->nspace + name;
2803   string class_name_lc = this->nspace_lc + initial_caps_to_underscores(name);
2804   string class_name_uc = to_upper_case(class_name_lc);
2805 
2806   string function_name;
2807   string args_indent;
2808 
2809   // write the instance definition
2810   f_types_ << "struct _" << this->nspace << name << endl << "{ " << endl
2811            << "  ThriftStruct parent; " << endl << endl << "  /* public */" << endl;
2812 
2813   // for each field, add a member variable
2814   vector<t_field*>::const_iterator m_iter;
2815   const vector<t_field*>& members = tstruct->get_members();
2816   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
2817     t_type* t = get_true_type((*m_iter)->get_type());
2818     f_types_ << "  " << type_name(t) << " " << (*m_iter)->get_name() << ";" << endl;
2819     if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
2820       f_types_ << "  gboolean __isset_" << (*m_iter)->get_name() << ";" << endl;
2821     }
2822   }
2823 
2824   // close the structure definition and create a typedef
2825   f_types_ << "};" << endl << "typedef struct _" << this->nspace << name << " " << this->nspace
2826            << name << ";" << endl << endl;
2827 
2828   // write the class definition
2829   f_types_ << "struct _" << this->nspace << name << "Class" << endl << "{" << endl
2830            << "  ThriftStructClass parent;" << endl << "};" << endl << "typedef struct _"
2831            << this->nspace << name << "Class " << this->nspace << name << "Class;" << endl << endl;
2832 
2833   // write the standard GObject boilerplate
2834   f_types_ << "GType " << this->nspace_lc << name_u << "_get_type (void);" << endl << "#define "
2835            << this->nspace_uc << "TYPE_" << name_uc << " (" << this->nspace_lc << name_u
2836            << "_get_type())" << endl << "#define " << this->nspace_uc << name_uc
2837            << "(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << name_uc
2838            << ", " << this->nspace << name << "))" << endl << "#define " << this->nspace_uc
2839            << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "_TYPE_"
2840            << name_uc << ", " << this->nspace << name << "Class))" << endl << "#define "
2841            << this->nspace_uc << "IS_" << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), "
2842            << this->nspace_uc << "TYPE_" << name_uc << "))" << endl << "#define " << this->nspace_uc
2843            << "IS_" << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc
2844            << "TYPE_" << name_uc << "))" << endl << "#define " << this->nspace_uc << name_uc
2845            << "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
2846            << name_uc << ", " << this->nspace << name << "Class))" << endl << endl;
2847 
2848   // start writing the object implementation .c file
2849 
2850   // generate properties enum
2851   if (members.size() > 0) {
2852     f_types_impl_ << "enum _" << class_name << "Properties" << endl << "{" << endl;
2853     indent_up();
2854     f_types_impl_ << indent() << "PROP_" << class_name_uc << "_0";
2855     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
2856       string member_name_uc
2857           = to_upper_case(to_lower_case(initial_caps_to_underscores((*m_iter)->get_name())));
2858 
2859       f_types_impl_ << "," << endl << indent() << "PROP_" << class_name_uc << "_" << member_name_uc;
2860     }
2861     f_types_impl_ << endl;
2862     indent_down();
2863     f_types_impl_ << "};" << endl << endl;
2864   }
2865 
2866   // generate struct I/O methods
2867   string this_get = this->nspace + name + " * this_object = " + this->nspace_uc + name_uc
2868                     + "(object);";
2869   generate_struct_reader(f_types_impl_, tstruct, "this_object->", this_get);
2870   generate_struct_writer(f_types_impl_, tstruct, "this_object->", this_get);
2871 
2872   // generate property setter and getter
2873   if (members.size() > 0) {
2874     // generate property setter
2875     function_name = class_name_lc + "_set_property";
2876     args_indent = string(function_name.length() + 2, ' ');
2877     f_types_impl_ << "static void" << endl << function_name << " (GObject *object," << endl
2878                   << args_indent << "guint property_id," << endl << args_indent
2879                   << "const GValue *value," << endl << args_indent << "GParamSpec *pspec)" << endl;
2880     scope_up(f_types_impl_);
2881     f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
2882                   << endl << indent() << "switch (property_id)" << endl;
2883     scope_up(f_types_impl_);
2884     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
2885       t_field* member = (*m_iter);
2886       string member_name = member->get_name();
2887       string member_name_uc
2888           = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
2889       t_type* member_type = get_true_type(member->get_type());
2890 
2891       string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;
2892 
2893       f_types_impl_ << indent() << "case " << property_identifier + ":" << endl;
2894       indent_up();
2895 
2896       if (member_type->is_base_type()) {
2897         t_base_type* base_type = ((t_base_type*)member_type);
2898         string assign_function_name;
2899 
2900         if (base_type->get_base() == t_base_type::TYPE_STRING) {
2901           string release_function_name;
2902 
2903           f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl;
2904           indent_up();
2905 
2906           if (base_type->is_binary()) {
2907             release_function_name = "g_byte_array_unref";
2908             assign_function_name = "g_value_dup_boxed";
2909           } else {
2910             release_function_name = "g_free";
2911             assign_function_name = "g_value_dup_string";
2912           }
2913 
2914           f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");"
2915                         << endl;
2916           indent_down();
2917         } else {
2918           switch (base_type->get_base()) {
2919           case t_base_type::TYPE_BOOL:
2920             assign_function_name = "g_value_get_boolean";
2921             break;
2922 
2923           case t_base_type::TYPE_I8:
2924           case t_base_type::TYPE_I16:
2925           case t_base_type::TYPE_I32:
2926             assign_function_name = "g_value_get_int";
2927             break;
2928 
2929           case t_base_type::TYPE_I64:
2930             assign_function_name = "g_value_get_int64";
2931             break;
2932 
2933           case t_base_type::TYPE_DOUBLE:
2934             assign_function_name = "g_value_get_double";
2935             break;
2936 
2937           default:
2938             throw "compiler error: "
2939                   "unrecognized base type \"" + base_type->get_name() + "\" "
2940                                                                         "for struct member \""
2941                 + member_name + "\"";
2942             break;
2943           }
2944         }
2945 
2946         f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name
2947                       << " (value);" << endl;
2948       } else if (member_type->is_enum()) {
2949         f_types_impl_ << indent() << "self->" << member_name << " = g_value_get_int (value);"
2950                       << endl;
2951       } else if (member_type->is_container()) {
2952         string release_function_name;
2953         string assign_function_name;
2954 
2955         if (member_type->is_list()) {
2956           t_type* elem_type = ((t_list*)member_type)->get_elem_type();
2957 
2958           // Lists of base types other than strings are represented as GArrays;
2959           // all others as GPtrArrays
2960           if (is_numeric(elem_type)) {
2961             release_function_name = "g_array_unref";
2962           } else {
2963             release_function_name = "g_ptr_array_unref";
2964           }
2965 
2966           assign_function_name = "g_value_dup_boxed";
2967         } else if (member_type->is_set() || member_type->is_map()) {
2968           release_function_name = "g_hash_table_unref";
2969           assign_function_name = "g_value_dup_boxed";
2970         }
2971 
2972         f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl;
2973         indent_up();
2974         f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");"
2975                       << endl;
2976         indent_down();
2977         f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name
2978                       << " (value);" << endl;
2979       } else if (member_type->is_struct() || member_type->is_xception()) {
2980         f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl;
2981         indent_up();
2982         f_types_impl_ << indent() << "g_object_unref (self->" << member_name << ");" << endl;
2983         indent_down();
2984         f_types_impl_ << indent() << "self->" << member_name << " = g_value_dup_object (value);"
2985                       << endl;
2986       }
2987 
2988       if (member->get_req() != t_field::T_REQUIRED) {
2989         f_types_impl_ << indent() << "self->__isset_" << member_name << " = TRUE;" << endl;
2990       }
2991 
2992       f_types_impl_ << indent() << "break;" << endl << endl;
2993       indent_down();
2994     }
2995     f_types_impl_ << indent() << "default:" << endl;
2996     indent_up();
2997     f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
2998                   << endl << indent() << "break;" << endl;
2999     indent_down();
3000     scope_down(f_types_impl_);
3001     scope_down(f_types_impl_);
3002     f_types_impl_ << endl;
3003 
3004     // generate property getter
3005     function_name = class_name_lc + "_get_property";
3006     args_indent = string(function_name.length() + 2, ' ');
3007     f_types_impl_ << "static void" << endl << function_name << " (GObject *object," << endl
3008                   << args_indent << "guint property_id," << endl << args_indent << "GValue *value,"
3009                   << endl << args_indent << "GParamSpec *pspec)" << endl;
3010     scope_up(f_types_impl_);
3011     f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
3012                   << endl << indent() << "switch (property_id)" << endl;
3013     scope_up(f_types_impl_);
3014     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
3015       t_field* member = (*m_iter);
3016       string member_name = (*m_iter)->get_name();
3017       string member_name_uc
3018           = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
3019       t_type* member_type = get_true_type(member->get_type());
3020 
3021       string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;
3022 
3023       string setter_function_name;
3024 
3025       if (member_type->is_base_type()) {
3026         t_base_type* base_type = ((t_base_type*)member_type);
3027 
3028         switch (base_type->get_base()) {
3029         case t_base_type::TYPE_BOOL:
3030           setter_function_name = "g_value_set_boolean";
3031           break;
3032 
3033         case t_base_type::TYPE_I8:
3034         case t_base_type::TYPE_I16:
3035         case t_base_type::TYPE_I32:
3036           setter_function_name = "g_value_set_int";
3037           break;
3038 
3039         case t_base_type::TYPE_I64:
3040           setter_function_name = "g_value_set_int64";
3041           break;
3042 
3043         case t_base_type::TYPE_DOUBLE:
3044           setter_function_name = "g_value_set_double";
3045           break;
3046 
3047         case t_base_type::TYPE_STRING:
3048           if (base_type->is_binary()) {
3049             setter_function_name = "g_value_set_boxed";
3050           } else {
3051             setter_function_name = "g_value_set_string";
3052           }
3053           break;
3054 
3055         default:
3056           throw "compiler error: "
3057                 "unrecognized base type \"" + base_type->get_name() + "\" "
3058                                                                       "for struct member \""
3059               + member_name + "\"";
3060           break;
3061         }
3062       } else if (member_type->is_enum()) {
3063         setter_function_name = "g_value_set_int";
3064       } else if (member_type->is_struct() || member_type->is_xception()) {
3065         setter_function_name = "g_value_set_object";
3066       } else if (member_type->is_container()) {
3067         setter_function_name = "g_value_set_boxed";
3068       } else {
3069         throw "compiler error: "
3070               "unrecognized type for struct member \"" + member_name + "\"";
3071       }
3072 
3073       f_types_impl_ << indent() << "case " << property_identifier + ":" << endl;
3074       indent_up();
3075       f_types_impl_ << indent() << setter_function_name << " (value, self->" << member_name << ");"
3076                     << endl << indent() << "break;" << endl << endl;
3077       indent_down();
3078     }
3079     f_types_impl_ << indent() << "default:" << endl;
3080     indent_up();
3081     f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
3082                   << endl << indent() << "break;" << endl;
3083     indent_down();
3084     scope_down(f_types_impl_);
3085     scope_down(f_types_impl_);
3086     f_types_impl_ << endl;
3087   }
3088 
3089   // generate the instance init function
3090 
3091   f_types_impl_ << "static void " << endl << this->nspace_lc << name_u << "_instance_init ("
3092                 << this->nspace << name << " * object)" << endl << "{" << endl;
3093   indent_up();
3094 
3095   // generate default-value structures for container-type members
3096   bool constant_declaration_output = false;
3097   bool string_list_constant_output = false;
3098   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
3099     t_field* member = *m_iter;
3100     t_const_value* member_value = member->get_value();
3101 
3102     if (member_value != nullptr) {
3103       string member_name = member->get_name();
3104       t_type* member_type = get_true_type(member->get_type());
3105 
3106       if (member_type->is_list()) {
3107         const vector<t_const_value*>& list = member_value->get_list();
3108         t_type* elem_type = ((t_list*)member_type)->get_elem_type();
3109 
3110         // Generate an array with the list literal
3111         indent(f_types_impl_) << "static " << type_name(elem_type, false, true) << " __default_"
3112                               << member_name << "[" << list.size() << "] = " << endl;
3113         indent_up();
3114         f_types_impl_ << indent() << constant_literal(member_type, member_value) << ";" << endl;
3115         indent_down();
3116 
3117         constant_declaration_output = true;
3118 
3119         // If we are generating values for a pointer array (i.e. a list of
3120         // strings), set a flag so we know to also declare an index variable to
3121         // use in pre-populating the array
3122         if (elem_type->is_string()) {
3123           string_list_constant_output = true;
3124         }
3125       }
3126 
3127       // TODO: Handle container types other than list
3128     }
3129   }
3130   if (constant_declaration_output) {
3131     if (string_list_constant_output) {
3132       indent(f_types_impl_) << "unsigned int list_index;" << endl;
3133     }
3134 
3135     f_types_impl_ << endl;
3136   }
3137 
3138   // satisfy compilers with -Wall turned on
3139   indent(f_types_impl_) << "/* satisfy -Wall */" << endl << indent()
3140                         << "THRIFT_UNUSED_VAR (object);" << endl;
3141 
3142   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
3143     t_type* member_type = (*m_iter)->get_type();
3144     t_type* t = get_true_type(member_type);
3145     if (t->is_base_type()) {
3146       string dval = " = ";
3147       if (t->is_enum()) {
3148         dval += "(" + type_name(t) + ")";
3149       }
3150       t_const_value* cv = (*m_iter)->get_value();
3151       if (cv != nullptr) {
3152         dval += constant_value("", t, cv);
3153       } else {
3154         dval += t->is_string() ? "NULL" : "0";
3155       }
3156       indent(f_types_impl_) << "object->" << (*m_iter)->get_name() << dval << ";" << endl;
3157     } else if (t->is_struct()) {
3158       string name = (*m_iter)->get_name();
3159       t_program* type_program = member_type->get_program();
3160       string type_nspace = type_program ? type_program->get_namespace("c_glib") : "";
3161       string type_nspace_prefix =
3162         type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_";
3163       string type_name_uc = to_upper_case(initial_caps_to_underscores(member_type->get_name()));
3164       indent(f_types_impl_) << "object->" << name << " = g_object_new ("
3165                             << to_upper_case(type_nspace_prefix) << "TYPE_" << type_name_uc
3166                             << ", NULL);" << endl;
3167     } else if (t->is_xception()) {
3168       string name = (*m_iter)->get_name();
3169       indent(f_types_impl_) << "object->" << name << " = NULL;" << endl;
3170     } else if (t->is_container()) {
3171       string name = (*m_iter)->get_name();
3172       string init_function;
3173       t_type* etype = nullptr;
3174 
3175       if (t->is_map()) {
3176         t_type* key = ((t_map*)t)->get_key_type();
3177         t_type* value = ((t_map*)t)->get_val_type();
3178         init_function = generate_new_hash_from_type(key, value);
3179       } else if (t->is_set()) {
3180         etype = ((t_set*)t)->get_elem_type();
3181         init_function = generate_new_hash_from_type(etype, nullptr);
3182       } else if (t->is_list()) {
3183         etype = ((t_list*)t)->get_elem_type();
3184         init_function = generate_new_array_from_type(etype);
3185       }
3186 
3187       indent(f_types_impl_) << "object->" << name << " = " << init_function << endl;
3188 
3189       // Pre-populate the container with the specified default values, if any
3190       if ((*m_iter)->get_value()) {
3191         t_const_value* member_value = (*m_iter)->get_value();
3192 
3193         if (t->is_list()) {
3194           const vector<t_const_value*>& list = member_value->get_list();
3195 
3196           if (is_numeric(etype)) {
3197             indent(f_types_impl_) <<
3198               "g_array_append_vals (object->" << name << ", &__default_" <<
3199               name << ", " << list.size() << ");" << endl;
3200           }
3201           else {
3202             indent(f_types_impl_) <<
3203               "for (list_index = 0; list_index < " << list.size() << "; " <<
3204               "list_index += 1)" << endl;
3205             indent_up();
3206             indent(f_types_impl_) <<
3207               "g_ptr_array_add (object->" << name << "," << endl <<
3208               indent() << string(17, ' ') << "g_strdup (__default_" <<
3209               name << "[list_index]));" << endl;
3210             indent_down();
3211           }
3212         }
3213 
3214         // TODO: Handle container types other than list
3215       }
3216     }
3217 
3218     /* if not required, initialize the __isset variable */
3219     if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
3220       indent(f_types_impl_) << "object->__isset_" << (*m_iter)->get_name() << " = FALSE;" << endl;
3221     }
3222   }
3223 
3224   indent_down();
3225   f_types_impl_ << "}" << endl << endl;
3226 
3227   /* create the destructor */
3228   f_types_impl_ << "static void " << endl << this->nspace_lc << name_u
3229                 << "_finalize (GObject *object)" << endl << "{" << endl;
3230   indent_up();
3231 
3232   f_types_impl_ << indent() << this->nspace << name << " *tobject = " << this->nspace_uc << name_uc
3233                 << " (object);" << endl << endl;
3234 
3235   f_types_impl_ << indent() << "/* satisfy -Wall in case we don't use tobject */" << endl
3236                 << indent() << "THRIFT_UNUSED_VAR (tobject);" << endl;
3237 
3238   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
3239     t_type* t = get_true_type((*m_iter)->get_type());
3240     if (t->is_container()) {
3241       string name = (*m_iter)->get_name();
3242       if (t->is_map() || t->is_set()) {
3243         f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
3244         f_types_impl_ << indent() << "{" << endl;
3245         indent_up();
3246         f_types_impl_ << indent() << "g_hash_table_destroy (tobject->" << name << ");" << endl;
3247         f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
3248         indent_down();
3249         f_types_impl_ << indent() << "}" << endl;
3250       } else if (t->is_list()) {
3251         t_type* etype = ((t_list*)t)->get_elem_type();
3252         string destructor_function = "g_ptr_array_unref";
3253 
3254         if (etype->is_base_type()) {
3255           t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
3256           switch (tbase) {
3257           case t_base_type::TYPE_VOID:
3258             throw "compiler error: cannot determine array type";
3259           case t_base_type::TYPE_BOOL:
3260           case t_base_type::TYPE_I8:
3261           case t_base_type::TYPE_I16:
3262           case t_base_type::TYPE_I32:
3263           case t_base_type::TYPE_I64:
3264           case t_base_type::TYPE_DOUBLE:
3265             destructor_function = "g_array_unref";
3266             break;
3267           case t_base_type::TYPE_STRING:
3268             break;
3269           default:
3270             throw "compiler error: no array info for type";
3271           }
3272         } else if (etype->is_enum()) {
3273           destructor_function = "g_array_unref";
3274         }
3275 
3276         f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
3277         f_types_impl_ << indent() << "{" << endl;
3278         indent_up();
3279         f_types_impl_ << indent() << destructor_function << " (tobject->" << name << ");" << endl;
3280         f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
3281         indent_down();
3282         f_types_impl_ << indent() << "}" << endl;
3283       }
3284     } else if (t->is_struct() || t->is_xception()) {
3285       string name = (*m_iter)->get_name();
3286       // TODO: g_clear_object needs glib >= 2.28
3287       // f_types_impl_ << indent() << "g_clear_object (&(tobject->" << name << "));" << endl;
3288       // does g_object_unref the trick?
3289       f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
3290       f_types_impl_ << indent() << "{" << endl;
3291       indent_up();
3292       f_types_impl_ << indent() << "g_object_unref(tobject->" << name << ");" << endl;
3293       f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
3294       indent_down();
3295       f_types_impl_ << indent() << "}" << endl;
3296     } else if (t->is_string()) {
3297       string name = (*m_iter)->get_name();
3298       f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
3299       f_types_impl_ << indent() << "{" << endl;
3300       indent_up();
3301       f_types_impl_ << indent() << generate_free_func_from_type(t) << "(tobject->" << name << ");"
3302                     << endl;
3303       f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
3304       indent_down();
3305       f_types_impl_ << indent() << "}" << endl;
3306     }
3307   }
3308 
3309   indent_down();
3310   f_types_impl_ << "}" << endl << endl;
3311 
3312   // generate the class init function
3313 
3314   f_types_impl_ << "static void" << endl << class_name_lc << "_class_init (" << class_name
3315                 << "Class * cls)" << endl;
3316   scope_up(f_types_impl_);
3317 
3318   f_types_impl_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl
3319                 << indent() << "ThriftStructClass *struct_class = "
3320                 << "THRIFT_STRUCT_CLASS (cls);" << endl << endl << indent()
3321                 << "struct_class->read = " << class_name_lc << "_read;" << endl << indent()
3322                 << "struct_class->write = " << class_name_lc << "_write;" << endl << endl
3323                 << indent() << "gobject_class->finalize = " << class_name_lc << "_finalize;"
3324                 << endl;
3325   if (members.size() > 0) {
3326     f_types_impl_ << indent() << "gobject_class->get_property = " << class_name_lc
3327                   << "_get_property;" << endl << indent()
3328                   << "gobject_class->set_property = " << class_name_lc << "_set_property;" << endl;
3329 
3330     // install a property for each member
3331     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
3332       t_field* member = (*m_iter);
3333       string member_name = member->get_name();
3334       string member_name_uc
3335           = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
3336       t_type* member_type = get_true_type(member->get_type());
3337       t_const_value* member_value = member->get_value();
3338 
3339       string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;
3340 
3341       f_types_impl_ << endl << indent() << "g_object_class_install_property" << endl;
3342       indent_up();
3343       args_indent = indent() + ' ';
3344       f_types_impl_ << indent() << "(gobject_class," << endl << args_indent << property_identifier
3345                     << "," << endl << args_indent;
3346 
3347       if (member_type->is_base_type()) {
3348         t_base_type::t_base base_type = ((t_base_type*)member_type)->get_base();
3349 
3350         if (base_type == t_base_type::TYPE_STRING) {
3351           if (((t_base_type*)member_type)->is_binary()) {
3352             args_indent += string(20, ' ');
3353             f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent
3354                           << "NULL," << endl << args_indent << "NULL," << endl << args_indent
3355                           << "G_TYPE_BYTE_ARRAY," << endl << args_indent << "G_PARAM_READWRITE));"
3356                           << endl;
3357           } else {
3358             args_indent += string(21, ' ');
3359             f_types_impl_ << "g_param_spec_string (\"" << member_name << "\"," << endl
3360                           << args_indent << "NULL," << endl << args_indent << "NULL," << endl
3361                           << args_indent
3362                           << ((member_value != NULL) ? "\"" + member_value->get_string() + "\""
3363                                                      : "NULL") << "," << endl << args_indent
3364                           << "G_PARAM_READWRITE));" << endl;
3365           }
3366         } else if (base_type == t_base_type::TYPE_BOOL) {
3367           args_indent += string(22, ' ');
3368           f_types_impl_ << "g_param_spec_boolean (\"" << member_name << "\"," << endl << args_indent
3369                         << "NULL," << endl << args_indent << "NULL," << endl << args_indent
3370                         << (((member_value != NULL) && (member_value->get_integer() != 0))
3371                                 ? "TRUE"
3372                                 : "FALSE") << "," << endl << args_indent << "G_PARAM_READWRITE));"
3373                         << endl;
3374         } else if ((base_type == t_base_type::TYPE_I8) || (base_type == t_base_type::TYPE_I16)
3375                    || (base_type == t_base_type::TYPE_I32) || (base_type == t_base_type::TYPE_I64)
3376                    || (base_type == t_base_type::TYPE_DOUBLE)) {
3377           string param_spec_function_name = "g_param_spec_int";
3378           string min_value;
3379           string max_value;
3380           ostringstream default_value;
3381 
3382           switch (base_type) {
3383           case t_base_type::TYPE_I8:
3384             min_value = "G_MININT8";
3385             max_value = "G_MAXINT8";
3386             break;
3387 
3388           case t_base_type::TYPE_I16:
3389             min_value = "G_MININT16";
3390             max_value = "G_MAXINT16";
3391             break;
3392 
3393           case t_base_type::TYPE_I32:
3394             min_value = "G_MININT32";
3395             max_value = "G_MAXINT32";
3396             break;
3397 
3398           case t_base_type::TYPE_I64:
3399             param_spec_function_name = "g_param_spec_int64";
3400             min_value = "G_MININT64";
3401             max_value = "G_MAXINT64";
3402             break;
3403 
3404           case t_base_type::TYPE_DOUBLE:
3405             param_spec_function_name = "g_param_spec_double";
3406             min_value = "-INFINITY";
3407             max_value = "INFINITY";
3408             break;
3409 
3410           default:
3411             throw "compiler error: "
3412                   "unrecognized base type \"" + member_type->get_name() + "\" "
3413                                                                           "for struct member \""
3414                 + member_name + "\"";
3415             break;
3416           }
3417 
3418           if (member_value != nullptr) {
3419             default_value << (base_type == t_base_type::TYPE_DOUBLE ? member_value->get_double()
3420                                                                     : member_value->get_integer());
3421           } else {
3422             default_value << "0";
3423           }
3424 
3425           args_indent += string(param_spec_function_name.length() + 2, ' ');
3426           f_types_impl_ << param_spec_function_name << " (\"" << member_name << "\"," << endl
3427                         << args_indent << "NULL," << endl << args_indent << "NULL," << endl
3428                         << args_indent << min_value << "," << endl << args_indent << max_value
3429                         << "," << endl << args_indent << default_value.str() << "," << endl
3430                         << args_indent << "G_PARAM_READWRITE));" << endl;
3431         }
3432 
3433         indent_down();
3434       } else if (member_type->is_enum()) {
3435         t_enum_value* enum_min_value = ((t_enum*)member_type)->get_min_value();
3436         t_enum_value* enum_max_value = ((t_enum*)member_type)->get_max_value();
3437         int min_value = (enum_min_value != nullptr) ? enum_min_value->get_value() : 0;
3438         int max_value = (enum_max_value != nullptr) ? enum_max_value->get_value() : 0;
3439 
3440         args_indent += string(18, ' ');
3441         f_types_impl_ << "g_param_spec_int (\"" << member_name << "\"," << endl << args_indent
3442                       << "NULL," << endl << args_indent << "NULL," << endl << args_indent
3443                       << min_value << "," << endl << args_indent << max_value << "," << endl
3444                       << args_indent << min_value << "," << endl << args_indent
3445                       << "G_PARAM_READWRITE));" << endl;
3446         indent_down();
3447       } else if (member_type->is_struct() || member_type->is_xception()) {
3448         t_program* type_program = member_type->get_program();
3449         string type_nspace = type_program ? type_program->get_namespace("c_glib") : "";
3450         string type_nspace_prefix =
3451           type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_";
3452 
3453         string param_type = to_upper_case(type_nspace_prefix) + "TYPE_"
3454                             + to_upper_case(initial_caps_to_underscores(member_type->get_name()));
3455 
3456         args_indent += string(20, ' ');
3457         f_types_impl_ << "g_param_spec_object (\"" << member_name << "\"," << endl << args_indent
3458                       << "NULL," << endl << args_indent << "NULL," << endl << args_indent
3459                       << param_type << "," << endl << args_indent << "G_PARAM_READWRITE));" << endl;
3460         indent_down();
3461       } else if (member_type->is_list()) {
3462         t_type* elem_type = ((t_list*)member_type)->get_elem_type();
3463         string param_type;
3464 
3465         if (elem_type->is_base_type() && !elem_type->is_string()) {
3466           param_type = "G_TYPE_ARRAY";
3467         } else {
3468           param_type = "G_TYPE_PTR_ARRAY";
3469         }
3470 
3471         args_indent += string(20, ' ');
3472         f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent
3473                       << "NULL," << endl << args_indent << "NULL," << endl << args_indent
3474                       << param_type << "," << endl << args_indent << "G_PARAM_READWRITE));" << endl;
3475         indent_down();
3476       } else if (member_type->is_set() || member_type->is_map()) {
3477         args_indent += string(20, ' ');
3478         f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent
3479                       << "NULL," << endl << args_indent << "NULL," << endl << args_indent
3480                       << "G_TYPE_HASH_TABLE," << endl << args_indent << "G_PARAM_READWRITE));"
3481                       << endl;
3482         indent_down();
3483       }
3484     }
3485   }
3486   scope_down(f_types_impl_);
3487   f_types_impl_ << endl;
3488 
3489   f_types_impl_ << "GType" << endl << this->nspace_lc << name_u << "_get_type (void)" << endl << "{"
3490                 << endl << "  static GType type = 0;" << endl << endl << "  if (type == 0) " << endl
3491                 << "  {" << endl << "    static const GTypeInfo type_info = " << endl << "    {"
3492                 << endl << "      sizeof (" << this->nspace << name << "Class)," << endl
3493                 << "      NULL, /* base_init */" << endl << "      NULL, /* base_finalize */"
3494                 << endl << "      (GClassInitFunc) " << this->nspace_lc << name_u << "_class_init,"
3495                 << endl << "      NULL, /* class_finalize */" << endl
3496                 << "      NULL, /* class_data */" << endl << "      sizeof (" << this->nspace
3497                 << name << ")," << endl << "      0, /* n_preallocs */" << endl
3498                 << "      (GInstanceInitFunc) " << this->nspace_lc << name_u << "_instance_init,"
3499                 << endl << "      NULL, /* value_table */" << endl << "    };" << endl << endl
3500                 << "    type = g_type_register_static (THRIFT_TYPE_STRUCT, " << endl
3501                 << "                                   \"" << this->nspace << name << "Type\","
3502                 << endl << "                                   &type_info, 0);" << endl << "  }"
3503                 << endl << endl << "  return type;" << endl << "}" << endl << endl;
3504 }
3505 
3506 /**
3507  * Generates functions to write Thrift structures to a stream.
3508  */
generate_struct_writer(ostream & out,t_struct * tstruct,string this_name,string this_get,bool is_function)3509 void t_c_glib_generator::generate_struct_writer(ostream& out,
3510                                                 t_struct* tstruct,
3511                                                 string this_name,
3512                                                 string this_get,
3513                                                 bool is_function) {
3514   string name = tstruct->get_name();
3515   string name_u = initial_caps_to_underscores(name);
3516   string name_uc = to_upper_case(name_u);
3517 
3518   const vector<t_field*>& fields = tstruct->get_members();
3519   vector<t_field*>::const_iterator f_iter;
3520   int error_ret = 0;
3521 
3522   if (is_function) {
3523     error_ret = -1;
3524     indent(out) << "static gint32" << endl << this->nspace_lc << name_u
3525                 << "_write (ThriftStruct *object, ThriftProtocol *protocol, GError **error)"
3526                 << endl;
3527   }
3528   indent(out) << "{" << endl;
3529   indent_up();
3530 
3531   out << indent() << "gint32 ret;" << endl << indent() << "gint32 xfer = 0;" << endl << endl;
3532 
3533   indent(out) << this_get << endl;
3534   // satisfy -Wall in the case of an empty struct
3535   if (!this_get.empty()) {
3536     indent(out) << "THRIFT_UNUSED_VAR (this_object);" << endl;
3537   }
3538 
3539   out << indent() << "if ((ret = thrift_protocol_write_struct_begin (protocol, \"" << name
3540       << "\", error)) < 0)" << endl << indent() << "  return " << error_ret << ";" << endl
3541       << indent() << "xfer += ret;" << endl;
3542 
3543   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3544     if ((*f_iter)->get_req() == t_field::T_OPTIONAL) {
3545       indent(out) << "if (this_object->__isset_" << (*f_iter)->get_name() << " == TRUE) {" << endl;
3546       indent_up();
3547     }
3548 
3549     out << indent() << "if ((ret = thrift_protocol_write_field_begin (protocol, "
3550         << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", "
3551         << (*f_iter)->get_key() << ", error)) < 0)" << endl << indent() << "  return " << error_ret
3552         << ";" << endl << indent() << "xfer += ret;" << endl;
3553     generate_serialize_field(out, *f_iter, this_name, "", error_ret);
3554     out << indent() << "if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)" << endl
3555         << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;"
3556         << endl;
3557 
3558     if ((*f_iter)->get_req() == t_field::T_OPTIONAL) {
3559       indent_down();
3560       indent(out) << "}" << endl;
3561     }
3562   }
3563 
3564   // write the struct map
3565   out << indent() << "if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0)" << endl
3566       << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
3567       << indent() << "if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0)" << endl
3568       << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
3569       << endl;
3570 
3571   if (is_function) {
3572     indent(out) << "return xfer;" << endl;
3573   }
3574 
3575   indent_down();
3576   indent(out) << "}" << endl << endl;
3577 }
3578 
3579 /**
3580  * Generates code to read Thrift structures from a stream.
3581  */
generate_struct_reader(ostream & out,t_struct * tstruct,string this_name,string this_get,bool is_function)3582 void t_c_glib_generator::generate_struct_reader(ostream& out,
3583                                                 t_struct* tstruct,
3584                                                 string this_name,
3585                                                 string this_get,
3586                                                 bool is_function) {
3587   string name = tstruct->get_name();
3588   string name_u = initial_caps_to_underscores(name);
3589   string name_uc = to_upper_case(name_u);
3590   int error_ret = 0;
3591   const vector<t_field*>& fields = tstruct->get_members();
3592   vector<t_field*>::const_iterator f_iter;
3593 
3594   if (is_function) {
3595     error_ret = -1;
3596     indent(out) << "/* reads a " << name_u << " object */" << endl << "static gint32" << endl
3597                 << this->nspace_lc << name_u
3598                 << "_read (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << endl;
3599   }
3600 
3601   indent(out) << "{" << endl;
3602   indent_up();
3603 
3604   // declare stack temp variables
3605   out << indent() << "gint32 ret;" << endl << indent() << "gint32 xfer = 0;" << endl << indent()
3606       << "gchar *name = NULL;" << endl << indent() << "ThriftType ftype;" << endl << indent()
3607       << "gint16 fid;" << endl << indent() << "guint32 len = 0;" << endl << indent()
3608       << "gpointer data = NULL;" << endl << indent() << this_get << endl;
3609 
3610   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3611     if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
3612       indent(out) << "gboolean isset_" << (*f_iter)->get_name() << " = FALSE;" << endl;
3613     }
3614   }
3615 
3616   out << endl;
3617 
3618   // satisfy -Wall in case we don't use some variables
3619   out << indent() << "/* satisfy -Wall in case these aren't used */" << endl << indent()
3620       << "THRIFT_UNUSED_VAR (len);" << endl << indent() << "THRIFT_UNUSED_VAR (data);" << endl;
3621 
3622   if (!this_get.empty()) {
3623     out << indent() << "THRIFT_UNUSED_VAR (this_object);" << endl;
3624   }
3625   out << endl;
3626 
3627   // read the beginning of the structure marker
3628   out << indent() << "/* read the struct begin marker */" << endl << indent()
3629       << "if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0)" << endl
3630       << indent() << "{" << endl << indent() << "  if (name) g_free (name);" << endl << indent()
3631       << "  return " << error_ret << ";" << endl << indent() << "}" << endl << indent()
3632       << "xfer += ret;" << endl << indent() << "if (name) g_free (name);" << endl << indent()
3633       << "name = NULL;" << endl << endl;
3634 
3635   // read the struct fields
3636   out << indent() << "/* read the struct fields */" << endl << indent() << "while (1)" << endl;
3637   scope_up(out);
3638 
3639   // read beginning field marker
3640   out << indent() << "/* read the beginning of a field */" << endl << indent()
3641       << "if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype, &fid, error)) < 0)"
3642       << endl << indent() << "{" << endl << indent() << "  if (name) g_free (name);" << endl
3643       << indent() << "  return " << error_ret << ";" << endl << indent() << "}" << endl << indent()
3644       << "xfer += ret;" << endl << indent() << "if (name) g_free (name);" << endl << indent()
3645       << "name = NULL;" << endl << endl;
3646 
3647   // check for field STOP marker
3648   out << indent() << "/* break if we get a STOP field */" << endl << indent()
3649       << "if (ftype == T_STOP)" << endl << indent() << "{" << endl << indent() << "  break;" << endl
3650       << indent() << "}" << endl << endl;
3651 
3652   // switch depending on the field type
3653   indent(out) << "switch (fid)" << endl;
3654 
3655   // start switch
3656   scope_up(out);
3657 
3658   // generate deserialization code for known types
3659   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3660     indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
3661     indent_up();
3662     indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ")" << endl;
3663     indent(out) << "{" << endl;
3664 
3665     indent_up();
3666     // generate deserialize field
3667     generate_deserialize_field(out, *f_iter, this_name, "", error_ret, false);
3668     indent_down();
3669 
3670     out << indent() << "} else {" << endl << indent()
3671         << "  if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << indent()
3672         << "    return " << error_ret << ";" << endl << indent() << "  xfer += ret;" << endl
3673         << indent() << "}" << endl << indent() << "break;" << endl;
3674     indent_down();
3675   }
3676 
3677   // create the default case
3678   out << indent() << "default:" << endl << indent()
3679       << "  if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << indent()
3680       << "    return " << error_ret << ";" << endl << indent() << "  xfer += ret;" << endl
3681       << indent() << "  break;" << endl;
3682 
3683   // end switch
3684   scope_down(out);
3685 
3686   // read field end marker
3687   out << indent() << "if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0)" << endl
3688       << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl;
3689 
3690   // end while loop
3691   scope_down(out);
3692   out << endl;
3693 
3694   // read the end of the structure
3695   out << indent() << "if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0)" << endl
3696       << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
3697       << endl;
3698 
3699   // if a required field is missing, throw an error
3700   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
3701     if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
3702       out << indent() << "if (!isset_" << (*f_iter)->get_name() << ")" << endl << indent() << "{"
3703           << endl << indent() << "  g_set_error (error, THRIFT_PROTOCOL_ERROR," << endl << indent()
3704           << "               THRIFT_PROTOCOL_ERROR_INVALID_DATA," << endl << indent()
3705           << "               \"missing field\");" << endl << indent() << "  return -1;" << endl
3706           << indent() << "}" << endl << endl;
3707     }
3708   }
3709 
3710   if (is_function) {
3711     indent(out) << "return xfer;" << endl;
3712   }
3713 
3714   // end the function/structure
3715   indent_down();
3716   indent(out) << "}" << endl << endl;
3717 }
3718 
generate_serialize_field(ostream & out,t_field * tfield,string prefix,string suffix,int error_ret)3719 void t_c_glib_generator::generate_serialize_field(ostream& out,
3720                                                   t_field* tfield,
3721                                                   string prefix,
3722                                                   string suffix,
3723                                                   int error_ret) {
3724   t_type* type = get_true_type(tfield->get_type());
3725   string name = prefix + tfield->get_name() + suffix;
3726 
3727   if (type->is_void()) {
3728     throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
3729   }
3730 
3731   if (type->is_struct() || type->is_xception()) {
3732     generate_serialize_struct(out, (t_struct*)type, name, error_ret);
3733   } else if (type->is_container()) {
3734     generate_serialize_container(out, type, name, error_ret);
3735   } else if (type->is_base_type() || type->is_enum()) {
3736     indent(out) << "if ((ret = thrift_protocol_write_";
3737 
3738     if (type->is_base_type()) {
3739       t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
3740       switch (tbase) {
3741       case t_base_type::TYPE_VOID:
3742         throw "compiler error: cannot serialize void field in a struct: " + name;
3743         break;
3744       case t_base_type::TYPE_BOOL:
3745         out << "bool (protocol, " << name;
3746         break;
3747       case t_base_type::TYPE_I8:
3748         out << "byte (protocol, " << name;
3749         break;
3750       case t_base_type::TYPE_I16:
3751         out << "i16 (protocol, " << name;
3752         break;
3753       case t_base_type::TYPE_I32:
3754         out << "i32 (protocol, " << name;
3755         break;
3756       case t_base_type::TYPE_I64:
3757         out << "i64 (protocol, " << name;
3758         break;
3759       case t_base_type::TYPE_DOUBLE:
3760         out << "double (protocol, " << name;
3761         break;
3762       case t_base_type::TYPE_STRING:
3763         if (type->is_binary()) {
3764           out << "binary (protocol, " << name << " ? ((GByteArray *) " << name << ")->data : NULL, "
3765               << name << " ? ((GByteArray *) " << name << ")->len : 0";
3766         } else {
3767           out << "string (protocol, " << name;
3768         }
3769         break;
3770       default:
3771         throw "compiler error: no C writer for base type " + t_base_type::t_base_name(tbase) + name;
3772       }
3773     } else {
3774       out << "i32 (protocol, (gint32) " << name;
3775     }
3776     out << ", error)) < 0)" << endl
3777         << indent() << "  return " << error_ret << ";" << endl
3778         << indent() << "xfer += ret;" << endl << endl;
3779   } else {
3780     throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + name + "' TYPE '"
3781                            + type_name(type));
3782   }
3783 }
3784 
generate_serialize_struct(ostream & out,t_struct * tstruct,string prefix,int error_ret)3785 void t_c_glib_generator::generate_serialize_struct(ostream& out,
3786                                                    t_struct* tstruct,
3787                                                    string prefix,
3788                                                    int error_ret) {
3789   (void)tstruct;
3790   out << indent() << "if ((ret = thrift_struct_write (THRIFT_STRUCT (" << prefix
3791       << "), protocol, error)) < 0)" << endl << indent() << "  return " << error_ret << ";" << endl
3792       << indent() << "xfer += ret;" << endl << endl;
3793 }
3794 
generate_serialize_container(ostream & out,t_type * ttype,string prefix,int error_ret)3795 void t_c_glib_generator::generate_serialize_container(ostream& out,
3796                                                       t_type* ttype,
3797                                                       string prefix,
3798                                                       int error_ret) {
3799   scope_up(out);
3800 
3801   if (ttype->is_map()) {
3802     t_type* tkey = ((t_map*)ttype)->get_key_type();
3803     t_type* tval = ((t_map*)ttype)->get_val_type();
3804     string tkey_name = type_name(tkey);
3805     string tval_name = type_name(tval);
3806     string tkey_ptr;
3807     string tval_ptr;
3808     string keyname = tmp("key");
3809     string valname = tmp("val");
3810 
3811     declore_local_variable_for_write(out, tkey, keyname);
3812     declore_local_variable_for_write(out, tval, valname);
3813 
3814     /* If either the key or value type is a typedef, find its underlying type so
3815        we can correctly determine how to generate a pointer to it */
3816     tkey = get_true_type(tkey);
3817     tval = get_true_type(tval);
3818 
3819     tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*";
3820     tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*";
3821 
3822     /*
3823      * Some ugliness here.  To maximize backwards compatibility, we
3824      * avoid using GHashTableIter and instead get a GList of all keys,
3825      * then copy it into a array on the stack, and free it.
3826      * This is because we may exit early before we get a chance to free the
3827      * GList.
3828      */
3829     out << indent() << "GList *key_list = NULL, *iter = NULL;" << endl
3830         << indent() << tkey_name << tkey_ptr << "* keys;" << endl
3831         << indent() << "int i = 0, key_count;" << endl
3832         << endl
3833         << indent() << "if ((ret = thrift_protocol_write_map_begin (protocol, "
3834         << type_to_enum(tkey) << ", " << type_to_enum(tval) << ", " << prefix << " ? "
3835         << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0"
3836         << ", error)) < 0)" << endl;
3837     indent_up();
3838     out << indent() << "return " << error_ret << ";" << endl;
3839     indent_down();
3840     out << indent() << "xfer += ret;" << endl
3841         << indent() << "if (" << prefix << ")" << endl
3842         << indent() << "  g_hash_table_foreach ((GHashTable *) " << prefix
3843         << ", thrift_hash_table_get_keys, &key_list);" << endl
3844         << indent() << "key_count = g_list_length (key_list);" << endl
3845         << indent() << "keys = g_newa (" << tkey_name << tkey_ptr
3846         << ", key_count);" << endl
3847         << indent() << "for (iter = g_list_first (key_list); iter; "
3848            "iter = iter->next)" << endl;
3849     indent_up();
3850     out << indent() << "keys[i++] = (" << tkey_name << tkey_ptr
3851         << ") iter->data;" << endl;
3852     indent_down();
3853     out << indent() << "g_list_free (key_list);" << endl
3854         << endl
3855         << indent() << "for (i = 0; i < key_count; ++i)" << endl;
3856     scope_up(out);
3857     out << indent() << keyname << " = keys[i];" << endl
3858         << indent() << valname << " = (" << tval_name << tval_ptr
3859         << ") g_hash_table_lookup (((GHashTable *) " << prefix
3860         << "), (gpointer) " << keyname << ");" << endl
3861         << endl;
3862     generate_serialize_map_element(out,
3863                                    (t_map*)ttype,
3864                                    tkey_ptr + " " + keyname,
3865                                    tval_ptr + " " + valname,
3866                                    error_ret);
3867     scope_down(out);
3868     out << indent() << "if ((ret = thrift_protocol_write_map_end (protocol, "
3869            "error)) < 0)" << endl;
3870     indent_up();
3871     out << indent() << "return " << error_ret << ";" << endl;
3872     indent_down();
3873     out << indent() << "xfer += ret;" << endl;
3874   } else if (ttype->is_set()) {
3875     t_type* telem = ((t_set*)ttype)->get_elem_type();
3876     string telem_name = type_name(telem);
3877     string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*";
3878     out << indent() << "GList *key_list = NULL, *iter = NULL;" << endl
3879         << indent() << telem_name << telem_ptr << "* keys;" << endl
3880         << indent() << "int i = 0, key_count;" << endl
3881         << indent() << telem_name << telem_ptr << " elem;" << endl
3882         << indent() << "gpointer value;" << endl
3883         << indent() << "THRIFT_UNUSED_VAR (value);" << endl
3884         << endl
3885         << indent() << "if ((ret = thrift_protocol_write_set_begin (protocol, "
3886         << type_to_enum(telem) << ", " << prefix << " ? "
3887         << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0"
3888         << ", error)) < 0)" << endl;
3889     indent_up();
3890     out << indent() << "return " << error_ret << ";" << endl;
3891     indent_down();
3892     out << indent() << "xfer += ret;" << endl
3893         << indent() << "if (" << prefix << ")" << endl
3894         << indent() << "  g_hash_table_foreach ((GHashTable *) " << prefix
3895         << ", thrift_hash_table_get_keys, &key_list);" << endl
3896         << indent() << "key_count = g_list_length (key_list);" << endl
3897         << indent() << "keys = g_newa (" << telem_name << telem_ptr
3898         << ", key_count);" << endl
3899         << indent() << "for (iter = g_list_first (key_list); iter; "
3900            "iter = iter->next)" << endl;
3901     indent_up();
3902     out << indent() << "keys[i++] = (" << telem_name << telem_ptr
3903         << ") iter->data;" << endl;
3904     indent_down();
3905     out << indent() << "g_list_free (key_list);" << endl
3906         << endl
3907         << indent() << "for (i = 0; i < key_count; ++i)" << endl;
3908     scope_up(out);
3909     out << indent() << "elem = keys[i];" << endl
3910         << indent() << "value = (gpointer) g_hash_table_lookup "
3911            "(((GHashTable *) " << prefix << "), (gpointer) elem);" << endl
3912         << endl;
3913     generate_serialize_set_element(out,
3914                                    (t_set*)ttype,
3915                                    telem_ptr + "elem",
3916                                    error_ret);
3917     scope_down(out);
3918     out << indent() << "if ((ret = thrift_protocol_write_set_end (protocol, "
3919            "error)) < 0)" << endl;
3920     indent_up();
3921     out << indent() << "return " << error_ret << ";" << endl;
3922     indent_down();
3923     out << indent() << "xfer += ret;" << endl;
3924   } else if (ttype->is_list()) {
3925     string length = "(" + prefix + " ? " + prefix + "->len : 0)";
3926     string i = tmp("i");
3927     out << indent() << "guint " << i << ";" << endl
3928         << endl
3929         << indent() << "if ((ret = thrift_protocol_write_list_begin (protocol, "
3930         << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", (gint32) "
3931         << length << ", error)) < 0)" << endl;
3932     indent_up();
3933     out << indent() << "return " << error_ret << ";" << endl;
3934     indent_down();
3935     out << indent() << "xfer += ret;" << endl
3936         << indent() << "for (" << i << " = 0; " << i << " < " << length << "; "
3937         << i << "++)" << endl;
3938     scope_up(out);
3939     generate_serialize_list_element(out, (t_list*)ttype, prefix, i, error_ret);
3940     scope_down(out);
3941     out << indent() << "if ((ret = thrift_protocol_write_list_end (protocol, "
3942            "error)) < 0)" << endl;
3943     indent_up();
3944     out << indent() << "return " << error_ret << ";" << endl;
3945     indent_down();
3946     out << indent() << "xfer += ret;" << endl;
3947   }
3948 
3949   scope_down(out);
3950 }
3951 
generate_serialize_map_element(ostream & out,t_map * tmap,string key,string value,int error_ret)3952 void t_c_glib_generator::generate_serialize_map_element(ostream& out,
3953                                                         t_map* tmap,
3954                                                         string key,
3955                                                         string value,
3956                                                         int error_ret) {
3957   t_field kfield(tmap->get_key_type(), key);
3958   generate_serialize_field(out, &kfield, "", "", error_ret);
3959 
3960   t_field vfield(tmap->get_val_type(), value);
3961   generate_serialize_field(out, &vfield, "", "", error_ret);
3962 }
3963 
generate_serialize_set_element(ostream & out,t_set * tset,string element,int error_ret)3964 void t_c_glib_generator::generate_serialize_set_element(ostream& out,
3965                                                         t_set* tset,
3966                                                         string element,
3967                                                         int error_ret) {
3968   t_field efield(tset->get_elem_type(), element);
3969   generate_serialize_field(out, &efield, "", "", error_ret);
3970 }
3971 
generate_serialize_list_element(ostream & out,t_list * tlist,string list,string index,int error_ret)3972 void t_c_glib_generator::generate_serialize_list_element(ostream& out,
3973                                                          t_list* tlist,
3974                                                          string list,
3975                                                          string index,
3976                                                          int error_ret) {
3977   t_type* ttype = get_true_type(tlist->get_elem_type());
3978 
3979   // cast to non-const
3980   string cast = "";
3981   string name = "g_ptr_array_index ((GPtrArray *) " + list + ", " + index + ")";
3982 
3983   if (ttype->is_void()) {
3984     throw std::runtime_error("compiler error: list element type cannot be void");
3985   } else if (is_numeric(ttype)) {
3986     name = "g_array_index (" + list + ", " + base_type_name(ttype) + ", " + index + ")";
3987   } else if (ttype->is_string()) {
3988     cast = "(gchar*)";
3989   } else if (ttype->is_map() || ttype->is_set()) {
3990     cast = "(GHashTable*)";
3991   } else if (ttype->is_list()) {
3992     t_type* etype = ((t_list*)ttype)->get_elem_type();
3993     if (etype->is_void()) {
3994       throw std::runtime_error("compiler error: list element type cannot be void");
3995     }
3996     cast = is_numeric(etype) ? "(GArray*)" : "(GPtrArray*)";
3997   }
3998 
3999   t_field efield(ttype, "(" + cast + name + ")");
4000   generate_serialize_field(out, &efield, "", "", error_ret);
4001 }
4002 
4003 /* deserializes a field of any type. */
generate_deserialize_field(ostream & out,t_field * tfield,string prefix,string suffix,int error_ret,bool allocate)4004 void t_c_glib_generator::generate_deserialize_field(ostream& out,
4005                                                     t_field* tfield,
4006                                                     string prefix,
4007                                                     string suffix,
4008                                                     int error_ret,
4009                                                     bool allocate) {
4010   t_type* type = get_true_type(tfield->get_type());
4011 
4012   if (type->is_void()) {
4013     throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix
4014                              + tfield->get_name());
4015   }
4016 
4017   string name = prefix + tfield->get_name() + suffix;
4018 
4019   if (type->is_struct() || type->is_xception()) {
4020     generate_deserialize_struct(out, (t_struct*)type, name, error_ret, allocate);
4021   } else if (type->is_container()) {
4022     generate_deserialize_container(out, type, name, error_ret);
4023   } else if (type->is_base_type()) {
4024     t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
4025     if (tbase == t_base_type::TYPE_STRING) {
4026       indent(out) << "if (" << name << " != NULL)" << endl << indent() << "{" << endl;
4027       indent_up();
4028       indent(out) << "g_free(" << name << ");" << endl << indent() << name << " = NULL;" << endl;
4029       indent_down();
4030       indent(out) << "}" << endl << endl;
4031     }
4032     indent(out) << "if ((ret = thrift_protocol_read_";
4033 
4034     switch (tbase) {
4035     case t_base_type::TYPE_VOID:
4036       throw "compiler error: cannot serialize void field in a struct: " + name;
4037       break;
4038     case t_base_type::TYPE_STRING:
4039       if (type->is_binary()) {
4040         out << "binary (protocol, &data, &len";
4041       } else {
4042         out << "string (protocol, &" << name;
4043       }
4044       break;
4045     case t_base_type::TYPE_BOOL:
4046       out << "bool (protocol, &" << name;
4047       break;
4048     case t_base_type::TYPE_I8:
4049       out << "byte (protocol, &" << name;
4050       break;
4051     case t_base_type::TYPE_I16:
4052       out << "i16 (protocol, &" << name;
4053       break;
4054     case t_base_type::TYPE_I32:
4055       out << "i32 (protocol, &" << name;
4056       break;
4057     case t_base_type::TYPE_I64:
4058       out << "i64 (protocol, &" << name;
4059       break;
4060     case t_base_type::TYPE_DOUBLE:
4061       out << "double (protocol, &" << name;
4062       break;
4063     default:
4064       throw "compiler error: no C reader for base type " + t_base_type::t_base_name(tbase) + name;
4065     }
4066     out << ", error)) < 0)" << endl;
4067     out << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;"
4068         << endl;
4069 
4070     // load the byte array with the data
4071     if (tbase == t_base_type::TYPE_STRING && type->is_binary()) {
4072       indent(out) << name << " = g_byte_array_new();" << endl;
4073       indent(out) << "g_byte_array_append (" << name << ", (guint8 *) data, (guint) len);" << endl;
4074       indent(out) << "g_free (data);" << endl;
4075     }
4076   } else if (type->is_enum()) {
4077     string t = tmp("ecast");
4078     out << indent() << "gint32 " << t << ";" << endl << indent()
4079         << "if ((ret = thrift_protocol_read_i32 (protocol, &" << t << ", error)) < 0)" << endl
4080         << indent() << "  return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
4081         << indent() << name << " = (" << type_name(type) << ")" << t << ";" << endl;
4082   } else {
4083     throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + tfield->get_name() + "' TYPE '"
4084                            + type_name(type));
4085   }
4086 
4087   // if the type is not required and this is a thrift struct (no prefix),
4088   // set the isset variable.  if the type is required, then set the
4089   // local variable indicating the value was set, so that we can do    // validation later.
4090   if (prefix != "" && tfield->get_req() != t_field::T_REQUIRED) {
4091     indent(out) << prefix << "__isset_" << tfield->get_name() << suffix << " = TRUE;" << endl;
4092   } else if (prefix != "" && tfield->get_req() == t_field::T_REQUIRED) {
4093     indent(out) << "isset_" << tfield->get_name() << " = TRUE;" << endl;
4094   }
4095 }
4096 
generate_deserialize_struct(ostream & out,t_struct * tstruct,string prefix,int error_ret,bool allocate)4097 void t_c_glib_generator::generate_deserialize_struct(ostream& out,
4098                                                      t_struct* tstruct,
4099                                                      string prefix,
4100                                                      int error_ret,
4101                                                      bool allocate) {
4102   string name_uc = to_upper_case(initial_caps_to_underscores(tstruct->get_name()));
4103   if (tstruct->is_xception()) {
4104     out << indent() << "/* This struct is an exception */" << endl;
4105     allocate = true;
4106   }
4107 
4108   if (allocate) {
4109     out << indent() << "if ( " << prefix << " != NULL)" << endl << indent() << "{" << endl;
4110     indent_up();
4111     out << indent() << "g_object_unref (" << prefix << ");" << endl;
4112     indent_down();
4113     out << indent() << "}" << endl << indent() << prefix << " = g_object_new (" << this->nspace_uc
4114         << "TYPE_" << name_uc << ", NULL);" << endl;
4115   }
4116   out << indent() << "if ((ret = thrift_struct_read (THRIFT_STRUCT (" << prefix
4117       << "), protocol, error)) < 0)" << endl << indent() << "{" << endl;
4118   indent_up();
4119   if (allocate) {
4120     indent(out) << "g_object_unref (" << prefix << ");" << endl;
4121     if (tstruct->is_xception()) {
4122       indent(out) << prefix << " = NULL;" << endl;
4123     }
4124   }
4125   out << indent() << "return " << error_ret << ";" << endl;
4126   indent_down();
4127   out << indent() << "}" << endl << indent() << "xfer += ret;" << endl;
4128 }
4129 
generate_deserialize_container(ostream & out,t_type * ttype,string prefix,int error_ret)4130 void t_c_glib_generator::generate_deserialize_container(ostream& out,
4131                                                         t_type* ttype,
4132                                                         string prefix,
4133                                                         int error_ret) {
4134   scope_up(out);
4135 
4136   if (ttype->is_map()) {
4137     out << indent() << "guint32 size;" << endl
4138         << indent() << "guint32 i;" << endl
4139         << indent() << "ThriftType key_type;" << endl
4140         << indent() << "ThriftType value_type;" << endl
4141         << endl
4142         << indent() << "/* read the map begin marker */" << endl
4143         << indent() << "if ((ret = thrift_protocol_read_map_begin (protocol, "
4144            "&key_type, &value_type, &size, error)) < 0)" << endl;
4145     indent_up();
4146     out << indent() << "return " << error_ret << ";" << endl;
4147     indent_down();
4148     out << indent() << "xfer += ret;" << endl
4149         << endl;
4150 
4151     // iterate over map elements
4152     out << indent() << "/* iterate through each of the map's fields */" << endl
4153         << indent() << "for (i = 0; i < size; i++)" << endl;
4154     scope_up(out);
4155     generate_deserialize_map_element(out, (t_map*)ttype, prefix, error_ret);
4156     scope_down(out);
4157     out << endl;
4158 
4159     // read map end
4160     out << indent() << "/* read the map end marker */" << endl
4161         << indent() << "if ((ret = thrift_protocol_read_map_end (protocol, "
4162            "error)) < 0)" << endl;
4163     indent_up();
4164     out << indent() << "return " << error_ret << ";" << endl;
4165     indent_down();
4166     out << indent() << "xfer += ret;" << endl;
4167   } else if (ttype->is_set()) {
4168     out << indent() << "guint32 size;" << endl
4169         << indent() << "guint32 i;" << endl
4170         << indent() << "ThriftType element_type;" << endl
4171         << endl
4172         << indent() << "if ((ret = thrift_protocol_read_set_begin (protocol, "
4173            "&element_type, &size, error)) < 0)" << endl;
4174     indent_up();
4175     out << indent() << "return " << error_ret << ";" << endl;
4176     indent_down();
4177     out << indent() << "xfer += ret;" << endl
4178         << endl;
4179 
4180     // iterate over the elements
4181     out << indent() << "/* iterate through the set elements */" << endl
4182         << indent() << "for (i = 0; i < size; ++i)" << endl;
4183     scope_up(out);
4184     generate_deserialize_set_element(out, (t_set*)ttype, prefix, error_ret);
4185     scope_down(out);
4186 
4187     // read set end
4188     out << indent() << "if ((ret = thrift_protocol_read_set_end (protocol, "
4189            "error)) < 0)" << endl;
4190     indent_up();
4191     out << indent() << "return " << error_ret << ";" << endl;
4192     indent_down();
4193     out << indent() << "xfer += ret;" << endl
4194         << endl;
4195   } else if (ttype->is_list()) {
4196     out << indent() << "guint32 size;" << endl
4197         << indent() << "guint32 i;" << endl
4198         << indent() << "ThriftType element_type;" << endl
4199         << endl
4200         << indent() << "if ((ret = thrift_protocol_read_list_begin (protocol, "
4201            "&element_type,&size, error)) < 0)" << endl;
4202     indent_up();
4203     out << indent() << "return " << error_ret << ";" << endl;
4204     indent_down();
4205     out << indent() << "xfer += ret;" << endl
4206         << endl;
4207 
4208     // iterate over the elements
4209     out << indent() << "/* iterate through list elements */" << endl
4210         << indent() << "for (i = 0; i < size; i++)" << endl;
4211     scope_up(out);
4212     generate_deserialize_list_element(out,
4213                                       (t_list*)ttype,
4214                                       prefix,
4215                                       "i",
4216                                       error_ret);
4217     scope_down(out);
4218 
4219     // read list end
4220     out << indent() << "if ((ret = thrift_protocol_read_list_end (protocol, "
4221            "error)) < 0)" << endl;
4222     indent_up();
4223     out << indent() << "return " << error_ret << ";" << endl;
4224     indent_down();
4225     out << indent() << "xfer += ret;" << endl;
4226   }
4227 
4228   scope_down(out);
4229 }
4230 
declare_local_variable(ostream & out,t_type * ttype,string & name,bool for_hash_table)4231 void t_c_glib_generator::declare_local_variable(ostream& out, t_type* ttype, string& name, bool for_hash_table) {
4232   string tname = type_name(ttype);
4233 
4234   /* If the given type is a typedef, find its underlying type so we
4235      can correctly determine how to generate a pointer to it */
4236   ttype = get_true_type(ttype);
4237   string ptr = !is_numeric(ttype) ? "" : "*";
4238 
4239   if (ttype->is_map()) {
4240     t_map* tmap = (t_map*)ttype;
4241     out << indent() << tname << ptr << " " << name << " = "
4242         << generate_new_hash_from_type(tmap->get_key_type(), tmap->get_val_type()) << endl;
4243   } else if (ttype->is_list()) {
4244     t_list* tlist = (t_list*)ttype;
4245     out << indent() << tname << ptr << " " << name << " = "
4246         << generate_new_array_from_type(tlist->get_elem_type()) << endl;
4247   } else if (for_hash_table && ttype->is_enum()) {
4248     out << indent() << tname << " " << name << ";" << endl;
4249   } else {
4250     out << indent() << tname << ptr << " " << name
4251         << (ptr != "" ? " = g_new (" + tname + ", 1)" : " = NULL") << ";" << endl;
4252   }
4253 }
4254 
declore_local_variable_for_write(ostream & out,t_type * ttype,string & name)4255 void t_c_glib_generator::declore_local_variable_for_write(ostream& out,
4256                                                           t_type* ttype,
4257                                                           string& name) {
4258   string tname = type_name(ttype);
4259   ttype = get_true_type(ttype);
4260   string ptr = ttype->is_string() || !ttype->is_base_type() ? " " : "* ";
4261   string init_val = ttype->is_enum() ? "" : " = NULL";
4262   out << indent() << tname << ptr << name << init_val << ";" << endl;
4263 }
4264 
generate_deserialize_map_element(ostream & out,t_map * tmap,string prefix,int error_ret)4265 void t_c_glib_generator::generate_deserialize_map_element(ostream& out,
4266                                                           t_map* tmap,
4267                                                           string prefix,
4268                                                           int error_ret) {
4269   t_type* tkey = tmap->get_key_type();
4270   t_type* tval = tmap->get_val_type();
4271   string keyname = tmp("key");
4272   string valname = tmp("val");
4273 
4274   declare_local_variable(out, tkey, keyname, true);
4275   declare_local_variable(out, tval, valname, true);
4276 
4277   /* If either the key or value type is a typedef, find its underlying
4278      type so we can correctly determine how to generate a pointer to
4279      it */
4280   tkey = get_true_type(tkey);
4281   tval = get_true_type(tval);
4282 
4283   string tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*";
4284   string tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*";
4285 
4286   // deserialize the fields of the map element
4287   t_field fkey(tkey, tkey_ptr + keyname);
4288   generate_deserialize_field(out, &fkey, "", "", error_ret);
4289   t_field fval(tval, tval_ptr + valname);
4290   generate_deserialize_field(out, &fval, "", "", error_ret);
4291 
4292   indent(out) << "if (" << prefix << " && " << keyname << ")" << endl;
4293   indent_up();
4294   indent(out) << "g_hash_table_insert ((GHashTable *)" << prefix << ", (gpointer) " << keyname
4295               << ", (gpointer) " << valname << ");" << endl;
4296   indent_down();
4297 }
4298 
generate_deserialize_set_element(ostream & out,t_set * tset,string prefix,int error_ret)4299 void t_c_glib_generator::generate_deserialize_set_element(ostream& out,
4300                                                           t_set* tset,
4301                                                           string prefix,
4302                                                           int error_ret) {
4303   t_type* telem = tset->get_elem_type();
4304   string elem = tmp("_elem");
4305   string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*";
4306 
4307   declare_local_variable(out, telem, elem, true);
4308 
4309   t_field felem(telem, telem_ptr + elem);
4310   generate_deserialize_field(out, &felem, "", "", error_ret);
4311 
4312   indent(out) << "if (" << prefix << " && " << elem << ")" << endl;
4313   indent_up();
4314   indent(out) << "g_hash_table_insert ((GHashTable *) " << prefix << ", (gpointer) " << elem
4315               << ", (gpointer) " << elem << ");" << endl;
4316   indent_down();
4317 }
4318 
generate_deserialize_list_element(ostream & out,t_list * tlist,string prefix,string index,int error_ret)4319 void t_c_glib_generator::generate_deserialize_list_element(ostream& out,
4320                                                            t_list* tlist,
4321                                                            string prefix,
4322                                                            string index,
4323                                                            int error_ret) {
4324   (void)index;
4325   t_type* ttype = get_true_type(tlist->get_elem_type());
4326   string elem = tmp("_elem");
4327   string telem_ptr = !is_numeric(ttype) ? "" : "*";
4328 
4329   declare_local_variable(out, ttype, elem, false);
4330 
4331   t_field felem(ttype, telem_ptr + elem);
4332   generate_deserialize_field(out, &felem, "", "", error_ret);
4333 
4334   if (ttype->is_void()) {
4335     throw std::runtime_error("compiler error: list element type cannot be void");
4336   } else if (is_numeric(ttype)) {
4337     indent(out) << "g_array_append_vals (" << prefix << ", " << elem << ", 1);" << endl;
4338     indent(out) << "g_free (" << elem << ");" << endl;
4339   } else {
4340     indent(out) << "g_ptr_array_add (" << prefix << ", " << elem << ");" << endl;
4341   }
4342 }
4343 
generate_free_func_from_type(t_type * ttype)4344 string t_c_glib_generator::generate_free_func_from_type(t_type* ttype) {
4345   if (ttype == nullptr)
4346     return "NULL";
4347 
4348   if (ttype->is_base_type()) {
4349     t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
4350     switch (tbase) {
4351     case t_base_type::TYPE_VOID:
4352       throw "compiler error: cannot determine hash type";
4353       break;
4354     case t_base_type::TYPE_BOOL:
4355     case t_base_type::TYPE_I8:
4356     case t_base_type::TYPE_I16:
4357     case t_base_type::TYPE_I32:
4358     case t_base_type::TYPE_I64:
4359     case t_base_type::TYPE_DOUBLE:
4360       return "g_free";
4361     case t_base_type::TYPE_STRING:
4362       if (((t_base_type*)ttype)->is_binary()) {
4363         return "thrift_string_free";
4364       }
4365       return "g_free";
4366     default:
4367       throw "compiler error: no hash table info for type";
4368     }
4369   } else if (ttype->is_enum()) {
4370     return "NULL";
4371   } else if (ttype->is_map() || ttype->is_set()) {
4372     return "(GDestroyNotify) thrift_safe_hash_table_destroy";
4373   } else if (ttype->is_struct()) {
4374     return "g_object_unref";
4375   } else if (ttype->is_list()) {
4376     t_type* etype = ((t_list*)ttype)->get_elem_type();
4377     if (etype->is_base_type()) {
4378       t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
4379       switch (tbase) {
4380       case t_base_type::TYPE_VOID:
4381         throw "compiler error: cannot determine array type";
4382         break;
4383       case t_base_type::TYPE_BOOL:
4384       case t_base_type::TYPE_I8:
4385       case t_base_type::TYPE_I16:
4386       case t_base_type::TYPE_I32:
4387       case t_base_type::TYPE_I64:
4388       case t_base_type::TYPE_DOUBLE:
4389         return "(GDestroyNotify) g_array_unref";
4390       case t_base_type::TYPE_STRING:
4391         return "(GDestroyNotify) g_ptr_array_unref";
4392       default:
4393         throw "compiler error: no array info for type";
4394       }
4395     } else if (etype->is_container() || etype->is_struct()) {
4396       return "(GDestroyNotify) g_ptr_array_unref";
4397       ;
4398     } else if (etype->is_enum()) {
4399       return "(GDestroyNotify) g_array_unref";
4400     }
4401     printf("Type not expected inside the array: %s\n", etype->get_name().c_str());
4402     throw "Type not expected inside array";
4403   } else if (ttype->is_typedef()) {
4404     return generate_free_func_from_type(((t_typedef*)ttype)->get_type());
4405   }
4406   printf("Type not expected: %s\n", ttype->get_name().c_str());
4407   throw "Type not expected";
4408 }
4409 
generate_hash_func_from_type(t_type * ttype)4410 string t_c_glib_generator::generate_hash_func_from_type(t_type* ttype) {
4411   if (ttype == nullptr)
4412     return "NULL";
4413 
4414   if (ttype->is_base_type()) {
4415     t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
4416     switch (tbase) {
4417     case t_base_type::TYPE_VOID:
4418       throw "compiler error: cannot determine hash type";
4419       break;
4420     case t_base_type::TYPE_BOOL:
4421       return "thrift_boolean_hash";
4422     case t_base_type::TYPE_I8:
4423       return "thrift_int8_hash";
4424     case t_base_type::TYPE_I16:
4425       return "thrift_int16_hash";
4426     case t_base_type::TYPE_I32:
4427       return "g_int_hash";
4428     case t_base_type::TYPE_I64:
4429       return "g_int64_hash";
4430     case t_base_type::TYPE_DOUBLE:
4431       return "g_double_hash";
4432     case t_base_type::TYPE_STRING:
4433       return "g_str_hash";
4434     default:
4435       throw "compiler error: no hash table info for type";
4436     }
4437   } else if (ttype->is_enum()) {
4438     return "g_direct_hash";
4439   } else if (ttype->is_container() || ttype->is_struct()) {
4440     return "g_direct_hash";
4441   } else if (ttype->is_typedef()) {
4442     return generate_hash_func_from_type(((t_typedef*)ttype)->get_type());
4443   }
4444   printf("Type not expected: %s\n", ttype->get_name().c_str());
4445   throw "Type not expected";
4446 }
4447 
generate_cmp_func_from_type(t_type * ttype)4448 string t_c_glib_generator::generate_cmp_func_from_type(t_type* ttype) {
4449   if (ttype == nullptr)
4450     return "NULL";
4451 
4452   if (ttype->is_base_type()) {
4453     t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
4454     switch (tbase) {
4455     case t_base_type::TYPE_VOID:
4456       throw "compiler error: cannot determine hash type";
4457       break;
4458     case t_base_type::TYPE_BOOL:
4459       return "thrift_boolean_equal";
4460     case t_base_type::TYPE_I8:
4461       return "thrift_int8_equal";
4462     case t_base_type::TYPE_I16:
4463       return "thrift_int16_equal";
4464     case t_base_type::TYPE_I32:
4465       return "g_int_equal";
4466     case t_base_type::TYPE_I64:
4467       return "g_int64_equal";
4468     case t_base_type::TYPE_DOUBLE:
4469       return "g_double_equal";
4470     case t_base_type::TYPE_STRING:
4471       return "g_str_equal";
4472     default:
4473       throw "compiler error: no hash table info for type";
4474     }
4475   } else if (ttype->is_enum()) {
4476     return "g_direct_equal";
4477   } else if (ttype->is_container() || ttype->is_struct()) {
4478     return "g_direct_equal";
4479   } else if (ttype->is_typedef()) {
4480     return generate_cmp_func_from_type(((t_typedef*)ttype)->get_type());
4481   }
4482   printf("Type not expected: %s\n", ttype->get_name().c_str());
4483   throw "Type not expected";
4484 }
4485 
generate_new_hash_from_type(t_type * key,t_type * value)4486 string t_c_glib_generator::generate_new_hash_from_type(t_type* key, t_type* value) {
4487   string hash_func = generate_hash_func_from_type(key);
4488   string cmp_func = generate_cmp_func_from_type(key);
4489   string key_free_func = generate_free_func_from_type(key);
4490   string value_free_func = generate_free_func_from_type(value);
4491 
4492   return "g_hash_table_new_full (" + hash_func + ", " + cmp_func + ", " + key_free_func + ", "
4493          + value_free_func + ");";
4494 }
4495 
generate_new_array_from_type(t_type * ttype)4496 string t_c_glib_generator::generate_new_array_from_type(t_type* ttype) {
4497   if (ttype->is_void()) {
4498     throw std::runtime_error("compiler error: cannot determine array type");
4499   } else if (is_numeric(ttype)) {
4500     return "g_array_new (0, 1, sizeof (" + base_type_name(ttype) + "));";
4501   } else {
4502     string free_func = generate_free_func_from_type(ttype);
4503     return "g_ptr_array_new_with_free_func (" + free_func + ");";
4504   }
4505 }
4506 
4507 /***************************************
4508  * UTILITY FUNCTIONS                   *
4509  ***************************************/
4510 
4511 /**
4512  * Upper case a string.
4513  */
to_upper_case(string name)4514 string to_upper_case(string name) {
4515   string s(name);
4516   std::transform(s.begin(), s.end(), s.begin(), ::toupper);
4517   return s;
4518 }
4519 
4520 /**
4521  * Lower case a string.
4522  */
to_lower_case(string name)4523 string to_lower_case(string name) {
4524   string s(name);
4525   std::transform(s.begin(), s.end(), s.begin(), ::tolower);
4526   return s;
4527 }
4528 
4529 /**
4530  * Makes a string friendly to C code standards by lowercasing and adding
4531  * underscores, with the exception of the first character.  For example:
4532  *
4533  * Input: "ZomgCamelCase"
4534  * Output: "zomg_camel_case"
4535  */
initial_caps_to_underscores(string name)4536 string initial_caps_to_underscores(string name) {
4537   string ret;
4538   const char* tmp = name.c_str();
4539   int pos = 0;
4540 
4541   /* the first character isn't underscored if uppercase, just lowercased */
4542   ret += tolower(tmp[pos]);
4543   pos++;
4544   for (unsigned int i = pos; i < name.length(); i++) {
4545     char lc = tolower(tmp[i]);
4546     if (lc != tmp[i]) {
4547       ret += '_';
4548     }
4549     ret += lc;
4550   }
4551 
4552   return ret;
4553 }
4554 
4555 /**
4556  * Performs the reverse operation of initial_caps_to_underscores: The first
4557  * character of the string is made uppercase, along with each character that
4558  * follows an underscore (which is removed). Useful for converting Thrift
4559  * service-method names into GObject-style class names.
4560  *
4561  * Input: "zomg_camel_case"
4562  * Output: "ZomgCamelCase"
4563  */
underscores_to_initial_caps(string name)4564 string underscores_to_initial_caps(string name) {
4565   string ret;
4566   const char* tmp = name.c_str();
4567   bool uppercase_next = true;
4568 
4569   for (unsigned int i = 0; i < name.length(); i++) {
4570     char c = tmp[i];
4571     if (c == '_') {
4572       uppercase_next = true;
4573     } else {
4574       if (uppercase_next) {
4575         ret += toupper(c);
4576         uppercase_next = false;
4577       } else {
4578         ret += c;
4579       }
4580     }
4581   }
4582 
4583   return ret;
4584 }
4585 
4586 /* register this generator with the main program */
display_name() const4587 std::string t_c_glib_generator::display_name() const {
4588   return "C, using GLib";
4589 }
4590 
4591 
4592 THRIFT_REGISTER_GENERATOR(c_glib, "C, using GLib", "")
4593