Skip to content

Commit 54e032b

Browse files
tikurahuloldergod
andauthored
Add support for mutable messages in Wire's Kotlin Generator. (#3217)
* Add support for mutable messages in Wire's Kotlin Generator. * Sometimes its very useful to avoid needing to create new object graph instances when all we need to do is to serialize the object graph into a `ProtoSink`. * Mutable messages solves for this, for code that is particularly performance sensitive where a large number of objects would have otherwise been created which adds unnecesssary GC pressure. * Mutable messages should be **used very carefully**. There are **no guarantees of safety whatsoever**. * When using mutable messages, `hashCode()` is no longer cached. * Mutable messages don't support `redact()`. Test: Added a unit test in KotlinGeneratorTest * Add mutableTypes to Wire plugin and set golden file * Address comments in the PR. * `newBuilder()` throws an `UnsupportedOperationException` instead. Thanks @oldergod. * Updated the `KotlinGeneratorTest` to use the renamed option in the `KotlinGenerator`. * Update goldens to include the new error message. * Update API files for `wire-compiler`. * Open `unknownFields` for Mutable messages. * `Mutable` messsages now have access to mutable `unknownFields`. Test: Updated KotlinGeneratorTest and the golden tests. * Open `unknownFields` for Mutable messages. * `Mutable` messsages now have access to mutable `unknownFields`. Test: Updated KotlinGeneratorTest and the golden tests. * Bring back referential equality check. * Regenerate golden files. * Update API files for `wire-gradle-plugin`. * Update API files for `wire-kotlin-generator`. * Spotless checks. --------- Co-authored-by: Benoît Quenaudon <benoit@quenaudon.com>
1 parent 658d608 commit 54e032b

21 files changed

Lines changed: 512 additions & 33 deletions

File tree

wire-compiler/api/wire-compiler.api

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ public final class com/squareup/wire/schema/JavaTarget : com/squareup/wire/schem
116116
}
117117

118118
public final class com/squareup/wire/schema/KotlinTarget : com/squareup/wire/schema/Target {
119-
public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;Z)V
120-
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
119+
public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;ZZ)V
120+
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
121121
public final fun component1 ()Ljava/util/List;
122122
public final fun component10 ()Lcom/squareup/wire/kotlin/RpcRole;
123123
public final fun component11 ()Z
@@ -134,8 +134,8 @@ public final class com/squareup/wire/schema/KotlinTarget : com/squareup/wire/sch
134134
public final fun component7 ()Z
135135
public final fun component8 ()Z
136136
public final fun component9 ()Lcom/squareup/wire/kotlin/RpcCallStyle;
137-
public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;Z)Lcom/squareup/wire/schema/KotlinTarget;
138-
public static synthetic fun copy$default (Lcom/squareup/wire/schema/KotlinTarget;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;ZILjava/lang/Object;)Lcom/squareup/wire/schema/KotlinTarget;
137+
public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;ZZ)Lcom/squareup/wire/schema/KotlinTarget;
138+
public static synthetic fun copy$default (Lcom/squareup/wire/schema/KotlinTarget;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZLcom/squareup/wire/kotlin/EnumMode;ZZILjava/lang/Object;)Lcom/squareup/wire/schema/KotlinTarget;
139139
public fun copyTarget (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;)Lcom/squareup/wire/schema/Target;
140140
public fun equals (Ljava/lang/Object;)Z
141141
public final fun getAndroid ()Z

wire-compiler/src/main/java/com/squareup/wire/schema/Target.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ data class KotlinTarget(
142142
* when targeting Kotlin/JS, where `Long` cursors are inefficient.
143143
*/
144144
private val emitProtoReader32: Boolean = false,
145+
146+
/**
147+
* If true, the generated classes will be mutable..
148+
*/
149+
private val mutableTypes: Boolean = false,
145150
) : Target() {
146151
override fun newHandler(): SchemaHandler {
147152
return KotlinSchemaHandler(
@@ -159,6 +164,7 @@ data class KotlinTarget(
159164
escapeKotlinKeywords = escapeKotlinKeywords,
160165
enumMode = enumMode,
161166
emitProtoReader32 = emitProtoReader32,
167+
mutableTypes = mutableTypes,
162168
)
163169
}
164170

wire-golden-files/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ plugins {
55
}
66

77
wire {
8+
kotlin {
9+
includes = listOf("squareup.wire.mutable.*")
10+
out = "src/main/kotlin"
11+
mutableTypes = true
12+
}
13+
814
kotlin {
915
includes = listOf("squareup.wire.unrecognized_constant.*")
1016
out = "src/main/kotlin"

wire-golden-files/src/main/kotlin/HundredsFields.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ import kotlin.String
2626
import kotlin.Suppress
2727
import okio.ByteString
2828

29-
/**
30-
* import "google/protobuf/descriptor.proto";
31-
*/
3229
public class HundredsFields(
3330
@field:WireField(
3431
tag = 1,
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Code generated by Wire protocol buffer compiler, do not edit.
2+
// Source: squareup.wire.mutable.Header in squareup/wire/mutable_types.proto
3+
@file:Suppress(
4+
"DEPRECATION",
5+
"RUNTIME_ANNOTATION_NOT_SUPPORTED",
6+
)
7+
8+
package squareup.wire.mutable
9+
10+
import com.squareup.wire.FieldEncoding
11+
import com.squareup.wire.Message
12+
import com.squareup.wire.ProtoAdapter
13+
import com.squareup.wire.ProtoReader
14+
import com.squareup.wire.ProtoWriter
15+
import com.squareup.wire.ReverseProtoWriter
16+
import com.squareup.wire.Syntax.PROTO_2
17+
import com.squareup.wire.WireField
18+
import com.squareup.wire.`internal`.JvmField
19+
import kotlin.Any
20+
import kotlin.Boolean
21+
import kotlin.Deprecated
22+
import kotlin.DeprecationLevel
23+
import kotlin.Int
24+
import kotlin.Long
25+
import kotlin.Nothing
26+
import kotlin.String
27+
import kotlin.Suppress
28+
import kotlin.UnsupportedOperationException
29+
import okio.ByteString
30+
31+
public class MutableHeader(
32+
@field:WireField(
33+
tag = 1,
34+
adapter = "com.squareup.wire.ProtoAdapter#UINT64",
35+
schemaIndex = 0,
36+
)
37+
public var id: Long? = null,
38+
override var unknownFields: ByteString = ByteString.EMPTY,
39+
) : Message<MutableHeader, Nothing>(ADAPTER, unknownFields) {
40+
@Deprecated(
41+
message = "Shouldn't be used in Kotlin",
42+
level = DeprecationLevel.HIDDEN,
43+
)
44+
override fun newBuilder(): Nothing = throw UnsupportedOperationException("newBuilder() is unsupported for mutable message types")
45+
46+
override fun equals(other: Any?): Boolean {
47+
if (other === this) return true
48+
if (other !is MutableHeader) return false
49+
if (unknownFields != other.unknownFields) return false
50+
if (id != other.id) return false
51+
return true
52+
}
53+
54+
override fun hashCode(): Int {
55+
var result = 0
56+
result = unknownFields.hashCode()
57+
result = result * 37 + (id?.hashCode() ?: 0)
58+
return result
59+
}
60+
61+
override fun toString(): String {
62+
val result = mutableListOf<String>()
63+
if (id != null) result += """id=$id"""
64+
return result.joinToString(prefix = "MutableHeader{", separator = ", ", postfix = "}")
65+
}
66+
67+
public companion object {
68+
@JvmField
69+
public val ADAPTER: ProtoAdapter<MutableHeader> = object : ProtoAdapter<MutableHeader>(
70+
FieldEncoding.LENGTH_DELIMITED,
71+
MutableHeader::class,
72+
"type.googleapis.com/squareup.wire.mutable.Header",
73+
PROTO_2,
74+
null,
75+
"squareup/wire/mutable_types.proto"
76+
) {
77+
override fun encodedSize(`value`: MutableHeader): Int {
78+
var size = value.unknownFields.size
79+
size += ProtoAdapter.UINT64.encodedSizeWithTag(1, value.id)
80+
return size
81+
}
82+
83+
override fun encode(writer: ProtoWriter, `value`: MutableHeader) {
84+
ProtoAdapter.UINT64.encodeWithTag(writer, 1, value.id)
85+
writer.writeBytes(value.unknownFields)
86+
}
87+
88+
override fun encode(writer: ReverseProtoWriter, `value`: MutableHeader) {
89+
writer.writeBytes(value.unknownFields)
90+
ProtoAdapter.UINT64.encodeWithTag(writer, 1, value.id)
91+
}
92+
93+
override fun decode(reader: ProtoReader): MutableHeader {
94+
var id: Long? = null
95+
val unknownFields = reader.forEachTag { tag ->
96+
when (tag) {
97+
1 -> id = ProtoAdapter.UINT64.decode(reader)
98+
else -> reader.readUnknownField(tag)
99+
}
100+
}
101+
return MutableHeader(
102+
id = id,
103+
unknownFields = unknownFields
104+
)
105+
}
106+
107+
override fun redact(`value`: MutableHeader): MutableHeader = throw UnsupportedOperationException("redact() is unsupported for mutable message types")
108+
}
109+
110+
private const val serialVersionUID: Long = 0L
111+
}
112+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Code generated by Wire protocol buffer compiler, do not edit.
2+
// Source: squareup.wire.mutable.Packet in squareup/wire/mutable_types.proto
3+
@file:Suppress(
4+
"DEPRECATION",
5+
"RUNTIME_ANNOTATION_NOT_SUPPORTED",
6+
)
7+
8+
package squareup.wire.mutable
9+
10+
import com.squareup.wire.FieldEncoding
11+
import com.squareup.wire.Message
12+
import com.squareup.wire.ProtoAdapter
13+
import com.squareup.wire.ProtoReader
14+
import com.squareup.wire.ProtoWriter
15+
import com.squareup.wire.ReverseProtoWriter
16+
import com.squareup.wire.Syntax.PROTO_2
17+
import com.squareup.wire.WireField
18+
import com.squareup.wire.`internal`.JvmField
19+
import kotlin.Any
20+
import kotlin.Boolean
21+
import kotlin.Deprecated
22+
import kotlin.DeprecationLevel
23+
import kotlin.Int
24+
import kotlin.Long
25+
import kotlin.Nothing
26+
import kotlin.String
27+
import kotlin.Suppress
28+
import kotlin.UnsupportedOperationException
29+
import okio.ByteString
30+
31+
public class MutablePacket(
32+
@field:WireField(
33+
tag = 1,
34+
adapter = "squareup.wire.mutable.MutableHeader#ADAPTER",
35+
declaredName = "header",
36+
schemaIndex = 0,
37+
)
38+
public var header_: MutableHeader? = null,
39+
@field:WireField(
40+
tag = 2,
41+
adapter = "squareup.wire.mutable.MutablePayload#ADAPTER",
42+
schemaIndex = 1,
43+
)
44+
public var payload: MutablePayload? = null,
45+
override var unknownFields: ByteString = ByteString.EMPTY,
46+
) : Message<MutablePacket, Nothing>(ADAPTER, unknownFields) {
47+
@Deprecated(
48+
message = "Shouldn't be used in Kotlin",
49+
level = DeprecationLevel.HIDDEN,
50+
)
51+
override fun newBuilder(): Nothing = throw UnsupportedOperationException("newBuilder() is unsupported for mutable message types")
52+
53+
override fun equals(other: Any?): Boolean {
54+
if (other === this) return true
55+
if (other !is MutablePacket) return false
56+
if (unknownFields != other.unknownFields) return false
57+
if (header_ != other.header_) return false
58+
if (payload != other.payload) return false
59+
return true
60+
}
61+
62+
override fun hashCode(): Int {
63+
var result = 0
64+
result = unknownFields.hashCode()
65+
result = result * 37 + (header_?.hashCode() ?: 0)
66+
result = result * 37 + (payload?.hashCode() ?: 0)
67+
return result
68+
}
69+
70+
override fun toString(): String {
71+
val result = mutableListOf<String>()
72+
if (header_ != null) result += """header_=$header_"""
73+
if (payload != null) result += """payload=$payload"""
74+
return result.joinToString(prefix = "MutablePacket{", separator = ", ", postfix = "}")
75+
}
76+
77+
public companion object {
78+
@JvmField
79+
public val ADAPTER: ProtoAdapter<MutablePacket> = object : ProtoAdapter<MutablePacket>(
80+
FieldEncoding.LENGTH_DELIMITED,
81+
MutablePacket::class,
82+
"type.googleapis.com/squareup.wire.mutable.Packet",
83+
PROTO_2,
84+
null,
85+
"squareup/wire/mutable_types.proto"
86+
) {
87+
override fun encodedSize(`value`: MutablePacket): Int {
88+
var size = value.unknownFields.size
89+
size += MutableHeader.ADAPTER.encodedSizeWithTag(1, value.header_)
90+
size += MutablePayload.ADAPTER.encodedSizeWithTag(2, value.payload)
91+
return size
92+
}
93+
94+
override fun encode(writer: ProtoWriter, `value`: MutablePacket) {
95+
MutableHeader.ADAPTER.encodeWithTag(writer, 1, value.header_)
96+
MutablePayload.ADAPTER.encodeWithTag(writer, 2, value.payload)
97+
writer.writeBytes(value.unknownFields)
98+
}
99+
100+
override fun encode(writer: ReverseProtoWriter, `value`: MutablePacket) {
101+
writer.writeBytes(value.unknownFields)
102+
MutablePayload.ADAPTER.encodeWithTag(writer, 2, value.payload)
103+
MutableHeader.ADAPTER.encodeWithTag(writer, 1, value.header_)
104+
}
105+
106+
override fun decode(reader: ProtoReader): MutablePacket {
107+
var header_: MutableHeader? = null
108+
var payload: MutablePayload? = null
109+
val unknownFields = reader.forEachTag { tag ->
110+
when (tag) {
111+
1 -> header_ = MutableHeader.ADAPTER.decode(reader)
112+
2 -> payload = MutablePayload.ADAPTER.decode(reader)
113+
else -> reader.readUnknownField(tag)
114+
}
115+
}
116+
return MutablePacket(
117+
header_ = header_,
118+
payload = payload,
119+
unknownFields = unknownFields
120+
)
121+
}
122+
123+
override fun redact(`value`: MutablePacket): MutablePacket = throw UnsupportedOperationException("redact() is unsupported for mutable message types")
124+
}
125+
126+
private const val serialVersionUID: Long = 0L
127+
}
128+
}

0 commit comments

Comments
 (0)