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
20 #include <map>
21 #include <string>
22 #include <fstream>
23 #include <iomanip>
24 #include <iostream>
25 #include <limits>
26 #include <vector>
27 #include <list>
28 #include <cassert>
29 #include <unordered_map>
30
31 #include <stdlib.h>
32 #include <sys/stat.h>
33 #include <sstream>
34 #include "thrift/platform.h"
35 #include "thrift/version.h"
36
37 using std::map;
38 using std::ostream;
39 using std::ostringstream;
40 using std::string;
41 using std::stringstream;
42 using std::unordered_map;
43 using std::vector;
44
45 static const string endl = "\n"; // avoid ostream << std::endl flushes
46 static const string episode_file_name = "thrift.js.episode";
47 // largest consecutive integer representable by a double (2 ^ 53 - 1)
48 static const int64_t max_safe_integer = 0x1fffffffffffff;
49 // smallest consecutive number representable by a double (-2 ^ 53 + 1)
50 static const int64_t min_safe_integer = -max_safe_integer;
51
52 #include "thrift/generate/t_oop_generator.h"
53
54
55 /**
56 * JS code generator.
57 */
58 class t_js_generator : public t_oop_generator {
59 public:
t_js_generator(t_program * program,const std::map<std::string,std::string> & parsed_options,const std::string & option_string)60 t_js_generator(t_program* program,
61 const std::map<std::string, std::string>& parsed_options,
62 const std::string& option_string)
63 : t_oop_generator(program) {
64 (void)option_string;
65 std::map<std::string, std::string>::const_iterator iter;
66
67 gen_node_ = false;
68 gen_jquery_ = false;
69 gen_ts_ = false;
70 gen_es6_ = false;
71 gen_episode_file_ = false;
72
73 bool with_ns_ = false;
74
75 for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
76 if( iter->first.compare("node") == 0) {
77 gen_node_ = true;
78 } else if( iter->first.compare("jquery") == 0) {
79 gen_jquery_ = true;
80 } else if( iter->first.compare("ts") == 0) {
81 gen_ts_ = true;
82 } else if( iter->first.compare("with_ns") == 0) {
83 with_ns_ = true;
84 } else if( iter->first.compare("es6") == 0) {
85 gen_es6_ = true;
86 } else if( iter->first.compare("imports") == 0) {
87 parse_imports(program, iter->second);
88 } else if (iter->first.compare("thrift_package_output_directory") == 0) {
89 parse_thrift_package_output_directory(iter->second);
90 } else {
91 throw std::invalid_argument("unknown option js:" + iter->first);
92 }
93 }
94
95 if (gen_es6_ && gen_jquery_) {
96 throw std::invalid_argument("invalid switch: [-gen js:es6,jquery] options not compatible");
97 }
98
99 if (gen_node_ && gen_jquery_) {
100 throw std::invalid_argument("invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
101 "js:jquery]");
102 }
103
104 if (!gen_node_ && with_ns_) {
105 throw std::invalid_argument("invalid switch: [-gen js:with_ns] is only valid when using node.js");
106 }
107
108 // Depending on the processing flags, we will update these to be ES6 compatible
109 js_const_type_ = "var ";
110 js_let_type_ = "var ";
111 js_var_type_ = "var ";
112 if (gen_es6_) {
113 js_const_type_ = "const ";
114 js_let_type_ = "let ";
115 }
116
117 if (gen_node_) {
118 out_dir_base_ = "gen-nodejs";
119 no_ns_ = !with_ns_;
120 } else {
121 out_dir_base_ = "gen-js";
122 no_ns_ = false;
123 }
124
125 escape_['\''] = "\\'";
126 }
127
128 /**
129 * Init and close methods
130 */
131
132 void init_generator() override;
133 void close_generator() override;
134 std::string display_name() const override;
135
136 /**
137 * Program-level generation functions
138 */
139
140 void generate_typedef(t_typedef* ttypedef) override;
141 void generate_enum(t_enum* tenum) override;
142 void generate_const(t_const* tconst) override;
143 void generate_struct(t_struct* tstruct) override;
144 void generate_xception(t_struct* txception) override;
145 void generate_service(t_service* tservice) override;
146
147 std::string render_recv_throw(std::string var);
148 std::string render_recv_return(std::string var);
149
150 std::string render_const_value(t_type* type, t_const_value* value);
151
152 /**
153 * Structs!
154 */
155
156 void generate_js_struct(t_struct* tstruct, bool is_exception);
157 void generate_js_struct_definition(std::ostream& out,
158 t_struct* tstruct,
159 bool is_xception = false,
160 bool is_exported = true);
161 void generate_js_struct_reader(std::ostream& out, t_struct* tstruct);
162 void generate_js_struct_writer(std::ostream& out, t_struct* tstruct);
163 void generate_js_function_helpers(t_function* tfunction);
164
165 /**
166 * Service-level generation functions
167 */
168
169 void generate_service_helpers(t_service* tservice);
170 void generate_service_interface(t_service* tservice);
171 void generate_service_rest(t_service* tservice);
172 void generate_service_client(t_service* tservice);
173 void generate_service_processor(t_service* tservice);
174 void generate_process_function(t_service* tservice, t_function* tfunction);
175
176 /**
177 * Serialization constructs
178 */
179
180 void generate_deserialize_field(std::ostream& out,
181 t_field* tfield,
182 std::string prefix = "",
183 bool inclass = false);
184
185 void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
186
187 void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
188
189 void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
190
191 void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
192
193 void generate_deserialize_list_element(std::ostream& out,
194 t_list* tlist,
195 std::string prefix = "");
196
197 void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
198
199 void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
200
201 void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
202
203 void generate_serialize_map_element(std::ostream& out,
204 t_map* tmap,
205 std::string kiter,
206 std::string viter);
207
208 void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
209
210 void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
211
212 /**
213 * Helper rendering functions
214 */
215
216 std::string js_includes();
217 std::string ts_includes();
218 std::string ts_service_includes();
219 std::string render_includes();
220 std::string render_ts_includes();
221 std::string get_import_path(t_program* program);
222 std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
223 std::string function_signature(t_function* tfunction,
224 std::string prefix = "",
225 bool include_callback = false);
226 std::string argument_list(t_struct* tstruct, bool include_callback = false);
227 std::string type_to_enum(t_type* ttype);
228 std::string make_valid_nodeJs_identifier(std::string const& name);
229 std::string next_identifier_name(std::vector<t_field*> const& fields, std::string const& base_name);
230 bool find_field(std::vector<t_field*> const& fields, std::string const& name);
231
232 /**
233 * Helper parser functions
234 */
235
236 void parse_imports(t_program* program, const std::string& imports_string);
237 void parse_thrift_package_output_directory(const std::string& thrift_package_output_directory);
238
autogen_comment()239 std::string autogen_comment() override {
240 return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
241 + "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
242 + "//\n";
243 }
244
245 t_type* get_contained_type(t_type* t);
246
js_namespace_pieces(t_program * p)247 std::vector<std::string> js_namespace_pieces(t_program* p) {
248 std::string ns = p->get_namespace("js");
249
250 std::string::size_type loc;
251 std::vector<std::string> pieces;
252
253 if (no_ns_) {
254 return pieces;
255 }
256
257 if (ns.size() > 0) {
258 while ((loc = ns.find(".")) != std::string::npos) {
259 pieces.push_back(ns.substr(0, loc));
260 ns = ns.substr(loc + 1);
261 }
262 }
263
264 if (ns.size() > 0) {
265 pieces.push_back(ns);
266 }
267
268 return pieces;
269 }
270
js_type_namespace(t_program * p)271 std::string js_type_namespace(t_program* p) {
272 if (gen_node_) {
273 if (p != nullptr && p != program_) {
274 return make_valid_nodeJs_identifier(p->get_name()) + "_ttypes.";
275 }
276 return "ttypes.";
277 }
278 return js_namespace(p);
279 }
280
js_export_namespace(t_program * p)281 std::string js_export_namespace(t_program* p) {
282 if (gen_node_) {
283 return "exports.";
284 }
285 return js_namespace(p);
286 }
287
has_js_namespace(t_program * p)288 bool has_js_namespace(t_program* p) {
289 if (no_ns_) {
290 return false;
291 }
292 std::string ns = p->get_namespace("js");
293 return (ns.size() > 0);
294 }
295
js_namespace(t_program * p)296 std::string js_namespace(t_program* p) {
297 if (no_ns_) {
298 return "";
299 }
300 std::string ns = p->get_namespace("js");
301 if (ns.size() > 0) {
302 ns += ".";
303 }
304
305 return ns;
306 }
307
308 /**
309 * TypeScript Definition File helper functions
310 */
311
312 string ts_function_signature(t_function* tfunction, bool include_callback);
313 string ts_get_type(t_type* type);
314
315 /**
316 * Special indentation for TypeScript Definitions because of the module.
317 * Returns the normal indentation + " " if a module was defined.
318 * @return string
319 */
ts_indent()320 string ts_indent() { return indent() + (!ts_module_.empty() ? " " : ""); }
321
322 /**
323 * Returns "declare " if no module was defined.
324 * @return string
325 */
ts_declare()326 string ts_declare() { return (ts_module_.empty() ? (gen_node_ ? "declare " : "export declare ") : ""); }
327
328 /**
329 * Returns "?" if the given field is optional or has a default value.
330 * @param t_field The field to check
331 * @return string
332 */
ts_get_req(t_field * field)333 string ts_get_req(t_field* field) {return (field->get_req() == t_field::T_OPTIONAL || field->get_value() != nullptr ? "?" : ""); }
334
335 /**
336 * Returns the documentation, if the provided documentable object has one.
337 * @param t_doc The object to get the documentation from
338 * @return string The documentation
339 */
ts_print_doc(t_doc * tdoc)340 string ts_print_doc(t_doc* tdoc) {
341 string result = endl;
342
343 if (tdoc->has_doc()) {
344 std::stringstream doc(tdoc->get_doc());
345 string item;
346
347 result += ts_indent() + "/**" + endl;
348 while (std::getline(doc, item)) {
349 result += ts_indent() + " * " + item + endl;
350 }
351 result += ts_indent() + " */" + endl;
352 }
353 return result;
354 }
355
356 private:
357 /**
358 * True if we should generate NodeJS-friendly RPC services.
359 */
360 bool gen_node_;
361
362 /**
363 * True if we should generate services that use jQuery ajax (async/sync).
364 */
365 bool gen_jquery_;
366
367 /**
368 * True if we should generate a TypeScript Definition File for each service.
369 */
370 bool gen_ts_;
371
372 /**
373 * True if we should generate ES6 code, i.e. with Promises
374 */
375 bool gen_es6_;
376
377 /**
378 * True if we will generate an episode file.
379 */
380 bool gen_episode_file_;
381
382 /**
383 * The name of the defined module(s), for TypeScript Definition Files.
384 */
385 string ts_module_;
386
387 /**
388 * True if we should not generate namespace objects for node.
389 */
390 bool no_ns_;
391
392 /**
393 * The node modules to use when importing the previously generated files.
394 */
395 vector<string> imports;
396
397 /**
398 * Cache for imported modules.
399 */
400 unordered_map<string, string> module_name_2_import_path;
401
402 /**
403 * Cache for TypeScript includes to generated import name.
404 */
405 unordered_map<t_program*, string> include_2_import_name;
406
407 /**
408 * The prefix to use when generating the episode file.
409 */
410 string thrift_package_output_directory_;
411
412 /**
413 * The variable decorator for "const" variables. Will default to "var" if in an incompatible language.
414 */
415 string js_const_type_;
416
417 /**
418 * The variable decorator for "let" variables. Will default to "var" if in an incompatible language.
419 */
420 string js_let_type_;
421
422 /**
423 * The default variable decorator. Supports all javascript languages, but is not scoped to functions or closures.
424 */
425 string js_var_type_;
426
427 /**
428 * File streams
429 */
430 ofstream_with_content_based_conditional_update f_episode_;
431 ofstream_with_content_based_conditional_update f_types_;
432 ofstream_with_content_based_conditional_update f_service_;
433 ofstream_with_content_based_conditional_update f_types_ts_;
434 ofstream_with_content_based_conditional_update f_service_ts_;
435 };
436
437 /**
438 * Prepares for file generation by opening up the necessary file output
439 * streams.
440 *
441 * @param tprogram The program to generate
442 */
init_generator()443 void t_js_generator::init_generator() {
444 // Make output directory
445 MKDIR(get_out_dir().c_str());
446
447 const auto outdir = get_out_dir();
448
449 // Make output file(s)
450 if (gen_episode_file_) {
451 const auto f_episode_file_path = outdir + episode_file_name;
452 f_episode_.open(f_episode_file_path);
453 }
454
455 const auto f_types_name = outdir + program_->get_name() + "_types.js";
456 f_types_.open(f_types_name.c_str());
457 if (gen_episode_file_) {
458 const auto types_module = program_->get_name() + "_types";
459 f_episode_ << types_module << ":" << thrift_package_output_directory_ << "/" << types_module << endl;
460 }
461
462 if (gen_ts_) {
463 const auto f_types_ts_name = outdir + program_->get_name() + "_types.d.ts";
464 f_types_ts_.open(f_types_ts_name.c_str());
465 }
466
467 // Print header
468 f_types_ << autogen_comment();
469
470 if ((gen_node_ || gen_es6_) && no_ns_) {
471 f_types_ << "\"use strict\";" << endl << endl;
472 }
473
474 f_types_ << js_includes() << endl << render_includes() << endl;
475
476 if (gen_ts_) {
477 f_types_ts_ << autogen_comment() << ts_includes() << endl << render_ts_includes() << endl;
478 }
479
480 if (gen_node_) {
481 f_types_ << js_const_type_ << "ttypes = module.exports = {};" << endl;
482 }
483
484 string pns;
485
486 // setup the namespace
487 // TODO should the namespace just be in the directory structure for node?
488 vector<string> ns_pieces = js_namespace_pieces(program_);
489 if (ns_pieces.size() > 0) {
490 for (size_t i = 0; i < ns_pieces.size(); ++i) {
491 pns += ((i == 0) ? "" : ".") + ns_pieces[i];
492 f_types_ << "if (typeof " << pns << " === 'undefined') {" << endl;
493 f_types_ << " " << pns << " = {};" << endl;
494 f_types_ << "}" << endl;
495 f_types_ << "" << "if (typeof module !== 'undefined' && module.exports) {" << endl;
496 f_types_ << " module.exports." << pns << " = " << pns << ";" << endl << "}" << endl;
497 }
498 if (gen_ts_) {
499 ts_module_ = pns;
500 f_types_ts_ << "declare module " << ts_module_ << " {";
501 }
502 }
503 }
504
505 /**
506 * Prints standard js imports
507 */
js_includes()508 string t_js_generator::js_includes() {
509 if (gen_node_) {
510 string result = js_const_type_ + "thrift = require('thrift');\n"
511 + js_const_type_ + "Thrift = thrift.Thrift;\n";
512 if (!gen_es6_) {
513 result += js_const_type_ + "Q = thrift.Q;\n";
514 }
515 result += js_const_type_ + "Int64 = require('node-int64');\n";
516 return result;
517 }
518 string result = "if (typeof Int64 === 'undefined' && typeof require === 'function') {\n " + js_const_type_ + "Int64 = require('node-int64');\n}\n";
519 return result;
520 }
521
522 /**
523 * Prints standard ts imports
524 */
ts_includes()525 string t_js_generator::ts_includes() {
526 if (gen_node_) {
527 return string(
528 "import thrift = require('thrift');\n"
529 "import Thrift = thrift.Thrift;\n"
530 "import Q = thrift.Q;\n"
531 "import Int64 = require('node-int64');");
532 }
533 return string("import Int64 = require('node-int64');");
534 }
535
536 /**
537 * Prints service ts imports
538 */
ts_service_includes()539 string t_js_generator::ts_service_includes() {
540 if (gen_node_) {
541 return string(
542 "import thrift = require('thrift');\n"
543 "import Thrift = thrift.Thrift;\n"
544 "import Q = thrift.Q;\n"
545 "import Int64 = require('node-int64');");
546 }
547 return string("import Int64 = require('node-int64');");
548 }
549
550 /**
551 * Renders all the imports necessary for including another Thrift program
552 */
render_includes()553 string t_js_generator::render_includes() {
554 string result = "";
555
556 if (gen_node_) {
557 const vector<t_program*>& includes = program_->get_includes();
558 for (auto include : includes) {
559 result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n";
560 }
561 if (includes.size() > 0) {
562 result += "\n";
563 }
564 }
565
566 return result;
567 }
568
569 /**
570 * Renders all the imports necessary for including another Thrift program
571 */
render_ts_includes()572 string t_js_generator::render_ts_includes() {
573 string result;
574
575 if (!gen_node_) {
576 return result;
577 }
578 const vector<t_program*>& includes = program_->get_includes();
579 for (auto include : includes) {
580 string include_name = make_valid_nodeJs_identifier(include->get_name()) + "_ttypes";
581 include_2_import_name.insert({include, include_name});
582 result += "import " + include_name + " = require('" + get_import_path(include) + "');\n";
583 }
584 if (includes.size() > 0) {
585 result += "\n";
586 }
587
588 return result;
589 }
590
get_import_path(t_program * program)591 string t_js_generator::get_import_path(t_program* program) {
592 const string import_file_name(program->get_name() + "_types");
593 if (program->get_recursive()) {
594 return "./" + import_file_name;
595 }
596
597 const string import_file_name_with_extension = import_file_name + ".js";
598
599 auto module_name_and_import_path_iterator = module_name_2_import_path.find(import_file_name);
600 if (module_name_and_import_path_iterator != module_name_2_import_path.end()) {
601 return module_name_and_import_path_iterator->second;
602 }
603 return "./" + import_file_name;
604 }
605
606 /**
607 * Close up (or down) some filez.
608 */
close_generator()609 void t_js_generator::close_generator() {
610 // Close types file(s)
611
612 f_types_.close();
613
614 if (gen_ts_) {
615 if (!ts_module_.empty()) {
616 f_types_ts_ << "}";
617 }
618 f_types_ts_.close();
619 }
620 if (gen_episode_file_){
621 f_episode_.close();
622 }
623 }
624
625 /**
626 * Generates a typedef. This is not done in JS, types are all implicit.
627 *
628 * @param ttypedef The type definition
629 */
generate_typedef(t_typedef * ttypedef)630 void t_js_generator::generate_typedef(t_typedef* ttypedef) {
631 (void)ttypedef;
632 }
633
634 /**
635 * Generates code for an enumerated type. Since define is expensive to lookup
636 * in JS, we use a global array for this.
637 *
638 * @param tenum The enumeration
639 */
generate_enum(t_enum * tenum)640 void t_js_generator::generate_enum(t_enum* tenum) {
641 f_types_ << js_type_namespace(tenum->get_program()) << tenum->get_name() << " = {" << endl;
642
643 if (gen_ts_) {
644 f_types_ts_ << ts_print_doc(tenum) << ts_indent() << ts_declare() << "enum "
645 << tenum->get_name() << " {" << endl;
646 }
647
648 indent_up();
649
650 vector<t_enum_value*> const& constants = tenum->get_constants();
651 vector<t_enum_value*>::const_iterator c_iter;
652 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
653 int value = (*c_iter)->get_value();
654 if (gen_ts_) {
655 f_types_ts_ << ts_indent() << (*c_iter)->get_name() << " = " << value << "," << endl;
656 // add 'value: key' in addition to 'key: value' for TypeScript enums
657 f_types_ << indent() << "'" << value << "' : '" << (*c_iter)->get_name() << "'," << endl;
658 }
659 f_types_ << indent() << "'" << (*c_iter)->get_name() << "' : " << value;
660 if (c_iter != constants.end() - 1) {
661 f_types_ << ",";
662 }
663 f_types_ << endl;
664 }
665
666 indent_down();
667
668 f_types_ << "};" << endl;
669
670 if (gen_ts_) {
671 f_types_ts_ << ts_indent() << "}" << endl;
672 }
673 }
674
675 /**
676 * Generate a constant value
677 */
generate_const(t_const * tconst)678 void t_js_generator::generate_const(t_const* tconst) {
679 t_type* type = tconst->get_type();
680 string name = tconst->get_name();
681 t_const_value* value = tconst->get_value();
682
683 f_types_ << js_type_namespace(program_) << name << " = ";
684 f_types_ << render_const_value(type, value) << ";" << endl;
685
686 if (gen_ts_) {
687 f_types_ts_ << ts_print_doc(tconst) << ts_indent() << ts_declare() << js_const_type_ << name << ": "
688 << ts_get_type(type) << ";" << endl;
689 }
690 }
691
692 /**
693 * Prints the value of a constant with the given type. Note that type checking
694 * is NOT performed in this function as it is always run beforehand using the
695 * validate_types method in main.cc
696 */
render_const_value(t_type * type,t_const_value * value)697 string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
698 std::ostringstream out;
699
700 type = get_true_type(type);
701
702 if (type->is_base_type()) {
703 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
704 switch (tbase) {
705 case t_base_type::TYPE_STRING:
706 out << "'" << get_escaped_string(value) << "'";
707 break;
708 case t_base_type::TYPE_BOOL:
709 out << (value->get_integer() > 0 ? "true" : "false");
710 break;
711 case t_base_type::TYPE_I8:
712 case t_base_type::TYPE_I16:
713 case t_base_type::TYPE_I32:
714 out << value->get_integer();
715 break;
716 case t_base_type::TYPE_I64:
717 {
718 int64_t const& integer_value = value->get_integer();
719 if (integer_value <= max_safe_integer && integer_value >= min_safe_integer) {
720 out << "new Int64(" << integer_value << ")";
721 } else {
722 out << "new Int64('" << std::hex << integer_value << std::dec << "')";
723 }
724 }
725 break;
726 case t_base_type::TYPE_DOUBLE:
727 if (value->get_type() == t_const_value::CV_INTEGER) {
728 out << value->get_integer();
729 } else {
730 out << emit_double_as_string(value->get_double());
731 }
732 break;
733 default:
734 throw std::runtime_error("compiler error: no const of base type " + t_base_type::t_base_name(tbase));
735 }
736 } else if (type->is_enum()) {
737 out << value->get_integer();
738 } else if (type->is_struct() || type->is_xception()) {
739 out << "new " << js_type_namespace(type->get_program()) << type->get_name() << "({";
740 indent_up();
741 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
742 vector<t_field*>::const_iterator f_iter;
743 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
744 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
745 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
746 t_type* field_type = nullptr;
747 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
748 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
749 field_type = (*f_iter)->get_type();
750 }
751 }
752 if (field_type == nullptr) {
753 throw std::runtime_error("type error: " + type->get_name() + " has no field " + v_iter->first->get_string());
754 }
755 if (v_iter != val.begin())
756 out << ",";
757 out << endl << indent() << render_const_value(g_type_string, v_iter->first);
758 out << " : ";
759 out << render_const_value(field_type, v_iter->second);
760 }
761 indent_down();
762 out << endl << indent() << "})";
763 } else if (type->is_map()) {
764 t_type* ktype = ((t_map*)type)->get_key_type();
765
766 t_type* vtype = ((t_map*)type)->get_val_type();
767 out << "{" << endl;
768 indent_up();
769
770 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
771 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
772 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
773 if (v_iter != val.begin())
774 out << "," << endl;
775
776 if (ktype->is_base_type() && ((t_base_type*)get_true_type(ktype))->get_base() == t_base_type::TYPE_I64){
777 out << indent() << "\"" << v_iter->first->get_integer() << "\"";
778 } else {
779 out << indent() << render_const_value(ktype, v_iter->first);
780 }
781
782 out << " : ";
783 out << render_const_value(vtype, v_iter->second);
784 }
785 indent_down();
786 out << endl << indent() << "}";
787 } else if (type->is_list() || type->is_set()) {
788 t_type* etype;
789 if (type->is_list()) {
790 etype = ((t_list*)type)->get_elem_type();
791 } else {
792 etype = ((t_set*)type)->get_elem_type();
793 }
794 out << "[";
795 const vector<t_const_value*>& val = value->get_list();
796 vector<t_const_value*>::const_iterator v_iter;
797 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
798 if (v_iter != val.begin())
799 out << ",";
800 out << render_const_value(etype, *v_iter);
801 }
802 out << "]";
803 }
804 return out.str();
805 }
806
807 /**
808 * Make a struct
809 */
generate_struct(t_struct * tstruct)810 void t_js_generator::generate_struct(t_struct* tstruct) {
811 generate_js_struct(tstruct, false);
812 }
813
814 /**
815 * Generates a struct definition for a thrift exception. Basically the same
816 * as a struct but extends the Exception class.
817 *
818 * @param txception The struct definition
819 */
generate_xception(t_struct * txception)820 void t_js_generator::generate_xception(t_struct* txception) {
821 generate_js_struct(txception, true);
822 }
823
824 /**
825 * Structs can be normal or exceptions.
826 */
generate_js_struct(t_struct * tstruct,bool is_exception)827 void t_js_generator::generate_js_struct(t_struct* tstruct, bool is_exception) {
828 generate_js_struct_definition(f_types_, tstruct, is_exception);
829 }
830
831 /**
832 * Return type of contained elements for a container type. For maps
833 * this is type of value (keys are always strings in js)
834 */
get_contained_type(t_type * t)835 t_type* t_js_generator::get_contained_type(t_type* t) {
836 t_type* etype;
837 if (t->is_list()) {
838 etype = ((t_list*)t)->get_elem_type();
839 } else if (t->is_set()) {
840 etype = ((t_set*)t)->get_elem_type();
841 } else {
842 etype = ((t_map*)t)->get_val_type();
843 }
844 return etype;
845 }
846
847 /**
848 * Generates a struct definition for a thrift data type. This is nothing in JS
849 * where the objects are all just associative arrays (unless of course we
850 * decide to start using objects for them...)
851 *
852 * @param tstruct The struct definition
853 */
generate_js_struct_definition(ostream & out,t_struct * tstruct,bool is_exception,bool is_exported)854 void t_js_generator::generate_js_struct_definition(ostream& out,
855 t_struct* tstruct,
856 bool is_exception,
857 bool is_exported) {
858 const vector<t_field*>& members = tstruct->get_members();
859 vector<t_field*>::const_iterator m_iter;
860
861 if (gen_node_) {
862 string prefix = has_js_namespace(tstruct->get_program()) ? js_namespace(tstruct->get_program()) : js_const_type_;
863 out << prefix << tstruct->get_name() <<
864 (is_exported ? " = module.exports." + tstruct->get_name() : "");
865 if (gen_ts_) {
866 f_types_ts_ << ts_print_doc(tstruct) << ts_indent() << ts_declare() << "class "
867 << tstruct->get_name() << (is_exception ? " extends Thrift.TException" : "")
868 << " {" << endl;
869 }
870 } else {
871 out << js_namespace(tstruct->get_program()) << tstruct->get_name();
872 if (gen_ts_) {
873 f_types_ts_ << ts_print_doc(tstruct) << ts_indent() << ts_declare() << "class "
874 << tstruct->get_name() << (is_exception ? " extends Thrift.TException" : "")
875 << " {" << endl;
876 }
877 }
878
879 if (gen_es6_) {
880 if (gen_node_ && is_exception) {
881 out << " = class extends Thrift.TException {" << endl;
882 } else {
883 out << " = class {" << endl;
884 }
885 indent_up();
886 indent(out) << "constructor(args) {" << endl;
887 } else {
888 out << " = function(args) {" << endl;
889 }
890
891 indent_up();
892
893 // Call super() method on inherited Error class
894 if (gen_node_ && is_exception) {
895 if (gen_es6_) {
896 indent(out) << "super(args);" << endl;
897 } else {
898 indent(out) << "Thrift.TException.call(this, \"" << js_namespace(tstruct->get_program())
899 << tstruct->get_name() << "\");" << endl;
900 }
901 out << indent() << "this.name = \"" << js_namespace(tstruct->get_program())
902 << tstruct->get_name() << "\";" << endl;
903 }
904
905 // members with arguments
906 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
907 string dval = declare_field(*m_iter, false, true);
908 t_type* t = get_true_type((*m_iter)->get_type());
909 if ((*m_iter)->get_value() != nullptr && !(t->is_struct() || t->is_xception())) {
910 dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
911 out << indent() << "this." << (*m_iter)->get_name() << " = " << dval << ";" << endl;
912 } else {
913 out << indent() << dval << ";" << endl;
914 }
915 if (gen_ts_) {
916 string ts_access = gen_node_ ? "public " : "";
917 string member_name = (*m_iter)->get_name();
918
919 // Special case. Exceptions derive from Error, and error has a non optional message field.
920 // Ignore the optional flag in this case, otherwise we will generate a incompatible field
921 // in the eyes of typescript.
922 string optional_flag = is_exception && member_name == "message" ? "" : ts_get_req(*m_iter);
923
924 f_types_ts_ << ts_indent() << ts_access << member_name << optional_flag << ": "
925 << ts_get_type((*m_iter)->get_type()) << ";" << endl;
926 }
927 }
928
929 // Generate constructor from array
930 if (members.size() > 0) {
931
932 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
933 t_type* t = get_true_type((*m_iter)->get_type());
934 if ((*m_iter)->get_value() != nullptr && (t->is_struct() || t->is_xception())) {
935 indent(out) << "this." << (*m_iter)->get_name() << " = "
936 << render_const_value(t, (*m_iter)->get_value()) << ";" << endl;
937 }
938 }
939
940 // Early returns for exceptions
941 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
942 t_type* t = get_true_type((*m_iter)->get_type());
943 if (t->is_xception()) {
944 out << indent() << "if (args instanceof " << js_type_namespace(t->get_program())
945 << t->get_name() << ") {" << endl << indent() << indent() << "this."
946 << (*m_iter)->get_name() << " = args;" << endl << indent() << indent() << "return;"
947 << endl << indent() << "}" << endl;
948 }
949 }
950
951 indent(out) << "if (args) {" << endl;
952 indent_up();
953 if (gen_ts_) {
954 f_types_ts_ << endl << ts_indent() << "constructor(args?: { ";
955 }
956
957 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
958 t_type* t = get_true_type((*m_iter)->get_type());
959 indent(out) << "if (args." << (*m_iter)->get_name() << " !== undefined && args." << (*m_iter)->get_name() << " !== null) {" << endl;
960 indent_up();
961 indent(out) << "this." << (*m_iter)->get_name();
962
963 if (t->is_struct()) {
964 out << (" = new " + js_type_namespace(t->get_program()) + t->get_name() +
965 "(args."+(*m_iter)->get_name() +");");
966 out << endl;
967 } else if (t->is_container()) {
968 t_type* etype = get_contained_type(t);
969 string copyFunc = t->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
970 string type_list = "";
971
972 while (etype->is_container()) {
973 if (type_list.length() > 0) {
974 type_list += ", ";
975 }
976 type_list += etype->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
977 etype = get_contained_type(etype);
978 }
979
980 if (etype->is_struct()) {
981 if (type_list.length() > 0) {
982 type_list += ", ";
983 }
984 type_list += js_type_namespace(etype->get_program()) + etype->get_name();
985 }
986 else {
987 if (type_list.length() > 0) {
988 type_list += ", ";
989 }
990 type_list += "null";
991 }
992
993 out << (" = " + copyFunc + "(args." + (*m_iter)->get_name() +
994 ", [" + type_list + "]);");
995 out << endl;
996 } else {
997 out << " = args." << (*m_iter)->get_name() << ";" << endl;
998 }
999
1000 indent_down();
1001 if (!(*m_iter)->get_req()) {
1002 indent(out) << "} else {" << endl;
1003 indent(out)
1004 << " throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.UNKNOWN, "
1005 "'Required field " << (*m_iter)->get_name() << " is unset!');" << endl;
1006 }
1007 indent(out) << "}" << endl;
1008 if (gen_ts_) {
1009 f_types_ts_ << (*m_iter)->get_name() << ts_get_req(*m_iter) << ": "
1010 << ts_get_type((*m_iter)->get_type()) << "; ";
1011 }
1012 }
1013 indent_down();
1014 out << indent() << "}" << endl;
1015 if (gen_ts_) {
1016 f_types_ts_ << "});" << endl;
1017 }
1018 }
1019
1020 // Done with constructor
1021 indent_down();
1022 if (gen_es6_) {
1023 indent(out) << "}" << endl << endl;
1024 } else {
1025 indent(out) << "};" << endl;
1026 }
1027
1028 if (gen_ts_) {
1029 f_types_ts_ << ts_indent() << "}" << endl;
1030 }
1031
1032 if (!gen_es6_) {
1033 if (is_exception) {
1034 out << "Thrift.inherits(" << js_namespace(tstruct->get_program()) << tstruct->get_name()
1035 << ", Thrift.TException);" << endl;
1036 out << js_namespace(tstruct->get_program()) << tstruct->get_name() << ".prototype.name = '"
1037 << tstruct->get_name() << "';" << endl;
1038 } else {
1039 // init prototype manually if we aren't using es6
1040 out << js_namespace(tstruct->get_program()) << tstruct->get_name() << ".prototype = {};"
1041 << endl;
1042 }
1043
1044 }
1045
1046 generate_js_struct_reader(out, tstruct);
1047 generate_js_struct_writer(out, tstruct);
1048
1049 // Close out the class definition
1050 if (gen_es6_) {
1051 indent_down();
1052 indent(out) << "};" << endl;
1053 }
1054 }
1055
1056 /**
1057 * Generates the read() method for a struct
1058 */
generate_js_struct_reader(ostream & out,t_struct * tstruct)1059 void t_js_generator::generate_js_struct_reader(ostream& out, t_struct* tstruct) {
1060 const vector<t_field*>& fields = tstruct->get_members();
1061 vector<t_field*>::const_iterator f_iter;
1062
1063 if (gen_es6_) {
1064 indent(out) << "read (input) {" << endl;
1065 } else {
1066 indent(out) << js_namespace(tstruct->get_program()) << tstruct->get_name()
1067 << ".prototype.read = function(input) {" << endl;
1068 }
1069
1070 indent_up();
1071
1072 indent(out) << "input.readStructBegin();" << endl;
1073
1074 // Loop over reading in fields
1075 indent(out) << "while (true) {" << endl;
1076
1077 indent_up();
1078
1079 indent(out) << js_const_type_ << "ret = input.readFieldBegin();" << endl;
1080 indent(out) << js_const_type_ << "ftype = ret.ftype;" << endl;
1081 if (!fields.empty()) {
1082 indent(out) << js_const_type_ << "fid = ret.fid;" << endl;
1083 }
1084
1085 // Check for field STOP marker and break
1086 indent(out) << "if (ftype == Thrift.Type.STOP) {" << endl;
1087 indent_up();
1088 indent(out) << "break;" << endl;
1089 indent_down();
1090 indent(out) << "}" << endl;
1091 if (!fields.empty()) {
1092 // Switch statement on the field we are reading
1093 indent(out) << "switch (fid) {" << endl;
1094
1095 indent_up();
1096
1097 // Generate deserialization code for known cases
1098 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1099
1100 indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
1101 indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
1102
1103 indent_up();
1104 generate_deserialize_field(out, *f_iter, "this.");
1105 indent_down();
1106
1107 indent(out) << "} else {" << endl;
1108
1109 indent(out) << " input.skip(ftype);" << endl;
1110
1111 out << indent() << "}" << endl << indent() << "break;" << endl;
1112 }
1113 if (fields.size() == 1) {
1114 // pseudo case to make jslint happy
1115 indent(out) << "case 0:" << endl;
1116 indent(out) << " input.skip(ftype);" << endl;
1117 indent(out) << " break;" << endl;
1118 }
1119 // In the default case we skip the field
1120 indent(out) << "default:" << endl;
1121 indent(out) << " input.skip(ftype);" << endl;
1122
1123 scope_down(out);
1124 } else {
1125 indent(out) << "input.skip(ftype);" << endl;
1126 }
1127
1128 indent(out) << "input.readFieldEnd();" << endl;
1129
1130 scope_down(out);
1131
1132 indent(out) << "input.readStructEnd();" << endl;
1133
1134 indent(out) << "return;" << endl;
1135
1136 indent_down();
1137
1138 if (gen_es6_) {
1139 indent(out) << "}" << endl << endl;
1140 } else {
1141 indent(out) << "};" << endl << endl;
1142 }
1143 }
1144
1145 /**
1146 * Generates the write() method for a struct
1147 */
generate_js_struct_writer(ostream & out,t_struct * tstruct)1148 void t_js_generator::generate_js_struct_writer(ostream& out, t_struct* tstruct) {
1149 string name = tstruct->get_name();
1150 const vector<t_field*>& fields = tstruct->get_members();
1151 vector<t_field*>::const_iterator f_iter;
1152
1153 if (gen_es6_) {
1154 indent(out) << "write (output) {" << endl;
1155 } else {
1156 indent(out) << js_namespace(tstruct->get_program()) << tstruct->get_name()
1157 << ".prototype.write = function(output) {" << endl;
1158 }
1159
1160 indent_up();
1161
1162 indent(out) << "output.writeStructBegin('" << name << "');" << endl;
1163
1164 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1165 out << indent() << "if (this." << (*f_iter)->get_name() << " !== null && this."
1166 << (*f_iter)->get_name() << " !== undefined) {" << endl;
1167 indent_up();
1168
1169 indent(out) << "output.writeFieldBegin("
1170 << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type())
1171 << ", " << (*f_iter)->get_key() << ");" << endl;
1172
1173 // Write field contents
1174 generate_serialize_field(out, *f_iter, "this.");
1175
1176 indent(out) << "output.writeFieldEnd();" << endl;
1177
1178 indent_down();
1179 indent(out) << "}" << endl;
1180 }
1181
1182 out << indent() << "output.writeFieldStop();" << endl << indent() << "output.writeStructEnd();"
1183 << endl;
1184
1185 out << indent() << "return;" << endl;
1186
1187 indent_down();
1188 if (gen_es6_) {
1189 out << indent() << "}" << endl << endl;
1190 } else {
1191 out << indent() << "};" << endl << endl;
1192 }
1193 }
1194
1195 /**
1196 * Generates a thrift service.
1197 *
1198 * @param tservice The service definition
1199 */
generate_service(t_service * tservice)1200 void t_js_generator::generate_service(t_service* tservice) {
1201 string f_service_name = get_out_dir() + service_name_ + ".js";
1202 f_service_.open(f_service_name.c_str());
1203 if (gen_episode_file_) {
1204 f_episode_ << service_name_ << ":" << thrift_package_output_directory_ << "/" << service_name_ << endl;
1205 }
1206
1207 if (gen_ts_) {
1208 string f_service_ts_name = get_out_dir() + service_name_ + ".d.ts";
1209 f_service_ts_.open(f_service_ts_name.c_str());
1210 }
1211
1212 f_service_ << autogen_comment();
1213
1214 if ((gen_node_ || gen_es6_) && no_ns_) {
1215 f_service_ << "\"use strict\";" << endl << endl;
1216 }
1217
1218 f_service_ << js_includes() << endl << render_includes() << endl;
1219
1220 if (gen_ts_) {
1221 if (tservice->get_extends() != nullptr) {
1222 f_service_ts_ << "/// <reference path=\"" << tservice->get_extends()->get_name()
1223 << ".d.ts\" />" << endl;
1224 }
1225 f_service_ts_ << autogen_comment() << endl << ts_includes() << endl << render_ts_includes() << endl;
1226 if (gen_node_) {
1227 f_service_ts_ << "import ttypes = require('./" + program_->get_name() + "_types');" << endl;
1228 // Generate type aliases
1229 // enum
1230 vector<t_enum*> const& enums = program_->get_enums();
1231 vector<t_enum*>::const_iterator e_iter;
1232 for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) {
1233 f_service_ts_ << "import " << (*e_iter)->get_name() << " = ttypes."
1234 << js_namespace(program_) << (*e_iter)->get_name() << endl;
1235 }
1236 // const
1237 vector<t_const*> const& consts = program_->get_consts();
1238 vector<t_const*>::const_iterator c_iter;
1239 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
1240 f_service_ts_ << "import " << (*c_iter)->get_name() << " = ttypes."
1241 << js_namespace(program_) << (*c_iter)->get_name() << endl;
1242 }
1243 // exception
1244 vector<t_struct*> const& exceptions = program_->get_xceptions();
1245 vector<t_struct*>::const_iterator x_iter;
1246 for (x_iter = exceptions.begin(); x_iter != exceptions.end(); ++x_iter) {
1247 f_service_ts_ << "import " << (*x_iter)->get_name() << " = ttypes."
1248 << js_namespace(program_) << (*x_iter)->get_name() << endl;
1249 }
1250 // structs
1251 vector<t_struct*> const& structs = program_->get_structs();
1252 vector<t_struct*>::const_iterator s_iter;
1253 for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) {
1254 f_service_ts_ << "import " << (*s_iter)->get_name() << " = ttypes."
1255 << js_namespace(program_) << (*s_iter)->get_name() << endl;
1256 }
1257 } else {
1258 f_service_ts_ << "import { " << program_->get_name() << " } from \"./" << program_->get_name() << "_types\";" << endl << endl;
1259 }
1260 if (!ts_module_.empty()) {
1261 if (gen_node_) {
1262 f_service_ts_ << "declare module " << ts_module_ << " {";
1263 } else {
1264 f_service_ts_ << "declare module \"./" << program_->get_name() << "_types\" {" << endl;
1265 indent_up();
1266 f_service_ts_ << ts_indent() << "module " << program_->get_name() << " {" << endl;
1267 indent_up();
1268 }
1269 }
1270 }
1271
1272 if (gen_node_) {
1273 if (tservice->get_extends() != nullptr) {
1274 f_service_ << js_const_type_ << tservice->get_extends()->get_name() << " = require('./"
1275 << tservice->get_extends()->get_name() << "');" << endl << js_const_type_
1276 << tservice->get_extends()->get_name()
1277 << "Client = " << tservice->get_extends()->get_name() << ".Client;" << endl
1278 << js_const_type_ << tservice->get_extends()->get_name()
1279 << "Processor = " << tservice->get_extends()->get_name() << ".Processor;" << endl;
1280
1281 f_service_ts_ << "import " << tservice->get_extends()->get_name() << " = require('./"
1282 << tservice->get_extends()->get_name() << "');" << endl;
1283 }
1284
1285 f_service_ << js_const_type_ << "ttypes = require('./" + program_->get_name() + "_types');" << endl;
1286 }
1287
1288 generate_service_helpers(tservice);
1289 generate_service_interface(tservice);
1290 generate_service_client(tservice);
1291
1292 if (gen_node_) {
1293 generate_service_processor(tservice);
1294 }
1295
1296 f_service_.close();
1297 if (gen_ts_) {
1298 if (!ts_module_.empty()) {
1299 if (gen_node_) {
1300 f_service_ts_ << "}" << endl;
1301 } else {
1302 indent_down();
1303 f_service_ts_ << ts_indent() << "}" << endl;
1304 f_service_ts_ << "}" << endl;
1305 }
1306 }
1307 f_service_ts_.close();
1308 }
1309 }
1310
1311 /**
1312 * Generates a service server definition.
1313 *
1314 * @param tservice The service to generate a server for.
1315 */
generate_service_processor(t_service * tservice)1316 void t_js_generator::generate_service_processor(t_service* tservice) {
1317 vector<t_function*> functions = tservice->get_functions();
1318 vector<t_function*>::iterator f_iter;
1319
1320 if (gen_node_) {
1321 string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_;
1322 f_service_ << prefix << service_name_ << "Processor = " << "exports.Processor";
1323 if (gen_ts_) {
1324 f_service_ts_ << endl << "declare class Processor ";
1325 if (tservice->get_extends() != nullptr) {
1326 f_service_ts_ << "extends " << tservice->get_extends()->get_name() << ".Processor ";
1327 }
1328 f_service_ts_ << "{" << endl;
1329 indent_up();
1330
1331 if(tservice->get_extends() == nullptr) {
1332 f_service_ts_ << ts_indent() << "private _handler: object;" << endl << endl;
1333 }
1334 f_service_ts_ << ts_indent() << "constructor(handler: object);" << endl;
1335 f_service_ts_ << ts_indent() << "process(input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl;
1336 indent_down();
1337 }
1338 } else {
1339 f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Processor = "
1340 << "exports.Processor";
1341 }
1342
1343 bool is_subclass_service = tservice->get_extends() != nullptr;
1344
1345 // ES6 Constructor
1346 if (gen_es6_) {
1347 if (is_subclass_service) {
1348 f_service_ << " = class " << service_name_ << "Processor extends " << tservice->get_extends()->get_name() << "Processor {" << endl;
1349 } else {
1350 f_service_ << " = class " << service_name_ << "Processor {" << endl;
1351 }
1352 indent_up();
1353 indent(f_service_) << "constructor(handler) {" << endl;
1354 } else {
1355 f_service_ << " = function(handler) {" << endl;
1356 }
1357
1358 indent_up();
1359 if (gen_es6_ && is_subclass_service) {
1360 indent(f_service_) << "super(handler);" << endl;
1361 }
1362 indent(f_service_) << "this._handler = handler;" << endl;
1363 indent_down();
1364
1365 // Done with constructor
1366 if (gen_es6_) {
1367 indent(f_service_) << "}" << endl;
1368 } else {
1369 indent(f_service_) << "};" << endl;
1370 }
1371
1372 // ES5 service inheritance
1373 if (!gen_es6_ && is_subclass_service) {
1374 indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program())
1375 << service_name_ << "Processor, " << tservice->get_extends()->get_name()
1376 << "Processor);" << endl;
1377 }
1378
1379 // Generate the server implementation
1380 if (gen_es6_) {
1381 indent(f_service_) << "process (input, output) {" << endl;
1382 } else {
1383 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
1384 << "Processor.prototype.process = function(input, output) {" << endl;
1385 }
1386
1387 indent_up();
1388
1389 indent(f_service_) << js_const_type_ << "r = input.readMessageBegin();" << endl << indent()
1390 << "if (this['process_' + r.fname]) {" << endl << indent()
1391 << " return this['process_' + r.fname].call(this, r.rseqid, input, output);" << endl
1392 << indent() << "} else {" << endl << indent() << " input.skip(Thrift.Type.STRUCT);"
1393 << endl << indent() << " input.readMessageEnd();" << endl << indent()
1394 << " " << js_const_type_ << "x = new "
1395 "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN_METHOD, "
1396 "'Unknown function ' + r.fname);" << endl << indent()
1397 << " output.writeMessageBegin(r.fname, Thrift.MessageType.EXCEPTION, r.rseqid);"
1398 << endl << indent() << " x.write(output);" << endl << indent()
1399 << " output.writeMessageEnd();" << endl << indent() << " output.flush();" << endl
1400 << indent() << "}" << endl;
1401
1402 indent_down();
1403 if (gen_es6_) {
1404 indent(f_service_) << "}" << endl;
1405 } else {
1406 indent(f_service_) << "};" << endl;
1407 }
1408
1409 // Generate the process subfunctions
1410 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1411 generate_process_function(tservice, *f_iter);
1412 }
1413
1414 // Close off the processor class definition
1415 if (gen_es6_) {
1416 indent_down();
1417 indent(f_service_) << "};" << endl;
1418 }
1419 if (gen_node_ && gen_ts_) {
1420 f_service_ts_ << "}" << endl;
1421 }
1422 }
1423
1424 /**
1425 * Generates a process function definition.
1426 *
1427 * @param tfunction The function to write a dispatcher for
1428 */
generate_process_function(t_service * tservice,t_function * tfunction)1429 void t_js_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
1430 if (gen_es6_) {
1431 indent(f_service_) << "process_" + tfunction->get_name() + " (seqid, input, output) {" << endl;
1432 } else {
1433 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
1434 << "Processor.prototype.process_" + tfunction->get_name()
1435 + " = function(seqid, input, output) {" << endl;
1436 }
1437 if (gen_ts_) {
1438 indent_up();
1439 f_service_ts_ << ts_indent() << "process_" << tfunction->get_name() << "(seqid: number, input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl;
1440 indent_down();
1441 }
1442
1443 indent_up();
1444
1445 string argsname = js_namespace(program_) + service_name_ + "_" + tfunction->get_name() + "_args";
1446 string resultname = js_namespace(program_) + service_name_ + "_" + tfunction->get_name()
1447 + "_result";
1448
1449 indent(f_service_) << js_const_type_ << "args = new " << argsname << "();" << endl << indent()
1450 << "args.read(input);" << endl << indent() << "input.readMessageEnd();" << endl;
1451
1452 // Generate the function call
1453 t_struct* arg_struct = tfunction->get_arglist();
1454 const std::vector<t_field*>& fields = arg_struct->get_members();
1455 vector<t_field*>::const_iterator f_iter;
1456
1457 // Shortcut out here for oneway functions
1458 if (tfunction->is_oneway()) {
1459 indent(f_service_) << "this._handler." << tfunction->get_name() << "(";
1460
1461 bool first = true;
1462 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1463 if (first) {
1464 first = false;
1465 } else {
1466 f_service_ << ", ";
1467 }
1468 f_service_ << "args." << (*f_iter)->get_name();
1469 }
1470
1471 f_service_ << ");" << endl;
1472 indent_down();
1473
1474 if (gen_es6_) {
1475 indent(f_service_) << "}" << endl;
1476 } else {
1477 indent(f_service_) << "};" << endl;
1478 }
1479 return;
1480 }
1481
1482 // Promise style invocation
1483 indent(f_service_) << "if (this._handler." << tfunction->get_name()
1484 << ".length === " << fields.size() << ") {" << endl;
1485 indent_up();
1486
1487 if (gen_es6_) {
1488 indent(f_service_) << "Promise.resolve(this._handler." << tfunction->get_name() << ".bind(this._handler)(" << endl;
1489 } else {
1490 string maybeComma = (fields.size() > 0 ? "," : "");
1491 indent(f_service_) << "Q.fcall(this._handler." << tfunction->get_name() << ".bind(this._handler)"
1492 << maybeComma << endl;
1493 }
1494
1495 indent_up();
1496 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1497 string maybeComma = (f_iter != fields.end() - 1 ? "," : "");
1498 indent(f_service_) << "args." << (*f_iter)->get_name() << maybeComma << endl;
1499 }
1500 indent_down();
1501
1502 if (gen_es6_) {
1503 indent(f_service_) << ")).then(result => {" << endl;
1504 } else {
1505 indent(f_service_) << ").then(function(result) {" << endl;
1506 }
1507
1508 indent_up();
1509 f_service_ << indent() << js_const_type_ << "result_obj = new " << resultname << "({success: result});" << endl
1510 << indent() << "output.writeMessageBegin(\"" << tfunction->get_name()
1511 << "\", Thrift.MessageType.REPLY, seqid);" << endl << indent()
1512 << "result_obj.write(output);" << endl << indent() << "output.writeMessageEnd();" << endl
1513 << indent() << "output.flush();" << endl;
1514 indent_down();
1515
1516 if (gen_es6_) {
1517 indent(f_service_) << "}).catch(err => {" << endl;
1518 } else {
1519 indent(f_service_) << "}).catch(function (err) {" << endl;
1520 }
1521 indent_up();
1522 indent(f_service_) << js_let_type_ << "result;" << endl;
1523
1524 bool has_exception = false;
1525 t_struct* exceptions = tfunction->get_xceptions();
1526 if (exceptions) {
1527 const vector<t_field*>& members = exceptions->get_members();
1528 for (auto member : members) {
1529 t_type* t = get_true_type(member->get_type());
1530 if (t->is_xception()) {
1531 if (!has_exception) {
1532 has_exception = true;
1533 indent(f_service_) << "if (err instanceof " << js_type_namespace(t->get_program())
1534 << t->get_name();
1535 } else {
1536 f_service_ << " || err instanceof " << js_type_namespace(t->get_program())
1537 << t->get_name();
1538 }
1539 }
1540 }
1541 }
1542
1543 if (has_exception) {
1544 f_service_ << ") {" << endl;
1545 indent_up();
1546 f_service_ << indent() << "result = new " << resultname << "(err);" << endl << indent()
1547 << "output.writeMessageBegin(\"" << tfunction->get_name()
1548 << "\", Thrift.MessageType.REPLY, seqid);" << endl;
1549
1550 indent_down();
1551 indent(f_service_) << "} else {" << endl;
1552 indent_up();
1553 }
1554
1555 f_service_ << indent() << "result = new "
1556 "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN,"
1557 " err.message);" << endl << indent() << "output.writeMessageBegin(\""
1558 << tfunction->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl;
1559
1560 if (has_exception) {
1561 indent_down();
1562 indent(f_service_) << "}" << endl;
1563 }
1564
1565 f_service_ << indent() << "result.write(output);" << endl << indent()
1566 << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl;
1567 indent_down();
1568 indent(f_service_) << "});" << endl;
1569 indent_down();
1570 // End promise style invocation
1571
1572 // Callback style invocation
1573 indent(f_service_) << "} else {" << endl;
1574 indent_up();
1575 indent(f_service_) << "this._handler." << tfunction->get_name() << "(";
1576
1577 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1578 f_service_ << "args." << (*f_iter)->get_name() << ", ";
1579 }
1580
1581 if (gen_es6_) {
1582 f_service_ << "(err, result) => {" << endl;
1583 } else {
1584 f_service_ << "function (err, result) {" << endl;
1585 }
1586 indent_up();
1587 indent(f_service_) << js_let_type_ << "result_obj;" << endl;
1588
1589 indent(f_service_) << "if ((err === null || typeof err === 'undefined')";
1590 if (has_exception) {
1591 const vector<t_field*>& members = exceptions->get_members();
1592 for (auto member : members) {
1593 t_type* t = get_true_type(member->get_type());
1594 if (t->is_xception()) {
1595 f_service_ << " || err instanceof " << js_type_namespace(t->get_program()) << t->get_name();
1596 }
1597 }
1598 }
1599 f_service_ << ") {" << endl;
1600 indent_up();
1601 f_service_ << indent() << "result_obj = new " << resultname
1602 << "((err !== null || typeof err === 'undefined') ? err : {success: result});" << endl << indent()
1603 << "output.writeMessageBegin(\"" << tfunction->get_name()
1604 << "\", Thrift.MessageType.REPLY, seqid);" << endl;
1605 indent_down();
1606 indent(f_service_) << "} else {" << endl;
1607 indent_up();
1608 f_service_ << indent() << "result_obj = new "
1609 "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN,"
1610 " err.message);" << endl << indent() << "output.writeMessageBegin(\""
1611 << tfunction->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl;
1612 indent_down();
1613 f_service_ << indent() << "}" << endl << indent() << "result_obj.write(output);" << endl << indent()
1614 << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl;
1615
1616 indent_down();
1617 indent(f_service_) << "});" << endl;
1618 indent_down();
1619 indent(f_service_) << "}" << endl;
1620 // End callback style invocation
1621
1622 indent_down();
1623
1624 if (gen_es6_) {
1625 indent(f_service_) << "}" << endl;
1626 } else {
1627 indent(f_service_) << "};" << endl;
1628 }
1629 }
1630
1631 /**
1632 * Generates helper functions for a service.
1633 *
1634 * @param tservice The service to generate a header definition for
1635 */
generate_service_helpers(t_service * tservice)1636 void t_js_generator::generate_service_helpers(t_service* tservice) {
1637 // Do not generate TS definitions for helper functions
1638 bool gen_ts_tmp = gen_ts_;
1639 gen_ts_ = false;
1640
1641 vector<t_function*> functions = tservice->get_functions();
1642 vector<t_function*>::iterator f_iter;
1643
1644 f_service_ << "//HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
1645
1646 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1647 t_struct* ts = (*f_iter)->get_arglist();
1648 string name = ts->get_name();
1649 ts->set_name(service_name_ + "_" + name);
1650 generate_js_struct_definition(f_service_, ts, false, false);
1651 generate_js_function_helpers(*f_iter);
1652 ts->set_name(name);
1653 }
1654
1655 gen_ts_ = gen_ts_tmp;
1656 }
1657
1658 /**
1659 * Generates a struct and helpers for a function.
1660 *
1661 * @param tfunction The function
1662 */
generate_js_function_helpers(t_function * tfunction)1663 void t_js_generator::generate_js_function_helpers(t_function* tfunction) {
1664 t_struct result(program_, service_name_ + "_" + tfunction->get_name() + "_result");
1665 t_field success(tfunction->get_returntype(), "success", 0);
1666 if (!tfunction->get_returntype()->is_void()) {
1667 result.append(&success);
1668 }
1669
1670 t_struct* xs = tfunction->get_xceptions();
1671 const vector<t_field*>& fields = xs->get_members();
1672 vector<t_field*>::const_iterator f_iter;
1673 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1674 result.append(*f_iter);
1675 }
1676
1677 generate_js_struct_definition(f_service_, &result, false, false);
1678 }
1679
1680 /**
1681 * Generates a service interface definition.
1682 *
1683 * @param tservice The service to generate a header definition for
1684 */
generate_service_interface(t_service * tservice)1685 void t_js_generator::generate_service_interface(t_service* tservice) {
1686 (void)tservice;
1687 }
1688
1689 /**
1690 * Generates a REST interface
1691 */
generate_service_rest(t_service * tservice)1692 void t_js_generator::generate_service_rest(t_service* tservice) {
1693 (void)tservice;
1694 }
1695
1696 /**
1697 * Generates a service client definition.
1698 *
1699 * @param tservice The service to generate a server for.
1700 */
generate_service_client(t_service * tservice)1701 void t_js_generator::generate_service_client(t_service* tservice) {
1702
1703 bool is_subclass_service = tservice->get_extends() != nullptr;
1704
1705 if (gen_node_) {
1706 string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_;
1707 f_service_ << prefix << service_name_ << "Client = " << "exports.Client";
1708 if (gen_ts_) {
1709 f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class "
1710 << "Client ";
1711 if (tservice->get_extends() != nullptr) {
1712 f_service_ts_ << "extends " << tservice->get_extends()->get_name() << ".Client ";
1713 }
1714 f_service_ts_ << "{" << endl;
1715 }
1716 } else {
1717 f_service_ << js_namespace(tservice->get_program()) << service_name_
1718 << "Client";
1719 if (gen_ts_) {
1720 f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class "
1721 << service_name_ << "Client ";
1722 if (is_subclass_service) {
1723 f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client ";
1724 }
1725 f_service_ts_ << "{" << endl;
1726 }
1727 }
1728
1729 // ES6 Constructor
1730 if (gen_es6_) {
1731
1732 if (is_subclass_service) {
1733 f_service_ << " = class " << service_name_ << "Client extends " << js_namespace(tservice->get_extends()->get_program())
1734 << tservice->get_extends()->get_name() << "Client {" << endl;
1735 } else {
1736 f_service_ << " = class " << service_name_ << "Client {" << endl;
1737 }
1738 indent_up();
1739 if (gen_node_) {
1740 indent(f_service_) << "constructor(output, pClass) {" << endl;
1741 } else {
1742 indent(f_service_) << "constructor(input, output) {" << endl;
1743 }
1744 } else {
1745 if (gen_node_) {
1746 f_service_ << " = function(output, pClass) {" << endl;
1747 } else {
1748 f_service_ << " = function(input, output) {" << endl;
1749 }
1750 }
1751
1752 indent_up();
1753
1754 if (gen_node_) {
1755 if (gen_es6_ && is_subclass_service) {
1756 indent(f_service_) << "super(output, pClass);" << endl;
1757 }
1758 indent(f_service_) << "this.output = output;" << endl;
1759 indent(f_service_) << "this.pClass = pClass;" << endl;
1760 indent(f_service_) << "this._seqid = 0;" << endl;
1761 indent(f_service_) << "this._reqs = {};" << endl;
1762 if (gen_ts_) {
1763 if(!is_subclass_service) {
1764 f_service_ts_ << ts_indent() << "private output: thrift.TTransport;" << endl
1765 << ts_indent() << "private pClass: thrift.TProtocol;" << endl
1766 << ts_indent() << "private _seqid: number;" << endl
1767 << endl;
1768 }
1769
1770 f_service_ts_ << ts_indent() << "constructor(output: thrift.TTransport, pClass: { new(trans: thrift.TTransport): thrift.TProtocol });"
1771 << endl;
1772 }
1773 } else {
1774 indent(f_service_) << "this.input = input;" << endl;
1775 indent(f_service_) << "this.output = (!output) ? input : output;" << endl;
1776 indent(f_service_) << "this.seqid = 0;" << endl;
1777 if (gen_ts_) {
1778 f_service_ts_ << ts_indent() << "input: Thrift.TJSONProtocol;" << endl << ts_indent()
1779 << "output: Thrift.TJSONProtocol;" << endl << ts_indent() << "seqid: number;"
1780 << endl << endl << ts_indent()
1781 << "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);"
1782 << endl;
1783 }
1784 }
1785
1786 indent_down();
1787
1788 if (gen_es6_) {
1789 indent(f_service_) << "}" << endl;
1790 } else {
1791 indent(f_service_) << "};" << endl;
1792 if (is_subclass_service) {
1793 indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program())
1794 << service_name_ << "Client, "
1795 << js_namespace(tservice->get_extends()->get_program())
1796 << tservice->get_extends()->get_name() << "Client);" << endl;
1797 } else {
1798 // init prototype
1799 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
1800 << "Client.prototype = {};" << endl;
1801 }
1802 }
1803
1804 // utils for multiplexed services
1805 if (gen_node_) {
1806 if (gen_es6_) {
1807 indent(f_service_) << "seqid () { return this._seqid; }" << endl;
1808 indent(f_service_) << "new_seqid () { return this._seqid += 1; }" << endl;
1809 } else {
1810 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
1811 << "Client.prototype.seqid = function() { return this._seqid; };" << endl
1812 << js_namespace(tservice->get_program()) << service_name_
1813 << "Client.prototype.new_seqid = function() { return this._seqid += 1; };"
1814 << endl;
1815 }
1816 }
1817
1818 // Generate client method implementations
1819 vector<t_function*> functions = tservice->get_functions();
1820 vector<t_function*>::const_iterator f_iter;
1821 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1822 t_struct* arg_struct = (*f_iter)->get_arglist();
1823 const vector<t_field*>& fields = arg_struct->get_members();
1824 vector<t_field*>::const_iterator fld_iter;
1825 string funname = (*f_iter)->get_name();
1826 string arglist = argument_list(arg_struct);
1827
1828 // Open function
1829 f_service_ << endl;
1830 if (gen_es6_) {
1831 indent(f_service_) << funname << " (" << arglist << ") {" << endl;
1832 } else {
1833 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype."
1834 << function_signature(*f_iter, "", !gen_es6_) << " {" << endl;
1835 }
1836
1837 indent_up();
1838
1839 if (gen_ts_) {
1840 // function definition without callback
1841 f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false) << endl;
1842 if (!gen_es6_) {
1843 // overload with callback
1844 f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl;
1845 } else {
1846 // overload with callback
1847 f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl;
1848 }
1849 }
1850
1851 if (gen_es6_ && gen_node_) {
1852 indent(f_service_) << "this._seqid = this.new_seqid();" << endl;
1853 indent(f_service_) << js_const_type_ << "self = this;" << endl << indent()
1854 << "return new Promise((resolve, reject) => {" << endl;
1855 indent_up();
1856 indent(f_service_) << "self._reqs[self.seqid()] = (error, result) => {" << endl;
1857 indent_up();
1858 indent(f_service_) << "return error ? reject(error) : resolve(result);" << endl;
1859 indent_down();
1860 indent(f_service_) << "};" << endl;
1861 indent(f_service_) << "self.send_" << funname << "(" << arglist << ");" << endl;
1862 indent_down();
1863 indent(f_service_) << "});" << endl;
1864 } else if (gen_node_) { // Node.js output ./gen-nodejs
1865 f_service_ << indent() << "this._seqid = this.new_seqid();" << endl << indent()
1866 << "if (callback === undefined) {" << endl;
1867 indent_up();
1868 f_service_ << indent() << js_const_type_ << "_defer = Q.defer();" << endl << indent()
1869 << "this._reqs[this.seqid()] = function(error, result) {" << endl;
1870 indent_up();
1871 indent(f_service_) << "if (error) {" << endl;
1872 indent_up();
1873 indent(f_service_) << "_defer.reject(error);" << endl;
1874 indent_down();
1875 indent(f_service_) << "} else {" << endl;
1876 indent_up();
1877 indent(f_service_) << "_defer.resolve(result);" << endl;
1878 indent_down();
1879 indent(f_service_) << "}" << endl;
1880 indent_down();
1881 indent(f_service_) << "};" << endl;
1882 f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl
1883 << indent() << "return _defer.promise;" << endl;
1884 indent_down();
1885 indent(f_service_) << "} else {" << endl;
1886 indent_up();
1887 f_service_ << indent() << "this._reqs[this.seqid()] = callback;" << endl << indent()
1888 << "this.send_" << funname << "(" << arglist << ");" << endl;
1889 indent_down();
1890 indent(f_service_) << "}" << endl;
1891 } else if (gen_es6_) {
1892 f_service_ << indent() << js_const_type_ << "self = this;" << endl << indent()
1893 << "return new Promise((resolve, reject) => {" << endl;
1894 indent_up();
1895 f_service_ << indent() << "self.send_" << funname << "(" << arglist
1896 << (arglist.empty() ? "" : ", ") << "(error, result) => {" << endl;
1897 indent_up();
1898 indent(f_service_) << "return error ? reject(error) : resolve(result);" << endl;
1899 indent_down();
1900 f_service_ << indent() << "});" << endl;
1901 indent_down();
1902 f_service_ << indent() << "});" << endl;
1903
1904 } else if (gen_jquery_) { // jQuery output ./gen-js
1905 f_service_ << indent() << "if (callback === undefined) {" << endl;
1906 indent_up();
1907 f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl;
1908 if (!(*f_iter)->is_oneway()) {
1909 f_service_ << indent();
1910 if (!(*f_iter)->get_returntype()->is_void()) {
1911 f_service_ << "return ";
1912 }
1913 f_service_ << "this.recv_" << funname << "();" << endl;
1914 }
1915 indent_down();
1916 f_service_ << indent() << "} else {" << endl;
1917 indent_up();
1918 f_service_ << indent() << js_const_type_ << "postData = this.send_" << funname << "(" << arglist
1919 << (arglist.empty() ? "" : ", ") << "true);" << endl;
1920 f_service_ << indent() << "return this.output.getTransport()" << endl;
1921 indent_up();
1922 f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname
1923 << ");" << endl;
1924 indent_down();
1925 indent_down();
1926 f_service_ << indent() << "}" << endl;
1927 } else { // Standard JavaScript ./gen-js
1928 f_service_ << indent() << "this.send_" << funname << "(" << arglist
1929 << (arglist.empty() ? "" : ", ") << "callback); " << endl;
1930 if (!(*f_iter)->is_oneway()) {
1931 f_service_ << indent() << "if (!callback) {" << endl;
1932 f_service_ << indent();
1933 if (!(*f_iter)->get_returntype()->is_void()) {
1934 f_service_ << " return ";
1935 }
1936 f_service_ << "this.recv_" << funname << "();" << endl;
1937 f_service_ << indent() << "}" << endl;
1938 }
1939 }
1940
1941 indent_down();
1942
1943 if (gen_es6_) {
1944 indent(f_service_) << "}" << endl << endl;
1945 } else {
1946 indent(f_service_) << "};" << endl << endl;
1947 }
1948
1949 // Send function
1950 if (gen_es6_) {
1951 if (gen_node_) {
1952 indent(f_service_) << "send_" << funname << " (" << arglist << ") {" << endl;
1953 } else {
1954 // ES6 js still uses callbacks here. Should refactor this to promise style later..
1955 indent(f_service_) << "send_" << funname << " (" << argument_list(arg_struct, true) << ") {" << endl;
1956 }
1957 } else {
1958 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype.send_"
1959 << function_signature(*f_iter, "", !gen_node_) << " {" << endl;
1960 }
1961
1962 indent_up();
1963
1964 std::string outputVar;
1965 if (gen_node_) {
1966 f_service_ << indent() << js_const_type_ << "output = new this.pClass(this.output);" << endl;
1967 outputVar = "output";
1968 } else {
1969 outputVar = "this.output";
1970 }
1971
1972 std::string argsname = js_namespace(program_) + service_name_ + "_" + (*f_iter)->get_name()
1973 + "_args";
1974
1975 std::string messageType = (*f_iter)->is_oneway() ? "Thrift.MessageType.ONEWAY"
1976 : "Thrift.MessageType.CALL";
1977 // Build args
1978 if (fields.size() > 0){
1979 // It is possible that a method argument is named "params", we need to ensure the locally
1980 // generated identifier "params" is uniquely named
1981 std::string params_identifier = this->next_identifier_name(fields, "params");
1982 f_service_ << indent() << js_const_type_ << params_identifier << " = {" << endl;
1983 indent_up();
1984 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1985 indent(f_service_) << (*fld_iter)->get_name() << ": " << (*fld_iter)->get_name();
1986 if (fld_iter != fields.end()-1) {
1987 f_service_ << "," << endl;
1988 } else {
1989 f_service_ << endl;
1990 }
1991 }
1992 indent_down();
1993 indent(f_service_) << "};" << endl;
1994
1995 // NOTE: "args" is a reserved keyword, so no need to generate a unique identifier
1996 indent(f_service_) << js_const_type_ << "args = new " << argsname << "(" << params_identifier << ");" << endl;
1997 } else {
1998 indent(f_service_) << js_const_type_ << "args = new " << argsname << "();" << endl;
1999 }
2000
2001
2002 // Serialize the request header within try/catch
2003 indent(f_service_) << "try {" << endl;
2004 indent_up();
2005
2006 if (gen_node_) {
2007 f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name()
2008 << "', " << messageType << ", this.seqid());" << endl;
2009 } else {
2010 f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name()
2011 << "', " << messageType << ", this.seqid);" << endl;
2012 }
2013
2014
2015 // Write to the stream
2016 f_service_ << indent() << "args.write(" << outputVar << ");" << endl << indent() << outputVar
2017 << ".writeMessageEnd();" << endl;
2018
2019 if (gen_node_) {
2020 if((*f_iter)->is_oneway()) {
2021 f_service_ << indent() << "this.output.flush();" << endl;
2022 f_service_ << indent() << js_const_type_ << "callback = this._reqs[this.seqid()] || function() {};" << endl;
2023 f_service_ << indent() << "delete this._reqs[this.seqid()];" << endl;
2024 f_service_ << indent() << "callback(null);" << endl;
2025 } else {
2026 f_service_ << indent() << "return this.output.flush();" << endl;
2027 }
2028 } else {
2029 if (gen_jquery_) {
2030 f_service_ << indent() << "return this.output.getTransport().flush(callback);" << endl;
2031 } else if (gen_es6_) {
2032 f_service_ << indent() << js_const_type_ << "self = this;" << endl;
2033 if((*f_iter)->is_oneway()) {
2034 f_service_ << indent() << "this.output.getTransport().flush(true, null);" << endl;
2035 f_service_ << indent() << "callback();" << endl;
2036 } else {
2037 f_service_ << indent() << "this.output.getTransport().flush(true, () => {" << endl;
2038 indent_up();
2039 f_service_ << indent() << js_let_type_ << "error = null, result = null;" << endl;
2040 f_service_ << indent() << "try {" << endl;
2041 f_service_ << indent() << " result = self.recv_" << funname << "();" << endl;
2042 f_service_ << indent() << "} catch (e) {" << endl;
2043 f_service_ << indent() << " error = e;" << endl;
2044 f_service_ << indent() << "}" << endl;
2045 f_service_ << indent() << "callback(error, result);" << endl;
2046 indent_down();
2047 f_service_ << indent() << "});" << endl;
2048 }
2049 } else {
2050 f_service_ << indent() << "if (callback) {" << endl;
2051 indent_up();
2052 if((*f_iter)->is_oneway()) {
2053 f_service_ << indent() << "this.output.getTransport().flush(true, null);" << endl;
2054 f_service_ << indent() << "callback();" << endl;
2055 } else {
2056 f_service_ << indent() << js_const_type_ << "self = this;" << endl;
2057 f_service_ << indent() << "this.output.getTransport().flush(true, function() {" << endl;
2058 indent_up();
2059 f_service_ << indent() << js_let_type_ << "result = null;" << endl;
2060 f_service_ << indent() << "try {" << endl;
2061 f_service_ << indent() << " result = self.recv_" << funname << "();" << endl;
2062 f_service_ << indent() << "} catch (e) {" << endl;
2063 f_service_ << indent() << " result = e;" << endl;
2064 f_service_ << indent() << "}" << endl;
2065 f_service_ << indent() << "callback(result);" << endl;
2066 indent_down();
2067 f_service_ << indent() << "});" << endl;
2068 }
2069 indent_down();
2070 f_service_ << indent() << "} else {" << endl;
2071 f_service_ << indent() << " return this.output.getTransport().flush();" << endl;
2072 f_service_ << indent() << "}" << endl;
2073 }
2074 }
2075
2076 indent_down();
2077 f_service_ << indent() << "}" << endl;
2078
2079 // Reset the transport and delete registered callback if there was a serialization error
2080 f_service_ << indent() << "catch (e) {" << endl;
2081 indent_up();
2082 if (gen_node_) {
2083 f_service_ << indent() << "delete this._reqs[this.seqid()];" << endl;
2084 f_service_ << indent() << "if (typeof " << outputVar << ".reset === 'function') {" << endl;
2085 f_service_ << indent() << " " << outputVar << ".reset();" << endl;
2086 f_service_ << indent() << "}" << endl;
2087 } else {
2088 f_service_ << indent() << "if (typeof " << outputVar << ".getTransport().reset === 'function') {" << endl;
2089 f_service_ << indent() << " " << outputVar << ".getTransport().reset();" << endl;
2090 f_service_ << indent() << "}" << endl;
2091 }
2092 f_service_ << indent() << "throw e;" << endl;
2093 indent_down();
2094 f_service_ << indent() << "}" << endl;
2095
2096 indent_down();
2097
2098 // Close send function
2099 if (gen_es6_) {
2100 indent(f_service_) << "}" << endl;
2101 } else {
2102 indent(f_service_) << "};" << endl;
2103 }
2104
2105 // Receive function
2106 if (!(*f_iter)->is_oneway()) {
2107 std::string resultname = js_namespace(tservice->get_program()) + service_name_ + "_"
2108 + (*f_iter)->get_name() + "_result";
2109
2110 f_service_ << endl;
2111 // Open receive function
2112 if (gen_node_) {
2113 if (gen_es6_) {
2114 indent(f_service_) << "recv_" << (*f_iter)->get_name() << " (input, mtype, rseqid) {" << endl;
2115 } else {
2116 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
2117 << "Client.prototype.recv_" << (*f_iter)->get_name()
2118 << " = function(input,mtype,rseqid) {" << endl;
2119 }
2120 } else {
2121 if (gen_es6_) {
2122 indent(f_service_) << "recv_" << (*f_iter)->get_name() << " () {" << endl;
2123 } else {
2124 t_struct noargs(program_);
2125
2126 t_function recv_function((*f_iter)->get_returntype(),
2127 string("recv_") + (*f_iter)->get_name(),
2128 &noargs);
2129 indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
2130 << "Client.prototype." << function_signature(&recv_function) << " {" << endl;
2131 }
2132 }
2133
2134 indent_up();
2135
2136 std::string inputVar;
2137 if (gen_node_) {
2138 inputVar = "input";
2139 } else {
2140 inputVar = "this.input";
2141 }
2142
2143 if (gen_node_) {
2144 f_service_ << indent() << js_const_type_ << "callback = this._reqs[rseqid] || function() {};" << endl
2145 << indent() << "delete this._reqs[rseqid];" << endl;
2146 } else {
2147 f_service_ << indent() << js_const_type_ << "ret = this.input.readMessageBegin();" << endl
2148 << indent() << js_const_type_ << "mtype = ret.mtype;" << endl;
2149 }
2150
2151 f_service_ << indent() << "if (mtype == Thrift.MessageType.EXCEPTION) {" << endl;
2152
2153 indent_up();
2154 f_service_ << indent() << js_const_type_ << "x = new Thrift.TApplicationException();" << endl
2155 << indent() << "x.read(" << inputVar << ");" << endl
2156 << indent() << inputVar << ".readMessageEnd();" << endl
2157 << indent() << render_recv_throw("x") << endl;
2158 scope_down(f_service_);
2159
2160 f_service_ << indent() << js_const_type_ << "result = new " << resultname << "();" << endl << indent()
2161 << "result.read(" << inputVar << ");" << endl;
2162
2163 f_service_ << indent() << inputVar << ".readMessageEnd();" << endl << endl;
2164
2165 t_struct* xs = (*f_iter)->get_xceptions();
2166 const std::vector<t_field*>& xceptions = xs->get_members();
2167 vector<t_field*>::const_iterator x_iter;
2168 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
2169 f_service_ << indent() << "if (null !== result." << (*x_iter)->get_name() << ") {" << endl
2170 << indent() << " " << render_recv_throw("result." + (*x_iter)->get_name())
2171 << endl << indent() << "}" << endl;
2172 }
2173
2174 // Careful, only return result if not a void function
2175 if (!(*f_iter)->get_returntype()->is_void()) {
2176 f_service_ << indent() << "if (null !== result.success) {" << endl << indent() << " "
2177 << render_recv_return("result.success") << endl << indent() << "}" << endl;
2178 f_service_ << indent()
2179 << render_recv_throw("'" + (*f_iter)->get_name() + " failed: unknown result'")
2180 << endl;
2181 } else {
2182 if (gen_node_) {
2183 indent(f_service_) << "callback(null);" << endl;
2184 } else {
2185 indent(f_service_) << "return;" << endl;
2186 }
2187 }
2188
2189 // Close receive function
2190 indent_down();
2191 if (gen_es6_) {
2192 indent(f_service_) << "}" << endl;
2193 } else {
2194 indent(f_service_) << "};" << endl;
2195 }
2196 }
2197 }
2198
2199 // Finish class definitions
2200 if (gen_ts_) {
2201 f_service_ts_ << ts_indent() << "}" << endl;
2202 }
2203 if (gen_es6_) {
2204 indent_down();
2205 f_service_ << "};" << endl;
2206 }
2207 }
2208
render_recv_throw(std::string var)2209 std::string t_js_generator::render_recv_throw(std::string var) {
2210 if (gen_node_) {
2211 return "return callback(" + var + ");";
2212 } else {
2213 return "throw " + var + ";";
2214 }
2215 }
2216
render_recv_return(std::string var)2217 std::string t_js_generator::render_recv_return(std::string var) {
2218 if (gen_node_) {
2219 return "return callback(null, " + var + ");";
2220 } else {
2221 return "return " + var + ";";
2222 }
2223 }
2224
2225 /**
2226 * Deserializes a field of any type.
2227 */
generate_deserialize_field(ostream & out,t_field * tfield,string prefix,bool inclass)2228 void t_js_generator::generate_deserialize_field(ostream& out,
2229 t_field* tfield,
2230 string prefix,
2231 bool inclass) {
2232 (void)inclass;
2233 t_type* type = get_true_type(tfield->get_type());
2234
2235 if (type->is_void()) {
2236 throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
2237 }
2238
2239 string name = prefix + tfield->get_name();
2240
2241 if (type->is_struct() || type->is_xception()) {
2242 generate_deserialize_struct(out, (t_struct*)type, name);
2243 } else if (type->is_container()) {
2244 generate_deserialize_container(out, type, name);
2245 } else if (type->is_base_type() || type->is_enum()) {
2246 indent(out) << name << " = input.";
2247
2248 if (type->is_base_type()) {
2249 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2250 switch (tbase) {
2251 case t_base_type::TYPE_VOID:
2252 throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
2253 break;
2254 case t_base_type::TYPE_STRING:
2255 out << (type->is_binary() ? "readBinary()" : "readString()");
2256 break;
2257 case t_base_type::TYPE_BOOL:
2258 out << "readBool()";
2259 break;
2260 case t_base_type::TYPE_I8:
2261 out << "readByte()";
2262 break;
2263 case t_base_type::TYPE_I16:
2264 out << "readI16()";
2265 break;
2266 case t_base_type::TYPE_I32:
2267 out << "readI32()";
2268 break;
2269 case t_base_type::TYPE_I64:
2270 out << "readI64()";
2271 break;
2272 case t_base_type::TYPE_DOUBLE:
2273 out << "readDouble()";
2274 break;
2275 default:
2276 throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
2277 }
2278 } else if (type->is_enum()) {
2279 out << "readI32()";
2280 }
2281
2282 if (!gen_node_) {
2283 out << ".value";
2284 }
2285
2286 out << ";" << endl;
2287 } else {
2288 printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
2289 tfield->get_name().c_str(),
2290 type->get_name().c_str());
2291 }
2292 }
2293
2294 /**
2295 * Generates an unserializer for a variable. This makes two key assumptions,
2296 * first that there is a const char* variable named data that points to the
2297 * buffer for deserialization, and that there is a variable protocol which
2298 * is a reference to a TProtocol serialization object.
2299 */
generate_deserialize_struct(ostream & out,t_struct * tstruct,string prefix)2300 void t_js_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
2301 out << indent() << prefix << " = new " << js_type_namespace(tstruct->get_program())
2302 << tstruct->get_name() << "();" << endl << indent() << prefix << ".read(input);" << endl;
2303 }
2304
generate_deserialize_container(ostream & out,t_type * ttype,string prefix)2305 void t_js_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
2306 string size = tmp("_size");
2307 string rtmp3 = tmp("_rtmp3");
2308
2309 t_field fsize(g_type_i32, size);
2310
2311 // Declare variables, read header
2312 if (ttype->is_map()) {
2313 out << indent() << prefix << " = {};" << endl;
2314
2315 out << indent() << js_const_type_ << rtmp3 << " = input.readMapBegin();" << endl;
2316 out << indent() << js_const_type_ << size << " = " << rtmp3 << ".size || 0;" << endl;
2317
2318 } else if (ttype->is_set()) {
2319
2320 out << indent() << prefix << " = [];" << endl
2321 << indent() << js_const_type_ << rtmp3 << " = input.readSetBegin();" << endl
2322 << indent() << js_const_type_ << size << " = " << rtmp3 << ".size || 0;" << endl;
2323
2324 } else if (ttype->is_list()) {
2325
2326 out << indent() << prefix << " = [];" << endl
2327 << indent() << js_const_type_ << rtmp3 << " = input.readListBegin();" << endl
2328 << indent() << js_const_type_ << size << " = " << rtmp3 << ".size || 0;" << endl;
2329 }
2330
2331 // For loop iterates over elements
2332 string i = tmp("_i");
2333 indent(out) << "for (" << js_let_type_ << i << " = 0; " << i << " < " << size << "; ++" << i << ") {" << endl;
2334
2335 indent_up();
2336
2337 if (ttype->is_map()) {
2338 if (!gen_node_) {
2339 out << indent() << "if (" << i << " > 0 ) {" << endl << indent()
2340 << " if (input.rstack.length > input.rpos[input.rpos.length -1] + 1) {" << endl
2341 << indent() << " input.rstack.pop();" << endl << indent() << " }" << endl << indent()
2342 << "}" << endl;
2343 }
2344
2345 generate_deserialize_map_element(out, (t_map*)ttype, prefix);
2346 } else if (ttype->is_set()) {
2347 generate_deserialize_set_element(out, (t_set*)ttype, prefix);
2348 } else if (ttype->is_list()) {
2349 generate_deserialize_list_element(out, (t_list*)ttype, prefix);
2350 }
2351
2352 scope_down(out);
2353
2354 // Read container end
2355 if (ttype->is_map()) {
2356 indent(out) << "input.readMapEnd();" << endl;
2357 } else if (ttype->is_set()) {
2358 indent(out) << "input.readSetEnd();" << endl;
2359 } else if (ttype->is_list()) {
2360 indent(out) << "input.readListEnd();" << endl;
2361 }
2362 }
2363
2364 /**
2365 * Generates code to deserialize a map
2366 */
generate_deserialize_map_element(ostream & out,t_map * tmap,string prefix)2367 void t_js_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
2368 string key = tmp("key");
2369 string val = tmp("val");
2370 t_field fkey(tmap->get_key_type(), key);
2371 t_field fval(tmap->get_val_type(), val);
2372
2373 indent(out) << declare_field(&fkey, false, false) << ";" << endl;
2374 indent(out) << declare_field(&fval, false, false) << ";" << endl;
2375
2376 generate_deserialize_field(out, &fkey);
2377 generate_deserialize_field(out, &fval);
2378
2379 indent(out) << prefix << "[" << key << "] = " << val << ";" << endl;
2380 }
2381
generate_deserialize_set_element(ostream & out,t_set * tset,string prefix)2382 void t_js_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
2383 string elem = tmp("elem");
2384 t_field felem(tset->get_elem_type(), elem);
2385
2386 indent(out) << js_let_type_ << elem << " = null;" << endl;
2387
2388 generate_deserialize_field(out, &felem);
2389
2390 indent(out) << prefix << ".push(" << elem << ");" << endl;
2391 }
2392
generate_deserialize_list_element(ostream & out,t_list * tlist,string prefix)2393 void t_js_generator::generate_deserialize_list_element(ostream& out,
2394 t_list* tlist,
2395 string prefix) {
2396 string elem = tmp("elem");
2397 t_field felem(tlist->get_elem_type(), elem);
2398
2399 indent(out) << js_let_type_ << elem << " = null;" << endl;
2400
2401 generate_deserialize_field(out, &felem);
2402
2403 indent(out) << prefix << ".push(" << elem << ");" << endl;
2404 }
2405
2406 /**
2407 * Serializes a field of any type.
2408 *
2409 * @param tfield The field to serialize
2410 * @param prefix Name to prepend to field name
2411 */
generate_serialize_field(ostream & out,t_field * tfield,string prefix)2412 void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
2413 t_type* type = get_true_type(tfield->get_type());
2414
2415 // Do nothing for void types
2416 if (type->is_void()) {
2417 throw std::runtime_error("CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
2418 }
2419
2420 if (type->is_struct() || type->is_xception()) {
2421 generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
2422 } else if (type->is_container()) {
2423 generate_serialize_container(out, type, prefix + tfield->get_name());
2424 } else if (type->is_base_type() || type->is_enum()) {
2425
2426 string name = tfield->get_name();
2427
2428 // Hack for when prefix is defined (always a hash ref)
2429 if (!prefix.empty())
2430 name = prefix + tfield->get_name();
2431
2432 indent(out) << "output.";
2433
2434 if (type->is_base_type()) {
2435 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2436 switch (tbase) {
2437 case t_base_type::TYPE_VOID:
2438 throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
2439 break;
2440 case t_base_type::TYPE_STRING:
2441 out << (type->is_binary() ? "writeBinary(" : "writeString(") << name << ")";
2442 break;
2443 case t_base_type::TYPE_BOOL:
2444 out << "writeBool(" << name << ")";
2445 break;
2446 case t_base_type::TYPE_I8:
2447 out << "writeByte(" << name << ")";
2448 break;
2449 case t_base_type::TYPE_I16:
2450 out << "writeI16(" << name << ")";
2451 break;
2452 case t_base_type::TYPE_I32:
2453 out << "writeI32(" << name << ")";
2454 break;
2455 case t_base_type::TYPE_I64:
2456 out << "writeI64(" << name << ")";
2457 break;
2458 case t_base_type::TYPE_DOUBLE:
2459 out << "writeDouble(" << name << ")";
2460 break;
2461 default:
2462 throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
2463 }
2464 } else if (type->is_enum()) {
2465 out << "writeI32(" << name << ")";
2466 }
2467 out << ";" << endl;
2468
2469 } else {
2470 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
2471 prefix.c_str(),
2472 tfield->get_name().c_str(),
2473 type->get_name().c_str());
2474 }
2475 }
2476
2477 /**
2478 * Serializes all the members of a struct.
2479 *
2480 * @param tstruct The struct to serialize
2481 * @param prefix String prefix to attach to all fields
2482 */
generate_serialize_struct(ostream & out,t_struct * tstruct,string prefix)2483 void t_js_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
2484 (void)tstruct;
2485 indent(out) << prefix << ".write(output);" << endl;
2486 }
2487
2488 /**
2489 * Writes out a container
2490 */
generate_serialize_container(ostream & out,t_type * ttype,string prefix)2491 void t_js_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
2492 if (ttype->is_map()) {
2493 indent(out) << "output.writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
2494 << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
2495 << "Thrift.objectLength(" << prefix << "));" << endl;
2496 } else if (ttype->is_set()) {
2497 indent(out) << "output.writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
2498 << prefix << ".length);" << endl;
2499
2500 } else if (ttype->is_list()) {
2501
2502 indent(out) << "output.writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
2503 << ", " << prefix << ".length);" << endl;
2504 }
2505
2506 if (ttype->is_map()) {
2507 string kiter = tmp("kiter");
2508 string viter = tmp("viter");
2509 indent(out) << "for (" << js_let_type_ << kiter << " in " << prefix << ") {" << endl;
2510 indent_up();
2511 indent(out) << "if (" << prefix << ".hasOwnProperty(" << kiter << ")) {" << endl;
2512 indent_up();
2513 indent(out) << js_let_type_ << viter << " = " << prefix << "[" << kiter << "];" << endl;
2514 generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
2515 scope_down(out);
2516 scope_down(out);
2517
2518 } else if (ttype->is_set()) {
2519 string iter = tmp("iter");
2520 indent(out) << "for (" << js_let_type_ << iter << " in " << prefix << ") {" << endl;
2521 indent_up();
2522 indent(out) << "if (" << prefix << ".hasOwnProperty(" << iter << ")) {" << endl;
2523 indent_up();
2524 indent(out) << iter << " = " << prefix << "[" << iter << "];" << endl;
2525 generate_serialize_set_element(out, (t_set*)ttype, iter);
2526 scope_down(out);
2527 scope_down(out);
2528
2529 } else if (ttype->is_list()) {
2530 string iter = tmp("iter");
2531 indent(out) << "for (" << js_let_type_ << iter << " in " << prefix << ") {" << endl;
2532 indent_up();
2533 indent(out) << "if (" << prefix << ".hasOwnProperty(" << iter << ")) {" << endl;
2534 indent_up();
2535 indent(out) << iter << " = " << prefix << "[" << iter << "];" << endl;
2536 generate_serialize_list_element(out, (t_list*)ttype, iter);
2537 scope_down(out);
2538 scope_down(out);
2539 }
2540
2541 if (ttype->is_map()) {
2542 indent(out) << "output.writeMapEnd();" << endl;
2543 } else if (ttype->is_set()) {
2544 indent(out) << "output.writeSetEnd();" << endl;
2545 } else if (ttype->is_list()) {
2546 indent(out) << "output.writeListEnd();" << endl;
2547 }
2548 }
2549
2550 /**
2551 * Serializes the members of a map.
2552 *
2553 */
generate_serialize_map_element(ostream & out,t_map * tmap,string kiter,string viter)2554 void t_js_generator::generate_serialize_map_element(ostream& out,
2555 t_map* tmap,
2556 string kiter,
2557 string viter) {
2558 t_field kfield(tmap->get_key_type(), kiter);
2559 generate_serialize_field(out, &kfield);
2560
2561 t_field vfield(tmap->get_val_type(), viter);
2562 generate_serialize_field(out, &vfield);
2563 }
2564
2565 /**
2566 * Serializes the members of a set.
2567 */
generate_serialize_set_element(ostream & out,t_set * tset,string iter)2568 void t_js_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
2569 t_field efield(tset->get_elem_type(), iter);
2570 generate_serialize_field(out, &efield);
2571 }
2572
2573 /**
2574 * Serializes the members of a list.
2575 */
generate_serialize_list_element(ostream & out,t_list * tlist,string iter)2576 void t_js_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
2577 t_field efield(tlist->get_elem_type(), iter);
2578 generate_serialize_field(out, &efield);
2579 }
2580
2581 /**
2582 * Declares a field, which may include initialization as necessary.
2583 *
2584 * @param ttype The type
2585 */
declare_field(t_field * tfield,bool init,bool obj)2586 string t_js_generator::declare_field(t_field* tfield, bool init, bool obj) {
2587 string result = "this." + tfield->get_name();
2588
2589 if (!obj) {
2590 result = js_let_type_ + tfield->get_name();
2591 }
2592
2593 if (init) {
2594 t_type* type = get_true_type(tfield->get_type());
2595 if (type->is_base_type()) {
2596 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2597 switch (tbase) {
2598 case t_base_type::TYPE_VOID:
2599 break;
2600 case t_base_type::TYPE_STRING:
2601 case t_base_type::TYPE_BOOL:
2602 case t_base_type::TYPE_I8:
2603 case t_base_type::TYPE_I16:
2604 case t_base_type::TYPE_I32:
2605 case t_base_type::TYPE_I64:
2606 case t_base_type::TYPE_DOUBLE:
2607 result += " = null";
2608 break;
2609 default:
2610 throw std::runtime_error("compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase));
2611 }
2612 } else if (type->is_enum()) {
2613 result += " = null";
2614 } else if (type->is_map()) {
2615 result += " = null";
2616 } else if (type->is_container()) {
2617 result += " = null";
2618 } else if (type->is_struct() || type->is_xception()) {
2619 if (obj) {
2620 result += " = new " + js_type_namespace(type->get_program()) + type->get_name() + "()";
2621 } else {
2622 result += " = null";
2623 }
2624 }
2625 } else {
2626 result += " = null";
2627 }
2628 return result;
2629 }
2630
2631 /**
2632 * Renders a function signature of the form 'type name(args)'
2633 *
2634 * @param tfunction Function definition
2635 * @return String of rendered function definition
2636 */
function_signature(t_function * tfunction,string prefix,bool include_callback)2637 string t_js_generator::function_signature(t_function* tfunction,
2638 string prefix,
2639 bool include_callback) {
2640
2641 string str;
2642
2643 str = prefix + tfunction->get_name() + " = function(";
2644
2645 str += argument_list(tfunction->get_arglist(), include_callback);
2646
2647 str += ")";
2648 return str;
2649 }
2650
2651 /**
2652 * Renders a field list
2653 */
argument_list(t_struct * tstruct,bool include_callback)2654 string t_js_generator::argument_list(t_struct* tstruct, bool include_callback) {
2655 string result = "";
2656
2657 const vector<t_field*>& fields = tstruct->get_members();
2658 vector<t_field*>::const_iterator f_iter;
2659 bool first = true;
2660 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2661 if (first) {
2662 first = false;
2663 } else {
2664 result += ", ";
2665 }
2666 result += (*f_iter)->get_name();
2667 }
2668
2669 if (include_callback) {
2670 if (!fields.empty()) {
2671 result += ", ";
2672 }
2673 result += "callback";
2674 }
2675
2676 return result;
2677 }
2678
2679 /**
2680 * Converts the parse type to a C++ enum string for the given type.
2681 */
type_to_enum(t_type * type)2682 string t_js_generator::type_to_enum(t_type* type) {
2683 type = get_true_type(type);
2684
2685 if (type->is_base_type()) {
2686 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2687 switch (tbase) {
2688 case t_base_type::TYPE_VOID:
2689 throw std::runtime_error("NO T_VOID CONSTRUCT");
2690 case t_base_type::TYPE_STRING:
2691 return "Thrift.Type.STRING";
2692 case t_base_type::TYPE_BOOL:
2693 return "Thrift.Type.BOOL";
2694 case t_base_type::TYPE_I8:
2695 return "Thrift.Type.BYTE";
2696 case t_base_type::TYPE_I16:
2697 return "Thrift.Type.I16";
2698 case t_base_type::TYPE_I32:
2699 return "Thrift.Type.I32";
2700 case t_base_type::TYPE_I64:
2701 return "Thrift.Type.I64";
2702 case t_base_type::TYPE_DOUBLE:
2703 return "Thrift.Type.DOUBLE";
2704 default:
2705 throw "compiler error: unhandled type";
2706 }
2707 } else if (type->is_enum()) {
2708 return "Thrift.Type.I32";
2709 } else if (type->is_struct() || type->is_xception()) {
2710 return "Thrift.Type.STRUCT";
2711 } else if (type->is_map()) {
2712 return "Thrift.Type.MAP";
2713 } else if (type->is_set()) {
2714 return "Thrift.Type.SET";
2715 } else if (type->is_list()) {
2716 return "Thrift.Type.LIST";
2717 }
2718
2719 throw std::runtime_error("INVALID TYPE IN type_to_enum: " + type->get_name());
2720 }
2721
2722 /**
2723 * Converts a t_type to a TypeScript type (string).
2724 * @param t_type Type to convert to TypeScript
2725 * @return String TypeScript type
2726 */
ts_get_type(t_type * type)2727 string t_js_generator::ts_get_type(t_type* type) {
2728 std::string ts_type;
2729
2730 type = get_true_type(type);
2731
2732 if (type->is_base_type()) {
2733 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2734 switch (tbase) {
2735 case t_base_type::TYPE_STRING:
2736 ts_type = type->is_binary() ? "Buffer" : "string";
2737 break;
2738 case t_base_type::TYPE_BOOL:
2739 ts_type = "boolean";
2740 break;
2741 case t_base_type::TYPE_I8:
2742 ts_type = "any";
2743 break;
2744 case t_base_type::TYPE_I16:
2745 case t_base_type::TYPE_I32:
2746 case t_base_type::TYPE_DOUBLE:
2747 ts_type = "number";
2748 break;
2749 case t_base_type::TYPE_I64:
2750 ts_type = "Int64";
2751 break;
2752 case t_base_type::TYPE_VOID:
2753 ts_type = "void";
2754 break;
2755 default:
2756 throw "compiler error: unhandled type";
2757 }
2758 } else if (type->is_enum() || type->is_struct() || type->is_xception()) {
2759 std::string type_name;
2760
2761 if (type->get_program()) {
2762 type_name = js_namespace(type->get_program());
2763
2764 // If the type is not defined within the current program, we need to prefix it with the same name as
2765 // the generated "import" statement for the types containing program
2766 if(type->get_program() != program_) {
2767 auto prefix = include_2_import_name.find(type->get_program());
2768
2769 if(prefix != include_2_import_name.end()) {
2770 type_name.append(prefix->second);
2771 type_name.append(".");
2772 }
2773 }
2774 }
2775
2776 type_name.append(type->get_name());
2777 ts_type = type_name;
2778 } else if (type->is_list() || type->is_set()) {
2779 t_type* etype;
2780
2781 if (type->is_list()) {
2782 etype = ((t_list*)type)->get_elem_type();
2783 } else {
2784 etype = ((t_set*)type)->get_elem_type();
2785 }
2786
2787 ts_type = ts_get_type(etype) + "[]";
2788 } else if (type->is_map()) {
2789 string ktype = ts_get_type(((t_map*)type)->get_key_type());
2790 string vtype = ts_get_type(((t_map*)type)->get_val_type());
2791
2792
2793 if (ktype == "number" || ktype == "string" ) {
2794 ts_type = "{ [k: " + ktype + "]: " + vtype + "; }";
2795 } else if ((((t_map*)type)->get_key_type())->is_enum()) {
2796 // Not yet supported (enum map): https://github.com/Microsoft/TypeScript/pull/2652
2797 //ts_type = "{ [k: " + ktype + "]: " + vtype + "; }";
2798 ts_type = "{ [k: number /*" + ktype + "*/]: " + vtype + "; }";
2799 } else {
2800 ts_type = "any";
2801 }
2802 }
2803
2804 return ts_type;
2805 }
2806
2807 /**
2808 * Renders a TypeScript function signature of the form 'name(args: types): type;'
2809 *
2810 * @param t_function Function definition
2811 * @param bool in-/exclude the callback argument
2812 * @return String of rendered function definition
2813 */
ts_function_signature(t_function * tfunction,bool include_callback)2814 std::string t_js_generator::ts_function_signature(t_function* tfunction, bool include_callback) {
2815 string str;
2816 const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
2817 vector<t_field*>::const_iterator f_iter;
2818
2819 str = tfunction->get_name() + "(";
2820
2821 bool has_written_optional = false;
2822
2823 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2824 // Ensure that non optional parameters do not follow optional parameters
2825 // E.g. public foo(a: string, b?: string; c: string) is invalid, c must be optional, or b non-optional
2826 string original_optional = ts_get_req(*f_iter);
2827 string optional = has_written_optional ? "?" : original_optional;
2828 has_written_optional = has_written_optional || optional.size() > 0;
2829
2830 str += (*f_iter)->get_name() + optional + ": " + ts_get_type((*f_iter)->get_type());
2831
2832 if (f_iter + 1 != fields.end() || (include_callback && fields.size() > 0)) {
2833 str += ", ";
2834 }
2835 }
2836
2837 if (include_callback) {
2838 if (gen_node_) {
2839 t_struct* exceptions = tfunction->get_xceptions();
2840 string exception_types;
2841 if (exceptions) {
2842 const vector<t_field*>& members = exceptions->get_members();
2843 for (vector<t_field*>::const_iterator it = members.begin(); it != members.end(); ++it) {
2844 t_type* t = get_true_type((*it)->get_type());
2845 if (it == members.begin()) {
2846 exception_types = js_type_namespace(t->get_program()) + t->get_name();
2847 } else {
2848 exception_types += " | " + js_type_namespace(t->get_program()) + t->get_name();
2849 }
2850 }
2851 }
2852 if (exception_types == "") {
2853 str += "callback?: (error: void, response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): ";
2854 } else {
2855 str += "callback?: (error: " + exception_types + ", response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): ";
2856 }
2857 } else {
2858 str += "callback?: (data: " + ts_get_type(tfunction->get_returntype()) + ")=>void): ";
2859 }
2860
2861 if (gen_jquery_) {
2862 str += "JQueryPromise<" + ts_get_type(tfunction->get_returntype()) +">;";
2863 } else {
2864 str += "void;";
2865 }
2866 } else {
2867 if (gen_es6_) {
2868 str += "): Promise<" + ts_get_type(tfunction->get_returntype()) + ">;";
2869 }
2870 else {
2871 str += "): " + ts_get_type(tfunction->get_returntype()) + ";";
2872 }
2873 }
2874
2875 return str;
2876 }
2877
2878 /**
2879 * Takes a name and produces a valid NodeJS identifier from it
2880 *
2881 * @param name The name which shall become a valid NodeJS identifier
2882 * @return The modified name with the updated identifier
2883 */
make_valid_nodeJs_identifier(std::string const & name)2884 std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name) {
2885 std::string str = name;
2886 if (str.empty()) {
2887 return str;
2888 }
2889
2890 // tests rely on this
2891 assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
2892
2893 // if the first letter is a number, we add an additional underscore in front of it
2894 char c = str.at(0);
2895 if (('0' <= c) && (c <= '9')) {
2896 str = "_" + str;
2897 }
2898
2899 // following chars: letter, number or underscore
2900 for (size_t i = 0; i < str.size(); ++i) {
2901 c = str.at(i);
2902 if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
2903 && ('_' != c) && ('$' != c)) {
2904 str.replace(i, 1, "_");
2905 }
2906 }
2907
2908 return str;
2909 }
2910
parse_imports(t_program * program,const std::string & imports_string)2911 void t_js_generator::parse_imports(t_program* program, const std::string& imports_string) {
2912 if (program->get_recursive()) {
2913 throw std::invalid_argument("[-gen js:imports=] option is not usable in recursive code generation mode");
2914 }
2915 std::stringstream sstream(imports_string);
2916 std::string import;
2917 while (std::getline(sstream, import, ':')) {
2918 imports.emplace_back(import);
2919 }
2920 if (imports.empty()) {
2921 throw std::invalid_argument("invalid usage: [-gen js:imports=] requires at least one path "
2922 "(multiple paths are separated by ':')");
2923 }
2924 for (auto& import : imports) {
2925 // Strip trailing '/'
2926 if (!import.empty() && import[import.size() - 1] == '/') {
2927 import = import.substr(0, import.size() - 1);
2928 }
2929 if (import.empty()) {
2930 throw std::invalid_argument("empty paths are not allowed in imports");
2931 }
2932 std::ifstream episode_file;
2933 string line;
2934 const auto episode_file_path = import + "/" + episode_file_name;
2935 episode_file.open(episode_file_path);
2936 if (!episode_file) {
2937 throw std::runtime_error("failed to open the file '" + episode_file_path + "'");
2938 }
2939 while (std::getline(episode_file, line)) {
2940 const auto separator_position = line.find(':');
2941 if (separator_position == string::npos) {
2942 // could not find the separator in the line
2943 throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the line '" + line
2944 + "' does not have a key:value separator ':'");
2945 }
2946 const auto module_name = line.substr(0, separator_position);
2947 const auto import_path = line.substr(separator_position + 1);
2948 if (module_name.empty()) {
2949 throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the module name is empty");
2950 }
2951 if (import_path.empty()) {
2952 throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the import path is empty");
2953 }
2954 const auto module_import_path = import.substr(import.find_last_of('/') + 1) + "/" + import_path;
2955 const auto result = module_name_2_import_path.emplace(module_name, module_import_path);
2956 if (!result.second) {
2957 throw std::runtime_error("multiple providers of import path found for " + module_name
2958 + "\n\t" + module_import_path + "\n\t" + result.first->second);
2959 }
2960 }
2961 }
2962 }
parse_thrift_package_output_directory(const std::string & thrift_package_output_directory)2963 void t_js_generator::parse_thrift_package_output_directory(const std::string& thrift_package_output_directory) {
2964 thrift_package_output_directory_ = thrift_package_output_directory;
2965 // Strip trailing '/'
2966 if (!thrift_package_output_directory_.empty() && thrift_package_output_directory_[thrift_package_output_directory_.size() - 1] == '/') {
2967 thrift_package_output_directory_ = thrift_package_output_directory_.substr(0, thrift_package_output_directory_.size() - 1);
2968 }
2969 // Check that the thrift_package_output_directory is not empty after stripping
2970 if (thrift_package_output_directory_.empty()) {
2971 throw std::invalid_argument("the thrift_package_output_directory argument must not be empty");
2972 } else {
2973 gen_episode_file_ = true;
2974 }
2975 }
2976
2977 /**
2978 * Checks is the specified field name is contained in the specified field vector
2979 */
find_field(const std::vector<t_field * > & fields,const std::string & name)2980 bool t_js_generator::find_field(const std::vector<t_field*>& fields, const std::string& name) {
2981 vector<t_field*>::const_iterator f_iter;
2982 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2983 if ((*f_iter)->get_name() == name) {
2984 return true;
2985 }
2986 }
2987
2988 return false;
2989 }
2990
2991 /**
2992 * Given a vector of fields, generate a valid identifier name that does not conflict with avaliable field names
2993 */
next_identifier_name(const std::vector<t_field * > & fields,const std::string & base_name)2994 std::string t_js_generator::next_identifier_name(const std::vector<t_field*>& fields, const std::string& base_name) {
2995 // Search through fields until a match is not found, if a match is found prepend "_" to the identifier name
2996 std::string current_name = this->make_valid_nodeJs_identifier(base_name);
2997 while(this->find_field(fields, current_name)) {
2998 current_name = this->make_valid_nodeJs_identifier("_" + current_name);
2999 }
3000
3001 return current_name;
3002 }
3003
display_name() const3004 std::string t_js_generator::display_name() const {
3005 return "Javascript";
3006 }
3007
3008
3009 THRIFT_REGISTER_GENERATOR(js,
3010 "Javascript",
3011 " jquery: Generate jQuery compatible code.\n"
3012 " node: Generate node.js compatible code.\n"
3013 " ts: Generate TypeScript definition files.\n"
3014 " with_ns: Create global namespace objects when using node.js\n"
3015 " es6: Create ES6 code with Promises\n"
3016 " thrift_package_output_directory=<path>:\n"
3017 " Generate episode file and use the <path> as prefix\n"
3018 " imports=<paths_to_modules>:\n"
3019 " ':' separated list of paths of modules that has episode files in their root\n")
3020