• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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