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