// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef UPB_UPBC_PLUGIN_H_ #define UPB_UPBC_PLUGIN_H_ #include #include #include #ifdef _WIN32 #include #include #endif // begin:google_only // #ifndef UPB_BOOTSTRAP_STAGE0 // #include "net/proto2/proto/descriptor.upb.h" // #include "third_party/protobuf/compiler/plugin.upb.h" // #else // #include "google/protobuf/compiler/plugin.upb.h" // #include "google/protobuf/descriptor.upb.h" // #endif // end:google_only // begin:github_only #include "google/protobuf/compiler/plugin.upb.h" #include "google/protobuf/descriptor.upb.h" // end:github_only #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/upb/reflection/def.hpp" // Must be last. #include "upb/upb/port/def.inc" namespace upbc { 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()); UPB_DESC(compiler_CodeGeneratorResponse_set_supported_features) (response_, UPB_DESC(compiler_CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)); } 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 upbc #include "upb/upb/port/undef.inc" #endif // UPB_UPBC_PLUGIN_H_