Thrift compiler now compiles both native Java and C++ code
Summary: Compiles to both C++ and Java, plus a host of other cool options like command line control over which languages to output code in
Reviewed By: aditya
Test Plan: The unit test checkins are coming momentarily...
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664713 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/Makefile b/compiler/Makefile
index 62ce275..49833cb 100644
--- a/compiler/Makefile
+++ b/compiler/Makefile
@@ -35,7 +35,8 @@
# Source files
SRC_FILES = main.cc \
generate/t_generator.cc \
- generate/t_cpp_generator.cc
+ generate/t_cpp_generator.cc \
+ generate/t_java_generator.cc
# Autogenerated files
GEN_FILES = thrift.tab.hh \
@@ -61,21 +62,23 @@
# Flex library
LIBS = -lfl
+# Build directories
+obj_dirs: $(OBJ_DIR)parse $(OBJ_DIR)generate
+$(OBJ_DIR)parse:
+ $(MKDIR) -p $(OBJ_DIR)parse
+$(OBJ_DIR)generate:
+ $(MKDIR) -p $(OBJ_DIR)generate
+
# Scanner generation
$(GEN_DIR)lex.yy.cc: $(SRC_DIR)thrift.l
$(LEX) -o$@ $(SRC_DIR)thrift.l
-
-# Parser generation
-$(GEN_DIR)thrift.tab.hh: $(GEN_DIR)thrift.tab.cc
-
-$(GEN_DIR)thrift.tab.cc: $(SRC_DIR)thrift.y
- $(YACC) -d -o$(GEN_DIR)thrift.tab.cc $(SRC_DIR)thrift.y
-
-# C++ compilation
$(OBJ_DIR)lex.yy.o: $(GEN_DIR)lex.yy.cc
$(CC) $(CFL) -c -o $@ $(GEN_DIR)lex.yy.cc
-# C++ compilation
+# Parser generation
+$(GEN_DIR)thrift.tab.hh: $(GEN_DIR)thrift.tab.cc
+$(GEN_DIR)thrift.tab.cc: $(SRC_DIR)thrift.y
+ $(YACC) -d -o$(GEN_DIR)thrift.tab.cc $(SRC_DIR)thrift.y
$(OBJ_DIR)thrift.tab.o: $(GEN_DIR)thrift.tab.cc
$(CC) $(CFL) -c -o $@ $(GEN_DIR)thrift.tab.cc
@@ -84,15 +87,10 @@
$(CC) $(CFL) -c -o $@ ${subst $(OBJ_DIR),$(SRC_DIR),$*.cc}
# Main build rule
-thrift: $(OBJS) $(GOBS)
+thrift: obj_dirs $(OBJS) $(GOBS)
$(LD) $(CFL) -o $(BIN_DIR)thrift $(OBJS) $(GOBS) $(LIBS)
-# Build directory
-obj_dirs:
- $(MKDIR) -p $(OBJ_DIR)parse
- $(MKDIR) -p $(OBJ_DIR)generate
-
-# Install it
+# Install
install: thrift
sudo install bin/thrift /usr/local/bin/thrift
diff --git a/compiler/src/generate/t_cpp_generator.cc b/compiler/src/generate/t_cpp_generator.cc
index e47c13b..18cfa31 100644
--- a/compiler/src/generate/t_cpp_generator.cc
+++ b/compiler/src/generate/t_cpp_generator.cc
@@ -2,7 +2,6 @@
#include <sys/stat.h>
#include <sstream>
#include "t_cpp_generator.h"
-#include "globals.h"
using namespace std;
/**
@@ -146,7 +145,8 @@
"#ifndef " << service_name_ << "_H" << endl <<
"#define " << service_name_ << "_H" << endl <<
endl <<
- "#include \"TDispatcher.h\"" << endl <<
+ "#include \"TProcessor.h\"" << endl <<
+ "#include \"transport/TTransport.h\"" << endl <<
"#include \"protocol/TProtocol.h\"" << endl <<
"#include \"" << program_name_ << "Types.h\"" << endl <<
endl;
@@ -201,16 +201,24 @@
"class " << service_name_ << "Client : " <<
"public " << service_name_ << "If {" << endl <<
" public:" << endl;
+
indent_up();
f_header_ <<
indent() << service_name_ << "Client" <<
- "(TDispatcher* dispatcher, TProtocol* protocol) : " <<
- "_dispatcher(dispatcher), _protocol(protocol) {}" << endl;
+ "(TTransport* trans, TProtocol* prot) : " <<
+ "_itrans(trans), _otrans(trans), " <<
+ "_iprot(prot), _oprot(prot) {}" << endl;
+ f_header_ <<
+ indent() << service_name_ << "Client" <<
+ "(TTransport* itrans, TTransport* otrans," <<
+ " TProtocol* iprot, TProtocol* oprot) : " <<
+ "_itrans(itrans), _otrans(otrans), " <<
+ "_iprot(iprot), _oprot(oprot) {}" << endl;
+
vector<t_function*> functions = tservice->get_functions();
- vector<t_function*>::iterator f_iter;
+ vector<t_function*>::const_iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
- f_header_ <<
- indent() << function_signature(*f_iter) << ";" << endl;
+ indent(f_header_) << function_signature(*f_iter) << ";" << endl;
}
indent_down();
@@ -218,47 +226,78 @@
" protected:" << endl;
indent_up();
f_header_ <<
- indent() << "TDispatcher* _dispatcher;" << endl <<
- indent() << "TProtocol* _protocol;" << endl;
+ indent() << "TTransport* _itrans;" << endl <<
+ indent() << "TTransport* _otrans;" << endl <<
+ indent() << "TProtocol* _iprot;" << endl <<
+ indent() << "TProtocol* _oprot;" << endl;
indent_down();
+
f_header_ <<
"};" << endl <<
endl;
+ string scope = service_name_ + "Client::";
+
// Generate client method implementations
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
- string prefix = service_name_ + "Client::";
+ string funname = (*f_iter)->get_name();
+
+ // Open function
f_service_ <<
- function_signature(*f_iter, prefix) << " {" << endl;
- indent_up();
+ function_signature(*f_iter, scope) << "" << endl;
+ scope_up(f_service_);
+ // Serialize the request
+ f_service_ <<
+ indent() <<
+ "_oprot->writeStructBegin(_otrans, \"function\");" << endl <<
+ indent() <<
+ "_oprot->writeFieldBegin(_otrans, \"name\", T_STRING, 0);" << endl <<
+ indent() <<
+ "_oprot->writeString(_otrans, \"" << funname << "\");" << endl <<
+ indent() <<
+ "_oprot->writeFieldEnd(_otrans);" << endl <<
+ indent() <<
+ "_oprot->writeFieldBegin(_otrans, \"args\", T_STRUCT, 1);" << endl;
+ generate_serialize_struct((*f_iter)->get_arglist());
+ f_service_ <<
+ indent() <<
+ "_oprot->writeFieldEnd(_otrans);" << endl <<
+ indent() <<
+ "_oprot->writeFieldStop(_otrans);" << endl <<
+ indent() <<
+ "_oprot->writeStructEnd(_otrans);" << endl;
+
+ // Flush the request
indent(f_service_) <<
- "std::string _argbuf = \"\";" << endl;
- generate_serialize_struct("_argbuf", (*f_iter)->get_arglist());
- indent(f_service_) <<
- "std::string _sbuf = _protocol->writeFunction(\"" <<
- (*f_iter)->get_name() << "\", _argbuf);" << endl;
- indent(f_service_) <<
- "std::string _rbuf = _dispatcher->dispatch(_sbuf);" << endl;
+ "_otrans->flush();" << endl;
+ // Read the response
+ t_struct result_struct((*f_iter)->get_name() + "_result");
+ t_field result_field((*f_iter)->get_returntype(), "_result");
+
+ // Add a field to the return struct if non void
if (!(*f_iter)->get_returntype()->is_void()) {
indent(f_service_) <<
- "TBuf _tbuf((uint8_t*)_rbuf.data(), _rbuf.size());" << endl;
- indent(f_service_) <<
type_name((*f_iter)->get_returntype()) << " _result;" << endl;
- t_field result_field((*f_iter)->get_returntype(), "_result", 0);
- generate_deserialize_field("_tbuf", &result_field);
+ result_struct.append(&result_field);
+ }
+
+ // Deserialize response struct
+ generate_deserialize_struct(&result_struct);
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
indent(f_service_) <<
"return _result;" << endl;
} else {
indent(f_service_) <<
"return;" << endl;
}
-
- indent_down();
- f_service_ <<
- "}" << endl <<
- endl;
+
+ // Close function
+ scope_down(f_service_);
+ indent(f_service_) << endl;
}
}
@@ -268,46 +307,76 @@
* @param tservice The service to generate a server for.
*/
void t_cpp_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
// Generate the header portion
f_header_ <<
"class " << service_name_ << "ServerIf : " <<
"public " << service_name_ << "If, " <<
- "public TDispatcher {" << endl <<
+ "public TProcessor {" << endl <<
" public: " << endl;
indent_up();
f_header_ <<
- indent() << service_name_ << "ServerIf(TProtocol* protocol) : " <<
- "_protocol(protocol) {}" << endl <<
- indent() << "std::string dispatch(const std::string& _in);" << endl <<
+ indent() <<
+ service_name_ << "ServerIf(TProtocol* protocol) : " <<
+ "_iprot(protocol), _oprot(protocol) {}" << endl <<
+ indent() <<
+ service_name_ << "ServerIf(TProtocol* iprot, TProtocol* oprot) : " <<
+ "_iprot(iprot), _oprot(oprot) {}" << endl <<
+ indent() << "bool process(TTransport *_itrans, " <<
+ "TTransport* _otrans);" << endl <<
indent() << "virtual ~" << service_name_ << "ServerIf() {}" << endl;
indent_down();
+
+ // Protected data members
f_header_ <<
" protected:" << endl;
indent_up();
f_header_ <<
- indent() << "TProtocol* _protocol;" << endl;
- indent_down();
+ indent() << "TProtocol* _iprot;" << endl <<
+ indent() << "TProtocol* _oprot;" << endl;
+ indent_down();
+
+ // Process function declarations
+ f_header_ <<
+ " private:" << endl;
+ indent_up();
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_header_) <<
+ "void process_" << (*f_iter)->get_name() <<
+ "(TTransport* _itrans, TTransport* _otrans);" << endl;
+ }
+ indent_down();
f_header_ <<
"};" << endl << endl;
- // Generate the dispatch methods
- vector<t_function*> functions = tservice->get_functions();
- vector<t_function*>::iterator f_iter;
- for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
- generate_dispatch_function(tservice, *f_iter);
- }
-
// Generate the server implementation
f_service_ <<
- "std::string " << service_name_ << "ServerIf::" <<
- "dispatch(const std::string& _in) {" << endl;
+ "bool " << service_name_ << "ServerIf::" <<
+ "process(TTransport* _itrans, TTransport* _otrans) {" << endl;
indent_up();
f_service_ <<
- indent() << "TBuf _tbuf((uint8_t*)_in.data(), _in.size());" << endl <<
- indent() << "std::string _fname = " <<
- "_protocol->readFunction(_tbuf);" << endl <<
- indent() << "std::string _result;" << endl;
+ indent() <<
+ "std::string _name;" << endl <<
+ indent() <<
+ "std::string _fname;" << endl <<
+ indent() <<
+ "TType _ftype;" << endl <<
+ indent() <<
+ "uint16_t _fid;" << endl <<
+ indent() <<
+ "_iprot->readStructBegin(_itrans, _name);" << endl <<
+ indent() <<
+ "_iprot->readFieldBegin(_itrans, _name, _ftype, _fid);" << endl <<
+ indent() <<
+ "_iprot->readString(_itrans, _fname);" << endl <<
+ indent() <<
+ "_iprot->readFieldEnd(_itrans);" << endl <<
+ indent() <<
+ "_iprot->readFieldBegin(_itrans, _name, _ftype, _fid);" << endl;
bool first = true;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
@@ -321,44 +390,56 @@
"if (_fname.compare(\"" << (*f_iter)->get_name() <<"\") == 0) {" << endl;
indent_up();
indent(f_service_) <<
- "_result = dispatch_" << (*f_iter)->get_name() <<
- "(_tbuf, this, _protocol);" << endl;
+ "process_" << (*f_iter)->get_name() <<
+ "(_itrans, _otrans);" << endl;
indent_down();
indent(f_service_) << "}";
}
indent(f_service_) <<
" else {" << endl;
indent_up();
- f_service_ <<
- indent() << "fprintf(stderr, \"Unknown function: '%s'\\n\", " <<
- "_fname.c_str());" << endl <<
- indent() << "_result = \"\";" << endl;
+ indent(f_service_) <<
+ "fprintf(stderr, \"Unknown function: '%s'\\n\", " <<
+ "_fname.c_str());" << endl;
indent_down();
indent(f_service_) <<
"}" << endl;
- indent(f_service_) <<
- "return _result;" << endl;
+ // Read end of args field, the T_STOP, and the struct close
+ f_service_ <<
+ indent() <<
+ "_iprot->readFieldEnd(_itrans);" << endl <<
+ indent() <<
+ "_iprot->readFieldBegin(_itrans, _name, _ftype, _fid);" << endl <<
+ indent() <<
+ "_iprot->readStructEnd(_itrans);" << endl <<
+ indent() <<
+ "return true;" << endl;
indent_down();
f_service_ <<
"}" << endl <<
endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
}
/**
- * Generates a dispatch function definition.
+ * Generates a process function definition.
*
* @param tfunction The function to write a dispatcher for
*/
-void t_cpp_generator::generate_dispatch_function(t_service* tservice,
- t_function* tfunction) {
+void t_cpp_generator::generate_process_function(t_service* tservice,
+ t_function* tfunction) {
+ // Open function
f_service_ <<
- "std::string dispatch_" << tfunction->get_name() <<
- "(TBuf _tbuf, " <<
- service_name_ << "If *_dispatcher, " <<
- "const TProtocol *_protocol) {" << endl;
- indent_up();
+ "void " << tservice->get_name() << "ServerIf::" <<
+ "process_" << tfunction->get_name() <<
+ "(TTransport *_itrans, TTransport *_otrans)" << endl;
+ scope_up(f_service_);
// Get the struct of function call params
t_struct* arg_struct = tfunction->get_arglist();
@@ -372,7 +453,7 @@
}
// Deserialize the function arguments as a struct
- generate_deserialize_struct("_tbuf", arg_struct);
+ generate_deserialize_struct(arg_struct);
// Generate the function call
f_service_ << indent();
@@ -380,7 +461,7 @@
f_service_ << type_name(tfunction->get_returntype()) << " _result = ";
}
f_service_ <<
- "_dispatcher->" << tfunction->get_name() << "(";
+ tfunction->get_name() << "(";
bool first = true;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if (first) {
@@ -392,28 +473,27 @@
}
f_service_ << ");" << endl;
- // Serialize the result into _sbuf
+ // Serialize the result into a struct
+ t_struct result_struct(tfunction->get_name() + "_result");
+ t_field result_field(tfunction->get_returntype(), "_result");
+
+ // Only append the field if non-void
+ if (!tfunction->get_returntype()->is_void()) {
+ result_struct.append(&result_field);
+ }
+ generate_serialize_struct(&result_struct);
indent(f_service_) <<
- "std::string _sbuf = \"\";" << endl;
+ "_otrans->flush();" << endl;
- t_field result_field(tfunction->get_returntype(), "_result", 0);
- generate_serialize_field("_sbuf", &result_field);
-
- // Return the serialized buffer
- indent(f_service_) <<
- "return _sbuf;" << endl;
-
- indent_down();
- f_service_ <<
- "}" << endl <<
- endl;
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
}
/**
* Deserializes a field of any type.
*/
-void t_cpp_generator::generate_deserialize_field(string src,
- t_field* tfield,
+void t_cpp_generator::generate_deserialize_field(t_field* tfield,
string prefix) {
t_type* type = tfield->get_type();
while (type->is_typedef()) {
@@ -421,59 +501,53 @@
}
if (type->is_void()) {
- return;
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " +
+ prefix + tfield->get_name();
}
+ string name = prefix + tfield->get_name();
+
if (type->is_struct()) {
- generate_deserialize_struct(src,
- (t_struct*)(tfield->get_type()),
- prefix + tfield->get_name() + ".");
+ generate_deserialize_struct((t_struct*)(tfield->get_type()),
+ name + ".");
} else if (type->is_container()) {
- generate_deserialize_container(src,
- tfield->get_type(),
- prefix + tfield->get_name());
+ generate_deserialize_container(tfield->get_type(), name);
} else if (type->is_base_type() || type->is_enum()) {
indent(f_service_) <<
- prefix << tfield->get_name() << " = ";
- if (type->is_enum()) {
- f_service_ << "(" << type_name(type) << ")";
- }
- f_service_ <<
- "_protocol->";
+ "_iprot->";
if (type->is_base_type()) {
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_VOID:
throw "compiler error: cannot serialize void field in a struct: " +
- src + ":" + tfield->get_name() + ":" + prefix;
+ name;
break;
case t_base_type::TYPE_STRING:
- f_service_ << "readString(" << src << ");";
+ f_service_ << "readString(_itrans, " << name << ");";
break;
case t_base_type::TYPE_BYTE:
- f_service_ << "readByte(" << src << ");";
+ f_service_ << "readByte(_itrans, " << name << ");";
break;
case t_base_type::TYPE_I32:
- f_service_ << "readI32(" << src << ");";
+ f_service_ << "readI32(_itrans, " << name << ");";
break;
case t_base_type::TYPE_U32:
- f_service_ << "readU32(" << src << ");";
+ f_service_ << "readU32(_itrans, " << name << ");";
break;
case t_base_type::TYPE_I64:
- f_service_ << "readI64(" << src << ");";
+ f_service_ << "readI64(_itrans, " << name << ");";
break;
case t_base_type::TYPE_U64:
- f_service_ << "readU64(" << src << ");";
+ f_service_ << "readU64(_itrans, " << name << ");";
break;
default:
throw "compiler error: no C++ name for base type " + tbase;
}
} else if (type->is_enum()) {
- f_service_ << "readI32(" << src << ");";
+ f_service_ << "readI32(_itrans, (int32_t&)" << name << ");";
}
-
f_service_ <<
endl;
} else {
@@ -488,83 +562,139 @@
* buffer for deserialization, and that there is a variable protocol which
* is a reference to a TProtocol serialization object.
*/
-void t_cpp_generator::generate_deserialize_struct(string src,
- t_struct* tstruct,
+void t_cpp_generator::generate_deserialize_struct(t_struct* tstruct,
string prefix) {
const vector<t_field*>& fields = tstruct->get_members();
vector<t_field*>::const_iterator f_iter;
- // Ensure that there are some fields
- if (fields.size() == 0) {
- return;
- }
-
scope_up(f_service_);
// Read the struct fields from the protocol
- string _struct = tmp("_struct");
+ string fid = tmp("_fid");
+ string ftype = tmp("_ftype");
+ string fname = tmp("_name");
+
+ // Declare stack tmp variables
+ f_service_ <<
+ indent() << "std::string " << fname << ";" << endl <<
+ indent() << "TType " << ftype << ";" << endl <<
+ indent() << "uint16_t " << fid << ";" << endl <<
+ indent() << "_iprot->readStructBegin(_itrans, " << fname << ");" << endl;
+
+ // Loop over reading in fields
indent(f_service_) <<
- "std::map<uint32_t,TBuf> " << _struct <<
- " = _protocol->readStruct(" << src << ");" << endl;
+ "while (true)" << endl;
+
+ scope_up(f_service_);
+
+ // Read beginning field marker
+ indent(f_service_) <<
+ "_iprot->readFieldBegin(_itrans, " <<
+ fname << ", " << ftype << ", " << fid << ");" << endl;
+
+ // Check for field STOP marker
+ indent(f_service_) <<
+ "if (" << ftype << " == T_STOP) { break; }" << endl;
+
+ // Switch statement on the field we are reading
+ indent(f_service_) <<
+ "switch (" << fid << ")" << endl;
+
+ scope_up(f_service_);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(f_service_) <<
+ "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ generate_deserialize_field(*f_iter, prefix);
+ indent(f_service_) <<
+ "break;" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ f_service_ <<
+ indent() << "default:" << endl <<
+ indent() << " _iprot->skip(_itrans, " << ftype << ");" << endl <<
+ indent() << " break;" << endl;
+
+ scope_down(f_service_);
+
+ // Read field end marker
+ indent(f_service_) <<
+ "_iprot->readFieldEnd(_itrans);" << endl;
+
+ scope_down(f_service_);
+
indent(f_service_) <<
- "std::map<uint32_t,TBuf>::iterator _field;" << endl;
-
- // Decleare a temp buffer for working with fields
- string fbuf = tmp("_fbuf");
-
- // Iterate over the fields and extract them
- for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
- // Check for existence of the field
- indent(f_service_) <<
- "if ((_field = " << _struct << ".find(" << (*f_iter)->get_key() <<
- ")) != " << _struct << ".end()) {" <<
- endl;
- indent_up();
-
- // Copy the field buffer into temp buffer
- indent(f_service_) <<
- "TBuf& " << fbuf << " = _field->second;" << endl;
-
- // Deserialize from the field's buffer
- generate_deserialize_field(fbuf, *f_iter, prefix);
-
- indent_down();
- indent(f_service_) <<
- "}" << endl;
- }
+ "_iprot->readStructEnd(_itrans);" << endl;
scope_down(f_service_);
}
-void t_cpp_generator::generate_deserialize_container(string src,
- t_type* ttype,
+void t_cpp_generator::generate_deserialize_container(t_type* ttype,
string prefix) {
scope_up(f_service_);
string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+
indent(f_service_) <<
- "uint32_t " << size << " = _protocol->readU32(" << src << ");" << endl;
+ "uint32_t " << size << ";" << endl;
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ f_service_ <<
+ indent() << "TType " << ktype << ";" << endl <<
+ indent() << "TType " << vtype << ";" << endl <<
+ indent() << "_iprot->readMapBegin(_itrans, " <<
+ ktype << ", " << vtype << ", " << size << ");" << endl;
+ } else if (ttype->is_set()) {
+ f_service_ <<
+ indent() << "TType " << etype << ";" << endl <<
+ indent() << "_iprot->readSetBegin(_itrans, " <<
+ etype << ", " << size << ");" << endl;
+ } else if (ttype->is_list()) {
+ f_service_ <<
+ indent() << "TType " << etype << ";" << endl <<
+ indent() << "_iprot->readListBegin(_itrans, " <<
+ etype << ", " << size << ");" << endl;
+ }
+
+
+ // For loop iterates over elements
string i = tmp("_i");
indent(f_service_) <<
"uint32_t " << i << ";" << endl;
indent(f_service_) <<
"for (" <<
- i << " = 0; " << i << " < " << size << "; ++" << i << ") {" << endl;
- indent_up();
+ i << " = 0; " << i << " < " << size << "; ++" << i << ")" << endl;
+
+ scope_up(f_service_);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element((t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element((t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element((t_list*)ttype, prefix);
+ }
+
+ scope_down(f_service_);
+ // Read container end
if (ttype->is_map()) {
- generate_deserialize_map_element(src, (t_map*)ttype, prefix);
+ indent(f_service_) << "_iprot->readMapEnd(_itrans);" << endl;
} else if (ttype->is_set()) {
- generate_deserialize_set_element(src, (t_set*)ttype, prefix);
+ indent(f_service_) << "_iprot->readSetEnd(_itrans);" << endl;
} else if (ttype->is_list()) {
- generate_deserialize_list_element(src, (t_list*)ttype, prefix);
+ indent(f_service_) << "_iprot->readListEnd(_itrans);" << endl;
}
- indent_down();
- indent(f_service_) <<
- "}" << endl;
-
scope_down(f_service_);
}
@@ -572,51 +702,48 @@
/**
* Generates code to deserialize a map
*/
-void t_cpp_generator::generate_deserialize_map_element(string src,
- t_map* tmap,
+void t_cpp_generator::generate_deserialize_map_element(t_map* tmap,
string prefix) {
string key = tmp("_key");
string val = tmp("_val");
- t_field fkey(tmap->get_key_type(), key, 0);
- t_field fval(tmap->get_val_type(), val, 0);
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
indent(f_service_) <<
declare_field(&fkey) << endl;
indent(f_service_) <<
declare_field(&fval) << endl;
- generate_deserialize_field(src, &fkey, "");
- generate_deserialize_field(src, &fval, "");
+ generate_deserialize_field(&fkey);
+ generate_deserialize_field(&fval);
indent(f_service_) <<
prefix << ".insert(std::make_pair(" << key << ", " << val << "));" << endl;
}
-void t_cpp_generator::generate_deserialize_set_element(string src,
- t_set* tset,
+void t_cpp_generator::generate_deserialize_set_element(t_set* tset,
string prefix) {
string elem = tmp("_elem");
- t_field felem(tset->get_elem_type(), elem, 0);
+ t_field felem(tset->get_elem_type(), elem);
indent(f_service_) <<
declare_field(&felem) << endl;
- generate_deserialize_field(src, &felem, "");
+ generate_deserialize_field(&felem);
indent(f_service_) <<
prefix << ".insert(" << elem << ");" << endl;
}
-void t_cpp_generator::generate_deserialize_list_element(string src,
- t_list* tlist,
+void t_cpp_generator::generate_deserialize_list_element(t_list* tlist,
string prefix) {
string elem = tmp("_elem");
- t_field felem(tlist->get_elem_type(), elem, 0);
+ t_field felem(tlist->get_elem_type(), elem);
indent(f_service_) <<
declare_field(&felem) << endl;
- generate_deserialize_field(src, &felem, "");
+ generate_deserialize_field(&felem);
indent(f_service_) <<
prefix << ".push_back(" << elem << ");" << endl;
@@ -629,66 +756,68 @@
* @param tfield The field to serialize
* @param prefix Name to prepend to field name
*/
-void t_cpp_generator::generate_serialize_field(string dest,
- t_field* tfield,
+void t_cpp_generator::generate_serialize_field(t_field* tfield,
string prefix) {
t_type* type = tfield->get_type();
while (type->is_typedef()) {
type = ((t_typedef*)type)->get_type();
}
-
+
// Do nothing for void types
if (type->is_void()) {
- return;
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " +
+ prefix + tfield->get_name();
}
-
+
if (type->is_struct()) {
- generate_serialize_struct(dest,
- (t_struct*)(tfield->get_type()),
+ generate_serialize_struct((t_struct*)(tfield->get_type()),
prefix + tfield->get_name() + ".");
} else if (type->is_container()) {
- generate_serialize_container(dest,
- tfield->get_type(),
+ generate_serialize_container(tfield->get_type(),
prefix + tfield->get_name());
} else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + tfield->get_name();
indent(f_service_) <<
- dest << " += _protocol->";
+ "_oprot->";
if (type->is_base_type()) {
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
switch (tbase) {
case t_base_type::TYPE_VOID:
- throw "compiler error: cannot serialize void field in a struct: " +
- dest + ":" + tfield->get_name() + ":" + prefix;
+ throw
+ "compiler error: cannot serialize void field in a struct: " + name;
break;
case t_base_type::TYPE_STRING:
- f_service_ << "writeString(" << prefix << tfield->get_name() << ");";
+ f_service_ << "writeString(_otrans, " << name << ");";
break;
case t_base_type::TYPE_BYTE:
- f_service_ << "writeByte(" << prefix << tfield->get_name() << ");";
+ f_service_ << "writeByte(_otrans, " << name << ");";
break;
case t_base_type::TYPE_I32:
- f_service_ << "writeI32(" << prefix << tfield->get_name() << ");";
+ f_service_ << "writeI32(_otrans, " << name << ");";
break;
case t_base_type::TYPE_U32:
- f_service_ << "writeU32(" << prefix << tfield->get_name() << ");";
+ f_service_ << "writeU32(_otrans, " << name << ");";
break;
case t_base_type::TYPE_I64:
- f_service_ << "writeI64(" << prefix << tfield->get_name() << ");";
+ f_service_ << "writeI64(_otrans, " << name << ");";
break;
case t_base_type::TYPE_U64:
- f_service_ << "writeU64(" << prefix << tfield->get_name() << ");";
+ f_service_ << "writeU64(_otrans, " << name << ");";
break;
default:
throw "compiler error: no C++ name for base type " + tbase;
}
} else if (type->is_enum()) {
- f_service_ << "writeI32(" << prefix << tfield->get_name() << ");";
- }
+ f_service_ << "writeI32(_otrans, (int32_t)" << name << ");";
+ }
f_service_ << endl;
} else {
- printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
- tfield->get_name().c_str(), type_name(type).c_str());
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
}
}
@@ -698,44 +827,57 @@
* @param tstruct The struct to serialize
* @param prefix String prefix to attach to all fields
*/
-void t_cpp_generator::generate_serialize_struct(string dest,
- t_struct* tstruct,
+void t_cpp_generator::generate_serialize_struct(t_struct* tstruct,
string prefix) {
+ string name = tstruct->get_name();
const vector<t_field*>& fields = tstruct->get_members();
vector<t_field*>::const_iterator f_iter;
- // Ensure that there are some fields
- if (fields.size() == 0) {
- return;
- }
-
scope_up(f_service_);
- string _struct = tmp("_struct");
-
indent(f_service_) <<
- "std::map<uint32_t,std::string> " << _struct << ";" << endl;
-
- // Serialize each of the fields into the map
+ "_oprot->writeStructBegin(_otrans, \"" << name << "\");" << endl;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
- ostringstream fdest;
- fdest << _struct << "[" << (*f_iter)->get_key() << "]";
- generate_serialize_field(fdest.str(), *f_iter, prefix);
+ // Write field header
+ indent(f_service_) <<
+ "_oprot->writeFieldBegin(_otrans, " <<
+ "\"" << (*f_iter)->get_name() << "\", " <<
+ type_to_enum((*f_iter)->get_type()) << ", " <<
+ (*f_iter)->get_key() << ");" << endl;
+ // Write field contents
+ generate_serialize_field(*f_iter, prefix);
+ // Write field closer
+ indent(f_service_) <<
+ "_oprot->writeFieldEnd(_otrans);" << endl;
}
-
// Write the struct map
- indent(f_service_) <<
- dest << " += _protocol->writeStruct(" << _struct << ");" << endl;
+ f_service_ <<
+ indent() << "_oprot->writeFieldStop(_otrans);" << endl <<
+ indent() << "_oprot->writeStructEnd(_otrans);" << endl;
scope_down(f_service_);
}
-void t_cpp_generator::generate_serialize_container(string dest,
- t_type* ttype,
+void t_cpp_generator::generate_serialize_container(t_type* ttype,
string prefix) {
scope_up(f_service_);
- indent(f_service_) <<
- dest << " += _protocol->writeU32(" << prefix << ".size());" << endl;
+ if (ttype->is_map()) {
+ indent(f_service_) <<
+ "_oprot->writeMapBegin(_otrans, " <<
+ type_to_enum(((t_map*)ttype)->get_key_type()) << ", " <<
+ type_to_enum(((t_map*)ttype)->get_val_type()) << ", " <<
+ prefix << ".size());" << endl;
+ } else if (ttype->is_set()) {
+ indent(f_service_) <<
+ "_oprot->writeSetBegin(_otrans, " <<
+ type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " <<
+ prefix << ".size());" << endl;
+ } else if (ttype->is_list()) {
+ indent(f_service_) <<
+ "_oprot->writeListBegin(_otrans, " <<
+ type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " <<
+ prefix << ".size());" << endl;
+ }
string iter = tmp("_iter");
indent(f_service_) <<
@@ -743,21 +885,31 @@
indent(f_service_) <<
"for (" << iter << " = " << prefix << ".begin(); " <<
iter << " != " << prefix << ".end(); " <<
- "++" << iter << ") {" << endl;
- indent_up();
-
- if (ttype->is_map()) {
- generate_serialize_map_element(dest, (t_map*)ttype, iter);
- } else if (ttype->is_set()) {
- generate_serialize_set_element(dest, (t_set*)ttype, iter);
- } else if (ttype->is_list()) {
- generate_serialize_list_element(dest, (t_list*)ttype, iter);
- }
+ "++" << iter << ")" << endl;
- indent_down();
- indent(f_service_) <<
- "}" << endl;
-
+ scope_up(f_service_);
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element((t_map*)ttype, iter);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element((t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element((t_list*)ttype, iter);
+ }
+
+ if (ttype->is_map()) {
+ indent(f_service_) <<
+ "_oprot->writeMapEnd(_otrans);" << endl;
+ } else if (ttype->is_set()) {
+ indent(f_service_) <<
+ "_oprot->writeSetEnd(_otrans);" << endl;
+ } else if (ttype->is_list()) {
+ indent(f_service_) <<
+ "_oprot->writeListEnd(_otrans);" << endl;
+ }
+
+ scope_down(f_service_);
+
scope_down(f_service_);
}
@@ -765,51 +917,31 @@
* Serializes the members of a map.
*
*/
-void t_cpp_generator::generate_serialize_map_element(string dest,
- t_map* tmap,
+void t_cpp_generator::generate_serialize_map_element(t_map* tmap,
string iter) {
- t_field kfield(tmap->get_key_type(), iter + "->first", 0);
- generate_serialize_field(dest, &kfield, "");
+ t_field kfield(tmap->get_key_type(), iter + "->first");
+ generate_serialize_field(&kfield, "");
- t_field vfield(tmap->get_val_type(), iter + "->second", 0);
- generate_serialize_field(dest, &vfield, "");
+ t_field vfield(tmap->get_val_type(), iter + "->second");
+ generate_serialize_field(&vfield, "");
}
/**
* Serializes the members of a set.
*/
-void t_cpp_generator::generate_serialize_set_element(string dest,
- t_set* tset,
+void t_cpp_generator::generate_serialize_set_element(t_set* tset,
string iter) {
- t_field efield(tset->get_elem_type(), "(*" + iter + ")", 0);
- generate_serialize_field(dest, &efield, "");
+ t_field efield(tset->get_elem_type(), "(*" + iter + ")");
+ generate_serialize_field(&efield, "");
}
/**
* Serializes the members of a list.
*/
-void t_cpp_generator::generate_serialize_list_element(string dest,
- t_list* tlist,
+void t_cpp_generator::generate_serialize_list_element(t_list* tlist,
string iter) {
- t_field efield(tlist->get_elem_type(), "(*" + iter + ")", 0);
- generate_serialize_field(dest, &efield, "");
-}
-
-/**
- * Generates a comment about this code being autogenerated.
- *
- * @return C-style comment mentioning that this file is autogenerated.
- */
-string t_cpp_generator::autogen_comment() {
- string result = "";
- return
- result +
- "/**\n" +
- " * Autogenerated by Thrift\n" +
- " * " + g_time_str +
- " *\n" +
- " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" +
- " */\n";
+ t_field efield(tlist->get_elem_type(), "(*" + iter + ")");
+ generate_serialize_field(&efield, "");
}
/**
@@ -934,3 +1066,44 @@
}
return result;
}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_cpp_generator::type_to_enum(t_type* type) {
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "T_STRING";
+ case t_base_type::TYPE_BYTE:
+ return "T_BYTE";
+ case t_base_type::TYPE_I32:
+ return "T_I32";
+ case t_base_type::TYPE_U32:
+ return "T_U32";
+ case t_base_type::TYPE_I64:
+ return "T_I64";
+ case t_base_type::TYPE_U64:
+ return "T_U64";
+ }
+ } else if (type->is_enum()) {
+ return "T_I32";
+ } else if (type->is_struct()) {
+ return "T_STRUCT";
+ } else if (type->is_map()) {
+ return "T_MAP";
+ } else if (type->is_set()) {
+ return "T_SET";
+ } else if (type->is_list()) {
+ return "T_LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
diff --git a/compiler/src/generate/t_cpp_generator.h b/compiler/src/generate/t_cpp_generator.h
index 079a26d..801c06c 100644
--- a/compiler/src/generate/t_cpp_generator.h
+++ b/compiler/src/generate/t_cpp_generator.h
@@ -6,16 +6,17 @@
#include <iostream>
#include <vector>
-#include "t_generator.h"
+#include "t_oop_generator.h"
+// TODO(mcslee: Paramaterize the output dir
#define T_CPP_DIR "gen-cpp"
/**
- * C++ code generator.
+ * C++ code generator. This is legitimacy incarnate.
*
* @author Mark Slee <mcslee@facebook.com>
*/
-class t_cpp_generator : public t_generator {
+class t_cpp_generator : public t_oop_generator {
public:
t_cpp_generator() {}
~t_cpp_generator() {}
@@ -37,80 +38,57 @@
void generate_service_interface (t_service* tservice);
void generate_service_client (t_service* tservice);
void generate_service_server (t_service* tservice);
- void generate_dispatch_function (t_service* tservice, t_function* tfunction);
+ void generate_process_function (t_service* tservice, t_function* tfunction);
/** Serialization constructs */
- void generate_deserialize_field (std::string src,
- t_field* tfield,
+ void generate_deserialize_field (t_field* tfield,
std::string prefix="");
- void generate_deserialize_struct (std::string src,
- t_struct* tstruct,
+ void generate_deserialize_struct (t_struct* tstruct,
std::string prefix="");
- void generate_deserialize_container (std::string src,
- t_type* ttype,
+ void generate_deserialize_container (t_type* ttype,
std::string prefix="");
- void generate_deserialize_set_element (std::string src,
- t_set* tset,
+ void generate_deserialize_set_element (t_set* tset,
std::string prefix="");
- void generate_deserialize_map_element (std::string src,
- t_map* tmap,
+ void generate_deserialize_map_element (t_map* tmap,
std::string prefix="");
- void generate_deserialize_list_element (std::string src,
- t_list* tlist,
+ void generate_deserialize_list_element (t_list* tlist,
std::string prefix="");
- void generate_serialize_field (std::string dest,
- t_field* tfield,
+ void generate_serialize_field (t_field* tfield,
std::string prefix="");
- void generate_serialize_struct (std::string dest,
- t_struct* tstruct,
+ void generate_serialize_struct (t_struct* tstruct,
std::string prefix="");
- void generate_serialize_container (std::string dest,
- t_type* ttype,
+ void generate_serialize_container (t_type* ttype,
std::string prefix="");
- void generate_serialize_map_element (std::string dest,
- t_map* tmap,
+ void generate_serialize_map_element (t_map* tmap,
std::string iter);
- void generate_serialize_set_element (std::string dest,
- t_set* tmap,
+ void generate_serialize_set_element (t_set* tmap,
std::string iter);
- void generate_serialize_list_element (std::string dest,
- t_list* tlist,
+ void generate_serialize_list_element (t_list* tlist,
std::string iter);
- /** Scoping */
-
- void scope_up(std::ostream& out) {
- indent(out) << "{" << std::endl;
- indent_up();
- }
-
- void scope_down(std::ostream& out) {
- indent_down();
- indent(out) << "}" << std::endl;
- }
-
/** Helper rendering functions */
- std::string autogen_comment();
std::string type_name(t_type* ttype);
std::string base_type_name(t_base_type::t_base tbase);
std::string declare_field(t_field* tfield, bool init=false);
std::string function_signature(t_function* tfunction, std::string prefix="");
std::string argument_list(t_struct* tstruct);
-
+ std::string type_to_enum(t_type* ttype);
+
private:
+
/** File streams */
std::ofstream f_types_;
diff --git a/compiler/src/generate/t_java_generator.cc b/compiler/src/generate/t_java_generator.cc
new file mode 100644
index 0000000..e4236fc
--- /dev/null
+++ b/compiler/src/generate/t_java_generator.cc
@@ -0,0 +1,1118 @@
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "t_java_generator.h"
+using namespace std;
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_java_generator::init_generator(t_program* tprogram) {
+ // Make output directory
+ mkdir(T_JAVA_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+}
+
+/**
+ * Packages the generated file
+ */
+string t_java_generator::java_package() {
+ // TODO(mcslee): Allow destination package to be specified in .thrift file
+ return string("package ") + program_name_ + ";\n\n";
+}
+
+/**
+ * Prints standard java imports
+ */
+string t_java_generator::java_type_imports() {
+ return
+ string() +
+ "import java.util.ArrayList;\n" +
+ "import java.util.HashMap;\n" +
+ "import java.util.HashSet;\n" +
+ "import com.facebook.thrift.*;\n" +
+ "import com.facebook.thrift.types.*;\n" +
+ "import com.facebook.thrift.protocol.TString;\n\n";
+}
+
+/**
+ * Prints standard java imports
+ */
+string t_java_generator::java_thrift_imports() {
+ return
+ string() +
+ "import com.facebook.thrift.protocol.*;\n" +
+ "import com.facebook.thrift.transport.*;\n\n";
+}
+
+/**
+ * Does nothing in Java
+ */
+void t_java_generator::close_generator() {}
+
+/**
+ * Generates a typedef. This is not done in Java.
+ *
+ * @param ttypedef The type definition
+ */
+void t_java_generator::generate_typedef(t_typedef* ttypedef) {}
+
+/**
+ * Generates code for an enumerated type. In C++, this is essentially the same
+ * as the thrift definition itself, using the enum keyword in C++.
+ *
+ * @param tenum The enumeration
+ */
+void t_java_generator::generate_enum(t_enum* tenum) {
+ // Make output file
+ string f_enum_name = string(T_JAVA_DIR)+"/"+(tenum->get_name())+".java";
+ ofstream f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ f_enum <<
+ autogen_comment() <<
+ java_package() <<
+ "import com.facebook.thrift.types.Int32;" << endl << endl;
+
+ f_enum <<
+ "public class " << tenum->get_name() << " ";
+ scope_up(f_enum);
+
+ vector<t_constant*> constants = tenum->get_constants();
+ vector<t_constant*>::iterator c_iter;
+ int value = -1;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ if ((*c_iter)->has_value()) {
+ value = (*c_iter)->get_value();
+ } else {
+ ++value;
+ }
+
+ indent(f_enum) <<
+ "public static final Int32 " << (*c_iter)->get_name() <<
+ " = new Int32(" << value << ");" << endl;
+ }
+
+ scope_down(f_enum);
+}
+
+/**
+ * Generates a struct definition for a thrift data type. In C++, this is just
+ * simple C struct with basic data members. There are no constructors,
+ * initializers, etc.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_struct(t_struct* tstruct) {
+ // Make output file
+ string f_struct_name = string(T_JAVA_DIR)+"/"+(tstruct->get_name())+".java";
+ ofstream f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct <<
+ autogen_comment() <<
+ java_package() <<
+ java_type_imports();
+
+ f_struct <<
+ "public class " << tstruct->get_name() << " ";
+
+ scope_up(f_struct);
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(f_struct) <<
+ "public " << declare_field(*m_iter, true) << endl;
+ }
+
+ scope_down(f_struct);
+}
+
+/**
+ * Generates a thrift service. In C++, this comprises an entirely separate
+ * header and source file. The header file defines the methods and includes
+ * the data types defined in the main header file, and the implementation
+ * file contains implementations of the basic printer and default interfaces.
+ *
+ * @param tservice The service definition
+ */
+void t_java_generator::generate_service(t_service* tservice) {
+ // Generate the three main parts of the service
+ generate_service_interface(tservice);
+ generate_service_server(tservice);
+ generate_service_client(tservice);
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_java_generator::generate_service_interface(t_service* tservice) {
+ // Make output file
+ string f_interface_name = string(T_JAVA_DIR)+"/"+service_name_+"If.java";
+ ofstream f_interface;
+ f_interface.open(f_interface_name.c_str());
+
+ f_interface <<
+ autogen_comment() <<
+ java_package() <<
+ java_type_imports();
+
+ f_interface <<
+ "public abstract class " << service_name_ << "If {" << endl;
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_interface) <<
+ "public abstract " << function_signature(*f_iter) <<
+ " throws TException;" << endl;
+ }
+ indent_down();
+ f_interface <<
+ "}" << endl;
+ f_interface.close();
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_java_generator::generate_service_client(t_service* tservice) {
+ string f_client_name = string(T_JAVA_DIR)+"/"+service_name_+"Client.java";
+ f_service_.open(f_client_name.c_str());
+
+ f_service_ <<
+ autogen_comment() <<
+ java_package() <<
+ java_type_imports() <<
+ java_thrift_imports();
+
+ f_service_ <<
+ "public class " << service_name_ << "Client " <<
+ "extends " << service_name_ << "If {" << endl;
+ indent_up();
+
+ indent(f_service_) <<
+ "public " << service_name_ << "Client" <<
+ "(TTransport trans, TProtocol prot)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) <<
+ "this(trans, trans, prot, prot);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) <<
+ "public " << service_name_ << "Client" <<
+ "(TTransport itrans, TTransport otrans," <<
+ " TProtocol iprot, TProtocol oprot)" << endl;
+ scope_up(f_service_);
+ f_service_ <<
+ indent() << "_itrans = itrans;" << endl <<
+ indent() << "_otrans = otrans;" << endl <<
+ indent() << "_iprot = iprot;" << endl <<
+ indent() << "_oprot = oprot;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ f_service_ <<
+ indent() << "private TTransport _itrans;" << endl <<
+ indent() << "private TTransport _otrans;" << endl <<
+ indent() << "private TProtocol _iprot;" << endl <<
+ indent() << "private TProtocol _oprot;" << endl << endl;
+
+ // 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) {
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ indent(f_service_) <<
+ "public " << function_signature(*f_iter) << " throws TException" << endl;
+ scope_up(f_service_);
+
+ // Serialize the request
+ f_service_ <<
+ indent() <<
+ "_oprot.writeStructBegin(_otrans, " <<
+ "new TStruct(\"function\"));" << endl <<
+ indent() <<
+ "_oprot.writeFieldBegin(_otrans, " <<
+ "new TField(\"name\", TType.STRING, 0));" << endl <<
+ indent() <<
+ "_oprot.writeString(_otrans, " <<
+ "new TString(\"" << funname << "\"));" << endl <<
+ indent() <<
+ "_oprot.writeFieldEnd(_otrans);" << endl <<
+ indent() <<
+ "_oprot.writeFieldBegin(_otrans, " <<
+ "new TField(\"args\", TType.STRUCT, 1));" << endl;
+ generate_serialize_struct((*f_iter)->get_arglist());
+ f_service_ <<
+ indent() <<
+ "_oprot.writeFieldEnd(_otrans);" << endl <<
+ indent() <<
+ "_oprot.writeFieldStop(_otrans);" << endl <<
+ indent() <<
+ "_oprot.writeStructEnd(_otrans);" << endl;
+
+ // Flush the request
+ indent(f_service_) <<
+ "_otrans.flush();" << endl;
+
+ // Read the response
+ t_struct result_struct((*f_iter)->get_name() + "_result");
+ t_field result_field((*f_iter)->get_returntype(), "_result");
+
+ // Add a field to the return struct if non void
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) <<
+ declare_field(&result_field, true) << endl;
+ result_struct.append(&result_field);
+ }
+
+ // Deserialize response struct
+ generate_deserialize_struct(&result_struct);
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) <<
+ "return _result;" << endl;
+ } else {
+ indent(f_service_) <<
+ "return;" << endl;
+ }
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ indent_down();
+ f_service_ <<
+ "}" << endl;
+ f_service_.close();
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_java_generator::generate_service_server(t_service* tservice) {
+ string f_server_name = string(T_JAVA_DIR)+"/"+service_name_+"ServerIf.java";
+ f_service_.open(f_server_name.c_str());
+
+ f_service_ <<
+ autogen_comment() <<
+ java_package() <<
+ java_type_imports() <<
+ java_thrift_imports();
+
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Generate the header portion
+ f_service_ <<
+ "public abstract class " << service_name_ << "ServerIf " <<
+ "extends " << service_name_ << "If implements TProcessor {" << endl;
+ indent_up();
+
+ indent(f_service_) <<
+ "public " << service_name_ << "ServerIf" <<
+ "(TProtocol prot)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) <<
+ "this(prot, prot);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) <<
+ "public " << service_name_ << "ServerIf" <<
+ "(TProtocol iprot, TProtocol oprot)" << endl;
+ scope_up(f_service_);
+ f_service_ <<
+ indent() << "_iprot = iprot;" << endl <<
+ indent() << "_oprot = oprot;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ f_service_ <<
+ indent() << "private TProtocol _iprot;" << endl <<
+ indent() << "private TProtocol _oprot;" << endl << endl;
+
+ // Generate the server implementation
+ indent(f_service_) <<
+ "public boolean process(TTransport _itrans, TTransport _otrans) " <<
+ "throws TException" << endl;
+ scope_up(f_service_);
+
+ f_service_ <<
+ indent() <<
+ "TString _fname = new TString();" << endl <<
+ indent() <<
+ "TStruct _struct = new TStruct();" << endl <<
+ indent() <<
+ "TField _field = new TField();" << endl <<
+ indent() <<
+ "_iprot.readStructBegin(_itrans, _struct);" << endl <<
+ indent() <<
+ "_iprot.readFieldBegin(_itrans, _field);" << endl <<
+ indent() <<
+ "_iprot.readString(_itrans, _fname);" << endl <<
+ indent() <<
+ "_iprot.readFieldEnd(_itrans);" << endl <<
+ indent() <<
+ "_iprot.readFieldBegin(_itrans, _field);" << endl;
+
+ bool first = true;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ if (!first) {
+ f_service_ << " else ";
+ } else {
+ f_service_ << indent();
+ first = false;
+ }
+ f_service_ <<
+ "if (_fname.value.equals(\"" << (*f_iter)->get_name() <<"\")) {" << endl;
+ indent_up();
+ indent(f_service_) <<
+ "process_" << (*f_iter)->get_name() <<
+ "(_itrans, _otrans);" << endl;
+ indent_down();
+ indent(f_service_) << "}";
+ }
+ f_service_ <<
+ " else {" << endl;
+ indent_up();
+ indent(f_service_) <<
+ "System.err.println" <<
+ "(\"Unknown function: '\" + _field.name + \"'\");" << endl;
+ indent_down();
+ indent(f_service_) <<
+ "}" << endl;
+
+ // Read end of args field, the T_STOP, and the struct close
+ f_service_ <<
+ indent() <<
+ "_iprot.readFieldEnd(_itrans);" << endl <<
+ indent() <<
+ "_iprot.readFieldBegin(_itrans, _field);" << endl <<
+ indent() <<
+ "_iprot.readStructEnd(_itrans);" << endl <<
+ indent() <<
+ "return true;" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ f_service_ <<
+ "}" << endl <<
+ endl;
+
+ f_service_.close();
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_java_generator::generate_process_function(t_service* tservice,
+ t_function* tfunction) {
+ // Open function
+ indent(f_service_) <<
+ "private void process_" << tfunction->get_name() <<
+ "(TTransport _itrans, TTransport _otrans) throws TException" << endl;
+ scope_up(f_service_);
+
+ // Get the struct of function call params
+ t_struct* arg_struct = tfunction->get_arglist();
+
+ // Declare the function arguments
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(f_service_) <<
+ declare_field(*f_iter, true) << endl;
+ }
+
+ // Deserialize the function arguments as a struct
+ generate_deserialize_struct(arg_struct);
+
+ // Generate the function call
+ f_service_ << indent();
+ if (!tfunction->get_returntype()->is_void()) {
+ f_service_ <<
+ type_name(tfunction->get_returntype()) << " _result = ";
+ }
+ f_service_ <<
+ tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ // Serialize the result into a struct
+ t_struct result_struct(tfunction->get_name() + "_result");
+ t_field result_field(tfunction->get_returntype(), "_result");
+
+ // Only append the field if non-void
+ if (!tfunction->get_returntype()->is_void()) {
+ result_struct.append(&result_field);
+ }
+ generate_serialize_struct(&result_struct);
+ indent(f_service_) <<
+ "_otrans.flush();" << endl;
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_java_generator::generate_deserialize_field(t_field* tfield,
+ string prefix) {
+ t_type* type = tfield->get_type();
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " +
+ prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct()) {
+ generate_deserialize_struct((t_struct*)(tfield->get_type()),
+ name + ".");
+ } else if (type->is_container()) {
+ generate_deserialize_container(tfield->get_type(), name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent(f_service_) <<
+ "_iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " +
+ name;
+ break;
+ case t_base_type::TYPE_STRING:
+ f_service_ << "readString(_itrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_BYTE:
+ f_service_ << "readByte(_itrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ f_service_ << "readI32(_itrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_U32:
+ f_service_ << "readU32(_itrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ f_service_ << "readI64(_itrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_U64:
+ f_service_ << "readU64(_itrans, " << name << ");";
+ break;
+ default:
+ throw "compiler error: no C++ name for base type " + tbase;
+ }
+ } else if (type->is_enum()) {
+ f_service_ << "readI32(_itrans, " << name << ");";
+ }
+ f_service_ <<
+ endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(), type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a variable. This makes two key assumptions,
+ * first that there is a const char* variable named data that points to the
+ * buffer for deserialization, and that there is a variable protocol which
+ * is a reference to a TProtocol serialization object.
+ */
+void t_java_generator::generate_deserialize_struct(t_struct* tstruct,
+ string prefix) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ scope_up(f_service_);
+
+ // Read the struct fields from the protocol
+ string _struct = tmp("_struct");
+ string _field = tmp("_field");
+
+ // Declare stack tmp variables
+ f_service_ <<
+ indent() << "TStruct " << _struct << " = new TStruct();" << endl <<
+ indent() << "TField " << _field << " = new TField();" << endl <<
+ indent() << "_iprot.readStructBegin(_itrans, " << _struct << ");" << endl;
+
+ // Loop over reading in fields
+ indent(f_service_) <<
+ "while (true)" << endl;
+
+ scope_up(f_service_);
+
+ // Read beginning field marker
+ indent(f_service_) <<
+ "_iprot.readFieldBegin(_itrans, " << _field << ");" << endl;
+
+ // Check for field STOP marker and break
+ indent(f_service_) <<
+ "if (" << _field << ".type.equals(TType.STOP)) { " << endl;
+ indent_up();
+ indent(f_service_) <<
+ "break;" << endl;
+ indent_down();
+ indent(f_service_) <<
+ "}" << endl;
+
+ // Switch statement on the field we are reading
+ indent(f_service_) <<
+ "switch ((int)" << _field << ".id.get())" << endl;
+
+ scope_up(f_service_);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(f_service_) <<
+ "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ generate_deserialize_field(*f_iter, prefix);
+ indent(f_service_) <<
+ "break;" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ f_service_ <<
+ indent() <<
+ "default:" << endl <<
+ indent() <<
+ " TProtocolUtil.skip(_iprot, _itrans, " <<
+ _field << ".type);" << endl <<
+ indent() <<
+ " break;" << endl;
+
+ scope_down(f_service_);
+
+ // Read field end marker
+ indent(f_service_) <<
+ "_iprot.readFieldEnd(_itrans);" << endl;
+
+ scope_down(f_service_);
+
+ indent(f_service_) <<
+ "_iprot.readStructEnd(_itrans);" << endl;
+
+ scope_down(f_service_);
+}
+
+void t_java_generator::generate_deserialize_container(t_type* ttype,
+ string prefix) {
+ scope_up(f_service_);
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ f_service_ <<
+ indent() << "TMap " << obj << " = new TMap();" << endl <<
+ indent() << "_iprot.readMapBegin(_itrans, " << obj << ");" << endl;
+ } else if (ttype->is_set()) {
+ f_service_ <<
+ indent() << "TSet " << obj << " = new TSet();" << endl <<
+ indent() << "_iprot.readSetBegin(_itrans, " << obj << ");" << endl;
+ } else if (ttype->is_list()) {
+ f_service_ <<
+ indent() << "TList " << obj << " = new TList();" << endl <<
+ indent() << "_iprot.readListBegin(_itrans, " << obj << ");" << endl;
+ }
+
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(f_service_) <<
+ "for (int " << i << " = 0; " <<
+ i << " < " << obj << ".size.get()" << "; " <<
+ "++" << i << ")" << endl;
+
+ scope_up(f_service_);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element((t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element((t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element((t_list*)ttype, prefix);
+ }
+
+ scope_down(f_service_);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(f_service_) << "_iprot.readMapEnd(_itrans);" << endl;
+ } else if (ttype->is_set()) {
+ indent(f_service_) << "_iprot.readSetEnd(_itrans);" << endl;
+ } else if (ttype->is_list()) {
+ indent(f_service_) << "_iprot.readListEnd(_itrans);" << endl;
+ }
+
+ scope_down(f_service_);
+}
+
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_java_generator::generate_deserialize_map_element(t_map* tmap,
+ string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(f_service_) <<
+ declare_field(&fkey, true) << endl;
+ indent(f_service_) <<
+ declare_field(&fval, true) << endl;
+
+ generate_deserialize_field(&fkey);
+ generate_deserialize_field(&fval);
+
+ indent(f_service_) <<
+ prefix << ".put(" << key << ", " << val << ");" << endl;
+}
+
+void t_java_generator::generate_deserialize_set_element(t_set* tset,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(f_service_) <<
+ declare_field(&felem, true) << endl;
+
+ generate_deserialize_field(&felem);
+
+ indent(f_service_) <<
+ prefix << ".add(" << elem << ");" << endl;
+}
+
+void t_java_generator::generate_deserialize_list_element(t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(f_service_) <<
+ declare_field(&felem, true) << endl;
+
+ generate_deserialize_field(&felem);
+
+ indent(f_service_) <<
+ prefix << ".add(" << elem << ");" << endl;
+}
+
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_java_generator::generate_serialize_field(t_field* tfield,
+ string prefix) {
+ t_type* type = tfield->get_type();
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " +
+ prefix + tfield->get_name();
+ }
+
+ if (type->is_struct()) {
+ generate_serialize_struct((t_struct*)(tfield->get_type()),
+ prefix + tfield->get_name() + ".");
+ } else if (type->is_container()) {
+ generate_serialize_container(tfield->get_type(),
+ prefix + tfield->get_name());
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + tfield->get_name();
+ indent(f_service_) <<
+ "_oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw
+ "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ f_service_ << "writeString(_otrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_BYTE:
+ f_service_ << "writeByte(_otrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ f_service_ << "writeI32(_otrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_U32:
+ f_service_ << "writeU32(_otrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ f_service_ << "writeI64(_otrans, " << name << ");";
+ break;
+ case t_base_type::TYPE_U64:
+ f_service_ << "writeU64(_otrans, " << name << ");";
+ break;
+ default:
+ throw "compiler error: no C++ name for base type " + tbase;
+ }
+ } else if (type->is_enum()) {
+ f_service_ << "writeI32(_otrans, " << name << ");";
+ }
+ f_service_ << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_java_generator::generate_serialize_struct(t_struct* tstruct,
+ string prefix) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ scope_up(f_service_);
+ string _struct = tmp("_struct");
+ string _field = tmp("_field");
+
+ f_service_ <<
+ indent() <<
+ "TStruct " << _struct << " = new TStruct(\"" << name << "\");" << endl <<
+ indent() <<
+ "TField " << _field << " = new TField();" << endl <<
+ indent() <<
+ "_oprot.writeStructBegin(_otrans, " << _struct << ");" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ f_service_ <<
+ indent() <<
+ _field << ".name = \"" << (*f_iter)->get_name() << "\";" << endl <<
+ indent() <<
+ _field << ".type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl <<
+ indent() <<
+ _field << ".id.set(" << (*f_iter)->get_key() << ");" << endl <<
+ indent() <<
+ "_oprot.writeFieldBegin(_otrans, " << _field << ");" << endl;
+ // Write field contents
+ generate_serialize_field(*f_iter, prefix);
+ // Write field closer
+ indent(f_service_) <<
+ "_oprot.writeFieldEnd(_otrans);" << endl;
+ }
+ // Write the struct map
+ f_service_ <<
+ indent() << "_oprot.writeFieldStop(_otrans);" << endl <<
+ indent() << "_oprot.writeStructEnd(_otrans);" << endl;
+
+ scope_down(f_service_);
+}
+
+void t_java_generator::generate_serialize_container(t_type* ttype,
+ string prefix) {
+ scope_up(f_service_);
+
+ if (ttype->is_map()) {
+ indent(f_service_) <<
+ "_oprot.writeMapBegin(_otrans, new TMap(" <<
+ type_to_enum(((t_map*)ttype)->get_key_type()) << ", " <<
+ type_to_enum(((t_map*)ttype)->get_val_type()) << ", " <<
+ prefix << ".size()));" << endl;
+ } else if (ttype->is_set()) {
+ indent(f_service_) <<
+ "_oprot.writeSetBegin(_otrans, new TSet(" <<
+ type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " <<
+ prefix << ".size()));" << endl;
+ } else if (ttype->is_list()) {
+ indent(f_service_) <<
+ "_oprot.writeListBegin(_otrans, new TList(" <<
+ type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " <<
+ prefix << ".size()));" << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map()) {
+ indent(f_service_) <<
+ "for (" <<
+ type_name(((t_map*)ttype)->get_key_type()) << " " << iter <<
+ " : " <<
+ prefix << ".keySet())";
+ } else if (ttype->is_set()) {
+ indent(f_service_) <<
+ "for (" <<
+ type_name(((t_set*)ttype)->get_elem_type()) << " " << iter <<
+ " : " <<
+ prefix << ")";
+ } else if (ttype->is_list()) {
+ indent(f_service_) <<
+ "for (" <<
+ type_name(((t_list*)ttype)->get_elem_type()) << " " << iter <<
+ " : " <<
+ prefix << ")";
+ }
+
+ scope_up(f_service_);
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element((t_map*)ttype, iter, prefix);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element((t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element((t_list*)ttype, iter);
+ }
+
+ if (ttype->is_map()) {
+ indent(f_service_) <<
+ "_oprot.writeMapEnd(_otrans);" << endl;
+ } else if (ttype->is_set()) {
+ indent(f_service_) <<
+ "_oprot.writeSetEnd(_otrans);" << endl;
+ } else if (ttype->is_list()) {
+ indent(f_service_) <<
+ "_oprot.writeListEnd(_otrans);" << endl;
+ }
+
+ scope_down(f_service_);
+
+ scope_down(f_service_);
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_java_generator::generate_serialize_map_element(t_map* tmap,
+ string iter,
+ string map) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(&kfield, "");
+
+ t_field vfield(tmap->get_val_type(), map + ".get(" + iter + ")");
+ generate_serialize_field(&vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_java_generator::generate_serialize_set_element(t_set* tset,
+ string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(&efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_java_generator::generate_serialize_list_element(t_list* tlist,
+ string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(&efield, "");
+}
+
+/**
+ * Returns a Java type name
+ *
+ * @param ttype The type
+ */
+string t_java_generator::type_name(t_type* ttype) {
+ // In Java typedefs are just resolved to their real type
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ if (ttype->is_base_type()) {
+ return base_type_name(((t_base_type*)ttype)->get_base());
+ } else if (ttype->is_enum()) {
+ return "Int32";
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*) ttype;
+ return "HashMap<" +
+ type_name(tmap->get_key_type()) + "," +
+ type_name(tmap->get_val_type()) + ">";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*) ttype;
+ return "HashSet<" + type_name(tset->get_elem_type()) + ">";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*) ttype;
+ return "ArrayList<" + type_name(tlist->get_elem_type()) + ">";
+ } else {
+ return ttype->get_name();
+ }
+}
+
+/**
+ * Returns the C++ type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ */
+string t_java_generator::base_type_name(t_base_type::t_base tbase) {
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ return "TString";
+ case t_base_type::TYPE_BYTE:
+ return "UInt8";
+ case t_base_type::TYPE_I32:
+ return "Int32";
+ case t_base_type::TYPE_U32:
+ return "UInt32";
+ case t_base_type::TYPE_I64:
+ return "Int64";
+ case t_base_type::TYPE_U64:
+ return "UInt64";
+ default:
+ throw "compiler error: no C++ name for base type " + tbase;
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_java_generator::declare_field(t_field* tfield, bool init) {
+ // TODO(mcslee): do we ever need to initialize the field?
+ string result = type_name(tfield->get_type()) + " " + tfield->get_name();
+ if (init) {
+ result += " = new " + type_name(tfield->get_type()) + "()";
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_java_generator::function_signature(t_function* tfunction,
+ string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ return
+ type_name(ttype) + " " + prefix + tfunction->get_name() +
+ "(" + argument_list(tfunction->get_arglist()) + ")";
+}
+
+/**
+ * Renders a field list
+ */
+string t_java_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type()) + " " + (*f_iter)->get_name();
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_java_generator::type_to_enum(t_type* type) {
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BYTE:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_U32:
+ return "TType.U32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_U64:
+ return "TType.U64";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
diff --git a/compiler/src/generate/t_java_generator.h b/compiler/src/generate/t_java_generator.h
new file mode 100644
index 0000000..ae96252
--- /dev/null
+++ b/compiler/src/generate/t_java_generator.h
@@ -0,0 +1,107 @@
+#ifndef T_JAVA_GENERATOR_H
+#define T_JAVA_GENERATOR_H
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include "t_oop_generator.h"
+
+// TODO(mcslee: Paramaterize the output dir
+#define T_JAVA_DIR "gen-java"
+
+/**
+ * Java code generator.
+ *
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+class t_java_generator : public t_oop_generator {
+ public:
+ t_java_generator() {}
+ ~t_java_generator() {}
+
+ /** Init and close methods */
+
+ void init_generator(t_program *tprogram);
+ void close_generator();
+
+ /** Program-level generation functions */
+
+ void generate_typedef (t_typedef* ttypedef);
+ void generate_enum (t_enum* tenum);
+ void generate_struct (t_struct* tstruct);
+ void generate_service (t_service* tservice);
+
+ /** Service-level generation functions */
+
+ void generate_service_interface (t_service* tservice);
+ void generate_service_client (t_service* tservice);
+ void generate_service_server (t_service* tservice);
+ void generate_process_function (t_service* tservice, t_function* tfunction);
+
+ /** Serialization constructs */
+
+ void generate_deserialize_field (t_field* tfield,
+ std::string prefix="");
+
+ void generate_deserialize_struct (t_struct* tstruct,
+ std::string prefix="");
+
+ void generate_deserialize_container (t_type* ttype,
+ std::string prefix="");
+
+ void generate_deserialize_set_element (t_set* tset,
+ std::string prefix="");
+
+ void generate_deserialize_map_element (t_map* tmap,
+ std::string prefix="");
+
+ void generate_deserialize_list_element (t_list* tlist,
+ std::string prefix="");
+
+ void generate_serialize_field (t_field* tfield,
+ std::string prefix="");
+
+ void generate_serialize_struct (t_struct* tstruct,
+ std::string prefix="");
+
+ void generate_serialize_container (t_type* ttype,
+ std::string prefix="");
+
+ void generate_serialize_map_element (t_map* tmap,
+ std::string iter,
+ std::string map);
+
+ void generate_serialize_set_element (t_set* tmap,
+ std::string iter);
+
+ void generate_serialize_list_element (t_list* tlist,
+ std::string iter);
+
+ /** Helper rendering functions */
+
+ std::string java_package();
+ std::string java_type_imports();
+ std::string java_thrift_imports();
+ std::string type_name(t_type* ttype);
+ std::string base_type_name(t_base_type::t_base tbase);
+ std::string declare_field(t_field* tfield, bool init=false);
+ std::string function_signature(t_function* tfunction, std::string prefix="");
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+
+ private:
+
+ /** File streams */
+
+ std::ofstream f_types_;
+ std::ofstream f_header_;
+ std::ofstream f_service_;
+
+ std::ofstream f_client_;
+ std::ofstream f_server_;
+
+};
+
+#endif
diff --git a/compiler/src/generate/t_oop_generator.h b/compiler/src/generate/t_oop_generator.h
new file mode 100644
index 0000000..7b7ada1
--- /dev/null
+++ b/compiler/src/generate/t_oop_generator.h
@@ -0,0 +1,44 @@
+#ifndef T_OOP_GENERATOR_H
+#define T_OOP_GENERATOR_H
+
+#include "globals.h"
+#include "t_generator.h"
+
+/**
+ * Class with utility methods shared across common object oriented languages.
+ *
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+class t_oop_generator : public t_generator {
+ public:
+
+ /** Scoping */
+
+ void scope_up(std::ostream& out) {
+ indent(out) << "{" << std::endl;
+ indent_up();
+ }
+
+ void scope_down(std::ostream& out) {
+ indent_down();
+ indent(out) << "}" << std::endl;
+ }
+
+ /**
+ * Generates a comment about this code being autogenerated.
+ *
+ * @return C-style comment mentioning that this file is autogenerated.
+ */
+ std::string autogen_comment() {
+ return
+ std::string("/**\n") +
+ " * Autogenerated by Thrift\n" +
+ " * " + g_time_str +
+ " *\n" +
+ " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" +
+ " */\n";
+ }
+};
+
+#endif
+
diff --git a/compiler/src/main.cc b/compiler/src/main.cc
index 8d8263c..7f87368 100644
--- a/compiler/src/main.cc
+++ b/compiler/src/main.cc
@@ -19,6 +19,7 @@
#include "main.h"
#include "parse/t_program.h"
#include "generate/t_cpp_generator.h"
+#include "generate/t_java_generator.h"
using namespace std;
@@ -91,7 +92,13 @@
* Diplays the usage message and then exits with an error code.
*/
void usage() {
- fprintf(stderr, "Usage: thrift [-d] <filename>\n");
+ fprintf(stderr, "Usage: thrift [options] file\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -cpp Generate C++ output files\n");
+ fprintf(stderr, " -java Generate Java output files\n");
+ //fprintf(stderr, " -php Generate PHP output files\n");
+ //fprintf(stderr, " -python Generate Python output files\n");
+ fprintf(stderr, " -d Print parse debugging to standard output\n");
exit(1);
}
@@ -100,23 +107,36 @@
*/
int main(int argc, char** argv) {
int i;
+ bool gen_cpp = false;
+ bool gen_java = false;
+
+ // Setup time string
+ time_t now = time(NULL);
+ g_time_str = ctime(&now);
// Check for necessary arguments
- if (argc < 2) usage();
+ if (argc < 2) {
+ usage();
+ }
for (i = 1; i < argc-1; i++) {
if (strcmp(argv[i], "-d") == 0) {
g_debug = 1;
+ } else if (strcmp(argv[i], "-cpp") == 0) {
+ gen_cpp = true;
+ } else if (strcmp(argv[i], "-java") == 0) {
+ gen_java = true;
} else {
fprintf(stderr, "!!! Unrecognized option: %s\n", argv[i]);
usage();
}
}
- // Setup time string
- time_t now = time(NULL);
- g_time_str = ctime(&now);
-
+ if (!gen_cpp && !gen_java) {
+ fprintf(stderr, "!!! No output language(s) specified\n\n");
+ usage();
+ }
+
// Open input file
char* input_file = argv[i];
yyin = fopen(input_file, "r");
@@ -144,9 +164,18 @@
// Generate code
try {
- t_cpp_generator* cpp = new t_cpp_generator();
- cpp->generate_program(g_program);
- delete cpp;
+ if (gen_cpp) {
+ t_cpp_generator* cpp = new t_cpp_generator();
+ cpp->generate_program(g_program);
+ delete cpp;
+ }
+
+ if (gen_java) {
+ t_java_generator* java = new t_java_generator();
+ java->generate_program(g_program);
+ delete java;
+ }
+
} catch (string s) {
printf("Error: %s\n", s.c_str());
} catch (const char* exc) {
@@ -157,6 +186,5 @@
delete g_program;
// Finished
- printf("\nDone!\n");
return 0;
}
diff --git a/compiler/src/parse/t_field.h b/compiler/src/parse/t_field.h
index 304bb16..fc38456 100644
--- a/compiler/src/parse/t_field.h
+++ b/compiler/src/parse/t_field.h
@@ -11,6 +11,9 @@
*/
class t_field {
public:
+ t_field(t_type* type, std::string name) :
+ type_(type), name_(name), key_(0) {}
+
t_field(t_type* type, std::string name, uint32_t key) :
type_(type), name_(name), key_(key) {}
diff --git a/compiler/src/parse/t_function.h b/compiler/src/parse/t_function.h
index 1caf54a..b248db7 100644
--- a/compiler/src/parse/t_function.h
+++ b/compiler/src/parse/t_function.h
@@ -19,29 +19,10 @@
~t_function() {}
- /**
- * Implementation of the Fowler / Noll / Vo (FNV) Hash
- * http://www.isthe.com/chongo/tech/comp/fnv/
- */
- static uint32_t fnv32(const char *s) {
- uint32_t hash = (uint32_t)216613626;
- while (*s) {
- hash +=
- (hash << 1) +
- (hash << 4) +
- (hash << 7) +
- (hash << 8) +
- (hash << 24);
- hash ^= *s++;
- }
- return hash;
- }
-
t_type* get_returntype() const { return returntype_; }
const std::string& get_name() const { return name_; }
t_struct* get_arglist() const { return arglist_; }
- uint32_t get_hash() const { return fnv32(name_.c_str()); }
-
+
private:
t_type* returntype_;
std::string name_;
diff --git a/compiler/src/parse/t_struct.h b/compiler/src/parse/t_struct.h
index 38eb750..6e87c8c 100644
--- a/compiler/src/parse/t_struct.h
+++ b/compiler/src/parse/t_struct.h
@@ -10,6 +10,8 @@
class t_struct : public t_type {
public:
t_struct() {}
+ t_struct(const std::string& name) : t_type(name) {}
+
~t_struct() {}
/** Set the struct name */
diff --git a/compiler/src/thrift.l b/compiler/src/thrift.l
index 61a0ce3..33cbda8 100644
--- a/compiler/src/thrift.l
+++ b/compiler/src/thrift.l
@@ -49,7 +49,6 @@
"typedef" { return tok_typedef; }
"struct" { return tok_struct; }
-"function" { return tok_function; }
"service" { return tok_service; }
"enum" { return tok_enum; }
diff --git a/compiler/src/thrift.y b/compiler/src/thrift.y
index a406c8e..269d163 100644
--- a/compiler/src/thrift.y
+++ b/compiler/src/thrift.y
@@ -56,7 +56,6 @@
/** Thrift actions */
%token tok_typedef
%token tok_struct
-%token tok_function
%token tok_service
%token tok_enum
@@ -213,6 +212,7 @@
Function:
FunctionType FunctionModifiers tok_identifier '(' FieldList ')'
{
+ $5->set_name(std::string($3) + "_args");
$$ = new t_function($1, $3, $5);
y_field_val = 0;
}