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 <cassert>
25 
26 #include <string>
27 #include <fstream>
28 #include <iostream>
29 #include <vector>
30 #include <cctype>
31 
32 #include <stdlib.h>
33 #include <sys/stat.h>
34 #include <sstream>
35 
36 #include "thrift/platform.h"
37 #include "thrift/generate/t_oop_generator.h"
38 #include "thrift/generate/t_netstd_generator.h"
39 
40 using std::map;
41 using std::ostream;
42 using std::ostringstream;
43 using std::string;
44 using std::stringstream;
45 using std::vector;
46 
47 //TODO: check for indentation
48 //TODO: Do we need seqId_ in generation?
49 
t_netstd_generator(t_program * program,const map<string,string> & parsed_options,const string & option_string)50 t_netstd_generator::t_netstd_generator(t_program* program, const map<string, string>& parsed_options, const string& option_string)
51     : t_oop_generator(program)
52 {
53     (void)option_string;
54 
55     union_ = false;
56     serialize_ = false;
57     wcf_ = false;
58     wcf_namespace_.clear();
59 
60     map<string, string>::const_iterator iter;
61 
62     for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter)
63     {
64         if (iter->first.compare("union") == 0)
65         {
66             union_ = true;
67         }
68         else if (iter->first.compare("serial") == 0)
69         {
70             serialize_ = true;
71             wcf_namespace_ = iter->second; // since there can be only one namespace
72         }
73         else if (iter->first.compare("wcf") == 0)
74         {
75             wcf_ = true;
76             wcf_namespace_ = iter->second;
77         }
78         else
79         {
80             throw "unknown option netstd:" + iter->first;
81         }
82     }
83 
84     out_dir_base_ = "gen-netstd";
85 }
86 
correct_function_name_for_async(string const & function_name)87 static string correct_function_name_for_async(string const& function_name)
88 {
89     string const async_end = "Async";
90     size_t i = function_name.find(async_end);
91     if (i != string::npos)
92     {
93         return function_name + async_end;
94     }
95 
96     return function_name;
97 }
98 
99 /**
100 * \brief Search and replace "_args" substring in struct name if exist (for C# class naming)
101 * \param struct_name
102 * \return Modified struct name ("Struct_args" -> "StructArgs") or original name
103 */
check_and_correct_struct_name(const string & struct_name)104 static string check_and_correct_struct_name(const string& struct_name)
105 {
106     string args_end = "_args";
107     size_t i = struct_name.find(args_end);
108     if (i != string::npos)
109     {
110         string new_struct_name = struct_name;
111         new_struct_name.replace(i, args_end.length(), "Args");
112         return new_struct_name;
113     }
114 
115     string result_end = "_result";
116     size_t j = struct_name.find(result_end);
117     if (j != string::npos)
118     {
119         string new_struct_name = struct_name;
120         new_struct_name.replace(j, result_end.length(), "Result");
121         return new_struct_name;
122     }
123 
124     return struct_name;
125 }
126 
field_has_default(t_field * tfield)127 static bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; }
128 
field_is_required(t_field * tfield)129 static bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; }
130 
type_can_be_null(t_type * ttype)131 static bool type_can_be_null(t_type* ttype)
132 {
133     while (ttype->is_typedef())
134     {
135         ttype = static_cast<t_typedef*>(ttype)->get_type();
136     }
137 
138     return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string();
139 }
140 
is_wcf_enabled() const141 bool t_netstd_generator::is_wcf_enabled() const { return wcf_; }
142 
is_serialize_enabled() const143 bool t_netstd_generator::is_serialize_enabled() const { return serialize_; }
144 
is_union_enabled() const145 bool t_netstd_generator::is_union_enabled() const { return union_; }
146 
get_keywords_list() const147 map<string, int> t_netstd_generator::get_keywords_list() const
148 {
149     return netstd_keywords;
150 }
151 
init_generator()152 void t_netstd_generator::init_generator()
153 {
154     MKDIR(get_out_dir().c_str());
155 
156     // for usage of csharp namespaces in thrift files (from files for csharp)
157     namespace_name_ = program_->get_namespace("netstd");
158     if (namespace_name_.empty())
159     {
160         namespace_name_ = program_->get_namespace("netstd");
161     }
162 
163     string dir = namespace_name_;
164     string subdir = get_out_dir().c_str();
165     string::size_type loc;
166 
167     while ((loc = dir.find(".")) != string::npos)
168     {
169         subdir = subdir + "/" + dir.substr(0, loc);
170         MKDIR(subdir.c_str());
171         dir = dir.substr(loc + 1);
172     }
173     if (dir.size() > 0)
174     {
175         subdir = subdir + "/" + dir;
176         MKDIR(subdir.c_str());
177     }
178 
179     namespace_dir_ = subdir;
180     init_keywords();
181 
182     while (!member_mapping_scopes.empty())
183     {
184         cleanup_member_name_mapping(member_mapping_scopes.back().scope_member);
185     }
186 
187     pverbose(".NET Standard options:\n");
188     pverbose("- union ...... %s\n", (is_union_enabled() ? "ON" : "off"));
189     pverbose("- serialize .. %s\n", (is_serialize_enabled() ? "ON" : "off"));
190     pverbose("- wcf ........ %s\n", (is_wcf_enabled() ? "ON" : "off"));
191 }
192 
normalize_name(string name)193 string t_netstd_generator::normalize_name(string name)
194 {
195     string tmp(name);
196     transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int(*)(int)>(tolower));
197 
198     // un-conflict keywords by prefixing with "@"
199     if (netstd_keywords.find(tmp) != netstd_keywords.end())
200     {
201         return "@" + name;
202     }
203 
204     // no changes necessary
205     return name;
206 }
207 
init_keywords()208 void t_netstd_generator::init_keywords()
209 {
210     netstd_keywords.clear();
211 
212     // C# keywords
213     netstd_keywords["abstract"] = 1;
214     netstd_keywords["as"] = 1;
215     netstd_keywords["base"] = 1;
216     netstd_keywords["bool"] = 1;
217     netstd_keywords["break"] = 1;
218     netstd_keywords["byte"] = 1;
219     netstd_keywords["case"] = 1;
220     netstd_keywords["catch"] = 1;
221     netstd_keywords["char"] = 1;
222     netstd_keywords["checked"] = 1;
223     netstd_keywords["class"] = 1;
224     netstd_keywords["const"] = 1;
225     netstd_keywords["continue"] = 1;
226     netstd_keywords["decimal"] = 1;
227     netstd_keywords["default"] = 1;
228     netstd_keywords["delegate"] = 1;
229     netstd_keywords["do"] = 1;
230     netstd_keywords["double"] = 1;
231     netstd_keywords["else"] = 1;
232     netstd_keywords["enum"] = 1;
233     netstd_keywords["event"] = 1;
234     netstd_keywords["explicit"] = 1;
235     netstd_keywords["extern"] = 1;
236     netstd_keywords["false"] = 1;
237     netstd_keywords["finally"] = 1;
238     netstd_keywords["fixed"] = 1;
239     netstd_keywords["float"] = 1;
240     netstd_keywords["for"] = 1;
241     netstd_keywords["foreach"] = 1;
242     netstd_keywords["goto"] = 1;
243     netstd_keywords["if"] = 1;
244     netstd_keywords["implicit"] = 1;
245     netstd_keywords["in"] = 1;
246     netstd_keywords["int"] = 1;
247     netstd_keywords["interface"] = 1;
248     netstd_keywords["internal"] = 1;
249     netstd_keywords["is"] = 1;
250     netstd_keywords["lock"] = 1;
251     netstd_keywords["long"] = 1;
252     netstd_keywords["namespace"] = 1;
253     netstd_keywords["new"] = 1;
254     netstd_keywords["null"] = 1;
255     netstd_keywords["object"] = 1;
256     netstd_keywords["operator"] = 1;
257     netstd_keywords["out"] = 1;
258     netstd_keywords["override"] = 1;
259     netstd_keywords["params"] = 1;
260     netstd_keywords["private"] = 1;
261     netstd_keywords["protected"] = 1;
262     netstd_keywords["public"] = 1;
263     netstd_keywords["readonly"] = 1;
264     netstd_keywords["ref"] = 1;
265     netstd_keywords["return"] = 1;
266     netstd_keywords["sbyte"] = 1;
267     netstd_keywords["sealed"] = 1;
268     netstd_keywords["short"] = 1;
269     netstd_keywords["sizeof"] = 1;
270     netstd_keywords["stackalloc"] = 1;
271     netstd_keywords["static"] = 1;
272     netstd_keywords["string"] = 1;
273     netstd_keywords["struct"] = 1;
274     netstd_keywords["switch"] = 1;
275     netstd_keywords["this"] = 1;
276     netstd_keywords["throw"] = 1;
277     netstd_keywords["true"] = 1;
278     netstd_keywords["try"] = 1;
279     netstd_keywords["typeof"] = 1;
280     netstd_keywords["uint"] = 1;
281     netstd_keywords["ulong"] = 1;
282     netstd_keywords["unchecked"] = 1;
283     netstd_keywords["unsafe"] = 1;
284     netstd_keywords["ushort"] = 1;
285     netstd_keywords["using"] = 1;
286     netstd_keywords["virtual"] = 1;
287     netstd_keywords["void"] = 1;
288     netstd_keywords["volatile"] = 1;
289     netstd_keywords["while"] = 1;
290 
291     // C# contextual keywords
292     netstd_keywords["add"] = 1;
293     netstd_keywords["alias"] = 1;
294     netstd_keywords["ascending"] = 1;
295     netstd_keywords["async"] = 1;
296     netstd_keywords["await"] = 1;
297     netstd_keywords["descending"] = 1;
298     netstd_keywords["dynamic"] = 1;
299     netstd_keywords["from"] = 1;
300     netstd_keywords["get"] = 1;
301     netstd_keywords["global"] = 1;
302     netstd_keywords["group"] = 1;
303     netstd_keywords["into"] = 1;
304     netstd_keywords["join"] = 1;
305     netstd_keywords["let"] = 1;
306     netstd_keywords["orderby"] = 1;
307     netstd_keywords["partial"] = 1;
308     netstd_keywords["remove"] = 1;
309     netstd_keywords["select"] = 1;
310     netstd_keywords["set"] = 1;
311     netstd_keywords["value"] = 1;
312     netstd_keywords["var"] = 1;
313     netstd_keywords["where"] = 1;
314     netstd_keywords["yield"] = 1;
315 
316     netstd_keywords["when"] = 1;
317 }
318 
start_netstd_namespace(ostream & out)319 void t_netstd_generator::start_netstd_namespace(ostream& out)
320 {
321     if (!namespace_name_.empty())
322     {
323         out << "namespace " << namespace_name_ << endl;
324         scope_up(out);
325     }
326 }
327 
end_netstd_namespace(ostream & out)328 void t_netstd_generator::end_netstd_namespace(ostream& out)
329 {
330     if (!namespace_name_.empty())
331     {
332         scope_down(out);
333     }
334 }
335 
netstd_type_usings() const336 string t_netstd_generator::netstd_type_usings() const
337 {
338     string namespaces =
339         "using System;\n"
340         "using System.Collections;\n"
341         "using System.Collections.Generic;\n"
342         "using System.Text;\n"
343         "using System.IO;\n"
344         "using System.Threading;\n"
345         "using System.Threading.Tasks;\n"
346         "using Thrift;\n"
347         "using Thrift.Collections;\n";
348 
349     if (is_wcf_enabled())
350     {
351         namespaces += "using System.ServiceModel;\n";
352         namespaces += "using System.Runtime.Serialization;\n";
353     }
354 
355     return namespaces + endl;
356 }
357 
netstd_thrift_usings() const358 string t_netstd_generator::netstd_thrift_usings() const
359 {
360     string namespaces =
361         "using Thrift.Protocol;\n"
362         "using Thrift.Protocol.Entities;\n"
363         "using Thrift.Protocol.Utilities;\n"
364         "using Thrift.Transport;\n"
365         "using Thrift.Transport.Client;\n"
366         "using Thrift.Transport.Server;\n"
367         "using Thrift.Processor;\n";
368 
369     return namespaces + endl;
370 }
371 
close_generator()372 void t_netstd_generator::close_generator()
373 {
374 }
375 
generate_typedef(t_typedef * ttypedef)376 void t_netstd_generator::generate_typedef(t_typedef* ttypedef)
377 {
378     (void)ttypedef;
379 }
380 
generate_enum(t_enum * tenum)381 void t_netstd_generator::generate_enum(t_enum* tenum)
382 {
383     int ic = indent_count();
384     string f_enum_name = namespace_dir_ + "/" + tenum->get_name() + ".cs";
385 
386     ofstream_with_content_based_conditional_update f_enum;
387     f_enum.open(f_enum_name.c_str());
388 
389     generate_enum(f_enum, tenum);
390 
391     f_enum.close();
392     indent_validate(ic, "generate_enum");
393 }
394 
generate_enum(ostream & out,t_enum * tenum)395 void t_netstd_generator::generate_enum(ostream& out, t_enum* tenum)
396 {
397     out << autogen_comment() << endl;
398 
399     start_netstd_namespace(out);
400     generate_netstd_doc(out, tenum);
401 
402     out << indent() << "public enum " << tenum->get_name() << endl;
403     scope_up(out);
404 
405     vector<t_enum_value*> constants = tenum->get_constants();
406     vector<t_enum_value*>::iterator c_iter;
407 
408     for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter)
409     {
410         generate_netstd_doc(out, *c_iter);
411         int value = (*c_iter)->get_value();
412         out << indent() << (*c_iter)->get_name() << " = " << value << "," << endl;
413     }
414 
415     scope_down(out);
416     end_netstd_namespace(out);
417 }
418 
generate_consts(vector<t_const * > consts)419 void t_netstd_generator::generate_consts(vector<t_const*> consts)
420 {
421     if (consts.empty())
422     {
423         return;
424     }
425 
426     string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs";
427     ofstream_with_content_based_conditional_update f_consts;
428     f_consts.open(f_consts_name.c_str());
429 
430     generate_consts(f_consts, consts);
431 
432     f_consts.close();
433 }
434 
generate_consts(ostream & out,vector<t_const * > consts)435 void t_netstd_generator::generate_consts(ostream& out, vector<t_const*> consts)
436 {
437     if (consts.empty())
438     {
439         return;
440     }
441 
442     out << autogen_comment() << netstd_type_usings() << endl;
443 
444     start_netstd_namespace(out);
445 
446     out << indent() << "public static class " << make_valid_csharp_identifier(program_name_) << "Constants" << endl;
447 
448     scope_up(out);
449 
450     vector<t_const*>::iterator c_iter;
451     bool need_static_constructor = false;
452     for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter)
453     {
454         generate_netstd_doc(out, *c_iter);
455         if (print_const_value(out, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false))
456         {
457             need_static_constructor = true;
458         }
459     }
460 
461     if (need_static_constructor)
462     {
463         print_const_constructor(out, consts);
464     }
465 
466     scope_down(out);
467     end_netstd_namespace(out);
468 }
469 
print_const_def_value(ostream & out,string name,t_type * type,t_const_value * value)470 void t_netstd_generator::print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value)
471 {
472     if (type->is_struct() || type->is_xception())
473     {
474         const vector<t_field*>& fields = static_cast<t_struct*>(type)->get_members();
475         const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
476         vector<t_field*>::const_iterator f_iter;
477         map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
478         prepare_member_name_mapping(static_cast<t_struct*>(type));
479 
480         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
481         {
482             t_field* field = NULL;
483 
484             for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
485             {
486                 if ((*f_iter)->get_name() == v_iter->first->get_string())
487                 {
488                     field = *f_iter;
489                 }
490             }
491 
492             if (field == NULL)
493             {
494                 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
495             }
496 
497             t_type* field_type = field->get_type();
498 
499             string val = render_const_value(out, name, field_type, v_iter->second);
500             out << indent() << name << "." << prop_name(field) << " = " << val << ";" << endl;
501         }
502 
503         cleanup_member_name_mapping(static_cast<t_struct*>(type));
504     }
505     else if (type->is_map())
506     {
507         t_type* ktype = static_cast<t_map*>(type)->get_key_type();
508         t_type* vtype = static_cast<t_map*>(type)->get_val_type();
509         const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
510         map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
511         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
512         {
513             string key = render_const_value(out, name, ktype, v_iter->first);
514             string val = render_const_value(out, name, vtype, v_iter->second);
515             out << indent() << name << "[" << key << "]" << " = " << val << ";" << endl;
516         }
517     }
518     else if (type->is_list() || type->is_set())
519     {
520         t_type* etype;
521         if (type->is_list())
522         {
523             etype = static_cast<t_list*>(type)->get_elem_type();
524         }
525         else
526         {
527             etype = static_cast<t_set*>(type)->get_elem_type();
528         }
529 
530         const vector<t_const_value*>& val = value->get_list();
531         vector<t_const_value*>::const_iterator v_iter;
532         for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
533         {
534             string val = render_const_value(out, name, etype, *v_iter);
535             out << indent() << name << ".Add(" << val << ");" << endl;
536         }
537     }
538 }
539 
print_const_constructor(ostream & out,vector<t_const * > consts)540 void t_netstd_generator::print_const_constructor(ostream& out, vector<t_const*> consts)
541 {
542     out << indent() << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl;
543     scope_up(out);
544 
545     vector<t_const*>::iterator c_iter;
546     for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter)
547     {
548         string name = (*c_iter)->get_name();
549         t_type* type = (*c_iter)->get_type();
550         t_const_value* value = (*c_iter)->get_value();
551 
552         print_const_def_value(out, name, type, value);
553     }
554     scope_down(out);
555 }
556 
print_const_value(ostream & out,string name,t_type * type,t_const_value * value,bool in_static,bool defval,bool needtype)557 bool t_netstd_generator::print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype)
558 {
559     out << indent();
560     bool need_static_construction = !in_static;
561     while (type->is_typedef())
562     {
563         type = static_cast<t_typedef*>(type)->get_type();
564     }
565 
566     if (!defval || needtype)
567     {
568         out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ") << type_name(type) << " ";
569     }
570 
571     if (type->is_base_type())
572     {
573         string v2 = render_const_value(out, name, type, value);
574         out << normalize_name(name) << " = " << v2 << ";" << endl;
575         need_static_construction = false;
576     }
577     else if (type->is_enum())
578     {
579         out << name << " = " << type_name(type) << "." << value->get_identifier_name() << ";" << endl;
580         need_static_construction = false;
581     }
582     else if (type->is_struct() || type->is_xception())
583     {
584         out << name << " = new " << type_name(type) << "();" << endl;
585     }
586     else if (type->is_map())
587     {
588         out << name << " = new " << type_name(type) << "();" << endl;
589     }
590     else if (type->is_list() || type->is_set())
591     {
592         out << name << " = new " << type_name(type) << "();" << endl;
593     }
594 
595     if (defval && !type->is_base_type() && !type->is_enum())
596     {
597         print_const_def_value(out, name, type, value);
598     }
599 
600     return need_static_construction;
601 }
602 
render_const_value(ostream & out,string name,t_type * type,t_const_value * value)603 string t_netstd_generator::render_const_value(ostream& out, string name, t_type* type, t_const_value* value)
604 {
605     (void)name;
606     ostringstream render;
607 
608     if (type->is_base_type())
609     {
610         t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
611         switch (tbase)
612         {
613         case t_base_type::TYPE_STRING:
614             render << '"' << get_escaped_string(value) << '"';
615             break;
616         case t_base_type::TYPE_BOOL:
617             render << ((value->get_integer() > 0) ? "true" : "false");
618             break;
619         case t_base_type::TYPE_I8:
620         case t_base_type::TYPE_I16:
621         case t_base_type::TYPE_I32:
622         case t_base_type::TYPE_I64:
623             render << value->get_integer();
624             break;
625         case t_base_type::TYPE_DOUBLE:
626             if (value->get_type() == t_const_value::CV_INTEGER)
627             {
628                 render << value->get_integer();
629             }
630             else
631             {
632                 render << value->get_double();
633             }
634             break;
635         default:
636             throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
637         }
638     }
639     else if (type->is_enum())
640     {
641         render << type->get_name() << "." << value->get_identifier_name();
642     }
643     else
644     {
645         string t = tmp("tmp");
646         print_const_value(out, t, type, value, true, true, true);
647         render << t;
648     }
649 
650     return render.str();
651 }
652 
generate_struct(t_struct * tstruct)653 void t_netstd_generator::generate_struct(t_struct* tstruct)
654 {
655     if (is_union_enabled() && tstruct->is_union())
656     {
657         generate_netstd_union(tstruct);
658     }
659     else
660     {
661         generate_netstd_struct(tstruct, false);
662     }
663 }
664 
generate_xception(t_struct * txception)665 void t_netstd_generator::generate_xception(t_struct* txception)
666 {
667     generate_netstd_struct(txception, true);
668 }
669 
generate_netstd_struct(t_struct * tstruct,bool is_exception)670 void t_netstd_generator::generate_netstd_struct(t_struct* tstruct, bool is_exception)
671 {
672     int ic = indent_count();
673 
674     string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs";
675     ofstream_with_content_based_conditional_update f_struct;
676 
677     f_struct.open(f_struct_name.c_str());
678 
679     f_struct << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl;
680 
681     generate_netstd_struct_definition(f_struct, tstruct, is_exception);
682 
683     f_struct.close();
684 
685     indent_validate(ic, "generate_netstd_struct");
686 }
687 
generate_netstd_struct_definition(ostream & out,t_struct * tstruct,bool is_exception,bool in_class,bool is_result)688 void t_netstd_generator::generate_netstd_struct_definition(ostream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result)
689 {
690     if (!in_class)
691     {
692         start_netstd_namespace(out);
693     }
694 
695     out << endl;
696 
697     generate_netstd_doc(out, tstruct);
698     prepare_member_name_mapping(tstruct);
699 
700     if ((is_serialize_enabled() || is_wcf_enabled()) && !is_exception)
701     {
702         out << indent() << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
703     }
704 
705     bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
706 
707     string sharp_struct_name = check_and_correct_struct_name(normalize_name(tstruct->get_name()));
708 
709     out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << sharp_struct_name << " : ";
710 
711     if (is_exception)
712     {
713         out << "TException, ";
714     }
715 
716     out << "TBase" << endl
717         << indent() << "{" << endl;
718     indent_up();
719 
720     const vector<t_field*>& members = tstruct->get_members();
721     vector<t_field*>::const_iterator m_iter;
722 
723     // make private members with public Properties
724     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
725     {
726         // if the field is required, then we use auto-properties
727         if (!field_is_required((*m_iter)))
728         {
729             out << indent() << "private " << declare_field(*m_iter, false, "_") << endl;
730         }
731     }
732     out << endl;
733 
734     bool has_non_required_fields = false;
735     bool has_required_fields = false;
736     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
737     {
738         generate_netstd_doc(out, *m_iter);
739         generate_property(out, *m_iter, true, true);
740         bool is_required = field_is_required((*m_iter));
741         if (is_required)
742         {
743             has_required_fields = true;
744         }
745         else
746         {
747             has_non_required_fields = true;
748         }
749     }
750 
751     bool generate_isset = has_non_required_fields;
752     if (generate_isset)
753     {
754         out << endl;
755         if (is_serialize_enabled() || is_wcf_enabled())
756         {
757             out << indent() << "[DataMember(Order = 1)]" << endl;
758         }
759         out << indent() << "public Isset __isset;" << endl;
760         if (is_serialize_enabled() || is_wcf_enabled())
761         {
762             out << indent() << "[DataContract]" << endl;
763         }
764 
765         out << indent() << "public struct Isset" << endl
766             << indent() << "{" << endl;
767         indent_up();
768 
769         for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
770         {
771             bool is_required = field_is_required((*m_iter));
772             // if it is required, don't need Isset for that variable
773             // if it is not required, if it has a default value, we need to generate Isset
774             if (!is_required)
775             {
776                 if (is_serialize_enabled() || is_wcf_enabled())
777                 {
778                     out << indent() << "[DataMember]" << endl;
779                 }
780                 out << indent() << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl;
781             }
782         }
783 
784         indent_down();
785         out << indent() << "}" << endl << endl;
786 
787         if (generate_isset && (is_serialize_enabled() || is_wcf_enabled()))
788         {
789             out << indent() << "#region XmlSerializer support" << endl << endl;
790 
791             for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
792             {
793                 bool is_required = field_is_required(*m_iter);
794                 // if it is required, don't need Isset for that variable
795                 // if it is not required, if it has a default value, we need to generate Isset
796                 if (!is_required)
797                 {
798                     out << indent() << "public bool ShouldSerialize" << prop_name(*m_iter) << "()" << endl
799                         << indent() << "{" << endl;
800                     indent_up();
801                     out << indent() << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl;
802                     indent_down();
803                     out << indent() << "}" << endl << endl;
804                 }
805             }
806 
807             out << indent() << "#endregion XmlSerializer support" << endl << endl;
808         }
809     }
810 
811     // We always want a default, no argument constructor for Reading
812     out << indent() << "public " << sharp_struct_name << "()" << endl
813         << indent() << "{" << endl;
814     indent_up();
815 
816     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
817     {
818         t_type* t = (*m_iter)->get_type();
819         while (t->is_typedef())
820         {
821             t = static_cast<t_typedef*>(t)->get_type();
822         }
823         if ((*m_iter)->get_value() != NULL)
824         {
825             if (field_is_required((*m_iter)))
826             {
827                 print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true);
828             }
829             else
830             {
831                 print_const_value(out, "this._" + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true);
832                 // Optionals with defaults are marked set
833                 out << indent() << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" << endl;
834             }
835         }
836     }
837     indent_down();
838     out << indent() << "}" << endl << endl;
839 
840     if (has_required_fields)
841     {
842         out << indent() << "public " << sharp_struct_name << "(";
843         bool first = true;
844         for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
845         {
846             if (field_is_required(*m_iter))
847             {
848                 if (first)
849                 {
850                     first = false;
851                 }
852                 else
853                 {
854                     out << ", ";
855                 }
856                 out << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name();
857             }
858         }
859         out << ") : this()" << endl
860             << indent() << "{" << endl;
861         indent_up();
862 
863         for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
864         {
865             if (field_is_required(*m_iter))
866             {
867                 out << indent() << "this." << prop_name(*m_iter) << " = " << (*m_iter)->get_name() << ";" << endl;
868             }
869         }
870 
871         indent_down();
872         out << indent() << "}" << endl << endl;
873     }
874 
875     generate_netstd_struct_reader(out, tstruct);
876     if (is_result)
877     {
878         generate_netstd_struct_result_writer(out, tstruct);
879     }
880     else
881     {
882         generate_netstd_struct_writer(out, tstruct);
883     }
884     generate_netstd_struct_equals(out, tstruct);
885     generate_netstd_struct_hashcode(out, tstruct);
886     generate_netstd_struct_tostring(out, tstruct);
887 
888     indent_down();
889     out << indent() << "}" << endl << endl;
890 
891     // generate a corresponding WCF fault to wrap the exception
892     if ((is_serialize_enabled() || is_wcf_enabled()) && is_exception)
893     {
894         generate_netstd_wcffault(out, tstruct);
895     }
896 
897     cleanup_member_name_mapping(tstruct);
898     if (!in_class)
899     {
900         end_netstd_namespace(out);
901     }
902 }
903 
generate_netstd_wcffault(ostream & out,t_struct * tstruct)904 void t_netstd_generator::generate_netstd_wcffault(ostream& out, t_struct* tstruct)
905 {
906     out << endl;
907     out << indent() << "[DataContract]" << endl;
908 
909     bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
910 
911     out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << "Fault" << endl
912         << indent() << "{" << endl;
913     indent_up();
914 
915     const vector<t_field*>& members = tstruct->get_members();
916     vector<t_field*>::const_iterator m_iter;
917 
918     // make private members with public Properties
919     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
920     {
921         // if the field is required, then we use auto-properties
922         if (!field_is_required((*m_iter)))
923         {
924             out << indent() << "private " << declare_field(*m_iter, false, "_") << endl;
925         }
926     }
927     out << endl;
928 
929     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
930     {
931         generate_property(out, *m_iter, true, false);
932     }
933 
934     indent_down();
935     out << indent() << "}" << endl << endl;
936 }
937 
generate_netstd_struct_reader(ostream & out,t_struct * tstruct)938 void t_netstd_generator::generate_netstd_struct_reader(ostream& out, t_struct* tstruct)
939 {
940     out << indent() << "public async Task ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl
941         << indent() << "{" << endl;
942     indent_up();
943     out << indent() << "iprot.IncrementRecursionDepth();" << endl
944         << indent() << "try" << endl
945         << indent() << "{" << endl;
946     indent_up();
947 
948     const vector<t_field*>& fields = tstruct->get_members();
949     vector<t_field*>::const_iterator f_iter;
950 
951     // Required variables aren't in __isset, so we need tmp vars to check them
952     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
953     {
954         if (field_is_required(*f_iter))
955         {
956             out << indent() << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
957         }
958     }
959 
960     out << indent() << "TField field;" << endl
961         << indent() << "await iprot.ReadStructBeginAsync(cancellationToken);" << endl
962         << indent() << "while (true)" << endl
963         << indent() << "{" << endl;
964     indent_up();
965     out << indent() << "field = await iprot.ReadFieldBeginAsync(cancellationToken);" << endl
966         << indent() << "if (field.Type == TType.Stop)" << endl
967         << indent() << "{" << endl;
968     indent_up();
969     out << indent() << "break;" << endl;
970     indent_down();
971     out << indent() << "}" << endl << endl
972         << indent() << "switch (field.ID)" << endl
973         << indent() << "{" << endl;
974     indent_up();
975 
976     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
977     {
978         bool is_required = field_is_required(*f_iter);
979         out << indent() << "case " << (*f_iter)->get_key() << ":" << endl;
980         indent_up();
981         out << indent() << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ")" << endl
982             << indent() << "{" << endl;
983         indent_up();
984 
985         generate_deserialize_field(out, *f_iter);
986         if (is_required)
987         {
988             out << indent() << "isset_" << (*f_iter)->get_name() << " = true;" << endl;
989         }
990 
991         indent_down();
992         out << indent() << "}" << endl
993             << indent() << "else" << endl
994             << indent() << "{" << endl;
995         indent_up();
996         out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl;
997         indent_down();
998         out << indent() << "}" << endl
999             << indent() << "break;" << endl;
1000         indent_down();
1001     }
1002 
1003     out << indent() << "default: " << endl;
1004     indent_up();
1005     out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl
1006         << indent() << "break;" << endl;
1007     indent_down();
1008     indent_down();
1009     out << indent() << "}" << endl
1010         << endl
1011         << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
1012     indent_down();
1013     out << indent() << "}" << endl
1014         << endl
1015         << indent() << "await iprot.ReadStructEndAsync(cancellationToken);" << endl;
1016 
1017     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1018     {
1019         if (field_is_required((*f_iter)))
1020         {
1021             out << indent() << "if (!isset_" << (*f_iter)->get_name() << ")" << endl
1022                 << indent() << "{" << endl;
1023             indent_up();
1024             out << indent() << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
1025             indent_down();
1026             out << indent() << "}" << endl;
1027         }
1028     }
1029 
1030     indent_down();
1031     out << indent() << "}" << endl;
1032     out << indent() << "finally" << endl
1033         << indent() << "{" << endl;
1034     indent_up();
1035     out << indent() << "iprot.DecrementRecursionDepth();" << endl;
1036     indent_down();
1037     out << indent() << "}" << endl;
1038     indent_down();
1039     out << indent() << "}" << endl << endl;
1040 }
1041 
generate_netstd_struct_writer(ostream & out,t_struct * tstruct)1042 void t_netstd_generator::generate_netstd_struct_writer(ostream& out, t_struct* tstruct)
1043 {
1044     out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
1045         << indent() << "{" << endl;
1046     indent_up();
1047 
1048     out << indent() << "oprot.IncrementRecursionDepth();" << endl
1049         << indent() << "try" << endl
1050         << indent() << "{" << endl;
1051     indent_up();
1052 
1053     string name = tstruct->get_name();
1054     const vector<t_field*>& fields = tstruct->get_sorted_members();
1055     vector<t_field*>::const_iterator f_iter;
1056 
1057     out << indent() << "var struc = new TStruct(\"" << name << "\");" << endl
1058         << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
1059 
1060     if (fields.size() > 0)
1061     {
1062         out << indent() << "var field = new TField();" << endl;
1063         for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1064         {
1065             bool is_required = field_is_required(*f_iter);
1066             if (!is_required)
1067             {
1068                 bool null_allowed = type_can_be_null((*f_iter)->get_type());
1069                 if (null_allowed)
1070                 {
1071                     out << indent() << "if (" << prop_name(*f_iter) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
1072                         << indent() << "{" << endl;
1073                     indent_up();
1074                 }
1075                 else
1076                 {
1077                     out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
1078                         << indent() << "{" << endl;
1079                     indent_up();
1080                 }
1081             }
1082             out << indent() << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl
1083                 << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl
1084                 << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl
1085                 << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
1086 
1087             generate_serialize_field(out, *f_iter);
1088 
1089             out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl;
1090             if (!is_required)
1091             {
1092                 indent_down();
1093                 out << indent() << "}" << endl;
1094             }
1095         }
1096     }
1097 
1098     out << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
1099         << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
1100     indent_down();
1101     out << indent() << "}" << endl
1102         << indent() << "finally" << endl
1103         << indent() << "{" << endl;
1104     indent_up();
1105     out << indent() << "oprot.DecrementRecursionDepth();" << endl;
1106     indent_down();
1107     out << indent() << "}" << endl;
1108     indent_down();
1109     out << indent() << "}" << endl << endl;
1110 }
1111 
generate_netstd_struct_result_writer(ostream & out,t_struct * tstruct)1112 void t_netstd_generator::generate_netstd_struct_result_writer(ostream& out, t_struct* tstruct)
1113 {
1114     out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
1115         << indent() << "{" << endl;
1116     indent_up();
1117 
1118     out << indent() << "oprot.IncrementRecursionDepth();" << endl
1119         << indent() << "try" << endl
1120         << indent() << "{" << endl;
1121     indent_up();
1122 
1123     string name = tstruct->get_name();
1124     const vector<t_field*>& fields = tstruct->get_sorted_members();
1125     vector<t_field*>::const_iterator f_iter;
1126 
1127     out << indent() << "var struc = new TStruct(\"" << name << "\");" << endl
1128         << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
1129 
1130     if (fields.size() > 0)
1131     {
1132         out << indent() << "var field = new TField();" << endl;
1133         bool first = true;
1134         for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1135         {
1136             if (first)
1137             {
1138                 first = false;
1139                 out << endl << indent() << "if";
1140             }
1141             else
1142             {
1143                 out << indent() << "else if";
1144             }
1145 
1146             out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
1147                 << indent() << "{" << endl;
1148             indent_up();
1149 
1150             bool null_allowed = type_can_be_null((*f_iter)->get_type());
1151             if (null_allowed)
1152             {
1153                 out << indent() << "if (" << prop_name(*f_iter) << " != null)" << endl
1154                     << indent() << "{" << endl;
1155                 indent_up();
1156             }
1157 
1158             out << indent() << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl
1159                 << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl
1160                 << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl
1161                 << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
1162 
1163             generate_serialize_field(out, *f_iter);
1164 
1165             out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl;
1166 
1167             if (null_allowed)
1168             {
1169                 indent_down();
1170                 out << indent() << "}" << endl;
1171             }
1172 
1173             indent_down();
1174             out << indent() << "}" << endl;
1175         }
1176     }
1177 
1178     out << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
1179         << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
1180     indent_down();
1181     out << indent() << "}" << endl
1182         << indent() << "finally" << endl
1183         << indent() << "{" << endl;
1184     indent_up();
1185     out << indent() << "oprot.DecrementRecursionDepth();" << endl;
1186     indent_down();
1187     out << indent() << "}" << endl;
1188     indent_down();
1189     out << indent() << "}" << endl << endl;
1190 }
1191 
generate_netstd_struct_tostring(ostream & out,t_struct * tstruct)1192 void t_netstd_generator::generate_netstd_struct_tostring(ostream& out, t_struct* tstruct)
1193 {
1194     out << indent() << "public override string ToString()" << endl
1195         << indent() << "{" << endl;
1196     indent_up();
1197     out << indent() << "var sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl;
1198 
1199     const vector<t_field*>& fields = tstruct->get_members();
1200     vector<t_field*>::const_iterator f_iter;
1201 
1202     bool useFirstFlag = false;
1203     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1204     {
1205         if (!field_is_required((*f_iter)))
1206         {
1207             out << indent() << "bool __first = true;" << endl;
1208             useFirstFlag = true;
1209         }
1210         break;
1211     }
1212 
1213     bool had_required = false; // set to true after first required field has been processed
1214 
1215     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1216     {
1217         bool is_required = field_is_required((*f_iter));
1218         if (!is_required)
1219         {
1220             bool null_allowed = type_can_be_null((*f_iter)->get_type());
1221             if (null_allowed)
1222             {
1223                 out << indent() << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
1224                     << indent() << "{" << endl;
1225                 indent_up();
1226             }
1227             else
1228             {
1229                 out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
1230                     << indent() << "{" << endl;
1231                 indent_up();
1232             }
1233         }
1234 
1235         if (useFirstFlag && (!had_required))
1236         {
1237             out << indent() << "if(!__first) { sb.Append(\", \"); }" << endl;
1238             if (!is_required)
1239             {
1240                 out << indent() << "__first = false;" << endl;
1241             }
1242             out << indent() << "sb.Append(\"" << prop_name(*f_iter) << ": \");" << endl;
1243         }
1244         else
1245         {
1246             out << indent() << "sb.Append(\", " << prop_name(*f_iter) << ": \");" << endl;
1247         }
1248 
1249         t_type* ttype = (*f_iter)->get_type();
1250         if (ttype->is_xception() || ttype->is_struct())
1251         {
1252             out << indent() << "sb.Append(" << prop_name(*f_iter) << "== null ? \"<null>\" : " << prop_name(*f_iter) << ".ToString());" << endl;
1253         }
1254         else
1255         {
1256             out << indent() << "sb.Append(" << prop_name(*f_iter) << ");" << endl;
1257         }
1258 
1259         if (!is_required)
1260         {
1261             indent_down();
1262             out << indent() << "}" << endl;
1263         }
1264         else
1265         {
1266             had_required = true; // now __first must be false, so we don't need to check it anymore
1267         }
1268     }
1269 
1270     out << indent() << "sb.Append(\")\");" << endl
1271         << indent() << "return sb.ToString();" << endl;
1272     indent_down();
1273     out << indent() << "}" << endl;
1274 }
1275 
generate_netstd_union(t_struct * tunion)1276 void t_netstd_generator::generate_netstd_union(t_struct* tunion)
1277 {
1278     int ic = indent_count();
1279 
1280     string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs";
1281     ofstream_with_content_based_conditional_update f_union;
1282 
1283     f_union.open(f_union_name.c_str());
1284 
1285     f_union << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl;
1286 
1287     generate_netstd_union_definition(f_union, tunion);
1288 
1289     f_union.close();
1290 
1291     indent_validate(ic, "generate_netstd_union.");
1292 }
1293 
generate_netstd_union_definition(ostream & out,t_struct * tunion)1294 void t_netstd_generator::generate_netstd_union_definition(ostream& out, t_struct* tunion)
1295 {
1296     // Let's define the class first
1297     start_netstd_namespace(out);
1298 
1299     out << indent() << "public abstract partial class " << tunion->get_name() << " : TUnionBase" << endl
1300         << indent() << "{" << endl;
1301     indent_up();
1302 
1303     out << indent() << "public abstract Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken);" << endl
1304         << indent() << "public readonly int Isset;" << endl
1305         << indent() << "public abstract object Data { get; }" << endl
1306         << indent() << "protected " << tunion->get_name() << "(int isset)" << endl
1307         << indent() << "{" << endl;
1308     indent_up();
1309     out << indent() << "Isset = isset;" << endl;
1310     indent_down();
1311     out << indent() << "}" << endl << endl;
1312 
1313     out << indent() << "public class ___undefined : " << tunion->get_name() << endl
1314         << indent() << "{" << endl;
1315     indent_up();
1316 
1317     out << indent() << "public override object Data { get { return null; } }" << endl
1318         << indent() << "public ___undefined() : base(0) {}" << endl << endl;
1319 
1320     out << indent() << "public override Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
1321         << indent() << "{" << endl;
1322     indent_up();
1323     out << indent() << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist an union type which is not set.\");" << endl;
1324     indent_down();
1325     out << indent() << "}" << endl << endl;
1326     indent_down();
1327     out << indent() << "}" << endl << endl;
1328 
1329     const vector<t_field*>& fields = tunion->get_members();
1330     vector<t_field*>::const_iterator f_iter;
1331 
1332     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1333     {
1334         generate_netstd_union_class(out, tunion, (*f_iter));
1335     }
1336 
1337     generate_netstd_union_reader(out, tunion);
1338 
1339     indent_down();
1340     out << indent() << "}" << endl << endl;
1341 
1342     end_netstd_namespace(out);
1343 }
1344 
generate_netstd_union_class(ostream & out,t_struct * tunion,t_field * tfield)1345 void t_netstd_generator::generate_netstd_union_class(ostream& out, t_struct* tunion, t_field* tfield)
1346 {
1347     out << indent() << "public " << type_name(tfield->get_type()) << " As_" << tfield->get_name() << endl;
1348     out << indent() << "{" << endl;
1349     indent_up();
1350     out << indent() << "get" << endl;
1351     out << indent() << "{" << endl;
1352     indent_up();
1353     out << indent() << "return (" << tfield->get_key() << " == Isset) ? (" << type_name(tfield->get_type()) << ")Data : default(" << type_name(tfield->get_type()) << ");" << endl;
1354     indent_down();
1355     out << indent() << "}" << endl;
1356     indent_down();
1357     out << indent() << "}" << endl
1358         << endl;
1359 
1360 
1361     out << indent() << "public class " << tfield->get_name() << " : " << tunion->get_name() << endl
1362         << indent() << "{" << endl;
1363     indent_up();
1364 
1365     out << indent() << "private " << type_name(tfield->get_type()) << " _data;" << endl
1366         << indent() << "public override object Data { get { return _data; } }" << endl
1367         << indent() << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base("<< tfield->get_key() <<")" << endl
1368         << indent() << "{" << endl;
1369     indent_up();
1370     out << indent() << "this._data = data;" << endl;
1371     indent_down();
1372     out << indent() << "}" << endl;
1373 
1374     out << indent() << "public override async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken) {" << endl;
1375     indent_up();
1376 
1377     out << indent() << "oprot.IncrementRecursionDepth();" << endl
1378         << indent() << "try" << endl
1379         << indent() << "{" << endl;
1380     indent_up();
1381 
1382     out << indent() << "var struc = new TStruct(\"" << tunion->get_name() << "\");" << endl
1383         << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
1384 
1385     out << indent() << "var field = new TField();" << endl
1386         << indent() << "field.Name = \"" << tfield->get_name() << "\";" << endl
1387         << indent() << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl
1388         << indent() << "field.ID = " << tfield->get_key() << ";" << endl
1389         << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
1390 
1391     generate_serialize_field(out, tfield, "_data", true);
1392 
1393     out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl
1394         << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
1395         << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
1396     indent_down();
1397     out << indent() << "}" << endl
1398         << indent() << "finally" << endl
1399         << indent() << "{" << endl;
1400     indent_up();
1401     out << indent() << "oprot.DecrementRecursionDepth();" << endl;
1402     indent_down();
1403     out << indent() << "}" << endl;
1404     out << indent() << "}" << endl;
1405     indent_down();
1406     out << indent() << "}" << endl << endl;
1407 }
1408 
generate_netstd_struct_equals(ostream & out,t_struct * tstruct)1409 void t_netstd_generator::generate_netstd_struct_equals(ostream& out, t_struct* tstruct)
1410 {
1411     out << indent() << "public override bool Equals(object that)" << endl
1412         << indent() << "{" << endl;
1413     indent_up();
1414     out << indent() << "var other = that as " << check_and_correct_struct_name(normalize_name(tstruct->get_name())) << ";" << endl
1415         << indent() << "if (other == null) return false;" << endl
1416         << indent() << "if (ReferenceEquals(this, other)) return true;" << endl;
1417 
1418     const vector<t_field*>& fields = tstruct->get_members();
1419     vector<t_field*>::const_iterator f_iter;
1420 
1421     bool first = true;
1422 
1423     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1424     {
1425         if (first)
1426         {
1427             first = false;
1428             out << indent() << "return ";
1429             indent_up();
1430         }
1431         else
1432         {
1433             out << endl;
1434             out << indent() << "&& ";
1435         }
1436         if (!field_is_required((*f_iter)))
1437         {
1438             out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset."
1439                 << normalize_name((*f_iter)->get_name()) << ") && ((!__isset."
1440                 << normalize_name((*f_iter)->get_name()) << ") || (";
1441         }
1442         t_type* ttype = (*f_iter)->get_type();
1443         if (ttype->is_container() || ttype->is_binary())
1444         {
1445             out << "TCollections.Equals(";
1446         }
1447         else
1448         {
1449             out << "System.Object.Equals(";
1450         }
1451         out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")";
1452         if (!field_is_required((*f_iter)))
1453         {
1454             out << ")))";
1455         }
1456     }
1457     if (first)
1458     {
1459         out << indent() << "return true;" << endl;
1460     }
1461     else
1462     {
1463         out << ";" << endl;
1464         indent_down();
1465     }
1466 
1467     indent_down();
1468     out << indent() << "}" << endl << endl;
1469 }
1470 
generate_netstd_struct_hashcode(ostream & out,t_struct * tstruct)1471 void t_netstd_generator::generate_netstd_struct_hashcode(ostream& out, t_struct* tstruct)
1472 {
1473     out << indent() << "public override int GetHashCode() {" << endl;
1474     indent_up();
1475 
1476     out << indent() << "int hashcode = 157;" << endl;
1477     out << indent() << "unchecked {" << endl;
1478     indent_up();
1479 
1480     const vector<t_field*>& fields = tstruct->get_members();
1481     vector<t_field*>::const_iterator f_iter;
1482 
1483     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1484     {
1485         t_type* ttype = (*f_iter)->get_type();
1486         if (!field_is_required((*f_iter)))
1487         {
1488             out << indent() << "if(__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl;
1489             indent_up();
1490         }
1491         out << indent() << "hashcode = (hashcode * 397) + ";
1492         if (ttype->is_container())
1493         {
1494             out << "TCollections.GetHashCode(" << prop_name((*f_iter)) << ")";
1495         }
1496         else
1497         {
1498             out << prop_name((*f_iter)) << ".GetHashCode()";
1499         }
1500         out << ";" << endl;
1501 
1502         if (!field_is_required((*f_iter)))
1503         {
1504             indent_down();
1505         }
1506     }
1507 
1508     indent_down();
1509     out << indent() << "}" << endl;
1510     out << indent() << "return hashcode;" << endl;
1511 
1512     indent_down();
1513     out << indent() << "}" << endl << endl;
1514 }
1515 
generate_service(t_service * tservice)1516 void t_netstd_generator::generate_service(t_service* tservice)
1517 {
1518     int ic = indent_count();
1519 
1520     string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs";
1521     ofstream_with_content_based_conditional_update f_service;
1522     f_service.open(f_service_name.c_str());
1523 
1524     f_service << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl;
1525 
1526     start_netstd_namespace(f_service);
1527 
1528     f_service << indent() << "public partial class " << normalize_name(service_name_) << endl
1529               << indent() << "{" << endl;
1530     indent_up();
1531 
1532     generate_service_interface(f_service, tservice);
1533     generate_service_client(f_service, tservice);
1534     generate_service_server(f_service, tservice);
1535     generate_service_helpers(f_service, tservice);
1536 
1537     indent_down();
1538     f_service << indent() << "}" << endl;
1539 
1540     end_netstd_namespace(f_service);
1541     f_service.close();
1542 
1543     indent_validate(ic, "generate_service.");
1544 }
1545 
generate_service_interface(ostream & out,t_service * tservice)1546 void t_netstd_generator::generate_service_interface(ostream& out, t_service* tservice)
1547 {
1548     string extends = "";
1549     string extends_iface = "";
1550     if (tservice->get_extends() != NULL)
1551     {
1552         extends = type_name(tservice->get_extends());
1553         extends_iface = " : " + extends + ".IAsync";
1554     }
1555 
1556     //out << endl << endl;
1557 
1558     generate_netstd_doc(out, tservice);
1559 
1560     if (is_wcf_enabled())
1561     {
1562         out << indent() << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
1563     }
1564 
1565     out << indent() << "public interface IAsync" << extends_iface << endl
1566         << indent() << "{" << endl;
1567 
1568     indent_up();
1569     vector<t_function*> functions = tservice->get_functions();
1570     vector<t_function*>::iterator f_iter;
1571     for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
1572     {
1573         generate_netstd_doc(out, *f_iter);
1574 
1575         // if we're using WCF, add the corresponding attributes
1576         if (is_wcf_enabled())
1577         {
1578             out << indent() << "[OperationContract]" << endl;
1579 
1580             const vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
1581             vector<t_field*>::const_iterator x_iter;
1582             for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
1583             {
1584                 out << indent() << "[FaultContract(typeof(" + type_name((*x_iter)->get_type()) + "Fault))]" << endl;
1585             }
1586         }
1587 
1588         out << indent() << function_signature_async(*f_iter) << ";" << endl << endl;
1589     }
1590     indent_down();
1591     out << indent() << "}" << endl << endl;
1592 }
1593 
generate_service_helpers(ostream & out,t_service * tservice)1594 void t_netstd_generator::generate_service_helpers(ostream& out, t_service* tservice)
1595 {
1596     vector<t_function*> functions = tservice->get_functions();
1597     vector<t_function*>::iterator f_iter;
1598 
1599     for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
1600     {
1601         t_struct* ts = (*f_iter)->get_arglist();
1602         generate_netstd_struct_definition(out, ts, false, true);
1603         generate_function_helpers(out, *f_iter);
1604     }
1605 }
1606 
generate_service_client(ostream & out,t_service * tservice)1607 void t_netstd_generator::generate_service_client(ostream& out, t_service* tservice)
1608 {
1609     string extends = "";
1610     string extends_client = "";
1611     if (tservice->get_extends() != NULL)
1612     {
1613         extends = type_name(tservice->get_extends());
1614         extends_client = extends + ".Client, ";
1615     }
1616     else
1617     {
1618         extends_client = "TBaseClient, IDisposable, ";
1619     }
1620 
1621     out << endl;
1622 
1623     generate_netstd_doc(out, tservice);
1624 
1625     out << indent() << "public class Client : " << extends_client << "IAsync" << endl
1626         << indent() << "{" << endl;
1627     indent_up();
1628 
1629     out << indent() << "public Client(TProtocol protocol) : this(protocol, protocol)" << endl
1630         << indent() << "{" << endl
1631         << indent() << "}" << endl
1632         << endl
1633         << indent() << "public Client(TProtocol inputProtocol, TProtocol outputProtocol) : base(inputProtocol, outputProtocol)"
1634         << indent() << "{" << endl
1635         << indent() << "}" << endl;
1636 
1637     vector<t_function*> functions = tservice->get_functions();
1638     vector<t_function*>::const_iterator functions_iterator;
1639 
1640     for (functions_iterator = functions.begin(); functions_iterator != functions.end(); ++functions_iterator)
1641     {
1642         string function_name = correct_function_name_for_async((*functions_iterator)->get_name());
1643 
1644         // async
1645         out << indent() << "public async " << function_signature_async(*functions_iterator, "") << endl
1646             << indent() << "{" << endl;
1647         indent_up();
1648 
1649         string argsname = (*functions_iterator)->get_name() + "Args";
1650 
1651         out << indent() << "await OutputProtocol.WriteMessageBeginAsync(new TMessage(\"" << function_name
1652             << "\", " << ((*functions_iterator)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") << ", SeqId), cancellationToken);" << endl
1653             << indent() << endl
1654             << indent() << "var args = new " << argsname << "();" << endl;
1655 
1656         t_struct* arg_struct = (*functions_iterator)->get_arglist();
1657         prepare_member_name_mapping(arg_struct);
1658         const vector<t_field*>& fields = arg_struct->get_members();
1659         vector<t_field*>::const_iterator fld_iter;
1660 
1661         for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter)
1662         {
1663             out << indent() << "args." << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << ";" << endl;
1664         }
1665 
1666         out << indent() << endl
1667             << indent() << "await args.WriteAsync(OutputProtocol, cancellationToken);" << endl
1668             << indent() << "await OutputProtocol.WriteMessageEndAsync(cancellationToken);" << endl
1669             << indent() << "await OutputProtocol.Transport.FlushAsync(cancellationToken);" << endl;
1670 
1671         if (!(*functions_iterator)->is_oneway())
1672         {
1673             string resultname = (*functions_iterator)->get_name() + "Result";
1674             t_struct noargs(program_);
1675             t_struct* xs = (*functions_iterator)->get_xceptions();
1676             prepare_member_name_mapping(xs, xs->get_members(), resultname);
1677 
1678             out << indent() << endl
1679                 << indent() << "var msg = await InputProtocol.ReadMessageBeginAsync(cancellationToken);" << endl
1680                 << indent() << "if (msg.Type == TMessageType.Exception)" << endl
1681                 << indent() << "{" << endl;
1682             indent_up();
1683 
1684             out << indent() << "var x = await TApplicationException.ReadAsync(InputProtocol, cancellationToken);" << endl
1685                 << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl
1686                 << indent() << "throw x;" << endl;
1687             indent_down();
1688 
1689             out << indent() << "}" << endl
1690                 << endl
1691                 << indent() << "var result = new " << resultname << "();" << endl
1692                 << indent() << "await result.ReadAsync(InputProtocol, cancellationToken);" << endl
1693                 << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl;
1694 
1695             if (!(*functions_iterator)->get_returntype()->is_void())
1696             {
1697                 out << indent() << "if (result.__isset.success)" << endl
1698                     << indent() << "{" << endl;
1699                 indent_up();
1700                 out << indent() << "return result.Success;" << endl;
1701                 indent_down();
1702                 out << indent() << "}" << endl;
1703             }
1704 
1705             const vector<t_field*>& xceptions = xs->get_members();
1706             vector<t_field*>::const_iterator x_iter;
1707             for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
1708             {
1709                 out << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) << ")" << endl
1710                     << indent() << "{" << endl;
1711                 indent_up();
1712                 out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl;
1713                 indent_down();
1714                 out << indent() << "}" << endl;
1715             }
1716 
1717             if ((*functions_iterator)->get_returntype()->is_void())
1718             {
1719                 out << indent() << "return;" << endl;
1720             }
1721             else
1722             {
1723                 out << indent() << "throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, \""
1724                     << function_name << " failed: unknown result\");" << endl;
1725             }
1726 
1727             cleanup_member_name_mapping((*functions_iterator)->get_xceptions());
1728             indent_down();
1729             out << indent() << "}" << endl << endl;
1730         }
1731         else
1732         {
1733             indent_down();
1734             out << indent() << "}" << endl;
1735         }
1736     }
1737 
1738     indent_down();
1739     out << indent() << "}" << endl << endl;
1740 }
1741 
generate_service_server(ostream & out,t_service * tservice)1742 void t_netstd_generator::generate_service_server(ostream& out, t_service* tservice)
1743 {
1744     vector<t_function*> functions = tservice->get_functions();
1745     vector<t_function*>::iterator f_iter;
1746 
1747     string extends = "";
1748     string extends_processor = "";
1749     if (tservice->get_extends() != NULL)
1750     {
1751         extends = type_name(tservice->get_extends());
1752         extends_processor = extends + ".AsyncProcessor, ";
1753     }
1754 
1755     out << indent() << "public class AsyncProcessor : " << extends_processor << "ITAsyncProcessor" << endl
1756         << indent() << "{" << endl;
1757 
1758     indent_up();
1759 
1760     out << indent() << "private IAsync _iAsync;" << endl
1761         << endl
1762         << indent() << "public AsyncProcessor(IAsync iAsync)";
1763 
1764     if (!extends.empty())
1765     {
1766         out << " : base(iAsync)";
1767     }
1768 
1769     out << endl
1770         << indent() << "{" << endl;
1771     indent_up();
1772 
1773     out << indent() << "if (iAsync == null) throw new ArgumentNullException(nameof(iAsync));" << endl
1774         << endl
1775         << indent() << "_iAsync = iAsync;" << endl;
1776 
1777     for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
1778     {
1779         string function_name = (*f_iter)->get_name();
1780         out << indent() << "processMap_[\"" << correct_function_name_for_async(function_name) << "\"] = " << function_name << "_ProcessAsync;" << endl;
1781     }
1782 
1783     indent_down();
1784     out << indent() << "}" << endl
1785         << endl;
1786 
1787     if (extends.empty())
1788     {
1789         out << indent() << "protected delegate Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken);" << endl;
1790     }
1791 
1792     if (extends.empty())
1793     {
1794         out << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new Dictionary<string, ProcessFunction>();" << endl;
1795     }
1796 
1797     out << endl;
1798 
1799     if (extends.empty())
1800     {
1801         out << indent() << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl
1802             << indent() << "{" << endl;
1803         indent_up();
1804         out << indent() << "return await ProcessAsync(iprot, oprot, CancellationToken.None);" << endl;
1805         indent_down();
1806         out << indent() << "}" << endl << endl;
1807 
1808         out << indent() << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl;
1809     }
1810     else
1811     {
1812         out << indent() << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl
1813             << indent() << "{" << endl;
1814         indent_up();
1815         out << indent() << "return await ProcessAsync(iprot, oprot, CancellationToken.None);" << endl;
1816         indent_down();
1817         out << indent() << "}" << endl << endl;
1818 
1819         out << indent() << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl;
1820     }
1821 
1822     out << indent() << "{" << endl;
1823     indent_up();
1824     out << indent() << "try" << endl
1825         << indent() << "{" << endl;
1826     indent_up();
1827     out << indent() << "var msg = await iprot.ReadMessageBeginAsync(cancellationToken);" << endl
1828         << endl
1829         << indent() << "ProcessFunction fn;" << endl
1830         << indent() << "processMap_.TryGetValue(msg.Name, out fn);" << endl
1831         << endl
1832         << indent() << "if (fn == null)" << endl
1833         << indent() << "{" << endl;
1834     indent_up();
1835     out << indent() << "await TProtocolUtil.SkipAsync(iprot, TType.Struct, cancellationToken);" << endl
1836         << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl
1837         << indent() << "var x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + msg.Name + \"'\");" << endl
1838         << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID), cancellationToken);" << endl
1839         << indent() << "await x.WriteAsync(oprot, cancellationToken);" << endl
1840         << indent() << "await oprot.WriteMessageEndAsync(cancellationToken);" << endl
1841         << indent() << "await oprot.Transport.FlushAsync(cancellationToken);" << endl
1842         << indent() << "return true;" << endl;
1843     indent_down();
1844     out << indent() << "}" << endl
1845         << endl
1846         << indent() << "await fn(msg.SeqID, iprot, oprot, cancellationToken);" << endl
1847         << endl;
1848     indent_down();
1849     out << indent() << "}" << endl;
1850     out << indent() << "catch (IOException)" << endl
1851         << indent() << "{" << endl;
1852     indent_up();
1853     out << indent() << "return false;" << endl;
1854     indent_down();
1855     out << indent() << "}" << endl
1856         << endl
1857         << indent() << "return true;" << endl;
1858     indent_down();
1859     out << indent() << "}" << endl << endl;
1860 
1861     for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
1862     {
1863         generate_process_function_async(out, tservice, *f_iter);
1864     }
1865 
1866     indent_down();
1867     out << indent() << "}" << endl << endl;
1868 }
1869 
generate_function_helpers(ostream & out,t_function * tfunction)1870 void t_netstd_generator::generate_function_helpers(ostream& out, t_function* tfunction)
1871 {
1872     if (tfunction->is_oneway())
1873     {
1874         return;
1875     }
1876 
1877     t_struct result(program_, tfunction->get_name() + "_result");
1878     t_field success(tfunction->get_returntype(), "success", 0);
1879     if (!tfunction->get_returntype()->is_void())
1880     {
1881         result.append(&success);
1882     }
1883 
1884     t_struct* xs = tfunction->get_xceptions();
1885     const vector<t_field*>& fields = xs->get_members();
1886     vector<t_field*>::const_iterator f_iter;
1887     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1888     {
1889         result.append(*f_iter);
1890     }
1891 
1892     generate_netstd_struct_definition(out, &result, false, true, true);
1893 }
1894 
generate_process_function_async(ostream & out,t_service * tservice,t_function * tfunction)1895 void t_netstd_generator::generate_process_function_async(ostream& out, t_service* tservice, t_function* tfunction)
1896 {
1897     (void)tservice;
1898     out << indent() << "public async Task " << tfunction->get_name()
1899         << "_ProcessAsync(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl
1900         << indent() << "{" << endl;
1901     indent_up();
1902 
1903     string argsname = tfunction->get_name() + "Args";
1904     string resultname = tfunction->get_name() + "Result";
1905 
1906     out << indent() << "var args = new " << argsname << "();" << endl
1907         << indent() << "await args.ReadAsync(iprot, cancellationToken);" << endl
1908         << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl;
1909 
1910     if (!tfunction->is_oneway())
1911     {
1912         out << indent() << "var result = new " << resultname << "();" << endl;
1913     }
1914 
1915     out << indent() << "try" << endl
1916         << indent() << "{" << endl;
1917     indent_up();
1918 
1919     t_struct* xs = tfunction->get_xceptions();
1920     const vector<t_field*>& xceptions = xs->get_members();
1921 
1922     if (xceptions.size() > 0)
1923     {
1924         out << indent() << "try" << endl
1925             << indent() << "{" << endl;
1926         indent_up();
1927     }
1928 
1929     t_struct* arg_struct = tfunction->get_arglist();
1930     const vector<t_field*>& fields = arg_struct->get_members();
1931     vector<t_field*>::const_iterator f_iter;
1932 
1933     out << indent();
1934     if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void())
1935     {
1936         out << "result.Success = ";
1937     }
1938 
1939     out << "await _iAsync." << normalize_name(tfunction->get_name()) << "Async(";
1940 
1941     bool first = true;
1942     prepare_member_name_mapping(arg_struct);
1943     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
1944     {
1945         if (first)
1946         {
1947             first = false;
1948         }
1949         else
1950         {
1951             out << ", ";
1952         }
1953 
1954         out << "args." << prop_name(*f_iter);
1955     }
1956 
1957     cleanup_member_name_mapping(arg_struct);
1958 
1959     if (!first)
1960     {
1961         out << ", ";
1962     }
1963 
1964     out << "cancellationToken);" << endl;
1965 
1966     vector<t_field*>::const_iterator x_iter;
1967 
1968     prepare_member_name_mapping(xs, xs->get_members(), resultname);
1969     if (xceptions.size() > 0)
1970     {
1971         indent_down();
1972         out << indent() << "}" << endl;
1973 
1974         for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
1975         {
1976             out << indent() << "catch (" << type_name((*x_iter)->get_type()) << " " << (*x_iter)->get_name() << ")" << endl
1977                 << indent() << "{" << endl;
1978 
1979             if (!tfunction->is_oneway())
1980             {
1981                 indent_up();
1982                 out << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl;
1983                 indent_down();
1984             }
1985             out << indent() << "}" << endl;
1986         }
1987     }
1988 
1989     if (!tfunction->is_oneway())
1990     {
1991         out << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(\""
1992                 << correct_function_name_for_async(tfunction->get_name()) << "\", TMessageType.Reply, seqid), cancellationToken); " << endl
1993             << indent() << "await result.WriteAsync(oprot, cancellationToken);" << endl;
1994     }
1995     indent_down();
1996 
1997     cleanup_member_name_mapping(xs);
1998 
1999     out << indent() << "}" << endl
2000         << indent() << "catch (TTransportException)" << endl
2001         << indent() << "{" << endl
2002         << indent() << "  throw;" << endl
2003         << indent() << "}" << endl
2004         << indent() << "catch (Exception ex)" << endl
2005         << indent() << "{" << endl;
2006     indent_up();
2007 
2008     out << indent() << "Console.Error.WriteLine(\"Error occurred in processor:\");" << endl
2009         << indent() << "Console.Error.WriteLine(ex.ToString());" << endl;
2010 
2011     if (tfunction->is_oneway())
2012     {
2013         indent_down();
2014         out << indent() << "}" << endl;
2015     }
2016     else
2017     {
2018         out << indent() << "var x = new TApplicationException(TApplicationException.ExceptionType.InternalError,\" Internal error.\");" << endl
2019             << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(\"" << correct_function_name_for_async(tfunction->get_name())
2020             << "\", TMessageType.Exception, seqid), cancellationToken);" << endl
2021             << indent() << "await x.WriteAsync(oprot, cancellationToken);" << endl;
2022         indent_down();
2023 
2024         out << indent() << "}" << endl
2025             << indent() << "await oprot.WriteMessageEndAsync(cancellationToken);" << endl
2026             << indent() << "await oprot.Transport.FlushAsync(cancellationToken);" << endl;
2027     }
2028 
2029     indent_down();
2030     out << indent() << "}" << endl << endl;
2031 }
2032 
generate_netstd_union_reader(ostream & out,t_struct * tunion)2033 void t_netstd_generator::generate_netstd_union_reader(ostream& out, t_struct* tunion)
2034 {
2035     // Thanks to THRIFT-1768, we don't need to check for required fields in the union
2036     const vector<t_field*>& fields = tunion->get_members();
2037     vector<t_field*>::const_iterator f_iter;
2038 
2039     out << indent() << "public static async Task<" << tunion->get_name() << "> ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl;
2040     scope_up(out);
2041 
2042     out << indent() << "iprot.IncrementRecursionDepth();" << endl;
2043     out << indent() << "try" << endl;
2044     scope_up(out);
2045 
2046     out << indent() << tunion->get_name() << " retval;" << endl;
2047     out << indent() << "await iprot.ReadStructBeginAsync(cancellationToken);" << endl;
2048     out << indent() << "TField field = await iprot.ReadFieldBeginAsync(cancellationToken);" << endl;
2049     // we cannot have the first field be a stop -- we must have a single field defined
2050     out << indent() << "if (field.Type == TType.Stop)" << endl;
2051     scope_up(out);
2052     out << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
2053     out << indent() << "retval = new ___undefined();" << endl;
2054     scope_down(out);
2055     out << indent() << "else" << endl;
2056     scope_up(out);
2057     out << indent() << "switch (field.ID)" << endl;
2058     scope_up(out);
2059 
2060     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
2061     {
2062         out << indent() << "case " << (*f_iter)->get_key() << ":" << endl;
2063         indent_up();
2064         out << indent() << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
2065         indent_up();
2066 
2067         out << indent() << type_name((*f_iter)->get_type()) << " temp;" << endl;
2068         generate_deserialize_field(out, (*f_iter), "temp", true);
2069         out << indent() << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl;
2070 
2071         indent_down();
2072         out << indent() << "} else { " << endl << indent() << " await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);"
2073             << endl << indent() << "  retval = new ___undefined();" << endl << indent() << "}" << endl
2074             << indent() << "break;" << endl;
2075         indent_down();
2076     }
2077 
2078     out << indent() << "default: " << endl;
2079     indent_up();
2080     out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl << indent()
2081         << "retval = new ___undefined();" << endl;
2082     out << indent() << "break;" << endl;
2083     indent_down();
2084 
2085     scope_down(out);
2086 
2087     out << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
2088 
2089     out << indent() << "if ((await iprot.ReadFieldBeginAsync(cancellationToken)).Type != TType.Stop)" << endl;
2090     scope_up(out);
2091     out << indent() << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
2092     scope_down(out);
2093 
2094     // end of else for TStop
2095     scope_down(out);
2096     out << indent() << "await iprot.ReadStructEndAsync(cancellationToken);" << endl;
2097     out << indent() << "return retval;" << endl;
2098     indent_down();
2099 
2100     scope_down(out);
2101     out << indent() << "finally" << endl;
2102     scope_up(out);
2103     out << indent() << "iprot.DecrementRecursionDepth();" << endl;
2104     scope_down(out);
2105 
2106     out << indent() << "}" << endl << endl;
2107 }
2108 
generate_deserialize_field(ostream & out,t_field * tfield,string prefix,bool is_propertyless)2109 void t_netstd_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless)
2110 {
2111     t_type* type = tfield->get_type();
2112     while (type->is_typedef())
2113     {
2114         type = static_cast<t_typedef*>(type)->get_type();
2115     }
2116 
2117     if (type->is_void())
2118     {
2119         throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
2120     }
2121 
2122     string name = prefix + (is_propertyless ? "" : prop_name(tfield));
2123 
2124     if (type->is_struct() || type->is_xception())
2125     {
2126         generate_deserialize_struct(out, static_cast<t_struct*>(type), name);
2127     }
2128     else if (type->is_container())
2129     {
2130         generate_deserialize_container(out, type, name);
2131     }
2132     else if (type->is_base_type() || type->is_enum())
2133     {
2134         out << indent() << name << " = ";
2135 
2136         if (type->is_enum())
2137         {
2138             out << "(" << type_name(type) << ")";
2139         }
2140 
2141         out << "await iprot.";
2142 
2143         if (type->is_base_type())
2144         {
2145             t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
2146             switch (tbase)
2147             {
2148             case t_base_type::TYPE_VOID:
2149                 throw "compiler error: cannot serialize void field in a struct: " + name;
2150                 break;
2151             case t_base_type::TYPE_STRING:
2152                 if (type->is_binary())
2153                 {
2154                     out << "ReadBinaryAsync(cancellationToken);";
2155                 }
2156                 else
2157                 {
2158                     out << "ReadStringAsync(cancellationToken);";
2159                 }
2160                 break;
2161             case t_base_type::TYPE_BOOL:
2162                 out << "ReadBoolAsync(cancellationToken);";
2163                 break;
2164             case t_base_type::TYPE_I8:
2165                 out << "ReadByteAsync(cancellationToken);";
2166                 break;
2167             case t_base_type::TYPE_I16:
2168                 out << "ReadI16Async(cancellationToken);";
2169                 break;
2170             case t_base_type::TYPE_I32:
2171                 out << "ReadI32Async(cancellationToken);";
2172                 break;
2173             case t_base_type::TYPE_I64:
2174                 out << "ReadI64Async(cancellationToken);";
2175                 break;
2176             case t_base_type::TYPE_DOUBLE:
2177                 out << "ReadDoubleAsync(cancellationToken);";
2178                 break;
2179             default:
2180                 throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
2181             }
2182         }
2183         else if (type->is_enum())
2184         {
2185             out << "ReadI32Async(cancellationToken);";
2186         }
2187         out << endl;
2188     }
2189     else
2190     {
2191         printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str());
2192     }
2193 }
2194 
generate_deserialize_struct(ostream & out,t_struct * tstruct,string prefix)2195 void t_netstd_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix)
2196 {
2197     if (is_union_enabled() && tstruct->is_union())
2198     {
2199         out << indent() << prefix << " = await " << type_name(tstruct) << ".ReadAsync(iprot, cancellationToken);" << endl;
2200     }
2201     else
2202     {
2203         out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl
2204             << indent() << "await " << prefix << ".ReadAsync(iprot, cancellationToken);" << endl;
2205     }
2206 }
2207 
generate_deserialize_container(ostream & out,t_type * ttype,string prefix)2208 void t_netstd_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix)
2209 {
2210     out << indent() << "{" << endl;
2211     indent_up();
2212 
2213     string obj;
2214 
2215     if (ttype->is_map())
2216     {
2217         obj = tmp("_map");
2218     }
2219     else if (ttype->is_set())
2220     {
2221         obj = tmp("_set");
2222     }
2223     else if (ttype->is_list())
2224     {
2225         obj = tmp("_list");
2226     }
2227 
2228     if (ttype->is_map())
2229     {
2230         out << indent() << "TMap " << obj << " = await iprot.ReadMapBeginAsync(cancellationToken);" << endl;
2231     }
2232     else if (ttype->is_set())
2233     {
2234         out << indent() << "TSet " << obj << " = await iprot.ReadSetBeginAsync(cancellationToken);" << endl;
2235     }
2236     else if (ttype->is_list())
2237     {
2238         out << indent() << "TList " << obj << " = await iprot.ReadListBeginAsync(cancellationToken);" << endl;
2239     }
2240 
2241     out << indent() << prefix << " = new " << type_name(ttype) << "(" << obj << ".Count);" << endl;
2242     string i = tmp("_i");
2243     out << indent() << "for(int " << i << " = 0; " << i << " < " << obj << ".Count; ++" << i << ")" << endl
2244         << indent() << "{" << endl;
2245     indent_up();
2246 
2247     if (ttype->is_map())
2248     {
2249         generate_deserialize_map_element(out, static_cast<t_map*>(ttype), prefix);
2250     }
2251     else if (ttype->is_set())
2252     {
2253         generate_deserialize_set_element(out, static_cast<t_set*>(ttype), prefix);
2254     }
2255     else if (ttype->is_list())
2256     {
2257         generate_deserialize_list_element(out, static_cast<t_list*>(ttype), prefix);
2258     }
2259 
2260     indent_down();
2261     out << indent() << "}" << endl;
2262 
2263     if (ttype->is_map())
2264     {
2265         out << indent() << "await iprot.ReadMapEndAsync(cancellationToken);" << endl;
2266     }
2267     else if (ttype->is_set())
2268     {
2269         out << indent() << "await iprot.ReadSetEndAsync(cancellationToken);" << endl;
2270     }
2271     else if (ttype->is_list())
2272     {
2273         out << indent() << "await iprot.ReadListEndAsync(cancellationToken);" << endl;
2274     }
2275 
2276     indent_down();
2277     out << indent() << "}" << endl;
2278 }
2279 
generate_deserialize_map_element(ostream & out,t_map * tmap,string prefix)2280 void t_netstd_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix)
2281 {
2282     string key = tmp("_key");
2283     string val = tmp("_val");
2284 
2285     t_field fkey(tmap->get_key_type(), key);
2286     t_field fval(tmap->get_val_type(), val);
2287 
2288     out << indent() << declare_field(&fkey) << endl;
2289     out << indent() << declare_field(&fval) << endl;
2290 
2291     generate_deserialize_field(out, &fkey);
2292     generate_deserialize_field(out, &fval);
2293 
2294     out << indent() << prefix << "[" << key << "] = " << val << ";" << endl;
2295 }
2296 
generate_deserialize_set_element(ostream & out,t_set * tset,string prefix)2297 void t_netstd_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix)
2298 {
2299     string elem = tmp("_elem");
2300     t_field felem(tset->get_elem_type(), elem);
2301 
2302     out << indent() << declare_field(&felem) << endl;
2303 
2304     generate_deserialize_field(out, &felem);
2305 
2306     out << indent() << prefix << ".Add(" << elem << ");" << endl;
2307 }
2308 
generate_deserialize_list_element(ostream & out,t_list * tlist,string prefix)2309 void t_netstd_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix)
2310 {
2311     string elem = tmp("_elem");
2312     t_field felem(tlist->get_elem_type(), elem);
2313 
2314     out << indent() << declare_field(&felem) << endl;
2315 
2316     generate_deserialize_field(out, &felem);
2317 
2318     out << indent() << prefix << ".Add(" << elem << ");" << endl;
2319 }
2320 
generate_serialize_field(ostream & out,t_field * tfield,string prefix,bool is_propertyless)2321 void t_netstd_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless)
2322 {
2323     t_type* type = tfield->get_type();
2324     while (type->is_typedef())
2325     {
2326         type = static_cast<t_typedef*>(type)->get_type();
2327     }
2328 
2329     string name = prefix + (is_propertyless ? "" : prop_name(tfield));
2330 
2331     if (type->is_void())
2332     {
2333         throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
2334     }
2335 
2336     if (type->is_struct() || type->is_xception())
2337     {
2338         generate_serialize_struct(out, static_cast<t_struct*>(type), name);
2339     }
2340     else if (type->is_container())
2341     {
2342         generate_serialize_container(out, type, name);
2343     }
2344     else if (type->is_base_type() || type->is_enum())
2345     {
2346         out << indent() << "await oprot.";
2347 
2348         string nullable_name = name;
2349 
2350         if (type->is_base_type())
2351         {
2352             t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
2353             switch (tbase)
2354             {
2355             case t_base_type::TYPE_VOID:
2356                 throw "compiler error: cannot serialize void field in a struct: " + name;
2357             case t_base_type::TYPE_STRING:
2358                 if (type->is_binary())
2359                 {
2360                     out << "WriteBinaryAsync(";
2361                 }
2362                 else
2363                 {
2364                     out << "WriteStringAsync(";
2365                 }
2366                 out << name << ", cancellationToken);";
2367                 break;
2368             case t_base_type::TYPE_BOOL:
2369                 out << "WriteBoolAsync(" << nullable_name << ", cancellationToken);";
2370                 break;
2371             case t_base_type::TYPE_I8:
2372                 out << "WriteByteAsync(" << nullable_name << ", cancellationToken);";
2373                 break;
2374             case t_base_type::TYPE_I16:
2375                 out << "WriteI16Async(" << nullable_name << ", cancellationToken);";
2376                 break;
2377             case t_base_type::TYPE_I32:
2378                 out << "WriteI32Async(" << nullable_name << ", cancellationToken);";
2379                 break;
2380             case t_base_type::TYPE_I64:
2381                 out << "WriteI64Async(" << nullable_name << ", cancellationToken);";
2382                 break;
2383             case t_base_type::TYPE_DOUBLE:
2384                 out << "WriteDoubleAsync(" << nullable_name << ", cancellationToken);";
2385                 break;
2386             default:
2387                 throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
2388             }
2389         }
2390         else if (type->is_enum())
2391         {
2392             out << "WriteI32Async((int)" << nullable_name << ", cancellationToken);";
2393         }
2394         out << endl;
2395     }
2396     else
2397     {
2398         printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", prefix.c_str(), tfield->get_name().c_str(), type_name(type).c_str());
2399     }
2400 }
2401 
generate_serialize_struct(ostream & out,t_struct * tstruct,string prefix)2402 void t_netstd_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix)
2403 {
2404     (void)tstruct;
2405     out << indent() << "await " << prefix << ".WriteAsync(oprot, cancellationToken);" << endl;
2406 }
2407 
generate_serialize_container(ostream & out,t_type * ttype,string prefix)2408 void t_netstd_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix)
2409 {
2410     out << indent() << "{" << endl;
2411     indent_up();
2412 
2413     if (ttype->is_map())
2414     {
2415         out << indent() << "await oprot.WriteMapBeginAsync(new TMap(" << type_to_enum(static_cast<t_map*>(ttype)->get_key_type())
2416             << ", " << type_to_enum(static_cast<t_map*>(ttype)->get_val_type()) << ", " << prefix
2417             << ".Count), cancellationToken);" << endl;
2418     }
2419     else if (ttype->is_set())
2420     {
2421         out << indent() << "await oprot.WriteSetBeginAsync(new TSet(" << type_to_enum(static_cast<t_set*>(ttype)->get_elem_type())
2422             << ", " << prefix << ".Count), cancellationToken);" << endl;
2423     }
2424     else if (ttype->is_list())
2425     {
2426         out << indent() << "await oprot.WriteListBeginAsync(new TList("
2427             << type_to_enum(static_cast<t_list*>(ttype)->get_elem_type()) << ", " << prefix << ".Count), cancellationToken);"
2428             << endl;
2429     }
2430 
2431     string iter = tmp("_iter");
2432     if (ttype->is_map())
2433     {
2434         out << indent() << "foreach (" << type_name(static_cast<t_map*>(ttype)->get_key_type()) << " " << iter
2435             << " in " << prefix << ".Keys)";
2436     }
2437     else if (ttype->is_set())
2438     {
2439         out << indent() << "foreach (" << type_name(static_cast<t_set*>(ttype)->get_elem_type()) << " " << iter
2440             << " in " << prefix << ")";
2441     }
2442     else if (ttype->is_list())
2443     {
2444         out << indent() << "foreach (" << type_name(static_cast<t_list*>(ttype)->get_elem_type()) << " " << iter
2445             << " in " << prefix << ")";
2446     }
2447 
2448     out << endl;
2449     out << indent() << "{" << endl;
2450     indent_up();
2451 
2452     if (ttype->is_map())
2453     {
2454         generate_serialize_map_element(out, static_cast<t_map*>(ttype), iter, prefix);
2455     }
2456     else if (ttype->is_set())
2457     {
2458         generate_serialize_set_element(out, static_cast<t_set*>(ttype), iter);
2459     }
2460     else if (ttype->is_list())
2461     {
2462         generate_serialize_list_element(out, static_cast<t_list*>(ttype), iter);
2463     }
2464 
2465     indent_down();
2466     out << indent() << "}" << endl;
2467 
2468     if (ttype->is_map())
2469     {
2470         out << indent() << "await oprot.WriteMapEndAsync(cancellationToken);" << endl;
2471     }
2472     else if (ttype->is_set())
2473     {
2474         out << indent() << "await oprot.WriteSetEndAsync(cancellationToken);" << endl;
2475     }
2476     else if (ttype->is_list())
2477     {
2478         out << indent() << "await oprot.WriteListEndAsync(cancellationToken);" << endl;
2479     }
2480 
2481     indent_down();
2482     out << indent() << "}" << endl;
2483 }
2484 
generate_serialize_map_element(ostream & out,t_map * tmap,string iter,string map)2485 void t_netstd_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map)
2486 {
2487     t_field kfield(tmap->get_key_type(), iter);
2488     generate_serialize_field(out, &kfield, "");
2489     t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
2490     generate_serialize_field(out, &vfield, "");
2491 }
2492 
generate_serialize_set_element(ostream & out,t_set * tset,string iter)2493 void t_netstd_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter)
2494 {
2495     t_field efield(tset->get_elem_type(), iter);
2496     generate_serialize_field(out, &efield, "");
2497 }
2498 
generate_serialize_list_element(ostream & out,t_list * tlist,string iter)2499 void t_netstd_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter)
2500 {
2501     t_field efield(tlist->get_elem_type(), iter);
2502     generate_serialize_field(out, &efield, "");
2503 }
2504 
generate_property(ostream & out,t_field * tfield,bool isPublic,bool generateIsset)2505 void t_netstd_generator::generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset)
2506 {
2507     generate_netstd_property(out, tfield, isPublic, generateIsset, "_");
2508 }
2509 
generate_netstd_property(ostream & out,t_field * tfield,bool isPublic,bool generateIsset,string fieldPrefix)2510 void t_netstd_generator::generate_netstd_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset, string fieldPrefix)
2511 {
2512     if ((is_serialize_enabled() || is_wcf_enabled()) && isPublic)
2513     {
2514         out << indent() << "[DataMember(Order = 0)]" << endl;
2515     }
2516     bool is_required = field_is_required(tfield);
2517     if (is_required)
2518     {
2519         out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type()) << " " << prop_name(tfield) << " { get; set; }" << endl;
2520     }
2521     else
2522     {
2523         out << indent() << (isPublic ? "public " : "private ")  << type_name(tfield->get_type()) << " " << prop_name(tfield) << endl
2524             << indent() << "{" << endl;
2525         indent_up();
2526 
2527         out << indent() << "get" << endl
2528             << indent() << "{" << endl;
2529         indent_up();
2530 
2531         bool use_nullable = false;
2532 
2533         out << indent() << "return " << fieldPrefix + tfield->get_name() << ";" << endl;
2534         indent_down();
2535         out << indent() << "}" << endl
2536             << indent() << "set" << endl
2537             << indent() << "{" << endl;
2538         indent_up();
2539 
2540         if (use_nullable)
2541         {
2542             if (generateIsset)
2543             {
2544                 out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" << endl;
2545             }
2546             out << indent() << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() << " = value.Value;" << endl;
2547         }
2548         else
2549         {
2550             if (generateIsset)
2551             {
2552                 out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl;
2553             }
2554             out << indent() << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl;
2555         }
2556 
2557         indent_down();
2558         out << indent() << "}" << endl;
2559         indent_down();
2560         out << indent() << "}" << endl;
2561     }
2562     out << endl;
2563 }
2564 
make_valid_csharp_identifier(string const & fromName)2565 string t_netstd_generator::make_valid_csharp_identifier(string const& fromName)
2566 {
2567     string str = fromName;
2568     if (str.empty())
2569     {
2570         return str;
2571     }
2572 
2573     // tests rely on this
2574     assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
2575 
2576     // if the first letter is a number, we add an additional underscore in front of it
2577     char c = str.at(0);
2578     if (('0' <= c) && (c <= '9'))
2579     {
2580         str = "_" + str;
2581     }
2582 
2583     // following chars: letter, number or underscore
2584     for (size_t i = 0; i < str.size(); ++i)
2585     {
2586         c = str.at(i);
2587         if (('A' > c || c > 'Z') && ('a' > c || c > 'z') && ('0' > c || c > '9') && '_' != c)
2588         {
2589             str.replace(i, 1, "_");
2590         }
2591     }
2592 
2593     return str;
2594 }
2595 
cleanup_member_name_mapping(void * scope)2596 void t_netstd_generator::cleanup_member_name_mapping(void* scope)
2597 {
2598     if (member_mapping_scopes.empty())
2599     {
2600         throw "internal error: cleanup_member_name_mapping() no scope active";
2601     }
2602 
2603     member_mapping_scope& active = member_mapping_scopes.back();
2604     if (active.scope_member != scope)
2605     {
2606         throw "internal error: cleanup_member_name_mapping() called for wrong struct";
2607     }
2608 
2609     member_mapping_scopes.pop_back();
2610 }
2611 
get_mapped_member_name(string name)2612 string t_netstd_generator::get_mapped_member_name(string name)
2613 {
2614     if (!member_mapping_scopes.empty())
2615     {
2616         member_mapping_scope& active = member_mapping_scopes.back();
2617         map<string, string>::iterator iter = active.mapping_table.find(name);
2618         if (active.mapping_table.end() != iter)
2619         {
2620             return iter->second;
2621         }
2622     }
2623 
2624     pverbose("no mapping for member %s\n", name.c_str());
2625     return name;
2626 }
2627 
prepare_member_name_mapping(t_struct * tstruct)2628 void t_netstd_generator::prepare_member_name_mapping(t_struct* tstruct)
2629 {
2630     prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name());
2631 }
2632 
prepare_member_name_mapping(void * scope,const vector<t_field * > & members,const string & structname)2633 void t_netstd_generator::prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname)
2634 {
2635     // begin new scope
2636     member_mapping_scopes.emplace_back();
2637     member_mapping_scope& active = member_mapping_scopes.back();
2638     active.scope_member = scope;
2639 
2640     // current C# generator policy:
2641     // - prop names are always rendered with an Uppercase first letter
2642     // - struct names are used as given
2643     std::set<string> used_member_names;
2644     vector<t_field*>::const_iterator iter;
2645 
2646     // prevent name conflicts with struct (CS0542 error)
2647     used_member_names.insert(structname);
2648 
2649     // prevent name conflicts with known methods (THRIFT-2942)
2650     used_member_names.insert("Read");
2651     used_member_names.insert("Write");
2652 
2653     for (iter = members.begin(); iter != members.end(); ++iter)
2654     {
2655         string oldname = (*iter)->get_name();
2656         string newname = prop_name(*iter, true);
2657         while (true)
2658         {
2659             // new name conflicts with another member
2660             if (used_member_names.find(newname) != used_member_names.end())
2661             {
2662                 pverbose("struct %s: member %s conflicts with another member\n", structname.c_str(), newname.c_str());
2663                 newname += '_';
2664                 continue;
2665             }
2666 
2667             // add always, this helps us to detect edge cases like
2668             // different spellings ("foo" and "Foo") within the same struct
2669             pverbose("struct %s: member mapping %s => %s\n", structname.c_str(), oldname.c_str(), newname.c_str());
2670             active.mapping_table[oldname] = newname;
2671             used_member_names.insert(newname);
2672             break;
2673         }
2674     }
2675 }
2676 
prop_name(t_field * tfield,bool suppress_mapping)2677 string t_netstd_generator::prop_name(t_field* tfield, bool suppress_mapping)
2678 {
2679     string name(tfield->get_name());
2680     if (suppress_mapping)
2681     {
2682         name[0] = toupper(name[0]);
2683     }
2684     else
2685     {
2686         name = get_mapped_member_name(name);
2687     }
2688     return name;
2689 }
2690 
type_name(t_type * ttype)2691 string t_netstd_generator::type_name(t_type* ttype)
2692 {
2693     while (ttype->is_typedef())
2694     {
2695         ttype = static_cast<t_typedef*>(ttype)->get_type();
2696     }
2697 
2698     if (ttype->is_base_type())
2699     {
2700         return base_type_name(static_cast<t_base_type*>(ttype));
2701     }
2702 
2703     if (ttype->is_map())
2704     {
2705         t_map* tmap = static_cast<t_map*>(ttype);
2706         return "Dictionary<" + type_name(tmap->get_key_type()) + ", " + type_name(tmap->get_val_type()) + ">";
2707     }
2708 
2709     if (ttype->is_set())
2710     {
2711         t_set* tset = static_cast<t_set*>(ttype);
2712         return "THashSet<" + type_name(tset->get_elem_type()) + ">";
2713     }
2714 
2715     if (ttype->is_list())
2716     {
2717         t_list* tlist = static_cast<t_list*>(ttype);
2718         return "List<" + type_name(tlist->get_elem_type()) + ">";
2719     }
2720 
2721     t_program* program = ttype->get_program();
2722     if (program != NULL && program != program_)
2723     {
2724         string ns = program->get_namespace("netstd");
2725         if (!ns.empty())
2726         {
2727             return ns + "." + normalize_name(ttype->get_name());
2728         }
2729     }
2730 
2731     return normalize_name(ttype->get_name());
2732 }
2733 
base_type_name(t_base_type * tbase)2734 string t_netstd_generator::base_type_name(t_base_type* tbase)
2735 {
2736     switch (tbase->get_base())
2737     {
2738     case t_base_type::TYPE_VOID:
2739         return "void";
2740     case t_base_type::TYPE_STRING:
2741         {
2742             if (tbase->is_binary())
2743             {
2744                 return "byte[]";
2745             }
2746             return "string";
2747         }
2748     case t_base_type::TYPE_BOOL:
2749         return "bool";
2750     case t_base_type::TYPE_I8:
2751         return "sbyte";
2752     case t_base_type::TYPE_I16:
2753         return "short";
2754     case t_base_type::TYPE_I32:
2755         return "int";
2756     case t_base_type::TYPE_I64:
2757         return "long";
2758     case t_base_type::TYPE_DOUBLE:
2759         return "double";
2760     default:
2761         throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base());
2762     }
2763 }
2764 
declare_field(t_field * tfield,bool init,string prefix)2765 string t_netstd_generator::declare_field(t_field* tfield, bool init, string prefix)
2766 {
2767     string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name();
2768     if (init)
2769     {
2770         t_type* ttype = tfield->get_type();
2771         while (ttype->is_typedef())
2772         {
2773             ttype = static_cast<t_typedef*>(ttype)->get_type();
2774         }
2775         if (ttype->is_base_type() && field_has_default(tfield))
2776         {
2777             std::ofstream dummy;
2778             result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
2779         }
2780         else if (ttype->is_base_type())
2781         {
2782             t_base_type::t_base tbase = static_cast<t_base_type*>(ttype)->get_base();
2783             switch (tbase)
2784             {
2785             case t_base_type::TYPE_VOID:
2786                 throw "NO T_VOID CONSTRUCT";
2787             case t_base_type::TYPE_STRING:
2788                 result += " = null";
2789                 break;
2790             case t_base_type::TYPE_BOOL:
2791                 result += " = false";
2792                 break;
2793             case t_base_type::TYPE_I8:
2794             case t_base_type::TYPE_I16:
2795             case t_base_type::TYPE_I32:
2796             case t_base_type::TYPE_I64:
2797                 result += " = 0";
2798                 break;
2799             case t_base_type::TYPE_DOUBLE:
2800                 result += " = (double)0";
2801                 break;
2802             }
2803         }
2804         else if (ttype->is_enum())
2805         {
2806             result += " = (" + type_name(ttype) + ")0";
2807         }
2808         else if (ttype->is_container())
2809         {
2810             result += " = new " + type_name(ttype) + "()";
2811         }
2812         else
2813         {
2814             result += " = new " + type_name(ttype) + "()";
2815         }
2816     }
2817     return result + ";";
2818 }
2819 
function_signature(t_function * tfunction,string prefix)2820 string t_netstd_generator::function_signature(t_function* tfunction, string prefix)
2821 {
2822     t_type* ttype = tfunction->get_returntype();
2823     return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + ")";
2824 }
2825 
function_signature_async(t_function * tfunction,string prefix)2826 string t_netstd_generator::function_signature_async(t_function* tfunction, string prefix)
2827 {
2828     t_type* ttype = tfunction->get_returntype();
2829     string task = "Task";
2830     if (!ttype->is_void())
2831     {
2832         task += "<" + type_name(ttype) + ">";
2833     }
2834 
2835     string result = task + " " + normalize_name(prefix + tfunction->get_name()) + "Async(";
2836     string args = argument_list(tfunction->get_arglist());
2837     result += args;
2838     if (!args.empty())
2839     {
2840         result += ", ";
2841     }
2842     result += "CancellationToken cancellationToken = default(CancellationToken))";
2843 
2844     return result;
2845 }
2846 
argument_list(t_struct * tstruct)2847 string t_netstd_generator::argument_list(t_struct* tstruct)
2848 {
2849     string result = "";
2850     const vector<t_field*>& fields = tstruct->get_members();
2851     vector<t_field*>::const_iterator f_iter;
2852     bool first = true;
2853     for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
2854     {
2855         if (first)
2856         {
2857             first = false;
2858         }
2859         else
2860         {
2861             result += ", ";
2862         }
2863         result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name());
2864     }
2865     return result;
2866 }
2867 
type_to_enum(t_type * type)2868 string t_netstd_generator::type_to_enum(t_type* type)
2869 {
2870     while (type->is_typedef())
2871     {
2872         type = static_cast<t_typedef*>(type)->get_type();
2873     }
2874 
2875     if (type->is_base_type())
2876     {
2877         t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
2878         switch (tbase)
2879         {
2880         case t_base_type::TYPE_VOID:
2881             throw "NO T_VOID CONSTRUCT";
2882         case t_base_type::TYPE_STRING:
2883             return "TType.String";
2884         case t_base_type::TYPE_BOOL:
2885             return "TType.Bool";
2886         case t_base_type::TYPE_I8:
2887             return "TType.Byte";
2888         case t_base_type::TYPE_I16:
2889             return "TType.I16";
2890         case t_base_type::TYPE_I32:
2891             return "TType.I32";
2892         case t_base_type::TYPE_I64:
2893             return "TType.I64";
2894         case t_base_type::TYPE_DOUBLE:
2895             return "TType.Double";
2896         }
2897     }
2898     else if (type->is_enum())
2899     {
2900         return "TType.I32";
2901     }
2902     else if (type->is_struct() || type->is_xception())
2903     {
2904         return "TType.Struct";
2905     }
2906     else if (type->is_map())
2907     {
2908         return "TType.Map";
2909     }
2910     else if (type->is_set())
2911     {
2912         return "TType.Set";
2913     }
2914     else if (type->is_list())
2915     {
2916         return "TType.List";
2917     }
2918 
2919     throw "INVALID TYPE IN type_to_enum: " + type->get_name();
2920 }
2921 
generate_netstd_docstring_comment(ostream & out,string contents)2922 void t_netstd_generator::generate_netstd_docstring_comment(ostream& out, string contents)
2923 {
2924     docstring_comment(out, "/// <summary>" + endl, "/// ", contents, "/// </summary>" + endl);
2925 }
2926 
generate_netstd_doc(ostream & out,t_field * field)2927 void t_netstd_generator::generate_netstd_doc(ostream& out, t_field* field)
2928 {
2929     if (field->get_type()->is_enum())
2930     {
2931         string combined_message = field->get_doc() + endl + "<seealso cref=\"" + get_enum_class_name(field->get_type()) + "\"/>";
2932         generate_netstd_docstring_comment(out, combined_message);
2933     }
2934     else
2935     {
2936         generate_netstd_doc(out, static_cast<t_doc*>(field));
2937     }
2938 }
2939 
generate_netstd_doc(ostream & out,t_doc * tdoc)2940 void t_netstd_generator::generate_netstd_doc(ostream& out, t_doc* tdoc)
2941 {
2942     if (tdoc->has_doc())
2943     {
2944         generate_netstd_docstring_comment(out, tdoc->get_doc());
2945     }
2946 }
2947 
generate_netstd_doc(ostream & out,t_function * tfunction)2948 void t_netstd_generator::generate_netstd_doc(ostream& out, t_function* tfunction)
2949 {
2950     if (tfunction->has_doc())
2951     {
2952         stringstream ps;
2953         const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
2954         vector<t_field*>::const_iterator p_iter;
2955         for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter)
2956         {
2957             t_field* p = *p_iter;
2958             ps << endl << "<param name=\"" << p->get_name() << "\">";
2959             if (p->has_doc())
2960             {
2961                 string str = p->get_doc();
2962                 str.erase(remove(str.begin(), str.end(), '\n'), str.end());
2963                 ps << str;
2964             }
2965             ps << "</param>";
2966         }
2967 
2968         docstring_comment(out,
2969                                    "",
2970                                    "/// ",
2971                                    "<summary>" + endl + tfunction->get_doc() + "</summary>" + ps.str(),
2972                                    "");
2973     }
2974 }
2975 
docstring_comment(ostream & out,const string & comment_start,const string & line_prefix,const string & contents,const string & comment_end)2976 void t_netstd_generator::docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end)
2977 {
2978     if (comment_start != "")
2979     {
2980         out << indent() << comment_start;
2981     }
2982 
2983     stringstream docs(contents, std::ios_base::in);
2984 
2985     while (!(docs.eof() || docs.fail()))
2986     {
2987         char line[1024];
2988         docs.getline(line, 1024);
2989 
2990         // Just prnt a newline when the line & prefix are empty.
2991         if (strlen(line) == 0 && line_prefix == "" && !docs.eof())
2992         {
2993             out << endl;
2994         }
2995         else if (strlen(line) > 0 || !docs.eof())
2996         { // skip the empty last line
2997             out << indent() << line_prefix << line << endl;
2998         }
2999     }
3000     if (comment_end != "")
3001     {
3002         out << indent() << comment_end;
3003     }
3004 }
3005 
get_enum_class_name(t_type * type)3006 string t_netstd_generator::get_enum_class_name(t_type* type)
3007 {
3008     string package = "";
3009     t_program* program = type->get_program();
3010     if (program != NULL && program != program_)
3011     {
3012         package = program->get_namespace("netstd") + ".";
3013     }
3014     return package + type->get_name();
3015 }
3016 
3017 THRIFT_REGISTER_GENERATOR(
3018     netstd,
3019     "C#",
3020     "    wcf:             Adds bindings for WCF to generated classes.\n"
3021     "    serial:          Add serialization support to generated classes.\n"
3022     "    union:           Use new union typing, which includes a static read function for union types.\n"
3023 )
3024