2024-03-15 12:31:34 +08:00

341 lines
13 KiB
C++

// Protocol Buffers - Google's data interchange format
// Copyright 2022 Google Inc. 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 Inc. 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 "google/protobuf/compiler/cpp/tracker.h"
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/descriptor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/types/optional.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/options.h"
#include "google/protobuf/io/printer.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
using Sub = ::google::protobuf::io::Printer::Sub;
constexpr absl::string_view kTracker = "Impl_::_tracker_";
constexpr absl::string_view kVarPrefix = "annotate_";
constexpr absl::string_view kTypeTraits = "_proto_TypeTraits";
struct Call {
Call(absl::string_view var, absl::string_view call) : var(var), call(call) {}
Call(absl::optional<int> field_index, absl::string_view var,
absl::string_view call)
: var(var), call(call), field_index(field_index) {}
Call This(absl::optional<absl::string_view> thiz) && {
this->thiz = thiz;
return std::move(*this);
}
template <typename... SubArgs>
Call Arg(absl::string_view format, const SubArgs&... args) && {
this->args.emplace_back(absl::Substitute(format, args...));
return std::move(*this);
}
Call Suppressed() && {
suppressed = true;
return std::move(*this);
}
absl::string_view var;
absl::string_view call;
absl::optional<int> field_index;
absl::optional<absl::string_view> thiz = "this";
std::vector<std::string> args;
bool suppressed = false;
};
std::vector<Sub> GenerateTrackerCalls(
const Options& opts, const Descriptor* message,
absl::optional<std::string> alt_annotation, absl::Span<const Call> calls) {
bool enable_tracking = HasTracker(message, opts);
const auto& forbidden =
opts.field_listener_options.forbidden_field_listener_events;
std::vector<Sub> subs;
for (const auto& call : calls) {
std::string call_str;
if (enable_tracking && !call.suppressed && !forbidden.contains(call.var)) {
absl::SubstituteAndAppend(&call_str, "$0.$1", kTracker, call.call);
if (call.field_index.has_value()) {
absl::SubstituteAndAppend(&call_str, "<$0>", *call.field_index);
}
absl::StrAppend(&call_str, "(");
absl::string_view arg_sep = "";
if (call.thiz.has_value()) {
absl::StrAppend(&call_str, *call.thiz);
arg_sep = ", ";
}
for (const auto& arg : call.args) {
absl::StrAppend(&call_str, arg_sep, arg);
arg_sep = ", ";
}
absl::StrAppend(&call_str, ");");
} else if (opts.annotate_accessor && alt_annotation.has_value()) {
call_str = *alt_annotation;
}
if (!call_str.empty()) {
// TODO(b/245791219): Until we migrate all of the C++ backend to use
// Emit(), we need to include a newline here so that the line that follows
// the annotation is on its own line.
call_str.push_back('\n');
}
subs.push_back(
Sub(absl::StrCat(kVarPrefix, call.var), call_str).WithSuffix(";"));
}
return subs;
}
} // namespace
std::vector<Sub> MakeTrackerCalls(const Descriptor* message,
const Options& opts) {
absl::string_view extns =
IsMapEntryMessage(message) ? "_extensions_" : "_impl_._extensions_";
auto primitive_extn_accessor = [extns](absl::string_view var,
absl::string_view call) {
return Call(var, call)
.Arg("id.number()")
.Arg("$0::GetPtr(id.number(), $1, id.default_value_ref())", kTypeTraits,
extns);
};
auto index_extn_accessor = [extns](absl::string_view var,
absl::string_view call) {
return Call(var, call)
.Arg("id.number()")
.Arg("$0::GetPtr(id.number(), $1, index)", kTypeTraits, extns);
};
auto add_extn_accessor = [extns](absl::string_view var,
absl::string_view call) {
return Call(var, call)
.Arg("id.number()")
.Arg("$0::GetPtr(id.number(), $1, $1.ExtensionSize(id.number()) - 1)",
kTypeTraits, extns);
};
auto list_extn_accessor = [extns](absl::string_view var,
absl::string_view call) {
return Call(var, call)
.Arg("id.number()")
.Arg("$0::GetRepeatedPtr(id.number(), $1)", kTypeTraits, extns);
};
return GenerateTrackerCalls(
opts, message, absl::nullopt,
{
Call("serialize", "OnSerialize"),
Call("deserialize", "OnDeserialize"),
// TODO(danilak): Ideally annotate_reflection should not exist and we
// need to annotate all reflective calls on our own, however, as this
// is a cause for side effects, i.e. reading values dynamically, we
// want the users know that dynamic access can happen.
Call("reflection", "OnGetMetadata").This(absl::nullopt),
Call("bytesize", "OnByteSize"),
Call("mergefrom", "OnMergeFrom").This("_this").Arg("&from"),
Call("unknown_fields", "OnUnknownFields"),
Call("mutable_unknown_fields", "OnMutableUnknownFields"),
// "Has" is here as users calling "has" on a repeated field is a
// mistake.
primitive_extn_accessor("extension_has", "OnHasExtension"),
primitive_extn_accessor("extension_get", "OnGetExtension"),
primitive_extn_accessor("extension_mutable", "OnMutableExtension"),
primitive_extn_accessor("extension_set", "OnSetExtension"),
primitive_extn_accessor("extension_release", "OnReleaseExtension"),
index_extn_accessor("repeated_extension_get", "OnGetExtension"),
index_extn_accessor("repeated_extension_mutable",
"OnMutableExtension"),
index_extn_accessor("repeated_extension_set", "OnSetExtension"),
add_extn_accessor("repeated_extension_add", "OnAddExtension"),
add_extn_accessor("repeated_extension_add_mutable",
"OnAddMutableExtension"),
list_extn_accessor("extension_repeated_size", "OnExtensionSize"),
list_extn_accessor("repeated_extension_list", "OnListExtension"),
list_extn_accessor("repeated_extension_list_mutable",
"OnMutableListExtension"),
// Generic accessors such as "clear".
// TODO(b/190614678): Generalize clear from both repeated and non
// repeated calls, currently their underlying memory interfaces are
// very different. Or think of removing clear callback as no usages
// are needed and no memory exist
Call("extension_clear", "OnClearExtension").Suppressed(),
});
}
namespace {
struct Getters {
std::string base = "nullptr";
std::string for_last = "nullptr";
std::string for_flat = "nullptr";
};
Getters RepeatedFieldGetters(const FieldDescriptor* field,
const Options& opts) {
Getters getters;
if (!field->is_map() &&
field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
std::string accessor = absl::StrCat("_internal_", FieldName(field), "()");
getters.base = absl::Substitute("&$0.Get(index)", accessor);
getters.for_last = absl::Substitute("&$0.Get($0.size() - 1)", accessor);
getters.for_flat = absl::StrCat("&", accessor);
}
return getters;
}
Getters StringFieldGetters(const FieldDescriptor* field, const Options& opts) {
std::string member = FieldMemberName(field, ShouldSplit(field, opts));
bool is_std_string = field->options().ctype() == FieldOptions::STRING;
Getters getters;
if (is_std_string && !field->default_value_string().empty()) {
getters.base =
absl::Substitute("$0.IsDefault() ? &$1.get() : $0.UnsafeGetPointer()",
member, MakeDefaultFieldName(field));
} else {
getters.base = absl::StrCat("&", member);
}
getters.for_flat = getters.base;
return getters;
}
Getters StringOneofGetters(const FieldDescriptor* field,
const OneofDescriptor* oneof, const Options& opts) {
ABSL_CHECK(oneof != nullptr);
std::string member = FieldMemberName(field, ShouldSplit(field, opts));
bool is_std_string = field->options().ctype() == FieldOptions::STRING;
std::string field_ptr = member;
if (is_std_string) {
field_ptr = absl::Substitute("$0.UnsafeGetPointer()", member);
}
std::string has =
absl::Substitute("$0_case() == k$1", oneof->name(),
UnderscoresToCamelCase(field->name(), true));
std::string default_field = MakeDefaultFieldName(field);
if (is_std_string) {
absl::StrAppend(&default_field, ".get()");
}
Getters getters;
if (field->default_value_string().empty() ||
field->options().ctype() == FieldOptions::STRING_PIECE) {
getters.base = absl::Substitute("$0 ? $1 : nullptr", has, field_ptr);
} else {
getters.base =
absl::Substitute("$0 ? $1 : &$2", has, field_ptr, default_field);
}
getters.for_flat = getters.base;
return getters;
}
Getters SingularFieldGetters(const FieldDescriptor* field,
const Options& opts) {
std::string member = FieldMemberName(field, ShouldSplit(field, opts));
Getters getters;
getters.base = absl::StrCat("&", member);
if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
getters.for_flat = absl::StrCat("&", member);
}
return getters;
}
} // namespace
std::vector<Sub> MakeTrackerCalls(const FieldDescriptor* field,
const Options& opts) {
Getters getters;
if (field->is_repeated()) {
getters = RepeatedFieldGetters(field, opts);
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
const auto* oneof = field->real_containing_oneof();
if (oneof != nullptr) {
getters = StringOneofGetters(field, oneof, opts);
} else {
getters = StringFieldGetters(field, opts);
}
} else if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE ||
IsExplicitLazy(field)) {
getters = SingularFieldGetters(field, opts);
}
auto index = field->index();
return GenerateTrackerCalls(
opts, field->containing_type(),
absl::Substitute("$0_AccessedNoStrip = true;", FieldName(field)),
{
Call(index, "get", "OnGet").Arg(getters.base),
Call(index, "set", "OnSet").Arg(getters.base),
Call(index, "has", "OnHas").Arg(getters.base),
Call(index, "mutable", "OnMutable").Arg(getters.base),
Call(index, "release", "OnRelease").Arg(getters.base),
Call(index, "clear", "OnClear").Arg(getters.for_flat),
Call(index, "size", "OnSize").Arg(getters.for_flat),
Call(index, "list", "OnList").Arg(getters.for_flat),
Call(index, "mutable_list", "OnMutableList").Arg(getters.for_flat),
Call(index, "add", "OnAdd").Arg(getters.for_last),
Call(index, "add_mutable", "OnAddMutable").Arg(getters.for_last),
});
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google