in compiler/cpp/src/thrift/generate/t_js_generator.cc [1701:2207]
void t_js_generator::generate_service_client(t_service* tservice) {
bool is_subclass_service = tservice->get_extends() != nullptr;
if (gen_node_) {
string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_;
f_service_ << prefix << service_name_ << "Client = " << "exports.Client";
if (gen_ts_) {
f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class "
<< "Client ";
if (tservice->get_extends() != nullptr) {
f_service_ts_ << "extends " << tservice->get_extends()->get_name() << ".Client ";
}
f_service_ts_ << "{" << '\n';
}
} else {
f_service_ << js_namespace(tservice->get_program()) << service_name_
<< "Client";
if (gen_ts_) {
f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class "
<< service_name_ << "Client ";
if (is_subclass_service) {
f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client ";
}
f_service_ts_ << "{" << '\n';
}
}
// ES6 Constructor
if (gen_es6_) {
if (is_subclass_service) {
f_service_ << " = class " << service_name_ << "Client extends " << js_namespace(tservice->get_extends()->get_program())
<< tservice->get_extends()->get_name() << "Client {" << '\n';
} else {
f_service_ << " = class " << service_name_ << "Client {" << '\n';
}
indent_up();
if (gen_node_) {
indent(f_service_) << "constructor(output, pClass) {" << '\n';
} else {
indent(f_service_) << "constructor(input, output) {" << '\n';
}
} else {
if (gen_node_) {
f_service_ << " = function(output, pClass) {" << '\n';
} else {
f_service_ << " = function(input, output) {" << '\n';
}
}
indent_up();
if (gen_node_) {
if (gen_es6_ && is_subclass_service) {
indent(f_service_) << "super(output, pClass);" << '\n';
}
indent(f_service_) << "this.output = output;" << '\n';
indent(f_service_) << "this.pClass = pClass;" << '\n';
indent(f_service_) << "this._seqid = 0;" << '\n';
indent(f_service_) << "this._reqs = {};" << '\n';
if (gen_ts_) {
if(!is_subclass_service) {
f_service_ts_ << ts_indent() << "private output: thrift.TTransport;" << '\n'
<< ts_indent() << "private pClass: thrift.TProtocol;" << '\n'
<< ts_indent() << "private _seqid: number;" << '\n'
<< '\n';
}
f_service_ts_ << ts_indent() << "constructor(output: thrift.TTransport, pClass: { new(trans: thrift.TTransport): thrift.TProtocol });"
<< '\n';
}
} else {
indent(f_service_) << "this.input = input;" << '\n';
indent(f_service_) << "this.output = (!output) ? input : output;" << '\n';
indent(f_service_) << "this.seqid = 0;" << '\n';
if (gen_ts_) {
f_service_ts_ << ts_indent() << "input: Thrift.TJSONProtocol;" << '\n' << ts_indent()
<< "output: Thrift.TJSONProtocol;" << '\n' << ts_indent() << "seqid: number;"
<< '\n' << '\n' << ts_indent()
<< "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);"
<< '\n';
}
}
indent_down();
if (gen_es6_) {
indent(f_service_) << "}" << '\n';
} else {
indent(f_service_) << "};" << '\n';
if (is_subclass_service) {
indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program())
<< service_name_ << "Client, "
<< js_namespace(tservice->get_extends()->get_program())
<< tservice->get_extends()->get_name() << "Client);" << '\n';
} else {
// init prototype
indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
<< "Client.prototype = {};" << '\n';
}
}
// utils for multiplexed services
if (gen_node_) {
if (gen_es6_) {
indent(f_service_) << "seqid () { return this._seqid; }" << '\n';
indent(f_service_) << "new_seqid () { return this._seqid += 1; }" << '\n';
} else {
indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
<< "Client.prototype.seqid = function() { return this._seqid; };" << '\n'
<< js_namespace(tservice->get_program()) << service_name_
<< "Client.prototype.new_seqid = function() { return this._seqid += 1; };"
<< '\n';
}
}
// Generate client method implementations
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::const_iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
t_struct* arg_struct = (*f_iter)->get_arglist();
const vector<t_field*>& fields = arg_struct->get_members();
vector<t_field*>::const_iterator fld_iter;
string funname = (*f_iter)->get_name();
string arglist = argument_list(arg_struct);
// Open function
f_service_ << '\n';
if (gen_es6_) {
indent(f_service_) << funname << " (" << arglist << ") {" << '\n';
} else {
indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype."
<< function_signature(*f_iter, "", !gen_es6_) << " {" << '\n';
}
indent_up();
if (gen_ts_) {
// function definition without callback
f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false) << '\n';
if (!gen_es6_) {
// overload with callback
f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << '\n';
} else {
// overload with callback
f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << '\n';
}
}
if (gen_es6_ && gen_node_) {
indent(f_service_) << "this._seqid = this.new_seqid();" << '\n';
indent(f_service_) << js_const_type_ << "self = this;" << '\n' << indent()
<< "return new Promise((resolve, reject) => {" << '\n';
indent_up();
indent(f_service_) << "self._reqs[self.seqid()] = (error, result) => {" << '\n';
indent_up();
indent(f_service_) << "return error ? reject(error) : resolve(result);" << '\n';
indent_down();
indent(f_service_) << "};" << '\n';
indent(f_service_) << "self.send_" << funname << "(" << arglist << ");" << '\n';
indent_down();
indent(f_service_) << "});" << '\n';
} else if (gen_node_) { // Node.js output ./gen-nodejs
f_service_ << indent() << "this._seqid = this.new_seqid();" << '\n' << indent()
<< "if (callback === undefined) {" << '\n';
indent_up();
f_service_ << indent() << js_const_type_ << "_defer = Q.defer();" << '\n' << indent()
<< "this._reqs[this.seqid()] = function(error, result) {" << '\n';
indent_up();
indent(f_service_) << "if (error) {" << '\n';
indent_up();
indent(f_service_) << "_defer.reject(error);" << '\n';
indent_down();
indent(f_service_) << "} else {" << '\n';
indent_up();
indent(f_service_) << "_defer.resolve(result);" << '\n';
indent_down();
indent(f_service_) << "}" << '\n';
indent_down();
indent(f_service_) << "};" << '\n';
f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << '\n'
<< indent() << "return _defer.promise;" << '\n';
indent_down();
indent(f_service_) << "} else {" << '\n';
indent_up();
f_service_ << indent() << "this._reqs[this.seqid()] = callback;" << '\n' << indent()
<< "this.send_" << funname << "(" << arglist << ");" << '\n';
indent_down();
indent(f_service_) << "}" << '\n';
} else if (gen_es6_) {
f_service_ << indent() << js_const_type_ << "self = this;" << '\n' << indent()
<< "return new Promise((resolve, reject) => {" << '\n';
indent_up();
f_service_ << indent() << "self.send_" << funname << "(" << arglist
<< (arglist.empty() ? "" : ", ") << "(error, result) => {" << '\n';
indent_up();
indent(f_service_) << "return error ? reject(error) : resolve(result);" << '\n';
indent_down();
f_service_ << indent() << "});" << '\n';
indent_down();
f_service_ << indent() << "});" << '\n';
} else if (gen_jquery_) { // jQuery output ./gen-js
f_service_ << indent() << "if (callback === undefined) {" << '\n';
indent_up();
f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << '\n';
if (!(*f_iter)->is_oneway()) {
f_service_ << indent();
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ << "return ";
}
f_service_ << "this.recv_" << funname << "();" << '\n';
}
indent_down();
f_service_ << indent() << "} else {" << '\n';
indent_up();
f_service_ << indent() << js_const_type_ << "postData = this.send_" << funname << "(" << arglist
<< (arglist.empty() ? "" : ", ") << "true);" << '\n';
f_service_ << indent() << "return this.output.getTransport()" << '\n';
indent_up();
f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname
<< ");" << '\n';
indent_down();
indent_down();
f_service_ << indent() << "}" << '\n';
} else { // Standard JavaScript ./gen-js
f_service_ << indent() << "this.send_" << funname << "(" << arglist
<< (arglist.empty() ? "" : ", ") << "callback); " << '\n';
if (!(*f_iter)->is_oneway()) {
f_service_ << indent() << "if (!callback) {" << '\n';
f_service_ << indent();
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ << " return ";
}
f_service_ << "this.recv_" << funname << "();" << '\n';
f_service_ << indent() << "}" << '\n';
}
}
indent_down();
if (gen_es6_) {
indent(f_service_) << "}" << '\n' << '\n';
} else {
indent(f_service_) << "};" << '\n' << '\n';
}
// Send function
if (gen_es6_) {
if (gen_node_) {
indent(f_service_) << "send_" << funname << " (" << arglist << ") {" << '\n';
} else {
// ES6 js still uses callbacks here. Should refactor this to promise style later..
indent(f_service_) << "send_" << funname << " (" << argument_list(arg_struct, true) << ") {" << '\n';
}
} else {
indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype.send_"
<< function_signature(*f_iter, "", !gen_node_) << " {" << '\n';
}
indent_up();
std::string outputVar;
if (gen_node_) {
f_service_ << indent() << js_const_type_ << "output = new this.pClass(this.output);" << '\n';
outputVar = "output";
} else {
outputVar = "this.output";
}
std::string argsname = js_namespace(program_) + service_name_ + "_" + (*f_iter)->get_name()
+ "_args";
std::string messageType = (*f_iter)->is_oneway() ? "Thrift.MessageType.ONEWAY"
: "Thrift.MessageType.CALL";
// Build args
if (fields.size() > 0){
// It is possible that a method argument is named "params", we need to ensure the locally
// generated identifier "params" is uniquely named
std::string params_identifier = this->next_identifier_name(fields, "params");
f_service_ << indent() << js_const_type_ << params_identifier << " = {" << '\n';
indent_up();
for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
indent(f_service_) << (*fld_iter)->get_name() << ": " << (*fld_iter)->get_name();
if (fld_iter != fields.end()-1) {
f_service_ << "," << '\n';
} else {
f_service_ << '\n';
}
}
indent_down();
indent(f_service_) << "};" << '\n';
// NOTE: "args" is a reserved keyword, so no need to generate a unique identifier
indent(f_service_) << js_const_type_ << "args = new " << argsname << "(" << params_identifier << ");" << '\n';
} else {
indent(f_service_) << js_const_type_ << "args = new " << argsname << "();" << '\n';
}
// Serialize the request header within try/catch
indent(f_service_) << "try {" << '\n';
indent_up();
if (gen_node_) {
f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name()
<< "', " << messageType << ", this.seqid());" << '\n';
} else {
f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name()
<< "', " << messageType << ", this.seqid);" << '\n';
}
// Write to the stream
f_service_ << indent() << "args[Symbol.for(\"write\")](" << outputVar << ");" << '\n' << indent() << outputVar
<< ".writeMessageEnd();" << '\n';
if (gen_node_) {
if((*f_iter)->is_oneway()) {
f_service_ << indent() << "this.output.flush();" << '\n';
f_service_ << indent() << js_const_type_ << "callback = this._reqs[this.seqid()] || function() {};" << '\n';
f_service_ << indent() << "delete this._reqs[this.seqid()];" << '\n';
f_service_ << indent() << "callback(null);" << '\n';
} else {
f_service_ << indent() << "return this.output.flush();" << '\n';
}
} else {
if (gen_jquery_) {
f_service_ << indent() << "return this.output.getTransport().flush(callback);" << '\n';
} else if (gen_es6_) {
f_service_ << indent() << js_const_type_ << "self = this;" << '\n';
if((*f_iter)->is_oneway()) {
f_service_ << indent() << "this.output.getTransport().flush(true, null);" << '\n';
f_service_ << indent() << "callback();" << '\n';
} else {
f_service_ << indent() << "this.output.getTransport().flush(true, () => {" << '\n';
indent_up();
f_service_ << indent() << js_let_type_ << "error = null, result = null;" << '\n';
f_service_ << indent() << "try {" << '\n';
f_service_ << indent() << " result = self.recv_" << funname << "();" << '\n';
f_service_ << indent() << "} catch (e) {" << '\n';
f_service_ << indent() << " error = e;" << '\n';
f_service_ << indent() << "}" << '\n';
f_service_ << indent() << "callback(error, result);" << '\n';
indent_down();
f_service_ << indent() << "});" << '\n';
}
} else {
f_service_ << indent() << "if (callback) {" << '\n';
indent_up();
if((*f_iter)->is_oneway()) {
f_service_ << indent() << "this.output.getTransport().flush(true, null);" << '\n';
f_service_ << indent() << "callback();" << '\n';
} else {
f_service_ << indent() << js_const_type_ << "self = this;" << '\n';
f_service_ << indent() << "this.output.getTransport().flush(true, function() {" << '\n';
indent_up();
f_service_ << indent() << js_let_type_ << "result = null;" << '\n';
f_service_ << indent() << "try {" << '\n';
f_service_ << indent() << " result = self.recv_" << funname << "();" << '\n';
f_service_ << indent() << "} catch (e) {" << '\n';
f_service_ << indent() << " result = e;" << '\n';
f_service_ << indent() << "}" << '\n';
f_service_ << indent() << "callback(result);" << '\n';
indent_down();
f_service_ << indent() << "});" << '\n';
}
indent_down();
f_service_ << indent() << "} else {" << '\n';
f_service_ << indent() << " return this.output.getTransport().flush();" << '\n';
f_service_ << indent() << "}" << '\n';
}
}
indent_down();
f_service_ << indent() << "}" << '\n';
// Reset the transport and delete registered callback if there was a serialization error
f_service_ << indent() << "catch (e) {" << '\n';
indent_up();
if (gen_node_) {
f_service_ << indent() << "delete this._reqs[this.seqid()];" << '\n';
f_service_ << indent() << "if (typeof " << outputVar << ".reset === 'function') {" << '\n';
f_service_ << indent() << " " << outputVar << ".reset();" << '\n';
f_service_ << indent() << "}" << '\n';
} else {
f_service_ << indent() << "if (typeof " << outputVar << ".getTransport().reset === 'function') {" << '\n';
f_service_ << indent() << " " << outputVar << ".getTransport().reset();" << '\n';
f_service_ << indent() << "}" << '\n';
}
f_service_ << indent() << "throw e;" << '\n';
indent_down();
f_service_ << indent() << "}" << '\n';
indent_down();
// Close send function
if (gen_es6_) {
indent(f_service_) << "}" << '\n';
} else {
indent(f_service_) << "};" << '\n';
}
// Receive function
if (!(*f_iter)->is_oneway()) {
std::string resultname = js_namespace(tservice->get_program()) + service_name_ + "_"
+ (*f_iter)->get_name() + "_result";
f_service_ << '\n';
// Open receive function
if (gen_node_) {
if (gen_es6_) {
indent(f_service_) << "recv_" << (*f_iter)->get_name() << " (input, mtype, rseqid) {" << '\n';
} else {
indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
<< "Client.prototype.recv_" << (*f_iter)->get_name()
<< " = function(input,mtype,rseqid) {" << '\n';
}
} else {
if (gen_es6_) {
indent(f_service_) << "recv_" << (*f_iter)->get_name() << " () {" << '\n';
} else {
t_struct noargs(program_);
t_function recv_function((*f_iter)->get_returntype(),
string("recv_") + (*f_iter)->get_name(),
&noargs);
indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
<< "Client.prototype." << function_signature(&recv_function) << " {" << '\n';
}
}
indent_up();
std::string inputVar;
if (gen_node_) {
inputVar = "input";
} else {
inputVar = "this.input";
}
if (gen_node_) {
f_service_ << indent() << js_const_type_ << "callback = this._reqs[rseqid] || function() {};" << '\n'
<< indent() << "delete this._reqs[rseqid];" << '\n';
} else {
f_service_ << indent() << js_const_type_ << "ret = this.input.readMessageBegin();" << '\n'
<< indent() << js_const_type_ << "mtype = ret.mtype;" << '\n';
}
f_service_ << indent() << "if (mtype == Thrift.MessageType.EXCEPTION) {" << '\n';
indent_up();
f_service_ << indent() << js_const_type_ << "x = new Thrift.TApplicationException();" << '\n'
<< indent() << "x[Symbol.for(\"read\")](" << inputVar << ");" << '\n'
<< indent() << inputVar << ".readMessageEnd();" << '\n'
<< indent() << render_recv_throw("x") << '\n';
scope_down(f_service_);
f_service_ << indent() << js_const_type_ << "result = new " << resultname << "();" << '\n' << indent()
<< "result[Symbol.for(\"read\")](" << inputVar << ");" << '\n';
f_service_ << indent() << inputVar << ".readMessageEnd();" << '\n' << '\n';
t_struct* xs = (*f_iter)->get_xceptions();
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
f_service_ << indent() << "if (null !== result." << (*x_iter)->get_name() << ") {" << '\n'
<< indent() << " " << render_recv_throw("result." + (*x_iter)->get_name())
<< '\n' << indent() << "}" << '\n';
}
// Careful, only return result if not a void function
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ << indent() << "if (null !== result.success) {" << '\n' << indent() << " "
<< render_recv_return("result.success") << '\n' << indent() << "}" << '\n';
f_service_ << indent()
<< render_recv_throw("'" + (*f_iter)->get_name() + " failed: unknown result'")
<< '\n';
} else {
if (gen_node_) {
indent(f_service_) << "callback(null);" << '\n';
} else {
indent(f_service_) << "return;" << '\n';
}
}
// Close receive function
indent_down();
if (gen_es6_) {
indent(f_service_) << "}" << '\n';
} else {
indent(f_service_) << "};" << '\n';
}
}
}
// Finish class definitions
if (gen_ts_) {
f_service_ts_ << ts_indent() << "}" << '\n';
}
if (gen_es6_) {
indent_down();
f_service_ << "};" << '\n';
}
}