From 9cb7c61b6b2c88bf41a23d1c4e9757c81d27d8ef Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Fri, 1 Sep 2006 22:17:45 +0000 Subject: [PATCH] Thrift compiler improvements, two modes for PHP Summary: Complete PHP generator and CPP generator to new formats, and offer PHP generator that generates inline code free of any TProtocol abstraction git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664771 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/{cpp.mk => Makefile} | 0 compiler/cpp/src/generate/t_cpp_generator.cc | 926 ++++++++++------ compiler/cpp/src/generate/t_cpp_generator.h | 20 +- compiler/cpp/src/generate/t_generator.cc | 9 +- compiler/cpp/src/generate/t_generator.h | 13 +- compiler/cpp/src/generate/t_java_generator.cc | 22 +- compiler/cpp/src/generate/t_java_generator.h | 2 +- compiler/cpp/src/generate/t_php_generator.cc | 993 +++++++++++------- compiler/cpp/src/generate/t_php_generator.h | 69 +- compiler/cpp/src/main.cc | 20 +- compiler/cpp/src/parse/t_base_type.h | 5 +- compiler/cpp/src/parse/t_field.h | 6 +- compiler/cpp/src/parse/t_function.h | 16 + compiler/cpp/src/parse/t_program.h | 55 +- compiler/cpp/src/parse/t_struct.h | 31 +- compiler/cpp/src/parse/t_type.h | 1 + compiler/cpp/src/thrift.l | 16 +- compiler/cpp/src/thrift.y | 100 +- compiler/py/setup.py | 14 +- 19 files changed, 1495 insertions(+), 823 deletions(-) rename compiler/cpp/{cpp.mk => Makefile} (100%) diff --git a/compiler/cpp/cpp.mk b/compiler/cpp/Makefile similarity index 100% rename from compiler/cpp/cpp.mk rename to compiler/cpp/Makefile diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc index 6014e5f4..43f80213 100644 --- a/compiler/cpp/src/generate/t_cpp_generator.cc +++ b/compiler/cpp/src/generate/t_cpp_generator.cc @@ -15,7 +15,7 @@ void t_cpp_generator::init_generator(t_program* tprogram) { mkdir(T_CPP_DIR, S_IREAD | S_IWRITE | S_IEXEC); // Make output file - string f_types_name = string(T_CPP_DIR)+"/"+program_name_+"Types.h"; + string f_types_name = string(T_CPP_DIR)+"/"+program_name_+"_types.h"; f_types_.open(f_types_name.c_str()); // Print header @@ -30,20 +30,75 @@ void t_cpp_generator::init_generator(t_program* tprogram) { // Include base types f_types_ << - "#include \"Thrift.h\"" << endl << + "#include " << endl << + endl; + + string open_ns = namespace_open(tprogram->get_namespace()); + + f_types_ << + open_ns << endl << + endl; + + // Make output files + string f_header_name = string(T_CPP_DIR)+"/"+program_name_+".h"; + f_header_.open(f_header_name.c_str()); + string f_service_name = string(T_CPP_DIR)+"/"+program_name_+".cc"; + f_service_.open(f_service_name.c_str()); + + // Print header file includes + f_header_ << + autogen_comment(); + f_header_ << + "#ifndef " << program_name_ << "_H" << endl << + "#define " << program_name_ << "_H" << endl << + endl << + "#include " << endl << + "#include " << endl << + "#include " << endl << + "#include " << endl << + "#include \"" << program_name_ << "_types.h\"" << endl << + endl << + open_ns << endl << + endl; + + // Service implementation file includes + f_service_ << + autogen_comment(); + f_service_ << + "#include \"" << program_name_ << ".h\"" << endl << + endl << + open_ns << endl << endl; } /** * Closes the output files. */ -void t_cpp_generator::close_generator() { +void t_cpp_generator::close_generator(t_program* tprogram) { + // Close ns + string close_ns = namespace_close(tprogram->get_namespace()); + f_types_ << + close_ns << endl << + endl; + f_header_ << + close_ns << endl << + endl; + f_service_ << + close_ns << endl << + endl; + // Close ifndef f_types_ << "#endif" << endl; + f_header_ << + "#endif" << endl; // Close output file f_types_.close(); + + // Close files + f_header_.close(); + f_service_.close(); } @@ -105,23 +160,302 @@ void t_cpp_generator::generate_enum(t_enum* tenum) { * @param tstruct The struct definition */ void t_cpp_generator::generate_struct(t_struct* tstruct) { - f_types_ << - indent() << "struct " << tstruct->get_name() << " {" << endl; + generate_struct_definition(f_types_, tstruct); + generate_struct_reader(f_service_, tstruct); + generate_struct_writer(f_service_, tstruct); +} + +/** + * Writes the struct def. + * + * @param out Output stream + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_definition(ofstream& out, + t_struct* tstruct) { + // Open struct def + out << + indent() << "typedef struct _" << tstruct->get_name() << " {" << endl; indent_up(); - const vector& members = tstruct->get_members(); + // Get members vector::const_iterator m_iter; + const vector& members = tstruct->get_members(); + + // Default constructor + bool init_ctor = false; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = (*m_iter)->get_type(); + while (t->is_typedef()) { + t = ((t_typedef*)t)->get_type(); + } + if (t->is_base_type() && + ((t_base_type*)t)->get_base() != t_base_type::TYPE_STRING) { + if (!init_ctor) { + init_ctor = true; + indent(out) << + "_" << tstruct->get_name() << "() : "; + out << (*m_iter)->get_name() << "(0)"; + } else + out << ", " << (*m_iter)->get_name() << "(0)"; + } + } + if (init_ctor) { + out << " {} " << endl; + } + + // Declare all fields for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - indent(f_types_) << + indent(out) << declare_field(*m_iter) << endl; } + + // Isset vector + if (members.size() > 0) { + indent(out) << + "struct __isset {" << endl; + indent_up(); + + indent(out) << + "__isset() : "; + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (first) { + first = false; + out << + (*m_iter)->get_name() << "(false)"; + } else { + out << + ", " << (*m_iter)->get_name() << "(false)"; + } + } + out << " {}" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << + "bool " << (*m_iter)->get_name() << ";" << endl; + } + indent_down(); + indent(out) << + "} __isset;" << endl; + } + + indent_down(); + indent(out) << + "} " << tstruct->get_name() << ";" << endl << + endl; +} + +/** + * Makes a helper function to gen a struct reader. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_reader(ofstream& out, + t_struct* tstruct) { + indent(out) << + "uint32_t read_struct_" << tstruct->get_name() << "(" << + "boost::shared_ptr iprot, " << + "boost::shared_ptr itrans, " << + tstruct->get_name() << "& value) {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Declare stack tmp variables + out << + endl << + indent() << "uint32_t xfer = 0;" << endl << + indent() << "std::string fname;" << endl << + indent() << "facebook::thrift::protocol::TType ftype;" << endl << + indent() << "int16_t fid;" << endl << + endl << + indent() << "xfer += iprot->readStructBegin(itrans, fname);" << endl << + endl; + // Loop over reading in fields + indent(out) << + "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << + "xfer += iprot->readFieldBegin(itrans, fname, ftype, fid);" << endl; + + // Check for field STOP marker + out << + indent() << "if (ftype == facebook::thrift::protocol::T_STOP) { " << endl << + indent() << " break;" << endl << + indent() << "}" << endl; + + // Switch statement on the field we are reading + indent(out) << + "switch (fid)" << endl; + + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << + "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + generate_deserialize_field(*f_iter, "value."); + out << + indent() << "value.__isset." << (*f_iter)->get_name() << " = true;" << endl << + indent() << "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + out << + indent() << "default:" << endl << + indent() << " xfer += iprot->skip(itrans, ftype);" << endl << + indent() << " break;" << endl; + + scope_down(out); + + // Read field end marker + indent(out) << + "xfer += iprot->readFieldEnd(itrans);" << endl; + + scope_down(out); + + out << + endl << + indent() << "xfer += iprot->readStructEnd(itrans);" << endl << + indent() <<"return xfer;" << endl; + indent_down(); - - f_types_ << - indent() << "};" << endl << + indent(out) << + "}" << endl << endl; +} + +/** + * Makes a helper function to gen a struct writer. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_writer(ofstream& out, + t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << + "uint32_t write_struct_" << name << "(" << + "boost::shared_ptr oprot, " << + "boost::shared_ptr otrans, " << + "const " << name << "& value) {" << endl; + indent_up(); + + out << + endl << + indent() << "uint32_t xfer = 0;" << endl << endl; + + indent(out) << + "xfer += oprot->writeStructBegin(otrans, \"" << name << "\");" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // Write field header + out << + endl << + indent() << "xfer += 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, "value."); + // Write field closer + indent(out) << + "xfer += oprot->writeFieldEnd(otrans);" << endl << + endl; + } + // Write the struct map + out << + indent() << "xfer += oprot->writeFieldStop(otrans);" << endl << + endl << + indent() << "xfer += oprot->writeStructEnd(otrans);" << endl << + indent() << "return xfer;" << endl; + + indent_down(); + indent(out) << + "}" << endl << endl; +} + +/** + * Struct writer for result of a function, which can have only one of its + * fields set and does a conditional if else look up into the __isset field + * of the struct. + * + * @param out Output stream + * @param tstruct The result struct + */ +void t_cpp_generator::generate_struct_result_writer(ofstream& out, + t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << + "uint32_t write_struct_" << name << "(" << + "boost::shared_ptr oprot, " << + "boost::shared_ptr otrans, " << + "const " << name << "& value) {" << endl; + indent_up(); + + out << + endl << + indent() << "uint32_t xfer = 0;" << endl << + endl; + + indent(out) << + "xfer += oprot->writeStructBegin(otrans, \"" << name << "\");" << endl; + + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + out << + endl << + indent() << "if "; + } else { + out << + " else if "; + } + + out << "(value.__isset." << (*f_iter)->get_name() << ") {" << endl; + + indent_up(); + + // Write field header + out << + indent() << "xfer += 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, "value."); + // Write field closer + indent(out) << "xfer += oprot->writeFieldEnd(otrans);" << endl; + + indent_down(); + indent(out) << "}"; + } + + // Write the struct map + out << + endl << + indent() << "xfer += oprot->writeFieldStop(otrans);" << endl << + indent() << "xfer += oprot->writeStructEnd(otrans);" << endl << + indent() << "return xfer;" << endl; + + indent_down(); + indent(out) << + "}" << endl << endl; } /** @@ -133,40 +467,27 @@ void t_cpp_generator::generate_struct(t_struct* tstruct) { * @param tservice The service definition */ void t_cpp_generator::generate_service(t_service* tservice) { - // Make output files - string f_header_name = string(T_CPP_DIR)+"/"+service_name_+".h"; - f_header_.open(f_header_name.c_str()); - string f_service_name = string(T_CPP_DIR)+"/"+service_name_+".cc"; - f_service_.open(f_service_name.c_str()); - - // Print header file includes - f_header_ << - autogen_comment(); - f_header_ << - "#ifndef " << service_name_ << "_H" << endl << - "#define " << service_name_ << "_H" << endl << - endl << - "#include \"TProcessor.h\"" << endl << - "#include \"transport/TTransport.h\"" << endl << - "#include \"protocol/TProtocol.h\"" << endl << - "#include \"" << program_name_ << "Types.h\"" << endl << - endl; - f_service_ << - autogen_comment(); - f_service_ << - "#include \"" << service_name_ << ".h\"" << endl << endl; - - // Generate the three main parts of the service generate_service_interface(tservice); + generate_service_helpers(tservice); generate_service_server(tservice); generate_service_client(tservice); +} - f_header_ << - "#endif" << endl; - - // Close files - f_header_.close(); - f_service_.close(); +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_struct_definition(f_service_, ts); + generate_struct_reader(f_service_, ts); + generate_struct_writer(f_service_, ts); + generate_function_helpers(*f_iter); + } } /** @@ -179,14 +500,14 @@ void t_cpp_generator::generate_service_interface(t_service* tservice) { "class " << service_name_ << "If {" << endl << " public: " << endl; indent_up(); + f_header_ << + indent() << "virtual ~" << service_name_ << "If() {}" << endl; vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_header_ << indent() << "virtual " << function_signature(*f_iter) << " = 0;" << endl; } - f_header_ << - indent() << "virtual ~" << service_name_ << "If() {}" << endl; indent_down(); f_header_ << "}; " << endl << endl; @@ -207,13 +528,13 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { indent_up(); f_header_ << indent() << service_name_ << "Client" << - "(boost::shared_ptr trans, boost::shared_ptr prot) : " << + "(boost::shared_ptr trans, boost::shared_ptr prot) : " << "_itrans(trans), _otrans(trans), " << "_iprot(prot), _oprot(prot) {}" << endl; f_header_ << indent() << service_name_ << "Client" << "(boost::shared_ptr itrans, boost::shared_ptr otrans," << - " boost::shared_ptr iprot, boost::shared_ptr oprot) : " << + " boost::shared_ptr iprot, boost::shared_ptr oprot) : " << "_itrans(itrans), _otrans(otrans), " << "_iprot(iprot), _oprot(oprot) {}" << endl; @@ -241,8 +562,8 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { f_header_ << indent() << "boost::shared_ptr _itrans;" << endl << indent() << "boost::shared_ptr _otrans;" << endl << - indent() << "boost::shared_ptr _iprot;" << endl << - indent() << "boost::shared_ptr _oprot;" << endl; + indent() << "boost::shared_ptr _iprot;" << endl << + indent() << "boost::shared_ptr _oprot;" << endl; indent_down(); f_header_ << @@ -294,39 +615,36 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { string("send_") + (*f_iter)->get_name(), (*f_iter)->get_arglist()); - // Open function + // Open the send function indent(f_service_) << function_signature(&send_function, scope) << endl; scope_up(f_service_); + string argsname = (*f_iter)->get_name() + "_args"; + string resultname = (*f_iter)->get_name() + "_result"; + // Serialize the request f_service_ << - indent() << - "_oprot->writeStructBegin(_otrans, \"function\");" << endl << - indent() << - "_oprot->writeFieldBegin(_otrans, \"name\", facebook::thrift::protocol::T_STRING, 0);" << endl << - indent() << - "_oprot->writeString(_otrans, \"" << funname << "\");" << endl << - indent() << - "_oprot->writeFieldEnd(_otrans);" << endl << - indent() << - "_oprot->writeFieldBegin(_otrans, \"args\", facebook::thrift::protocol::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_) << - "_otrans->flush();" << endl; + indent() << "int32_t cseqid = 0;" << endl << + indent() << "_oprot->writeMessageBegin(_otrans, \"" << (*f_iter)->get_name() << "\", facebook::thrift::protocol::T_CALL, cseqid);" << endl << + endl << + indent() << argsname << " __args;" << endl; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << + indent() << "__args." << (*fld_iter)->get_name() << " = " << (*fld_iter)->get_name() << ";" << endl; + } + + f_service_ << + indent() << "write_struct_" << argsname << "(_oprot, _otrans, __args);" << endl << + endl << + indent() << "_oprot->writeMessageEnd(_otrans);" << endl << + indent() << "_otrans->flush();" << endl; + scope_down(f_service_); f_service_ << endl; + // Generate recv function only if not an async function if (!(*f_iter)->is_async()) { t_struct noargs; t_function recv_function((*f_iter)->get_returntype(), @@ -337,34 +655,63 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { function_signature(&recv_function, scope) << endl; scope_up(f_service_); - // Read the response - t_struct result_struct((*f_iter)->get_name() + "_result"); - t_field result_field((*f_iter)->get_returntype(), "_result"); - + f_service_ << + endl << + indent() << "int32_t rseqid = 0;" << endl << + indent() << "std::string fname;" << endl << + indent() << "facebook::thrift::protocol::TMessageType mtype;" << endl << + endl << + indent() << "_iprot->readMessageBegin(_itrans, fname, mtype, rseqid);" << endl << + indent() << "if (mtype != facebook::thrift::protocol::T_REPLY || fname.compare(\"" << (*f_iter)->get_name() << "\") != 0) {" << endl; + indent_up(); + f_service_ << + indent() << "throw facebook::thrift::Exception(\"Unexpected message type, name, or id\");" << endl; + indent_down(); + f_service_ << + indent() << "}" << endl; + + f_service_ << + endl << + indent() << resultname << " __result;" << endl; + // Add a field to the return struct if non void + f_service_ << + indent() << "read_struct_" << resultname << "(_iprot, _itrans, __result);" << endl << + indent() << "_iprot->readMessageEnd(_itrans);" << endl << + endl; + + + // Careful, only return _result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { - indent(f_service_) << - type_name((*f_iter)->get_returntype()) << " _result;" << endl; - result_struct.append(&result_field); + f_service_ << + indent() << "if (__result.__isset.success) {" << endl << + indent() << " return __result.success;" << endl << + indent() << "}" << endl; } - - // Deserialize response struct - generate_deserialize_struct(&result_struct); - + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << + indent() << "if (__result.__isset." << (*x_iter)->get_name() << ") {" << endl << + indent() << " throw __result." << (*x_iter)->get_name() << ";" << endl << + indent() << "}" << endl; + } + // Careful, only return _result if not a void function - if (!(*f_iter)->get_returntype()->is_void()) { - indent(f_service_) << - "return _result;" << endl; - } else { + if ((*f_iter)->get_returntype()->is_void()) { indent(f_service_) << "return;" << endl; + } else { + f_service_ << + indent() << "throw facebook::thrift::Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; } - + // Close function scope_down(f_service_); f_service_ << endl; } - } } @@ -387,10 +734,10 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { indent_up(); f_header_ << indent() << - service_name_ << "ServerIf(boost::shared_ptr protocol) : " << + service_name_ << "ServerIf(boost::shared_ptr protocol) : " << "_iprot(protocol), _oprot(protocol) {}" << endl << indent() << - service_name_ << "ServerIf(boost::shared_ptr iprot, boost::shared_ptr oprot) : " << + service_name_ << "ServerIf(boost::shared_ptr iprot, boost::shared_ptr oprot) : " << "_iprot(iprot), _oprot(oprot) {}" << endl << indent() << "bool process(boost::shared_ptr _itrans, " << "boost::shared_ptr _otrans);" << endl << @@ -402,8 +749,8 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { " protected:" << endl; indent_up(); f_header_ << - indent() << "boost::shared_ptr _iprot;" << endl << - indent() << "boost::shared_ptr _oprot;" << endl; + indent() << "boost::shared_ptr _iprot;" << endl << + indent() << "boost::shared_ptr _oprot;" << endl; indent_down(); // Process function declarations @@ -413,7 +760,7 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { indent(f_header_) << "void process_" << (*f_iter)->get_name() << - "(boost::shared_ptr _itrans, boost::shared_ptr _otrans);" << endl; + "(int32_t seqid, boost::shared_ptr _itrans, boost::shared_ptr _otrans);" << endl; } indent_down(); f_header_ << @@ -422,29 +769,22 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { // Generate the server implementation f_service_ << "bool " << service_name_ << "ServerIf::" << - "process(boost::shared_ptr _itrans, boost::shared_ptr _otrans) {" << endl; + "process(boost::shared_ptr itrans, boost::shared_ptr otrans) {" << endl; indent_up(); f_service_ << - indent() << - "std::string _name;" << endl << - indent() << - "std::string _fname;" << endl << - indent() << - "facebook::thrift::protocol::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; - + endl << + indent() << "std::string fname;" << endl << + indent() << "facebook::thrift::protocol::TMessageType mtype;" << endl << + indent() << "int32_t seqid;" << endl << + endl << + indent() << "_iprot->readMessageBegin(itrans, fname, mtype, seqid);" << endl << + endl << + indent() << "if (mtype != facebook::thrift::protocol::T_CALL) {" << endl << + indent() << " throw facebook::thrift::Exception(\"Unexpected message type\");" << endl << + indent() << "}" << endl << + endl; + bool first = true; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { if (!first) { @@ -454,11 +794,11 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { first = false; } f_service_ << - "if (_fname.compare(\"" << (*f_iter)->get_name() <<"\") == 0) {" << endl; + "if (fname.compare(\"" << (*f_iter)->get_name() <<"\") == 0) {" << endl; indent_up(); indent(f_service_) << "process_" << (*f_iter)->get_name() << - "(_itrans, _otrans);" << endl; + "(seqid, itrans, otrans);" << endl; indent_down(); indent(f_service_) << "}"; } @@ -466,22 +806,14 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { " else {" << endl; indent_up(); indent(f_service_) << - "fprintf(stderr, \"Unknown function: '%s'\\n\", " << - "_fname.c_str());" << endl; + "throw facebook::thrift::Exception(\"Unknown function name: '\"+fname+\"'\");" << 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, _name, _ftype, _fid);" << endl << - indent() << - "_iprot->readStructEnd(_itrans);" << endl << - indent() << - "return true;" << endl; + indent() << "return true;" << endl; indent_down(); f_service_ << @@ -494,6 +826,30 @@ void t_cpp_generator::generate_service_server(t_service* tservice) { } } +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_cpp_generator::generate_function_helpers(t_function* tfunction) { + t_struct result(tfunction->get_name() + "_result"); + t_field success(tfunction->get_returntype(), "success", 0); + if (!tfunction->get_returntype()->is_void()) { + result.append(&success); + } + + t_struct* xs = tfunction->get_xceptions(); + const vector& fields = xs->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + result.append(*f_iter); + } + + generate_struct_definition(f_service_, &result); + generate_struct_reader(f_service_, &result); + generate_struct_result_writer(f_service_, &result); +} + /** * Generates a process function definition. * @@ -505,27 +861,37 @@ void t_cpp_generator::generate_process_function(t_service* tservice, f_service_ << "void " << tservice->get_name() << "ServerIf::" << "process_" << tfunction->get_name() << - "(boost::shared_ptr _itrans, boost::shared_ptr _otrans)" << endl; + "(int32_t seqid, boost::shared_ptr itrans, boost::shared_ptr otrans)" << endl; scope_up(f_service_); - // Get the struct of function call params - t_struct* arg_struct = tfunction->get_arglist(); + string argsname = tfunction->get_name() + "_args"; + string resultname = tfunction->get_name() + "_result"; - // 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; + f_service_ << + indent() << argsname << " __args;" << endl << + indent() << "read_struct_" << argsname << "(_iprot, itrans, __args);" << endl << + indent() << "_iprot->readMessageEnd(itrans);" << endl << + endl << + indent() << resultname << " __result;" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + if (xceptions.size() > 0) { + f_service_ << + indent() << "try {" << endl; + indent_up(); } - // Deserialize the function arguments as a struct - generate_deserialize_struct(arg_struct); - // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + f_service_ << indent(); if (!tfunction->get_returntype()->is_void()) { - f_service_ << type_name(tfunction->get_returntype()) << " _result = "; + f_service_ << "__result.success = "; } f_service_ << tfunction->get_name() << "("; @@ -536,22 +902,40 @@ void t_cpp_generator::generate_process_function(t_service* tservice, } else { f_service_ << ", "; } - f_service_ << (*f_iter)->get_name(); + f_service_ << "__args." << (*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 + // Set isset on success field if (!tfunction->get_returntype()->is_void()) { - result_struct.append(&result_field); + f_service_ << + indent() << "__result.__isset.success = true;" << endl; + } + + if (xceptions.size() > 0) { + indent_down(); + f_service_ << indent() << "}"; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << " catch (" << (*x_iter)->get_type()->get_name() << " &" << (*x_iter)->get_name() << ") {" << endl; + indent_up(); + f_service_ << + indent() << "__result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << ";" << endl << + indent() << "__result.__isset." << (*x_iter)->get_name() << " = true;" << endl; + indent_down(); + f_service_ << indent() << "}"; + } + f_service_ << endl; } - generate_serialize_struct(&result_struct); - indent(f_service_) << - "_otrans->flush();" << endl; + + // Serialize the result into a struct + f_service_ << + endl << + indent() << "_oprot->writeMessageBegin(otrans, \"" << tfunction->get_name() << "\", facebook::thrift::protocol::T_REPLY, seqid);" << endl << + indent() << "write_struct_" << resultname << "(_oprot, otrans, __result);" << endl << + indent() << "_oprot->writeMessageEnd(otrans);" << endl << + indent() << "otrans->flush();" << endl; + // Close function scope_down(f_service_); f_service_ << endl; @@ -574,15 +958,14 @@ void t_cpp_generator::generate_deserialize_field(t_field* tfield, string name = prefix + tfield->get_name(); - if (type->is_struct()) { - generate_deserialize_struct((t_struct*)(tfield->get_type()), - name + "."); + if (type->is_struct() || type->is_xception()) { + 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->"; + "xfer += iprot->"; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); @@ -592,28 +975,25 @@ void t_cpp_generator::generate_deserialize_field(t_field* tfield, name; break; case t_base_type::TYPE_STRING: - f_service_ << "readString(_itrans, " << name << ");"; + f_service_ << "readString(itrans, " << name << ");"; break; case t_base_type::TYPE_BYTE: - f_service_ << "readByte(_itrans, " << name << ");"; + f_service_ << "readByte(itrans, " << name << ");"; break; - case t_base_type::TYPE_I32: - f_service_ << "readI32(_itrans, " << name << ");"; + case t_base_type::TYPE_I16: + f_service_ << "readI16(itrans, " << name << ");"; break; - case t_base_type::TYPE_U32: - f_service_ << "readU32(_itrans, " << name << ");"; + case t_base_type::TYPE_I32: + f_service_ << "readI32(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 << ");"; + f_service_ << "readI64(itrans, " << name << ");"; break; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no C++ reader for base type " + tbase + name; } } else if (type->is_enum()) { - f_service_ << "readI32(_itrans, (int32_t&)" << name << ");"; + f_service_ << "readI32(itrans, (int32_t&)" << name << ");"; } f_service_ << endl; @@ -631,73 +1011,8 @@ void t_cpp_generator::generate_deserialize_field(t_field* tfield, */ void t_cpp_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 fid = tmp("_fid"); - string ftype = tmp("_ftype"); - string fname = tmp("_name"); - - // Declare stack tmp variables - f_service_ << - indent() << "std::string " << fname << ";" << endl << - indent() << "facebook::thrift::protocol::TType " << ftype << ";" << endl << - indent() << "uint16_t " << fid << ";" << endl << - indent() << "_iprot->readStructBegin(_itrans, " << fname << ");" << 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, " << - fname << ", " << ftype << ", " << fid << ");" << endl; - - // Check for field STOP marker - indent(f_service_) << - "if (" << ftype << " == facebook::thrift::protocol::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_) << - "_iprot->readStructEnd(_itrans);" << endl; - - scope_down(f_service_); + "xfer += read_struct_" << tstruct->get_name() << "(iprot, itrans, " << prefix << ");" << endl; } void t_cpp_generator::generate_deserialize_container(t_type* ttype, @@ -710,24 +1025,24 @@ void t_cpp_generator::generate_deserialize_container(t_type* ttype, string etype = tmp("_etype"); indent(f_service_) << - "int32_t " << size << ";" << endl; + "uint32_t " << size << ";" << endl; // Declare variables, read header if (ttype->is_map()) { f_service_ << indent() << "facebook::thrift::protocol::TType " << ktype << ";" << endl << indent() << "facebook::thrift::protocol::TType " << vtype << ";" << endl << - indent() << "_iprot->readMapBegin(_itrans, " << + indent() << "iprot->readMapBegin(itrans, " << ktype << ", " << vtype << ", " << size << ");" << endl; } else if (ttype->is_set()) { f_service_ << indent() << "facebook::thrift::protocol::TType " << etype << ";" << endl << - indent() << "_iprot->readSetBegin(_itrans, " << + indent() << "iprot->readSetBegin(itrans, " << etype << ", " << size << ");" << endl; } else if (ttype->is_list()) { f_service_ << indent() << "facebook::thrift::protocol::TType " << etype << ";" << endl << - indent() << "_iprot->readListBegin(_itrans, " << + indent() << "iprot->readListBegin(itrans, " << etype << ", " << size << ");" << endl; } @@ -735,7 +1050,7 @@ void t_cpp_generator::generate_deserialize_container(t_type* ttype, // For loop iterates over elements string i = tmp("_i"); indent(f_service_) << - "int32_t " << i << ";" << endl; + "uint32_t " << i << ";" << endl; indent(f_service_) << "for (" << i << " = 0; " << i << " < " << size << "; ++" << i << ")" << endl; @@ -754,11 +1069,11 @@ void t_cpp_generator::generate_deserialize_container(t_type* ttype, // Read container end if (ttype->is_map()) { - indent(f_service_) << "_iprot->readMapEnd(_itrans);" << endl; + indent(f_service_) << "iprot->readMapEnd(itrans);" << endl; } else if (ttype->is_set()) { - indent(f_service_) << "_iprot->readSetEnd(_itrans);" << endl; + indent(f_service_) << "iprot->readSetEnd(itrans);" << endl; } else if (ttype->is_list()) { - indent(f_service_) << "_iprot->readListEnd(_itrans);" << endl; + indent(f_service_) << "iprot->readListEnd(itrans);" << endl; } scope_down(f_service_); @@ -835,9 +1150,9 @@ void t_cpp_generator::generate_serialize_field(t_field* tfield, prefix + tfield->get_name(); } - if (type->is_struct()) { + if (type->is_struct() || type->is_xception()) { generate_serialize_struct((t_struct*)(tfield->get_type()), - prefix + tfield->get_name() + "."); + prefix + tfield->get_name()); } else if (type->is_container()) { generate_serialize_container(tfield->get_type(), prefix + tfield->get_name()); @@ -845,7 +1160,7 @@ void t_cpp_generator::generate_serialize_field(t_field* tfield, string name = prefix + tfield->get_name(); indent(f_service_) << - "_oprot->"; + "xfer += oprot->"; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); @@ -855,28 +1170,25 @@ void t_cpp_generator::generate_serialize_field(t_field* tfield, "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: - f_service_ << "writeString(_otrans, " << name << ");"; + f_service_ << "writeString(otrans, " << name << ");"; break; case t_base_type::TYPE_BYTE: - f_service_ << "writeByte(_otrans, " << name << ");"; + f_service_ << "writeByte(otrans, " << name << ");"; break; - case t_base_type::TYPE_I32: - f_service_ << "writeI32(_otrans, " << name << ");"; + case t_base_type::TYPE_I16: + f_service_ << "writeI16(otrans, " << name << ");"; break; - case t_base_type::TYPE_U32: - f_service_ << "writeU32(_otrans, " << name << ");"; + case t_base_type::TYPE_I32: + f_service_ << "writeI32(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 << ");"; + f_service_ << "writeI64(otrans, " << name << ");"; break; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no C++ writer for base type " + tbase + name; } } else if (type->is_enum()) { - f_service_ << "writeI32(_otrans, (int32_t)" << name << ");"; + f_service_ << "writeI32(otrans, (int32_t)" << name << ");"; } f_service_ << endl; } else { @@ -895,32 +1207,8 @@ void t_cpp_generator::generate_serialize_field(t_field* tfield, */ 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; - - scope_up(f_service_); indent(f_service_) << - "_oprot->writeStructBegin(_otrans, \"" << name << "\");" << endl; - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - // 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 - f_service_ << - indent() << "_oprot->writeFieldStop(_otrans);" << endl << - indent() << "_oprot->writeStructEnd(_otrans);" << endl; - - scope_down(f_service_); + "xfer += write_struct_" << tstruct->get_name() << "(oprot, otrans, " << prefix << ");" << endl; } void t_cpp_generator::generate_serialize_container(t_type* ttype, @@ -929,18 +1217,18 @@ void t_cpp_generator::generate_serialize_container(t_type* ttype, if (ttype->is_map()) { indent(f_service_) << - "_oprot->writeMapBegin(_otrans, " << + "xfer += 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, " << + "xfer += 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, " << + "xfer += oprot->writeListBegin(otrans, " << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".size());" << endl; } @@ -962,20 +1250,20 @@ void t_cpp_generator::generate_serialize_container(t_type* ttype, } else if (ttype->is_list()) { generate_serialize_list_element((t_list*)ttype, iter); } + + scope_down(f_service_); if (ttype->is_map()) { indent(f_service_) << - "_oprot->writeMapEnd(_otrans);" << endl; + "xfer += oprot->writeMapEnd(otrans);" << endl; } else if (ttype->is_set()) { indent(f_service_) << - "_oprot->writeSetEnd(_otrans);" << endl; + "xfer += oprot->writeSetEnd(otrans);" << endl; } else if (ttype->is_list()) { indent(f_service_) << - "_oprot->writeListEnd(_otrans);" << endl; - } - - scope_down(f_service_); - + "xfer += oprot->writeListEnd(otrans);" << endl; + } + scope_down(f_service_); } @@ -1010,6 +1298,51 @@ void t_cpp_generator::generate_serialize_list_element(t_list* tlist, generate_serialize_field(&efield, ""); } +/** + * Opens namespace. + * + * @param ns The namepsace, w/ periods in it + * @return Namespaces + */ +string t_cpp_generator::namespace_open(string ns) { + if (ns.size() == 0) { + return ""; + } + string result = ""; + string::size_type loc; + while ((loc = ns.find(".")) != string::npos) { + result += "namespace "; + result += ns.substr(0, loc); + result += " { "; + ns = ns.substr(loc+1); + } + if (ns.size() > 0) { + result += "namespace " + ns + " { "; + } + return result; +} + +/** + * Closes namespace. + * + * @param ns The namepsace, w/ periods in it + * @return Namespaces + */ +string t_cpp_generator::namespace_close(string ns) { + if (ns.size() == 0) { + return ""; + } + string result = "}"; + string::size_type loc; + while ((loc = ns.find(".")) != string::npos) { + result += "}"; + ns = ns.substr(loc+1); + } + result += " // namespace"; + return result; +} + + /** * Returns a C++ type name * @@ -1046,17 +1379,15 @@ string t_cpp_generator::base_type_name(t_base_type::t_base tbase) { case t_base_type::TYPE_STRING: return "std::string"; case t_base_type::TYPE_BYTE: - return "uint8_t"; + return "int8_t"; + case t_base_type::TYPE_I16: + return "int16_t"; case t_base_type::TYPE_I32: return "int32_t"; - case t_base_type::TYPE_U32: - return "uint32_t"; case t_base_type::TYPE_I64: return "int64_t"; - case t_base_type::TYPE_U64: - return "uint64_t"; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no C++ base type name for base type " + tbase; } } @@ -1083,10 +1414,9 @@ string t_cpp_generator::declare_field(t_field* tfield, bool init) { result += " = \"\""; break; case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: - case t_base_type::TYPE_U32: case t_base_type::TYPE_I64: - case t_base_type::TYPE_U64: result += " = 0"; break; default: @@ -1150,19 +1480,19 @@ string t_cpp_generator::type_to_enum(t_type* type) { return "facebook::thrift::protocol::T_STRING"; case t_base_type::TYPE_BYTE: return "facebook::thrift::protocol::T_BYTE"; + case t_base_type::TYPE_I16: + return "facebook::thrift::protocol::T_I16"; case t_base_type::TYPE_I32: return "facebook::thrift::protocol::T_I32"; - case t_base_type::TYPE_U32: - return "facebook::thrift::protocol::T_U32"; case t_base_type::TYPE_I64: return "facebook::thrift::protocol::T_I64"; - case t_base_type::TYPE_U64: - return "facebook::thrift::protocol::T_U64"; } } else if (type->is_enum()) { return "facebook::thrift::protocol::T_I32"; } else if (type->is_struct()) { return "facebook::thrift::protocol::T_STRUCT"; + } else if (type->is_xception()) { + return "facebook::thrift::protocol::T_STRUCT"; } else if (type->is_map()) { return "facebook::thrift::protocol::T_MAP"; } else if (type->is_set()) { diff --git a/compiler/cpp/src/generate/t_cpp_generator.h b/compiler/cpp/src/generate/t_cpp_generator.h index 801c06c1..48cdb4c0 100644 --- a/compiler/cpp/src/generate/t_cpp_generator.h +++ b/compiler/cpp/src/generate/t_cpp_generator.h @@ -24,22 +24,30 @@ class t_cpp_generator : public t_oop_generator { /** Init and close methods */ void init_generator(t_program *tprogram); - void close_generator(); + void close_generator(t_program *tprogram); /** 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); + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_struct (t_struct* tstruct); + void generate_service (t_service* tservice); + + void generate_struct_definition (std::ofstream& out, t_struct* tstruct); + void generate_struct_reader (std::ofstream& out, t_struct* tstruct); + void generate_struct_writer (std::ofstream& out, t_struct* tstruct); + void generate_struct_result_writer (std::ofstream& out, t_struct* tstruct); /** Service-level generation functions */ void generate_service_interface (t_service* tservice); + void generate_service_helpers (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); + void generate_function_helpers (t_function* tfunction); + /** Serialization constructs */ void generate_deserialize_field (t_field* tfield, @@ -80,6 +88,8 @@ class t_cpp_generator : public t_oop_generator { /** Helper rendering functions */ + std::string namespace_open(std::string ns); + std::string namespace_close(std::string ns); 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); diff --git a/compiler/cpp/src/generate/t_generator.cc b/compiler/cpp/src/generate/t_generator.cc index d68e66da..99baf331 100644 --- a/compiler/cpp/src/generate/t_generator.cc +++ b/compiler/cpp/src/generate/t_generator.cc @@ -36,6 +36,13 @@ void t_generator::generate_program(t_program *tprogram) { generate_struct(*st_iter); } + // Generate xceptions + vector xceptions = tprogram->get_xceptions(); + vector::iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + generate_xception(*x_iter); + } + // Generate services vector services = tprogram->get_services(); vector::iterator sv_iter; @@ -45,5 +52,5 @@ void t_generator::generate_program(t_program *tprogram) { } // Close the generator - close_generator(); + close_generator(tprogram); } diff --git a/compiler/cpp/src/generate/t_generator.h b/compiler/cpp/src/generate/t_generator.h index be0b83e3..16d80848 100644 --- a/compiler/cpp/src/generate/t_generator.h +++ b/compiler/cpp/src/generate/t_generator.h @@ -29,14 +29,17 @@ class t_generator { /** Optional methods that may be imlemented by subclasses. */ virtual void init_generator (t_program* tprogram) {} - virtual void close_generator () {} + virtual void close_generator (t_program* tprogram) {} /** Pure virtual methods implemented by the generator subclasses. */ - virtual void generate_typedef (t_typedef* ttypedef) = 0; - virtual void generate_enum (t_enum* tenum) = 0; - virtual void generate_struct (t_struct* tstruct) = 0; - virtual void generate_service (t_service* tservice) = 0; + virtual void generate_typedef (t_typedef* ttypedef) = 0; + virtual void generate_enum (t_enum* tenum) = 0; + virtual void generate_struct (t_struct* tstruct) = 0; + virtual void generate_xception (t_struct* txception) { + generate_struct(txception); + } + virtual void generate_service (t_service* tservice) = 0; /** Method to get the program name, may be overridden */ diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc index 13b50220..f1124079 100644 --- a/compiler/cpp/src/generate/t_java_generator.cc +++ b/compiler/cpp/src/generate/t_java_generator.cc @@ -49,7 +49,7 @@ string t_java_generator::java_thrift_imports() { /** * Does nothing in Java */ -void t_java_generator::close_generator() {} +void t_java_generator::close_generator(t_program *tprogram) {} /** * Generates a typedef. This is not done in Java. @@ -583,15 +583,9 @@ void t_java_generator::generate_deserialize_field(t_field* tfield, case t_base_type::TYPE_I32: f_service_ << "readI32(_itrans);"; break; - case t_base_type::TYPE_U32: - f_service_ << "readU32(_itrans);"; - break; case t_base_type::TYPE_I64: f_service_ << "readI64(_itrans);"; break; - case t_base_type::TYPE_U64: - f_service_ << "readU64(_itrans);"; - break; default: throw "compiler error: no C++ name for base type " + tbase; } @@ -846,15 +840,9 @@ void t_java_generator::generate_serialize_field(t_field* tfield, 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; } @@ -1066,10 +1054,8 @@ string t_java_generator::base_type_name(t_base_type::t_base tbase, case t_base_type::TYPE_BYTE: return "byte"; case t_base_type::TYPE_I32: - case t_base_type::TYPE_U32: return (in_container ? "Integer" : "int"); case t_base_type::TYPE_I64: - case t_base_type::TYPE_U64: return (in_container ? "Long" : "long"); default: throw "compiler error: no C++ name for base type " + tbase; @@ -1099,9 +1085,7 @@ string t_java_generator::declare_field(t_field* tfield, bool init) { break; case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I32: - case t_base_type::TYPE_U32: case t_base_type::TYPE_I64: - case t_base_type::TYPE_U64: result += " = 0"; break; } @@ -1168,12 +1152,8 @@ string t_java_generator::type_to_enum(t_type* type) { 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"; diff --git a/compiler/cpp/src/generate/t_java_generator.h b/compiler/cpp/src/generate/t_java_generator.h index 3dd4dbd2..0fa90741 100644 --- a/compiler/cpp/src/generate/t_java_generator.h +++ b/compiler/cpp/src/generate/t_java_generator.h @@ -24,7 +24,7 @@ class t_java_generator : public t_oop_generator { /** Init and close methods */ void init_generator(t_program *tprogram); - void close_generator(); + void close_generator(t_program *tprogram); /** Program-level generation functions */ diff --git a/compiler/cpp/src/generate/t_php_generator.cc b/compiler/cpp/src/generate/t_php_generator.cc index 09a39a65..efd24c58 100644 --- a/compiler/cpp/src/generate/t_php_generator.cc +++ b/compiler/cpp/src/generate/t_php_generator.cc @@ -15,7 +15,7 @@ void t_php_generator::init_generator(t_program* tprogram) { mkdir(T_PHP_DIR, S_IREAD | S_IWRITE | S_IEXEC); // Make output file - string f_types_name = string(T_PHP_DIR)+"/"+program_name_+"Types.php"; + string f_types_name = string(T_PHP_DIR)+"/"+program_name_+"_types.php"; f_types_.open(f_types_name.c_str()); // Print header @@ -30,13 +30,13 @@ void t_php_generator::init_generator(t_program* tprogram) { */ string t_php_generator::php_includes() { return - string("require_once THRIFT_ROOT.'/Thrift.php';\n\n"); + string("require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';\n\n"); } /** * Does nothing in PHP */ -void t_php_generator::close_generator() { +void t_php_generator::close_generator(t_program *tprogram) { // Close types file f_types_ << "?>" << endl; f_types_.close(); @@ -101,6 +101,27 @@ void t_php_generator::generate_enum(t_enum* tenum) { f_types_ << "}" << endl << endl; } +void t_php_generator::generate_struct(t_struct* tstruct) { + generate_php_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_php_generator::generate_xception(t_struct* txception) { + generate_php_struct(txception, true); +} + +void t_php_generator::generate_php_struct(t_struct* tstruct, + bool is_exception) { + generate_php_struct_definition(f_types_, tstruct, is_exception); + generate_php_struct_reader(f_types_, tstruct); + generate_php_struct_writer(f_types_, tstruct); +} + /** * Generates a struct definition for a thrift data type. This is nothing in PHP * where the objects are all just associative arrays (unless of course we @@ -108,30 +129,206 @@ void t_php_generator::generate_enum(t_enum* tenum) { * * @param tstruct The struct definition */ -void t_php_generator::generate_struct(t_struct* tstruct) { - f_types_ << - "class " << tstruct->get_name() << " {" << endl; - indent_up(); - +void t_php_generator::generate_php_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception) { const vector& members = tstruct->get_members(); vector::const_iterator m_iter; + + out << + "class " << tstruct->get_name(); + if (is_exception) { + out << " extends Exception"; + } + out << + " {" << endl; + indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - indent(f_types_) << - "public " << declare_field(*m_iter, true) << endl; + // This fills in default values, as opposed to nulls + //indent(out) << + //"public " << declare_field(*m_iter, true) << endl; + + indent(out) << + "public $" << (*m_iter)->get_name() << " = null;" << endl; } indent_down(); - f_types_ << + out << + indent() << "}" << endl << + endl; +} + +void t_php_generator::generate_php_struct_reader(ofstream& out, + t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << + "function read_struct_" << tstruct->get_name() << + "($iprot, $itrans, &$value) " << endl; + scope_up(out); + + out << + indent() << "$xfer = 0;" << endl << + indent() << "$fname = null;" << endl << + indent() << "$ftype = 0;" << endl << + indent() << "$fid = 0;" << endl; + + // Declare stack tmp variables + if (!binary_inline_) { + indent(out) << + "$xfer += $iprot->readStructBegin($itrans, $fname);" << endl; + } + + // Loop over reading in fields + indent(out) << + "while (true)" << endl; + + scope_up(out); + + // Read beginning field marker + if (binary_inline_) { + t_field fftype(g_program->get_byte_type(), "ftype"); + t_field ffid(g_program->get_i16_type(), "fid"); + generate_deserialize_field(out, &fftype); + out << + indent() << "if ($ftype == TType::STOP) {" << endl << + indent() << " break;" << endl << + indent() << "}" << endl; + generate_deserialize_field(out, &ffid); + } else { + indent(out) << + "$xfer += $iprot->readFieldBegin($itrans, $fname, $ftype, $fid);" << endl; + // Check for field STOP marker and break + indent(out) << + "if ($ftype == TType::STOP) {" << endl; + indent_up(); + indent(out) << + "break;" << endl; + indent_down(); + indent(out) << + "}" << endl; + } + + // Switch statement on the field we are reading + indent(out) << + "switch ($fid)" << endl; + + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << + "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + generate_deserialize_field(out, *f_iter, "value->"); + indent(out) << + "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + out << + indent() << "default:" << endl << + indent() << " $xfer += $iprot->skip($itrans, $ftype);" << endl << + indent() << " break;" << endl; + + scope_down(out); + + if (!binary_inline_) { + // Read field end marker + indent(out) << + "$xfer += $iprot->readFieldEnd($itrans);" << endl; + } + + scope_down(out); + + if (!binary_inline_) { + indent(out) << + "$xfer += $iprot->readStructEnd($itrans);" << endl; + } + + indent(out) << + "return $xfer;" << endl; + + indent_down(); + out << + indent() << "}" << endl << + endl; +} + +void t_php_generator::generate_php_struct_writer(ofstream& out, + t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + if (binary_inline_) { + indent(out) << + "function write_struct_" << name << + "(&$_output, &$value) {" << endl; + } else { + indent(out) << + "function write_struct_" << name << + "($oprot, $otrans, &$value) {" << endl; + } + indent_up(); + + indent(out) << + "$xfer = 0;" << endl; + + if (!binary_inline_) { + indent(out) << + "$xfer += $oprot->writeStructBegin($otrans, '" << name << "');" << endl; + } + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // Write field header + if (binary_inline_) { + out << + indent() << "$_output .= pack('c', " << type_to_enum((*f_iter)->get_type()) << ");" << endl << + indent() << "$_output .= pack('n', " << (*f_iter)->get_key() << ");" << endl; + } else { + indent(out) << + "$xfer += $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(out, *f_iter, "value->"); + + // Write field closer + if (!binary_inline_) { + indent(out) << + "$xfer += $oprot->writeFieldEnd($otrans);" << endl; + } + } + + if (binary_inline_) { + out << + indent() << "$_output .= pack('c', TType::STOP);" << endl; + } else { + // Write the struct map + out << + indent() << "$xfer += $oprot->writeFieldStop($otrans);" << endl << + indent() << "$xfer += $oprot->writeStructEnd($otrans);" << endl; + } + + out << + indent() << "return $xfer;" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; } /** - * 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. + * Generates a thrift service. * * @param tservice The service definition */ @@ -145,18 +342,63 @@ void t_php_generator::generate_service(t_service* tservice) { php_includes(); f_service_ << - "require_once dirname(__FILE__).'/" << service_name_ << "Types.php';" << endl << endl; + "require_once dirname(__FILE__).'/" << service_name_ << "_types.php';" << endl << endl; // Generate the three main parts of the service (well, two for now in PHP) generate_service_interface(tservice); - // generate_service_server(tservice); generate_service_client(tservice); + generate_service_helpers(tservice); + // generate_service_server(tservice); // Close service file f_service_ << "?>" << endl; f_service_.close(); } +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_php_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + f_service_ << + "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_php_struct_definition(f_service_, ts, false); + generate_php_struct_reader(f_service_, ts); + generate_php_struct_writer(f_service_, ts); + generate_php_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_php_generator::generate_php_function_helpers(t_function* tfunction) { + t_struct result(tfunction->get_name() + "_result"); + t_field success(tfunction->get_returntype(), "success", 0); + if (!tfunction->get_returntype()->is_void()) { + result.append(&success); + } + + t_struct* xs = tfunction->get_xceptions(); + const vector& fields = xs->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + result.append(*f_iter); + } + + generate_php_struct_definition(f_service_, &result, false); + generate_php_struct_reader(f_service_, &result); +} + /** * Generates a service interface definition. * @@ -191,14 +433,20 @@ void t_php_generator::generate_service_client(t_service* tservice) { // Private members f_service_ << indent() << "private $_itrans = null;" << endl << - indent() << "private $_otrans = null;" << endl << endl; + indent() << "private $_otrans = null;" << endl << + endl; if (!binary_inline_) { f_service_ << - indent() << "private $_iprot = null;" << endl << - indent() << "private $_oprot = null;" << endl << endl; + indent() << "private $_iprot = null;" << endl << + indent() << "private $_oprot = null;" << endl << + endl; } + f_service_ << + indent() << "private $_seqid = 0;" << endl << + endl; + // Constructor function f_service_ << indent() << "public function __construct() {" << endl << @@ -230,92 +478,176 @@ void t_php_generator::generate_service_client(t_service* tservice) { // Generate client method implementations vector functions = tservice->get_functions(); - vector::const_iterator f_iter; + vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; string funname = (*f_iter)->get_name(); // Open function indent(f_service_) << "public function " << function_signature(*f_iter) << endl; scope_up(f_service_); + indent(f_service_) << + "$this->send_" << funname << "("; + + bool first = true; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "$" << (*fld_iter)->get_name(); + } + f_service_ << ");" << endl; - // Serialize the request header - if (binary_inline_) { - f_service_ << - indent() << "$_output = '';" << endl << - indent() << "$_output .= pack('c', TType::STRING);" << endl << - indent() << "$_output .= strrev(pack('l', 0));" << endl << - indent() << "$_output .= strrev(pack('l', strlen('" << funname << "')));" << endl << - indent() << "$_output .= '" << funname << "';" << endl << - indent() << "$_output .= pack('c', TType::STRUCT);" << endl << - indent() << "$_output .= strrev(pack('l', 1));" << endl; - } else { + if (!(*f_iter)->is_async()) { + f_service_ << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << "return "; + } + f_service_ << + "$this->recv_" << funname << "();" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << + "public function send_" << function_signature(*f_iter) << endl; + scope_up(f_service_); + + std::string argsname = (*f_iter)->get_name() + "_args"; + + // Serialize the request header + if (binary_inline_) { + f_service_ << + indent() << "$_output = '';" << endl << + indent() << "$_output .= pack('N', strlen('" << funname << "'));" << endl << + indent() << "$_output .= '" << funname << "';" << endl << + indent() << "$_output .= pack('cN', TMessageType::CALL, $this->seqid);" << endl; + } else { + f_service_ << + indent() << "$this->_oprot->writeMessageBegin($this->_otrans, '" << (*f_iter)->get_name() << "', TMessageType::CALL, $this->seqid);" << endl; + } + f_service_ << - indent() << "$this->_oprot->writeStructBegin($this->_otrans, 'function');" << endl << - indent() << "$this->_oprot->writeFieldBegin($this->_otrans, 'name', TType::STRING, 0);" << endl << - indent() << "$this->_oprot->writeString($this->_otrans, '" << funname << "');" << endl << - indent() << "$this->_oprot->writeFieldEnd($this->_otrans);" << endl << - indent() << "$this->_oprot->writeFieldBegin($this->_otrans, 'args', TType::STRUCT, 1);" << endl; - } + indent() << "$__args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << + indent() << "$__args->" << (*fld_iter)->get_name() << " = $" << (*fld_iter)->get_name() << ";" << endl; + } + + // Write to the stream + if (binary_inline_) { + f_service_ << + indent() << "write_struct_" << argsname << "($_output, $__args);" << endl << + indent() << "$this->_otrans->write($_output);" << endl; + } else { + f_service_ << + indent() << "write_struct_" << argsname << "($this->_oprot, $this->_otrans, $__args);" << endl << + indent() << "$this->_oprot->writeMessageEnd($this->_otrans);" << endl; + } + + // Flush the request + indent(f_service_) << + "$this->_otrans->flush();" << endl; + + + scope_down(f_service_); + - // Serialize request arguments - generate_serialize_struct((*f_iter)->get_arglist()); + if (!(*f_iter)->is_async()) { + std::string resultname = (*f_iter)->get_name() + "_result"; + t_struct noargs; + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + // Open function + f_service_ << + endl << + indent() << "public function " << function_signature(&recv_function) << endl; + scope_up(f_service_); - // Write to the stream - if (binary_inline_) { f_service_ << - indent() << "$_output .= pack('c', TType::STOP);" << endl << - indent() << "$this->_otrans->write($_output);" << endl; - } else { + indent() << "$rseqid = 0;" << endl << + indent() << "$fname = null;" << endl << + indent() << "$mtype = 0;" << endl << + endl; + + if (binary_inline_) { + t_field ffname(g_program->get_string_type(), "fname"); + t_field fmtype(g_program->get_byte_type(), "mtype"); + t_field fseqid(g_program->get_i32_type(), "rseqid"); + generate_deserialize_field(f_service_, &ffname, "", true); + generate_deserialize_field(f_service_, &fmtype, "", true); + generate_deserialize_field(f_service_, &fseqid, "", true); + } else { + f_service_ << + indent() << "$this->_iprot->readMessageBegin($this->_itrans, $fname, $mtype, $rseqid);" << endl; + } + + // TODO(mcslee): Validate message reply here + f_service_ << - indent() << "$this->_oprot->writeFieldEnd($this->_otrans);" << endl << - indent() << "$this->_oprot->writeFieldStop($this->_otrans);" << endl << - indent() << "$this->_oprot->writeStructEnd($this->_otrans);" << endl; - } - - // Flush the request - indent(f_service_) << - "$this->_otrans->flush();" << endl; + indent() << "$__result = new " << resultname << "();" << endl << + indent() << "read_struct_" << resultname << "($this->_iprot, $this->_otrans, $__result);" << endl; - // Read the response - t_struct result_struct((*f_iter)->get_name() + "_result"); - t_field result_field((*f_iter)->get_returntype(), "_result"); + if (!binary_inline_) { + f_service_ << + indent() << "$this->_iprot->readMessageEnd($this->_itrans);" << endl << + endl; + } - // 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, true) << endl; - result_struct.append(&result_field); - } + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << + indent() << "if ($__result->success !== null) {" << endl << + indent() << " return $__result->success;" << endl << + indent() << "}" << endl; + } - // Deserialize response struct - generate_deserialize_struct(&result_struct); + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << + indent() << "if ($__result->" << (*x_iter)->get_name() << " !== null) {" << endl << + indent() << " throw $__result->" << (*x_iter)->get_name() << ";" << endl << + indent() << "}" << endl; + } - // 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; - } + // Careful, only return _result if not a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << + "return;" << endl; + } else { + f_service_ << + indent() << "throw Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; + } + } // Close function scope_down(f_service_); f_service_ << endl; + } indent_down(); f_service_ << - "}" << endl; - f_service_.close(); + "}" << endl << endl; } /** * Deserializes a field of any type. */ -void t_php_generator::generate_deserialize_field(t_field* tfield, - string prefix) { +void t_php_generator::generate_deserialize_field(ofstream &out, + t_field* tfield, + string prefix, + bool inclass) { t_type* type = tfield->get_type(); while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); @@ -328,14 +660,17 @@ void t_php_generator::generate_deserialize_field(t_field* tfield, string name = prefix + tfield->get_name(); - if (type->is_struct()) { - generate_deserialize_struct((t_struct*)(tfield->get_type()), - name + "->"); + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, + (t_struct*)(tfield->get_type()), + name); } else if (type->is_container()) { - generate_deserialize_container(tfield->get_type(), name); + generate_deserialize_container(out, tfield->get_type(), name); } else if (type->is_base_type() || type->is_enum()) { if (binary_inline_) { + std::string itrans = inclass ? "$this->_itrans" : "$itrans"; + if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { @@ -343,28 +678,41 @@ void t_php_generator::generate_deserialize_field(t_field* tfield, throw "compiler error: cannot serialize void field in a struct: " + name; break; - case t_base_type::TYPE_STRING: - f_service_ << - indent() << "$_len = unpack('l', strrev($this->_itrans->readAll(4)));" << endl << + case t_base_type::TYPE_STRING: + out << + indent() << "$_len = unpack('N', " << itrans << "->readAll(4));" << endl << indent() << "$_len = $_len[1];" << endl << - indent() << "$" << name << " = $this->_itrans->readAll($_len);" << endl; + indent() << "if ($_len > 0x7fffffff) {" << endl << + indent() << " $_len = 0 - (($len - 1) ^ 0xffffffff);" << endl << + indent() << "}" << endl << + indent() << "$" << name << " = " << itrans << "->readAll($_len);" << endl; break; case t_base_type::TYPE_BYTE: - f_service_ << - indent() << "$" << name << " = unpack('c', $this->_itrans->readAll(1));" << endl << + out << + indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));" << endl << indent() << "$" << name << " = $" << name << "[1];" << endl; break; - case t_base_type::TYPE_I32: - f_service_ << - indent() << "$" << name << " = unpack('l', strrev($this->_itrans->readAll(4)));" << endl << - indent() << "$" << name << " = $" << name << "[1];" << endl; + case t_base_type::TYPE_I16: + out << + indent() << "$_val = unpack('n', " << itrans << "->readAll(2));" << endl << + indent() << "$_val = $_val[1];" << endl << + indent() << "if ($_val > 0x7fff) {" << endl << + indent() << " $_val = 0 - (($_val - 1) ^ 0xffff);" << endl << + indent() << "}" << endl << + indent() << "$" << name << " = $_val;" << endl; break; - case t_base_type::TYPE_U32: - f_service_ << "readU32($this->_itrans, $" << name << ");"; + case t_base_type::TYPE_I32: + out << + indent() << "$_val = unpack('N', " << itrans << "->readAll(4));" << endl << + indent() << "$_val = $_val[1];" << endl << + indent() << "if ($_val > 0x7fffffff) {" << endl << + indent() << " $_val = 0 - (($_val - 1) ^ 0xffffffff);" << endl << + indent() << "}" << endl << + indent() << "$" << name << " = $_val;" << endl; break; case t_base_type::TYPE_I64: - f_service_ << - indent() << "$_arr = unpack('N2', $this->_itrans->readAll(8));" << endl << + out << + indent() << "$_arr = unpack('N2', " << itrans << "->readAll(8));" << endl << indent() << "if ($_arr[1] & 0x80000000) {" << endl << indent() << " $_arr[1] = $_arr[1] ^ 0xFFFFFFFF;" << endl << indent() << " $_arr[2] = $_arr[2] ^ 0xFFFFFFFF;" << endl << @@ -373,22 +721,22 @@ void t_php_generator::generate_deserialize_field(t_field* tfield, indent() << " $" << name << " = $_arr[1]*4294967296 + $_arr[2];" << endl << indent() << "}" << endl; break; - case t_base_type::TYPE_U64: - f_service_ << "readU64($this->_itrans, $" << name << ");"; - break; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no PHP name for base type " + tbase + tfield->get_name(); } } else if (type->is_enum()) { - f_service_ << - indent() << "$" << name << " = unpack('l', strrev($this->_itrans->readAll(4)));" << endl << - indent() << "$" << name << " = $" << name << "[1];" << endl; + out << + indent() << "$_val = unpack('N', " << itrans << "->readAll(4));" << endl << + indent() << "$_val = $_val[1];" << endl << + indent() << "if ($_val > 0x7fffffff) {" << endl << + indent() << " $_val = 0 - (($_val - 1) ^ 0xffffffff);" << endl << + indent() << "}" << endl << + indent() << "$" << name << " = $_val;" << endl; } - } else { - indent(f_service_) << - "$this->_iprot->"; + indent(out) << + "$xfer += $iprot->"; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); @@ -398,30 +746,27 @@ void t_php_generator::generate_deserialize_field(t_field* tfield, name; break; case t_base_type::TYPE_STRING: - f_service_ << "readString($this->_itrans, $" << name << ");"; + out << "readString($itrans, $" << name << ");"; break; case t_base_type::TYPE_BYTE: - f_service_ << "readByte($this->_itrans, $" << name << ");"; + out << "readByte($itrans, $" << name << ");"; break; - case t_base_type::TYPE_I32: - f_service_ << "readI32($this->_itrans, $" << name << ");"; + case t_base_type::TYPE_I16: + out << "readI16($itrans, $" << name << ");"; break; - case t_base_type::TYPE_U32: - f_service_ << "readU32($this->_itrans, $" << name << ");"; + case t_base_type::TYPE_I32: + out << "readI32($itrans, $" << name << ");"; break; case t_base_type::TYPE_I64: - f_service_ << "readI64($this->_itrans, $" << name << ");"; - break; - case t_base_type::TYPE_U64: - f_service_ << "readU64($this->_itrans, $" << name << ");"; + out << "readI64($itrans, $" << name << ");"; break; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no PHP name for base type " + tbase; } } else if (type->is_enum()) { - f_service_ << "readI32($this->_itrans, $" << name << ");"; + out << "readI32($itrans, $" << name << ");"; } - f_service_ << endl; + out << endl; } } else { @@ -436,107 +781,18 @@ void t_php_generator::generate_deserialize_field(t_field* tfield, * buffer for deserialization, and that there is a variable protocol which * is a reference to a TProtocol serialization object. */ -void t_php_generator::generate_deserialize_struct(t_struct* tstruct, +void t_php_generator::generate_deserialize_struct(ofstream &out, + 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 fid = tmp("_fid"); - string ftype = tmp("_ftype"); - string fname = tmp("_name"); - - t_field ffid(g_program->get_i32_type(), fid); - t_field fftype(g_program->get_byte_type(), ftype); - - f_service_ << - indent() << "$" << fname << " = null;" << endl << - indent() << "$" << ftype << " = null;" << endl << - indent() << "$" << fid << " = 0;" << endl; - - // Declare stack tmp variables - if (!binary_inline_) { - f_service_ << - indent() << "$this->_iprot->readStructBegin($this->_itrans, $" << fname << ");" << endl; - } - - // Loop over reading in fields - indent(f_service_) << - "while (true)" << endl; - - scope_up(f_service_); - - // Read beginning field marker - if (binary_inline_) { - generate_deserialize_field(&fftype); - f_service_ << - indent() << "if ($" << ftype << " == TType::STOP) {" << endl << - indent() << " break;" << endl << - indent() << "}" << endl; - generate_deserialize_field(&ffid); - } else { - indent(f_service_) << - "$this->_iprot->readFieldBegin($this->_itrans, " << - "$" << fname << ", $" << ftype << ", $" << fid << ");" << endl; - - // Check for field STOP marker and break - indent(f_service_) << - "if ($" << ftype << " == 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 ($" << 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() << " $this->_iprot->skip($this->_itrans, $" << ftype << ");" << endl << - indent() << " break;" << endl; - - scope_down(f_service_); - - if (!binary_inline_) { - // Read field end marker - indent(f_service_) << - "$this->_iprot->readFieldEnd($this->_itrans);" << endl; - } - - scope_down(f_service_); - - if (!binary_inline_) { - indent(f_service_) << - "$this->_iprot->readStructEnd($this->_itrans);" << endl; - } - - scope_down(f_service_); + out << + indent() << "$" << prefix << " = new " << tstruct->get_name() << "();" << endl << + indent() << "$xfer += read_struct_" << tstruct->get_name() << "($iprot, $itrans, $" << prefix << ");" << endl; } -void t_php_generator::generate_deserialize_container(t_type* ttype, +void t_php_generator::generate_deserialize_container(ofstream &out, + t_type* ttype, string prefix) { - scope_up(f_service_); + scope_up(out); string size = tmp("_size"); string ktype = tmp("_ktype"); @@ -548,125 +804,128 @@ void t_php_generator::generate_deserialize_container(t_type* ttype, t_field fvtype(g_program->get_byte_type(), vtype); t_field fetype(g_program->get_byte_type(), etype); - indent(f_service_) << + indent(out) << "$" << size << " = 0;" << endl; // Declare variables, read header if (ttype->is_map()) { - f_service_ << + out << indent() << "$" << ktype << " = 0;" << endl << indent() << "$" << vtype << " = 0;" << endl; if (binary_inline_) { - generate_deserialize_field(&fktype); - generate_deserialize_field(&fvtype); - generate_deserialize_field(&fsize); + generate_deserialize_field(out, &fktype); + generate_deserialize_field(out, &fvtype); + generate_deserialize_field(out, &fsize); } else { - f_service_ << - indent() << "$this->_iprot->readMapBegin($this->_itrans, " << + out << + indent() << "$xfer += $iprot->readMapBegin($itrans, " << "$" << ktype << ", $" << vtype << ", $" << size << ");" << endl; } } else if (ttype->is_set()) { if (binary_inline_) { - generate_deserialize_field(&fetype); - generate_deserialize_field(&fsize); + generate_deserialize_field(out, &fetype); + generate_deserialize_field(out, &fsize); } else { - f_service_ << + out << indent() << "$" << etype << " = 0;" << endl << - indent() << "$this->_iprot->readSetBegin($this->_itrans, " << + indent() << "$xfer += $iprot->readSetBegin($itrans, " << "$" << etype << ", $" << size << ");" << endl; } } else if (ttype->is_list()) { if (binary_inline_) { - generate_deserialize_field(&fetype); - generate_deserialize_field(&fsize); + generate_deserialize_field(out, &fetype); + generate_deserialize_field(out, &fsize); } else { - f_service_ << + out << indent() << "$" << etype << " = 0;" << endl << - indent() << "$this->_iprot->readListBegin($this->_itrans, " << + indent() << "$xfer += $iprot->readListBegin($itrans, " << "$" << etype << ", $" << size << ");" << endl; } } // For loop iterates over elements string i = tmp("_i"); - indent(f_service_) << + indent(out) << "for ($" << i << " = 0; $" << i << " < $" << size << "; ++$" << i << ")" << endl; - scope_up(f_service_); + scope_up(out); if (ttype->is_map()) { - generate_deserialize_map_element((t_map*)ttype, prefix); + generate_deserialize_map_element(out, (t_map*)ttype, prefix); } else if (ttype->is_set()) { - generate_deserialize_set_element((t_set*)ttype, prefix); + generate_deserialize_set_element(out, (t_set*)ttype, prefix); } else if (ttype->is_list()) { - generate_deserialize_list_element((t_list*)ttype, prefix); + generate_deserialize_list_element(out, (t_list*)ttype, prefix); } - scope_down(f_service_); + scope_down(out); if (!binary_inline_) { // Read container end if (ttype->is_map()) { - indent(f_service_) << "$this->_iprot->readMapEnd($this->_itrans);" << endl; + indent(out) << "$xfer += $iprot->readMapEnd($itrans);" << endl; } else if (ttype->is_set()) { - indent(f_service_) << "$this->_iprot->readSetEnd($this->_itrans);" << endl; + indent(out) << "$xfer += $iprot->readSetEnd($itrans);" << endl; } else if (ttype->is_list()) { - indent(f_service_) << "$this->_iprot->readListEnd($this->_itrans);" << endl; + indent(out) << "$xfer += $iprot->readListEnd($itrans);" << endl; } } - scope_down(f_service_); + scope_down(out); } /** * Generates code to deserialize a map */ -void t_php_generator::generate_deserialize_map_element(t_map* tmap, +void t_php_generator::generate_deserialize_map_element(ofstream &out, + 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_) << + indent(out) << declare_field(&fkey, true, true) << endl; - indent(f_service_) << + indent(out) << declare_field(&fval, true, true) << endl; - generate_deserialize_field(&fkey); - generate_deserialize_field(&fval); + generate_deserialize_field(out, &fkey); + generate_deserialize_field(out, &fval); - indent(f_service_) << + indent(out) << "$" << prefix << "[$" << key << "] = $" << val << ";" << endl; } -void t_php_generator::generate_deserialize_set_element(t_set* tset, +void t_php_generator::generate_deserialize_set_element(ofstream &out, + t_set* tset, string prefix) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); - indent(f_service_) << + indent(out) << "$" << elem << " = null;" << endl; - generate_deserialize_field(&felem); + generate_deserialize_field(out, &felem); - indent(f_service_) << + indent(out) << "$" << prefix << " []= $" << elem << ";" << endl; } -void t_php_generator::generate_deserialize_list_element(t_list* tlist, +void t_php_generator::generate_deserialize_list_element(ofstream &out, + t_list* tlist, string prefix) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); - indent(f_service_) << + indent(out) << "$" << elem << " = null;" << endl; - generate_deserialize_field(&felem); + generate_deserialize_field(out, &felem); - indent(f_service_) << + indent(out) << "$" << prefix << " []= $" << elem << ";" << endl; } @@ -677,7 +936,8 @@ void t_php_generator::generate_deserialize_list_element(t_list* tlist, * @param tfield The field to serialize * @param prefix Name to prepend to field name */ -void t_php_generator::generate_serialize_field(t_field* tfield, +void t_php_generator::generate_serialize_field(ofstream &out, + t_field* tfield, string prefix) { t_type* type = tfield->get_type(); while (type->is_typedef()) { @@ -690,11 +950,13 @@ void t_php_generator::generate_serialize_field(t_field* tfield, prefix + tfield->get_name(); } - if (type->is_struct()) { - generate_serialize_struct((t_struct*)(tfield->get_type()), - prefix + tfield->get_name() + "->"); + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, + (t_struct*)(tfield->get_type()), + prefix + tfield->get_name()); } else if (type->is_container()) { - generate_serialize_container(tfield->get_type(), + generate_serialize_container(out, + tfield->get_type(), prefix + tfield->get_name()); } else if (type->is_base_type() || type->is_enum()) { @@ -709,40 +971,37 @@ void t_php_generator::generate_serialize_field(t_field* tfield, "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: - f_service_ << - indent() << "$_output .= strrev(pack('l', strlen($" << name << ")));" << endl << + out << + indent() << "$_output .= pack('N', strlen($" << name << "));" << endl << indent() << "$_output .= $" << name << ";" << endl; break; case t_base_type::TYPE_BYTE: - f_service_ << + out << indent() << "$_output .= pack('c', $" << name << ");" << endl; break; - case t_base_type::TYPE_I32: - f_service_ << - indent() << "$_output .= strrev(pack('l', $" << name << "));" << endl; + case t_base_type::TYPE_I16: + out << + indent() << "$_output .= pack('n', $" << name << ");" << endl; break; - case t_base_type::TYPE_U32: - f_service_ << - indent() << "writeU32($this->_otrans, $" << name << ");" << endl; + case t_base_type::TYPE_I32: + out << + indent() << "$_output .= pack('N', $" << name << ");" << endl; break; case t_base_type::TYPE_I64: - f_service_ << + out << indent() << "$_output .= pack('N2', $" << name << " >> 32, $" << name << " & 0xFFFFFFFF);" << endl; break; - case t_base_type::TYPE_U64: - f_service_ << "writeU64($this->_otrans, $" << name << ");"; - break; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no PHP name for base type " + tbase; } } else if (type->is_enum()) { - f_service_ << - indent() << "$_output .= strrev(pack('l', $" << name << "));" << endl; + out << + indent() << "$_output .= pack('N', $" << name << ");" << endl; } } else { - indent(f_service_) << - "$this->_oprot->"; + indent(out) << + "$xfer += $oprot->"; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); @@ -752,30 +1011,27 @@ void t_php_generator::generate_serialize_field(t_field* tfield, "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: - f_service_ << "writeString($this->_otrans, $" << name << ");"; + out << "writeString($otrans, $" << name << ");"; break; case t_base_type::TYPE_BYTE: - f_service_ << "writeByte($this->_otrans, $" << name << ");"; + out << "writeByte($otrans, $" << name << ");"; break; - case t_base_type::TYPE_I32: - f_service_ << "writeI32($this->_otrans, $" << name << ");"; + case t_base_type::TYPE_I16: + out << "writeI16($otrans, $" << name << ");"; break; - case t_base_type::TYPE_U32: - f_service_ << "writeU32($this->_otrans, $" << name << ");"; + case t_base_type::TYPE_I32: + out << "writeI32($otrans, $" << name << ");"; break; case t_base_type::TYPE_I64: - f_service_ << "writeI64($this->_otrans, $" << name << ");"; - break; - case t_base_type::TYPE_U64: - f_service_ << "writeU64($this->_otrans, $" << name << ");"; + out << "writeI64($otrans, $" << name << ");"; break; default: - throw "compiler error: no C++ name for base type " + tbase; + throw "compiler error: no PHP name for base type " + tbase; } } else if (type->is_enum()) { - f_service_ << "writeI32($this->_otrans, $" << name << ");"; + out << "writeI32($otrans, $" << name << ");"; } - f_service_ << endl; + out << endl; } } else { printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", @@ -791,176 +1047,140 @@ void t_php_generator::generate_serialize_field(t_field* tfield, * @param tstruct The struct to serialize * @param prefix String prefix to attach to all fields */ -void t_php_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_); - - if (!binary_inline_) { - indent(f_service_) << - "$this->_oprot->writeStructBegin($this->_otrans, '" << name << "');" << endl; - } - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - // Write field header - if (binary_inline_) { - f_service_ << - indent() << "$_output .= pack('c', " << type_to_enum((*f_iter)->get_type()) << ");" << endl << - indent() << "$_output .= strrev(pack('l', " << (*f_iter)->get_key() << "));" << endl; - } else { - indent(f_service_) << - "$this->_oprot->writeFieldBegin($this->_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 - if (binary_inline_) { - } else { - indent(f_service_) << - "$this->_oprot->writeFieldEnd($this->_otrans);" << endl; - } - } - +void t_php_generator::generate_serialize_struct(ofstream &out, + t_struct* tstruct, + string prefix) { if (binary_inline_) { - f_service_ << - indent() << "$_output .= pack('c', TType::STOP);" << endl; + indent(out) << + "$xfer += write_struct_" << tstruct->get_name() << "($_output, $" << prefix << ");" << endl; } else { - // Write the struct map - f_service_ << - indent() << "$this->_oprot->writeFieldStop($this->_otrans);" << endl << - indent() << "$this->_oprot->writeStructEnd($this->_otrans);" << endl; + indent(out) << + "$xfer += write_struct_" << tstruct->get_name() << "($oprot, $otrans, $" << prefix << ");" << endl; } - - scope_down(f_service_); } -void t_php_generator::generate_serialize_container(t_type* ttype, +void t_php_generator::generate_serialize_container(ofstream &out, + t_type* ttype, string prefix) { - scope_up(f_service_); + scope_up(out); if (ttype->is_map()) { if (binary_inline_) { - f_service_ << + out << indent() << "$_output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_key_type()) << ");" << endl << indent() << "$_output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_val_type()) << ");" << endl << indent() << "$_output .= strrev(pack('l', count($" << prefix << ")));" << endl; } else { - indent(f_service_) << - "$this->_oprot->writeMapBegin($this->_otrans, " << + indent(out) << + "$oprot->writeMapBegin($otrans, " << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << "count($" << prefix << "));" << endl; } } else if (ttype->is_set()) { if (binary_inline_) { - f_service_ << + out << indent() << "$_output .= pack('c', " << type_to_enum(((t_set*)ttype)->get_elem_type()) << ");" << endl << indent() << "$_output .= strrev(pack('l', count($" << prefix << ")));" << endl; } else { - indent(f_service_) << - "$this->_oprot->writeSetBegin($this->_otrans, " << + indent(out) << + "$oprot->writeSetBegin($otrans, " << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << "count($" << prefix << "));" << endl; } } else if (ttype->is_list()) { if (binary_inline_) { - f_service_ << + out << indent() << "$_output .= pack('c', " << type_to_enum(((t_list*)ttype)->get_elem_type()) << ");" << endl << indent() << "$_output .= strrev(pack('l', count($" << prefix << ")));" << endl; } else { - indent(f_service_) << - "$this->_oprot->writeListBegin($this->_otrans, " << + indent(out) << + "$oprot->writeListBegin($otrans, " << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << "count($" << prefix << "));" << endl; } } - scope_up(f_service_); + scope_up(out); if (ttype->is_map()) { string kiter = tmp("_kiter"); string viter = tmp("_viter"); - indent(f_service_) << + indent(out) << "foreach ($" << prefix << " as " << "$" << kiter << " => $" << viter << ")" << endl; - scope_up(f_service_); - generate_serialize_map_element((t_map*)ttype, kiter, viter); - scope_down(f_service_); + scope_up(out); + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + scope_down(out); } else if (ttype->is_set()) { string iter = tmp("_iter"); - indent(f_service_) << + indent(out) << "foreach ($" << prefix << " as $" << iter << ")" << endl; - scope_up(f_service_); - generate_serialize_set_element((t_set*)ttype, iter); - scope_down(f_service_); + scope_up(out); + generate_serialize_set_element(out, (t_set*)ttype, iter); + scope_down(out); } else if (ttype->is_list()) { string iter = tmp("_iter"); - indent(f_service_) << + indent(out) << "foreach ($" << prefix << " as $" << iter << ")" << endl; - scope_up(f_service_); - generate_serialize_list_element((t_list*)ttype, iter); - scope_down(f_service_); + scope_up(out); + generate_serialize_list_element(out, (t_list*)ttype, iter); + scope_down(out); } - - scope_down(f_service_); + scope_down(out); if (!binary_inline_) { if (ttype->is_map()) { - indent(f_service_) << - "$this->_oprot->writeMapEnd($this->_otrans);" << endl; + indent(out) << + "$oprot->writeMapEnd($otrans);" << endl; } else if (ttype->is_set()) { - indent(f_service_) << - "$this->_oprot->writeSetEnd($this->_otrans);" << endl; + indent(out) << + "$oprot->writeSetEnd($otrans);" << endl; } else if (ttype->is_list()) { - indent(f_service_) << - "$this->_oprot->writeListEnd($this->_otrans);" << endl; + indent(out) << + "$oprot->writeListEnd($otrans);" << endl; } } - scope_down(f_service_); + scope_down(out); } /** * Serializes the members of a map. * */ -void t_php_generator::generate_serialize_map_element(t_map* tmap, - string kiter, - string viter) { +void t_php_generator::generate_serialize_map_element(ofstream &out, + t_map* tmap, + string kiter, + string viter) { t_field kfield(tmap->get_key_type(), kiter); - generate_serialize_field(&kfield, ""); + generate_serialize_field(out, &kfield, ""); t_field vfield(tmap->get_val_type(), viter); - generate_serialize_field(&vfield, ""); + generate_serialize_field(out, &vfield, ""); } /** * Serializes the members of a set. */ -void t_php_generator::generate_serialize_set_element(t_set* tset, - string iter) { +void t_php_generator::generate_serialize_set_element(ofstream &out, + t_set* tset, + string iter) { t_field efield(tset->get_elem_type(), iter); - generate_serialize_field(&efield, ""); + generate_serialize_field(out, &efield, ""); } /** * Serializes the members of a list. */ -void t_php_generator::generate_serialize_list_element(t_list* tlist, +void t_php_generator::generate_serialize_list_element(ofstream &out, + t_list* tlist, string iter) { t_field efield(tlist->get_elem_type(), iter); - generate_serialize_field(&efield, ""); + generate_serialize_field(out, &efield, ""); } /** @@ -1007,16 +1227,14 @@ string t_php_generator::base_type_name(t_base_type::t_base tbase) { return "TString"; case t_base_type::TYPE_BYTE: return "UInt8"; + case t_base_type::TYPE_I16: + return "Int16"; 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; + throw "compiler error: no PHP name for base type " + tbase; } } @@ -1041,10 +1259,9 @@ string t_php_generator::declare_field(t_field* tfield, bool init, bool obj) { result += " = ''"; break; case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: - case t_base_type::TYPE_U32: case t_base_type::TYPE_I64: - case t_base_type::TYPE_U64: result += " = 0"; break; default: @@ -1115,14 +1332,12 @@ string t_php_generator ::type_to_enum(t_type* type) { return "TType::STRING"; case t_base_type::TYPE_BYTE: return "TType::BYTE"; + case t_base_type::TYPE_I16: + return "TType::I16"; 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"; diff --git a/compiler/cpp/src/generate/t_php_generator.h b/compiler/cpp/src/generate/t_php_generator.h index e5fb30cf..d5bc8e0b 100644 --- a/compiler/cpp/src/generate/t_php_generator.h +++ b/compiler/cpp/src/generate/t_php_generator.h @@ -8,9 +8,6 @@ #include "t_oop_generator.h" -// TODO(mcslee: Paramaterize the output dir -#define T_PHP_DIR "gen-php" - /** * Java code generator. * @@ -20,6 +17,11 @@ class t_php_generator : public t_oop_generator { public: t_php_generator(bool binary_inline=false) { binary_inline_ = binary_inline; + if (binary_inline_) { + T_PHP_DIR = "gen-phpi"; + } else { + T_PHP_DIR = "gen-php"; + } } ~t_php_generator() {} @@ -27,57 +29,80 @@ class t_php_generator : public t_oop_generator { /** Init and close methods */ void init_generator(t_program *tprogram); - void close_generator(); + void close_generator(t_program *tprogram); /** 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); + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_struct (t_struct* tstruct); + void generate_xception (t_struct* txception); + void generate_service (t_service* tservice); + + + void generate_php_struct(t_struct* tstruct, bool is_exception); + void generate_php_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception); + void generate_php_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_php_struct_writer(std::ofstream& out, t_struct* tstruct); + + void generate_php_function_helpers (t_function* tfunction); /** Service-level generation functions */ + void generate_service_helpers(t_service* tservice); void generate_service_interface (t_service* tservice); void generate_service_client (t_service* tservice); /** Serialization constructs */ - void generate_deserialize_field (t_field* tfield, - std::string prefix=""); + void generate_deserialize_field (std::ofstream &out, + t_field* tfield, + std::string prefix="", + bool inclass=false); - void generate_deserialize_struct (t_struct* tstruct, + void generate_deserialize_struct (std::ofstream &out, + t_struct* tstruct, std::string prefix=""); - void generate_deserialize_container (t_type* ttype, + void generate_deserialize_container (std::ofstream &out, + t_type* ttype, std::string prefix=""); - void generate_deserialize_set_element (t_set* tset, + void generate_deserialize_set_element (std::ofstream &out, + t_set* tset, std::string prefix=""); - void generate_deserialize_map_element (t_map* tmap, + void generate_deserialize_map_element (std::ofstream &out, + t_map* tmap, std::string prefix=""); - void generate_deserialize_list_element (t_list* tlist, + void generate_deserialize_list_element (std::ofstream &out, + t_list* tlist, std::string prefix=""); - void generate_serialize_field (t_field* tfield, + void generate_serialize_field (std::ofstream &out, + t_field* tfield, std::string prefix=""); - void generate_serialize_struct (t_struct* tstruct, + void generate_serialize_struct (std::ofstream &out, + t_struct* tstruct, std::string prefix=""); - void generate_serialize_container (t_type* ttype, + void generate_serialize_container (std::ofstream &out, + t_type* ttype, std::string prefix=""); - void generate_serialize_map_element (t_map* tmap, + void generate_serialize_map_element (std::ofstream &out, + t_map* tmap, std::string kiter, std::string viter); - void generate_serialize_set_element (t_set* tmap, + void generate_serialize_set_element (std::ofstream &out, + t_set* tmap, std::string iter); - void generate_serialize_list_element (t_list* tlist, + void generate_serialize_list_element (std::ofstream &out, + t_list* tlist, std::string iter); /** Helper rendering functions */ @@ -93,8 +118,10 @@ class t_php_generator : public t_oop_generator { private: /** File streams */ + char* T_PHP_DIR; std::ofstream f_types_; + std::ofstream f_helpers_; std::ofstream f_service_; /** Generate protocol-independent template? Or Binary inline code? */ diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc index 6a7faf8c..944cd66d 100644 --- a/compiler/cpp/src/main.cc +++ b/compiler/cpp/src/main.cc @@ -95,12 +95,12 @@ void failure(char* fmt, ...) { void usage() { 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, " -phpi Generate PHP inlined files\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, " --phpi Generate PHP inlined files\n"); //fprintf(stderr, " -python Generate Python output files\n"); - fprintf(stderr, " -d Print parse debugging to standard output\n"); + fprintf(stderr, " --debug Print parse debugging to standard output\n"); exit(1); } @@ -124,16 +124,16 @@ int main(int argc, char** argv) { } for (i = 1; i < argc-1; i++) { - if (strcmp(argv[i], "-d") == 0) { + if (strcmp(argv[i], "--debug") == 0) { g_debug = 1; - } else if (strcmp(argv[i], "-cpp") == 0) { + } else if (strcmp(argv[i], "--cpp") == 0) { gen_cpp = true; - } else if (strcmp(argv[i], "-java") == 0) { + } else if (strcmp(argv[i], "--java") == 0) { gen_java = true; - } else if (strcmp(argv[i], "-php") == 0) { + } else if (strcmp(argv[i], "--php") == 0) { gen_php = true; php_inline = false; - } else if (strcmp(argv[i], "-phpi") == 0) { + } else if (strcmp(argv[i], "--phpi") == 0) { gen_php = true; php_inline = true; } else { diff --git a/compiler/cpp/src/parse/t_base_type.h b/compiler/cpp/src/parse/t_base_type.h index f6cc197b..5aebba7a 100644 --- a/compiler/cpp/src/parse/t_base_type.h +++ b/compiler/cpp/src/parse/t_base_type.h @@ -15,10 +15,9 @@ class t_base_type : public t_type { TYPE_VOID, TYPE_STRING, TYPE_BYTE, + TYPE_I16, TYPE_I32, - TYPE_U32, - TYPE_I64, - TYPE_U64 + TYPE_I64 }; t_base_type(std::string name, t_base base) : diff --git a/compiler/cpp/src/parse/t_field.h b/compiler/cpp/src/parse/t_field.h index fc384563..7f4d0e97 100644 --- a/compiler/cpp/src/parse/t_field.h +++ b/compiler/cpp/src/parse/t_field.h @@ -14,19 +14,19 @@ class t_field { 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) : + t_field(t_type* type, std::string name, int32_t key) : type_(type), name_(name), key_(key) {} ~t_field() {} t_type* get_type() const { return type_; } const std::string& get_name() const { return name_; } - uint32_t get_key() const { return key_; } + int32_t get_key() const { return key_; } private: t_type* type_; std::string name_; - uint32_t key_; + int32_t key_; }; #endif diff --git a/compiler/cpp/src/parse/t_function.h b/compiler/cpp/src/parse/t_function.h index 9e6c56a2..58667d89 100644 --- a/compiler/cpp/src/parse/t_function.h +++ b/compiler/cpp/src/parse/t_function.h @@ -20,6 +20,20 @@ class t_function { returntype_(returntype), name_(name), arglist_(arglist), + async_(async) { + xceptions_ = new t_struct; + } + + + t_function(t_type* returntype, + std::string name, + t_struct* arglist, + t_struct* xceptions, + bool async=false) : + returntype_(returntype), + name_(name), + arglist_(arglist), + xceptions_(xceptions), async_(async) {} ~t_function() {} @@ -27,12 +41,14 @@ class t_function { t_type* get_returntype() const { return returntype_; } const std::string& get_name() const { return name_; } t_struct* get_arglist() const { return arglist_; } + t_struct* get_xceptions() const { return xceptions_; } bool is_async() const { return async_; } private: t_type* returntype_; std::string name_; t_struct* arglist_; + t_struct* xceptions_; bool async_; }; diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h index cda24c63..98968ff2 100644 --- a/compiler/cpp/src/parse/t_program.h +++ b/compiler/cpp/src/parse/t_program.h @@ -21,6 +21,7 @@ * Typedefs * Enumerations * Structs + * Exceptions * Services * * @author Mark Slee @@ -28,52 +29,54 @@ class t_program { public: t_program(std::string name) : - name_(name) { + name_(name), namespace_() { type_void = new t_base_type("void", t_base_type::TYPE_VOID); type_string = new t_base_type("string", t_base_type::TYPE_STRING); type_byte = new t_base_type("byte", t_base_type::TYPE_BYTE); + type_i16 = new t_base_type("i16", t_base_type::TYPE_I16); type_i32 = new t_base_type("i32", t_base_type::TYPE_I32); - type_u32 = new t_base_type("u32", t_base_type::TYPE_U32); type_i64 = new t_base_type("i64", t_base_type::TYPE_I64); - type_u64 = new t_base_type("u64", t_base_type::TYPE_U64); } ~t_program() { delete type_string; delete type_byte; + delete type_i16; delete type_i32; - delete type_u32; delete type_i64; - delete type_u64; } // Name accessor const std::string& get_name() const { return name_; } + // Namespace + const std::string& get_namespace() const { return namespace_; } + // Accessors for program elements - const std::vector& get_typedefs() const { return typedefs_; } - const std::vector& get_enums() const { return enums_; } - const std::vector& get_structs() const { return structs_; } - const std::vector& get_services() const { return services_; } + const std::vector& get_typedefs() const { return typedefs_; } + const std::vector& get_enums() const { return enums_; } + const std::vector& get_structs() const { return structs_; } + const std::vector& get_xceptions() const { return xceptions_; } + const std::vector& get_services() const { return services_; } // Accessors for global types t_type* get_void_type() const { return type_void; } t_type* get_string_type() const { return type_string; } t_type* get_byte_type() const { return type_byte; } + t_type* get_i16_type() const { return type_i16; } t_type* get_i32_type() const { return type_i32; } - t_type* get_u32_type() const { return type_u32; } t_type* get_i64_type() const { return type_i64; } - t_type* get_u64_type() const { return type_u64; } // Custom data type lookup - void add_custom_type(std::string name, t_type* type) { - custom_types_[name] = type; - } t_type* get_custom_type(std::string name) { return custom_types_[name]; } // New program element addition + void set_namespace(std::string name) { + namespace_ = name; + } + void add_typedef(t_typedef* td) { typedefs_.push_back(td); add_custom_type(td->get_symbolic(), td); @@ -86,19 +89,32 @@ class t_program { structs_.push_back(ts); add_custom_type(ts->get_name(), ts); } + void add_xception(t_struct* tx) { + xceptions_.push_back(tx); + add_custom_type(tx->get_name(), tx); + } void add_service(t_service* ts) { services_.push_back(ts); } private: + // Add custom type for lookup + void add_custom_type(std::string name, t_type* type) { + custom_types_[name] = type; + } + // Name std::string name_; + // Namespace + std::string namespace_; + // Components - std::vector typedefs_; - std::vector enums_; - std::vector structs_; - std::vector services_; + std::vector typedefs_; + std::vector enums_; + std::vector structs_; + std::vector xceptions_; + std::vector services_; // Type map std::map custom_types_; @@ -107,10 +123,9 @@ class t_program { t_type* type_void; t_type* type_string; t_type* type_byte; + t_type* type_i16; t_type* type_i32; - t_type* type_u32; t_type* type_i64; - t_type* type_u64; }; #endif diff --git a/compiler/cpp/src/parse/t_struct.h b/compiler/cpp/src/parse/t_struct.h index 6e87c8c6..a365cd44 100644 --- a/compiler/cpp/src/parse/t_struct.h +++ b/compiler/cpp/src/parse/t_struct.h @@ -9,22 +9,41 @@ class t_struct : public t_type { public: - t_struct() {} - t_struct(const std::string& name) : t_type(name) {} + t_struct() : is_xception_(false) {} + t_struct(const std::string& name) : t_type(name), is_xception_(false) {} ~t_struct() {} /** Set the struct name */ - void set_name(const std::string& name) { name_ = name; } + void set_name(const std::string& name) { + name_ = name; + } + + /** Mark as an exception */ + void set_xception(bool is_xception) { + is_xception_ = is_xception; + } /** Add a new field to the list */ - void append(t_field* elem) { members_.push_back(elem); } + void append(t_field* elem) { + members_.push_back(elem); + } + + const std::vector& get_members() { + return members_; + } + + bool is_struct() const { + return !is_xception_; + } - const std::vector& get_members() { return members_; } - bool is_struct() const { return true; } + bool is_xception() const { + return is_xception_; + } private: std::vector members_; + bool is_xception_; }; #endif diff --git a/compiler/cpp/src/parse/t_type.h b/compiler/cpp/src/parse/t_type.h index 6703bfa6..6328961e 100644 --- a/compiler/cpp/src/parse/t_type.h +++ b/compiler/cpp/src/parse/t_type.h @@ -19,6 +19,7 @@ class t_type { virtual bool is_typedef() const { return false; } virtual bool is_enum() const { return false; } virtual bool is_struct() const { return false; } + virtual bool is_xception() const { return false; } virtual bool is_list() const { return false; } virtual bool is_set() const { return false; } virtual bool is_map() const { return false; } diff --git a/compiler/cpp/src/thrift.l b/compiler/cpp/src/thrift.l index 9d2944ac..1f4d7fe9 100644 --- a/compiler/cpp/src/thrift.l +++ b/compiler/cpp/src/thrift.l @@ -19,7 +19,7 @@ /** Helper definitions */ intconstant ([0-9]+) -identifier ([a-zA-Z_][a-zA-Z_0-9]*) +identifier ([a-zA-Z_][\.a-zA-Z_0-9]*) whitespace ([ \t\r\n]*) multicomm ("/*""/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/") comment ("//"[^\n]*) @@ -31,15 +31,17 @@ symbol ([\,\{\}\(\)\=<>]) {multicomm} { /* do nothing */ } {comment} { /* do nothing */ } -{symbol} { return yytext[0]; } +{symbol} { return yytext[0]; } + +"namespace" { return tok_namespace; } "void" { return tok_void; } "byte" { return tok_byte; } -"string" { return tok_string; } +"i16" { return tok_i16; } "i32" { return tok_i32; } -"u32" { return tok_u32; } "i64" { return tok_i64; } -"u64" { return tok_u64; } +"double" { return tok_double; } +"string" { return tok_string; } "map" { return tok_map; } "list" { return tok_list; } @@ -49,11 +51,13 @@ symbol ([\,\{\}\(\)\=<>]) "typedef" { return tok_typedef; } "struct" { return tok_struct; } +"exception" { return tok_xception; } +"throws" { return tok_throws; } "service" { return tok_service; } "enum" { return tok_enum; } -{intconstant} { yylval.iconst = atoi(yytext) ; return tok_int_constant; } +{intconstant} { yylval.iconst = atoi(yytext); return tok_int_constant; } {identifier} { yylval.id = strdup(yytext); return tok_identifier; } %% diff --git a/compiler/cpp/src/thrift.y b/compiler/cpp/src/thrift.y index d0882ff7..069d6c8b 100644 --- a/compiler/cpp/src/thrift.y +++ b/compiler/cpp/src/thrift.y @@ -13,7 +13,7 @@ #include "globals.h" #include "parse/t_program.h" -int y_field_val = 0; +int y_field_val = -1; %} @@ -35,13 +35,16 @@ int y_field_val = 0; %token tok_identifier %token tok_int_constant +/** Namespace */ +%token tok_namespace + /** Base datatypes */ %token tok_byte %token tok_string +%token tok_i16 %token tok_i32 -%token tok_u32 %token tok_i64 -%token tok_u64 +%token tok_double /** Complex Types */ %token tok_map @@ -57,6 +60,8 @@ int y_field_val = 0; /** Thrift actions */ %token tok_typedef %token tok_struct +%token tok_xception +%token tok_throws %token tok_service %token tok_enum @@ -67,18 +72,22 @@ int y_field_val = 0; %type SetType %type ListType +%type Namespace + %type Typedef %type DefinitionType %type Field %type FieldType %type FieldList +%type ThrowsOptional %type Enum %type EnumDefList %type EnumDef %type Struct +%type Xception %type Service @@ -109,7 +118,12 @@ DefinitionList: } Definition: - Typedef + Namespace + { + pdebug("Definition -> Namespace"); + g_program->set_namespace($1); + } +| Typedef { pdebug("Definition -> Typedef"); g_program->add_typedef($1); @@ -124,11 +138,23 @@ Definition: pdebug("Definition -> Struct"); g_program->add_struct($1); } +| Xception + { + pdebug("Definition -> Xception"); + g_program->add_xception($1); + } | Service { pdebug("Definition -> Service"); g_program->add_service($1); - } + } + +Namespace: + tok_namespace tok_identifier + { + pdebug("Namespace -> tok_namespace tok_identifier"); + $$ = $2; + } Typedef: tok_typedef DefinitionType tok_identifier @@ -187,7 +213,17 @@ Struct: pdebug("Struct -> tok_struct tok_identifier { FieldList }"); $4->set_name($2); $$ = $4; - y_field_val = 0; + y_field_val = -1; + } + +Xception: + tok_xception tok_identifier '{' FieldList '}' + { + pdebug("Xception -> tok_xception tok_identifier { FieldList }"); + $4->set_name($2); + $4->set_xception(true); + $$ = $4; + y_field_val = -1; } Service: @@ -199,7 +235,7 @@ Service: } FunctionList: - FunctionList Function + FunctionList Function CommaOptional { pdebug("FunctionList -> FunctionList Function"); $$ = $1; @@ -211,12 +247,18 @@ FunctionList: $$ = new t_service; } +CommaOptional: + ',' + {} +| + {} + Function: - FunctionType AsyncOptional tok_identifier '(' FieldList ')' + FunctionType AsyncOptional tok_identifier '(' FieldList ')' ThrowsOptional { $5->set_name(std::string($3) + "_args"); - $$ = new t_function($1, $3, $5, $2); - y_field_val = 0; + $$ = new t_function($1, $3, $5, $7, $2); + y_field_val = -1; } AsyncOptional: @@ -229,6 +271,16 @@ AsyncOptional: $$ = false; } +ThrowsOptional: + tok_throws '(' FieldList ')' + { + $$ = $3; + } +| + { + $$ = new t_struct; + } + FieldList: FieldList ',' Field { @@ -252,18 +304,17 @@ Field: FieldType tok_identifier '=' tok_int_constant { pdebug("Field -> FieldType tok_identifier = tok_int_constant"); - if ($4 < 0) { - yyerror("Negative value (%d) not allowed as a field key.", $4); - exit(1); + if ($4 <= 0) { + printf("WARNING (%d): Nonpositive value (%d) not allowed as a field key for '%s'.\n", yylineno, $4, $2); + $4 = y_field_val--; } - $$ = new t_field($1, $2, (uint32_t)$4); - y_field_val = $4+1; + $$ = new t_field($1, $2, $4); } | FieldType tok_identifier { pdebug("Field -> FieldType tok_identifier"); - printf("WARNING (%d): No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", yylineno, $2); - $$ = new t_field($1, $2, y_field_val++); + printf("WARNING (%d): No field key specified for '%s', resulting protocol may have conflicts or not be backwards compatible!\n", yylineno, $2); + $$ = new t_field($1, $2, y_field_val--); } DefinitionType: @@ -322,26 +373,21 @@ BaseType: pdebug("BaseType -> tok_byte"); $$ = g_program->get_byte_type(); } +| tok_i16 + { + pdebug("BaseType -> tok_i16"); + $$ = g_program->get_i16_type(); + } | tok_i32 { pdebug("BaseType -> tok_i32"); $$ = g_program->get_i32_type(); } -| tok_u32 - { - pdebug("BaseType -> tok_u32"); - $$ = g_program->get_u32_type(); - } | tok_i64 { pdebug("BaseType -> tok_i64"); $$ = g_program->get_i64_type(); } -| tok_u64 - { - pdebug("BaseType -> tok_u64"); - $$ = g_program->get_u64_type(); - } ContainerType: MapType diff --git a/compiler/py/setup.py b/compiler/py/setup.py index 3901042b..7167181c 100644 --- a/compiler/py/setup.py +++ b/compiler/py/setup.py @@ -1,12 +1,12 @@ from distutils.core import setup -setup(name='Thrift', - version='1.0', - description='Thrift IDL compiler', - author =['Mark Slee', 'Marc Kwiatkowski'], - author_email= ['mcslee@facebook.com', 'marc@facebook.com'], - url='http://code.facebook.com/thrift', - package_dir={'thrift' : 'src'}, +setup(name = 'Thrift', + version = '1.0', + description = 'Thrift IDL compiler', + author = ['Mark Slee', 'Marc Kwiatkowski'], + author_email = ['mcslee@facebook.com', 'marc@facebook.com'], + url = 'http://code.facebook.com/thrift', + package_dir = {'thrift' : 'src'}, py_modules = ['thrift.parser', 'thrift.cpp_generator', 'thrift.generator', 'thrift.php_generator'], scripts = ['src/thrift'] ) -- 2.17.1