From: Mark Slee Date: Fri, 6 Jul 2007 02:45:25 +0000 (+0000) Subject: Lots of Ruby code generation improvements X-Git-Tag: 0.2.0~1321 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=58dfb4fbe82c346814c2e4f33b6045468bfd68c6;p=common%2Fthrift.git Lots of Ruby code generation improvements Summary: Submitted by Kevin Clark, Ruby guru from Powerset Reviewed By: mcslee Test Plan: He updated the tests in trunk/test/rb/ Notes: The code is now officially "ruby-ish" and idiomatic git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665151 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index 4c7d15f5..98d00080 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -17,8 +17,8 @@ thrift_SOURCES = src/thrifty.yy \ src/generate/t_perl_generator.cc \ src/generate/t_erl_generator.cc -thrift_CXXFLAGS = -Wall -Isrc -thrift_LDFLAGS = -Wall +thrift_CXXFLAGS = -Wall -Isrc $(BOOST_CPPFLAGS) +thrift_LDFLAGS = -Wall $(BOOST_LDFLAGS) thrift_LDADD = @LEXLIB@ diff --git a/compiler/cpp/bootstrap.sh b/compiler/cpp/bootstrap.sh index 3189372b..67b6eea9 100755 --- a/compiler/cpp/bootstrap.sh +++ b/compiler/cpp/bootstrap.sh @@ -2,7 +2,7 @@ ./cleanup.sh autoscan -aclocal +aclocal -I ../../lib/cpp/aclocal libtoolize --automake touch NEWS README AUTHORS ChangeLog autoconf diff --git a/compiler/cpp/configure.ac b/compiler/cpp/configure.ac index f6aa0f5d..2e4b603d 100644 --- a/compiler/cpp/configure.ac +++ b/compiler/cpp/configure.ac @@ -10,6 +10,8 @@ AM_INIT_AUTOMAKE AC_CHECK_HEADERS([stddef.h]) +AX_BOOST_BASE([1.33.1]) + AC_CHECK_FUNCS([mkdir]) AC_CHECK_FUNCS([realpath]) diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc index 9932afdc..bb884653 100644 --- a/compiler/cpp/src/generate/t_rb_generator.cc +++ b/compiler/cpp/src/generate/t_rb_generator.cc @@ -33,12 +33,15 @@ void t_rb_generator::init_generator() { rb_autogen_comment() << endl << rb_imports() << endl << render_includes() << endl; + begin_namespace(f_types_, ruby_modules(program_)); f_consts_ << rb_autogen_comment() << endl << rb_imports() << endl << "require '" << program_name_ << "_types'" << endl << endl; + begin_namespace(f_consts_, ruby_modules(program_)); + } /** @@ -81,6 +84,8 @@ string t_rb_generator::rb_imports() { */ void t_rb_generator::close_generator() { // Close types file + end_namespace(f_types_, ruby_modules(program_)); + end_namespace(f_consts_, ruby_modules(program_)); f_types_.close(); f_consts_.close(); } @@ -99,7 +104,7 @@ void t_rb_generator::generate_typedef(t_typedef* ttypedef) {} * @param tenum The enumeration */ void t_rb_generator::generate_enum(t_enum* tenum) { - f_types_ << + indent(f_types_) << "module " << tenum->get_name() << endl; indent_up(); @@ -123,9 +128,8 @@ void t_rb_generator::generate_enum(t_enum* tenum) { } indent_down(); - f_types_ << - "end" << endl << - endl; + indent(f_types_) << + "end" << endl << endl; } /** @@ -254,7 +258,7 @@ string t_rb_generator::render_const_value(t_type* type, t_const_value* value) { * Generates a ruby struct */ void t_rb_generator::generate_struct(t_struct* tstruct) { - generate_rb_struct(tstruct, false); + generate_rb_struct(f_types_, tstruct, false); } /** @@ -264,232 +268,109 @@ void t_rb_generator::generate_struct(t_struct* tstruct) { * @param txception The struct definition */ void t_rb_generator::generate_xception(t_struct* txception) { - generate_rb_struct(txception, true); + generate_rb_struct(f_types_, txception, true); } /** * Generates a ruby struct */ -void t_rb_generator::generate_rb_struct(t_struct* tstruct, - bool is_exception) { - generate_rb_struct_definition(f_types_, tstruct, is_exception); -} - -/** - * 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 - * decide to start using objects for them...) - * - * @param tstruct The struct definition - */ -void t_rb_generator::generate_rb_struct_definition(ofstream& out, - t_struct* tstruct, - bool is_exception, - bool is_result) { - const vector& members = tstruct->get_members(); - vector::const_iterator m_iter; - - indent(out) << - "class " << type_name(tstruct); +void t_rb_generator::generate_rb_struct(std::ofstream& out, t_struct* tstruct, bool is_exception = false) { + indent(out) << "class " << type_name(tstruct); if (is_exception) { out << " < StandardError"; } - out << endl; + out << endl; + indent_up(); + indent(out) << "include ThriftStruct" << endl; + + generate_accessors(out, tstruct); + generate_field_defns(out, tstruct); - out << endl; + indent_down(); + indent(out) << "end" << endl << endl; +} +void t_rb_generator::generate_accessors(std::ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + if (members.size() > 0) { - indent(out) << "attr_writer "; - bool first = true; + indent(out) << "attr_accessor "; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if (first) { - first = false; - } else { + if (m_iter != members.begin()) { out << ", "; } out << ":" << (*m_iter)->get_name(); } out << endl; - indent(out) << "attr_reader "; - first = true; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if (first) { - first = false; - } else { - out << ", "; - } - out << ":" << (*m_iter)->get_name(); - } - out << endl; - out << endl; } - - out << - indent() << "def initialize(d=nil)" << endl; - indent_up(); - - if (members.size() > 0) { - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - if ((*m_iter)->get_value() != NULL) { - indent(out) << declare_field(*m_iter) << endl; - } - } - indent(out) << - "if (d != nil)" << endl; - indent_up(); - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - out << - indent() << "if (d.has_key?('" << (*m_iter)->get_name() << "'))" << endl << - indent() << " @" << (*m_iter)->get_name() << " = d['" << (*m_iter)->get_name() << "']" << endl << - indent() << "end" << endl; - } - indent_down(); - indent(out) << "end" << endl; - } - - indent_down(); - indent(out) << "end" << endl; - - out << endl; - - generate_rb_struct_reader(out, tstruct); - generate_rb_struct_writer(out, tstruct); - - indent_down(); - indent(out) << "end" << endl << endl; } -/** - * Generates the read method for a struct - */ -void t_rb_generator::generate_rb_struct_reader(ofstream& out, - t_struct* tstruct) { +void t_rb_generator::generate_field_defns(std::ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; - - indent(out) << - "def read(iprot)" << endl; - indent_up(); - - indent(out) << - "iprot.readStructBegin()" << endl; - - // Loop over reading in fields - indent(out) << - "while true" << endl; - indent_up(); - - // Read beginning field marker - indent(out) << - "fname, ftype, fid = iprot.readFieldBegin()" << endl; - - // Check for field STOP marker and break - indent(out) << - "if (ftype === TType::STOP)" << endl; - indent_up(); - indent(out) << - "break" << endl; - indent_down(); - if (fields.size() > 0) { - indent(out) << - "end" << endl; - } - // Switch statement on the field we are reading - bool first = true; - - // Generate deserialization code for known cases - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - if (first) { - first = false; - out << - indent() << "if "; - } else { - out << - indent() << "elsif "; - } - out << "(fid == " << (*f_iter)->get_key() << ")" << endl; - indent_up(); - indent(out) << "if (ftype === " << type_to_enum((*f_iter)->get_type()) << ")" << endl; - indent_up(); - generate_deserialize_field(out, *f_iter, "@"); - indent_down(); - out << - indent() << "else" << endl << - indent() << " iprot.skip(ftype)" << endl << - indent() << "end" << endl; - indent_down(); + indent(out) << "FIELDS = {" << endl; + indent_up(); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (f_iter != fields.begin()) { + out << "," << endl; } - // In the default case we skip the field - out << - indent() << "else" << endl << - indent() << " iprot.skip(ftype)" << endl << - indent() << "end" << endl; - - // Read field end marker - indent(out) << - "iprot.readFieldEnd()" << endl; + indent(out) << + (*f_iter)->get_key() << " => "; - indent_down(); - indent(out) << "end" << endl; - - indent(out) << - "iprot.readStructEnd()" << endl; - - indent_down(); - indent(out) << "end" << endl; + generate_field_data(out, (*f_iter)->get_type(), (*f_iter)->get_name()); + } + indent_down(); out << endl; + indent(out) << "}" << endl; } -void t_rb_generator::generate_rb_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) << - "def write(oprot)" << endl; - indent_up(); - - indent(out) << - "oprot.writeStructBegin('" << name << "')" << endl; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - // Write field header - indent(out) << - "if (@" << (*f_iter)->get_name() << " != nil)" << endl; +void t_rb_generator::generate_field_data(std::ofstream& out, t_type* field_type, const std::string& field_name = "") { + // Begin this field's defn + out << "{:type => " << type_to_enum(field_type); + + if (!field_name.empty()) + out << ", :name => '" << field_name << "'"; + + if (! field_type->is_base_type()) { + if (field_type->is_struct()) { + out << ", :class => " << type_name(((t_struct*)field_type)); + } else if (field_type->is_list()) { + out << ", :element => "; + generate_field_data(out, ((t_list*)field_type)->get_elem_type()); + } else if (field_type->is_map()) { + out << ", :key => "; + generate_field_data(out, ((t_map*)field_type)->get_key_type()); + out << ", :value => "; + generate_field_data(out, ((t_map*)field_type)->get_val_type()); + } else if (field_type->is_set()) { + out << ", :element => "; + generate_field_data(out, ((t_set*)field_type)->get_elem_type()); + } + } + + // End of this field's defn + out << "}"; +} + +void t_rb_generator::begin_namespace(std::ofstream& out, vector modules) { + for (vector::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { + indent(out) << "module " << *m_iter << endl; indent_up(); - indent(out) << - "oprot.writeFieldBegin(" << - "'" << (*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, "@"); - - // Write field closer - indent(out) << - "oprot.writeFieldEnd()" << endl; + } +} +void t_rb_generator::end_namespace(std::ofstream& out, vector modules) { + for (vector::reverse_iterator m_iter = modules.rbegin(); m_iter != modules.rend(); ++m_iter) { indent_down(); indent(out) << "end" << endl; } - - // Write the struct map - out << - indent() << "oprot.writeFieldStop()" << endl << - indent() << "oprot.writeStructEnd()" << endl; - - indent_down(); - indent(out) << "end" << endl; - - out << - endl; } + /** * Generates a thrift service. * @@ -513,18 +394,21 @@ void t_rb_generator::generate_service(t_service* tservice) { "require '" << program_name_ << "_types'" << endl << endl; - f_service_ << "module " << tservice->get_name() << endl; + begin_namespace(f_service_, ruby_modules(tservice->get_program())); + + indent(f_service_) << "module " << tservice->get_name() << endl; indent_up(); // Generate the three main parts of the service (well, two for now in PHP) - generate_service_interface(tservice); generate_service_client(tservice); generate_service_server(tservice); generate_service_helpers(tservice); indent_down(); - f_service_ << "end" << endl << + indent(f_service_) << "end" << endl << endl; + + end_namespace(f_service_, ruby_modules(tservice->get_program())); // Close service file f_service_.close(); @@ -544,7 +428,7 @@ void t_rb_generator::generate_service_helpers(t_service* tservice) { for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); - generate_rb_struct_definition(f_service_, ts, false); + generate_rb_struct(f_service_, ts); generate_rb_function_helpers(*f_iter); } } @@ -567,33 +451,7 @@ void t_rb_generator::generate_rb_function_helpers(t_function* tfunction) { for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } - generate_rb_struct_definition(f_service_, &result, false, true); -} - -/** - * Generates a service interface definition. - * - * @param tservice The service to generate a header definition for - */ -void t_rb_generator::generate_service_interface(t_service* tservice) { - f_service_ << - indent() << "module Iface" << endl; - indent_up(); - - if (tservice->get_extends() != NULL) { - string extends = type_name(tservice->get_extends()); - indent(f_service_) << "include " << extends << "::Iface" << endl; - } - - vector functions = tservice->get_functions(); - vector::iterator f_iter; - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - f_service_ << - indent() << "def " << function_signature(*f_iter) << "" << endl << - indent() << "end" << endl << endl; - } - indent_down(); - indent(f_service_) << "end" << endl << endl; + generate_rb_struct(f_service_, &result); } /** @@ -614,24 +472,8 @@ void t_rb_generator::generate_service_client(t_service* tservice) { indent_up(); indent(f_service_) << - "include Iface" << endl << endl; + "include ThriftClient" << endl << endl; - // Constructor function - f_service_ << - indent() << "def initialize(iprot, oprot=nil)" << endl; - if (extends.empty()) { - f_service_ << - indent() << " @iprot = @oprot = iprot" << endl << - indent() << " if (oprot != nil)" << endl << - indent() << " @oprot = oprot" << endl << - indent() << " end" << endl << - indent() << " @seqid = 0" << endl; - } else { - f_service_ << - indent() << " super(iprot, oprot)" << endl; - } - indent(f_service_) << "end" << endl << endl; - // Generate client method implementations vector functions = tservice->get_functions(); vector::const_iterator f_iter; @@ -677,23 +519,13 @@ void t_rb_generator::generate_service_client(t_service* tservice) { std::string argsname = capitalize((*f_iter)->get_name() + "_args"); - // Serialize the request header - f_service_ << - indent() << "@oprot.writeMessageBegin('" << (*f_iter)->get_name() << "', TMessageType::CALL, @seqid)" << endl; - - f_service_ << - indent() << "args = " << argsname << ".new()" << endl; - + indent(f_service_) << "send_message('" << funname << "', " << argsname; + 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 - f_service_ << - indent() << "args.write(@oprot)" << endl << - indent() << "@oprot.writeMessageEnd()" << endl << - indent() << "@oprot.trans.flush()" << endl; + f_service_ << ", :" << (*fld_iter)->get_name() << " => " << (*fld_iter)->get_name(); + } + + f_service_ << ")" << endl; indent_down(); indent(f_service_) << "end" << endl; @@ -714,35 +546,21 @@ void t_rb_generator::generate_service_client(t_service* tservice) { // TODO(mcslee): Validate message reply here, seq ids etc. f_service_ << - indent() << "fname, mtype, rseqid = @iprot.readMessageBegin()" << endl << - indent() << "if mtype == TMessageType::EXCEPTION" << endl << - indent() << " x = TApplicationException.new()" << endl << - indent() << " x.read(@iprot)" << endl << - indent() << " @iprot.readMessageEnd()" << endl << - indent() << " raise x" << endl << - indent() << "end" << endl; - - f_service_ << - indent() << "result = " << resultname << ".new()" << endl << - indent() << "result.read(@iprot)" << endl << - indent() << "@iprot.readMessageEnd()" << endl; - + indent() << "result = receive_message(" << resultname << ")" << endl; + // Careful, only return _result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << - indent() << "if result.success != nil" << endl << - indent() << " return result.success" << endl << - indent() << "end" << endl; + indent() << "return result.success unless result.success.nil?" << endl; } 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() << " != nil" << endl << - indent() << " raise result." << (*x_iter)->get_name() << "" << endl << - indent() << "end" << endl; + indent(f_service_) << + "raise result." << (*x_iter)->get_name() << + " unless result." << (*x_iter)->get_name() << ".nil?" << endl; } // Careful, only return _result if not a void function @@ -787,60 +605,9 @@ void t_rb_generator::generate_service_server(t_service* tservice) { indent_up(); f_service_ << - indent() << "include Iface" << endl << indent() << "include TProcessor" << endl << endl; - indent(f_service_) << - "def initialize(handler)" << endl; - indent_up(); - if (extends.empty()) { - f_service_ << - indent() << "@handler = handler" << endl << - indent() << "@processMap = {}" << endl; - } else { - f_service_ << - indent() << "super(handler)" << endl; - } - for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - f_service_ << - indent() << "@processMap['" << (*f_iter)->get_name() << "'] = method(:process_" << (*f_iter)->get_name() << ")" << endl; - } - indent_down(); - indent(f_service_) << "end" << endl << endl; - - // Generate the server implementation - indent(f_service_) << - "def process(iprot, oprot)" << endl; - indent_up(); - - f_service_ << - indent() << "name, type, seqid = iprot.readMessageBegin()" << endl; - - // TODO(mcslee): validate message - - // HOT: dictionary function lookup - f_service_ << - indent() << "if (@processMap.has_key?(name))" << endl << - indent() << " @processMap[name].call(seqid, iprot, oprot)" << endl << - indent() << "else" << endl << - indent() << " iprot.skip(TType::STRUCT)" << endl << - indent() << " iprot.readMessageEnd()" << endl << - indent() << " x = TApplicationException.new(TApplicationException::UNKNOWN_METHOD, 'Unknown function '+name)" << endl << - indent() << " oprot.writeMessageBegin(name, TMessageType::EXCEPTION, seqid)" << endl << - indent() << " x.write(oprot)" << endl << - indent() << " oprot.writeMessageEnd()" << endl << - indent() << " oprot.trans.flush()" << endl << - indent() << " return" << endl << - indent() << "end" << endl; - - // Read end of args field, the T_STOP, and the struct close - f_service_ << - indent() << "return true" << endl; - - indent_down(); - indent(f_service_) << "end" << endl << endl; - // Generate the process subfunctions for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_process_function(tservice, *f_iter); @@ -867,9 +634,7 @@ void t_rb_generator::generate_process_function(t_service* tservice, string resultname = capitalize(tfunction->get_name()) + "_result"; f_service_ << - indent() << "args = " << argsname << ".new()" << endl << - indent() << "args.read(iprot)" << endl << - indent() << "iprot.readMessageEnd()" << endl; + indent() << "args = read_args(iprot, " << argsname << ")" << endl; t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); @@ -935,395 +700,13 @@ void t_rb_generator::generate_process_function(t_service* tservice, } f_service_ << - indent() << "oprot.writeMessageBegin('" << tfunction->get_name() << "', TMessageType::REPLY, seqid)" << endl << - indent() << "result.write(oprot)" << endl << - indent() << "oprot.writeMessageEnd()" << endl << - indent() << "oprot.trans.flush()" << endl; + indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)" << endl; // Close function indent_down(); indent(f_service_) << "end" << endl << endl; } -/** - * Deserializes a field of any type. - */ -void t_rb_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(); - } - - if (type->is_void()) { - throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + - prefix + tfield->get_name(); - } - - string name = prefix + tfield->get_name(); - - if (type->is_struct() || type->is_xception()) { - generate_deserialize_struct(out, - (t_struct*)type, - name); - } else if (type->is_container()) { - generate_deserialize_container(out, type, name); - } else if (type->is_base_type() || type->is_enum()) { - indent(out) << - name << " = iprot."; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "compiler error: cannot serialize void field in a struct: " + - name; - break; - case t_base_type::TYPE_STRING: - out << "readString();"; - break; - case t_base_type::TYPE_BOOL: - out << "readBool();"; - break; - case t_base_type::TYPE_BYTE: - out << "readByte();"; - break; - case t_base_type::TYPE_I16: - out << "readI16();"; - break; - case t_base_type::TYPE_I32: - out << "readI32();"; - break; - case t_base_type::TYPE_I64: - out << "readI64();"; - break; - case t_base_type::TYPE_DOUBLE: - out << "readDouble();"; - break; - default: - throw "compiler error: no PHP name for base type " + tbase; - } - } else if (type->is_enum()) { - out << "readI32();"; - } - out << endl; - - } else { - printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", - tfield->get_name().c_str(), type->get_name().c_str()); - } -} - -/** - * Generates an unserializer for a struct, calling read() - */ -void t_rb_generator::generate_deserialize_struct(ofstream &out, - t_struct* tstruct, - string prefix) { - out << - indent() << prefix << " = " << type_name(tstruct) << ".new()" << endl << - indent() << prefix << ".read(iprot)" << endl; -} - -/** - * Serialize a container by writing out the header followed by - * data and then a footer. - */ -void t_rb_generator::generate_deserialize_container(ofstream &out, - t_type* ttype, - string prefix) { - string size = tmp("_size"); - string ktype = tmp("_ktype"); - string vtype = tmp("_vtype"); - string etype = tmp("_etype"); - - t_field fsize(g_type_i32, size); - t_field fktype(g_type_byte, ktype); - t_field fvtype(g_type_byte, vtype); - t_field fetype(g_type_byte, etype); - - // Declare variables, read header - if (ttype->is_map()) { - out << - indent() << prefix << " = {}" << endl << - indent() << "(" << ktype << ", " << vtype << ", " << size << " ) = iprot.readMapBegin() " << endl; - } else if (ttype->is_set()) { - out << - indent() << prefix << " = {}" << endl << - indent() << "(" << etype << ", " << size << ") = iprot.readSetBegin()" << endl; - } else if (ttype->is_list()) { - out << - indent() << prefix << " = []" << endl << - indent() << "(" << etype << ", " << size << ") = iprot.readListBegin()" << endl; - } - - // For loop iterates over elements - string i = tmp("_i"); - indent(out) << - "for " << i << " in (1.." << size << ")" << endl; - - indent_up(); - - if (ttype->is_map()) { - generate_deserialize_map_element(out, (t_map*)ttype, prefix); - } else if (ttype->is_set()) { - generate_deserialize_set_element(out, (t_set*)ttype, prefix); - } else if (ttype->is_list()) { - generate_deserialize_list_element(out, (t_list*)ttype, prefix); - } - - indent_down(); - indent(out) << "end" << endl; - - // Read container end - if (ttype->is_map()) { - indent(out) << "iprot.readMapEnd()" << endl; - } else if (ttype->is_set()) { - indent(out) << "iprot.readSetEnd()" << endl; - } else if (ttype->is_list()) { - indent(out) << "iprot.readListEnd()" << endl; - } -} - - -/** - * Generates code to deserialize a map - */ -void t_rb_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); - - generate_deserialize_field(out, &fkey); - generate_deserialize_field(out, &fval); - - indent(out) << - prefix << "[" << key << "] = " << val << endl; -} - -/** - * Write a set element - */ -void t_rb_generator::generate_deserialize_set_element(ofstream &out, - t_set* tset, - string prefix) { - string elem = tmp("_elem"); - t_field felem(tset->get_elem_type(), elem); - - generate_deserialize_field(out, &felem); - - indent(out) << - prefix << "[" << elem << "] = true" << endl; -} - -/** - * Write a list element - */ -void t_rb_generator::generate_deserialize_list_element(ofstream &out, - t_list* tlist, - string prefix) { - string elem = tmp("_elem"); - t_field felem(tlist->get_elem_type(), elem); - - generate_deserialize_field(out, &felem); - - indent(out) << - prefix << ".push(" << elem << ")" << endl; -} - - -/** - * Serializes a field of any type. - * - * @param tfield The field to serialize - * @param prefix Name to prepend to field name - */ -void t_rb_generator::generate_serialize_field(ofstream &out, - t_field* tfield, - string prefix) { - t_type* type = tfield->get_type(); - while (type->is_typedef()) { - type = ((t_typedef*)type)->get_type(); - } - - // Do nothing for void types - if (type->is_void()) { - throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + - prefix + tfield->get_name(); - } - - if (type->is_struct() || type->is_xception()) { - generate_serialize_struct(out, - (t_struct*)type, - prefix + tfield->get_name()); - } else if (type->is_container()) { - generate_serialize_container(out, - type, - prefix + tfield->get_name()); - } else if (type->is_base_type() || type->is_enum()) { - - string name = prefix + tfield->get_name(); - - indent(out) << - "oprot."; - - if (type->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw - "compiler error: cannot serialize void field in a struct: " + name; - break; - case t_base_type::TYPE_STRING: - out << "writeString(" << name << ")"; - break; - case t_base_type::TYPE_BOOL: - out << "writeBool(" << name << ")"; - break; - case t_base_type::TYPE_BYTE: - out << "writeByte(" << name << ")"; - break; - case t_base_type::TYPE_I16: - out << "writeI16(" << name << ")"; - break; - case t_base_type::TYPE_I32: - out << "writeI32(" << name << ")"; - break; - case t_base_type::TYPE_I64: - out << "writeI64(" << name << ")"; - break; - case t_base_type::TYPE_DOUBLE: - out << "writeDouble(" << name << ")"; - break; - default: - throw "compiler error: no PHP name for base type " + tbase; - } - } else if (type->is_enum()) { - out << "writeI32(" << name << ")"; - } - out << endl; - } else { - printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", - prefix.c_str(), - tfield->get_name().c_str(), - type->get_name().c_str()); - } -} - -/** - * Serializes all the members of a struct. - * - * @param tstruct The struct to serialize - * @param prefix String prefix to attach to all fields - */ -void t_rb_generator::generate_serialize_struct(ofstream &out, - t_struct* tstruct, - string prefix) { - indent(out) << - prefix << ".write(oprot)" << endl; -} - -void t_rb_generator::generate_serialize_container(ofstream &out, - t_type* ttype, - string prefix) { - if (ttype->is_map()) { - indent(out) << - "oprot.writeMapBegin(" << - type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << - type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << - prefix << ".length)" << endl; - } else if (ttype->is_set()) { - indent(out) << - "oprot.writeSetBegin(" << - type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << - prefix << ".length)" << endl; - } else if (ttype->is_list()) { - indent(out) << - "oprot.writeListBegin(" << - type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << - prefix << ".length)" << endl; - } - - if (ttype->is_map()) { - string kiter = tmp("kiter"); - string viter = tmp("viter"); - indent(out) << - prefix << ".each do |" << kiter << ", " << viter << "|" << endl; - indent_up(); - generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); - indent_down(); - indent(out) << "end" << endl; - } else if (ttype->is_set()) { - string iter = tmp("iter"); - string t = tmp("true"); - indent(out) << - prefix << ".each do |" << iter << ", " << t << "|" << endl; - indent_up(); - generate_serialize_set_element(out, (t_set*)ttype, iter); - indent_down(); - indent(out) << "end" << endl; - } else if (ttype->is_list()) { - string iter = tmp("iter"); - indent(out) << - prefix << ".each do |" << iter << "|" << endl; - indent_up(); - generate_serialize_list_element(out, (t_list*)ttype, iter); - indent_down(); - indent(out) << "end" << endl; - } - - if (ttype->is_map()) { - indent(out) << - "oprot.writeMapEnd()" << endl; - } else if (ttype->is_set()) { - indent(out) << - "oprot.writeSetEnd()" << endl; - } else if (ttype->is_list()) { - indent(out) << - "oprot.writeListEnd()" << endl; - } -} - -/** - * Serializes the members of a map. - * - */ -void t_rb_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(out, &kfield, ""); - - t_field vfield(tmap->get_val_type(), viter); - generate_serialize_field(out, &vfield, ""); -} - -/** - * Serializes the members of a set. - */ -void t_rb_generator::generate_serialize_set_element(ofstream &out, - t_set* tset, - string iter) { - t_field efield(tset->get_elem_type(), iter); - generate_serialize_field(out, &efield, ""); -} - -/** - * Serializes the members of a list. - */ -void t_rb_generator::generate_serialize_list_element(ofstream &out, - t_list* tlist, - string iter) { - t_field efield(tlist->get_elem_type(), iter); - generate_serialize_field(out, &efield, ""); -} - /** * Declares a field, which may include initialization as necessary. * diff --git a/compiler/cpp/src/generate/t_rb_generator.h b/compiler/cpp/src/generate/t_rb_generator.h index f766de90..61a1d301 100644 --- a/compiler/cpp/src/generate/t_rb_generator.h +++ b/compiler/cpp/src/generate/t_rb_generator.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "t_oop_generator.h" @@ -37,12 +38,12 @@ class t_rb_generator : public t_oop_generator { * Program-level generation functions */ - void generate_typedef (t_typedef* ttypedef); - void generate_enum (t_enum* tenum); - void generate_const (t_const* tconst); - void generate_struct (t_struct* tstruct); - void generate_xception (t_struct* txception); - void generate_service (t_service* tservice); + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_const (t_const* tconst); + void generate_struct (t_struct* tstruct); + void generate_xception (t_struct* txception); + void generate_service (t_service* tservice); std::string render_const_value(t_type* type, t_const_value* value); @@ -50,11 +51,13 @@ class t_rb_generator : public t_oop_generator { * Struct generation code */ - void generate_rb_struct(t_struct* tstruct, bool is_exception); - void generate_rb_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false); + void generate_rb_struct(std::ofstream& out, t_struct* tstruct, bool is_exception); void generate_rb_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_rb_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_rb_function_helpers(t_function* tfunction); + void generate_accessors (std::ofstream& out, t_struct* tstruct); + void generate_field_defns (std::ofstream& out, t_struct* tstruct); + void generate_field_data (std::ofstream& out, t_type* field_type, const std::string& field_name); /** * Service-level generation functions @@ -137,6 +140,26 @@ class t_rb_generator : public t_oop_generator { in[0] = toupper(in[0]); return in; } + + std::string ruby_namespace(t_program* p) { + std::string ns = p->get_ruby_namespace(); + return ns.size() ? ns : ""; + } + + std::vector ruby_modules(t_program* p) { + std::string ns = p->get_ruby_namespace(); + boost::tokenizer<> tok(ns); + std::vector modules; + + for(boost::tokenizer<>::iterator beg=tok.begin(); beg != tok.end(); ++beg) { + modules.push_back(*beg); + } + + return modules; + } + + void begin_namespace(std::ofstream&, std::vector); + void end_namespace(std::ofstream&, std::vector); private: diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h index f39852d9..8b4f59cd 100644 --- a/compiler/cpp/src/parse/t_program.h +++ b/compiler/cpp/src/parse/t_program.h @@ -145,6 +145,14 @@ class t_program { return xsd_namespace_; } + void set_ruby_namespace(std::string ruby_namespace) { + ruby_namespace_ = ruby_namespace; + } + + const std::string& get_ruby_namespace() const { + return ruby_namespace_; + } + void set_perl_namespace(std::string perl_namespace) { perl_namespace_ = perl_namespace; } @@ -193,6 +201,9 @@ class t_program { // XSD namespace std::string xsd_namespace_; + // Ruby namespace + std::string ruby_namespace_; + // Perl namespace std::string perl_namespace_; diff --git a/compiler/cpp/src/thriftl.ll b/compiler/cpp/src/thriftl.ll index 3100c6a6..803f786a 100644 --- a/compiler/cpp/src/thriftl.ll +++ b/compiler/cpp/src/thriftl.ll @@ -63,42 +63,43 @@ sliteral ("'"[^']*"'") {symbol} { return yytext[0]; } -"namespace" { return tok_namespace; } -"cpp_namespace" { return tok_cpp_namespace; } -"cpp_include" { return tok_cpp_include; } -"cpp_type" { return tok_cpp_type; } -"java_package" { return tok_java_package; } -"php_namespace" { return tok_php_namespace; } -"xsd_all" { return tok_xsd_all; } -"xsd_optional" { return tok_xsd_optional; } -"xsd_nillable" { return tok_xsd_nillable; } -"xsd_namespace" { return tok_xsd_namespace; } -"xsd_attrs" { return tok_xsd_attrs; } -"include" { return tok_include; } - -"void" { return tok_void; } -"bool" { return tok_bool; } -"byte" { return tok_byte; } -"i16" { return tok_i16; } -"i32" { return tok_i32; } -"i64" { return tok_i64; } -"double" { return tok_double; } -"string" { return tok_string; } -"binary" { return tok_binary; } -"slist" { return tok_slist; } -"senum" { return tok_senum; } -"map" { return tok_map; } -"list" { return tok_list; } -"set" { return tok_set; } -"async" { return tok_async; } -"typedef" { return tok_typedef; } -"struct" { return tok_struct; } -"exception" { return tok_xception; } -"extends" { return tok_extends; } -"throws" { return tok_throws; } -"service" { return tok_service; } -"enum" { return tok_enum; } -"const" { return tok_const; } +"namespace" { return tok_namespace; } +"cpp_namespace" { return tok_cpp_namespace; } +"cpp_include" { return tok_cpp_include; } +"cpp_type" { return tok_cpp_type; } +"java_package" { return tok_java_package; } +"php_namespace" { return tok_php_namespace; } +"ruby_namespace" { return tok_ruby_namespace; } +"xsd_all" { return tok_xsd_all; } +"xsd_optional" { return tok_xsd_optional; } +"xsd_nillable" { return tok_xsd_nillable; } +"xsd_namespace" { return tok_xsd_namespace; } +"xsd_attrs" { return tok_xsd_attrs; } +"include" { return tok_include; } + +"void" { return tok_void; } +"bool" { return tok_bool; } +"byte" { return tok_byte; } +"i16" { return tok_i16; } +"i32" { return tok_i32; } +"i64" { return tok_i64; } +"double" { return tok_double; } +"string" { return tok_string; } +"binary" { return tok_binary; } +"slist" { return tok_slist; } +"senum" { return tok_senum; } +"map" { return tok_map; } +"list" { return tok_list; } +"set" { return tok_set; } +"async" { return tok_async; } +"typedef" { return tok_typedef; } +"struct" { return tok_struct; } +"exception" { return tok_xception; } +"extends" { return tok_extends; } +"throws" { return tok_throws; } +"service" { return tok_service; } +"enum" { return tok_enum; } +"const" { return tok_const; } "abstract" { thrift_reserved_keyword(yytext); } "and" { thrift_reserved_keyword(yytext); } diff --git a/compiler/cpp/src/thrifty.yy b/compiler/cpp/src/thrifty.yy index 23b9fa06..fe64e853 100644 --- a/compiler/cpp/src/thrifty.yy +++ b/compiler/cpp/src/thrifty.yy @@ -79,6 +79,7 @@ int y_field_val = -1; %token tok_xsd_nillable %token tok_xsd_namespace %token tok_xsd_attrs +%token tok_ruby_namespace /** * Base datatype keywords @@ -234,6 +235,13 @@ Header: g_program->set_php_namespace($2); } } +| tok_ruby_namespace tok_identifier + { + pdebug("Header -> tok_ruby_namespace tok_identifier"); + if (g_parse_mode == PROGRAM) { + g_program->set_ruby_namespace($2); + } + } | tok_java_package tok_identifier { pdebug("Header -> tok_java_package tok_identifier"); diff --git a/lib/rb/lib/thrift/protocol/tprotocol.rb b/lib/rb/lib/thrift/protocol/tprotocol.rb index da079d1c..fb43705b 100644 --- a/lib/rb/lib/thrift/protocol/tprotocol.rb +++ b/lib/rb/lib/thrift/protocol/tprotocol.rb @@ -114,6 +114,56 @@ class TProtocol def readString(); nil; end + def write_field(name, type, fid, value) + writeFieldBegin(name, type, fid) + write_type(type, value) + writeFieldEnd + end + + def write_type(type, value) + case type + when TType::BOOL + writeBool(value) + when TType::BYTE + writeByte(value) + when TType::DOUBLE + writeDouble(value) + when TType::I16 + writeI16(value) + when TType::I32 + writeI32(value) + when TType::I64 + writeI64(value) + when TType::STRING + writeString(value) + when TType::STRUCT + value.write(self) + else + raise NotImplementedError + end + end + + def read_type(type) + case type + when TType::BOOL + readBool + when TType::BYTE + readByte + when TType::DOUBLE + readDouble + when TType::I16 + readI16 + when TType::I32 + readI32 + when TType::I64 + readI64 + when TType::STRING + readString + else + raise NotImplementedError + end + end + def skip(type) if type === TType::STOP nil diff --git a/lib/rb/lib/thrift/thrift.rb b/lib/rb/lib/thrift/thrift.rb index 2b9ebbb1..d8e77a15 100644 --- a/lib/rb/lib/thrift/thrift.rb +++ b/lib/rb/lib/thrift/thrift.rb @@ -32,7 +32,40 @@ class TMessageType end module TProcessor - def process(iprot, oprot); nil; end + def initialize(handler) + @handler = handler + end + + def process(iprot, oprot) + name, type, seqid = iprot.readMessageBegin() + if respond_to?("process_#{name}") + send("process_#{name}", seqid, iprot, oprot) + return true + else + iprot.skip(TType::STRUCT) + iprot.readMessageEnd() + x = TApplicationException.new(TApplicationException::UNKNOWN_METHOD, 'Unknown function '+name) + oprot.writeMessageBegin(name, TMessageType::EXCEPTION, seqid) + x.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + return + end + end + + def read_args(iprot, args_class) + args = args_class.new + args.read(iprot) + iprot.readMessageEnd + args + end + + def write_result(result, oprot, name, seqid) + oprot.writeMessageBegin(name, TMessageType::REPLY, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + end end class TException < StandardError @@ -104,3 +137,176 @@ class TApplicationException < TException end end + +module ThriftClient + def initialize(iprot, oprot=nil) + @iprot = iprot + @oprot = oprot || iprot + @seqid = 0 + end + + def send_message(name, args_class, args = {}) + @oprot.writeMessageBegin(name, TMessageType::CALL, @seqid) + data = args_class.new + args.each do |k, v| + data.send("#{k.to_s}=", v) + end + data.write(@oprot) + @oprot.writeMessageEnd() + @oprot.trans.flush() + end + + def receive_message(result_klass) + fname, mtype, rseqid = @iprot.readMessageBegin() + handle_exception(mtype) + result = result_klass.new + result.read(@iprot) + @iprot.readMessageEnd() + return result + end + + def handle_exception(mtype) + if mtype == TMessageType::EXCEPTION + x = TApplicationException.new() + x.read(@iprot) + @iprot.readMessageEnd() + raise x + end + end +end + +module ThriftStruct + def initialize(d={}) + each_field do |fid, type, name| + instance_variable_set("@#{name}", d[name.to_s]) + end + end + + def fields + self.class.const_get(:FIELDS) + end + + def each_field + fields.each do |fid, data| + yield fid, data[:type], data[:name] + end + end + + def read(iprot) + iprot.readStructBegin() + loop do + fname, ftype, fid = iprot.readFieldBegin() + break if (ftype === TType::STOP) + handle_message(iprot, fid, ftype) + iprot.readFieldEnd() + end + iprot.readStructEnd() + end + + def write(oprot) + oprot.writeStructBegin(self.class.name) + each_field do |fid, type, name| + if (value = instance_variable_get("@#{name}")) + if is_container? type + oprot.writeFieldBegin(name, type, fid) + write_container(oprot, value, fields[fid]) + oprot.writeFieldEnd + else + oprot.write_field(name, type, fid, value) + end + end + end + oprot.writeFieldStop() + oprot.writeStructEnd() + end + + protected + + def handle_message(iprot, fid, ftype) + field = fields[fid] + if field && field[:type] == ftype + value = read_field(iprot, field) + instance_variable_set("@#{field[:name]}", value) + else + iprot.skip(ftype) + end + end + + def read_field(iprot, field = {}) + if field[:type] == TType::STRUCT + value = field[:class].new + value.read(iprot) + elsif field[:type] == TType::MAP + key_type, val_type, size = iprot.readMapBegin + value = {} + size.times do + k = read_field(iprot, field_info(field[:key])) + v = read_field(iprot, field_info(field[:value])) + value[k] = v + end + iprot.readMapEnd + elsif field[:type] == TType::LIST + e_type, size = iprot.readListBegin + value = Array.new(size) do |n| + read_field(iprot, field_info(field[:element])) + end + iprot.readListEnd + elsif field[:type] == TType::SET + e_type, size = iprot.readSetBegin + value = {} + size.times do + element = read_field(iprot, field_info(field[:element])) + value[element] = true + end + iprot.readSetEnd + else + value = iprot.read_type(field[:type]) + end + value + end + + def write_data(oprot, value, field) + if is_container? field[:type] + write_container(oprot, value, field) + else + oprot.write_type(field[:type], value) + end + end + + def write_container(oprot, value, field = {}) + if field[:type] == TType::MAP + oprot.writeMapBegin(field[:key][:type], field[:value][:type], value.size) + value.each do |k, v| + write_data(oprot, k, field[:key]) + write_data(oprot, v, field[:value]) + end + oprot.writeMapEnd + elsif field[:type] == TType::LIST + oprot.writeListBegin(field[:element][:type], value.size) + value.each do |elem| + write_data(oprot, elem, field[:element]) + end + oprot.writeListEnd + elsif field[:type] == TType::SET + oprot.writeSetBegin(field[:element][:type], value.size) + value.each do |k, v| + write_data(oprot, k, field[:element]) + end + oprot.writeSetEnd + else + raise "Not a container type: #{field[:type]}" + end + end + + def is_container?(type) + [TType::LIST, TType::MAP, TType::SET].include? type + end + + def field_info(field) + { :type => field[:type], + :class => field[:class], + :key => field[:key], + :value => field[:value], + :element => field[:element] } + end +end diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index d4b0a4dd..92a3d21f 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -1,5 +1,6 @@ java_package thrift.test cpp_namespace thrift.test +ruby_namespace Thrift.Test enum Numberz { diff --git a/test/rb/TestClient.rb b/test/rb/TestClient.rb index fedb818e..1adb5c74 100755 --- a/test/rb/TestClient.rb +++ b/test/rb/TestClient.rb @@ -9,7 +9,7 @@ require 'ThriftTest' s = TSocket.new('localhost', 9090) p = TBinaryProtocol.new(s) -c = ThriftTest::Client.new(p) +c = Thrift::Test::ThriftTest::Client.new(p) s.open() @@ -25,10 +25,12 @@ puts c.testDouble(-3.14) puts c.testMap({1 => 1, 2 => 2, 3 => 3}) puts c.testList([1,2,3,4,5]) puts c.testSet({1 => true, 2 => true, 3 => true}) - -struct = Xtruct.new({'string_thing' => 'hi!', 'i32_thing' => 4 }) +struct = Thrift::Test::Xtruct.new({'string_thing' => 'hi!', 'i32_thing' => 4 }) puts c.testStruct(struct) -puts c.testNest(Xtruct2.new({'struct_thing' => struct, 'i32_thing' => 10})) +puts c.testNest(Thrift::Test::Xtruct2.new({'struct_thing' => struct, 'i32_thing' => 10})) +insane = Thrift::Test::Insanity.new({'userMap' => { Thrift::Test::Numberz::ONE => 44 }, 'xtructs' => [struct, Thrift::Test::Xtruct.new({'string_thing' => 'hi again', 'i32_thing' => 12})]}) +puts c.testInsanity(insane) +puts c.testMapMap(4).inspect s.close() diff --git a/test/rb/TestServer.rb b/test/rb/TestServer.rb index c0d7b59f..892037ec 100755 --- a/test/rb/TestServer.rb +++ b/test/rb/TestServer.rb @@ -9,8 +9,6 @@ require 'thrift/server/tserver' require 'ThriftTest' class TestHandler - include ThriftTest::Iface - def testVoid() print "testVoid()\n" end @@ -42,6 +40,7 @@ class TestHandler def testStruct(thing) print "testStruct(#{thing})\n" + print " with attrs: #{thing.string_thing}, #{thing.byte_thing}, #{thing.i32_thing}" return thing end @@ -69,11 +68,33 @@ class TestHandler end return thing end + + def testNest(thing) + print "testNest(#{thing})\n" + puts " i32_thing: #{thing.i32_thing}" + puts " with struct: " + %w{ string_thing byte_thing i32_thing }.each do |t| + puts " #{t}: #{thing.struct_thing.send(t)}" + end + return thing + end + + def testInsanity(thing) + puts "insanity:" + puts " #{thing.inspect}" + num, uid = thing.userMap.find { true } + return {uid => {num => thing}} + end + + def testMapMap(thing) + puts "got: #{thing}" + return {thing => {thing => thing}} + end end handler = TestHandler.new() -processor = ThriftTest::Processor.new(handler) +processor = Thrift::Test::ThriftTest::Processor.new(handler) transport = TServerSocket.new(9090) server = TSimpleServer.new(processor, transport) server.serve()