16
16
17
17
package org.jetbrains.kotlin.android.parcel
18
18
19
+ import kotlinx.android.parcel.Parceler
19
20
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
20
21
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension.Companion.createMethod
22
+ import org.jetbrains.kotlin.android.parcel.serializers.PARCEL_TYPE
21
23
import org.jetbrains.kotlin.android.parcel.serializers.ParcelSerializer
24
+ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
22
25
import org.jetbrains.kotlin.codegen.ExpressionCodegen
23
26
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
24
27
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
@@ -33,13 +36,15 @@ import org.jetbrains.kotlin.codegen.OwnerKind
33
36
import org.jetbrains.kotlin.codegen.context.ClassContext
34
37
import org.jetbrains.kotlin.codegen.writeSyntheticClassMetadata
35
38
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
39
+ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
36
40
import org.jetbrains.kotlin.incremental.components.NoLookupLocation.*
37
41
import org.jetbrains.kotlin.name.FqName
38
42
import org.jetbrains.kotlin.resolve.DescriptorFactory
39
43
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
40
44
import org.jetbrains.kotlin.resolve.descriptorUtil.module
41
45
import org.jetbrains.kotlin.resolve.scopes.MemberScope
42
46
import org.jetbrains.kotlin.types.KotlinType
47
+ import org.jetbrains.kotlin.types.TypeUtils
43
48
import org.jetbrains.kotlin.types.Variance
44
49
import org.jetbrains.org.objectweb.asm.Opcodes.*
45
50
import org.jetbrains.org.objectweb.asm.Type
@@ -48,6 +53,9 @@ import java.io.FileDescriptor
48
53
class ParcelableCodegenExtension : ExpressionCodegenExtension {
49
54
private companion object {
50
55
private val FILE_DESCRIPTOR_FQNAME = FqName (FileDescriptor ::class .java.canonicalName)
56
+ private val PARCELER_FQNAME = FqName (Parceler ::class .java.canonicalName)
57
+
58
+ fun KotlinType.isParceler () = constructor .declarationDescriptor?.fqNameSafe == PARCELER_FQNAME
51
59
}
52
60
53
61
override fun generateClassSyntheticParts (codegen : ImplementationBodyCodegen ) {
@@ -60,32 +68,54 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension {
60
68
val parcelClassType = ParcelableResolveExtension .resolveParcelClassType(parcelableClass.module)
61
69
val parcelAsmType = codegen.typeMapper.mapType(parcelClassType)
62
70
71
+ val parcelerObject = parcelableClass.companionObjectDescriptor?.takeIf {
72
+ TypeUtils .getAllSupertypes(it.defaultType).any { it.isParceler() }
73
+ }
74
+
63
75
with (parcelableClass) {
64
76
writeDescribeContentsFunction(codegen, propertiesToSerialize)
65
- writeWriteToParcel(codegen, propertiesToSerialize, parcelAsmType)
77
+ writeWriteToParcel(codegen, propertiesToSerialize, parcelAsmType, parcelerObject )
66
78
}
67
79
68
80
writeCreatorAccessField(codegen, parcelableClass)
69
- writeCreatorClass(codegen, parcelableClass, parcelClassType, parcelAsmType, propertiesToSerialize)
81
+ writeCreatorClass(codegen, parcelableClass, parcelClassType, parcelAsmType, parcelerObject, propertiesToSerialize)
82
+ }
83
+
84
+ private fun getCompanionClassType (containerAsmType : Type , parcelerObject : ClassDescriptor ): Pair <Type , String > {
85
+ val shortName = parcelerObject.name
86
+ return Pair (Type .getObjectType(containerAsmType.internalName + " \$ $shortName " ), shortName.asString())
70
87
}
71
88
72
89
private fun ClassDescriptor.writeWriteToParcel (
73
90
codegen : ImplementationBodyCodegen ,
74
91
properties : List <Pair <String , KotlinType >>,
75
- parcelAsmType : Type
92
+ parcelAsmType : Type ,
93
+ parcelerObject : ClassDescriptor ?
76
94
): Unit? {
77
95
val containerAsmType = codegen.typeMapper.mapType(this .defaultType)
78
96
79
97
return findFunction(WRITE_TO_PARCEL )?.write(codegen) {
80
- for ((fieldName, type) in properties ) {
81
- val asmType = codegen.typeMapper.mapType(type )
98
+ if (parcelerObject != null ) {
99
+ val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject )
82
100
83
- v.load( 1 , parcelAsmType )
101
+ v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor )
84
102
v.load(0 , containerAsmType)
85
- v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor)
103
+ v.load(1 , PARCEL_TYPE )
104
+ v.load(2 , Type .INT_TYPE )
105
+ v.invokevirtual(companionAsmType.internalName, " write" ,
106
+ " (${containerAsmType.descriptor}${PARCEL_TYPE .descriptor} I)V" , false )
107
+ }
108
+ else {
109
+ for ((fieldName, type) in properties) {
110
+ val asmType = codegen.typeMapper.mapType(type)
86
111
87
- val serializer = ParcelSerializer .get(type, asmType, codegen.typeMapper)
88
- serializer.writeValue(v)
112
+ v.load(1 , parcelAsmType)
113
+ v.load(0 , containerAsmType)
114
+ v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor)
115
+
116
+ val serializer = ParcelSerializer .get(type, asmType, codegen.typeMapper)
117
+ serializer.writeValue(v)
118
+ }
89
119
}
90
120
91
121
v.areturn(Type .VOID_TYPE )
@@ -135,26 +165,38 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension {
135
165
creatorClass : ClassDescriptorImpl ,
136
166
parcelClassType : KotlinType ,
137
167
parcelAsmType : Type ,
168
+ parcelerObject : ClassDescriptor ? ,
138
169
properties : List <Pair <String , KotlinType >>
139
170
) {
140
171
val containerAsmType = codegen.typeMapper.mapType(parcelableClass)
141
172
142
173
createMethod(creatorClass, CREATE_FROM_PARCEL , parcelableClass.defaultType, " in" to parcelClassType).write(codegen) {
143
- v.anew(containerAsmType)
144
- v.dup( )
174
+ if (parcelerObject != null ) {
175
+ val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject )
145
176
146
- val asmConstructorParameters = StringBuilder ()
177
+ v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
178
+ v.load(1 , PARCEL_TYPE )
179
+ v.invokevirtual(companionAsmType.internalName, " create" ,
180
+ " (${PARCEL_TYPE .descriptor} )${containerAsmType.descriptor} " , false )
181
+ }
182
+ else {
183
+ v.anew(containerAsmType)
184
+ v.dup()
185
+
186
+ val asmConstructorParameters = StringBuilder ()
147
187
148
- for ((_, type) in properties) {
149
- val asmType = codegen.typeMapper.mapType(type)
150
- asmConstructorParameters.append(asmType.descriptor)
188
+ for ((_, type) in properties) {
189
+ val asmType = codegen.typeMapper.mapType(type)
190
+ asmConstructorParameters.append(asmType.descriptor)
191
+
192
+ val serializer = ParcelSerializer .get(type, asmType, codegen.typeMapper)
193
+ v.load(1 , parcelAsmType)
194
+ serializer.readValue(v)
195
+ }
151
196
152
- val serializer = ParcelSerializer .get(type, asmType, codegen.typeMapper)
153
- v.load(1 , parcelAsmType)
154
- serializer.readValue(v)
197
+ v.invokespecial(containerAsmType.internalName, " <init>" , " ($asmConstructorParameters )V" , false )
155
198
}
156
199
157
- v.invokespecial(containerAsmType.internalName, " <init>" , " ($asmConstructorParameters )V" , false )
158
200
v.areturn(containerAsmType)
159
201
}
160
202
}
@@ -171,6 +213,7 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension {
171
213
parcelableClass : ClassDescriptor ,
172
214
parcelClassType : KotlinType ,
173
215
parcelAsmType : Type ,
216
+ parcelerObject : ClassDescriptor ? ,
174
217
properties : List <Pair <String , KotlinType >>
175
218
) {
176
219
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
@@ -201,8 +244,8 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension {
201
244
writeSyntheticClassMetadata(classBuilderForCreator, codegen.state)
202
245
203
246
writeCreatorConstructor(codegenForCreator, creatorClass, creatorAsmType)
204
- writeNewArrayMethod(codegenForCreator, parcelableClass, creatorClass)
205
- writeCreateFromParcel(codegenForCreator, parcelableClass, creatorClass, parcelClassType, parcelAsmType, properties)
247
+ writeNewArrayMethod(codegenForCreator, parcelableClass, creatorClass, parcelerObject )
248
+ writeCreateFromParcel(codegenForCreator, parcelableClass, creatorClass, parcelClassType, parcelAsmType, parcelerObject, properties)
206
249
207
250
classBuilderForCreator.done()
208
251
}
@@ -221,7 +264,8 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension {
221
264
private fun writeNewArrayMethod (
222
265
codegen : ImplementationBodyCodegen ,
223
266
parcelableClass : ClassDescriptor ,
224
- creatorClass : ClassDescriptorImpl
267
+ creatorClass : ClassDescriptorImpl ,
268
+ parcelerObject : ClassDescriptor ?
225
269
) {
226
270
val builtIns = parcelableClass.builtIns
227
271
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass)
@@ -230,6 +274,29 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension {
230
274
builtIns.getArrayType(Variance .INVARIANT , parcelableClass.defaultType),
231
275
" size" to builtIns.intType
232
276
).write(codegen) {
277
+ if (parcelerObject != null ) {
278
+ val newArrayMethod = parcelerObject.unsubstitutedMemberScope
279
+ .getContributedFunctions(Name .identifier(" newArray" ), NoLookupLocation .WHEN_GET_ALL_DESCRIPTORS )
280
+ .filter {
281
+ it.typeParameters.isEmpty()
282
+ && it.kind == CallableMemberDescriptor .Kind .DECLARATION
283
+ && (it.valueParameters.size == 1 && KotlinBuiltIns .isInt(it.valueParameters[0 ].type))
284
+ && ! ((it.containingDeclaration as ? ClassDescriptor )?.defaultType?.isParceler() ? : true )
285
+ }.firstOrNull()
286
+
287
+ if (newArrayMethod != null ) {
288
+ val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
289
+ val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
290
+
291
+ v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
292
+ v.load(1 , Type .INT_TYPE )
293
+ v.invokevirtual(companionAsmType.internalName, " newArray" , " (I)[${containerAsmType.descriptor} " , false )
294
+ v.areturn(Type .getType(" [L$parcelableAsmType ;" ))
295
+
296
+ return @write
297
+ }
298
+ }
299
+
233
300
v.load(1 , Type .INT_TYPE )
234
301
v.newarray(parcelableAsmType)
235
302
v.areturn(Type .getType(" [L$parcelableAsmType ;" ))
0 commit comments