Skip to content

Commit e9c9970

Browse files
eamonnmcmanuscopybara-github
authored andcommitted
In the Kotlin DSL, reference builder methods with property syntax.
Instead of `_builder.getFoo()` and `_builder.setFoo(value)`, the generated code now uses `_builder.foo` and `_builder.foo = value`. When compiling against the Java Proto API, this makes no difference, since the Kotlin compiler treats Java methods `getFoo`/`setFoo` as defining a Kotlin property `foo`. But if a compatible proto API is implemented in Kotlin then there is no such equivalence. Such an implementation would have to define either both forms or just the one that the DSL uses. For a Kotlin API it is more natural to use properties. Similarly, the generated Java methods `getFoosList()`, `getFoosCount()`, `getFoosMap()`, and `getEnumValue()` are accessed via property names (`foosList`, `foosCount`, `foosMap`, `enumValue`). PiperOrigin-RevId: 629220383
1 parent f2e1ad3 commit e9c9970

File tree

16 files changed

+174
-57
lines changed

16 files changed

+174
-57
lines changed

java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class Proto2Test {
225225
TestAllTypesKt.repeatedGroup { a = 1 },
226226
TestAllTypesKt.repeatedGroup { a = 2 },
227227
TestAllTypesKt.repeatedGroup { a = 3 },
228-
TestAllTypesKt.repeatedGroup { a = 4 }
228+
TestAllTypesKt.repeatedGroup { a = 4 },
229229
)
230230
)
231231
repeatedGroup[0] = TestAllTypesKt.repeatedGroup { a = 5 }
@@ -235,7 +235,7 @@ class Proto2Test {
235235
TestAllTypesKt.repeatedGroup { a = 5 },
236236
TestAllTypesKt.repeatedGroup { a = 2 },
237237
TestAllTypesKt.repeatedGroup { a = 3 },
238-
TestAllTypesKt.repeatedGroup { a = 4 }
238+
TestAllTypesKt.repeatedGroup { a = 4 },
239239
)
240240
)
241241

@@ -249,7 +249,7 @@ class Proto2Test {
249249
nestedMessage { bb = 1 },
250250
nestedMessage { bb = 2 },
251251
nestedMessage { bb = 3 },
252-
nestedMessage { bb = 4 }
252+
nestedMessage { bb = 4 },
253253
)
254254
)
255255
repeatedNestedMessage[0] = nestedMessage { bb = 5 }
@@ -259,7 +259,7 @@ class Proto2Test {
259259
nestedMessage { bb = 5 },
260260
nestedMessage { bb = 2 },
261261
nestedMessage { bb = 3 },
262-
nestedMessage { bb = 4 }
262+
nestedMessage { bb = 4 },
263263
)
264264
)
265265

@@ -548,7 +548,7 @@ class Proto2Test {
548548
repeatedGroupExtension { a = 1 },
549549
repeatedGroupExtension { a = 2 },
550550
repeatedGroupExtension { a = 3 },
551-
repeatedGroupExtension { a = 4 }
551+
repeatedGroupExtension { a = 4 },
552552
)
553553
)
554554
this[UnittestProto.repeatedGroupExtension][0] = repeatedGroupExtension { a = 5 }
@@ -558,7 +558,7 @@ class Proto2Test {
558558
repeatedGroupExtension { a = 5 },
559559
repeatedGroupExtension { a = 2 },
560560
repeatedGroupExtension { a = 3 },
561-
repeatedGroupExtension { a = 4 }
561+
repeatedGroupExtension { a = 4 },
562562
)
563563
)
564564

@@ -575,7 +575,7 @@ class Proto2Test {
575575
nestedMessage { bb = 1 },
576576
nestedMessage { bb = 2 },
577577
nestedMessage { bb = 3 },
578-
nestedMessage { bb = 4 }
578+
nestedMessage { bb = 4 },
579579
)
580580
)
581581
this[UnittestProto.repeatedNestedMessageExtension][0] = nestedMessage { bb = 5 }
@@ -585,7 +585,7 @@ class Proto2Test {
585585
nestedMessage { bb = 5 },
586586
nestedMessage { bb = 2 },
587587
nestedMessage { bb = 3 },
588-
nestedMessage { bb = 4 }
588+
nestedMessage { bb = 4 },
589589
)
590590
)
591591

@@ -757,7 +757,7 @@ class Proto2Test {
757757
1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO,
758758
2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR,
759759
3 to Proto2MapEnum.PROTO2_MAP_ENUM_BAZ,
760-
4 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO
760+
4 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO,
761761
)
762762
)
763763
}
@@ -844,6 +844,11 @@ class Proto2Test {
844844
cachedSize_ = "foo"
845845
serializedSize_ = true
846846
by = "foo"
847+
dEPRECATEDFoo = "foo"
848+
DEPRECATEDBar = "foo"
849+
iD = "foo"
850+
aBNotification = "foo"
851+
notDEPRECATEDFoo = "foo"
847852
}
848853
)
849854
.isEqualTo(
@@ -869,6 +874,11 @@ class Proto2Test {
869874
.setCachedSize_("foo")
870875
.setSerializedSize_(true)
871876
.setBy("foo")
877+
.setDEPRECATEDFoo("foo")
878+
.setDEPRECATEDBar("foo")
879+
.setID("foo")
880+
.setABNotification("foo")
881+
.setNotDEPRECATEDFoo("foo")
872882
.build()
873883
)
874884

java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class Proto3Test {
8181
nestedMessage { bb = 1 },
8282
nestedMessage { bb = 2 },
8383
nestedMessage { bb = 3 },
84-
nestedMessage { bb = 4 }
84+
nestedMessage { bb = 4 },
8585
)
8686
)
8787
repeatedNestedMessage[0] = nestedMessage { bb = 5 }
@@ -91,7 +91,7 @@ class Proto3Test {
9191
nestedMessage { bb = 5 },
9292
nestedMessage { bb = 2 },
9393
nestedMessage { bb = 3 },
94-
nestedMessage { bb = 4 }
94+
nestedMessage { bb = 4 },
9595
)
9696
)
9797

@@ -200,6 +200,11 @@ class Proto3Test {
200200
pairs["foo"] = 1
201201
LeadingUnderscore = "foo"
202202
option = 1
203+
dEPRECATEDFoo = "foo"
204+
iD = "foo"
205+
aBNotification = "foo"
206+
DEPRECATEDBar = "foo"
207+
notDEPRECATEDFoo = "foo"
203208
}
204209
)
205210
.isEqualTo(
@@ -237,6 +242,11 @@ class Proto3Test {
237242
.putPairs("foo", 1)
238243
.setLeadingUnderscore("foo")
239244
.setOption(1)
245+
.setDEPRECATEDFoo("foo")
246+
.setID("foo")
247+
.setABNotification("foo")
248+
.setDEPRECATEDBar("foo")
249+
.setNotDEPRECATEDFoo("foo")
240250
.build()
241251
)
242252

java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ message EvilNamesProto2 {
4343
optional string cached_size = 23;
4444
optional bool serialized_size = 24;
4545
optional string by = 25;
46+
47+
optional string DEPRECATED_foo = 26;
48+
optional group DEPRECATED_NavigationImageRequested = 27 {
49+
optional int32 DEPRECATED_FooBar = 28;
50+
}
51+
optional string __DEPRECATED_Bar = 29;
52+
optional string ID = 30;
53+
optional string a_b_notification = 31;
54+
optional string not_DEPRECATED_foo = 32;
4655
}
4756

4857
message List {}

java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ message EvilNamesProto3 {
5656
oneof _leading_underscore_oneof {
5757
int32 option = 34;
5858
}
59+
60+
optional string DEPRECATED_foo = 35;
61+
optional string ID = 36;
62+
optional string a_b_notification = 37;
63+
optional string __DEPRECATED_Bar = 38;
64+
optional string not_DEPRECATED_foo = 39;
5965
}
6066

6167
message HardKeywordsAllTypesProto3 {

src/google/protobuf/compiler/java/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ cc_library(
144144
"@com_google_absl//absl/container:flat_hash_map",
145145
"@com_google_absl//absl/log:absl_check",
146146
"@com_google_absl//absl/log:absl_log",
147+
"@com_google_absl//absl/strings",
147148
"@com_google_absl//absl/strings:string_view",
148149
],
149150
)

src/google/protobuf/compiler/java/field_common.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
#include "google/protobuf/compiler/java/field_common.h"
22

3+
#include <cstddef>
34
#include <string>
45

6+
#include "absl/strings/str_cat.h"
57
#include "google/protobuf/compiler/java/helpers.h"
8+
#include "google/protobuf/compiler/java/names.h"
69
#include "google/protobuf/descriptor.h"
710

811
namespace google {
912
namespace protobuf {
1013
namespace compiler {
1114
namespace java {
1215

16+
std::string GetKotlinPropertyName(const FieldDescriptor* descriptor);
17+
1318
void SetCommonFieldVariables(
1419
const FieldDescriptor* descriptor, const FieldGeneratorInfo* info,
1520
absl::flat_hash_map<absl::string_view, std::string>* variables) {
@@ -30,6 +35,11 @@ void SetCommonFieldVariables(
3035
(*variables)["kt_name"] = IsForbiddenKotlin(info->name)
3136
? absl::StrCat(info->name, "_")
3237
: info->name;
38+
auto kt_property_name = GetKotlinPropertyName(descriptor);
39+
(*variables)["kt_property_name"] = kt_property_name;
40+
(*variables)["kt_safe_name"] = IsForbiddenKotlin(kt_property_name)
41+
? absl::StrCat("`", kt_property_name, "`")
42+
: kt_property_name;
3343
(*variables)["kt_capitalized_name"] =
3444
IsForbiddenKotlin(info->name) ? absl::StrCat(info->capitalized_name, "_")
3545
: info->capitalized_name;
@@ -51,6 +61,52 @@ void SetCommonFieldVariables(
5161
}
5262
}
5363

64+
// Locale-independent ASCII upper and lower case munging.
65+
static bool IsUpper(char c) {
66+
return static_cast<unsigned int>(c - 'A') <= 'Z' - 'A';
67+
}
68+
69+
static char ToLower(char c) { return IsUpper(c) ? c - 'A' + 'a' : c; }
70+
71+
// Returns the name by which the generated Java getters and setters should be
72+
// referenced from Kotlin as properties. In the simplest case, the original name
73+
// is something like `foo_bar`, which gets translated into `getFooBar()` etc,
74+
// and that in turn can be referenced from Kotlin as `fooBar`.
75+
//
76+
// The algorithm for translating proto names into Java getters and setters is
77+
// straightforward. The first letter of each underscore-separated word gets
78+
// uppercased and the underscores are deleted. There are no other changes, so in
79+
// particular if the proto name has a string of capitals then those remain
80+
// as-is.
81+
//
82+
// The algorithm that the Kotlin compiler uses to derive the property name is
83+
// slightly more complicated. If the first character after `get` (etc) is a
84+
// capital and the second isn't, then the property name is just that string with
85+
// its first letter lowercased. So `getFoo` becomes `foo` and `getX` becomes
86+
// `x`. But if there is more than one capital, then all but the last get
87+
// lowercased. So `getHTMLPage` becomes `htmlPage`. If there are only capitals
88+
// then they all get lowercased, so `getID` becomes `id`.
89+
// TODO: move this to a Kotlin-specific location
90+
std::string GetKotlinPropertyName(const FieldDescriptor* descriptor) {
91+
// Find the first non-capital. If it is the second character, then we just
92+
// need to lowercase the first one. Otherwise we need to lowercase everything
93+
// up to but not including the last capital, except that if everything is
94+
// capitals then everything must be lowercased.
95+
std::string capitalized_name = CapitalizedFieldName(descriptor);
96+
std::string kt_property_name = capitalized_name;
97+
size_t first_non_capital;
98+
for (first_non_capital = 0; first_non_capital < capitalized_name.length() &&
99+
IsUpper(capitalized_name[first_non_capital]);
100+
first_non_capital++) {
101+
}
102+
size_t stop = first_non_capital;
103+
if (stop > 1 && stop < capitalized_name.length()) stop--;
104+
for (size_t i = 0; i < stop; i++) {
105+
kt_property_name[i] = ToLower(kt_property_name[i]);
106+
}
107+
return kt_property_name;
108+
}
109+
54110
void SetCommonOneofVariables(
55111
const FieldDescriptor* descriptor, const OneofGeneratorInfo* info,
56112
absl::flat_hash_map<absl::string_view, std::string>* variables) {

src/google/protobuf/compiler/java/immutable/enum_field.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,21 +273,21 @@ void ImmutableEnumFieldGenerator::GenerateKotlinDslMembers(
273273
printer->Print(variables_,
274274
"$kt_deprecation$public var $kt_name$: $kt_type$\n"
275275
" @JvmName(\"${$get$kt_capitalized_name$$}$\")\n"
276-
" get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n"
276+
" get() = $kt_dsl_builder$.${$$kt_safe_name$$}$\n"
277277
" @JvmName(\"${$set$kt_capitalized_name$$}$\")\n"
278278
" set(value) {\n"
279-
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
279+
" $kt_dsl_builder$.${$$kt_safe_name$$}$ = value\n"
280280
" }\n");
281281

282282
if (SupportUnknownEnumValue(descriptor_)) {
283283
printer->Print(
284284
variables_,
285285
"$kt_deprecation$public var $kt_name$Value: kotlin.Int\n"
286286
" @JvmName(\"${$get$kt_capitalized_name$Value$}$\")\n"
287-
" get() = $kt_dsl_builder$.${$get$capitalized_name$Value$}$()\n"
287+
" get() = $kt_dsl_builder$.${$$kt_property_name$Value$}$\n"
288288
" @JvmName(\"${$set$kt_capitalized_name$Value$}$\")\n"
289289
" set(value) {\n"
290-
" $kt_dsl_builder$.${$set$capitalized_name$Value$}$(value)\n"
290+
" $kt_dsl_builder$.${$$kt_property_name$Value$}$ = value\n"
291291
" }\n");
292292
}
293293

@@ -1100,7 +1100,7 @@ void RepeatedImmutableEnumFieldGenerator::GenerateKotlinDslMembers(
11001100
"<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n"
11011101
" @kotlin.jvm.JvmSynthetic\n"
11021102
" get() = com.google.protobuf.kotlin.DslList(\n"
1103-
" $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n"
1103+
" $kt_dsl_builder$.${$$kt_property_name$List$}$\n"
11041104
" )\n");
11051105

11061106
WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER,

src/google/protobuf/compiler/java/immutable/map_field.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ void ImmutableMapFieldGenerator::GenerateKotlinDslMembers(
981981
" @kotlin.jvm.JvmSynthetic\n"
982982
" @JvmName(\"get$kt_capitalized_name$Map\")\n"
983983
" get() = com.google.protobuf.kotlin.DslMap(\n"
984-
" $kt_dsl_builder$.${$get$capitalized_name$Map$}$()\n"
984+
" $kt_dsl_builder$.${$$kt_property_name$Map$}$\n"
985985
" )\n");
986986

987987
WriteFieldDocComment(printer, descriptor_, context_->options(),

src/google/protobuf/compiler/java/immutable/message_field.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,10 @@ void ImmutableMessageFieldGenerator::GenerateKotlinDslMembers(
378378
printer->Print(variables_,
379379
"$kt_deprecation$public var $kt_name$: $kt_type$\n"
380380
" @JvmName(\"${$get$kt_capitalized_name$$}$\")\n"
381-
" get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n"
381+
" get() = $kt_dsl_builder$.${$$kt_safe_name$$}$\n"
382382
" @JvmName(\"${$set$kt_capitalized_name$$}$\")\n"
383383
" set(value) {\n"
384-
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
384+
" $kt_dsl_builder$.${$$kt_safe_name$$}$ = value\n"
385385
" }\n");
386386

387387
WriteFieldAccessorDocComment(printer, descriptor_, CLEARER,
@@ -1376,7 +1376,7 @@ void RepeatedImmutableMessageFieldGenerator::GenerateKotlinDslMembers(
13761376
"<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n"
13771377
" @kotlin.jvm.JvmSynthetic\n"
13781378
" get() = com.google.protobuf.kotlin.DslList(\n"
1379-
" $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n"
1379+
" $kt_dsl_builder$.${$$kt_property_name$List$}$\n"
13801380
" )\n");
13811381

13821382
WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER,

src/google/protobuf/compiler/java/immutable/primitive_field.cc

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,27 @@ void ImmutablePrimitiveFieldGenerator::GenerateKotlinDslMembers(
291291
io::Printer* printer) const {
292292
WriteFieldDocComment(printer, descriptor_, context_->options(),
293293
/* kdoc */ true);
294-
printer->Print(variables_,
295-
"$kt_deprecation$public var $kt_name$: $kt_type$\n"
296-
" @JvmName(\"${$get$kt_capitalized_name$$}$\")\n"
297-
" get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n"
298-
" @JvmName(\"${$set$kt_capitalized_name$$}$\")\n"
299-
" set(value) {\n"
300-
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
301-
" }\n");
294+
if (descriptor_->name() == "is_initialized") {
295+
printer->Print(
296+
variables_,
297+
"// TODO: remove this hack; we should access properties\n"
298+
"$kt_deprecation$public var $kt_name$: $kt_type$\n"
299+
" @JvmName(\"${$get$kt_capitalized_name$$}$\")\n"
300+
" get() = $kt_dsl_builder$.${$get$kt_capitalized_name$$}$()\n"
301+
" @JvmName(\"${$set$kt_capitalized_name$$}$\")\n"
302+
" set(value) {\n"
303+
" $kt_dsl_builder$.${$set$kt_capitalized_name$$}$(value)\n"
304+
" }\n");
305+
} else {
306+
printer->Print(variables_,
307+
"$kt_deprecation$public var $kt_name$: $kt_type$\n"
308+
" @JvmName(\"${$get$kt_capitalized_name$$}$\")\n"
309+
" get() = $kt_dsl_builder$.${$$kt_safe_name$$}$\n"
310+
" @JvmName(\"${$set$kt_capitalized_name$$}$\")\n"
311+
" set(value) {\n"
312+
" $kt_dsl_builder$.${$$kt_safe_name$$}$ = value\n"
313+
" }\n");
314+
}
302315

303316
WriteFieldAccessorDocComment(printer, descriptor_, CLEARER,
304317
context_->options(),
@@ -848,7 +861,7 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateKotlinDslMembers(
848861
"<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n"
849862
" @kotlin.jvm.JvmSynthetic\n"
850863
" get() = com.google.protobuf.kotlin.DslList(\n"
851-
" $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n"
864+
" $kt_dsl_builder$.${$$kt_property_name$List$}$\n"
852865
" )\n");
853866

854867
WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER,

0 commit comments

Comments
 (0)