Parcelable: Use ClassLoader from the container class to load Parcelable (KT-20027)

When the parameter type is just "Parcelable", we would not pick the wrong (system) class loader anymore.
This commit is contained in:
Yan Zhulanow
2017-09-01 22:28:04 +03:00
committed by Yan Zhulanow
parent c9ec1a2511
commit 3587a2a08e
7 changed files with 111 additions and 15 deletions
@@ -114,6 +114,8 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
"(${containerAsmType.descriptor}${PARCEL_TYPE.descriptor}I)V", false)
}
else {
val context = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType)
for ((fieldName, type) in properties) {
val asmType = codegen.typeMapper.mapType(type)
@@ -121,7 +123,7 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
v.load(0, containerAsmType)
v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor)
val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper)
val serializer = ParcelSerializer.get(type, asmType, context)
serializer.writeValue(v)
}
}
@@ -191,12 +193,13 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
v.dup()
val asmConstructorParameters = StringBuilder()
val context = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType)
for ((_, type) in properties) {
val asmType = codegen.typeMapper.mapType(type)
asmConstructorParameters.append(asmType.descriptor)
val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper)
val serializer = ParcelSerializer.get(type, asmType, context)
v.load(1, parcelAsmType)
serializer.readValue(v)
}
@@ -183,11 +183,16 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
/* isJvm8TargetWithDefaults */ false)
for (parameter in primaryConstructor?.valueParameters.orEmpty()) {
checkParcelableClassProperty(parameter, diagnosticHolder, typeMapper)
checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper)
}
}
private fun checkParcelableClassProperty(parameter: KtParameter, diagnosticHolder: DiagnosticSink, typeMapper: KotlinTypeMapper) {
private fun checkParcelableClassProperty(
parameter: KtParameter,
containerClass: ClassDescriptor,
diagnosticHolder: DiagnosticSink,
typeMapper: KotlinTypeMapper
) {
if (!parameter.hasValOrVar()) {
val reportElement = parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(
@@ -201,7 +206,8 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
val asmType = typeMapper.mapType(type)
try {
ParcelSerializer.get(type, asmType, typeMapper, strict = true)
val context = ParcelSerializer.ParcelSerializerContext(typeMapper, typeMapper.mapType(containerClass.defaultType))
ParcelSerializer.get(type, asmType, context, strict = true)
}
catch (e: IllegalArgumentException) {
// get() throws IllegalArgumentException on unknown types
@@ -44,6 +44,8 @@ interface ParcelSerializer {
fun writeValue(v: InstructionAdapter)
fun readValue(v: InstructionAdapter)
class ParcelSerializerContext(val typeMapper: KotlinTypeMapper, val containerClassType: Type)
companion object {
private fun KotlinTypeMapper.mapTypeSafe(type: KotlinType): Type {
return if (type.isError) Type.getObjectType("java/lang/Object") else mapType(type)
@@ -52,17 +54,19 @@ interface ParcelSerializer {
fun get(
type: KotlinType,
asmType: Type,
typeMapper: KotlinTypeMapper,
context: ParcelSerializerContext,
forceBoxed: Boolean = false,
strict: Boolean = false
): ParcelSerializer {
val typeMapper = context.typeMapper
val className = asmType.className
fun strict() = strict && !type.annotations.hasAnnotation(RAWVALUE_ANNOTATION_FQNAME)
return when {
asmType.sort == Type.ARRAY -> {
val elementType = type.builtIns.getArrayElementType(type)
val elementSerializer = get(elementType, typeMapper.mapTypeSafe(elementType), typeMapper, strict = strict())
val elementSerializer = get(elementType, typeMapper.mapTypeSafe(elementType), context, strict = strict())
wrapToNullAwareIfNeeded(type, ArrayParcelSerializer(asmType, elementSerializer))
}
@@ -90,7 +94,7 @@ interface ParcelSerializer {
-> {
val elementType = type.arguments.single().type
val elementSerializer = get(
elementType, typeMapper.mapTypeSafe(elementType), typeMapper, forceBoxed = true, strict = strict())
elementType, typeMapper.mapTypeSafe(elementType), context, forceBoxed = true, strict = strict())
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer))
}
@@ -104,9 +108,9 @@ interface ParcelSerializer {
-> {
val (keyType, valueType) = type.arguments.apply { assert(this.size == 2) }
val keySerializer = get(
keyType.type, typeMapper.mapTypeSafe(keyType.type), typeMapper, forceBoxed = true, strict = strict())
keyType.type, typeMapper.mapTypeSafe(keyType.type), context, forceBoxed = true, strict = strict())
val valueSerializer = get(
valueType.type, typeMapper.mapTypeSafe(valueType.type), typeMapper, forceBoxed = true, strict = strict())
valueType.type, typeMapper.mapTypeSafe(valueType.type), context, forceBoxed = true, strict = strict())
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer))
}
@@ -153,7 +157,7 @@ interface ParcelSerializer {
asmType.isSparseArray() -> {
val elementType = type.arguments.single().type
val elementSerializer = get(
elementType, typeMapper.mapTypeSafe(elementType), typeMapper, forceBoxed = true, strict = strict())
elementType, typeMapper.mapTypeSafe(elementType), context, forceBoxed = true, strict = strict())
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer))
}
@@ -186,10 +190,10 @@ interface ParcelSerializer {
}
creatorAsmType?.let { EfficientParcelableParcelSerializer(asmType, creatorAsmType) }
?: GenericParcelableParcelSerializer(asmType)
?: GenericParcelableParcelSerializer(asmType, context.containerClassType)
}
else {
GenericParcelableParcelSerializer(asmType)
GenericParcelableParcelSerializer(asmType, context.containerClassType)
}
}
@@ -462,7 +462,7 @@ internal class EfficientParcelableParcelSerializer(override val asmType: Type, p
}
}
internal class GenericParcelableParcelSerializer(override val asmType: Type) : ParcelSerializer {
internal class GenericParcelableParcelSerializer(override val asmType: Type, val containerClassType: Type) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
// -> parcel, parcelable
v.aconst(0) // -> parcel, parcelable, flags
@@ -471,7 +471,7 @@ internal class GenericParcelableParcelSerializer(override val asmType: Type) : P
override fun readValue(v: InstructionAdapter) {
// -> parcel
v.aconst(asmType) // -> parcel, type
v.aconst(containerClassType) // -> parcel, type
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
v.invokevirtual(PARCEL_TYPE.internalName, "readParcelable", "(Ljava/lang/ClassLoader;)Landroid/os/Parcelable;", false)
v.castIfNeeded(asmType)