1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2022 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/cpp/tracker.h"
9
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "absl/log/absl_check.h"
15 #include "absl/strings/str_cat.h"
16 #include "absl/strings/str_format.h"
17 #include "absl/strings/string_view.h"
18 #include "absl/strings/substitute.h"
19 #include "absl/types/optional.h"
20 #include "absl/types/span.h"
21 #include "google/protobuf/compiler/cpp/helpers.h"
22 #include "google/protobuf/compiler/cpp/options.h"
23 #include "google/protobuf/descriptor.h"
24 #include "google/protobuf/io/printer.h"
25
26 namespace google {
27 namespace protobuf {
28 namespace compiler {
29 namespace cpp {
30 namespace {
31 using Sub = ::google::protobuf::io::Printer::Sub;
32
33 constexpr absl::string_view kTracker = "Impl_::_tracker_";
34 constexpr absl::string_view kVarPrefix = "annotate_";
35 constexpr absl::string_view kTypeTraits = "_proto_TypeTraits";
36
37 struct Call {
Callgoogle::protobuf::compiler::cpp::__anon65d789c30111::Call38 Call(absl::string_view var, absl::string_view call) : var(var), call(call) {}
Callgoogle::protobuf::compiler::cpp::__anon65d789c30111::Call39 Call(absl::optional<int> field_index, absl::string_view var,
40 absl::string_view call)
41 : var(var), call(call), field_index(field_index) {}
42
Thisgoogle::protobuf::compiler::cpp::__anon65d789c30111::Call43 Call This(absl::optional<absl::string_view> thiz) && {
44 this->thiz = thiz;
45 return std::move(*this);
46 }
47
48 template <typename... SubArgs>
Arggoogle::protobuf::compiler::cpp::__anon65d789c30111::Call49 Call Arg(absl::string_view format, const SubArgs&... args) && {
50 this->args.emplace_back(absl::Substitute(format, args...));
51 return std::move(*this);
52 }
53
Suppressedgoogle::protobuf::compiler::cpp::__anon65d789c30111::Call54 Call Suppressed() && {
55 suppressed = true;
56 return std::move(*this);
57 }
58
59 absl::string_view var;
60 absl::string_view call;
61 absl::optional<int> field_index;
62 absl::optional<absl::string_view> thiz = "this";
63 std::vector<std::string> args;
64 bool suppressed = false;
65 };
66
GenerateTrackerCalls(const Options & opts,const Descriptor * message,absl::optional<std::string> alt_annotation,absl::Span<const Call> calls)67 std::vector<Sub> GenerateTrackerCalls(
68 const Options& opts, const Descriptor* message,
69 absl::optional<std::string> alt_annotation, absl::Span<const Call> calls) {
70 bool enable_tracking = HasTracker(message, opts);
71 const auto& forbidden =
72 opts.field_listener_options.forbidden_field_listener_events;
73
74 std::vector<Sub> subs;
75 for (const auto& call : calls) {
76 std::string call_str;
77 if (enable_tracking && !call.suppressed && !forbidden.contains(call.var)) {
78 absl::SubstituteAndAppend(&call_str, "$0.$1", kTracker, call.call);
79 if (call.field_index.has_value()) {
80 absl::SubstituteAndAppend(&call_str, "<$0>", *call.field_index);
81 }
82 absl::StrAppend(&call_str, "(");
83
84 absl::string_view arg_sep = "";
85 if (call.thiz.has_value()) {
86 absl::StrAppend(&call_str, *call.thiz);
87 arg_sep = ", ";
88 }
89
90 for (const auto& arg : call.args) {
91 absl::StrAppend(&call_str, arg_sep, arg);
92 arg_sep = ", ";
93 }
94
95 absl::StrAppend(&call_str, ");");
96 } else if (opts.annotate_accessor && alt_annotation.has_value()) {
97 call_str = *alt_annotation;
98 }
99
100 if (!call_str.empty()) {
101 // TODO: Until we migrate all of the C++ backend to use
102 // Emit(), we need to include a newline here so that the line that follows
103 // the annotation is on its own line.
104 call_str.push_back('\n');
105 if (enable_tracking) {
106 call_str =
107 absl::StrCat("if (::", ProtobufNamespace(opts),
108 "::internal::cpp::IsTrackingEnabled()) ", call_str);
109 }
110 }
111
112 subs.push_back(
113 Sub(absl::StrCat(kVarPrefix, call.var), call_str).WithSuffix(";"));
114 }
115
116 return subs;
117 }
118 } // namespace
119
MakeTrackerCalls(const Descriptor * message,const Options & opts)120 std::vector<Sub> MakeTrackerCalls(const Descriptor* message,
121 const Options& opts) {
122 absl::string_view extns =
123 IsMapEntryMessage(message) ? "_extensions_" : "_impl_._extensions_";
124
125 auto primitive_extn_accessor = [extns](absl::string_view var,
126 absl::string_view call) {
127 return Call(var, call)
128 .Arg("id.number()")
129 .Arg("$0::GetPtr(id.number(), $1, id.default_value_ref())", kTypeTraits,
130 extns);
131 };
132
133 auto index_extn_accessor = [extns](absl::string_view var,
134 absl::string_view call) {
135 return Call(var, call)
136 .Arg("id.number()")
137 .Arg("$0::GetPtr(id.number(), $1, index)", kTypeTraits, extns);
138 };
139
140 auto add_extn_accessor = [extns](absl::string_view var,
141 absl::string_view call) {
142 return Call(var, call)
143 .Arg("id.number()")
144 .Arg("$0::GetPtr(id.number(), $1, $1.ExtensionSize(id.number()) - 1)",
145 kTypeTraits, extns);
146 };
147
148 auto list_extn_accessor = [extns](absl::string_view var,
149 absl::string_view call) {
150 return Call(var, call)
151 .Arg("id.number()")
152 .Arg("$0::GetRepeatedPtr(id.number(), $1)", kTypeTraits, extns);
153 };
154
155 return GenerateTrackerCalls(
156 opts, message, absl::nullopt,
157 {
158 Call("serialize", "OnSerialize").This("&this_"),
159 Call("deserialize", "OnDeserialize").This("_this"),
160 // TODO: Ideally annotate_reflection should not exist and we
161 // need to annotate all reflective calls on our own, however, as this
162 // is a cause for side effects, i.e. reading values dynamically, we
163 // want the users know that dynamic access can happen.
164 Call("reflection", "OnGetMetadata").This(absl::nullopt),
165 Call("bytesize", "OnByteSize").This("&this_"),
166 Call("mergefrom", "OnMergeFrom").This("_this").Arg("&from"),
167 Call("unknown_fields", "OnUnknownFields"),
168 Call("mutable_unknown_fields", "OnMutableUnknownFields"),
169
170 // "Has" is here as users calling "has" on a repeated field is a
171 // mistake.
172 primitive_extn_accessor("extension_has", "OnHasExtension"),
173 primitive_extn_accessor("extension_get", "OnGetExtension"),
174 primitive_extn_accessor("extension_mutable", "OnMutableExtension"),
175 primitive_extn_accessor("extension_set", "OnSetExtension"),
176 primitive_extn_accessor("extension_release", "OnReleaseExtension"),
177
178 index_extn_accessor("repeated_extension_get", "OnGetExtension"),
179 index_extn_accessor("repeated_extension_mutable",
180 "OnMutableExtension"),
181 index_extn_accessor("repeated_extension_set", "OnSetExtension"),
182
183 add_extn_accessor("repeated_extension_add", "OnAddExtension"),
184 add_extn_accessor("repeated_extension_add_mutable",
185 "OnAddMutableExtension"),
186
187 list_extn_accessor("extension_repeated_size", "OnExtensionSize"),
188 list_extn_accessor("repeated_extension_list", "OnListExtension"),
189 list_extn_accessor("repeated_extension_list_mutable",
190 "OnMutableListExtension"),
191
192 // Generic accessors such as "clear".
193 // TODO: Generalize clear from both repeated and non
194 // repeated calls, currently their underlying memory interfaces are
195 // very different. Or think of removing clear callback as no usages
196 // are needed and no memory exist
197 Call("extension_clear", "OnClearExtension").Suppressed(),
198 });
199 }
200
201 namespace {
202 struct Getters {
203 std::string base = "nullptr";
204 std::string for_last = "nullptr";
205 std::string for_flat = "nullptr";
206 };
207
RepeatedFieldGetters(const FieldDescriptor * field,const Options & opts)208 Getters RepeatedFieldGetters(const FieldDescriptor* field,
209 const Options& opts) {
210 Getters getters;
211 if (!field->is_map() &&
212 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
213 std::string accessor = absl::StrCat("_internal_", FieldName(field), "()");
214 getters.base = absl::Substitute("&$0.Get(index)", accessor);
215 getters.for_last = absl::Substitute("&$0.Get($0.size() - 1)", accessor);
216 getters.for_flat = absl::StrCat("&", accessor);
217 }
218
219 return getters;
220 }
221
StringFieldGetters(const FieldDescriptor * field,const Options & opts)222 Getters StringFieldGetters(const FieldDescriptor* field, const Options& opts) {
223 std::string member = FieldMemberName(field, ShouldSplit(field, opts));
224 bool is_std_string =
225 field->cpp_string_type() == FieldDescriptor::CppStringType::kString;
226
227 Getters getters;
228 if (is_std_string && !field->default_value_string().empty()) {
229 getters.base =
230 absl::Substitute("$0.IsDefault() ? &$1.get() : $0.UnsafeGetPointer()",
231 member, MakeDefaultFieldName(field));
232 } else {
233 getters.base = absl::StrCat("&", member);
234 }
235
236 getters.for_flat = getters.base;
237 return getters;
238 }
239
StringOneofGetters(const FieldDescriptor * field,const OneofDescriptor * oneof,const Options & opts)240 Getters StringOneofGetters(const FieldDescriptor* field,
241 const OneofDescriptor* oneof, const Options& opts) {
242 ABSL_CHECK(oneof != nullptr);
243
244 std::string member = FieldMemberName(field, ShouldSplit(field, opts));
245 bool is_std_string =
246 field->cpp_string_type() == FieldDescriptor::CppStringType::kString;
247
248 std::string field_ptr = member;
249 if (is_std_string) {
250 field_ptr = absl::Substitute("$0.UnsafeGetPointer()", member);
251 }
252
253 std::string has =
254 absl::Substitute("$0_case() == k$1", oneof->name(),
255 UnderscoresToCamelCase(field->name(), true));
256
257 std::string default_field = MakeDefaultFieldName(field);
258 if (is_std_string) {
259 absl::StrAppend(&default_field, ".get()");
260 }
261
262 Getters getters;
263 if (field->default_value_string().empty()
264 ) {
265 getters.base = absl::Substitute("$0 ? $1 : nullptr", has, field_ptr);
266 } else {
267 getters.base =
268 absl::Substitute("$0 ? $1 : &$2", has, field_ptr, default_field);
269 }
270
271 getters.for_flat = getters.base;
272 return getters;
273 }
274
SingularFieldGetters(const FieldDescriptor * field,const Options & opts)275 Getters SingularFieldGetters(const FieldDescriptor* field,
276 const Options& opts) {
277 std::string member = FieldMemberName(field, ShouldSplit(field, opts));
278
279 Getters getters;
280 getters.base = absl::StrCat("&", member);
281 if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
282 getters.for_flat = absl::StrCat("&", member);
283 }
284 return getters;
285 }
286 } // namespace
287
MakeTrackerCalls(const FieldDescriptor * field,const Options & opts)288 std::vector<Sub> MakeTrackerCalls(const FieldDescriptor* field,
289 const Options& opts) {
290 Getters getters;
291 if (field->is_repeated()) {
292 getters = RepeatedFieldGetters(field, opts);
293 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
294 const auto* oneof = field->real_containing_oneof();
295 if (oneof != nullptr) {
296 getters = StringOneofGetters(field, oneof, opts);
297 } else {
298 getters = StringFieldGetters(field, opts);
299 }
300 } else if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE ||
301 IsExplicitLazy(field)) {
302 getters = SingularFieldGetters(field, opts);
303 }
304
305 auto index = field->index();
306 return GenerateTrackerCalls(
307 opts, field->containing_type(),
308 absl::Substitute("$0_AccessedNoStrip = true;", FieldName(field)),
309 {
310 Call(index, "get", "OnGet").Arg(getters.base),
311 Call(index, "set", "OnSet").Arg(getters.base),
312 Call(index, "has", "OnHas").Arg(getters.base),
313 Call(index, "mutable", "OnMutable").Arg(getters.base),
314 Call(index, "release", "OnRelease").Arg(getters.base),
315 Call(index, "clear", "OnClear").Arg(getters.for_flat),
316 Call(index, "size", "OnSize").Arg(getters.for_flat),
317 Call(index, "list", "OnList").Arg(getters.for_flat),
318 Call(index, "mutable_list", "OnMutableList").Arg(getters.for_flat),
319 Call(index, "add", "OnAdd").Arg(getters.for_last),
320 Call(index, "add_mutable", "OnAddMutable").Arg(getters.for_last),
321 });
322 }
323 } // namespace cpp
324 } // namespace compiler
325 } // namespace protobuf
326 } // namespace google
327