C++ Code generator

PiperOrigin-RevId: 486158566
diff --git a/protos_generator/BUILD b/protos_generator/BUILD
new file mode 100644
index 0000000..394668e
--- /dev/null
+++ b/protos_generator/BUILD
@@ -0,0 +1,85 @@
+# Copyright (c) 2009-2021, Google LLC
+# All rights reserved.
+#
+# 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 Google LLC 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.
+
+load(
+    "//bazel:build_defs.bzl",
+    "UPB_DEFAULT_CPPOPTS",
+)
+
+licenses(["notice"])
+
+cc_binary(
+    name = "protoc-gen-upb-protos",
+    srcs = [
+        "protoc-gen-upb-protos.cc",
+    ],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":generator",
+        "//:mini_table",
+        "//upbc:file_layout",
+        "@com_google_absl//absl/base:core_headers",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/strings",
+        "@com_google_protobuf//:protobuf",
+        "@com_google_protobuf//src/google/protobuf/compiler:code_generator",
+    ],
+)
+
+cc_library(
+    name = "generator",
+    srcs = [
+        "gen_accessors.cc",
+        "gen_enums.cc",
+        "gen_extensions.cc",
+        "gen_messages.cc",
+        "gen_utils.cc",
+        "output.cc",
+    ],
+    hdrs = [
+        "gen_accessors.h",
+        "gen_enums.h",
+        "gen_extensions.h",
+        "gen_messages.h",
+        "gen_utils.h",
+        "output.h",
+    ],
+    visibility = ["//visibility:private"],
+    deps = [
+        "//upbc:common",
+        "//upbc:file_layout",
+        "//upbc:keywords",
+        "@com_google_absl//absl/base:log_severity",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/log",
+        "@com_google_absl//absl/log:check",
+        "@com_google_absl//absl/strings",
+        "@com_google_protobuf//:protobuf",
+        "@com_google_protobuf//src/google/protobuf/compiler:code_generator",
+    ],
+)
diff --git a/protos_generator/gen_accessors.cc b/protos_generator/gen_accessors.cc
new file mode 100644
index 0000000..740daab
--- /dev/null
+++ b/protos_generator/gen_accessors.cc
@@ -0,0 +1,722 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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.
+
+#include "protos_generator/gen_accessors.h"
+
+#include "google/protobuf/descriptor.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/output.h"
+#include "upbc/file_layout.h"
+#include "upbc/keywords.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+using FileLayout = ::upbc::FileLayout;
+using NameToFieldDescriptorMap =
+    absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>;
+
+void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
+                              const protobuf::FieldDescriptor* field,
+                              const absl::string_view resolved_field_name,
+                              const FileLayout& layout, Output& output);
+void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
+                             const protobuf::FieldDescriptor* field,
+                             const absl::string_view resolved_field_name,
+                             const FileLayout& layout, Output& output);
+void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
+                            const protobuf::FieldDescriptor* field,
+                            const absl::string_view resolved_field_name,
+                            Output& output);
+
+void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output);
+void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
+                                  const protobuf::FieldDescriptor* field,
+                                  const absl::string_view resolved_field_name,
+                                  absl::string_view class_name, Output& output);
+void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output);
+void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 absl::string_view class_name, Output& output);
+
+// Returns C++ class member name by resolving naming conflicts across
+// proto field names (such as clear_ prefixes) and keyword collisions.
+//
+// The Upb C generator prefixes all accessors with package and class names
+// avoiding collisions. Therefore we need to use raw field names when calling
+// into C accessors but need to fully resolve conflicts for C++ class members.
+std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
+                             const NameToFieldDescriptorMap& field_names);
+
+NameToFieldDescriptorMap CreateFieldNameMap(
+    const protobuf::Descriptor* message) {
+  NameToFieldDescriptorMap field_names;
+  for (int i = 0; i < message->field_count(); i++) {
+    const protobuf::FieldDescriptor* field = message->field(i);
+    field_names.emplace(field->name(), field);
+  }
+  return field_names;
+}
+
+void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 Output& output) {
+  FileLayout layout(desc->file());
+
+  // Generate const methods.
+  OutputIndenter i(output);
+
+  auto field_names = CreateFieldNameMap(desc);
+
+  for (auto field : ::upbc::FieldNumberOrder(desc)) {
+    std::string resolved_field_name = ResolveFieldName(field, field_names);
+    absl::string_view upbc_name = field->name();
+    WriteFieldAccessorHazzer(desc, field, resolved_field_name, layout, output);
+    WriteFieldAccessorClear(desc, field, resolved_field_name, layout, output);
+
+    if (field->is_map()) {
+      WriteMapFieldAccessors(desc, field, resolved_field_name, output);
+    } else if (desc->options().map_entry()) {
+      // TODO(b/237399867) Implement map entry
+    } else if (field->is_repeated()) {
+      output(
+          R"cc(
+            inline size_t $1_size() const {
+              size_t len;
+              $0_$2(msg_, &len);
+              return len;
+            }
+
+            inline void clear_$1() { $0_clear_$2(msg_); }
+          )cc",
+          MessageName(desc), resolved_field_name, upbc_name);
+
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output(
+            R"cc(
+              $1 $2(size_t index) const;
+              absl::StatusOr<$0> add_$2();
+              $0 mutable_$3(size_t index) const;
+            )cc",
+            MessagePtrConstType(field, /* const */ false),
+            MessagePtrConstType(field, /* const */ true), resolved_field_name,
+            upbc_name);
+      } else {
+        output(
+            R"cc(
+              $0 $1(size_t index) const;
+              bool add_$1($0 val);
+              void set_$1(size_t index, $0 val);
+              bool resize_$1(size_t len);
+            )cc",
+            CppConstType(field), resolved_field_name);
+      }
+    } else {
+      // non-repeated.
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+        output(R"cc(
+                 $0 $1() const;
+                 void set_$1($0 value);
+               )cc",
+               CppConstType(field), resolved_field_name);
+      } else if (field->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output(R"cc(
+                 $1 $2() const;
+                 $0 mutable_$3();
+               )cc",
+               MessagePtrConstType(field, /* const */ false),
+               MessagePtrConstType(field, /* const */ true),
+               resolved_field_name, upbc_name);
+      } else {
+        output(
+            R"cc(
+              inline $0 $1() const { return $2_$3(msg_); }
+              inline void set_$1($0 value) { return $2_set_$3(msg_, value); }
+            )cc",
+            CppConstType(field), resolved_field_name, MessageName(desc),
+            upbc_name);
+      }
+    }
+  }
+}
+
+void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
+                              const protobuf::FieldDescriptor* field,
+                              const absl::string_view resolved_field_name,
+                              const FileLayout& layout, Output& output) {
+  // Generate hazzer (if any).
+  if (layout.HasHasbit(field) || field->real_containing_oneof()) {
+    absl::string_view upbc_name = field->name();
+    // Has presence.
+    output("inline bool has_$0() const { return $1_has_$2(msg_); }\n",
+           resolved_field_name, MessageName(desc), upbc_name);
+  }
+}
+
+void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
+                             const protobuf::FieldDescriptor* field,
+                             const absl::string_view resolved_field_name,
+                             const FileLayout& layout, Output& output) {
+  if (layout.HasHasbit(field) || field->real_containing_oneof()) {
+    absl::string_view upbc_name = field->name();
+    output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name,
+           upbc_name, MessageName(desc));
+  }
+}
+
+void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
+                            const protobuf::FieldDescriptor* field,
+                            const absl::string_view resolved_field_name,
+                            Output& output) {
+  const protobuf::Descriptor* entry = field->message_type();
+  const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
+  const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        inline size_t $0_size() const { return $1_$3_size(msg_); }
+        inline void clear_$0() { $1_clear_$3(msg_); }
+        void delete_$0($2 key);
+      )cc",
+      resolved_field_name, MessageName(desc), CppConstType(key), upbc_name);
+
+  if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+    output(
+        R"cc(
+          bool set_$0($1 key, $3 value);
+          bool set_$0($1 key, $4 value);
+          absl::StatusOr<$3> get_$0($1 key);
+        )cc",
+        resolved_field_name, CppConstType(key), CppConstType(val),
+        MessagePtrConstType(val, /* is_const */ true),
+        MessagePtrConstType(val, /* is_const */ false));
+  } else {
+    output(
+        R"cc(
+          bool set_$0($1 key, $2 value);
+          absl::StatusOr<$2> get_$0($1 key);
+        )cc",
+        resolved_field_name, CppConstType(key), CppConstType(val));
+  }
+}
+
+void WriteAccessorsInSource(const protobuf::Descriptor* desc,
+                            const FileLayout& layout, Output& output) {
+  std::string class_name = ClassName(desc);
+  absl::StrAppend(&class_name, "Access");
+  output("namespace internal {\n");
+  const char arena_expression[] = "arena_";
+  auto field_names = CreateFieldNameMap(desc);
+
+  // Generate const methods.
+  OutputIndenter i(output);
+  for (auto field : ::upbc::FieldNumberOrder(desc)) {
+    std::string resolved_field_name = ResolveFieldName(field, field_names);
+    if (field->is_map()) {
+      WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name,
+                                  output);
+    } else if (desc->options().map_entry()) {
+      // TODO(b/237399867) Implement map entry
+    } else if (field->is_repeated()) {
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        WriteRepeatedMessageAccessor(desc, field, resolved_field_name,
+                                     class_name, output);
+      } else if (field->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_STRING) {
+        WriteRepeatedStringAccessor(desc, field, resolved_field_name,
+                                    class_name, output);
+      } else {
+        WriteRepeatedScalarAccessor(desc, field, resolved_field_name,
+                                    class_name, output);
+      }
+    } else {
+      absl::string_view upbc_name = field->name();
+      // non-repeated field.
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+        output(
+            R"cc(
+              $1 $0::$2() const {
+                return ::protos::UpbStrToStringView($3_$4(msg_));
+              }
+            )cc",
+            class_name, CppConstType(field), resolved_field_name,
+            MessageName(desc), upbc_name);
+        // Set string.
+        output(
+            R"cc(
+              void $0::set_$2($1 value) {
+                $4_set_$3(msg_, ::protos::UpbStrFromStringView(value, $5));
+              }
+            )cc",
+            class_name, CppConstType(field), resolved_field_name, upbc_name,
+            MessageName(desc), arena_expression);
+      } else if (field->cpp_type() ==
+                 protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output(
+            R"cc(
+              $1 $0::$2() const {
+                if (!has_$2()) {
+                  return $4::default_instance();
+                }
+                return ::protos::internal::CreateMessage<$4>((upb_Message*)($3_$5(msg_)));
+              }
+            )cc",
+            class_name, MessagePtrConstType(field, /* is_const */ true),
+            resolved_field_name, MessageName(desc),
+            MessageBaseType(field, /* maybe_const */ false), upbc_name);
+
+        output(
+            R"cc(
+              $1 $0::mutable_$2() {
+                return ::protos::internal::CreateMessageProxy<$4>(
+                    (upb_Message*)($3_mutable_$5(msg_, $6)), $6);
+              }
+            )cc",
+            class_name, MessagePtrConstType(field, /* is_const */ false),
+            resolved_field_name, MessageName(desc),
+            MessageBaseType(field, /* maybe_const */ false), upbc_name,
+            arena_expression);
+      }
+    }
+  }
+  output("\n");
+  output("}  // namespace internal\n\n");
+}
+
+void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
+                                  const protobuf::FieldDescriptor* field,
+                                  const absl::string_view resolved_field_name,
+                                  const absl::string_view class_name,
+                                  Output& output) {
+  const char arena_expression[] = "arena_";
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        $1 $0::$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_$5(msg_, &len);
+          assert(0 <= index && index < len);
+          return ::protos::internal::CreateMessage<$4>((upb_Message*)*(ptr + index));
+        }
+      )cc",
+      class_name, MessagePtrConstType(field, /* is_const */ true),
+      resolved_field_name, MessageName(message),
+      MessageBaseType(field, /* maybe_const */ false), upbc_name);
+  output(
+      R"cc(
+        absl::StatusOr<$1> $0::add_$2() {
+          auto new_msg = $3_add_$6(msg_, $5);
+          if (!new_msg) {
+            return ::protos::MessageAllocationError();
+          }
+          return ::protos::internal::CreateMessageProxy<$4>((upb_Message*)new_msg, $5);
+        }
+      )cc",
+      class_name, MessagePtrConstType(field, /* const */ false),
+      resolved_field_name, MessageName(message),
+      MessageBaseType(field, /* maybe_const */ false), arena_expression,
+      upbc_name);
+  output(
+      R"cc(
+        $1 $0::mutable_$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_$6(msg_, &len);
+          assert(0 <= index && index < len);
+          return ::protos::internal::CreateMessageProxy<$4>(
+              (upb_Message*)*(ptr + index), $5);
+        }
+      )cc",
+      class_name, MessagePtrConstType(field, /* is_const */ false),
+      resolved_field_name, MessageName(message),
+      MessageBaseType(field, /* maybe_const */ false), arena_expression,
+      upbc_name);
+}
+
+void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output) {
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        $1 $0::$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(0 <= index && index < len);
+          return ::protos::UpbStrToStringView(*(ptr + index));
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::resize_$1(size_t len) {
+          return $2_resize_$3(msg_, len, arena_);
+        }
+      )cc",
+      class_name, resolved_field_name, MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::add_$2($1 val) {
+          return $3_add_$4(msg_, ::protos::UpbStrFromStringView(val, arena_), arena_);
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        void $0::set_$2(size_t index, $1 val) {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(0 <= index && index < len);
+          *(ptr + index) = ::protos::UpbStrFromStringView(val, arena_);
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+}
+
+void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output) {
+  absl::string_view upbc_name = field->name();
+  output(
+      R"cc(
+        $1 $0::$2(size_t index) const {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(0 <= index && index < len);
+          return *(ptr + index);
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::resize_$1(size_t len) {
+          return $2_resize_$3(msg_, len, arena_);
+        }
+      )cc",
+      class_name, resolved_field_name, MessageName(message), upbc_name);
+  output(
+      R"cc(
+        bool $0::add_$2($1 val) { return $3_add_$4(msg_, val, arena_); }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+  output(
+      R"cc(
+        void $0::set_$2(size_t index, $1 val) {
+          size_t len;
+          auto* ptr = $3_mutable_$4(msg_, &len);
+          assert(0 <= index && index < len);
+          *(ptr + index) = val;
+        }
+      )cc",
+      class_name, CppConstType(field), resolved_field_name,
+      MessageName(message), upbc_name);
+}
+
+void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
+                                 const protobuf::FieldDescriptor* field,
+                                 const absl::string_view resolved_field_name,
+                                 const absl::string_view class_name,
+                                 Output& output) {
+  const protobuf::Descriptor* entry = field->message_type();
+  const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
+  const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
+  absl::string_view upbc_name = field->name();
+  absl::string_view converted_key_name = "key";
+  absl::string_view optional_conversion_code = "";
+
+  if (key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+    // Insert conversion from absl::string_view to upb_StringView.
+    // Creates upb_StringView on stack to prevent allocation.
+    converted_key_name = "upb_key";
+    optional_conversion_code =
+        "upb_StringView upb_key = {key.data(), key.size()};\n";
+  }
+  if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            $6return $4_$8_set(msg_, $7, ($5*)value->msg(), arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ true), MessageName(message),
+        MessageName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name);
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            $6return $4_$8_set(msg_, $7, ($5*)value->msg(), arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ false), MessageName(message),
+        MessageName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name);
+    output(
+        R"cc(
+          absl::StatusOr<$3> $0::get_$1($2 key) {
+            $5* msg_value;
+            $7bool success = $4_$9_get(msg_, $8, &msg_value);
+            if (success) {
+              return ::protos::internal::CreateMessage<$6>(msg_value);
+            }
+            return absl::NotFoundError("");
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ true), MessageName(message),
+        MessageName(val->message_type()),
+        QualifiedClassName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name);
+    output(
+        R"cc(
+          void $0::delete_$1($2 key) { $6$4_$8_delete(msg_, $7); }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key),
+        MessagePtrConstType(val, /* is_const */ false), MessageName(message),
+        MessageName(val->message_type()), optional_conversion_code,
+        converted_key_name, upbc_name);
+  } else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            $5return $4_$7_set(msg_, $6,
+                               ::protos::UpbStrFromStringView(value, arena_),
+                               arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          absl::StatusOr<$3> $0::get_$1($2 key) {
+            upb_StringView value;
+            $5bool success = $4_$7_get(msg_, $6, &value);
+            if (success) {
+              return absl::string_view(value.data, value.size);
+            }
+            return absl::NotFoundError("");
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+  } else {
+    output(
+        R"cc(
+          bool $0::set_$1($2 key, $3 value) {
+            $5return $4_$7_set(msg_, $6, value, arena_);
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          absl::StatusOr<$3> $0::get_$1($2 key) {
+            $3 value;
+            $5bool success = $4_$7_get(msg_, $6, &value);
+            if (success) {
+              return value;
+            }
+            return absl::NotFoundError("");
+          }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+    output(
+        R"cc(
+          void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
+        )cc",
+        class_name, resolved_field_name, CppConstType(key), CppConstType(val),
+        MessageName(message), optional_conversion_code, converted_key_name,
+        upbc_name);
+  }
+}
+
+void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 MessageClassType handle_type, Output& output) {
+  FileLayout layout(desc->file());
+  bool read_only = handle_type == MessageClassType::kMessageCProxy;
+
+  // Generate const methods.
+  OutputIndenter i(output);
+  std::string class_name = ClassName(desc);
+  auto field_names = CreateFieldNameMap(desc);
+
+  for (auto field : ::upbc::FieldNumberOrder(desc)) {
+    std::string resolved_field_name = ResolveFieldName(field, field_names);
+    // Generate hazzer (if any).
+    if (layout.HasHasbit(field) || field->real_containing_oneof()) {
+      // Has presence.
+      output("using $0Access::has_$1;\n", class_name, resolved_field_name);
+    }
+    // Generate clear.
+    if (layout.HasHasbit(field) || field->real_containing_oneof()) {
+      output("using $0Access::clear_$1;\n", class_name, resolved_field_name);
+    }
+    if (field->is_map()) {
+      output(
+          R"cc(
+            using $0Access::$1_size;
+            using $0Access::clear_$1;
+            using $0Access::delete_$1;
+            using $0Access::get_$1;
+            using $0Access::set_$1;
+          )cc",
+          class_name, resolved_field_name);
+    } else if (desc->options().map_entry()) {
+      // TODO(b/237399867) Implement map entry
+    } else if (field->is_repeated()) {
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output(
+            R"cc(
+              using $0Access::$1;
+              using $0Access::$1_size;
+              using $0Access::mutable_$1;
+            )cc",
+            class_name, resolved_field_name);
+        if (!read_only) {
+          output(
+              R"cc(
+                using $0Access::add_$1;
+                using $0Access::clear_$1;
+              )cc",
+              class_name, resolved_field_name);
+        }
+      } else {
+        output(
+            R"cc(
+              using $0Access::$1;
+              using $0Access::$1_size;
+            )cc",
+            class_name, resolved_field_name);
+        if (!read_only) {
+          output(
+              R"cc(
+                using $0Access::add_$1;
+                using $0Access::clear_$1;
+                using $0Access::resize_$1;
+                using $0Access::set_$1;
+              )cc",
+              class_name, resolved_field_name);
+        }
+      }
+    } else {
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        output("using $0Access::$1;\n", ClassName(desc), resolved_field_name);
+        if (!read_only) {
+          output("using $0Access::mutable_$1;\n", class_name,
+                 resolved_field_name);
+        }
+      } else {
+        output("using $0Access::$1;\n", class_name, resolved_field_name);
+        if (!read_only) {
+          output("using $0Access::set_$1;\n", class_name, resolved_field_name);
+        }
+      }
+    }
+  }
+  output("using $0Access::msg;\n", class_name);
+}
+
+std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
+                             const NameToFieldDescriptorMap& field_names) {
+  // C++ implementation specific reserved names.
+  static const auto& kReservedNames =
+      *new absl::flat_hash_set<absl::string_view>({
+          "msg",
+          "msg_",
+          "arena",
+          "arena_",
+      });
+
+  // List of generated accessor prefixes to check against.
+  // Example:
+  //     optional repeated string phase = 236;
+  //     optional bool clear_phase = 237;
+  static constexpr absl::string_view kAccessorPrefixes[] = {
+      "clear_",
+      "delete_",
+      "add_",
+      "resize_",
+  };
+
+  absl::string_view field_name = field->name();
+  if (kReservedNames.count(field_name) > 0) {
+    if (absl::EndsWith(field_name, "_")) {
+      return absl::StrCat(field_name, "_");
+    } else {
+      return absl::StrCat(field_name, "__");
+    }
+  }
+  for (const auto prefix : kAccessorPrefixes) {
+    // If field name starts with a prefix such as clear_ and the proto
+    // contains a field name with trailing end, depending on type of field
+    // (repeated, map, message) we have a conflict to resolve.
+    if (absl::StartsWith(field_name, prefix)) {
+      auto match = field_names.find(field_name.substr(prefix.size()));
+      if (match != field_names.end()) {
+        const auto* candidate = match->second;
+        if (candidate->is_repeated() || candidate->is_map()) {
+          return absl::StrCat(field_name, "_");
+        }
+      }
+    }
+  }
+  return upbc::ResolveKeywordConflict(std::string(field_name));
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_accessors.h b/protos_generator/gen_accessors.h
new file mode 100644
index 0000000..90da017
--- /dev/null
+++ b/protos_generator/gen_accessors.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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_PROTOS_GENERATOR_ACCESSORS_H_
+#define UPB_PROTOS_GENERATOR_ACCESSORS_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/output.h"
+#include "upbc/file_layout.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 Output& output);
+void WriteAccessorsInSource(const protobuf::Descriptor* desc,
+                            const ::upbc::FileLayout& layout, Output& output);
+void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
+                                 MessageClassType handle_type, Output& output);
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_ACCESSORS_H_
diff --git a/protos_generator/gen_enums.cc b/protos_generator/gen_enums.cc
new file mode 100644
index 0000000..0197cd1
--- /dev/null
+++ b/protos_generator/gen_enums.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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.
+
+#include "protos_generator/gen_enums.h"
+
+#include "google/protobuf/descriptor.pb.h"
+#include "google/protobuf/descriptor.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+// Convert enum value to C++ literal.
+//
+// In C++, an value of -2147483648 gets interpreted as the negative of
+// 2147483648, and since 2147483648 can't fit in an integer, this produces a
+// compiler warning.  This works around that issue.
+std::string EnumInt32ToString(int number) {
+  if (number == std::numeric_limits<int32_t>::min()) {
+    // This needs to be special-cased, see explanation here:
+    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661
+    return absl::StrCat(number + 1, " - 1");
+  } else {
+    return absl::StrCat(number);
+  }
+}
+
+std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor) {
+  auto containing_type = enum_descriptor->containing_type();
+  if (containing_type == nullptr) {
+    // enums types with no package name are prefixed with protos_ to prevent
+    // conflicts with generated C headers.
+    if (enum_descriptor->file()->package().empty()) {
+      return absl::StrCat("protos_", ToCIdent(enum_descriptor->name()));
+    }
+    return ToCIdent(enum_descriptor->name());
+  } else {
+    return ToCIdent(
+        absl::StrCat(containing_type->name(), "_", enum_descriptor->name()));
+  }
+}
+
+std::string EnumValueSymbolInNameSpace(
+    const protobuf::EnumDescriptor* desc,
+    const protobuf::EnumValueDescriptor* value) {
+  auto containing_type = desc->containing_type();
+  if (containing_type != nullptr) {
+    return ToCIdent(absl::StrCat(containing_type->name(), "_", desc->name(),
+                                 "_", value->name()));
+  } else {
+    // protos enum values with no package name are prefixed with protos_ to
+    // prevent conflicts with generated C headers.
+    if (desc->file()->package().empty()) {
+      return absl::StrCat("protos_", ToCIdent(value->name()));
+    }
+    return ToCIdent(value->name());
+  }
+}
+
+void WriteEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
+  std::vector<const protobuf::EnumValueDescriptor*> values;
+  auto value_count = desc->value_count();
+  values.reserve(value_count);
+  for (int i = 0; i < value_count; i++) {
+    values.push_back(desc->value(i));
+  }
+  std::sort(values.begin(), values.end(),
+            [](const protobuf::EnumValueDescriptor* a,
+               const protobuf::EnumValueDescriptor* b) {
+              return a->number() < b->number();
+            });
+
+  for (size_t i = 0; i < values.size(); i++) {
+    auto value = values[i];
+    output("  $0", EnumValueSymbolInNameSpace(desc, value));
+    if (value->options().deprecated()) {
+      output(" ABSL_DEPRECATED(\"Proto enum $0\")",
+             EnumValueSymbolInNameSpace(desc, value));
+    }
+    output(" = $0", EnumInt32ToString(value->number()));
+    if (i != values.size() - 1) {
+      output(",");
+    }
+    output("\n");
+  }
+}
+
+void WriteEnumDeclarations(
+    const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
+  for (auto enumdesc : enums) {
+    output("enum $0 : int {\n", EnumTypeName(enumdesc));
+    WriteEnumValues(enumdesc, output);
+    output("};\n\n");
+  }
+}
+
+void WriteHeaderEnumForwardDecls(
+    std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
+  for (const auto* enumdesc : enums) {
+    output("enum $0 : int;\n", EnumTypeName(enumdesc));
+  }
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_enums.h b/protos_generator/gen_enums.h
new file mode 100644
index 0000000..8e7d6ea
--- /dev/null
+++ b/protos_generator/gen_enums.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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_PROTOS_GENERATOR_ENUMS_H_
+#define UPB_PROTOS_GENERATOR_ENUMS_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor);
+void WriteHeaderEnumForwardDecls(
+    std::vector<const protobuf::EnumDescriptor*>& enums, Output& output);
+void WriteEnumDeclarations(
+    const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_ENUMS_H_
diff --git a/protos_generator/gen_extensions.cc b/protos_generator/gen_extensions.cc
new file mode 100644
index 0000000..94a0a12
--- /dev/null
+++ b/protos_generator/gen_extensions.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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.
+
+#include "protos_generator/gen_extensions.h"
+
+#include "absl/strings/str_cat.h"
+#include "protos_generator/gen_utils.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+std::string ExtensionIdentifierBase(const protobuf::FieldDescriptor* ext) {
+  assert(ext->is_extension());
+  std::string ext_scope;
+  if (ext->extension_scope()) {
+    return MessageName(ext->extension_scope());
+  } else {
+    return ToCIdent(ext->file()->package());
+  }
+}
+
+std::string ContainingTypeName(const protobuf::FieldDescriptor* ext) {
+  return ext->containing_type()->file() != ext->file()
+             ? QualifiedClassName(ext->containing_type())
+             : ClassName(ext->containing_type());
+}
+
+void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext,
+                                    Output& output) {
+  std::string mini_table_name =
+      absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext");
+  if (ext->extension_scope()) {
+    output(R"cc(
+             static ::protos::internal::ExtensionIdentifier<$0, $1> $2;
+           )cc",
+           ContainingTypeName(ext), CppTypeParameterName(ext), ext->name());
+  } else {
+    output(
+        R"cc(
+          extern ::protos::internal::ExtensionIdentifier<$0, $1> $2;
+        )cc",
+        ContainingTypeName(ext), CppTypeParameterName(ext), ext->name());
+  }
+}
+
+void WriteExtensionIdentifiersHeader(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output) {
+  for (const auto* ext : extensions) {
+    if (!ext->extension_scope()) {
+      WriteExtensionIdentifierHeader(ext, output);
+    }
+  }
+}
+
+void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext,
+                              Output& output) {
+  std::string mini_table_name =
+      absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext");
+  if (ext->extension_scope()) {
+    output(
+        R"cc(
+          ::protos::internal::ExtensionIdentifier<$0, $3> $4::$2(&$1);
+        )cc",
+        ContainingTypeName(ext), mini_table_name, ext->name(),
+        CppTypeParameterName(ext), ClassName(ext->extension_scope()));
+  } else {
+    output(
+        R"cc(
+          ::protos::internal::ExtensionIdentifier<$0, $3> $2(&$1);
+        )cc",
+        ContainingTypeName(ext), mini_table_name, ext->name(),
+        CppTypeParameterName(ext));
+  }
+}
+
+void WriteExtensionIdentifiers(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output) {
+  for (const auto* ext : extensions) {
+    if (!ext->extension_scope()) {
+      WriteExtensionIdentifier(ext, output);
+    }
+  }
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_extensions.h b/protos_generator/gen_extensions.h
new file mode 100644
index 0000000..7313acb
--- /dev/null
+++ b/protos_generator/gen_extensions.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
+#define UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void WriteExtensionIdentifiersHeader(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output);
+void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext,
+                                    Output& output);
+void WriteExtensionIdentifiers(
+    const std::vector<const protobuf::FieldDescriptor*>& extensions,
+    Output& output);
+void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext,
+                              Output& output);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
diff --git a/protos_generator/gen_messages.cc b/protos_generator/gen_messages.cc
new file mode 100644
index 0000000..7433ba5
--- /dev/null
+++ b/protos_generator/gen_messages.cc
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2009-2021, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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.
+ */
+
+#include "protos_generator/gen_messages.h"
+
+#include "google/protobuf/descriptor.pb.h"
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/gen_accessors.h"
+#include "protos_generator/gen_extensions.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/output.h"
+#include "upbc/common.h"
+#include "upbc/file_layout.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output);
+void WriteModelPublicDeclaration(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+void WriteExtensionIdentifiersInClassHeader(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                Output& output);
+void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output);
+void WriteInternalForwardDeclarationsInHeader(
+    const protobuf::Descriptor* message, Output& output);
+void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
+                                Output& output);
+void WriteExtensionIdentifiersImplementation(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+
+// Writes message class declarations into .upb.proto.h.
+//
+// For each proto Foo, FooAccess and FooProxy/FooCProxy are generated
+// that are exposed to users as Foo , Ptr<Foo> and Ptr<const Foo>.
+void WriteMessageClassDeclarations(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  if (IsMapEntryMessage(descriptor)) {
+    // Skip map entry generation. Low level accessors for maps are
+    // generated that don't require a separate map type.
+    return;
+  }
+
+  // Forward declaration of Proto Class for GCC handling of free friend method.
+  output("class $0;", ClassName(descriptor));
+  output("namespace internal {\n");
+  WriteModelAccessDeclaration(descriptor, output);
+  output("\n");
+  WriteInternalForwardDeclarationsInHeader(descriptor, output);
+  output("\n");
+  output("}  // namespace internal\n");
+  WriteModelPublicDeclaration(descriptor, file_exts, output);
+  output("namespace internal {\n");
+  WriteModelProxyDeclaration(descriptor, output);
+  WriteModelCProxyDeclaration(descriptor, output);
+  output("}  // namespace internal\n");
+}
+
+void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output) {
+  output(
+      R"cc(
+
+        class $0Access {
+         public:
+          $0Access() {}
+          $0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) {}  // NOLINT
+          $0Access(const $1* msg, upb_Arena* arena)
+              : msg_(const_cast<$1*>(msg)), arena_(arena) {}  // NOLINT
+          void* GetInternalArena() const { return arena_; }
+      )cc",
+      ClassName(descriptor), MessageName(descriptor));
+  WriteFieldAccessorsInHeader(descriptor, output);
+  output.Indent();
+  output(
+      R"cc(
+        private:
+        void* msg() const { return msg_; }
+
+        friend class $2;
+        friend class $0Proxy;
+        friend class $0CProxy;
+        friend void* ::protos::internal::GetInternalMsg<$2>(const $2& message);
+        friend void* ::protos::internal::GetInternalMsg<$2>(
+            const ::protos::Ptr<$2>& message);
+        $1* msg_;
+        upb_Arena* arena_;
+      )cc",
+      ClassName(descriptor), MessageName(descriptor),
+      QualifiedClassName(descriptor));
+  output.Outdent();
+  output("};\n");
+}
+
+void WriteModelPublicDeclaration(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  output(
+      R"cc(
+        class $0 final : private internal::$0Access {
+         public:
+          using Access = internal::$0Access;
+          using Proxy = internal::$0Proxy;
+          using CProxy = internal::$0CProxy;
+          $0();
+          $0(const $0& m) = delete;
+          $0& operator=(const $0& m) = delete;
+          $0($0&& m)
+              : Access(m.msg_, m.arena_),
+                owned_arena_(std::move(m.owned_arena_)) {}
+          $0& operator=($0&& m) {
+            msg_ = m.msg_;
+            arena_ = m.arena_;
+            m.msg_ = nullptr;
+            m.arena_ = nullptr;
+            owned_arena_ = std::move(m.owned_arena_);
+            return *this;
+          }
+      )cc",
+      ClassName(descriptor));
+
+  WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output);
+  WriteDefaultInstanceHeader(descriptor, output);
+  WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
+  output.Indent();
+  output.Indent();
+  if (descriptor->extension_range_count()) {
+    // for typetrait checking
+    output("using ExtendableType = $0;\n", ClassName(descriptor));
+  }
+  // Note: free function friends that are templates such as ::protos::Parse
+  // require explicit <$2> type parameter in declaration to be able to compile
+  // with gcc otherwise the compiler will fail with
+  // "has not been declared within namespace" error. Even though there is a
+  // namespace qualifier, cross namespace matching fails.
+  output(
+      R"cc(
+        static const upb_MiniTable* minitable();
+        using $0Access::GetInternalArena;
+
+        private:
+        $0(upb_Message* msg, upb_Arena* arena) : $0Access() {
+          msg_ = ($1*)msg;
+          arena_ = owned_arena_.ptr();
+          upb_Arena_Fuse(arena_, arena);
+        }
+        ::protos::Arena owned_arena_;
+        friend Proxy;
+        friend CProxy;
+        friend absl::StatusOr<$2>(::protos::Parse<$2>(absl::string_view bytes,
+                                                      int options));
+        friend absl::StatusOr<$2>(::protos::Parse<$2>(
+            absl::string_view bytes,
+            const ::protos::ExtensionRegistry& extension_registry,
+            int options));
+        friend upb_Arena* ::protos::internal::GetArena<$0>(const $0& message);
+        friend upb_Arena* ::protos::internal::GetArena<$0>(
+            const ::protos::Ptr<$0>& message);
+        friend $0(::protos::internal::MoveMessage<$0>(upb_Message* msg,
+                                                      upb_Arena* arena));
+      )cc",
+      ClassName(descriptor), MessageName(descriptor),
+      QualifiedClassName(descriptor));
+  output.Outdent();
+  output("};\n\n");
+}
+
+void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                Output& output) {
+  // Foo::Proxy.
+  output(
+      R"cc(
+        class $0Proxy final : private internal::$0Access {
+         public:
+          $0Proxy() = delete;
+          $0Proxy(const $0Proxy& m) : internal::$0Access() {
+            msg_ = m.msg_;
+            arena_ = m.arena_;
+          }
+          $0Proxy operator=(const $0Proxy& m) {
+            msg_ = m.msg_;
+            arena_ = m.arena_;
+            return *this;
+          }
+          using $0Access::GetInternalArena;
+      )cc",
+      ClassName(descriptor));
+
+  WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
+                              output);
+  output("\n");
+  output.Indent(1);
+  output(
+      R"cc(
+        private:
+        $0Proxy(void* msg, upb_Arena* arena) : internal::$0Access(($1*)msg, arena) {}
+        friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena));
+        friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>(
+            upb_Message*, upb_Arena*));
+        friend class $0CProxy;
+        friend class $0Access;
+        friend class ::protos::Ptr<$0>;
+        friend class ::protos::Ptr<const $0>;
+        friend upb_Arena* ::protos::internal::GetArena<$2>(const $2& message);
+        friend upb_Arena* ::protos::internal::GetArena<$2>(
+            const ::protos::Ptr<$2>& message);
+        static void Rebind($0Proxy& lhs, const $0Proxy& rhs) {
+          lhs.msg_ = rhs.msg_;
+          lhs.arena_ = rhs.arena_;
+        }
+      )cc",
+      ClassName(descriptor), MessageName(descriptor),
+      QualifiedClassName(descriptor));
+  output.Outdent(1);
+  output("};\n\n");
+}
+
+void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
+                                 Output& output) {
+  // Foo::CProxy.
+  output(
+      R"cc(
+        class $0CProxy final : private internal::$0Access {
+         public:
+          $0CProxy() = delete;
+          $0CProxy(const $0* m) : internal::$0Access(m->msg_, nullptr) {}
+          using $0Access::GetInternalArena;
+      )cc",
+      ClassName(descriptor), MessageName(descriptor));
+
+  WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
+                              output);
+
+  output.Indent(1);
+  output(
+      R"cc(
+        private:
+        $0CProxy(void* msg) : internal::$0Access(($1*)msg, nullptr){};
+        friend $0::CProxy(::protos::internal::CreateMessage<$0>(upb_Message* msg));
+        friend class ::protos::Ptr<$0>;
+        friend class ::protos::Ptr<const $0>;
+        static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {
+          lhs.msg_ = rhs.msg_;
+          lhs.arena_ = rhs.arena_;
+        }
+      )cc",
+      ClassName(descriptor), MessageName(descriptor));
+  output.Outdent(1);
+  output("};\n\n");
+}
+
+void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
+                                Output& output) {
+  output("  static ::protos::Ptr<const $0> default_instance();\n",
+         ClassName(message));
+}
+
+void WriteMessageImplementation(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  bool message_is_map_entry = descriptor->options().map_entry();
+  if (!message_is_map_entry) {
+    // Constructor.
+    output(
+        R"cc(
+          $0::$0() : $0Access() {
+            arena_ = owned_arena_.ptr();
+            msg_ = $1_new(arena_);
+          }
+        )cc",
+        ClassName(descriptor), MessageName(descriptor));
+    // Minitable
+    OutputIndenter i(output);
+    output(
+        R"cc(
+          const upb_MiniTable* $0::minitable() { return &$1; }
+        )cc",
+        ClassName(descriptor), ::upbc::MessageInit(descriptor));
+  }
+
+  ::upbc::FileLayout layout(descriptor->file());
+  WriteAccessorsInSource(descriptor, layout, output);
+
+  if (!message_is_map_entry) {
+    output(
+        R"cc(
+          struct $0DefaultTypeInternal {
+            $1* msg;
+          };
+          $0DefaultTypeInternal _$0_default_instance_ =
+              $0DefaultTypeInternal{$1_new(upb_Arena_New())};
+        )cc",
+        ClassName(descriptor), MessageName(descriptor));
+
+    output(
+        R"cc(
+          ::protos::Ptr<const $0> $0::default_instance() {
+            return ::protos::internal::CreateMessage<$0>(
+                (upb_Message *)_$0_default_instance_.msg);
+          }
+        )cc",
+        ClassName(descriptor));
+  }
+}
+
+void WriteInternalForwardDeclarationsInHeader(
+    const protobuf::Descriptor* message, Output& output) {
+  // Write declaration for internal re-usable default_instance without
+  // leaking implementation.
+  output(
+      R"cc(
+        struct $0DefaultTypeInternal;
+        extern $0DefaultTypeInternal _$0_default_instance_;
+      )cc",
+      ClassName(message));
+}
+
+void WriteExtensionIdentifiersInClassHeader(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  for (auto* ext : file_exts) {
+    if (ext->extension_scope() &&
+        ext->extension_scope()->full_name() == message->full_name()) {
+      WriteExtensionIdentifierHeader(ext, output);
+    }
+  }
+}
+
+void WriteExtensionIdentifiersImplementation(
+    const protobuf::Descriptor* message,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  for (auto* ext : file_exts) {
+    if (ext->extension_scope() &&
+        ext->extension_scope()->full_name() == message->full_name()) {
+      WriteExtensionIdentifier(ext, output);
+    }
+  }
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_messages.h b/protos_generator/gen_messages.h
new file mode 100644
index 0000000..0b8aa95
--- /dev/null
+++ b/protos_generator/gen_messages.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2009-2021, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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_PROTOS_GENERATOR_GEN_MESSAGES_H_
+#define UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
+
+#include "google/protobuf/descriptor.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+namespace protobuf = ::google::protobuf;
+
+void WriteMessageClassDeclarations(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+void WriteMessageImplementation(
+    const protobuf::Descriptor* descriptor,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
diff --git a/protos_generator/gen_utils.cc b/protos_generator/gen_utils.cc
new file mode 100644
index 0000000..bd8a2b3
--- /dev/null
+++ b/protos_generator/gen_utils.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009-2021, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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.
+ */
+
+#include "protos_generator/gen_utils.h"
+
+#include <string>
+
+#include "absl/strings/str_cat.h"
+// begin:google_only
+// #include "absl/log/check.h"
+// #include "absl/strings/str_replace.h"
+// end:google_only
+#include "absl/strings/str_split.h"
+#include "upbc/keywords.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+// begin:github_only
+#ifndef DCHECK
+#ifndef NDEBUG
+#define DCHECK(condition) if(!(condition)) LOG(FATAL);
+#else
+#define DCHECK(condition) if(!(true || condition)) LOG(FATAL);
+#endif
+#endif
+// end:github_only
+
+std::string DotsToColons(const std::string& name) {
+  return absl::StrReplaceAll(name, {{".", "::"}});
+}
+
+std::string Namespace(const std::string& package) {
+  if (package.empty()) return "";
+  return "::" + DotsToColons(package);
+}
+
+// Return the qualified C++ name for a file level symbol.
+std::string QualifiedFileLevelSymbol(const protobuf::FileDescriptor* file,
+                                     const std::string& name) {
+  if (file->package().empty()) {
+    return absl::StrCat("::", name);
+  }
+  // Append ::protos postfix to package name.
+  return absl::StrCat(Namespace(file->package()), "::protos::", name);
+}
+
+std::string ClassName(const protobuf::Descriptor* descriptor) {
+  const protobuf::Descriptor* parent = descriptor->containing_type();
+  std::string res;
+  if (parent) res += ClassName(parent) + "_";
+  absl::StrAppend(&res, descriptor->name());
+  return ::upbc::ResolveKeywordConflict(res);
+}
+
+std::string QualifiedClassName(const protobuf::Descriptor* descriptor) {
+  return QualifiedFileLevelSymbol(descriptor->file(), ClassName(descriptor));
+}
+
+std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor) {
+  return QualifiedFileLevelSymbol(
+      descriptor->file(), absl::StrCat("internal::", ClassName(descriptor)));
+}
+
+std::string CppSourceFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.proto.cc";
+}
+
+std::string UpbCFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.h";
+}
+
+std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.fwd.h";
+}
+
+std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.proto.h";
+}
+
+std::string NamespaceFromPackageName(absl::string_view package_name) {
+  return absl::StrCat(absl::StrReplaceAll(package_name, {{".", "::"}}),
+                      "::protos");
+}
+
+void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output) {
+  // Skip namespace generation if package name is not specified.
+  if (file->package().empty()) {
+    return;
+  }
+
+  output("namespace $0 {\n\n", NamespaceFromPackageName(file->package()));
+}
+
+void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output) {
+  if (file->package().empty()) {
+    return;
+  }
+  output("} //  namespace $0\n\n", NamespaceFromPackageName(file->package()));
+}
+
+std::string CppTypeInternal(const protobuf::FieldDescriptor* field,
+                            bool is_const, bool is_type_parameter) {
+  std::string maybe_const = is_const ? "const " : "";
+  switch (field->cpp_type()) {
+    case protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
+      if (is_type_parameter) {
+        return absl::StrCat(maybe_const,
+                            QualifiedClassName(field->message_type()));
+      } else {
+        return absl::StrCat(maybe_const,
+                            QualifiedClassName(field->message_type()), "*");
+      }
+    }
+    case protobuf::FieldDescriptor::CPPTYPE_BOOL:
+      return "bool";
+    case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
+      return "float";
+    case protobuf::FieldDescriptor::CPPTYPE_INT32:
+    case protobuf::FieldDescriptor::CPPTYPE_ENUM:
+      return "int32_t";
+    case protobuf::FieldDescriptor::CPPTYPE_UINT32:
+      return "uint32_t";
+    case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
+      return "double";
+    case protobuf::FieldDescriptor::CPPTYPE_INT64:
+      return "int64_t";
+    case protobuf::FieldDescriptor::CPPTYPE_UINT64:
+      return "uint64_t";
+    case protobuf::FieldDescriptor::CPPTYPE_STRING:
+      return "absl::string_view";
+    default:
+      LOG(FATAL) << "Unexpected type: " << field->cpp_type();
+  }
+}
+
+std::string CppConstType(const protobuf::FieldDescriptor* field) {
+  return CppTypeInternal(field, /* is_const= */ true,
+                         /* is_type_parameter= */ false);
+}
+
+std::string CppTypeParameterName(const protobuf::FieldDescriptor* field) {
+  return CppTypeInternal(field, /* is_const= */ false,
+                         /* is_type_parameter= */ true);
+}
+
+std::string MessageBaseType(const protobuf::FieldDescriptor* field,
+                            bool is_const) {
+  DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return maybe_const + QualifiedClassName(field->message_type());
+}
+
+std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
+                                bool is_const) {
+  DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return "::protos::Ptr<" + maybe_const +
+         QualifiedClassName(field->message_type()) + ">";
+}
+
+std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
+                              bool is_const) {
+  DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return maybe_const + QualifiedInternalClassName(field->message_type()) +
+         "CProxy";
+}
+
+std::string MessageProxyType(const protobuf::FieldDescriptor* field,
+                             bool is_const) {
+  DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
+  std::string maybe_const = is_const ? "const " : "";
+  return maybe_const + QualifiedInternalClassName(field->message_type()) +
+         "Proxy";
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/gen_utils.h b/protos_generator/gen_utils.h
new file mode 100644
index 0000000..02ab8b5
--- /dev/null
+++ b/protos_generator/gen_utils.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2021, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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_PROTOS_GENERATOR_GEN_UTILS_H_
+#define UPB_PROTOS_GENERATOR_GEN_UTILS_H_
+
+#include "google/protobuf/compiler/code_generator.h"
+#include "google/protobuf/compiler/plugin.h"
+#include "google/protobuf/descriptor.pb.h"
+#include "google/protobuf/descriptor.h"
+#include "google/protobuf/wire_format.h"
+#include "absl/container/flat_hash_map.h"
+#include "protos_generator/output.h"
+
+namespace protos_generator {
+
+namespace protobuf = ::google::protobuf;
+
+enum class MessageClassType {
+  kMessage,
+  kMessageCProxy,
+  kMessageProxy,
+  kMessageAccess,
+};
+
+std::string ClassName(const protobuf::Descriptor* descriptor);
+std::string QualifiedClassName(const protobuf::Descriptor* descriptor);
+std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor);
+
+inline bool IsMapEntryMessage(const protobuf::Descriptor* descriptor) {
+  return descriptor->options().map_entry();
+}
+std::string CppSourceFilename(const google::protobuf::FileDescriptor* file);
+std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file);
+std::string UpbCFilename(const google::protobuf::FileDescriptor* file);
+std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file);
+
+void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output);
+void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output);
+
+std::string CppConstType(const protobuf::FieldDescriptor* field);
+std::string CppTypeParameterName(const protobuf::FieldDescriptor* field);
+
+std::string MessageBaseType(const protobuf::FieldDescriptor* field,
+                            bool is_const);
+// Generate protos::Ptr<const Model> to be used in accessors as public
+// signatures.
+std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
+                                bool is_const);
+std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
+                              bool is_const);
+std::string MessageProxyType(const protobuf::FieldDescriptor* field,
+                             bool is_const);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_GEN_UTILS_H_
diff --git a/protos_generator/output.cc b/protos_generator/output.cc
new file mode 100644
index 0000000..e5a9b79
--- /dev/null
+++ b/protos_generator/output.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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.
+
+#include "protos_generator/output.h"
+
+#include "absl/strings/str_replace.h"
+
+namespace protos_generator {
+namespace {
+
+namespace protobuf = ::google::protobuf;
+
+}  // 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(
+      R"cc(
+        /* This file was generated by protos_generator (the upb C++ compiler) "
+        from the input
+         * file:
+         *
+         *     $0
+         *
+         * Do not edit -- your changes will be discarded when the file is
+         * regenerated. */
+      )cc",
+      file->name());
+  output("\n");
+}
+
+std::string MessageName(const protobuf::Descriptor* descriptor) {
+  return ToCIdent(descriptor->full_name());
+}
+
+std::string FileLayoutName(const google::protobuf::FileDescriptor* file) {
+  return ToCIdent(file->name()) + "_upb_file_layout";
+}
+
+std::string CHeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.h";
+}
+
+std::string CSourceFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.c";
+}
+
+}  // namespace protos_generator
diff --git a/protos_generator/output.h b/protos_generator/output.h
new file mode 100644
index 0000000..fa6c683
--- /dev/null
+++ b/protos_generator/output.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2021, Google LLC
+ * All rights reserved.
+ *
+ * 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 Google LLC 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_PROTOS_GENERATOR_OUTPUT_H
+#define UPB_PROTOS_GENERATOR_OUTPUT_H
+
+#include <vector>
+
+#include "google/protobuf/io/zero_copy_stream.h"
+#include "google/protobuf/descriptor.h"
+#include "absl/base/log_severity.h"
+#include "absl/log/log.h"
+#include "absl/strings/str_replace.h"
+#include "absl/strings/substitute.h"
+
+namespace protos_generator {
+
+class Output {
+ public:
+  Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {}
+  ~Output() { stream_->BackUp((int)buffer_size_); }
+
+  template <class... Arg>
+  void operator()(absl::string_view format, const Arg&... arg) {
+    Write(absl::Substitute(format, arg...));
+  }
+
+  // Indentation size in characters.
+  static constexpr size_t kIndentationSize = 2;
+
+  void Indent() { Indent(kIndentationSize); }
+  void Indent(size_t size) { indent_ += size; }
+
+  void Outdent() { Outdent(kIndentationSize); }
+  void Outdent(size_t size) {
+    if (indent_ < size) {
+      LOG(FATAL) << "mismatched Output indent/unindent calls";
+    }
+    indent_ -= size;
+  }
+
+ private:
+  void Write(absl::string_view data) {
+    std::string stripped;
+    if (absl::StartsWith(data, "\n ")) {
+      size_t indent = data.substr(1).find_first_not_of(' ');
+      if (indent > indent_) {
+        indent -= indent_;
+      }
+      if (indent != absl::string_view::npos) {
+        // Remove indentation from all lines.
+        auto line_prefix = data.substr(0, indent + 1);
+        // The final line has an extra newline and is indented two less, eg.
+        //    R"cc(
+        //      UPB_INLINE $0 $1_$2(const $1 *msg) {
+        //        return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4;
+        //      }
+        //    )cc",
+        std::string last_line_prefix = std::string(line_prefix);
+        last_line_prefix.resize(last_line_prefix.size() - 2);
+        data.remove_prefix(line_prefix.size());
+        stripped = absl::StrReplaceAll(
+            data, {{line_prefix, "\n"}, {last_line_prefix, "\n"}});
+        data = stripped;
+      }
+    } else {
+      WriteIndent();
+    }
+    WriteRaw(data);
+  }
+
+  void WriteRaw(absl::string_view data) {
+    while (!data.empty()) {
+      RefreshOutput();
+      size_t to_write = std::min(data.size(), buffer_size_);
+      memcpy(output_buffer_, data.data(), to_write);
+      data.remove_prefix(to_write);
+      output_buffer_ += to_write;
+      buffer_size_ -= to_write;
+    }
+  }
+
+  void WriteIndent() {
+    if (indent_ == 0) {
+      return;
+    }
+    size_t size = indent_;
+    while (size > buffer_size_) {
+      if (buffer_size_ > 0) {
+        memset(output_buffer_, ' ', buffer_size_);
+      }
+      size -= buffer_size_;
+      buffer_size_ = 0;
+      RefreshOutput();
+    }
+    memset(output_buffer_, ' ', size);
+    output_buffer_ += size;
+    buffer_size_ -= size;
+  }
+
+  void RefreshOutput() {
+    while (buffer_size_ == 0) {
+      void* void_buffer;
+      int size;
+      if (!stream_->Next(&void_buffer, &size)) {
+        fprintf(stderr, "upbc: Failed to write to to output\n");
+        abort();
+      }
+      output_buffer_ = static_cast<char*>(void_buffer);
+      buffer_size_ = size;
+    }
+  }
+
+  google::protobuf::io::ZeroCopyOutputStream* stream_;
+  char* output_buffer_ = nullptr;
+  size_t buffer_size_ = 0;
+  // Current indentation size in characters.
+  size_t indent_ = 0;
+  friend class OutputIndenter;
+};
+
+class OutputIndenter {
+ public:
+  OutputIndenter(Output& output)
+      : OutputIndenter(output, Output::kIndentationSize) {}
+  OutputIndenter(Output& output, size_t indent_size)
+      : indent_size_(indent_size), output_(output) {
+    output.Indent(indent_size);
+  }
+  ~OutputIndenter() { output_.Outdent(indent_size_); }
+
+ private:
+  size_t indent_size_;
+  Output& output_;
+};
+
+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::string MessageName(const google::protobuf::Descriptor* descriptor);
+std::string FileLayoutName(const google::protobuf::FileDescriptor* file);
+std::string CHeaderFilename(const google::protobuf::FileDescriptor* file);
+std::string CSourceFilename(const google::protobuf::FileDescriptor* file);
+
+}  // namespace protos_generator
+
+#endif  // UPB_PROTOS_GENERATOR_OUTPUT_H
diff --git a/protos_generator/protoc-gen-upb-protos.cc b/protos_generator/protoc-gen-upb-protos.cc
new file mode 100644
index 0000000..f9522f7
--- /dev/null
+++ b/protos_generator/protoc-gen-upb-protos.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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.
+
+#include <memory>
+
+#include "google/protobuf/compiler/code_generator.h"
+#include "google/protobuf/compiler/plugin.h"
+#include "google/protobuf/descriptor.pb.h"
+#include "protos_generator/gen_enums.h"
+#include "protos_generator/gen_extensions.h"
+#include "protos_generator/gen_messages.h"
+#include "protos_generator/gen_utils.h"
+#include "protos_generator/output.h"
+#include "upbc/file_layout.h"
+
+namespace protos_generator {
+namespace {
+
+namespace protoc = ::google::protobuf::compiler;
+namespace protobuf = ::google::protobuf;
+using FileDescriptor = ::google::protobuf::FileDescriptor;
+using FileLayout = ::upbc::FileLayout;
+
+void WriteSource(const FileLayout& layout, Output& output,
+                 bool fasttable_enabled);
+void WriteHeader(const FileLayout& layout, Output& output);
+void WriteForwardingHeader(const FileLayout& layout, Output& output);
+void WriteMessageImplementations(const protobuf::FileDescriptor* file,
+                                 Output& output);
+void WriteTypedefForwardingHeader(
+    const protobuf::FileDescriptor* file,
+    const std::vector<const protobuf::Descriptor*>& file_messages,
+    Output& output);
+void WriteHeaderMessageForwardDecls(
+    const protobuf::FileDescriptor* file,
+    const std::vector<const protobuf::Descriptor*>& file_messages,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output);
+
+class Generator : public protoc::CodeGenerator {
+ public:
+  ~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 {
+  bool fasttable_enabled = false;
+  std::vector<std::pair<std::string, std::string>> params;
+  google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
+
+  for (const auto& pair : params) {
+    if (pair.first == "fasttable") {
+      fasttable_enabled = true;
+    } else {
+      *error = "Unknown parameter: " + pair.first;
+      return false;
+    }
+  }
+
+  FileLayout layout(file);
+
+  // Write model.upb.fwd.h
+  Output forwarding_header_output(
+      context->Open(ForwardingHeaderFilename(file)));
+  WriteForwardingHeader(layout, forwarding_header_output);
+  // Write model.upb.proto.h
+  Output header_output(context->Open(CppHeaderFilename(file)));
+  WriteHeader(layout, header_output);
+  // Write model.upb.proto.cc
+  Output cc_output(context->Open(CppSourceFilename(file)));
+  WriteSource(layout, cc_output, fasttable_enabled);
+  return true;
+}
+
+// The forwarding header defines Access/Proxy/CProxy for message classes
+// used to include when referencing dependencies to prevent transitive
+// dependency headers from being included.
+void WriteForwardingHeader(const FileLayout& layout, Output& output) {
+  const protobuf::FileDescriptor* file = layout.descriptor();
+  EmitFileWarning(file, output);
+  output(
+      R"cc(
+#ifndef $0_UPB_FWD_H_
+#define $0_UPB_FWD_H_
+      )cc",
+      ToPreproc(file->name()));
+  output("\n");
+  const std::vector<const protobuf::Descriptor*> this_file_messages =
+      ::upbc::SortedMessages(file);
+  WriteTypedefForwardingHeader(file, this_file_messages, output);
+  output("#endif  /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name()));
+}
+
+void WriteHeader(const FileLayout& layout, Output& output) {
+  const protobuf::FileDescriptor* file = layout.descriptor();
+  EmitFileWarning(file, output);
+  output(
+      R"cc(
+#ifndef $0_UPB_PROTO_H_
+#define $0_UPB_PROTO_H_
+
+#include "protos/protos.h"
+#include "protos/protos_internal.h"
+#include "upb/upb.hpp"
+
+#include "absl/strings/string_view.h"
+#include "absl/status/statusor.h"
+#include "upb/msg_internal.h"
+      )cc",
+      ToPreproc(file->name()));
+
+  // Import headers for proto public dependencies.
+  for (int i = 0; i < file->public_dependency_count(); i++) {
+    if (i == 0) {
+      output("// Public Imports.\n");
+    }
+    output("#include \"$0\"\n", CppHeaderFilename(file->public_dependency(i)));
+    if (i == file->public_dependency_count() - 1) {
+      output("\n");
+    }
+  }
+
+  output("#include \"upb/port_def.inc\"\n");
+
+  const std::vector<const protobuf::Descriptor*> this_file_messages =
+      ::upbc::SortedMessages(file);
+  const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
+      ::upbc::SortedExtensions(file);
+
+  if (!this_file_messages.empty()) {
+    output("\n");
+  }
+
+  WriteHeaderMessageForwardDecls(file, this_file_messages, this_file_exts,
+                                 output);
+  WriteStartNamespace(file, output);
+
+  std::vector<const protobuf::EnumDescriptor*> this_file_enums =
+      ::upbc::SortedEnums(file);
+
+  // Write Class and Enums.
+  WriteEnumDeclarations(this_file_enums, output);
+  output("\n");
+
+  for (auto message : this_file_messages) {
+    WriteMessageClassDeclarations(message, this_file_exts, output);
+  }
+  output("\n");
+
+  WriteExtensionIdentifiersHeader(this_file_exts, output);
+  output("\n");
+
+  WriteEndNamespace(file, output);
+
+  output("\n#include \"upb/port_undef.inc\"\n\n");
+  // End of "C" section.
+
+  output("#endif  /* $0_UPB_PROTO_H_ */\n", ToPreproc(file->name()));
+}
+
+// Writes a .upb.cc source file.
+void WriteSource(const FileLayout& layout, Output& output,
+                 bool fasttable_enabled) {
+  const protobuf::FileDescriptor* file = layout.descriptor();
+  EmitFileWarning(file, output);
+
+  output(
+      R"cc(
+#include <stddef.h>
+#include "absl/strings/string_view.h"
+#include "upb/msg_internal.h"
+#include "protos/protos.h"
+#include "$0"
+      )cc",
+      CppHeaderFilename(file));
+
+  for (int i = 0; i < file->dependency_count(); i++) {
+    output("#include \"$0\"\n", CppHeaderFilename(file->dependency(i)));
+  }
+  output("#include \"upb/port_def.inc\"\n");
+
+  WriteStartNamespace(file, output);
+  WriteMessageImplementations(file, output);
+  const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
+      ::upbc::SortedExtensions(file);
+  WriteExtensionIdentifiers(this_file_exts, output);
+  WriteEndNamespace(file, output);
+
+  output("#include \"upb/port_undef.inc\"\n\n");
+}
+
+void WriteMessageImplementations(const protobuf::FileDescriptor* file,
+                                 Output& output) {
+  const std::vector<const protobuf::FieldDescriptor*> file_exts =
+      ::upbc::SortedExtensions(file);
+  const std::vector<const protobuf::Descriptor*> this_file_messages =
+      ::upbc::SortedMessages(file);
+  for (auto message : this_file_messages) {
+    WriteMessageImplementation(message, file_exts, output);
+  }
+}
+
+void WriteTypedefForwardingHeader(
+    const protobuf::FileDescriptor* file,
+    const std::vector<const protobuf::Descriptor*>& file_messages,
+    Output& output) {
+  WriteStartNamespace(file, output);
+
+  // Forward-declare types defined in this file.
+  for (auto message : file_messages) {
+    output(
+        R"cc(
+          class $0;
+          namespace internal {
+          class $0Access;
+          class $0Proxy;
+          class $0CProxy;
+          }  // namespace internal
+        )cc",
+        ClassName(message));
+  }
+  output("\n");
+  WriteEndNamespace(file, output);
+}
+
+/// Writes includes for upb C minitables and fwd.h for transitive typedefs.
+void WriteHeaderMessageForwardDecls(
+    const protobuf::FileDescriptor* file,
+    const std::vector<const protobuf::Descriptor*>& file_messages,
+    const std::vector<const protobuf::FieldDescriptor*>& file_exts,
+    Output& output) {
+  // Import forward-declaration of types defined in this file.
+  output("#include \"$0\"\n", UpbCFilename(file));
+  output("#include \"$0\"\n", ForwardingHeaderFilename(file));
+  // Forward-declare types not in this file, but used as submessages.
+  // Order by full name for consistent ordering.
+  std::map<std::string, const protobuf::Descriptor*> forward_messages;
+
+  for (auto* message : file_messages) {
+    for (int i = 0; i < message->field_count(); i++) {
+      const protobuf::FieldDescriptor* field = message->field(i);
+      if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
+          field->file() != field->message_type()->file()) {
+        forward_messages[field->message_type()->full_name()] =
+            field->message_type();
+      }
+    }
+  }
+  for (auto* ext : file_exts) {
+    if (ext->file() != ext->containing_type()->file()) {
+      forward_messages[ext->containing_type()->full_name()] =
+          ext->containing_type();
+      if (ext->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+        forward_messages[ext->message_type()->full_name()] =
+            ext->message_type();
+      }
+    }
+  }
+  std::map<std::string, const protobuf::FileDescriptor*> files_to_import;
+  for (const auto& pair : forward_messages) {
+    files_to_import[ForwardingHeaderFilename(pair.second->file())] = file;
+  }
+  for (const auto& pair : files_to_import) {
+    output("#include \"$0\"\n", UpbCFilename(pair.second));
+    output("#include \"$0\"\n", pair.first);
+  }
+  output("\n");
+}
+
+}  // namespace
+}  // namespace protos_generator
+
+int main(int argc, char** argv) {
+  protos_generator::Generator generator_cc;
+  return google::protobuf::compiler::PluginMain(argc, argv, &generator_cc);
+}
diff --git a/protos_generator/tests/BUILD b/protos_generator/tests/BUILD
new file mode 100644
index 0000000..4151ba7
--- /dev/null
+++ b/protos_generator/tests/BUILD
@@ -0,0 +1,118 @@
+# Copyright (c) 2009-2021, Google LLC
+# All rights reserved.
+#
+# 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 Google LLC 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.
+
+load(
+    "//bazel:build_defs.bzl",
+    "UPB_DEFAULT_CPPOPTS",
+)
+load(
+    "//protos/bazel:upb_cc_proto_library.bzl",
+    "upb_cc_proto_library",
+)
+load(
+    "//bazel:upb_proto_library.bzl",
+    "upb_proto_library",
+)
+load(
+    "@rules_cc//cc:defs.bzl",
+    "cc_proto_library",
+)
+
+licenses(["notice"])
+
+proto_library(
+    name = "test_model_proto",
+    srcs = [
+        "child_model.proto",
+        "test_enum.proto",
+        "test_extension.proto",
+        "test_model.proto",
+    ],
+)
+
+proto_library(
+    name = "no_package_proto",
+    srcs = [
+        "no_package.proto",
+    ],
+)
+
+upb_proto_library(
+    name = "test_model_upb_proto",
+    visibility = [
+        "//protos:__pkg__",
+    ],
+    deps = [":test_model_proto"],
+)
+
+upb_cc_proto_library(
+    name = "test_model_upb_cc_proto",
+    visibility = [
+        "//protos:__pkg__",
+        "//protos_generator/nc_tests:__pkg__",
+    ],
+    deps = [":test_model_proto"],
+)
+
+upb_cc_proto_library(
+    name = "no_package_upb_cc_proto",
+    deps = [":no_package_proto"],
+)
+
+cc_proto_library(
+    name = "test_model_cc_proto",
+    deps = [":test_model_proto"],
+)
+
+# begin:google_only
+# proto_library(
+#     name = "legacy_name_proto",
+#     srcs = [
+#         "legacy-name.proto",
+#     ],
+# )
+#
+# upb_cc_proto_library(
+#     name = "legacy_name_test_proto",
+#     visibility = ["//protos_generator/nc_tests:__pkg__"],
+#     deps = [":legacy_name_proto"],
+# )
+# end:google_only
+
+cc_test(
+    name = "test_generated_cc_code",
+    srcs = ["test_generated.cc"],
+    copts = UPB_DEFAULT_CPPOPTS,
+    deps = [
+        # begin:google_only
+#         ":legacy_name_test_proto",
+        # end:google_only
+        ":no_package_upb_cc_proto",
+        ":test_model_upb_cc_proto",
+        ":test_model_upb_proto",
+        "@com_google_googletest//:gtest_main",
+        "//:upb",
+    ],
+)
diff --git a/protos_generator/tests/child_model.proto b/protos_generator/tests/child_model.proto
new file mode 100644
index 0000000..05d0c41
--- /dev/null
+++ b/protos_generator/tests/child_model.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+package protos_generator.test;
+
+import public "protos_generator/tests/test_enum.proto";
+
+message ChildModel1 {
+  optional bool child_b1 = 44;
+  optional string child_str1 = 56;
+}
+
+message ChildModel3 {
+  string sub_key = 1;
+  bool bool1 = 2;
+  int32 i32 = 3;
+  optional string opt_str = 4;
+  optional bool opt_bool = 5;
+  optional int32 opt_i32 = 6;
+}
diff --git a/protos_generator/tests/legacy-name.proto b/protos_generator/tests/legacy-name.proto
new file mode 100644
index 0000000..8685219
--- /dev/null
+++ b/protos_generator/tests/legacy-name.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package protos_generator.test;
+
+// option java_multiple_files = true;
+
+enum LegacyEnum {
+  PHASE_DEFAULT = 0;
+  PHASE_BUSY = 1;
+}
diff --git a/protos_generator/tests/no_package.proto b/protos_generator/tests/no_package.proto
new file mode 100644
index 0000000..a52376b
--- /dev/null
+++ b/protos_generator/tests/no_package.proto
@@ -0,0 +1,8 @@
+syntax = "proto2";
+
+// option java_multiple_files = true;
+
+enum EnumWithNoPackage {
+  CELSIUS = 1;
+  FAHRENHEIT = 2;
+}
diff --git a/protos_generator/tests/test_enum.proto b/protos_generator/tests/test_enum.proto
new file mode 100644
index 0000000..c5354cc
--- /dev/null
+++ b/protos_generator/tests/test_enum.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package protos_generator.test;
+
+enum TestEnum {
+  DEVICE_UNKNOWN = 0;
+  DEVICE_KEYBOARD = 1;
+  DEVICE_MOUSE = 2;
+  DEVICE_MONITOR = 3;
+}
diff --git a/protos_generator/tests/test_extension.proto b/protos_generator/tests/test_extension.proto
new file mode 100644
index 0000000..e720773
--- /dev/null
+++ b/protos_generator/tests/test_extension.proto
@@ -0,0 +1,12 @@
+syntax = "proto2";
+
+package protos_generator.test.someotherpackage;
+
+import "protos_generator/tests/test_model.proto";
+
+// Define extension that is extending proto outside this package with a type
+// defined in different file.
+
+extend TestModel {
+  optional ThemeExtension styling = 13001;
+}
diff --git a/protos_generator/tests/test_generated.cc b/protos_generator/tests/test_generated.cc
new file mode 100644
index 0000000..f9fcea5
--- /dev/null
+++ b/protos_generator/tests/test_generated.cc
@@ -0,0 +1,582 @@
+// Copyright (c) 2009-2021, Google LLC
+// All rights reserved.
+//
+// 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 Google LLC 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.
+
+#include <limits>
+
+#include "gtest/gtest.h"
+#include "protos_generator/tests/child_model.upb.proto.h"
+#include "protos_generator/tests/no_package.upb.proto.h"
+#include "protos_generator/tests/test_model.upb.proto.h"
+#include "upb/string_view.h"
+
+using ::protos_generator::test::protos::ChildModel1;
+using ::protos_generator::test::protos::other_ext;
+using ::protos_generator::test::protos::RED;
+using ::protos_generator::test::protos::TestEnum;
+using ::protos_generator::test::protos::TestModel;
+using ::protos_generator::test::protos::TestModel_Category_IMAGES;
+using ::protos_generator::test::protos::TestModel_Category_NEWS;
+using ::protos_generator::test::protos::TestModel_Category_VIDEO;
+using ::protos_generator::test::protos::theme;
+using ::protos_generator::test::protos::ThemeExtension;
+
+TEST(CppGeneratedCode, Constructor) { TestModel test_model; }
+
+TEST(CppGeneratedCode, MessageEnum) { EXPECT_EQ(5, TestModel_Category_IMAGES); }
+
+TEST(CppGeneratedCode, ImportedEnum) { EXPECT_EQ(3, TestEnum::DEVICE_MONITOR); }
+
+TEST(CppGeneratedCode, Enum) { EXPECT_EQ(1, RED); }
+
+TEST(CppGeneratedCode, EnumNoPackage) { EXPECT_EQ(1, ::protos_CELSIUS); }
+
+TEST(CppGeneratedCode, ArenaConstructor) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(false, testModel.has_b1());
+}
+
+TEST(CppGeneratedCode, Booleans) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_FALSE(testModel.b1());
+  testModel.set_b1(true);
+  EXPECT_TRUE(testModel.b1());
+  testModel.set_b1(false);
+  EXPECT_FALSE(testModel.b1());
+  testModel.set_b1(true);
+  EXPECT_TRUE(testModel.b1());
+  testModel.clear_b1();
+  EXPECT_FALSE(testModel.has_b1());
+}
+
+TEST(CppGeneratedCode, ScalarInt32) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test int32 defaults.
+  EXPECT_EQ(testModel.value(), 0);
+  EXPECT_FALSE(testModel.has_value());
+  // Floating point defautls.
+  EXPECT_EQ(std::numeric_limits<float>::infinity(),
+            testModel.float_value_with_default());
+  EXPECT_EQ(-std::numeric_limits<double>::infinity(),
+            testModel.double_value_with_default());
+
+  // Set value.
+  testModel.set_value(5);
+  EXPECT_TRUE(testModel.has_value());
+  EXPECT_EQ(testModel.value(), 5);
+  // Change value.
+  testModel.set_value(10);
+  EXPECT_TRUE(testModel.has_value());
+  EXPECT_EQ(testModel.value(), 10);
+  // Clear value.
+  testModel.clear_value();
+  EXPECT_FALSE(testModel.has_value());
+  EXPECT_EQ(testModel.value(), 0);
+}
+
+const char kTestStr1[] = "abcdefg";
+const char kTestStr2[] = "just another test string";
+
+TEST(CppGeneratedCode, Strings) {
+  TestModel testModel;
+  testModel.set_str1(kTestStr1);
+  testModel.set_str2(kTestStr2);
+  EXPECT_EQ(testModel.str1(), kTestStr1);
+  EXPECT_EQ(testModel.str2(), kTestStr2);
+  EXPECT_TRUE(testModel.has_str1());
+  EXPECT_TRUE(testModel.has_str2());
+
+  testModel.clear_str1();
+  EXPECT_FALSE(testModel.has_str1());
+  EXPECT_TRUE(testModel.has_str2());
+}
+
+TEST(CppGeneratedCode, ScalarUInt32) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_uint32(), 0);
+  EXPECT_FALSE(testModel.has_optional_uint32());
+  // Set value.
+  testModel.set_optional_uint32(0xA0001000);
+  EXPECT_TRUE(testModel.has_optional_uint32());
+  EXPECT_EQ(testModel.optional_uint32(), 0xA0001000);
+  // Change value.
+  testModel.set_optional_uint32(0x70002000);
+  EXPECT_TRUE(testModel.has_optional_uint32());
+  EXPECT_EQ(testModel.optional_uint32(), 0x70002000);
+  // Clear value.
+  testModel.clear_optional_uint32();
+  EXPECT_FALSE(testModel.has_optional_uint32());
+  EXPECT_EQ(testModel.optional_uint32(), 0);
+}
+
+TEST(CppGeneratedCode, ScalarInt64) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_int64(), 0);
+  EXPECT_FALSE(testModel.has_optional_int64());
+  // Set value.
+  testModel.set_optional_int64(0xFF00CCDDA0001000);
+  EXPECT_TRUE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDDA0001000);
+  // Change value.
+  testModel.set_optional_int64(0xFF00CCDD70002000);
+  EXPECT_TRUE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDD70002000);
+  // Clear value.
+  testModel.clear_optional_int64();
+  EXPECT_FALSE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0);
+  // Set after clear.
+  testModel.set_optional_int64(0xFF00CCDDA0001000);
+  EXPECT_TRUE(testModel.has_optional_int64());
+  EXPECT_EQ(testModel.optional_int64(), 0xFF00CCDDA0001000);
+}
+
+TEST(CppGeneratedCode, ScalarFloat) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_float(), 0.0f);
+  EXPECT_FALSE(testModel.has_optional_float());
+  EXPECT_EQ(std::numeric_limits<float>::infinity(),
+            testModel.float_value_with_default());
+  EXPECT_EQ(-std::numeric_limits<double>::infinity(),
+            testModel.double_value_with_default());
+  // Set value.
+  testModel.set_optional_float(3.14159265f);
+  EXPECT_TRUE(testModel.has_optional_float());
+  EXPECT_NEAR(testModel.optional_float(), 3.14159265f, 1e-9f);
+  // Change value.
+  testModel.set_optional_float(-2.0f);
+  EXPECT_TRUE(testModel.has_optional_float());
+  EXPECT_NEAR(testModel.optional_float(), -2, 1e-9f);
+  // Clear value.
+  testModel.clear_optional_float();
+  EXPECT_FALSE(testModel.has_optional_float());
+  EXPECT_EQ(testModel.optional_float(), 0.0f);
+  // Set after clear.
+  testModel.set_optional_float(3.14159265f);
+  EXPECT_TRUE(testModel.has_optional_float());
+  EXPECT_NEAR(testModel.optional_float(), 3.14159265f, 1e-9f);
+}
+
+TEST(CppGeneratedCode, ScalarDouble) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+  // Test defaults.
+  EXPECT_EQ(testModel.optional_double(), 0.0);
+  EXPECT_FALSE(testModel.has_optional_double());
+  // Set value.
+  testModel.set_optional_double(3.141592653589793);
+  EXPECT_TRUE(testModel.has_optional_double());
+  EXPECT_NEAR(testModel.optional_double(), 3.141592653589793, 1e-16f);
+  // Change value.
+  testModel.set_optional_double(-1.0);
+  EXPECT_TRUE(testModel.has_optional_double());
+  EXPECT_NEAR(testModel.optional_double(), -1.0, 1e-16f);
+  // Clear value.
+  testModel.clear_optional_double();
+  EXPECT_FALSE(testModel.has_optional_double());
+  EXPECT_EQ(testModel.optional_double(), 0.0f);
+  // Set after clear.
+  testModel.set_optional_double(3.141592653589793);
+  EXPECT_TRUE(testModel.has_optional_double());
+  EXPECT_NEAR(testModel.optional_double(), 3.141592653589793, 1e-16f);
+}
+
+TEST(CppGeneratedCode, Enums) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+
+  // Check enum default value.
+  EXPECT_EQ(TestModel_Category_IMAGES, 5);
+
+  // Test defaults.
+  EXPECT_FALSE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_IMAGES);
+  // Set value.
+  testModel.set_category(TestModel_Category_NEWS);
+  EXPECT_TRUE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_NEWS);
+  // Change value.
+  testModel.set_category(TestModel_Category_VIDEO);
+  EXPECT_TRUE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_VIDEO);
+  // Clear value.
+  testModel.clear_category();
+  EXPECT_FALSE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_IMAGES);
+  // Set after clear.
+  testModel.set_category(TestModel_Category_VIDEO);
+  EXPECT_TRUE(testModel.has_category());
+  EXPECT_EQ(testModel.category(), TestModel_Category_VIDEO);
+}
+
+TEST(CppGeneratedCode, FieldWithDefaultValue) {
+  ::protos::Arena arena;
+  auto testModel = ::protos::CreateMessage<TestModel>(arena);
+
+  EXPECT_FALSE(testModel.has_int_value_with_default());
+  EXPECT_EQ(testModel.int_value_with_default(), 65);
+  testModel.set_int_value_with_default(10);
+  EXPECT_EQ(testModel.int_value_with_default(), 10);
+
+  EXPECT_FALSE(testModel.has_string_value_with_default());
+  EXPECT_EQ(testModel.string_value_with_default(), "hello");
+  testModel.set_string_value_with_default("new string");
+  EXPECT_EQ(testModel.string_value_with_default(), "new string");
+}
+
+TEST(CppGeneratedCode, OneOfFields) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+
+  EXPECT_FALSE(test_model.has_oneof_member1());
+  EXPECT_FALSE(test_model.has_oneof_member2());
+
+  test_model.set_oneof_member1("one of string");
+  EXPECT_TRUE(test_model.has_oneof_member1());
+  EXPECT_FALSE(test_model.has_oneof_member2());
+  EXPECT_EQ(test_model.oneof_member1(), "one of string");
+
+  test_model.set_oneof_member2(true);
+  EXPECT_FALSE(test_model.has_oneof_member1());
+  EXPECT_TRUE(test_model.has_oneof_member2());
+  EXPECT_EQ(test_model.oneof_member2(), true);
+
+  test_model.clear_oneof_member2();
+  EXPECT_FALSE(test_model.has_oneof_member1());
+  EXPECT_FALSE(test_model.has_oneof_member2());
+  EXPECT_EQ(test_model.oneof_member1(), "");
+  EXPECT_EQ(test_model.oneof_member2(), false);
+}
+
+TEST(CppGeneratedCode, Messages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(false, test_model.has_child_model_1());
+  auto child_model = test_model.child_model_1();
+  EXPECT_EQ(false, child_model->has_child_b1());
+  EXPECT_EQ(false, child_model->child_b1());
+  auto mutable_child = test_model.mutable_child_model_1();
+  mutable_child->set_child_b1(true);
+  EXPECT_EQ(true, mutable_child->has_child_b1());
+  EXPECT_EQ(true, mutable_child->child_b1());
+  // The View should not change due to mutation since it
+  // is default_instance.
+  EXPECT_EQ(false, child_model->has_child_b1());
+  // Readonly View should now show change.
+  child_model = test_model.child_model_1();
+  EXPECT_EQ(true, child_model->has_child_b1());
+  EXPECT_EQ(true, child_model->child_b1());
+  // Clear message field.
+  EXPECT_EQ(true, test_model.has_child_model_1());
+  test_model.clear_child_model_1();
+  EXPECT_EQ(false, test_model.has_child_model_1());
+}
+
+TEST(CppGeneratedCode, NestedMessages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  auto nested_child = test_model.nested_child_1();
+  EXPECT_EQ(0, nested_child->nested_child_name().size());
+  auto mutable_nested_child = test_model.mutable_nested_child_1();
+  EXPECT_EQ(false, mutable_nested_child->has_nested_child_name());
+  mutable_nested_child->set_nested_child_name(kTestStr1);
+  EXPECT_EQ(true, mutable_nested_child->has_nested_child_name());
+}
+
+TEST(CppGeneratedCode, RepeatedMessages) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.child_model_2_size());
+  // Should be able to clear repeated field when empty.
+  test_model.clear_child_model_2();
+  EXPECT_EQ(0, test_model.child_model_2_size());
+  // Add 2 children.
+  auto new_child = test_model.add_child_model_2();
+  EXPECT_EQ(true, new_child.ok());
+  new_child.value()->set_child_str1(kTestStr1);
+  new_child = test_model.add_child_model_2();
+  EXPECT_EQ(true, new_child.ok());
+  new_child.value()->set_child_str1(kTestStr2);
+  EXPECT_EQ(2, test_model.child_model_2_size());
+  // Mutable access.
+  auto mutable_first = test_model.mutable_child_model_2(0);
+  EXPECT_EQ(mutable_first->child_str1(), kTestStr1);
+  mutable_first->set_child_str1("change1");
+  auto mutable_second = test_model.mutable_child_model_2(1);
+  EXPECT_EQ(mutable_second->child_str1(), kTestStr2);
+  mutable_second->set_child_str1("change2");
+  // Check mutations using views.
+  auto view_first = test_model.child_model_2(0);
+  EXPECT_EQ(view_first->child_str1(), "change1");
+  auto view_second = test_model.child_model_2(1);
+  EXPECT_EQ(view_second->child_str1(), "change2");
+}
+
+TEST(CppGeneratedCode, RepeatedScalar) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.value_array_size());
+  // Should be able to clear repeated field when empty.
+  test_model.clear_value_array();
+  EXPECT_EQ(0, test_model.value_array_size());
+  // Add 2 children.
+  EXPECT_EQ(true, test_model.add_value_array(5));
+  EXPECT_EQ(true, test_model.add_value_array(6));
+  EXPECT_EQ(2, test_model.value_array_size());
+  EXPECT_EQ(5, test_model.value_array(0));
+  EXPECT_EQ(6, test_model.value_array(1));
+  EXPECT_EQ(true, test_model.resize_value_array(3));
+  EXPECT_EQ(3, test_model.value_array_size());
+  test_model.set_value_array(2, 7);
+  EXPECT_EQ(5, test_model.value_array(0));
+  EXPECT_EQ(6, test_model.value_array(1));
+  EXPECT_EQ(7, test_model.value_array(2));
+}
+
+TEST(CppGeneratedCode, RepeatedStrings) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.repeated_string_size());
+  // Should be able to clear repeated field when empty.
+  test_model.clear_repeated_string();
+  EXPECT_EQ(0, test_model.repeated_string_size());
+  // Add 2 children.
+  EXPECT_EQ(true, test_model.add_repeated_string("Hello"));
+  EXPECT_EQ(true, test_model.add_repeated_string("World"));
+  EXPECT_EQ(2, test_model.repeated_string_size());
+  EXPECT_EQ("Hello", test_model.repeated_string(0));
+  EXPECT_EQ("World", test_model.repeated_string(1));
+  EXPECT_EQ(true, test_model.resize_repeated_string(3));
+  EXPECT_EQ(3, test_model.repeated_string_size());
+  test_model.set_repeated_string(2, "Test");
+  EXPECT_EQ("Hello", test_model.repeated_string(0));
+  EXPECT_EQ("World", test_model.repeated_string(1));
+  EXPECT_EQ("Test", test_model.repeated_string(2));
+}
+
+TEST(CppGeneratedCode, MessageMapInt32KeyMessageValue) {
+  const int key_test_value = 3;
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.child_map_size());
+  test_model.clear_child_map();
+  EXPECT_EQ(0, test_model.child_map_size());
+  auto child_model1 = ::protos::CreateMessage<ChildModel1>(arena);
+  child_model1.set_child_str1("abc");
+  test_model.set_child_map(key_test_value, child_model1);
+  auto map_result = test_model.get_child_map(key_test_value);
+  EXPECT_EQ(true, map_result.ok());
+  EXPECT_EQ("abc", map_result.value()->child_str1());
+  test_model.delete_child_map(key_test_value);
+  auto map_result_after_delete = test_model.get_child_map(key_test_value);
+  EXPECT_EQ(false, map_result_after_delete.ok());
+}
+
+TEST(CppGeneratedCode, MessageMapStringKeyAndStringValue) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.str_to_str_map_size());
+  test_model.clear_str_to_str_map();
+  EXPECT_EQ(0, test_model.str_to_str_map_size());
+  test_model.set_str_to_str_map("first", "abc");
+  test_model.set_str_to_str_map("second", "def");
+  auto result = test_model.get_str_to_str_map("second");
+  EXPECT_EQ(true, result.ok());
+  EXPECT_EQ("def", result.value());
+  test_model.delete_str_to_str_map("first");
+  auto result_after_delete = test_model.get_str_to_str_map("first");
+  EXPECT_EQ(false, result_after_delete.ok());
+}
+
+TEST(CppGeneratedCode, MessageMapStringKeyAndInt32Value) {
+  ::protos::Arena arena;
+  auto test_model = ::protos::CreateMessage<TestModel>(arena);
+  EXPECT_EQ(0, test_model.str_to_int_map_size());
+  test_model.clear_str_to_int_map();
+  EXPECT_EQ(0, test_model.str_to_int_map_size());
+  test_model.set_str_to_int_map("first", 10);
+  test_model.set_str_to_int_map("second", 20);
+  auto result = test_model.get_str_to_int_map("second");
+  EXPECT_EQ(true, result.ok());
+  EXPECT_EQ(20, result.value());
+  test_model.delete_str_to_int_map("first");
+  auto result_after_delete = test_model.get_str_to_int_map("first");
+  EXPECT_EQ(false, result_after_delete.ok());
+}
+
+TEST(CppGeneratedCode, HasExtension) {
+  TestModel model;
+  EXPECT_EQ(false, ::protos::HasExtension(model, theme));
+}
+
+TEST(CppGeneratedCode, HasExtensionPtr) {
+  TestModel model;
+  EXPECT_EQ(false, ::protos::HasExtension(model.recursive_child(), theme));
+}
+
+TEST(CppGeneratedCode, ClearExtensionWithEmptyExtension) {
+  TestModel model;
+  EXPECT_EQ(false, ::protos::HasExtension(model, theme));
+  ::protos::ClearExtension(model, theme);
+  EXPECT_EQ(false, ::protos::HasExtension(model, theme));
+}
+
+TEST(CppGeneratedCode, ClearExtensionWithEmptyExtensionPtr) {
+  TestModel model;
+  ::protos::Ptr<TestModel> recursive_child = model.mutable_recursive_child();
+  ::protos::ClearExtension(recursive_child, theme);
+  EXPECT_EQ(false, ::protos::HasExtension(recursive_child, theme));
+}
+
+TEST(CppGeneratedCode, SetExtension) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false, ::protos::HasExtension(model, theme));
+  EXPECT_EQ(true, ::protos::SetExtension(model, theme, extension1).ok());
+  EXPECT_EQ(true, ::protos::HasExtension(model, theme));
+}
+
+TEST(CppGeneratedCode, SetExtensionOnMutableChild) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false,
+            ::protos::HasExtension(model.mutable_recursive_child(), theme));
+  EXPECT_EQ(true, ::protos::SetExtension(model.mutable_recursive_child(), theme,
+                                         extension1)
+                      .ok());
+  EXPECT_EQ(true,
+            ::protos::HasExtension(model.mutable_recursive_child(), theme));
+}
+
+TEST(CppGeneratedCode, GetExtension) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(false, ::protos::HasExtension(model, theme));
+  EXPECT_EQ(true, ::protos::SetExtension(model, theme, extension1).ok());
+  EXPECT_EQ("Hello World",
+            ::protos::GetExtension(model, theme).value()->ext_name());
+}
+
+TEST(CppGeneratedCode, GetExtensionOnMutableChild) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  ::protos::Ptr<TestModel> mutable_recursive_child =
+      model.mutable_recursive_child();
+  EXPECT_EQ(false, ::protos::HasExtension(mutable_recursive_child, theme));
+  EXPECT_EQ(
+      true,
+      ::protos::SetExtension(mutable_recursive_child, theme, extension1).ok());
+  EXPECT_EQ("Hello World",
+            ::protos::GetExtension(mutable_recursive_child, theme)
+                .value()
+                ->ext_name());
+}
+
+TEST(CppGeneratedCode, GetExtensionOnImmutableChild) {
+  TestModel model;
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  ::protos::Ptr<TestModel> mutable_recursive_child =
+      model.mutable_recursive_child();
+  EXPECT_EQ(false, ::protos::HasExtension(mutable_recursive_child, theme));
+  EXPECT_EQ(
+      true,
+      ::protos::SetExtension(mutable_recursive_child, theme, extension1).ok());
+  ::protos::Ptr<const TestModel> recursive_child = model.recursive_child();
+  EXPECT_EQ("Hello World",
+            ::protos::GetExtension(recursive_child, theme).value()->ext_name());
+}
+
+TEST(CppGeneratedCode, SerializeUsingArena) {
+  TestModel model;
+  model.set_str1("Hello World");
+  ::upb::Arena arena;
+  absl::StatusOr<absl::string_view> bytes = ::protos::Serialize(model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Hello World", parsed_model.str1());
+}
+
+TEST(CppGeneratedCode, SerializeNestedMessageUsingArena) {
+  TestModel model;
+  model.mutable_recursive_child()->set_str1("Hello World");
+  ::upb::Arena arena;
+  ::protos::Ptr<const TestModel> child = model.recursive_child();
+  absl::StatusOr<absl::string_view> bytes = ::protos::Serialize(child, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Hello World", parsed_model.str1());
+}
+
+TEST(CppGeneratedCode, Parse) {
+  TestModel model;
+  model.set_str1("Test123");
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(true, ::protos::SetExtension(model, theme, extension1).ok());
+  ::upb::Arena arena;
+  auto bytes = ::protos::Serialize(model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  TestModel parsed_model = ::protos::Parse<TestModel>(bytes.value()).value();
+  EXPECT_EQ("Test123", parsed_model.str1());
+  // Should not return an extension since we did not pass ExtensionRegistry.
+  EXPECT_EQ(false, ::protos::GetExtension(parsed_model, theme).ok());
+}
+
+TEST(CppGeneratedCode, ParseWithExtensionRegistry) {
+  TestModel model;
+  model.set_str1("Test123");
+  ThemeExtension extension1;
+  extension1.set_ext_name("Hello World");
+  EXPECT_EQ(true, ::protos::SetExtension(model, theme, extension1).ok());
+  ::upb::Arena arena;
+  auto bytes = ::protos::Serialize(model, arena);
+  EXPECT_EQ(true, bytes.ok());
+  ::protos::ExtensionRegistry extensions({&theme, &other_ext}, arena);
+  TestModel parsed_model =
+      ::protos::Parse<TestModel>(bytes.value(), extensions).value();
+  EXPECT_EQ("Test123", parsed_model.str1());
+  EXPECT_EQ(true, ::protos::GetExtension(parsed_model, theme).ok());
+}
+
+TEST(CppGeneratedCode, NameCollisions) {
+  TestModel model;
+  model.set_template_("test");
+  EXPECT_EQ("test", model.template_());
+  model.set_arena__("test");
+  EXPECT_EQ("test", model.arena__());
+}
diff --git a/protos_generator/tests/test_model.proto b/protos_generator/tests/test_model.proto
new file mode 100644
index 0000000..9109153
--- /dev/null
+++ b/protos_generator/tests/test_model.proto
@@ -0,0 +1,149 @@
+syntax = "proto2";
+
+package protos_generator.test;
+
+import "protos_generator/tests/child_model.proto";
+
+message TestModelContainer {
+  repeated TestModel models = 1;
+  optional ChildModel3 proto_3_child = 2;
+}
+
+message TestModel {
+  optional int32 value = 1;
+  repeated int32 value_array = 2;  // _UPB_MODE_ARRAY
+  repeated int32 value_packed_array = 3
+      [packed = true];  // _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED
+  repeated int32 value_deprec = 4 [deprecated = true];
+  optional string str1 = 115;
+  optional bool b1 = 9;
+  optional bool b2 = 10;
+  optional string str2 = 50;
+  optional string str3 = 11;
+  optional float optional_float = 14;
+  optional double optional_double = 15;
+  optional int64 optional_int64 = 16;
+  optional uint32 optional_uint32 = 17;
+  optional uint64 optional_uint64 = 18;
+  optional sint32 optional_sint32 = 19;
+  optional sint64 optional_sint64 = 20;
+  optional fixed32 optional_fixed32 = 21;
+  optional fixed64 optional_fixed64 = 22;
+  optional sfixed32 optional_sfixed32 = 23;
+  optional sfixed64 optional_sfixed64 = 24;
+  repeated int64 repeated_int64 = 25;
+  repeated uint64 repeated_uint64 = 26;
+  repeated fixed64 repeated_fixed64 = 27;
+  repeated sfixed64 repeated_sfixed64 = 28;
+  repeated bool repeated_bool = 29;
+  repeated string repeated_string = 35;
+  optional bytes optional_bytes = 36;
+  message NestedChild {
+    optional string nested_child_name = 211;
+  }
+  optional NestedChild nested_child_1 = 212;
+  optional ChildModel1 child_model_1 = 222;
+  repeated ChildModel1 child_model_2 = 223;
+  optional ChildModel1 bar = 224;
+  oneof child_oneof1 {
+    string oneof_member1 = 98;
+    bool oneof_member2 = 99;
+  }
+  optional int32 int_value_with_default = 31
+      [default = 65];  // Not supported yet
+  optional string string_value_with_default = 32
+      [default = "hello"];  // Not supported yet
+  optional float float_value_with_default = 33 [default = inf];
+  optional float double_value_with_default = 34 [default = -inf];
+
+  map<int32, ChildModel1> child_map = 225;
+  optional TestModel recursive_child = 226;
+  map<string, ChildModel1> child_str_map = 227;
+  map<string, int32> str_to_int_map = 228;
+  map<string, string> str_to_str_map = 229;
+
+  extend TestAnnotation {
+    optional OtherExtension in_message_ext = 15000;
+  }
+
+  enum Category {
+    IMAGES = 5;
+    NEWS = 6;
+    VIDEO = 7;
+    RADIO = 8 [deprecated = true];
+  }
+  optional Category category = 37;
+
+  // keyword collisions (double, template, ...)
+  oneof type {
+    string string = 230;
+    int64 int64 = 231;
+    double double = 232;
+  }
+  optional string template = 233;
+  optional string msg = 234;
+  optional string arena = 235;
+
+  // Tests publicly imported enum.
+  optional TestEnum imported_enum = 238;
+
+  // TODO(243705098) accessor name collision with field name
+  // optional repeated string phase = 236;
+  // optional bool clear_phase = 237;
+
+  extensions 10000 to max;
+}
+
+// Old version with fewer fields to test backward/forward compatibility.
+message TestModelContainerV1 {
+  repeated TestModelV1 models = 1;
+}
+
+message TestModelV1 {
+  optional int32 value = 1;
+  repeated int32 value2 = 2;
+  repeated int32 value3 = 3 [packed = true];
+  repeated int32 value4 = 4 [deprecated = true];
+  optional bool b1 = 9;
+  optional bool b2 = 10;
+  optional string str2 = 50;
+}
+
+enum PrimaryColors {
+  RED = 1;
+  GREEN = 2;
+  BLUE = 3;
+}
+
+// TestModel extension.
+message ThemeExtension {
+  optional string ext_name = 1;
+  optional bool ext_bool = 2;
+}
+
+extend TestModel {
+  optional ThemeExtension theme = 12001;
+}
+
+message OtherExtension {
+  optional string ext2_name = 1;
+}
+
+extend TestModel {
+  optional OtherExtension other_ext = 12002;
+}
+
+message TestAnnotation {
+  extensions 10000 to max;
+}
+
+message TestMessageHasEnum {
+  optional EnumDeclaredAfterMessage enum_declared_after_message = 1;
+}
+
+enum EnumDeclaredAfterMessage {
+  ZERO = 0;
+  ONE = 1;
+  TWO = 2;
+  THREE = 3;
+}