Repository: thrift Updated Branches: refs/heads/master 2d3eff5d3 -> 673c44bc1
Fix for misc. codegen issues with THRIFT-2905 Client: Cocoa Patch: Kevin Wooten <[email protected]> This closes #652 Project: http://git-wip-us.apache.org/repos/asf/thrift/repo Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/673c44bc Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/673c44bc Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/673c44bc Branch: refs/heads/master Commit: 673c44bc11250cf5ed891d7855e915de5ed70ed8 Parents: 2d3eff5 Author: Jens Geyer <[email protected]> Authored: Tue Oct 13 23:58:50 2015 +0200 Committer: Jens Geyer <[email protected]> Committed: Tue Oct 13 23:58:50 2015 +0200 ---------------------------------------------------------------------- compiler/cpp/src/generate/t_cocoa_generator.cc | 310 ++++++++++++-------- 1 file changed, 180 insertions(+), 130 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/thrift/blob/673c44bc/compiler/cpp/src/generate/t_cocoa_generator.cc ---------------------------------------------------------------------- diff --git a/compiler/cpp/src/generate/t_cocoa_generator.cc b/compiler/cpp/src/generate/t_cocoa_generator.cc index f8924bb..5319ba9 100644 --- a/compiler/cpp/src/generate/t_cocoa_generator.cc +++ b/compiler/cpp/src/generate/t_cocoa_generator.cc @@ -29,6 +29,7 @@ #include "platform.h" using std::map; +using std::ostream; using std::ofstream; using std::ostringstream; using std::string; @@ -91,16 +92,15 @@ public: void generate_xception(t_struct* txception); void generate_service(t_service* tservice); - void print_const_value(std::ofstream& out, - std::string name, + void print_const_value(ostream& out, + string name, t_type* type, t_const_value* value, - bool defval = false, - bool is_property = false); - std::string render_const_value(ofstream& out, + bool defval = false); + std::string render_const_value(ostream& out, t_type* type, t_const_value* value, - bool containerize_it = false); + bool box_it = false); void generate_cocoa_struct(t_struct* tstruct, bool is_exception); void generate_cocoa_struct_interface(std::ofstream& out, @@ -213,6 +213,7 @@ public: std::string cocoa_imports(); std::string cocoa_thrift_imports(); std::string type_name(t_type* ttype, bool class_ref = false, bool needs_mutable = false); + std::string element_type_name(t_type* ttype); std::string base_type_name(t_base_type* tbase); std::string declare_property(t_field* tfield); std::string declare_property_isset(t_field* tfield); @@ -226,9 +227,8 @@ public: std::string format_string_for_type(t_type* type); std::string format_cast_for_type(t_type* type); std::string call_field_setter(t_field* tfield, std::string fieldName); - std::string containerize(t_type* ttype, std::string fieldName); - std::string decontainerize(t_field* tfield, std::string fieldName); - std::string decontainerize(t_type* ttype, std::string fieldName); + std::string box(t_type *ttype, std::string field_name); + std::string unbox(t_type* ttype, std::string field_name); std::string getter_name(string field_name); std::string setter_name(string field_name); @@ -442,7 +442,7 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { // Public constants for base types & strings vector<t_const*>::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - t_type* type = (*c_iter)->get_type(); + t_type* type = (*c_iter)->get_type()->get_true_type(); if (!type->is_container() && !type->is_struct()) { const_interface << "FOUNDATION_EXPORT " << type_name(type) << " " << cocoa_prefix_ << capitalize((*c_iter)->get_name()) << ";" << endl; @@ -456,6 +456,8 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { string constants_class_name = cocoa_prefix_ + capitalize(program_name_) + "Constants"; if (needs_class) { + + const_interface << endl; const_interface << "@interface " << constants_class_name << " : NSObject "; scope_up(const_interface); @@ -464,14 +466,14 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { // getter method for each constant defined. for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type(); + t_type* type = (*c_iter)->get_type()->get_true_type(); if (type->is_container() || type->is_struct()) { t_type* type = (*c_iter)->get_type(); - const_interface << "+ (" << type_name(type) << ") " << name << ";" << endl; + const_interface << endl << "+ (" << type_name(type) << ") " << name << ";" << endl; } } - const_interface << "@end"; + const_interface << endl << "@end"; } // this gets spit into the header file in ::close_generator @@ -485,7 +487,8 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { string name = (*c_iter)->get_name(); t_type* type = (*c_iter)->get_type(); f_impl_ << type_name(type) << " " << cocoa_prefix_ << name; - if (!type->is_container() && !type->is_struct()) { + t_type* ttype = type->get_true_type(); + if (!ttype->is_container() && !ttype->is_struct()) { f_impl_ << " = " << render_const_value(f_impl_, type, (*c_iter)->get_value()); } f_impl_ << ";" << endl; @@ -493,19 +496,21 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { f_impl_ << endl; if (needs_class) { - f_impl_ << "@implementation " << constants_class_name << endl; + + f_impl_ << "@implementation " << constants_class_name << endl << endl; // initialize complex constants when the class is loaded f_impl_ << "+ (void) initialize "; scope_up(f_impl_); for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - if ((*c_iter)->get_type()->is_container() || (*c_iter)->get_type()->is_struct()) { + t_type* ttype = (*c_iter)->get_type()->get_true_type(); + if (ttype->is_container() || ttype->is_struct()) { + f_impl_ << endl; print_const_value(f_impl_, cocoa_prefix_ + (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), - false, false); f_impl_ << ";" << endl; } @@ -515,9 +520,9 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { // getter method for each constant for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { string name = (*c_iter)->get_name(); - t_type* type = (*c_iter)->get_type(); + t_type* type = (*c_iter)->get_type()->get_true_type(); if (type->is_container() || type->is_struct()) { - f_impl_ << "+ (" << type_name(type) << ") " << name; + f_impl_ << endl << "+ (" << type_name(type) << ") " << name << " "; scope_up(f_impl_); indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl; scope_down(f_impl_); @@ -932,8 +937,7 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, "self." + (*m_iter)->get_name(), t, (*m_iter)->get_value(), - false, - true); + false); } } } @@ -2107,7 +2111,7 @@ void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& o out << ": __thriftError];" << endl; out << indent() << "if (!serviceResult) return NO;" << endl; if (!(*f_iter)->get_returntype()->is_void()) { - out << indent() << "[result setSuccess: " << decontainerize((*f_iter)->get_returntype(), "serviceResult") << "];" << endl; + out << indent() << "[result setSuccess: " << unbox((*f_iter)->get_returntype(), "serviceResult") << "];" << endl; } // write out the result if not oneway @@ -2270,35 +2274,63 @@ void t_cocoa_generator::generate_deserialize_container(ofstream& out, * suitable for putting into a container, if necessary. Basically, * wrap scaler primitives in NSNumber objects. */ -string t_cocoa_generator::containerize(t_type* ttype, string fieldName) { - // FIXME - optimize here to avoid autorelease pool? +string t_cocoa_generator::box(t_type* ttype, string field_name) { + ttype = get_true_type(ttype); if (ttype->is_enum()) { - return "[NSNumber numberWithInt: " + fieldName + "]"; + return "@(" + field_name + ")"; } else if (ttype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); switch (tbase) { - case t_base_type::TYPE_VOID: - throw "can't containerize void"; - case t_base_type::TYPE_BOOL: - return "[NSNumber numberWithBool: " + fieldName + "]"; - case t_base_type::TYPE_BYTE: - return "[NSNumber numberWithChar: " + fieldName + "]"; - case t_base_type::TYPE_I16: - return "[NSNumber numberWithShort: " + fieldName + "]"; - case t_base_type::TYPE_I32: - return "[NSNumber numberWithLong: " + fieldName + "]"; - case t_base_type::TYPE_I64: - return "[NSNumber numberWithLongLong: " + fieldName + "]"; - case t_base_type::TYPE_DOUBLE: - return "[NSNumber numberWithDouble: " + fieldName + "]"; - default: - break; + case t_base_type::TYPE_VOID: + throw "can't box void"; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + return "@(" + field_name + ")"; + default: + break; } } // do nothing - return fieldName; + return field_name; +} + +/** + * Extracts the actual value from a boxed value + */ +string t_cocoa_generator::unbox(t_type* ttype, string field_name) { + ttype = get_true_type(ttype); + if (ttype->is_enum()) { + return "[" + field_name + " intValue]"; + } else if (ttype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "can't unbox void"; + case t_base_type::TYPE_BOOL: + return "[" + field_name + " boolValue]"; + case t_base_type::TYPE_BYTE: + return "((SInt8)[" + field_name + " charValue])"; + case t_base_type::TYPE_I16: + return "((SInt16)[" + field_name + " shortValue])"; + case t_base_type::TYPE_I32: + return "((SInt32)[" + field_name + " longValue])"; + case t_base_type::TYPE_I64: + return "((SInt64)[" + field_name + " longLongValue])"; + case t_base_type::TYPE_DOUBLE: + return "[" + field_name + " doubleValue]"; + default: + break; + } + } + + // do nothing + return field_name; } /** @@ -2317,8 +2349,8 @@ void t_cocoa_generator::generate_deserialize_map_element(ofstream& out, generate_deserialize_field(out, &fkey, key); generate_deserialize_field(out, &fval, val); - indent(out) << "[" << fieldName << " setObject: " << containerize(valType, val) - << " forKey: " << containerize(keyType, key) << "];" << endl; + indent(out) << "[" << fieldName << " setObject: " << box(valType, val) + << " forKey: " << box(keyType, key) << "];" << endl; } /** @@ -2333,7 +2365,7 @@ void t_cocoa_generator::generate_deserialize_set_element(ofstream& out, generate_deserialize_field(out, &felem, elem); - indent(out) << "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl; + indent(out) << "[" << fieldName << " addObject: " << box(type, elem) << "];" << endl; } /** @@ -2348,7 +2380,7 @@ void t_cocoa_generator::generate_deserialize_list_element(ofstream& out, generate_deserialize_field(out, &felem, elem); - indent(out) << "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl; + indent(out) << "[" << fieldName << " addObject: " << box(type, elem) << "];" << endl; } /** @@ -2501,44 +2533,6 @@ void t_cocoa_generator::generate_serialize_container(ofstream& out, } /** - * Given a field variable name, wrap it in code that converts it to a - * primitive type, if necessary. - */ -string t_cocoa_generator::decontainerize(t_field* tfield, string fieldName) { - return decontainerize(tfield->get_type(), fieldName); -} - -string t_cocoa_generator::decontainerize(t_type* ttype, string fieldName) { - ttype = get_true_type(ttype); - if (ttype->is_enum()) { - return "[" + fieldName + " intValue]"; - } else if (ttype->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); - switch (tbase) { - case t_base_type::TYPE_VOID: - throw "can't decontainerize void"; - case t_base_type::TYPE_BOOL: - return "[" + fieldName + " boolValue]"; - case t_base_type::TYPE_BYTE: - return "((SInt8)[" + fieldName + " charValue])"; - case t_base_type::TYPE_I16: - return "((SInt16)[" + fieldName + " shortValue])"; - case t_base_type::TYPE_I32: - return "((SInt32)[" + fieldName + " longValue])"; - case t_base_type::TYPE_I64: - return "((SInt64)[" + fieldName + " longLongValue])"; - case t_base_type::TYPE_DOUBLE: - return "[" + fieldName + " doubleValue]"; - default: - break; - } - } - - // do nothing - return fieldName; -} - -/** * Serializes the members of a map. */ void t_cocoa_generator::generate_serialize_map_element(ofstream& out, @@ -2546,9 +2540,9 @@ void t_cocoa_generator::generate_serialize_map_element(ofstream& out, string key, string mapName) { t_field kfield(tmap->get_key_type(), key); - generate_serialize_field(out, &kfield, decontainerize(&kfield, key)); + generate_serialize_field(out, &kfield, unbox(kfield.get_type(), key)); t_field vfield(tmap->get_val_type(), "[" + mapName + " objectForKey: " + key + "]"); - generate_serialize_field(out, &vfield, decontainerize(&vfield, vfield.get_name())); + generate_serialize_field(out, &vfield, unbox(vfield.get_type(), vfield.get_name())); } /** @@ -2558,7 +2552,7 @@ void t_cocoa_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string elementName) { t_field efield(tset->get_elem_type(), elementName); - generate_serialize_field(out, &efield, decontainerize(&efield, elementName)); + generate_serialize_field(out, &efield, unbox(efield.get_type(), elementName)); } /** @@ -2569,7 +2563,7 @@ void t_cocoa_generator::generate_serialize_list_element(ofstream& out, string index, string listName) { t_field efield(tlist->get_elem_type(), "[" + listName + " objectAtIndex: " + index + "]"); - generate_serialize_field(out, &efield, decontainerize(&efield, efield.get_name())); + generate_serialize_field(out, &efield, unbox(efield.get_type(), efield.get_name())); } /** @@ -2594,15 +2588,15 @@ string t_cocoa_generator::type_name(t_type* ttype, bool class_ref, bool needs_mu } else if (ttype->is_map()) { t_map *map = (t_map *)ttype; result = needs_mutable ? "NSMutableDictionary" : "NSDictionary"; - result += "<" + type_name(map->get_key_type()) + ", " + type_name(map->get_val_type()) + ">"; + result += "<" + element_type_name(map->get_key_type()) + ", " + element_type_name(map->get_val_type()) + ">"; } else if (ttype->is_set()) { t_set *set = (t_set *)ttype; result = needs_mutable ? "NSMutableSet" : "NSSet"; - result += "<" + type_name(set->get_elem_type()) + ">"; + result += "<" + element_type_name(set->get_elem_type()) + ">"; } else if (ttype->is_list()) { t_list *list = (t_list *)ttype; result = needs_mutable ? "NSMutableArray" : "NSArray"; - result += "<" + type_name(list->get_elem_type()) + ">"; + result += "<" + element_type_name(list->get_elem_type()) + ">"; } else { // Check for prefix t_program* program = ttype->get_program(); @@ -2620,6 +2614,51 @@ string t_cocoa_generator::type_name(t_type* ttype, bool class_ref, bool needs_mu } /** + * Returns an Objective-C type name for container types + * + * @param ttype the type + */ +string t_cocoa_generator::element_type_name(t_type* etype) { + + t_type* ttype = etype->get_true_type(); + + if (etype->is_typedef() && type_can_be_null(ttype)) { + return type_name(etype); + } + + string result; + if (ttype->is_base_type()) { + t_base_type* tbase = (t_base_type*)ttype; + switch (tbase->get_base()) { + case t_base_type::TYPE_STRING: + if (tbase->is_binary()) { + result = "NSData *"; + } + else { + result = "NSString *"; + } + break; + default: + result = "NSNumber *"; + break; + } + } else if (ttype->is_map()) { + t_map *map = (t_map *)ttype; + result = "NSDictionary<" + element_type_name(map->get_key_type()) + ", " + element_type_name(map->get_val_type()) + ">"; + } else if (ttype->is_set()) { + t_set *set = (t_set *)ttype; + result = "NSSet<" + element_type_name(set->get_elem_type()) + ">"; + } else if (ttype->is_list()) { + t_list *list = (t_list *)ttype; + result = "NSArray<" + element_type_name(list->get_elem_type()) + ">"; + } else if (ttype->is_struct() || ttype->is_xception()) { + result = cocoa_prefix_ + ttype->get_name() + " *"; + } + + return result; +} + +/** * Returns the Objective-C type that corresponds to the thrift type. * * @param tbase The base type @@ -2658,36 +2697,34 @@ string t_cocoa_generator::base_type_name(t_base_type* type) { * is NOT performed in this function as it is always run beforehand using the * validate_types method in main.cc */ -void t_cocoa_generator::print_const_value(std::ofstream& out, - std::string name, +void t_cocoa_generator::print_const_value(ostream& out, + string name, t_type* type, t_const_value* value, - bool defval, - bool is_property) { + bool defval) { type = get_true_type(type); - indent(out); if (type->is_base_type()) { string v2 = render_const_value(out, type, value); + indent(out); if (defval) out << type_name(type) << " "; out << name << " = " << v2 << ";" << endl << endl; } else if (type->is_enum()) { + indent(out); if (defval) out << type_name(type) << " "; out << name << " = " << render_const_value(out, type, value) << ";" << endl << endl; } else if (type->is_struct() || type->is_xception()) { + indent(out); const vector<t_field*>& fields = ((t_struct*)type)->get_members(); vector<t_field*>::const_iterator f_iter; const map<t_const_value*, t_const_value*>& val = value->get_map(); map<t_const_value*, t_const_value*>::const_iterator v_iter; if (defval) out << type_name(type) << " "; - if (defval || is_property) - out << name << " = [" << type_name(type, true) << " new];" - << endl; - else - out << name << " = [" << type_name(type, true) << " new];" << endl; + out << name << " = [" << type_name(type, true) << " new];" + << endl; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { t_type* field_type = NULL; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { @@ -2702,55 +2739,68 @@ void t_cocoa_generator::print_const_value(std::ofstream& out, std::string cap_name = capitalize(v_iter->first->get_string()); indent(out) << "[" << name << " set" << cap_name << ":" << val << "];" << endl; } - out << endl; } else if (type->is_map()) { + ostringstream mapout; + indent(mapout); t_type* ktype = ((t_map*)type)->get_key_type(); t_type* vtype = ((t_map*)type)->get_val_type(); const map<t_const_value*, t_const_value*>& val = value->get_map(); map<t_const_value*, t_const_value*>::const_iterator v_iter; if (defval) - out << "NSMutableDictionary *" - << name << " = [[NSMutableDictionary alloc] initWithCapacity:" << val.size() << "]; " - << endl; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - string key = render_const_value(out, ktype, v_iter->first, true); - string val = render_const_value(out, vtype, v_iter->second, true); - indent(out) << "[" << name << " setObject:" << val << " forKey:" << key << "];" << endl; + mapout << type_name(type) << " "; + mapout << name << " = @{"; + for (v_iter = val.begin(); v_iter != val.end();) { + mapout << render_const_value(out, ktype, v_iter->first, true) << ": " + << render_const_value(out, vtype, v_iter->second, true); + if (++v_iter != val.end()) { + mapout << ", "; + } } - out << endl; + mapout << "}"; + out << mapout.str(); } else if (type->is_list()) { + ostringstream listout; + indent(listout); t_type* etype = ((t_list*)type)->get_elem_type(); const vector<t_const_value*>& val = value->get_list(); vector<t_const_value*>::const_iterator v_iter; if (defval) - out << "NSMutableArray *" - << name << " = [[NSMutableArray alloc] initWithCapacity:" << val.size() << "];" << endl; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - string val = render_const_value(out, etype, *v_iter, true); - indent(out) << "[" << name << " addObject:" << val << "];" << endl; + listout << type_name(type) << " "; + listout << name << " = @["; + for (v_iter = val.begin(); v_iter != val.end();) { + listout << render_const_value(out, etype, *v_iter, true); + if (++v_iter != val.end()) { + listout << ", "; + } } - out << endl; + listout << "]"; + out << listout.str(); } else if (type->is_set()) { + ostringstream setout; + indent(setout); t_type* etype = ((t_set*)type)->get_elem_type(); const vector<t_const_value*>& val = value->get_list(); vector<t_const_value*>::const_iterator v_iter; if (defval) - out << "NSMutableSet *" - << name << " = [[NSMutableSet alloc] initWithCapacity:" << val.size() << "];" << endl; - for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - string val = render_const_value(out, etype, *v_iter, true); - indent(out) << "[" << name << " addObject:" << val << "];" << endl; + setout << type_name(type) << " "; + setout << name << " = NSSet setWithArray:@["; + for (v_iter = val.begin(); v_iter != val.end();) { + setout << render_const_value(out, etype, *v_iter, true); + if (++v_iter != val.end()) { + setout << ", "; + } } - out << endl; + setout << "]]"; + out << setout.str(); } else { throw "compiler error: no const of type " + type->get_name(); } } -string t_cocoa_generator::render_const_value(ofstream& out, +string t_cocoa_generator::render_const_value(ostream& out, t_type* type, t_const_value* value, - bool containerize_it) { + bool box_it) { type = get_true_type(type); std::ostringstream render; @@ -2787,12 +2837,12 @@ string t_cocoa_generator::render_const_value(ofstream& out, render << value->get_integer(); } else { string t = tmp("tmp"); - print_const_value(out, t, type, value, true, false); + print_const_value(out, t, type, value, true); render << t; } - if (containerize_it) { - return containerize(type, render.str()); + if (box_it) { + return box(type, render.str()); } return render.str(); } @@ -2805,7 +2855,7 @@ ORIGINAL string t_cocoa_generator::render_const_value(string name, t_type* type, t_const_value* value, - bool containerize_it) { + bool box_it) { type = get_true_type(type); std::ostringstream render; @@ -2929,8 +2979,8 @@ string t_cocoa_generator::render_const_value(string name, throw "don't know how to render constant for type: " + type->get_name(); } - if (containerize_it) { - return containerize(type, render.str()); + if (box_it) { + return box(type, render.str()); } return render.str();
