diff --git a/bazel/upb_proto_library.bzl b/bazel/upb_proto_library.bzl index bec5d9cbd1..155d18d98c 100644 --- a/bazel/upb_proto_library.bzl +++ b/bazel/upb_proto_library.bzl @@ -153,26 +153,29 @@ _UpbDefsWrappedCcInfo = provider(fields = ["cc_info"]) _WrappedGeneratedSrcsInfo = provider(fields = ["srcs"]) _WrappedDefsGeneratedSrcsInfo = provider(fields = ["srcs"]) -def _compile_upb_protos(ctx, proto_info, proto_sources, ext): +def _compile_upb_protos(ctx, generator, proto_info, proto_sources): if len(proto_sources) == 0: return GeneratedSrcsInfo(srcs = [], hdrs = []) + ext = "." + generator + tool = getattr(ctx.executable, "_gen_" + generator) srcs = [_generate_output_file(ctx, name, ext + ".c") for name in proto_sources] hdrs = [_generate_output_file(ctx, name, ext + ".h") for name in proto_sources] transitive_sets = proto_info.transitive_descriptor_sets.to_list() - fasttable_enabled = ctx.attr._fasttable_enabled[_FastTableEnabled].enabled + fasttable_enabled = (hasattr(ctx.attr, "_fasttable_enabled") and + ctx.attr._fasttable_enabled[_FastTableEnabled].enabled) codegen_params = "fasttable:" if fasttable_enabled else "" ctx.actions.run( inputs = depset( direct = [proto_info.direct_descriptor_set], transitive = [proto_info.transitive_descriptor_sets], ), - tools = [ctx.executable._upbc], + tools = [tool], outputs = srcs + hdrs, executable = ctx.executable._protoc, arguments = [ - "--upb_out=" + codegen_params + _get_real_root(srcs[0]), - "--plugin=protoc-gen-upb=" + ctx.executable._upbc.path, + "--" + generator + "_out=" + codegen_params + _get_real_root(srcs[0]), + "--plugin=protoc-gen-" + generator + "=" + tool.path, "--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]), ] + [_get_real_short_path(file) for file in proto_sources], @@ -213,22 +216,20 @@ def _upb_proto_rule_impl(ctx): cc_info, ] -def _upb_proto_aspect_impl(target, ctx, cc_provider, file_provider): +def _upb_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider): proto_info = target[ProtoInfo] - files = _compile_upb_protos(ctx, proto_info, proto_info.direct_sources, ctx.attr._ext) - deps = ctx.rule.attr.deps + ctx.attr._upb - if cc_provider == _UpbDefsWrappedCcInfo: - deps += ctx.attr._upb_reflection + files = _compile_upb_protos(ctx, generator, proto_info, proto_info.direct_sources) + deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator) dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep] dep_ccinfos += [dep[_UpbWrappedCcInfo].cc_info for dep in deps if _UpbWrappedCcInfo in dep] dep_ccinfos += [dep[_UpbDefsWrappedCcInfo].cc_info for dep in deps if _UpbDefsWrappedCcInfo in dep] - if cc_provider == _UpbDefsWrappedCcInfo: + if generator == "upbdefs": if _UpbWrappedCcInfo not in target: fail("Target should have _UpbDefsWrappedCcInfo provider") dep_ccinfos += [target[_UpbWrappedCcInfo].cc_info] cc_info = _cc_library_func( ctx = ctx, - name = ctx.rule.attr.name + ctx.attr._ext, + name = ctx.rule.attr.name + "." + generator, hdrs = files.hdrs, srcs = files.srcs, copts = ctx.attr._copts[_UpbProtoLibraryCopts].copts, @@ -237,10 +238,10 @@ def _upb_proto_aspect_impl(target, ctx, cc_provider, file_provider): return [cc_provider(cc_info = cc_info), file_provider(srcs = files)] def _upb_proto_library_aspect_impl(target, ctx): - return _upb_proto_aspect_impl(target, ctx, _UpbWrappedCcInfo, _WrappedGeneratedSrcsInfo) + return _upb_proto_aspect_impl(target, ctx, "upb", _UpbWrappedCcInfo, _WrappedGeneratedSrcsInfo) def _upb_proto_reflection_library_aspect_impl(target, ctx): - return _upb_proto_aspect_impl(target, ctx, _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo) + return _upb_proto_aspect_impl(target, ctx, "upbdefs", _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo) def _maybe_add(d): if not _is_bazel: @@ -258,7 +259,7 @@ _upb_proto_library_aspect = aspect( "_copts": attr.label( default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use", ), - "_upbc": attr.label( + "_gen_upb": attr.label( executable = True, cfg = "host", default = "//upbc:protoc-gen-upb", @@ -275,7 +276,6 @@ _upb_proto_library_aspect = aspect( "//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me", "//:upb", ]), - "_ext": attr.string(default = ".upb"), "_fasttable_enabled": attr.label(default = "//:fasttable_enabled"), }), implementation = _upb_proto_library_aspect_impl, @@ -308,10 +308,10 @@ _upb_proto_reflection_library_aspect = aspect( "_copts": attr.label( default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use", ), - "_upbc": attr.label( + "_gen_upbdefs": attr.label( executable = True, cfg = "host", - default = "//upbc:protoc-gen-upb", + default = "//upbc:protoc-gen-upbdefs", ), "_protoc": attr.label( executable = True, @@ -321,22 +321,12 @@ _upb_proto_reflection_library_aspect = aspect( "_cc_toolchain": attr.label( default = "@bazel_tools//tools/cpp:current_cc_toolchain", ), - # For unknown reasons, this gets overwritten. - "_upb": attr.label_list( + "_upbdefs": attr.label_list( default = [ - "//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me", "//:upb", "//:reflection", ], ), - "_upb_reflection": attr.label_list( - default = [ - "//:upb", - "//:reflection", - ], - ), - "_ext": attr.string(default = ".upbdefs"), - "_fasttable_enabled": attr.label(default = "//:fasttable_enabled"), }), implementation = _upb_proto_reflection_library_aspect_impl, provides = [ diff --git a/upbc/BUILD b/upbc/BUILD index 23897c9ac4..3b7d69bd78 100644 --- a/upbc/BUILD +++ b/upbc/BUILD @@ -6,15 +6,27 @@ load( licenses(["notice"]) cc_library( - name = "upbc_generator", + name = "common", + hdrs = ["common.h"], + srcs = ["common.cc"], + copts = UPB_DEFAULT_CPPOPTS, + deps = [ + "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/strings", + ], +) + +cc_binary( + name = "protoc-gen-upb", srcs = [ - "generator.cc", + "protoc-gen-upb.cc", "message_layout.cc", "message_layout.h", ], - hdrs = ["generator.h"], copts = UPB_DEFAULT_CPPOPTS, + visibility = ["//visibility:public"], deps = [ + ":common", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", @@ -24,12 +36,18 @@ cc_library( ) cc_binary( - name = "protoc-gen-upb", - srcs = ["main.cc"], + name = "protoc-gen-upbdefs", + srcs = [ + "protoc-gen-upbdefs.cc", + ], copts = UPB_DEFAULT_CPPOPTS, visibility = ["//visibility:public"], deps = [ - ":upbc_generator", + ":common", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + "@com_google_protobuf//:protobuf", "@com_google_protobuf//:protoc_lib", ], ) diff --git a/upbc/common.cc b/upbc/common.cc new file mode 100644 index 0000000000..c1b30a3401 --- /dev/null +++ b/upbc/common.cc @@ -0,0 +1,65 @@ + +#include "absl/strings/str_replace.h" +#include "upbc/common.h" + +namespace upbc { +namespace { + +namespace protobuf = ::google::protobuf; + +void AddMessages(const protobuf::Descriptor* message, + std::vector* messages) { + messages->push_back(message); + for (int i = 0; i < message->nested_type_count(); i++) { + AddMessages(message->nested_type(i), messages); + } +} + +} // namespace + +std::string StripExtension(absl::string_view fname) { + size_t lastdot = fname.find_last_of("."); + if (lastdot == std::string::npos) { + return std::string(fname); + } + return std::string(fname.substr(0, lastdot)); +} + +std::string ToCIdent(absl::string_view str) { + return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}}); +} + +std::string ToPreproc(absl::string_view str) { + return absl::AsciiStrToUpper(ToCIdent(str)); +} + +void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) { + output( + "/* This file was generated by upbc (the upb compiler) from the input\n" + " * file:\n" + " *\n" + " * $0\n" + " *\n" + " * Do not edit -- your changes will be discarded when the file is\n" + " * regenerated. */\n\n", + file->name()); +} + +std::vector SortedMessages( + const protobuf::FileDescriptor* file) { + std::vector messages; + for (int i = 0; i < file->message_type_count(); i++) { + AddMessages(file->message_type(i), &messages); + } + return messages; +} + +std::string MessageName(const protobuf::Descriptor* descriptor) { + return ToCIdent(descriptor->full_name()); +} + +std::string MessageInit(const protobuf::Descriptor* descriptor) { + return MessageName(descriptor) + "_msginit"; +} + +} // namespace upbc diff --git a/upbc/common.h b/upbc/common.h new file mode 100644 index 0000000000..5825786fb2 --- /dev/null +++ b/upbc/common.h @@ -0,0 +1,66 @@ + +#ifndef UPBC_COMMON_H +#define UPBC_COMMON_H + +#include + +#include "absl/strings/substitute.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/io/zero_copy_stream.h" + +namespace upbc { + +class Output { + public: + Output(google::protobuf::io::ZeroCopyOutputStream* stream) + : stream_(stream) {} + ~Output() { stream_->BackUp((int)size_); } + + template + void operator()(absl::string_view format, const Arg&... arg) { + Write(absl::Substitute(format, arg...)); + } + + private: + void Write(absl::string_view data) { + while (!data.empty()) { + RefreshOutput(); + size_t to_write = std::min(data.size(), size_); + memcpy(ptr_, data.data(), to_write); + data.remove_prefix(to_write); + ptr_ += to_write; + size_ -= to_write; + } + } + + void RefreshOutput() { + while (size_ == 0) { + void *ptr; + int size; + if (!stream_->Next(&ptr, &size)) { + fprintf(stderr, "upbc: Failed to write to to output\n"); + abort(); + } + ptr_ = static_cast(ptr); + size_ = size; + } + } + + google::protobuf::io::ZeroCopyOutputStream* stream_; + char *ptr_ = nullptr; + size_t size_ = 0; +}; + +std::string StripExtension(absl::string_view fname); +std::string ToCIdent(absl::string_view str); +std::string ToPreproc(absl::string_view str); +void EmitFileWarning(const google::protobuf::FileDescriptor* file, + Output& output); +std::vector SortedMessages( + const google::protobuf::FileDescriptor* file); +std::string MessageInit(const google::protobuf::Descriptor* descriptor); +std::string MessageName(const google::protobuf::Descriptor* descriptor); + +} // namespace upbc + +# endif // UPBC_COMMON_H diff --git a/upbc/generator.h b/upbc/generator.h deleted file mode 100644 index ed6cedc6c7..0000000000 --- a/upbc/generator.h +++ /dev/null @@ -1,12 +0,0 @@ - -#ifndef UPBC_GENERATOR_H_ -#define UPBC_GENERATOR_H_ - -#include -#include - -namespace upbc { -std::unique_ptr GetGenerator(); -} - -#endif // UPBC_GENERATOR_H_ diff --git a/upbc/main.cc b/upbc/main.cc deleted file mode 100644 index 3a38937797..0000000000 --- a/upbc/main.cc +++ /dev/null @@ -1,9 +0,0 @@ - -#include "google/protobuf/compiler/plugin.h" - -#include "upbc/generator.h" - -int main(int argc, char** argv) { - return google::protobuf::compiler::PluginMain(argc, argv, - upbc::GetGenerator().get()); -} diff --git a/upbc/generator.cc b/upbc/protoc-gen-upb.cc similarity index 82% rename from upbc/generator.cc rename to upbc/protoc-gen-upb.cc index 4f9db9ad91..e099ad4649 100644 --- a/upbc/generator.cc +++ b/upbc/protoc-gen-upb.cc @@ -1,107 +1,31 @@ -#include "upbc/generator.h" - #include -#include "absl/base/attributes.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/ascii.h" -#include "absl/strings/str_replace.h" #include "absl/strings/substitute.h" #include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/plugin.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.pb.h" -#include "google/protobuf/io/zero_copy_stream.h" #include "google/protobuf/wire_format.h" +#include "upbc/common.h" #include "upbc/message_layout.h" +namespace upbc { +namespace { + namespace protoc = ::google::protobuf::compiler; namespace protobuf = ::google::protobuf; -static std::string StripExtension(absl::string_view fname) { - size_t lastdot = fname.find_last_of("."); - if (lastdot == std::string::npos) { - return std::string(fname); - } - return std::string(fname.substr(0, lastdot)); -} - -static std::string HeaderFilename(std::string proto_filename) { +std::string HeaderFilename(std::string proto_filename) { return StripExtension(proto_filename) + ".upb.h"; } -static std::string SourceFilename(std::string proto_filename) { +std::string SourceFilename(std::string proto_filename) { return StripExtension(proto_filename) + ".upb.c"; } -static std::string DefHeaderFilename(std::string proto_filename) { - return StripExtension(proto_filename) + ".upbdefs.h"; -} - -static std::string DefSourceFilename(std::string proto_filename) { - return StripExtension(proto_filename) + ".upbdefs.c"; -} - -class Output { - public: - Output(protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {} - ~Output() { stream_->BackUp((int)size_); } - - template - void operator()(absl::string_view format, const Arg&... arg) { - Write(absl::Substitute(format, arg...)); - } - - private: - void Write(absl::string_view data) { - while (!data.empty()) { - RefreshOutput(); - size_t to_write = std::min(data.size(), size_); - memcpy(ptr_, data.data(), to_write); - data.remove_prefix(to_write); - ptr_ += to_write; - size_ -= to_write; - } - } - - void RefreshOutput() { - while (size_ == 0) { - void *ptr; - int size; - if (!stream_->Next(&ptr, &size)) { - fprintf(stderr, "upbc: Failed to write to to output\n"); - abort(); - } - ptr_ = static_cast(ptr); - size_ = size; - } - } - - protobuf::io::ZeroCopyOutputStream* stream_; - char *ptr_ = nullptr; - size_t size_ = 0; -}; - -namespace upbc { - -class Generator : public protoc::CodeGenerator { - ~Generator() override {} - bool Generate(const protobuf::FileDescriptor* file, - const std::string& parameter, protoc::GeneratorContext* context, - std::string* error) const override; - uint64_t GetSupportedFeatures() const override { - return FEATURE_PROTO3_OPTIONAL; - } -}; - -void AddMessages(const protobuf::Descriptor* message, - std::vector* messages) { - messages->push_back(message); - for (int i = 0; i < message->nested_type_count(); i++) { - AddMessages(message->nested_type(i), messages); - } -} - void AddEnums(const protobuf::Descriptor* message, std::vector* enums) { for (int i = 0; i < message->enum_type_count(); i++) { @@ -118,15 +42,6 @@ void SortDefs(std::vector* defs) { [](T a, T b) { return a->full_name() < b->full_name(); }); } -std::vector SortedMessages( - const protobuf::FileDescriptor* file) { - std::vector messages; - for (int i = 0; i < file->message_type_count(); i++) { - AddMessages(file->message_type(i), &messages); - } - return messages; -} - std::vector SortedEnums( const protobuf::FileDescriptor* file) { std::vector enums; @@ -172,18 +87,6 @@ std::vector SortedSubmessages( return ret; } -std::string ToCIdent(absl::string_view str) { - return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}}); -} - -std::string DefInitSymbol(const protobuf::FileDescriptor *file) { - return ToCIdent(file->name()) + "_upbdefinit"; -} - -std::string ToPreproc(absl::string_view str) { - return absl::AsciiStrToUpper(ToCIdent(str)); -} - std::string EnumValueSymbol(const protobuf::EnumValueDescriptor* value) { return ToCIdent(value->full_name()); } @@ -192,14 +95,6 @@ std::string GetSizeInit(const MessageLayout::Size& size) { return absl::Substitute("UPB_SIZE($0, $1)", size.size32, size.size64); } -std::string MessageName(const protobuf::Descriptor* descriptor) { - return ToCIdent(descriptor->full_name()); -} - -std::string MessageInit(const protobuf::Descriptor* descriptor) { - return MessageName(descriptor) + "_msginit"; -} - std::string CTypeInternal(const protobuf::FieldDescriptor* field, bool is_const) { std::string maybe_const = is_const ? "const " : ""; @@ -320,18 +215,6 @@ void DumpEnumValues(const protobuf::EnumDescriptor* desc, Output& output) { } } -void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) { - output( - "/* This file was generated by upbc (the upb compiler) from the input\n" - " * file:\n" - " *\n" - " * $0\n" - " *\n" - " * Do not edit -- your changes will be discarded when the file is\n" - " * regenerated. */\n\n", - file->name()); -} - void GenerateMessageInHeader(const protobuf::Descriptor* message, Output& output) { MessageLayout layout(message); @@ -1064,150 +947,47 @@ void WriteSource(const protobuf::FileDescriptor* file, Output& output, output("\n"); } -void GenerateMessageDefAccessor(const protobuf::Descriptor* d, Output& output) { - output("UPB_INLINE const upb_msgdef *$0_getmsgdef(upb_symtab *s) {\n", - ToCIdent(d->full_name())); - output(" _upb_symtab_loaddefinit(s, &$0);\n", DefInitSymbol(d->file())); - output(" return upb_symtab_lookupmsg(s, \"$0\");\n", d->full_name()); - output("}\n"); - output("\n"); - - for (int i = 0; i < d->nested_type_count(); i++) { - GenerateMessageDefAccessor(d->nested_type(i), output); - } -} - -void WriteDefHeader(const protobuf::FileDescriptor* file, Output& output) { - EmitFileWarning(file, output); - - output( - "#ifndef $0_UPBDEFS_H_\n" - "#define $0_UPBDEFS_H_\n\n" - "#include \"upb/def.h\"\n" - "#include \"upb/port_def.inc\"\n" - "#ifdef __cplusplus\n" - "extern \"C\" {\n" - "#endif\n\n", - ToPreproc(file->name())); - - output("#include \"upb/def.h\"\n"); - output("\n"); - output("#include \"upb/port_def.inc\"\n"); - output("\n"); - - output("extern upb_def_init $0;\n", DefInitSymbol(file)); - output("\n"); - - for (int i = 0; i < file->message_type_count(); i++) { - GenerateMessageDefAccessor(file->message_type(i), output); - } - - output( - "#ifdef __cplusplus\n" - "} /* extern \"C\" */\n" - "#endif\n" - "\n" - "#include \"upb/port_undef.inc\"\n" - "\n" - "#endif /* $0_UPBDEFS_H_ */\n", - ToPreproc(file->name())); -} - -// Escape C++ trigraphs by escaping question marks to \? -std::string EscapeTrigraphs(absl::string_view to_escape) { - return absl::StrReplaceAll(to_escape, {{"?", "\\?"}}); -} - -void WriteDefSource(const protobuf::FileDescriptor* file, Output& output) { - EmitFileWarning(file, output); - - output("#include \"upb/def.h\"\n"); - output("#include \"$0\"\n", DefHeaderFilename(file->name())); - output("\n"); - - for (int i = 0; i < file->dependency_count(); i++) { - output("extern upb_def_init $0;\n", DefInitSymbol(file->dependency(i))); - } - - std::vector file_messages = - SortedMessages(file); - - for (auto message : file_messages) { - output("extern const upb_msglayout $0;\n", MessageInit(message)); - } - output("\n"); - - if (!file_messages.empty()) { - output("static const upb_msglayout *layouts[$0] = {\n", file_messages.size()); - for (auto message : file_messages) { - output(" &$0,\n", MessageInit(message)); - } - output("};\n"); - output("\n"); - } - - protobuf::FileDescriptorProto file_proto; - file->CopyTo(&file_proto); - std::string file_data; - file_proto.SerializeToString(&file_data); - - output("static const char descriptor[$0] = {", file_data.size()); - - // C90 only guarantees that strings can be up to 509 characters, and some - // implementations have limits here (for example, MSVC only allows 64k: - // https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/fatal-error-c1091. - // So we always emit an array instead of a string. - for (size_t i = 0; i < file_data.size();) { - for (size_t j = 0; j < 25 && i < file_data.size(); ++i, ++j) { - output("'$0', ", absl::CEscape(file_data.substr(i, 1))); - } - output("\n"); - } - output("};\n\n"); - - output("static upb_def_init *deps[$0] = {\n", file->dependency_count() + 1); - for (int i = 0; i < file->dependency_count(); i++) { - output(" &$0,\n", DefInitSymbol(file->dependency(i))); - } - output(" NULL\n"); - output("};\n"); - output("\n"); - - output("upb_def_init $0 = {\n", DefInitSymbol(file)); - output(" deps,\n"); - if (file_messages.empty()) { - output(" NULL,\n"); - } else { - output(" layouts,\n"); +class Generator : public protoc::CodeGenerator { + ~Generator() override {} + bool Generate(const protobuf::FileDescriptor* file, + const std::string& parameter, protoc::GeneratorContext* context, + std::string* error) const override; + uint64_t GetSupportedFeatures() const override { + return FEATURE_PROTO3_OPTIONAL; } - output(" \"$0\",\n", file->name()); - output(" UPB_STRVIEW_INIT(descriptor, $0)\n", file_data.size()); - output("};\n"); -} +}; bool Generator::Generate(const protobuf::FileDescriptor* file, const std::string& parameter, protoc::GeneratorContext* context, - std::string* /* error */) const { - bool fasttable_enabled = parameter == "fasttable"; + std::string* error) const { + bool fasttable_enabled = false; + std::vector> params; + google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms); + + for (const auto& pair : params) { + if (pair.first == "fasttable") { + fasttable_enabled = true; + } else { + *error = "Unknown parameter: " + pair.first; + return false; + } + } + Output h_output(context->Open(HeaderFilename(file->name()))); WriteHeader(file, h_output); Output c_output(context->Open(SourceFilename(file->name()))); WriteSource(file, c_output, fasttable_enabled); - Output h_def_output(context->Open(DefHeaderFilename(file->name()))); - WriteDefHeader(file, h_def_output); - - Output c_def_output(context->Open(DefSourceFilename(file->name()))); - WriteDefSource(file, c_def_output); - return true; } -std::unique_ptr GetGenerator() { - return std::unique_ptr( - new Generator()); -} - +} // namespace } // namespace upbc + +int main(int argc, char** argv) { + std::unique_ptr generator( + new upbc::Generator()); + return google::protobuf::compiler::PluginMain(argc, argv, generator.get()); +} diff --git a/upbc/protoc-gen-upbdefs.cc b/upbc/protoc-gen-upbdefs.cc new file mode 100644 index 0000000000..f51ae0792f --- /dev/null +++ b/upbc/protoc-gen-upbdefs.cc @@ -0,0 +1,183 @@ + +#include + +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/plugin.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor.pb.h" +#include "upbc/common.h" + +namespace upbc { +namespace { + +namespace protoc = ::google::protobuf::compiler; +namespace protobuf = ::google::protobuf; + +std::string DefInitSymbol(const protobuf::FileDescriptor *file) { + return ToCIdent(file->name()) + "_upbdefinit"; +} + +static std::string DefHeaderFilename(std::string proto_filename) { + return StripExtension(proto_filename) + ".upbdefs.h"; +} + +static std::string DefSourceFilename(std::string proto_filename) { + return StripExtension(proto_filename) + ".upbdefs.c"; +} + +void GenerateMessageDefAccessor(const protobuf::Descriptor* d, Output& output) { + output("UPB_INLINE const upb_msgdef *$0_getmsgdef(upb_symtab *s) {\n", + ToCIdent(d->full_name())); + output(" _upb_symtab_loaddefinit(s, &$0);\n", DefInitSymbol(d->file())); + output(" return upb_symtab_lookupmsg(s, \"$0\");\n", d->full_name()); + output("}\n"); + output("\n"); + + for (int i = 0; i < d->nested_type_count(); i++) { + GenerateMessageDefAccessor(d->nested_type(i), output); + } +} + +void WriteDefHeader(const protobuf::FileDescriptor* file, Output& output) { + EmitFileWarning(file, output); + + output( + "#ifndef $0_UPBDEFS_H_\n" + "#define $0_UPBDEFS_H_\n\n" + "#include \"upb/def.h\"\n" + "#include \"upb/port_def.inc\"\n" + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n\n", + ToPreproc(file->name())); + + output("#include \"upb/def.h\"\n"); + output("\n"); + output("#include \"upb/port_def.inc\"\n"); + output("\n"); + + output("extern upb_def_init $0;\n", DefInitSymbol(file)); + output("\n"); + + for (int i = 0; i < file->message_type_count(); i++) { + GenerateMessageDefAccessor(file->message_type(i), output); + } + + output( + "#ifdef __cplusplus\n" + "} /* extern \"C\" */\n" + "#endif\n" + "\n" + "#include \"upb/port_undef.inc\"\n" + "\n" + "#endif /* $0_UPBDEFS_H_ */\n", + ToPreproc(file->name())); +} + + +void WriteDefSource(const protobuf::FileDescriptor* file, Output& output) { + EmitFileWarning(file, output); + + output("#include \"upb/def.h\"\n"); + output("#include \"$0\"\n", DefHeaderFilename(file->name())); + output("\n"); + + for (int i = 0; i < file->dependency_count(); i++) { + output("extern upb_def_init $0;\n", DefInitSymbol(file->dependency(i))); + } + + std::vector file_messages = + SortedMessages(file); + + for (auto message : file_messages) { + output("extern const upb_msglayout $0;\n", MessageInit(message)); + } + output("\n"); + + if (!file_messages.empty()) { + output("static const upb_msglayout *layouts[$0] = {\n", file_messages.size()); + for (auto message : file_messages) { + output(" &$0,\n", MessageInit(message)); + } + output("};\n"); + output("\n"); + } + + protobuf::FileDescriptorProto file_proto; + file->CopyTo(&file_proto); + std::string file_data; + file_proto.SerializeToString(&file_data); + + output("static const char descriptor[$0] = {", file_data.size()); + + // C90 only guarantees that strings can be up to 509 characters, and some + // implementations have limits here (for example, MSVC only allows 64k: + // https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/fatal-error-c1091. + // So we always emit an array instead of a string. + for (size_t i = 0; i < file_data.size();) { + for (size_t j = 0; j < 25 && i < file_data.size(); ++i, ++j) { + output("'$0', ", absl::CEscape(file_data.substr(i, 1))); + } + output("\n"); + } + output("};\n\n"); + + output("static upb_def_init *deps[$0] = {\n", file->dependency_count() + 1); + for (int i = 0; i < file->dependency_count(); i++) { + output(" &$0,\n", DefInitSymbol(file->dependency(i))); + } + output(" NULL\n"); + output("};\n"); + output("\n"); + + output("upb_def_init $0 = {\n", DefInitSymbol(file)); + output(" deps,\n"); + if (file_messages.empty()) { + output(" NULL,\n"); + } else { + output(" layouts,\n"); + } + output(" \"$0\",\n", file->name()); + output(" UPB_STRVIEW_INIT(descriptor, $0)\n", file_data.size()); + output("};\n"); +} + +class Generator : public protoc::CodeGenerator { + ~Generator() override {} + bool Generate(const protobuf::FileDescriptor* file, + const std::string& parameter, protoc::GeneratorContext* context, + std::string* error) const override; + uint64_t GetSupportedFeatures() const override { + return FEATURE_PROTO3_OPTIONAL; + } +}; + +bool Generator::Generate(const protobuf::FileDescriptor* file, + const std::string& parameter, + protoc::GeneratorContext* context, + std::string* error) const { + std::vector> params; + google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms); + + for (const auto& pair : params) { + *error = "Unknown parameter: " + pair.first; + return false; + } + + Output h_def_output(context->Open(DefHeaderFilename(file->name()))); + WriteDefHeader(file, h_def_output); + + Output c_def_output(context->Open(DefSourceFilename(file->name()))); + WriteDefSource(file, c_def_output); + + return true; +} + +} // namespace +} // namespace upbc + +int main(int argc, char** argv) { + std::unique_ptr generator( + new upbc::Generator()); + return google::protobuf::compiler::PluginMain(argc, argv, generator.get()); +}