From: Mark Slee Date: Wed, 7 Jun 2006 06:46:24 +0000 (+0000) Subject: Thrift compiler now compiles both native Java and C++ code X-Git-Tag: 0.2.0~1759 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=b15a68bd89428630ac1775812082a7c4a034c3b7;p=common%2Fthrift.git 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 62ce2752..49833cb6 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -35,7 +35,8 @@ BIN_DIR = bin/ # 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 @@ CFL = -g -Wall -I$(SRC_DIR) -I$(OBJ_DIR) # 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 +$(OBJ_DIR)lex.yy.o: $(GEN_DIR)lex.yy.cc + $(CC) $(CFL) -c -o $@ $(GEN_DIR)lex.yy.cc # 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 $(OBJ_DIR)thrift.tab.o: $(GEN_DIR)thrift.tab.cc $(CC) $(CFL) -c -o $@ $(GEN_DIR)thrift.tab.cc @@ -84,15 +87,10 @@ $(OBJS): $(SRCS) $(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 e47c13b2..18cfa312 100644 --- a/compiler/src/generate/t_cpp_generator.cc +++ b/compiler/src/generate/t_cpp_generator.cc @@ -2,7 +2,6 @@ #include #include #include "t_cpp_generator.h" -#include "globals.h" using namespace std; /** @@ -146,7 +145,8 @@ void t_cpp_generator::generate_service(t_service* tservice) { "#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 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { "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 functions = tservice->get_functions(); - vector::iterator f_iter; + vector::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 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { " 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 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { * @param tservice The service to generate a server for. */ void t_cpp_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::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(); - f_header_ << - "};" << endl << endl; + indent() << "TProtocol* _iprot;" << endl << + indent() << "TProtocol* _oprot;" << endl; + indent_down(); - // Generate the dispatch methods - vector functions = tservice->get_functions(); - vector::iterator f_iter; + // Process function declarations + f_header_ << + " private:" << endl; + indent_up(); for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_dispatch_function(tservice, *f_iter); + indent(f_header_) << + "void process_" << (*f_iter)->get_name() << + "(TTransport* _itrans, TTransport* _otrans);" << endl; } + indent_down(); + f_header_ << + "};" << endl << endl; // 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 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { "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 @@ void t_cpp_generator::generate_dispatch_function(t_service* tservice, } // 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 @@ void t_cpp_generator::generate_dispatch_function(t_service* tservice, 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 @@ void t_cpp_generator::generate_dispatch_function(t_service* tservice, } f_service_ << ");" << endl; - // Serialize the result into _sbuf - indent(f_service_) << - "std::string _sbuf = \"\";" << endl; - - t_field result_field(tfunction->get_returntype(), "_result", 0); - generate_serialize_field("_sbuf", &result_field); + // Serialize the result into a struct + t_struct result_struct(tfunction->get_name() + "_result"); + t_field result_field(tfunction->get_returntype(), "_result"); - // Return the serialized buffer + // 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_) << - "return _sbuf;" << endl; + "_otrans->flush();" << 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 @@ void t_cpp_generator::generate_deserialize_field(string src, } 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 @@ void t_cpp_generator::generate_deserialize_field(string src, * 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& fields = tstruct->get_members(); vector::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"); - indent(f_service_) << - "std::map " << _struct << - " = _protocol->readStruct(" << src << ");" << endl; + 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::iterator _field;" << endl; - - // Decleare a temp buffer for working with fields - string fbuf = tmp("_fbuf"); + "while (true)" << endl; - // Iterate over the fields and extract them - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - // Check for existence of the field + scope_up(f_service_); + + // Read beginning field marker indent(f_service_) << - "if ((_field = " << _struct << ".find(" << (*f_iter)->get_key() << - ")) != " << _struct << ".end()) {" << - endl; - indent_up(); - - // Copy the field buffer into temp buffer + "_iprot->readFieldBegin(_itrans, " << + fname << ", " << ftype << ", " << fid << ");" << endl; + + // Check for field STOP marker indent(f_service_) << - "TBuf& " << fbuf << " = _field->second;" << endl; - - // Deserialize from the field's buffer - generate_deserialize_field(fbuf, *f_iter, prefix); + "if (" << ftype << " == T_STOP) { break; }" << endl; + + // Switch statement on the field we are reading + indent(f_service_) << + "switch (" << fid << ")" << endl; - indent_down(); + 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_) << - "}" << endl; - } + "_iprot->readFieldEnd(_itrans);" << endl; + + scope_down(f_service_); + + indent(f_service_) << + "_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 @@ void t_cpp_generator::generate_deserialize_container(string src, /** * 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 @@ void t_cpp_generator::generate_deserialize_list_element(string src, * @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 @@ void t_cpp_generator::generate_serialize_field(string dest, * @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& fields = tstruct->get_members(); vector::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 " << _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 @@ void t_cpp_generator::generate_serialize_container(string dest, 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 @@ void t_cpp_generator::generate_serialize_container(string dest, * 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 @@ string t_cpp_generator::argument_list(t_struct* tstruct) { } 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 079a26dd..801c06c1 100644 --- a/compiler/src/generate/t_cpp_generator.h +++ b/compiler/src/generate/t_cpp_generator.h @@ -6,16 +6,17 @@ #include #include -#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 */ -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 @@ class t_cpp_generator : public t_generator { 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 00000000..e4236fc7 --- /dev/null +++ b/compiler/src/generate/t_java_generator.cc @@ -0,0 +1,1118 @@ +#include +#include +#include +#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 constants = tenum->get_constants(); + vector::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& members = tstruct->get_members(); + vector::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 functions = tservice->get_functions(); + vector::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 functions = tservice->get_functions(); + vector::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 functions = tservice->get_functions(); + vector::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& fields = arg_struct->get_members(); + vector::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& fields = tstruct->get_members(); + vector::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& fields = tstruct->get_members(); + vector::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& fields = tstruct->get_members(); + vector::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 00000000..ae96252f --- /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 +#include +#include +#include + +#include "t_oop_generator.h" + +// TODO(mcslee: Paramaterize the output dir +#define T_JAVA_DIR "gen-java" + +/** + * Java code generator. + * + * @author Mark Slee + */ +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 00000000..7b7ada1a --- /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 + */ +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 8d8263c1..7f87368b 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 @@ void failure(char* fmt, ...) { * Diplays the usage message and then exits with an error code. */ void usage() { - fprintf(stderr, "Usage: thrift [-d] \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 @@ void usage() { */ 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 @@ int main(int argc, char** argv) { // 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 @@ int main(int argc, char** argv) { 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 304bb168..fc384563 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 1caf54a3..b248db7c 100644 --- a/compiler/src/parse/t_function.h +++ b/compiler/src/parse/t_function.h @@ -19,29 +19,10 @@ class t_function { ~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 38eb7500..6e87c8c6 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 61a0ce3c..33cbda84 100644 --- a/compiler/src/thrift.l +++ b/compiler/src/thrift.l @@ -49,7 +49,6 @@ symbol ([\,\{\}\(\)\=<>]) "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 a406c8e0..269d1632 100644 --- a/compiler/src/thrift.y +++ b/compiler/src/thrift.y @@ -56,7 +56,6 @@ int y_field_val = 0; /** Thrift actions */ %token tok_typedef %token tok_struct -%token tok_function %token tok_service %token tok_enum @@ -213,6 +212,7 @@ FunctionList: Function: FunctionType FunctionModifiers tok_identifier '(' FieldList ')' { + $5->set_name(std::string($3) + "_args"); $$ = new t_function($1, $3, $5); y_field_val = 0; }