Skip to content

Commit 05ecf32

Browse files
committed
Fix default values
1 parent 2000ea5 commit 05ecf32

File tree

13 files changed

+222
-78
lines changed

13 files changed

+222
-78
lines changed

gradle-conventions/src/main/kotlin/conventions-protoc-gen.gradle.kts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ dependencies {
1818
testImplementation(libs.kotlin.test)
1919
}
2020

21-
tasks.jar {
22-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
23-
archiveClassifier = "all"
24-
25-
// Protoc plugins are all fat jars basically (the ones built on jvm)
26-
// be really careful of what you put in the classpath here
27-
from(
28-
configurations.runtimeClasspath.map { prop ->
29-
prop.map { if (it.isDirectory()) it else zipTree(it) }
30-
}
31-
)
21+
if (project.name != "common") {
22+
tasks.jar {
23+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
24+
archiveClassifier = "all"
25+
26+
// Protoc plugins are all fat jars basically (the ones built on jvm)
27+
// be really careful of what you put in the classpath here
28+
from(
29+
configurations.runtimeClasspath.map { prop ->
30+
prop.map { if (it.isDirectory()) it else zipTree(it) }
31+
}
32+
)
33+
}
3234
}
3335

3436
kotlin {

protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Api.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ public fun com.google.protobuf.kotlin.ApiInternal.encodeWith(encoder: kotlinx.rp
191191
}
192192
}
193193

194-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
194+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
195195
encoder.writeEnum(fieldNr = 7, value = syntax.number)
196196
}
197197
}
@@ -275,7 +275,7 @@ private fun com.google.protobuf.kotlin.ApiInternal.computeSize(): Int {
275275
__result = mixins.sumOf { it.asInternal()._size + kotlinx.rpc.protobuf.internal.WireSize.tag(6, kotlinx.rpc.protobuf.internal.WireType.LENGTH_DELIMITED) }
276276
}
277277

278-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
278+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
279279
__result += (kotlinx.rpc.protobuf.internal.WireSize.tag(7, kotlinx.rpc.protobuf.internal.WireType.VARINT) + kotlinx.rpc.protobuf.internal.WireSize.enum(syntax.number))
280280
}
281281

@@ -323,7 +323,7 @@ public fun com.google.protobuf.kotlin.MethodInternal.encodeWith(encoder: kotlinx
323323
}
324324
}
325325

326-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
326+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
327327
encoder.writeEnum(fieldNr = 7, value = syntax.number)
328328
}
329329
}
@@ -399,7 +399,7 @@ private fun com.google.protobuf.kotlin.MethodInternal.computeSize(): Int {
399399
__result = options.sumOf { it.asInternal()._size + kotlinx.rpc.protobuf.internal.WireSize.tag(6, kotlinx.rpc.protobuf.internal.WireType.LENGTH_DELIMITED) }
400400
}
401401

402-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
402+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
403403
__result += (kotlinx.rpc.protobuf.internal.WireSize.tag(7, kotlinx.rpc.protobuf.internal.WireType.VARINT) + kotlinx.rpc.protobuf.internal.WireSize.enum(syntax.number))
404404
}
405405

protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Type.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ public fun com.google.protobuf.kotlin.TypeInternal.encodeWith(encoder: kotlinx.r
279279
encoder.writeMessage(fieldNr = 5, value = sourceContext.asInternal()) { encodeWith(it) }
280280
}
281281

282-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
282+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
283283
encoder.writeEnum(fieldNr = 6, value = syntax.number)
284284
}
285285

@@ -362,7 +362,7 @@ private fun com.google.protobuf.kotlin.TypeInternal.computeSize(): Int {
362362
__result += sourceContext.asInternal()._size.let { kotlinx.rpc.protobuf.internal.WireSize.tag(5, kotlinx.rpc.protobuf.internal.WireType.LENGTH_DELIMITED) + kotlinx.rpc.protobuf.internal.WireSize.int32(it) + it }
363363
}
364364

365-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
365+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
366366
__result += (kotlinx.rpc.protobuf.internal.WireSize.tag(6, kotlinx.rpc.protobuf.internal.WireType.VARINT) + kotlinx.rpc.protobuf.internal.WireSize.enum(syntax.number))
367367
}
368368

@@ -388,11 +388,11 @@ public fun com.google.protobuf.kotlin.FieldInternal.checkRequiredFields() {
388388

389389
@kotlinx.rpc.internal.utils.InternalRpcApi
390390
public fun com.google.protobuf.kotlin.FieldInternal.encodeWith(encoder: kotlinx.rpc.protobuf.internal.WireEncoder) {
391-
if (com.google.protobuf.kotlin.Field.Kind.TYPE_UNKNOWN != kind) {
391+
if (kind != com.google.protobuf.kotlin.Field.Kind.TYPE_UNKNOWN) {
392392
encoder.writeEnum(fieldNr = 1, value = kind.number)
393393
}
394394

395-
if (com.google.protobuf.kotlin.Field.Cardinality.CARDINALITY_UNKNOWN != cardinality) {
395+
if (cardinality != com.google.protobuf.kotlin.Field.Cardinality.CARDINALITY_UNKNOWN) {
396396
encoder.writeEnum(fieldNr = 2, value = cardinality.number)
397397
}
398398

@@ -490,11 +490,11 @@ public fun com.google.protobuf.kotlin.FieldInternal.Companion.decodeWith(msg: co
490490

491491
private fun com.google.protobuf.kotlin.FieldInternal.computeSize(): Int {
492492
var __result = 0
493-
if (com.google.protobuf.kotlin.Field.Kind.TYPE_UNKNOWN != kind) {
493+
if (kind != com.google.protobuf.kotlin.Field.Kind.TYPE_UNKNOWN) {
494494
__result += (kotlinx.rpc.protobuf.internal.WireSize.tag(1, kotlinx.rpc.protobuf.internal.WireType.VARINT) + kotlinx.rpc.protobuf.internal.WireSize.enum(kind.number))
495495
}
496496

497-
if (com.google.protobuf.kotlin.Field.Cardinality.CARDINALITY_UNKNOWN != cardinality) {
497+
if (cardinality != com.google.protobuf.kotlin.Field.Cardinality.CARDINALITY_UNKNOWN) {
498498
__result += (kotlinx.rpc.protobuf.internal.WireSize.tag(2, kotlinx.rpc.protobuf.internal.WireType.VARINT) + kotlinx.rpc.protobuf.internal.WireSize.enum(cardinality.number))
499499
}
500500

@@ -576,7 +576,7 @@ public fun com.google.protobuf.kotlin.EnumInternal.encodeWith(encoder: kotlinx.r
576576
encoder.writeMessage(fieldNr = 4, value = sourceContext.asInternal()) { encodeWith(it) }
577577
}
578578

579-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
579+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
580580
encoder.writeEnum(fieldNr = 5, value = syntax.number)
581581
}
582582

@@ -650,7 +650,7 @@ private fun com.google.protobuf.kotlin.EnumInternal.computeSize(): Int {
650650
__result += sourceContext.asInternal()._size.let { kotlinx.rpc.protobuf.internal.WireSize.tag(4, kotlinx.rpc.protobuf.internal.WireType.LENGTH_DELIMITED) + kotlinx.rpc.protobuf.internal.WireSize.int32(it) + it }
651651
}
652652

653-
if (com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2 != syntax) {
653+
if (syntax != com.google.protobuf.kotlin.Syntax.SYNTAX_PROTO2) {
654654
__result += (kotlinx.rpc.protobuf.internal.WireSize.tag(5, kotlinx.rpc.protobuf.internal.WireType.VARINT) + kotlinx.rpc.protobuf.internal.WireSize.enum(syntax.number))
655655
}
656656

protobuf/protobuf-core/src/commonTest/proto/all_primitives.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ message AllPrimitives {
1818
optional bool bool = 13;
1919
optional string string = 14;
2020
optional bytes bytes = 15;
21+
string requiredString = 16;
22+
bytes requiredBytes = 17;
2123
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
syntax = "proto2";
2+
3+
// https://protobuf.dev/programming-guides/proto2/#default
4+
message WithDefaults {
5+
optional double double = 1 [default = 2.0];
6+
optional float float = 2 [default = 2.0];
7+
optional int32 int32 = 3 [default = 2];
8+
optional int64 int64 = 4 [default = 2];
9+
optional uint32 uint32 = 5 [default = 2];
10+
optional uint64 uint64 = 6 [default = 2];
11+
optional sint32 sint32 = 7 [default = 2];
12+
optional sint64 sint64 = 8 [default = 2];
13+
optional fixed32 fixed32 = 9 [default = 2];
14+
optional fixed64 fixed64 = 10 [default = 2];
15+
optional sfixed32 sfixed32 = 11 [default = 2];
16+
optional sfixed64 sfixed64 = 12 [default = 2];
17+
optional bool bool = 13 [default = true];
18+
optional string string = 14 [default = "str"];
19+
optional bytes bytes = 15 [default = "bytes"];
20+
21+
optional Custom1 enum1 = 16;
22+
optional Custom2 enum2 = 17 [default = BAZ];
23+
24+
enum Custom1 {
25+
FOO = 1; // default
26+
}
27+
28+
enum Custom2 {
29+
BAR = 1; // default
30+
BAZ = 2; // default
31+
}
32+
}

protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ private fun DescriptorProtos.FileDescriptorProto.toDescriptor(
7474
*
7575
* @return The fully qualified name represented as an instance of FqName, specific to the descriptor's context.
7676
*/
77-
private fun Descriptors.GenericDescriptor.fqName(): FqName {
77+
fun Descriptors.GenericDescriptor.fqName(): FqName {
7878
if (nameCache.containsKey(this)) return nameCache[this]!!
7979
val nameCapital = name.simpleProtoNameToKotlin(firstLetterUpper = true)
8080
val nameLower = name.simpleProtoNameToKotlin()

protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ sealed interface FieldType {
5353
override val defaultValue: String,
5454
override val wireType: WireType,
5555
override val isPackable: Boolean = true
56-
) :
57-
FieldType {
56+
) : FieldType {
5857
STRING("String", "\"\"", WireType.LENGTH_DELIMITED, false),
5958
BYTES("ByteArray", "byteArrayOf()", WireType.LENGTH_DELIMITED, false),
6059
BOOL("Boolean", "false", WireType.VARINT),
@@ -74,3 +73,20 @@ sealed interface FieldType {
7473
val fqName: FqName = FqName.Declaration(simpleName, FqName.Package.fromString("kotlin"))
7574
}
7675
}
76+
77+
fun FieldType.scalarDefaultSuffix(): String = when (this) {
78+
FieldType.IntegralType.BOOL -> ""
79+
FieldType.IntegralType.FLOAT -> "f"
80+
FieldType.IntegralType.DOUBLE -> ""
81+
FieldType.IntegralType.INT32 -> ""
82+
FieldType.IntegralType.INT64 -> "L"
83+
FieldType.IntegralType.UINT32 -> "u"
84+
FieldType.IntegralType.UINT64 -> "uL"
85+
FieldType.IntegralType.FIXED32 -> "u"
86+
FieldType.IntegralType.FIXED64 -> "uL"
87+
FieldType.IntegralType.SINT32 -> ""
88+
FieldType.IntegralType.SINT64 -> "L"
89+
FieldType.IntegralType.SFIXED32 -> ""
90+
FieldType.IntegralType.SFIXED64 -> "L"
91+
else -> error("Unsupported scalar type: ${this::class}")
92+
}

protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ data class EnumDeclaration(
4444
) {
4545

4646
fun defaultEntry(): Entry {
47-
return originalEntries.minBy { it.dec.number }
47+
// In proto3 and editions:
48+
// The first value must be a zero value, so that we can use 0 as a numeric default value
49+
// In proto2:
50+
// We use the first value as the default value
51+
return originalEntries.first()
4852
}
4953

5054
data class Entry(

protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66

77
package kotlinx.rpc.protoc.gen
88

9+
import com.google.protobuf.ByteString
10+
import com.google.protobuf.Descriptors
911
import kotlinx.rpc.protoc.gen.core.AModelToKotlinCommonGenerator
1012
import kotlinx.rpc.protoc.gen.core.CodeGenerator
1113
import kotlinx.rpc.protoc.gen.core.INTERNAL_RPC_API_ANNO
1214
import kotlinx.rpc.protoc.gen.core.PB_PKG
1315
import kotlinx.rpc.protoc.gen.core.WITH_CODEC_ANNO
16+
import kotlinx.rpc.protoc.gen.core.fqName
1417
import kotlinx.rpc.protoc.gen.core.model.EnumDeclaration
1518
import kotlinx.rpc.protoc.gen.core.model.FieldDeclaration
1619
import kotlinx.rpc.protoc.gen.core.model.FieldType
@@ -19,6 +22,7 @@ import kotlinx.rpc.protoc.gen.core.model.MessageDeclaration
1922
import kotlinx.rpc.protoc.gen.core.model.Model
2023
import kotlinx.rpc.protoc.gen.core.model.OneOfDeclaration
2124
import kotlinx.rpc.protoc.gen.core.model.WireType
25+
import kotlinx.rpc.protoc.gen.core.model.scalarDefaultSuffix
2226
import org.slf4j.Logger
2327

2428
class ModelToProtobufKotlinCommonGenerator(
@@ -119,6 +123,7 @@ class ModelToProtobufKotlinCommonGenerator(
119123
) {
120124

121125
generatePresenceIndicesObject(declaration)
126+
generateBytesDefaultsObject(declaration)
122127

123128
property(
124129
name = "_size",
@@ -143,7 +148,7 @@ class ModelToProtobufKotlinCommonGenerator(
143148

144149
else -> {
145150
val fieldPresence = if (field.presenceIdx != null) "(PresenceIndices.${field.name})" else ""
146-
"MsgFieldDelegate$fieldPresence { ${field.type.defaultValue} }"
151+
"MsgFieldDelegate$fieldPresence { ${field.safeDefaultValue()} }"
147152
}
148153
}
149154

@@ -184,16 +189,50 @@ class ModelToProtobufKotlinCommonGenerator(
184189
}
185190

186191
clazz("PresenceIndices", "private", declarationType = CodeGenerator.DeclarationType.Object) {
187-
declaration.actualFields.forEachIndexed { i, field ->
188-
if (field.presenceIdx != null) {
189-
property(
190-
field.name,
191-
modifiers = "const",
192-
value = field.presenceIdx.toString(),
193-
type = "Int",
194-
needsNewLineAfterDeclaration = i == declaration.actualFields.lastIndex,
195-
)
192+
val fieldDeclarations = declaration.actualFields.filter { it.presenceIdx != null }
193+
fieldDeclarations.forEachIndexed { i, field ->
194+
property(
195+
field.name,
196+
modifiers = "const",
197+
value = field.presenceIdx.toString(),
198+
type = "Int",
199+
needsNewLineAfterDeclaration = i == fieldDeclarations.lastIndex,
200+
)
201+
}
202+
}
203+
}
204+
205+
private fun CodeGenerator.generateBytesDefaultsObject(declaration: MessageDeclaration) {
206+
val fieldDeclarations = declaration.actualFields
207+
.filter {
208+
it.type == FieldType.IntegralType.BYTES &&
209+
it.dec.hasDefaultValue() &&
210+
!(it.dec.defaultValue as ByteString).isEmpty
211+
}
212+
213+
if (fieldDeclarations.isEmpty()) {
214+
return
215+
}
216+
217+
clazz("BytesDefaults", "private", declarationType = CodeGenerator.DeclarationType.Object) {
218+
fieldDeclarations.forEachIndexed { i, field ->
219+
val value = if (field.dec.hasDefaultValue()) {
220+
val stringValue = (field.dec.defaultValue as ByteString).toString(Charsets.UTF_8)
221+
if (stringValue.isNotEmpty()) {
222+
"\"${stringValue}\".encodeToByteArray()"
223+
} else {
224+
FieldType.IntegralType.BYTES.defaultValue
225+
}
226+
} else {
227+
FieldType.IntegralType.BYTES.defaultValue
196228
}
229+
230+
property(
231+
name = field.name,
232+
value = value,
233+
type = "ByteArray",
234+
needsNewLineAfterDeclaration = i == fieldDeclarations.lastIndex,
235+
)
197236
}
198237
}
199238
}
@@ -369,7 +408,7 @@ class ModelToProtobufKotlinCommonGenerator(
369408
}
370409

371410
is FieldType.List -> if (isPacked) {
372-
val conversion = if (fieldType.value is FieldType.Enum){
411+
val conversion = if (fieldType.value is FieldType.Enum) {
373412
".map { ${(fieldType.value as FieldType.Enum).dec.name.safeFullName()}.fromNumber(it) }"
374413
} else {
375414
""
@@ -791,17 +830,55 @@ class ModelToProtobufKotlinCommonGenerator(
791830

792831
private fun FieldDeclaration.notDefaultCheck(): String {
793832
return when (val fieldType = type) {
794-
is FieldType.IntegralType -> when (fieldType) {
795-
FieldType.IntegralType.BYTES, FieldType.IntegralType.STRING -> "$name.isNotEmpty()"
796-
else -> "$name != ${fieldType.defaultValue}"
833+
is FieldType.IntegralType -> {
834+
val defaultValue = safeDefaultValue()
835+
when {
836+
fieldType == FieldType.IntegralType.STRING &&
837+
defaultValue == FieldType.IntegralType.STRING.defaultValue -> "$name.isNotEmpty()"
838+
839+
fieldType == FieldType.IntegralType.STRING -> "!$name.contentEquals($defaultValue)"
840+
841+
fieldType == FieldType.IntegralType.BYTES &&
842+
defaultValue == FieldType.IntegralType.BYTES.defaultValue -> "$name.isNotEmpty()"
843+
844+
fieldType == FieldType.IntegralType.BYTES -> "!$name.contentEquals($defaultValue)"
845+
846+
else -> "$name != $defaultValue"
847+
}
848+
}
849+
850+
is FieldType.List, is FieldType.Map -> "$name.isNotEmpty()"
851+
852+
is FieldType.Enum -> {
853+
"$name != ${safeDefaultValue()}"
797854
}
798855

799-
is FieldType.List -> "$name.isNotEmpty()"
800856
is FieldType.Message -> error("Message fields should not be checked for default values.")
857+
is FieldType.OneOf -> "null"
858+
}
859+
}
860+
861+
private fun FieldDeclaration.safeDefaultValue(): String {
862+
if (!dec.hasDefaultValue()) {
863+
return type.defaultValue ?: error("No default value for field $name")
864+
}
865+
866+
return when (val value = dec.defaultValue) {
867+
is String -> {
868+
"\"$value\""
869+
}
801870

802-
is FieldType.Enum -> "${fieldType.defaultValue} != $name"
871+
is ByteString -> {
872+
"BytesDefaults.$name"
873+
}
803874

804-
else -> "$name.isNotEmpty()"
875+
is Descriptors.EnumValueDescriptor -> {
876+
value.fqName().safeFullName()
877+
}
878+
879+
else -> {
880+
"${value}${type.scalarDefaultSuffix()}"
881+
}
805882
}
806883
}
807884

0 commit comments

Comments
 (0)