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