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