Skip to content

Commit bfef25e

Browse files
RayanRalmeta-codesync[bot]
authored andcommitted
Add ThriftMetadataHandler codegen to the Java compiler
Summary: Adds compile-time codegen for Thrift service metadata in the Java compiler, replacing the need for runtime reflection or `ThriftCodecManager`, used by legacy `ThriftMetadataServiceHandler`. **Codegen pipeline:** 1. `t_mstch_java_generator.cc` — computes metadata (FQNs, function signatures, type expressions) for each Thrift service at compile time and feeds them into mustache templates. 2. `ThriftMetadataHandler.mustache` — generates a per-service `XxxThriftMetadataHandler.java` class implementing the `ThriftMetadataHandler` interface with service metadata, parent chain traversal, and type maps. 3. `Service.mustache` — adds a `static getMetadataHandler()` method on each generated service interface. **What is codegen'd:** - Per-service function signatures with full type resolution - All primitive types (void, bool, byte, i16, i32, i64, float, double, string, binary) - Struct, enum, union type references in function arguments/return types - Container types (list, set, map) including nested containers - Service inheritance (parent chain via `getParentHandler()`) - Exception declarations in function throws clauses - Oneway function flag **Runtime interface:** - `ThriftMetadataHandler` interface in `thrift/lib/java/runtime` — defines the contract for generated handlers. Reviewed By: j-bahr Differential Revision: D89291108 fbshipit-source-id: 63acd19ec94cea8f844215ec1d3db79d21414fc2
1 parent 8c04ba0 commit bfef25e

72 files changed

Lines changed: 5624 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

third-party/thrift/src/thrift/compiler/generate/t_mstch_java_generator.cc

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
769865
class 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

8411077
class mstch_java_interaction : public mstch_java_service {

third-party/thrift/src/thrift/compiler/generate/templates/java/Service.mustache

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public interface {{service:javaCapitalName}} extends java.io.Closeable, com.face
4343
};
4444
}
4545

46+
static com.facebook.thrift.server.ThriftMetadataHandler getMetadataHandler() {
47+
return new {{service:javaCapitalName}}ThriftMetadataHandler();
48+
}
49+
4650
static com.facebook.thrift.client.ClientBuilder<{{service:javaCapitalName}}> clientBuilder() {
4751
return new ClientBuilder<{{service:javaCapitalName}}>() {
4852
@java.lang.Override

0 commit comments

Comments
 (0)