http://git-wip-us.apache.org/repos/asf/thrift/blob/56e5b9b0/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 a90c937..f8924bb 100644 --- a/compiler/cpp/src/generate/t_cocoa_generator.cc +++ b/compiler/cpp/src/generate/t_cocoa_generator.cc @@ -59,7 +59,16 @@ public: iter = parsed_options.find("async_clients"); async_clients_ = (iter != parsed_options.end()); - + + iter = parsed_options.find("promise_kit"); + promise_kit_ = (iter != parsed_options.end()); + + iter = parsed_options.find("debug_descriptions"); + debug_descriptions_ = (iter != parsed_options.end()); + + iter = parsed_options.find("pods"); + pods_ = (iter != parsed_options.end()); + out_dir_base_ = "gen-cocoa"; } @@ -108,11 +117,13 @@ public: void generate_cocoa_struct_encode_with_coder_method(ofstream& out, t_struct* tstruct, bool is_exception); + void generate_cocoa_struct_copy_method(ofstream& out, + t_struct* tstruct, + bool is_exception); void generate_cocoa_struct_hash_method(ofstream& out, t_struct* tstruct); - void generate_cocoa_struct_is_equal_method(ofstream& out, t_struct* tstruct); - void generate_cocoa_struct_field_accessor_declarations(std::ofstream& out, - t_struct* tstruct, - bool is_exception); + void generate_cocoa_struct_is_equal_method(ofstream& out, + t_struct* tstruct, + bool is_exception); void generate_cocoa_struct_field_accessor_implementations(std::ofstream& out, t_struct* tstruct, bool is_exception); @@ -122,9 +133,9 @@ public: void generate_cocoa_struct_validator(std::ofstream& out, t_struct* tstruct); void generate_cocoa_struct_description(std::ofstream& out, t_struct* tstruct); - std::string function_result_helper_struct_type(t_function* tfunction); - std::string function_args_helper_struct_type(t_function* tfunction); - void generate_function_helpers(t_function* tfunction); + std::string function_result_helper_struct_type(t_service *tservice, t_function* tfunction); + std::string function_args_helper_struct_type(t_service* tservice, t_function* tfunction); + void generate_function_helpers(t_service *tservice, t_function* tfunction); /** * Service-level generation functions @@ -137,10 +148,17 @@ public: void generate_cocoa_service_client_async_interface(std::ofstream& out, t_service* tservice); void generate_cocoa_service_client_send_function_implementation(ofstream& out, - t_function* tfunction); + t_service* tservice, + t_function* tfunction, + bool needs_protocol); void generate_cocoa_service_client_send_function_invocation(ofstream& out, t_function* tfunction); + void generate_cocoa_service_client_send_async_function_invocation(ofstream& out, + t_function* tfunction, + string failureBlockName); void generate_cocoa_service_client_recv_function_implementation(ofstream& out, - t_function* tfunction); + t_service* tservice, + t_function* tfunction, + bool needs_protocol); void generate_cocoa_service_client_implementation(std::ofstream& out, t_service* tservice); void generate_cocoa_service_client_async_implementation(std::ofstream& out, t_service* tservice); @@ -194,18 +212,25 @@ public: std::string cocoa_prefix(); std::string cocoa_imports(); std::string cocoa_thrift_imports(); - std::string type_name(t_type* ttype, bool class_ref = false); + std::string type_name(t_type* ttype, bool class_ref = false, bool needs_mutable = false); std::string base_type_name(t_base_type* tbase); - std::string declare_field(t_field* tfield); std::string declare_property(t_field* tfield); - std::string function_signature(t_function* tfunction); - std::string async_function_signature(t_function* tfunction); - std::string argument_list(t_struct* tstruct); + std::string declare_property_isset(t_field* tfield); + std::string declare_property_unset(t_field* tfield); + std::string invalid_return_statement(t_function* tfunction); + std::string function_signature(t_function* tfunction, bool include_error); + std::string async_function_signature(t_function* tfunction, bool include_error); + std::string promise_function_signature(t_function* tfunction); + std::string argument_list(t_struct* tstruct, string protocol_name, bool include_error); std::string type_to_enum(t_type* ttype); 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 getter_name(string field_name); + std::string setter_name(string field_name); bool type_can_be_null(t_type* ttype) { ttype = get_true_type(ttype); @@ -217,6 +242,7 @@ public: private: std::string cocoa_prefix_; std::string constants_declarations_; + int error_constant_; /** * File streams @@ -228,6 +254,9 @@ private: bool log_unexpected_; bool validate_required_; bool async_clients_; + bool promise_kit_; + bool debug_descriptions_; + bool pods_; }; /** @@ -240,7 +269,7 @@ void t_cocoa_generator::init_generator() { cocoa_prefix_ = program_->get_namespace("cocoa"); // we have a .h header file... - string f_header_name = program_name_ + ".h"; + string f_header_name = cocoa_prefix_ + capitalize(program_name_) + ".h"; string f_header_fullname = get_out_dir() + f_header_name; f_header_.open(f_header_fullname.c_str()); @@ -249,13 +278,16 @@ void t_cocoa_generator::init_generator() { f_header_ << cocoa_imports() << cocoa_thrift_imports(); // ...and a .m implementation file - string f_impl_name = get_out_dir() + program_name_ + ".m"; - f_impl_.open(f_impl_name.c_str()); + string f_impl_name = cocoa_prefix_ + capitalize(program_name_) + ".m"; + string f_impl_fullname = get_out_dir() + f_impl_name; + f_impl_.open(f_impl_fullname.c_str()); f_impl_ << autogen_comment() << endl; f_impl_ << cocoa_imports() << cocoa_thrift_imports() << "#import \"" << f_header_name << "\"" << endl << endl; + + error_constant_ = 60000; } /** @@ -273,25 +305,52 @@ string t_cocoa_generator::cocoa_imports() { * @return List of imports necessary for thrift runtime */ string t_cocoa_generator::cocoa_thrift_imports() { - string result = string() + "#import \"TProtocol.h\"\n" + "#import \"TApplicationException.h\"\n" - + "#import \"TProtocolException.h\"\n" - + "#import \"TProtocolUtil.h\"\n" - + "#import \"TProcessor.h\"\n" - + "#import \"TObjective-C.h\"\n" - + "#import \"TBase.h\"\n" - + "#import \"TAsyncTransport.h\"\n" - + "#import \"TProtocolFactory.h\"\n" - + "#import \"TBaseClient.h\"\n" - + "\n"; + + vector<string> includes_list; + includes_list.push_back("TProtocol.h"); + includes_list.push_back("TProtocolFactory.h"); + includes_list.push_back("TApplicationError.h"); + includes_list.push_back("TProtocolError.h"); + includes_list.push_back("TProtocolUtil.h"); + includes_list.push_back("TProcessor.h"); + includes_list.push_back("TBase.h"); + includes_list.push_back("TAsyncTransport.h"); + includes_list.push_back("TBaseClient.h"); + + std::ostringstream includes; + + vector<string>::const_iterator i_iter; + for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) { + includes << "#import "; + if (pods_) { + includes << "<Thrift/" << *i_iter << ">"; + } else { + includes << "\"" << *i_iter << "\""; + } + includes << endl; + } + + includes << endl; + + if (promise_kit_) { + includes << "#import "; + if (pods_) { + includes << "<PromiseKit/PromiseKit.h>"; + } else { + includes << "\"PromiseKit.h\""; + } + includes << endl; + } // Include other Thrift includes - const vector<t_program*>& includes = program_->get_includes(); - for (size_t i = 0; i < includes.size(); ++i) { - result += "#import \"" + includes[i]->get_name() + ".h\"" + "\n"; + const vector<t_program*>& other_includes = program_->get_includes(); + for (size_t i = 0; i < other_includes.size(); ++i) { + includes << "#import \"" << other_includes[i]->get_name() << ".h\"" << endl; } - result += "\n"; + + includes << endl; - return result; + return includes.str(); } /** @@ -309,20 +368,46 @@ void t_cocoa_generator::close_generator() { * @param ttypedef The type definition */ void t_cocoa_generator::generate_typedef(t_typedef* ttypedef) { + if (ttypedef->get_type()->is_map()) { + t_map *map = (t_map *)ttypedef->get_type(); + if (map->get_key_type()->is_struct()) { + f_header_ << indent() << "@class " << type_name(map->get_key_type(), true) << ";" << endl; + } + if (map->get_val_type()->is_struct()) { + f_header_ << indent() << "@class " << type_name(map->get_val_type(), true) << ";" << endl; + } + } + else if (ttypedef->get_type()->is_set()) { + t_set *set = (t_set *)ttypedef->get_type(); + if (set->get_elem_type()->is_struct()) { + f_header_ << indent() << "@class " << type_name(set->get_elem_type(), true) << ";" << endl; + } + } + else if (ttypedef->get_type()->is_list()) { + t_list *list = (t_list *)ttypedef->get_type(); + if (list->get_elem_type()->is_struct()) { + f_header_ << indent() << "@class " << type_name(list->get_elem_type(), true) << ";" << endl; + } + } f_header_ << indent() << "typedef " << type_name(ttypedef->get_type()) << " " << cocoa_prefix_ << ttypedef->get_symbolic() << ";" << endl << endl; + if (ttypedef->get_type()->is_container()) { + f_header_ << indent() << "typedef " << type_name(ttypedef->get_type(), false, true) << " " << cocoa_prefix_ + << "Mutable" << ttypedef->get_symbolic() << ";" << endl << endl; + } } /** * Generates code for an enumerated type. In Objective-C, this is - * essentially the same as the thrift definition itself, using the - * enum keyword in Objective-C. For namespace purposes, the name of - * the enum plus an underscore is prefixed onto each element. + * essentially the same as the thrift definition itself, instead using + * NS_ENUM keyword in Objective-C. For namespace purposes, the name of + * the enum is prefixed to each element in keeping with Cocoa & Swift + * standards. * * @param tenum The enumeration */ void t_cocoa_generator::generate_enum(t_enum* tenum) { - f_header_ << indent() << "enum " << cocoa_prefix_ << tenum->get_name() << " {" << endl; + f_header_ << indent() << "typedef NS_ENUM(SInt32, " << cocoa_prefix_ << tenum->get_name() << ") {" << endl; indent_up(); vector<t_enum_value*> constants = tenum->get_constants(); @@ -334,7 +419,7 @@ void t_cocoa_generator::generate_enum(t_enum* tenum) { } else { f_header_ << "," << endl; } - f_header_ << indent() << tenum->get_name() << "_" << (*c_iter)->get_name(); + f_header_ << indent() << cocoa_prefix_ << tenum->get_name() << (*c_iter)->get_name(); f_header_ << " = " << (*c_iter)->get_value(); } @@ -343,36 +428,63 @@ void t_cocoa_generator::generate_enum(t_enum* tenum) { } /** - * Generates a class that holds all the constants. Primitive values - * could have been placed outside this class, but I just put - * everything in for consistency. + * Generates a class that holds all the constants. */ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { std::ostringstream const_interface; - string constants_class_name = cocoa_prefix_ + program_name_ + "Constants"; - const_interface << "@interface " << constants_class_name << " : NSObject "; - scope_up(const_interface); - scope_down(const_interface); + const_interface << "FOUNDATION_EXPORT NSString *" << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain;" << endl + << endl; + + + bool needs_class = false; - // getter method for each constant defined. + // Public constants for base types & strings vector<t_const*>::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { - string name = (*c_iter)->get_name(); t_type* type = (*c_iter)->get_type(); - const_interface << "+ (" << type_name(type) << ") " << name << ";" << endl; + if (!type->is_container() && !type->is_struct()) { + const_interface << "FOUNDATION_EXPORT " << type_name(type) << " " + << cocoa_prefix_ << capitalize((*c_iter)->get_name()) << ";" << endl; + } + else { + needs_class = true; + } } + + + string constants_class_name = cocoa_prefix_ + capitalize(program_name_) + "Constants"; + + if (needs_class) { + + const_interface << "@interface " << constants_class_name << " : NSObject "; + scope_up(const_interface); + scope_down(const_interface); - const_interface << "@end"; + // 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(); + if (type->is_container() || type->is_struct()) { + t_type* type = (*c_iter)->get_type(); + const_interface << "+ (" << type_name(type) << ") " << name << ";" << endl; + } + } + + const_interface << "@end"; + } // this gets spit into the header file in ::close_generator constants_declarations_ = const_interface.str(); + + f_impl_ << "NSString *" << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain = " + << "@\"" << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain\";" << endl << endl; - // static variables in the .m hold all constant values + // variables in the .m hold all simple constant values for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { string name = (*c_iter)->get_name(); t_type* type = (*c_iter)->get_type(); - f_impl_ << "static " << type_name(type) << " " << cocoa_prefix_ << name; + f_impl_ << type_name(type) << " " << cocoa_prefix_ << name; if (!type->is_container() && !type->is_struct()) { f_impl_ << " = " << render_const_value(f_impl_, type, (*c_iter)->get_value()); } @@ -380,36 +492,40 @@ void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) { } f_impl_ << endl; - f_impl_ << "@implementation " << constants_class_name << endl; + if (needs_class) { + f_impl_ << "@implementation " << constants_class_name << endl; - // initialize complex constants when the class is loaded - f_impl_ << "+ (void) initialize "; - scope_up(f_impl_); + // 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()) { - print_const_value(f_impl_, - cocoa_prefix_ + (*c_iter)->get_name(), - (*c_iter)->get_type(), - (*c_iter)->get_value(), - false, - false); - f_impl_ << ";" << endl; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + if ((*c_iter)->get_type()->is_container() || (*c_iter)->get_type()->is_struct()) { + print_const_value(f_impl_, + cocoa_prefix_ + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + false, + false); + f_impl_ << ";" << endl; + } } - } - scope_down(f_impl_); - - // 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(); - f_impl_ << "+ (" << type_name(type) << ") " << name; - scope_up(f_impl_); - indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl; scope_down(f_impl_); - } - f_impl_ << "@end" << endl << endl; + // 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(); + if (type->is_container() || type->is_struct()) { + f_impl_ << "+ (" << type_name(type) << ") " << name; + scope_up(f_impl_); + indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl; + scope_down(f_impl_); + } + } + + f_impl_ << "@end" << endl << endl; + } } /** @@ -441,51 +557,38 @@ void t_cocoa_generator::generate_xception(t_struct* txception) { void t_cocoa_generator::generate_cocoa_struct_interface(ofstream& out, t_struct* tstruct, bool is_exception) { + + if (is_exception) { + out << "enum {" << endl + << " " << cocoa_prefix_ << capitalize(program_name_) << "Error" << tstruct->get_name() << " = -" << error_constant_++ << endl + << "};" << endl + << endl; + } + out << "@interface " << cocoa_prefix_ << tstruct->get_name() << " : "; if (is_exception) { - out << "NSException "; + out << "NSError "; } else { out << "NSObject "; } - out << "<TBase, NSCoding> "; - - scope_up(out); - - // members are protected. this is redundant, but explicit. - // f_header_ << endl << "@protected:" << endl; - - const vector<t_field*>& members = tstruct->get_members(); - - // member varialbes - vector<t_field*>::const_iterator m_iter; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - out << indent() << declare_field(*m_iter) << endl; - } - - if (members.size() > 0) { - out << endl; - // isset fields - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - indent(out) << "BOOL __" << (*m_iter)->get_name() << "_isset;" << endl; - } - } - - scope_down(out); + out << "<TBase, NSCoding, NSCopying> " << endl; + out << endl; // properties + const vector<t_field*>& members = tstruct->get_members(); if (members.size() > 0) { - out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" - << endl; + vector<t_field*>::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { out << indent() << declare_property(*m_iter) << endl; + out << indent() << declare_property_isset(*m_iter) << endl; + out << indent() << declare_property_unset(*m_iter) << endl; + out << endl; } - out << "#endif" << endl << endl; } - - // default initializer - out << indent() << "- (id) init;" << endl; + + out << endl; // initializer for all fields if (!members.empty()) { @@ -494,17 +597,6 @@ void t_cocoa_generator::generate_cocoa_struct_interface(ofstream& out, } out << endl; - // read and write - out << "- (void) read: (id <TProtocol>) inProtocol;" << endl; - out << "- (void) write: (id <TProtocol>) outProtocol;" << endl; - out << endl; - - // validator - out << "- (void) validate;" << endl << endl; - - // getters and setters - generate_cocoa_struct_field_accessor_declarations(out, tstruct, is_exception); - out << "@end" << endl << endl; } @@ -516,7 +608,7 @@ void t_cocoa_generator::generate_cocoa_struct_initializer_signature(ofstream& ou t_struct* tstruct) { const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; - indent(out) << "- (id) initWith"; + indent(out) << "- (instancetype) initWith"; for (m_iter = members.begin(); m_iter != members.end();) { if (m_iter == members.begin()) { out << capitalize((*m_iter)->get_name()); @@ -532,41 +624,25 @@ void t_cocoa_generator::generate_cocoa_struct_initializer_signature(ofstream& ou } /** - * Generate getter and setter declarations for all fields, plus an - * IsSet getter. - */ -void t_cocoa_generator::generate_cocoa_struct_field_accessor_declarations(ofstream& out, - t_struct* tstruct, - bool is_exception) { - (void)is_exception; - const vector<t_field*>& members = tstruct->get_members(); - vector<t_field*>::const_iterator m_iter; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - out << indent() << "#if !__has_feature(objc_arc)" << endl; - out << indent() << "- (" << type_name((*m_iter)->get_type()) << ") " - << decapitalize((*m_iter)->get_name()) << ";" << endl; - out << indent() << "- (void) set" << capitalize((*m_iter)->get_name()) << ": (" - << type_name((*m_iter)->get_type()) << ") " << (*m_iter)->get_name() << ";" << endl; - out << indent() << "#endif" << endl; - out << indent() << "- (BOOL) " << (*m_iter)->get_name() << "IsSet;" << endl << endl; - } -} - -/** * Generate the initWithCoder method for this struct so it's compatible with * the NSCoding protocol */ void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream& out, t_struct* tstruct, bool is_exception) { - indent(out) << "- (id) initWithCoder: (NSCoder *) decoder" << endl; + + indent(out) << "- (instancetype) initWithCoder: (NSCoder *) decoder" << endl; scope_up(out); + if (is_exception) { // NSExceptions conform to NSCoding, so we can call super - out << indent() << "self = [super initWithCoder: decoder];" << endl; + indent(out) << "self = [super initWithCoder: decoder];" << endl; } else { - out << indent() << "self = [super init];" << endl; + indent(out) << "self = [super init];" << endl; } + + indent(out) << "if (self) "; + scope_up(out); const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; @@ -576,9 +652,9 @@ void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream& o out << indent() << "if ([decoder containsValueForKey: @\"" << (*m_iter)->get_name() << "\"])" << endl; scope_up(out); - out << indent() << "__" << (*m_iter)->get_name() << " = "; + out << indent() << "_" << (*m_iter)->get_name() << " = "; if (type_can_be_null(t)) { - out << "[[decoder decodeObjectForKey: @\"" << (*m_iter)->get_name() << "\"] retain_stub];" + out << "[decoder decodeObjectForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; } else if (t->is_enum()) { out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; @@ -608,9 +684,11 @@ void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream& o + t_base_type::t_base_name(tbase); } } - out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl; + out << indent() << "_" << (*m_iter)->get_name() << "IsSet = YES;" << endl; scope_down(out); } + + scope_down(out); out << indent() << "return self;" << endl; scope_down(out); @@ -624,8 +702,10 @@ void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream& o void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ofstream& out, t_struct* tstruct, bool is_exception) { + indent(out) << "- (void) encodeWithCoder: (NSCoder *) encoder" << endl; scope_up(out); + if (is_exception) { // NSExceptions conform to NSCoding, so we can call super out << indent() << "[super encodeWithCoder: encoder];" << endl; @@ -636,40 +716,39 @@ void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ofstream& for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "if (__" << (*m_iter)->get_name() << "_isset)" << endl; + out << indent() << "if (_" << (*m_iter)->get_name() << "IsSet)" << endl; scope_up(out); - // out << indent() << "__" << (*m_iter)->get_name() << " = "; if (type_can_be_null(t)) { - out << indent() << "[encoder encodeObject: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeObject: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; } else if (t->is_enum()) { - out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeInt: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; } else { t_base_type::t_base tbase = ((t_base_type*)t)->get_base(); switch (tbase) { case t_base_type::TYPE_BOOL: - out << indent() << "[encoder encodeBool: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeBool: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_BYTE: - out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeInt: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I16: - out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeInt: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I32: - out << indent() << "[encoder encodeInt32: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeInt32: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I64: - out << indent() << "[encoder encodeInt64: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeInt64: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_DOUBLE: - out << indent() << "[encoder encodeDouble: __" << (*m_iter)->get_name() << " forKey: @\"" + out << indent() << "[encoder encodeDouble: _" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; default: @@ -685,6 +764,38 @@ void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ofstream& } /** + * Generate the copy method for this struct + */ +void t_cocoa_generator::generate_cocoa_struct_copy_method(ofstream& out, t_struct* tstruct, bool is_exception) { + out << indent() << "- (instancetype) copyWithZone:(NSZone *)zone" << endl; + scope_up(out); + + if (is_exception) { + out << indent() << type_name(tstruct) << " val = [" << cocoa_prefix_ << tstruct->get_name() << " errorWithDomain: self.domain code: self.code userInfo: self.userInfo];" << endl; + } else { + out << indent() << type_name(tstruct) << " val = [" << cocoa_prefix_ << tstruct->get_name() << " new];" << endl; + } + + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + if (type_can_be_null(t)) { + out << indent() << "val." << (*m_iter)->get_name() << " = [self." << (*m_iter)->get_name() << " copy];"; + } else { + out << indent() << "val." << (*m_iter)->get_name() << " = self." << (*m_iter)->get_name() << ";"; + } + out << endl; + } + + out << indent() << "return val;" << endl; + + scope_down(out); + out << endl; +} + +/** * Generate the hash method for this struct */ void t_cocoa_generator::generate_cocoa_struct_hash_method(ofstream& out, t_struct* tstruct) { @@ -697,14 +808,14 @@ void t_cocoa_generator::generate_cocoa_struct_hash_method(ofstream& out, t_struc for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "hash = (hash * 31) ^ __" << (*m_iter)->get_name() - << "_isset ? 2654435761 : 0;" << endl; - out << indent() << "if (__" << (*m_iter)->get_name() << "_isset)" << endl; + out << indent() << "hash = (hash * 31) ^ _" << (*m_iter)->get_name() + << "IsSet ? 2654435761 : 0;" << endl; + out << indent() << "if (_" << (*m_iter)->get_name() << "IsSet)" << endl; scope_up(out); if (type_can_be_null(t)) { - out << indent() << "hash = (hash * 31) ^ [__" << (*m_iter)->get_name() << " hash];" << endl; + out << indent() << "hash = (hash * 31) ^ [_" << (*m_iter)->get_name() << " hash];" << endl; } else { - out << indent() << "hash = (hash * 31) ^ [@(__" << (*m_iter)->get_name() << ") hash];" + out << indent() << "hash = (hash * 31) ^ [@(_" << (*m_iter)->get_name() << ") hash];" << endl; } scope_down(out); @@ -718,7 +829,7 @@ void t_cocoa_generator::generate_cocoa_struct_hash_method(ofstream& out, t_struc /** * Generate the isEqual method for this struct */ -void t_cocoa_generator::generate_cocoa_struct_is_equal_method(ofstream& out, t_struct* tstruct) { +void t_cocoa_generator::generate_cocoa_struct_is_equal_method(ofstream& out, t_struct* tstruct, bool is_exception) { indent(out) << "- (BOOL) isEqual: (id) anObject" << endl; scope_up(out); @@ -730,36 +841,47 @@ void t_cocoa_generator::generate_cocoa_struct_is_equal_method(ofstream& out, t_s string class_name = cocoa_prefix_ + tstruct->get_name(); - indent(out) << "if (![anObject isKindOfClass:[" << class_name << " class]]) {" << endl; - indent_up(); - indent(out) << "return NO;" << endl; - indent_down(); - indent(out) << "}" << endl; - - indent(out) << class_name << " *other = (" << class_name << " *)anObject;" << endl; + if (is_exception) { + indent(out) << "if (![super isEqual:anObject]) {" << endl; + indent_up(); + indent(out) << "return NO;" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + else { + indent(out) << "if (![anObject isKindOfClass:[" << class_name << " class]]) {" << endl; + indent_up(); + indent(out) << "return NO;" << endl; + indent_down(); + indent(out) << "}" << endl; + } const vector<t_field*>& members = tstruct->get_members(); vector<t_field*>::const_iterator m_iter; - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - string name = (*m_iter)->get_name(); - if (type_can_be_null(t)) { - out << indent() << "if ((__" << name << "_isset != other->__" << name << "_isset) ||" << endl - << indent() << " " - << "(__" << name << "_isset && " - << "((__" << name << " || other->__" << name << ") && " - << "![__" << name << " isEqual:other->__" << name << "]))) {" << endl; - } else { - out << indent() << "if ((__" << name << "_isset != other->__" << name << "_isset) ||" << endl - << indent() << " " - << "(__" << name << "_isset && " - << "(__" << name << " != other->__" << name << "))) {" << endl; + if (!members.empty()) { + indent(out) << class_name << " *other = (" << class_name << " *)anObject;" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + string name = (*m_iter)->get_name(); + if (type_can_be_null(t)) { + out << indent() << "if ((_" << name << "IsSet != other->_" << name << "IsSet) ||" << endl + << indent() << " " + << "(_" << name << "IsSet && " + << "((_" << name << " || other->_" << name << ") && " + << "![_" << name << " isEqual:other->_" << name << "]))) {" << endl; + } else { + out << indent() << "if ((_" << name << "IsSet != other->_" << name << "IsSet) ||" << endl + << indent() << " " + << "(_" << name << "IsSet && " + << "(_" << name << " != other->_" << name << "))) {" << endl; + } + indent_up(); + indent(out) << "return NO;" << endl; + indent_down(); + indent(out) << "}" << endl; } - indent_up(); - indent(out) << "return NO;" << endl; - indent_down(); - indent(out) << "}" << endl; } out << indent() << "return YES;" << endl; @@ -785,10 +907,11 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, // exceptions need to call the designated initializer on NSException if (is_exception) { - out << indent() << "- (id) init" << endl; + out << indent() << "- (instancetype) init" << endl; scope_up(out); - out << indent() << "return [super initWithName: @\"" << cocoa_prefix_ << tstruct->get_name() - << "\" reason: @\"unknown\" userInfo: nil];" << endl; + out << indent() << "return [super initWithDomain: " << cocoa_prefix_ << capitalize(program_name_) << "ErrorDomain" << endl + << indent() << " code: " << cocoa_prefix_ << capitalize(program_name_) << "Error" << tstruct->get_name() << endl + << indent() << " userInfo: nil];" << endl; scope_down(out); out << endl; } else { @@ -796,12 +919,12 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, // default initializer // setup instance variables with default values - indent(out) << "- (id) init" << endl; + indent(out) << "- (instancetype) init" << endl; scope_up(out); indent(out) << "self = [super init];" << endl; + indent(out) << "if (self)"; + scope_up(out); if (members.size() > 0) { - out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" - << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); if ((*m_iter)->get_value() != NULL) { @@ -813,8 +936,8 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, true); } } - out << "#endif" << endl; } + scope_down(out); indent(out) << "return self;" << endl; scope_down(out); out << endl; @@ -830,17 +953,19 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, } else { out << indent() << "self = [super init];" << endl; } - + + indent(out) << "if (self)"; + scope_up(out); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - out << indent() << "__" << (*m_iter)->get_name() << " = "; - if (type_can_be_null(t)) { - out << "[" << (*m_iter)->get_name() << " retain_stub];" << endl; + out << indent() << "_" << (*m_iter)->get_name() << " = "; + if (get_true_type((*m_iter)->get_type())->is_container()) { + out << "[" << (*m_iter)->get_name() << " mutableCopy];" << endl; } else { out << (*m_iter)->get_name() << ";" << endl; } - out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl; + out << indent() << "_" << (*m_iter)->get_name() << "IsSet = YES;" << endl; } + scope_down(out); out << indent() << "return self;" << endl; scope_down(out); @@ -853,24 +978,9 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, generate_cocoa_struct_encode_with_coder_method(out, tstruct, is_exception); // hash and isEqual for NSObject generate_cocoa_struct_hash_method(out, tstruct); - generate_cocoa_struct_is_equal_method(out, tstruct); - - // dealloc - if (!members.empty()) { - out << "- (void) dealloc" << endl; - scope_up(out); - - for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - t_type* t = get_true_type((*m_iter)->get_type()); - if (type_can_be_null(t)) { - indent(out) << "[__" << (*m_iter)->get_name() << " release_stub];" << endl; - } - } - - out << indent() << "[super dealloc_stub];" << endl; - scope_down(out); - out << endl; - } + generate_cocoa_struct_is_equal_method(out, tstruct, is_exception); + // copy for NSObject + generate_cocoa_struct_copy_method(out, tstruct, is_exception); // the rest of the methods generate_cocoa_struct_field_accessor_implementations(out, tstruct, is_exception); @@ -892,7 +1002,7 @@ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream& out, * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* tstruct) { - out << "- (void) read: (id <TProtocol>) inProtocol" << endl; + out << "- (BOOL) read: (id <TProtocol>) inProtocol error: (NSError *__autoreleasing *)__thriftError" << endl; scope_up(out); const vector<t_field*>& fields = tstruct->get_members(); @@ -900,11 +1010,11 @@ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* ts // Declare stack tmp variables indent(out) << "NSString * fieldName;" << endl; - indent(out) << "int fieldType;" << endl; - indent(out) << "int fieldID;" << endl; + indent(out) << "SInt32 fieldType;" << endl; + indent(out) << "SInt32 fieldID;" << endl; out << endl; - indent(out) << "[inProtocol readStructBeginReturningName: NULL];" << endl; + indent(out) << "if (![inProtocol readStructBeginReturningName: NULL error: __thriftError]) return NO;" << endl; // Loop over reading in fields indent(out) << "while (true)" << endl; @@ -912,11 +1022,11 @@ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* ts // Read beginning field marker indent(out) - << "[inProtocol readFieldBeginReturningName: &fieldName type: &fieldType fieldID: &fieldID];" + << "if (![inProtocol readFieldBeginReturningName: &fieldName type: &fieldType fieldID: &fieldID error: __thriftError]) return NO;" << endl; // Check for field STOP marker and break - indent(out) << "if (fieldType == TType_STOP) { " << endl; + indent(out) << "if (fieldType == TTypeSTOP) { " << endl; indent_up(); indent(out) << "break;" << endl; indent_down(); @@ -936,23 +1046,16 @@ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* ts generate_deserialize_field(out, *f_iter, "fieldValue"); indent(out) << call_field_setter(*f_iter, "fieldValue") << endl; - // if this is an allocated field, release it since the struct - // is now retaining it - if (type_can_be_null((*f_iter)->get_type())) { - // deserialized strings are autorelease, so don't release them - if (!(get_true_type((*f_iter)->get_type())->is_string())) { - indent(out) << "[fieldValue release_stub];" << endl; - } - } indent_down(); out << indent() << "} else { " << endl; if (log_unexpected_) { out << indent() << " NSLog(@\"%s: field ID %i has unexpected type %i. Skipping.\", " - "__PRETTY_FUNCTION__, fieldID, fieldType);" << endl; + "__PRETTY_FUNCTION__, (int)fieldID, (int)fieldType);" << endl; } - out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl - << indent() << "}" << endl << indent() << "break;" << endl; + + out << indent() << " if (![TProtocolUtil skipType: fieldType onProtocol: inProtocol error: __thriftError]) return NO;" << endl; + out << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } @@ -960,25 +1063,29 @@ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* ts out << indent() << "default:" << endl; if (log_unexpected_) { out << indent() << " NSLog(@\"%s: unexpected field ID %i with type %i. Skipping.\", " - "__PRETTY_FUNCTION__, fieldID, fieldType);" << endl; + "__PRETTY_FUNCTION__, (int)fieldID, (int)fieldType);" << endl; } - out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl - << indent() << " break;" << endl; + + out << indent() << " if (![TProtocolUtil skipType: fieldType onProtocol: inProtocol error: __thriftError]) return NO;" << endl; + + out << indent() << " break;" << endl; scope_down(out); // Read field end marker - indent(out) << "[inProtocol readFieldEnd];" << endl; + indent(out) << "if (![inProtocol readFieldEnd: __thriftError]) return NO;" << endl; scope_down(out); - out << indent() << "[inProtocol readStructEnd];" << endl; + out << indent() << "if (![inProtocol readStructEnd: __thriftError]) return NO;" << endl; // performs various checks (e.g. check that all required fields are set) if (validate_required_) { - out << indent() << "[self validate];" << endl; + out << indent() << "if (![self validate: __thriftError]) return NO;" << endl; } + indent(out) << "return YES;" << endl; + indent_down(); out << indent() << "}" << endl << endl; } @@ -989,33 +1096,33 @@ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* ts * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_writer(ofstream& out, t_struct* tstruct) { - out << indent() << "- (void) write: (id <TProtocol>) outProtocol {" << endl; + out << indent() << "- (BOOL) write: (id <TProtocol>) outProtocol error: (NSError *__autoreleasing *)__thriftError {" << endl; indent_up(); string name = tstruct->get_name(); const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; - out << indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl; + out << indent() << "if (![outProtocol writeStructBeginWithName: @\"" << name << "\" error: __thriftError]) return NO;" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - out << indent() << "if (__" << (*f_iter)->get_name() << "_isset) {" << endl; + out << indent() << "if (_" << (*f_iter)->get_name() << "IsSet) {" << endl; indent_up(); bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { - out << indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl; + out << indent() << "if (_" << (*f_iter)->get_name() << " != nil) {" << endl; indent_up(); } - indent(out) << "[outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() + indent(out) << "if (![outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) - << " fieldID: " << (*f_iter)->get_key() << "];" << endl; + << " fieldID: " << (*f_iter)->get_key() << " error: __thriftError]) return NO;" << endl; // Write field contents - generate_serialize_field(out, *f_iter, "__" + (*f_iter)->get_name()); + generate_serialize_field(out, *f_iter, "_" + (*f_iter)->get_name()); // Write field closer - indent(out) << "[outProtocol writeFieldEnd];" << endl; + indent(out) << "if (![outProtocol writeFieldEnd: __thriftError]) return NO;" << endl; if (null_allowed) { scope_down(out); @@ -1023,9 +1130,11 @@ void t_cocoa_generator::generate_cocoa_struct_writer(ofstream& out, t_struct* ts scope_down(out); } // Write the struct map - out << indent() << "[outProtocol writeFieldStop];" << endl << indent() - << "[outProtocol writeStructEnd];" << endl; + out << indent() << "if (![outProtocol writeFieldStop: __thriftError]) return NO;" << endl + << indent() << "if (![outProtocol writeStructEnd: __thriftError]) return NO;" << endl; + indent(out) << "return YES;" << endl; + indent_down(); out << indent() << "}" << endl << endl; } @@ -1038,14 +1147,14 @@ void t_cocoa_generator::generate_cocoa_struct_writer(ofstream& out, t_struct* ts * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out, t_struct* tstruct) { - out << indent() << "- (void) write: (id <TProtocol>) outProtocol {" << endl; + out << indent() << "- (BOOL) write: (id <TProtocol>) outProtocol error: (NSError *__autoreleasing *)__thriftError {" << endl; indent_up(); string name = tstruct->get_name(); const vector<t_field*>& fields = tstruct->get_members(); vector<t_field*>::const_iterator f_iter; - out << indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl; + out << indent() << "if (![outProtocol writeStructBeginWithName: @\"" << name << "\" error: __thriftError]) return NO;" << endl; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { @@ -1056,24 +1165,24 @@ void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out, t_str out << " else if "; } - out << "(__" << (*f_iter)->get_name() << "_isset) {" << endl; + out << "(_" << (*f_iter)->get_name() << "IsSet) {" << endl; indent_up(); bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { - out << indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl; + out << indent() << "if (_" << (*f_iter)->get_name() << " != nil) {" << endl; indent_up(); } - indent(out) << "[outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() + indent(out) << "if (![outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) - << " fieldID: " << (*f_iter)->get_key() << "];" << endl; + << " fieldID: " << (*f_iter)->get_key() << " error: __thriftError]) return NO;" << endl; // Write field contents - generate_serialize_field(out, *f_iter, "__" + (*f_iter)->get_name()); + generate_serialize_field(out, *f_iter, "_" + (*f_iter)->get_name()); // Write field closer - indent(out) << "[outProtocol writeFieldEnd];" << endl; + indent(out) << "if (![outProtocol writeFieldEnd: __thriftError]) return NO;" << endl; if (null_allowed) { indent_down(); @@ -1084,9 +1193,12 @@ void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out, t_str indent(out) << "}"; } // Write the struct map - out << endl << indent() << "[outProtocol writeFieldStop];" << endl << indent() - << "[outProtocol writeStructEnd];" << endl; + out << endl << indent() << "if (![outProtocol writeFieldStop: __thriftError]) return NO;" + << endl << indent() << "if (![outProtocol writeStructEnd: __thriftError]) return NO;" + << endl; + indent(out) << "return YES;" << endl; + indent_down(); out << indent() << "}" << endl << endl; } @@ -1098,7 +1210,7 @@ void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out, t_str * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_validator(ofstream& out, t_struct* tstruct) { - out << indent() << "- (void) validate {" << endl; + out << indent() << "- (BOOL) validate: (NSError *__autoreleasing *)__thriftError {" << endl; indent_up(); const vector<t_field*>& fields = tstruct->get_members(); @@ -1108,13 +1220,19 @@ void t_cocoa_generator::generate_cocoa_struct_validator(ofstream& out, t_struct* for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_field* field = (*f_iter); if ((*f_iter)->get_req() == t_field::T_REQUIRED) { - out << indent() << "if (!__" << field->get_name() << "_isset) {" << endl << indent() - << " @throw [TProtocolException exceptionWithName: @\"TProtocolException\"" << endl - << indent() << " reason: @\"Required field '" - << (*f_iter)->get_name() << "' is not set.\"];" << endl << indent() << "}" << endl; + out << indent() << "if (!_" << field->get_name() << "IsSet) "; + scope_up(out); + indent(out) << "if (__thriftError) "; + scope_up(out); + out << indent() << "*__thriftError = [NSError errorWithDomain: TProtocolErrorDomain" << endl + << indent() << " code: TProtocolErrorUnknown" << endl + << indent() << " userInfo: @{TProtocolErrorExtendedErrorKey: @(TProtocolExtendedErrorMissingRequiredField)," << endl + << indent() << " TProtocolErrorFieldNameKey: @\"" << (*f_iter)->get_name() << "\"}];" << endl; + scope_down(out); + scope_down(out); } } - + indent(out) << "return YES;" << endl; indent_down(); out << indent() << "}" << endl << endl; } @@ -1138,37 +1256,12 @@ void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ofs std::string cap_name = field_name; cap_name[0] = toupper(cap_name[0]); - // Simple getter - indent(out) << "- (" << type_name(type) << ") "; - out << field_name << " {" << endl; - indent_up(); - if (!type_can_be_null(type)) { - indent(out) << "return __" << field_name << ";" << endl; - } else { - indent(out) << "return [[__" << field_name << " retain_stub] autorelease_stub];" << endl; - } - indent_down(); - indent(out) << "}" << endl << endl; - // Simple setter - indent(out) << "- (void) set" << cap_name << ": (" << type_name(type) << ") " << field_name + indent(out) << "- (void) set" << cap_name << ": (" << type_name(type, false, true) << ") " << field_name << " {" << endl; indent_up(); - if (!type_can_be_null(type)) { - indent(out) << "__" << field_name << " = " << field_name << ";" << endl; - } else { - indent(out) << "[" << field_name << " retain_stub];" << endl; - indent(out) << "[__" << field_name << " release_stub];" << endl; - indent(out) << "__" << field_name << " = " << field_name << ";" << endl; - } - indent(out) << "__" << field_name << "_isset = YES;" << endl; - indent_down(); - indent(out) << "}" << endl << endl; - - // IsSet - indent(out) << "- (BOOL) " << field_name << "IsSet {" << endl; - indent_up(); - indent(out) << "return __" << field_name << "_isset;" << endl; + indent(out) << "_" << field_name << " = " << field_name << ";" << endl; + indent(out) << "_" << field_name << "IsSet = YES;" << endl; indent_down(); indent(out) << "}" << endl << endl; @@ -1176,10 +1269,9 @@ void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ofs indent(out) << "- (void) unset" << cap_name << " {" << endl; indent_up(); if (type_can_be_null(type)) { - indent(out) << "[__" << field_name << " release_stub];" << endl; - indent(out) << "__" << field_name << " = nil;" << endl; + indent(out) << "_" << field_name << " = nil;" << endl; } - indent(out) << "__" << field_name << "_isset = NO;" << endl; + indent(out) << "_" << field_name << "IsSet = NO;" << endl; indent_down(); indent(out) << "}" << endl << endl; } @@ -1191,7 +1283,14 @@ void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ofs * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_description(ofstream& out, t_struct* tstruct) { - out << indent() << "- (NSString *) description {" << endl; + + // Allow use of debugDescription so the app can add description via a cateogory/extension + if (debug_descriptions_) { + out << indent() << "- (NSString *) debugDescription {" << endl; + } + else { + out << indent() << "- (NSString *) description {" << endl; + } indent_up(); out << indent() << "NSMutableString * ms = [NSMutableString stringWithString: @\"" @@ -1208,8 +1307,8 @@ void t_cocoa_generator::generate_cocoa_struct_description(ofstream& out, t_struc indent(out) << "[ms appendString: @\"," << (*f_iter)->get_name() << ":\"];" << endl; } t_type* ttype = (*f_iter)->get_type(); - indent(out) << "[ms appendFormat: @\"" << format_string_for_type(ttype) << "\", __" - << (*f_iter)->get_name() << "];" << endl; + indent(out) << "[ms appendFormat: @\"" << format_string_for_type(ttype) << "\", " + << format_cast_for_type(ttype) << "_" << (*f_iter)->get_name() << "];" << endl; } out << indent() << "[ms appendString: @\")\"];" << endl << indent() << "return [NSString stringWithString: ms];" << endl; @@ -1247,23 +1346,35 @@ void t_cocoa_generator::generate_cocoa_service_helpers(t_service* tservice) { vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); - generate_cocoa_struct_interface(f_impl_, ts, false); - generate_cocoa_struct_implementation(f_impl_, ts, false, false); - generate_function_helpers(*f_iter); + + string qname = function_args_helper_struct_type(tservice, *f_iter); + + t_struct qname_ts = t_struct(ts->get_program(), qname); + + const vector<t_field*>& members = ts->get_members(); + vector<t_field*>::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + qname_ts.append(*m_iter); + } + + generate_cocoa_struct_interface(f_impl_, &qname_ts, false); + generate_cocoa_struct_implementation(f_impl_, &qname_ts, false, false); + generate_function_helpers(tservice, *f_iter); } } -string t_cocoa_generator::function_result_helper_struct_type(t_function* tfunction) { +string t_cocoa_generator::function_result_helper_struct_type(t_service *tservice, t_function* tfunction) { if (tfunction->is_oneway()) { - return capitalize(tfunction->get_name()); + return tservice->get_name() + "_" + tfunction->get_name(); } else { - return capitalize(tfunction->get_name()) + "_result"; + return tservice->get_name() + "_" + tfunction->get_name() + "_result"; } } -string t_cocoa_generator::function_args_helper_struct_type(t_function* tfunction) { - return tfunction->get_name() + "_args"; +string t_cocoa_generator::function_args_helper_struct_type(t_service *tservice, t_function* tfunction) { + return tservice->get_name() + "_" + tfunction->get_name() + "_args"; } /** @@ -1271,14 +1382,14 @@ string t_cocoa_generator::function_args_helper_struct_type(t_function* tfunction * * @param tfunction The function */ -void t_cocoa_generator::generate_function_helpers(t_function* tfunction) { +void t_cocoa_generator::generate_function_helpers(t_service *tservice, t_function* tfunction) { if (tfunction->is_oneway()) { return; } // create a result struct with a success field of the return type, // and a field for each type of exception thrown - t_struct result(program_, function_result_helper_struct_type(tfunction)); + t_struct result(program_, function_result_helper_struct_type(tservice, tfunction)); t_field success(tfunction->get_returntype(), "success", 0); if (!tfunction->get_returntype()->is_void()) { result.append(&success); @@ -1307,7 +1418,7 @@ void t_cocoa_generator::generate_cocoa_service_protocol(ofstream& out, t_service vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - out << "- " << function_signature(*f_iter) << ";" + out << "- " << function_signature(*f_iter, true) << ";" << " // throws "; t_struct* xs = (*f_iter)->get_xceptions(); const std::vector<t_field*>& xceptions = xs->get_members(); @@ -1332,7 +1443,10 @@ void t_cocoa_generator::generate_cocoa_service_async_protocol(ofstream& out, t_s vector<t_function*> functions = tservice->get_functions(); vector<t_function*>::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - out << "- " << async_function_signature(*f_iter) << ";" << endl; + out << "- " << async_function_signature(*f_iter, false) << ";" << endl; + if (promise_kit_) { + out << "- " << promise_function_signature(*f_iter) << ";" << endl; + } } out << "@end" << endl << endl; } @@ -1345,7 +1459,7 @@ void t_cocoa_generator::generate_cocoa_service_async_protocol(ofstream& out, t_s void t_cocoa_generator::generate_cocoa_service_client_interface(ofstream& out, t_service* tservice) { out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Client : TBaseClient <" - << cocoa_prefix_ << tservice->get_name() << "> "; + << cocoa_prefix_ << tservice->get_name() << "> " << endl; out << "- (id) initWithProtocol: (id <TProtocol>) protocol;" << endl; out << "- (id) initWithInProtocol: (id <TProtocol>) inProtocol outProtocol: (id <TProtocol>) " @@ -1361,14 +1475,11 @@ void t_cocoa_generator::generate_cocoa_service_client_interface(ofstream& out, void t_cocoa_generator::generate_cocoa_service_client_async_interface(ofstream& out, t_service* tservice) { out << "@interface " << cocoa_prefix_ << tservice->get_name() << "ClientAsync : TBaseClient <" - << cocoa_prefix_ << tservice->get_name() << "Async> "; - - scope_up(out); - out << indent() << "id <TAsyncTransport> asyncTransport;" << endl; - scope_down(out); + << cocoa_prefix_ << tservice->get_name() << "Async> " << endl + << endl; - out << "- (id) initWithProtocolFactory: (id <TProtocolFactory>) factory " - "transport: (id <TAsyncTransport>) transport;" << endl; + out << "- (id) initWithProtocolFactory: (id <TProtocolFactory>) protocolFactory " + << "transportFactory: (id <TAsyncTransportFactory>) transportFactory;" << endl; out << "@end" << endl << endl; } @@ -1382,12 +1493,7 @@ void t_cocoa_generator::generate_cocoa_service_client_async_interface(ofstream& void t_cocoa_generator::generate_cocoa_service_server_interface(ofstream& out, t_service* tservice) { out << "@interface " << cocoa_prefix_ << tservice->get_name() - << "Processor : NSObject <TProcessor> "; - - scope_up(out); - out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() << "> mService;" << endl; - out << indent() << "NSDictionary * mMethodMap;" << endl; - scope_down(out); + << "Processor : NSObject <TProcessor> " << endl; out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service;" << endl; @@ -1398,25 +1504,28 @@ void t_cocoa_generator::generate_cocoa_service_server_interface(ofstream& out, void t_cocoa_generator::generate_cocoa_service_client_send_function_implementation( ofstream& out, - t_function* tfunction) { + t_service *tservice, + t_function* tfunction, + bool needs_protocol) { string funname = tfunction->get_name(); - t_function send_function(g_type_void, + t_function send_function(g_type_bool, string("send_") + tfunction->get_name(), tfunction->get_arglist()); - string argsname = tfunction->get_name() + "_args"; + string argsname = function_args_helper_struct_type(tservice, tfunction); // Open function - indent(out) << "- " << function_signature(&send_function) << endl; + indent(out) << "- (BOOL) send_" << tfunction->get_name() << argument_list(tfunction->get_arglist(), needs_protocol ? "outProtocol" : "", true) << endl; scope_up(out); - + // Serialize the request - out << indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" - << (tfunction->is_oneway() ? " type: TMessageType_ONEWAY" : " type: TMessageType_CALL") - << " sequenceID: 0];" << endl; + out << indent() << "if (![outProtocol writeMessageBeginWithName: @\"" << funname << "\"" + << (tfunction->is_oneway() ? " type: TMessageTypeONEWAY" : " type: TMessageTypeCALL") + << " sequenceID: 0 error: __thriftError]) return NO;" << endl; - out << indent() << "[outProtocol writeStructBeginWithName: @\"" << argsname << "\"];" << endl; + out << indent() << "if (![outProtocol writeStructBeginWithName: @\"" << argsname + << "\" error: __thriftError]) return NO;" << endl; // write out function parameters t_struct* arg_struct = tfunction->get_arglist(); @@ -1428,14 +1537,14 @@ void t_cocoa_generator::generate_cocoa_service_client_send_function_implementati out << indent() << "if (" << fieldName << " != nil)"; scope_up(out); } - out << indent() << "[outProtocol writeFieldBeginWithName: @\"" << fieldName + out << indent() << "if (![outProtocol writeFieldBeginWithName: @\"" << fieldName << "\"" " type: " << type_to_enum((*fld_iter)->get_type()) - << " fieldID: " << (*fld_iter)->get_key() << "];" << endl; + << " fieldID: " << (*fld_iter)->get_key() << " error: __thriftError]) return NO;" << endl; generate_serialize_field(out, *fld_iter, fieldName); - out << indent() << "[outProtocol writeFieldEnd];" << endl; + out << indent() << "if (![outProtocol writeFieldEnd: __thriftError]) return NO;" << endl; if (type_can_be_null((*fld_iter)->get_type())) { indent_down(); @@ -1443,63 +1552,91 @@ void t_cocoa_generator::generate_cocoa_service_client_send_function_implementati } } - out << indent() << "[outProtocol writeFieldStop];" << endl; - out << indent() << "[outProtocol writeStructEnd];" << endl; - out << indent() << "[outProtocol writeMessageEnd];" << endl; + out << indent() << "if (![outProtocol writeFieldStop: __thriftError]) return NO;" << endl; + out << indent() << "if (![outProtocol writeStructEnd: __thriftError]) return NO;" << endl; + out << indent() << "if (![outProtocol writeMessageEnd: __thriftError]) return NO;" << endl; + out << indent() << "return YES;" << endl; scope_down(out); out << endl; } void t_cocoa_generator::generate_cocoa_service_client_recv_function_implementation( ofstream& out, - t_function* tfunction) { - t_struct noargs(program_); - t_function recv_function(tfunction->get_returntype(), - string("recv_") + tfunction->get_name(), - &noargs, - tfunction->get_xceptions()); + t_service* tservice, + t_function* tfunction, + bool needs_protocol) { + + // Open function - indent(out) << "- " << function_signature(&recv_function) << endl; + indent(out) << "- (BOOL) recv_" << tfunction->get_name(); + if (!tfunction->get_returntype()->is_void()) { + out << ": (" << type_name(tfunction->get_returntype(), false, true) << " *) result "; + if (needs_protocol) { + out << "protocol"; + } else { + out << "error"; + } + } + if (needs_protocol) { + out << ": (id<TProtocol>) inProtocol error"; + } + out << ": (NSError *__autoreleasing *)__thriftError" << endl; scope_up(out); // TODO(mcslee): Message validation here, was the seqid etc ok? // check for an exception - out << indent() << "TApplicationException * x = [self checkIncomingMessageException];" << endl - << indent() << "if (x != nil)"; + out << indent() << "NSError *incomingException = [self checkIncomingMessageException: inProtocol];" << endl + << indent() << "if (incomingException)"; + scope_up(out); + out << indent() << "if (__thriftError)"; scope_up(out); - out << indent() << "@throw x;" << endl; + out << indent() << "*__thriftError = incomingException;" << endl; + scope_down(out); + out << indent() << "return NO;" << endl; scope_down(out); // FIXME - could optimize here to reduce creation of temporary objects. - string resultname = function_result_helper_struct_type(tfunction); - out << indent() << cocoa_prefix_ << resultname << " * result = [[[" << cocoa_prefix_ << resultname - << " alloc] init] autorelease_stub];" << endl; - indent(out) << "[result read: inProtocol];" << endl; - indent(out) << "[inProtocol readMessageEnd];" << endl; + string resultname = function_result_helper_struct_type(tservice, tfunction); + out << indent() << cocoa_prefix_ << resultname << " * resulter = [" << cocoa_prefix_ << resultname << " new];" << endl; + indent(out) << "if (![resulter read: inProtocol error: __thriftError]) return NO;" << endl; + indent(out) << "if (![inProtocol readMessageEnd: __thriftError]) return NO;" << endl; // Careful, only return _result if not a void function if (!tfunction->get_returntype()->is_void()) { - out << indent() << "if ([result successIsSet]) {" << endl << indent() - << " return [result success];" << endl << indent() << "}" << endl; + out << indent() << "if (resulter.successIsSet)"; + scope_up(out); + out << indent() << "*result = resulter.success;" << endl; + out << indent() << "return YES;" << endl; + scope_down(out); } t_struct* xs = tfunction->get_xceptions(); const std::vector<t_field*>& xceptions = xs->get_members(); vector<t_field*>::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - out << indent() << "if ([result " << (*x_iter)->get_name() << "IsSet]) {" << endl << indent() - << " @throw [result " << (*x_iter)->get_name() << "];" << endl << indent() << "}" << endl; + out << indent() << "if (resulter." << (*x_iter)->get_name() << "IsSet)"; + scope_up(out); + out << indent() << "if (__thriftError)"; + scope_up(out); + out << indent() << "*__thriftError = [resulter " << (*x_iter)->get_name() << "];" << endl; + scope_down(out); + out << indent() << "return NO;" << endl; + scope_down(out); } // If you get here it's an exception, unless a void function if (tfunction->get_returntype()->is_void()) { - indent(out) << "return;" << endl; + indent(out) << "return YES;" << endl; } else { - out << indent() << "@throw [TApplicationException exceptionWithType: " - "TApplicationException_MISSING_RESULT" << endl << indent() - << " reason: @\"" << tfunction->get_name() - << " failed: unknown result\"];" << endl; + out << indent() << "if (__thriftError)"; + scope_up(out); + out << indent() << "*__thriftError = [NSError errorWithDomain: TApplicationErrorDomain" << endl + << indent() << " code: TApplicationErrorMissingResult" << endl + << indent() << " userInfo: @{TApplicationErrorMethodKey: @\"" + << tfunction->get_name() << "\"}];" << endl; + scope_down(out); + out << indent() << "return NO;" << endl; } // Close function @@ -1513,12 +1650,13 @@ void t_cocoa_generator::generate_cocoa_service_client_recv_function_implementati * @param tfunction The service to generate an implementation for */ void t_cocoa_generator::generate_cocoa_service_client_send_function_invocation( - ofstream& out, - t_function* tfunction) { + ofstream& out, + t_function* tfunction) { + t_struct* arg_struct = tfunction->get_arglist(); const vector<t_field*>& fields = arg_struct->get_members(); vector<t_field*>::const_iterator fld_iter; - indent(out) << "[self send_" << tfunction->get_name(); + out << indent() << "if (![self send_" << tfunction->get_name(); bool first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { string fieldName = (*fld_iter)->get_name(); @@ -1530,7 +1668,45 @@ void t_cocoa_generator::generate_cocoa_service_client_send_function_invocation( out << fieldName << ": " << fieldName; } } - out << "];" << endl; + if (!fields.empty()) { + out << " error"; + } + out << ": __thriftError]) " << invalid_return_statement(tfunction) << endl; +} + +/** + * Generates an invocation of a given 'send_' function. + * + * @param tfunction The service to generate an implementation for + */ +void t_cocoa_generator::generate_cocoa_service_client_send_async_function_invocation( + ofstream& out, + t_function* tfunction, + string failureBlockName) { + + t_struct* arg_struct = tfunction->get_arglist(); + const vector<t_field*>& fields = arg_struct->get_members(); + vector<t_field*>::const_iterator fld_iter; + out << indent() << "if (![self send_" << tfunction->get_name(); + bool first = true; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + string fieldName = (*fld_iter)->get_name(); + out << " "; + if (first) { + first = false; + out << ": " << fieldName; + } else { + out << fieldName << ": " << fieldName; + } + } + if (!fields.empty()) { + out << " protocol"; + } + out << ": protocol error: &thriftError]) "; + scope_up(out); + out << indent() << failureBlockName << "(thriftError);" << endl + << indent() << "return;" << endl; + scope_down(out); } /** @@ -1540,7 +1716,20 @@ void t_cocoa_generator::generate_cocoa_service_client_send_function_invocation( */ void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& out, t_service* tservice) { - out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Client" << endl; + + string name = cocoa_prefix_ + tservice->get_name() + "Client"; + + out << "@interface " << name << " () "; + scope_up(out); + out << endl; + out << indent() << "id<TProtocol> inProtocol;" << endl; + out << indent() << "id<TProtocol> outProtocol;" << endl; + out << endl; + scope_down(out); + out << endl; + out << "@end" << endl << endl; + + out << "@implementation " << name << endl; // initializers out << "- (id) initWithProtocol: (id <TProtocol>) protocol" << endl; @@ -1553,8 +1742,11 @@ void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& o "anOutProtocol" << endl; scope_up(out); out << indent() << "self = [super init];" << endl; - out << indent() << "inProtocol = [anInProtocol retain_stub];" << endl; - out << indent() << "outProtocol = [anOutProtocol retain_stub];" << endl; + out << indent() << "if (self) "; + scope_up(out); + out << indent() << "inProtocol = anInProtocol;" << endl; + out << indent() << "outProtocol = anOutProtocol;" << endl; + scope_down(out); out << indent() << "return self;" << endl; scope_down(out); out << endl; @@ -1564,29 +1756,40 @@ void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& o vector<t_function*>::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_cocoa_service_client_send_function_implementation(out, *f_iter); + generate_cocoa_service_client_send_function_implementation(out, tservice, *f_iter, false); if (!(*f_iter)->is_oneway()) { - generate_cocoa_service_client_recv_function_implementation(out, *f_iter); + generate_cocoa_service_client_recv_function_implementation(out, tservice, *f_iter, false); } // Open function - indent(out) << "- " << function_signature(*f_iter) << endl; + indent(out) << "- " << function_signature(*f_iter, true) << endl; scope_up(out); generate_cocoa_service_client_send_function_invocation(out, *f_iter); - out << indent() << "[[outProtocol transport] flush];" << endl; + out << indent() << "if (![[outProtocol transport] flush: __thriftError]) " << invalid_return_statement(*f_iter) << endl; if (!(*f_iter)->is_oneway()) { - out << indent(); - if (!(*f_iter)->get_returntype()->is_void()) { - out << "return "; + if ((*f_iter)->get_returntype()->is_void()) { + out << indent() << "if (![self recv_" << (*f_iter)->get_name() << ": __thriftError]) return NO;" << endl; + out << indent() << "return YES;" << endl; + } else { + out << indent() << type_name((*f_iter)->get_returntype(), false, true) << " __result;" << endl + << indent() << "if (![self recv_" << (*f_iter)->get_name() << ": &__result error: __thriftError]) " + << invalid_return_statement(*f_iter) << endl; + if (type_can_be_null((*f_iter)->get_returntype())) { + out << indent() << "return __result;" << endl; + } else { + out << indent() << "return @(__result);" << endl; + } } - out << "[self recv_" << (*f_iter)->get_name() << "];" << endl; + } + else { + out << indent() << "return YES;" << endl; } scope_down(out); out << endl; } - indent_down(); + out << "@end" << endl << endl; } @@ -1597,16 +1800,30 @@ void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& o */ void t_cocoa_generator::generate_cocoa_service_client_async_implementation(ofstream& out, t_service* tservice) { - out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "ClientAsync" << endl - << endl << "- (id) initWithProtocolFactory: (id <TProtocolFactory>) factory " - "transport: (id <TAsyncTransport>) transport;" << endl; + + string name = cocoa_prefix_ + tservice->get_name() + "ClientAsync"; + + out << "@interface " << name << " () "; + scope_up(out); + out << endl; + out << indent() << "id<TProtocolFactory> protocolFactory;" << endl; + out << indent() << "id<TAsyncTransportFactory> transportFactory;" << endl; + out << endl; + scope_down(out); + out << endl; + out << "@end" << endl << endl; + + + out << "@implementation " << name << endl + << endl << "- (id) initWithProtocolFactory: (id <TProtocolFactory>) aProtocolFactory " + "transportFactory: (id <TAsyncTransportFactory>) aTransportFactory;" << endl; scope_up(out); out << indent() << "self = [super init];" << endl; - out << indent() << "inProtocol = [[factory newProtocolOnTransport:transport] retain_stub];" - << endl; - out << indent() << "outProtocol = inProtocol;" << endl; - out << indent() << "asyncTransport = transport;" << endl; + out << indent() << "if (self) {" << endl; + out << indent() << " protocolFactory = aProtocolFactory;" << endl; + out << indent() << " transportFactory = aTransportFactory;" << endl; + out << indent() << "}" << endl; out << indent() << "return self;" << endl; scope_down(out); out << endl; @@ -1616,57 +1833,120 @@ void t_cocoa_generator::generate_cocoa_service_client_async_implementation(ofstr vector<t_function*>::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { - generate_cocoa_service_client_send_function_implementation(out, *f_iter); + generate_cocoa_service_client_send_function_implementation(out, tservice, *f_iter, true); if (!(*f_iter)->is_oneway()) { - generate_cocoa_service_client_recv_function_implementation(out, *f_iter); + generate_cocoa_service_client_recv_function_implementation(out, tservice, *f_iter, true); } // Open function - indent(out) << "- " << async_function_signature(*f_iter) << endl; + indent(out) << "- " << async_function_signature(*f_iter, false) << endl; scope_up(out); - indent(out) << "@try {" << endl; - indent_up(); - generate_cocoa_service_client_send_function_invocation(out, *f_iter); - indent_down(); - out << indent() << "} @catch(TException * texception) {" << endl; - indent_up(); - out << indent() << "failureBlock(texception);" << endl << indent() << "return;" << endl; - indent_down(); - indent(out) << "}" << endl; - - out << indent() << "[asyncTransport flush:^{" << endl; - indent_up(); + + out << indent() << "NSError *thriftError;" << endl + << indent() << "id<TAsyncTransport> transport = [transportFactory newTransport];" << endl + << indent() << "id<TProtocol> protocol = [protocolFactory newProtocolOnTransport:transport];" << endl + << endl; + + generate_cocoa_service_client_send_async_function_invocation(out, *f_iter, "failureBlock"); - out << indent() << "@try {" << endl; + out << indent() << "[transport flushWithCompletion:^{" << endl; indent_up(); - string recv_invocation = "[self recv_" + (*f_iter)->get_name() + "]"; - if (!(*f_iter)->is_oneway() && (*f_iter)->get_returntype()->is_void()) { - out << indent() << recv_invocation << ";" << endl; + if (!(*f_iter)->is_oneway()) { + out << indent() << "NSError *thriftError;" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + out << indent() << type_name((*f_iter)->get_returntype()) << " result;" << endl; + } + out << indent() << "if (![self recv_" << (*f_iter)->get_name(); + if (!(*f_iter)->get_returntype()->is_void()) { + out << ": &result protocol"; + } + out << ": protocol error: &thriftError]) "; + scope_up(out); + out << indent() << "failureBlock(thriftError);" << endl + << indent() << "return;" << endl; + scope_down(out); } + out << indent() << "responseBlock("; if (!(*f_iter)->is_oneway() && !(*f_iter)->get_returntype()->is_void()) { - out << recv_invocation; + out << "result"; } out << ");" << endl; indent_down(); - - out << indent() << "} @catch(TException * texception) {" << endl; - indent_up(); - - out << indent() << "failureBlock(texception);" << endl; - - indent_down(); - out << indent() << "}" << endl; - - indent_down(); + out << indent() << "} failure:failureBlock];" << endl; - + scope_down(out); out << endl; + + // Promise function + if (promise_kit_) { + + indent(out) << "- " << promise_function_signature(*f_iter) << endl; + scope_up(out); + + out << indent() << "return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) {" << endl; + indent_up(); + + out << indent() << "NSError *thriftError;" << endl + << indent() << "id<TAsyncTransport> transport = [transportFactory newTransport];" << endl + << indent() << "id<TProtocol> protocol = [protocolFactory newProtocolOnTransport:transport];" << endl + << endl; + + generate_cocoa_service_client_send_async_function_invocation(out, *f_iter, "resolver"); + + out << indent() << "[transport flushWithCompletion:^{" << endl; + indent_up(); + + if (!(*f_iter)->is_oneway()) { + out << indent() << "NSError *thriftError;" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + out << indent() << type_name((*f_iter)->get_returntype()) << " result;" << endl; + } + out << indent() << "if (![self recv_" << (*f_iter)->get_name(); + if (!(*f_iter)->get_returntype()->is_void()) { + out << ": &result protocol"; + } + out << ": protocol error: &thriftError]) "; + scope_up(out); + out << indent() << "resolver(thriftError);" << endl + << indent() << "return;" << endl; + scope_down(out); + } + + out << indent() << "resolver("; + if ((*f_iter)->is_oneway() || (*f_iter)->get_returntype()->is_void()) { + out << "@YES"; + } else if (type_can_be_null((*f_iter)->get_returntype())) { + out << "result"; + } else { + out << "@(result)"; + } + out << ");" << endl; + + indent_down(); + + out << indent() << "} failure:^(NSError *error) {" << endl; + indent_up(); + out << indent() << "resolver(error);" << endl; + indent_down(); + out << indent() << "}];" << endl; + + indent_down(); + out << indent() << "}];" << endl; + + scope_down(out); + + out << endl; + + } + } out << "@end" << endl << endl; @@ -1680,19 +1960,29 @@ void t_cocoa_generator::generate_cocoa_service_client_async_implementation(ofstr */ void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& out, t_service* tservice) { - out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Processor" << endl; + + string name = cocoa_prefix_ + tservice->get_name() + "Processor"; + + out << "@interface " << name << " () "; + + scope_up(out); + out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() << "> service;" << endl; + out << indent() << "NSDictionary * methodMap;" << endl; + scope_down(out); + + out << "@end" << endl << endl; + + out << "@implementation " << name << endl; // initializer out << endl; - out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ - << tservice->get_name() << ">) service" << endl; + out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) aService" << endl; scope_up(out); out << indent() << "self = [super init];" << endl; - out << indent() << "if (!self) {" << endl; - out << indent() << " return nil;" << endl; - out << indent() << "}" << endl; - out << indent() << "mService = [service retain_stub];" << endl; - out << indent() << "mMethodMap = [[NSMutableDictionary dictionary] retain_stub];" << endl; + out << indent() << "if (self) "; + scope_up(out); + out << indent() << "service = aService;" << endl; + out << indent() << "methodMap = [NSMutableDictionary dictionary];" << endl; // generate method map for routing incoming calls vector<t_function*> functions = tservice->get_functions(); @@ -1700,16 +1990,15 @@ void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& o for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string funname = (*f_iter)->get_name(); scope_up(out); - out << indent() << "SEL s = @selector(process_" << funname - << "_withSequenceID:inProtocol:outProtocol:);" << endl; + out << indent() << "SEL s = @selector(process_" << funname << "_withSequenceID:inProtocol:outProtocol:error:);" << endl; out << indent() << "NSMethodSignature * sig = [self methodSignatureForSelector: s];" << endl; - out << indent() - << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl; + out << indent() << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl; out << indent() << "[invocation setSelector: s];" << endl; out << indent() << "[invocation retainArguments];" << endl; - out << indent() << "[mMethodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl; + out << indent() << "[methodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl; scope_down(out); } + scope_down(out); out << indent() << "return self;" << endl; scope_down(out); @@ -1718,33 +2007,36 @@ void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& o out << endl; out << indent() << "- (id<" << cocoa_prefix_ << tservice->get_name() << ">) service" << endl; out << indent() << "{" << endl; - out << indent() << " return [[mService retain_stub] autorelease_stub];" << endl; + out << indent() << " return service;" << endl; out << indent() << "}" << endl; // implementation of the TProcess method, which dispatches the incoming call using the method map out << endl; out << indent() << "- (BOOL) processOnInputProtocol: (id <TProtocol>) inProtocol" << endl; out << indent() << " outputProtocol: (id <TProtocol>) outProtocol" << endl; + out << indent() << " error: (NSError *__autoreleasing *)__thriftError" << endl; out << indent() << "{" << endl; out << indent() << " NSString * messageName;" << endl; - out << indent() << " int messageType;" << endl; - out << indent() << " int seqID;" << endl; - out << indent() << " [inProtocol readMessageBeginReturningName: &messageName" << endl; + out << indent() << " SInt32 messageType;" << endl; + out << indent() << " SInt32 seqID;" << endl; + out << indent() << " if (![inProtocol readMessageBeginReturningName: &messageName" << endl; out << indent() << " type: &messageType" << endl; - out << indent() << " sequenceID: &seqID];" << endl; - out << indent() << " NSInvocation * invocation = [mMethodMap valueForKey: messageName];" << endl; + out << indent() << " sequenceID: &seqID" << endl; + out << indent() << " error: __thriftError]) return NO;" << endl; + out << indent() << " NSInvocation * invocation = [methodMap valueForKey: messageName];" << endl; out << indent() << " if (invocation == nil) {" << endl; - out << indent() << " [TProtocolUtil skipType: TType_STRUCT onProtocol: inProtocol];" << endl; - out << indent() << " [inProtocol readMessageEnd];" << endl; - out << indent() << " TApplicationException * x = [TApplicationException exceptionWithType: " - "TApplicationException_UNKNOWN_METHOD reason: [NSString stringWithFormat: " - "@\"Invalid method name: '%@'\", messageName]];" << endl; - out << indent() << " [outProtocol writeMessageBeginWithName: messageName" << endl; - out << indent() << " type: TMessageType_EXCEPTION" << endl; - out << indent() << " sequenceID: seqID];" << endl; - out << indent() << " [x write: outProtocol];" << endl; - out << indent() << " [outProtocol writeMessageEnd];" << endl; - out << indent() << " [[outProtocol transport] flush];" << endl; + out << indent() << " if (![TProtocolUtil skipType: TTypeSTRUCT onProtocol: inProtocol error: __thriftError]) return NO;" << endl; + out << indent() << " if (![inProtocol readMessageEnd: __thriftError]) return NO;" << endl; + out << indent() << " NSError * x = [NSError errorWithDomain: TApplicationErrorDomain" << endl; + out << indent() << " code: TApplicationErrorUnknownMethod" << endl; + out << indent() << " userInfo: @{TApplicationErrorMethodKey: messageName}];" << endl; + out << indent() << " if (