C++ Code generator

PiperOrigin-RevId: 486158566
diff --git a/WORKSPACE b/WORKSPACE
index 1f4fef6..20dfc57 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -52,6 +52,15 @@
     urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.3.2.zip"],
 )
 
+http_archive(
+    name = "com_google_absl",
+    sha256 = "e7fdfe0bed87702a22c5b73b6b5fe08bedd25f17d617e52df6061b0f47d480b0",
+    strip_prefix = "abseil-cpp-e6044634dd7caec2d79a13aecc9e765023768757",
+    urls = [
+        "https://github.com/abseil/abseil-cpp/archive/e6044634dd7caec2d79a13aecc9e765023768757.tar.gz"
+    ],
+)
+
 load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
 
 rules_fuzzing_dependencies()
diff --git a/bazel/workspace_deps.bzl b/bazel/workspace_deps.bzl
index 84ad53a..bfe1ea5 100644
--- a/bazel/workspace_deps.bzl
+++ b/bazel/workspace_deps.bzl
@@ -15,9 +15,9 @@
     maybe(
         http_archive,
         name = "com_google_absl",
-        url = "https://github.com/abseil/abseil-cpp/archive/b9b925341f9e90f5e7aa0cf23f036c29c7e454eb.zip",
-        strip_prefix = "abseil-cpp-b9b925341f9e90f5e7aa0cf23f036c29c7e454eb",
-        sha256 = "bb2a0b57c92b6666e8acb00f4cbbfce6ddb87e83625fb851b0e78db581340617",
+        url = "https://github.com/abseil/abseil-cpp/archive/e6044634dd7caec2d79a13aecc9e765023768757.tar.gz",
+        strip_prefix = "abseil-cpp-e6044634dd7caec2d79a13aecc9e765023768757",
+        sha256 = "e7fdfe0bed87702a22c5b73b6b5fe08bedd25f17d617e52df6061b0f47d480b0",
     )
 
     maybe(
diff --git a/benchmarks/BUILD b/benchmarks/BUILD
index a7852c7..9515e86 100644
--- a/benchmarks/BUILD
+++ b/benchmarks/BUILD
@@ -24,7 +24,7 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 # begin:google_only
-# load("//tools/build_defs/proto/cpp:cc_proto_library.bzl", "cc_proto_library")
+# load("@rules_cc//cc:defs.bzl", "cc_proto_library")
 # end:google_only
 
 load(
diff --git a/benchmarks/build_defs.bzl b/benchmarks/build_defs.bzl
index 3fc4dd2..168dab6 100644
--- a/benchmarks/build_defs.bzl
+++ b/benchmarks/build_defs.bzl
@@ -24,7 +24,7 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 # begin:google_only
-# load("//tools/build_defs/proto/cpp:cc_proto_library.bzl", _cc_proto_library = "cc_proto_library")
+# load("@rules_cc//cc:defs.bzl", _cc_proto_library = "cc_proto_library")
 #
 # _is_google3 = True
 # end:google_only
diff --git a/protos/BUILD b/protos/BUILD
new file mode 100644
index 0000000..f68a1ec
--- /dev/null
+++ b/protos/BUILD
@@ -0,0 +1,98 @@
+# 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_copts",
+)
+
+licenses(["notice"])
+
+cc_library(
+    name = "protos",
+    srcs = ["protos.cc"],
+    hdrs = ["protos.h"],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        "//:mini_table",
+        "//:upb",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings:str_format",
+    ],
+)
+
+cc_library(
+    name = "protos_internal",
+    hdrs = ["protos_internal.h"],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":protos",
+        "//:mini_table",
+        "//:upb",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings:str_format",
+    ],
+)
+
+# Common support code for C++ generated code.
+cc_library(
+    name = "generated_protos_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
+    hdrs = [
+        "protos_internal.h",
+    ],
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":protos",
+        ":protos_internal",
+    ],
+)
+
+cc_test(
+    name = "protos_internal_test",
+    srcs = ["protos_internal_test.cc"],
+    copts = UPB_DEFAULT_CPPOPTS,
+    deps = [
+        ":protos_internal",
+        "//:upb",
+        "//protos_generator/tests:test_model_upb_cc_proto",
+        "//protos_generator/tests:test_model_upb_proto",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+upb_cc_proto_library_copts(
+    name = "upb_cc_proto_library_copts__for_generated_code_only_do_not_use",
+    copts = UPB_DEFAULT_CPPOPTS,
+    visibility = ["//visibility:public"],
+)
diff --git a/protos/bazel/BUILD b/protos/bazel/BUILD
new file mode 100644
index 0000000..607edae
--- /dev/null
+++ b/protos/bazel/BUILD
@@ -0,0 +1,39 @@
+# 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_skylib//:bzl_library.bzl", "bzl_library")
+
+licenses(["notice"])
+
+bzl_library(
+    name = "upb_cc_proto_library_bzl",
+    srcs = ["upb_cc_proto_library.bzl"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "@bazel_skylib//lib:paths",
+        "//bazel:upb_proto_library_bzl",
+        "@bazel_tools//tools/cpp:toolchain_utils.bzl",
+    ],
+)
diff --git a/protos/bazel/upb_cc_proto_library.bzl b/protos/bazel/upb_cc_proto_library.bzl
new file mode 100644
index 0000000..77b2f67
--- /dev/null
+++ b/protos/bazel/upb_cc_proto_library.bzl
@@ -0,0 +1,346 @@
+# 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.
+
+"""Public rules for using upb protos:
+  - upb_cc_proto_library()
+"""
+
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("//bazel:upb_proto_library.bzl", "GeneratedSrcsInfo", "UpbWrappedCcInfo", "UpbWrappedGeneratedSrcsInfo", "upb_proto_library_aspect")
+
+# begin:google_only
+# load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
+#
+# end:google_only
+# begin:github_only
+# Compatibility code for Bazel 4.x. Remove this when we drop support for Bazel 4.x.
+load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
+
+def use_cpp_toolchain():
+    return ["@bazel_tools//tools/cpp:toolchain_type"]
+# end:github_only
+
+# Generic support code #########################################################
+
+# begin:github_only
+_is_google3 = False
+# end:github_only
+
+# begin:google_only
+# _is_google3 = True
+# end:google_only
+
+def _get_real_short_path(file):
+    # For some reason, files from other archives have short paths that look like:
+    #   ../com_google_protobuf/google/protobuf/descriptor.proto
+    short_path = file.short_path
+    if short_path.startswith("../"):
+        second_slash = short_path.index("/", 3)
+        short_path = short_path[second_slash + 1:]
+
+    # Sometimes it has another few prefixes like:
+    #   _virtual_imports/any_proto/google/protobuf/any.proto
+    #   benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
+    # We want just google/protobuf/any.proto.
+    virtual_imports = "_virtual_imports/"
+    if virtual_imports in short_path:
+        short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
+    return short_path
+
+def _get_real_root(file):
+    real_short_path = _get_real_short_path(file)
+    return file.path[:-len(real_short_path) - 1]
+
+def _generate_output_file(ctx, src, extension):
+    real_short_path = _get_real_short_path(src)
+    real_short_path = paths.relativize(real_short_path, ctx.label.package)
+    output_filename = paths.replace_extension(real_short_path, extension)
+    ret = ctx.actions.declare_file(output_filename)
+    return ret
+
+def _filter_none(elems):
+    out = []
+    for elem in elems:
+        if elem:
+            out.append(elem)
+    return out
+
+def _cc_library_func(ctx, name, hdrs, srcs, copts, dep_ccinfos):
+    """Like cc_library(), but callable from rules.
+
+    Args:
+      ctx: Rule context.
+      name: Unique name used to generate output files.
+      hdrs: Public headers that can be #included from other rules.
+      srcs: C/C++ source files.
+      copts: Additional options for cc compilation.
+      dep_ccinfos: CcInfo providers of dependencies we should build/link against.
+
+    Returns:
+      CcInfo provider for this compilation.
+    """
+
+    compilation_contexts = [info.compilation_context for info in dep_ccinfos]
+    linking_contexts = [info.linking_context for info in dep_ccinfos]
+    toolchain = find_cpp_toolchain(ctx)
+    feature_configuration = cc_common.configure_features(
+        ctx = ctx,
+        cc_toolchain = toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+
+    blaze_only_args = {}
+
+    if _is_google3:
+        blaze_only_args["grep_includes"] = ctx.file._grep_includes
+
+    (compilation_context, compilation_outputs) = cc_common.compile(
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = toolchain,
+        name = name,
+        srcs = srcs,
+        public_hdrs = hdrs,
+        user_compile_flags = copts,
+        compilation_contexts = compilation_contexts,
+        **blaze_only_args
+    )
+
+    # buildifier: disable=unused-variable
+    (linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(
+        actions = ctx.actions,
+        name = name,
+        feature_configuration = feature_configuration,
+        cc_toolchain = toolchain,
+        compilation_outputs = compilation_outputs,
+        linking_contexts = linking_contexts,
+        **blaze_only_args
+    )
+
+    return CcInfo(
+        compilation_context = compilation_context,
+        linking_context = linking_context,
+    )
+
+# Build setting for whether fasttable code generation is enabled ###############
+
+FastTableEnabledInfo = provider(
+    "Provides fasttable configuration to compiler",
+    fields = {
+        "enabled": "whether fasttable is enabled",
+    },
+)
+
+def fasttable_enabled_impl(ctx):
+    raw_setting = ctx.build_setting_value
+
+    if raw_setting:
+        # TODO(haberman): check that the target CPU supports fasttable.
+        pass
+
+    return FastTableEnabledInfo(enabled = raw_setting)
+
+upb_fasttable_enabled = rule(
+    implementation = fasttable_enabled_impl,
+    build_setting = config.bool(flag = True),
+)
+
+# Dummy rule to expose select() copts to aspects  ##############################
+
+UpbCcProtoLibraryCoptsInfo = provider(
+    "Provides copts for upb cc proto targets",
+    fields = {
+        "copts": "copts for upb_cc_proto_library()",
+    },
+)
+
+def upb_cc_proto_library_copts_impl(ctx):
+    return UpbCcProtoLibraryCoptsInfo(copts = ctx.attr.copts)
+
+upb_cc_proto_library_copts = rule(
+    implementation = upb_cc_proto_library_copts_impl,
+    attrs = {"copts": attr.string_list(default = [])},
+)
+
+_UpbCcWrappedCcInfo = provider("Provider for cc_info for protos", fields = ["cc_info"])
+_WrappedCcGeneratedSrcsInfo = provider("Provider for generated sources", fields = ["srcs"])
+
+def _compile_upb_cc_protos(ctx, generator, proto_info, proto_sources):
+    if len(proto_sources) == 0:
+        return GeneratedSrcsInfo(srcs = [], hdrs = [])
+
+    tool = getattr(ctx.executable, "_gen_" + generator)
+    srcs = [_generate_output_file(ctx, name, ".upb.proto.cc") for name in proto_sources]
+    hdrs = [_generate_output_file(ctx, name, ".upb.proto.h") for name in proto_sources]
+    hdrs += [_generate_output_file(ctx, name, ".upb.fwd.h") for name in proto_sources]
+    transitive_sets = proto_info.transitive_descriptor_sets.to_list()
+    fasttable_enabled = (hasattr(ctx.attr, "_fasttable_enabled") and
+                         ctx.attr._fasttable_enabled[FastTableEnabledInfo].enabled)
+    codegen_params = "fasttable:" if fasttable_enabled else ""
+    ctx.actions.run(
+        inputs = depset(
+            direct = [proto_info.direct_descriptor_set],
+            transitive = [proto_info.transitive_descriptor_sets],
+        ),
+        tools = [tool],
+        outputs = srcs + hdrs,
+        executable = ctx.executable._protoc,
+        arguments = [
+                        "--" + generator + "_out=" + codegen_params + _get_real_root(srcs[0]),
+                        "--plugin=protoc-gen-" + generator + "=" + tool.path,
+                        "--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]),
+                    ] +
+                    [_get_real_short_path(file) for file in proto_sources],
+        progress_message = "Generating upb cc protos for :" + ctx.label.name,
+    )
+    return GeneratedSrcsInfo(srcs = srcs, hdrs = hdrs)
+
+def _upb_cc_proto_rule_impl(ctx):
+    if len(ctx.attr.deps) != 1:
+        fail("only one deps dependency allowed.")
+    dep = ctx.attr.deps[0]
+
+    if _WrappedCcGeneratedSrcsInfo in dep:
+        srcs = dep[_WrappedCcGeneratedSrcsInfo].srcs
+    elif UpbWrappedGeneratedSrcsInfo in dep:
+        srcs = dep[UpbWrappedGeneratedSrcsInfo].srcs
+    else:
+        fail("proto_library rule must generate UpbWrappedGeneratedSrcsInfo or " +
+             "_WrappedCcGeneratedSrcsInfo (aspect should have handled this).")
+
+    if _UpbCcWrappedCcInfo in dep:
+        cc_info = dep[_UpbCcWrappedCcInfo].cc_info
+    elif UpbWrappedCcInfo in dep:
+        cc_info = dep[UpbWrappedCcInfo].cc_info
+    else:
+        fail("proto_library rule must generate UpbWrappedCcInfo or " +
+             "_UpbCcWrappedCcInfo (aspect should have handled this).")
+
+    lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0]
+    files = _filter_none([
+        lib.static_library,
+        lib.pic_static_library,
+        lib.dynamic_library,
+    ])
+    return [
+        DefaultInfo(files = depset(files + srcs.hdrs + srcs.srcs)),
+        srcs,
+        cc_info,
+    ]
+
+def _upb_cc_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider):
+    proto_info = target[ProtoInfo]
+    files = _compile_upb_cc_protos(ctx, generator, proto_info, proto_info.direct_sources)
+    deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator)
+    dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep]
+    dep_ccinfos += [dep[UpbWrappedCcInfo].cc_info for dep in deps if UpbWrappedCcInfo in dep]
+    dep_ccinfos += [dep[_UpbCcWrappedCcInfo].cc_info for dep in deps if _UpbCcWrappedCcInfo in dep]
+    if UpbWrappedCcInfo not in target:
+        fail("Target should have UpbWrappedCcInfo provider")
+    dep_ccinfos.append(target[UpbWrappedCcInfo].cc_info)
+    cc_info = _cc_library_func(
+        ctx = ctx,
+        name = ctx.rule.attr.name + "." + generator,
+        hdrs = files.hdrs,
+        srcs = files.srcs,
+        copts = ctx.attr._ccopts[UpbCcProtoLibraryCoptsInfo].copts,
+        dep_ccinfos = dep_ccinfos,
+    )
+    return [cc_provider(cc_info = cc_info), file_provider(srcs = files)]
+
+def _upb_cc_proto_library_aspect_impl(target, ctx):
+    return _upb_cc_proto_aspect_impl(target, ctx, "upbprotos", _UpbCcWrappedCcInfo, _WrappedCcGeneratedSrcsInfo)
+
+def _maybe_add(d):
+    if _is_google3:
+        d["_grep_includes"] = attr.label(
+            allow_single_file = True,
+            cfg = "exec",
+            default = "@bazel_tools//tools/cpp:grep-includes",
+        )
+    return d
+
+_upb_cc_proto_library_aspect = aspect(
+    attrs = _maybe_add({
+        "_ccopts": attr.label(
+            default = "//protos:upb_cc_proto_library_copts__for_generated_code_only_do_not_use",
+        ),
+        "_gen_upbprotos": attr.label(
+            executable = True,
+            cfg = "exec",
+            default = "//protos_generator:protoc-gen-upb-protos",
+        ),
+        "_protoc": attr.label(
+            executable = True,
+            cfg = "exec",
+            default = "@com_google_protobuf//:protoc",
+        ),
+        "_cc_toolchain": attr.label(
+            default = "@bazel_tools//tools/cpp:current_cc_toolchain",
+        ),
+        "_upbprotos": attr.label_list(
+            default = [
+                # TODO: Add dependencies for cc runtime (absl/string etc..)
+                "//:generated_cpp_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
+                "//protos:generated_protos_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
+                "@com_google_absl//absl/strings",
+                "@com_google_absl//absl/status:statusor",
+                "//protos",
+            ],
+        ),
+    }),
+    implementation = _upb_cc_proto_library_aspect_impl,
+    provides = [
+        _UpbCcWrappedCcInfo,
+        _WrappedCcGeneratedSrcsInfo,
+    ],
+    required_aspect_providers = [
+        UpbWrappedCcInfo,
+        UpbWrappedGeneratedSrcsInfo,
+    ],
+    attr_aspects = ["deps"],
+    fragments = ["cpp"],
+    toolchains = use_cpp_toolchain(),
+    incompatible_use_toolchain_transition = True,
+)
+
+upb_cc_proto_library = rule(
+    output_to_genfiles = True,
+    implementation = _upb_cc_proto_rule_impl,
+    attrs = {
+        "deps": attr.label_list(
+            aspects = [
+                upb_proto_library_aspect,
+                _upb_cc_proto_library_aspect,
+            ],
+            allow_rules = ["proto_library"],
+            providers = [ProtoInfo],
+        ),
+        "_ccopts": attr.label(
+            default = "//protos:upb_cc_proto_library_copts__for_generated_code_only_do_not_use",
+        ),
+    },
+)
diff --git a/protos/protos.cc b/protos/protos.cc
new file mode 100644
index 0000000..7a651c4
--- /dev/null
+++ b/protos/protos.cc
@@ -0,0 +1,108 @@
+/*
+ * 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/protos.h"
+
+#include "absl/strings/str_format.h"
+
+namespace protos {
+
+// begin:google_only
+// absl::Status MessageAllocationError(SourceLocation loc) {
+//   return absl::Status(absl::StatusCode::kInternal,
+//                       "Upb message allocation error", loc);
+// }
+//
+// absl::Status ExtensionNotFoundError(int extension_number, SourceLocation loc) {
+//   return absl::Status(
+//       absl::StatusCode::kInternal,
+//       absl::StrFormat("Extension %d not found", extension_number), loc);
+// }
+//
+// absl::Status MessageEncodeError(upb_EncodeStatus status, SourceLocation loc) {
+//   return absl::Status(absl::StatusCode::kInternal,
+//                       absl::StrFormat("Upb message encoding error %d", status),
+//                       loc
+//
+//   );
+// }
+//
+// absl::Status MessageDecodeError(upb_DecodeStatus status, SourceLocation loc
+//
+// ) {
+//   return absl::Status(absl::StatusCode::kInternal,
+//                       absl::StrFormat("Upb message parse error %d", status), loc
+//
+//   );
+// }
+// end:google_only
+
+// begin:github_only
+absl::Status MessageAllocationError(SourceLocation loc) {
+  return absl::Status(absl::StatusCode::kUnknown,
+                      "Upb message allocation error");
+}
+
+absl::Status ExtensionNotFoundError(int ext_number, SourceLocation loc) {
+  return absl::Status(absl::StatusCode::kUnknown,
+                      absl::StrFormat("Extension %d not found", ext_number));
+}
+
+absl::Status MessageEncodeError(upb_EncodeStatus s, SourceLocation loc) {
+  return absl::Status(absl::StatusCode::kUnknown, "Encoding error");
+}
+
+absl::Status MessageDecodeError(upb_DecodeStatus status, SourceLocation loc
+
+) {
+  return absl::Status(absl::StatusCode::kUnknown, "Upb message parse error");
+}
+// end:github_only
+
+namespace internal {
+
+upb_ExtensionRegistry* GetUpbExtensions(
+    const ExtensionRegistry& extension_registry) {
+  return extension_registry.registry_;
+}
+
+absl::StatusOr<absl::string_view> Serialize(const upb_Message* message,
+                                            const upb_MiniTable* mini_table,
+                                            upb_Arena* arena, int options) {
+  size_t len;
+  char* ptr;
+  upb_EncodeStatus status =
+      upb_Encode(message, mini_table, options, arena, &ptr, &len);
+  if (status == kUpb_EncodeStatus_Ok) {
+    return absl::string_view(ptr, len);
+  }
+  return MessageEncodeError(status);
+}
+
+}  // namespace internal
+
+}  // namespace protos
diff --git a/protos/protos.h b/protos/protos.h
new file mode 100644
index 0000000..85c61e1
--- /dev/null
+++ b/protos/protos.h
@@ -0,0 +1,407 @@
+/*
+ * 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_PROTOS_H_
+#define UPB_PROTOS_PROTOS_H_
+
+#include <type_traits>
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "upb/decode.h"
+#include "upb/encode.h"
+#include "upb/mini_table.h"
+#include "upb/upb.hpp"
+
+namespace protos {
+
+using Arena = ::upb::Arena;
+class ExtensionRegistry;
+
+template <typename T>
+using Proxy = std::conditional_t<std::is_const<T>::value,
+                                 typename std::remove_const_t<T>::CProxy,
+                                 typename T::Proxy>;
+
+// Provides convenient access to Proxy and CProxy message types.
+//
+// Using rebinding and handling of const, Ptr<Message> and Ptr<const Message>
+// allows copying const with T* const and avoids using non-copyable Proxy types
+// directly.
+template <typename T>
+class Ptr final {
+ public:
+  Ptr() = delete;
+
+  // Implicit conversions
+  Ptr(T* m) : p_(m) {}                // NOLINT
+  Ptr(const Proxy<T>* p) : p_(*p) {}  // NOLINT
+  Ptr(Proxy<T> p) : p_(p) {}          // NOLINT
+  Ptr(const Ptr& m) = default;
+
+  Ptr& operator=(Ptr v) & {
+    Proxy<T>::Rebind(p_, v.p_);
+    return *this;
+  }
+
+  Proxy<T> operator*() const { return p_; }
+  Proxy<T>* operator->() const {
+    return const_cast<Proxy<T>*>(std::addressof(p_));
+  }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wclass-conversion"
+#endif
+  template <typename U = T, std::enable_if_t<!std::is_const<U>::value, int> = 0>
+  operator Ptr<const T>() const {
+    Proxy<const T> p(p_);
+    return Ptr<const T>(&p);
+  }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+ private:
+  Ptr(void* msg, upb_Arena* arena) : p_(msg, arena) {}  // NOLINT
+
+  friend class Ptr<const T>;
+  friend typename T::Access;
+
+  Proxy<T> p_;
+};
+
+inline absl::string_view UpbStrToStringView(upb_StringView str) {
+  return absl::string_view(str.data, str.size);
+}
+
+// TODO: update bzl and move to upb runtime / protos.cc.
+inline upb_StringView UpbStrFromStringView(absl::string_view str,
+                                           upb_Arena* arena) {
+  upb_alloc* alloc = upb_Arena_Alloc(arena);
+  const size_t str_size = str.size();
+  char* buffer = static_cast<char*>(upb_malloc(alloc, str_size));
+  memcpy(buffer, str.data(), str_size);
+  return upb_StringView_FromDataAndSize(buffer, str_size);
+}
+
+template <typename T>
+typename T::Proxy CreateMessage(::protos::Arena& arena) {
+  return typename T::Proxy(upb_Message_New(T::minitable(), arena.ptr()),
+                           arena.ptr());
+}
+
+// begin:github_only
+// This type exists to work around an absl type that has not yet been
+// released.
+struct SourceLocation {
+  static SourceLocation current() { return {}; }
+  absl::string_view file_name() { return "<unknown>"; }
+  int line() { return 0; }
+};
+// end:github_only
+
+// begin:google_only
+// using SourceLocation = absl::SourceLocation;
+// end:google_only
+
+absl::Status MessageAllocationError(
+    SourceLocation loc = SourceLocation::current());
+
+absl::Status ExtensionNotFoundError(
+    int extension_number, SourceLocation loc = SourceLocation::current());
+
+absl::Status MessageDecodeError(upb_DecodeStatus status,
+                                SourceLocation loc = SourceLocation::current());
+
+absl::Status MessageEncodeError(upb_EncodeStatus status,
+                                SourceLocation loc = SourceLocation::current());
+
+namespace internal {
+template <typename T>
+T CreateMessage() {
+  return T();
+}
+
+template <typename T>
+typename T::Proxy CreateMessageProxy(void* msg, upb_Arena* arena) {
+  return typename T::Proxy(msg, arena);
+}
+
+template <typename T>
+typename T::CProxy CreateMessage(upb_Message* msg) {
+  return typename T::CProxy(msg);
+}
+
+class ExtensionMiniTableProvider {
+ public:
+  ExtensionMiniTableProvider(const upb_MiniTable_Extension* mini_table_ext)
+      : mini_table_ext_(mini_table_ext) {}
+  const upb_MiniTable_Extension* mini_table_ext() const {
+    return mini_table_ext_;
+  }
+
+ private:
+  const upb_MiniTable_Extension* mini_table_ext_;
+};
+
+// -------------------------------------------------------------------
+// ExtensionIdentifier
+// This is the type of actual extension objects.  E.g. if you have:
+//   extend Foo {
+//     optional MyExtension bar = 1234;
+//   }
+// then "bar" will be defined in C++ as:
+//   ExtensionIdentifier<Foo, MyExtension> bar(&namespace_bar_ext);
+template <typename ExtendeeType, typename ExtensionType>
+class ExtensionIdentifier : public ExtensionMiniTableProvider {
+ public:
+  using Extension = ExtensionType;
+  using Extendee = ExtendeeType;
+
+  ExtensionIdentifier(const upb_MiniTable_Extension* mini_table_ext)
+      : ExtensionMiniTableProvider(mini_table_ext) {}
+};
+
+template <typename T>
+void* GetInternalMsg(const T& message) {
+  return message.msg();
+}
+
+template <typename T>
+void* GetInternalMsg(const Ptr<T>& message) {
+  return message->msg();
+}
+
+template <typename T>
+upb_Arena* GetArena(const T& message) {
+  return static_cast<upb_Arena*>(message.GetInternalArena());
+}
+
+template <typename T>
+upb_Arena* GetArena(const Ptr<T>& message) {
+  return static_cast<upb_Arena*>(message->GetInternalArena());
+}
+
+upb_ExtensionRegistry* GetUpbExtensions(
+    const ExtensionRegistry& extension_registry);
+
+absl::StatusOr<absl::string_view> Serialize(const upb_Message* message,
+                                            const upb_MiniTable* mini_table,
+                                            upb_Arena* arena, int options);
+
+}  // namespace internal
+
+class ExtensionRegistry {
+ public:
+  ExtensionRegistry(
+      const std::vector<const ::protos::internal::ExtensionMiniTableProvider*>
+          extensions,
+      const upb::Arena& arena)
+      : registry_(upb_ExtensionRegistry_New(arena.ptr())) {
+    if (registry_) {
+      for (const auto& ext_provider : extensions) {
+        const auto* ext = ext_provider->mini_table_ext();
+        bool success = upb_ExtensionRegistry_AddArray(registry_, &ext, 1);
+        if (!success) {
+          registry_ = nullptr;
+          break;
+        }
+      }
+    }
+  }
+
+ private:
+  friend upb_ExtensionRegistry* ::protos::internal::GetUpbExtensions(
+      const ExtensionRegistry& extension_registry);
+  upb_ExtensionRegistry* registry_;
+};
+
+template <typename T>
+using EnableIfProtosClass = std::enable_if_t<
+    std::is_base_of<typename T::Access, T>::value &&
+    std::is_base_of<typename T::Access, typename T::ExtendableType>::value>;
+
+template <typename T>
+using EnableIfMutableProto = std::enable_if_t<!std::is_const<T>::value>;
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>>
+bool HasExtension(
+    const T& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
+  return _upb_Message_Getext(message.msg(), id.mini_table_ext()) != nullptr;
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>>
+bool HasExtension(
+    const Ptr<T>& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
+  return _upb_Message_Getext(message->msg(), id.mini_table_ext()) != nullptr;
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>, typename = EnableIfMutableProto<T>>
+void ClearExtension(
+    const Ptr<T>& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
+  _upb_Message_Clearext(message->msg(), id.mini_table_ext());
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>>
+void ClearExtension(
+    const T& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
+  _upb_Message_Clearext(message.msg(), id.mini_table_ext());
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>>
+absl::Status SetExtension(
+    const T& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id,
+    Extension& value) {
+  auto* message_arena = static_cast<upb_Arena*>(message.GetInternalArena());
+  upb_Message_Extension* msg_ext = _upb_Message_GetOrCreateExtension(
+      message.msg(), id.mini_table_ext(), message_arena);
+  if (!msg_ext) {
+    return MessageAllocationError();
+  }
+  auto* extension_arena = static_cast<upb_Arena*>(value.GetInternalArena());
+  if (message_arena != extension_arena) {
+    upb_Arena_Fuse(message_arena, extension_arena);
+  }
+  msg_ext->data.ptr = value.msg();
+  return absl::OkStatus();
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>, typename = EnableIfMutableProto<T>>
+absl::Status SetExtension(
+    const Ptr<T>& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id,
+    Extension& value) {
+  auto* message_arena = static_cast<upb_Arena*>(message->GetInternalArena());
+  upb_Message_Extension* msg_ext = _upb_Message_GetOrCreateExtension(
+      message->msg(), id.mini_table_ext(), message_arena);
+  if (!msg_ext) {
+    return MessageAllocationError();
+  }
+  auto* extension_arena = static_cast<upb_Arena*>(message->GetInternalArena());
+  if (message_arena != extension_arena) {
+    upb_Arena_Fuse(message_arena, extension_arena);
+  }
+  msg_ext->data.ptr = ::protos::internal::GetInternalMsg(value);
+  return absl::OkStatus();
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>>
+absl::StatusOr<Ptr<const Extension>> GetExtension(
+    const T& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
+  const upb_Message_Extension* ext =
+      _upb_Message_Getext(message.msg(), id.mini_table_ext());
+  if (!ext) {
+    return ExtensionNotFoundError(id.mini_table_ext()->field.number);
+  }
+  return Ptr<const Extension>(
+      ::protos::internal::CreateMessage<Extension>(ext->data.ptr));
+}
+
+template <typename T, typename Extendee, typename Extension,
+          typename = EnableIfProtosClass<T>>
+absl::StatusOr<Ptr<const Extension>> GetExtension(
+    const Ptr<T>& message,
+    const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
+  const upb_Message_Extension* ext =
+      _upb_Message_Getext(message->msg(), id.mini_table_ext());
+  if (!ext) {
+    return ExtensionNotFoundError(id.mini_table_ext()->field.number);
+  }
+  return Ptr<const Extension>(
+      ::protos::internal::CreateMessage<Extension>(ext->data.ptr));
+}
+
+template <typename T>
+bool Parse(T& message, absl::string_view bytes) {
+  _upb_Message_Clear(message.msg(), T::minitable());
+  auto* arena = static_cast<upb_Arena*>(message.GetInternalArena());
+  return upb_Decode(bytes.data(), bytes.size(), message.msg(), T::minitable(),
+                    /* extreg= */ nullptr, /* options= */ 0,
+                    arena) == kUpb_DecodeStatus_Ok;
+}
+
+template <typename T>
+absl::StatusOr<T> Parse(absl::string_view bytes, int options = 0) {
+  T message;
+  auto* arena = static_cast<upb_Arena*>(message.GetInternalArena());
+  upb_DecodeStatus status =
+      upb_Decode(bytes.data(), bytes.size(), message.msg(), T::minitable(),
+                 /* extreg= */ nullptr, /* options= */ 0, arena);
+  if (status == kUpb_DecodeStatus_Ok) {
+    return message;
+  }
+  return MessageDecodeError(status);
+}
+
+template <typename T>
+absl::StatusOr<T> Parse(absl::string_view bytes,
+                        const ::protos::ExtensionRegistry& extension_registry,
+                        int options = 0) {
+  T message;
+  auto* arena = static_cast<upb_Arena*>(message.GetInternalArena());
+  upb_DecodeStatus status =
+      upb_Decode(bytes.data(), bytes.size(), message.msg(), T::minitable(),
+                 ::protos::internal::GetUpbExtensions(extension_registry),
+                 /* options= */ 0, arena);
+  if (status == kUpb_DecodeStatus_Ok) {
+    return message;
+  }
+  return MessageDecodeError(status);
+}
+
+template <typename T>
+absl::StatusOr<absl::string_view> Serialize(const T& message, upb::Arena& arena,
+                                            int options = 0) {
+  return ::protos::internal::Serialize(message.msg(), T::minitable(),
+                                       arena.ptr(), options);
+}
+
+template <typename T>
+absl::StatusOr<absl::string_view> Serialize(Ptr<const T> message,
+                                            upb::Arena& arena,
+                                            int options = 0) {
+  return ::protos::internal::Serialize(message->msg(), T::minitable(),
+                                       arena.ptr(), options);
+}
+
+}  // namespace protos
+
+#endif  // UPB_PROTOS_PROTOS_H_
diff --git a/protos/protos_internal.h b/protos/protos_internal.h
new file mode 100644
index 0000000..85ba4d0
--- /dev/null
+++ b/protos/protos_internal.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_PROTOS_INTERNAL_H_
+#define UPB_PROTOS_PROTOS_INTERNAL_H_
+
+#include "protos/protos.h"
+
+namespace protos::internal {
+
+// Moves ownership of a message created in a source arena.
+//
+// Utility function to provide a way to move ownership across languages or VMs.
+template <typename T>
+T MoveMessage(upb_Message* msg, upb_Arena* arena) {
+  return T(msg, arena);
+}
+
+}  // namespace protos::internal
+#endif
diff --git a/protos/protos_internal_test.cc b/protos/protos_internal_test.cc
new file mode 100644
index 0000000..835601c
--- /dev/null
+++ b/protos/protos_internal_test.cc
@@ -0,0 +1,55 @@
+// 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/protos_internal.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "protos_generator/tests/test_model.upb.h"
+#include "protos_generator/tests/test_model.upb.proto.h"
+#include "upb/arena.h"
+
+namespace protos::testing {
+namespace {
+using ::protos_generator::test::protos::TestModel;
+
+TEST(CppGeneratedCode, InternalMoveMessage) {
+  // Generate message (simulating message created in another VM/language)
+  upb_Arena* source_arena = upb_Arena_New();
+  protos_generator_test_TestModel* message =
+      protos_generator_test_TestModel_new(source_arena);
+  protos_generator_test_TestModel_set_int_value_with_default(message, 123);
+
+  EXPECT_NE(message, nullptr);
+  // Move ownership.
+  TestModel model =
+      protos::internal::MoveMessage<TestModel>(message, source_arena);
+  // Now that we have moved ownership, free original arena.
+  upb_Arena_Free(source_arena);
+  EXPECT_EQ(model.int_value_with_default(), 123);
+}
+
+}  // namespace
+}  // namespace protos::testing
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;
+}
diff --git a/upbc/BUILD b/upbc/BUILD
index a68057d..227d748 100644
--- a/upbc/BUILD
+++ b/upbc/BUILD
@@ -75,7 +75,7 @@
         "common.h",
     ],
     copts = UPB_DEFAULT_CPPOPTS,
-    visibility = ["//third_party/upb/protos_generator:__pkg__"],
+    visibility = ["//protos_generator:__pkg__"],
     deps = [
         "@com_google_absl//absl/strings",
         "@com_google_protobuf//:protobuf",
@@ -91,7 +91,7 @@
         "file_layout.h",
     ],
     copts = UPB_DEFAULT_CPPOPTS,
-    visibility = ["//third_party/upb/protos_generator:__pkg__"],
+    visibility = ["//protos_generator:__pkg__"],
     deps = [
         ":common",
         "//:mini_table",
@@ -112,7 +112,7 @@
         "keywords.h",
     ],
     copts = UPB_DEFAULT_CPPOPTS,
-    visibility = ["//third_party/upb/protos_generator:__pkg__"],
+    visibility = ["//protos_generator:__pkg__"],
 )
 
 cc_library(
@@ -124,7 +124,7 @@
         "names.h",
     ],
     copts = UPB_DEFAULT_CPPOPTS,
-    visibility = ["//third_party/upb/protos_generator:__pkg__"],
+    visibility = ["//protos_generator:__pkg__"],
     deps = [
         "@com_google_absl//absl/container:flat_hash_map",
         "@com_google_absl//absl/strings",
diff --git a/upbc/protoc-gen-upb.cc b/upbc/protoc-gen-upb.cc
index 72ee8b2..c97030a 100644
--- a/upbc/protoc-gen-upb.cc
+++ b/upbc/protoc-gen-upb.cc
@@ -141,7 +141,7 @@
     case protobuf::FieldDescriptor::CPPTYPE_ENUM:
       return std::to_string(2);
     case protobuf::FieldDescriptor::CPPTYPE_BOOL:
-      return std::to_string(1);
+      return std::to_string(0);
     case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
       return std::to_string(2);
     case protobuf::FieldDescriptor::CPPTYPE_INT32: