// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #ifndef UPB_UPB_GENERATOR_PLUGIN_H_ #define UPB_UPB_GENERATOR_PLUGIN_H_ #include #include #include #ifdef _WIN32 #include #include #endif #include "absl/container/flat_hash_set.h" #include "absl/log/absl_log.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "upb/reflection/def.hpp" #include "upb/reflection/descriptor_bootstrap.h" #include "upb_generator/plugin_bootstrap.h" // Must be last. #include "upb/port/def.inc" namespace upb { namespace generator { inline std::vector> ParseGeneratorParameter( const absl::string_view text) { std::vector> ret; for (absl::string_view sp : absl::StrSplit(text, ',', absl::SkipEmpty())) { std::string::size_type equals_pos = sp.find_first_of('='); std::pair value; if (equals_pos == std::string::npos) { value.first = std::string(sp); } else { value.first = std::string(sp.substr(0, equals_pos)); value.second = std::string(sp.substr(equals_pos + 1)); } ret.push_back(std::move(value)); } return ret; } class Plugin { public: Plugin() { ReadRequest(); } ~Plugin() { WriteResponse(); } absl::string_view parameter() const { return ToStringView( UPB_DESC(compiler_CodeGeneratorRequest_parameter)(request_)); } template void GenerateFilesRaw(T&& func) { absl::flat_hash_set files_to_generate; size_t size; const upb_StringView* file_to_generate = UPB_DESC( compiler_CodeGeneratorRequest_file_to_generate)(request_, &size); for (size_t i = 0; i < size; i++) { files_to_generate.insert( {file_to_generate[i].data, file_to_generate[i].size}); } const UPB_DESC(FileDescriptorProto)* const* files = UPB_DESC(compiler_CodeGeneratorRequest_proto_file)(request_, &size); for (size_t i = 0; i < size; i++) { upb::Status status; absl::string_view name = ToStringView(UPB_DESC(FileDescriptorProto_name)(files[i])); func(files[i], files_to_generate.contains(name)); } } template void GenerateFiles(T&& func) { GenerateFilesRaw( [this, &func](const UPB_DESC(FileDescriptorProto) * file_proto, bool generate) { upb::Status status; upb::FileDefPtr file = pool_.AddFile(file_proto, &status); if (!file) { absl::string_view name = ToStringView(UPB_DESC(FileDescriptorProto_name)(file_proto)); ABSL_LOG(FATAL) << "Couldn't add file " << name << " to DefPool: " << status.error_message(); } if (generate) func(file); }); } void SetError(absl::string_view error) { char* data = static_cast(upb_Arena_Malloc(arena_.ptr(), error.size())); memcpy(data, error.data(), error.size()); UPB_DESC(compiler_CodeGeneratorResponse_set_error) (response_, upb_StringView_FromDataAndSize(data, error.size())); } void AddOutputFile(absl::string_view filename, absl::string_view content) { UPB_DESC(compiler_CodeGeneratorResponse_File)* file = UPB_DESC( compiler_CodeGeneratorResponse_add_file)(response_, arena_.ptr()); UPB_DESC(compiler_CodeGeneratorResponse_File_set_name) (file, StringDup(filename)); UPB_DESC(compiler_CodeGeneratorResponse_File_set_content) (file, StringDup(content)); } private: upb::Arena arena_; upb::DefPool pool_; UPB_DESC(compiler_CodeGeneratorRequest) * request_; UPB_DESC(compiler_CodeGeneratorResponse) * response_; static absl::string_view ToStringView(upb_StringView sv) { return absl::string_view(sv.data, sv.size); } upb_StringView StringDup(absl::string_view s) { char* data = reinterpret_cast(upb_Arena_Malloc(arena_.ptr(), s.size())); memcpy(data, s.data(), s.size()); return upb_StringView_FromDataAndSize(data, s.size()); } std::string ReadAllStdinBinary() { std::string data; #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY); #endif char buf[4096]; while (size_t len = fread(buf, 1, sizeof(buf), stdin)) { data.append(buf, len); } return data; } void ReadRequest() { std::string data = ReadAllStdinBinary(); request_ = UPB_DESC(compiler_CodeGeneratorRequest_parse)( data.data(), data.size(), arena_.ptr()); if (!request_) { ABSL_LOG(FATAL) << "Failed to parse CodeGeneratorRequest"; } response_ = UPB_DESC(compiler_CodeGeneratorResponse_new)(arena_.ptr()); int features = UPB_DESC(compiler_CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) | UPB_DESC(compiler_CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS); UPB_DESC(compiler_CodeGeneratorResponse_set_supported_features) (response_, features); UPB_DESC(compiler_CodeGeneratorResponse_set_minimum_edition) (response_, UPB_DESC(EDITION_PROTO2)); UPB_DESC(compiler_CodeGeneratorResponse_set_maximum_edition) (response_, UPB_DESC(EDITION_2023)); } void WriteResponse() { size_t size; char* serialized = UPB_DESC(compiler_CodeGeneratorResponse_serialize)( response_, arena_.ptr(), &size); if (!serialized) { ABSL_LOG(FATAL) << "Failed to serialize CodeGeneratorResponse"; } if (fwrite(serialized, 1, size, stdout) != size) { ABSL_LOG(FATAL) << "Failed to write response to stdout"; } } }; } // namespace generator } // namespace upb #include "upb/port/undef.inc" #endif // UPB_UPB_GENERATOR_PLUGIN_H_