• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 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/kotlin/message.h"
9 
10 #include <string>
11 
12 #include "absl/log/absl_check.h"
13 #include "absl/strings/str_cat.h"
14 #include "google/protobuf/compiler/java/context.h"
15 #include "google/protobuf/compiler/java/doc_comment.h"
16 #include "google/protobuf/compiler/java/field_common.h"
17 #include "google/protobuf/compiler/java/generator_common.h"
18 #include "google/protobuf/compiler/java/helpers.h"
19 #include "google/protobuf/compiler/java/full/field_generator.h"
20 #include "google/protobuf/compiler/java/full/make_field_gens.h"
21 #include "google/protobuf/compiler/java/lite/field_generator.h"
22 #include "google/protobuf/compiler/java/lite/make_field_gens.h"
23 #include "google/protobuf/compiler/java/name_resolver.h"
24 #include "google/protobuf/io/printer.h"
25 
26 // Must be last.
27 #include "google/protobuf/port_def.inc"
28 
29 namespace google {
30 namespace protobuf {
31 namespace compiler {
32 namespace kotlin {
33 
MessageGenerator(const Descriptor * descriptor,java::Context * context)34 MessageGenerator::MessageGenerator(const Descriptor* descriptor,
35                                    java::Context* context)
36     : context_(context),
37       name_resolver_(context->GetNameResolver()),
38       descriptor_(descriptor),
39       lite_(!java::HasDescriptorMethods(descriptor_->file(),
40                                         context->EnforceLite())),
41       jvm_dsl_(!lite_ || context->options().jvm_dsl),
42       lite_field_generators_(
43           java::FieldGeneratorMap<java::ImmutableFieldLiteGenerator>(
44               descriptor_)),
45       field_generators_(
46           java::FieldGeneratorMap<java::ImmutableFieldGenerator>(descriptor_)) {
47   for (int i = 0; i < descriptor_->field_count(); i++) {
48     if (java::IsRealOneof(descriptor_->field(i))) {
49       const OneofDescriptor* oneof = descriptor_->field(i)->containing_oneof();
50       ABSL_CHECK(oneofs_.emplace(oneof->index(), oneof).first->second == oneof);
51     }
52   }
53   if (lite_) {
54     lite_field_generators_ =
55         java::MakeImmutableFieldLiteGenerators(descriptor_, context_);
56   } else {
57     field_generators_ =
58         java::MakeImmutableFieldGenerators(descriptor_, context_);
59   }
60 }
61 
GenerateFieldMembers(io::Printer * printer) const62 void MessageGenerator::GenerateFieldMembers(io::Printer* printer) const {
63   for (int i = 0; i < descriptor_->field_count(); i++) {
64     printer->Print("\n");
65     if (lite_) {
66       lite_field_generators_.get(descriptor_->field(i))
67           .GenerateKotlinDslMembers(printer);
68     } else {
69       field_generators_.get(descriptor_->field(i))
70           .GenerateKotlinDslMembers(printer);
71     }
72   }
73 }
74 
Generate(io::Printer * printer) const75 void MessageGenerator::Generate(io::Printer* printer) const {
76   printer->Print(
77       "@kotlin.OptIn"
78       "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n"
79       "@com.google.protobuf.kotlin.ProtoDslMarker\n");
80   printer->Print(
81       "public class Dsl private constructor(\n"
82       "  private val _builder: $message$.Builder\n"
83       ") {\n"
84       "  public companion object {\n"
85       "    $jvm_synthetic$"
86       "    @kotlin.PublishedApi\n"
87       "    internal fun _create(builder: $message$.Builder): Dsl = "
88       "Dsl(builder)\n"
89       "  }\n"
90       "\n"
91       "  $jvm_synthetic$"
92       "  @kotlin.PublishedApi\n"
93       "  internal fun _build(): $message$ = _builder.build()\n",
94       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message",
95       java::EscapeKotlinKeywords(
96           name_resolver_->GetClassName(descriptor_, true)));
97 
98   printer->Indent();
99 
100   GenerateFieldMembers(printer);
101 
102   for (auto& kv : oneofs_) {
103     java::JvmNameContext name_ctx = {context_->options(), printer, lite_};
104     const OneofDescriptor* oneof = kv.second;
105     auto oneof_name = context_->GetOneofGeneratorInfo(oneof)->name;
106     std::string oneof_capitalized_name =
107         context_->GetOneofGeneratorInfo(oneof)->capitalized_name;
108     std::string oneof_case_getter =
109         lite_ ? absl::StrCat(java::GetKotlinPropertyName(oneof_name), "Case")
110               : absl::StrCat("get", oneof_capitalized_name, "Case()");
111     printer->Emit(
112         {
113             {"jvm_name",
114              [&] {
115                java::JvmName("get$oneof_capitalized_name$Case", name_ctx);
116              }},
117             {"oneof_name", oneof_name},
118             {"oneof_capitalized_name", oneof_capitalized_name},
119             {"oneof_case_getter", oneof_case_getter},
120             {"message", java::EscapeKotlinKeywords(
121                             name_resolver_->GetClassName(descriptor_, true))},
122         },
123         "public val $oneof_name$Case: $message$.$oneof_capitalized_name$Case\n"
124         "$jvm_name$"
125         "  get() = _builder.$oneof_case_getter$\n\n"
126         "public fun clear$oneof_capitalized_name$() {\n"
127         "  _builder.clear$oneof_capitalized_name$()\n"
128         "}\n");
129   }
130 
131   if (descriptor_->extension_range_count() > 0) {
132     GenerateExtensions(printer);
133   }
134 
135   printer->Outdent();
136   printer->Print("}\n");
137 }
138 
GenerateMembers(io::Printer * printer) const139 void MessageGenerator::GenerateMembers(io::Printer* printer) const {
140   if (jvm_dsl_) {
141     printer->Print("@kotlin.jvm.JvmName(\"-initialize$camelcase_name$\")\n",
142                    "camelcase_name",
143                    name_resolver_->GetKotlinFactoryName(descriptor_));
144   }
145 
146   printer->Print(
147       "public inline fun $camelcase_name$(block: $message_kt$.Dsl.() -> "
148       "kotlin.Unit): $message$ =\n"
149       "  $message_kt$.Dsl._create($message$.newBuilder()).apply { block() "
150       "}._build()\n",
151       "camelcase_name", name_resolver_->GetKotlinFactoryName(descriptor_),
152       "message_kt",
153       java::EscapeKotlinKeywords(
154           name_resolver_->GetKotlinExtensionsClassName(descriptor_)),
155       "message",
156       java::EscapeKotlinKeywords(
157           name_resolver_->GetClassName(descriptor_, true)));
158 
159   WriteMessageDocComment(printer, descriptor_, context_->options(),
160                          /* kdoc */ true);
161   printer->Emit(
162       {
163           io::Printer::Sub{"name_kt", absl::StrCat(descriptor_->name(), "Kt")}
164               .AnnotatedAs(descriptor_),
165           {"body",
166            [&]() {
167              Generate(printer);
168              for (int i = 0; i < descriptor_->nested_type_count(); i++) {
169                if (java::IsMapEntry(descriptor_->nested_type(i))) continue;
170                MessageGenerator(descriptor_->nested_type(i), context_)
171                    .GenerateMembers(printer);
172              }
173            }},
174       },
175       R"kt(
176     public object $name_kt$ {
177       $body$;
178     }
179   )kt");
180 }
181 
GenerateTopLevelMembers(io::Printer * printer) const182 void MessageGenerator::GenerateTopLevelMembers(io::Printer* printer) const {
183   if (!lite_) printer->Print("@kotlin.jvm.JvmSynthetic\n");
184   printer->Print(
185       "public inline fun $message$.copy(block: $message_kt$.Dsl.() -> "
186       "kotlin.Unit): $message$ =\n"
187       "  $message_kt$.Dsl._create(this.toBuilder()).apply { block() "
188       "}._build()\n\n",
189       "message",
190       java::EscapeKotlinKeywords(
191           name_resolver_->GetClassName(descriptor_, true)),
192       "message_kt",
193       name_resolver_->GetKotlinExtensionsClassNameEscaped(descriptor_));
194 
195   for (int i = 0; i < descriptor_->nested_type_count(); i++) {
196     if (java::IsMapEntry(descriptor_->nested_type(i))) continue;
197     MessageGenerator(descriptor_->nested_type(i), context_)
198         .GenerateTopLevelMembers(printer);
199   }
200 
201   GenerateOrNull(printer);
202 }
203 
GenerateOrNull(io::Printer * printer) const204 void MessageGenerator::GenerateOrNull(io::Printer* printer) const {
205   for (int i = 0; i < descriptor_->field_count(); i++) {
206     const FieldDescriptor* field = descriptor_->field(i);
207     if (!field->has_presence() ||
208         java::GetJavaType(field) != java::JAVATYPE_MESSAGE) {
209       continue;
210     }
211     if (field->options().deprecated()) {
212       printer->Print(
213           "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n",
214           "name", context_->GetFieldGeneratorInfo(field)->name);
215     }
216     if (jvm_dsl_) {
217       // On the JVM, we can use `FooOrBuilder`, and it saves code size to
218       // generate only one method instead of two.
219       printer->Print(
220           "public val $full_classname$OrBuilder.$camelcase_name$OrNull: "
221           "$full_name$?\n"
222           "  get() = if (has$name$()) get$name$() else null\n\n",
223           "full_classname",
224           java::EscapeKotlinKeywords(
225               name_resolver_->GetClassName(descriptor_, true)),
226           "camelcase_name", context_->GetFieldGeneratorInfo(field)->name,
227           "full_name",
228           java::EscapeKotlinKeywords(
229               name_resolver_->GetImmutableClassName(field->message_type())),
230           "name", context_->GetFieldGeneratorInfo(field)->capitalized_name);
231     } else {
232       // Non-JVM platforms don't have `FooOrBuilder`, so we generate `Foo`
233       // and `Foo.Builder` methods.
234       printer->Print(
235           "public val $full_classname$.$camelcase_name$OrNull: "
236           "$full_name$?\n"
237           "  get() = if (has$capitalized_name$()) this.$name$ else null\n\n",
238           "full_classname",
239           java::EscapeKotlinKeywords(
240               name_resolver_->GetClassName(descriptor_, true)),
241           "camelcase_name", context_->GetFieldGeneratorInfo(field)->name,
242           "full_name",
243           java::EscapeKotlinKeywords(
244               name_resolver_->GetImmutableClassName(field->message_type())),
245           "capitalized_name",
246           context_->GetFieldGeneratorInfo(field)->capitalized_name, "name",
247           java::EscapeKotlinKeywords(java::GetKotlinPropertyName(
248               context_->GetFieldGeneratorInfo(field)->capitalized_name)));
249       if (field->options().deprecated()) {
250         printer->Print(
251             "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n",
252             "name", context_->GetFieldGeneratorInfo(field)->name);
253       }
254       printer->Print(
255           "public val $full_classname$.Builder.$camelcase_name$OrNull: "
256           "$full_name$?\n"
257           "  get() = if (has$capitalized_name$()) this.$name$ else null\n\n",
258           "full_classname",
259           java::EscapeKotlinKeywords(
260               name_resolver_->GetClassName(descriptor_, true)),
261           "camelcase_name", context_->GetFieldGeneratorInfo(field)->name,
262           "full_name",
263           java::EscapeKotlinKeywords(
264               name_resolver_->GetImmutableClassName(field->message_type())),
265           "capitalized_name",
266           context_->GetFieldGeneratorInfo(field)->capitalized_name, "name",
267           java::EscapeKotlinKeywords(java::GetKotlinPropertyName(
268               context_->GetFieldGeneratorInfo(field)->capitalized_name)));
269     }
270   }
271 }
272 
GenerateExtensions(io::Printer * printer) const273 void MessageGenerator::GenerateExtensions(io::Printer* printer) const {
274   java::JvmNameContext name_ctx = {context_->options(), printer, lite_};
275   std::string message_name = java::EscapeKotlinKeywords(
276       name_resolver_->GetClassName(descriptor_, true));
277 
278   printer->Print(
279       "@Suppress(\"UNCHECKED_CAST\")\n"
280       "$jvm_synthetic$"
281       "public operator fun <T : kotlin.Any> get(extension: "
282       "com.google.protobuf.ExtensionLite<$message$, T>): T {\n"
283       "  return if (extension.isRepeated) {\n"
284       "    get(extension as com.google.protobuf.ExtensionLite<$message$, "
285       "kotlin.collections.List<*>>) as T\n"
286       "  } else {\n"
287       "    _builder.getExtension(extension)\n"
288       "  }\n"
289       "}\n\n",
290       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
291 
292   if (jvm_dsl_) {
293     // TODO: generate this on Kotlin Native once the [Mutable]List
294     // issue is resolved.
295     printer->Emit(
296         {
297             {"jvm_name",
298              [&] { java::JvmName("-getRepeatedExtension", name_ctx); }},
299             {"jvm_synthetic", java::JvmSynthetic(jvm_dsl_)},
300             {"message", message_name},
301         },
302         "$jvm_synthetic$"
303         "@kotlin.OptIn"
304         "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n"
305         "$jvm_name$"
306         "public operator fun <E : kotlin.Any> get(\n"
307         "  extension: com.google.protobuf.ExtensionLite<$message$, "
308         "kotlin.collections.List<E>>\n"
309         "): com.google.protobuf.kotlin.ExtensionList<E, $message$> {\n"
310         "  return com.google.protobuf.kotlin.ExtensionList(extension, "
311         "_builder.getExtension(extension))\n"
312         "}\n\n");
313   }
314 
315   printer->Print(
316       "$jvm_synthetic$"
317       "public operator fun contains(extension: "
318       "com.google.protobuf.ExtensionLite<$message$, *>): "
319       "Boolean {\n"
320       "  return _builder.hasExtension(extension)\n"
321       "}\n\n",
322       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
323 
324   printer->Print(
325       "$jvm_synthetic$"
326       "public fun clear(extension: "
327       "com.google.protobuf.ExtensionLite<$message$, *>) "
328       "{\n"
329       "  _builder.clearExtension(extension)\n"
330       "}\n\n",
331       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
332 
333   printer->Print(
334       "$jvm_synthetic$"
335       "public fun <T : kotlin.Any> setExtension(extension: "
336       "com.google.protobuf.ExtensionLite<$message$, T>, "
337       "value: T) {\n"
338       "  _builder.setExtension(extension, value)\n"
339       "}\n\n",
340       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
341 
342   printer->Print(
343       "$jvm_synthetic$"
344       "@Suppress(\"NOTHING_TO_INLINE\")\n"
345       "public inline operator fun <T : Comparable<T>> set(\n"
346       "  extension: com.google.protobuf.ExtensionLite<$message$, T>,\n"
347       "  value: T\n"
348       ") {\n"
349       "  setExtension(extension, value)\n"
350       "}\n\n",
351       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
352 
353   printer->Print(
354       "$jvm_synthetic$"
355       "@Suppress(\"NOTHING_TO_INLINE\")\n"
356       "public inline operator fun set(\n"
357       "  extension: com.google.protobuf.ExtensionLite<$message$, "
358       "com.google.protobuf.ByteString>,\n"
359       "  value: com.google.protobuf.ByteString\n"
360       ") {\n"
361       "  setExtension(extension, value)\n"
362       "}\n\n",
363       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
364 
365   printer->Print(
366       "$jvm_synthetic$"
367       "@Suppress(\"NOTHING_TO_INLINE\")\n"
368       "public inline operator fun <T : com.google.protobuf.MessageLite> set(\n"
369       "  extension: com.google.protobuf.ExtensionLite<$message$, T>,\n"
370       "  value: T\n"
371       ") {\n"
372       "  setExtension(extension, value)\n"
373       "}\n\n",
374       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
375 
376   // TODO: generate these methods on Kotlin Native once the
377   // [Mutable]List issue is resolved.
378   if (!jvm_dsl_) return;
379 
380   printer->Print(
381       "$jvm_synthetic$"
382       "public fun <E : kotlin.Any> com.google.protobuf.kotlin.ExtensionList<E, "
383       "$message$>.add(value: E) {\n"
384       "  _builder.addExtension(this.extension, value)\n"
385       "}\n\n",
386       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
387 
388   printer->Print(
389       "$jvm_synthetic$"
390       "@Suppress(\"NOTHING_TO_INLINE\")\n"
391       "public inline operator fun <E : kotlin.Any> "
392       "com.google.protobuf.kotlin.ExtensionList<E, "
393       "$message$>.plusAssign"
394       "(value: E) {\n"
395       "  add(value)\n"
396       "}\n\n",
397       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
398 
399   printer->Print(
400       "$jvm_synthetic$"
401       "public fun <E : kotlin.Any> com.google.protobuf.kotlin.ExtensionList<E, "
402       "$message$>.addAll(values: Iterable<E>) {\n"
403       "  for (value in values) {\n"
404       "    add(value)\n"
405       "  }\n"
406       "}\n\n",
407       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
408 
409   printer->Print(
410       "$jvm_synthetic$"
411       "@Suppress(\"NOTHING_TO_INLINE\")\n"
412       "public inline operator fun <E : kotlin.Any> "
413       "com.google.protobuf.kotlin.ExtensionList<E, "
414       "$message$>.plusAssign(values: "
415       "Iterable<E>) {\n"
416       "  addAll(values)\n"
417       "}\n\n",
418       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
419 
420   printer->Print(
421       "$jvm_synthetic$"
422       "public operator fun <E : kotlin.Any> "
423       "com.google.protobuf.kotlin.ExtensionList<E, "
424       "$message$>.set(index: Int, value: "
425       "E) {\n"
426       "  _builder.setExtension(this.extension, index, value)\n"
427       "}\n\n",
428       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
429 
430   printer->Print(
431       "$jvm_synthetic$"
432       "@Suppress(\"NOTHING_TO_INLINE\")\n"
433       "public inline fun com.google.protobuf.kotlin.ExtensionList<*, "
434       "$message$>.clear() {\n"
435       "  clear(extension)\n"
436       "}\n\n",
437       "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name);
438 }
439 }  // namespace kotlin
440 }  // namespace compiler
441 }  // namespace protobuf
442 }  // namespace google
443 
444 #include "google/protobuf/port_undef.inc"
445