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