Parcelable: Add CREATOR field (and other generated declarations) in light classes (KT-19300, KT-19853)
This commit is contained in:
+1
-1
@@ -144,7 +144,7 @@ class ParcelableClinitClassBuilderInterceptorExtension : ClassBuilderInterceptor
|
||||
iv.anew(creatorType)
|
||||
iv.dup()
|
||||
iv.invokespecial(creatorName, "<init>", "()V", false)
|
||||
iv.putstatic(parcelableName, "CREATOR", creatorType.descriptor)
|
||||
iv.putstatic(parcelableName, "CREATOR", "Landroid/os/Parcelable\$Creator;")
|
||||
}
|
||||
|
||||
super.visitInsn(opcode)
|
||||
|
||||
+56
-17
@@ -1,3 +1,5 @@
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
@@ -14,10 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel
|
||||
|
||||
import kotlinx.android.parcel.TypeParceler
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension.Companion.createMethod
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.*
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.*
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
@@ -57,10 +58,14 @@ import java.io.FileDescriptor
|
||||
open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
private companion object {
|
||||
private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
||||
private val CREATOR_NAME = Name.identifier("CREATOR")
|
||||
}
|
||||
|
||||
protected open fun isExperimental(element: KtElement) = true
|
||||
|
||||
override val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean
|
||||
get() = true
|
||||
|
||||
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
|
||||
val parcelableClass = codegen.descriptor
|
||||
if (!parcelableClass.isParcelize) return
|
||||
@@ -68,24 +73,60 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
val sourceElement = (codegen.myClass as? KtClassOrObject) ?: return
|
||||
if (!isExperimental(sourceElement)) return
|
||||
|
||||
assert(parcelableClass.kind == ClassKind.CLASS || parcelableClass.kind == ClassKind.OBJECT)
|
||||
if (parcelableClass.kind != ClassKind.CLASS && parcelableClass.kind != ClassKind.OBJECT) return
|
||||
|
||||
val propertiesToSerialize = getPropertiesToSerialize(codegen, parcelableClass)
|
||||
|
||||
val parcelClassType = ParcelableResolveExtension.resolveParcelClassType(parcelableClass.module)
|
||||
val parcelAsmType = codegen.typeMapper.mapType(parcelClassType)
|
||||
|
||||
val parcelerObject = parcelableClass.companionObjectDescriptor?.takeIf {
|
||||
TypeUtils.getAllSupertypes(it.defaultType).any { it.isParceler }
|
||||
}
|
||||
|
||||
with (parcelableClass) {
|
||||
writeDescribeContentsFunction(codegen, propertiesToSerialize)
|
||||
writeWriteToParcel(codegen, propertiesToSerialize, parcelAsmType, parcelerObject)
|
||||
if (hasSyntheticDescribeContents()) {
|
||||
writeDescribeContentsFunction(codegen, propertiesToSerialize)
|
||||
}
|
||||
|
||||
if (hasSyntheticWriteToParcel()) {
|
||||
writeWriteToParcel(codegen, propertiesToSerialize, PARCEL_TYPE, parcelerObject)
|
||||
}
|
||||
|
||||
if (!hasCreatorField()) {
|
||||
writeCreatorAccessField(codegen)
|
||||
}
|
||||
}
|
||||
|
||||
writeCreatorAccessField(codegen, parcelableClass)
|
||||
writeCreatorClass(codegen, parcelableClass, parcelClassType, parcelAsmType, parcelerObject, propertiesToSerialize)
|
||||
if (codegen.state.classBuilderMode != ClassBuilderMode.LIGHT_CLASSES) {
|
||||
val parcelClassType = ParcelableResolveExtension.resolveParcelClassType(parcelableClass.module)
|
||||
?: error("Can't resolve 'android.os.Parcel' class")
|
||||
|
||||
writeCreatorClass(codegen, parcelableClass, parcelClassType, PARCEL_TYPE, parcelerObject, propertiesToSerialize)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasCreatorField(): Boolean {
|
||||
val companionObject = companionObjectDescriptor ?: return false
|
||||
|
||||
if (companionObject.name == CREATOR_NAME) {
|
||||
return true
|
||||
}
|
||||
|
||||
return companionObject.unsubstitutedMemberScope
|
||||
.getContributedVariables(CREATOR_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(ComponentKind.DESCRIBE_CONTENTS)
|
||||
|
||||
private fun ClassDescriptor.hasSyntheticWriteToParcel() = hasParcelizeSyntheticMethod(ComponentKind.WRITE_TO_PARCEL)
|
||||
|
||||
private fun ClassDescriptor.hasParcelizeSyntheticMethod(componentKind: ParcelableSyntheticComponent.ComponentKind): Boolean {
|
||||
val methodName = Name.identifier(componentKind.methodName)
|
||||
|
||||
val writeToParcelMethods = unsubstitutedMemberScope
|
||||
.getContributedFunctions(methodName, NoLookupLocation.FROM_BACKEND)
|
||||
.filter { it is ParcelableSyntheticComponent && it.componentKind == componentKind }
|
||||
|
||||
return writeToParcelMethods.size == 1
|
||||
}
|
||||
|
||||
private fun getCompanionClassType(containerAsmType: Type, parcelerObject: ClassDescriptor): Pair<Type, String> {
|
||||
@@ -166,11 +207,10 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor
|
||||
): List<PropertyToSerialize> {
|
||||
val constructor = parcelableClass.constructors.first { it.isPrimary }
|
||||
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
|
||||
|
||||
val propertiesToSerialize = constructor.valueParameters.map { param ->
|
||||
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
|
||||
codegen.bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, param]
|
||||
?: error("Value parameter should have 'val' or 'var' keyword")
|
||||
}
|
||||
|
||||
val classParcelers = getTypeParcelers(parcelableClass.annotations)
|
||||
@@ -228,12 +268,11 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeCreatorAccessField(codegen: ImplementationBodyCodegen, parcelableClass: ClassDescriptor) {
|
||||
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val creatorAsmType = Type.getObjectType(parcelableAsmType.internalName + "\$Creator")
|
||||
private fun writeCreatorAccessField(codegen: ImplementationBodyCodegen) {
|
||||
val creatorType = Type.getObjectType("android/os/Parcelable\$Creator")
|
||||
|
||||
codegen.v.newField(JvmDeclarationOrigin.NO_ORIGIN, ACC_STATIC or ACC_PUBLIC or ACC_FINAL, "CREATOR",
|
||||
creatorAsmType.descriptor, null, null)
|
||||
creatorType.descriptor, null, null)
|
||||
}
|
||||
|
||||
private fun writeCreatorClass(
|
||||
|
||||
+6
-7
@@ -33,14 +33,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
open class ParcelableResolveExtension : SyntheticResolveExtension {
|
||||
companion object {
|
||||
fun resolveParcelClassType(module: ModuleDescriptor): SimpleType {
|
||||
return module.findClassAcrossModuleDependencies(
|
||||
ClassId.topLevel(FqName("android.os.Parcel")))?.defaultType ?: error("Can't resolve 'android.os.Parcel' class")
|
||||
fun resolveParcelClassType(module: ModuleDescriptor): SimpleType? {
|
||||
return module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("android.os.Parcel")))?.defaultType
|
||||
}
|
||||
|
||||
fun createMethod(
|
||||
@@ -103,7 +103,7 @@ open class ParcelableResolveExtension : SyntheticResolveExtension {
|
||||
&& result.none { it.isWriteToParcel() }
|
||||
) {
|
||||
val builtIns = clazz.builtIns
|
||||
val parcelClassType = resolveParcelClassType(clazz.module)
|
||||
val parcelClassType = resolveParcelClassType(clazz.module) ?: ErrorUtils.createErrorType("Unresolved 'Parcel' type")
|
||||
result += createMethod(clazz, WRITE_TO_PARCEL, builtIns.unitType, "parcel" to parcelClassType, "flags" to builtIns.intType)
|
||||
}
|
||||
}
|
||||
@@ -120,12 +120,11 @@ open class ParcelableResolveExtension : SyntheticResolveExtension {
|
||||
internal fun SimpleFunctionDescriptor.isWriteToParcel(): Boolean {
|
||||
return typeParameters.isEmpty()
|
||||
&& valueParameters.size == 2
|
||||
&& valueParameters[0].type.isParcel()
|
||||
// Unfortunately, we can't check the first parameter cause it can be unresolved
|
||||
&& KotlinBuiltIns.isInt(valueParameters[1].type)
|
||||
&& returnType?.let { KotlinBuiltIns.isUnit(it) } == true
|
||||
}
|
||||
|
||||
private fun KotlinType.isParcel() = constructor.declarationDescriptor?.fqNameSafe == ANDROID_PARCEL_CLASS_FQNAME
|
||||
|
||||
interface ParcelableSyntheticComponent {
|
||||
val componentKind: ComponentKind
|
||||
|
||||
|
||||
+1
-1
@@ -534,7 +534,7 @@ internal class EfficientParcelableParcelSerializer(override val asmType: Type, p
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.getstatic(asmType.internalName, "CREATOR", creatorAsmType.descriptor) // -> parcel, creator
|
||||
v.getstatic(asmType.internalName, "CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
|
||||
v.swap() // -> creator, parcel
|
||||
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
|
||||
v.castIfNeeded(asmType)
|
||||
|
||||
+1
-4
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.android.synthetic.descriptors.ContainerOptionsProxy
|
||||
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFunction
|
||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
|
||||
Reference in New Issue
Block a user