Added new, split upb rules for MiniTables and the C API.

The new rules are:
- `upb_minitable_proto_library()`: contains the MiniTables only
- `upb_c_proto_library()`: Contains the C API.  Depends on the MiniTables

This involved splitting upb code generation into two separate aspects, one for MiniTables and one for the C API.

PiperOrigin-RevId: 565518070
pull/14091/head
Joshua Haberman 2 years ago committed by Copybara-Service
parent f7de3bf1a9
commit 2b87999fc1
  1. 10
      upb/BUILD
  2. 1
      upb/bazel/BUILD
  3. 26
      upb/bazel/upb_c_proto_library.bzl
  4. 86
      upb/bazel/upb_minitable_proto_library.bzl
  5. 4
      upb/bazel/upb_proto_library_internal/aspect.bzl
  6. 22
      upb/bazel/upb_proto_reflection_library.bzl
  7. 11
      upb/cmake/BUILD.bazel
  8. 2
      upb/cmake/build_defs.bzl
  9. 3
      upb/cmake/make_cmakelists.py
  10. 41
      upb/upbc/BUILD
  11. 68
      upb/upbc/bootstrap_compiler.bzl
  12. 104
      upb/upbc/common.cc
  13. 12
      upb/upbc/common.h
  14. 626
      upb/upbc/protoc-gen-upb.cc
  15. 706
      upb/upbc/protoc-gen-upb_minitable.cc
  16. 2
      upb/upbc/protoc-gen-upbdefs.cc

@ -8,6 +8,7 @@
load("@rules_python//python:defs.bzl", "py_binary")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("//upb/bazel:build_defs.bzl", "UPB_DEFAULT_COPTS")
load("//upb/bazel:upb_minitable_proto_library.bzl", "upb_minitable_proto_library")
load("//upb/bazel:upb_proto_library.bzl", "upb_proto_reflection_library")
load("//upb/bazel:upb_proto_library_internal/copts.bzl", "upb_proto_library_copts")
load(
@ -173,6 +174,12 @@ upb_proto_reflection_library(
deps = ["//:descriptor_proto"],
)
upb_minitable_proto_library(
name = "descriptor_upb_minitable_proto",
visibility = ["//upb:__subpackages__"],
deps = ["//:descriptor_proto"],
)
# TODO(b/232091617): Once we can delete the deprecated forwarding headers
# (= everything in upb/) we can move this build target down into reflection/
bootstrap_cc_library(
@ -523,6 +530,7 @@ upb_amalgamation(
":base",
":base_internal",
":collections_internal",
":descriptor_upb_minitable_proto",
":descriptor_upb_proto",
":eps_copy_input_stream",
":generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
@ -569,6 +577,7 @@ upb_amalgamation(
":base",
":base_internal",
":collections_internal",
":descriptor_upb_minitable_proto",
":descriptor_upb_proto_reflection",
":descriptor_upb_proto",
":eps_copy_input_stream",
@ -619,6 +628,7 @@ upb_amalgamation(
":base",
":base_internal",
":collections_internal",
":descriptor_upb_minitable_proto",
":descriptor_upb_proto",
":eps_copy_input_stream",
":generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",

@ -51,6 +51,7 @@ bzl_library(
name = "upb_proto_library_bzl",
srcs = [
"upb_c_proto_library.bzl",
"upb_minitable_proto_library.bzl",
"upb_proto_library.bzl",
"upb_proto_reflection_library.bzl",
],

@ -1,5 +1,6 @@
"""upb_c_proto_library() exposes upb's generated C API for protobuf (foo.upb.h)"""
load("//upb/bazel:upb_minitable_proto_library.bzl", "UpbMinitableCcInfo", "upb_minitable_proto_library_aspect")
load("//upb/bazel:upb_proto_library_internal/aspect.bzl", "upb_proto_aspect_impl")
load("//upb/bazel:upb_proto_library_internal/cc_library_func.bzl", "upb_use_cpp_toolchain")
load("//upb/bazel:upb_proto_library_internal/rule.bzl", "upb_proto_rule_impl")
@ -20,24 +21,11 @@ def _upb_c_proto_library_aspect_impl(target, ctx):
ctx = ctx,
generator = "upb",
cc_provider = UpbWrappedCcInfo,
dep_cc_provider = None,
dep_cc_provider = UpbMinitableCcInfo,
file_provider = _UpbWrappedGeneratedSrcsInfo,
provide_cc_shared_library_hints = False,
)
def _get_upb_c_proto_library_aspect_provides():
provides = [
UpbWrappedCcInfo,
_UpbWrappedGeneratedSrcsInfo,
]
if hasattr(cc_common, "CcSharedLibraryHintInfo"):
provides.append(cc_common.CcSharedLibraryHintInfo)
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_getter_do_not_use"):
# This branch can be deleted once 6.X is not supported by upb rules
provides.append(cc_common.CcSharedLibraryHintInfo_6_X_getter_do_not_use)
return provides
upb_c_proto_library_aspect = aspect(
attrs = {
"_copts": attr.label(
@ -59,10 +47,14 @@ upb_c_proto_library_aspect = aspect(
"_upb": attr.label_list(default = [
"//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
]),
"_fasttable_enabled": attr.label(default = "//upb:fasttable_enabled"),
},
implementation = _upb_c_proto_library_aspect_impl,
provides = _get_upb_c_proto_library_aspect_provides(),
requires = [upb_minitable_proto_library_aspect],
required_aspect_providers = [UpbMinitableCcInfo],
provides = [
UpbWrappedCcInfo,
_UpbWrappedGeneratedSrcsInfo,
],
attr_aspects = ["deps"],
fragments = ["cpp"],
toolchains = upb_use_cpp_toolchain(),

@ -0,0 +1,86 @@
"""upb_minitable_proto_library() exposes upb's generated minitables (foo.upb_minitable.h)"""
load("//upb/bazel:upb_proto_library_internal/aspect.bzl", "upb_proto_aspect_impl")
load("//upb/bazel:upb_proto_library_internal/cc_library_func.bzl", "upb_use_cpp_toolchain")
load("//upb/bazel:upb_proto_library_internal/rule.bzl", "upb_proto_rule_impl")
UpbMinitableCcInfo = provider(
"Provider for cc_info for protos",
fields = ["cc_info"],
)
_UpbWrappedGeneratedSrcsInfo = provider(
"Provider for generated sources",
fields = ["srcs"],
)
def _upb_minitable_proto_library_aspect_impl(target, ctx):
return upb_proto_aspect_impl(
target = target,
ctx = ctx,
generator = "upb_minitable",
cc_provider = UpbMinitableCcInfo,
dep_cc_provider = None,
file_provider = _UpbWrappedGeneratedSrcsInfo,
)
def _get_upb_minitable_proto_library_aspect_provides():
provides = [
UpbMinitableCcInfo,
_UpbWrappedGeneratedSrcsInfo,
]
if hasattr(cc_common, "CcSharedLibraryHintInfo"):
provides.append(cc_common.CcSharedLibraryHintInfo)
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_getter_do_not_use"):
# This branch can be deleted once 6.X is not supported by upb rules
provides.append(cc_common.CcSharedLibraryHintInfo_6_X_getter_do_not_use)
return provides
upb_minitable_proto_library_aspect = aspect(
attrs = {
"_copts": attr.label(
default = "//upb:upb_proto_library_copts__for_generated_code_only_do_not_use",
),
"_gen_upb_minitable": attr.label(
executable = True,
cfg = "exec",
default = "//upb/upbc:protoc-gen-upb_minitable_stage1",
),
"_protoc": attr.label(
executable = True,
cfg = "exec",
default = "//:protoc",
),
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_upb_minitable": attr.label_list(default = [
"//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
]),
"_fasttable_enabled": attr.label(default = "//upb:fasttable_enabled"),
},
implementation = _upb_minitable_proto_library_aspect_impl,
provides = _get_upb_minitable_proto_library_aspect_provides(),
attr_aspects = ["deps"],
fragments = ["cpp"],
toolchains = upb_use_cpp_toolchain(),
incompatible_use_toolchain_transition = True,
)
def _upb_minitable_proto_library_rule_impl(ctx):
return upb_proto_rule_impl(ctx, UpbMinitableCcInfo, _UpbWrappedGeneratedSrcsInfo)
upb_minitable_proto_library = rule(
output_to_genfiles = True,
implementation = _upb_minitable_proto_library_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [upb_minitable_proto_library_aspect],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),
},
provides = [CcInfo],
)

@ -101,7 +101,7 @@ def _generate_upb_protos(ctx, generator, proto_info, proto_sources):
args.use_param_file(param_file_arg = "@%s")
args.set_param_file_format("multiline")
args.add("--" + generator + "_out=" + _get_real_root(ctx, srcs[0]))
args.add("--" + generator + "_out=" + _get_real_root(ctx, hdrs[0]))
args.add("--plugin=protoc-gen-" + generator + "=" + tool.path)
args.add("--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]))
args.add_all(proto_sources, map_each = _get_real_short_path)
@ -180,7 +180,7 @@ def _compile_upb_protos(ctx, files, generator, dep_ccinfos, cc_provider):
cc_info = cc_info,
)
_GENERATORS = ["upb", "upbdefs"]
_GENERATORS = ["upb", "upbdefs", "upb_minitable"]
def _get_hint_providers(ctx, generator):
if generator not in _GENERATORS:

@ -1,6 +1,6 @@
"""upb_c_proto_reflection_library() exposes upb reflection for protobuf (foo.upbdefs.h)"""
load("//upb/bazel:upb_c_proto_library.bzl", "UpbWrappedCcInfo", "upb_c_proto_library_aspect")
load("//upb/bazel:upb_minitable_proto_library.bzl", "UpbMinitableCcInfo", "upb_minitable_proto_library_aspect")
load("//upb/bazel:upb_proto_library_internal/aspect.bzl", "upb_proto_aspect_impl")
load("//upb/bazel:upb_proto_library_internal/cc_library_func.bzl", "upb_use_cpp_toolchain")
load("//upb/bazel:upb_proto_library_internal/rule.bzl", "upb_proto_rule_impl")
@ -18,7 +18,7 @@ def _upb_proto_reflection_library_aspect_impl(target, ctx):
ctx = ctx,
generator = "upbdefs",
cc_provider = _UpbDefsWrappedCcInfo,
dep_cc_provider = UpbWrappedCcInfo,
dep_cc_provider = UpbMinitableCcInfo,
file_provider = _WrappedDefsGeneratedSrcsInfo,
provide_cc_shared_library_hints = False,
)
@ -41,20 +41,17 @@ _upb_proto_reflection_library_aspect = aspect(
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_upbdefs": attr.label_list(
default = [
"//upb:generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
],
),
"_upbdefs": attr.label_list(default = [
"//upb:generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
]),
},
implementation = _upb_proto_reflection_library_aspect_impl,
requires = [upb_minitable_proto_library_aspect],
required_aspect_providers = [UpbMinitableCcInfo],
provides = [
_UpbDefsWrappedCcInfo,
_WrappedDefsGeneratedSrcsInfo,
],
required_aspect_providers = [
UpbWrappedCcInfo,
],
attr_aspects = ["deps"],
fragments = ["cpp"],
toolchains = upb_use_cpp_toolchain(),
@ -69,10 +66,7 @@ upb_proto_reflection_library = rule(
implementation = _upb_proto_reflection_library_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [
upb_c_proto_library_aspect,
_upb_proto_reflection_library_aspect,
],
aspects = [_upb_proto_reflection_library_aspect],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),

@ -45,10 +45,14 @@ genrule(
genrule(
name = "copy_protos",
srcs = ["//upb:descriptor_upb_proto"],
srcs = [
"//upb:descriptor_upb_proto",
"//upb:descriptor_upb_minitable_proto",
],
outs = [
"generated-in/google/protobuf/descriptor.upb.c",
"generated-in/google/protobuf/descriptor.upb.h",
"generated-in/google/protobuf/descriptor.upb_minitable.c",
"generated-in/google/protobuf/descriptor.upb_minitable.h",
],
cmd = "cp $(SRCS) $(@D)/generated-in/google/protobuf",
)
@ -57,8 +61,9 @@ staleness_test(
name = "test_generated_files",
outs = [
"CMakeLists.txt",
"google/protobuf/descriptor.upb.c",
"google/protobuf/descriptor.upb.h",
"google/protobuf/descriptor.upb_minitable.c",
"google/protobuf/descriptor.upb_minitable.h",
],
generated_pattern = "generated-in/%s",
tags = ["manual"],

@ -31,7 +31,7 @@ def staleness_test(name, outs, generated_pattern, target_files = None, tags = []
# Filter out non-existing rules so Blaze doesn't error out before we even
# run the test.
existing_outs = native.glob(include = outs)
existing_outs = native.glob(include = outs, allow_empty = True)
# The file list contains a few extra bits of information at the end.
# These get unpacked by the Config class in staleness_test_lib.py.

@ -187,6 +187,9 @@ class BuildFileFunctions(object):
def upb_proto_library(self, **kwargs):
pass
def upb_minitable_proto_library(self, **kwargs):
pass
def upb_proto_library_copts(self, **kwargs):
pass

@ -10,6 +10,10 @@ load(
"UPB_DEFAULT_COPTS",
"UPB_DEFAULT_CPPOPTS",
)
load(
"//upb/bazel:upb_minitable_proto_library.bzl",
"upb_minitable_proto_library",
)
load(
"//upb/bazel:upb_proto_library.bzl",
"upb_proto_library",
@ -47,6 +51,12 @@ upb_proto_reflection_library(
deps = [":code_generator_request"],
)
upb_minitable_proto_library(
name = "code_generator_request_upb_minitable_proto",
visibility = ["//upb:friends"],
deps = [":code_generator_request"],
)
bootstrap_upb_proto_library(
name = "plugin_upb_proto",
base_dir = "",
@ -92,6 +102,9 @@ bootstrap_cc_library(
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//upb/protos_generator:__pkg__"],
deps = [
"//upb:mini_table",
"//upb:mini_table_internal",
"//upb:port",
"@com_google_absl//absl/strings",
],
)
@ -250,6 +263,34 @@ bootstrap_cc_binary(
],
)
bootstrap_cc_binary(
name = "protoc-gen-upb_minitable",
srcs = ["protoc-gen-upb_minitable.cc"],
bootstrap_deps = [
":common",
":file_layout",
":names",
":plugin",
":plugin_upb_proto",
"//upb:descriptor_upb_proto",
"//upb:reflection",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
"//upb:base",
"//upb:mem",
"//upb:mini_table_internal",
"//upb:port",
"//upb:wire_types",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/strings",
],
)
cc_binary(
name = "protoc-gen-upbdefs",
srcs = [

@ -11,7 +11,7 @@ load(
_stages = ["_stage0", "_stage1", ""]
_protoc = "//:protoc"
_upbc_base = "//upb/upbc:protoc-gen-upb"
_upbc_base = "//upb/upbc:protoc-gen-"
# begin:google_only
# _is_google3 = True
@ -23,8 +23,8 @@ _is_google3 = False
_extra_proto_path = "-I$$(dirname $(location @com_google_protobuf//:descriptor_proto_srcs))/../.. "
# end:github_only
def _upbc(stage):
return _upbc_base + _stages[stage]
def _upbc(generator, stage):
return _upbc_base + generator + _stages[stage]
def bootstrap_cc_library(name, visibility, deps, bootstrap_deps, **kwargs):
for stage in _stages:
@ -47,19 +47,27 @@ def bootstrap_cc_binary(name, deps, bootstrap_deps, **kwargs):
def _generated_srcs_for_suffix(prefix, srcs, suffix):
return [prefix + "/" + src[:-len(".proto")] + suffix for src in srcs]
def _generated_srcs_for_generator(prefix, srcs, generator):
ret = _generated_srcs_for_suffix(prefix, srcs, ".{}.h".format(generator))
# if not (generator == "upb" and prefix.endswith("stage1")):
# ret += _generated_srcs_for_suffix(prefix, srcs, ".{}.c".format(generator))
ret += _generated_srcs_for_suffix(prefix, srcs, ".{}.c".format(generator))
return ret
def _generated_srcs(prefix, srcs):
return _generated_srcs_for_suffix(prefix, srcs, ".upb.h") + _generated_srcs_for_suffix(prefix, srcs, ".upb.c")
return _generated_srcs_for_generator(prefix, srcs, "upb")
def _stage0_proto_staleness_test(name, base_dir, src_files, src_rules, strip_prefix):
native.genrule(
name = name + "_generate_bootstrap",
srcs = src_rules,
outs = _generated_srcs("bootstrap_generated_sources/" + base_dir + "stage0", src_files),
tools = [_protoc, _upbc(0)],
tools = [_protoc, _upbc("upb", 0)],
cmd =
"$(location " + _protoc + ") " +
"-I$(GENDIR)/" + strip_prefix + " " + _extra_proto_path +
"--plugin=protoc-gen-upb=$(location " + _upbc(0) + ") " +
"--plugin=protoc-gen-upb=$(location " + _upbc("upb", 0) + ") " +
"--upb_out=bootstrap_upb:$(@D)/bootstrap_generated_sources/" + base_dir + "stage0 " +
" ".join(src_files),
)
@ -76,6 +84,24 @@ def _stage0_proto_staleness_test(name, base_dir, src_files, src_rules, strip_pre
tags = ["manual"],
)
def _generate_stage1_proto(name, base_dir, src_files, src_rules, generator, kwargs):
native.genrule(
name = "gen_{}_{}_stage1".format(name, generator),
srcs = src_rules,
outs = _generated_srcs_for_generator(base_dir + "stage1", src_files, generator),
cmd = "$(location " + _protoc + ") " +
"--plugin=protoc-gen-" + generator +
"=$(location " + _upbc(generator, 0) + ") " + _extra_proto_path +
"--" + generator + "_out=$(RULEDIR)/" + base_dir + "stage1 " +
" ".join(src_files),
visibility = ["//upb/upbc:__pkg__"],
tools = [
_protoc,
_upbc(generator, 0),
],
**kwargs
)
def bootstrap_upb_proto_library(
name,
base_dir,
@ -109,7 +135,7 @@ def bootstrap_upb_proto_library(
"""
_stage0_proto_staleness_test(name, base_dir, oss_src_files, oss_src_rules, oss_strip_prefix)
# stage0 uses checked-in protos.
# stage0 uses checked-in protos, and has no MiniTable.
native.cc_library(
name = name + "_stage0",
srcs = _generated_srcs_for_suffix(base_dir + "stage0", oss_src_files, ".upb.c"),
@ -128,31 +154,29 @@ def bootstrap_upb_proto_library(
src_files = google3_src_files if _is_google3 else oss_src_files
src_rules = google3_src_rules if _is_google3 else oss_src_rules
# Generate stage1 protos using stage0 compiler.
native.genrule(
name = "gen_" + name + "_stage1",
srcs = src_rules,
outs = _generated_srcs(base_dir + "stage1", src_files),
cmd = "$(location " + _protoc + ") " +
"--plugin=protoc-gen-upb=$(location " + _upbc(0) + ") " + _extra_proto_path +
"--upb_out=$(@D)/" + base_dir + "stage1 " +
" ".join(src_files),
# Generate stage1 protos (C API and MiniTables) using stage0 compiler.
_generate_stage1_proto(name, base_dir, src_files, src_rules, "upb", kwargs)
_generate_stage1_proto(name, base_dir, src_files, src_rules, "upb_minitable", kwargs)
native.cc_library(
name = name + "_minitable_stage1",
srcs = _generated_srcs_for_suffix(base_dir + "stage1", src_files, ".upb_minitable.c"),
hdrs = _generated_srcs_for_suffix(base_dir + "stage1", src_files, ".upb_minitable.h"),
includes = [base_dir + "stage1"],
visibility = ["//upb/upbc:__pkg__"],
tools = [
_protoc,
_upbc(0),
],
deps = [
"//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
] + [dep + "_stage1" for dep in deps],
**kwargs
)
native.cc_library(
name = name + "_stage1",
srcs = _generated_srcs_for_suffix(base_dir + "stage1", src_files, ".upb.c"),
hdrs = _generated_srcs_for_suffix(base_dir + "stage1", src_files, ".upb.h"),
includes = [base_dir + "stage1"],
visibility = ["//upb/upbc:__pkg__"],
deps = [
"//upb:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
":" + name + "_minitable_stage1",
] + [dep + "_stage1" for dep in deps],
**kwargs
)

@ -30,9 +30,24 @@
#include "upb/upbc/common.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string>
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "upb/upb/mini_table/field.h"
#include "upb/upb/mini_table/internal/field.h"
#include "upb/upb/reflection/def.hpp"
// Must be last
#include "upb/upb/port/def.inc"
namespace upbc {
std::string StripExtension(absl::string_view fname) {
@ -71,10 +86,14 @@ std::string FileLayoutName(upb::FileDefPtr file) {
return ToCIdent(file.name()) + "_upb_file_layout";
}
std::string HeaderFilename(upb::FileDefPtr file) {
std::string CApiHeaderFilename(upb::FileDefPtr file) {
return StripExtension(file.name()) + ".upb.h";
}
std::string MiniTableHeaderFilename(upb::FileDefPtr file) {
return StripExtension(file.name()) + ".upb_minitable.h";
}
std::string MessageInit(absl::string_view full_name) {
return ToCIdent(full_name) + "_msg_init";
}
@ -83,4 +102,87 @@ std::string EnumInit(upb::EnumDefPtr descriptor) {
return ToCIdent(descriptor.full_name()) + "_enum_init";
}
std::string FieldInitializer(upb::FieldDefPtr field,
const upb_MiniTableField* field64,
const upb_MiniTableField* field32) {
return absl::Substitute(
"{$0, $1, $2, $3, $4, $5}", field64->number,
ArchDependentSize(field32->offset, field64->offset),
ArchDependentSize(field32->presence, field64->presence),
field64->UPB_PRIVATE(submsg_index) == kUpb_NoSub
? "kUpb_NoSub"
: absl::StrCat(field64->UPB_PRIVATE(submsg_index)).c_str(),
field64->UPB_PRIVATE(descriptortype), GetModeInit(field32, field64));
}
std::string ArchDependentSize(int64_t size32, int64_t size64) {
if (size32 == size64) return absl::StrCat(size32);
return absl::Substitute("UPB_SIZE($0, $1)", size32, size64);
}
// Returns the field mode as a string initializer.
//
// We could just emit this as a number (and we may yet go in that direction) but
// for now emitting symbolic constants gives this better readability and
// debuggability.
std::string GetModeInit(const upb_MiniTableField* field32,
const upb_MiniTableField* field64) {
std::string ret;
uint8_t mode32 = field32->mode;
switch (mode32 & kUpb_FieldMode_Mask) {
case kUpb_FieldMode_Map:
ret = "(int)kUpb_FieldMode_Map";
break;
case kUpb_FieldMode_Array:
ret = "(int)kUpb_FieldMode_Array";
break;
case kUpb_FieldMode_Scalar:
ret = "(int)kUpb_FieldMode_Scalar";
break;
default:
break;
}
if (mode32 & kUpb_LabelFlags_IsPacked) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsPacked");
}
if (mode32 & kUpb_LabelFlags_IsExtension) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsExtension");
}
if (mode32 & kUpb_LabelFlags_IsAlternate) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsAlternate");
}
absl::StrAppend(&ret, " | ((int)", GetFieldRep(field32, field64),
" << kUpb_FieldRep_Shift)");
return ret;
}
std::string GetFieldRep(const upb_MiniTableField* field32,
const upb_MiniTableField* field64) {
switch (_upb_MiniTableField_GetRep(field32)) {
case kUpb_FieldRep_1Byte:
return "kUpb_FieldRep_1Byte";
break;
case kUpb_FieldRep_4Byte: {
if (_upb_MiniTableField_GetRep(field64) == kUpb_FieldRep_4Byte) {
return "kUpb_FieldRep_4Byte";
} else {
assert(_upb_MiniTableField_GetRep(field64) == kUpb_FieldRep_8Byte);
return "UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte)";
}
break;
}
case kUpb_FieldRep_StringView:
return "kUpb_FieldRep_StringView";
break;
case kUpb_FieldRep_8Byte:
return "kUpb_FieldRep_8Byte";
break;
}
UPB_UNREACHABLE();
}
} // namespace upbc

@ -82,11 +82,21 @@ std::string ToPreproc(absl::string_view str);
void EmitFileWarning(absl::string_view name, Output& output);
std::string MessageName(upb::MessageDefPtr descriptor);
std::string FileLayoutName(upb::FileDefPtr file);
std::string HeaderFilename(upb::FileDefPtr file);
std::string MiniTableHeaderFilename(upb::FileDefPtr file);
std::string CApiHeaderFilename(upb::FileDefPtr file);
std::string MessageInit(absl::string_view full_name);
std::string EnumInit(upb::EnumDefPtr descriptor);
std::string FieldInitializer(upb::FieldDefPtr field,
const upb_MiniTableField* field64,
const upb_MiniTableField* field32);
std::string ArchDependentSize(int64_t size32, int64_t size64);
std::string GetModeInit(const upb_MiniTableField* field32,
const upb_MiniTableField* field64);
std::string GetFieldRep(const upb_MiniTableField* field32,
const upb_MiniTableField* field64);
} // namespace upbc
#endif // UPBC_COMMON_H

@ -68,25 +68,6 @@ struct Options {
bool bootstrap = false;
};
// Returns fields in order of "hotness", eg. how frequently they appear in
// serialized payloads. Ideally this will use a profile. When we don't have
// that, we assume that fields with smaller numbers are used more frequently.
inline std::vector<upb::FieldDefPtr> FieldHotnessOrder(
upb::MessageDefPtr message) {
std::vector<upb::FieldDefPtr> fields;
size_t field_count = message.field_count();
fields.reserve(field_count);
for (size_t i = 0; i < field_count; i++) {
fields.push_back(message.field(i));
}
std::sort(fields.begin(), fields.end(),
[](upb::FieldDefPtr a, upb::FieldDefPtr b) {
return std::make_pair(!a.is_required(), a.number()) <
std::make_pair(!b.is_required(), b.number());
});
return fields;
}
std::string SourceFilename(upb::FileDefPtr file) {
return StripExtension(file.name()) + ".upb.c";
}
@ -131,10 +112,6 @@ std::string ExtensionLayout(upb::FieldDefPtr ext) {
return absl::StrCat(ExtensionIdentBase(ext), "_", ext.name(), "_ext");
}
const char* kEnumsInit = "enums_layout";
const char* kExtensionsInit = "extensions_layout";
const char* kMessagesInit = "messages_layout";
std::string EnumValueSymbol(upb::EnumValDefPtr value) {
return ToCIdent(value.full_name());
}
@ -300,7 +277,9 @@ void DumpEnumValues(upb::EnumDefPtr desc, Output& output) {
}
}
std::string GetFieldRep(const DefPoolPair& pools, upb::FieldDefPtr field);
std::string GetFieldRep(const DefPoolPair& pools, upb::FieldDefPtr field) {
return upbc::GetFieldRep(pools.GetField32(field), pools.GetField64(field));
}
void GenerateExtensionInHeader(const DefPoolPair& pools, upb::FieldDefPtr ext,
Output& output) {
@ -859,7 +838,7 @@ void WriteHeader(const DefPoolPair& pools, upb::FileDefPtr file,
if (i == 0) {
output("/* Public Imports. */\n");
}
output("#include \"$0\"\n", HeaderFilename(file.public_dependency(i)));
output("#include \"$0\"\n", CApiHeaderFilename(file.public_dependency(i)));
if (i == file.public_dependency_count() - 1) {
output("\n");
}
@ -991,311 +970,6 @@ void WriteHeader(const DefPoolPair& pools, upb::FileDefPtr file,
ToPreproc(file.name()));
}
typedef std::pair<std::string, uint64_t> TableEntry;
uint32_t GetWireTypeForField(upb::FieldDefPtr field) {
if (field.packed()) return kUpb_WireType_Delimited;
switch (field.type()) {
case kUpb_FieldType_Double:
case kUpb_FieldType_Fixed64:
case kUpb_FieldType_SFixed64:
return kUpb_WireType_64Bit;
case kUpb_FieldType_Float:
case kUpb_FieldType_Fixed32:
case kUpb_FieldType_SFixed32:
return kUpb_WireType_32Bit;
case kUpb_FieldType_Int64:
case kUpb_FieldType_UInt64:
case kUpb_FieldType_Int32:
case kUpb_FieldType_Bool:
case kUpb_FieldType_UInt32:
case kUpb_FieldType_Enum:
case kUpb_FieldType_SInt32:
case kUpb_FieldType_SInt64:
return kUpb_WireType_Varint;
case kUpb_FieldType_Group:
return kUpb_WireType_StartGroup;
case kUpb_FieldType_Message:
case kUpb_FieldType_String:
case kUpb_FieldType_Bytes:
return kUpb_WireType_Delimited;
}
UPB_UNREACHABLE();
}
uint32_t MakeTag(uint32_t field_number, uint32_t wire_type) {
return field_number << 3 | wire_type;
}
size_t WriteVarint32ToArray(uint64_t val, char* buf) {
size_t i = 0;
do {
uint8_t byte = val & 0x7fU;
val >>= 7;
if (val) byte |= 0x80U;
buf[i++] = byte;
} while (val);
return i;
}
uint64_t GetEncodedTag(upb::FieldDefPtr field) {
uint32_t wire_type = GetWireTypeForField(field);
uint32_t unencoded_tag = MakeTag(field.number(), wire_type);
char tag_bytes[10] = {0};
WriteVarint32ToArray(unencoded_tag, tag_bytes);
uint64_t encoded_tag = 0;
memcpy(&encoded_tag, tag_bytes, sizeof(encoded_tag));
// TODO: byte-swap for big endian.
return encoded_tag;
}
int GetTableSlot(upb::FieldDefPtr field) {
uint64_t tag = GetEncodedTag(field);
if (tag > 0x7fff) {
// Tag must fit within a two-byte varint.
return -1;
}
return (tag & 0xf8) >> 3;
}
bool TryFillTableEntry(const DefPoolPair& pools, upb::FieldDefPtr field,
TableEntry& ent) {
const upb_MiniTable* mt = pools.GetMiniTable64(field.containing_type());
const upb_MiniTableField* mt_f =
upb_MiniTable_FindFieldByNumber(mt, field.number());
std::string type = "";
std::string cardinality = "";
switch (upb_MiniTableField_Type(mt_f)) {
case kUpb_FieldType_Bool:
type = "b1";
break;
case kUpb_FieldType_Enum:
if (upb_MiniTableField_IsClosedEnum(mt_f)) {
// We don't have the means to test proto2 enum fields for valid values.
return false;
}
[[fallthrough]];
case kUpb_FieldType_Int32:
case kUpb_FieldType_UInt32:
type = "v4";
break;
case kUpb_FieldType_Int64:
case kUpb_FieldType_UInt64:
type = "v8";
break;
case kUpb_FieldType_Fixed32:
case kUpb_FieldType_SFixed32:
case kUpb_FieldType_Float:
type = "f4";
break;
case kUpb_FieldType_Fixed64:
case kUpb_FieldType_SFixed64:
case kUpb_FieldType_Double:
type = "f8";
break;
case kUpb_FieldType_SInt32:
type = "z4";
break;
case kUpb_FieldType_SInt64:
type = "z8";
break;
case kUpb_FieldType_String:
type = "s";
break;
case kUpb_FieldType_Bytes:
type = "b";
break;
case kUpb_FieldType_Message:
type = "m";
break;
default:
return false; // Not supported yet.
}
switch (upb_FieldMode_Get(mt_f)) {
case kUpb_FieldMode_Map:
return false; // Not supported yet (ever?).
case kUpb_FieldMode_Array:
if (mt_f->mode & kUpb_LabelFlags_IsPacked) {
cardinality = "p";
} else {
cardinality = "r";
}
break;
case kUpb_FieldMode_Scalar:
if (mt_f->presence < 0) {
cardinality = "o";
} else {
cardinality = "s";
}
break;
}
uint64_t expected_tag = GetEncodedTag(field);
// Data is:
//
// 48 32 16 0
// |--------|--------|--------|--------|--------|--------|--------|--------|
// | offset (16) |case offset (16) |presence| submsg | exp. tag (16) |
// |--------|--------|--------|--------|--------|--------|--------|--------|
//
// - |presence| is either hasbit index or field number for oneofs.
uint64_t data = static_cast<uint64_t>(mt_f->offset) << 48 | expected_tag;
if (field.IsSequence()) {
// No hasbit/oneof-related fields.
}
if (field.real_containing_oneof()) {
uint64_t case_offset = ~mt_f->presence;
if (case_offset > 0xffff || field.number() > 0xff) return false;
data |= field.number() << 24;
data |= case_offset << 32;
} else {
uint64_t hasbit_index = 63; // No hasbit (set a high, unused bit).
if (mt_f->presence) {
hasbit_index = mt_f->presence;
if (hasbit_index > 31) return false;
}
data |= hasbit_index << 24;
}
if (field.ctype() == kUpb_CType_Message) {
uint64_t idx = mt_f->UPB_PRIVATE(submsg_index);
if (idx > 255) return false;
data |= idx << 16;
std::string size_ceil = "max";
size_t size = SIZE_MAX;
if (field.message_type().file() == field.file()) {
// We can only be guaranteed the size of the sub-message if it is in the
// same file as us. We could relax this to increase the speed of
// cross-file sub-message parsing if we are comfortable requiring that
// users compile all messages at the same time.
const upb_MiniTable* sub_mt = pools.GetMiniTable64(field.message_type());
size = sub_mt->size + 8;
}
std::vector<size_t> breaks = {64, 128, 192, 256};
for (auto brk : breaks) {
if (size <= brk) {
size_ceil = std::to_string(brk);
break;
}
}
ent.first = absl::Substitute("upb_p$0$1_$2bt_max$3b", cardinality, type,
expected_tag > 0xff ? "2" : "1", size_ceil);
} else {
ent.first = absl::Substitute("upb_p$0$1_$2bt", cardinality, type,
expected_tag > 0xff ? "2" : "1");
}
ent.second = data;
return true;
}
std::vector<TableEntry> FastDecodeTable(upb::MessageDefPtr message,
const DefPoolPair& pools) {
std::vector<TableEntry> table;
for (const auto field : FieldHotnessOrder(message)) {
TableEntry ent;
int slot = GetTableSlot(field);
// std::cerr << "table slot: " << field->number() << ": " << slot << "\n";
if (slot < 0) {
// Tag can't fit in the table.
continue;
}
if (!TryFillTableEntry(pools, field, ent)) {
// Unsupported field type or offset, hasbit index, etc. doesn't fit.
continue;
}
while ((size_t)slot >= table.size()) {
size_t size = std::max(static_cast<size_t>(1), table.size() * 2);
table.resize(size, TableEntry{"_upb_FastDecoder_DecodeGeneric", 0});
}
if (table[slot].first != "_upb_FastDecoder_DecodeGeneric") {
// A hotter field already filled this slot.
continue;
}
table[slot] = ent;
}
return table;
}
std::string ArchDependentSize(int64_t size32, int64_t size64) {
if (size32 == size64) return absl::StrCat(size32);
return absl::Substitute("UPB_SIZE($0, $1)", size32, size64);
}
std::string GetFieldRep(const upb_MiniTableField* field32,
const upb_MiniTableField* field64) {
switch (_upb_MiniTableField_GetRep(field32)) {
case kUpb_FieldRep_1Byte:
return "kUpb_FieldRep_1Byte";
break;
case kUpb_FieldRep_4Byte: {
if (_upb_MiniTableField_GetRep(field64) == kUpb_FieldRep_4Byte) {
return "kUpb_FieldRep_4Byte";
} else {
assert(_upb_MiniTableField_GetRep(field64) == kUpb_FieldRep_8Byte);
return "UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte)";
}
break;
}
case kUpb_FieldRep_StringView:
return "kUpb_FieldRep_StringView";
break;
case kUpb_FieldRep_8Byte:
return "kUpb_FieldRep_8Byte";
break;
}
UPB_UNREACHABLE();
}
std::string GetFieldRep(const DefPoolPair& pools, upb::FieldDefPtr field) {
return GetFieldRep(pools.GetField32(field), pools.GetField64(field));
}
// Returns the field mode as a string initializer.
//
// We could just emit this as a number (and we may yet go in that direction) but
// for now emitting symbolic constants gives this better readability and
// debuggability.
std::string GetModeInit(const upb_MiniTableField* field32,
const upb_MiniTableField* field64) {
std::string ret;
uint8_t mode32 = field32->mode;
switch (mode32 & kUpb_FieldMode_Mask) {
case kUpb_FieldMode_Map:
ret = "(int)kUpb_FieldMode_Map";
break;
case kUpb_FieldMode_Array:
ret = "(int)kUpb_FieldMode_Array";
break;
case kUpb_FieldMode_Scalar:
ret = "(int)kUpb_FieldMode_Scalar";
break;
default:
break;
}
if (mode32 & kUpb_LabelFlags_IsPacked) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsPacked");
}
if (mode32 & kUpb_LabelFlags_IsExtension) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsExtension");
}
if (mode32 & kUpb_LabelFlags_IsAlternate) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsAlternate");
}
absl::StrAppend(&ret, " | ((int)", GetFieldRep(field32, field64),
" << kUpb_FieldRep_Shift)");
return ret;
}
std::string FieldInitializer(upb::FieldDefPtr field,
const upb_MiniTableField* field64,
const upb_MiniTableField* field32,
@ -1306,14 +980,7 @@ std::string FieldInitializer(upb::FieldDefPtr field,
"*upb_MiniTable_FindFieldByNumber($0, $1)",
MessageMiniTableRef(field.containing_type(), options), field.number());
} else {
return absl::Substitute(
"{$0, $1, $2, $3, $4, $5}", field64->number,
ArchDependentSize(field32->offset, field64->offset),
ArchDependentSize(field32->presence, field64->presence),
field64->UPB_PRIVATE(submsg_index) == kUpb_NoSub
? "kUpb_NoSub"
: absl::StrCat(field64->UPB_PRIVATE(submsg_index)).c_str(),
field64->UPB_PRIVATE(descriptortype), GetModeInit(field32, field64));
return upbc::FieldInitializer(field, field64, field32);
}
}
@ -1323,262 +990,6 @@ std::string FieldInitializer(const DefPoolPair& pools, upb::FieldDefPtr field,
pools.GetField32(field), options);
}
// Writes a single field into a .upb.c source file.
void WriteMessageField(upb::FieldDefPtr field,
const upb_MiniTableField* field64,
const upb_MiniTableField* field32,
const Options& options, Output& output) {
output(" $0,\n", FieldInitializer(field, field64, field32, options));
}
std::string GetSub(upb::FieldDefPtr field) {
if (auto message_def = field.message_type()) {
return absl::Substitute("{.submsg = &$0}", MessageInitName(message_def));
}
if (auto enum_def = field.enum_subdef()) {
if (enum_def.is_closed()) {
return absl::Substitute("{.subenum = &$0}", EnumInit(enum_def));
}
}
return std::string("{.submsg = NULL}");
}
// Writes a single message into a .upb.c source file.
void WriteMessage(upb::MessageDefPtr message, const DefPoolPair& pools,
const Options& options, Output& output) {
std::string msg_name = ToCIdent(message.full_name());
std::string fields_array_ref = "NULL";
std::string submsgs_array_ref = "NULL";
std::string subenums_array_ref = "NULL";
const upb_MiniTable* mt_32 = pools.GetMiniTable32(message);
const upb_MiniTable* mt_64 = pools.GetMiniTable64(message);
std::map<int, std::string> subs;
for (int i = 0; i < mt_64->field_count; i++) {
const upb_MiniTableField* f = &mt_64->fields[i];
uint32_t index = f->UPB_PRIVATE(submsg_index);
if (index != kUpb_NoSub) {
auto pair =
subs.emplace(index, GetSub(message.FindFieldByNumber(f->number)));
ABSL_CHECK(pair.second);
}
}
if (!subs.empty()) {
std::string submsgs_array_name = msg_name + "_submsgs";
submsgs_array_ref = "&" + submsgs_array_name + "[0]";
output("static const upb_MiniTableSub $0[$1] = {\n", submsgs_array_name,
subs.size());
int i = 0;
for (const auto& pair : subs) {
ABSL_CHECK(pair.first == i++);
output(" $0,\n", pair.second);
}
output("};\n\n");
}
if (mt_64->field_count > 0) {
std::string fields_array_name = msg_name + "__fields";
fields_array_ref = "&" + fields_array_name + "[0]";
output("static const upb_MiniTableField $0[$1] = {\n", fields_array_name,
mt_64->field_count);
for (int i = 0; i < mt_64->field_count; i++) {
WriteMessageField(message.FindFieldByNumber(mt_64->fields[i].number),
&mt_64->fields[i], &mt_32->fields[i], options, output);
}
output("};\n\n");
}
std::vector<TableEntry> table;
uint8_t table_mask = -1;
table = FastDecodeTable(message, pools);
if (table.size() > 1) {
assert((table.size() & (table.size() - 1)) == 0);
table_mask = (table.size() - 1) << 3;
}
std::string msgext = "kUpb_ExtMode_NonExtendable";
if (message.extension_range_count()) {
if (UPB_DESC(MessageOptions_message_set_wire_format)(message.options())) {
msgext = "kUpb_ExtMode_IsMessageSet";
} else {
msgext = "kUpb_ExtMode_Extendable";
}
}
output("const upb_MiniTable $0 = {\n", MessageInitName(message));
output(" $0,\n", submsgs_array_ref);
output(" $0,\n", fields_array_ref);
output(" $0, $1, $2, $3, UPB_FASTTABLE_MASK($4), $5,\n",
ArchDependentSize(mt_32->size, mt_64->size), mt_64->field_count,
msgext, mt_64->dense_below, table_mask, mt_64->required_count);
if (!table.empty()) {
output(" UPB_FASTTABLE_INIT({\n");
for (const auto& ent : table) {
output(" {0x$1, &$0},\n", ent.first,
absl::StrCat(absl::Hex(ent.second, absl::kZeroPad16)));
}
output(" })\n");
}
output("};\n\n");
}
void WriteEnum(upb::EnumDefPtr e, Output& output) {
std::string values_init = "{\n";
const upb_MiniTableEnum* mt = e.mini_table();
uint32_t value_count = (mt->mask_limit / 32) + mt->value_count;
for (uint32_t i = 0; i < value_count; i++) {
absl::StrAppend(&values_init, " 0x", absl::Hex(mt->data[i]),
",\n");
}
values_init += " }";
output(
R"cc(
const upb_MiniTableEnum $0 = {
$1,
$2,
$3,
};
)cc",
EnumInit(e), mt->mask_limit, mt->value_count, values_init);
output("\n");
}
int WriteEnums(const DefPoolPair& pools, upb::FileDefPtr file, Output& output) {
if (file.syntax() != kUpb_Syntax_Proto2) return 0;
std::vector<upb::EnumDefPtr> this_file_enums = SortedEnums(file);
for (const auto e : this_file_enums) {
WriteEnum(e, output);
}
if (!this_file_enums.empty()) {
output("static const upb_MiniTableEnum *$0[$1] = {\n", kEnumsInit,
this_file_enums.size());
for (const auto e : this_file_enums) {
output(" &$0,\n", EnumInit(e));
}
output("};\n");
output("\n");
}
return this_file_enums.size();
}
int WriteMessages(const DefPoolPair& pools, upb::FileDefPtr file,
const Options& options, Output& output) {
std::vector<upb::MessageDefPtr> file_messages = SortedMessages(file);
if (file_messages.empty()) return 0;
for (auto message : file_messages) {
WriteMessage(message, pools, options, output);
}
output("static const upb_MiniTable *$0[$1] = {\n", kMessagesInit,
file_messages.size());
for (auto message : file_messages) {
output(" &$0,\n", MessageInitName(message));
}
output("};\n");
output("\n");
return file_messages.size();
}
void WriteExtension(upb::FieldDefPtr ext, const DefPoolPair& pools,
const Options& options, Output& output) {
output("$0,\n", FieldInitializer(pools, ext, options));
output(" &$0,\n", MessageInitName(ext.containing_type()));
output(" $0,\n", GetSub(ext));
}
int WriteExtensions(const DefPoolPair& pools, upb::FileDefPtr file,
const Options& options, Output& output) {
auto exts = SortedExtensions(file);
if (exts.empty()) return 0;
// Order by full name for consistent ordering.
std::map<std::string, upb::MessageDefPtr> forward_messages;
for (auto ext : exts) {
forward_messages[ext.containing_type().full_name()] = ext.containing_type();
if (ext.message_type()) {
forward_messages[ext.message_type().full_name()] = ext.message_type();
}
}
for (const auto& decl : forward_messages) {
ForwardDeclareMiniTableInit(decl.second, options, output);
}
for (auto ext : exts) {
output("const upb_MiniTableExtension $0 = {\n ", ExtensionLayout(ext));
WriteExtension(ext, pools, options, output);
output("\n};\n");
}
output(
"\n"
"static const upb_MiniTableExtension *$0[$1] = {\n",
kExtensionsInit, exts.size());
for (auto ext : exts) {
output(" &$0,\n", ExtensionLayout(ext));
}
output(
"};\n"
"\n");
return exts.size();
}
void WriteMiniTableSource(const DefPoolPair& pools, upb::FileDefPtr file,
const Options& options, Output& output) {
EmitFileWarning(file.name(), output);
output(
"#include <stddef.h>\n"
"#include \"upb/upb/generated_code_support.h\"\n"
"#include \"$0\"\n",
HeaderFilename(file));
for (int i = 0; i < file.dependency_count(); i++) {
output("#include \"$0\"\n", HeaderFilename(file.dependency(i)));
}
output(
"\n"
"// Must be last.\n"
"#include \"upb/upb/port/def.inc\"\n"
"\n");
int msg_count = WriteMessages(pools, file, options, output);
int ext_count = WriteExtensions(pools, file, options, output);
int enum_count = WriteEnums(pools, file, output);
output("const upb_MiniTableFile $0 = {\n", FileLayoutName(file));
output(" $0,\n", msg_count ? kMessagesInit : "NULL");
output(" $0,\n", enum_count ? kEnumsInit : "NULL");
output(" $0,\n", ext_count ? kExtensionsInit : "NULL");
output(" $0,\n", msg_count);
output(" $0,\n", enum_count);
output(" $0,\n", ext_count);
output("};\n\n");
output("#include \"upb/upb/port/undef.inc\"\n");
output("\n");
}
void WriteMessageMiniDescriptorInitializer(upb::MessageDefPtr msg,
const Options& options,
Output& output) {
@ -1642,10 +1053,10 @@ void WriteMiniDescriptorSource(const DefPoolPair& pools, upb::FileDefPtr file,
"#include <stddef.h>\n"
"#include \"upb/upb/generated_code_support.h\"\n"
"#include \"$0\"\n\n",
HeaderFilename(file));
CApiHeaderFilename(file));
for (int i = 0; i < file.dependency_count(); i++) {
output("#include \"$0\"\n", HeaderFilename(file.dependency(i)));
output("#include \"$0\"\n", CApiHeaderFilename(file.dependency(i)));
}
output(
@ -1668,24 +1079,21 @@ void WriteMiniDescriptorSource(const DefPoolPair& pools, upb::FileDefPtr file,
}
}
void WriteSource(const DefPoolPair& pools, upb::FileDefPtr file,
const Options& options, Output& output) {
if (options.bootstrap) {
WriteMiniDescriptorSource(pools, file, options, output);
} else {
WriteMiniTableSource(pools, file, options, output);
}
}
void GenerateFile(const DefPoolPair& pools, upb::FileDefPtr file,
const Options& options, Plugin* plugin) {
Output h_output;
WriteHeader(pools, file, options, h_output);
plugin->AddOutputFile(HeaderFilename(file), h_output.output());
plugin->AddOutputFile(CApiHeaderFilename(file), h_output.output());
Output c_output;
WriteSource(pools, file, options, c_output);
plugin->AddOutputFile(SourceFilename(file), c_output.output());
if (options.bootstrap) {
Output c_output;
WriteMiniDescriptorSource(pools, file, options, c_output);
plugin->AddOutputFile(SourceFilename(file), c_output.output());
} else {
// TODO: remove once we can figure out how to make both Blaze and Bazel
// happy with header-only libraries.
plugin->AddOutputFile(SourceFilename(file), "\n");
}
}
bool ParseOptions(Plugin* plugin, Options* options) {

@ -0,0 +1,706 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "upb/upb/base/descriptor_constants.h"
#include "upb/upb/base/string_view.h"
#include "upb/upb/reflection/def.hpp"
#include "upb/upb/wire/types.h"
#include "upb/upbc/common.h"
#include "upb/upbc/file_layout.h"
#include "upb/upbc/names.h"
#include "upb/upbc/plugin.h"
// Must be last.
#include "upb/upb/port/def.inc"
namespace upbc {
namespace {
// Returns fields in order of "hotness", eg. how frequently they appear in
// serialized payloads. Ideally this will use a profile. When we don't have
// that, we assume that fields with smaller numbers are used more frequently.
inline std::vector<upb::FieldDefPtr> FieldHotnessOrder(
upb::MessageDefPtr message) {
std::vector<upb::FieldDefPtr> fields;
size_t field_count = message.field_count();
fields.reserve(field_count);
for (size_t i = 0; i < field_count; i++) {
fields.push_back(message.field(i));
}
std::sort(fields.begin(), fields.end(),
[](upb::FieldDefPtr a, upb::FieldDefPtr b) {
return std::make_pair(!a.is_required(), a.number()) <
std::make_pair(!b.is_required(), b.number());
});
return fields;
}
std::string SourceFilename(upb::FileDefPtr file) {
return StripExtension(file.name()) + ".upb_minitable.c";
}
std::string MessageInitName(upb::MessageDefPtr descriptor) {
return absl::StrCat(MessageName(descriptor), "_msg_init");
}
std::string ExtensionIdentBase(upb::FieldDefPtr 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 ExtensionLayout(upb::FieldDefPtr ext) {
return absl::StrCat(ExtensionIdentBase(ext), "_", ext.name(), "_ext");
}
const char* kEnumsInit = "enums_layout";
const char* kExtensionsInit = "extensions_layout";
const char* kMessagesInit = "messages_layout";
void WriteHeader(const DefPoolPair& pools, upb::FileDefPtr file,
Output& output) {
EmitFileWarning(file.name(), output);
output(
"#ifndef $0_UPB_MINITABLE_H_\n"
"#define $0_UPB_MINITABLE_H_\n\n"
"#include \"upb/upb/generated_code_support.h\"\n",
ToPreproc(file.name()));
for (int i = 0; i < file.public_dependency_count(); i++) {
if (i == 0) {
output("/* Public Imports. */\n");
}
output("#include \"$0\"\n",
MiniTableHeaderFilename(file.public_dependency(i)));
if (i == file.public_dependency_count() - 1) {
output("\n");
}
}
output(
"\n"
"// Must be last.\n"
"#include \"upb/upb/port/def.inc\"\n"
"\n"
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n"
"\n");
const std::vector<upb::MessageDefPtr> this_file_messages =
SortedMessages(file);
for (auto message : this_file_messages) {
output("extern const upb_MiniTable $0;\n", MessageInitName(message));
}
output("\n");
std::vector<upb::EnumDefPtr> this_file_enums = SortedEnums(file);
if (file.syntax() == kUpb_Syntax_Proto2) {
for (const auto enumdesc : this_file_enums) {
output("extern const upb_MiniTableEnum $0;\n", EnumInit(enumdesc));
}
}
output("extern const upb_MiniTableFile $0;\n\n", FileLayoutName(file));
output(
"#ifdef __cplusplus\n"
"} /* extern \"C\" */\n"
"#endif\n"
"\n"
"#include \"upb/upb/port/undef.inc\"\n"
"\n"
"#endif /* $0_UPB_MINITABLE_H_ */\n",
ToPreproc(file.name()));
}
typedef std::pair<std::string, uint64_t> TableEntry;
uint32_t GetWireTypeForField(upb::FieldDefPtr field) {
if (field.packed()) return kUpb_WireType_Delimited;
switch (field.type()) {
case kUpb_FieldType_Double:
case kUpb_FieldType_Fixed64:
case kUpb_FieldType_SFixed64:
return kUpb_WireType_64Bit;
case kUpb_FieldType_Float:
case kUpb_FieldType_Fixed32:
case kUpb_FieldType_SFixed32:
return kUpb_WireType_32Bit;
case kUpb_FieldType_Int64:
case kUpb_FieldType_UInt64:
case kUpb_FieldType_Int32:
case kUpb_FieldType_Bool:
case kUpb_FieldType_UInt32:
case kUpb_FieldType_Enum:
case kUpb_FieldType_SInt32:
case kUpb_FieldType_SInt64:
return kUpb_WireType_Varint;
case kUpb_FieldType_Group:
return kUpb_WireType_StartGroup;
case kUpb_FieldType_Message:
case kUpb_FieldType_String:
case kUpb_FieldType_Bytes:
return kUpb_WireType_Delimited;
}
UPB_UNREACHABLE();
}
uint32_t MakeTag(uint32_t field_number, uint32_t wire_type) {
return field_number << 3 | wire_type;
}
size_t WriteVarint32ToArray(uint64_t val, char* buf) {
size_t i = 0;
do {
uint8_t byte = val & 0x7fU;
val >>= 7;
if (val) byte |= 0x80U;
buf[i++] = byte;
} while (val);
return i;
}
uint64_t GetEncodedTag(upb::FieldDefPtr field) {
uint32_t wire_type = GetWireTypeForField(field);
uint32_t unencoded_tag = MakeTag(field.number(), wire_type);
char tag_bytes[10] = {0};
WriteVarint32ToArray(unencoded_tag, tag_bytes);
uint64_t encoded_tag = 0;
memcpy(&encoded_tag, tag_bytes, sizeof(encoded_tag));
// TODO: byte-swap for big endian.
return encoded_tag;
}
int GetTableSlot(upb::FieldDefPtr field) {
uint64_t tag = GetEncodedTag(field);
if (tag > 0x7fff) {
// Tag must fit within a two-byte varint.
return -1;
}
return (tag & 0xf8) >> 3;
}
bool TryFillTableEntry(const DefPoolPair& pools, upb::FieldDefPtr field,
TableEntry& ent) {
const upb_MiniTable* mt = pools.GetMiniTable64(field.containing_type());
const upb_MiniTableField* mt_f =
upb_MiniTable_FindFieldByNumber(mt, field.number());
std::string type = "";
std::string cardinality = "";
switch (upb_MiniTableField_Type(mt_f)) {
case kUpb_FieldType_Bool:
type = "b1";
break;
case kUpb_FieldType_Enum:
if (upb_MiniTableField_IsClosedEnum(mt_f)) {
// We don't have the means to test proto2 enum fields for valid values.
return false;
}
[[fallthrough]];
case kUpb_FieldType_Int32:
case kUpb_FieldType_UInt32:
type = "v4";
break;
case kUpb_FieldType_Int64:
case kUpb_FieldType_UInt64:
type = "v8";
break;
case kUpb_FieldType_Fixed32:
case kUpb_FieldType_SFixed32:
case kUpb_FieldType_Float:
type = "f4";
break;
case kUpb_FieldType_Fixed64:
case kUpb_FieldType_SFixed64:
case kUpb_FieldType_Double:
type = "f8";
break;
case kUpb_FieldType_SInt32:
type = "z4";
break;
case kUpb_FieldType_SInt64:
type = "z8";
break;
case kUpb_FieldType_String:
type = "s";
break;
case kUpb_FieldType_Bytes:
type = "b";
break;
case kUpb_FieldType_Message:
type = "m";
break;
default:
return false; // Not supported yet.
}
switch (upb_FieldMode_Get(mt_f)) {
case kUpb_FieldMode_Map:
return false; // Not supported yet (ever?).
case kUpb_FieldMode_Array:
if (mt_f->mode & kUpb_LabelFlags_IsPacked) {
cardinality = "p";
} else {
cardinality = "r";
}
break;
case kUpb_FieldMode_Scalar:
if (mt_f->presence < 0) {
cardinality = "o";
} else {
cardinality = "s";
}
break;
}
uint64_t expected_tag = GetEncodedTag(field);
// Data is:
//
// 48 32 16 0
// |--------|--------|--------|--------|--------|--------|--------|--------|
// | offset (16) |case offset (16) |presence| submsg | exp. tag (16) |
// |--------|--------|--------|--------|--------|--------|--------|--------|
//
// - |presence| is either hasbit index or field number for oneofs.
uint64_t data = static_cast<uint64_t>(mt_f->offset) << 48 | expected_tag;
if (field.IsSequence()) {
// No hasbit/oneof-related fields.
}
if (field.real_containing_oneof()) {
uint64_t case_offset = ~mt_f->presence;
if (case_offset > 0xffff || field.number() > 0xff) return false;
data |= field.number() << 24;
data |= case_offset << 32;
} else {
uint64_t hasbit_index = 63; // No hasbit (set a high, unused bit).
if (mt_f->presence) {
hasbit_index = mt_f->presence;
if (hasbit_index > 31) return false;
}
data |= hasbit_index << 24;
}
if (field.ctype() == kUpb_CType_Message) {
uint64_t idx = mt_f->UPB_PRIVATE(submsg_index);
if (idx > 255) return false;
data |= idx << 16;
std::string size_ceil = "max";
size_t size = SIZE_MAX;
if (field.message_type().file() == field.file()) {
// We can only be guaranteed the size of the sub-message if it is in the
// same file as us. We could relax this to increase the speed of
// cross-file sub-message parsing if we are comfortable requiring that
// users compile all messages at the same time.
const upb_MiniTable* sub_mt = pools.GetMiniTable64(field.message_type());
size = sub_mt->size + 8;
}
std::vector<size_t> breaks = {64, 128, 192, 256};
for (auto brk : breaks) {
if (size <= brk) {
size_ceil = std::to_string(brk);
break;
}
}
ent.first = absl::Substitute("upb_p$0$1_$2bt_max$3b", cardinality, type,
expected_tag > 0xff ? "2" : "1", size_ceil);
} else {
ent.first = absl::Substitute("upb_p$0$1_$2bt", cardinality, type,
expected_tag > 0xff ? "2" : "1");
}
ent.second = data;
return true;
}
std::vector<TableEntry> FastDecodeTable(upb::MessageDefPtr message,
const DefPoolPair& pools) {
std::vector<TableEntry> table;
for (const auto field : FieldHotnessOrder(message)) {
TableEntry ent;
int slot = GetTableSlot(field);
// std::cerr << "table slot: " << field->number() << ": " << slot << "\n";
if (slot < 0) {
// Tag can't fit in the table.
continue;
}
if (!TryFillTableEntry(pools, field, ent)) {
// Unsupported field type or offset, hasbit index, etc. doesn't fit.
continue;
}
while ((size_t)slot >= table.size()) {
size_t size = std::max(static_cast<size_t>(1), table.size() * 2);
table.resize(size, TableEntry{"_upb_FastDecoder_DecodeGeneric", 0});
}
if (table[slot].first != "_upb_FastDecoder_DecodeGeneric") {
// A hotter field already filled this slot.
continue;
}
table[slot] = ent;
}
return table;
}
std::string ArchDependentSize(int64_t size32, int64_t size64) {
if (size32 == size64) return absl::StrCat(size32);
return absl::Substitute("UPB_SIZE($0, $1)", size32, size64);
}
std::string FieldInitializer(const DefPoolPair& pools, upb::FieldDefPtr field) {
return upbc::FieldInitializer(field, pools.GetField64(field),
pools.GetField32(field));
}
// Writes a single field into a .upb.c source file.
void WriteMessageField(upb::FieldDefPtr field,
const upb_MiniTableField* field64,
const upb_MiniTableField* field32,
Output& output) {
output(" $0,\n", upbc::FieldInitializer(field, field64, field32));
}
std::string GetSub(upb::FieldDefPtr field) {
if (auto message_def = field.message_type()) {
return absl::Substitute("{.submsg = &$0}", MessageInitName(message_def));
}
if (auto enum_def = field.enum_subdef()) {
if (enum_def.is_closed()) {
return absl::Substitute("{.subenum = &$0}", EnumInit(enum_def));
}
}
return std::string("{.submsg = NULL}");
}
// Writes a single message into a .upb.c source file.
void WriteMessage(upb::MessageDefPtr message, const DefPoolPair& pools,
Output& output) {
std::string msg_name = ToCIdent(message.full_name());
std::string fields_array_ref = "NULL";
std::string submsgs_array_ref = "NULL";
std::string subenums_array_ref = "NULL";
const upb_MiniTable* mt_32 = pools.GetMiniTable32(message);
const upb_MiniTable* mt_64 = pools.GetMiniTable64(message);
std::map<int, std::string> subs;
for (int i = 0; i < mt_64->field_count; i++) {
const upb_MiniTableField* f = &mt_64->fields[i];
uint32_t index = f->UPB_PRIVATE(submsg_index);
if (index != kUpb_NoSub) {
auto pair =
subs.emplace(index, GetSub(message.FindFieldByNumber(f->number)));
ABSL_CHECK(pair.second);
}
}
if (!subs.empty()) {
std::string submsgs_array_name = msg_name + "_submsgs";
submsgs_array_ref = "&" + submsgs_array_name + "[0]";
output("static const upb_MiniTableSub $0[$1] = {\n", submsgs_array_name,
subs.size());
int i = 0;
for (const auto& pair : subs) {
ABSL_CHECK(pair.first == i++);
output(" $0,\n", pair.second);
}
output("};\n\n");
}
if (mt_64->field_count > 0) {
std::string fields_array_name = msg_name + "__fields";
fields_array_ref = "&" + fields_array_name + "[0]";
output("static const upb_MiniTableField $0[$1] = {\n", fields_array_name,
mt_64->field_count);
for (int i = 0; i < mt_64->field_count; i++) {
WriteMessageField(message.FindFieldByNumber(mt_64->fields[i].number),
&mt_64->fields[i], &mt_32->fields[i], output);
}
output("};\n\n");
}
std::vector<TableEntry> table;
uint8_t table_mask = -1;
table = FastDecodeTable(message, pools);
if (table.size() > 1) {
assert((table.size() & (table.size() - 1)) == 0);
table_mask = (table.size() - 1) << 3;
}
std::string msgext = "kUpb_ExtMode_NonExtendable";
if (message.extension_range_count()) {
if (UPB_DESC(MessageOptions_message_set_wire_format)(message.options())) {
msgext = "kUpb_ExtMode_IsMessageSet";
} else {
msgext = "kUpb_ExtMode_Extendable";
}
}
output("const upb_MiniTable $0 = {\n", MessageInitName(message));
output(" $0,\n", submsgs_array_ref);
output(" $0,\n", fields_array_ref);
output(" $0, $1, $2, $3, UPB_FASTTABLE_MASK($4), $5,\n",
ArchDependentSize(mt_32->size, mt_64->size), mt_64->field_count,
msgext, mt_64->dense_below, table_mask, mt_64->required_count);
if (!table.empty()) {
output(" UPB_FASTTABLE_INIT({\n");
for (const auto& ent : table) {
output(" {0x$1, &$0},\n", ent.first,
absl::StrCat(absl::Hex(ent.second, absl::kZeroPad16)));
}
output(" })\n");
}
output("};\n\n");
}
void WriteEnum(upb::EnumDefPtr e, Output& output) {
std::string values_init = "{\n";
const upb_MiniTableEnum* mt = e.mini_table();
uint32_t value_count = (mt->mask_limit / 32) + mt->value_count;
for (uint32_t i = 0; i < value_count; i++) {
absl::StrAppend(&values_init, " 0x", absl::Hex(mt->data[i]),
",\n");
}
values_init += " }";
output(
R"cc(
const upb_MiniTableEnum $0 = {
$1,
$2,
$3,
};
)cc",
EnumInit(e), mt->mask_limit, mt->value_count, values_init);
output("\n");
}
int WriteEnums(const DefPoolPair& pools, upb::FileDefPtr file, Output& output) {
if (file.syntax() != kUpb_Syntax_Proto2) return 0;
std::vector<upb::EnumDefPtr> this_file_enums = SortedEnums(file);
for (const auto e : this_file_enums) {
WriteEnum(e, output);
}
if (!this_file_enums.empty()) {
output("static const upb_MiniTableEnum *$0[$1] = {\n", kEnumsInit,
this_file_enums.size());
for (const auto e : this_file_enums) {
output(" &$0,\n", EnumInit(e));
}
output("};\n");
output("\n");
}
return this_file_enums.size();
}
int WriteMessages(const DefPoolPair& pools, upb::FileDefPtr file,
Output& output) {
std::vector<upb::MessageDefPtr> file_messages = SortedMessages(file);
if (file_messages.empty()) return 0;
for (auto message : file_messages) {
WriteMessage(message, pools, output);
}
output("static const upb_MiniTable *$0[$1] = {\n", kMessagesInit,
file_messages.size());
for (auto message : file_messages) {
output(" &$0,\n", MessageInitName(message));
}
output("};\n");
output("\n");
return file_messages.size();
}
void WriteExtension(upb::FieldDefPtr ext, const DefPoolPair& pools,
Output& output) {
output("$0,\n", FieldInitializer(pools, ext));
output(" &$0,\n", MessageInitName(ext.containing_type()));
output(" $0,\n", GetSub(ext));
}
int WriteExtensions(const DefPoolPair& pools, upb::FileDefPtr file,
Output& output) {
auto exts = SortedExtensions(file);
if (exts.empty()) return 0;
// Order by full name for consistent ordering.
std::map<std::string, upb::MessageDefPtr> forward_messages;
for (auto ext : exts) {
forward_messages[ext.containing_type().full_name()] = ext.containing_type();
if (ext.message_type()) {
forward_messages[ext.message_type().full_name()] = ext.message_type();
}
}
for (auto ext : exts) {
output("const upb_MiniTableExtension $0 = {\n ", ExtensionLayout(ext));
WriteExtension(ext, pools, output);
output("\n};\n");
}
output(
"\n"
"static const upb_MiniTableExtension *$0[$1] = {\n",
kExtensionsInit, exts.size());
for (auto ext : exts) {
output(" &$0,\n", ExtensionLayout(ext));
}
output(
"};\n"
"\n");
return exts.size();
}
void WriteMiniTableSource(const DefPoolPair& pools, upb::FileDefPtr file,
Output& output) {
EmitFileWarning(file.name(), output);
output(
"#include <stddef.h>\n"
"#include \"upb/upb/generated_code_support.h\"\n"
"#include \"$0\"\n",
MiniTableHeaderFilename(file));
for (int i = 0; i < file.dependency_count(); i++) {
output("#include \"$0\"\n", MiniTableHeaderFilename(file.dependency(i)));
}
output(
"\n"
"// Must be last.\n"
"#include \"upb/upb/port/def.inc\"\n"
"\n");
int msg_count = WriteMessages(pools, file, output);
int ext_count = WriteExtensions(pools, file, output);
int enum_count = WriteEnums(pools, file, output);
output("const upb_MiniTableFile $0 = {\n", FileLayoutName(file));
output(" $0,\n", msg_count ? kMessagesInit : "NULL");
output(" $0,\n", enum_count ? kEnumsInit : "NULL");
output(" $0,\n", ext_count ? kExtensionsInit : "NULL");
output(" $0,\n", msg_count);
output(" $0,\n", enum_count);
output(" $0,\n", ext_count);
output("};\n\n");
output("#include \"upb/upb/port/undef.inc\"\n");
output("\n");
}
void GenerateFile(const DefPoolPair& pools, upb::FileDefPtr file,
Plugin* plugin) {
Output h_output;
WriteHeader(pools, file, h_output);
plugin->AddOutputFile(MiniTableHeaderFilename(file), h_output.output());
Output c_output;
WriteMiniTableSource(pools, file, c_output);
plugin->AddOutputFile(SourceFilename(file), c_output.output());
}
bool ParseOptions(Plugin* plugin) {
for (const auto& pair : ParseGeneratorParameter(plugin->parameter())) {
plugin->SetError(absl::Substitute("Unknown parameter: $0", pair.first));
return false;
}
return true;
}
absl::string_view ToStringView(upb_StringView str) {
return absl::string_view(str.data, str.size);
}
} // namespace
} // namespace upbc
int main(int argc, char** argv) {
upbc::DefPoolPair pools;
upbc::Plugin plugin;
if (!upbc::ParseOptions(&plugin)) return 0;
plugin.GenerateFilesRaw([&](const UPB_DESC(FileDescriptorProto) * file_proto,
bool generate) {
upb::Status status;
upb::FileDefPtr file = pools.AddFile(file_proto, &status);
if (!file) {
absl::string_view name =
upbc::ToStringView(UPB_DESC(FileDescriptorProto_name)(file_proto));
ABSL_LOG(FATAL) << "Couldn't add file " << name
<< " to DefPool: " << status.error_message();
}
if (generate) upbc::GenerateFile(pools, file, &plugin);
});
return 0;
}

@ -103,7 +103,7 @@ void WriteDefSource(upb::FileDefPtr file, Output& output) {
output("#include \"upb/upb/reflection/def.h\"\n");
output("#include \"$0\"\n", DefHeaderFilename(file));
output("#include \"$0\"\n", HeaderFilename(file));
output("#include \"$0\"\n", MiniTableHeaderFilename(file));
output("\n");
for (int i = 0; i < file.dependency_count(); i++) {

Loading…
Cancel
Save