@@ -568,6 +568,20 @@ class t_mstch_java_generator : public t_mstch_generator {
568568 cache[rpc_server_handler_id],
569569 " RpcServerHandler" ,
570570 package_dir / rpc_server_handler_filename);
571+
572+ auto metadata_handler_filename =
573+ service_name + " ThriftMetadataHandler.java" ;
574+ const auto & metadata_handler_id =
575+ id + service->name () + " ThriftMetadataHandler" ;
576+ if (!cache.count (metadata_handler_id)) {
577+ cache[metadata_handler_id] =
578+ service_factory.make_mstch_object (service, mstch_context_);
579+ }
580+
581+ render_to_file (
582+ cache[metadata_handler_id],
583+ " ThriftMetadataHandler" ,
584+ package_dir / metadata_handler_filename);
571585 }
572586 }
573587
@@ -766,6 +780,88 @@ class mstch_java_struct : public mstch_struct {
766780 }
767781};
768782
783+ std::string build_java_type_expression (const t_type* type) {
784+ type = type->get_true_type ();
785+
786+ if (type->is_void ()) {
787+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_VOID_TYPE)" ;
788+ } else if (type->is_bool ()) {
789+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_BOOL_TYPE)" ;
790+ } else if (type->is_byte ()) {
791+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_BYTE_TYPE)" ;
792+ } else if (type->is_i16 ()) {
793+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_I16_TYPE)" ;
794+ } else if (type->is_i32 ()) {
795+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_I32_TYPE)" ;
796+ } else if (type->is_i64 ()) {
797+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_I64_TYPE)" ;
798+ } else if (type->is_float ()) {
799+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_FLOAT_TYPE)" ;
800+ } else if (type->is_double ()) {
801+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_DOUBLE_TYPE)" ;
802+ } else if (type->is_string ()) {
803+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_STRING_TYPE)" ;
804+ } else if (type->is_binary ()) {
805+ return " ThriftType.fromTPrimitive(ThriftPrimitiveType.THRIFT_BINARY_TYPE)" ;
806+ } else if (type->is <t_list>()) {
807+ auto elem_expr =
808+ build_java_type_expression (&type->as <t_list>().elem_type ().deref ());
809+ return " ThriftType.fromTList(new ThriftListType.Builder().setValueType(" +
810+ elem_expr + " ).build())" ;
811+ } else if (type->is <t_set>()) {
812+ auto elem_expr =
813+ build_java_type_expression (&type->as <t_set>().elem_type ().deref ());
814+ return " ThriftType.fromTSet(new ThriftSetType.Builder().setValueType(" +
815+ elem_expr + " ).build())" ;
816+ } else if (type->is <t_map>()) {
817+ auto key_expr =
818+ build_java_type_expression (&type->as <t_map>().key_type ().deref ());
819+ auto val_expr =
820+ build_java_type_expression (&type->as <t_map>().val_type ().deref ());
821+ return " ThriftType.fromTMap(new ThriftMapType.Builder().setKeyType(" +
822+ key_expr + " ).setValueType(" + val_expr + " ).build())" ;
823+ } else if (type->is <t_enum>()) {
824+ auto fqn = get_namespace_or_default (*type->program ()) + " ." +
825+ java::mangle_java_name (type->name (), true );
826+ return " ThriftType.fromTEnum(new ThriftEnumType.Builder().setName(\" " +
827+ fqn + " \" ).build())" ;
828+ } else if (type->is <t_structured>()) {
829+ auto fqn = get_namespace_or_default (*type->program ()) + " ." +
830+ java::mangle_java_name (type->name (), true );
831+ return " ThriftType.fromTStruct(new ThriftStructType.Builder().setName(\" " +
832+ fqn + " \" ).build())" ;
833+ }
834+
835+ throw std::runtime_error (
836+ " Unknown Thrift type in metadata codegen: " + std::string (type->name ()));
837+ }
838+
839+ mstch::array build_meta_fields (
840+ const t_structured& fields, bool use_exception_naming) {
841+ mstch::array result;
842+ int idx = 0 ;
843+ for (const auto & field : fields.fields ()) {
844+ mstch::map entry;
845+ entry[" metaField:id" ] = std::to_string (field.id ());
846+ if (use_exception_naming) {
847+ // Synthetic names to match legacy handler's Swift annotation behavior.
848+ entry[" metaField:name" ] =
849+ std::string (" exception_" ) + std::to_string (field.id ());
850+ } else {
851+ entry[" metaField:name" ] = field.name ();
852+ }
853+ entry[" metaField:typeExpression" ] =
854+ build_java_type_expression (field.type ().get_type ());
855+ entry[" metaField:isOptional" ] =
856+ field.qualifier () == t_field_qualifier::optional;
857+ bool is_last = (idx == static_cast <int >(fields.fields ().size ()) - 1 );
858+ entry[" last?" ] = is_last;
859+ ++idx;
860+ result.emplace_back (entry);
861+ }
862+ return result;
863+ }
864+
769865class mstch_java_service : public mstch_service {
770866 public:
771867 mstch_java_service (
@@ -786,6 +882,22 @@ class mstch_java_service : public mstch_service {
786882 {" service:streamingFunctions" ,
787883 &mstch_java_service::get_streaming_functions},
788884 {" service:sinkFunctions" , &mstch_java_service::get_sink_functions},
885+ {" service:metaHasParent?" , &mstch_java_service::meta_has_parent},
886+ {" service:metaParentFQN" , &mstch_java_service::meta_parent_fqn},
887+ {" service:metaParentPackage" ,
888+ &mstch_java_service::meta_parent_package},
889+ {" service:metaParentCapitalName" ,
890+ &mstch_java_service::meta_parent_capital_name},
891+ {" service:metaFunctions" , &mstch_java_service::meta_functions},
892+ {" service:metaFunctionBatches" ,
893+ &mstch_java_service::meta_function_batches},
894+ {" service:metaFQN" , &mstch_java_service::meta_fqn},
895+ {" service:metaReferencedStructs" ,
896+ &mstch_java_service::meta_referenced_structs},
897+ {" service:metaReferencedEnums" ,
898+ &mstch_java_service::meta_referenced_enums},
899+ {" service:metaReferencedExceptions" ,
900+ &mstch_java_service::meta_referenced_exceptions},
789901 });
790902 }
791903 mstch::node get_oneway_functions () {
@@ -836,6 +948,130 @@ class mstch_java_service : public mstch_service {
836948 }
837949 return make_mstch_functions (funcs);
838950 }
951+
952+ mstch::node meta_has_parent () { return service_->extends () != nullptr ; }
953+
954+ mstch::node meta_parent_fqn () {
955+ if (auto * parent = service_->extends ()) {
956+ return get_namespace_or_default (*parent->program ()) + " ." +
957+ java::mangle_java_name (parent->name (), true );
958+ }
959+ return std::string ();
960+ }
961+
962+ mstch::node meta_parent_package () {
963+ if (auto * parent = service_->extends ()) {
964+ return get_namespace_or_default (*parent->program ());
965+ }
966+ return std::string ();
967+ }
968+
969+ mstch::node meta_parent_capital_name () {
970+ if (auto * parent = service_->extends ()) {
971+ return java::mangle_java_name (parent->name (), true );
972+ }
973+ return std::string ();
974+ }
975+
976+ mstch::node meta_fqn () {
977+ return get_namespace_or_default (*service_->program ()) + " ." +
978+ java::mangle_java_name (service_->name (), true );
979+ }
980+
981+ mstch::node meta_functions () {
982+ mstch::array functions;
983+ int func_idx = 0 ;
984+ auto all_funcs = get_functions ();
985+ std::vector<const t_function*> meta_funcs;
986+ for (const auto * func : all_funcs) {
987+ if (!func->is_interaction_constructor ()) {
988+ meta_funcs.push_back (func);
989+ }
990+ }
991+ for (const auto * func : meta_funcs) {
992+ mstch::map func_meta;
993+ func_meta[" metaFunction:name" ] = func->name ();
994+ func_meta[" metaFunction:returnTypeExpr" ] =
995+ build_java_type_expression (func->return_type ().get_type ());
996+ func_meta[" metaFunction:isOneway" ] =
997+ func->qualifier () == t_function_qualifier::oneway;
998+
999+ func_meta[" metaFunction:args" ] = build_meta_fields (func->params (), false );
1000+ func_meta[" metaFunction:hasArgs" ] = !func->params ().fields ().empty ();
1001+
1002+ mstch::array exceptions;
1003+ bool has_exceptions = false ;
1004+ if (func->exceptions () && !func->exceptions ()->fields ().empty ()) {
1005+ exceptions = build_meta_fields (*func->exceptions (), true );
1006+ has_exceptions = true ;
1007+ }
1008+ func_meta[" metaFunction:exceptions" ] = exceptions;
1009+ func_meta[" metaFunction:hasExceptions" ] = has_exceptions;
1010+
1011+ bool is_last = (func_idx == static_cast <int >(meta_funcs.size ()) - 1 );
1012+ func_meta[" last?" ] = is_last;
1013+ ++func_idx;
1014+ functions.emplace_back (func_meta);
1015+ }
1016+ return functions;
1017+ }
1018+
1019+ // Batches functions to avoid exceeding the JVM's 64KB method bytecode limit.
1020+ mstch::node meta_function_batches () {
1021+ static constexpr int kBatchSize = 100 ;
1022+ auto all_funcs = get_functions ();
1023+ std::vector<const t_function*> meta_funcs;
1024+ for (const auto * func : all_funcs) {
1025+ if (!func->is_interaction_constructor ()) {
1026+ meta_funcs.push_back (func);
1027+ }
1028+ }
1029+
1030+ mstch::array batches;
1031+ int batch_index = 0 ;
1032+ for (size_t i = 0 ; i < meta_funcs.size (); i += kBatchSize ) {
1033+ mstch::map batch;
1034+ batch[" metaBatch:index" ] = batch_index;
1035+
1036+ mstch::array batch_functions;
1037+ size_t end =
1038+ std::min (i + static_cast <size_t >(kBatchSize ), meta_funcs.size ());
1039+ for (size_t j = i; j < end; ++j) {
1040+ const auto * func = meta_funcs[j];
1041+ mstch::map func_meta;
1042+ func_meta[" metaFunction:name" ] = func->name ();
1043+ func_meta[" metaFunction:returnTypeExpr" ] =
1044+ build_java_type_expression (func->return_type ().get_type ());
1045+ func_meta[" metaFunction:isOneway" ] =
1046+ func->qualifier () == t_function_qualifier::oneway;
1047+ func_meta[" metaFunction:args" ] =
1048+ build_meta_fields (func->params (), false );
1049+ func_meta[" metaFunction:hasArgs" ] = !func->params ().fields ().empty ();
1050+
1051+ mstch::array exceptions;
1052+ bool has_exceptions = false ;
1053+ if (func->exceptions () && !func->exceptions ()->fields ().empty ()) {
1054+ exceptions = build_meta_fields (*func->exceptions (), true );
1055+ has_exceptions = true ;
1056+ }
1057+ func_meta[" metaFunction:exceptions" ] = exceptions;
1058+ func_meta[" metaFunction:hasExceptions" ] = has_exceptions;
1059+
1060+ batch_functions.emplace_back (std::move (func_meta));
1061+ }
1062+
1063+ batch[" metaBatch:functions" ] = std::move (batch_functions);
1064+ batches.emplace_back (std::move (batch));
1065+ ++batch_index;
1066+ }
1067+ return batches;
1068+ }
1069+
1070+ // TODO(leochashnikov): Traverse type graph to populate
1071+ // structs/enums/exceptions.
1072+ mstch::node meta_referenced_structs () { return mstch::array (); }
1073+ mstch::node meta_referenced_enums () { return mstch::array (); }
1074+ mstch::node meta_referenced_exceptions () { return mstch::array (); }
8391075};
8401076
8411077class mstch_java_interaction : public mstch_java_service {
0 commit comments