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