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 <string>
25 #include <fstream>
26 #include <iostream>
27 #include <vector>
28
29 #include <stdlib.h>
30 #include <time.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sstream>
34
35 #include "thrift/platform.h"
36 #include "thrift/version.h"
37 #include "thrift/generate/t_oop_generator.h"
38
39 using std::map;
40 using std::ofstream;
41 using std::ostringstream;
42 using std::string;
43 using std::stringstream;
44 using std::vector;
45
46 static const string endl = "\n"; // avoid ostream << std::endl flushes
47
48 /**
49 * Smalltalk code generator.
50 *
51 */
52 class t_st_generator : public t_oop_generator {
53 public:
t_st_generator(t_program * program,const std::map<std::string,std::string> & parsed_options,const std::string & option_string)54 t_st_generator(t_program* program,
55 const std::map<std::string, std::string>& parsed_options,
56 const std::string& option_string)
57 : t_oop_generator(program) {
58 (void)option_string;
59 temporary_var = 0;
60 std::map<std::string, std::string>::const_iterator iter;
61
62 /* no options yet */
63 for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
64 throw "unknown option st:" + iter->first;
65 }
66
67 out_dir_base_ = "gen-st";
68 }
69
70 /**
71 * Init and close methods
72 */
73
74 void init_generator() override;
75 void close_generator() override;
76 std::string display_name() const override;
77
78 /**
79 * Program-level generation functions
80 */
81
82 void generate_typedef(t_typedef* ttypedef) override;
83 void generate_enum(t_enum* tenum) override;
84 void generate_const(t_const* tconst) override;
85 void generate_struct(t_struct* tstruct) override;
86 void generate_xception(t_struct* txception) override;
87 void generate_service(t_service* tservice) override;
88 void generate_class_side_definition();
89 void generate_force_consts();
90
91 std::string render_const_value(t_type* type, t_const_value* value);
92
93 /**
94 * Struct generation code
95 */
96
97 void generate_st_struct(std::ostream& out, t_struct* tstruct, bool is_exception);
98 void generate_accessors(std::ostream& out, t_struct* tstruct);
99
100 /**
101 * Service-level generation functions
102 */
103
104 void generate_service_client(t_service* tservice);
105
106 void generate_send_method(t_function* tfunction);
107 void generate_recv_method(t_function* tfunction);
108
109 std::string map_reader(t_map* tmap);
110 std::string list_reader(t_list* tlist);
111 std::string set_reader(t_set* tset);
112 std::string struct_reader(t_struct* tstruct, std::string clsName);
113
114 std::string map_writer(t_map* tmap, std::string name);
115 std::string list_writer(t_list* tlist, std::string name);
116 std::string set_writer(t_set* tset, std::string name);
117 std::string struct_writer(t_struct* tstruct, std::string fname);
118
119 std::string write_val(t_type* t, std::string fname);
120 std::string read_val(t_type* t);
121
122 /**
123 * Helper rendering functions
124 */
125
126 std::string st_autogen_comment();
127
128 void st_class_def(std::ostream& out, std::string name);
129 void st_method(std::ostream& out, std::string cls, std::string name);
130 void st_method(std::ostream& out, std::string cls, std::string name, std::string category);
131 void st_close_method(std::ostream& out);
132 void st_class_method(std::ostream& out, std::string cls, std::string name);
133 void st_class_method(std::ostream& out, std::string cls, std::string name, std::string category);
134 void st_setter(std::ostream& out, std::string cls, std::string name, std::string type);
135 void st_getter(std::ostream& out, std::string cls, std::string name);
136 void st_accessors(std::ostream& out, std::string cls, std::string name, std::string type);
137
138 std::string class_name();
139 static bool is_valid_namespace(const std::string& sub_namespace);
140 std::string client_class_name();
141 std::string prefix(std::string name);
142 std::string declare_field(t_field* tfield);
143 std::string type_name(t_type* ttype);
144
145 std::string function_signature(t_function* tfunction);
146 std::string argument_list(t_struct* tstruct);
147 std::string function_types_comment(t_function* fn);
148
149 std::string type_to_enum(t_type* ttype);
150 std::string a_type(t_type* type);
151 bool is_vowel(char c);
152 std::string temp_name();
153 std::string generated_category();
154
155 private:
156 /**
157 * File streams
158 */
159 int temporary_var;
160 ofstream_with_content_based_conditional_update f_;
161 };
162
163 /**
164 * Prepares for file generation by opening up the necessary file output
165 * streams.
166 *
167 * @param tprogram The program to generate
168 */
init_generator()169 void t_st_generator::init_generator() {
170 // Make output directory
171 MKDIR(get_out_dir().c_str());
172
173 temporary_var = 0;
174
175 // Make output file
176 string f_name = get_out_dir() + "/" + program_name_ + ".st";
177 f_.open(f_name.c_str());
178
179 // Print header
180 f_ << st_autogen_comment() << endl;
181
182 st_class_def(f_, program_name_);
183 generate_class_side_definition();
184
185 // Generate enums
186 vector<t_enum*> enums = program_->get_enums();
187 vector<t_enum*>::iterator en_iter;
188 for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
189 generate_enum(*en_iter);
190 }
191 }
192
class_name()193 string t_st_generator::class_name() {
194 return capitalize(program_name_);
195 }
196
is_valid_namespace(const std::string & sub_namespace)197 bool t_st_generator::is_valid_namespace(const std::string& sub_namespace) {
198 return sub_namespace == "prefix" || sub_namespace == "category";
199 }
200
prefix(string class_name)201 string t_st_generator::prefix(string class_name) {
202 string prefix = program_->get_namespace("smalltalk.prefix");
203 string name = capitalize(class_name);
204 name = prefix.empty() ? name : (prefix + name);
205 return name;
206 }
207
client_class_name()208 string t_st_generator::client_class_name() {
209 return capitalize(service_name_) + "Client";
210 }
211
212 /**
213 * Autogen'd comment
214 */
st_autogen_comment()215 string t_st_generator::st_autogen_comment() {
216 return std::string("'") + "Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "\n"
217 + "DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "'!\n";
218 }
219
generate_force_consts()220 void t_st_generator::generate_force_consts() {
221 f_ << prefix(class_name()) << " enums keysAndValuesDo: [:k :v | " << prefix(class_name())
222 << " enums at: k put: v value].!" << endl;
223
224 f_ << prefix(class_name()) << " constants keysAndValuesDo: [:k :v | " << prefix(class_name())
225 << " constants at: k put: v value].!" << endl;
226 }
227
close_generator()228 void t_st_generator::close_generator() {
229 generate_force_consts();
230 f_.close();
231 }
232
generated_category()233 string t_st_generator::generated_category() {
234 string cat = program_->get_namespace("smalltalk.category");
235 // For compatibility with the Thrift grammar, the category must
236 // be punctuated by dots. Replaces them with dashes here.
237 for (char & iter : cat) {
238 if (iter == '.') {
239 iter = '-';
240 }
241 }
242 return cat.size() ? cat : "Generated-" + class_name();
243 }
244
245 /**
246 * Generates a typedef. This is not done in Smalltalk, types are all implicit.
247 *
248 * @param ttypedef The type definition
249 */
generate_typedef(t_typedef * ttypedef)250 void t_st_generator::generate_typedef(t_typedef* ttypedef) {
251 (void)ttypedef;
252 }
253
st_class_def(std::ostream & out,string name)254 void t_st_generator::st_class_def(std::ostream& out, string name) {
255 out << "Object subclass: #" << prefix(name) << endl;
256 indent_up();
257 out << indent() << "instanceVariableNames: ''" << endl << indent() << "classVariableNames: ''"
258 << endl << indent() << "poolDictionaries: ''" << endl << indent() << "category: '"
259 << generated_category() << "'!" << endl << endl;
260 }
261
st_method(std::ostream & out,string cls,string name)262 void t_st_generator::st_method(std::ostream& out, string cls, string name) {
263 st_method(out, cls, name, "as yet uncategorized");
264 }
265
st_class_method(std::ostream & out,string cls,string name)266 void t_st_generator::st_class_method(std::ostream& out, string cls, string name) {
267 st_method(out, cls + " class", name);
268 }
269
st_class_method(std::ostream & out,string cls,string name,string category)270 void t_st_generator::st_class_method(std::ostream& out, string cls, string name, string category) {
271 st_method(out, cls, name, category);
272 }
273
st_method(std::ostream & out,string cls,string name,string category)274 void t_st_generator::st_method(std::ostream& out, string cls, string name, string category) {
275 char timestr[50];
276 time_t rawtime;
277 struct tm* tinfo;
278
279 time(&rawtime);
280 tinfo = localtime(&rawtime);
281 strftime(timestr, 50, "%m/%d/%Y %H:%M", tinfo);
282
283 out << "!" << prefix(cls) << " methodsFor: '" + category + "' stamp: 'thrift " << timestr
284 << "'!\n" << name << endl;
285
286 indent_up();
287 out << indent();
288 }
289
st_close_method(std::ostream & out)290 void t_st_generator::st_close_method(std::ostream& out) {
291 out << "! !" << endl << endl;
292 indent_down();
293 }
294
st_setter(std::ostream & out,string cls,string name,string type="anObject")295 void t_st_generator::st_setter(std::ostream& out,
296 string cls,
297 string name,
298 string type = "anObject") {
299 st_method(out, cls, name + ": " + type);
300 out << name << " := " + type;
301 st_close_method(out);
302 }
303
st_getter(std::ostream & out,string cls,string name)304 void t_st_generator::st_getter(std::ostream& out, string cls, string name) {
305 st_method(out, cls, name + "");
306 out << "^ " << name;
307 st_close_method(out);
308 }
309
st_accessors(std::ostream & out,string cls,string name,string type="anObject")310 void t_st_generator::st_accessors(std::ostream& out,
311 string cls,
312 string name,
313 string type = "anObject") {
314 st_setter(out, cls, name, type);
315 st_getter(out, cls, name);
316 }
317
generate_class_side_definition()318 void t_st_generator::generate_class_side_definition() {
319 f_ << prefix(class_name()) << " class" << endl << "\tinstanceVariableNames: 'constants enums'!"
320 << endl << endl;
321
322 st_accessors(f_, class_name() + " class", "enums");
323 st_accessors(f_, class_name() + " class", "constants");
324
325 f_ << prefix(class_name()) << " enums: Dictionary new!" << endl;
326 f_ << prefix(class_name()) << " constants: Dictionary new!" << endl;
327
328 f_ << endl;
329 }
330
331 /**
332 * Generates code for an enumerated type. Done using a class to scope
333 * the values.
334 *
335 * @param tenum The enumeration
336 */
generate_enum(t_enum * tenum)337 void t_st_generator::generate_enum(t_enum* tenum) {
338 string cls_name = program_name_ + capitalize(tenum->get_name());
339
340 f_ << prefix(class_name()) << " enums at: '" << tenum->get_name() << "' put: ["
341 << "(Dictionary new " << endl;
342
343 vector<t_enum_value*> constants = tenum->get_constants();
344 vector<t_enum_value*>::iterator c_iter;
345 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
346 int value = (*c_iter)->get_value();
347 f_ << "\tat: '" << (*c_iter)->get_name() << "' put: " << value << ";" << endl;
348 }
349
350 f_ << "\tyourself)]!" << endl << endl;
351 }
352
353 /**
354 * Generate a constant value
355 */
generate_const(t_const * tconst)356 void t_st_generator::generate_const(t_const* tconst) {
357 t_type* type = tconst->get_type();
358 string name = tconst->get_name();
359 t_const_value* value = tconst->get_value();
360
361 f_ << prefix(class_name()) << " constants at: '" << name << "' put: ["
362 << render_const_value(type, value) << "]!" << endl << endl;
363 }
364
365 /**
366 * Prints the value of a constant with the given type. Note that type checking
367 * is NOT performed in this function as it is always run beforehand using the
368 * validate_types method in main.cc
369 */
render_const_value(t_type * type,t_const_value * value)370 string t_st_generator::render_const_value(t_type* type, t_const_value* value) {
371 type = get_true_type(type);
372 std::ostringstream out;
373 if (type->is_base_type()) {
374 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
375 switch (tbase) {
376 case t_base_type::TYPE_STRING:
377 out << '"' << get_escaped_string(value) << '"';
378 break;
379 case t_base_type::TYPE_BOOL:
380 out << (value->get_integer() > 0 ? "true" : "false");
381 break;
382 case t_base_type::TYPE_I8:
383 case t_base_type::TYPE_I16:
384 case t_base_type::TYPE_I32:
385 case t_base_type::TYPE_I64:
386 out << value->get_integer();
387 break;
388 case t_base_type::TYPE_DOUBLE:
389 if (value->get_type() == t_const_value::CV_INTEGER) {
390 out << value->get_integer();
391 } else {
392 out << value->get_double();
393 }
394 break;
395 default:
396 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
397 }
398 } else if (type->is_enum()) {
399 indent(out) << value->get_integer();
400 } else if (type->is_struct() || type->is_xception()) {
401 out << "(" << capitalize(type->get_name()) << " new " << endl;
402 indent_up();
403
404 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
405 vector<t_field*>::const_iterator f_iter;
406 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
407 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
408
409 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
410 t_type* field_type = nullptr;
411 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
412 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
413 field_type = (*f_iter)->get_type();
414 }
415 }
416 if (field_type == nullptr) {
417 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
418 }
419
420 out << indent() << v_iter->first->get_string() << ": "
421 << render_const_value(field_type, v_iter->second) << ";" << endl;
422 }
423 out << indent() << "yourself)";
424
425 indent_down();
426 } else if (type->is_map()) {
427 t_type* ktype = ((t_map*)type)->get_key_type();
428 t_type* vtype = ((t_map*)type)->get_val_type();
429 out << "(Dictionary new" << endl;
430 indent_up();
431 indent_up();
432 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
433 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
434 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
435 out << indent() << indent();
436 out << "at: " << render_const_value(ktype, v_iter->first);
437 out << " put: ";
438 out << render_const_value(vtype, v_iter->second);
439 out << ";" << endl;
440 }
441 out << indent() << indent() << "yourself)";
442 indent_down();
443 indent_down();
444 } else if (type->is_list() || type->is_set()) {
445 t_type* etype;
446 if (type->is_list()) {
447 etype = ((t_list*)type)->get_elem_type();
448 } else {
449 etype = ((t_set*)type)->get_elem_type();
450 }
451 if (type->is_set()) {
452 out << "(Set new" << endl;
453 } else {
454 out << "(OrderedCollection new" << endl;
455 }
456 indent_up();
457 indent_up();
458 const vector<t_const_value*>& val = value->get_list();
459 vector<t_const_value*>::const_iterator v_iter;
460 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
461 out << indent() << indent();
462 out << "add: " << render_const_value(etype, *v_iter);
463 out << ";" << endl;
464 }
465 out << indent() << indent() << "yourself)";
466 indent_down();
467 indent_down();
468 } else {
469 throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
470 }
471 return out.str();
472 }
473
474 /**
475 * Generates a Smalltalk struct
476 */
generate_struct(t_struct * tstruct)477 void t_st_generator::generate_struct(t_struct* tstruct) {
478 generate_st_struct(f_, tstruct, false);
479 }
480
481 /**
482 * Generates a struct definition for a thrift exception. Basically the same
483 * as a struct but extends the Exception class.
484 *
485 * @param txception The struct definition
486 */
generate_xception(t_struct * txception)487 void t_st_generator::generate_xception(t_struct* txception) {
488 generate_st_struct(f_, txception, true);
489 }
490
491 /**
492 * Generates a smalltalk class to represent a struct
493 */
generate_st_struct(std::ostream & out,t_struct * tstruct,bool is_exception=false)494 void t_st_generator::generate_st_struct(std::ostream& out,
495 t_struct* tstruct,
496 bool is_exception = false) {
497 const vector<t_field*>& members = tstruct->get_members();
498 vector<t_field*>::const_iterator m_iter;
499
500 if (is_exception)
501 out << "Error";
502 else
503 out << "Object";
504
505 out << " subclass: #" << prefix(type_name(tstruct)) << endl << "\tinstanceVariableNames: '";
506
507 if (members.size() > 0) {
508 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
509 if (m_iter != members.begin())
510 out << " ";
511 out << camelcase((*m_iter)->get_name());
512 }
513 }
514
515 out << "'\n"
516 << "\tclassVariableNames: ''\n"
517 << "\tpoolDictionaries: ''\n"
518 << "\tcategory: '" << generated_category() << "'!\n\n";
519
520 generate_accessors(out, tstruct);
521 }
522
is_vowel(char c)523 bool t_st_generator::is_vowel(char c) {
524 switch (tolower(c)) {
525 case 'a':
526 case 'e':
527 case 'i':
528 case 'o':
529 case 'u':
530 return true;
531 }
532 return false;
533 }
534
a_type(t_type * type)535 string t_st_generator::a_type(t_type* type) {
536 string prefix;
537
538 if (is_vowel(type_name(type)[0]))
539 prefix = "an";
540 else
541 prefix = "a";
542
543 return prefix + capitalize(type_name(type));
544 }
545
generate_accessors(std::ostream & out,t_struct * tstruct)546 void t_st_generator::generate_accessors(std::ostream& out, t_struct* tstruct) {
547 const vector<t_field*>& members = tstruct->get_members();
548 vector<t_field*>::const_iterator m_iter;
549 string type;
550 string prefix;
551
552 if (members.size() > 0) {
553 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
554 st_accessors(out,
555 capitalize(type_name(tstruct)),
556 camelcase((*m_iter)->get_name()),
557 a_type((*m_iter)->get_type()));
558 }
559 out << endl;
560 }
561 }
562
563 /**
564 * Generates a thrift service.
565 *
566 * @param tservice The service definition
567 */
generate_service(t_service * tservice)568 void t_st_generator::generate_service(t_service* tservice) {
569 generate_service_client(tservice);
570 // generate_service_server(tservice);
571 }
572
temp_name()573 string t_st_generator::temp_name() {
574 std::ostringstream out;
575 out << "temp" << temporary_var++;
576 return out.str();
577 }
578
map_writer(t_map * tmap,string fname)579 string t_st_generator::map_writer(t_map* tmap, string fname) {
580 std::ostringstream out;
581 string key = temp_name();
582 string val = temp_name();
583
584 out << "[oprot writeMapBegin: (TMap new keyType: " << type_to_enum(tmap->get_key_type())
585 << "; valueType: " << type_to_enum(tmap->get_val_type()) << "; size: " << fname << " size)."
586 << endl;
587 indent_up();
588
589 out << indent() << fname << " keysAndValuesDo: [:" << key << " :" << val << " |" << endl;
590 indent_up();
591
592 out << indent() << write_val(tmap->get_key_type(), key) << "." << endl << indent()
593 << write_val(tmap->get_val_type(), val);
594 indent_down();
595
596 out << "]." << endl << indent() << "oprot writeMapEnd] value";
597 indent_down();
598
599 return out.str();
600 }
601
map_reader(t_map * tmap)602 string t_st_generator::map_reader(t_map* tmap) {
603 std::ostringstream out;
604 string desc = temp_name();
605 string val = temp_name();
606
607 out << "[|" << desc << " " << val << "| " << endl;
608 indent_up();
609
610 out << indent() << desc << " := iprot readMapBegin." << endl << indent() << val
611 << " := Dictionary new." << endl << indent() << desc << " size timesRepeat: [" << endl;
612
613 indent_up();
614 out << indent() << val << " at: " << read_val(tmap->get_key_type())
615 << " put: " << read_val(tmap->get_val_type());
616 indent_down();
617
618 out << "]." << endl << indent() << "iprot readMapEnd." << endl << indent() << val << "] value";
619 indent_down();
620
621 return out.str();
622 }
623
list_writer(t_list * tlist,string fname)624 string t_st_generator::list_writer(t_list* tlist, string fname) {
625 std::ostringstream out;
626 string val = temp_name();
627
628 out << "[oprot writeListBegin: (TList new elemType: " << type_to_enum(tlist->get_elem_type())
629 << "; size: " << fname << " size)." << endl;
630 indent_up();
631
632 out << indent() << fname << " do: [:" << val << "|" << endl;
633 indent_up();
634
635 out << indent() << write_val(tlist->get_elem_type(), val) << endl;
636 indent_down();
637
638 out << "]." << endl << indent() << "oprot writeListEnd] value";
639 indent_down();
640
641 return out.str();
642 }
643
list_reader(t_list * tlist)644 string t_st_generator::list_reader(t_list* tlist) {
645 std::ostringstream out;
646 string desc = temp_name();
647 string val = temp_name();
648
649 out << "[|" << desc << " " << val << "| " << desc << " := iprot readListBegin." << endl;
650 indent_up();
651
652 out << indent() << val << " := OrderedCollection new." << endl << indent() << desc
653 << " size timesRepeat: [" << endl;
654
655 indent_up();
656 out << indent() << val << " add: " << read_val(tlist->get_elem_type());
657 indent_down();
658
659 out << "]." << endl << indent() << "iprot readListEnd." << endl << indent() << val << "] value";
660 indent_down();
661
662 return out.str();
663 }
664
set_writer(t_set * tset,string fname)665 string t_st_generator::set_writer(t_set* tset, string fname) {
666 std::ostringstream out;
667 string val = temp_name();
668
669 out << "[oprot writeSetBegin: (TSet new elemType: " << type_to_enum(tset->get_elem_type())
670 << "; size: " << fname << " size)." << endl;
671 indent_up();
672
673 out << indent() << fname << " do: [:" << val << "|" << endl;
674 indent_up();
675
676 out << indent() << write_val(tset->get_elem_type(), val) << endl;
677 indent_down();
678
679 out << "]." << endl << indent() << "oprot writeSetEnd] value";
680 indent_down();
681
682 return out.str();
683 }
684
set_reader(t_set * tset)685 string t_st_generator::set_reader(t_set* tset) {
686 std::ostringstream out;
687 string desc = temp_name();
688 string val = temp_name();
689
690 out << "[|" << desc << " " << val << "| " << desc << " := iprot readSetBegin." << endl;
691 indent_up();
692
693 out << indent() << val << " := Set new." << endl << indent() << desc << " size timesRepeat: ["
694 << endl;
695
696 indent_up();
697 out << indent() << val << " add: " << read_val(tset->get_elem_type());
698 indent_down();
699
700 out << "]." << endl << indent() << "iprot readSetEnd." << endl << indent() << val << "] value";
701 indent_down();
702
703 return out.str();
704 }
705
struct_writer(t_struct * tstruct,string sname)706 string t_st_generator::struct_writer(t_struct* tstruct, string sname) {
707 std::ostringstream out;
708 const vector<t_field*>& fields = tstruct->get_sorted_members();
709 vector<t_field*>::const_iterator fld_iter;
710
711 out << "[oprot writeStructBegin: "
712 << "(TStruct new name: '" + tstruct->get_name() + "')." << endl;
713 indent_up();
714
715 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
716 bool optional = (*fld_iter)->get_req() == t_field::T_OPTIONAL;
717 string fname = camelcase((*fld_iter)->get_name());
718 string accessor = sname + " " + camelcase(fname);
719
720 if (optional) {
721 out << indent() << accessor << " ifNotNil: [" << endl;
722 indent_up();
723 }
724
725 out << indent() << "oprot writeFieldBegin: (TField new name: '" << fname
726 << "'; type: " << type_to_enum((*fld_iter)->get_type())
727 << "; id: " << (*fld_iter)->get_key() << ")." << endl;
728
729 out << indent() << write_val((*fld_iter)->get_type(), accessor) << "." << endl << indent()
730 << "oprot writeFieldEnd";
731
732 if (optional) {
733 out << "]";
734 indent_down();
735 }
736
737 out << "." << endl;
738 }
739
740 out << indent() << "oprot writeFieldStop; writeStructEnd] value";
741 indent_down();
742
743 return out.str();
744 }
745
struct_reader(t_struct * tstruct,string clsName="")746 string t_st_generator::struct_reader(t_struct* tstruct, string clsName = "") {
747 std::ostringstream out;
748 const vector<t_field*>& fields = tstruct->get_members();
749 vector<t_field*>::const_iterator fld_iter;
750 string val = temp_name();
751 string desc = temp_name();
752 string found = temp_name();
753
754 if (clsName.size() == 0) {
755 clsName = tstruct->get_name();
756 }
757
758 out << "[|" << desc << " " << val << "|" << endl;
759 indent_up();
760
761 // This is nasty, but without it we'll break things by prefixing TResult.
762 string name = ((capitalize(clsName) == "TResult") ? capitalize(clsName) : prefix(clsName));
763 out << indent() << val << " := " << name << " new." << endl;
764
765 out << indent() << "iprot readStructBegin." << endl << indent() << "[" << desc
766 << " := iprot readFieldBegin." << endl << indent() << desc
767 << " type = TType stop] whileFalse: [|" << found << "|" << endl;
768 indent_up();
769
770 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
771 out << indent() << desc << " id = " << (*fld_iter)->get_key() << " ifTrue: [" << endl;
772 indent_up();
773
774 out << indent() << found << " := true." << endl << indent() << val << " "
775 << camelcase((*fld_iter)->get_name()) << ": " << read_val((*fld_iter)->get_type());
776 indent_down();
777
778 out << "]." << endl;
779 }
780
781 out << indent() << found << " ifNil: [iprot skip: " << desc << " type]]." << endl;
782 indent_down();
783
784 out << indent() << "oprot readStructEnd." << endl << indent() << val << "] value";
785 indent_down();
786
787 return out.str();
788 }
789
write_val(t_type * t,string fname)790 string t_st_generator::write_val(t_type* t, string fname) {
791 t = get_true_type(t);
792
793 if (t->is_base_type()) {
794 t_base_type::t_base tbase = ((t_base_type*)t)->get_base();
795 switch (tbase) {
796 case t_base_type::TYPE_DOUBLE:
797 return "iprot writeDouble: " + fname + " asFloat";
798 break;
799 case t_base_type::TYPE_I8:
800 case t_base_type::TYPE_I16:
801 case t_base_type::TYPE_I32:
802 case t_base_type::TYPE_I64:
803 return "iprot write" + capitalize(type_name(t)) + ": " + fname + " asInteger";
804 default:
805 return "iprot write" + capitalize(type_name(t)) + ": " + fname;
806 }
807 } else if (t->is_map()) {
808 return map_writer((t_map*)t, fname);
809 } else if (t->is_struct() || t->is_xception()) {
810 return struct_writer((t_struct*)t, fname);
811 } else if (t->is_list()) {
812 return list_writer((t_list*)t, fname);
813 } else if (t->is_set()) {
814 return set_writer((t_set*)t, fname);
815 } else if (t->is_enum()) {
816 return "iprot writeI32: " + fname;
817 } else {
818 throw "Sorry, I don't know how to write this: " + type_name(t);
819 }
820 }
821
read_val(t_type * t)822 string t_st_generator::read_val(t_type* t) {
823 t = get_true_type(t);
824
825 if (t->is_base_type()) {
826 return "iprot read" + capitalize(type_name(t));
827 } else if (t->is_map()) {
828 return map_reader((t_map*)t);
829 } else if (t->is_struct() || t->is_xception()) {
830 return struct_reader((t_struct*)t);
831 } else if (t->is_list()) {
832 return list_reader((t_list*)t);
833 } else if (t->is_set()) {
834 return set_reader((t_set*)t);
835 } else if (t->is_enum()) {
836 return "iprot readI32";
837 } else {
838 throw "Sorry, I don't know how to read this: " + type_name(t);
839 }
840 }
841
generate_send_method(t_function * function)842 void t_st_generator::generate_send_method(t_function* function) {
843 string funname = function->get_name();
844 string signature = function_signature(function);
845 t_struct* arg_struct = function->get_arglist();
846 const vector<t_field*>& fields = arg_struct->get_members();
847 vector<t_field*>::const_iterator fld_iter;
848
849 st_method(f_, client_class_name(), "send" + capitalize(signature));
850 f_ << "oprot writeMessageBegin:" << endl;
851 indent_up();
852
853 f_ << indent() << "(TCallMessage new" << endl;
854 indent_up();
855
856 f_ << indent() << "name: '" << funname << "'; " << endl << indent() << "seqid: self nextSeqid)."
857 << endl;
858 indent_down();
859 indent_down();
860
861 f_ << indent() << "oprot writeStructBegin: "
862 << "(TStruct new name: '" + capitalize(camelcase(funname)) + "_args')." << endl;
863
864 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
865 string fname = camelcase((*fld_iter)->get_name());
866
867 f_ << indent() << "oprot writeFieldBegin: (TField new name: '" << fname
868 << "'; type: " << type_to_enum((*fld_iter)->get_type()) << "; id: " << (*fld_iter)->get_key()
869 << ")." << endl;
870
871 f_ << indent() << write_val((*fld_iter)->get_type(), fname) << "." << endl << indent()
872 << "oprot writeFieldEnd." << endl;
873 }
874
875 f_ << indent() << "oprot writeFieldStop; writeStructEnd; writeMessageEnd." << endl;
876 f_ << indent() << "oprot transport flush";
877
878 st_close_method(f_);
879 }
880
881 // We only support receiving TResult structures (so this won't work on the server side)
generate_recv_method(t_function * function)882 void t_st_generator::generate_recv_method(t_function* function) {
883 string funname = camelcase(function->get_name());
884 string signature = function_signature(function);
885
886 t_struct result(program_, "TResult");
887 t_field success(function->get_returntype(), "success", 0);
888 result.append(&success);
889
890 t_struct* xs = function->get_xceptions();
891 const vector<t_field*>& fields = xs->get_members();
892 vector<t_field*>::const_iterator f_iter;
893 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
894 // duplicate the field, but call it "exception"... we don't need a dynamic name
895 t_field* exception = new t_field((*f_iter)->get_type(), "exception", (*f_iter)->get_key());
896 result.append(exception);
897 }
898
899 st_method(f_, client_class_name(), "recv" + capitalize(funname));
900 f_ << "| f msg res | " << endl << indent() << "msg := oprot readMessageBegin." << endl << indent()
901 << "self validateRemoteMessage: msg." << endl << indent()
902 << "res := " << struct_reader(&result) << "." << endl << indent() << "oprot readMessageEnd."
903 << endl << indent() << "oprot transport flush." << endl << indent()
904 << "res exception ifNotNil: [res exception signal]." << endl << indent() << "^ res";
905 st_close_method(f_);
906 }
907
function_types_comment(t_function * fn)908 string t_st_generator::function_types_comment(t_function* fn) {
909 std::ostringstream out;
910 const vector<t_field*>& fields = fn->get_arglist()->get_members();
911 vector<t_field*>::const_iterator f_iter;
912
913 out << "\"";
914
915 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
916 out << camelcase((*f_iter)->get_name()) << ": " << type_name((*f_iter)->get_type());
917 if ((f_iter + 1) != fields.end()) {
918 out << ", ";
919 }
920 }
921
922 out << "\"";
923
924 return out.str();
925 }
926
927 /**
928 * Generates a service client definition.
929 *
930 * @param tservice The service to generate a server for.
931 */
generate_service_client(t_service * tservice)932 void t_st_generator::generate_service_client(t_service* tservice) {
933 string extends = "";
934 string extends_client = "TClient";
935 vector<t_function*> functions = tservice->get_functions();
936 vector<t_function*>::iterator f_iter;
937
938 if (tservice->get_extends() != nullptr) {
939 extends = type_name(tservice->get_extends());
940 extends_client = extends + "Client";
941 }
942
943 f_ << extends_client << " subclass: #" << prefix(client_class_name()) << endl
944 << "\tinstanceVariableNames: ''\n"
945 << "\tclassVariableNames: ''\n"
946 << "\tpoolDictionaries: ''\n"
947 << "\tcategory: '" << generated_category() << "'!\n\n";
948
949 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
950 string funname = camelcase((*f_iter)->get_name());
951 string signature = function_signature(*f_iter);
952
953 st_method(f_, client_class_name(), signature);
954 f_ << function_types_comment(*f_iter) << endl << indent() << "self send"
955 << capitalize(signature) << "." << endl;
956
957 if (!(*f_iter)->is_oneway()) {
958 f_ << indent() << "^ self recv" << capitalize(funname) << " success " << endl;
959 }
960
961 st_close_method(f_);
962
963 generate_send_method(*f_iter);
964 if (!(*f_iter)->is_oneway()) {
965 generate_recv_method(*f_iter);
966 }
967 }
968 }
969
970 /**
971 * Renders a function signature of the form 'type name(args)'
972 *
973 * @param tfunction Function definition
974 * @return String of rendered function definition
975 */
function_signature(t_function * tfunction)976 string t_st_generator::function_signature(t_function* tfunction) {
977 return camelcase(tfunction->get_name()) + capitalize(argument_list(tfunction->get_arglist()));
978 }
979
980 /**
981 * Renders a field list
982 */
argument_list(t_struct * tstruct)983 string t_st_generator::argument_list(t_struct* tstruct) {
984 string result = "";
985
986 const vector<t_field*>& fields = tstruct->get_members();
987 vector<t_field*>::const_iterator f_iter;
988 bool first = true;
989 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
990 if (first) {
991 first = false;
992 } else {
993 result += " ";
994 }
995 string name = camelcase((*f_iter)->get_name());
996 result += name + ": " + name;
997 }
998 return result;
999 }
1000
type_name(t_type * ttype)1001 string t_st_generator::type_name(t_type* ttype) {
1002 string prefix = "";
1003 t_program* program = ttype->get_program();
1004 if (program != nullptr && program != program_) {
1005 if (!ttype->is_service()) {
1006 prefix = program->get_name() + "_types.";
1007 }
1008 }
1009
1010 string name = ttype->get_name();
1011 if (ttype->is_struct() || ttype->is_xception()) {
1012 name = capitalize(ttype->get_name());
1013 }
1014
1015 return prefix + name;
1016 }
1017
1018 /* Convert t_type to Smalltalk type code */
type_to_enum(t_type * type)1019 string t_st_generator::type_to_enum(t_type* type) {
1020 type = get_true_type(type);
1021
1022 if (type->is_base_type()) {
1023 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1024 switch (tbase) {
1025 case t_base_type::TYPE_VOID:
1026 throw "NO T_VOID CONSTRUCT";
1027 case t_base_type::TYPE_STRING:
1028 return "TType string";
1029 case t_base_type::TYPE_BOOL:
1030 return "TType bool";
1031 case t_base_type::TYPE_I8:
1032 return "TType byte";
1033 case t_base_type::TYPE_I16:
1034 return "TType i16";
1035 case t_base_type::TYPE_I32:
1036 return "TType i32";
1037 case t_base_type::TYPE_I64:
1038 return "TType i64";
1039 case t_base_type::TYPE_DOUBLE:
1040 return "TType double";
1041 default:
1042 throw "compiler error: unhandled type";
1043 }
1044 } else if (type->is_enum()) {
1045 return "TType i32";
1046 } else if (type->is_struct() || type->is_xception()) {
1047 return "TType struct";
1048 } else if (type->is_map()) {
1049 return "TType map";
1050 } else if (type->is_set()) {
1051 return "TType set";
1052 } else if (type->is_list()) {
1053 return "TType list";
1054 }
1055
1056 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
1057 }
1058
display_name() const1059 std::string t_st_generator::display_name() const {
1060 return "Smalltalk";
1061 }
1062
1063
1064 THRIFT_REGISTER_GENERATOR(st, "Smalltalk", "")
1065