Parcelable: Migrate to canonical NEW-DUP-INVOKESPECIAL form (KT-20545)
This commit is contained in:
+1
-10
@@ -75,11 +75,6 @@ class UninitializedStoresProcessor(
|
||||
private val methodNode: MethodNode,
|
||||
private val shouldPreserveClassInitialization: Boolean
|
||||
) {
|
||||
companion object {
|
||||
val AVOID_UNINITIALIZED_OBJECT_COPYING_CHECK_ANNOTATION_DESCRIPTOR =
|
||||
"Lkotlin/internal/annotations/AvoidUninitializedObjectCopyingCheck;"
|
||||
}
|
||||
|
||||
// <init> method is "special", because it will invoke <init> from this class or from a base class for #0
|
||||
//
|
||||
// <clinit> method is "special", because <clinit> for singleton objects is generated as:
|
||||
@@ -88,11 +83,7 @@ class UninitializedStoresProcessor(
|
||||
// and the newly created value is dropped.
|
||||
private val isInSpecialMethod = methodNode.name == "<init>" || methodNode.name == "<clinit>"
|
||||
|
||||
private val shouldCheckUninitializedObjectCopy = !methodNode.invisibleAnnotations.orEmpty()
|
||||
.any { it.desc == AVOID_UNINITIALIZED_OBJECT_COPYING_CHECK_ANNOTATION_DESCRIPTOR }
|
||||
|
||||
fun run() {
|
||||
if (!shouldCheckUninitializedObjectCopy) return
|
||||
val interpreter = UninitializedNewValueMarkerInterpreter(methodNode.instructions)
|
||||
|
||||
val frames = CustomFramesMethodAnalyzer(
|
||||
@@ -183,7 +174,7 @@ class UninitializedStoresProcessor(
|
||||
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
|
||||
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
|
||||
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue ?:
|
||||
if (isInSpecialMethod || !shouldCheckUninitializedObjectCopy)
|
||||
if (isInSpecialMethod)
|
||||
return null
|
||||
else
|
||||
error("Expected value generated with NEW")
|
||||
|
||||
+23
-17
@@ -18,36 +18,34 @@ 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.serializers.*
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.PARCEL_TYPE
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelSerializer
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.TypeParcelerMapping
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy.CodegenBased
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.codegen.writeSyntheticClassMetadata
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
@@ -104,8 +102,6 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
val containerAsmType = codegen.typeMapper.mapType(this.defaultType)
|
||||
|
||||
return findFunction(WRITE_TO_PARCEL)?.write(codegen) {
|
||||
v.visitAnnotation(UninitializedStoresProcessor.AVOID_UNINITIALIZED_OBJECT_COPYING_CHECK_ANNOTATION_DESCRIPTOR, false)
|
||||
|
||||
if (parcelerObject != null) {
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
@@ -117,7 +113,13 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
"(${containerAsmType.descriptor}${PARCEL_TYPE.descriptor}I)V", false)
|
||||
}
|
||||
else {
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList())
|
||||
val frameMap = FrameMap().apply {
|
||||
enterTemp(containerAsmType)
|
||||
enterTemp(PARCEL_TYPE)
|
||||
enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
|
||||
|
||||
for ((fieldName, type, parcelers) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
@@ -188,10 +190,9 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
properties: List<PropertyToSerialize>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
val creatorAsmType = codegen.typeMapper.mapType(creatorClass)
|
||||
|
||||
createMethod(creatorClass, CREATE_FROM_PARCEL, parcelableClass.builtIns.anyType, "in" to parcelClassType).write(codegen) {
|
||||
v.visitAnnotation(UninitializedStoresProcessor.AVOID_UNINITIALIZED_OBJECT_COPYING_CHECK_ANNOTATION_DESCRIPTOR, false)
|
||||
|
||||
if (parcelerObject != null) {
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
@@ -204,7 +205,12 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
v.dup()
|
||||
|
||||
val asmConstructorParameters = StringBuilder()
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList())
|
||||
val frameMap = FrameMap().apply {
|
||||
enterTemp(creatorAsmType)
|
||||
enterTemp(PARCEL_TYPE)
|
||||
}
|
||||
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
|
||||
|
||||
for ((_, type, parcelers) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
|
||||
+7
-1
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.android.parcel.serializers.isParcelable
|
||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
|
||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -214,7 +215,12 @@ class ParcelableDeclarationChecker : SimpleDeclarationChecker {
|
||||
|
||||
try {
|
||||
val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations)
|
||||
val context = ParcelSerializer.ParcelSerializerContext(typeMapper, typeMapper.mapType(containerClass.defaultType), parcelers)
|
||||
val context = ParcelSerializer.ParcelSerializerContext(
|
||||
typeMapper,
|
||||
typeMapper.mapType(containerClass.defaultType),
|
||||
parcelers,
|
||||
FrameMap())
|
||||
|
||||
ParcelSerializer.get(type, asmType, context, strict = true)
|
||||
}
|
||||
catch (e: IllegalArgumentException) {
|
||||
|
||||
+8
-6
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.android.parcel.serializers
|
||||
|
||||
import kotlinx.android.parcel.WriteWith
|
||||
import org.jetbrains.kotlin.android.parcel.isParcelize
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
@@ -54,7 +55,8 @@ interface ParcelSerializer {
|
||||
data class ParcelSerializerContext(
|
||||
val typeMapper: KotlinTypeMapper,
|
||||
val containerClassType: Type,
|
||||
val typeParcelers: List<TypeParcelerMapping>
|
||||
val typeParcelers: List<TypeParcelerMapping>,
|
||||
val frameMap: FrameMap
|
||||
) {
|
||||
fun findParcelerClass(type: KotlinType): KotlinType? {
|
||||
return typeParcelers.firstOrNull { it.first == type }?.second
|
||||
@@ -152,7 +154,7 @@ interface ParcelSerializer {
|
||||
}
|
||||
|
||||
val elementSerializer = get(elementType, elementAsmType, context, forceBoxed = true, strict = strict())
|
||||
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer))
|
||||
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
className == Map::class.java.canonicalName
|
||||
@@ -168,7 +170,7 @@ interface ParcelSerializer {
|
||||
keyType.type, typeMapper.mapTypeSafe(keyType.type, forceBoxed = true), context, forceBoxed = true, strict = strict())
|
||||
val valueSerializer = get(
|
||||
valueType.type, typeMapper.mapTypeSafe(valueType.type, forceBoxed = true), context, forceBoxed = true, strict = strict())
|
||||
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer))
|
||||
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
asmType.isBoxedPrimitive() -> wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forBoxedType(asmType))
|
||||
@@ -206,16 +208,16 @@ interface ParcelSerializer {
|
||||
Method("readSparseBooleanArray"))
|
||||
|
||||
asmType.isSparseIntArray() -> wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.INT_TYPE)))
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.INT_TYPE), context.frameMap))
|
||||
|
||||
asmType.isSparseLongArray() -> wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.LONG_TYPE)))
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.LONG_TYPE), context.frameMap))
|
||||
|
||||
asmType.isSparseArray() -> {
|
||||
val elementType = type.arguments.single().type
|
||||
val elementSerializer = get(
|
||||
elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, forceBoxed = true, strict = strict())
|
||||
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer))
|
||||
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
type.isCharSequence() -> CharSequenceParcelSerializer(asmType)
|
||||
|
||||
+44
-32
@@ -19,9 +19,10 @@ package org.jetbrains.kotlin.android.parcel.serializers
|
||||
import kotlinx.android.parcel.Parceler
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.BoxedPrimitiveTypeParcelSerializer.Companion.BOXED_VALUE_METHOD_NAMES
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.codegen.useTmpVar
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -186,8 +187,9 @@ internal fun InstructionAdapter.castIfNeeded(targetType: Type) {
|
||||
|
||||
internal class ListSetParcelSerializer(
|
||||
asmType: Type,
|
||||
elementSerializer: ParcelSerializer
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer) {
|
||||
elementSerializer: ParcelSerializer,
|
||||
frameMap: FrameMap
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
|
||||
override fun getSize(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Collection", "size", "()I")
|
||||
}
|
||||
@@ -216,8 +218,9 @@ internal class ListSetParcelSerializer(
|
||||
internal class MapParcelSerializer(
|
||||
asmType: Type,
|
||||
private val keySerializer: ParcelSerializer,
|
||||
elementSerializer: ParcelSerializer
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer) {
|
||||
elementSerializer: ParcelSerializer,
|
||||
frameMap: FrameMap
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
|
||||
override fun getSize(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Map", "size", "()I")
|
||||
}
|
||||
@@ -260,7 +263,8 @@ internal class MapParcelSerializer(
|
||||
|
||||
abstract internal class AbstractCollectionParcelSerializer(
|
||||
final override val asmType: Type,
|
||||
protected val elementSerializer: ParcelSerializer
|
||||
protected val elementSerializer: ParcelSerializer,
|
||||
private val frameMap: FrameMap
|
||||
) : ParcelSerializer {
|
||||
protected val collectionType: Type = Type.getObjectType(when (asmType.internalName) {
|
||||
"java/util/List" -> "java/util/ArrayList"
|
||||
@@ -331,27 +335,27 @@ abstract internal class AbstractCollectionParcelSerializer(
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.store(sizeVarIndex, Type.INT_TYPE)
|
||||
|
||||
v.anew(collectionType) // -> list
|
||||
v.dup() // -> list, list
|
||||
|
||||
if (hasConstructorWithCapacity) {
|
||||
v.load(sizeVarIndex, Type.INT_TYPE)
|
||||
v.invokespecial(collectionType.internalName, "<init>", "(I)V", false) // -> list
|
||||
} else {
|
||||
v.invokespecial(collectionType.internalName, "<init>", "()V", false) // -> list
|
||||
}
|
||||
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> list, size
|
||||
}
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
// Read list size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.dup() // -> size, size
|
||||
|
||||
v.anew(collectionType) // -> size, size, list
|
||||
v.dupX1() // -> size, list, size, list
|
||||
v.swap() // -> size, list, list, size
|
||||
|
||||
if (hasConstructorWithCapacity) {
|
||||
v.invokespecial(collectionType.internalName, "<init>", "(I)V", false) // -> size, list
|
||||
}
|
||||
else {
|
||||
v.pop() // -> size, list, list
|
||||
v.invokespecial(collectionType.internalName, "<init>", "()V", false) // -> size, list
|
||||
}
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.swap() // -> list, size
|
||||
v.dupX1() // -> size, list, size
|
||||
v.ifeq(loopIsOver) // -> size, list
|
||||
v.dup() // -> size, list, list
|
||||
@@ -362,16 +366,19 @@ abstract internal class AbstractCollectionParcelSerializer(
|
||||
v.aconst(-1) // -> list, size, (-1)
|
||||
v.add(Type.INT_TYPE) // -> list, (size - 1)
|
||||
|
||||
v.swap() // -> size, list
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.swap()
|
||||
v.swap() // -> list, size
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal class SparseArrayParcelSerializer(override val asmType: Type, private val valueSerializer: ParcelSerializer) : ParcelSerializer {
|
||||
internal class SparseArrayParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val valueSerializer: ParcelSerializer,
|
||||
private val frameMap: FrameMap
|
||||
) : ParcelSerializer {
|
||||
private val valueType = (valueSerializer as? PrimitiveTypeParcelSerializer)?.asmType ?: Type.getObjectType("java/lang/Object")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
@@ -421,12 +428,17 @@ internal class SparseArrayParcelSerializer(override val asmType: Type, private v
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.dup() // -> size, size
|
||||
v.anew(asmType) // -> size, size, arr
|
||||
v.dupX2() // -> arr, size, size, arr
|
||||
v.swap() // -> arr, size, arr, size
|
||||
v.invokespecial(asmType.internalName, "<init>", "(I)V", false) // -> arr, size
|
||||
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.store(sizeVarIndex, Type.INT_TYPE) // -> (empty)
|
||||
|
||||
v.anew(asmType) // -> arr
|
||||
v.dup() // -> arr, arr
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, arr, size
|
||||
v.invokespecial(asmType.internalName, "<init>", "(I)V", false) // -> arr
|
||||
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, size
|
||||
}
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/IBinderIInterface.txt
Vendored
+4
-5
@@ -18,13 +18,13 @@ public final class User$Creator : java/lang/Object, android/os/Parcelable$Creato
|
||||
CHECKCAST
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
DUP
|
||||
ISTORE (2)
|
||||
NEW
|
||||
DUP_X1
|
||||
SWAP
|
||||
DUP
|
||||
ILOAD (2)
|
||||
INVOKESPECIAL (java/util/ArrayList, <init>, (I)V)
|
||||
ILOAD (2)
|
||||
LABEL (L2)
|
||||
SWAP
|
||||
DUP_X1
|
||||
IFEQ (L3)
|
||||
DUP
|
||||
@@ -35,7 +35,6 @@ public final class User$Creator : java/lang/Object, android/os/Parcelable$Creato
|
||||
SWAP
|
||||
LDC (-1)
|
||||
IADD
|
||||
SWAP
|
||||
GOTO (L2)
|
||||
LABEL (L3)
|
||||
SWAP
|
||||
|
||||
@@ -75,8 +75,6 @@ class KaptJavaLog(
|
||||
}
|
||||
|
||||
override fun report(diagnostic: JCDiagnostic) {
|
||||
System.err.println("Diagnostic code: ${diagnostic.code}")
|
||||
|
||||
if (diagnostic.type == JCDiagnostic.DiagnosticType.ERROR && diagnostic.code in IGNORED_DIAGNOSTICS) {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user